feat: add Quests.generate, Quests.describe and Ranged.ammoIndex
This commit is contained in:
1 parent
54569d9e04
commit
10488b48e8
2 files changed
+163
No files matched your search
@@ -0,0 +1,73 @@
|
||||
-- Player ranged combat via an equipped bow and a stack of arrows carried in
|
||||
-- the inventory. Unlike throwing, firing never costs the launcher itself -
|
||||
-- only ammunition - so a bow stays equipped shot after shot as long as the
|
||||
-- quiver holds out. A loosed arrow lands where the target stood either way,
|
||||
-- recoverable exactly like a thrown weapon.
|
||||
|
||||
local Los = require("los")
|
||||
local Items = require("items")
|
||||
local Skills = require("skills")
|
||||
local Artifacts = require("artifacts")
|
||||
local Resistances = require("resistances")
|
||||
local Proficiency = require("proficiency")
|
||||
local Durability = require("durability")
|
||||
|
||||
local Ranged = {}
|
||||
|
||||
-- Inventory index of the first non-empty arrow stack, or nil if the quiver
|
||||
-- is dry. Ammo of the same name merges into one stack on pickup, so in
|
||||
-- practice there is at most one.
|
||||
function Ranged.ammoIndex(entity)
|
||||
for i, item in ipairs(entity.inventory) do
|
||||
if item.kind == Items.AMMO and item.count > 0 then return i end
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function Ranged.canReach(game, x1, y1, x2, y2, range)
|
||||
local dist = math.max(math.abs(x1 - x2), math.abs(y1 - y2))
|
||||
return dist <= range and Los.clear(game.level.map, x1, y1, x2, y2)
|
||||
end
|
||||
|
||||
-- Looses one arrow from `shooter`'s equipped bow at `target`. Returns (true,
|
||||
-- lines) if a shot went out - whatever it did on arrival - or (false, lines)
|
||||
-- if it never left the string: no bow readied, no arrows, or no clear shot.
|
||||
-- Damage comes from baseAttack rather than effectiveAttack so a melee
|
||||
-- weapon's power does not also inflate every arrow; the bow's own power and
|
||||
-- the target's armour are what separate a longbow from a sling. Mastery of
|
||||
-- that specific bow (see proficiency.lua) still applies on top, since it is
|
||||
-- read and recorded explicitly by the bow's own name rather than riding
|
||||
-- effectiveAttack's blended total.
|
||||
function Ranged.fire(game, shooter, target)
|
||||
local bow = shooter.bow
|
||||
if not bow then return false, { "you have no bow readied" } end
|
||||
if not target or not target:isAlive() then return false, { "no target" } end
|
||||
local ammoIdx = Ranged.ammoIndex(shooter)
|
||||
if not ammoIdx then return false, { "you have no arrows left" } end
|
||||
if not Ranged.canReach(game, shooter.x, shooter.y, target.x, target.y, bow.range) then
|
||||
return false, { "you have no clear shot" }
|
||||
end
|
||||
local ammo = shooter.inventory[ammoIdx]
|
||||
ammo.count = ammo.count - 1
|
||||
if ammo.count <= 0 then table.remove(shooter.inventory, ammoIdx) end
|
||||
local defense = math.max(0, target:effectiveDefense() - Artifacts.bowPierce(shooter))
|
||||
local dealt = shooter.baseAttack + Durability.effectivePower(bow) + Skills.rangedDamageBonus(shooter)
|
||||
+ Proficiency.bonusFor(shooter, bow.name) - defense
|
||||
if dealt < 1 then dealt = 1 end
|
||||
dealt = Resistances.apply(target, dealt, Resistances.PHYSICAL)
|
||||
target.hp = target.hp - dealt
|
||||
local lines = { string.format("your %s hits %s for %d", ammo.name, target.name, dealt) }
|
||||
game.level.floorItems[#game.level.floorItems + 1] = {
|
||||
x = target.x, y = target.y, item = { kind = Items.AMMO, name = ammo.name, glyph = ammo.glyph, count = 1 },
|
||||
}
|
||||
local masteredKey, masteredBonus = Proficiency.onHitWith(shooter, bow.name)
|
||||
if masteredKey then
|
||||
lines[#lines + 1] = string.format("you grow more skilled with your %s (+%d attack)", masteredKey, masteredBonus)
|
||||
end
|
||||
local broke = Durability.wearBow(shooter)
|
||||
if broke then lines[#lines + 1] = broke end
|
||||
if not target:isAlive() then game:awardKill(target) end
|
||||
return true, lines
|
||||
end
|
||||
|
||||
return Ranged
|
||||
Reference in new issue
Block a user