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

412 lines
14 KiB
Lua

local U = require("tests.drivers.util")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/game3_daycare_menu"
-- 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_menu.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_menu")
love.event.quit(0)
else
say("FAIL daycare_menu 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 DaycareMenu = require("src.ui.game3.daycare_menu")
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)
session.money = 3000
-- 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 SeAudio = require("src.core.game3.audio")
local function pumpUntil(pred, frames)
local budget = frames
while budget > 0 do
if pred() then return true end
if SeAudio.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 answer(which)
for _ = 1, 10 do
if Choice.cursor == which then break end
U.tap(game, which == 1 and "up" or "down")
U.wait(6)
end
U.tap(game, "a")
U.wait(20)
end
local function waitIdle()
for _ = 1, 600 do
if Choice.active then return false end
if not scriptRunning() 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 talkToWoman()
goTo(DAYCARE, WOMAN_X, WOMAN_Y + 1, "up")
U.tap(game, "a")
U.wait(20)
end
local function boardOne(label, slot)
result(pumpUntil(function() return Choice.active end, 90),
"the day-care woman asked: " .. label)
answer(1)
if not result(pumpUntil(function() return PartyMenu.isOpen and PartyMenu.isOpen() end, 180),
"ChooseSendDaycareMon opened the party picker for " .. label) then
return false
end
for _ = 2, (slot or 1) do
U.tap(game, "down")
U.wait(8)
end
U.tap(game, "a")
U.wait(30)
return true
end
talkToWoman()
say("[driver] boarding both MAGIKARP")
boardOne("the first MAGIKARP")
result(pumpUntil(function() return Choice.active end, 240),
"and offered to raise one more")
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
say("[driver] walking until both boarders gain a level")
goTo(DAYCARE, WOMAN_X + 1, WOMAN_Y + 2, "down")
Field.unlock()
U.wait(20)
local walked = walkUntil(400, function()
return Daycare.levelsGained(Daycare.mon(dc, 1), dc.steps[1]) >= 1
and Daycare.levelsGained(Daycare.mon(dc, 2), dc.steps[2]) >= 1
end)
say("[driver] walked " .. tostring(walked) .. " grid steps")
result(Daycare.levelsGained(Daycare.mon(dc, 2), dc.steps[2]) >= 1,
"slot 2 gained a level, now Lv" ..
tostring(Daycare.levelAfterSteps(Daycare.mon(dc, 2), dc.steps[2])))
say("[driver] asking for a mon back, which opens the level menu")
talkToWoman()
result(pumpUntil(function()
return Choice.active and page():find("take your POKéMON back") ~= nil
end, 300), "she asks: " .. page())
answer(1)
if not result(pumpUntil(function()
return DaycareMenu.isOpen and DaycareMenu.isOpen()
end, 240), "ShowDaycareLevelMenu put the level menu on screen") then
return finish()
end
U.wait(30)
U.shot(game, DIR .. "/daycare_menu_01_level_menu.png")
local rows = DaycareMenu.rowList
result(rows and #rows == 3, "the menu lists both mons plus EXIT")
result(rows and rows[1].level == "Lv" ..
tostring(Daycare.levelAfterSteps(Daycare.mon(dc, 1), dc.steps[1])),
"row 1 shows the level the steps earned: " .. tostring(rows and rows[1].level))
result(rows and rows[1].symbol == "♀" and rows[2].symbol == "♂",
"the rows carry the gender symbols: " .. tostring(rows and rows[1].symbol) ..
tostring(rows and rows[2].symbol))
result(rows and rows[3].text == "EXIT", "and the third row is EXIT")
U.tap(game, "down")
U.wait(10)
result(DaycareMenu.cursor == 2, "the cursor moved to the second mon")
U.tap(game, "a")
U.wait(20)
result(pumpUntil(function()
return Choice.active and page():find("it will cost") ~= nil
end, 240), "the pick leads to the cost prompt: " .. page())
U.shot(game, DIR .. "/daycare_menu_02_cost.png")
local moneyBefore = tonumber(session.money) or 0
answer(1)
result(pumpUntil(function()
return page():find("Here's your POKéMON") ~= nil
end, 600), "she hands it over: " .. page())
waitIdle()
-- pokefirered/data/maps/FourIsland_PokemonDayCare/scripts.inc:132
result(pumpUntil(function()
return Choice.active and page():find("take back the other one") ~= nil
end, 300), "she asks about the other one: " .. page())
answer(2)
waitIdle()
local back, backSlot
for i, mon in ipairs(session.party) do
if mon == father then back, backSlot = mon, i end
end
result(back ~= nil, "the second MAGIKARP is back in the party")
result(back and back.level == 6, "it came back at level 6, got " ..
tostring(back and back.level))
result((moneyBefore - (tonumber(session.money) or 0)) == 200,
"200 was paid for the one level, paid " ..
tostring(moneyBefore - (tonumber(session.money) or 0)))
result(Daycare.count(dc) == 1, "one mon is left in the day care, got " ..
tostring(Daycare.count(dc)))
say("[driver] boarding it again so the pair can produce an egg")
talkToWoman()
result(pumpUntil(function()
return Choice.active and page():find("more POKéMON for you") ~= nil
end, 300), "she offers to raise one more: " .. page())
boardOne("the returned MAGIKARP", backSlot)
waitIdle()
result(Daycare.mon(dc, 2) == back, "the MAGIKARP went back in, not the PIDGEY")
if not result(Daycare.count(dc) == 2, "both are boarded again, got " ..
tostring(Daycare.count(dc))) then
return finish()
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)
walked = walkUntil(256 * 4, function()
return Daycare.isEggPending(dc)
end)
say("[driver] walked " .. tostring(walked) .. " grid steps")
if not result(Daycare.isEggPending(dc), "an egg is waiting at the day care") then
return finish()
end
-- pokefirered/data/maps/FourIsland/scripts.inc:97 goto_if_ne VAR_RESULT, PARTY_SIZE
while #session.party < 6 do
Party.giveMon(session, PIDGEY, 9)
end
result(#session.party == 6, "the party is full, got " .. tostring(#session.party))
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
say("[driver] asking for the egg with no room for it")
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())
answer(1)
result(pumpUntil(function()
return page():find("no room for it") ~= nil and Message.isWaiting and Message.isWaiting()
end, 240), "he refuses to hand it over: " .. page())
U.shot(game, DIR .. "/daycare_menu_03_no_room.png")
waitIdle()
local carried = 0
for _, mon in ipairs(session.party) do
if mon.isEgg then carried = carried + 1 end
end
result(carried == 0, "no egg entered the full party, got " .. tostring(carried))
result(Daycare.isEggPending(dc), "and the egg is still waiting for the player")
say("[driver] making room and collecting the egg")
-- pokefirered/include/constants/songs.h:264 MUS_LEVEL_UP
local MUS_LEVEL_UP = 257
local Audio = require("src.core.game3.audio")
local heard = {}
local realFanfare = Audio.playFanfare
Audio.playFanfare = function(id, ...)
heard[#heard + 1] = tonumber(id) or id
return realFanfare(id, ...)
end
table.remove(session.party)
result(#session.party == 5, "one mon left behind, party is " .. tostring(#session.party))
if not result(talkToDaycareMan(), "back to the day-care man") then return finish() end
result(pumpUntil(function() return Choice.active end, 240),
"he offers the egg again: " .. page())
answer(1)
result(pumpUntil(function()
return page():find("received the EGG") ~= nil and Message.isWaiting and Message.isWaiting()
end, 300), "the hand-off scene plays: " .. page())
U.tap(game, "a")
result(pumpUntil(function()
return page():find("Take good care of it") ~= nil
and Message.isWaiting and Message.isWaiting()
end, 300), "and he asks: " .. page())
U.wait(30)
U.shot(game, DIR .. "/daycare_menu_04_take_care.png")
local rangFanfare = false
for _, id in ipairs(heard) do
if id == MUS_LEVEL_UP then rangFanfare = true end
end
-- pokefirered/data/maps/FourIsland/scripts.inc:106 playfanfare MUS_LEVEL_UP
result(rangFanfare, "the hand-off played MUS_LEVEL_UP, heard " ..
tostring(#heard) .. " fanfares")
Audio.playFanfare = realFanfare
waitIdle()
local egg
for _, mon in ipairs(session.party) do
if mon.isEgg then egg = mon end
end
result(egg ~= nil, "the EGG is in the party now")
result(egg and egg.species == MAGIKARP,
"two MAGIKARP made a MAGIKARP egg, got " .. tostring(egg and egg.species))
result(not Daycare.isEggPending(dc), "the day care has nothing pending any more")
finish()
end