feat: extend items with Items.tome and Items.amulet
This commit is contained in:
1 parent
2faa8dbe75
commit
9a647af6e3
2 files changed
+186
-7
No files matched your search
+131
-7
@@ -3,6 +3,8 @@
|
|||||||
-- modifies effective stats when equipped and may carry an affix. Potion and
|
-- modifies effective stats when equipped and may carry an affix. Potion and
|
||||||
-- scroll type catalogs drive the identification system.
|
-- scroll type catalogs drive the identification system.
|
||||||
|
|
||||||
|
local Durability = require("durability")
|
||||||
|
|
||||||
local Items = {}
|
local Items = {}
|
||||||
|
|
||||||
Items.POTION = "potion"
|
Items.POTION = "potion"
|
||||||
@@ -12,14 +14,40 @@ Items.ARMOR = "armor"
|
|||||||
Items.RING = "ring"
|
Items.RING = "ring"
|
||||||
Items.FOOD = "food"
|
Items.FOOD = "food"
|
||||||
Items.KEY = "key"
|
Items.KEY = "key"
|
||||||
|
Items.TOME = "tome"
|
||||||
|
Items.AMULET = "amulet"
|
||||||
|
Items.BOW = "bow"
|
||||||
|
Items.AMMO = "ammo"
|
||||||
|
Items.GOLD = "gold"
|
||||||
|
Items.GEM = "gem"
|
||||||
|
Items.CHARM = "charm"
|
||||||
|
Items.KIT = "kit"
|
||||||
|
Items.HERB = "herb"
|
||||||
|
Items.RUNE = "rune"
|
||||||
|
Items.PICK = "pick"
|
||||||
|
Items.LOCKPICK = "lockpick"
|
||||||
|
Items.CONTRACT = "contract"
|
||||||
|
Items.NET = "net"
|
||||||
|
Items.RIFTSTONE = "riftstone"
|
||||||
|
|
||||||
-- Catalogs list every distinct type in a fixed order; identification assigns a
|
-- Catalogs list every distinct type in a fixed order; identification assigns a
|
||||||
-- random appearance to each entry, so the order here must stay stable.
|
-- random appearance to each entry, so the order here must stay stable.
|
||||||
|
-- New entries append at the end: identify.lua assigns appearances by index
|
||||||
|
-- into this list, so inserting anywhere else would reshuffle which look
|
||||||
|
-- every earlier, already-loot-tested effect gets for a given seed. There are
|
||||||
|
-- exactly as many potionLooks in identify.lua as there are entries here, so
|
||||||
|
-- adding one more type here requires a matching look there too.
|
||||||
Items.potionTypes = {
|
Items.potionTypes = {
|
||||||
{ effect = "heal", name = "potion of healing", power = 10 },
|
{ effect = "heal", name = "potion of healing", power = 10 },
|
||||||
{ effect = "poison", name = "potion of poison", power = 3 },
|
{ effect = "poison", name = "potion of poison", power = 3 },
|
||||||
{ effect = "strength", name = "potion of strength", power = 0 },
|
{ effect = "strength", name = "potion of strength", power = 0 },
|
||||||
{ effect = "haste", name = "potion of speed", power = 0 },
|
{ effect = "haste", name = "potion of speed", power = 0 },
|
||||||
|
{ effect = "fortify", name = "potion of fortification", power = 0 },
|
||||||
|
{ effect = "clarity", name = "potion of clarity", power = 30 },
|
||||||
|
{ effect = "cure", name = "potion of cleansing", power = 0 },
|
||||||
|
{ effect = "confuse", name = "potion of confusion", power = 0 },
|
||||||
|
{ effect = "blind", name = "potion of blindness", power = 0 },
|
||||||
|
{ effect = "levitate", name = "potion of levitation", power = 0 },
|
||||||
}
|
}
|
||||||
|
|
||||||
Items.scrollTypes = {
|
Items.scrollTypes = {
|
||||||
@@ -27,6 +55,7 @@ Items.scrollTypes = {
|
|||||||
{ effect = "identify", name = "scroll of identify" },
|
{ effect = "identify", name = "scroll of identify" },
|
||||||
{ effect = "mapping", name = "scroll of magic mapping" },
|
{ effect = "mapping", name = "scroll of magic mapping" },
|
||||||
{ effect = "fireball", name = "scroll of fireball" },
|
{ effect = "fireball", name = "scroll of fireball" },
|
||||||
|
{ effect = "removecurse", name = "scroll of remove curse" },
|
||||||
}
|
}
|
||||||
|
|
||||||
function Items.typeId(item)
|
function Items.typeId(item)
|
||||||
@@ -54,11 +83,13 @@ function Items.scroll(effect)
|
|||||||
end
|
end
|
||||||
|
|
||||||
function Items.weapon(name, power)
|
function Items.weapon(name, power)
|
||||||
return { kind = Items.WEAPON, name = name, glyph = "/", power = power }
|
return { kind = Items.WEAPON, name = name, glyph = "/", power = power,
|
||||||
|
durability = Durability.MAX, maxDurability = Durability.MAX }
|
||||||
end
|
end
|
||||||
|
|
||||||
function Items.armor(name, power)
|
function Items.armor(name, power)
|
||||||
return { kind = Items.ARMOR, name = name, glyph = "[", power = power }
|
return { kind = Items.ARMOR, name = name, glyph = "[", power = power,
|
||||||
|
durability = Durability.MAX, maxDurability = Durability.MAX }
|
||||||
end
|
end
|
||||||
|
|
||||||
function Items.ring(name, attack, defense)
|
function Items.ring(name, attack, defense)
|
||||||
@@ -73,26 +104,115 @@ function Items.key(name)
|
|||||||
return { kind = Items.KEY, name = name or "iron key", glyph = "*" }
|
return { kind = Items.KEY, name = name or "iron key", glyph = "*" }
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Items.tome(spellKey, name)
|
||||||
|
return { kind = Items.TOME, spell = spellKey, name = name or ("tome of " .. spellKey), glyph = "&" }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.amulet(name)
|
||||||
|
return { kind = Items.AMULET, name = name or "Amulet of Warren", glyph = "\"" }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.bow(name, power, range)
|
||||||
|
return { kind = Items.BOW, name = name, glyph = "}", power = power, range = range,
|
||||||
|
durability = Durability.MAX, maxDurability = Durability.MAX }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.ammo(name, count)
|
||||||
|
return { kind = Items.AMMO, name = name or "arrow", glyph = "^", count = count }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.gold(amount)
|
||||||
|
return { kind = Items.GOLD, name = "gold", glyph = "$", amount = amount }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.charm(companionKey, name)
|
||||||
|
return { kind = Items.CHARM, companion = companionKey, name = name or ("charm of the " .. companionKey), glyph = "0" }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.kit(effect)
|
||||||
|
return { kind = Items.KIT, effect = effect, name = effect .. " trap kit", glyph = "t" }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- A shopkeeper's contract to hire mercenaries.lua's `key` roster entry: paid
|
||||||
|
-- for outright at the shop's own price (see shop.lua's CONTRACT branch), so
|
||||||
|
-- using it just takes the mercenary on rather than spending any further
|
||||||
|
-- gold - unlike a trap kit above, which is free to find but costs nothing
|
||||||
|
-- to use either.
|
||||||
|
function Items.contract(key, name)
|
||||||
|
return { kind = Items.CONTRACT, hireling = key, name = name or (key .. " contract"), glyph = "P" }
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.pick(name, uses)
|
||||||
|
return { kind = Items.PICK, name = name or "miner's pick", glyph = "\\", uses = uses or 20 }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- A single-use gamble against a locked chest's own lock (see chests.lua's
|
||||||
|
-- Chests.pick) - consumed win or lose, unlike the miner's pick above which
|
||||||
|
-- wears down over many swings instead of vanishing in one.
|
||||||
|
function Items.lockpick(name)
|
||||||
|
return { kind = Items.LOCKPICK, name = name or "lockpick", glyph = ";" }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- A weighted throwing net (see captives.lua's Captives.resolve): unlike a
|
||||||
|
-- thrown weapon above, which is aimed to hurt, a net is aimed to subdue a
|
||||||
|
-- battered wild monster alive instead of finishing it off - and is spent
|
||||||
|
-- from the inventory whether or not it actually holds, exactly like a
|
||||||
|
-- lockpick's own single gamble.
|
||||||
|
function Items.net(name)
|
||||||
|
return { kind = Items.NET, name = name or "net", glyph = "o" }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Fuel for a single rift crossing (see branches.lua): unlike a key, which
|
||||||
|
-- only ever opens the one lock it was cut for, a shard is spent the instant
|
||||||
|
-- Game:enterRift first stabilizes a given rift, not on every crossing after -
|
||||||
|
-- so carrying more than one only ever matters if the run finds more than one
|
||||||
|
-- rift to open.
|
||||||
|
function Items.riftstone(name)
|
||||||
|
return { kind = Items.RIFTSTONE, name = name or "rift shard", glyph = "R" }
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Adds/removes a weapon or armor's power using its own durability-aware
|
||||||
|
-- effective power (see durability.lua) rather than its raw `power` field, so
|
||||||
|
-- unequipping and later re-equipping an already-broken piece never bakes
|
||||||
|
-- power back in for free.
|
||||||
function Items.equipWeapon(item, entity)
|
function Items.equipWeapon(item, entity)
|
||||||
if entity.weapon then
|
if entity.weapon then
|
||||||
entity.attack = entity.attack - entity.weapon.power
|
entity.attack = entity.attack - Durability.effectivePower(entity.weapon)
|
||||||
entity.inventory[#entity.inventory + 1] = entity.weapon
|
entity.inventory[#entity.inventory + 1] = entity.weapon
|
||||||
end
|
end
|
||||||
entity.weapon = item
|
entity.weapon = item
|
||||||
entity.attack = entity.attack + item.power
|
local gained = Durability.effectivePower(item)
|
||||||
return item.power
|
entity.attack = entity.attack + gained
|
||||||
|
return gained
|
||||||
end
|
end
|
||||||
|
|
||||||
function Items.equipArmor(item, entity)
|
function Items.equipArmor(item, entity)
|
||||||
if entity.armor then
|
if entity.armor then
|
||||||
entity.defense = entity.defense - entity.armor.power
|
entity.defense = entity.defense - Durability.effectivePower(entity.armor)
|
||||||
entity.inventory[#entity.inventory + 1] = entity.armor
|
entity.inventory[#entity.inventory + 1] = entity.armor
|
||||||
end
|
end
|
||||||
entity.armor = item
|
entity.armor = item
|
||||||
entity.defense = entity.defense + item.power
|
local gained = Durability.effectivePower(item)
|
||||||
|
entity.defense = entity.defense + gained
|
||||||
|
return gained
|
||||||
|
end
|
||||||
|
|
||||||
|
function Items.equipBow(item, entity)
|
||||||
|
if entity.bow then
|
||||||
|
entity.inventory[#entity.inventory + 1] = entity.bow
|
||||||
|
end
|
||||||
|
entity.bow = item
|
||||||
return item.power
|
return item.power
|
||||||
end
|
end
|
||||||
|
|
||||||
|
function Items.equipPick(item, entity)
|
||||||
|
if entity.pick then
|
||||||
|
entity.inventory[#entity.inventory + 1] = entity.pick
|
||||||
|
end
|
||||||
|
entity.pick = item
|
||||||
|
return item.uses
|
||||||
|
end
|
||||||
|
|
||||||
function Items.equipRing(item, entity)
|
function Items.equipRing(item, entity)
|
||||||
if entity.ring then
|
if entity.ring then
|
||||||
entity.attack = entity.attack - entity.ring.attack
|
entity.attack = entity.attack - entity.ring.attack
|
||||||
@@ -119,6 +239,10 @@ function Items.use(item, entity)
|
|||||||
return Items.equipArmor(item, entity)
|
return Items.equipArmor(item, entity)
|
||||||
elseif item.kind == Items.RING then
|
elseif item.kind == Items.RING then
|
||||||
return Items.equipRing(item, entity)
|
return Items.equipRing(item, entity)
|
||||||
|
elseif item.kind == Items.BOW then
|
||||||
|
return Items.equipBow(item, entity)
|
||||||
|
elseif item.kind == Items.PICK then
|
||||||
|
return Items.equipPick(item, entity)
|
||||||
end
|
end
|
||||||
return 0
|
return 0
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
-- Persistent top-N high score table. A run's own numeric score (see
|
||||||
|
-- morgue.lua's Morgue.score) only matters across runs if something
|
||||||
|
-- remembers it - this is that memory, carried inside meta.lua's own save
|
||||||
|
-- file exactly like renown and achievements already are, so a leaderboard
|
||||||
|
-- outlives every single run that fed it.
|
||||||
|
|
||||||
|
local Leaderboard = {}
|
||||||
|
|
||||||
|
Leaderboard.CAPACITY = 10
|
||||||
|
|
||||||
|
function Leaderboard.new()
|
||||||
|
return {}
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Shifts board's tail down to open a gap for `entry` at the first slot
|
||||||
|
-- whose current occupant does not outscore it, then drops it there.
|
||||||
|
-- Returns the 1-based rank entry lands at, counting past Leaderboard.CAPACITY
|
||||||
|
-- when the board is already full - the caller trims that overflow back off.
|
||||||
|
local function insertSorted(board, entry)
|
||||||
|
local i = #board + 1
|
||||||
|
while i > 1 and board[i - 1].score < entry.score do
|
||||||
|
board[i] = board[i - 1]
|
||||||
|
i = i - 1
|
||||||
|
end
|
||||||
|
board[i] = entry
|
||||||
|
return i
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Inserts `entry` (expects at least a numeric .score field) into `board`,
|
||||||
|
-- keeping it sorted best-score-first and capped at Leaderboard.CAPACITY.
|
||||||
|
-- A run that does not crack the cap is dropped outright rather than kept
|
||||||
|
-- around uncounted - a leaderboard only remembers what actually placed.
|
||||||
|
-- Returns the rank it landed at, or nil if it did not make the cut.
|
||||||
|
function Leaderboard.record(board, entry)
|
||||||
|
local rank = insertSorted(board, entry)
|
||||||
|
while #board > Leaderboard.CAPACITY do
|
||||||
|
table.remove(board)
|
||||||
|
end
|
||||||
|
if rank > Leaderboard.CAPACITY then return nil end
|
||||||
|
return rank
|
||||||
|
end
|
||||||
|
|
||||||
|
function Leaderboard.top(board, count)
|
||||||
|
local out = {}
|
||||||
|
for i = 1, math.min(count or Leaderboard.CAPACITY, #board) do
|
||||||
|
out[i] = board[i]
|
||||||
|
end
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
function Leaderboard.bestScore(board)
|
||||||
|
return board[1] and board[1].score or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
return Leaderboard
|
||||||
Reference in new issue
Block a user