mirror of
https://github.com/bryanthaboi/gen1recomp
synced 2026-10-07 09:27:38 -04:00
125 lines
6.5 KiB
Lua
125 lines
6.5 KiB
Lua
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
love = require("tests.love_stub")
|
|
local T = require("tests.harness")
|
|
local Serializer = require("src.core.SaveSerializer")
|
|
local Catalog = require("src.box.Catalog")
|
|
local CacheFs = require("src.import.CacheFs")
|
|
local Sprites = require("src.online.OnlineSprites")
|
|
local MonAnim = require("src.core.game3.mon_anim")
|
|
local GameVersion = require("src.core.GameVersion")
|
|
local Data = MonAnim.Data
|
|
local bodies, images, draws, quads = {}, {}, {}, 0
|
|
local previousRead, previousArt = CacheFs.readAt, Catalog.art
|
|
local previousData = Data._cache
|
|
Data._cache = { sentinel = true }
|
|
CacheFs.readAt = function(path) return bodies[path] end
|
|
local front = {
|
|
functions = { [0] = "Anim_VerticalSquishBounce" },
|
|
animIds = { [1] = 0, [308] = 0 }, animDelays = { [1] = 2 },
|
|
species = { [1] = { 0, 1 }, [308] = { 0, 1 } },
|
|
lists = {
|
|
[0] = { cmds = { { frame = 0, duration = 0 }, { op = "end" } } },
|
|
[1] = { cmds = { { frame = 0, duration = 4 }, { frame = 1, duration = 4 },
|
|
{ frame = 0, duration = 4 }, { op = "end" } } },
|
|
},
|
|
}
|
|
bodies["emerald/data/generated/gba/pokemon/front_anims.lua"] = Serializer.encode(front)
|
|
local frameBytes = 64 * 64 * 4
|
|
bodies["emerald/data/generated/gba/pokemon/front_anim/1.rgba"] = string.rep("A", frameBytes) .. string.rep("B", frameBytes)
|
|
bodies["emerald/data/generated/gba/pokemon/front_anim_shiny/1.rgba"] = string.rep("S", frameBytes) .. string.rep("T", frameBytes)
|
|
local function image(raw, w, h)
|
|
local obj = { raw = raw, w = w or 64, h = h or 64 }
|
|
function obj:getDimensions() return self.w, self.h end
|
|
function obj:setFilter(a, b) self.filter = { a, b } end
|
|
function obj:release() self.released = true end
|
|
images[#images + 1] = obj
|
|
return obj
|
|
end
|
|
love.image.newImageData = function(w, h, _, raw)
|
|
return { w = w, h = h, raw = raw, release = function(self) self.released = true end }
|
|
end
|
|
love.graphics.newImage = function(pixels) return image(pixels.raw, pixels.w, pixels.h) end
|
|
love.graphics.newQuad = function(x, y, w, h) quads = quads + 1; return { x = x, y = y, w = w, h = h } end
|
|
love.graphics.draw = function(...) draws[#draws + 1] = { ... } end
|
|
local ordinary = { front = image("ordinary") }
|
|
local shiny = { front = image("shiny") }
|
|
local spotted = { front = image("personality-specific spots") }
|
|
local artVersion = "emerald"
|
|
Catalog.art = function(entry)
|
|
return entry.mon.species == 308 and spotted or entry.display.shiny and shiny or ordinary,
|
|
artVersion, { species = entry.mon.species, personality = entry.mon.personality }
|
|
end
|
|
local function entry(species, isShiny)
|
|
return { version = "red", generation = 1, mon = { species = species, personality = 1001 },
|
|
display = { national = species, shiny = isShiny } }
|
|
end
|
|
local Animation = require("src.box.Animation")
|
|
local mon = entry(1)
|
|
local before = Serializer.encode(mon)
|
|
local animation = Animation.new(mon)
|
|
T.check(animation ~= nil, "imported Emerald metadata starts the real summary animator")
|
|
T.eq(animation.sprite.animationData.version, "emerald", "animator uses an explicit Emerald provider")
|
|
T.eq(animation.frames[1].raw:sub(1, 1), "B", "alternate pose comes from the normal Emerald sheet")
|
|
T.same(animation.frames[1].filter, { "nearest", "nearest" }, "animated pixels use nearest filtering")
|
|
local alternate, transformed = false, false
|
|
for _ = 1, 300 do
|
|
Animation.update(animation, 1 / 60)
|
|
if animation.sprite.frame == 1 then
|
|
alternate = true
|
|
love.graphics.setColor(0.2, 0.3, 0.4, 0.5)
|
|
Animation.draw(animation, 10, 20, 128)
|
|
T.eq(draws[#draws][1], animation.frames[1], "detail drawing uses the current animation pose")
|
|
T.same({ love.graphics.getColor() }, { 0.2, 0.3, 0.4, 0.5 }, "animation restores the surrounding UI colour")
|
|
break
|
|
end
|
|
end
|
|
T.check(alternate, "real sprite command stream advances into the alternate pose")
|
|
for _ = 1, 300 do
|
|
Animation.update(animation, 1 / 60)
|
|
local transform = MonAnim.transform(animation.sprite)
|
|
transformed = transformed or transform.y2 ~= 0 or transform.sx ~= 1 or transform.sy ~= 1
|
|
if MonAnim.done(animation.sprite) then break end
|
|
end
|
|
T.check(transformed, "species callback animates its position or affine scale")
|
|
T.check(MonAnim.done(animation.sprite), "summary animation finishes instead of looping forever")
|
|
local n = animation.sprite.frames
|
|
Animation.update(animation, 1)
|
|
T.eq(animation.sprite.frames, n, "finished animations remain on their resting pose")
|
|
T.eq(Serializer.encode(mon), before, "animation leaves the stored Pokémon unchanged")
|
|
T.eq(GameVersion.get(), "red", "Emerald animation does not switch the active game")
|
|
T.eq(Data._cache.sentinel, true, "Emerald animation does not replace the active animation cache")
|
|
T.eq(CacheFs.prefix, "", "Emerald animation does not remount the imported cache")
|
|
|
|
local sparkling = Animation.new(entry(1, true))
|
|
T.eq(sparkling.frames[1].raw:sub(1, 1), "T", "shiny alternate poses retain the shiny palette")
|
|
local spinda = Animation.new(entry(308))
|
|
T.check(spinda ~= nil, "one-frame species still get their species motion")
|
|
T.eq(spinda.art.front, spotted.front, "Spinda motion retains its personality-specific spots")
|
|
T.eq(next(spinda.frames), nil, "Spinda never substitutes a generic second-frame picture")
|
|
local egg = entry(1); egg.mon.isEgg = true
|
|
T.eq(Animation.new(egg), nil, "eggs keep their egg picture")
|
|
artVersion = "sapphire"
|
|
T.eq(Animation.new(mon), nil, "non-Emerald fallback artwork stays valid and static")
|
|
artVersion = "emerald"
|
|
T.eq(Animation.new(entry(999)), nil, "unsupported animation species safely stays static")
|
|
Animation.reset()
|
|
bodies["emerald/data/generated/gba/pokemon/front_anims.lua"] = nil
|
|
T.eq(Animation.new(mon), nil, "older imported caches without animation metadata safely stay static")
|
|
T.check(sparkling.released and spinda.released, "refresh releases all current animated pictures")
|
|
T.check(animation.released, "finished animation pictures are also released on refresh")
|
|
T.eq(Animation.draw(animation, 0, 0, 64), false, "released pictures cannot be drawn")
|
|
|
|
local icon = { icon = image("icon sheet", 32, 64), iconW = 32, iconH = 32 }
|
|
Sprites.drawIcon(icon, 0, 0, 48, 0)
|
|
T.eq(draws[#draws][2].y, 0, "icon animation begins at its first pose")
|
|
Sprites.drawIcon(icon, 0, 0, 48, 1)
|
|
T.eq(draws[#draws][2].y, 32, "icon animation selects its second pose")
|
|
Sprites.drawIcon(icon, 0, 0, 48, 2)
|
|
T.eq(draws[#draws][2].y, 0, "icon poses loop within the actual sheet")
|
|
T.eq(quads, 2, "steady icon animation reuses its quads")
|
|
Sprites.drawIcon(icon, 0, 0, 48)
|
|
T.eq(draws[#draws][2].y, 0, "existing callers retain the original static icon")
|
|
CacheFs.readAt, Catalog.art, Data._cache = previousRead, previousArt, previousData
|
|
Animation.reset()
|
|
T.finish()
|