CLOSES #2230, CLOSES #2232

This commit is contained in:
bryanthaboi
2026-09-09 06:05:00 -04:00
parent fdd1d61ea6
commit 60df2fbb65
5 changed files with 223 additions and 21 deletions
+11 -15
View File
@@ -1086,23 +1086,19 @@ function BattleState:drawPic(mon, back)
local bandY = (back and BattleState.PLAYER_PIC_TILE_Y
or BattleState.ENEMY_PIC_TILE_Y) * 8 + lifted[1] * 8
local bandH = lifted[2] * 8
local psx, psy, psw, psh
if G.getScissor then psx, psy, psw, psh = G.getScissor() end
if self.liftedPass then
G.setScissor(0, bandY, 160, bandH)
local function band(y, h)
G.push("all")
Chrome.clipTo(0, y, 160, h)
paint()
else
if bandY > 0 then
G.setScissor(0, 0, 160, bandY)
paint()
end
local below = 144 - bandY - bandH
if below > 0 then
G.setScissor(0, bandY + bandH, 160, below)
paint()
end
G.pop()
end
if self.liftedPass then
band(bandY, bandH)
else
if bandY > 0 then band(0, bandY) end
local below = 144 - bandY - bandH
if below > 0 then band(bandY + bandH, below) end
end
if psx then G.setScissor(psx, psy, psw, psh) else G.setScissor() end
end
-- MonsterSpriteGFX (gfx/sprites.asm:82): the facing-DOWN 16x16 frame for the
+6
View File
@@ -118,6 +118,12 @@ end
-- pokegold engine/battle/core.asm:8646, engine/events/halloffame.asm:270
local function clipTo(x, y, w, h)
local G = love.graphics
if G.transformPoint then
local x1, y1 = G.transformPoint(x, y)
local x2, y2 = G.transformPoint(x + w, y + h)
x, y = math.floor(math.min(x1, x2)), math.floor(math.min(y1, y2))
w, h = math.ceil(math.abs(x2 - x1)), math.ceil(math.abs(y2 - y1))
end
if G.intersectScissor then G.intersectScissor(x, y, w, h)
else G.setScissor(x, y, w, h) end
end
+2 -6
View File
@@ -427,11 +427,7 @@ function MagnetTrainRide:drawPanel()
end
function MagnetTrainRide:draw()
local G = love.graphics
G.push("all")
G.setScissor(0, 0, SCREEN_W, SCREEN_H)
self:drawPanel()
G.pop()
Chrome.withClip(function() self:drawPanel() end)
end
-- MagnetTrain_LoadGFX_PlayMusic opens on ClearBGPalettes / ClearSprites
@@ -449,7 +445,7 @@ function MagnetTrainRide:drawWidescreen(winW, winH)
local scale = Chrome.fitScale(winW, winH)
local ox, oy = Chrome.fitOrigin(winW, winH, scale)
G.push("all")
G.setScissor(ox, oy, SCREEN_W * scale, SCREEN_H * scale)
Chrome.clipTo(ox, oy, SCREEN_W * scale, SCREEN_H * scale)
G.translate(ox, oy)
G.scale(scale, scale)
self:drawPanel()
@@ -0,0 +1,83 @@
-- Mom's SetDayOfWeek wheel drawn over the living room (#2232).
-- ../pokecrystal/engine/rtc/timeset.asm:385
-- ../pokecrystal/maps/PlayersHouse1F.asm:49
--
-- POKEPORT_VERSION=crystal POKEPORT_IDENTITY=<sandbox> POKEPORT_TOUCH=0 \
-- POKEPORT_SHOT_DIR=<dir> POKEPORT_DRIVER=tests/drivers/crystal_day_wheel_bug2232.lua love .
local U = require("tests.drivers.util")
local Chrome = require("src.ui.gen2.Chrome")
local SHOT_DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/pokeport-shots"
return function(game)
local fails = 0
local function say(line) print("[2232] " .. line) end
local function ok(cond, line)
if not cond then fails = fails + 1 end
say((cond and "PASS " or "FAIL ") .. line)
end
local function tap(button, frames)
game.input.pressQueue[#game.input.pressQueue + 1] = button
game.input.state[button] = true
U.wait(2)
game.input.state[button] = false
U.wait(frames or 4)
end
local function dayWheel()
local top = game.stack and game.stack:top()
if top and top.mode == "day" and top.pickerBox then return top end
return nil
end
U.wait(45)
local world = game.world
if not (world and world.map) then
say("FAIL the crystal world did not boot")
return
end
-- ../pokecrystal/maps/PlayersHouse1F.asm:395
world:setMap("PLAYERS_HOUSE_1F", 7, 3, "down")
U.wait(20)
for _ = 1, 3 do tap("down", 8) end
U.wait(40)
ok(world.map and world.map.id == "PLAYERS_HOUSE_1F", "in the living room")
local wheel
for _ = 1, 300 do
wheel = dayWheel()
if wheel then break end
tap("a", 4)
end
ok(wheel ~= nil, "Mom's scene reached the day-of-week wheel")
if not wheel then return end
local seen
local drawPanel = wheel.drawPanel
wheel.drawPanel = function(self)
seen = { love.graphics.getScissor() }
return drawPanel(self)
end
U.wait(3)
wheel.drawPanel = drawPanel
local w, h = love.graphics.getDimensions()
local scale = Chrome.fitScale(w, h)
local ox, oy = Chrome.fitOrigin(w, h, scale)
ok(seen ~= nil, "the wheel drew its panel")
if seen then
ok(seen[1] == ox and seen[2] == oy
and seen[3] == 160 * scale and seen[4] == 144 * scale,
("the wheel clips to the panel %d,%d %dx%d, got %s,%s %sx%s"):format(
ox, oy, 160 * scale, 144 * scale,
tostring(seen[1]), tostring(seen[2]), tostring(seen[3]), tostring(seen[4])))
end
U.shot(game, SHOT_DIR .. "/2232_day_wheel.png")
say(fails == 0 and "all claims passed" or (fails .. " claims failed"))
say("the SUNDAY picker at the top right and \"What day is it?\" should both")
say("be on screen over the living room. a bare room with Mom next to you")
say("and nothing to answer is the bug. up/down changes the day, A confirms.")
while true do U.wait(60) end
end
@@ -0,0 +1,121 @@
-- NEW GAME with a touch skin active, from the intro menu to the bedroom (#2230).
-- ../pokecrystal/engine/menus/init_gender.asm:23
-- ../pokecrystal/engine/menus/intro_menu.asm:80
--
-- POKEPORT_VERSION=crystal POKEPORT_IDENTITY=<sandbox> POKEPORT_TOUCH=1 \
-- POKEPORT_BOOT_CINEMA=1 POKEPORT_SKIN=<skin id> POKEPORT_SHOT_DIR=<dir> \
-- POKEPORT_DRIVER=tests/drivers/crystal_skin_newgame_bug2230.lua love .
local U = require("tests.drivers.util")
local TouchSkin = require("src.core.TouchSkin")
local TouchControls = require("src.core.TouchControls")
local Playfield = require("src.render.Playfield")
local MainMenu = require("src.ui.gen2.MainMenu")
local GenderSelect = require("src.ui.gen2.GenderSelect")
local InitClock = require("src.ui.gen2.InitClock")
local OakSpeech = require("src.ui.gen2.OakSpeech")
local NamePick = require("src.ui.gen2.NamePick")
local SHOT_DIR = os.getenv("POKEPORT_SHOT_DIR") or "/tmp/pokeport-shots"
return function(game)
local fails = 0
local function say(line) print("[2230] " .. line) end
local function ok(cond, line)
if not cond then fails = fails + 1 end
say((cond and "PASS " or "FAIL ") .. line)
end
local function top() return game.stack:top() end
local function isA(class) return top() ~= nil and getmetatable(top()) == class end
local function tap(button, frames)
game.input.pressQueue[#game.input.pressQueue + 1] = button
game.input.state[button] = true
U.wait(2)
game.input.state[button] = false
U.wait(frames or 4)
end
local function waitFor(label, predicate, frames, spam)
for _ = 1, frames or 900 do
if predicate() then return true end
if spam then tap(spam, 2) else U.wait(1) end
end
ok(false, "stalled waiting for " .. label .. " (top is " .. tostring(top()) .. ")")
U.shot(game, SHOT_DIR .. "/2230_stall.png")
return false
end
-- Counts the non-white, non-black pixels inside the skin's screen cutout:
-- a GB page that actually painted there has plenty, a page clipped away
-- leaves only the letterbox fill.
local function paintedInCutout(path)
local f = io.open(path, "rb")
if not f then return nil end
local bytes = f:read("*a")
f:close()
local image = love.image.newImageData(love.filesystem.newFileData(bytes, "shot.png"))
local w, h = love.graphics.getDimensions()
local cx, cy, cw, ch = Playfield.cutout(w, h)
if not cx then cx, cy, cw, ch = 0, 0, w, h end
local sx = image:getWidth() / w
local painted = 0
for y = cy, cy + ch - 1, 4 do
for x = cx, cx + cw - 1, 4 do
local r, g, b = image:getPixel(math.floor(x * sx), math.floor(y * sx))
local lit = r > 0.05 or g > 0.05 or b > 0.05
local white = r > 0.95 and g > 0.95 and b > 0.95
if lit and not white then painted = painted + 1 end
end
end
return painted
end
local skins = TouchSkin.list()
local want = os.getenv("POKEPORT_SKIN")
if not want or want == "" then want = skins[1] and skins[1].id end
ok(want ~= nil, "a skin is installed to select")
if not want then return end
local controls = game.touchControls or TouchControls
local skin, err = controls:selectSkin(want)
ok(skin ~= nil, "selected skin " .. tostring(want) .. " " .. tostring(err or ""))
ok(TouchSkin.drawable(), "the skin is live, so Playfield cuts the screen out")
if not waitFor("the intro menu", function() return isA(MainMenu) end, 2400, "start") then return end
local menu = top()
for i, item in ipairs(menu.list.items) do
if item.value == "new" then menu.list.index = i end
end
tap("a")
if not waitFor("the gender menu",
function() return isA(GenderSelect) and top():menuOpen() end, 900) then return end
U.wait(5)
local genderShot = SHOT_DIR .. "/2230_01_gender_in_cutout.png"
U.shot(game, genderShot)
local painted = paintedInCutout(genderShot)
ok(painted and painted > 200,
("the gender page painted inside the cutout (%s coloured samples)"):format(tostring(painted)))
tap("a")
if not waitFor("the clock", function() return isA(InitClock) end, 900) then return end
U.shot(game, SHOT_DIR .. "/2230_02_clock.png")
if not waitFor("Oak", function() return isA(OakSpeech) end, 1200, "a") then return end
U.wait(30)
U.shot(game, SHOT_DIR .. "/2230_03_oak.png")
if not waitFor("the name picker", function() return isA(NamePick) end, 1500, "a") then return end
U.wait(60)
top().cursor = 2
tap("a")
if not waitFor("the bedroom", function()
return game.phase == "play" and game.world and game.world.map ~= nil
end, 2400, "a") then return end
ok(game.world.map.id == "PLAYERS_HOUSE_2F", "new game landed in the bedroom under the skin")
U.wait(30)
U.shot(game, SHOT_DIR .. "/2230_04_bedroom.png")
say(fails == 0 and "all claims passed" or (fails .. " claims failed"))
say("2230_01_gender_in_cutout.png should show the boy/girl prompt inside the")
say("skin's screen window. an empty white window there, or a lua error after")
say("the name picker, is the bug.")
love.event.quit(fails == 0 and 0 or 1)
end