-- ..(engine/movie/intro.asm ln 8) -- ..(engine/movie/splash.asm ln 27) local Font = require("src.render.Font") local GameVersion = require("src.core.GameVersion") local Music = require("src.core.Music") local Sound = require("src.core.Sound") local Strings = require("src.core.Strings") local Timing = require("src.core.Timing") local FaithfulRes = require("src.core.FaithfulRes") local TouchSkin = require("src.core.TouchSkin") local IntroMovie = {} IntroMovie.__index = IntroMovie IntroMovie.isOpaque = true -- Same as the title screen: full-bleed art, no world behind it, no player zoom -- to respect, so fill the window rather than sit at the fixed integer scale. function IntroMovie:wantsFillScale() if FaithfulRes.locked then return false end if TouchSkin.drawable() then return false end return true end -- SGB intro palettes: the splash uses PalPacket_GameFreakIntro (logo -- GAMEFREAK, falling star columns RED/VIRIDIAN/BLUEMON), the attract -- fight PalPacket_NidorinoIntro (PURPLEMON letterbox, BLACK bars) function IntroMovie:sgbPalettes(game) local P = require("src.render.PaletteFX") if self.phase == 1 and (self.studio.card or self.studio.credit) then return nil end -- engine/movie/intro.asm:306 if self.phase == 1 or self.phase == 2 then local logo = P.pal(game.data, "GAMEFREAK") if not logo then return nil end return { P.whole(logo), P.zone(P.pal(game.data, "REDMON"), 5, 11, 7, 13), P.zone(P.pal(game.data, "VIRIDIAN"), 8, 11, 9, 13), P.zone(P.pal(game.data, "BLUEMON"), 12, 11, 14, 13), } elseif self.phase == 3 then local purple = P.pal(game.data, "PURPLEMON") if not purple then return nil end return { P.zone(P.pal(game.data, "BLACK"), 0, 0, 19, 3), P.zone(purple, 0, 4, 19, 13), P.zone(P.pal(game.data, "BLACK"), 0, 14, 19, 17), } end return nil end local COPYRIGHT_FRAMES = 180 -- ld c, 180 (intro.asm:311-312) -- phase 2 (shooting star) timeline, in frames from phase start local STAR_START = 64 -- ld c, 64 (intro.asm:323-324) local STAR_FRAMES = 40 -- OAM Y 0->160 in +4 steps (splash.asm:32-60) local FLASH_START = STAR_START + STAR_FRAMES local FLASH_FRAMES = 30 -- 3 loops x 10 frames (splash.asm:72-82) local WAVES_START = FLASH_START + FLASH_FRAMES local WAVE_FRAMES = 24 -- 8 substeps x 3 frames (splash.asm:186-209) local WAVES_END = WAVES_START + 6 * WAVE_FRAMES -- 4 waves + 2 empty local SPLASH_FRAMES = WAVES_END + 40 -- ld c, 40 (intro.asm:329-331) -- After GBFadeOutToWhite, PlayIntro still DelayFrame's once (intro.asm:20) -- and DisplayTitleScreen keeps GBPalWhiteOut through two -- TitleScreenCopyTileMapToVRAM Delay3 waits (title.asm:136, 142) before -- GBPalNormal and the logo bounce. Without this hold the title drops in -- the same breath as the fade. local POST_FADE_WHITE = 1 + 3 + 3 -- ..(engine/movie/splash.asm ln 211) local LOGO_X, LOGO_Y = 72, 56 local TEXT_X, TEXT_Y = 40, 80 -- engine/movie/splash.asm:72 local LOGO_OBP0 = { [0] = 0xF9, 0x7E, 0x9F, 0xE7 } -- engine/movie/splash.asm:4 local STAR_OBP1 = 0xA4 local rampCache = setmetatable({}, { __mode = "k" }) local function obpRamp(obp, ramp) local byRamp = rampCache[ramp] if not byRamp then byRamp = {}; rampCache[ramp] = byRamp end local c = byRamp[obp] if not c then c = { ramp[1] } for i = 1, 3 do c[i + 1] = ramp[math.floor(obp / 4 ^ i) % 4 + 1] end byRamp[obp] = c end return c end local function romArt(path) return path and not path:find("^mods/") and path or nil end -- CopyrightTextString (engine/movie/title.asm). Red/Blue years are -- (c)'95.'96.'98; Yellow's sheet spells (c)1995-1999 and finishes the -- last digit with NineTile (title screen). Intro originally overflows -- into the font "A" tile for that digit; we draw NineTile so both -- screens read 1999. local COPY_PREFIX_RB = { 0, 1, 2, 1, 3, 1, 4 } local COPY_PREFIX_YELLOW = { 0, 1, 2, 3, 1, 2 } local COPY_NINTENDO = { 5, 6, 7, 8, 9, 10 } local COPY_CREATURES = { 11, 12, 13, 14, 15, 16, 17, 18 } local STUDIO_BOX = { w = 128, h = 52, cx = 80, cy = 60 } -- the 4 waves of small stars: screen X positions, all spawning at y=88 -- (OAM $68; SmallStarsWave*Coords, splash.asm:160-183) local STAR_WAVES = { { 40, 56, 80, 112 }, { 48, 64, 88, 104 }, { 44, 68, 76, 92 }, { 52, 84, 100, 108 }, } -- Nidorino movement lists: {dy, dx} applied every 5 frames -- (AnimateIntroNidorino, intro.asm:143-158) local ANIM = { -- IntroNidorinoAnimation1..7 (intro.asm:370-437) { {0,0}, {-2,2}, {-1,2}, {1,2}, {2,2} }, -- 1: hop arc, +8 right { {0,0}, {-2,-2}, {-1,-2}, {1,-2}, {2,-2} }, -- 2: hop arc, -8 left { {0,0}, {-12,6}, {-8,6}, {8,6}, {12,6} }, -- 3: dodge leap, +24 right { {0,0}, {-8,-4}, {-4,-4}, {4,-4}, {8,-4} }, -- 4: high hop, -16 left { {0,0}, {-8,4}, {-4,4}, {4,4}, {8,4} }, -- 5: high hop, +16 right { {0,0}, {2,0}, {2,0}, {0,0} }, -- 6: crouch, +4 down { {-8,-16}, {-7,-14}, {-6,-12}, {-4,-10} }, -- 7: lunge, -52/-25 up-left } -- PlayIntroScene, in source order (intro.asm:23-141). `move` ops shift -- 2px per 2 frames (IntroMoveMon, intro.asm:235-269): "scrollIn" moves -- Nidorino right AND Gengar left together (the fallthrough at :247-259), -- gengar dx<0 = MOVE_GENGAR_LEFT (SCX+2), dx>0 = MOVE_GENGAR_RIGHT. local FIGHT_SCRIPT = { { move = "scrollIn", px = 80 }, -- intro.asm:40-41 { sfx = "Intro_Hip" }, { anim = 1 }, -- :44-50 { sfx = "Intro_Hop" }, { anim = 2 }, { wait = 10 }, -- :51-57 { sfx = "Intro_Hip" }, { anim = 1 }, -- :60-64 { sfx = "Intro_Hop" }, { anim = 2 }, { wait = 30 }, -- :65-71 { pose = 2 }, { sfx = "Intro_Raise" }, -- :74-78 { move = "gengar", dx = -8 }, { wait = 30 }, -- :79-82 { pose = 3 }, { sfx = "Intro_Crash" }, -- :85-89 { move = "gengar", dx = 16 }, -- :90-91 { sfx = "Intro_Hip" }, { frame = 2 }, { anim = 3 }, -- :92-98 { wait = 30 }, -- :99-100 { move = "gengar", dx = -8 }, { pose = 1 }, -- :103-106 { wait = 60 }, -- :107-108 { sfx = "Intro_Hip" }, { frame = 1 }, { anim = 4 }, -- :111-117 { sfx = "Intro_Hop" }, { anim = 5 }, { wait = 20 }, -- :118-124 { frame = 2 }, { anim = 6 }, { wait = 30 }, -- :127-132 { sfx = "Intro_Lunge" }, { frame = 3 }, { anim = 7 }, -- :135-141 { fade = 24 }, -- GBFadeOutToWhite: 3 pals x 8 frames (home/fade.asm:26-40) } local FIGHT_FADE_INDEX = #FIGHT_SCRIPT -- home/fade.asm:71-73 local FADE_TO_WHITE = { { bgp = 0x90, obp = 0x80 }, { bgp = 0x40, obp = 0x40 }, { bgp = 0x00, obp = 0x00 }, } local BGP_NORMAL = 0xE4 local function tryImage(path) if not path then return nil end local ok, img = pcall(love.graphics.newImage, path) return ok and img or nil end function IntroMovie.new(game, onDone) local self = setmetatable({}, IntroMovie) self.game = game self.onDone = onDone self.phase = 1 self.timer = 0 self.finished = false local intro = game.data.field and game.data.field.intro or {} self.introCfg = intro -- brand-level knobs (12 4.7): studio strings and the skip a total -- conversion or dev profile sets to jump straight to the title self.studio = intro.studio or {} self.skipAll = intro.skip and true or false local function img(e) return tryImage(e and e.path) end local titleCfg = game.data.field and game.data.field.title or {} self.copyright = img(titleCfg.copyright) or tryImage("assets/generated/title/copyright.png") self.copyQuads = {} if self.copyright then local iw, ih = self.copyright:getDimensions() for t = 0, 18 do self.copyQuads[t] = love.graphics.newQuad(t * 8, 0, 8, 8, iw, ih) end end self.gfInc = img(titleCfg.gamefreakInc) or tryImage("assets/generated/title/gamefreak_inc.png") -- ..(pokeyellow engine/movie/title.asm CopyrightTextString / NineTile) self.yellowCopy = GameVersion.isYellow() or titleCfg.layout == "yellow_pikachu" self.copyPrefix = self.yellowCopy and COPY_PREFIX_YELLOW or COPY_PREFIX_RB self.nineImg = self.yellowCopy and ( img(titleCfg.nine) or tryImage("assets/generated/title/nine.png")) or nil self.studioLogo = tryImage(self.studio.logo) if self.studioLogo then self.studioLogo:setFilter("nearest", "nearest") local iw, ih = self.studioLogo:getDimensions() self.studioScale = math.min(STUDIO_BOX.w / iw, STUDIO_BOX.h / ih) self.studioX = STUDIO_BOX.cx - iw * self.studioScale / 2 self.studioY = STUDIO_BOX.cy - ih * self.studioScale / 2 end self.logo = img(intro.gamefreakLogo) self.gfText = img(intro.gamefreakText) self.logoPath = self.logo and romArt(intro.gamefreakLogo.path) self.gfTextPath = self.gfText and romArt(intro.gamefreakText.path) self.bigStar = img(intro.bigStar) self.bigStarPath = self.bigStar and romArt(intro.bigStar.path) self.smallStar = img(intro.fallingStar) self.smallStarBlink = img(intro.fallingStarBlink) self.gengarFrames, self.nidoFrames = {}, {} self.gengarPaths, self.nidoPaths = {}, {} for i = 1, 3 do local g = intro.gengar and intro.gengar["frame" .. i] local n = intro.nidorino and intro.nidorino["frame" .. i] self.gengarFrames[i] = img(g) self.nidoFrames[i] = img(n) self.gengarPaths[i] = g and g.path self.nidoPaths[i] = n and n.path end -- fight state (PlayIntroScene entry, intro.asm:30-39): Gengar BG pose at -- tile (13,7) = screen (104,56); Nidorino OAM base (0,80) = screen -- (-8,72) after the OAM +8 offsets self.gengarX, self.gengarY = 104, 56 self.nidoX, self.nidoY = -8, 72 self.gengarPose, self.nidoFrame = 1, 1 self.opIndex, self.opTimer = 1, 0 self.fadeStep = nil return self end -- PlayIntro never StopAllSounds's: Music_IntroBattle outlasts the fade and -- keeps playing over the title logo drop until title.asm starts -- MUSIC_TITLE_SCREEN (same continuity Yellow got in #436). function IntroMovie:finish() if self.finished then return end self.finished = true self.game.stack:pop() if self.onDone then self.onDone() end end -- Solid white beat before the title screen is pushed (see POST_FADE_WHITE). function IntroMovie:exitToTitle() if self.finished or self.phase == 4 then return end self.phase = 4 self.timer = 0 end function IntroMovie:startPhase(phase) self.phase = phase self.timer = 0 if phase == 3 then -- intro.asm:333-338 local data = self.game.data local songs = data.audio and data.audio.songs local song = self.introCfg.music or "Music_IntroBattle" if songs and songs[song] then pcall(Music.play, data, song, false) end end end -- one frame of the fight script (see FIGHT_SCRIPT) function IntroMovie:endScene() self.opIndex = FIGHT_FADE_INDEX self.opTimer = 0 end -- home/overworld.asm:2395 function IntroMovie:pollInterrupt() local input = self.game.input if not input then return false end local function down(b) return (input.isDown and input:isDown(b)) or input:wasPressed(b) end local last = self.lastPollHeld or {} local held = { a = down("a"), start = down("start") } self.lastPollHeld = held if (held.a and not last.a) or (held.start and not last.start) then return true end return down("up") and down("select") and down("b") and not (held.a or held.start or down("down") or down("left") or down("right")) end function IntroMovie:fightStep() while true do local op = FIGHT_SCRIPT[self.opIndex] if not op then self:finish() return end -- an op that consumed frames ends ON its last frame; only the instant -- ops (sfx / pose / frame, which are plain writes between DelayFrames -- calls in PlayIntroScene) chain into the next op the same frame local timed = false if op.sfx then Sound.play(self.game.data, op.sfx) elseif op.pose then self.gengarPose = op.pose elseif op.frame then self.nidoFrame = op.frame elseif op.move then -- 2px per 2 frames (IntroMoveMon: CheckForUserInterruption c=2) if self.opTimer % 2 == 0 then if op.move == "scrollIn" then self.gengarX = self.gengarX - 2 self.nidoX = self.nidoX + 2 else self.gengarX = self.gengarX + (op.dx > 0 and 2 or -2) end end self.opTimer = self.opTimer + 1 if self:pollInterrupt() then -- engine/movie/intro.asm:264 if op.move == "scrollIn" then self:endScene() return end self.opIndex = self.opIndex + 1 self.opTimer = 0 return end if self.opTimer < (op.px or math.abs(op.dx)) then return end timed = true elseif op.anim then -- one {dy,dx} delta per 5 frames (AnimateIntroNidorino: DelayFrames 5) if self.opTimer % 5 == 0 then local d = ANIM[op.anim][self.opTimer / 5 + 1] self.nidoY = self.nidoY + d[1] self.nidoX = self.nidoX + d[2] end self.opTimer = self.opTimer + 1 if self.opTimer < #ANIM[op.anim] * 5 then return end timed = true elseif op.wait then self.opTimer = self.opTimer + 1 if self:pollInterrupt() then self:endScene() return end if self.opTimer < op.wait then return end timed = true elseif op.fade then self.opTimer = self.opTimer + 1 self.fadeStep = math.floor((self.opTimer - 1) / 8) + 1 if self.opTimer >= op.fade then self:exitToTitle() end return end self.opIndex = self.opIndex + 1 self.opTimer = 0 if timed then return end end end function IntroMovie:update(dt) if self.skipAll then self:finish() return end if self.phase == 4 then self.timer = self.timer + 1 if self.timer >= POST_FADE_WHITE then self:finish() end return end self.timer = self.timer + 1 if self.phase == 1 then -- the copyright card is a bare DelayFrames, deaf to input (intro.asm:311) if self.timer >= COPYRIGHT_FRAMES then self:startPhase(2) end elseif self.phase == 2 then if self.timer == STAR_START then Sound.play(self.game.data, "Shooting_Star") -- splash.asm:29-30 end -- intro.asm:325 `jr c, .next` if self.timer >= STAR_START and self.timer < WAVES_END and self:pollInterrupt() then self:startPhase(3) return end if self.timer >= SPLASH_FRAMES then self:startPhase(3) end else -- PlayShootingStar ends `jp Delay3` once Music_IntroBattle is playing -- (intro.asm:337), so PlayIntroScene's first op is not on the music's -- own frame if self.timer > Timing.DELAY3 then self:fightStep() end end end -- the letterbox bars: 4 black tile rows top and bottom -- (IntroDrawBlackBars, intro.asm:343-357); drawn AFTER the sprites since -- both Nidorino and the small stars carry OAM_PRIO (intro.asm:195, -- splash.asm:149) so the bars cover them. local function drawBars(bgp) local g = (3 - math.floor((bgp or BGP_NORMAL) / 64) % 4) / 3 love.graphics.setColor(g, g, g, 1) love.graphics.rectangle("fill", 0, 0, 160, 32) love.graphics.rectangle("fill", 0, 112, 160, 32) love.graphics.setColor(1, 1, 1, 1) end function IntroMovie:logoObp0() local t = self.timer - FLASH_START if t < 0 then return LOGO_OBP0[0] end return LOGO_OBP0[math.min(3, math.floor(t / 10) + 1)] end function IntroMovie:drawObp0(img, path, x, y, obp) if not img then return end if not path then love.graphics.draw(img, x, y) return end local P = require("src.render.PaletteFX") local SR = require("src.render.SpriteRenderer") love.graphics.draw(SR.obpImage(path, obpRamp(obp, P.GRAYS), "gfobp" .. obp), x, y) if P.usesSpriteObp() then local blue = GameVersion.isBlue() P.markUiSpriteRedraw(SR.obpImage(path, obpRamp(obp, blue and P.GBC_OBJ_BLUE or P.ogObjBase()), (blue and "gfobj_blue" or "gfobj_soft") .. obp), nil, x, y) end end function IntroMovie:drawSplash() local t = self.timer if t >= STAR_START then -- logo + GAME FREAK letters appear with the star OAM -- (LoadShootingStarGraphics, splash.asm:18-25); the logo palette -- rotates during the 3-flash loop (splash.asm:72-82) if self.studioLogo then local flashing = t >= FLASH_START and t < FLASH_START + FLASH_FRAMES local dim = flashing and math.floor((t - FLASH_START) / 5) % 2 == 0 love.graphics.setColor(1, 1, 1, dim and 0.35 or 1) love.graphics.draw(self.studioLogo, self.studioX, self.studioY, 0, self.studioScale, self.studioScale) else local obp = self:logoObp0() self:drawObp0(self.logo, self.logoPath, LOGO_X, LOGO_Y, obp) self:drawObp0(self.gfText, self.gfTextPath, TEXT_X, TEXT_Y, obp) end love.graphics.setColor(1, 1, 1, 1) end if t >= WAVES_START then -- small stars: wave w spawns at y=88 every 24 frames, everything falls -- +1px per 3-frame substep until the wave loop ends; the lower star in -- the tile blinks every substep (splash.asm:97-146, 186-209) -- engine/movie/splash.asm:186 local moves = math.min(math.floor((t - WAVES_START) / 3) + 1, 6 * 8) local lit = moves % 2 == 1 for w, xs in ipairs(STAR_WAVES) do local spawn = (w - 1) * 8 -- in substeps if moves > spawn then local y = 88 + (moves - spawn) if y < 144 then local img = lit and (self.smallStarBlink or self.smallStar) or self.smallStar for _, x in ipairs(xs) do if img then love.graphics.draw(img, x, y) else love.graphics.setColor(0, 0, 0, 1) love.graphics.rectangle("fill", x + 3, y + 1, 2, 2) love.graphics.setColor(1, 1, 1, 1) end end end end end end drawBars() if t >= STAR_START and t < FLASH_START then -- ..(engine/movie/splash.asm ln 32) local n = t - STAR_START + 1 local sx, sy = 152 - 4 * n, -16 + 4 * n if self.bigStar and self.bigStarPath then local P = require("src.render.PaletteFX") local SR = require("src.render.SpriteRenderer") love.graphics.draw(SR.obpImage(self.bigStarPath, obpRamp(STAR_OBP1, P.GRAYS), "gfobp" .. STAR_OBP1), sx, sy) if P.usesSpriteObp() then -- engine/movie/splash.asm:22 local blue = GameVersion.isBlue() P.markUiSpriteRedraw(SR.obpImage(self.bigStarPath, obpRamp(STAR_OBP1, P.ogBg()), (blue and "gfobj1_blue" or "gfobj1_soft") .. STAR_OBP1), nil, sx, sy) end elseif self.bigStar then love.graphics.draw(self.bigStar, sx, sy) else love.graphics.setColor(0, 0, 0, 1) love.graphics.rectangle("fill", sx + 6, sy + 6, 4, 4) love.graphics.setColor(1, 1, 1, 1) end end end function IntroMovie:drawFight() -- Gengar: a 56x56 BG-tile pose recomposed from gengar_N.tilemap, moved -- by scrolling SCX (intro.asm:32-33, 235-269) -- Nidorino: 6x6 OAM sprite, one of the three red_nidorino poses local fade = self.fadeStep and FADE_TO_WHITE[self.fadeStep] local bgp = fade and fade.bgp or BGP_NORMAL local obp = fade and fade.obp or BGP_NORMAL local P = require("src.render.PaletteFX") local SR = require("src.render.SpriteRenderer") local nido = self.nidoFrames[self.nidoFrame] local nidoPath = self.nidoPaths and self.nidoPaths[self.nidoFrame] if nido then if fade and nidoPath then love.graphics.draw(SR.obpImage(nidoPath, obpRamp(obp, P.GRAYS), "fadeobp" .. obp), self.nidoX, self.nidoY) else love.graphics.draw(nido, self.nidoX, self.nidoY) end end local gengar = self.gengarFrames[self.gengarPose] local gengarPath = self.gengarPaths and self.gengarPaths[self.gengarPose] if gengar then if fade and gengarPath then love.graphics.draw(SR.obpImage(gengarPath, obpRamp(bgp, P.GRAYS), "fadebgp" .. bgp), self.gengarX, self.gengarY) else love.graphics.draw(gengar, self.gengarX, self.gengarY) end end if not gengar and not nido then love.graphics.setColor(0, 0, 0, 1) Font.draw(Strings("GENGAR VS NIDORINO"), (160 - 18 * 8) / 2, 64) love.graphics.setColor(1, 1, 1, 1) end drawBars(bgp) self:replayObjLayer(nido, gengar, fade) end local FIGHT_CLIP = { 0, 32, 160, 80 } function IntroMovie:replayObjLayer(nido, gengar, fade) local P = require("src.render.PaletteFX") local nidoPath = self.nidoPaths and self.nidoPaths[self.nidoFrame] if not (nido and nidoPath and P.usesSpriteObp()) then return end local SR = require("src.render.SpriteRenderer") local blue = GameVersion.isBlue() local opts = { clip = FIGHT_CLIP } local objColors = blue and P.GBC_OBJ_BLUE or P.ogObjBase() local objGroup = blue and "gbcobj_blue" or "gbcobj_soft" local bgColors, bgGroup = P.ogBg(), blue and "ogbg_blue" or "ogbg" if fade then objColors, objGroup = obpRamp(fade.obp, objColors), objGroup .. fade.obp bgColors, bgGroup = obpRamp(fade.bgp, bgColors), bgGroup .. fade.bgp end -- engine/movie/intro.asm:28 P.markUiSpriteRedraw(SR.obpImage(nidoPath, objColors, objGroup), nil, self.nidoX, self.nidoY, opts) local gengarPath = self.gengarPaths[self.gengarPose] if gengar and gengarPath then -- engine/movie/intro.asm:195 P.markUiSpriteRedraw(SR.obpImage(gengarPath, bgColors, bgGroup), nil, self.gengarX, self.gengarY, opts) end end function IntroMovie:drawCopyPrefix(x, y) for _, t in ipairs(self.copyPrefix) do love.graphics.draw(self.copyright, self.copyQuads[t], x, y) x = x + 8 end if self.nineImg then love.graphics.draw(self.nineImg, x, y) end end function IntroMovie:drawCopyright() if self.studio.card or self.studio.credit then local card = self.studio.card or "" local credit = self.studio.credit or "" love.graphics.setColor(0, 0, 0, 1) Font.draw(card, (160 - #card * 8) / 2, 64) Font.draw(credit, (160 - #credit * 8) / 2, 80) elseif self.copyright and self.gfInc then local function row(seq, x, y) for _, t in ipairs(seq) do love.graphics.draw(self.copyright, self.copyQuads[t], x, y) x = x + 8 end end for _, y in ipairs({ 56, 72, 88 }) do self:drawCopyPrefix(16, y) end row(COPY_NINTENDO, 80, 56) row(COPY_CREATURES, 80, 72) love.graphics.draw(self.gfInc, 80, 88) else love.graphics.setColor(0, 0, 0, 1) Font.draw(Strings("Nintendo"), 80, 56) Font.draw(Strings("Creatures inc."), 80, 72) Font.draw(Strings("GAME FREAK inc."), 16, 88) end love.graphics.setColor(1, 1, 1, 1) end function IntroMovie:draw() love.graphics.setColor(1, 1, 1, 1) love.graphics.rectangle("fill", 0, 0, 160, 144) if self.phase == 4 then -- GBPalWhiteOut hold (title.asm DisplayTitleScreen load-in) return elseif self.phase == 1 then self:drawCopyright() elseif self.phase == 2 then self:drawSplash() else self:drawFight() end love.graphics.setColor(1, 1, 1, 1) end return IntroMovie