mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
132 lines
4.8 KiB
Lua
132 lines
4.8 KiB
Lua
-- The per-step event chain, in the running game.
|
|
--
|
|
-- `World` kept no step counter at all until this, so `Happiness.step` and
|
|
-- `Breeding.step` were written, tested and never called: eggs never hatched.
|
|
-- CountStep (engine/overworld/events.asm) now runs between the coord events and
|
|
-- the wild roll, and DoEggStep ticks at wStepCount $80.
|
|
--
|
|
-- This walks a real party with a real egg in it until the counter reaches the
|
|
-- egg phase, then asserts the slot came out of it as a Pokemon.
|
|
--
|
|
-- POKEPORT_IDENTITY=gold-dev POKEPORT_GAME=gold \
|
|
-- POKEPORT_DRIVER=tests/drivers/gold_egg_hatch.lua \
|
|
-- perl -e 'alarm 300; exec @ARGV' \
|
|
-- python3 -c "import pty; pty.spawn(['love','.'])"
|
|
local SHOT_DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-steps"
|
|
|
|
return function(game)
|
|
local w = game.world
|
|
local fails = 0
|
|
|
|
local function wait(n) for _ = 1, n do coroutine.yield() end end
|
|
|
|
local function ok(cond, msg)
|
|
if cond then print("[steps] ok " .. msg)
|
|
else fails = fails + 1 print("[steps] FAIL " .. msg) end
|
|
return cond
|
|
end
|
|
|
|
local function clearDirs()
|
|
game.input.pressQueue = {}
|
|
for _, d in ipairs({ "up", "down", "left", "right" }) do
|
|
game.input.state[d] = false
|
|
game.input.sources[d] = nil
|
|
end
|
|
end
|
|
|
|
-- Walk back and forth on a clear row. Holding one direction for a fixed
|
|
-- stretch is the reliable shape here: a step is 16 pixels at one a frame, so
|
|
-- 20 held frames is always at least one full footfall.
|
|
local function pace(frames, dir)
|
|
for _ = 1, frames do
|
|
if w:busy() then clearDirs() return true end
|
|
table.insert(game.input.pressQueue, dir)
|
|
game.input.state[dir] = true
|
|
coroutine.yield()
|
|
end
|
|
clearDirs()
|
|
coroutine.yield()
|
|
return w:busy()
|
|
end
|
|
|
|
os.execute('mkdir -p "' .. SHOT_DIR .. '" 2>/dev/null')
|
|
wait(45)
|
|
|
|
local save = game.save
|
|
-- No wild encounters and no coord events in the way: this is about the step
|
|
-- counter, not about what else a footfall can trigger.
|
|
w.mapScenes.NEW_BARK_TOWN = 1
|
|
w:setMap("NEW_BARK_TOWN", 6, 8, "down")
|
|
wait(15)
|
|
|
|
-- An egg on its last cycle, and a mon in front of it so the party is honest.
|
|
local Mon = require("src.battle.gen2.Mon")
|
|
local lead = Mon.new(game.data, "CYNDAQUIL", 5)
|
|
save.party = { lead, {
|
|
isEgg = true, species = "TOGEPI", name = "EGG", level = 5,
|
|
eggSteps = 1, dvs = lead.dvs, moves = {},
|
|
ot = save.player and save.player.name,
|
|
otId = save.player and save.player.id,
|
|
} }
|
|
save.stepCount = nil
|
|
save.poisonStepCount = nil
|
|
|
|
local Breeding = require("src.core.gen2.Breeding")
|
|
ok(Breeding.isEgg(save.party[2]), "the party starts with an egg in slot 2")
|
|
|
|
-- DoEggStep fires at wStepCount $80, so at most 128 footfalls from zero.
|
|
local hit = false
|
|
for i = 1, 300 do
|
|
if pace(24, (i % 2 == 1) and "left" or "right") then hit = true break end
|
|
if not Breeding.isEgg(save.party[2]) then hit = true break end
|
|
if i == 1 then
|
|
ok((save.stepCount or 0) > 0,
|
|
"one lap already moved wStepCount to " .. tostring(save.stepCount))
|
|
end
|
|
end
|
|
ok((save.stepCount or 0) > 0,
|
|
"the world counts steps at all now (wStepCount = "
|
|
.. tostring(save.stepCount) .. ")")
|
|
ok(hit, "and something fired inside 128 footfalls")
|
|
ok(save.stepCount == 0x80,
|
|
"at wStepCount $80, DoEggStep's own phase (got "
|
|
.. tostring(save.stepCount) .. ")")
|
|
|
|
game.capturePath = SHOT_DIR .. "/hatch-huh.png"
|
|
wait(4)
|
|
|
|
-- "Huh?" and the hatch line advance on A; the nickname prompt is a yes/no and
|
|
-- B is NO, which is the arm that keeps the species name (HatchEggs' own
|
|
-- .nonickname). Answering YES would push the naming screen, which is a stack
|
|
-- state rather than a World busy flag and would sit there forever.
|
|
for _ = 1, 600 do
|
|
if not w:busy() then break end
|
|
table.insert(game.input.pressQueue, w.choicebox and "b" or "a")
|
|
wait(3)
|
|
end
|
|
wait(20)
|
|
|
|
local slot = save.party[2]
|
|
ok(slot ~= nil and not Breeding.isEgg(slot), "the egg is no longer an egg")
|
|
ok(slot and slot.species == "TOGEPI", "it is a TOGEPI (got "
|
|
.. tostring(slot and slot.species) .. ")")
|
|
ok(slot and (slot.hp or 0) > 0 and slot.hp == slot.maxHp,
|
|
"at full health, the way HatchEggs copies MON_MAXHP into MON_HP")
|
|
ok(slot and slot.happiness == 0x78,
|
|
"with the hatch happiness of $78 (got "
|
|
.. tostring(slot and slot.happiness) .. ")")
|
|
ok(save.pokedex and save.pokedex.caught
|
|
and save.pokedex.caught.TOGEPI, "and SetSeenAndCaughtMon ticked the #DEX")
|
|
-- HatchEggs sets EVENT_TOGEPI_HATCHED (84) by hand, for this species alone.
|
|
ok(w.events and w.events:get(84) == true,
|
|
"and the world set EVENT_TOGEPI_HATCHED")
|
|
|
|
game.capturePath = SHOT_DIR .. "/hatched.png"
|
|
wait(30)
|
|
|
|
if fails > 0 then
|
|
error(("gold egg hatch: %d assertion(s) failed"):format(fails))
|
|
end
|
|
print("[driver] PASS gold per-step chain: the egg hatched")
|
|
end
|