Files
alchemist-accessibility/accessibility/states/ingame_targeting_throw.lua
T

92 lines
2.9 KiB
Lua
Raw Normal View History

-- states/ingame_targeting_throw.lua — the ranged targeting cursor.
--
-- Same shape, and same reasoning, as accessibility/states/ingame_observe.lua:
-- read level.cursorX/cursorY straight off the level model each frame rather than
-- listening to the game's cursor popup. The popup is refreshed from the cursor
-- animation's completion callback (`onFinish`), so relaying it would put every
-- announcement a keypress behind. animations/moveCursor.lua assigns cursorX/Y in
-- its constructor, so polling is correct on the frame the key is pressed.
--
-- The extra job here over examine mode is saying whether the tile is a LEGAL
-- target, which is the whole point of being in this state.
local describe = require("accessibility.describe")
-- Recompute validity from the level rather than reading the state's own
-- `targetAllowed` upvalue.
--
-- Why: states/ingame_targeting_throw.lua:228 sets targetAllowed = false for the
-- duration of the 0.3s cursor glide and only the animation's onFinish restores
-- it. We speak on the frame the key is pressed, i.e. mid-glide, so that upvalue
-- would report "blocked" for every single move. The three inputs the game
-- itself uses (:86-89) are all public fields, so recomputing is both cheap and
-- honest.
local function validity(level, x, y)
if not level.interactableArea then
return nil
end
if not (level.FoV and level.FoV[x] and level.FoV[x][y]) then
return "no line of sight"
end
if not (level.interactableArea[x] and level.interactableArea[x][y]) then
return "out of range"
end
local ok, throwable = pcall(level.canThrowOnCell, x, y)
if ok and not throwable then
return "blocked"
end
return "can throw"
end
return {
read = function()
local ok, world = pcall(require, "models.world")
if not ok or not world.currentLevel then
return nil, nil
end
local level = world.currentLevel
local x, y = level.cursorX, level.cursorY
if type(x) ~= "number" or type(y) ~= "number" then
return nil, nil
end
local player = level.playerActor
local px = player and player.x
local py = player and player.y
-- Terse: in combat the target's name, its legality and how far away it is
-- are what matter; the scenery adjectives are noise.
--
-- Described without an origin so that describe.tile does not append the
-- offset itself -- the validity belongs between the two, giving
-- "rat, can throw, 3 tiles north-east".
local previousVerbosity = describe.verbosity
describe.verbosity = "terse"
local okTile, text = pcall(describe.tile, level, x, y)
describe.verbosity = previousVerbosity
if not okTile or not text or text == "" then
return nil, nil
end
local parts = {text}
local state = validity(level, x, y)
if state then
parts[#parts + 1] = state
end
if px and py then
parts[#parts + 1] = describe.offset(x - px, y - py)
end
return table.concat(parts, ", "), x .. "," .. y
end,
}