Files
warren/src/durability.lua
T

128 lines
5.0 KiB
Lua

-- Weapon, armor and bow wear: unlike digging.lua's pick, a single limited-use
-- tool discarded outright once spent, a piece of gear wears down from
-- ordinary use but survives reaching zero - it just stops contributing to
-- combat until repaired, exactly like disenchanting a rune off a weapon
-- strips its bonus without destroying the weapon itself (see crafting.lua).
-- A weapon or armor's power is baked straight into the wielder's
-- attack/defense the instant it is equipped (see items.lua's
-- Items.equipWeapon/equipArmor), so breaking or repairing one here adjusts
-- that same baked total by the identical delta swapping gear already uses -
-- never a separate "effective power" read live in combat. A bow is the one
-- exception: its power is read live at the point of firing (see
-- ranged.lua's Ranged.fire), so Durability.effectivePower covers that
-- instead. Only the player's own gear wears at all - monsters have no
-- inventory management or repair access, so wear is never applied to theirs.
local Durability = {}
Durability.MAX = 30
Durability.WEAR_PER_HIT = 1
Durability.REPAIR_COST_PER_POINT = 2
-- Lazily stamps a fresh durability pair onto `item` the moment anything
-- first asks about it, so an item built before this field existed on it
-- (a raw table assembled somewhere this module cannot see) still gets a
-- sensible default rather than an error.
function Durability.ensure(item)
if not item.durability then
item.durability = Durability.MAX
item.maxDurability = Durability.MAX
end
return item
end
function Durability.isBroken(item)
Durability.ensure(item)
return item.durability <= 0
end
-- A broken bow contributes nothing to a shot's damage; read live in
-- Ranged.fire in place of a bare `bow.power`.
function Durability.effectivePower(item)
if not item then return 0 end
if Durability.isBroken(item) then return 0 end
return item.power or 0
end
local function isPlayerOwned(entity)
return entity.kind == "player"
end
-- Wears one point off `entity.weapon`, subtracting its power out of
-- entity.attack the instant it actually breaks (never before, and never
-- twice for the same break). A no-op, nil return, for an unarmed entity or
-- one whose weapon already sits at zero.
function Durability.wearWeapon(entity)
local item = entity.weapon
if not item then return nil end
Durability.ensure(item)
if item.durability <= 0 then return nil end
item.durability = item.durability - Durability.WEAR_PER_HIT
if item.durability <= 0 then
entity.attack = entity.attack - item.power
return isPlayerOwned(entity) and ("your " .. item.name .. " breaks apart from wear")
or (entity.name .. "'s " .. item.name .. " breaks apart from wear")
end
return nil
end
function Durability.wearArmor(entity)
local item = entity.armor
if not item then return nil end
Durability.ensure(item)
if item.durability <= 0 then return nil end
item.durability = item.durability - Durability.WEAR_PER_HIT
if item.durability <= 0 then
entity.defense = entity.defense - item.power
return isPlayerOwned(entity) and ("your " .. item.name .. " is battered to uselessness")
or (entity.name .. "'s " .. item.name .. " is battered to uselessness")
end
return nil
end
-- A bow's power is never baked into attack, so breaking one touches no stat
-- - Ranged.fire simply stops reading any power out of it via
-- Durability.effectivePower.
function Durability.wearBow(entity)
local item = entity.bow
if not item then return nil end
Durability.ensure(item)
if item.durability <= 0 then return nil end
item.durability = item.durability - Durability.WEAR_PER_HIT
if item.durability <= 0 then
return isPlayerOwned(entity) and ("your " .. item.name .. " snaps from wear")
or (entity.name .. "'s " .. item.name .. " snaps from wear")
end
return nil
end
function Durability.missing(item)
Durability.ensure(item)
return item.maxDurability - item.durability
end
function Durability.repairCost(item)
return Durability.missing(item) * Durability.REPAIR_COST_PER_POINT
end
-- Spends `entity.gold` to restore `entity`'s gear in `slotName` (one of
-- "weapon", "armor", "bow") fully, folding its power back into attack or
-- defense if the break had already stripped it out. Fails without spending
-- anything for an empty slot, an already-pristine item, or too thin a purse.
function Durability.repair(entity, slotName)
local item = entity[slotName]
if not item then return false, "nothing equipped there" end
Durability.ensure(item)
if item.durability >= item.maxDurability then return false, "already in perfect condition" end
local cost = Durability.repairCost(item)
if entity.gold < cost then return false, "not enough gold" end
local wasBroken = item.durability <= 0
entity.gold = entity.gold - cost
item.durability = item.maxDurability
if wasBroken and slotName == "weapon" then entity.attack = entity.attack + item.power end
if wasBroken and slotName == "armor" then entity.defense = entity.defense + item.power end
return true, cost
end
return Durability