mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-07 17:31:35 -04:00
Merge pull request #2396 from thibautbus/fix/game3-egg-nickname
This commit is contained in:
@@ -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
|
||||
@@ -1246,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"
|
||||
@@ -1572,6 +1589,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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]
|
||||
|
||||
@@ -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({
|
||||
@@ -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))
|
||||
|
||||
+34
-28
@@ -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
|
||||
|
||||
@@ -381,7 +387,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 +407,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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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",
|
||||
}
|
||||
|
||||
|
||||
@@ -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")
|
||||
@@ -0,0 +1,38 @@
|
||||
#!/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 (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", 3 },
|
||||
{ "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 draws %d mon(s) by speciesOrEgg (found %d)"):format(site[1], site[2], n))
|
||||
end
|
||||
|
||||
T.finish("game3_egg_icon_species_test")
|
||||
@@ -0,0 +1,135 @@
|
||||
#!/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 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
|
||||
|
||||
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/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")
|
||||
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")
|
||||
@@ -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",
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user