From ee63ffff2d778abbefd27cd126b294fa36fefc22 Mon Sep 17 00:00:00 2001 From: thibautbus <310327033+thibautbus@users.noreply.github.com> Date: Tue, 22 Sep 2026 10:51:24 +0200 Subject: [PATCH 1/4] Name a FireRed egg by the language's own EGG MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Party.giveMon stores "EGG" as a new egg's nickname (party.lua for a gift egg, breeding.lua for the Day-Care's), and the party slots and the other screens that name a mon through Pokemon.displayName and displayMonName print that nickname as it is. A translated game therefore listed its eggs as EGG, while the summary, which prints Strings("EGG") for an egg, already said OEUF, EI, HUEVO or UOVO. The cart does not rely on the stored nickname either: its Day-Care writes the Japanese タマゴ there (daycare.c:1100), and GetMonData returns gText_EggNickname for any egg's nickname (pokemon.c:3020). Do the same in both name helpers: an egg reads as Strings("EGG"), whatever its nickname holds, which also covers the eggs saves already carry. A hatched egg, whose nickname is cleared, goes back to its species name. --- src/core/game3/pokemon.lua | 12 ++++++ tests/engine/game3_egg_display_name_test.lua | 44 ++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 tests/engine/game3_egg_display_name_test.lua diff --git a/src/core/game3/pokemon.lua b/src/core/game3/pokemon.lua index 555cdd34..9dd36b35 100644 --- a/src/core/game3/pokemon.lua +++ b/src/core/game3/pokemon.lua @@ -1160,8 +1160,18 @@ function Pokemon.replaceMove(mon, slot, newMoveId) return old end +-- An egg reads as the language's own EGG whatever its nickname holds: pret's +-- GetMonData(MON_DATA_NICKNAME) returns gText_EggNickname for any egg +-- (pokefirered/src/pokemon.c:3020). The stored nickname is only a placeholder +-- -- the cart's daycare writes タマゴ (daycare.c:1100), this engine "EGG". +local function eggName(mon) + if Pokemon.isEgg(mon) then return Strings("EGG") end +end + function Pokemon.displayMonName(mon) if not mon then return "POKéMON" end + local egg = eggName(mon) + if egg then return egg end local nick = mon.nickname if type(nick) == "string" and nick ~= "" then return nick end if mon.name and mon.name ~= "" then return mon.name end @@ -1572,6 +1582,8 @@ end function Pokemon.displayName(mon) if not mon then return "?????" end + local egg = eggName(mon) + if egg then return egg end if mon.nickname and mon.nickname ~= "" then return tostring(mon.nickname) end -- Prefer pack name over host species string when we can resolve. local sp = Pokemon.speciesOf(mon) diff --git a/tests/engine/game3_egg_display_name_test.lua b/tests/engine/game3_egg_display_name_test.lua new file mode 100644 index 00000000..994d3e77 --- /dev/null +++ b/tests/engine/game3_egg_display_name_test.lua @@ -0,0 +1,44 @@ +#!/usr/bin/env luajit +-- An egg is named by the language's own EGG, as pret's GetMonData(MON_DATA_NICKNAME) +-- does for any egg (pokefirered/src/pokemon.c:3020): the party and every screen +-- that names a mon through Pokemon.displayName / displayMonName show it, whatever +-- placeholder the egg's nickname stores. + +package.path = "./?.lua;./?/init.lua;" .. package.path +love = require("tests.love_stub") + +local T = require("tests.harness") +local check = T.check + +local Strings = require("src.core.Strings") +local Pokemon = require("src.core.game3.pokemon") + +-- A minimal pack, so no ROM is needed. +Pokemon._names = { [25] = "PIKACHU", [172] = "PICHU" } + +local egg = { species = 172, isEgg = true, nickname = "EGG", name = "EGG" } +local hatched = { species = 172, isEgg = false, nickname = "", name = "PICHU" } +local named = { species = 25, nickname = "SPARKY" } + +Strings.load({}) +check(Pokemon.displayName(egg) == "EGG", "an egg reads EGG with no catalog") +check(Pokemon.displayMonName(egg) == "EGG", "in both name helpers") + +Strings.load({ strings = { EGG = "OEUF" } }) +check(Pokemon.displayName(egg) == "OEUF", "an egg follows the catalog's EGG") +check(Pokemon.displayMonName(egg) == "OEUF", "in both name helpers") +check(egg.nickname == "EGG", "without its stored nickname being rewritten") + +local bare = { species = 172, isEgg = true, nickname = "" } +check(Pokemon.displayName(bare) == "OEUF", "an egg with no nickname at all is still named EGG") +local species_only = { species = 412, nickname = "" } +check(Pokemon.displayName(species_only) == "OEUF", "and so is one known only by the egg species") + +-- (looking up a species the pack lacks reloads it; put the minimal one back) +Pokemon._names = { [25] = "PIKACHU", [172] = "PICHU" } +check(Pokemon.displayName(hatched) == "PICHU", "a hatched egg goes back to its species name") +check(Pokemon.displayName(named) == "SPARKY", "and a nicknamed mon keeps its nickname") +check(Pokemon.displayMonName(named) == "SPARKY", "in both name helpers") + +Strings.load({}) +T.finish("game3_egg_display_name_test") From aaacf02dc0df030360f6004d43b7cede9e96a37e Mon Sep 17 00:00:00 2001 From: thibautbus <310327033+thibautbus@users.noreply.github.com> Date: Tue, 22 Sep 2026 11:51:01 +0200 Subject: [PATCH 2/4] Draw a FireRed egg's menu icon as an egg The party, the PC boxes and the release animation drew an egg with the icon of the species it will hatch into, so a Pichu egg showed as a Pichu. pret's party menu picks the icon from GetMonData(MON_DATA_SPECIES_OR_EGG), which is SPECIES_EGG for any egg (pokefirered/src/pokemon.c:3245, party_menu.c:2655). Pokemon.speciesOrEgg does the same, and the icon call sites use it. The species pass only extracted icons up to NUM_SPECIES - 1, so the egg extractor now also bakes gMonIconTable[SPECIES_EGG] as pokemon/icons/412.rgba. Cache version 114 re-imports existing caches so the icon appears. --- src/core/game3/pokemon.lua | 7 ++++ src/import/CacheContract.lua | 2 ++ src/import/gba/egg_extract.lua | 12 ++++++- src/import/gba/pokemon_extract.lua | 38 ++++++++++---------- src/import/gba/versions.lua | 4 ++- src/ui/game3/box_storage_ui.lua | 4 +-- src/ui/game3/party_menu.lua | 2 +- src/ui/game3/pc_chrome.lua | 4 +-- src/ui/game3/release_seq.lua | 2 +- tests/drivers/game3_import2_fresh_cache.lua | 1 + tests/engine/game3_egg_icon_species_test.lua | 37 +++++++++++++++++++ tests/game3_import2_cache_version_test.lua | 1 + tests/game3_import2_misc_assets_test.lua | 6 ++++ 13 files changed, 92 insertions(+), 28 deletions(-) create mode 100644 tests/engine/game3_egg_icon_species_test.lua diff --git a/src/core/game3/pokemon.lua b/src/core/game3/pokemon.lua index 9dd36b35..a0b30cb6 100644 --- a/src/core/game3/pokemon.lua +++ b/src/core/game3/pokemon.lua @@ -1256,6 +1256,13 @@ function Pokemon.isEgg(mon) return (mon.isEgg == true) or (mon.egg == true) or (mon.species == 412) end +-- pokefirered/src/pokemon.c:3245 MON_DATA_SPECIES_OR_EGG: an egg's menu icon is +-- SPECIES_EGG's, not the species it will hatch into (party_menu.c:2655). +function Pokemon.speciesOrEgg(mon) + if Pokemon.isEgg(mon) then return Pokemon.SPECIES_EGG end + return Pokemon.speciesOf(mon) +end + local function read_rgba(species) local cache = resolve_cache(Pokemon._cache) local root = (Extract.CACHE_ROOT or "data/generated/gba") .. "/pokemon" diff --git a/src/import/CacheContract.lua b/src/import/CacheContract.lua index 3e1c7d5a..3b2f5453 100644 --- a/src/import/CacheContract.lua +++ b/src/import/CacheContract.lua @@ -377,6 +377,8 @@ CacheContract.VERSION_REQUIRED_FILES_OVERRIDE = { "data/generated/gba/pokemon/egg/hatch.rgba", "data/generated/gba/pokemon/egg/shard.rgba", "data/generated/gba/pokemon/front/412.rgba", + -- src/party_menu.c:2655 + "data/generated/gba/pokemon/icons/412.rgba", -- src/battle_records.c:563 "data/generated/gba/trainer_tower/manifest.lua", "data/generated/gba/trainer_tower/records_bg.rgba", diff --git a/src/import/gba/egg_extract.lua b/src/import/gba/egg_extract.lua index 4aee2c4a..917f111c 100644 --- a/src/import/gba/egg_extract.lua +++ b/src/import/gba/egg_extract.lua @@ -3,6 +3,7 @@ local Versions = require("src.import.gba.versions") local Lz77 = require("src.import.gba.lz77") local BgBake = require("src.import.gba.bg_bake") +local PokemonExtract = require("src.import.gba.pokemon_extract") local EggExtract = {} @@ -77,6 +78,10 @@ function EggExtract.run(rom, cache, opts) cache:write(cacheRoot .. "/pokemon/front/" .. EggExtract.SPECIES_EGG .. ".rgba", BgBake.bakeSpriteRgba(tiles, picBank, 0, PIC_W, PIC_H, false, false)) + -- src/party_menu.c:2655 draws an egg's icon from MON_DATA_SPECIES_OR_EGG + cache:write(cacheRoot .. "/pokemon/icons/" .. EggExtract.SPECIES_EGG .. ".rgba", + PokemonExtract.iconRgba(rom, EggExtract.SPECIES_EGG)) + cache:write(root .. "/manifest.lua", string.format([[ return { format_version = %d, @@ -102,7 +107,12 @@ function EggExtract.ready(cache, cacheRoot) for _, rel in ipairs({ "hatch.rgba", "shard.rgba", "manifest.lua" }) do if not cache:exists(root .. "/" .. rel) then return false end end - return cache:exists(cacheRoot .. "/pokemon/front/" .. EggExtract.SPECIES_EGG .. ".rgba") + for _, sub in ipairs({ "front", "icons" }) do + if not cache:exists(cacheRoot .. "/pokemon/" .. sub .. "/" .. EggExtract.SPECIES_EGG .. ".rgba") then + return false + end + end + return true end return EggExtract diff --git a/src/import/gba/pokemon_extract.lua b/src/import/gba/pokemon_extract.lua index 46b02ffb..f4beff83 100644 --- a/src/import/gba/pokemon_extract.lua +++ b/src/import/gba/pokemon_extract.lua @@ -648,9 +648,26 @@ local function extract_egg_moves(rom, num) return eggMoves end +-- gMonIconTable / gMonIconPaletteIndices entry for one species, both frames +-- (32x64 RGBA); they run past NUM_SPECIES, so SPECIES_EGG has its own icon. +local function icon_rgba(rom, sp, pals) + pals = pals or load_icon_pals(rom) + local w = Versions.MON_ICON_W or 32 + local iconH = (Versions.MON_ICON_H or 32) * 2 -- 64 (2 frames) + local iconBytes = Versions.MON_ICON_BYTES or math.floor(w * iconH / 2) -- 1024 for 32x64 + local off = gba_off(rom:u32(Versions.MON_ICON_TABLE + sp * 4)) + if not off then return string.rep(string.char(0, 0, 0, 0), w * iconH * 4) end + local palIdx = rom:get(Versions.MON_ICON_PAL_INDICES + sp) or 0 + if palIdx >= Versions.MON_ICON_PAL_COUNT then palIdx = 0 end + local pixels = decode_4bpp(rom:readBytes(off, iconBytes), w, iconH) + return bake_icon_rgba(pixels, pals[palIdx] or pals[0], w, iconH) +end + -- Exposed for tests (tests/engine/game3_egg_moves.lua). PokemonExtract.eggMovesFromRom = extract_egg_moves PokemonExtract.writeEggMovesLua = write_egg_moves_lua +-- src/import/gba/egg_extract.lua bakes the SPECIES_EGG icon with it. +PokemonExtract.iconRgba = icon_rgba --- Extract full pack into cache under {cacheRoot}/pokemon/. function PokemonExtract.run(rom, cache, opts) @@ -669,13 +686,7 @@ function PokemonExtract.run(rom, cache, opts) local nameBase = Versions.SPECIES_NAMES local infoBase = Versions.SPECIES_INFO local natBase = Versions.SPECIES_TO_NATIONAL - local w = Versions.MON_ICON_W or 32 - local h = Versions.MON_ICON_H or 32 - local iconH = h * 2 -- 64 (2 frames) - local iconBytes = Versions.MON_ICON_BYTES or math.floor(w * iconH / 2) -- 1024 for 32x64 - local palIdxBase = Versions.MON_ICON_PAL_INDICES local pals = load_icon_pals(rom) - local iconTable = Versions.MON_ICON_TABLE local frontPicTable = (Versions.OAK_SPEECH and Versions.OAK_SPEECH.mon_front_pic_table) or 0x2350AC local backPicTable = Versions.MON_BACK_PIC_TABLE or 0x23654C local palTable = (Versions.OAK_SPEECH and Versions.OAK_SPEECH.mon_palette_table) or 0x23730C @@ -727,20 +738,7 @@ function PokemonExtract.run(rom, cache, opts) -- Table omits SPECIES_NONE; SpeciesToNationalPokedexNum uses [species - 1]. toNat[sp] = (sp >= 1) and rom:u16(natBase + (sp - 1) * 2) or 0 - local ptr = rom:u32(iconTable + sp * 4) - local off = gba_off(ptr) - local palIdx = rom:get(palIdxBase + sp) or 0 - if palIdx >= Versions.MON_ICON_PAL_COUNT then palIdx = 0 end - local pal = pals[palIdx] or pals[0] - local rgba - if off then - local bytes = rom:readBytes(off, iconBytes) - local pixels = decode_4bpp(bytes, w, iconH) - rgba = bake_icon_rgba(pixels, pal, w, iconH) - else - rgba = string.rep(string.char(0, 0, 0, 0), w * iconH * 4) - end - put(cache, root .. "/icons/" .. sp .. ".rgba", rgba) + put(cache, root .. "/icons/" .. sp .. ".rgba", icon_rgba(rom, sp, pals)) picsWritten.icons = picsWritten.icons + 1 -- Front Pic (64x64 RGBA) diff --git a/src/import/gba/versions.lua b/src/import/gba/versions.lua index 0294e685..ee5dff12 100644 --- a/src/import/gba/versions.lua +++ b/src/import/gba/versions.lua @@ -37,7 +37,9 @@ Versions.ROM_SIZE = 16777216 -- setptrbyte each carry a leading byte plus a word (were shorter). The -- old sizes mis-decoded every instruction after one, so every cached -- script is stale. -Versions.CACHE_VERSION = 113 +-- v114: pokemon/icons/412.rgba, the SPECIES_EGG menu icon — eggs were drawn +-- with the icon of the species they hatch into. +Versions.CACHE_VERSION = 114 Versions.NATIVE_VERSION = 6 Versions.OW_VERSION = 1 Versions.ANIM_VERSION = 1 diff --git a/src/ui/game3/box_storage_ui.lua b/src/ui/game3/box_storage_ui.lua index 93796ed9..14cacbf7 100644 --- a/src/ui/game3/box_storage_ui.lua +++ b/src/ui/game3/box_storage_ui.lua @@ -738,7 +738,7 @@ function BoxStorageUI.draw() local isHovered = (BoxStorageUI.cursorSlot == s and BoxStorageUI.mode ~= "party_drawer" and not BoxStorageUI.holdingMon) local bounceY = (isHovered and BoxStorageUI.hoverFrame == 1) and -2 or 0 local f = (isHovered and BoxStorageUI.hoverFrame == 1) and 1 or 0 - local sp = Pokemon.speciesOf(mon) + local sp = Pokemon.speciesOrEgg(mon) local icon = Pokemon.icon(sp) if icon and icon.image then @@ -804,7 +804,7 @@ function BoxStorageUI.draw() -- If holding a mon, draw floating mini-icon under hand cursor if BoxStorageUI.holdingMon then - local hSp = Pokemon.speciesOf(BoxStorageUI.holdingMon) + local hSp = Pokemon.speciesOrEgg(BoxStorageUI.holdingMon) local hIcon = Pokemon.icon(hSp) if hIcon and hIcon.image then local q = hIcon.quads and hIcon.quads[0] diff --git a/src/ui/game3/party_menu.lua b/src/ui/game3/party_menu.lua index 2af1b24d..e01474fb 100644 --- a/src/ui/game3/party_menu.lua +++ b/src/ui/game3/party_menu.lua @@ -446,7 +446,7 @@ local function ensure_slot_sprites(i, mon, selected) local maxHp = tonumber(mon.maxHp) or tonumber(mon.maxhp) or 1 local hpLevel = get_hp_bar_level(hp, maxHp, mon.isEgg) - local icon = Pokemon.icon(Pokemon.speciesOf(mon)) + local icon = Pokemon.icon(Pokemon.speciesOrEgg(mon)) local q0 = icon and icon.quads and icon.quads[0] if not slot.mon then local id = select(1, Oam.createSprite({ diff --git a/src/ui/game3/pc_chrome.lua b/src/ui/game3/pc_chrome.lua index 553c9340..84997fb2 100644 --- a/src/ui/game3/pc_chrome.lua +++ b/src/ui/game3/pc_chrome.lua @@ -381,7 +381,7 @@ function PcChrome.drawPartyDrawer(party, partyCursor, hoverFrame, holdingSource) local isLeadPickedUp = (holdingSource and holdingSource.loc == "party" and holdingSource.slot == 1) local leadMon = (not isLeadPickedUp) and party[1] if leadMon then - local sp = Pokemon.speciesOf(leadMon) + local sp = Pokemon.speciesOrEgg(leadMon) local icon = Pokemon.icon(sp) if icon and icon.image then local isHovered = (partyCursor == 1) @@ -401,7 +401,7 @@ function PcChrome.drawPartyDrawer(party, partyCursor, hoverFrame, holdingSource) local isPickedUp = (holdingSource and holdingSource.loc == "party" and holdingSource.slot == p) local pMon = (not isPickedUp) and party[p] if pMon then - local sp = Pokemon.speciesOf(pMon) + local sp = Pokemon.speciesOrEgg(pMon) local icon = Pokemon.icon(sp) if icon and icon.image then local isHovered = (partyCursor == p) diff --git a/src/ui/game3/release_seq.lua b/src/ui/game3/release_seq.lua index 9a8411df..3bf4ffde 100644 --- a/src/ui/game3/release_seq.lua +++ b/src/ui/game3/release_seq.lua @@ -125,7 +125,7 @@ function ReleaseSeq.draw() local curX = ReleaseSeq.startX local curY = ReleaseSeq.startY - (progress * 40) -- float upward 40px - local icon = ReleaseSeq.mon and Pokemon.icon(Pokemon.speciesOf(ReleaseSeq.mon)) + local icon = ReleaseSeq.mon and Pokemon.icon(Pokemon.speciesOrEgg(ReleaseSeq.mon)) if icon and icon.image then local q = icon.quads and icon.quads[0] love.graphics.setColor(1, 1, 1, alpha) diff --git a/tests/drivers/game3_import2_fresh_cache.lua b/tests/drivers/game3_import2_fresh_cache.lua index ce6dff15..76dc14f3 100644 --- a/tests/drivers/game3_import2_fresh_cache.lua +++ b/tests/drivers/game3_import2_fresh_cache.lua @@ -42,6 +42,7 @@ local ROUND2_SAMPLE = { "data/generated/gba/move_relearner/bg.rgba", "data/generated/gba/pokemon/egg/hatch.rgba", "data/generated/gba/pokemon/front/412.rgba", + "data/generated/gba/pokemon/icons/412.rgba", "data/generated/gba/native/layouts/alt_366.mid", } diff --git a/tests/engine/game3_egg_icon_species_test.lua b/tests/engine/game3_egg_icon_species_test.lua new file mode 100644 index 00000000..e80a7efb --- /dev/null +++ b/tests/engine/game3_egg_icon_species_test.lua @@ -0,0 +1,37 @@ +#!/usr/bin/env luajit +-- An egg's menu icon is SPECIES_EGG's, as pret draws it from +-- GetMonData(MON_DATA_SPECIES_OR_EGG) (pokefirered/src/pokemon.c:3245, +-- party_menu.c:2655), not the icon of the species it will hatch into. + +package.path = "./?.lua;./?/init.lua;" .. package.path +love = require("tests.love_stub") + +local T = require("tests.harness") +local check = T.check + +local Pokemon = require("src.core.game3.pokemon") +Pokemon._names = { [25] = "PIKACHU", [172] = "PICHU" } -- a minimal pack, so no ROM is needed + +check(Pokemon.speciesOrEgg({ species = 172, isEgg = true }) == 412, "a Pichu egg's icon is the EGG's") +check(Pokemon.speciesOrEgg({ species = 172, egg = true }) == 412, "whichever egg flag it carries") +check(Pokemon.speciesOrEgg({ species = 412 }) == 412, "an egg known only by the egg species stays one") +check(Pokemon.speciesOrEgg({ species = 172, isEgg = false }) == 172, "a hatched mon keeps its species") +check(Pokemon.speciesOrEgg({ species = 25 }) == 25, "and so does any other mon") +check(Pokemon.speciesOrEgg(nil) == nil, "no mon, no species") + +-- Every screen that draws a party or box mon's icon goes through it. +local SITES = { + { "src/ui/game3/party_menu.lua", 1 }, + { "src/ui/game3/box_storage_ui.lua", 2 }, + { "src/ui/game3/pc_chrome.lua", 2 }, + { "src/ui/game3/release_seq.lua", 1 }, +} +for _, site in ipairs(SITES) do + local f = assert(io.open(site[1], "rb")) + local src = f:read("*a") + f:close() + local n = select(2, src:gsub("Pokemon%.speciesOrEgg%(", "")) + check(n == site[2], ("%s picks %d icon(s) by speciesOrEgg (found %d)"):format(site[1], site[2], n)) +end + +T.finish("game3_egg_icon_species_test") diff --git a/tests/game3_import2_cache_version_test.lua b/tests/game3_import2_cache_version_test.lua index 9830444f..adcdb34d 100644 --- a/tests/game3_import2_cache_version_test.lua +++ b/tests/game3_import2_cache_version_test.lua @@ -164,6 +164,7 @@ local ROUND2_KEYS = { "data/generated/gba/pokemon/egg/hatch.rgba", "data/generated/gba/pokemon/egg/shard.rgba", "data/generated/gba/pokemon/front/412.rgba", + "data/generated/gba/pokemon/icons/412.rgba", -- src/trainer_card.c:265, :1454, :1560 "data/generated/gba/trainer_card/front_0.rgba", "data/generated/gba/trainer_card/front_4_female.rgba", diff --git a/tests/game3_import2_misc_assets_test.lua b/tests/game3_import2_misc_assets_test.lua index 54aa47c3..b26719a5 100644 --- a/tests/game3_import2_misc_assets_test.lua +++ b/tests/game3_import2_misc_assets_test.lua @@ -95,6 +95,7 @@ local WANT = { "data/generated/gba/pokemon/egg/hatch.rgba", "data/generated/gba/pokemon/egg/shard.rgba", "data/generated/gba/pokemon/front/412.rgba", + "data/generated/gba/pokemon/icons/412.rgba", "data/generated/gba/trainer_tower/manifest.lua", "data/generated/gba/trainer_tower/records_bg.rgba", "data/generated/gba/trainer_card/front_0.rgba", @@ -165,6 +166,8 @@ written["x/pokemon/egg/shard.rgba"] = "stub" written["x/pokemon/egg/manifest.lua"] = "stub" check(EggExtract.ready(stub, "x") == false, "egg ready() also wants the SPECIES_EGG front pic") written["x/pokemon/front/412.rgba"] = "stub" +check(EggExtract.ready(stub, "x") == false, "egg ready() also wants the SPECIES_EGG icon") +written["x/pokemon/icons/412.rgba"] = "stub" check(EggExtract.ready(stub, "x") == true, "egg ready() accepts the whole group") local regionFiles = {} @@ -234,6 +237,7 @@ local SHEETS = { { "pokemon/egg/hatch.rgba", 32, 128 }, { "pokemon/egg/shard.rgba", 32, 8 }, { "pokemon/front/412.rgba", 64, 64 }, + { "pokemon/icons/412.rgba", 32, 64 }, { "trainer_tower/records_bg.rgba", 240, 160 }, { "trainer_card/front_0.rgba", 240, 160 }, { "trainer_card/front_4_female.rgba", 240, 160 }, @@ -389,6 +393,8 @@ local eggPic = blobs["pokemon/front/412.rgba"] check(distinctColors(eggPic) > 4, "the EGG front pic is painted art") local _, _, _, eggCorner = pixel(eggPic, 64, 0, 0) eq(eggCorner, 0, "the EGG pic corner is transparent, as pic index 0 is") +local eggIcon = blobs["pokemon/icons/412.rgba"] +check(distinctColors(eggIcon) > 2, "the EGG menu icon is painted art") local eggMan = loadTable("pokemon/egg/manifest.lua") check(eggMan ~= nil and eggMan.hatch ~= nil and eggMan.hatch.frames == 4 and eggMan.hatch.sheetHeight == 128 and eggMan.shard.sheetWidth == 32, From f8fbe53346e9cceb46fccb3819fd9ee5bdaa6554 Mon Sep 17 00:00:00 2001 From: thibautbus <310327033+thibautbus@users.noreply.github.com> Date: Tue, 22 Sep 2026 12:07:30 +0200 Subject: [PATCH 3/4] Show a FireRed egg as an egg on the PC panel, summary and trade screens Three screens that show a single mon still drew an egg as the species it will hatch into, and printed its stored nickname untranslated: - The PC's hovered-mon panel drew the hatch species' front pic, the stored "EGG" nickname, "/PICHU", gender, level and held item. pret draws the SPECIES_OR_EGG pic and, for an egg, prints only gText_EggNickname with the other lines blank (pokemon_storage_system_data.c:1034, :1091). - The summary's egg page drew the hatch species' front pic, where pret uses SPECIES_OR_EGG (pokemon_summary_screen.c:4016). It also recognised an egg by mon.isEgg alone; it now uses Pokemon.isEgg like the rest of the engine. - The trade scene drew a traded egg as its species and named it by the stored nickname (trade_scene.c:757, :1239). The script string buffers (BufferMonNickname and the other nicknameOf helpers) now also give the language's EGG for an egg, as GetMonData (MON_DATA_NICKNAME) does (pokemon.c:3020). --- src/core/game3/scripting/natives.lua | 5 +- src/core/game3/scripting/natives_queries.lua | 6 +- src/core/game3/scripting/natives_trade.lua | 3 + src/core/game3/trade_scene.lua | 3 + src/ui/game3/pc_chrome.lua | 58 +++++---- src/ui/game3/summary_menu.lua | 11 +- src/ui/game3/trade_scene.lua | 9 +- tests/engine/game3_egg_icon_species_test.lua | 7 +- tests/engine/game3_egg_screens_test.lua | 119 +++++++++++++++++++ 9 files changed, 180 insertions(+), 41 deletions(-) create mode 100644 tests/engine/game3_egg_screens_test.lua diff --git a/src/core/game3/scripting/natives.lua b/src/core/game3/scripting/natives.lua index 2fe06362..ebaaa2b5 100644 --- a/src/core/game3/scripting/natives.lua +++ b/src/core/game3/scripting/natives.lua @@ -79,10 +79,13 @@ local function boxedMon() (tonumber(session.monBoxId) or 0) + 1, (tonumber(session.monBoxPos) or 0) + 1) end +-- GetMonData(MON_DATA_NICKNAME) is gText_EggNickname for an egg +-- (pokefirered/src/pokemon.c:3020) local function nicknameOf(mon) if not mon then return "" end - if mon.nickname and mon.nickname ~= "" then return tostring(mon.nickname) end local Pokemon = require("src.core.game3.pokemon") + if Pokemon.isEgg(mon) then return Strings("EGG") end + if mon.nickname and mon.nickname ~= "" then return tostring(mon.nickname) end pcall(function() if not Pokemon._names then Pokemon.install(nil) end end) diff --git a/src/core/game3/scripting/natives_queries.lua b/src/core/game3/scripting/natives_queries.lua index 02b36acb..e1ec57a5 100644 --- a/src/core/game3/scripting/natives_queries.lua +++ b/src/core/game3/scripting/natives_queries.lua @@ -118,11 +118,13 @@ local function speciesFromNational(nat) return (ok and tonumber(sp)) or nat end --- pokefirered/src/field_specials.c:1671 +-- pokefirered/src/field_specials.c:1671; GetMonData(MON_DATA_NICKNAME) is +-- gText_EggNickname for an egg (pokemon.c:3020) local function nicknameOf(mon) if not mon then return "" end - if mon.nickname and mon.nickname ~= "" then return tostring(mon.nickname) end local Pokemon = require("src.core.game3.pokemon") + if Pokemon.isEgg(mon) then return Strings("EGG") end + if mon.nickname and mon.nickname ~= "" then return tostring(mon.nickname) end pcall(function() if not Pokemon._names then Pokemon.install(nil) end end) diff --git a/src/core/game3/scripting/natives_trade.lua b/src/core/game3/scripting/natives_trade.lua index 6c426b7f..7b6ba06c 100644 --- a/src/core/game3/scripting/natives_trade.lua +++ b/src/core/game3/scripting/natives_trade.lua @@ -137,8 +137,11 @@ local function speciesName(species) return (Pokemon.name and Pokemon.name(species)) or "" end +-- GetMonData(MON_DATA_NICKNAME) is gText_EggNickname for an egg +-- (pokefirered/src/pokemon.c:3020) local function nicknameOf(mon) if not mon then return "" end + if require("src.core.game3.pokemon").isEgg(mon) then return Strings("EGG") end if mon.nickname and mon.nickname ~= "" then return tostring(mon.nickname) end return speciesName(speciesOf(mon)) end diff --git a/src/core/game3/trade_scene.lua b/src/core/game3/trade_scene.lua index 4688520f..be5318fc 100644 --- a/src/core/game3/trade_scene.lua +++ b/src/core/game3/trade_scene.lua @@ -212,8 +212,11 @@ local function cryFinished(s) return done and true or false end +-- pokefirered/src/trade_scene.c:1239 GetMonData(MON_DATA_NICKNAME), which is +-- gText_EggNickname for an egg (pokemon.c:3020) local function nameOf(mon) if not mon then return "" end + if isEgg(mon) then return Strings("EGG") end local nick = mon.nickname or mon.name if type(nick) == "string" and nick ~= "" then return nick end local ok, Pokemon = pcall(require, "src.core.game3.pokemon") diff --git a/src/ui/game3/pc_chrome.lua b/src/ui/game3/pc_chrome.lua index 84997fb2..b41e5023 100644 --- a/src/ui/game3/pc_chrome.lua +++ b/src/ui/game3/pc_chrome.lua @@ -205,8 +205,8 @@ function PcChrome.drawLeftDataPanel(hoveredMon, hoverFrame) if not hoveredMon then return end -- 1. Front Sprite in TV Screen (X: 10..73, Y: 19..80, W: 64, H: 61) - local sp = Pokemon.speciesOf(hoveredMon) - local sprite = Pokemon.frontPic(sp) + -- pokefirered/src/pokemon_storage_system_data.c:1034, :1057 MON_DATA_SPECIES_OR_EGG + local sprite = Pokemon.frontPic(Pokemon.speciesOrEgg(hoveredMon)) if sprite and sprite.image then love.graphics.setColor(1, 1, 1, 1) local sw, sh = sprite.image:getDimensions() @@ -229,6 +229,10 @@ function PcChrome.drawLeftDataPanel(hoveredMon, hoverFrame) end local lvl = hoveredMon.level or 5 local gender = hoveredMon.gender or (hoveredMon.personality and ((hoveredMon.personality % 256 < 127) and "F" or "M")) + -- pokefirered/src/pokemon_storage_system_data.c:1091: an egg shows only + -- gText_EggNickname; the species, gender/level and item lines stay blank. + local isEgg = Pokemon.isEgg(hoveredMon) + if isEgg then nick = Strings("EGG") end -- Line 1: Nickname or Species Name (FONT_NORMAL, Y: 88) FrlgFont.draw(nick:sub(1, 10), 6, 88, { @@ -236,32 +240,34 @@ function PcChrome.drawLeftDataPanel(hoveredMon, hoverFrame) colors = FrlgFont.COLOR.WHITE }) - -- Line 2: /Species Name (FONT_NORMAL, Y: 102) - FrlgFont.draw("/" .. spName:sub(1, 10), 6, 102, { - small = false, - colors = FrlgFont.COLOR.WHITE - }) + if not isEgg then + -- Line 2: /Species Name (FONT_NORMAL, Y: 102) + FrlgFont.draw("/" .. spName:sub(1, 10), 6, 102, { + small = false, + colors = FrlgFont.COLOR.WHITE + }) - -- Line 3: Gender & Level (FONT_NORMAL, Y: 116) - if gender == "M" or gender == "male" then - FrlgFont.draw("♂", 6, 116, { small = false, colors = FrlgFont.COLOR.MALE }) - FrlgFont.draw(Strings("Lv%s", tostring(lvl)), 18, 116, { small = false, colors = FrlgFont.COLOR.WHITE }) - elseif gender == "F" or gender == "female" then - FrlgFont.draw("♀", 6, 116, { small = false, colors = FrlgFont.COLOR.FEMALE }) - FrlgFont.draw(Strings("Lv%s", tostring(lvl)), 18, 116, { small = false, colors = FrlgFont.COLOR.WHITE }) - else - FrlgFont.draw(Strings("Lv%s", tostring(lvl)), 6, 116, { small = false, colors = FrlgFont.COLOR.WHITE }) - end + -- Line 3: Gender & Level (FONT_NORMAL, Y: 116) + if gender == "M" or gender == "male" then + FrlgFont.draw("♂", 6, 116, { small = false, colors = FrlgFont.COLOR.MALE }) + FrlgFont.draw(Strings("Lv%s", tostring(lvl)), 18, 116, { small = false, colors = FrlgFont.COLOR.WHITE }) + elseif gender == "F" or gender == "female" then + FrlgFont.draw("♀", 6, 116, { small = false, colors = FrlgFont.COLOR.FEMALE }) + FrlgFont.draw(Strings("Lv%s", tostring(lvl)), 18, 116, { small = false, colors = FrlgFont.COLOR.WHITE }) + else + FrlgFont.draw(Strings("Lv%s", tostring(lvl)), 6, 116, { small = false, colors = FrlgFont.COLOR.WHITE }) + end - -- Line 4: Held Item Name (if holding an item) (FONT_SMALL, Y: 132) - local held = hoveredMon.heldItem or hoveredMon.item - if held and held > 0 then - local heldName = ItemsData.displayName(held) - if heldName and heldName ~= "" and heldName ~= "NONE" then - FrlgFont.draw(heldName:sub(1, 10), 6, 132, { - small = true, - colors = FrlgFont.COLOR.WHITE - }) + -- Line 4: Held Item Name (if holding an item) (FONT_SMALL, Y: 132) + local held = hoveredMon.heldItem or hoveredMon.item + if held and held > 0 then + local heldName = ItemsData.displayName(held) + if heldName and heldName ~= "" and heldName ~= "NONE" then + FrlgFont.draw(heldName:sub(1, 10), 6, 132, { + small = true, + colors = FrlgFont.COLOR.WHITE + }) + end end end diff --git a/src/ui/game3/summary_menu.lua b/src/ui/game3/summary_menu.lua index fb18e4b1..a969188b 100644 --- a/src/ui/game3/summary_menu.lua +++ b/src/ui/game3/summary_menu.lua @@ -56,7 +56,7 @@ end -- pokefirered/src/pokemon_summary_screen.c:5180 local function play_mon_cry() local mon = current_mon() - if not mon or mon.isEgg then return end + if not mon or Pokemon.isEgg(mon) then return end local species = Pokemon.speciesOf(mon) if not species then return end local okA, Audio = pcall(require, "src.core.game3.audio") @@ -153,7 +153,7 @@ function SummaryMenu.openMenu(party, startIndex, opts) SummaryMenu._slide.active = false local mon = current_mon() - if mon and mon.isEgg then + if mon and Pokemon.isEgg(mon) then SummaryMenu._page = PAGE_EGG elseif SummaryMenu._mode == "select_move" then SummaryMenu._page = PAGE_MOVES_INFO @@ -194,7 +194,7 @@ local function change_mon(delta) SummaryMenu._moveCursor = 1 SummaryMenu._swapSlot = nil local mon = current_mon() - if mon and mon.isEgg then + if mon and Pokemon.isEgg(mon) then SummaryMenu._page = PAGE_EGG elseif SummaryMenu._page == PAGE_EGG then SummaryMenu._page = PAGE_INFO @@ -676,7 +676,8 @@ local function draw_page_moves(mon, isDetail) end local function draw_page_egg(mon) - local species = Pokemon.speciesOf(mon) + -- pokefirered/src/pokemon_summary_screen.c:4016 MON_DATA_SPECIES_OR_EGG + local species = Pokemon.speciesOrEgg(mon) local nx, ny = cxy("name", 40, 18) draw_text(Strings("EGG"), nx, ny, 64, "NORMAL") @@ -759,7 +760,7 @@ function SummaryMenu.draw() end -- 2. OVERLAY LAYER (Foreground elements anchored to screen coordinates) - draw_top_bar_text(SummaryMenu._page, mon.isEgg) + draw_top_bar_text(SummaryMenu._page, Pokemon.isEgg(mon)) if SummaryMenu._page == PAGE_EGG then draw_page_egg(mon) else diff --git a/src/ui/game3/trade_scene.lua b/src/ui/game3/trade_scene.lua index ca7e9374..d8015ca3 100644 --- a/src/ui/game3/trade_scene.lua +++ b/src/ui/game3/trade_scene.lua @@ -257,23 +257,24 @@ function TradeSceneUi.draw() love.graphics.draw(art.linkMonShadow, 129 - w2, (s.crossMonBy or -10) + (s.crossY2b or 0) - h2) end + -- pokefirered/src/trade_scene.c:757 draws every mon by MON_DATA_SPECIES_OR_EGG -- pokefirered/src/trade_scene.c:1541 if s.crossMonVisible then - draw_mon(Pokemon.frontPic(s.offer and (s.offer.species or s.offer.speciesId)), + draw_mon(Pokemon.frontPic(Pokemon.speciesOrEgg(s.offer)), 60, 192 + (s.monY2a or 0), 1) - draw_mon(Pokemon.frontPic(s.received and (s.received.species or s.received.speciesId)), + draw_mon(Pokemon.frontPic(Pokemon.speciesOrEgg(s.received)), 180, -32 + (s.monY2b or 0), 1) end -- pokefirered/src/trade_scene.c:772 if s.playerVisible then - draw_mon(Pokemon.frontPic(s.offer and (s.offer.species or s.offer.speciesId)), + draw_mon(Pokemon.frontPic(Pokemon.speciesOrEgg(s.offer)), 120 + (s.monX2 or 0), 60, s.monScale or 1) end -- pokefirered/src/trade_scene.c:1716 if s.partnerVisible then - draw_mon(Pokemon.frontPic(s.received and (s.received.species or s.received.speciesId)), + draw_mon(Pokemon.frontPic(Pokemon.speciesOrEgg(s.received)), 120, 60, 1) end diff --git a/tests/engine/game3_egg_icon_species_test.lua b/tests/engine/game3_egg_icon_species_test.lua index e80a7efb..744f7538 100644 --- a/tests/engine/game3_egg_icon_species_test.lua +++ b/tests/engine/game3_egg_icon_species_test.lua @@ -19,11 +19,12 @@ check(Pokemon.speciesOrEgg({ species = 172, isEgg = false }) == 172, "a hatched check(Pokemon.speciesOrEgg({ species = 25 }) == 25, "and so does any other mon") check(Pokemon.speciesOrEgg(nil) == nil, "no mon, no species") --- Every screen that draws a party or box mon's icon goes through it. +-- Every screen that draws a party or box mon's icon goes through it (the PC +-- chrome's third call is its hovered-mon front pic). local SITES = { { "src/ui/game3/party_menu.lua", 1 }, { "src/ui/game3/box_storage_ui.lua", 2 }, - { "src/ui/game3/pc_chrome.lua", 2 }, + { "src/ui/game3/pc_chrome.lua", 3 }, { "src/ui/game3/release_seq.lua", 1 }, } for _, site in ipairs(SITES) do @@ -31,7 +32,7 @@ for _, site in ipairs(SITES) do local src = f:read("*a") f:close() local n = select(2, src:gsub("Pokemon%.speciesOrEgg%(", "")) - check(n == site[2], ("%s picks %d icon(s) by speciesOrEgg (found %d)"):format(site[1], site[2], n)) + check(n == site[2], ("%s draws %d mon(s) by speciesOrEgg (found %d)"):format(site[1], site[2], n)) end T.finish("game3_egg_icon_species_test") diff --git a/tests/engine/game3_egg_screens_test.lua b/tests/engine/game3_egg_screens_test.lua new file mode 100644 index 00000000..69b519a6 --- /dev/null +++ b/tests/engine/game3_egg_screens_test.lua @@ -0,0 +1,119 @@ +#!/usr/bin/env luajit +-- The screens that show a single mon draw an egg as an egg, as pret does with +-- GetMonData(MON_DATA_SPECIES_OR_EGG) and MON_DATA_NICKNAME +-- (pokefirered/src/pokemon.c:3245, :3020): the PC's hovered-mon panel, the +-- summary's egg page, the trade scene, and the script string buffers. + +package.path = "./?.lua;./?/init.lua;" .. package.path + +local T = require("tests.harness") +local check = T.check + +require("src.core.GameVersion").set("firered") +package.loaded["src.core.game3.audio"] = setmetatable({}, { __index = function() return function() end end }) + +local gfx = setmetatable({}, { __index = function() return function() end end }) +gfx.newQuad = function() return {} end +gfx.newImage = function() return nil end +gfx.getWidth = function() return 240 end +gfx.getHeight = function() return 160 end +_G.love = { graphics = gfx, timer = { getTime = function() return 0 end } } + +local Strings = require("src.core.Strings") +local Pokemon = require("src.core.game3.pokemon") +Pokemon._names = { [25] = "PIKACHU", [172] = "PICHU" } -- a minimal pack, so no ROM is needed +Strings.load({ strings = { EGG = "OEUF" } }) + +local pics = {} +Pokemon.frontPic = function(species) + pics[#pics + 1] = species + return { image = { getDimensions = function() return 64, 64 end }, w = 64, h = 64 } +end +local texts = {} +local FrlgFont = require("src.ui.game3.frlg_font") +FrlgFont.draw = function(text) texts[#texts + 1] = tostring(text) end + +local function egg() + return { species = 172, isEgg = true, nickname = "EGG", name = "EGG", level = 5, + heldItem = 1, gender = "M", personality = 1, otName = "RED", otId = 1, + hp = 1, maxHp = 1, moves = {}, pp = {} } +end +local function has(list, value) + for _, v in ipairs(list) do if v == value then return true end end + return false +end + +-- pokefirered/src/pokemon_storage_system_data.c:1034, :1091 +local PcChrome = require("src.ui.game3.pc_chrome") +PcChrome.ensure = function() end +PcChrome.drawWaveforms = function() end +pics, texts = {}, {} +PcChrome.drawLeftDataPanel(egg(), 0) +check(pics[1] == 412, "the PC panel draws an egg's front pic as the EGG's (got " .. tostring(pics[1]) .. ")") +check(texts[1] == "OEUF", "and names it by the language's EGG (got " .. tostring(texts[1]) .. ")") +check(not has(texts, "/PICHU") and not has(texts, "Lv5"), + "with no species, level or item line: " .. table.concat(texts, " | ")) +pics, texts = {}, {} +PcChrome.drawLeftDataPanel({ species = 172, nickname = "PICHU", level = 5, gender = "M" }, 0) +check(pics[1] == 172 and has(texts, "/PICHU") and has(texts, "Lv5"), + "a hatched mon keeps its pic, species and level lines") + +-- pokefirered/src/pokemon_summary_screen.c:4016 +local SummaryMenu = require("src.ui.game3.summary_menu") +local SummaryChrome = require("src.ui.game3.summary_chrome") +for k, v in pairs(SummaryChrome) do + if type(v) == "function" and k:match("^draw") then SummaryChrome[k] = function() end end +end +pics = {} +SummaryMenu.openMenu({ egg() }, 1, { page = 0 }) +SummaryMenu.draw() +SummaryMenu.close() +check(has(pics, 412) and not has(pics, 172), + "the summary's egg page draws the EGG's front pic (got " .. table.concat(pics, ",") .. ")") +pics = {} +local flagOnly = egg() +flagOnly.isEgg, flagOnly.egg = nil, true +SummaryMenu.openMenu({ flagOnly }, 1, { page = 0 }) +SummaryMenu.draw() +SummaryMenu.close() +check(has(pics, 412) and not has(pics, 172), "for any egg flag (got " .. table.concat(pics, ",") .. ")") + +-- pokefirered/src/trade_scene.c:757, :1239 +local TradeSceneUi = require("src.ui.game3.trade_scene") +local offer, received = egg(), { species = 25, nickname = "SPARKY" } +TradeSceneUi._core = { state = function() + return { playerVisible = true, partnerVisible = true, offer = offer, received = received } +end } +pics = {} +TradeSceneUi.draw() +check(pics[1] == 412 and pics[2] == 25, + "the trade scene draws a sent egg as the EGG (got " .. table.concat(pics, ",") .. ")") +TradeSceneUi._core = nil +local savedLove = _G.love +_G.love = nil +local TradeScene = require("src.core.game3.trade_scene") +TradeScene.play(offer, received, nil, {}) +local st = TradeScene.state() +check(st.sentName == "OEUF", "and names it by the language's EGG (got " .. tostring(st.sentName) .. ")") +check(st.recvName == "SPARKY", "while the other mon keeps its nickname") +_G.love = savedLove + +-- pokefirered/src/field_specials.c:1671 BufferMonNickname, daycare.c:1216 +local Ctx = require("src.core.game3.scripting.ctx") +local Flags = require("src.core.game3.scripting.flags") +local Std = require("src.core.game3.scripting.stdscripts") +local Natives = require("src.core.game3.scripting.natives") +package.loaded["src.core.game3.runtime"] = { + getSession = function() return { party = { egg() } } end, + isActive = function() return true end, +} +local ctx = Ctx.new({}) +ctx.mode, ctx.status = "bytecode", "running" +Flags.setVar(nil, ctx, 0x8004, 0) +Natives.special(ctx, Std.SPECIAL.GetSelectedMonNicknameAndSpecies, { log = function() end }) +check(ctx.stringVars and ctx.stringVars[1] == "OEUF", + "a script buffering an egg's nickname gets the language's EGG (got " + .. tostring(ctx.stringVars and ctx.stringVars[1]) .. ")") + +Strings.load({}) +T.finish("game3_egg_screens_test") From 32d52b88c075f32b2553623dbbfe9385fdba174f Mon Sep 17 00:00:00 2001 From: thibautbus <310327033+thibautbus@users.noreply.github.com> Date: Tue, 22 Sep 2026 12:47:50 +0200 Subject: [PATCH 4/4] Show only an egg's name in its FireRed party slot The party menu drew an egg's slot like any other mon's: level, gender, HP numbers and HP bar. pret's DisplayPartyPokemonData prints only the nickname for an egg and blits the slot without its HP frame (sSlotTilemap_MainNoHP / WideNoHP; pokefirered/src/party_menu.c:781, :2197), and DisplayPartyPokemonDescriptionData likewise skips the level and gender of an egg (party_menu.c:804). The slot now does the same, reusing the no-HP frame the description slots already draw. --- src/ui/game3/party_menu.lua | 9 ++++++++- tests/engine/game3_egg_screens_test.lua | 20 ++++++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/src/ui/game3/party_menu.lua b/src/ui/game3/party_menu.lua index e01474fb..f6782538 100644 --- a/src/ui/game3/party_menu.lua +++ b/src/ui/game3/party_menu.lua @@ -2111,10 +2111,17 @@ local function draw_filled_slot(i, mon, selected) local info = slot_info(i) local desc = slot_description(i, mon) - PartyChrome.drawSlot(win.kind, win.left, win.top, selected, desc ~= nil) + -- pokefirered/src/party_menu.c:781 DisplayPartyPokemonData: an egg's slot + -- has no HP frame and shows only its nickname (gText_EggNickname). + local isEgg = Pokemon.isEgg(mon) + PartyChrome.drawSlot(win.kind, win.left, win.top, selected, desc ~= nil or isEgg) local name = Pokemon.displayName(mon) party_print(name, baseX + info.nick[1], baseY + info.nick[2], 56) + if isEgg then + if desc then party_print(desc, baseX + info.desc[1], baseY + info.desc[2], 64) end + return + end party_print("Lv" .. tostring(mon.level or 0), baseX + info.level[1], baseY + info.level[2], 32) local gender = mon.gender or (Pokemon.gender and Pokemon.gender(mon.species, mon.personality)) diff --git a/tests/engine/game3_egg_screens_test.lua b/tests/engine/game3_egg_screens_test.lua index 69b519a6..e5fa3191 100644 --- a/tests/engine/game3_egg_screens_test.lua +++ b/tests/engine/game3_egg_screens_test.lua @@ -1,8 +1,8 @@ #!/usr/bin/env luajit -- The screens that show a single mon draw an egg as an egg, as pret does with -- GetMonData(MON_DATA_SPECIES_OR_EGG) and MON_DATA_NICKNAME --- (pokefirered/src/pokemon.c:3245, :3020): the PC's hovered-mon panel, the --- summary's egg page, the trade scene, and the script string buffers. +-- (pokefirered/src/pokemon.c:3245, :3020): the party slots, the PC's hovered-mon +-- panel, the summary's egg page, the trade scene, and the script string buffers. package.path = "./?.lua;./?/init.lua;" .. package.path @@ -58,6 +58,22 @@ PcChrome.drawLeftDataPanel({ species = 172, nickname = "PICHU", level = 5, gende check(pics[1] == 172 and has(texts, "/PICHU") and has(texts, "Lv5"), "a hatched mon keeps its pic, species and level lines") +-- pokefirered/src/party_menu.c:781 DisplayPartyPokemonData, :2197 sSlotTilemap_MainNoHP +local PartyMenu = require("src.ui.game3.party_menu") +local PartyChrome = require("src.ui.game3.party_chrome") +local slotsHidingHp = {} +PartyChrome.drawSlot = function(kind, _, _, _, hideHp) + if kind ~= "empty" then slotsHidingHp[#slotsHidingHp + 1] = hideHp and true or false end +end +texts, slotsHidingHp = {}, {} +PartyMenu.show({ { species = 25, nickname = "SPARKY", level = 12, gender = "M", hp = 30, maxHp = 30 }, egg() }) +PartyMenu.draw() +PartyMenu.close() +check(has(texts, "OEUF") and has(texts, "SPARKY"), "the party names both mons: " .. table.concat(texts, " | ")) +check(not has(texts, "Lv5") and has(texts, "Lv12"), "an egg's party slot shows no level, the other one does") +check(slotsHidingHp[1] == false and slotsHidingHp[2] == true, + "and an egg's slot has no HP frame (hideHp = " .. tostring(slotsHidingHp[1]) .. ", " .. tostring(slotsHidingHp[2]) .. ")") + -- pokefirered/src/pokemon_summary_screen.c:4016 local SummaryMenu = require("src.ui.game3.summary_menu") local SummaryChrome = require("src.ui.game3.summary_chrome")