Files
gen1recomp/tests/drivers/game3_daycare_egg.lua
bryanthaboi dee935a4ea CLOSES #2430
2026-09-23 11:58:32 -04:00

359 lines
12 KiB
Lua

local U = require("tests.drivers.util")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/game3_daycare_egg"
-- pokefirered/data/maps/FourIsland_PokemonDayCare/scripts.inc:4
local DAYCARE = "FR_FOUR_ISLAND_POKEMON_DAY_CARE"
local WOMAN_X, WOMAN_Y = 2, 2
local ISLAND = "FR_FOUR_ISLAND"
local DAYCARE_MAN_ID = 1
local MAGIKARP = 129
local PIDGEY = 16
local VAR_MAP_SCENE_FOUR_ISLAND = 0x4086
local failures = 0
local function say(line)
print(line)
os.execute('mkdir -p "' .. DIR .. '" 2>/dev/null')
local fh = io.open(DIR .. "/daycare_egg.log", "a")
if fh then
fh:write(line, "\n")
fh:close()
end
end
local function result(ok, label)
say((ok and "PASS " or "FAIL ") .. label)
if not ok then failures = failures + 1 end
return ok
end
local function finish()
if failures == 0 then
say("PASS daycare_egg")
love.event.quit(0)
else
say("FAIL daycare_egg failures=" .. failures)
love.event.quit(1)
end
end
return function(game)
for _ = 1, 900 do
if game.phase == "boot" and game.boot then break end
U.wait(1)
end
game:_handleBootAction({ action = "new_game", name = "RED" })
U.wait(240)
local Runtime = require("src.core.game3.runtime")
local Map = require("src.core.game3.map")
local Space = require("src.core.game3.scripting.space")
local Player = require("src.core.game3.player")
local Collision = require("src.core.game3.collision")
local Objects = require("src.core.game3.objects")
local Party = require("src.core.game3.party")
local PartyMenu = require("src.ui.game3.party_menu")
local Message = require("src.ui.game3.message")
local Choice = require("src.ui.game3.choice")
local Field = require("src.core.game3.field")
local Flags = require("src.core.game3.scripting.flags")
local Pokemon = require("src.core.game3.pokemon")
local Daycare = require("src.core.game3.daycare")
local Breeding = require("src.core.game3.breeding")
local StepEvents = require("src.core.game3.step_events")
local session = Runtime.getSession()
if not result(session ~= nil, "new game reached the game3 field") then return finish() end
-- pokefirered/data/maps/FourIsland/scripts.inc:23 the rival has already left
Flags.setVar(session.store or Space.store, nil, VAR_MAP_SCENE_FOUR_ISLAND, 1)
session.party = {}
Party.giveMon(session, MAGIKARP, 5)
Party.giveMon(session, MAGIKARP, 5)
Party.giveMon(session, PIDGEY, 9)
-- pokefirered/src/pokemon.c:2733 GetGenderFromSpeciesAndPersonality
local mother, father = session.party[1], session.party[2]
mother.personality = mother.personality - (mother.personality % 256)
father.personality = father.personality - (father.personality % 256) + 254
mother.gender = Pokemon.gender(MAGIKARP, mother.personality)
father.gender = Pokemon.gender(MAGIKARP, father.personality)
-- pokefirered/src/daycare.c:1321 different trainers score higher
father.otId = (tonumber(mother.otId) or 0) + 1111
result(mother.gender == "F" and father.gender == "M",
"a MAGIKARP pair, one of each gender: " .. tostring(mother.gender) ..
"/" .. tostring(father.gender))
local function goTo(mapId, x, y, facing)
Map.load(nil, game, mapId, { x = x, y = y, facing = facing or "up" })
if game.session then
game.session.x, game.session.y, game.session.facing = x, y, facing or "up"
end
Player.cellX, Player.cellY = x, y
Player.px, Player.py = x * 16, y * 16
Player.targetX, Player.targetY = x, y
Player.facing = facing or "up"
U.wait(30)
local Preview = package.loaded["src.ui.game3.map_preview_screen"]
for _ = 1, 240 do
if not (Preview and Preview.isActive and Preview.isActive()) then break end
U.wait(5)
end
U.wait(60)
end
local function scriptRunning()
return Space.vm and Space.vm.isRunning and Space.vm:isRunning() or false
end
local function page()
return tostring((Message.currentPage and Message.currentPage()) or "")
end
local Audio = require("src.core.game3.audio")
local function pumpUntil(pred, frames)
local budget = frames
while budget > 0 do
if pred() then return true end
if Audio.isSePlaying() then
budget = budget + 1
elseif Choice.active then
return false
elseif Message.isWaiting and Message.isWaiting() then
U.tap(game, "a")
elseif not scriptRunning() then
U.tap(game, "a")
end
U.wait(6)
budget = budget - 1
end
return pred()
end
local function answerYes()
for _ = 1, 10 do
if Choice.cursor == 1 then break end
U.tap(game, "up")
U.wait(6)
end
U.tap(game, "a")
U.wait(20)
end
local function waitIdle()
for _ = 1, 600 do
if not scriptRunning() and not Choice.active
and not (Message.isOpen and Message.isOpen()) then
return true
end
if Message.isWaiting and Message.isWaiting() then U.tap(game, "a") end
U.wait(5)
end
return false
end
local function tryStep(dir)
local x, y = Player.cellX, Player.cellY
U.hold(game, dir, 8)
for _ = 1, 60 do
if not Player.moving then break end
U.wait(1)
end
U.wait(2)
return Player.cellX ~= x or Player.cellY ~= y
end
local DIRS = { "left", "right", "up", "down" }
local function walkUntil(limit, done)
local idx, walked, stuck = 1, 0, 0
while walked < limit and stuck < 12 do
if Collision.warpAt and Collision.warpAt(Player.cellX, Player.cellY) then
break
end
if tryStep(DIRS[idx]) then
walked = walked + 1
stuck = 0
idx = (idx % 2 == 1) and idx + 1 or idx - 1
else
stuck = stuck + 1
idx = (idx <= 2) and 3 or 1
end
if done and done(walked) then return walked end
if StepEvents.busy() then
for _ = 1, 600 do
if not StepEvents.busy() then break end
if Message.isWaiting and Message.isWaiting() then U.tap(game, "a") end
U.wait(5)
end
end
end
return walked
end
local function boardOne(label)
result(pumpUntil(function() return Choice.active end, 90),
"the day-care woman asked: " .. label)
answerYes()
if not result(pumpUntil(function() return PartyMenu.isOpen and PartyMenu.isOpen() end, 180),
"ChooseSendDaycareMon opened the party picker for " .. label) then
return false
end
U.tap(game, "a")
U.wait(30)
return true
end
goTo(DAYCARE, WOMAN_X, WOMAN_Y + 1, "up")
say("[driver] boarding the female MAGIKARP")
boardOne("the first MAGIKARP")
result(pumpUntil(function() return Choice.active end, 240),
"and offered to raise one more")
U.shot(game, DIR .. "/daycare_egg_01_one_more.png")
say("[driver] boarding the male MAGIKARP")
boardOne("the second MAGIKARP")
waitIdle()
local dc = Daycare.stateOf(session)
if not result(Daycare.count(dc) == 2, "both MAGIKARP are boarded, got " ..
tostring(Daycare.count(dc))) then
return finish()
end
result(Breeding.compatibility(dc) == 70,
"GetDaycareCompatibilityScore is PARENTS_MAX_COMPATIBILITY, got " ..
tostring(Breeding.compatibility(dc)))
-- pokefirered/data/maps/FourIsland/scripts.inc:121 FourIsland_EventScript_CheckOnTwoMons
local function talkToDaycareMan()
goTo(ISLAND, 16, 17, "up")
-- pokefirered/data/maps/FourIsland/scripts.inc:18 setobjectxyperm on transition
for _ = 1, 3 do
local man = Objects.find(DAYCARE_MAN_ID)
if not man then return false end
if man.cellX == Player.cellX and man.cellY == Player.cellY - 1 then return true end
goTo(ISLAND, man.cellX, man.cellY + 1, "up")
end
return false
end
if result(talkToDaycareMan(), "the day-care man is on Four Island") then
result(pumpUntil(function()
return page():find("get along") ~= nil and Message.isWaiting and Message.isWaiting()
end, 180), "he quotes the compatibility line: " .. page())
U.shot(game, DIR .. "/daycare_egg_02_compatibility.png")
waitIdle()
end
say("[driver] walking until the day care produces an egg")
goTo(DAYCARE, WOMAN_X + 1, WOMAN_Y + 2, "down")
Field.unlock()
U.wait(20)
local walked = walkUntil(256 * 5, function()
return Daycare.isEggPending(dc)
end)
say("[driver] walked " .. tostring(walked) .. " grid steps")
if not result(Daycare.isEggPending(dc),
"TriggerPendingDaycareEgg fired, offspringPersonality=" ..
tostring(dc.offspringPersonality)) then
return finish()
end
result((walked % 256) == 255,
"the egg came on a step where (mons[1].steps & 0xFF) == 0xFF, at " .. tostring(walked))
result(Flags.getFlag(session.store or Space.store, nil,
Breeding.FLAG_PENDING_DAYCARE_EGG) == true,
"FLAG_PENDING_DAYCARE_EGG is set")
say("[driver] collecting the egg from the day-care man")
if not result(talkToDaycareMan(), "the day-care man moved out to meet the player") then
return finish()
end
result(pumpUntil(function() return Choice.active end, 240),
"he offers the egg: " .. page())
U.shot(game, DIR .. "/daycare_egg_03_offer.png")
answerYes()
result(pumpUntil(function()
return page():find("received the EGG") ~= nil
and Message.isWaiting and Message.isWaiting()
end, 240), "the egg hand-off scene plays: " .. page())
U.shot(game, DIR .. "/daycare_egg_04_received.png")
waitIdle()
local egg
for _, mon in ipairs(session.party) do
if mon.isEgg then egg = mon end
end
if not result(egg ~= nil, "the EGG is in the party") then return finish() end
result(egg.species == MAGIKARP,
"two MAGIKARP made a MAGIKARP egg, got " .. tostring(egg.species))
local cycles = tonumber(Pokemon.speciesMeta(MAGIKARP).eggCycles)
result(egg.friendship == cycles,
"its hatch counter is the ROM's " .. tostring(cycles) .. " egg cycles, got " ..
tostring(egg.friendship))
result(Daycare.isEggPending(dc) == false, "the day care has no egg waiting any more")
say("[driver] walking the egg to its hatch (" .. tostring(cycles) .. " cycles)")
goTo(DAYCARE, WOMAN_X + 1, WOMAN_Y + 2, "down")
Field.unlock()
U.wait(20)
local EggHatch = require("src.ui.game3.egg_hatch")
walked = walkUntil(256 * (cycles + 2), function()
return StepEvents.busy()
end)
say("[driver] walked " .. tostring(walked) .. " more grid steps")
-- pokefirered/data/scripts/day_care.inc:112 DayCare_Text_Huh
result(pumpUntil(function() return page():find("Huh") ~= nil end, 120),
"the field script prints Huh?: " .. page())
-- pokefirered/data/scripts/day_care.inc:113 special EggHatch
result(pumpUntil(function() return EggHatch.isOpen() end, 120),
"dismissing Huh? opens the hatch scene")
local function waitScene(name, frames)
for _ = 1, frames do
if EggHatch.isOpen() and EggHatch._state == name then return true end
U.wait(2)
end
return EggHatch.isOpen() and EggHatch._state == name
end
-- pokefirered/src/daycare.c:1927 gText_HatchedFromEgg
result(waitScene("hatched_msg", 300), "the egg-hatch scene reached the hatch line")
U.wait(20)
U.shot(game, DIR .. "/daycare_egg_05_hatch.png")
result(page():find("hatched from the EGG") ~= nil,
"the hatch line is on screen, not an evolution: " .. page())
-- pokefirered/src/daycare.c:1944 gText_NickHatchPrompt
result(waitScene("nickname_ask", 300), "and the nickname prompt follows")
U.shot(game, DIR .. "/daycare_egg_06_nickname.png")
-- pokefirered/src/daycare.c:1966 case 1 declines the naming screen
for _ = 1, 10 do
if Choice.cursor == 2 then break end
U.tap(game, "down")
U.wait(6)
end
U.tap(game, "a")
for _ = 1, 600 do
if not EggHatch.isOpen() then break end
U.wait(5)
end
result(not EggHatch.isOpen(), "the scene handed control back to the field")
result(egg.isEgg == false, "the EGG hatched")
result(egg.level == 5, "it hatched at level 5, got " .. tostring(egg.level))
result(egg.friendship == 120, "with friendship 120, got " .. tostring(egg.friendship))
result((egg.nickname == "" or egg.nickname == nil)
and egg.name == Pokemon.name(MAGIKARP),
"and it is called " .. tostring(egg.name) .. ", not EGG")
-- pokefirered/src/daycare.c:1671 MonRestorePP
result(egg.pp == nil or egg.maxPp == nil or egg.pp[1] == egg.maxPp[1],
"its PP came back full, got " .. tostring(egg.pp and egg.pp[1]))
for _ = 1, 600 do
if not StepEvents.busy() then break end
if Message.isWaiting and Message.isWaiting() then U.tap(game, "a") end
U.wait(5)
end
U.wait(60)
U.shot(game, DIR .. "/daycare_egg_07_hatched.png")
finish()
end