Files
gen1recomp/tests/drivers/em_link_trade.lua
T
bryanthaboi 690f0cd9a1 TOUCHDOWN
2026-09-27 16:07:31 -04:00

231 lines
8.6 KiB
Lua

local U = require("tests.drivers.util")
local DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/em_link_trade"
local failures = 0
local function result(ok, label)
print((ok and "PASS " or "FAIL ") .. label)
if not ok then failures = failures + 1 end
return ok
end
local function finish()
print((failures == 0 and "PASS" or "FAIL") .. " em_link_trade failures=" .. failures)
love.event.quit(failures == 0 and 0 or 1)
U.wait(10)
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 = "MAY", gender = 1 })
U.wait(60)
local Runtime = require("src.core.game3.runtime")
local Map = require("src.core.game3.map")
local Space = require("src.core.game3.scripting.space")
local Flags = require("src.core.game3.scripting.flags")
local Player = require("src.core.game3.player")
local Party = require("src.core.game3.party")
local Natives = require("src.core.game3.scripting.natives")
local Link = require("src.core.game3.link")
local LB = require("src.core.game3.link.battle")
local LT = require("src.core.game3.link.trade")
local TradeScene = require("src.core.game3.trade_scene")
local Game3Link = require("src.link.Game3Link")
local Protocol = require("src.link.Protocol")
local Wire = require("src.link.Wire")
local Message = require("src.ui.game3.message")
local C = require("src.core.game3.constants").of("emerald")
local session = Runtime.getSession()
if not result(session ~= nil and session.version == "emerald", "new game reached the Emerald field") then
return finish()
end
local CENTER_2F = "EM_OLDALE_TOWN_POKEMON_CENTER_2F"
local TRADE_CENTER = "EM_TRADE_CENTER"
local function ctx() return Space.vm and Space.vm.ctx end
local function adapters() return Space.vm and Space.vm.adapters end
local function getVar(name) return tonumber(Flags.getVar(Space.store, ctx(), C:require("vars", name))) or 0 end
local function setVar(name, v) Flags.setVar(Space.store, ctx(), C:require("vars", name), v) end
local function special(name)
local fn = Natives.handlerFor(name)
if not fn then return nil end
return fn(ctx(), adapters())
end
local function place(x, y, facing)
Player.cellX, Player.cellY = x, y
Player.px, Player.py = x * 16, y * 16
Player.targetX, Player.targetY = x, y
Player.facing = facing
session.x, session.y, session.facing = x, y, facing
end
local peerHolder = { name = "RED", trainerId = 0x2222, party = {} }
Party.giveMon(peerHolder, C:require("species", "SPECIES_PIKACHU"), 14)
local peerMon = peerHolder.party[1]
peerMon.otName, peerMon.ot = "RED", "RED"
local peer, peerSeat, peerParty, peerGot, peerConfirmed = nil, false, false, nil, false
local function pumpPeer()
if not peer then return end
peer:update(0)
local lu = peer:take(LB.MSG.LINKUP)
if lu then peer:send({ type = LB.MSG.LINKUP, linkType = lu.linkType, players = lu.players }) end
if peer:take(LB.MSG.SEAT) then
peerSeat = true
peer:send({ type = LB.MSG.SEAT, seat = 1 })
end
if peer:take(LT.MSG.PARTY) then
peerParty = true
-- pokefirered/src/link.c:350
peer:send({ type = LT.MSG.PARTY, name = "RED", trainerId = 0x2222, gender = 0,
version = 4, progressFlags = 0x11, party = Protocol.packParty3({ peerMon }, { 1 }) })
end
local block = peer:take(LT.MSG.MON)
if block then
peerGot = block
local theirs = { type = LT.MSG.MON, name = "RED", trainerId = 0x2222, mon = Protocol.packMon3(peerMon) }
peer:send(theirs)
peer:send({ type = LT.MSG.CONFIRM, digest = Protocol.tradeDigest(block.mon, Wire.sanitize(theirs).mon) })
end
local cmd = peer:take(LT.MSG.CMD)
while cmd do
if cmd.cmd == LT.LINKCMD.SET_MONS_TO_TRADE then
peer:send({ type = LT.MSG.CMD, cmd = LT.LINKCMD.INIT_BLOCK })
elseif cmd.cmd == LT.LINKCMD.CONFIRM_FINISH_TRADE then
peerConfirmed = true
peer:send({ type = LT.MSG.CMD, cmd = LT.LINKCMD.CONFIRM_FINISH_TRADE })
end
cmd = peer:take(LT.MSG.CMD)
end
end
local function waitP(frames)
for _ = 1, frames do
U.wait(1)
pumpPeer()
end
end
local function waitFor(pred, limit)
local n = 0
while n < (limit or 300) and not pred() do
waitP(1)
n = n + 1
end
return pred()
end
Flags.setFlag(Space.store, ctx(), C:require("flags", "FLAG_SYS_POKEDEX_GET"), true)
Flags.setFlag(Space.store, ctx(), C:require("flags", "FLAG_IS_CHAMPION"), true)
session.party = {}
Party.giveMon(session, C:require("species", "SPECIES_TREECKO"), 16)
Party.giveMon(session, C:require("species", "SPECIES_ZIGZAGOON"), 12)
local sentSpecies = session.party[1].species
local Choice = require("src.ui.game3.choice")
local function busy()
return (Space.vm and Space.vm:isRunning()) or (Message.isOpen and Message.isOpen()) or Choice.active
end
local function unknownSpecials()
local n = 0
for key in pairs(Natives._logged or {}) do
if tostring(key):find("^special:") then n = n + 1 print("[driver] unknown " .. tostring(key)) end
end
return n
end
Natives.resetLog()
Map.load(nil, game, CENTER_2F, { x = 10, y = 3, facing = "up" })
place(10, 3, "up")
U.wait(60)
result(Space.mapId == CENTER_2F, "the player is at the Oldale POKeMON CENTER 2F counter")
U.shot(game, DIR .. "/em_link_trade_01_center_2f.png")
-- pokeemerald/data/scripts/cable_club.inc:897 CableClub_EventScript_DirectCornerAttendant
U.tap(game, "a")
local n = 0
while n < 600 and not (Link.connectPrompt ~= nil and Choice.active) do
if Link.connectPrompt == nil and Message.isOpen and Message.isOpen() and not Choice.active then U.tap(game, "a") end
U.wait(10)
n = n + 10
end
result(Link.connectPrompt ~= nil and Choice.active,
"offline, IsWirelessAdapterConnected (link.c:237) asks to connect")
U.shot(game, DIR .. "/em_link_trade_01b_connect_prompt.png")
U.tap(game, "down")
U.wait(10)
U.tap(game, "a")
U.wait(30)
n = 0
while n < 600 and not Choice.active do
if Message.isOpen and Message.isOpen() then U.tap(game, "a") end
U.wait(10)
n = n + 10
end
result(Choice.active, "NO falls back to the Cable Club service menu (cable_club.inc:WelcomeToCableClub)")
U.shot(game, DIR .. "/em_link_trade_01c_cable_club_menu.png")
n = 0
while n < 900 and busy() do
U.tap(game, "b")
U.wait(15)
n = n + 15
end
result(not busy(), "B backs out of the Cable Club")
result(unknownSpecials() == 0, "the Direct Corner script hit no unbound special")
-- pokeemerald/data/scripts/cable_club.inc:384 CableClub_EventScript_EnterTradeCenter
place(9, 2, "up")
U.wait(20)
place(9, 1, "up")
U.wait(10)
special("SetCableClubWarp")
result(session.dynamicWarp ~= nil and session.dynamicWarp.map == CENTER_2F,
"SetCableClubWarp (field_control_avatar.c:995) recorded the way back")
local host
host, peer = Game3Link.loopback({ game = game, linkType = Game3Link.LINKTYPE.TRADE_SETUP })
host:update(0)
peer:update(0)
Link.attach(host)
LT.loopbackCommit = true
result(host:isReady(), "the other game is on the link")
result(host.myHello.game3.version == "emerald" and host.myHello.game3.gameVersion == 0x4003,
"the hello announces VERSION_EMERALD (link.c:330)")
local yielded = special("TryTradeLinkup")
result(yielded, "TryTradeLinkup (cable_club.c:610) parked the script")
local linkup, guard = nil, 0
while guard < 300 and linkup == nil do
pumpPeer()
local poll = ctx().nativePoll
if poll and poll() then
linkup = getVar("VAR_RESULT")
ctx().nativePoll = nil
ctx().mode = "bytecode"
elseif LB.linkup ~= nil and LB.linkup ~= Link.LINKUP.ONGOING then
linkup = LB.linkup
end
U.wait(1)
guard = guard + 1
end
result(linkup == Link.LINKUP.SUCCESS, "both games chose TRADE, so the linkup succeeded (" .. tostring(linkup) .. ")")
setVar("VAR_0x8004", Link.USING.TRADE_CENTER)
setVar("VAR_CABLE_CLUB_STATE", Link.USING.TRADE_CENTER)
result(Link.VAR_CABLE_CLUB_STATE == C:require("vars", "VAR_CABLE_CLUB_STATE"),
"the link layer reads Emerald's VAR_CABLE_CLUB_STATE (0x4087)")
Map.load(nil, game, TRADE_CENTER, { x = 5, y = 8, facing = "up" })
place(5, 8, "up")
waitP(90)
result(Space.mapId == TRADE_CENTER, "the player is inside the Emerald TRADE CENTER")
U.shot(game, DIR .. "/em_link_trade_02_trade_center.png")
result(unknownSpecials() == 0, "the link path hit no unbound special")
print("NOTE the trade seat opens src/ui/game3/link_trade_menu.lua, whose FireRed text keys are a crossfile request (W4-link)")
LT.loopbackCommit = false
Link.reset()
finish()
end