-- Parity test: scrolling text never draws onto the box border. -- -- pokered has no sub-tile text scroll at all: ScrollTextUpOneLine -- (home/text.asm:283) copies the three text rows up a whole row, blanks the -- bottom one and waits 5 frames, so a glyph is never drawn between two -- rows, let alone below the last one. The port slides the scroll smoothly -- instead, and used to add that offset to both visible lines -- which put -- the incoming line 8px low, on the box's bottom border, for the four -- frames of the slide (#314). Oak's Hall of Fame speech is where players -- hit it, being one long run of `\v` conts, but every scrolled box did it. -- -- The invariant under test is geometric, so it is checked by capturing the -- glyph draws rather than by looking at pixels: nothing may be drawn below -- line2Y, ever, at any point in the scroll. -- -- Self-contained; run via `luajit tests/parity_text_scroll_bounds.lua`. package.path = "./?.lua;./?/init.lua;" .. package.path if not _G.love then _G.love = require("tests.love_stub") end local S = require("tests.harness").suite("parity text scroll bounds") local check, eq = S.check, S.eq local Data = require("src.core.Data") if not Data.maps then Data:load() end local Font = require("src.render.Font") Font.load(Data) local TextBox = require("src.render.TextBox") -- capture glyph placement instead of drawing it local drawn = {} local realDrawCode, realDrawBox = Font.drawCode, Font.drawBox Font.drawCode = function(code, x, y) drawn[#drawn + 1] = { x = x, y = y } end Font.drawBox = function() end local pressed = false local game = { data = Data, save = { options = { textSpeed = 1 } }, -- fastest typewriter, fewest frames input = { wasPressed = function() return pressed end, isDown = function() return false end }, stack = { push = function() end, pop = function() end, top = function() end }, } -- three lines joined by \v (cont), which is what forces the scroll; the -- Hall of Fame speech is built out of exactly this shape local box = TextBox.new(game, "OAK: Er-hem!\nCongratulations\vCHAMP!") local floorY = box.line2Y -- The page-advance arrow is the one glyph that belongs on the border: the -- original prints ▼ into the bottom-right border tile (home/text.asm), and -- TextBox draws it at a fixed slot 4px into that row. Everything else is -- body text and must stay inside. local arrowX = (box.boxTx + box.boxTw - 2) * 8 local arrowY = (box.boxTy + box.boxTh - 1) * 8 - 4 local sawScroll, sawArrow, worst = false, false, -math.huge for _ = 1, 600 do -- advance past the cont wait the same way a player would pressed = box.waiting or false box:update(1 / 60) pressed = false drawn = {} box:draw() if (box.scrollPx or 0) > 0 then sawScroll = true end for _, g in ipairs(drawn) do if g.x == arrowX and g.y == arrowY then sawArrow = true elseif g.y > worst then worst = g.y end end if box.done and not box.waiting then break end end Font.drawCode, Font.drawBox = realDrawCode, realDrawBox check(sawScroll, "the box really did scroll (otherwise this proves nothing)") check(worst > -math.huge, "body glyphs were drawn") check(worst <= floorY, ("no body glyph drawn below line2Y (worst y=%d, line2Y=%d)"):format(worst, floorY)) check(sawArrow, "the page-advance arrow still prints in the border row") eq(box.line1Y + 16, box.line2Y, "the two text rows stay two tiles apart") S.finish()