-- 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()