-- The #DEX-completion diploma (engine/events/diploma.asm PlaceDiplomaOnScreen, -- reached through `special Diploma` after Celadon Mansion 3F's game designer -- checks VAR_DEXCAUGHT == 251). Only page 1 is ever shown in play -- -- PrintDiplomaPage2 is the Game Boy Printer's second sheet -- (engine/printer/printer.asm _PrintDiploma), stubbed separately as -- "printer: no Game Boy Printer" -- so this transcribes PlaceDiplomaOnScreen -- alone, not diploma2.asm. -- -- THE CERTIFICATE IS A TILEMAP, NOT A TEXT BOX. PlaceDiplomaOnScreen -- decompresses DiplomaGFX into vTiles2 and then CopyBytes' DiplomaPage1Tilemap -- (a whole SCREEN_AREA of tile ids) straight over the background before a -- single string is placed, so the border, the seal and the ribbon are cart -- art. Both come out of the cache as `data.gen2Diploma` -- (RomExtractorGen2:extractDiploma); a cache that predates that stage falls -- back to the plain Chrome.box frame below, which is a placeholder and not -- the real seal. -- -- Positions below are the literal hlcoord operands PlaceDiplomaOnScreen -- calls PlaceString with, not a layout guessed from a screenshot: -- -- hlcoord 2, 5 "PLAYER" (.Player, "PLAYER@") -- hlcoord 15, 5 .EmptyString ("@") -- a bare terminator, nothing to draw -- hlcoord 9, 5 wPlayerName, dropped in over the row .Player/.EmptyString -- bracket -- hlcoord 2, 8 .Certification, five `next`-joined lines that PlaceString -- walks one row down at column 2 apiece: rows 8-12 -- -- COLOUR. _CGB_Diploma (engine/gfx/cgb_layouts.asm) loads all eight -- DiplomaPalettes sets and then WipeAttrmap zeroes the attrmap, so every tile -- on the screen -- art and text alike -- draws through set 0. The strings go -- down with Chrome.printThrough rather than Chrome.print for that reason: a -- black print over the art would be the one thing on screen not going through -- the palette. -- -- WaitPressAorB_BlinkCursor just parks on A or B; there is no menu here. local Chrome = require("src.ui.gen2.Chrome") local GbcPalette = require("src.render.GbcPalette") local Strings = require("src.core.Strings") local Diploma = {} Diploma.__index = Diploma Diploma.isOpaque = true function Diploma:wantsFillScale() return true end local CERTIFICATION = { Strings.source("This certifies"), Strings.source("that you have"), Strings.source("completed the"), Strings.source("new #DEX."), Strings.source("Congratulations!"), } -- opts: playerName, gfx, onClose() function Diploma.new(game, opts) opts = opts or {} local self = setmetatable({}, Diploma) self.game = game local save = game and game.save self.playerName = opts.playerName or (save and save.player and save.player.name) or "?" self.onClose = opts.onClose self.gfx = opts.gfx or ((game and game.data) or {}).gen2Diploma self.images = {} self.done = false return self end function Diploma:finish() if self.done then return end self.done = true if self.onClose then self.onClose() end end function Diploma:update(_dt) if self.done then return end local input = self.game and self.game.input if not input then return end if input:wasPressed("a") or input:wasPressed("b") then self:finish() end end -- Set 0 of DiplomaPalettes, the one WipeAttrmap leaves the whole screen on. function Diploma:palette() local palettes = self.gfx and self.gfx.palettes return palettes and palettes[1] or nil end function Diploma:image(path) if not path then return nil end local cached = self.images[path] if cached == nil then local Assets = require("src.render.Assets") local ok, image = pcall(Assets.image, path) cached = ok and image or false if cached then cached:setFilter("nearest", "nearest") end self.images[path] = cached end return cached or nil end -- The page as one batch of 8x8 tiles. Built once, because the tilemap is -- copied once and nothing on this screen ever moves; the palette is applied at -- draw time instead of baked in, so a COLOR mode change needs no rebuild. function Diploma:batch() if self.tilemap ~= nil then return self.tilemap or nil end local gfx = self.gfx local image = gfx and gfx.page1 and self:image(gfx.image) if not image then self.tilemap = false return nil end local across = gfx.sheetTiles or 16 local width = gfx.width or Chrome.SCREEN_W local height = gfx.height or Chrome.SCREEN_H local batch = love.graphics.newSpriteBatch(image, width * height) local quads = {} for index = 0, width * height - 1 do local tile = gfx.page1[index + 1] or 0 local quad = quads[tile] if not quad then quad = love.graphics.newQuad(tile % across * 8, math.floor(tile / across) * 8, 8, 8, image:getDimensions()) quads[tile] = quad end batch:add(quad, index % width * 8, math.floor(index / width) * 8) end self.tilemap = batch return batch end function Diploma:drawPanel() local palette = self:palette() local batch = self:batch() if batch then -- ClearTilemap leaves the screen on colour 0 of the loaded set, and the -- page covers all of it, but the fill is what a letterboxed fill-scale -- draw shows outside the 160x144 page. local paper = GbcPalette.color(palette, 1) love.graphics.setColor(paper[1] / 255, paper[2] / 255, paper[3] / 255, 1) love.graphics.rectangle("fill", 0, 0, Chrome.SCREEN_W * 8, Chrome.SCREEN_H * 8) love.graphics.setColor(1, 1, 1, 1) local function blit() love.graphics.draw(batch, 0, 0) end -- A palette-less cache would send the shader four black entries, so the -- sheet's own grey shades are the fallback rather than a black page. if palette then GbcPalette.with(palette, blit) else blit() end love.graphics.setColor(1, 1, 1, 1) else -- No gfx/diploma in the cache: a placeholder frame, not the real seal. Chrome.clear() Chrome.box(0, 0, Chrome.SCREEN_W, Chrome.SCREEN_H) end Chrome.printThrough(Strings("PLAYER"), 2, 5, palette) Chrome.printThrough(self.playerName, 9, 5, palette) for i, line in ipairs(CERTIFICATION) do Chrome.printThrough(Strings(line), 2, 7 + i, palette) end end function Diploma:draw() self:drawPanel() end -- PlaceDiplomaOnScreen opens on ClearBGPalettes / ClearTilemap -- (engine/events/diploma.asm:13-14), so the map is gone and the diploma's own -- paper is the whole screen. Colour 1 of the page palette is that paper (a -- pale green), which is why the surround cannot be the generic white the -- fallback paints: the page would sit in a white field instead of running to -- the window edge. function Diploma:drawsWidescreen() return true end function Diploma:drawWidescreen(winW, winH) local G = love.graphics -- Nil palette degrades to the DMG ramp's colour 1, i.e. white, matching the -- Chrome.clear() the panel falls back to when the cache has no gfx/diploma. local paper = GbcPalette.color(self:palette(), 1) G.setColor(paper[1] / 255, paper[2] / 255, paper[3] / 255, 1) G.rectangle("fill", 0, 0, winW, winH) G.setColor(1, 1, 1, 1) local scale = Chrome.fitScale(winW, winH) local ox, oy = Chrome.fitOrigin(winW, winH, scale) G.push() G.translate(ox, oy) G.scale(scale, scale) self:drawPanel() G.pop() end return Diploma