mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 19:24:01 +02:00
@@ -0,0 +1,68 @@
|
||||
-- engine/battle_anims/bg_effects.asm:406-471 BattleBGEffect_BattlerObj_1Row
|
||||
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
love = require("tests.love_stub")
|
||||
|
||||
local T = require("tests.harness")
|
||||
local BgEffects = require("src.battle.gen2.BgEffects")
|
||||
local BattleAnimView = require("src.ui.gen2.BattleAnimView")
|
||||
|
||||
do
|
||||
local bg = BgEffects.new(nil, { battleTurn = 0 })
|
||||
bg:queue("BATTLE_BG_EFFECT_BATTLEROBJ_1ROW", 0, 0, 0)
|
||||
bg:playFrame()
|
||||
local spawns = bg:takeSpawns()
|
||||
T.eq(spawns[1] and spawns[1].object, "BATTLE_ANIM_OBJ_ENEMYFEET_1ROW",
|
||||
"player attacking: the enemy's feet row becomes an OBJ")
|
||||
T.eq(spawns[1] and spawns[1].x, 16 * 8 + 4, "at the asm's fixed x")
|
||||
T.eq(bg.liftedRows.enemy, nil, "the tilemap row is intact on frame one")
|
||||
T.eq(BattleAnimView.needsCanvas({ bg = bg }), false,
|
||||
"an intact tilemap with no scroll skips the bake canvas")
|
||||
bg:playFrame()
|
||||
local lifted = bg.liftedRows.enemy
|
||||
T.check(lifted and lifted[1] == 6 and lifted[2] == 1,
|
||||
"frame two ClearBoxes row 6 of the enemy box (hlcoord 12, 6)")
|
||||
T.eq(bg.hidden.enemy, false, "the rest of the pic stays on the BG")
|
||||
T.eq(BattleAnimView.needsCanvas({ bg = bg }), true,
|
||||
"a lifted row keeps the panel on the bake canvas even with scx 0 and no"
|
||||
.. " lcdc pointer, so drawPic's 160x144 scissor stays in canvas space")
|
||||
for _ = 1, 4 do bg:playFrame() end
|
||||
T.eq(bg:activeCount(), 0, ".five ends the effect")
|
||||
lifted = bg.liftedRows.enemy
|
||||
T.check(lifted and lifted[1] == 6 and lifted[2] == 1,
|
||||
".five never restores the row")
|
||||
T.eq(BattleAnimView.needsCanvas({ bg = bg }), true,
|
||||
"and the wait frames after .five stay baked as well")
|
||||
bg:queue("BATTLE_BG_EFFECT_SHOW_MON", 0, 0, 0)
|
||||
bg:playFrame()
|
||||
T.eq(bg.liftedRows.enemy, nil, "SHOW_MON's box redraw puts the row back")
|
||||
T.eq(BattleAnimView.needsCanvas({ bg = bg }), false,
|
||||
"after which the plain no-canvas path returns")
|
||||
end
|
||||
|
||||
do
|
||||
local bg = BgEffects.new(nil, { battleTurn = 1 })
|
||||
bg:queue("BATTLE_BG_EFFECT_BATTLEROBJ_2ROW", 0, 0, 0)
|
||||
bg:playFrame()
|
||||
local spawns = bg:takeSpawns()
|
||||
T.eq(spawns[1] and spawns[1].object, "BATTLE_ANIM_OBJ_PLAYERHEAD_2ROW",
|
||||
"enemy attacking: the player's head rows become an OBJ")
|
||||
T.eq(spawns[1] and spawns[1].x, 6 * 8, "at the asm's fixed x")
|
||||
bg:playFrame()
|
||||
local lifted = bg.liftedRows.player
|
||||
T.check(lifted and lifted[1] == 0 and lifted[2] == 2,
|
||||
"rows 0-1 of the player box (hlcoord 2, 6, two rows)")
|
||||
T.eq(bg.liftedRows.enemy, nil, "the attacker keeps its own rows")
|
||||
end
|
||||
|
||||
do
|
||||
local bg = BgEffects.new(nil, { battleTurn = 0, flying = { enemy = true } })
|
||||
bg:queue("BATTLE_BG_EFFECT_BATTLEROBJ_1ROW", 0, 0, 0)
|
||||
bg:playFrame()
|
||||
T.eq(#bg:takeSpawns(), 0, "a flying target spawns nothing")
|
||||
T.eq(bg:activeCount(), 0, "and the effect ends at once")
|
||||
T.eq(bg.liftedRows.enemy, nil, "with no row lifted")
|
||||
end
|
||||
|
||||
T.finish("gen2 battler row lift bug 1231")
|
||||
@@ -0,0 +1,54 @@
|
||||
-- Gen 2 map bakes take dpiscale 1 so map pixels stay square LCD pixels
|
||||
-- (#208 #1301, constants/hardware.inc:932; see src/render/PixelCanvas.lua).
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
local T = require("tests.modkit")
|
||||
|
||||
local g = love.graphics
|
||||
local seen = {}
|
||||
local realNewCanvas = g.newCanvas
|
||||
g.newCanvas = function(w, h, settings)
|
||||
seen[#seen + 1] = { w = w, h = h, dpiscale = settings and settings.dpiscale }
|
||||
local c = realNewCanvas(w, h)
|
||||
c.renderTo = function(_, fn) fn() end
|
||||
c.setFilter = function() end
|
||||
return c
|
||||
end
|
||||
|
||||
local World = require("src.world.gen2.World")
|
||||
local MapPreview = require("src.world.gen2.MapPreview")
|
||||
|
||||
local atlas = {
|
||||
getDimensions = function() return 128, 128 end,
|
||||
setFilter = function() end,
|
||||
}
|
||||
local tileset = { blocks = { [1] = {} }, tilesPerRow = 16 }
|
||||
local map = { def = { tileset = "TILESET_JOHTO" }, width = 20, height = 18,
|
||||
blocks = {}, borderBlock = 0 }
|
||||
|
||||
local world = setmetatable({
|
||||
atlasCache = {},
|
||||
atlasFor = function() return atlas, tileset end,
|
||||
}, { __index = World })
|
||||
|
||||
World.bakeMapImage(world, map, nil, nil)
|
||||
local bakeCount = #seen
|
||||
T.check(bakeCount >= 1, "the map bake allocated a canvas")
|
||||
|
||||
World.scrollStrip(world, map.def, tileset, 3, { h = 1, v = 0 })
|
||||
T.check(#seen > bakeCount, "the scroll strip allocated a canvas")
|
||||
local stripCount = #seen
|
||||
|
||||
local origAtlasFor = MapPreview.atlasFor
|
||||
MapPreview.atlasFor = function() return atlas, tileset end
|
||||
MapPreview.bake({ tilesets = {}, atlasCache = {}, mapImages = {} }, map, "DAY")
|
||||
MapPreview.atlasFor = origAtlasFor
|
||||
T.check(#seen > stripCount, "the save-editor bake allocated a canvas")
|
||||
|
||||
for i, c in ipairs(seen) do
|
||||
T.eq(c.dpiscale, 1,
|
||||
("canvas %d (%dx%d) is allocated at dpiscale 1"):format(i, c.w, c.h))
|
||||
end
|
||||
|
||||
g.newCanvas = realNewCanvas
|
||||
T.finish("gen2 map bake dpi")
|
||||
@@ -44,4 +44,46 @@ do
|
||||
"the identity byte returns the palette untouched")
|
||||
end
|
||||
|
||||
-- engine/battle_anims/anim_commands.asm:1293 BattleAnim_SetBGPals
|
||||
do
|
||||
local BattleAnimView = require("src.ui.gen2.BattleAnimView")
|
||||
local palettes = {
|
||||
pokemon = {
|
||||
[6] = { normal = { { 200, 100, 50 }, { 80, 40, 20 } } },
|
||||
[25] = { normal = { { 230, 200, 40 }, { 150, 90, 20 } } },
|
||||
},
|
||||
hpBar = {
|
||||
green = { { 100, 220, 100 }, { 30, 160, 30 } },
|
||||
yellow = { { 230, 220, 90 }, { 180, 150, 20 } },
|
||||
red = { { 230, 100, 90 }, { 180, 30, 20 } },
|
||||
},
|
||||
expBar = { { 120, 140, 230 }, { 40, 60, 160 } },
|
||||
}
|
||||
local view = BattleAnimView.new({}, palettes)
|
||||
local battle = { player = { species = 6 }, enemy = { species = 25 } }
|
||||
local list = view:panelPalettes(battle)
|
||||
T.eq(#list, 7, "shades + two mons + three hp bars + exp bar")
|
||||
local src, dst, count, ambiguous = GbcPalette.remapTable(list, 0x1b)
|
||||
T.check(count > 0 and count <= GbcPalette.REMAP_MAX,
|
||||
"the table fits the shader array")
|
||||
T.eq(ambiguous, 0, "no colour maps two ways")
|
||||
local function mapped(from)
|
||||
for i = 1, count do
|
||||
if src[i][1] == from[1] and src[i][2] == from[2]
|
||||
and src[i][3] == from[3] then
|
||||
return dst[i]
|
||||
end
|
||||
end
|
||||
end
|
||||
local black = mapped({ 255, 255, 255 })
|
||||
T.check(black and black[1] == 0 and black[2] == 0 and black[3] == 0,
|
||||
"white inverts to black")
|
||||
local white = mapped({ 0, 0, 0 })
|
||||
T.check(white and white[1] == 255 and white[2] == 255 and white[3] == 255,
|
||||
"black inverts to white")
|
||||
local mid = mapped({ 200, 100, 50 })
|
||||
T.check(mid and mid[1] == 80 and mid[2] == 40 and mid[3] == 20,
|
||||
"the mon's colour 1 shows its colour 2")
|
||||
end
|
||||
|
||||
T.finish("gen2 shadow ball bgp bug 1269")
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
-- engine/battle_anims/anim_commands.asm:1293 BattleAnim_SetBGPals
|
||||
--
|
||||
-- gen2_shadow_ball_bgp_bug1269.lua proves the runner lands bg.bgp and that
|
||||
-- panelPalettes/remapTable would invert correctly; it never calls
|
||||
-- BattleAnimView:present, which is where #1269 actually lived (the byte
|
||||
-- was landed but nothing read it). This suite drives present() itself and
|
||||
-- watches the shader binding around the backdrop draw.
|
||||
|
||||
package.path = "./?.lua;./?/init.lua;" .. package.path
|
||||
|
||||
love = require("tests.love_stub")
|
||||
|
||||
-- A remap shader that never touches the GPU: present() only needs
|
||||
-- love.graphics.newShader to succeed so GbcPalette.remapShader() is
|
||||
-- non-nil, which is the gate `present` checks before it will bake+remap.
|
||||
local sentShader = { calls = {} }
|
||||
function sentShader:send(name, ...) self.calls[#self.calls + 1] = name end
|
||||
love.graphics.newShader = function() return sentShader end
|
||||
|
||||
-- The stub's stand-in Quad has no setViewport/getViewport, which blitRow
|
||||
-- (the scanline blit present() drives 144 times a frame) needs; the real
|
||||
-- love.graphics.Quad has both.
|
||||
love.graphics.newQuad = function(x, y, w, h)
|
||||
local q = { x = x, y = y, w = w, h = h }
|
||||
function q:setViewport(x2, y2, w2, h2) self.x, self.y, self.w, self.h = x2, y2, w2, h2 end
|
||||
function q:getViewport() return self.x, self.y, self.w, self.h end
|
||||
return q
|
||||
end
|
||||
|
||||
local T = require("tests.harness")
|
||||
local GbcPalette = require("src.render.GbcPalette")
|
||||
local BattleAnimView = require("src.ui.gen2.BattleAnimView")
|
||||
|
||||
local shaderDuringFill = "unset"
|
||||
local realRectangle = love.graphics.rectangle
|
||||
love.graphics.rectangle = function(...)
|
||||
if shaderDuringFill == "unset" then
|
||||
shaderDuringFill = love.graphics.getShader()
|
||||
end
|
||||
return realRectangle(...)
|
||||
end
|
||||
|
||||
local view = BattleAnimView.new({}, nil)
|
||||
-- data/moves/animations.asm:4509 BattleAnim_ShadowBall's anim_bgp $1b, with
|
||||
-- no scroll and no rBGP window queued, is exactly the frame that used to
|
||||
-- fall through the old `needsCanvas`-only early-out untouched.
|
||||
local runner = {
|
||||
bg = {
|
||||
bgp = 0x1b,
|
||||
lcdc = nil,
|
||||
scx = 0,
|
||||
scy = 0,
|
||||
lyStart = 0,
|
||||
lyEnd = 0,
|
||||
lyBackup = {},
|
||||
},
|
||||
}
|
||||
|
||||
T.check(love.graphics.getShader() == nil, "no shader bound before present")
|
||||
|
||||
view:present(runner, function() end, nil)
|
||||
|
||||
T.check(shaderDuringFill == sentShader,
|
||||
"the panel backdrop is drawn through the remap shader, not plainly")
|
||||
T.check(love.graphics.getShader() == nil,
|
||||
"the shader is unbound again once present returns, so drawObjects is unaffected")
|
||||
|
||||
local sawRemapSend = false
|
||||
for _, name in ipairs(sentShader.calls) do
|
||||
if name == "remapSrc" then sawRemapSend = true end
|
||||
end
|
||||
T.check(sawRemapSend, "GbcPalette.useRemap actually sent a remap table, not just bound the shader")
|
||||
|
||||
-- The identity byte must take the plain path: no bake, no shader, ever.
|
||||
shaderDuringFill = "unset"
|
||||
local identityRunner = {
|
||||
bg = { bgp = GbcPalette.BGP_IDENTITY, lcdc = nil, scx = 0, scy = 0,
|
||||
lyStart = 0, lyEnd = 0, lyBackup = {} },
|
||||
}
|
||||
local plainDrawCalled = false
|
||||
view:present(identityRunner, function() plainDrawCalled = true end, nil)
|
||||
T.check(plainDrawCalled, "identity rBGP takes the plain drawBg() path")
|
||||
T.check(shaderDuringFill == "unset",
|
||||
"identity rBGP never touches the remap shader")
|
||||
|
||||
T.finish("gen2 shadow ball bgp view bug 1269")
|
||||
Reference in New Issue
Block a user