mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-09-26 13:33:27 -04:00
aaacf02dc0
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.
404 lines
18 KiB
Lua
404 lines
18 KiB
Lua
#!/usr/bin/env luajit
|
|
-- src/script_menu.c:1161, src/region_map.c:790, src/learn_move.c:403,
|
|
-- src/battle_records.c:563, src/trainer_card.c:265, :1454, :1560
|
|
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local failed = 0
|
|
local function check(cond, msg)
|
|
if cond then
|
|
print("[ok] " .. msg)
|
|
else
|
|
failed = failed + 1
|
|
print("[FAIL] " .. msg)
|
|
end
|
|
end
|
|
|
|
local function eq(a, b, msg)
|
|
check(a == b, string.format("%s (%s == %s)", msg, tostring(a), tostring(b)))
|
|
end
|
|
|
|
local function finish()
|
|
if failed > 0 then
|
|
print(string.format("[result] %d CHECK(S) FAILED", failed))
|
|
os.exit(1)
|
|
end
|
|
print("[result] all checks passed")
|
|
os.exit(0)
|
|
end
|
|
|
|
local CacheContract = require("src.import.CacheContract")
|
|
local Versions = require("src.import.gba.versions")
|
|
local MuseumExtract = require("src.import.gba.museum_extract")
|
|
local MoveRelearnerExtract = require("src.import.gba.move_relearner_extract")
|
|
local TrainerTowerExtract = require("src.import.gba.trainer_tower_extract")
|
|
local TrainerCardExtract = require("src.import.gba.trainer_card_extract")
|
|
local RegionMapExtract = require("src.import.gba.region_map_extract")
|
|
local EggExtract = require("src.import.gba.egg_extract")
|
|
|
|
print("[test] 1. the ROM offsets follow pret's declaration order")
|
|
-- src/script_menu.c:647 aerodactyl tiles, palette, then kabutops tiles, palette
|
|
eq(Versions.MUSEUM_AERODACTYL_GFX, 0x3E0780, "MUSEUM_AERODACTYL_GFX is sMuseumAerodactylSprTiles")
|
|
eq(Versions.MUSEUM_AERODACTYL_GFX + 64 * 64 / 2, Versions.MUSEUM_AERODACTYL_PAL,
|
|
"the aerodactyl palette follows its 2048-byte 64x64 sheet")
|
|
eq(Versions.MUSEUM_AERODACTYL_PAL + 32, Versions.MUSEUM_KABUTOPS_GFX,
|
|
"the kabutops sheet follows the aerodactyl palette bank")
|
|
eq(Versions.MUSEUM_KABUTOPS_GFX + 64 * 64 / 2, Versions.MUSEUM_KABUTOPS_PAL,
|
|
"the kabutops palette follows its sheet")
|
|
eq(Versions.MUSEUM_FOSSIL_SIZE, 64, "the fossil sprite is SPRITE_SIZE(64x64)")
|
|
-- src/learn_move.c:403, pokefirered.map:34302
|
|
eq(Versions.MOVE_RELEARNER_PAL, 0xE97DDC, "MOVE_RELEARNER_PAL is gMoveRelearner_Pal")
|
|
eq(Versions.MOVE_RELEARNER_PAL + 32, Versions.MOVE_RELEARNER_GFX,
|
|
"gMoveRelearner_Gfx follows the one loaded palette bank")
|
|
eq(Versions.MOVE_RELEARNER_TILEMAP, 0xE97EC4, "MOVE_RELEARNER_TILEMAP is gMoveRelearner_Tilemap")
|
|
-- src/battle_records.c:37 sTiles, :38 sPalette, :39 sTilemap
|
|
eq(Versions.BATTLE_RECORDS_GFX + 0xC0, Versions.BATTLE_RECORDS_PAL,
|
|
"sPalette follows sTiles' 0xC0 bytes")
|
|
eq(Versions.BATTLE_RECORDS_PAL + 32, Versions.BATTLE_RECORDS_TILEMAP,
|
|
"sTilemap follows the one palette bank")
|
|
-- src/trainer_card.c:174 sKantoTrainerCardBadges_Pal, not the Hoenn bank one slot earlier
|
|
eq(Versions.TRAINER_CARD_BADGES_PAL, 0x3CD2E0, "TRAINER_CARD_BADGES_PAL is the Kanto bank")
|
|
eq(Versions.TRAINER_CARD_BADGES_PAL + 32, Versions.TRAINER_CARD_STAR_PAL,
|
|
"sTrainerCardStar_Pal follows sKantoTrainerCardBadges_Pal")
|
|
eq(Versions.TRAINER_CARD_STAR_PAL + 32, Versions.TRAINER_CARD_STICKER_PAL1,
|
|
"sTrainerCardStickerPal1 follows sTrainerCardStar_Pal")
|
|
for i, off in ipairs({ Versions.TRAINER_CARD_STICKER_PAL2, Versions.TRAINER_CARD_STICKER_PAL3,
|
|
Versions.TRAINER_CARD_STICKER_PAL4 }) do
|
|
eq(Versions.TRAINER_CARD_STICKER_PAL1 + 32 * i, off,
|
|
"sTrainerCardStickerPal" .. (i + 1) .. " is one bank on")
|
|
end
|
|
for i, off in ipairs({ Versions.TRAINER_CARD_BRONZE_PAL, Versions.TRAINER_CARD_SILVER_PAL,
|
|
Versions.TRAINER_CARD_GOLD_PAL }) do
|
|
eq(Versions.TRAINER_CARD_GREEN_PAL + 0xC0 * i, off,
|
|
"the " .. (i + 1) .. "-star palette is 0xC0 past the green one")
|
|
end
|
|
eq(Versions.TRAINER_CARD_STAR_TILE, 143, "the star is card tile 143")
|
|
-- src/daycare.c:137 sEggPalette, then the two raw 4bpp sheets
|
|
eq(Versions.EGG_PALETTE, 0x25F842, "EGG_PALETTE is sEggPalette")
|
|
eq(Versions.EGG_PALETTE + 32, Versions.EGG_HATCH_GFX, "sEggHatchTiles follows the one egg bank")
|
|
eq(Versions.EGG_HATCH_GFX + 2048, Versions.EGG_SHARD_GFX,
|
|
"sEggShardTiles follows the four 32x32 hatch frames")
|
|
eq(EggExtract.SPECIES_EGG, 412, "the EGG is species 412, one past the last species")
|
|
|
|
print("[test] 2. the cache contract requires every new key")
|
|
local required = CacheContract.requiredFiles("firered")
|
|
local have = {}
|
|
for _, path in ipairs(required) do have[path] = true end
|
|
local WANT = {
|
|
"data/generated/gba/museum/manifest.lua",
|
|
"data/generated/gba/museum/kabutops.rgba",
|
|
"data/generated/gba/museum/aerodactyl.rgba",
|
|
"data/generated/gba/region_map/dungeon_icon_visited.rgba",
|
|
"data/generated/gba/region_map/dungeon_icon_visited.png",
|
|
"data/generated/gba/move_relearner/manifest.lua",
|
|
"data/generated/gba/move_relearner/bg.rgba",
|
|
"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",
|
|
"data/generated/gba/trainer_card/back_0.rgba",
|
|
"data/generated/gba/trainer_card/back_4_female.rgba",
|
|
"data/generated/gba/trainer_card/screen_0.rgba",
|
|
"data/generated/gba/trainer_card/star.rgba",
|
|
"data/generated/gba/trainer_card/stickers.rgba",
|
|
"data/generated/gba/teachy_tv/static.rgba",
|
|
"data/generated/gba/teachy_tv/bg3.rgba",
|
|
}
|
|
for _, path in ipairs(WANT) do
|
|
check(have[path] == true, "the firered contract requires " .. path)
|
|
end
|
|
for _, path in ipairs(WANT) do
|
|
local fs = {
|
|
prefix = "",
|
|
exists = function(candidate) return candidate ~= path end,
|
|
}
|
|
local complete, missing = CacheContract.allRequiredFilesExist("firered", fs)
|
|
check(complete == false and missing == path,
|
|
"a cache without " .. path .. " is incomplete (" .. tostring(missing) .. ")")
|
|
end
|
|
|
|
print("[test] 3. ready() refuses a half-written group, so an old cache repairs")
|
|
local written = {}
|
|
local stub = {
|
|
write = function(_, rel, bytes) written[rel] = bytes end,
|
|
read = function(_, rel) return written[rel] end,
|
|
exists = function(_, rel) return written[rel] ~= nil end,
|
|
}
|
|
check(MuseumExtract.ready(stub, "x") == false, "museum ready() is false with nothing written")
|
|
written["x/museum/kabutops.rgba"] = "stub"
|
|
written["x/museum/manifest.lua"] = "stub"
|
|
check(MuseumExtract.ready(stub, "x") == false, "museum ready() is false without aerodactyl")
|
|
written["x/museum/aerodactyl.rgba"] = "stub"
|
|
check(MuseumExtract.ready(stub, "x") == true, "museum ready() accepts both fossils")
|
|
|
|
check(MoveRelearnerExtract.ready(stub, "x") == false, "relearner ready() is false with no bg")
|
|
written["x/move_relearner/bg.rgba"] = "stub"
|
|
check(MoveRelearnerExtract.ready(stub, "x") == false, "relearner ready() wants its manifest too")
|
|
written["x/move_relearner/manifest.lua"] = "stub"
|
|
check(MoveRelearnerExtract.ready(stub, "x") == true, "relearner ready() accepts the pair")
|
|
|
|
written["x/trainer_tower.lua"] = "stub"
|
|
check(TrainerTowerExtract.ready(stub, "x") == false,
|
|
"tower ready() is false with the floor table but no records background")
|
|
written["x/trainer_tower/records_bg.rgba"] = "stub"
|
|
written["x/trainer_tower/manifest.lua"] = "stub"
|
|
check(TrainerTowerExtract.ready(stub, "x") == true, "tower ready() accepts table plus records art")
|
|
|
|
local cardCache = {}
|
|
local cardStub = {
|
|
read = function(_, rel) return cardCache[rel] end,
|
|
}
|
|
cardCache["x/trainer_card/bg.rgba"] = string.rep("z", 240 * 160 * 4)
|
|
check(TrainerCardExtract.ready(cardStub, "x") == false,
|
|
"card ready() is false with only the legacy bg composite")
|
|
cardCache["x/trainer_card/back_0.rgba"] = string.rep("z", 240 * 160 * 4)
|
|
cardCache["x/trainer_card/star.rgba"] = string.rep("z", 8 * 8 * 4)
|
|
check(TrainerCardExtract.ready(cardStub, "x") == false, "card ready() still wants the stickers")
|
|
cardCache["x/trainer_card/stickers.rgba"] = string.rep("z", 64 * 64 * 4)
|
|
check(TrainerCardExtract.ready(cardStub, "x") == true, "card ready() accepts the full set")
|
|
|
|
check(EggExtract.ready(stub, "x") == false, "egg ready() is false with no hatch sheet")
|
|
written["x/pokemon/egg/hatch.rgba"] = "stub"
|
|
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 = {}
|
|
for _, name in ipairs(RegionMapExtract.FILES) do regionFiles[name] = true end
|
|
check(regionFiles["dungeon_icon_visited.png"] == true,
|
|
"region_map ready() lists the visited dungeon marker")
|
|
|
|
print("[test] 4. a built cache carries the baked art")
|
|
local Cache = require("tests.game3_cache")
|
|
local root = Cache.root("museum/manifest.lua")
|
|
if not root then
|
|
print("[skip] museum, marker, relearner, records and trainer card art: "
|
|
.. tostring(Cache.reason))
|
|
finish()
|
|
end
|
|
print("[info] FireRed cache at " .. root)
|
|
|
|
local function readFile(rel)
|
|
local f = io.open(root .. "/" .. rel, "rb")
|
|
if not f then return nil end
|
|
local d = f:read("*a")
|
|
f:close()
|
|
return d
|
|
end
|
|
|
|
local function loadTable(rel)
|
|
local src = readFile(rel)
|
|
if not src then return nil end
|
|
local chunk = (loadstring or load)(src)
|
|
if not chunk then return nil end
|
|
local ok, t = pcall(chunk)
|
|
if ok and type(t) == "table" then return t end
|
|
return nil
|
|
end
|
|
|
|
local function pixel(blob, w, x, y)
|
|
local o = (y * w + x) * 4
|
|
return blob:byte(o + 1), blob:byte(o + 2), blob:byte(o + 3), blob:byte(o + 4)
|
|
end
|
|
|
|
local function distinctColors(blob)
|
|
local seen, n = {}, 0
|
|
for i = 1, #blob, 4 do
|
|
local key = blob:sub(i, i + 3)
|
|
if not seen[key] then
|
|
seen[key] = true
|
|
n = n + 1
|
|
end
|
|
end
|
|
return n
|
|
end
|
|
|
|
local function opaquePixels(blob)
|
|
local n = 0
|
|
for i = 4, #blob, 4 do
|
|
if blob:byte(i) == 255 then n = n + 1 end
|
|
end
|
|
return n
|
|
end
|
|
|
|
local SHEETS = {
|
|
{ "museum/kabutops.rgba", 64, 64 },
|
|
{ "museum/aerodactyl.rgba", 64, 64 },
|
|
{ "region_map/dungeon_icon.rgba", 8, 8 },
|
|
{ "region_map/dungeon_icon_visited.rgba", 8, 8 },
|
|
{ "move_relearner/bg.rgba", 240, 160 },
|
|
{ "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 },
|
|
{ "trainer_card/back_0.rgba", 240, 160 },
|
|
{ "trainer_card/back_4.rgba", 240, 160 },
|
|
{ "trainer_card/screen_0.rgba", 240, 160 },
|
|
{ "trainer_card/star.rgba", 8, 8 },
|
|
{ "trainer_card/stickers.rgba", 64, 64 },
|
|
}
|
|
local blobs = {}
|
|
for _, sheet in ipairs(SHEETS) do
|
|
local rel, w, h = sheet[1], sheet[2], sheet[3]
|
|
local blob = readFile(rel)
|
|
blobs[rel] = blob
|
|
eq(blob and #blob or nil, w * h * 4, rel .. " is " .. w .. "x" .. h)
|
|
end
|
|
|
|
print("[test] 5. the museum fossil pictures are the cart's sprites")
|
|
-- src/script_menu.c:1173 CreateSprite(&sMuseumFossilSprTemplate), OAM colour 0 transparent
|
|
check(distinctColors(blobs["museum/kabutops.rgba"]) > 4, "the KABUTOPS fossil is painted art")
|
|
check(distinctColors(blobs["museum/aerodactyl.rgba"]) > 4, "the AERODACTYL fossil is painted art")
|
|
check(blobs["museum/kabutops.rgba"] ~= blobs["museum/aerodactyl.rgba"],
|
|
"the two fossils are different pictures")
|
|
local _, _, _, kCorner = pixel(blobs["museum/kabutops.rgba"], 64, 0, 0)
|
|
eq(kCorner, 0, "the fossil sheet corner is transparent, as OAM index 0 is")
|
|
local kOpaque = opaquePixels(blobs["museum/kabutops.rgba"])
|
|
check(kOpaque > 500 and kOpaque < 64 * 64,
|
|
"the KABUTOPS skeleton covers part of the 64x64 frame (" .. kOpaque .. " px)")
|
|
local museumMan = loadTable("museum/manifest.lua")
|
|
check(type(museumMan) == "table", "museum/manifest.lua loads")
|
|
if museumMan then
|
|
eq(museumMan.width, 64, "the manifest carries the 64x64 size")
|
|
eq(museumMan.species.kabutops, 141, "the manifest names SPECIES_KABUTOPS")
|
|
eq(museumMan.species.aerodactyl, 142, "the manifest names SPECIES_AERODACTYL")
|
|
end
|
|
|
|
print("[test] 6. the second dungeon marker frame")
|
|
-- src/region_map.c:790 sAnim_DungeonIconVisited is ANIMCMD_FRAME(1), :795 not-visited is frame 0
|
|
check(blobs["region_map/dungeon_icon.rgba"] ~= blobs["region_map/dungeon_icon_visited.rgba"],
|
|
"frame 1 is different art from frame 0")
|
|
local blobOpaque = opaquePixels(blobs["region_map/dungeon_icon.rgba"])
|
|
local ringOpaque = opaquePixels(blobs["region_map/dungeon_icon_visited.rgba"])
|
|
check(blobOpaque > 0 and ringOpaque > 0, "both marker frames paint pixels")
|
|
check(ringOpaque > blobOpaque,
|
|
string.format("the visited ring covers more of the 8x8 cell than the unvisited blob (%d > %d)",
|
|
ringOpaque, blobOpaque))
|
|
local visitedPng = readFile("region_map/dungeon_icon_visited.png")
|
|
check(type(visitedPng) == "string" and visitedPng:sub(1, 8) == "\137PNG\r\n\026\n",
|
|
"dungeon_icon_visited.png is a PNG, which is what the Town Map loads")
|
|
|
|
print("[test] 7. the move relearner and battle records backgrounds")
|
|
-- src/learn_move.c:403, BG1 covers 19x15 tiles and leaves the rest on the backdrop
|
|
check(distinctColors(blobs["move_relearner/bg.rgba"]) > 4, "the relearner background is painted art")
|
|
local pr, pg, pb = pixel(blobs["move_relearner/bg.rgba"], 240, 8, 8)
|
|
check(pr > 100 and pg > 150 and pb > 150,
|
|
string.format("the relearner panel is the cart's pale green (%d,%d,%d)", pr, pg, pb))
|
|
local br, bg2, bb = pixel(blobs["move_relearner/bg.rgba"], 240, 239, 159)
|
|
check(br == 0 and bg2 == 0 and bb == 0,
|
|
"outside the 19x15 tile panel the relearner background is the backdrop colour")
|
|
local relearnerMan = loadTable("move_relearner/manifest.lua")
|
|
check(relearnerMan ~= nil and relearnerMan.width == 240 and relearnerMan.height == 160,
|
|
"move_relearner/manifest.lua is a 240x160 sheet")
|
|
check(distinctColors(blobs["trainer_tower/records_bg.rgba"]) > 4,
|
|
"the battle records board is painted art")
|
|
local towerMan = loadTable("trainer_tower/manifest.lua")
|
|
check(towerMan ~= nil and towerMan.recordsBg ~= nil and towerMan.recordsBg.width == 240,
|
|
"trainer_tower/manifest.lua carries recordsBg 240x160")
|
|
|
|
print("[test] 8. the five trainer card star palettes, the star and the stickers")
|
|
-- src/trainer_card.c:1484 LoadPalette(sKantoTrainerCardPals[stars], BG_PLTT_ID(0), 3 banks)
|
|
local legacy = readFile("trainer_card/bg.rgba")
|
|
check(legacy == blobs["trainer_card/front_0.rgba"],
|
|
"front_0 is the blue 0-star card the old bg.rgba already was")
|
|
check(readFile("trainer_card/bg_female.rgba") == readFile("trainer_card/front_0_female.rgba"),
|
|
"front_0_female matches the old bg_female.rgba")
|
|
local seen = {}
|
|
for stars = 0, 4 do
|
|
local blob = readFile(string.format("trainer_card/front_%d.rgba", stars))
|
|
eq(blob and #blob or nil, 240 * 160 * 4, "front_" .. stars .. " is 240x160")
|
|
check(seen[blob] == nil, "the " .. stars .. "-star front is its own palette")
|
|
seen[blob] = stars
|
|
end
|
|
check(blobs["trainer_card/back_0.rgba"] ~= blobs["trainer_card/front_0.rgba"],
|
|
"the back of the card is a different tilemap from the front")
|
|
check(blobs["trainer_card/screen_0.rgba"] ~= blobs["trainer_card/front_0.rgba"],
|
|
"the flip screen is the bg layer alone")
|
|
-- src/trainer_card.c:1494 the female override only repaints BG bank 1
|
|
check(blobs["trainer_card/front_4_female.rgba"] ~= readFile("trainer_card/front_4.rgba"),
|
|
"the female card differs from the male one")
|
|
-- src/trainer_card.c:1560 FillBgTilemapBufferRect(3, 143, ..., palette 4)
|
|
local starOpaque = opaquePixels(blobs["trainer_card/star.rgba"])
|
|
check(starOpaque > 8 and starOpaque < 64,
|
|
"the star tile paints a star on transparency (" .. starOpaque .. " px)")
|
|
-- src/trainer_card.c:1450 sticker slot i is the 2x2 group at tile 4i, palettes 11..14
|
|
local stickers = blobs["trainer_card/stickers.rgba"]
|
|
local rows = {}
|
|
for p = 0, 3 do
|
|
rows[p] = stickers:sub(p * 16 * 64 * 4 + 1, (p + 1) * 16 * 64 * 4)
|
|
end
|
|
for p = 1, 3 do
|
|
check(rows[p] ~= rows[0], "sticker row " .. p .. " is its own palette bank")
|
|
end
|
|
local slot0 = 0
|
|
for y = 0, 15 do
|
|
for x = 0, 15 do
|
|
local _, _, _, a = pixel(stickers, 64, x, y)
|
|
if a == 255 then slot0 = slot0 + 1 end
|
|
end
|
|
end
|
|
check(slot0 > 32, "sticker slot 0 paints its 16x16 cell (" .. slot0 .. " px)")
|
|
local cardMan = loadTable("trainer_card/manifest.lua")
|
|
check(cardMan ~= nil and cardMan.starCount == 5 and cardMan.stickerSlots == 4
|
|
and cardMan.stickerPalettes == 4,
|
|
"trainer_card/manifest.lua carries the star and sticker metrics")
|
|
|
|
print("[test] 9. the EGG front pic, the hatch frames and the shell shards")
|
|
-- src/daycare.c:158 the four hatch anims are frames 0, 16, 32 and 48 of one sheet
|
|
local hatch = blobs["pokemon/egg/hatch.rgba"]
|
|
local hatchFrames = {}
|
|
for f = 0, 3 do hatchFrames[f] = hatch:sub(f * 32 * 32 * 4 + 1, (f + 1) * 32 * 32 * 4) end
|
|
for f = 1, 3 do
|
|
check(hatchFrames[f] ~= hatchFrames[f - 1],
|
|
"hatch frame " .. f .. " cracks further than frame " .. (f - 1))
|
|
end
|
|
local cracked = 0
|
|
for i = 1, #hatchFrames[0], 4 do
|
|
if hatchFrames[0]:sub(i, i + 3) ~= hatchFrames[3]:sub(i, i + 3) then cracked = cracked + 1 end
|
|
end
|
|
check(cracked > 20,
|
|
string.format("the last hatch frame repaints the shell with cracks (%d px differ)", cracked))
|
|
-- src/daycare.c:243 the four shard anims are single 8x8 tiles, one after the other
|
|
local shard = blobs["pokemon/egg/shard.rgba"]
|
|
local shardCells = {}
|
|
for f = 0, 3 do
|
|
local cell, rows = 0, {}
|
|
for y = 0, 7 do
|
|
for x = 0, 7 do
|
|
local r, g, b, a = pixel(shard, 32, f * 8 + x, y)
|
|
rows[#rows + 1] = string.char(r or 0, g or 0, b or 0, a or 0)
|
|
if a == 255 then cell = cell + 1 end
|
|
end
|
|
end
|
|
check(cell > 0 and cell < 64, "shell shard " .. f .. " paints part of its tile (" .. cell .. " px)")
|
|
shardCells[f] = table.concat(rows)
|
|
end
|
|
for f = 1, 3 do
|
|
check(shardCells[f] ~= shardCells[f - 1], "shell shard " .. f .. " is its own piece")
|
|
end
|
|
local Pokemon = require("src.core.game3.pokemon")
|
|
eq(Pokemon.SPECIES_EGG, EggExtract.SPECIES_EGG,
|
|
"the engine and the extractor agree on the EGG species id")
|
|
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,
|
|
"pokemon/egg/manifest.lua carries both sheet layouts")
|
|
|
|
finish()
|