Merge branch 'dev' into fix-battle-menu-cursor

This commit is contained in:
bryanthaboi
2026-07-31 08:14:08 -04:00
committed by GitHub
12 changed files with 1037 additions and 51 deletions
+4 -1
View File
@@ -62,7 +62,10 @@ function love.conf(t)
-- the game follow the device. The renderer letterboxes the 160x144 -- the game follow the device. The renderer letterboxes the 160x144
-- viewport into whatever size results, and the on-screen touch controls -- viewport into whatever size results, and the on-screen touch controls
-- re-lay themselves out from the new window size, so both orientations -- re-lay themselves out from the new window size, so both orientations
-- just work. iOS follows the Info.plist orientations -- just work. FULL_SENSOR ignores the device's rotation lock, so
-- GameActivity.setOrientationBis remaps it to FULL_USER after SDL has
-- run: same orientations allowed, but auto-rotate being off now wins.
-- iOS follows the Info.plist orientations
-- (see mobile/ios/overlays/love-ios.plist, now portrait + landscape). -- (see mobile/ios/overlays/love-ios.plist, now portrait + landscape).
t.window.resizable = true t.window.resizable = true
-- Starting size is a tall portrait hint; the OS resizes to the real -- Starting size is a tall portrait hint; the OS resizes to the real
+686
View File
@@ -0,0 +1,686 @@
-- Generated by tools/build_data.py. DO NOT EDIT.
-- Pokemon Yellow palettes (COLORS=OG YELLOW): SuperPalettes +
-- CGBBasePalettes (authentic GBC look) as 4 8-bit RGB colors
-- per palette (color 0 first), plus MonsterPalettes.
return {
cgbBase = {
["0F"] = {
{ 255, 255, 255 },
{ 107, 8, 255 },
{ 0, 74, 255 },
{ 8, 8, 8 },
},
BADGE = {
{ 255, 255, 255 },
{ 189, 66, 0 },
{ 140, 115, 90 },
{ 25, 25, 25 },
},
BLACK = {
{ 255, 255, 255 },
{ 25, 25, 25 },
{ 25, 25, 25 },
{ 25, 25, 25 },
},
BLUEMON = {
{ 255, 255, 255 },
{ 132, 148, 255 },
{ 0, 8, 206 },
{ 25, 25, 25 },
},
BROWNMON = {
{ 255, 255, 255 },
{ 239, 148, 82 },
{ 140, 74, 41 },
{ 25, 25, 25 },
},
CAVE = {
{ 255, 255, 255 },
{ 189, 66, 0 },
{ 140, 115, 90 },
{ 25, 25, 25 },
},
CELADON = {
{ 255, 255, 255 },
{ 41, 255, 41 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
CERULEAN = {
{ 255, 255, 255 },
{ 41, 66, 255 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
CINNABAR = {
{ 255, 255, 255 },
{ 255, 66, 66 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
CYANMON = {
{ 255, 255, 255 },
{ 132, 214, 255 },
{ 0, 140, 255 },
{ 25, 25, 25 },
},
FUCHSIA = {
{ 255, 255, 255 },
{ 255, 123, 123 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
GAMEFREAK = {
{ 255, 255, 255 },
{ 255, 156, 0 },
{ 156, 156, 0 },
{ 25, 25, 25 },
},
GRAYMON = {
{ 255, 255, 255 },
{ 165, 189, 82 },
{ 90, 90, 41 },
{ 25, 25, 25 },
},
GREENBAR = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 0, 255, 0 },
{ 25, 25, 25 },
},
GREENMON = {
{ 255, 255, 255 },
{ 140, 255, 90 },
{ 8, 181, 49 },
{ 25, 25, 25 },
},
INDIGO = {
{ 255, 255, 255 },
{ 90, 66, 255 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
LAVENDER = {
{ 255, 255, 255 },
{ 206, 33, 255 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
LOGO1 = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 255, 0, 0 },
{ 255, 0, 0 },
},
LOGO2 = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 58, 58, 206 },
{ 0, 0, 140 },
},
MEWMON = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 255, 8, 8 },
{ 25, 25, 25 },
},
PALLET = {
{ 255, 255, 255 },
{ 189, 140, 255 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
PEWTER = {
{ 255, 255, 255 },
{ 148, 148, 123 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
PIKACHUS_BEACH = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
PIKACHUS_BEACH_TITLE = {
{ 255, 255, 255 },
{ 74, 74, 74 },
{ 255, 173, 0 },
{ 25, 25, 25 },
},
PIKACHU_PORTRAIT = {
{ 255, 255, 255 },
{ 255, 148, 0 },
{ 156, 58, 8 },
{ 25, 25, 25 },
},
PINKMON = {
{ 255, 255, 255 },
{ 255, 123, 148 },
{ 255, 0, 49 },
{ 25, 25, 25 },
},
PURPLEMON = {
{ 255, 255, 255 },
{ 206, 123, 255 },
{ 156, 0, 181 },
{ 25, 25, 25 },
},
REDBAR = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 255, 0, 0 },
{ 25, 25, 25 },
},
REDMON = {
{ 255, 255, 255 },
{ 255, 140, 0 },
{ 255, 0, 0 },
{ 25, 25, 25 },
},
ROUTE = {
{ 255, 255, 255 },
{ 132, 255, 33 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
SAFFRON = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
SLOTS1 = {
{ 255, 255, 255 },
{ 206, 8, 255 },
{ 255, 0, 0 },
{ 25, 25, 25 },
},
SLOTS2 = {
{ 255, 255, 255 },
{ 255, 33, 156 },
{ 255, 255, 0 },
{ 25, 25, 25 },
},
SLOTS3 = {
{ 255, 255, 255 },
{ 66, 255, 0 },
{ 255, 255, 0 },
{ 25, 25, 25 },
},
SLOTS4 = {
{ 255, 255, 255 },
{ 0, 255, 255 },
{ 255, 255, 0 },
{ 25, 25, 25 },
},
TOWNMAP = {
{ 255, 255, 255 },
{ 0, 173, 255 },
{ 82, 230, 0 },
{ 8, 8, 8 },
},
VERMILION = {
{ 255, 255, 255 },
{ 255, 156, 0 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
VIRIDIAN = {
{ 255, 255, 255 },
{ 156, 255, 0 },
{ 90, 189, 255 },
{ 25, 25, 25 },
},
YELLOWBAR = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 255, 148, 0 },
{ 25, 25, 25 },
},
YELLOWMON = {
{ 255, 255, 255 },
{ 255, 255, 0 },
{ 230, 115, 0 },
{ 25, 25, 25 },
},
},
order = {
"ROUTE",
"PALLET",
"VIRIDIAN",
"PEWTER",
"CERULEAN",
"LAVENDER",
"VERMILION",
"CELADON",
"FUCHSIA",
"CINNABAR",
"INDIGO",
"SAFFRON",
"TOWNMAP",
"LOGO1",
"LOGO2",
"0F",
"MEWMON",
"BLUEMON",
"REDMON",
"CYANMON",
"PURPLEMON",
"BROWNMON",
"GREENMON",
"PINKMON",
"YELLOWMON",
"GRAYMON",
"SLOTS1",
"SLOTS2",
"SLOTS3",
"SLOTS4",
"BLACK",
"GREENBAR",
"YELLOWBAR",
"REDBAR",
"BADGE",
"CAVE",
"GAMEFREAK",
"PIKACHUS_BEACH",
"PIKACHU_PORTRAIT",
"PIKACHUS_BEACH_TITLE",
},
palettes = {
["0F"] = {
{ 255, 255, 247 },
{ 197, 165, 247 },
{ 90, 165, 247 },
{ 25, 16, 16 },
},
BADGE = {
{ 255, 255, 247 },
{ 165, 123, 90 },
{ 181, 173, 165 },
{ 49, 49, 49 },
},
BLACK = {
{ 255, 255, 247 },
{ 49, 49, 49 },
{ 49, 49, 49 },
{ 49, 49, 49 },
},
BLUEMON = {
{ 255, 255, 247 },
{ 173, 181, 255 },
{ 74, 82, 165 },
{ 49, 49, 49 },
},
BROWNMON = {
{ 255, 255, 247 },
{ 214, 189, 148 },
{ 148, 115, 82 },
{ 49, 49, 49 },
},
CAVE = {
{ 255, 255, 247 },
{ 165, 123, 90 },
{ 181, 173, 165 },
{ 49, 49, 49 },
},
CELADON = {
{ 255, 255, 247 },
{ 181, 255, 181 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
CERULEAN = {
{ 255, 255, 247 },
{ 181, 189, 255 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
CINNABAR = {
{ 255, 255, 247 },
{ 255, 123, 115 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
CYANMON = {
{ 255, 255, 247 },
{ 214, 230, 255 },
{ 58, 197, 230 },
{ 49, 49, 49 },
},
FUCHSIA = {
{ 255, 255, 247 },
{ 255, 214, 214 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
GAMEFREAK = {
{ 255, 255, 247 },
{ 230, 197, 115 },
{ 165, 165, 90 },
{ 49, 49, 49 },
},
GRAYMON = {
{ 255, 255, 247 },
{ 206, 206, 148 },
{ 132, 132, 115 },
{ 49, 49, 49 },
},
GREENBAR = {
{ 255, 255, 247 },
{ 255, 255, 156 },
{ 0, 173, 0 },
{ 49, 49, 49 },
},
GREENMON = {
{ 255, 255, 247 },
{ 197, 230, 148 },
{ 107, 173, 123 },
{ 49, 49, 49 },
},
INDIGO = {
{ 255, 255, 247 },
{ 140, 140, 206 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
LAVENDER = {
{ 255, 255, 247 },
{ 222, 189, 239 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
LOGO1 = {
{ 255, 255, 247 },
{ 247, 247, 140 },
{ 173, 0, 33 },
{ 173, 0, 33 },
},
LOGO2 = {
{ 255, 255, 247 },
{ 247, 247, 140 },
{ 148, 148, 197 },
{ 58, 58, 132 },
},
MEWMON = {
{ 255, 255, 247 },
{ 255, 247, 181 },
{ 222, 132, 132 },
{ 49, 49, 49 },
},
PALLET = {
{ 255, 255, 247 },
{ 230, 222, 255 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
PEWTER = {
{ 255, 255, 247 },
{ 189, 189, 181 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
PIKACHUS_BEACH = {
{ 255, 255, 247 },
{ 255, 247, 181 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
PIKACHUS_BEACH_TITLE = {
{ 255, 255, 247 },
{ 132, 132, 132 },
{ 255, 206, 74 },
{ 49, 49, 49 },
},
PIKACHU_PORTRAIT = {
{ 255, 255, 247 },
{ 230, 189, 74 },
{ 148, 115, 82 },
{ 49, 49, 49 },
},
PINKMON = {
{ 255, 255, 247 },
{ 255, 197, 214 },
{ 255, 148, 173 },
{ 49, 49, 49 },
},
PURPLEMON = {
{ 255, 255, 247 },
{ 222, 181, 247 },
{ 181, 123, 189 },
{ 49, 49, 49 },
},
REDBAR = {
{ 255, 255, 247 },
{ 255, 255, 156 },
{ 214, 74, 49 },
{ 49, 49, 49 },
},
REDMON = {
{ 255, 255, 247 },
{ 255, 197, 90 },
{ 214, 74, 49 },
{ 49, 49, 49 },
},
ROUTE = {
{ 255, 255, 247 },
{ 189, 214, 156 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
SAFFRON = {
{ 255, 255, 247 },
{ 255, 255, 156 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
SLOTS1 = {
{ 255, 255, 247 },
{ 222, 181, 247 },
{ 214, 74, 49 },
{ 49, 49, 49 },
},
SLOTS2 = {
{ 255, 255, 247 },
{ 255, 189, 214 },
{ 239, 239, 66 },
{ 49, 49, 49 },
},
SLOTS3 = {
{ 255, 255, 247 },
{ 189, 255, 165 },
{ 239, 239, 66 },
{ 49, 49, 49 },
},
SLOTS4 = {
{ 255, 255, 247 },
{ 189, 239, 255 },
{ 239, 239, 66 },
{ 49, 49, 49 },
},
TOWNMAP = {
{ 255, 255, 247 },
{ 165, 214, 255 },
{ 140, 189, 82 },
{ 25, 16, 16 },
},
VERMILION = {
{ 255, 255, 247 },
{ 255, 206, 132 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
VIRIDIAN = {
{ 255, 255, 247 },
{ 214, 255, 173 },
{ 189, 222, 255 },
{ 49, 49, 49 },
},
YELLOWBAR = {
{ 255, 255, 247 },
{ 255, 255, 156 },
{ 230, 189, 74 },
{ 49, 49, 49 },
},
YELLOWMON = {
{ 255, 255, 247 },
{ 255, 255, 156 },
{ 230, 189, 74 },
{ 49, 49, 49 },
},
},
pokemon = {
ABRA = "YELLOWMON",
AERODACTYL = "GRAYMON",
ALAKAZAM = "YELLOWMON",
ARBOK = "PURPLEMON",
ARCANINE = "REDMON",
ARTICUNO = "BLUEMON",
BEEDRILL = "YELLOWMON",
BELLSPROUT = "GREENMON",
BLASTOISE = "CYANMON",
BULBASAUR = "GREENMON",
BUTTERFREE = "CYANMON",
CATERPIE = "GREENMON",
CHANSEY = "PINKMON",
CHARIZARD = "REDMON",
CHARMANDER = "REDMON",
CHARMELEON = "REDMON",
CLEFABLE = "PINKMON",
CLEFAIRY = "PINKMON",
CLOYSTER = "GRAYMON",
CUBONE = "GRAYMON",
DEWGONG = "BLUEMON",
DIGLETT = "BROWNMON",
DITTO = "GRAYMON",
DODRIO = "BROWNMON",
DODUO = "BROWNMON",
DRAGONAIR = "BLUEMON",
DRAGONITE = "BROWNMON",
DRATINI = "GRAYMON",
DROWZEE = "YELLOWMON",
DUGTRIO = "BROWNMON",
EEVEE = "GRAYMON",
EKANS = "PURPLEMON",
ELECTABUZZ = "YELLOWMON",
ELECTRODE = "YELLOWMON",
EXEGGCUTE = "PINKMON",
EXEGGUTOR = "GREENMON",
FARFETCHD = "BROWNMON",
FEAROW = "BROWNMON",
FLAREON = "REDMON",
GASTLY = "PURPLEMON",
GENGAR = "PURPLEMON",
GEODUDE = "GRAYMON",
GLOOM = "REDMON",
GOLBAT = "BLUEMON",
GOLDEEN = "REDMON",
GOLDUCK = "CYANMON",
GOLEM = "GRAYMON",
GRAVELER = "GRAYMON",
GRIMER = "PURPLEMON",
GROWLITHE = "BROWNMON",
GYARADOS = "BLUEMON",
HAUNTER = "PURPLEMON",
HITMONCHAN = "BROWNMON",
HITMONLEE = "BROWNMON",
HORSEA = "CYANMON",
HYPNO = "YELLOWMON",
IVYSAUR = "GREENMON",
JIGGLYPUFF = "PINKMON",
JOLTEON = "YELLOWMON",
JYNX = "MEWMON",
KABUTO = "BROWNMON",
KABUTOPS = "BROWNMON",
KADABRA = "YELLOWMON",
KAKUNA = "YELLOWMON",
KANGASKHAN = "BROWNMON",
KINGLER = "REDMON",
KOFFING = "PURPLEMON",
KRABBY = "REDMON",
LAPRAS = "CYANMON",
LICKITUNG = "PINKMON",
MACHAMP = "GRAYMON",
MACHOKE = "GRAYMON",
MACHOP = "GRAYMON",
MAGIKARP = "REDMON",
MAGMAR = "REDMON",
MAGNEMITE = "GRAYMON",
MAGNETON = "GRAYMON",
MANKEY = "BROWNMON",
MAROWAK = "GRAYMON",
MEOWTH = "YELLOWMON",
METAPOD = "GREENMON",
MEW = "MEWMON",
MEWTWO = "MEWMON",
MOLTRES = "REDMON",
MR_MIME = "PINKMON",
MUK = "PURPLEMON",
NIDOKING = "PURPLEMON",
NIDOQUEEN = "BLUEMON",
NIDORAN_F = "BLUEMON",
NIDORAN_M = "PURPLEMON",
NIDORINA = "BLUEMON",
NIDORINO = "PURPLEMON",
NINETALES = "YELLOWMON",
ODDISH = "GREENMON",
OMANYTE = "BLUEMON",
OMASTAR = "BLUEMON",
ONIX = "GRAYMON",
PARAS = "REDMON",
PARASECT = "REDMON",
PERSIAN = "YELLOWMON",
PIDGEOT = "BROWNMON",
PIDGEOTTO = "BROWNMON",
PIDGEY = "BROWNMON",
PIKACHU = "YELLOWMON",
PINSIR = "BROWNMON",
POLIWAG = "BLUEMON",
POLIWHIRL = "BLUEMON",
POLIWRATH = "BLUEMON",
PONYTA = "REDMON",
PORYGON = "GRAYMON",
PRIMEAPE = "BROWNMON",
PSYDUCK = "YELLOWMON",
RAICHU = "YELLOWMON",
RAPIDASH = "REDMON",
RATICATE = "GRAYMON",
RATTATA = "GRAYMON",
RHYDON = "GRAYMON",
RHYHORN = "GRAYMON",
SANDSHREW = "BROWNMON",
SANDSLASH = "BROWNMON",
SCYTHER = "GREENMON",
SEADRA = "CYANMON",
SEAKING = "REDMON",
SEEL = "BLUEMON",
SHELLDER = "GRAYMON",
SLOWBRO = "PINKMON",
SLOWPOKE = "PINKMON",
SNORLAX = "PINKMON",
SPEAROW = "BROWNMON",
SQUIRTLE = "CYANMON",
STARMIE = "GRAYMON",
STARYU = "REDMON",
TANGELA = "BLUEMON",
TAUROS = "GRAYMON",
TENTACOOL = "CYANMON",
TENTACRUEL = "CYANMON",
VAPOREON = "CYANMON",
VENOMOTH = "PURPLEMON",
VENONAT = "PURPLEMON",
VENUSAUR = "GREENMON",
VICTREEBEL = "GREENMON",
VILEPLUME = "REDMON",
VOLTORB = "YELLOWMON",
VULPIX = "REDMON",
WARTORTLE = "CYANMON",
WEEDLE = "YELLOWMON",
WEEPINBELL = "GREENMON",
WEEZING = "PURPLEMON",
WIGGLYTUFF = "PINKMON",
ZAPDOS = "YELLOWMON",
ZUBAT = "BLUEMON",
},
source = "pokeyellow data/sgb/sgb_palettes.asm + data/pokemon/palettes.asm",
}
@@ -42,6 +42,7 @@ local function middleAgedMan(game, ow, npc, done)
end end
local menu = ListMenu.new(game, "", items, { local menu = ListMenu.new(game, "", items, {
onChoose = function(item) onChoose = function(item)
game.stack:pop()
push(game, t[BADGE_TEXT[item.value]], loop) push(game, t[BADGE_TEXT[item.value]], loop)
end, end,
onCancel = function() onCancel = function()
+12 -6
View File
@@ -86,14 +86,20 @@ Game Boy equivalent:
## Colors mode ## Colors mode
The `2` key (and the Options menu COLORS row) cycles the display mode The `2` key (and the Options menu COLORS row) cycles the display mode
through **OG RED → SGB → ADVANCED → OG → OG INV → SGB INV → CLASSIC → OG RED**. through **OG RED → SGB → ADVANCED → OG → OG INV → SGB INV → CLASSIC → OG RED**
(on Blue the first slot labels **OG BLUE**; on Yellow, **OG YELLOW**).
The first three are the real colorizations; the rest are DMG-shade novelties: The first three are the real colorizations; the rest are DMG-shade novelties:
- **OG RED**: the Game Boy Color boot-ROM look for Pokemon Red -- one global - **OG RED** / **OG BLUE**: the Game Boy Color boot-ROM look for that cart --
red BG palette + one green OBJ palette, every map, no per-map variation one global BG palette + one OBJ palette, every map, no per-map variation
(Pokemon Red has no CGB code, so on a GBC the boot ROM colors it globally). (Red/Blue ship no CGB code, so on a GBC the boot ROM colors them globally).
The player/NPCs stay green over the red terrain via the OBP bake + The player/NPCs keep the boot-ROM OBJ color over the terrain via the OBP
post-zone redraw (`PaletteFX.GBC_BG` / `GBC_OBJ`). bake + post-zone redraw (`PaletteFX.GBC_BG` / `GBC_OBJ`, or Blue's blue/pink
pair).
- **OG YELLOW** (Yellow playthrough, same `ogred` save id): Pokemon Yellow's
authentic GBC look from `CGBBasePalettes` (`data/palettes_yellow.lua`,
sourced from pret/pokeyellow). Per-map / per-species colors, not a single
boot-ROM ramp -- Yellow was CGB-enhanced.
- **SGB** (default): the per-map Super Game Boy region palettes - **SGB** (default): the per-map Super Game Boy region palettes
(`data/sgb/sgb_palettes.asm`). Sprites tint with the region palette, as on (`data/sgb/sgb_palettes.asm`). Sprites tint with the region palette, as on
real SGB. (This is the mode formerly mislabeled "GBC".) real SGB. (This is the mode formerly mislabeled "GBC".)
+1 -1
View File
@@ -82,7 +82,7 @@ scripts, tests, and mobile build sources are excluded.
| --- | --- | | --- | --- |
| `app.application_id` | `com.theboisclub.pokemonred` | | `app.application_id` | `com.theboisclub.pokemonred` |
| `app.name` | Pokemon Red | | `app.name` | Pokemon Red |
| `app.orientation` | `portrait` | | `app.orientation` | `fullUser`. This is only the manifest default: SDL requests FULL_SENSOR at window creation (resizable window, no `SDL_HINT_ORIENTATIONS`), and `GameActivity.setOrientationBis` remaps that to FULL_USER so the device's rotation lock is honoured. |
| `app.version_name` / `app.version_code` | set from `--version X.Y.Z` (code = major*10000 + minor*100 + patch); left as-is if `--version` is omitted | | `app.version_name` / `app.version_code` | set from `--version X.Y.Z` (code = major*10000 + minor*100 + patch); left as-is if `--version` is omitted |
| Permissions | INTERNET / RECORD_AUDIO / WRITE_EXTERNAL_STORAGE stripped; VIBRATE + BLUETOOTH kept | | Permissions | INTERNET / RECORD_AUDIO / WRITE_EXTERNAL_STORAGE stripped; VIBRATE + BLUETOOTH kept |
@@ -41,6 +41,7 @@ import android.app.AlertDialog;
import android.content.Context; import android.content.Context;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.pm.ActivityInfo;
import android.content.pm.ApplicationInfo; import android.content.pm.ApplicationInfo;
import android.content.res.AssetManager; import android.content.res.AssetManager;
import android.media.AudioManager; import android.media.AudioManager;
@@ -303,6 +304,54 @@ public class GameActivity extends SDLActivity {
super.onResume(); super.onResume();
} }
/**
* SDL decides the activity's requested orientation at window creation
* (SDLActivity.setOrientationBis). With a resizable window and no
* SDL_HINT_ORIENTATIONS -- exactly what conf.lua produces on Android --
* it asks for SCREEN_ORIENTATION_FULL_SENSOR, and that request overrides
* the android:screenOrientation="fullUser" set in the manifest. The
* *_SENSOR constants follow the accelerometer even when the player has
* turned auto-rotate off, so the game kept rotating on a device whose
* rotation was locked.
*
* Remap SDL's choice onto the matching *_USER constant, which allows the
* same orientations but defers to the system rotation setting. Applied
* after super so SDL keeps deciding *which* orientations the window may
* take; this only changes who breaks the tie, the sensor or the player.
*/
@Override
public void setOrientationBis(int w, int h, boolean resizable, String hint) {
super.setOrientationBis(w, h, resizable, hint);
// The *_USER constants only exist from API 18; below that the sensor
// ones are all there is, so leave SDL's request alone.
if (android.os.Build.VERSION.SDK_INT < 18) {
return;
}
int requested = getRequestedOrientation();
int userRequested;
switch (requested) {
case ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR:
userRequested = ActivityInfo.SCREEN_ORIENTATION_FULL_USER;
break;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE:
userRequested = ActivityInfo.SCREEN_ORIENTATION_USER_LANDSCAPE;
break;
case ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT:
userRequested = ActivityInfo.SCREEN_ORIENTATION_USER_PORTRAIT;
break;
default:
// SENSOR / plain LANDSCAPE / PORTRAIT etc: either already
// explicit or never produced by setOrientationBis.
return;
}
Log.d("GameActivity", "requestedOrientation " + requested + " -> " + userRequested
+ " (honour the device rotation lock)");
setRequestedOrientation(userRequested);
}
@Keep @Keep
public void setImmersiveMode(boolean immersive_mode) { public void setImmersiveMode(boolean immersive_mode) {
if (android.os.Build.VERSION.SDK_INT >= 28) { if (android.os.Build.VERSION.SDK_INT >= 28) {
+3 -1
View File
@@ -4491,7 +4491,9 @@ function BattleState:sgbBattlePals()
-- true white, which is what drew a white box around each pic on the pink -- true white, which is what drew a white box around each pic on the pink
-- field. Snap every zone's color 0/3 to the global GBC white/black; the -- field. Snap every zone's color 0/3 to the global GBC white/black; the
-- mid shades (and the green bar the user prefers) stay untouched. -- mid shades (and the green bar the user prefers) stay untouched.
if PaletteFX.mode == "ogred" then -- Boot-ROM OG only (Red/Blue): snap zone paper to the global GBC white/black.
-- OG YELLOW keeps each CGBBasePalettes endpoint (already near-white / near-black).
if PaletteFX.mode == "ogred" and not require("src.core.GameVersion").isYellow() then
local white, black = PaletteFX.GBC_BG[1], PaletteFX.GBC_BG[4] local white, black = PaletteFX.GBC_BG[1], PaletteFX.GBC_BG[4]
for i = 0, 3 do for i = 0, 3 do
local c = out[i] local c = out[i]
+97 -39
View File
@@ -5,11 +5,13 @@
-- then drawn once per zone through a shader that remaps the four DMG -- then drawn once per zone through a shader that remaps the four DMG
-- shades to that zone's palette. -- shades to that zone's palette.
-- --
-- Port display option: COLORS (OG RED / SGB / ADVANCED / OG / OG INV / -- Port display option: COLORS (OG RED / OG BLUE / OG YELLOW / SGB /
-- SGB INV / CLASSIC) transforms every zone's palette at send time via -- ADVANCED / OG / OG INV / SGB INV / CLASSIC) transforms every zone's
-- effectiveColors. ADVANCED (the `redpp` id below) swaps the named-palette -- palette at send time via effectiveColors. ADVANCED (the `redpp` id
-- pack for pokered-gbc SuperPalettes (data/palettes_gbc.lua), including -- below) swaps the named-palette pack for pokered-gbc SuperPalettes
-- per-species mon colors. -- (data/palettes_gbc.lua), including per-species mon colors. OG YELLOW
-- (Yellow playthrough + `ogred` id) uses pokeyellow CGBBasePalettes
-- (data/palettes_yellow.lua / ROM cgbBase).
local GameVersion = require("src.core.GameVersion") local GameVersion = require("src.core.GameVersion")
@@ -17,10 +19,11 @@ local PaletteFX = {}
local shader -- false = unavailable (headless / no shader support) local shader -- false = unavailable (headless / no shader support)
local gbcPack -- false = missing; nil = not loaded yet local gbcPack -- false = missing; nil = not loaded yet
local yellowPack -- false = missing; nil = not loaded yet
-- Cycle order matches OptionsMenu / hotkey 2. The three real colorizations -- Cycle order matches OptionsMenu / hotkey 2. The three real colorizations
-- come first (OG RED = GBC hardware, SGB = per-map Super Game Boy, ADVANCED = -- come first (OG RED/BLUE/YELLOW = GBC hardware, SGB = per-map Super Game Boy,
-- pokered-gbc per-tile), then the DMG-shade novelty modes. -- ADVANCED = pokered-gbc per-tile), then the DMG-shade novelty modes.
PaletteFX.MODES = { "ogred", "gbc", "redpp", "og", "og_inv", "gbc_inv", "classic" } PaletteFX.MODES = { "ogred", "gbc", "redpp", "og", "og_inv", "gbc_inv", "classic" }
-- `gbc`/`gbc_inv`/`redpp` keep their save-value ids for back-compat while -- `gbc`/`gbc_inv`/`redpp` keep their save-value ids for back-compat while
-- their LABELS say what the mode actually is: "SGB"/"SGB INV" because the old -- their LABELS say what the mode actually is: "SGB"/"SGB INV" because the old
@@ -117,14 +120,19 @@ PaletteFX.GBC_OBJ_BLUE = {
} }
-- The active game's OG boot-ROM background palette: blue for a Blue -- The active game's OG boot-ROM background palette: blue for a Blue
-- playthrough, red otherwise. White (index 1) and black (index 4) are -- playthrough, red for Red. White (index 1) and black (index 4) are
-- identical across versions, so callers that only touch the endpoints -- identical across Red/Blue, so callers that only touch the endpoints
-- (e.g. BattleState's zone white/black snap) need no version branch. -- (e.g. BattleState's zone white/black snap) need no version branch there.
-- Yellow is CGB-enhanced (pokeyellow CGBBasePalettes) and has no extracted -- Yellow is CGB-enhanced (pokeyellow CGBBasePalettes): named zones go through
-- boot-ROM auto-palette here; keep the Red ramp -- never Blue's GBC_BG_BLUE. -- pal() / usesYellowCgb(), and ogBg() falls back to CGBBase PAL_ROUTE for any
-- remaining whole-screen callers -- never Blue's GBC_BG_BLUE.
function PaletteFX.ogBg() function PaletteFX.ogBg()
if GameVersion.isBlue() then return PaletteFX.GBC_BG_BLUE end if GameVersion.isBlue() then return PaletteFX.GBC_BG_BLUE end
if GameVersion.isYellow() then return PaletteFX.GBC_BG end if GameVersion.isYellow() then
local y = PaletteFX.yellowPack()
local route = y and y.cgbBase and y.cgbBase.ROUTE
if route then return route end
end
return PaletteFX.GBC_BG return PaletteFX.GBC_BG
end end
@@ -133,14 +141,12 @@ end
-- version-distinct cache-group string, because SpriteRenderer.getObpImage keys -- version-distinct cache-group string, because SpriteRenderer.getObpImage keys
-- its baked-image cache by (image path, group): a shared group would collide a -- its baked-image cache by (image path, group): a shared group would collide a
-- Red bake with a Blue one and one version would show the other's colors. -- Red bake with a Blue one and one version would show the other's colors.
-- Yellow: same Red OBJ green as above until Yellow-specific tables land. -- Yellow OG does not bake a boot-ROM OBJ (usesSpriteObp is false); this path
-- is unused there and kept as Red green only as a safe leftover.
function PaletteFX.ogObj() function PaletteFX.ogObj()
if GameVersion.isBlue() then if GameVersion.isBlue() then
return PaletteFX.darkObp(PaletteFX.GBC_OBJ_BLUE, "gbcobj_blue") return PaletteFX.darkObp(PaletteFX.GBC_OBJ_BLUE, "gbcobj_blue")
end end
if GameVersion.isYellow() then
return PaletteFX.darkObp(PaletteFX.GBC_OBJ, "gbcobj")
end
return PaletteFX.darkObp(PaletteFX.GBC_OBJ, "gbcobj") return PaletteFX.darkObp(PaletteFX.GBC_OBJ, "gbcobj")
end end
@@ -271,13 +277,31 @@ function PaletteFX.gbcPack()
return gbcPack or nil return gbcPack or nil
end end
-- pokeyellow SuperPalettes + CGBBasePalettes (committed; not wiped by import).
-- ROM import may also stamp palettes.cgbBase; yellowCgbNamedPal prefers that.
function PaletteFX.yellowPack()
if yellowPack == nil then
local ok, pack = pcall(require, "data.palettes_yellow")
yellowPack = ok and pack or false
end
return yellowPack or nil
end
function PaletteFX.usesGbcPack(mode) function PaletteFX.usesGbcPack(mode)
mode = mode or PaletteFX.mode mode = mode or PaletteFX.mode
return mode == "redpp" return mode == "redpp"
end end
-- Yellow's authentic GBC look is CGBBasePalettes (per-map), not a boot-ROM
-- auto-palette. The shared `ogred` save id wears that table on a Yellow
-- playthrough and labels itself "OG YELLOW".
function PaletteFX.usesYellowCgb(mode)
mode = mode or PaletteFX.mode
return GameVersion.isYellow() and mode == "ogred"
end
-- Whether the active mode bakes a per-OBJ palette onto overworld sprites -- Whether the active mode bakes a per-OBJ palette onto overworld sprites
-- (the OBP bake + post-zone redraw path). OG RED alone does: the Game Boy -- (the OBP bake + post-zone redraw path). OG RED / OG BLUE do: the Game Boy
-- Color boot ROM hands the game one global object palette (PaletteFX.ogObj -- -- Color boot ROM hands the game one global object palette (PaletteFX.ogObj --
-- green over Red's red background, pink over Blue's blue background), so on -- green over Red's red background, pink over Blue's blue background), so on
-- that machine the player and NPCs carry a fixed object color instead of -- that machine the player and NPCs carry a fixed object color instead of
@@ -288,6 +312,10 @@ end
-- regression. Terrain is unaffected -- pal() below still hands SGB its per-map -- regression. Terrain is unaffected -- pal() below still hands SGB its per-map
-- BG palette; only OG RED short-circuits BG to the one global red palette. -- BG palette; only OG RED short-circuits BG to the one global red palette.
-- --
-- OG YELLOW does NOT. Yellow ships CGB code and colors regions via
-- CGBBasePalettes the same way SGB mode colors SuperPalettes, so sprites tint
-- with the map zone (usesSpriteObp stays off).
--
-- SGB does NOT. The Super Game Boy colorizes the composited DMG picture it is -- SGB does NOT. The Super Game Boy colorizes the composited DMG picture it is
-- handed and cannot tell an OBJ pixel from a BG one; pokered never sends the -- handed and cannot tell an OBJ pixel from a BG one; pokered never sends the
-- OBJ_TRN packet that would enable SGB sprite mode (data/sgb/sgb_packets.asm -- OBJ_TRN packet that would enable SGB sprite mode (data/sgb/sgb_packets.asm
@@ -306,7 +334,7 @@ end
-- usesGbcPack() path in SpriteRenderer instead. -- usesGbcPack() path in SpriteRenderer instead.
function PaletteFX.usesSpriteObp(mode) function PaletteFX.usesSpriteObp(mode)
mode = mode or PaletteFX.mode mode = mode or PaletteFX.mode
return mode == "ogred" return mode == "ogred" and not GameVersion.isYellow()
end end
-- ------- post-zone sprite redraw (OG RED) -- ------- post-zone sprite redraw (OG RED)
@@ -363,8 +391,8 @@ end
-- read these from the ROM-imported table or the title ribbon stays red -- read these from the ROM-imported table or the title ribbon stays red
-- and the Game Corner reels keep Red's pink (issue #128). -- and the Game Corner reels keep Red's pink (issue #128).
-- Yellow is intentionally NOT in BLUE_VERSIONED: skip Blue LOGO1/SLOTS* -- Yellow is intentionally NOT in BLUE_VERSIONED: skip Blue LOGO1/SLOTS*
-- recolors. When CGBBasePalettes were imported (palettes.cgbBase), Yellow -- recolors. OG YELLOW (usesYellowCgb) reads CGBBasePalettes; SGB mode on
-- prefers those over SGB SuperPalettes for named zones. -- Yellow keeps SuperPalettes.
local BLUE_VERSIONED = { local BLUE_VERSIONED = {
LOGO1 = true, SLOTS2 = true, SLOTS3 = true, SLOTS4 = true, LOGO1 = true, SLOTS2 = true, SLOTS3 = true, SLOTS4 = true,
} }
@@ -376,31 +404,42 @@ end
local function yellowCgbNamedPal(data, name) local function yellowCgbNamedPal(data, name)
local p = data and data.palettes local p = data and data.palettes
return p and p.cgbBase and p.cgbBase[name] local fromRom = p and p.cgbBase and p.cgbBase[name]
if fromRom then return fromRom end
local y = PaletteFX.yellowPack()
return y and y.cgbBase and y.cgbBase[name] or nil
end end
-- named palette from the active pack (nil on stale builds / missing name). -- named palette from the active pack (nil on stale builds / missing name).
-- RED++ falls back to the ROM pack for names the gbc table omits (rare). -- RED++ falls back to the ROM pack for names the gbc table omits (rare).
-- OG RED short-circuits EVERY name to the one global GBC boot-ROM BG palette -- OG RED / OG BLUE short-circuit EVERY name to the one global GBC boot-ROM
-- (the hardware had a single BGP for the whole game), so terrain zones, -- BG palette (the hardware had a single BGP for the whole game), so terrain
-- battle HP bars / text, and menu boxes all come out red -- everything a -- zones, battle HP bars / text, and menu boxes all come out red/blue --
-- background tile drew. Objects do not come through here (they bake -- everything a background tile drew. Objects do not come through here
-- GBC_OBJ green), so this stays a BG-only hook. -- (they bake GBC_OBJ), so this stays a BG-only hook.
-- OG YELLOW instead resolves each name through CGBBasePalettes.
function PaletteFX.pal(data, name) function PaletteFX.pal(data, name)
if PaletteFX.mode == "ogred" then return PaletteFX.ogBg() end if PaletteFX.mode == "ogred" and not GameVersion.isYellow() then
return PaletteFX.ogBg()
end
-- Blue-only ROM override for versioned SuperPals. Yellow (isYellow) and -- Blue-only ROM override for versioned SuperPals. Yellow (isYellow) and
-- Red keep the active pack / Red-like path -- do not apply Blue recolors. -- Red keep the active pack / Red-like path -- do not apply Blue recolors.
if GameVersion.isBlue() and BLUE_VERSIONED[name] then if GameVersion.isBlue() and BLUE_VERSIONED[name] then
local fromRom = romNamedPal(data, name) local fromRom = romNamedPal(data, name)
if fromRom then return fromRom end if fromRom then return fromRom end
end end
if GameVersion.isYellow() then if PaletteFX.usesYellowCgb() then
local fromCgb = yellowCgbNamedPal(data, name) local fromCgb = yellowCgbNamedPal(data, name)
if fromCgb then return fromCgb end if fromCgb then return fromCgb end
end end
local p = PaletteFX.pack(data) local p = PaletteFX.pack(data)
local c = p and p.palettes[name] local c = p and p.palettes[name]
if c then return c end if c then return c end
if GameVersion.isYellow() then
local y = PaletteFX.yellowPack()
local yc = y and y.palettes and y.palettes[name]
if yc then return yc end
end
if PaletteFX.usesGbcPack() then if PaletteFX.usesGbcPack() then
return romNamedPal(data, name) return romNamedPal(data, name)
end end
@@ -413,14 +452,20 @@ end
-- Transformed mon's pic is tinted gray, not the copied species' own -- Transformed mon's pic is tinted gray, not the copied species' own
-- SGB color). RED++ uses per-species pals from mon_palettes.asm. -- SGB color). RED++ uses per-species pals from mon_palettes.asm.
function PaletteFX.monPal(data, species, transformed) function PaletteFX.monPal(data, species, transformed)
-- OG RED: a battle mon pic is a BG tile on the Game Boy Color (drawn into -- OG RED / OG BLUE: a battle mon pic is a BG tile on the Game Boy Color
-- the tilemap, colored by BGP), so it wears the global red BG palette, not -- (drawn into the tilemap, colored by BGP), so it wears the global boot-ROM
-- a per-species one -- matching the hardware capture where both mons are -- BG palette, not a per-species one -- matching the hardware capture where
-- red/pink on the white field. -- both mons are red/pink (or blue/pink) on the white field.
if PaletteFX.mode == "ogred" then return PaletteFX.ogBg() end -- OG YELLOW keeps per-species CGBBasePalettes (Yellow had real CGB code).
if PaletteFX.mode == "ogred" and not GameVersion.isYellow() then
return PaletteFX.ogBg()
end
local p = PaletteFX.pack(data) local p = PaletteFX.pack(data)
if not p then return nil end if not p then return nil end
if transformed then if transformed then
if PaletteFX.usesYellowCgb() then
return PaletteFX.pal(data, "GRAYMON")
end
return p.palettes.GRAYMON return p.palettes.GRAYMON
or (data and data.palettes and data.palettes.palettes.GRAYMON) or (data and data.palettes and data.palettes.palettes.GRAYMON)
end end
@@ -430,17 +475,32 @@ function PaletteFX.monPal(data, species, transformed)
-- so fall back to it when the pack itself doesn't carry the name. -- so fall back to it when the pack itself doesn't carry the name.
local def = data and data.pokemon and data.pokemon[species] local def = data and data.pokemon and data.pokemon[species]
if def and def.palette then if def and def.palette then
if PaletteFX.usesYellowCgb() then
local yc = PaletteFX.pal(data, def.palette)
if yc then return yc end
end
local pal = p.palettes[def.palette] local pal = p.palettes[def.palette]
or (data and data.palettes and data.palettes.palettes[def.palette]) or (data and data.palettes and data.palettes.palettes[def.palette])
if pal then return pal end if pal then return pal end
end end
local name = p.pokemon[species] or "MEWMON" local name = p.pokemon[species] or "MEWMON"
if PaletteFX.usesYellowCgb() then
local yc = PaletteFX.pal(data, name)
if yc then return yc end
end
local c = p.palettes[name] local c = p.palettes[name]
if c then return c end if c then return c end
if PaletteFX.usesGbcPack() and data and data.palettes then if PaletteFX.usesGbcPack() and data and data.palettes then
name = data.palettes.pokemon[species] or "MEWMON" name = data.palettes.pokemon[species] or "MEWMON"
return data.palettes.palettes[name] return data.palettes.palettes[name]
end end
if GameVersion.isYellow() then
local y = PaletteFX.yellowPack()
if y and y.pokemon then
name = y.pokemon[species] or "MEWMON"
return y.palettes and y.palettes[name]
end
end
return nil return nil
end end
@@ -707,12 +767,10 @@ end
function PaletteFX.modeLabel(mode) function PaletteFX.modeLabel(mode)
mode = mode or PaletteFX.mode mode = mode or PaletteFX.mode
-- The GBC boot-ROM mode wears the running game's name: it is red for Red and -- The GBC hardware mode wears the running game's name: OG RED / OG BLUE
-- blue for Blue (see ogBg), so a Blue playthrough shows "OG BLUE". -- (boot-ROM auto-palette) or OG YELLOW (pokeyellow CGBBasePalettes).
-- Yellow still uses the Red boot-ROM ramp (no Yellow table yet), so keep
-- the "OG RED" label rather than inventing an "OG YELLOW" without colors.
if mode == "ogred" and GameVersion.isBlue() then return "OG BLUE" end if mode == "ogred" and GameVersion.isBlue() then return "OG BLUE" end
if mode == "ogred" and GameVersion.isYellow() then return "OG RED" end if mode == "ogred" and GameVersion.isYellow() then return "OG YELLOW" end
return PaletteFX.MODE_LABELS[mode] or "GBC" return PaletteFX.MODE_LABELS[mode] or "GBC"
end end
+20 -2
View File
@@ -550,9 +550,26 @@ end
-- UI-pass palette (text boxes and menus tint with the current map). OG RED -- UI-pass palette (text boxes and menus tint with the current map). OG RED
-- resolves every name to the one global red BG palette inside PaletteFX.pal, -- resolves every name to the one global red BG palette inside PaletteFX.pal,
-- so this needs no mode-specific branch. -- so this needs no mode-specific branch.
--
-- TalkToPikachu's framed frontpic is the one exception: pokeyellow
-- LoadOverworldPikachuFrontpicPalettes loads the map pal as slot 0 and
-- PAL_PIKACHU_PORTRAIT as slot 1, then ATTR_BLK's the 5x5 pic at
-- (7,6)-(11,10) onto slot 1 (engine/gfx/palettes.asm:345-391). Without
-- that zone the pic wears the route/town palette and looks washed out.
function OverworldState:sgbPalettes() function OverworldState:sgbPalettes()
local PaletteFX = require("src.render.PaletteFX") local PaletteFX = require("src.render.PaletteFX")
return PaletteFX.wholeNamed(Game.data, self:paletteNameFor(self.map)) local mapName = self:paletteNameFor(self.map)
if self.emote and self.emote.pikaPic then
local base = PaletteFX.pal(Game.data, mapName)
if not base then return nil end
local zones = { PaletteFX.whole(base) }
local portrait = PaletteFX.pal(Game.data, "PIKACHU_PORTRAIT")
if portrait then
zones[#zones + 1] = PaletteFX.zone(portrait, 7, 6, 11, 10)
end
return zones
end
return PaletteFX.wholeNamed(Game.data, mapName)
end end
-- World-pass palette zones in world-canvas pixels: each visible map -- World-pass palette zones in world-canvas pixels: each visible map
@@ -4479,7 +4496,8 @@ function OverworldState:drawUI()
-- per-emotion frame gfx (gfx/pikachu/unknown_*) are not extracted, so the -- per-emotion frame gfx (gfx/pikachu/unknown_*) are not extracted, so the
-- front pic stands in for every frame of the script; PikachuFollower -- front pic stands in for every frame of the script; PikachuFollower
-- .picLift lifts it on the runs that draw the alternate pose, and the -- .picLift lifts it on the runs that draw the alternate pose, and the
-- script's own duration times the beat (#407, #424). -- script's own duration times the beat (#407, #424). Palette zone
-- PAL_PIKACHU_PORTRAIT covers (7,6)-(11,10) via sgbPalettes above.
if self.emote and self.emote.pikaPic then if self.emote and self.emote.pikaPic then
require("src.render.Font").drawBox(6, 5, 7, 7) require("src.render.Font").drawBox(6, 5, 7, 7)
-- one image per path, cached: this draws every frame of the hold, and -- one image per path, cached: this draws every frame of the hold, and
+63
View File
@@ -0,0 +1,63 @@
-- Parity: leaving the Cerulean Badge House badge-description menu must
-- return control to the overworld after viewing a badge (#454).
package.path = "./?.lua;./?/init.lua;" .. package.path
if not _G.love then _G.love = require("tests.love_stub") end
local S = require("tests.harness").suite("parity Cerulean Badge House")
local eq = S.eq
local script = require("data.scripts.flavor.cerulean_badge_house")
.CERULEAN_BADGE_HOUSE.talk.TEXT_CERULEANBADGEHOUSE_MIDDLE_AGED_MAN
local states = {}
local game = {
data = {
items = {
BOULDERBADGE = { name = "BOULDERBADGE" },
CASCADEBADGE = { name = "CASCADEBADGE" },
THUNDERBADGE = { name = "THUNDERBADGE" },
RAINBOWBADGE = { name = "RAINBOWBADGE" },
SOULBADGE = { name = "SOULBADGE" },
MARSHBADGE = { name = "MARSHBADGE" },
VOLCANOBADGE = { name = "VOLCANOBADGE" },
EARTHBADGE = { name = "EARTHBADGE" },
},
text = {
_CeruleanBadgeHouseMiddleAgedManText = "",
_CeruleanBadgeHouseMiddleAgedManWhichBadgeText = "",
_CeruleanBadgeHouseBoulderBadgeText = "",
_CeruleanBadgeHouseMiddleAgedManVisitAnyTimeText = "",
},
},
save = { player = { name = "RED" } },
input = { wasPressed = function(_, key) return key == "b" end },
stack = {
push = function(_, state) states[#states + 1] = state end,
pop = function() return table.remove(states) end,
top = function() return states[#states] end,
},
}
local done = false
script(game, nil, nil, function() done = true end)
local function dismissText()
local text = game.stack:pop()
text.onDone()
end
-- Greeting -> prompt -> first menu -> badge description -> prompt -> second menu.
dismissText()
dismissText()
local firstMenu = game.stack:top()
firstMenu.onChoose(firstMenu.items[1])
dismissText()
dismissText()
-- B follows ListMenu's real cancellation path: pop menu, then show goodbye.
game.stack:top():update(0)
dismissText()
eq(#states, 0, "backing out after viewing a badge leaves no menu behind")
eq(done, true, "backing out after viewing a badge completes the NPC script")
S.finish()
+1
View File
@@ -3275,6 +3275,7 @@ runSuites(orderedGlob("tests/parity_*.lua", {
"tests/parity_H.lua", "tests/parity_G.lua", "tests/parity_I_M.lua", "tests/parity_H.lua", "tests/parity_G.lua", "tests/parity_I_M.lua",
"tests/parity_B.lua", "tests/parity_J.lua", "tests/parity_A.lua", "tests/parity_B.lua", "tests/parity_J.lua", "tests/parity_A.lua",
"tests/parity_battle_menu_cursor.lua", "tests/parity_battle_menu_cursor.lua",
"tests/parity_cerulean_badge_house.lua",
"tests/parity_flavor.lua", "tests/parity_trainer_sight.lua", "tests/parity_flavor.lua", "tests/parity_trainer_sight.lua",
"tests/parity_static.lua", "tests/parity_trashcans.lua", "tests/parity_static.lua", "tests/parity_trashcans.lua",
"tests/parity_hof.lua", "tests/parity_trade_gift.lua", "tests/parity_hof.lua", "tests/parity_trade_gift.lua",
+100 -1
View File
@@ -21,6 +21,16 @@ Output: data/palettes_gbc.lua (committed; not wiped by ROM import)
same shape, plus per-species palette entries (BULBASAUR, ), plus a same shape, plus per-species palette entries (BULBASAUR, ), plus a
`world` table (below) for true overworld tile/roof/sprite coloring. `world` table (below) for true overworld tile/roof/sprite coloring.
Sources (pokeyellow / COLORS OG YELLOW):
data/sgb/sgb_palettes.asm SuperPalettes + CGBBasePalettes (Yellow is
CGB-enhanced; CGBBase is the authentic GBC look)
data/pokemon/palettes.asm MonsterPalettes (same shape as Red)
Output: data/palettes_yellow.lua (committed; not wiped by ROM import)
palettes[NAME] = SuperPalettes RGB
cgbBase[NAME] = CGBBasePalettes RGB
pokemon[SPECIES] = NAME
The HP bar fill is GB color 2 of PAL_GREENBAR / PAL_YELLOWBAR / The HP bar fill is GB color 2 of PAL_GREENBAR / PAL_YELLOWBAR /
PAL_REDBAR; the thresholds live in home/palettes.asm GetHealthBarColor PAL_REDBAR; the thresholds live in home/palettes.asm GetHealthBarColor
(>= 27 pixels green, >= 10 yellow, else red). (>= 27 pixels green, >= 10 yellow, else red).
@@ -536,6 +546,89 @@ def extract_gbc(pokered_gbc, out_path):
return palettes, mon_pals return palettes, mon_pals
def _parse_pokeyellow_palette_tables(path):
"""Parse SuperPalettes and CGBBasePalettes from pokeyellow sgb_palettes.asm."""
tables = {"SuperPalettes": {}, "CGBBasePalettes": {}}
orders = {"SuperPalettes": [], "CGBBasePalettes": []}
current = None
for lineno, line in _read_raw(path):
s = line.strip()
if s.startswith("SuperPalettes:"):
current = "SuperPalettes"
continue
if s.startswith("CGBBasePalettes:"):
current = "CGBBasePalettes"
continue
if s.startswith("assert_table_length") or s.endswith(":") and " " not in s:
if s.endswith(":") and not s.startswith(("Super", "CGB")):
current = None
continue
if current is None:
continue
m = re.match(r"RGB\s+([\d,\s]+);\s*PAL_(\w+)", s)
if not m:
continue
nums = [n for n in re.split(r"[,\s]+", m.group(1).strip()) if n]
if len(nums) != 12:
util.die(f"{path}:{lineno}: expected 12 components, got {len(nums)}")
name = m.group(2)
tables[current][name] = _rgb8(nums)
orders[current].append(name)
return tables, orders
def extract_yellow(pokeyellow, out_path):
"""Extract Yellow SuperPalettes + CGBBasePalettes for COLORS=OG YELLOW."""
pal_path = os.path.join(pokeyellow, "data/sgb/sgb_palettes.asm")
mon_path = os.path.join(pokeyellow, "data/pokemon/palettes.asm")
tables, orders = _parse_pokeyellow_palette_tables(pal_path)
palettes = tables["SuperPalettes"]
cgb_base = tables["CGBBasePalettes"]
order = orders["SuperPalettes"]
if not palettes:
util.die(f"{pal_path}: SuperPalettes empty")
if not cgb_base:
util.die(f"{pal_path}: CGBBasePalettes empty")
if orders["CGBBasePalettes"] != order:
util.die(f"{pal_path}: CGBBasePalettes order differs from SuperPalettes")
mon_pals = {}
in_table = False
for lineno, line in _read_raw(mon_path):
s = line.strip()
if s.startswith("MonsterPalettes:"):
in_table = True
continue
if not in_table:
continue
m = re.match(r"db\s+PAL_(\w+)\s*;\s*(\w+)", s)
if not m:
continue
pal, species = m.group(1), m.group(2)
if pal not in palettes:
util.die(f"{mon_path}:{lineno}: unknown palette PAL_{pal}")
if species != "MISSINGNO":
mon_pals[species] = pal
if len(mon_pals) != 151:
util.die(f"MonsterPalettes parsed {len(mon_pals)} species (want 151)")
for name in ("MEWMON", "GREENBAR", "YELLOWBAR", "REDBAR", "ROUTE"):
if name not in cgb_base:
util.die(f"{pal_path}: CGBBase PAL_{name} missing")
util.write_lua(
out_path,
{"source": "pokeyellow data/sgb/sgb_palettes.asm + data/pokemon/palettes.asm",
"palettes": palettes,
"cgbBase": cgb_base,
"order": order,
"pokemon": mon_pals},
header="Pokemon Yellow palettes (COLORS=OG YELLOW): SuperPalettes +\n"
"CGBBasePalettes (authentic GBC look) as 4 8-bit RGB colors\n"
"per palette (color 0 first), plus MonsterPalettes.")
return palettes, cgb_base, mon_pals
def main(argv=None): def main(argv=None):
parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0]) parser = argparse.ArgumentParser(description=__doc__.split("\n\n")[0])
sub = parser.add_subparsers(dest="cmd", required=True) sub = parser.add_subparsers(dest="cmd", required=True)
@@ -548,11 +641,17 @@ def main(argv=None):
p_gbc.add_argument("--pokered-gbc", required=True) p_gbc.add_argument("--pokered-gbc", required=True)
p_gbc.add_argument("--out", default="data/palettes_gbc.lua") p_gbc.add_argument("--out", default="data/palettes_gbc.lua")
p_yel = sub.add_parser("yellow", help="extract pokeyellow CGBBasePalettes")
p_yel.add_argument("--pokeyellow", required=True)
p_yel.add_argument("--out", default="data/palettes_yellow.lua")
args = parser.parse_args(argv) args = parser.parse_args(argv)
if args.cmd == "vanilla": if args.cmd == "vanilla":
extract(args.pokered, args.out_dir) extract(args.pokered, args.out_dir)
else: elif args.cmd == "gbc":
extract_gbc(args.pokered_gbc, args.out) extract_gbc(args.pokered_gbc, args.out)
else:
extract_yellow(args.pokeyellow, args.out)
return 0 return 0