feat: add Sets.create and Sets.equippedCounts
This commit is contained in:
1 parent
00b89a9133
commit
2eb903beb5
1 file changed
+159
+159
@@ -0,0 +1,159 @@
|
||||
-- Item sets: matching gear that all share a `set` tag and grants an
|
||||
-- escalating stat bonus once enough pieces are worn together at once - a
|
||||
-- fixed weapon/armor/ring/bow catalog of named pieces (see Sets.create),
|
||||
-- unlike affixes.lua's random roll bolted onto an ordinary base item. The
|
||||
-- bonus is read live off however many pieces of a given set the wearer
|
||||
-- currently has equipped (Sets.attackBonus/defenseBonus, wired into
|
||||
-- combat.lua exactly the way runewords.lua's own live bonus is), so
|
||||
-- swapping a single piece out mid-run instantly drops back to a lower
|
||||
-- threshold - or none at all - rather than needing an explicit uninstall
|
||||
-- step the way a socketed gem or an enchant would.
|
||||
|
||||
local Items = require("items")
|
||||
|
||||
local Sets = {}
|
||||
|
||||
local SLOT_ORDER = { "weapon", "armor", "ring", "bow" }
|
||||
|
||||
Sets.catalog = {
|
||||
{
|
||||
key = "ironclad", name = "Ironclad",
|
||||
pieces = {
|
||||
weapon = { name = "Ironclad Greatsword", power = 3 },
|
||||
armor = { name = "Ironclad Plate", power = 3 },
|
||||
ring = { name = "Ironclad Signet", attack = 0, defense = 1 },
|
||||
},
|
||||
-- Independent totals, not a stacking ladder: reaching the 3-piece
|
||||
-- threshold replaces the 2-piece bonus rather than adding to it.
|
||||
bonuses = {
|
||||
[2] = { attack = 1, defense = 1 },
|
||||
[3] = { attack = 2, defense = 3, regen = 1 },
|
||||
},
|
||||
},
|
||||
{
|
||||
key = "duskrunner", name = "Duskrunner",
|
||||
pieces = {
|
||||
weapon = { name = "Duskrunner Fang", power = 2 },
|
||||
bow = { name = "Duskrunner Bow", power = 2, range = 5 },
|
||||
ring = { name = "Duskrunner Loop", attack = 1, defense = 0 },
|
||||
},
|
||||
bonuses = {
|
||||
[2] = { attack = 1 },
|
||||
[3] = { attack = 3, defense = 1 },
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Sets.byKey = {}
|
||||
for _, spec in ipairs(Sets.catalog) do
|
||||
Sets.byKey[spec.key] = spec
|
||||
end
|
||||
|
||||
-- Every (set, slot) pairing flattened in a fixed order, mirroring
|
||||
-- artifacts.lua's own Artifacts.order: the list a builder placement rolls
|
||||
-- a single uniform index into so every piece of every set is equally
|
||||
-- likely to turn up on a given floor.
|
||||
Sets.order = {}
|
||||
for _, spec in ipairs(Sets.catalog) do
|
||||
for _, slot in ipairs(SLOT_ORDER) do
|
||||
if spec.pieces[slot] then
|
||||
Sets.order[#Sets.order + 1] = { set = spec.key, slot = slot }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Builds the dropped item for `setKey`'s `slot` piece in the same shape
|
||||
-- items.lua's own constructors return for that slot, tagged with the
|
||||
-- `set` field every function below keys its live bonus off.
|
||||
function Sets.create(setKey, slot)
|
||||
local spec = Sets.byKey[setKey]
|
||||
local piece = spec and spec.pieces[slot]
|
||||
if not piece then return nil end
|
||||
local item
|
||||
if slot == "weapon" then item = Items.weapon(piece.name, piece.power)
|
||||
elseif slot == "armor" then item = Items.armor(piece.name, piece.power)
|
||||
elseif slot == "ring" then item = Items.ring(piece.name, piece.attack, piece.defense)
|
||||
elseif slot == "bow" then item = Items.bow(piece.name, piece.power, piece.range)
|
||||
end
|
||||
item.set = setKey
|
||||
return item
|
||||
end
|
||||
|
||||
-- How many of `entity`'s four equip slots currently carry a piece of each
|
||||
-- set it has at least one piece of, keyed by set key. Walks a fixed slot
|
||||
-- order rather than pairs() over the entity itself, so the result never
|
||||
-- depends on Lua's unspecified table iteration order.
|
||||
function Sets.equippedCounts(entity)
|
||||
local counts = {}
|
||||
for _, slot in ipairs(SLOT_ORDER) do
|
||||
local item = entity[slot]
|
||||
if item and item.set then
|
||||
counts[item.set] = (counts[item.set] or 0) + 1
|
||||
end
|
||||
end
|
||||
return counts
|
||||
end
|
||||
|
||||
-- The richest bonus tier `count` pieces of `setKey` qualify for, and the
|
||||
-- threshold it came from, or nil below its lowest threshold.
|
||||
function Sets.bonusFor(setKey, count)
|
||||
local spec = Sets.byKey[setKey]
|
||||
if not spec or not count or count <= 0 then return nil end
|
||||
local best, bestThreshold
|
||||
for threshold, bonus in pairs(spec.bonuses) do
|
||||
if count >= threshold and (not bestThreshold or threshold > bestThreshold) then
|
||||
best, bestThreshold = bonus, threshold
|
||||
end
|
||||
end
|
||||
return best, bestThreshold
|
||||
end
|
||||
|
||||
function Sets.attackBonus(entity)
|
||||
local total = 0
|
||||
local counts = Sets.equippedCounts(entity)
|
||||
for _, spec in ipairs(Sets.catalog) do
|
||||
local bonus = Sets.bonusFor(spec.key, counts[spec.key])
|
||||
if bonus then total = total + (bonus.attack or 0) end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
function Sets.defenseBonus(entity)
|
||||
local total = 0
|
||||
local counts = Sets.equippedCounts(entity)
|
||||
for _, spec in ipairs(Sets.catalog) do
|
||||
local bonus = Sets.bonusFor(spec.key, counts[spec.key])
|
||||
if bonus then total = total + (bonus.defense or 0) end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
-- Hp `entity` regenerates this turn from any active set bonus that grants
|
||||
-- one, summed the same way attack/defense are above.
|
||||
function Sets.regenAmount(entity)
|
||||
local total = 0
|
||||
local counts = Sets.equippedCounts(entity)
|
||||
for _, spec in ipairs(Sets.catalog) do
|
||||
local bonus = Sets.bonusFor(spec.key, counts[spec.key])
|
||||
if bonus then total = total + (bonus.regen or 0) end
|
||||
end
|
||||
return total
|
||||
end
|
||||
|
||||
-- A human-readable "Name Npc" tag for every set currently meeting its
|
||||
-- lowest threshold, in catalog order - read by render.lua's status panel
|
||||
-- and by nothing that affects game logic.
|
||||
function Sets.activeDescriptions(entity)
|
||||
local counts = Sets.equippedCounts(entity)
|
||||
local parts = {}
|
||||
for _, spec in ipairs(Sets.catalog) do
|
||||
local count = counts[spec.key]
|
||||
if Sets.bonusFor(spec.key, count) then
|
||||
parts[#parts + 1] = spec.name .. " " .. count .. "pc"
|
||||
end
|
||||
end
|
||||
if #parts == 0 then return nil end
|
||||
return table.concat(parts, ", ")
|
||||
end
|
||||
|
||||
return Sets
|
||||
Reference in new issue
Block a user