mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 16:31:05 +02:00
270 lines
9.7 KiB
Lua
270 lines
9.7 KiB
Lua
-- Screenshots of every Gen 2 menu, for eyes that a test cannot replace.
|
|
--
|
|
-- POKEPORT_GAME=gold POKEPORT_DRIVER=tests/drivers/gold_menu_shots.lua love .
|
|
-- POKEPORT_SHOT_DIR=/tmp/gold-menus (default)
|
|
--
|
|
-- The driver boots straight into the world (Game2 skips the cinema under
|
|
-- POKEPORT_DRIVER), gives the save enough content that the screens have
|
|
-- something to draw, then pushes each one and captures it.
|
|
local U = require("tests.drivers.util")
|
|
|
|
local InitClock = require("src.ui.gen2.InitClock")
|
|
local MainMenu = require("src.ui.gen2.MainMenu")
|
|
local NamingScreen = require("src.ui.gen2.NamingScreen")
|
|
local OptionsMenu = require("src.ui.gen2.OptionsMenu")
|
|
local BoxMenu = require("src.ui.gen2.BoxMenu")
|
|
local PackMenu = require("src.ui.gen2.PackMenu")
|
|
local PcMenu = require("src.ui.gen2.PcMenu")
|
|
local PartyMenu = require("src.ui.gen2.PartyMenu")
|
|
local PokedexMenu = require("src.ui.gen2.PokedexMenu")
|
|
local Pokegear = require("src.ui.gen2.Pokegear")
|
|
local SaveMenu = require("src.ui.gen2.SaveMenu")
|
|
local StartMenu = require("src.ui.gen2.StartMenu")
|
|
local TrainerCard = require("src.ui.gen2.TrainerCard")
|
|
local GoldSilverIntro = require("src.ui.gen2.GoldSilverIntro")
|
|
|
|
return function(game)
|
|
local out = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/gold-menus"
|
|
|
|
local function shot(name)
|
|
U.wait(3)
|
|
U.shot(game, ("%s/%s.png"):format(out, name))
|
|
end
|
|
|
|
-- Capture a state on its own, then take it back off the stack.
|
|
local function show(name, state)
|
|
game.stack:push(state)
|
|
shot(name)
|
|
game.stack:pop()
|
|
end
|
|
|
|
U.wait(45)
|
|
assert(game.world and game.world.map, "gold world did not boot")
|
|
|
|
-- Give the save something to show: a party, a bag across pockets, badges,
|
|
-- dex progress, a phone number and an unlocked Pokegear.
|
|
local save = game.save
|
|
local pokemon = game.data.pokemon or {}
|
|
local function mon(species, level, hp)
|
|
local def = pokemon[species]
|
|
local maxHp = 20 + level
|
|
return {
|
|
species = species, name = def and def.name or species,
|
|
nickname = def and def.name or species,
|
|
level = level, hp = hp or maxHp, maxHp = maxHp,
|
|
}
|
|
end
|
|
save.party = {
|
|
mon("CYNDAQUIL", 12),
|
|
mon("TOTODILE", 10, 9),
|
|
mon("PIDGEY", 8, 2),
|
|
}
|
|
-- Bag ids are the CONSTANT names ItemAttributes is keyed by, not the printed
|
|
-- name: a TM's id is what it teaches (TM_DYNAMICPUNCH prints as "TM01"), so
|
|
-- seeding "TM01" here would leave the TM pocket empty and drop three unknown
|
|
-- rows into the ITEMS pocket instead. TOWN_MAP is not a bag item in Gold
|
|
-- either (the map is a Pokegear card, and item 6 is one of the unused
|
|
-- TERU-SAMA slots).
|
|
save.inventory = {
|
|
POTION = 5, SUPER_POTION = 2, ANTIDOTE = 1, FULL_HEAL = 1,
|
|
REVIVE = 1, ETHER = 2, X_ATTACK = 1, REPEL = 3,
|
|
POKE_BALL = 10, GREAT_BALL = 3, ULTRA_BALL = 1,
|
|
BICYCLE = 1, ITEMFINDER = 1, OLD_ROD = 1, COIN_CASE = 1,
|
|
SQUIRTBOTTLE = 1,
|
|
TM_DYNAMICPUNCH = 1, TM_HEADBUTT = 1, TM_ROCK_SMASH = 1,
|
|
HM_CUT = 1, HM_SURF = 1,
|
|
}
|
|
save.player.badges = { true, true }
|
|
save.player.money = 3210
|
|
save.player.id = 12345
|
|
save.player.name = "GOLD"
|
|
save.playTime = { hours = 4, minutes = 37, seconds = 0, frames = 0 }
|
|
-- The unlocks the way the game writes them: ENGINE_RADIO_CARD 0,
|
|
-- ENGINE_MAP_CARD 1, ENGINE_PHONE_CARD 2, ENGINE_POKEGEAR 4 and
|
|
-- ENGINE_POKEDEX 11 through the same store `setflag` lands in.
|
|
save.engineFlags = save.engineFlags or {}
|
|
for _, flag in ipairs({ 0, 1, 2, 4, 11 }) do
|
|
save.engineFlags[flag] = true
|
|
end
|
|
save.phoneContacts = { ELM = true, MOM = true }
|
|
for _, species in ipairs({ "CYNDAQUIL", "TOTODILE", "CHIKORITA", "PIDGEY",
|
|
"RATTATA", "SENTRET", "HOOTHOOT" }) do
|
|
save.pokedex.seen[species] = true
|
|
end
|
|
for _, species in ipairs({ "CYNDAQUIL", "TOTODILE", "PIDGEY" }) do
|
|
save.pokedex.caught[species] = true
|
|
end
|
|
|
|
-- The overworld itself, for reference.
|
|
shot("00-overworld")
|
|
|
|
-- Boot screens.
|
|
show("01-mainmenu-newgame", MainMenu.new(game, {
|
|
hasSave = false, save = false,
|
|
clock = { hour = 10, minute = 5, weekday = 3 },
|
|
}))
|
|
show("02-mainmenu-continue", MainMenu.new(game, {
|
|
hasSave = true, save = save,
|
|
clock = { hour = 20, minute = 42, weekday = 6 },
|
|
}))
|
|
|
|
local sprites = game.data.gen2Sprites
|
|
local chris = sprites and sprites.SPRITE_CHRIS
|
|
local Palettes = require("src.world.gen2.Palettes")
|
|
local naming = NamingScreen.new(game, {
|
|
type = "player",
|
|
menuGfx = game.data.gen2MenuGfx,
|
|
iconPath = chris and chris.image or nil,
|
|
iconColors = game.data.gen2Palettes
|
|
and Palettes.spritePalette(game.data.gen2Palettes, "DAY", chris) or nil,
|
|
})
|
|
naming.text = "GOL"
|
|
show("03-naming-upper", naming)
|
|
naming.lower = true
|
|
naming.row = 4
|
|
naming.col = 3
|
|
show("04-naming-lower-del", naming)
|
|
|
|
-- The movie is a state machine, so a still is "run it to frame N": one from
|
|
-- each act, picked where its cast is on screen.
|
|
local intro = GoldSilverIntro.new(game, {})
|
|
local function seek(target)
|
|
while intro.frames < target and not intro.done do intro:step() end
|
|
return intro
|
|
end
|
|
show("05-intro-water", seek(600))
|
|
show("06-intro-grass", seek(1500))
|
|
show("07-intro-fire", seek(2200))
|
|
|
|
-- In-game menus. The start menu is not opaque, so the overworld shows
|
|
-- through it -- which is exactly how it looks in play.
|
|
show("08-startmenu", StartMenu.new(game, { save = save }))
|
|
-- QUIT's confirmation, which is the port's own row rather than the cart's
|
|
-- EXIT: the yes/no defaults to NO so a stray A never throws away progress.
|
|
local quitting = StartMenu.new(game, { save = save })
|
|
quitting.phase = "confirm"
|
|
quitting.confirmChoice = 2
|
|
show("24-startmenu-quit", quitting)
|
|
show("09-party", PartyMenu.new(game, { prompt = "choose" }))
|
|
show("10-pack-items", PackMenu.new(game, { pocket = "ITEM" }))
|
|
show("11-pack-tms", PackMenu.new(game, { pocket = "TM_HM" }))
|
|
show("12-pokegear-clock", Pokegear.new(game, {
|
|
clock = { hour = 14, minute = 8, weekday = 2 },
|
|
currentLandmark = "LANDMARK_NEW_BARK_TOWN",
|
|
}))
|
|
|
|
local gear = Pokegear.new(game, {
|
|
currentLandmark = "LANDMARK_NEW_BARK_TOWN",
|
|
})
|
|
gear.cardIndex = 2
|
|
gear.mode = "card"
|
|
show("13-pokegear-map", gear)
|
|
-- The radio card, tuned and playing, and the phone mid-call.
|
|
local radio = Pokegear.new(game, {})
|
|
radio.mode = "card"
|
|
for i, card in ipairs(radio.cards) do
|
|
if card.id == "radio" then radio.cardIndex = i end
|
|
end
|
|
radio.station = 1
|
|
radio.radioLine = 2
|
|
show("13b-pokegear-radio", radio)
|
|
local phone = Pokegear.new(game, {})
|
|
phone.mode = "card"
|
|
for i, card in ipairs(phone.cards) do
|
|
if card.id == "phone" then phone.cardIndex = i end
|
|
end
|
|
phone:callContact((phone:phoneList() or {})[1])
|
|
show("13c-pokegear-phone", phone)
|
|
|
|
|
|
local card = TrainerCard.new(game, {})
|
|
show("14-trainercard", card)
|
|
card.page = 2
|
|
show("15-trainercard-badges", card)
|
|
|
|
show("16-pokedex", PokedexMenu.new(game, {}))
|
|
local dex = PokedexMenu.new(game, {})
|
|
dex.view = "entry"
|
|
for i, row in ipairs(dex.rows) do
|
|
if row.caught then dex.index = i break end
|
|
end
|
|
show("17-pokedex-entry", dex)
|
|
-- The two screens SELECT and START open (Pokedex_InitOptionScreen /
|
|
-- Pokedex_InitSearchScreen).
|
|
local dexOption = PokedexMenu.new(game, {})
|
|
dexOption.view = "option"
|
|
show("17b-pokedex-option", dexOption)
|
|
local dexSearch = PokedexMenu.new(game, {})
|
|
dexSearch.view = "search"
|
|
dexSearch.searchType = { 10, 0 } -- FIRE / -----
|
|
show("17c-pokedex-search", dexSearch)
|
|
|
|
show("18-options", OptionsMenu.new(game, { options = game.options }))
|
|
-- ...and scrolled to the port's own display rows, which is what the ▼ on
|
|
-- the first page points at.
|
|
local scrolled = OptionsMenu.new(game, { options = game.options })
|
|
scrolled.index = #OptionsMenu.ROWS
|
|
scrolled:ensureVisible()
|
|
show("25-options-display", scrolled)
|
|
-- writer is stubbed so the shot never touches a real save file.
|
|
show("19-save", SaveMenu.new(game, {
|
|
save = save, existed = false,
|
|
writer = function() return true end,
|
|
}))
|
|
|
|
-- The storage system: the PC's top menu, the box picker, and the withdraw
|
|
-- and deposit lists. Stock a box first so the list has rows and the left
|
|
-- panel has a pic to draw.
|
|
local Boxes = require("src.core.gen2.Boxes")
|
|
local stored = Boxes.box(save, 1)
|
|
for i, species in ipairs({ "GEODUDE", "ZUBAT", "RATTATA", "SENTRET" }) do
|
|
stored[i] = mon(species, 10 + i)
|
|
end
|
|
Boxes.rename(save, 2, "GRASS")
|
|
|
|
local pc = PcMenu.new(game, { save = save })
|
|
show("20-pc-menu", pc)
|
|
pc.picking = true
|
|
pc.pickIndex = 2
|
|
show("21-pc-changebox", pc)
|
|
|
|
show("22-pc-withdraw", BoxMenu.new(game, {
|
|
save = save, mode = "withdraw",
|
|
}))
|
|
show("23-pc-deposit", BoxMenu.new(game, {
|
|
save = save, mode = "deposit",
|
|
}))
|
|
|
|
-- The two clock screens NEW GAME and Mom open (timeset.asm InitClock and
|
|
-- SetDayOfWeek), each at its picker rather than at its opening page.
|
|
local clock = InitClock.new(game, { save = save })
|
|
clock.phase = "hour"
|
|
show("26-initclock-hour", clock)
|
|
clock.phase = "minute"
|
|
clock.minute = 25
|
|
show("27-initclock-minutes", clock)
|
|
clock.phase = "confirm-hour"
|
|
show("28-initclock-confirm", clock)
|
|
local wheel = InitClock.new(game, { mode = "day", save = save })
|
|
wheel.day = 2
|
|
show("29-dayofweek", wheel)
|
|
|
|
-- FLY's own picker (_FlyMap): the town map with the cursor on a visited
|
|
-- flypoint, not the yes/no chain the port used to fall back to.
|
|
local FieldMoves = require("src.world.gen2.FieldMoves")
|
|
save.engineFlags = save.engineFlags or {}
|
|
for _, row in ipairs(FieldMoves.FLYPOINTS) do
|
|
save.engineFlags[row.flag] = true
|
|
end
|
|
local points = FieldMoves.flyPoints(save, game.data.gen2Landmarks, "johto")
|
|
show("30-flymap", Pokegear.new(game, {
|
|
save = save,
|
|
currentLandmark = "LANDMARK_NEW_BARK_TOWN",
|
|
fly = points,
|
|
onFly = function() end,
|
|
onClose = function() end,
|
|
}))
|
|
|
|
print("[driver] PASS gold menu shots in " .. out)
|
|
end
|