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

292 lines
12 KiB
Lua

#!/usr/bin/env luajit
package.path = "./?.lua;./?/init.lua;" .. package.path
require("tests.game3_cache").stubSpeciesNames()
local failed = 0
local function check(cond, msg)
if cond then
print("[ok] " .. msg)
else
failed = failed + 1
print("[FAIL] " .. msg)
end
end
local function eq(a, b, msg)
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
end
local Storage = require("src.core.game3.storage")
local Party = require("src.core.game3.party")
local Flags = require("src.core.game3.scripting.flags")
local Queries = require("src.core.game3.scripting.natives_queries")
local Std = require("src.core.game3.scripting.stdscripts")
local VAR_PC_BOX_TO_SEND_MON = 0x4037
local FLAG_SHOWN_BOX_WAS_FULL_MESSAGE = 0x843
local function new_session()
local session = {
party = {},
store = Flags.newStore(),
dex = { seen = {}, owned = {} },
}
Storage.ensure(session)
return session
end
local function fill_box(session, boxId, count)
local box = session.storage.boxes[boxId]
for s = 1, count do
box.mons[s] = { species = 19, speciesId = 19, level = 3, hp = 12, maxHp = 12 }
end
end
local function mon()
return { species = 16, speciesId = 16, level = 5, hp = 20, maxHp = 20 }
end
print("[test] 1. a send into the current box writes the 0-based box id")
local s1 = new_session()
s1.storage.currentBox = 1
local ok, bId, slot = Storage.sendMonToPC(s1, mon())
check(ok, "sendMonToPC succeeded")
eq(bId, 1, "landed in box 1 (1-based)")
eq(slot, 1, "in slot 1")
eq(Flags.getVar(s1.store, nil, VAR_PC_BOX_TO_SEND_MON), 0,
"VAR_PC_BOX_TO_SEND_MON holds the cart's 0-based id")
print("[test] 2. box 3 writes 2, not 3")
local s2 = new_session()
s2.storage.currentBox = 3
local ok2, b2 = Storage.sendMonToPC(s2, mon())
check(ok2, "sendMonToPC succeeded")
eq(b2, 3, "landed in box 3 (1-based)")
eq(Flags.getVar(s2.store, nil, VAR_PC_BOX_TO_SEND_MON), 2, "the var holds 2")
print("[test] 3. the intended box is read back before the search")
local s3 = new_session()
s3.storage.currentBox = 1
Flags.setVar(s3.store, nil, VAR_PC_BOX_TO_SEND_MON, 7)
local ok3, b3, _, intended = Storage.sendMonToPC(s3, mon())
check(ok3, "sendMonToPC succeeded")
eq(intended, 7, "GetPCBoxToSendMon is the pre-search value")
eq(Queries.pcBoxToSendMon, 7, "and it is left on the Queries module for the reader")
eq(b3, 1, "the mon still went to the current box")
eq(Flags.getVar(s3.store, nil, VAR_PC_BOX_TO_SEND_MON), 0, "the var was updated to 0")
print("[test] 4. the flag is cleared when the box changed")
local s4 = new_session()
s4.storage.currentBox = 1
Flags.setVar(s4.store, nil, VAR_PC_BOX_TO_SEND_MON, 5)
Flags.setFlag(s4.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE, true)
Storage.sendMonToPC(s4, mon())
eq(Flags.getFlag(s4.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE), false,
"FLAG_SHOWN_BOX_WAS_FULL_MESSAGE was cleared on the box change")
print("[test] 5. the flag survives when the box did not change")
local s5 = new_session()
s5.storage.currentBox = 1
Flags.setVar(s5.store, nil, VAR_PC_BOX_TO_SEND_MON, 0)
Flags.setFlag(s5.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE, true)
Storage.sendMonToPC(s5, mon())
eq(Flags.getFlag(s5.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE), true,
"the flag is untouched when the box is the same")
print("[test] 6. spillover to the next box updates the var to that box")
local s6 = new_session()
s6.storage.currentBox = 2
fill_box(s6, 2, Storage.IN_BOX_COUNT)
Flags.setVar(s6.store, nil, VAR_PC_BOX_TO_SEND_MON, 1)
Flags.setFlag(s6.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE, true)
local ok6, b6, sl6, intended6 = Storage.sendMonToPC(s6, mon())
check(ok6, "sendMonToPC spilled over")
eq(b6, 3, "the mon went to box 3")
eq(sl6, 1, "slot 1 of box 3")
eq(intended6, 1, "the intended box was still box 2 (0-based 1)")
eq(Flags.getVar(s6.store, nil, VAR_PC_BOX_TO_SEND_MON), 2, "the var now holds 2")
eq(Flags.getFlag(s6.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE), false,
"the flag was cleared because the box changed")
print("[test] 7. gSpecialVar_MonBoxId / MonBoxPos are 0-based")
eq(s6.monBoxId, 2, "session.monBoxId")
eq(s6.monBoxPos, 0, "session.monBoxPos")
print("[test] 8. the ShouldShowBoxWasFullMessage reader agrees with the writer")
local Runtime = require("src.core.game3.runtime")
local prevSession = Runtime.getSession and Runtime.getSession() or nil
local s8 = new_session()
s8.storage.currentBox = 2
fill_box(s8, 2, Storage.IN_BOX_COUNT)
Flags.setVar(s8.store, nil, VAR_PC_BOX_TO_SEND_MON, 1)
Runtime.session = s8
local ctx8 = { specialVars = {} }
Storage.sendMonToPC(s8, mon())
local handler = Queries.HANDLERS and Queries.HANDLERS[Std.SPECIAL.ShouldShowBoxWasFullMessage]
if handler then
local _, v = handler(ctx8)
eq(v, 1, "the box was full, so the message should show")
local _, v2 = handler(ctx8)
eq(v2, 0, "and only once")
else
check(false, "Queries exposes the ShouldShowBoxWasFullMessage handler")
end
local getBox = Queries.HANDLERS and Queries.HANDLERS[Std.SPECIAL.GetPCBoxToSendMon]
local _, boxVal = getBox(ctx8)
eq(boxVal, 1, "GetPCBoxToSendMon still reports the box it was going to")
Runtime.session = prevSession
print("[test] 9. depositCaught routes through sendMonToPC")
local s9 = new_session()
s9.storage.currentBox = 4
local okD, bD, slD = Storage.depositCaught(s9, mon())
check(okD, "depositCaught succeeded")
eq(bD, 4, "box 4")
eq(slD, 1, "slot 1")
eq(Flags.getVar(s9.store, nil, VAR_PC_BOX_TO_SEND_MON), 3, "the var holds 3")
print("[test] 10. a completely full PC gives back the intended box and no write")
local s10 = new_session()
s10.storage.currentBox = 1
for b = 1, Storage.TOTAL_BOXES_COUNT do fill_box(s10, b, Storage.IN_BOX_COUNT) end
Flags.setVar(s10.store, nil, VAR_PC_BOX_TO_SEND_MON, 9)
local okF, bF, slF, intF = Storage.sendMonToPC(s10, mon())
check(okF == false, "sendMonToPC refused")
check(bF == nil and slF == nil, "no box or slot came back")
eq(intF, 9, "the intended box is reported")
eq(Flags.getVar(s10.store, nil, VAR_PC_BOX_TO_SEND_MON), 9, "the var was not moved")
local okDC, reason = Storage.depositCaught(s10, mon())
check(okDC == false and reason == "storage_full", "depositCaught reports storage_full")
print("[test] 11. Party.giveMonToPlayer overflows into the PC and returns MON_GIVEN_TO_PC")
local s11 = new_session()
s11.storage.currentBox = 1
for i = 1, 6 do
local codeG = Party.giveMonToPlayer(s11, 1, 5)
eq(codeG, Party.MON_GIVEN_TO_PARTY, "slot " .. i .. " is MON_GIVEN_TO_PARTY")
end
eq(#s11.party, 6, "the party is full")
local codeP, monP, boxP, slotP = Party.giveMonToPlayer(s11, 25, 10)
eq(codeP, Party.MON_GIVEN_TO_PC, "it is MON_GIVEN_TO_PC")
eq(#s11.party, 6, "the party did not grow")
eq(boxP, 1, "it landed in box 1")
eq(slotP, 1, "slot 1")
check(s11.storage.boxes[1].mons[1] == monP, "the returned mon is the boxed mon")
eq(tonumber(monP.species), 25, "and it is the right species")
eq(s11.dex.owned[25], true, "the dex was still marked owned")
eq(Flags.getVar(s11.store, nil, VAR_PC_BOX_TO_SEND_MON), 0, "the var was written")
print("[test] 12. MON_CANT_GIVE when party and PC are both full")
local s12 = new_session()
for i = 1, 6 do Party.giveMonToPlayer(s12, 1, 5) end
for b = 1, Storage.TOTAL_BOXES_COUNT do fill_box(s12, b, Storage.IN_BOX_COUNT) end
local codeC, monC = Party.giveMonToPlayer(s12, 25, 10)
eq(codeC, Party.MON_CANT_GIVE, "the code is MON_CANT_GIVE")
check(monC == nil, "no mon came back")
check(s12.dex.owned[25] == nil, "the dex was not marked for a mon that was never given")
print("[test] 13. giveEggToPlayer marks the egg wherever it landed")
local s13 = new_session()
for i = 1, 6 do Party.giveMonToPlayer(s13, 1, 5) end
local codeE, egg = Party.giveEggToPlayer(s13, 4)
eq(codeE, Party.MON_GIVEN_TO_PC, "the egg went to the PC")
check(egg ~= nil and egg.isEgg == true, "the boxed egg is flagged isEgg")
check(s13.storage.boxes[s13.storage.currentBox].mons[1] == egg,
"and it is the mon sitting in the box, not a party mon")
check(s13.party[6].isEgg ~= true, "the last party mon was not mislabelled as the egg")
print("[test] 14. a caller that cannot report MON_GIVEN_TO_PC keeps the mon out of the PC")
local s14 = new_session()
for i = 1, 6 do Party.giveMon(s14, 1, 5) end
eq(#s14.party, 6, "the party is full")
local ok14, code14, mon14 = Party.giveMon(s14, 25, 10)
check(ok14 == false, "the plain giveMon refused")
eq(code14, Party.MON_CANT_GIVE, "the code is MON_CANT_GIVE")
check(mon14 == nil, "no mon came back")
eq(Storage.countTotalMons(s14.storage), 0, "and nothing reached the PC")
check(s14.dex.owned[25] == nil, "the dex was not marked")
local okO, codeO, monO = Party.giveMon(s14, 25, 10, nil, { toPC = true })
check(okO, "the same call with toPC gives the mon")
eq(codeO, Party.MON_GIVEN_TO_PC, "and reports MON_GIVEN_TO_PC")
check(s14.storage.boxes[1].mons[1] == monO, "the mon is in box 1")
if not require("tests.game3_cache").bundle() then
print("[skip] 15-17: no FireRed cache for sTransferredToPCMessages")
if failed > 0 then os.exit(1) end
os.exit(0)
end
print("[test] 15. the PC transfer message picks pret's four strings")
local Runtime15 = require("src.core.game3.runtime")
local prev15 = Runtime15.getSession and Runtime15.getSession() or nil
local s15 = new_session()
s15.storage.currentBox = 1
Runtime15.session = s15
Party.giveMonToPlayer(s15, 25, 10)
local plain = Storage.pcTransferMessage(s15, "PIKACHU")
check(plain:find("PIKACHU was transferred to\nSomeone's PC.", 1, true) ~= nil,
"someone's PC: " .. plain:gsub("\n", " "):gsub("\f", " | "))
check(plain:find("BOX “BOX 1.”", 1, true) ~= nil, "and it names the box it was placed in")
Flags.setFlag(s15.store, nil, 0x834, true)
local bills = Storage.pcTransferMessage(s15, "PIKACHU")
check(bills:find("BILL'S PC.", 1, true) ~= nil,
"FLAG_SYS_NOT_SOMEONES_PC swaps in BILL'S PC")
Flags.setFlag(s15.store, nil, 0x834, false)
print("[test] 16. a spillover names the full box and the box it went to, once")
local s16 = new_session()
s16.storage.currentBox = 1
for _ = 1, 6 do Party.giveMon(s16, 1, 5) end
fill_box(s16, 1, Storage.IN_BOX_COUNT)
Runtime15.session = s16
Flags.setVar(s16.store, nil, VAR_PC_BOX_TO_SEND_MON, 0)
Flags.setFlag(s16.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE, true)
local code16 = Party.giveMonToPlayer(s16, 25, 10)
eq(code16, Party.MON_GIVEN_TO_PC, "the mon spilled over")
local full16 = Storage.pcTransferMessage(s16, "PIKACHU")
check(full16:find("BOX “BOX 1” on\nSomeone's PC was full.", 1, true) ~= nil,
"box was full: " .. full16:gsub("\n", " "):gsub("\f", " | "))
check(full16:find("PIKACHU was transferred to\nBOX “BOX 2.”", 1, true) ~= nil,
"and it went to BOX 2")
local again16 = Storage.pcTransferMessage(s16, "PIKACHU")
check(again16:find("was full", 1, true) == nil,
"the box-was-full line is only shown once: " .. again16:gsub("\n", " "):gsub("\f", " | "))
check(again16:find("PIKACHU was transferred to\nSomeone's PC.", 1, true) ~= nil,
"the plain line follows")
print("[test] 17. IsDestinationBoxFull answers the naming screen")
local s17 = new_session()
s17.storage.currentBox = 1
for _ = 1, 6 do Party.giveMon(s17, 1, 5) end
fill_box(s17, 1, Storage.IN_BOX_COUNT)
Runtime15.session = s17
Flags.setVar(s17.store, nil, VAR_PC_BOX_TO_SEND_MON, 0)
Flags.setFlag(s17.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE, true)
Party.giveMonToPlayer(s17, 25, 10)
local battleLine = Storage.pcTransferMessage(s17, "PIKACHU")
check(battleLine:find("BOX “BOX 1” on", 1, true) ~= nil, "the battle line named the full box")
local full17 = Storage.isDestinationBoxFull(s17)
check(full17 == false, "the naming screen does not repeat it, the battle already showed it")
eq(Flags.getVar(s17.store, nil, VAR_PC_BOX_TO_SEND_MON), 1, "and the var still names BOX 2")
local named = Storage.pcTransferMessage(s17, "PIKACHU", full17)
check(named:find("PIKACHU was transferred to\nSomeone's PC.", 1, true) ~= nil,
"so the naming screen prints the plain line: " .. named:gsub("\n", " "):gsub("\f", " | "))
Flags.setFlag(s17.store, nil, FLAG_SHOWN_BOX_WAS_FULL_MESSAGE, false)
local forced = Storage.isDestinationBoxFull(s17)
check(forced == true, "with the flag clear it answers yes")
local quirk = Storage.pcTransferMessage(s17, "PIKACHU", forced)
check(quirk:find("BOX “BOX 2” on\nSomeone's PC was full.", 1, true) ~= nil,
"and names the box it went to twice, as the cart does: "
.. quirk:gsub("\n", " "):gsub("\f", " | "))
local s18 = new_session()
s18.storage.currentBox = 1
Runtime15.session = s18
check(Storage.isDestinationBoxFull(s18) == false, "an empty current box is never full")
Runtime15.session = prev15
if failed > 0 then
print("[test] FAILED " .. failed)
os.exit(1)
end
print("[test] all passed")