feat: add groundIndexHere and Commands.apply to commands

This commit is contained in:
dualfroz committed 2026-09-02 22:31:27 +02:00
1 parent 4e4d22cf2e
commit 6f9d811c81
1 file changed
+146
+146
View File
@@ -477,3 +477,149 @@ Handlers.enchant = function(game, args)
return false, "that cannot be enchanted" return false, "that cannot be enchanted"
end end
Handlers.disenchant = function(game, args)
local target = targetArg(args[1])
if not target then return false, "disenchant what? weapon, armor or an item number" end
if game:disenchantItem(target) then return true, nil end
return false, "that cannot be disenchanted"
end
Handlers.repair = function(game, args)
local slot = args[1]
if slot ~= "weapon" and slot ~= "armor" and slot ~= "bow" then
return false, "repair what? weapon, armor or bow"
end
if game:repairEquipped(slot) then return true, nil end
return false, nil
end
-- Steals whichever warded ware sits underfoot, the same tile Game:pickupHere
-- already resolves against - unlike Game:stealFromShop's own raw floorItems
-- index, a typed command can only ever mean "the ware I am standing on".
local function groundIndexHere(game)
for i, ground in ipairs(game.level.floorItems) do
if ground.x == game.player.x and ground.y == game.player.y then return i, ground end
end
return nil
end
Handlers.steal = function(game)
local index = groundIndexHere(game)
if index and game:stealFromShop(index) then return true, nil end
return false, "there is nothing here worth stealing"
end
Handlers.dismiss = function(game)
if game:dismissHireling() then return true, nil end
return false, nil
end
Handlers.learn = function(game, args)
local key = resolveSkillKey(args[1])
if not key then return false, "learn which skill?" end
game:learnSkill(key)
return false, nil
end
-- Duplicates Game:brewAt's own precondition checks (cauldron underfoot, two
-- distinct herb items) before ever calling it, the same shape as disarm
-- above and for the same reason: past those preconditions, brewing always
-- spends both herbs, whether the pairing brews a potion or curdles into gas
-- (see Alchemy.brew), so this handler must not read a curdle's own `false`
-- as "nothing happened" and hand the player a free turn back.
Handlers.brew = function(game, args)
local a, b = tonumber(args[1]), tonumber(args[2])
if not a or not b then return false, "brew which two herbs? give two inventory numbers" end
if not game.level:cauldronAt(game.player.x, game.player.y) then
return false, "there is no cauldron here"
end
if a == b then return false, "brew two different herbs, not the same one twice" end
local herbA, herbB = game.player.inventory[a], game.player.inventory[b]
if not herbA or not herbB or herbA.kind ~= "herb" or herbB.kind ~= "herb" then
return false, "you need two herbs to brew"
end
game:brewAt(a, b)
return true, nil
end
-- Purely informational commands. Every one of these returns false (spends no
-- turn) and carries its whole answer in the message, which is the one place
-- a caller should print something the game log itself would never contain.
Handlers.inventory = function(game)
if #game.player.inventory == 0 then return false, "you are carrying nothing" end
local lines = {}
for i, item in ipairs(game.player.inventory) do
lines[#lines + 1] = string.format(" %d) %s", i, Identify.label(game.identify, item))
end
return false, table.concat(lines, "\n")
end
Handlers.i = Handlers.inventory
Handlers.status = function(game)
local p = game.player
return false, string.format(
"hp %d/%d mana %d/%d gold %d essence %d level %d xp %d/%d depth %d turn %d",
p.hp, p.maxHp, p.mana or 0, p.maxMana or 0, p.gold, p.essence,
p.level, p.xp, p.xpNext, game.depth, game.turn)
end
Handlers.look = function(game)
local targets = visibleHostilesInOrder(game)
if #targets == 0 then return false, "you see no threats nearby" end
local names = {}
for i, entity in ipairs(targets) do
names[#names + 1] = string.format("%d) %s", i, entity.name)
end
return false, "in sight: " .. table.concat(names, ", ")
end
Handlers.l = Handlers.look
-- Detail views (examine.lua): `examine <n>` inspects inventory item n, the
-- same index use/drop/sell already key off; `examine monster <n>` inspects
-- the nth entry of the very list Handlers.look just numbered. Both branches
-- are free, like every other informational command here.
Handlers.examine = function(game, args)
if args[1] == "monster" then
local index = tonumber(args[2])
if not index then return false, "examine which monster? give the number look showed you" end
local target = visibleHostilesInOrder(game)[index]
if not target then return false, "no monster in sight at that number" end
return false, Examine.monster(game.player, target)
end
local index = indexArg(args)
if not index then return false, "examine what? give an inventory number, or 'monster <n>'" end
local item = game.player.inventory[index]
if not item then return false, "you are not carrying anything at that number" end
return false, Examine.item(game.identify, item)
end
Handlers.x = Handlers.examine
Handlers.help = function()
return false, "commands: north/south/east/west/ne/nw/se/sw (or n/s/e/w/...), attack <dir>, " ..
"wait, pickup, explore, rest, travel down/up, inventory, use <n>, drop <n>, sell <n>, throw <n> [target], " ..
"fire [target], capture <n> [target], cast <spell> [target], useskill <skill> [target], " ..
"open, pray, quest, appease <faction>, butcher, raise, " ..
"scavenge, harvest, ransom, sellcaptive, interrogate, dig <dir>, disarm <dir>, " ..
"descend, ascend, enterrift, exitrift, horde, " ..
"steal, dismiss, learn <skill>, forge <element>, merge <a> <b>, brew <a> <b>, " ..
"socket <target> <gem>, socketrune <target> <rune>, temper <target>, " ..
"enchant <target> <affix>, disenchant <target>, repair <slot>, " ..
"examine <n>, examine monster <n>, look, status, help " ..
"([target] is the number look/examine gives a monster; omit it to auto-target the nearest one)"
end
-- Parses `text` and dispatches it against `game`. Returns (consumesTurn,
-- message): Game:manualStep spends the player's turn iff the first value is
-- true, and either surfaces `message` itself (a failure reason, or an
-- informational answer) or leaves the game log to speak for a successful
-- action, exactly the way every Game:<action> method already logs its own
-- outcome.
function Commands.apply(game, text)
local parsed = Commands.parse(text)
if not parsed then return false, "say something" end
local handler = Handlers[parsed.verb]
if not handler then return false, "unknown command: " .. parsed.verb end
return handler(game, parsed.args)
end
return Commands