mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 11:11:10 +02:00
G2 support
This commit is contained in:
@@ -0,0 +1,131 @@
|
||||
-- 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
|
||||
Reference in New Issue
Block a user