This commit is contained in:
bryanthaboi
2026-09-29 16:00:57 -04:00
parent 9eb6f199b9
commit bf45bb51d6
3 changed files with 157 additions and 1 deletions
+2 -1
View File
@@ -1697,7 +1697,8 @@ function OverworldState:handleInput()
-- the edge outright made START a coin flip on the Cycling Road roll,
-- where the pull below re-arms a step on the single idle frame in
-- bikeStepFrames (#525).
if self.player.moving then
-- engine/gfx/screen_effects.asm:7-8
if self.player.moving or (self.poisonFlash or 0) > 0 then
local held = self.joyLatch
if not held then held = {}; self.joyLatch = held end
if input:wasPressed("a") then held.a = true end
@@ -0,0 +1,84 @@
-- engine/events/poison.asm:93 / engine/gfx/screen_effects.asm:2 (#2557)
return function(game)
local U = dofile("tests/drivers/util.lua")
local Pokemon = require("src.pokemon.Pokemon")
local shotDir = os.getenv("POKEPORT_SHOT_DIR") or ".bazinga/BSA/09-29-26-01-userreported/shots"
local fails = 0
local function check(label, ok)
U.log(ok and "PASS" or "FAIL", label)
if not ok then fails = fails + 1 end
return ok
end
game.save.party = { Pokemon.new(game.data, "CHARIZARD", 50) }
game.save.party[1].status = "PSN"
game.save.player.name = "bryan"
game.save.usedPokecenter = true
local function scenario(label, shots)
U.log("=== " .. label)
U.teleport(game, "VIRIDIAN_POKECENTER", 3, 7, "up")
game.save.poisonSteps = 0
local ow = game.overworld
local p = ow.player
local hpBefore = game.save.party[1].hp
local flashFrames, shotTaken = 0, false
for _ = 1, 400 do
table.insert(game.input.pressQueue, "up")
game.input.state.up = true
if p.moving and p.targetY == 3 and not game.input.state.a then
table.insert(game.input.pressQueue, "a")
game.input.state.a = true
end
coroutine.yield()
if (ow.poisonFlash or 0) > 0 and game.stack:top() == ow then
flashFrames = flashFrames + 1
if shots and not shotTaken then
shotTaken = true
game.input.state.up = false
U.still(game, shotDir .. "/2557_01_flash_before_nurse_talk.png")
end
end
if game.stack:top() ~= ow then break end
end
game.input.state.up = false
game.input.state.a = false
check(label .. ": 4th step landed at (3,3)", p.cellY == 3 and not p.moving)
check(label .. ": the 4th step cost 1 HP", game.save.party[1].hp == hpBefore - 1)
check(label .. ": flash plays 4 frames before the nurse talk", flashFrames == 4)
check(label .. ": nurse dialogue opened after the flash", game.stack:top() ~= ow)
check(label .. ": nothing frozen under the text box", (ow.poisonFlash or 0) == 0)
local deferred, balls = 0, false
for tick = 1, 1200 do
if game.stack:top() ~= ow and tick % 4 == 0 then
table.insert(game.input.pressQueue, "a")
end
coroutine.yield()
game.input.state.a = false
if game.stack:top() == ow and (ow.poisonFlash or 0) > 0 then
deferred = deferred + 1
end
if ow.healAnim and ow.healAnim.lit >= 1 then
balls = true
break
end
end
check(label .. ": machine started", balls)
check(label .. ": no poison flash after the nurse text", deferred == 0)
if shots and balls then
U.still(game, shotDir .. "/2557_02_no_flash_at_balls.png")
end
for _ = 1, 400 do
if game.stack:top() == ow and not ow.healAnim and not ow.emote then break end
if game.stack:top() ~= ow then table.insert(game.input.pressQueue, "a") end
coroutine.yield()
game.input.state.a = false
end
game.save.party[1].status = "PSN"
end
scenario("count_pass", false)
scenario("shot_pass", true)
love.event.quit(fails == 0 and 0 or 1)
end
@@ -0,0 +1,71 @@
-- engine/gfx/screen_effects.asm:2-12, home/overworld.asm:317 (#2557)
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local T = require("tests.modkit")
local check, eq = T.check, T.eq
T.fixtures.fresh()
local OW = require("src.world.OverworldController")
local function setUpvalue(fn, name, val)
local i = 1
while true do
local n = debug.getupvalue(fn, i)
if not n then return false end
if n == name then debug.setupvalue(fn, i, val); return true end
i = i + 1
end
end
local down = { a = true }
local pressed = {}
local input = {
wasPressed = function(_, k) return pressed[k] == true end,
isDown = function(_, k) return down[k] == true end,
}
local stack = { top = function() return nil end }
local game = { input = input, stack = stack, data = {}, save = {} }
setUpvalue(OW.handleInput, "Game", game)
setUpvalue(OW.poisonFlashLive, "Game", game)
local interacts = 0
local ow = setmetatable({
player = { moving = false, facing = "up" },
poisonFlash = 4,
dirHeld = function() return false end,
interact = function() interacts = interacts + 1 end,
}, { __index = OW })
stack.top = function() return ow end
pressed.a = true
ow:handleInput()
eq(interacts, 0, "A pressed during the poison flash does not interact")
check(ow.joyLatch and ow.joyLatch.a, "and is latched for the poll after the flash")
pressed.a = false
for i = 3, 1, -1 do
ow:tickPoisonFlash()
eq(ow.poisonFlash, i, "flash frame counts down")
ow:handleInput()
eq(interacts, 0, "held A still waits out flash frame " .. i)
end
check(ow.joyLatch and ow.joyLatch.a, "latch survives the whole flash")
ow:tickPoisonFlash()
eq(ow.poisonFlash, 0, "flash spent")
ow:handleInput()
eq(interacts, 1, "the still-held A talks on the first poll after the flash")
eq(ow.joyLatch, nil, "and the latch is consumed")
down.a = false
ow.poisonFlash = 2
ow.joyLatch = nil
pressed.a = true
ow:handleInput()
pressed.a = false
ow.poisonFlash = 0
ow:handleInput()
eq(interacts, 1, "an A released before the poll is lost, as on the cart")