feat: add indexArg and hereHandler to commands

This commit is contained in:
dualfroz committed 2026-09-02 20:44:24 +02:00
1 parent 63b15fb146
commit 1d5768acd1
1 file changed
+163
+163
View File
@@ -159,3 +159,166 @@ Handlers.attack = function(game, args)
return false, "there is nothing to attack there"
end
Handlers.wait = function() return true, "you wait" end
Handlers.z = Handlers.wait
Handlers["."] = Handlers.wait
Handlers.pickup = function(game)
game:pickupHere()
return true, nil
end
Handlers.g = Handlers.pickup
-- Game:autoExplore already returns the exact (consumesTurn, message) shape
-- every handler here does - it just may have spent several turns getting
-- there instead of one, stopping itself the moment a hostile comes into
-- sight or the player takes damage rather than walking on regardless.
Handlers.explore = function(game)
return game:autoExplore()
end
-- Game:restUntilHealed shares the same shape and the same "may spend several
-- turns before returning" behaviour as Game:autoExplore just above.
Handlers.rest = function(game)
return game:restUntilHealed()
end
-- Game:travelTo shares that same "may spend several turns" shape too. "down"/
-- "up" are the level's own stairs (self.level.downX/Y, upX/Y) - always a
-- valid, already-walkable destination regardless of what the player has
-- explored yet, exactly like choosePlayerAction's own goal-seeking already
-- assumes; a raw x/y command is deliberately not offered since nothing in
-- this interface ever shows the player a raw coordinate to aim one at.
Handlers.travel = function(game, args)
if args[1] == "down" then return game:travelTo(game.level.downX, game.level.downY) end
if args[1] == "up" then return game:travelTo(game.level.upX, game.level.upY) end
return false, "travel where? down or up"
end
-- Every handler below that takes an inventory index shares the same shape:
-- a missing or non-numeric first arg fails without spending a turn, exactly
-- like an unknown verb, rather than crashing into the underlying Game
-- method with a nil index.
local function indexArg(args)
return tonumber(args[1])
end
Handlers.use = function(game, args)
local index = indexArg(args)
if not index then return false, "use what? give an inventory number" end
if game:useItem(index) then return true, nil end
return false, "you cannot use that"
end
Handlers.u = Handlers.use
Handlers.drop = function(game, args)
local index = indexArg(args)
if not index then return false, "drop what? give an inventory number" end
if game:dropItem(index) then return true, nil end
return false, "you have nothing there to drop"
end
Handlers.sell = function(game, args)
local index = indexArg(args)
if not index then return false, "sell what? give an inventory number" end
if game:sellItem(index) then return true, nil end
return false, "that cannot be sold here"
end
-- Trailing target number is always the argument slot right after whatever
-- an individual handler needed for itself (an inventory index, a spell/skill
-- key) - see resolveTarget above for exactly what "explicit" changes.
Handlers.throw = function(game, args)
local index = indexArg(args)
if not index then return false, "throw what? give an inventory number" end
local target, explicit = resolveTarget(game, args[2])
if not target then
if explicit then return false, "no monster in sight at that number" end
return false, "there is nothing in sight to throw at"
end
if game:throwAt(index, target) then return true, nil end
return false, "you cannot throw that"
end
Handlers.capture = function(game, args)
local index = indexArg(args)
if not index then return false, "capture with what? give an inventory number" end
local target, explicit = resolveTarget(game, args[2])
if not target then
if explicit then return false, "no monster in sight at that number" end
return false, "there is nothing in sight to capture"
end
if game:captureAt(index, target) then return true, nil end
return false, "the capture attempt fails"
end
Handlers.fire = function(game, args)
local target, explicit = resolveTarget(game, args[1])
if not target then
if explicit then return false, "no monster in sight at that number" end
return false, "there is nothing in sight to fire at"
end
if game:fireAt(target) then return true, nil end
return false, "you cannot fire right now"
end
Handlers.cast = function(game, args)
local key = args[1]
if not key then return false, "cast what spell?" end
local def = Spells.catalog[key]
if not def then return false, "you know no spell by that name" end
local target = nil
if def.targeted then
local explicit
target, explicit = resolveTarget(game, args[2])
if not target then
if explicit then return false, "no monster in sight at that number" end
return false, "there is nothing in sight to cast that at"
end
end
if game:castSpell(key, target) then return true, nil end
return false, "the spell fails"
end
-- multishot's two targets are named together or not at all: giving only one
-- explicit number is ambiguous about which of the (up to) two shots it picks
-- out, so that case falls back to the same auto-pick-both-nearest behaviour
-- as giving none, rather than guessing which slot the player meant.
Handlers.useskill = function(game, args)
local key = resolveSkillKey(args[1])
if not key then return false, "use which skill?" end
local success
if key == "cleave" then
local target, explicit = resolveTarget(game, args[2])
if not target and explicit then return false, "no monster in sight at that number" end
success = game:useSkill(key, target)
elseif key == "multishot" then
if args[2] and args[3] then
local list = visibleHostilesInOrder(game)
local t1, t2 = list[tonumber(args[2]) or -1], list[tonumber(args[3]) or -1]
if not t1 or not t2 then return false, "no monster in sight at that number" end
success = game:useSkill(key, t1, t2)
else
local targets = nearestHostiles(game, 2)
success = game:useSkill(key, targets[1], targets[2])
end
else
success = game:useSkill(key)
end
if success then return true, nil end
return false, "that skill fails"
end
-- Zero-argument actions that always resolve against whatever stands on the
-- player's own tile (a chest, a corpse, a captive, a node, a stairwell...),
-- the same way every one of these Game methods already looks itself up by
-- self.player.x/y rather than taking a target. `nothingMessage` only ever
-- surfaces when the underlying method itself failed silently (no log line
-- of its own) - most branches already explain themselves through game.log.
local function hereHandler(methodName, nothingMessage)
return function(game)
if game[methodName](game) then return true, nil end
return false, nothingMessage
end
end