mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
3c71afb9fa
GameSpeed is a single fast-forward multiplier applied uniformly to the whole logic clock -- overworld walking, menu navigation and battle turns all scale together. A player who wants 4X battles but 1X overworld (so a cutscene or NPC dialogue doesn't blur past) has no way to get both. Splits save.options.speed into speedOverworld/speedBattle/speedMenu, each cycling independently, with an automatic migration so an existing save's speed choice carries over. Game.speedCategoryInStack resolves which category is active by walking the state stack (the same idiom wideBattleInStack/fillScaleInStack already use), so a menu opened mid- battle inherits battle speed rather than resetting to whatever "menu" defaults to. Adds a new core.logic_speed hook so a mod can read or override the resolved multiplier for the current frame regardless of which category produced it, sitting after the link-play and run-argument overrides so neither is a seam a mod can defeat. RFC 0007 status: Proposed.
149 lines
5.1 KiB
Lua
149 lines
5.1 KiB
Lua
-- Driver: play a real online match, hosting side.
|
|
--
|
|
-- Pair with tests/drivers/online_match_join.lua in a second instance:
|
|
--
|
|
-- POKEPORT_IDENTITY=pokehost ONLINE_CODE_FILE=/tmp/poke_code.txt \
|
|
-- POKEPORT_DRIVER=tests/drivers/online_match_host.lua love .
|
|
-- POKEPORT_IDENTITY=pokeguest ONLINE_CODE_FILE=/tmp/poke_code.txt \
|
|
-- POKEPORT_DRIVER=tests/drivers/online_match_join.lua love .
|
|
--
|
|
-- Two real windows, the real relay (POKEPORT_RELAY_ADDR to point elsewhere),
|
|
-- the real LinkState menus and the real lockstep battle -- the loopback
|
|
-- suites can't see anything the transport, the two separate processes or
|
|
-- the two save identities contribute. The host writes its room code to
|
|
-- ONLINE_CODE_FILE for the joiner to pick up, since on a real screen that
|
|
-- code is read aloud to a friend.
|
|
--
|
|
-- Prints ONLINE_MATCH_HOST: lines; the wrapper script greps them.
|
|
|
|
return function(game)
|
|
local U = dofile("tests/drivers/util.lua")
|
|
local Pokemon = require("src.pokemon.Pokemon")
|
|
local LinkState = require("src.link.LinkState")
|
|
local Runtime = require("src.mods.Runtime")
|
|
local DIR = os.getenv("SHOT_DIR") or "/tmp/shots"
|
|
local CODE_FILE = os.getenv("ONLINE_CODE_FILE") or "/tmp/poke_code.txt"
|
|
local TAG = "ONLINE_MATCH_HOST:"
|
|
|
|
local function log(...) U.log(TAG, ...) end
|
|
|
|
-- a desync is the whole point of the exercise, so make it loud
|
|
local desyncs = {}
|
|
-- wrap emit rather than subscribing: with no mods loaded Runtime.events is
|
|
-- the null sink, and a vanilla online run is exactly the case under test
|
|
local realEmit = Runtime.emit
|
|
Runtime.emit = function(name, p)
|
|
if name == "link.desync" and p then
|
|
desyncs[#desyncs + 1] = p
|
|
log(("DESYNC turn=%s component=%s fatal=%s"):format(
|
|
tostring(p.turn), tostring(p.component), tostring(p.fatal)))
|
|
end
|
|
return realEmit(name, p)
|
|
end
|
|
|
|
game.save.player.name = "HOST"
|
|
game.save.party = {
|
|
Pokemon.new(game.data, "CHARIZARD", 50),
|
|
Pokemon.new(game.data, "SNORLAX", 50),
|
|
Pokemon.new(game.data, "ALAKAZAM", 50),
|
|
}
|
|
U.teleport(game, "PALLET_TOWN", 10, 8, "down")
|
|
U.wait(30)
|
|
|
|
local link = LinkState.new(game)
|
|
game.stack:push(link)
|
|
U.wait(10)
|
|
|
|
-- the GAME SPEED forcing under test: a link session pins the logic clock
|
|
-- to 1X no matter what the option or POKEPORT_SPEED says. RFC 0007: set
|
|
-- all three per-category speeds high, since the link lock has to win over
|
|
-- every one of them, not just whichever category happens to be active.
|
|
game.save.options.speedOverworld = 10
|
|
game.save.options.speedBattle = 10
|
|
game.save.options.speedMenu = 10
|
|
U.wait(2)
|
|
log("logicSpeed with GAME SPEED=10 during link:", game:logicSpeed())
|
|
|
|
-- top menu: LAN / ONLINE MATCH / TOURNAMENT
|
|
U.tap(game, "down"); U.wait(3)
|
|
U.tap(game, "a"); U.wait(5) -- ONLINE MATCH
|
|
if link.stage ~= "onlineMenu" then
|
|
log("FAIL expected onlineMenu, at", tostring(link.stage))
|
|
-- a mods-enabled build lands on the vanilla-restart prompt instead
|
|
U.wait(60)
|
|
U.shot(game, DIR .. "/online_host_blocked.png")
|
|
return
|
|
end
|
|
U.tap(game, "a"); U.wait(10) -- HOST ONLINE
|
|
|
|
-- wait for the relay to hand back a room code
|
|
local code
|
|
for _ = 1, 900 do
|
|
if link.net and link.net.code then code = link.net.code break end
|
|
U.wait(1)
|
|
end
|
|
if not code then
|
|
log("FAIL no room code from the relay:", tostring(link.net and link.net.error))
|
|
U.shot(game, DIR .. "/online_host_nocode.png")
|
|
return
|
|
end
|
|
log("hosting code", code)
|
|
U.shot(game, DIR .. "/online_host_1_code.png")
|
|
local f = io.open(CODE_FILE, "w")
|
|
if f then f:write(code) f:close() end
|
|
|
|
-- wait for the joiner
|
|
for _ = 1, 1800 do
|
|
if link.stage == "modeSelect" then break end
|
|
U.wait(1)
|
|
end
|
|
if link.stage ~= "modeSelect" then
|
|
log("FAIL nobody joined (stage " .. tostring(link.stage) .. ")")
|
|
return
|
|
end
|
|
log("paired with", tostring(link.peerName))
|
|
U.shot(game, DIR .. "/online_host_2_paired.png")
|
|
|
|
U.tap(game, "down"); U.wait(3) -- TRADE / BATTLE -> BATTLE
|
|
U.tap(game, "a"); U.wait(10)
|
|
-- host owns the level rule; ANY is the default row
|
|
for _ = 1, 600 do
|
|
if link.stage == "battleOptions" then break end
|
|
U.wait(1)
|
|
end
|
|
if link.stage == "battleOptions" then
|
|
U.shot(game, DIR .. "/online_host_3_options.png")
|
|
U.tap(game, "a"); U.wait(5)
|
|
end
|
|
|
|
-- the battle: mash A, exactly like a player who just wants it over with
|
|
local battle
|
|
for _ = 1, 1800 do
|
|
local top = game.stack:top()
|
|
if top and top.kind == "link" then battle = top break end
|
|
U.wait(1)
|
|
end
|
|
if not battle then
|
|
log("FAIL battle never started (stage " .. tostring(link.stage) .. ")")
|
|
U.shot(game, DIR .. "/online_host_nobattle.png")
|
|
return
|
|
end
|
|
log("battle started vs", tostring(battle.opponentName))
|
|
U.shot(game, DIR .. "/online_host_4_battle.png")
|
|
|
|
local shots = 0
|
|
for i = 1, 200000 do
|
|
if battle.result then break end
|
|
U.tap(game, "a")
|
|
if i % 900 == 0 and shots < 3 then
|
|
shots = shots + 1
|
|
U.shot(game, DIR .. ("/online_host_5_turn%d.png"):format(shots))
|
|
end
|
|
end
|
|
U.wait(60)
|
|
U.shot(game, DIR .. "/online_host_6_result.png")
|
|
log("result", tostring(battle.result), "turns", tostring(battle.turnCount))
|
|
log("desyncs", #desyncs)
|
|
log("DONE")
|
|
end
|