mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 08:21:02 +02:00
166 lines
6.8 KiB
Lua
166 lines
6.8 KiB
Lua
-- The evolution movie (engine/movie/evolution.asm): the mon's pic
|
|
-- flashes back and forth with the evolved form, speeding up, then the
|
|
-- new form appears with its cry and the congratulations text.
|
|
-- pokered engine/movie/evolution.asm (Evolution_CheckForCancel) polls the
|
|
-- joypad during the flash: holding B aborts the evolution -- the mon keeps
|
|
-- its species and _StoppedEvolvingText ("Huh? MON stopped evolving!")
|
|
-- prints. Two kinds are exempt: trade evolutions, which evos_moves.asm
|
|
-- routes past the poll entirely (wLinkState == LINK_STATE_TRADING, #213),
|
|
-- and stone evolutions, where the B press is read but thrown away because
|
|
-- ItemUseEvoStone left wForceEvolution set (#290).
|
|
|
|
local Font = require("src.render.Font")
|
|
local Music = require("src.core.Music")
|
|
local Strings = require("src.core.Strings")
|
|
|
|
local EvolutionState = {}
|
|
EvolutionState.__index = EvolutionState
|
|
EvolutionState.isOpaque = true
|
|
|
|
-- SGB: SetPal_PokemonWholeScreen for the mon on display
|
|
function EvolutionState:sgbPalettes(game)
|
|
local P = require("src.render.PaletteFX")
|
|
-- engine/movie/evolution.asm EvolveMon runs the back-and-forth flash with
|
|
-- the whole screen on PAL_BLACK -- `ld c, 1 ; set PAL_BLACK instead of mon
|
|
-- palette` right before .animLoop, then `ld c, 0` again at .done once the
|
|
-- loop is over -- so both forms read as silhouettes while they trade places
|
|
-- and only the settled form wears a mon palette (#279). PAL_BLACK is not
|
|
-- four blacks: data/sgb/sgb_palettes.asm gives it `RGB 31,29,31, 07,07,07,
|
|
-- 02,03,03, 03,02,02`, the usual paper white with the three darker shades
|
|
-- crushed, which is why a hardware capture shows a dark mon on an unchanged
|
|
-- background rather than an all-black screen. Going through P.pal keeps
|
|
-- every COLORS mode honest for free: OG RED short-circuits every name to the
|
|
-- one global boot-ROM palette (a Game Boy Color ignores the SGB packets, so
|
|
-- it never blacks out) and the mono modes replace it in effectiveColors.
|
|
if not self.done then
|
|
local black = P.pal(game.data, "BLACK")
|
|
if black then return { P.whole(black) } end
|
|
end
|
|
-- a cancelled evolution keeps the old species (never applied), so only
|
|
-- colorize with the new form once it has actually evolved
|
|
local species = (self.done and not self.canceled) and self.newSpecies
|
|
or self.mon.species
|
|
local c = P.monPal(game.data, species)
|
|
if c then return { P.whole(c) } end
|
|
return P.wholeNamed(game.data, "MEWMON")
|
|
end
|
|
|
|
local FLASH_FRAMES = 220
|
|
|
|
local function frontSprite(game, species, mon)
|
|
local path, trueColor = require("src.pokemon.Sprites").path(
|
|
game.data, species, "front", { mon = mon, kind = "evolution" })
|
|
if not path then return nil, false end
|
|
local ok, img = pcall(love.graphics.newImage, path)
|
|
return ok and img or nil, ok and trueColor or false
|
|
end
|
|
|
|
function EvolutionState.new(game, mon, newSpecies, onDone, via)
|
|
local self = setmetatable({}, EvolutionState)
|
|
self.game = game
|
|
self.mon = mon
|
|
self.newSpecies = newSpecies
|
|
self.onDone = onDone
|
|
self.via = via
|
|
-- evolution.asm Evolution_CheckForCancel: a B press is discarded when
|
|
-- wForceEvolution is set, and ItemUseEvoStone sets it before calling
|
|
-- TryEvolvingMon, so a stone evolution (via == "ITEM") cannot be
|
|
-- cancelled either. Only level-up and rare-candy evolutions run with
|
|
-- wForceEvolution clear and so honour B (#290, #213).
|
|
self.cancelable = (via ~= "TRADE" and via ~= "ITEM")
|
|
self.oldName = mon.nickname or game.data.pokemon[mon.species].name
|
|
self.oldSprite, self.oldSpriteTrueColor = frontSprite(game, mon.species, mon)
|
|
self.newSprite, self.newSpriteTrueColor = frontSprite(game, newSpecies, mon)
|
|
self.t = 0
|
|
self.done = false
|
|
self.canceled = false
|
|
Music.play(game.data, Music.special(game.data, "evolution"))
|
|
return self
|
|
end
|
|
|
|
function EvolutionState:update(dt)
|
|
self.t = self.t + 1
|
|
if self.done then return end
|
|
local game = self.game
|
|
-- evos_moves.asm EvolveMon: each flash iteration polls hJoyHeld and, for
|
|
-- a cancelable evolution, aborts when B is held -- the mon keeps its
|
|
-- species (Evolution.apply never runs) and _StoppedEvolvingText prints.
|
|
if self.cancelable and game.input:isDown("b") then
|
|
self.done = true
|
|
self.canceled = true
|
|
local TextBox = require("src.render.TextBox")
|
|
-- mirrors data/generated/text.lua _StoppedEvolvingText
|
|
game.stack:push(TextBox.new(game,
|
|
Strings("Huh? %s\nstopped evolving!", self.oldName),
|
|
function()
|
|
Music.restoreMap(game.data)
|
|
game.stack:pop() -- the evolution screen itself
|
|
if self.onDone then self.onDone() end
|
|
end))
|
|
return
|
|
end
|
|
if self.t >= FLASH_FRAMES then
|
|
self.done = true
|
|
local Evolution = require("src.pokemon.Evolution")
|
|
Evolution.apply(game, self.mon, self.newSpecies, self.via)
|
|
require("src.core.Sound").playCry(game.data, self.newSpecies)
|
|
local TextBox = require("src.render.TextBox")
|
|
local newName = game.data.pokemon[self.newSpecies].name
|
|
game.stack:push(TextBox.new(game,
|
|
Strings("Congratulations!\nYour %s\nevolved into\n%s!",
|
|
self.oldName, newName),
|
|
function()
|
|
Music.restoreMap(game.data)
|
|
game.stack:pop() -- the evolution screen itself
|
|
-- Gen1 re-runs the level-up learn check on the evolved species
|
|
-- after the "evolved into" text (evos_moves.asm EvolveMon ->
|
|
-- learn_move.asm LearnMoveFromLevelUp, #12). Pop the evo screen
|
|
-- first so the "learned MOVE!" text / forget prompt push onto the
|
|
-- overworld / battle-return, not this state.
|
|
Evolution.learnEvolutionMoves(game, self.mon, self.onDone)
|
|
end))
|
|
end
|
|
end
|
|
|
|
function EvolutionState:draw()
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
love.graphics.rectangle("fill", 0, 0, 160, 144)
|
|
|
|
-- accelerating flash between the two forms
|
|
local sprite, spriteTrueColor
|
|
if self.done then
|
|
-- a cancelled evolution settles back on the original form
|
|
if self.canceled then
|
|
sprite, spriteTrueColor = self.oldSprite, self.oldSpriteTrueColor
|
|
else
|
|
sprite, spriteTrueColor = self.newSprite, self.newSpriteTrueColor
|
|
end
|
|
else
|
|
local period = math.max(4, 28 - math.floor(self.t / 40) * 6)
|
|
local showNew = math.floor(self.t / period) % 2 == 1
|
|
if showNew then
|
|
sprite, spriteTrueColor = self.newSprite, self.newSpriteTrueColor
|
|
else
|
|
sprite, spriteTrueColor = self.oldSprite, self.oldSpriteTrueColor
|
|
end
|
|
end
|
|
if sprite then
|
|
local x = math.floor((160 - sprite:getWidth()) / 2)
|
|
local y = math.max(8, 64 - sprite:getHeight())
|
|
love.graphics.draw(sprite, x, y)
|
|
if spriteTrueColor then
|
|
require("src.render.PaletteFX").markTrueColor(x, y, sprite:getDimensions())
|
|
end
|
|
end
|
|
|
|
love.graphics.setColor(0, 0, 0, 1)
|
|
if not self.done then
|
|
Font.draw(Strings("What?"), 8, 104)
|
|
Font.draw(self.oldName .. " is", 8, 114)
|
|
Font.draw(Strings("evolving!"), 8, 124)
|
|
end
|
|
love.graphics.setColor(1, 1, 1, 1)
|
|
end
|
|
|
|
return EvolutionState
|