feat: add Biomes.forDepth, Biomes.describe and Blocks.treasure

This commit is contained in:
dualfroz committed 2026-09-01 21:03:17 +02:00
1 parent fbfe8411ac
commit 9bcc8fe6e2
2 files changed
+175

No files matched your search

+113
View File
@@ -0,0 +1,113 @@
-- Pushable blocks: a block occupies a floor cell and blocks movement and
-- pathing exactly like a wall, but shoving it (see Game:movePlayer) relocates
-- it one cell in the push direction instead of just refusing the move, the
-- same "walk into it to interact" shape a closed door or a vault lever
-- already has. A block shoved into lava solidifies into rubble and one
-- shoved into a chasm fills the gap into plain floor - both permanently
-- consume the block, since it has nothing left to rest on once the hazard
-- it corrected is gone. Shoved onto a pressure plate instead, the block
-- survives and holds its linked gate open for as long as it stays put (see
-- Blocks.updateGates) - unlike vaults.lua's lever, a gate reseals the
-- instant the weight holding it open moves off the plate again.
local Tiles = require("tiles")
local Items = require("items")
local Affixes = require("affixes")
local Blocks = {}
local treasureWeapons = {
{ name = "gate-forged blade", power = 7 },
{ name = "gate-forged maul", power = 8 },
}
local treasureArmors = {
{ name = "gate-forged plate", power = 6 },
}
-- A guaranteed affix on a base already well above anything the floor's own
-- loot table can roll at this depth, mirroring vaults.lua's own Vaults.
-- treasure but with names that never claim to come from a vault.
function Blocks.treasure(rng, depth)
if rng:chance(1, 2) then
local base = treasureWeapons[rng:range(1, #treasureWeapons)]
local item = Items.weapon(base.name, base.power + depth // 3)
return Affixes.decorate(item, Affixes.weapon[rng:range(1, #Affixes.weapon)])
end
local base = treasureArmors[rng:range(1, #treasureArmors)]
local item = Items.armor(base.name, base.power + depth // 3)
return Affixes.decorate(item, Affixes.armor[rng:range(1, #Affixes.armor)])
end
-- Whether a cell a block might be shoved into can actually receive it: in
-- bounds, not a wall, a door of any kind, or a gate, and not already host to
-- another block or a living entity. Lava and chasm cells count as
-- receiving even though Blocks.push special-cases what happens to them
-- next, rather than refusing the shove outright.
function Blocks.canReceive(level, x, y)
if not level.map:inBounds(x, y) then return false end
local tile = level.map:get(x, y)
if tile == Tiles.WALL or Tiles.isDoor(tile) or Tiles.isVaultDoor(tile) or Tiles.isGate(tile) then
return false
end
if level:blockAt(x, y) or level:entityAt(x, y) then return false end
return true
end
local function remove(level, block)
for i, b in ipairs(level.blocks) do
if b == block then
table.remove(level.blocks, i)
return
end
end
end
-- Shoves `block` by (dx, dy) if the far cell can receive it. Returns
-- (true, message) on a successful shove, resolving whatever lava or chasm
-- it lands in along the way (Blocks.remove), or (false) when the far cell
-- refuses it, exactly like bumping into a wall.
function Blocks.push(level, block, dx, dy)
local nx, ny = block.x + dx, block.y + dy
if not Blocks.canReceive(level, nx, ny) then return false end
local tile = level.map:get(nx, ny)
if tile == Tiles.LAVA then
level.map:set(nx, ny, Tiles.RUBBLE)
remove(level, block)
return true, "the block hisses into the lava and cools into rubble"
end
if tile == Tiles.CHASM then
level.map:set(nx, ny, Tiles.FLOOR)
remove(level, block)
return true, "the block tumbles into the chasm and fills it"
end
block.x, block.y = nx, ny
return true, "you shove the block"
end
-- Whether any plate linked to `gate` currently carries a block's weight.
local function gateHeld(level, gate)
for _, plate in ipairs(level.plates) do
if plate.gateX == gate.x and plate.gateY == gate.y and level:blockAt(plate.x, plate.y) then
return true
end
end
return false
end
-- Re-evaluates every gate on the level against its own plates' current
-- weight, flipping both its tile glyph and its `open` field the instant a
-- plate gains or loses a block. Called after any block shove (Game:
-- movePlayer) so a gate reacts immediately rather than waiting for a full
-- turn tick, the same way Vaults.pull's door flip is immediate on the pull
-- that completes it.
function Blocks.updateGates(level)
for _, gate in ipairs(level.gates) do
local held = gateHeld(level, gate)
if held ~= gate.open then
gate.open = held
level.map:set(gate.x, gate.y, held and Tiles.GATE_OPEN or Tiles.GATE_CLOSED)
end
end
end
return Blocks