Files
gen1recomp/tests/gen2_map_radio_test.lua
T
bryanthaboi ae6cac89e1 G2 support
2026-08-11 11:53:30 -04:00

174 lines
6.1 KiB
Lua

-- The wall radios: `special MapRadio` -> src/ui/gen2/MapRadio.lua, a port of
-- engine/pokegear/pokegear.asm PlayRadio.
--
-- luajit tests/gen2_map_radio_test.lua
--
-- PlayRadio's contract, row by row: resolve the MAPRADIO_* index through
-- PlayRadioStationPointers (index 0, the Pokemon Channel, resolves by region
-- and time of day), wait 100 frames with only the station name up, then run
-- one show frame per loop until A or B, and leave the channel song playing as
-- the map music on the way out (ExitPokegearRadio_HandleMusic /
-- RadioMusicRestartDE). Every interaction builds a fresh machine, which is
-- what makes the radio replayable: back out, talk again, it plays again.
package.path = "./?.lua;./?/init.lua;" .. package.path
local S = require("tests.harness").suite("gen2 map radio")
local check, eq = S.check, S.eq
love = require("tests.love_stub")
require("src.core.Logger").warn = function() end
local MapRadio = require("src.ui.gen2.MapRadio")
local Music = require("src.core.Music")
local Save = require("src.core.gen2.Save")
-- `call Random`, scripted: hand back the listed bytes in order, cycling.
local function rolls(...)
local list = { ... }
local index = 0
return function()
index = index % #list + 1
return list[index]
end
end
local function newInput()
local input = { pressed = {} }
function input:press(...)
for _, b in ipairs({ ... }) do self.pressed[b] = true end
end
function input:wasPressed(b)
if self.pressed[b] then self.pressed[b] = nil return true end
return false
end
function input:isDown() return false end
return input
end
local LANDMARKS = { landmarks = {
LANDMARK_NEW_BARK_TOWN = { index = 2 },
LANDMARK_VERMILION_CITY = { index = 50 },
} }
local function newGame(save)
return {
input = newInput(),
save = save,
options = Save.defaultOptions(),
data = { audio = {}, pokemon = {}, items = {},
gen2Landmarks = LANDMARKS },
stack = { _items = {},
push = function(self, s) self._items[#self._items + 1] = s end,
pop = function(self) return table.remove(self._items) end,
top = function(self) return self._items[#self._items] end,
},
}
end
-- Enough of the show tables for LETS_ALL_SING (the Ben/Fern chatter needs
-- only a weekday) -- the full fixtures live in gen2_menus_test.lua, which
-- owns the line-by-line show assertions.
local RADIO_DATA = {
landmarks = {}, mapLandmark = {}, grass = {}, species = {},
caught = function() return false end,
dex = {}, classes = {}, hidden = {},
weekday = 2, luckyNumber = 42, inJohto = true,
}
-- ---- the blocking loop -----------------------------------------------------
do
local save = Save.newGame()
local game = newGame(save)
game.world = { map = { def = { music = "Music_NewBarkTown" } } }
local done = false
local mr = MapRadio.new(game, {
channel = 7, radioData = RADIO_DATA, radioRng = rolls(0),
save = save, onDone = function() done = true end,
})
game.stack:push(mr)
eq(mr.station, "LETS_ALL_SING", "MAPRADIO index 7 is Let's All Sing")
eq(mr.radioMusicPlaying, "enterMap",
".PlayStation parks ENTER_MAP_MUSIC before the show starts")
-- `ld c, 100 / call DelayFrames`: no lines and no buttons for 100 frames.
game.input:press("a")
for _ = 1, 100 do mr:update(0) end
eq(#mr.radio.log, 0, "nothing prints during the delay")
eq(game.stack:top(), mr, "and A during the delay does not close it")
game.input.pressed = {}
-- The loop's first frame: StartPokemonMusicChannel reads the weekday's low
-- bit and RadioMusicRestartDE lands the song (an even weekday marches).
mr:update(0)
eq(mr.radio.music, "Music_PokemonMarch",
"the sing show starts the weekday's song")
eq(mr.radioMusicPlaying, "Music_PokemonMarch",
"and RadioMusicRestartDE parks it for the exit handler")
-- Left running, the DJ chatter scrolls through the box.
for _ = 1, 700 do mr:update(0) end
check(#mr.radio.log >= 1, "the show prints once the delay is spent")
-- A stops the loop: pop, resume, and the song stays as the map music.
Music.setMapSong("Music_NewBarkTown")
game.input:press("a")
mr:update(0)
eq(game.stack:top(), nil, "A closes the radio")
eq(done, true, "and resumes the parked script")
eq(Music.mapSong(), "Music_PokemonMarch",
"with the playing song left as the map music (wMapMusic)")
-- Replay: the next interaction builds a fresh machine and plays again.
local again = MapRadio.new(game, {
channel = 7, radioData = RADIO_DATA, radioRng = rolls(0), save = save,
})
game.stack:push(again)
for _ = 1, 101 do again:update(0) end
eq(again.radio.music, "Music_PokemonMarch", "a second listen plays again")
for _ = 1, 400 do again:update(0) end
check(#again.radio.log >= 1, "and prints again")
game.input:press("b")
again:update(0)
eq(game.stack:top(), nil, "B closes it too")
end
-- ---- LoadStation_PokemonChannel --------------------------------------------
-- Index 0 is not a station: it resolves by region, and in Johto by time of
-- day -- the morning airs the Pokedex Show, the rest of the day Oak.
do
local save = Save.newGame()
local function stationFor(landmark, daytime)
local game = newGame(save)
game.world = { daytime = daytime,
map = { def = { music = "Music_NewBarkTown" } } }
local mr = MapRadio.new(game, {
channel = 0, radioData = RADIO_DATA, radioRng = rolls(0),
save = save, currentLandmark = landmark,
})
return mr.station
end
eq(stationFor("LANDMARK_NEW_BARK_TOWN", "MORN"), "POKEDEX_SHOW",
"Johto mornings air the Pokedex Show")
eq(stationFor("LANDMARK_NEW_BARK_TOWN", "DAY"), "OAKS_POKEMON_TALK",
"and the day belongs to Oak")
eq(stationFor("LANDMARK_VERMILION_CITY", "DAY"), "PLACES_AND_PEOPLE",
"from Kanto the same dial carries Places & People")
end
-- ---- the fixed stations ----------------------------------------------------
do
local save = Save.newGame()
local game = newGame(save)
local mr = MapRadio.new(game, {
channel = 4, radioData = RADIO_DATA, radioRng = rolls(0), save = save,
})
eq(mr.station, "LUCKY_CHANNEL",
"Radio2Script's MAPRADIO_LUCKY_CHANNEL is index 4")
end
S.finish()