mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
399 lines
15 KiB
Lua
399 lines
15 KiB
Lua
local U = require("tests.drivers.util")
|
|
local DIR = os.getenv("POKEPORT_SHOT_DIR") or ".bazinga/BSA/09-23-26-03-gen3online/shots/minigames"
|
|
|
|
local CORNER = "FR_TWO_ISLAND_JOYFUL_GAME_CORNER"
|
|
-- pokefirered/include/constants/flags.h:790
|
|
local FLAG_GOT_MOON_STONE_FROM_JOYFUL_GAME_CORNER = 0x2FB
|
|
-- pokefirered/include/constants/flags.h:1375
|
|
local FLAG_SYS_POKEDEX_GET = 0x829
|
|
-- pokefirered/include/constants/vars.h:173
|
|
local VAR_MAP_SCENE_TWO_ISLAND_JOYFUL_GAME_CORNER = 0x4079
|
|
-- pokefirered/include/constants/vars.h:163
|
|
local VAR_CABLE_CLUB_STATE = 0x406F
|
|
local CHARMANDER, DODRIO = 4, 85
|
|
|
|
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()
|
|
local Link = package.loaded["src.core.game3.link"]
|
|
if Link then pcall(Link.reset) end
|
|
local Client = package.loaded["src.online.Client"]
|
|
if Client then pcall(Client.disconnect) end
|
|
if failures == 0 then
|
|
print("PASS g3mg_common_group")
|
|
love.event.quit(0)
|
|
else
|
|
print("FAIL g3mg_common_group failures=" .. failures)
|
|
love.event.quit(1)
|
|
end
|
|
end
|
|
|
|
local function now() return love.timer.getTime() end
|
|
|
|
local Stub = { MIN = 2, MAX = 5, END_FRAME = 420 }
|
|
Stub.__index = Stub
|
|
|
|
function Stub.loadArt() return {} end
|
|
|
|
function Stub.new(ctx)
|
|
local sim = setmetatable({ ctx = ctx, f = 0, scores = { 0, 0, 0, 0, 0 },
|
|
last = { 0, 0, 0, 0, 0 }, presses = 0, leader = ctx.leader }, Stub)
|
|
return sim
|
|
end
|
|
|
|
function Stub:leaderStep(inputs, present)
|
|
for s = 0, 4 do
|
|
local i = inputs[s + 1]
|
|
if present[s + 1] and type(i) == "number" and i > self.last[s + 1] then
|
|
self.scores[s + 1] = self.scores[s + 1] + (i - self.last[s + 1])
|
|
self.last[s + 1] = i
|
|
end
|
|
end
|
|
self.f = self.f + 1
|
|
end
|
|
|
|
function Stub:snapshot()
|
|
return { f = self.f, sc = { unpack(self.scores) }, la = { unpack(self.last) } }
|
|
end
|
|
|
|
function Stub:applySnapshot(s)
|
|
self.f = s.f
|
|
for i = 1, 5 do
|
|
self.scores[i] = s.sc[i] or 0
|
|
self.last[i] = s.la[i] or 0
|
|
end
|
|
end
|
|
|
|
function Stub:localInput(input)
|
|
if input and input:wasPressed("a") then self.presses = self.presses + 1 end
|
|
return self.presses
|
|
end
|
|
|
|
function Stub:predict(i) self.predicted = i end
|
|
|
|
function Stub:becomeLeader(last)
|
|
if last then self:applySnapshot(last) end
|
|
self.leader = true
|
|
end
|
|
|
|
function Stub:update() end
|
|
|
|
function Stub:draw()
|
|
local FrlgFont = require("src.ui.game3.frlg_font")
|
|
local y = 16
|
|
for _, p in ipairs(self.ctx.players) do
|
|
local mine = p.seat == self.ctx.seat
|
|
FrlgFont.draw(string.format("%s %d", p.name or "?", self.scores[p.seat + 1] or 0), 24, y,
|
|
{ colors = mine and FrlgFont.COLOR.GREEN or FrlgFont.COLOR.WHITE })
|
|
y = y + 16
|
|
end
|
|
FrlgFont.draw(string.format("%d", self.f), 200, 136, { colors = FrlgFont.COLOR.WHITE })
|
|
end
|
|
|
|
function Stub:finished() return self.f >= Stub.END_FRAME end
|
|
|
|
function Stub:results()
|
|
local results, powder = {}, {}
|
|
for _, p in ipairs(self.ctx.players) do
|
|
local sc = self.scores[p.seat + 1]
|
|
results[#results + 1] = { seat = p.seat, score = sc, stats = { presses = sc } }
|
|
powder[#powder + 1] = { seat = p.seat, amount = sc }
|
|
end
|
|
return { results = results, powder = powder }
|
|
end
|
|
|
|
function Stub.applyResults(session, result, mySeat)
|
|
local Records = require("src.ui.game3.minigame_records")
|
|
for _, r in ipairs(result.results) do
|
|
if r.seat == mySeat then Records.updatePokemonJump(session, r.score, r.score, 0) end
|
|
end
|
|
end
|
|
|
|
return function(game)
|
|
print("PASS driver_started")
|
|
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 Flags = require("src.core.game3.scripting.flags")
|
|
local Player = require("src.core.game3.player")
|
|
local Message = require("src.ui.game3.message")
|
|
local Choice = require("src.ui.game3.choice")
|
|
local SaveMenu = require("src.ui.game3.save_menu")
|
|
local PartyMenu = require("src.ui.game3.party_menu")
|
|
local Party = require("src.core.game3.party")
|
|
local Link = require("src.core.game3.link")
|
|
local Client = require("src.online.Client")
|
|
local Relay = require("tests.support.fake_relay")
|
|
local Wire = require("src.link.Wire")
|
|
local MG = require("src.core.game3.minigames.common")
|
|
local Lobby = require("src.ui.game3.minigames.common_lobby")
|
|
local Countdown = require("src.ui.game3.minigames.common_countdown")
|
|
|
|
package.loaded[MG.GAMES.jump] = Stub
|
|
|
|
local session = Runtime.getSession()
|
|
if not result(session ~= nil, "new game reached the game3 field") then return finish() end
|
|
session.trainerId = 0x1234
|
|
Party.giveMon(session, CHARMANDER, 12)
|
|
Party.giveMon(session, DODRIO, 30)
|
|
|
|
local relay = Relay.new({ clock = function() return now() end })
|
|
local me = relay:seat("a0000001", "RED")
|
|
Client.configure({ relayAddress = "fake:1", connect = function() return me.transport end })
|
|
|
|
local function ctx() return Space.vm and Space.vm.ctx end
|
|
local function getVar(id) return tonumber(Flags.getVar(Space.store, ctx(), id)) or 0 end
|
|
local function setVar(id, v) Flags.setVar(Space.store, ctx(), id, v) end
|
|
|
|
local peers = {}
|
|
local function peerSession(s)
|
|
local rs = { paired = true, closed = false, left = false, seq = 0, target = s.room }
|
|
function rs:send(msg)
|
|
self.seq = self.seq + 1
|
|
relay:handle(s, { type = "room_msg", seq = self.seq, msg = msg })
|
|
end
|
|
function rs:poll()
|
|
local out = {}
|
|
for _, m in ipairs(s.transport.inbox) do
|
|
if m.type == "room_msg" and type(m.msg) == "table" then
|
|
local inner = Wire.sanitize(m.msg)
|
|
if inner then
|
|
if m.relay then
|
|
inner.relay = true
|
|
if type(inner.seat) ~= "number" then inner.seat = -1 end
|
|
else
|
|
inner.seat = m.seat
|
|
end
|
|
out[#out + 1] = inner
|
|
end
|
|
end
|
|
end
|
|
s.transport.inbox = {}
|
|
return out
|
|
end
|
|
function rs:players()
|
|
local room = relay.rooms[s.room or ""]
|
|
return room and room.players or {}
|
|
end
|
|
function rs:close()
|
|
self.left = true
|
|
self.closed = true
|
|
relay:handle(s, { type = "room_leave" })
|
|
end
|
|
return rs
|
|
end
|
|
local function startPeer(s, pressEvery)
|
|
local room = relay.rooms[s.room or ""]
|
|
local seat = relay:seatOf(room, s.id)
|
|
local players = {}
|
|
for _, p in ipairs(room.players) do
|
|
players[#players + 1] = { id = p.id, name = p.name, seat = p.seat, trainerId = 0x2000 + p.seat, gender = 0 }
|
|
end
|
|
local input = { n = 0 }
|
|
function input:wasPressed(b) return b == "a" and self.n % pressEvery == 0 end
|
|
local m = MG.Match.new({ game = "jump", session = peerSession(s), seat = seat, seats = #players,
|
|
players = players, seed = room.seed, partySlot = 0 },
|
|
{ module = Stub, partyMon = { species = CHARMANDER }, leader = room.leader,
|
|
countdown = function() return Countdown.new(120, 80, { playSe = function() end }) end })
|
|
peers[#peers + 1] = { s = s, m = m, input = input, acc = 0, last = now() }
|
|
return m
|
|
end
|
|
local function stepPeers()
|
|
for _, p in ipairs(peers) do
|
|
if not p.gone and not p.m:finished() then
|
|
local t = now()
|
|
p.acc = p.acc + (t - p.last)
|
|
p.last = t
|
|
local n = 0
|
|
while p.acc >= MG.DT and n < 8 do
|
|
p.acc = p.acc - MG.DT
|
|
n = n + 1
|
|
p.input.n = p.input.n + 1
|
|
p.m:step(p.input)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
local function wait(n)
|
|
for _ = 1, n do
|
|
relay:pump()
|
|
stepPeers()
|
|
U.wait(1)
|
|
end
|
|
end
|
|
local function waitFor(cond, seconds)
|
|
local t0 = now()
|
|
while not cond() do
|
|
relay:pump()
|
|
stepPeers()
|
|
U.wait(1)
|
|
if now() - t0 > (seconds or 5) then return false end
|
|
end
|
|
return true
|
|
end
|
|
|
|
local live = Link.liveProfile()
|
|
if not result(live ~= nil, "the live g3 profile computes (vanilla game)") then return finish() end
|
|
Link.connect()
|
|
result(waitFor(function() return Client.state() == "online" end, 5), "the Client is online on the relay")
|
|
wait(20)
|
|
print("[driver] lobby screens")
|
|
Lobby.showPlayers({}, { mode = "leader", group = { min = 2, max = 5 }, activity = 9 })
|
|
wait(12)
|
|
U.shot(game, DIR .. "/g3mg_common_lobby_leader_alone.png")
|
|
result(Lobby.modeText():find("1 player") ~= nil, "leader alone: 1 player needed")
|
|
local WirelessIcon = require("src.ui.game3.wireless_icon")
|
|
result(WirelessIcon.anim() == "3bars" and (WirelessIcon.frame() or 1) > 1,
|
|
"the lobby shows the animating wireless icon (frame " .. tostring(WirelessIcon.frame()) .. ")")
|
|
Lobby.showPlayers({ { name = "BLUE", trainerId = 0x2222 }, { name = "LEAF", trainerId = 0x3333, pending = true } },
|
|
{ mode = "leader", group = { min = 2, max = 5 }, activity = 9 })
|
|
wait(12)
|
|
U.shot(game, DIR .. "/g3mg_common_lobby_leader_3p.png")
|
|
result(Lobby.canStart(), "leader with members: START allowed (AwaitingLinkPressStart)")
|
|
Lobby.showPlayers({ { slot = 1, name = "BLUE", trainerId = 0x2222 }, { slot = 2, name = "LEAF", trainerId = 0x3333, started = true } },
|
|
{ mode = "group", capacity = { activity = 9, min = 2, max = 5 } })
|
|
wait(12)
|
|
U.shot(game, DIR .. "/g3mg_common_lobby_joiner.png")
|
|
Lobby.reset()
|
|
wait(2)
|
|
|
|
|
|
local blue = relay:seat("b0000002", "BLUE")
|
|
relay:handle(blue, { type = "lobby_hello", protocol = 3, name = "BLUE", profiles = { live } })
|
|
local leaf = relay:seat("c0000003", "LEAF")
|
|
relay:handle(leaf, { type = "lobby_hello", protocol = 3, name = "LEAF", profiles = { live } })
|
|
relay:handle(blue, { type = "group_open", activity = "minigame_jump", profile = live,
|
|
avatar = { name = "BLUE", trainerId = 0x2222, gender = 0, version = "leafgreen" } })
|
|
|
|
Flags.setFlag(Space.store, ctx(), FLAG_SYS_POKEDEX_GET, true)
|
|
Flags.setFlag(Space.store, ctx(), FLAG_GOT_MOON_STONE_FROM_JOYFUL_GAME_CORNER, true)
|
|
Map.load(nil, game, CORNER, { x = 6, y = 3, facing = "up" })
|
|
wait(60)
|
|
setVar(VAR_MAP_SCENE_TWO_ISLAND_JOYFUL_GAME_CORNER, 4)
|
|
Player.cellX, Player.cellY = 6, 3
|
|
Player.px, Player.py = 6 * 16, 3 * 16
|
|
Player.targetX, Player.targetY = 6, 3
|
|
Player.facing = "up"
|
|
session.x, session.y, session.facing = 6, 3, "up"
|
|
wait(10)
|
|
U.tap(game, "a")
|
|
|
|
local picked = false
|
|
local joined = false
|
|
local t0 = now()
|
|
while now() - t0 < 40 do
|
|
relay:pump()
|
|
stepPeers()
|
|
if PartyMenu.isOpen() and not picked then
|
|
wait(10)
|
|
U.shot(game, DIR .. "/g3mg_common_choose_mon.png")
|
|
picked = true
|
|
U.tap(game, "a")
|
|
elseif Lobby.isOpen() and Lobby.mode == "group" and #Lobby.players > 0 and not joined then
|
|
wait(30)
|
|
U.shot(game, DIR .. "/g3mg_common_group_list.png")
|
|
result(not Message.isOpen() and Lobby.message() == Lobby.chooseText() and Lobby.chooseText() ~= "",
|
|
"the group list owns the textbox: " .. Lobby.chooseText())
|
|
joined = true
|
|
U.tap(game, "a")
|
|
elseif Choice.active or SaveMenu.isOpen() or (Message.isOpen() and Message.isWaiting and Message.isWaiting()) then
|
|
U.tap(game, "a")
|
|
end
|
|
local g = relay.groups[blue.id]
|
|
if g and #g.pending > 0 then
|
|
for _, p in ipairs(g.pending) do
|
|
relay:handle(blue, { type = "group_accept", from = p.id, ok = true })
|
|
end
|
|
if #g.members == 2 then
|
|
relay:handle(leaf, { type = "group_join", leader = blue.id, profile = live,
|
|
avatar = { name = "LEAF", trainerId = 0x3333, gender = 1, version = "firered" } })
|
|
end
|
|
end
|
|
if g and #g.members == 3 then
|
|
relay:handle(blue, { type = "group_start" })
|
|
end
|
|
if MG.isActive() then break end
|
|
U.wait(2)
|
|
end
|
|
result(picked, "ChooseMonForWirelessMinigame opened the party menu")
|
|
result(MG.partySlot == 0, "Charmander (slot 0) entered")
|
|
result(joined, "the group list showed BLUE's group")
|
|
if not result(MG.isActive(), "MG.arm launched the minigame on the script's waitstate") then
|
|
U.shot(game, DIR .. "/g3mg_common_no_launch.png")
|
|
return finish()
|
|
end
|
|
local room = Client.room()
|
|
result(room and room.intent == "minigame" and room.origin == "group", "a group-origin minigame room")
|
|
startPeer(blue, 5)
|
|
startPeer(leaf, 9)
|
|
local mine = MG.match()
|
|
result(mine and mine.seat == 1, "RED sits at seat 1")
|
|
result(mine and mine.leader == 0, "BLUE leads")
|
|
|
|
result(waitFor(function() return mine.phase ~= "ready" end, 10), "every seat readied and BLUE started")
|
|
result(#(mine.startPlayers or {}) == 3, "three players in game3_mg_start")
|
|
result(waitFor(function() return mine.countdown and mine.countdown:digitShown() == 3 end, 5), "3")
|
|
wait(10)
|
|
U.shot(game, DIR .. "/g3mg_common_countdown_3.png")
|
|
result(waitFor(function() return mine.countdown and mine.countdown.starts ~= nil end, 8), "START")
|
|
wait(12)
|
|
U.shot(game, DIR .. "/g3mg_common_countdown_start.png")
|
|
result(waitFor(function() return mine.phase == "play" end, 6), "play")
|
|
for _ = 1, 40 do
|
|
U.tap(game, "a")
|
|
wait(5)
|
|
end
|
|
result(waitFor(function() return (mine.sim.scores[2] or 0) > 0 end, 5),
|
|
"RED's presses reach the leader and come back in states")
|
|
|
|
local roomId = Client.room() and Client.room().room
|
|
peers[1].gone = true
|
|
relay:handle(blue, { type = "room_leave" })
|
|
relay:migrateLeader(roomId)
|
|
result(waitFor(function() return mine:isLeader() end, 3), "BLUE dropped: leadership migrated to RED")
|
|
result(peers[2].m.leader == 1, "LEAF follows RED")
|
|
for _ = 1, 20 do
|
|
U.tap(game, "a")
|
|
wait(5)
|
|
end
|
|
U.shot(game, DIR .. "/g3mg_common_after_migration.png")
|
|
result(waitFor(function() return mine.phase == "results" or mine.phase == "done" or not MG.isActive() end, 15),
|
|
"RED finished the game as leader")
|
|
local res = mine.result
|
|
waitFor(function() return peers[2].m.result ~= nil or peers[2].m:finished() end, 5)
|
|
print("[driver] LEAF phase=" .. tostring(peers[2].m.phase) .. " why=" .. tostring(peers[2].m.why))
|
|
result(res ~= nil and peers[2].m.result ~= nil
|
|
and require("src.link.Json").encode(res.results) == require("src.link.Json").encode(peers[2].m.result.results),
|
|
"RED and LEAF agree on the results")
|
|
local myScore = 0
|
|
for _, r in ipairs(res and res.results or {}) do if r.seat == 1 then myScore = r.score end end
|
|
result(myScore > 0, "RED scored " .. myScore)
|
|
result((session.berryPowder or 0) == myScore, "Berry Powder = RED's share (" .. tostring(session.berryPowder) .. ")")
|
|
result(session.pokemonJumpRecords and session.pokemonJumpRecords.bestJumpScore == myScore, "record written")
|
|
|
|
result(waitFor(function() return not MG.isActive() end, 8), "the minigame exits")
|
|
result(waitFor(function() return Space.mapId == CORNER and getVar(VAR_CABLE_CLUB_STATE) == 0 end, 10),
|
|
"warped back and ExitMinigameRoom cleared VAR_CABLE_CLUB_STATE")
|
|
local Fade = require("src.ui.game3.fade")
|
|
waitFor(function() return not Fade.isActive() and (tonumber(Fade.t) or 0) == 0 and not Space.vm:isRunning() end, 10)
|
|
print(string.format("[driver] after exit fade.t=%s active=%s map=%s pos=%s,%s visible=%s stack=%s",
|
|
tostring(Fade.t), tostring(Fade.active), tostring(Space.mapId), tostring(Player.cellX), tostring(Player.cellY),
|
|
tostring(Player.visible), tostring(require("src.ui.game3.stack").depth())))
|
|
wait(30)
|
|
U.shot(game, DIR .. "/g3mg_common_returned.png")
|
|
result(Client.room() == nil, "RED left the relay room")
|
|
finish()
|
|
end
|