mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-02 15:50:39 -04:00
451 lines
17 KiB
Lua
451 lines
17 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, PIKACHU, SQUIRTLE = 4, 25, 7
|
|
|
|
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_pokemon_jump_group")
|
|
love.event.quit(0)
|
|
else
|
|
print("FAIL g3mg_pokemon_jump_group failures=" .. failures)
|
|
love.event.quit(1)
|
|
end
|
|
end
|
|
|
|
local function now() return love.timer.getTime() 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 Bag = require("src.core.game3.bag")
|
|
local Link = require("src.core.game3.link")
|
|
local Screen = require("src.ui.game3.union_room")
|
|
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")
|
|
local G = require(MG.GAMES.jump)
|
|
local Game = G.Game
|
|
local FUNC = Game.FUNC
|
|
|
|
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)
|
|
|
|
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 peerHooks = {}
|
|
local function fakeHooks()
|
|
local h = { bag = {}, log = {} }
|
|
h.hooks = {
|
|
canAdd = function() return true end,
|
|
addItem = function(item, qty) h.bag[item] = (h.bag[item] or 0) + qty return true end,
|
|
updateRecords = function(score, row, exc) h.records = { score, row, exc } return true end,
|
|
fanfareDone = function() return true end,
|
|
}
|
|
return h
|
|
end
|
|
G.hooksFor = function(c)
|
|
local h = peerHooks[tonumber(c.seat)]
|
|
if h then return h.hooks end
|
|
return G.defaultHooks()
|
|
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 art = assert(G.loadArt())
|
|
local function planPeer(p)
|
|
local input = p.input
|
|
input.want = nil
|
|
local sim = p.m.sim
|
|
if not sim then return end
|
|
local g = sim.game
|
|
local gfx = g.gfx
|
|
if gfx.yesno then
|
|
input.want = gfx.yesno.cursor == 0 and "a" or "up"
|
|
return
|
|
end
|
|
if gfx.msgWindow and gfx.msgWindow.itemId and gfx.msgWindow.shown then
|
|
input.want = "a"
|
|
return
|
|
end
|
|
if p.jump and g.comm.funcId == FUNC.GAME_ROUND and g.vineState == 4
|
|
and g.player.monState == Game.MONSTATE.NORMAL then
|
|
input.want = "a"
|
|
end
|
|
end
|
|
|
|
local function startPeer(s, species)
|
|
local room = relay.rooms[s.room or ""]
|
|
local seat = relay:seatOf(room, s.id)
|
|
peerHooks[seat] = fakeHooks()
|
|
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 = p.seat % 2 }
|
|
end
|
|
local input = { want = nil }
|
|
function input:wasPressed(b) return self.want == b end
|
|
function input:isDown() return false end
|
|
local m = MG.Match.new({ game = "jump", session = peerSession(s), seat = seat, seats = #players,
|
|
players = players, seed = room.seed, partySlot = 0 },
|
|
{ module = G, art = art, partyMon = { species = species, personality = 0x5a5a0000 + seat, otId = 77 },
|
|
me = { name = s.name, trainerId = 0x2000 + seat, gender = seat % 2 }, leader = room.leader,
|
|
countdown = function() return Countdown.new(120, 80, { playSe = function() end }) end })
|
|
local peer = { s = s, m = m, input = input, acc = 0, last = now(), jump = true, seat = seat }
|
|
peers[#peers + 1] = peer
|
|
return peer
|
|
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
|
|
planPeer(p)
|
|
p.m:step(p.input)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
local redJump = false
|
|
local lastTap = -1
|
|
local function mySim()
|
|
local m = MG.match()
|
|
return m and m.sim or nil
|
|
end
|
|
local function redTick()
|
|
local sim = mySim()
|
|
if not (sim and redJump) then return false end
|
|
local g = sim.game
|
|
if g.comm.funcId == FUNC.GAME_ROUND and (g.vineState == 3 or g.vineState == 4)
|
|
and g.player.monState == Game.MONSTATE.NORMAL and lastTap ~= g.comm.jumpsInRow then
|
|
lastTap = g.comm.jumpsInRow
|
|
U.tap(game, "a")
|
|
return true
|
|
end
|
|
return false
|
|
end
|
|
|
|
local function wait(n)
|
|
for _ = 1, n do
|
|
relay:pump()
|
|
stepPeers()
|
|
if not redTick() then U.wait(1) end
|
|
end
|
|
end
|
|
local function waitFor(cond, seconds)
|
|
local t0 = now()
|
|
while not cond() do
|
|
relay:pump()
|
|
stepPeers()
|
|
if not redTick() then U.wait(1) end
|
|
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({ { name = "BLUE", trainerId = 0x2222 }, { name = "LEAF", trainerId = 0x3333 } },
|
|
{ mode = "leader", group = { min = 2, max = 5 }, activity = 9 })
|
|
wait(12)
|
|
U.shot(game, DIR .. "/g3mg_pokemon_jump_lobby_leader.png")
|
|
result(Lobby.canStart(), "POKEMON JUMP leader screen: two members, START allowed")
|
|
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, joined = false, false
|
|
local t0 = now()
|
|
while now() - t0 < 40 do
|
|
relay:pump()
|
|
stepPeers()
|
|
if PartyMenu.isOpen() and not picked then
|
|
picked = true
|
|
wait(10)
|
|
U.tap(game, "a")
|
|
elseif not joined and ((Screen.isOpen() and Screen.mode == "group" and #Screen.players > 0)
|
|
or (Lobby.isOpen() and Lobby.mode == "group" and #Lobby.players > 0)) then
|
|
wait(6)
|
|
U.shot(game, DIR .. "/g3mg_pokemon_jump_lobby_join.png")
|
|
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 POKEMON JUMP group")
|
|
if not result(MG.isActive(), "MG.arm launched Pokemon Jump on the script's waitstate") then
|
|
U.shot(game, DIR .. "/g3mg_pokemon_jump_no_launch.png")
|
|
return finish()
|
|
end
|
|
local mine = MG.match()
|
|
startPeer(blue, PIKACHU)
|
|
startPeer(leaf, SQUIRTLE)
|
|
result(mine and mine.seat == 1, "RED sits at seat 1")
|
|
result(mine and mine.leader == 0, "BLUE leads")
|
|
result(waitFor(function() return mine.sim ~= nil end, 10), "every seat readied and BLUE started Pokemon Jump")
|
|
result(mine.sim and mine.sim.game.numPlayers == 3, "three jumpers")
|
|
|
|
local namesShot, countdownShot = false, false
|
|
local reached = waitFor(function()
|
|
local sim = mine.sim
|
|
local g = sim.game
|
|
local gfx = g.gfx
|
|
if not countdownShot then
|
|
local c = (mine.phase == "countdown" and mine.countdown) or gfx.countdown
|
|
if c and c:running() and c:digitShown() == 3 then
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_countdown.png")
|
|
countdownShot = true
|
|
end
|
|
end
|
|
if not namesShot and gfx.names.visible and gfx.names.highlight and gfx:isMonIntroBounceActive() then
|
|
if gfx.monSprites[g.multiplayerId].y2 <= -20 then
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_intro_names.png")
|
|
namesShot = true
|
|
end
|
|
end
|
|
return g.comm.funcId == FUNC.GAME_ROUND
|
|
end, 30)
|
|
result(countdownShot, "the 3-2-1-START countdown played")
|
|
result(namesShot, "intro: names over the mons, RED's highlighted, RED's mon hops")
|
|
if not result(reached, "the vine starts swinging") then
|
|
U.shot(game, DIR .. "/g3mg_pokemon_jump_stuck.png")
|
|
return finish()
|
|
end
|
|
redJump = true
|
|
|
|
local midShot, bonusShot = false, false
|
|
waitFor(function()
|
|
local g = mine.sim.game
|
|
local gfx = g.gfx
|
|
if not midShot and g.comm.jumpsInRow >= 2 and gfx.monSprites[g.multiplayerId].y2 <= -20
|
|
and gfx.vine.priority == 2 then
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_midgame.png")
|
|
midShot = true
|
|
end
|
|
if not bonusShot and gfx.bonus.visible and gfx:bonusScrollY() > -36 then
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_bonus.png")
|
|
bonusShot = true
|
|
end
|
|
return g.comm.jumpsInRow >= 4 and midShot
|
|
end, 60)
|
|
local g = mine.sim.game
|
|
result(g.comm.jumpsInRow >= 4, "four jumps in a row with RED tapping A (" .. g.comm.jumpsInRow .. ")")
|
|
result(midShot, "mid-game shot: mons in the air over the vine")
|
|
result(g.comm.jumpScore > g.comm.jumpsInRow * 10, "same-time bonus scored (" .. g.comm.jumpScore .. ")")
|
|
result(bonusShot, "bonus plate shown")
|
|
local Audio = require("src.core.game3.audio")
|
|
local playing = Audio._currentSong and Audio._currentSong.id
|
|
result(playing == Game.MUS_POKE_JUMP, "MUS_POKE_JUMP plays during the game (" .. tostring(playing) .. ")")
|
|
|
|
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: RED leads Pokemon Jump")
|
|
result(waitFor(function() return peers[2].m.leader == 1 end, 3), "LEAF follows RED")
|
|
local row = g.comm.jumpsInRow
|
|
result(waitFor(function() return mine.sim.game.comm.jumpsInRow >= row + 2 end, 30),
|
|
"the vine keeps swinging under RED")
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_after_migration.png")
|
|
g = mine.sim.game
|
|
g.comm.jumpScore = math.max(g.comm.jumpScore, 4990)
|
|
result(waitFor(function() return mine.sim.game.comm.jumpScore >= 5000 end, 20), "score reaches 5000")
|
|
peers[2].jump = false
|
|
|
|
local overShot, prizeShot, savingShot, againShot, droppedShot = false, false, false, false, false
|
|
local prizeItem
|
|
local done = waitFor(function()
|
|
local sim = mine.sim
|
|
if not sim then return true end
|
|
local gm = sim.game
|
|
local gfx = gm.gfx
|
|
if not overShot and gm.comm.funcId == FUNC.GAME_OVER and gfx.names.visible and not gfx.names.highlight then
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_game_over.png")
|
|
overShot = true
|
|
end
|
|
local w = gfx.msgWindow
|
|
if w and w.shown then
|
|
if w.key == "gText_AwesomeWonF701F700" and not prizeShot then
|
|
wait(20)
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_prize.png")
|
|
prizeShot = true
|
|
prizeItem = w.itemId
|
|
U.tap(game, "a")
|
|
elseif w.key == "gText_SavingDontTurnOffPower" and not savingShot then
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_saving.png")
|
|
savingShot = true
|
|
elseif w.key == "gText_SomeoneDroppedOut2" and not droppedShot then
|
|
wait(10)
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_dropped_out.png")
|
|
droppedShot = true
|
|
end
|
|
end
|
|
if gfx.yesno and not againShot then
|
|
wait(10)
|
|
U.still(game, DIR .. "/g3mg_pokemon_jump_play_again.png")
|
|
againShot = true
|
|
U.tap(game, "down")
|
|
wait(4)
|
|
U.tap(game, "a")
|
|
end
|
|
return not MG.isActive()
|
|
end, 60)
|
|
result(overShot, "game over: LEAF's mon flashes, names shown")
|
|
result(prizeShot, "prize: Awesome score! You've won a berry!")
|
|
result(savingShot, "SAVING... DON'T TURN OFF THE POWER.")
|
|
result(againShot, "Want to play again? YES/NO")
|
|
result(droppedShot, "RED said NO: Somebody dropped out. The link will be canceled.")
|
|
result(done, "the minigame exits")
|
|
result(prizeItem ~= nil and Bag.get(session.bag, prizeItem) >= 1,
|
|
"prize berry " .. tostring(prizeItem) .. " in RED's bag")
|
|
local rec = session.pokemonJumpRecords or {}
|
|
result((rec.bestJumpScore or 0) >= 5000, "best score record " .. tostring(rec.bestJumpScore))
|
|
result((rec.jumpsInRow or 0) >= 6, "jumps in a row record " .. tostring(rec.jumpsInRow))
|
|
local leafRes = peers[2].m.result
|
|
result(leafRes ~= nil and leafRes.results and leafRes.results[1] and leafRes.results[1].score == rec.bestJumpScore,
|
|
"LEAF got RED's game3_mg_result")
|
|
result(peerHooks[2] and peerHooks[2].bag[prizeItem] == 1, "LEAF got the same berry")
|
|
|
|
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)
|
|
wait(30)
|
|
U.shot(game, DIR .. "/g3mg_pokemon_jump_returned.png")
|
|
local after = Audio._currentSong and Audio._currentSong.id
|
|
result(after ~= Game.MUS_POKE_JUMP, "the corner's map music is back after the warp (" .. tostring(after) .. ")")
|
|
result(Client.room() == nil, "RED left the relay room")
|
|
finish()
|
|
end
|