mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-16 16:21:30 +02:00
Merge branch 'fix1037' into fix1038
# Conflicts: # docs/modding.md
This commit is contained in:
+13
-1
@@ -253,6 +253,18 @@ local function useOn(game, battle, id, target, list, moveIndex, picker)
|
||||
return
|
||||
end
|
||||
|
||||
if result == "kept" then
|
||||
if battle then
|
||||
list:close()
|
||||
showMessages(game, payload, function()
|
||||
battle:itemUsed({})
|
||||
end)
|
||||
else
|
||||
showMessages(game, payload, closePicker)
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
if result == "consumed" then
|
||||
consume(game, id)
|
||||
-- refresh counts in the list
|
||||
@@ -404,7 +416,7 @@ local function useItem(game, battle, id, list)
|
||||
showMessages(game, payload)
|
||||
return
|
||||
end
|
||||
if ItemEffects.needsTarget(id, def) and not ItemEffects.isBall(id) then
|
||||
if ItemEffects.needsTarget(id, def, game.data) and not ItemEffects.isBall(id) then
|
||||
-- TMs/HMs boot up and announce their move before the target picker
|
||||
-- (ItemUseTMHM: BootedUpTMText / BootedUpHMText + TeachMachineMoveText)
|
||||
if def and def.machine then
|
||||
|
||||
@@ -67,6 +67,7 @@ local function withdraw(game)
|
||||
game.stack:push(ListMenu.new(game,
|
||||
Strings("BOX %d (WITHDRAW)", game.save.currentBox), items, {
|
||||
noSound = true, -- PCMainMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
kind = "pc_box_withdraw",
|
||||
onChoose = function(item, list)
|
||||
local mon = box[item.value]
|
||||
if not mon then return end
|
||||
@@ -114,6 +115,7 @@ local function deposit(game)
|
||||
end
|
||||
game.stack:push(ListMenu.new(game, "PARTY (DEPOSIT)", items, {
|
||||
noSound = true, -- PCMainMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
kind = "pc_box_deposit",
|
||||
onChoose = function(item, list)
|
||||
local mon = game.save.party[item.value]
|
||||
if not mon then return end
|
||||
@@ -160,6 +162,7 @@ local function release(game)
|
||||
game.stack:push(ListMenu.new(game,
|
||||
Strings("BOX %d (RELEASE)", game.save.currentBox), items, {
|
||||
noSound = true, -- PCMainMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
kind = "pc_box_release",
|
||||
onChoose = function(_, list)
|
||||
local mon = box[list.index]
|
||||
if not mon then return end
|
||||
@@ -193,6 +196,7 @@ local function changeBox(game)
|
||||
end
|
||||
game.stack:push(ListMenu.new(game, "CHANGE BOX", items, {
|
||||
noSound = true, -- PCMainMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
kind = "pc_box_change",
|
||||
onChoose = function(item, list)
|
||||
-- the original asks BEFORE switching ("When you change a #MON
|
||||
-- BOX, data will be saved. OK?"); declining aborts the change
|
||||
|
||||
+65
-59
@@ -1,29 +1,5 @@
|
||||
-- Boot splash + attract movie, a faithful port of PlayIntro
|
||||
-- (engine/movie/intro.asm) and AnimateShootingStar (engine/movie/splash.asm)
|
||||
-- using the real extracted art (data/generated/field.lua `intro` manifest).
|
||||
--
|
||||
-- Three frame-counted phases:
|
||||
-- 1. copyright card, 180 frames (intro.asm:311-312).
|
||||
-- 2. shooting star: 64 frames of empty letterbox (intro.asm:323-324), then
|
||||
-- the big star streaks down-left for 40 frames while the studio logo
|
||||
-- sits centered in the letterbox band (the GAME FREAK logo + letter
|
||||
-- row it replaces sat at (72,56)/(40,80); splash.asm:27-60, 211-228),
|
||||
-- the logo flashes 3x10 frames (splash.asm:72-82), 4 waves of small
|
||||
-- stars rain from the logo -- 6x24 frames, +1px every 3 frames, lower
|
||||
-- star blinking (splash.asm:97-146, 163-209) -- and a 40 frame hold
|
||||
-- (intro.asm:329-331).
|
||||
-- 3. the Gengar/Nidorino fight (PlayIntroScene, intro.asm:23-141), played
|
||||
-- from FIGHT_SCRIPT below: Music_IntroBattle starts, Gengar (56x56 BG
|
||||
-- pose from a gengar_N.tilemap, at tile 13,7 = x104,y56) scrolls left
|
||||
-- while Nidorino (48x48 OAM at x-8,y72) walks right, then the scripted
|
||||
-- hip/hop hops, Gengar's raise + slash lunge, Nidorino's dodge leap,
|
||||
-- retreat, crouch and final lunge, ending in a 24-frame fade to white
|
||||
-- (GBFadeOutToWhite, home/fade.asm:26-40).
|
||||
--
|
||||
-- Any of A/B/START skips the whole movie (CheckForUserInterruption).
|
||||
-- Pops itself and calls onDone() when finished or skipped. All art loads
|
||||
-- through pcall and every missing graphic degrades to a text/rect
|
||||
-- fallback, so the movie stays headless-safe.
|
||||
-- ..(engine/movie/intro.asm ln 8)
|
||||
-- ..(engine/movie/splash.asm ln 27)
|
||||
|
||||
local Font = require("src.render.Font")
|
||||
local Music = require("src.core.Music")
|
||||
@@ -76,15 +52,15 @@ 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)
|
||||
|
||||
-- logo 16x24 at grid (10,9), letters row at grid y=12 cols 6..15
|
||||
-- (GameFreakLogoOAMData, splash.asm:211-228; screen = grid*8, OAM offsets
|
||||
-- cancel)
|
||||
-- ..(engine/movie/splash.asm ln 211)
|
||||
local LOGO_X, LOGO_Y = 72, 56
|
||||
local TEXT_X, TEXT_Y = 40, 80
|
||||
|
||||
-- ..(engine/movie/title.asm ln 390)
|
||||
local COPY_PREFIX = { 0, 1, 2, 1, 3, 1, 4 }
|
||||
local COPY_NINTENDO = { 5, 6, 7, 8, 9, 10 }
|
||||
local COPY_CREATURES = { 11, 12, 13, 14, 15, 16, 17, 18 }
|
||||
|
||||
-- The studio logo (assets/logo/minilogo.png) stands in for both the
|
||||
-- GAME FREAK logo and its letter row, so it gets the whole band between
|
||||
-- the letterbox bars (y 32..112) down to where the star waves spawn
|
||||
-- (y=88): fit it inside this box, centered, aspect preserved.
|
||||
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
|
||||
@@ -155,10 +131,19 @@ function IntroMovie.new(game, onDone)
|
||||
self.studio = intro.studio or {}
|
||||
self.skipAll = intro.skip and true or false
|
||||
local function img(e) return tryImage(e and e.path) end
|
||||
self.copyright = tryImage("assets/generated/title/copyright.png")
|
||||
-- studio mark replaces the GAME FREAK logo + splash text entirely; the
|
||||
-- extracted logo stays as the fallback if the asset is missing
|
||||
self.studioLogo = tryImage(self.studio.logo or "assets/logo/minilogo.png")
|
||||
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")
|
||||
self.studioLogo = tryImage(self.studio.logo)
|
||||
if self.studioLogo then
|
||||
self.studioLogo:setFilter("nearest", "nearest")
|
||||
local iw, ih = self.studioLogo:getDimensions()
|
||||
@@ -306,24 +291,12 @@ function IntroMovie:drawSplash()
|
||||
if self.studioLogo then
|
||||
love.graphics.draw(self.studioLogo, self.studioX, self.studioY,
|
||||
0, self.studioScale, self.studioScale)
|
||||
elseif self.logo then
|
||||
love.graphics.draw(self.logo, LOGO_X, LOGO_Y)
|
||||
else
|
||||
if self.logo then love.graphics.draw(self.logo, LOGO_X, LOGO_Y) end
|
||||
if self.gfText then love.graphics.draw(self.gfText, TEXT_X, TEXT_Y) end
|
||||
end
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
if t >= STAR_START and t < FLASH_START then
|
||||
-- big star: from OAM (160,0) moving +4Y/-4X per frame
|
||||
-- (GameFreakShootingStarOAMData + .bigStarLoop, splash.asm:32-60)
|
||||
local n = t - STAR_START + 1
|
||||
local sx, sy = 152 - 4 * n, -16 + 4 * n
|
||||
if 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
|
||||
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
|
||||
@@ -351,6 +324,18 @@ function IntroMovie:drawSplash()
|
||||
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 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()
|
||||
@@ -379,17 +364,38 @@ function IntroMovie:drawFight()
|
||||
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 row(COPY_PREFIX, 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 == 1 then
|
||||
-- custom boot card (replaces the Nintendo / GAME FREAK copyright
|
||||
-- card; no (c) glyph in the charmap, keep it ASCII-safe)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
local credit = self.studio.credit or Strings("bois club")
|
||||
Font.draw("2026", (160 - 4 * 8) / 2, 48)
|
||||
Font.draw(credit, (160 - #credit * 8) / 2, 64)
|
||||
Font.draw("bryanthaboi", (160 - 11 * 8) / 2, 80)
|
||||
self:drawCopyright()
|
||||
elseif self.phase == 2 then
|
||||
self:drawSplash()
|
||||
else
|
||||
|
||||
@@ -51,6 +51,7 @@ function ListMenu.new(game, title, items, opts)
|
||||
local self = setmetatable({}, ListMenu)
|
||||
self.game = game
|
||||
self.title = title
|
||||
self.kind = opts.kind or title
|
||||
self.items = items
|
||||
self.index = 1
|
||||
self.scroll = 0
|
||||
|
||||
@@ -72,6 +72,7 @@ end
|
||||
local function withdraw(game)
|
||||
local pc = game.save.pcItems
|
||||
game.stack:push(ListMenu.new(game, "WITHDRAW ITEM", buildItems(game, pc), {
|
||||
kind = "pc_item_withdraw",
|
||||
messageBox = true,
|
||||
noSound = true, -- PlayerPCMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
onChoose = function(item, list)
|
||||
@@ -110,6 +111,7 @@ local function deposit(game)
|
||||
if not Bag.isBadge(id) then depositable[id] = count end
|
||||
end
|
||||
game.stack:push(ListMenu.new(game, "DEPOSIT ITEM", buildItems(game, depositable), {
|
||||
kind = "pc_item_deposit",
|
||||
messageBox = true,
|
||||
noSound = true, -- PlayerPCMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
onChoose = function(item, list)
|
||||
@@ -131,6 +133,7 @@ end
|
||||
local function toss(game)
|
||||
local pc = game.save.pcItems
|
||||
game.stack:push(ListMenu.new(game, "TOSS ITEM", buildItems(game, pc), {
|
||||
kind = "pc_item_toss",
|
||||
messageBox = true,
|
||||
noSound = true, -- PlayerPCMenu holds BIT_NO_MENU_BUTTON_SOUND (#570)
|
||||
onChoose = function(item, list)
|
||||
|
||||
+169
-46
@@ -103,7 +103,31 @@ local YELLOW_CYCLE_SPECIES = {
|
||||
"JIGGLYPUFF", "MEOWTH", "PSYDUCK", "VULPIX", "ABRA",
|
||||
"GROWLITHE", "CUBONE", "GASTLY", "HITMONLEE", "SNORLAX", "DRAGONITE",
|
||||
}
|
||||
local CYCLE_FRAMES = 240 -- the original waits ~4s between picks
|
||||
-- ..(engine/movie/title.asm ln 227)
|
||||
local HOLD_FRAMES = 200
|
||||
local STARTERS = { CHARMANDER = true, SQUIRTLE = true, BULBASAUR = true }
|
||||
|
||||
-- ..(engine/movie/title2.asm ln 13)
|
||||
local function scrollFrames(steps, offset)
|
||||
local frames = {}
|
||||
for _, step in ipairs(steps) do
|
||||
for _ = 1, step[2] do
|
||||
frames[#frames + 1] = offset
|
||||
offset = offset - step[1]
|
||||
end
|
||||
end
|
||||
return frames
|
||||
end
|
||||
local OUT_FRAMES = scrollFrames(
|
||||
{ { 1, 2 }, { 2, 2 }, { 3, 2 }, { 4, 2 }, { 5, 2 }, { 6, 2 },
|
||||
{ 8, 3 }, { 9, 3 } }, 0)
|
||||
local IN_FRAMES = scrollFrames(
|
||||
{ { 10, 2 }, { 9, 4 }, { 8, 4 }, { 6, 3 }, { 5, 2 }, { 3, 1 },
|
||||
{ 1, 1 } }, 120)
|
||||
|
||||
-- ..(engine/movie/title2.asm ln 85)
|
||||
local BALL_FRAMES = { 97, 95, 94, 93, 92, 93, 94, 95, 97, 100 }
|
||||
local BALL_REST = 100
|
||||
|
||||
local function tryImage(path)
|
||||
if not path then return nil end
|
||||
@@ -178,6 +202,27 @@ function TitleState.new(game, opts)
|
||||
or "assets/generated/title/red_version.png")
|
||||
self.player = tryImage(imagePath(title.player)
|
||||
or "assets/generated/title/player.png")
|
||||
-- ..(engine/movie/title2.asm ln 85)
|
||||
if self.player then
|
||||
local pw, ph = self.player:getDimensions()
|
||||
self.ballQuad = love.graphics.newQuad(0, 16, 8, 8, pw, ph)
|
||||
self.playerQuads = {
|
||||
{ love.graphics.newQuad(0, 0, pw, 16, pw, ph), 0, 0 },
|
||||
{ love.graphics.newQuad(8, 16, pw - 8, 8, pw, ph), 8, 16 },
|
||||
{ love.graphics.newQuad(0, 24, pw, ph - 24, pw, ph), 0, 24 },
|
||||
}
|
||||
end
|
||||
self.copyImg = tryImage(imagePath(title.copyright)
|
||||
or "assets/generated/title/copyright.png")
|
||||
self.copyQuads = {}
|
||||
if self.copyImg then
|
||||
local iw, ih = self.copyImg:getDimensions()
|
||||
for t = 0, 18 do
|
||||
self.copyQuads[t] = love.graphics.newQuad(t * 8, 0, 8, 8, iw, ih)
|
||||
end
|
||||
end
|
||||
self.gfInc = tryImage(imagePath(title.gamefreakInc)
|
||||
or "assets/generated/title/gamefreak_inc.png")
|
||||
self.blue = GameVersion.isBlue()
|
||||
self.yellow = GameVersion.isYellow()
|
||||
or title.layout == "yellow_pikachu"
|
||||
@@ -203,7 +248,10 @@ function TitleState.new(game, opts)
|
||||
self.blinkTimer = 0
|
||||
self.blinkAt = nil
|
||||
else
|
||||
self.phase = "loop"
|
||||
-- ..(engine/movie/title.asm ln 28)
|
||||
self.scy = 0x40
|
||||
self.phase = "drop"
|
||||
self.dropStep, self.dropLeft = 1, nil
|
||||
self.showBubble = true
|
||||
end
|
||||
local defaultCycle = self.yellowLayout and { "PIKACHU" }
|
||||
@@ -216,14 +264,15 @@ function TitleState.new(game, opts)
|
||||
self.cycleIndex = 1
|
||||
self.timer = 0
|
||||
self.blink = 0
|
||||
self.scrollPhase = "hold"
|
||||
self.scrollFrame = 1
|
||||
self.monOffset = 0
|
||||
self.ballY = BALL_REST
|
||||
return self
|
||||
end
|
||||
|
||||
function TitleState:enter()
|
||||
-- Yellow defers the title theme until after the logo drop and
|
||||
-- Pikachu's cry (title.asm plays MUSIC_TITLE_SCREEN only after
|
||||
-- WaitForSoundToFinish on PikachuCry1)
|
||||
if self.yellowLayout then return end
|
||||
if self.phase ~= "loop" then return end
|
||||
self:startMusic()
|
||||
end
|
||||
|
||||
@@ -240,6 +289,11 @@ end
|
||||
local DROP_STEPS = {
|
||||
{ -4, 16 }, { 3, 4 }, { -3, 4 }, { 2, 2 }, { -2, 2 }, { 1, 2 }, { -1, 2 },
|
||||
}
|
||||
local SETTLE_FRAMES = 36
|
||||
|
||||
-- ..(engine/movie/title.asm ln 201)
|
||||
local RIBBON_FRAMES = {}
|
||||
for offset = 112, 4, -4 do RIBBON_FRAMES[#RIBBON_FRAMES + 1] = offset end
|
||||
|
||||
-- the boot cinematic up to the interactive loop; one call per frame
|
||||
function TitleState:updateSequence()
|
||||
@@ -263,12 +317,23 @@ function TitleState:updateSequence()
|
||||
self.dropLeft = nil
|
||||
end
|
||||
elseif self.phase == "settle" then
|
||||
-- ld c, 36 / DelayFrames, then the whoosh and the bubble
|
||||
self.timer = self.timer + 1
|
||||
if self.timer >= 36 then
|
||||
if self.timer >= SETTLE_FRAMES then
|
||||
Sound.play(data, "Intro_Whoosh")
|
||||
self.showBubble = true
|
||||
self.phase = "bubble"
|
||||
self.phase = self.yellowLayout and "bubble" or "ribbon"
|
||||
self.ribbonOffset = RIBBON_FRAMES[1]
|
||||
self.timer = 0
|
||||
end
|
||||
elseif self.phase == "ribbon" then
|
||||
self.timer = self.timer + 1
|
||||
local offset = RIBBON_FRAMES[self.timer + 1]
|
||||
if offset then
|
||||
self.ribbonOffset = offset
|
||||
else
|
||||
self.ribbonOffset = nil
|
||||
self:startMusic()
|
||||
self.phase = "loop"
|
||||
self.timer = 0
|
||||
end
|
||||
elseif self.phase == "bubble" then
|
||||
@@ -432,12 +497,63 @@ function TitleState:openMenu()
|
||||
game.stack:push(menu)
|
||||
end
|
||||
|
||||
-- ..(engine/movie/title.asm ln 271)
|
||||
function TitleState:pickNewMon()
|
||||
if #self.cycleSpecies < 2 then return end
|
||||
local pick = self.cycleIndex
|
||||
while pick == self.cycleIndex do
|
||||
pick = love.math.random(1, #self.cycleSpecies)
|
||||
end
|
||||
self.cycleIndex = pick
|
||||
end
|
||||
|
||||
function TitleState:setCyclePhase(phase)
|
||||
self.scrollPhase = phase
|
||||
self.scrollFrame = 1
|
||||
self.timer = 0
|
||||
if phase == "in" then
|
||||
self:pickNewMon()
|
||||
self.monOffset = IN_FRAMES[1]
|
||||
elseif phase == "out" then
|
||||
self.monOffset = OUT_FRAMES[1]
|
||||
elseif phase == "ball" then
|
||||
self.ballY = BALL_FRAMES[1]
|
||||
else
|
||||
self.monOffset = 0
|
||||
end
|
||||
end
|
||||
|
||||
function TitleState:updateCycle()
|
||||
local phase = self.scrollPhase
|
||||
if phase == "hold" then
|
||||
if self.timer >= HOLD_FRAMES then self:setCyclePhase("out") end
|
||||
return
|
||||
end
|
||||
local frames = phase == "out" and OUT_FRAMES
|
||||
or phase == "ball" and BALL_FRAMES or IN_FRAMES
|
||||
self.scrollFrame = self.scrollFrame + 1
|
||||
local value = frames[self.scrollFrame]
|
||||
if value then
|
||||
if phase == "ball" then self.ballY = value else self.monOffset = value end
|
||||
return
|
||||
end
|
||||
if phase == "out" then
|
||||
-- ..(engine/movie/title.asm ln 235)
|
||||
self:setCyclePhase(
|
||||
STARTERS[self.cycleSpecies[self.cycleIndex]] and "ball" or "in")
|
||||
elseif phase == "ball" then
|
||||
self:setCyclePhase("in")
|
||||
else
|
||||
self:setCyclePhase("hold")
|
||||
end
|
||||
end
|
||||
|
||||
function TitleState:update(dt)
|
||||
if self.phase ~= "loop" then
|
||||
self:updateSequence()
|
||||
return
|
||||
end
|
||||
if self.yellowLayout then
|
||||
if self.phase ~= "loop" then
|
||||
self:updateSequence()
|
||||
return -- input is ignored until the cinematic lands (title.asm)
|
||||
end
|
||||
self:updateBlink()
|
||||
local input = self.game.input
|
||||
if input:wasPressed("start") or input:wasPressed("a") then
|
||||
@@ -452,21 +568,7 @@ function TitleState:update(dt)
|
||||
end
|
||||
self.timer = self.timer + 1
|
||||
self.blink = (self.blink + 1) % 60
|
||||
if not self.yellowLayout and self.timer >= CYCLE_FRAMES then
|
||||
self.timer = 0
|
||||
-- random pick that never repeats the current one
|
||||
if #self.cycleSpecies > 1 then
|
||||
local pick = self.cycleIndex
|
||||
while pick == self.cycleIndex do
|
||||
pick = love.math.random(1, #self.cycleSpecies)
|
||||
end
|
||||
self.cycleIndex = pick
|
||||
end
|
||||
self.slideIn = 20 -- TitleScreenScrollInMon slides the pic in
|
||||
end
|
||||
if self.slideIn and self.slideIn > 0 then
|
||||
self.slideIn = self.slideIn - 1
|
||||
end
|
||||
self:updateCycle()
|
||||
local input = self.game.input
|
||||
if input:wasPressed("start") or input:wasPressed("a") then
|
||||
-- the title mon cries when you leave the title (.finishedWaiting);
|
||||
@@ -478,15 +580,14 @@ function TitleState:update(dt)
|
||||
end
|
||||
end
|
||||
|
||||
-- The original tilemap (engine/movie/title.asm): logo at tile (2,1),
|
||||
-- the version ribbon at (7,8), Red's title art as OAM at px (82,80),
|
||||
-- the title mon in the 7x7 box at tile (5,10), copyright on row 17.
|
||||
-- Yellow (title_yellow.asm): logo (2,1), speech bubble (6,4), Pikachu
|
||||
-- (4,8) 12x9 -- no version ribbon, no cycling mon, no Red OAM.
|
||||
-- ..(engine/movie/title.asm ln 28)
|
||||
function TitleState:draw()
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle("fill", 0, 0, 160, 144)
|
||||
local scrollY = self.yellowLayout and -(self.scy or 0) or 0
|
||||
local scrollY = -(self.scy or 0)
|
||||
-- ..(engine/movie/title.asm ln 28)
|
||||
local preRibbon = not self.yellowLayout
|
||||
and (self.phase == "drop" or self.phase == "settle")
|
||||
if self.logo then
|
||||
love.graphics.draw(self.logo, 16, 8 + scrollY)
|
||||
else
|
||||
@@ -514,27 +615,29 @@ function TitleState:draw()
|
||||
-- Yellow's Version_GFX slot holds a leftover "Blue Version" ribbon
|
||||
-- (pokeyellow gfx/title/blue_version.png, unreferenced by title code);
|
||||
-- the Yellow fallback layout draws no ribbon at all.
|
||||
if self.version and not self.yellow then
|
||||
if self.version and not self.yellow and not preRibbon then
|
||||
local iw, ih = self.version:getDimensions()
|
||||
local rx = self.ribbonOffset or 0
|
||||
if self.versionFull then
|
||||
-- a continuous ribbon (versionRibbon) centers as one piece
|
||||
love.graphics.draw(self.version, math.floor((160 - iw) / 2), 64)
|
||||
love.graphics.draw(self.version, math.floor((160 - iw) / 2) + rx, 64)
|
||||
elseif self.blue then
|
||||
love.graphics.draw(self.version,
|
||||
love.graphics.newQuad(0, 0, 64, 8, iw, ih), 56, 64)
|
||||
love.graphics.newQuad(0, 0, 64, 8, iw, ih), 56 + rx, 64)
|
||||
else
|
||||
love.graphics.draw(self.version,
|
||||
love.graphics.newQuad(0, 0, 16, 8, iw, ih), 56, 64)
|
||||
love.graphics.newQuad(0, 0, 16, 8, iw, ih), 56 + rx, 64)
|
||||
love.graphics.draw(self.version,
|
||||
love.graphics.newQuad(40, 0, 40, 8, iw, ih), 80, 64)
|
||||
love.graphics.newQuad(40, 0, 40, 8, iw, ih), 80 + rx, 64)
|
||||
end
|
||||
end
|
||||
local sprite, spriteTrueColor = self:currentSprite()
|
||||
local sprite, spriteTrueColor
|
||||
if self.scrollPhase ~= "ball" then
|
||||
sprite, spriteTrueColor = self:currentSprite()
|
||||
end
|
||||
if sprite then
|
||||
local w, h = sprite:getDimensions()
|
||||
local slide = (self.slideIn or 0) * 8 -- scroll in from the right
|
||||
-- bottom-aligned and centered in the (5,10)-(11,16) tile box
|
||||
local x = 40 + math.floor((56 - w) / 2) + slide
|
||||
local x = 40 + math.floor((56 - w) / 2) + self.monOffset
|
||||
local y = 136 - h
|
||||
love.graphics.draw(sprite, x, y)
|
||||
-- a full-color mon keeps its own palette through the SGB pass, minus
|
||||
@@ -550,13 +653,33 @@ function TitleState:draw()
|
||||
end
|
||||
end
|
||||
-- Red is OAM in the original: he draws over the mon's box edge
|
||||
if self.player then
|
||||
if self.playerQuads then
|
||||
for _, part in ipairs(self.playerQuads) do
|
||||
love.graphics.draw(self.player, part[1], 82 + part[2], 80 + part[3])
|
||||
end
|
||||
love.graphics.draw(self.player, self.ballQuad, 82, self.ballY)
|
||||
elseif self.player then
|
||||
love.graphics.draw(self.player, 82, 80)
|
||||
end
|
||||
end
|
||||
self:drawCopyright(136 + (preRibbon and 0 or scrollY))
|
||||
end
|
||||
|
||||
-- ..(engine/movie/title.asm ln 117)
|
||||
local COPY_PREFIX = { 0, 1, 2, 1, 3, 1, 4 }
|
||||
|
||||
function TitleState:drawCopyright(y)
|
||||
if not self.title.copyrightText and self.copyImg and self.gfInc then
|
||||
local x = 16
|
||||
for _, t in ipairs(COPY_PREFIX) do
|
||||
love.graphics.draw(self.copyImg, self.copyQuads[t], x, y)
|
||||
x = x + 8
|
||||
end
|
||||
love.graphics.draw(self.gfInc, x, y)
|
||||
return
|
||||
end
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
Font.draw(self.title.copyrightText or Strings("2026 bois club games"),
|
||||
1, 136 + scrollY)
|
||||
Font.draw(self.title.copyrightText or Strings("GAME FREAK inc."), 16, y)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
|
||||
Reference in New Issue
Block a user