mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-17 11:11:10 +02:00
CLOSES #644, CLOSES #703, CLOSES #726, CLOSES #737, CLOSES #750, CLOSES #752, CLOSES #764, CLOSES #765, CLOSES #768, CLOSES #773, CLOSES #774, CLOSES #775, CLOSES #777, CLOSES #780, CLOSES #782
This commit is contained in:
+21
-6
@@ -9,8 +9,9 @@
|
||||
-- CRED_TEXT_MON text appears at once, hold 110, mon wipe
|
||||
-- CRED_TEXT_FADE fade in, hold 120, next screen replaces the text
|
||||
-- CRED_TEXT text appears at once, hold 140
|
||||
-- The mon wipe is DisplayCreditsMon: the middle band scrolls left 8px per
|
||||
-- frame for 27 frames (ScrollCreditsMonLeft x7 then x20) while the next
|
||||
-- The mon wipe is DisplayCreditsMon: three CreditsCopyTileMapToVRAM copies
|
||||
-- (9 frames of Delay3, text still up), then the middle band scrolls left 8px
|
||||
-- per frame for 27 frames (ScrollCreditsMonLeft x7 then x20) while the next
|
||||
-- CreditsMons entry crosses right-to-left as a black silhouette
|
||||
-- (BGP %11111100), leaving the band blank; BGP is left at %11000000, which
|
||||
-- is why every post-wipe screen is a FADE variant. CRED_COPYRIGHT
|
||||
@@ -52,6 +53,14 @@ local HOLD_FADE = 120
|
||||
local HOLD_TEXT = 140
|
||||
|
||||
local WIPE_FRAMES = 27 -- ScrollCreditsMonLeft: 7 + 20 calls, 8px/frame
|
||||
-- DisplayCreditsMon runs three CreditsCopyTileMapToVRAM calls (vBGMap0+$c,
|
||||
-- vBGMap0, vBGMap1) before the first scroll, and each one ends in `jp Delay3`
|
||||
-- (home/palettes.asm), so the credits text sits still for 9 more frames on
|
||||
-- every mon screen. Dropping them ran the 15 mon screens 135 frames short and
|
||||
-- brought THE END up 2.2s early against a credits theme whose length is fixed
|
||||
-- by the ROM program (Music_Credits is 5880 frames and does not loop), which
|
||||
-- is what made the song look like it overran the roll (#703).
|
||||
local MON_PREP_FRAMES = 9
|
||||
|
||||
-- LoadCopyrightTiles (engine/movie/title.asm CopyrightTextString): tile
|
||||
-- sequences into the extracted title/copyright.png strip (tiles $60-$72:
|
||||
@@ -204,12 +213,17 @@ function Credits:update(dt)
|
||||
self.timer = self.screen.mon and HOLD_FADE_MON or HOLD_FADE
|
||||
elseif self.phase == "hold" then
|
||||
if self.screen.mon then
|
||||
self.phase = "wipe"
|
||||
self.timer = WIPE_FRAMES
|
||||
self.monImg, self.monTint = self:monSprite(self.screen.mon)
|
||||
-- the text stays up through DisplayCreditsMon's VRAM copies; the
|
||||
-- silhouette only starts moving once ScrollCreditsMonLeft does
|
||||
self.phase = "mon_prep"
|
||||
self.timer = MON_PREP_FRAMES
|
||||
else
|
||||
self:nextScreen()
|
||||
end
|
||||
elseif self.phase == "mon_prep" then
|
||||
self.phase = "wipe"
|
||||
self.timer = WIPE_FRAMES
|
||||
self.monImg, self.monTint = self:monSprite(self.screen.mon)
|
||||
elseif self.phase == "wipe" then
|
||||
self.monImg = nil
|
||||
self:nextScreen()
|
||||
@@ -313,7 +327,8 @@ function Credits:draw()
|
||||
love.graphics.rectangle("fill", 0, 0, 160, 32)
|
||||
love.graphics.rectangle("fill", 0, 112, 160, 32)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
if self.phase == "fade" or self.phase == "hold" then
|
||||
if self.phase == "fade" or self.phase == "hold"
|
||||
or self.phase == "mon_prep" then
|
||||
self:drawPage(self.screen, 0, self.shade)
|
||||
elseif self.phase == "wipe" then
|
||||
-- ScrollCreditsMonLeft: the middle band scrolls left 8px/frame while
|
||||
|
||||
+36
-8
@@ -155,7 +155,11 @@ local function obpIcon(path)
|
||||
return love.graphics.newImage(id)
|
||||
end
|
||||
|
||||
local function drawIcon(game, mon, x, y, selected, counter)
|
||||
-- `forceAlt` picks the second animation frame outright, for callers with no
|
||||
-- selection cursor of their own: Trade_AnimCircledMon
|
||||
-- (engine/movie/trade.asm) cycles the party sprite's two frames the whole
|
||||
-- time the mon rides the link cable (#750).
|
||||
function PartyMenu.drawIcon(game, mon, x, y, selected, counter, forceAlt)
|
||||
local icons = game.data.icons
|
||||
if not icons then return end
|
||||
local def = game.data.pokemon[mon.species]
|
||||
@@ -202,7 +206,7 @@ local function drawIcon(game, mon, x, y, selected, counter)
|
||||
end
|
||||
local img = iconImages[key]
|
||||
if not img then return end
|
||||
local alt = false
|
||||
local alt = forceAlt or false
|
||||
if selected then
|
||||
local px = math.floor(mon.hp * 48 / math.max(1, mon.stats.hp))
|
||||
local speed = px >= 27 and 5 or px >= 10 and 16 or 32
|
||||
@@ -237,13 +241,24 @@ local function drawIcon(game, mon, x, y, selected, counter)
|
||||
-- whatever size the file is (unchanged path)
|
||||
love.graphics.draw(img, x, y)
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
function PartyMenu.new(game, opts)
|
||||
opts = opts or {}
|
||||
local self = setmetatable({}, PartyMenu)
|
||||
self.game = game
|
||||
self.index = 1
|
||||
-- PartyMenuInit (home/pokemon.asm) seeds the cursor from
|
||||
-- wPartyAndBillsPCSavedMenuItem rather than from zero, and
|
||||
-- HandlePartyMenuInput writes wCurrentMenuItem back into it on every
|
||||
-- input, so the party cursor survives closing and reopening the menu.
|
||||
-- Only a battle clears it -- InitBattleVariables and end_of_battle.asm
|
||||
-- both zero the byte, which BattleState mirrors. The clamp covers a
|
||||
-- party that shrank (deposit / release) while the saved index was
|
||||
-- pointing past the end. #768
|
||||
local count = #(opts.party or (game.save and game.save.party) or {})
|
||||
self.index = math.min(math.max(1, game.partyMenuSavedIndex or 1),
|
||||
math.max(1, count))
|
||||
self.onSwitch = opts.onSwitch
|
||||
self.onCancel = opts.onCancel
|
||||
self.pickOnly = opts.pickOnly
|
||||
@@ -518,8 +533,10 @@ function PartyMenu:update(dt)
|
||||
|
||||
if input:wasPressed("up") then
|
||||
self.index = self.index > 1 and self.index - 1 or math.max(1, #party)
|
||||
self.game.partyMenuSavedIndex = self.index -- HandlePartyMenuInput #768
|
||||
elseif input:wasPressed("down") then
|
||||
self.index = self.index < #party and self.index + 1 or 1
|
||||
self.game.partyMenuSavedIndex = self.index -- HandlePartyMenuInput #768
|
||||
elseif input:wasPressed("b") then
|
||||
self.game.stack:pop()
|
||||
if self.onCancel then self.onCancel() end
|
||||
@@ -567,10 +584,17 @@ function PartyMenu:update(dt)
|
||||
{ label = Strings("STATS"), action = "stats" },
|
||||
{ label = Strings("CANCEL"), action = "cancel" } }
|
||||
else
|
||||
-- STATS/SWITCH plus this mon's field moves (start_sub_menus.asm
|
||||
-- builds the same dynamic list)
|
||||
items = { { label = Strings("STATS"), action = "stats" },
|
||||
{ label = Strings("SWITCH"), action = "switch" } }
|
||||
-- This mon's field moves FIRST, then STATS/SWITCH
|
||||
-- (start_sub_menus.asm builds the same dynamic list). The order is
|
||||
-- load bearing: DisplayFieldMoveMonMenu (engine/menus/text_box.asm)
|
||||
-- grows the box upward one row per field move and prints the field
|
||||
-- move names ABOVE PokemonMenuEntries ("STATS/SWITCH/CANCEL"), and
|
||||
-- StartMenu_Pokemon .choseOutOfBattleMove indexes wFieldMoves with
|
||||
-- menu items 0..n-1 while STATS/SWITCH sit at the bottom of the
|
||||
-- list. GetMonFieldMoves walks wPartyMon1Moves in slot order, so
|
||||
-- the field moves keep the mon's move-list order -- which the loop
|
||||
-- below already does. #768
|
||||
items = {}
|
||||
-- Field moves (HMs/TMs) are usable out of battle even when the mon
|
||||
-- is fainted -- Gen 1 does not require HP for Cut/Fly/Surf/etc.
|
||||
-- Battle still excludes this list via `not self.battle`. Softboiled
|
||||
@@ -615,6 +639,10 @@ function PartyMenu:update(dt)
|
||||
end
|
||||
end
|
||||
end
|
||||
-- PokemonMenuEntries always closes the list, under the field moves
|
||||
-- (text_box.asm .donePrintingNames). #768
|
||||
items[#items + 1] = { label = Strings("STATS"), action = "stats" }
|
||||
items[#items + 1] = { label = Strings("SWITCH"), action = "switch" }
|
||||
end
|
||||
local ctx = { battle = self.battle, overworld = ow }
|
||||
local hooked = Runtime.call("ui.party.submenu", sameItems,
|
||||
@@ -685,7 +713,7 @@ function PartyMenu:draw()
|
||||
local def = self.game.data.pokemon[mon.species]
|
||||
local y = PartyMenu.entryY(i)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
drawIcon(self.game, mon, 8, y, i == self.index, self.blink or 0)
|
||||
PartyMenu.drawIcon(self.game, mon, 8, y, i == self.index, self.blink or 0)
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
Font.draw(mon.nickname or def.name, 24, y)
|
||||
-- level at column 13 (<LV> tile + digits, PrintLevel) AND the
|
||||
|
||||
+15
-5
@@ -66,14 +66,24 @@ function StartMenu.new(game)
|
||||
panel .. Strings("\fWould you like to\nSAVE the game?"), nil, {
|
||||
choice = function(yes)
|
||||
if not yes then return end
|
||||
-- "Now saving..." beat before the write (save.asm
|
||||
-- NowSavingString), then GameSavedText + SFX_SAVE
|
||||
-- SaveMenu .save (engine/menus/save.asm:164-181): "Now saving..."
|
||||
-- is a bare PlaceString held by DelayFrames 120, then GameSavedText,
|
||||
-- which ends in `done` and so never reaches TX_PROMPT_BUTTON.
|
||||
-- Neither page takes a button press (#765); the second waits on
|
||||
-- SFX_SAVE (PlaySoundWaitForCurrent + WaitForSoundToFinish) and then
|
||||
-- DelayFrames 30. The write itself is invisible either side of the
|
||||
-- "Now saving..." hold, so it stays on that box's onDone.
|
||||
game.stack:push(TextBox.new(game, Strings("Now saving..."), function()
|
||||
game:writeSave()
|
||||
require("src.core.Sound").play(game.data, "Save")
|
||||
game.stack:push(TextBox.new(game,
|
||||
Strings("%s saved\nthe game!", game.save.player.name or "RED")))
|
||||
end))
|
||||
Strings("%s saved\nthe game!", game.save.player.name or "RED"),
|
||||
nil, { auto = {
|
||||
sound = function()
|
||||
return require("src.core.Sound").play(game.data, "Save")
|
||||
end,
|
||||
delay = 30,
|
||||
} }))
|
||||
end, { auto = { delay = 120 } }))
|
||||
end,
|
||||
}))
|
||||
end })
|
||||
|
||||
+281
-102
@@ -3,14 +3,21 @@
|
||||
-- spin in the air and land flat for points; a crooked landing wipes out
|
||||
-- and ends the run. The scene is built from the real ROM sheets
|
||||
-- (gfx/surfing_pikachu.asm, ripped at import to
|
||||
-- assets/generated/minigame/surf_1a/1b.png): the scalloped water tiles,
|
||||
-- the beach with the palm and the doll hut, the "HP:" score strip with
|
||||
-- the sheet digits, the cloud, and the OAM Pikachu poses -- the air
|
||||
-- tricks quantize to the sheet's rotation frames like the original's
|
||||
-- sprite anims, instead of free-rotating one pose. The original drew
|
||||
-- the big wave with per-scanline scroll tricks (wLYOverrides); here the
|
||||
-- crest profile is a curve filled with the sheet's foam/shade tiles.
|
||||
-- Score model keeps the original's shape (ride ticks + airtime + full
|
||||
-- assets/generated/minigame/surf_1a/1b.png).
|
||||
--
|
||||
-- #726: the background is the original's own metatile scroller, not a
|
||||
-- procedural stand-in. SurfingPikachu1Graphics1 is copied to vChars2
|
||||
-- with LCDC's BG char base unset, so BG tile id N is simply tile N of
|
||||
-- surf_1a (5 tiles per row). SurfingMinigame_ScrollAndGenerateBGMap
|
||||
-- walks a jumptable of wave states, each of which hands back one
|
||||
-- 8-metatile column (2x2 tiles each, so 16px wide by the 128px the BG
|
||||
-- shows above the HP window) plus the two Pikachu ride heights for that
|
||||
-- column. Porting those tables verbatim is what makes the water read as
|
||||
-- water: the earlier stand-in tiled the wave-face tiles ($02/$07) over
|
||||
-- the whole sea and drew the swell as a LOVE ellipse, which is the
|
||||
-- "messed up graphics" in the report.
|
||||
--
|
||||
-- Score model keeps the port's shape (ride ticks + airtime + full
|
||||
-- rotations); high score persists in save.surfingHighScore for the
|
||||
-- beach-house printer.
|
||||
|
||||
@@ -23,10 +30,13 @@ local SurfingMinigame = {}
|
||||
SurfingMinigame.__index = SurfingMinigame
|
||||
SurfingMinigame.isOpaque = true
|
||||
|
||||
local PIKA_X = 44 -- fixed screen x while riding
|
||||
local RUN_DISTANCE = 3200 -- scroll px from paddle-out to the beach
|
||||
-- SURFING_MINIGAME_CENTER_X/FLAT_WATER_Y (surfing_pikachu.asm:1-2) are OAM
|
||||
-- coordinates; screen x/y are those minus OAM_X_OFS/OAM_Y_OFS.
|
||||
local FLAT_WATER_Y = 116
|
||||
local PIKA_X = 68 -- fixed screen x while riding (center 80, 24px pose)
|
||||
local RUN_DISTANCE = 3072 -- 24 sections of 8 metatile columns
|
||||
local GRAVITY = 0.14
|
||||
local HORIZON = 24 -- sea starts under the sky strip
|
||||
local BG_HEIGHT = 128 -- rows the BG shows; the HP window covers the rest
|
||||
|
||||
-- surf_1b quads: {x, y, w, h} in sheet pixels (pose pitch is 24x24)
|
||||
local B = {
|
||||
@@ -50,18 +60,158 @@ local POSES = {
|
||||
[315] = { 0, 0, 24, 24 }, -- tail down
|
||||
}
|
||||
|
||||
-- surf_1a quads (BG tiles)
|
||||
local A = {
|
||||
scallop = { 16, 0, 8, 8 }, -- open-water pattern, row A
|
||||
scallop2 = { 16, 8, 8, 8 }, -- row B variant
|
||||
shade = { 8, 16, 8, 8 }, -- gray dither, wave belly
|
||||
lip = { 24, 0, 8, 8 }, -- foam curl for the crest edge
|
||||
palm = { 8, 32, 8, 8 }, -- palm fronds
|
||||
beach = { 24, 32, 16, 8 }, -- black shore silhouette
|
||||
hut = { 8, 40, 16, 8 }, -- the Pikachu doll hut on the sand
|
||||
hp = { 20, 40, 20, 8 }, -- "HP:" score label
|
||||
-- surf_1a is 5 tiles wide, so BG tile id N lives at (N%5*8, N/5*8). The
|
||||
-- only quad the scene needs by hand is the window's "HP:" label, which
|
||||
-- straddles a tile boundary in the sheet.
|
||||
local HP_LABEL = { 20, 40, 20, 8 }
|
||||
|
||||
-- SurfingMinigame_BGMetatileTable (surfing_pikachu.asm): 2x2 tiles each,
|
||||
-- stored top-left, top-right, bottom-left, bottom-right.
|
||||
local BG_METATILES = {
|
||||
[0x00] = { 0x00, 0x00, 0x00, 0x00 }, -- sky block (blank)
|
||||
[0x01] = { 0x0b, 0x0b, 0x0b, 0x0b }, -- open water
|
||||
[0x02] = { 0x0b, 0x02, 0x02, 0x06 },
|
||||
[0x03] = { 0x03, 0x0b, 0x07, 0x03 },
|
||||
[0x04] = { 0x06, 0x06, 0x06, 0x06 },
|
||||
[0x05] = { 0x07, 0x07, 0x07, 0x07 },
|
||||
[0x06] = { 0x06, 0x04, 0x04, 0x08 },
|
||||
[0x07] = { 0x05, 0x07, 0x08, 0x05 },
|
||||
[0x08] = { 0x0b, 0x0b, 0x11, 0x12 },
|
||||
[0x09] = { 0x0b, 0x0b, 0x13, 0x03 },
|
||||
[0x0a] = { 0x14, 0x12, 0x04, 0x08 },
|
||||
[0x0b] = { 0x13, 0x07, 0x08, 0x05 },
|
||||
[0x0c] = { 0x06, 0x14, 0x06, 0x14 }, -- unused, identical to 11
|
||||
[0x0d] = { 0x13, 0x07, 0x13, 0x07 },
|
||||
[0x0e] = { 0x08, 0x08, 0x08, 0x08 }, -- solid blue
|
||||
[0x0f] = { 0x14, 0x12, 0x14, 0x12 },
|
||||
[0x10] = { 0x0b, 0x11, 0x02, 0x14 },
|
||||
[0x11] = { 0x06, 0x14, 0x06, 0x14 },
|
||||
[0x12] = { 0x0c, 0x0c, 0x0d, 0x0d }, -- beach top block
|
||||
[0x13] = { 0x0d, 0x0d, 0x0d, 0x0d }, -- beach sand block
|
||||
[0x14] = { 0x0e, 0x0f, 0x10, 0x0b }, -- beach shore block
|
||||
[0x15] = { 0x12, 0x13, 0x12, 0x13 },
|
||||
}
|
||||
|
||||
-- SurfingMinigameWavePattern00..1C plus SurfingMinigameBeachPattern: one
|
||||
-- column of 8 metatiles, top to bottom.
|
||||
local WAVE_PATTERNS = {
|
||||
[0x00] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x01, 0x01 },
|
||||
[0x01] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x06 },
|
||||
[0x02] = { 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x06, 0x0e },
|
||||
[0x03] = { 0x00, 0x00, 0x00, 0x10, 0x11, 0x06, 0x0e, 0x0e },
|
||||
[0x04] = { 0x00, 0x00, 0x00, 0x15, 0x15, 0x0e, 0x0e, 0x0e },
|
||||
[0x05] = { 0x00, 0x00, 0x00, 0x03, 0x05, 0x07, 0x0e, 0x0e },
|
||||
[0x06] = { 0x00, 0x00, 0x00, 0x01, 0x03, 0x05, 0x07, 0x0e },
|
||||
[0x07] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x05, 0x07 },
|
||||
[0x08] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x06 },
|
||||
[0x09] = { 0x00, 0x00, 0x00, 0x01, 0x02, 0x04, 0x06, 0x0e },
|
||||
[0x0a] = { 0x00, 0x00, 0x00, 0x08, 0x0f, 0x0a, 0x0e, 0x0e },
|
||||
[0x0b] = { 0x00, 0x00, 0x00, 0x09, 0x0d, 0x0b, 0x0e, 0x0e },
|
||||
[0x0c] = { 0x00, 0x00, 0x00, 0x01, 0x03, 0x05, 0x07, 0x0e },
|
||||
[0x0d] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x05, 0x07 },
|
||||
[0x0e] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x06 },
|
||||
[0x0f] = { 0x00, 0x00, 0x00, 0x01, 0x10, 0x11, 0x06, 0x0e },
|
||||
[0x10] = { 0x00, 0x00, 0x00, 0x01, 0x15, 0x15, 0x0e, 0x0e },
|
||||
[0x11] = { 0x00, 0x00, 0x00, 0x01, 0x03, 0x05, 0x07, 0x0e },
|
||||
[0x12] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x05, 0x07 },
|
||||
[0x13] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x02, 0x04, 0x06 },
|
||||
[0x14] = { 0x00, 0x00, 0x00, 0x01, 0x08, 0x0f, 0x0a, 0x0e },
|
||||
[0x15] = { 0x00, 0x00, 0x00, 0x01, 0x09, 0x0d, 0x0b, 0x0e },
|
||||
[0x16] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x05, 0x07 },
|
||||
[0x17] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x10, 0x11, 0x06 },
|
||||
[0x18] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x15, 0x15, 0x0e },
|
||||
[0x19] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x03, 0x05, 0x07 },
|
||||
[0x1a] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x08, 0x0f, 0x0a },
|
||||
[0x1b] = { 0x00, 0x00, 0x00, 0x01, 0x01, 0x09, 0x0d, 0x0b },
|
||||
[0x1c] = { 0x00, 0x00, 0x00, 0x14, 0x14, 0x14, 0x14, 0x14 },
|
||||
beach = { 0x00, 0x00, 0x00, 0x12, 0x13, 0x13, 0x13, 0x13 },
|
||||
}
|
||||
|
||||
-- RunSurfingMinigameRoutine's .WaveFunctions jumptable, flattened:
|
||||
-- { pattern, left ride height, right ride height, what to do next }.
|
||||
-- next: 0 = advance one state, 1 = reset to the chooser, 2 = stay put.
|
||||
-- State 0 is SurfingMinigame_ChooseNextWaveSequence and is handled in
|
||||
-- code because it rolls Random and forces the Big Kahuna near the goal.
|
||||
local ADV, RESET, STAY = 0, 1, 2
|
||||
local WAVE_STEPS = {
|
||||
[0x01] = { 0x13, 116, 108, ADV }, [0x02] = { 0x14, 100, 92, ADV },
|
||||
[0x03] = { 0x15, 92, 92, ADV }, [0x04] = { 0x16, 100, 108, ADV },
|
||||
[0x05] = { 0x00, 116, 116, ADV }, [0x06] = { 0x17, 116, 108, ADV },
|
||||
[0x07] = { 0x18, 100, 100, ADV }, [0x08] = { 0x19, 100, 108, ADV },
|
||||
[0x09] = { 0x00, 116, 116, ADV }, [0x0a] = { 0x00, 116, 116, ADV },
|
||||
[0x0b] = { 0x00, 116, 116, ADV }, [0x0c] = { 0x00, 116, 116, ADV },
|
||||
[0x0d] = { 0x00, 116, 116, RESET },
|
||||
[0x0e] = { 0x08, 116, 108, ADV }, [0x0f] = { 0x09, 100, 92, ADV },
|
||||
[0x10] = { 0x0a, 84, 76, ADV }, [0x11] = { 0x0b, 76, 76, ADV },
|
||||
[0x12] = { 0x0c, 84, 92, ADV }, [0x13] = { 0x0d, 100, 108, ADV },
|
||||
[0x14] = { 0x00, 116, 116, ADV }, [0x15] = { 0x00, 116, 116, ADV },
|
||||
[0x16] = { 0x00, 116, 116, ADV }, [0x17] = { 0x00, 116, 116, ADV },
|
||||
[0x18] = { 0x00, 116, 116, ADV }, [0x19] = { 0x00, 116, 116, RESET },
|
||||
[0x1a] = { 0x0e, 116, 108, ADV }, [0x1b] = { 0x0f, 100, 92, ADV },
|
||||
[0x1c] = { 0x10, 84, 84, ADV }, [0x1d] = { 0x11, 84, 92, ADV },
|
||||
[0x1e] = { 0x12, 100, 108, ADV }, [0x1f] = { 0x0e, 116, 108, ADV },
|
||||
[0x20] = { 0x0f, 100, 92, ADV }, [0x21] = { 0x10, 84, 84, ADV },
|
||||
[0x22] = { 0x11, 84, 92, ADV }, [0x23] = { 0x12, 100, 108, ADV },
|
||||
[0x24] = { 0x00, 116, 116, ADV }, [0x25] = { 0x00, 116, 116, ADV },
|
||||
[0x26] = { 0x00, 116, 116, ADV }, [0x27] = { 0x00, 116, 116, ADV },
|
||||
[0x28] = { 0x00, 116, 116, RESET },
|
||||
[0x29] = { 0x13, 116, 108, ADV }, [0x2a] = { 0x14, 100, 92, ADV },
|
||||
[0x2b] = { 0x15, 92, 92, ADV }, [0x2c] = { 0x16, 100, 108, ADV },
|
||||
[0x2d] = { 0x00, 116, 116, ADV }, [0x2e] = { 0x00, 116, 116, ADV },
|
||||
[0x2f] = { 0x00, 116, 116, ADV }, [0x30] = { 0x00, 116, 116, ADV },
|
||||
[0x31] = { 0x00, 116, 116, RESET },
|
||||
[0x32] = { 0x17, 116, 108, ADV }, [0x33] = { 0x18, 100, 100, ADV },
|
||||
[0x34] = { 0x19, 100, 108, ADV }, [0x35] = { 0x17, 116, 108, ADV },
|
||||
[0x36] = { 0x18, 100, 100, ADV }, [0x37] = { 0x19, 100, 108, ADV },
|
||||
[0x38] = { 0x17, 116, 108, ADV }, [0x39] = { 0x18, 100, 100, ADV },
|
||||
[0x3a] = { 0x19, 100, 108, ADV }, [0x3b] = { 0x00, 116, 116, ADV },
|
||||
[0x3c] = { 0x00, 116, 116, ADV }, [0x3d] = { 0x00, 116, 116, ADV },
|
||||
[0x3e] = { 0x00, 116, 116, ADV }, [0x3f] = { 0x00, 116, 116, RESET },
|
||||
[0x40] = { 0x1a, 116, 108, ADV }, [0x41] = { 0x1b, 108, 108, ADV },
|
||||
[0x42] = { 0x0e, 116, 108, ADV }, [0x43] = { 0x0f, 100, 92, ADV },
|
||||
[0x44] = { 0x10, 84, 84, ADV }, [0x45] = { 0x11, 84, 92, ADV },
|
||||
[0x46] = { 0x12, 100, 108, ADV }, [0x47] = { 0x1a, 116, 108, ADV },
|
||||
[0x48] = { 0x1b, 108, 108, ADV }, [0x49] = { 0x00, 116, 116, ADV },
|
||||
[0x4a] = { 0x00, 116, 116, ADV }, [0x4b] = { 0x00, 116, 116, ADV },
|
||||
[0x4c] = { 0x00, 116, 116, RESET },
|
||||
[0x4d] = { 0x08, 116, 108, ADV }, [0x4e] = { 0x09, 100, 92, ADV },
|
||||
[0x4f] = { 0x0a, 84, 76, ADV }, [0x50] = { 0x0b, 76, 76, ADV },
|
||||
[0x51] = { 0x0c, 84, 92, ADV }, [0x52] = { 0x0d, 100, 108, ADV },
|
||||
[0x53] = { 0x00, 116, 116, ADV }, [0x54] = { 0x1a, 116, 108, ADV },
|
||||
[0x55] = { 0x1b, 108, 108, ADV }, [0x56] = { 0x1a, 116, 108, ADV },
|
||||
[0x57] = { 0x1b, 108, 108, ADV }, [0x58] = { 0x00, 116, 116, ADV },
|
||||
[0x59] = { 0x00, 116, 116, ADV }, [0x5a] = { 0x00, 116, 116, ADV },
|
||||
[0x5b] = { 0x00, 116, 116, RESET },
|
||||
[0x5c] = { 0x0e, 116, 108, ADV }, [0x5d] = { 0x0f, 100, 92, ADV },
|
||||
[0x5e] = { 0x10, 84, 84, ADV }, [0x5f] = { 0x11, 84, 92, ADV },
|
||||
[0x60] = { 0x12, 100, 108, ADV }, [0x61] = { 0x13, 116, 108, ADV },
|
||||
[0x62] = { 0x14, 100, 92, ADV }, [0x63] = { 0x15, 92, 92, ADV },
|
||||
[0x64] = { 0x16, 100, 108, ADV }, [0x65] = { 0x00, 116, 116, ADV },
|
||||
[0x66] = { 0x00, 116, 116, ADV }, [0x67] = { 0x00, 116, 116, ADV },
|
||||
[0x68] = { 0x00, 116, 116, ADV }, [0x69] = { 0x00, 116, 116, RESET },
|
||||
-- 6a..71: the forced "Big Kahuna" finale; 71 holds flat water (its
|
||||
-- loader just rets, so the state never advances on its own).
|
||||
[0x6a] = { 0x01, 116, 108, ADV }, [0x6b] = { 0x02, 100, 92, ADV },
|
||||
[0x6c] = { 0x03, 84, 76, ADV }, [0x6d] = { 0x04, 68, 68, ADV },
|
||||
[0x6e] = { 0x05, 68, 76, ADV }, [0x6f] = { 0x06, 84, 92, ADV },
|
||||
[0x70] = { 0x07, 100, 108, ADV }, [0x71] = { 0x00, 116, 116, STAY },
|
||||
-- 72..7b: the run-out to the beach, entered by hand at the goal
|
||||
-- (SurfingMinigame_WaitToShowResults writes $72).
|
||||
[0x72] = { 0x00, 116, 116, ADV }, [0x73] = { 0x1c, 116, 116, ADV },
|
||||
[0x74] = { "beach", 116, 116, ADV }, [0x75] = { "beach", 116, 116, ADV },
|
||||
[0x76] = { "beach", 116, 116, ADV }, [0x77] = { "beach", 116, 116, ADV },
|
||||
[0x78] = { "beach", 116, 116, ADV }, [0x79] = { "beach", 116, 116, ADV },
|
||||
[0x7a] = { "beach", 116, 116, ADV }, [0x7b] = { "beach", 116, 116, RESET },
|
||||
}
|
||||
-- SurfingMinigame_WaveSequenceStarts
|
||||
local SEQ_STARTS = { 0x01, 0x0e, 0x1a, 0x29, 0x32, 0x40, 0x4d, 0x5c }
|
||||
|
||||
-- the #726 table-integrity check in tests/drivers reads these; nothing
|
||||
-- else should
|
||||
SurfingMinigame.BG_METATILES = BG_METATILES
|
||||
SurfingMinigame.WAVE_PATTERNS = WAVE_PATTERNS
|
||||
SurfingMinigame.WAVE_STEPS = WAVE_STEPS
|
||||
|
||||
-- SGB-style zones: one sea palette over the frame plus a yellow
|
||||
-- OBJ-flavored palette tracking Pikachu's tiles (rectangular attribute
|
||||
-- blocks are all the SGB could do, bleed and all)
|
||||
@@ -91,6 +241,17 @@ function SurfingMinigame.new(game, onDone)
|
||||
self.resultShown = 0
|
||||
self.banner = nil -- {quad, frames}: GOOD!/YEAH-/Oh no..
|
||||
|
||||
-- SurfingPikachuMinigame_LoadGFXAndLayout prefills the BG with flat
|
||||
-- water and only starts generating $a0 pixels (ten metatile columns)
|
||||
-- ahead of the viewport, so the run opens on calm sea.
|
||||
self.waveFn = 0
|
||||
self.cols = {}
|
||||
for c = 0, 10 do
|
||||
self.cols[c] = { pat = WAVE_PATTERNS[0x00],
|
||||
hl = FLAT_WATER_Y, hr = FLAT_WATER_Y }
|
||||
end
|
||||
self.colTail = 10
|
||||
|
||||
local function sheet(path)
|
||||
local ok, img = pcall(love.graphics.newImage, path)
|
||||
return ok and img or nil
|
||||
@@ -98,8 +259,12 @@ function SurfingMinigame.new(game, onDone)
|
||||
self.bg = sheet("assets/generated/minigame/surf_1a.png")
|
||||
self.ob = sheet("assets/generated/minigame/surf_1b.png")
|
||||
if self.bg then
|
||||
self.aq = {}
|
||||
for k, spec in pairs(A) do self.aq[k] = newQuad(spec, self.bg) end
|
||||
self.tq = {}
|
||||
for n = 0, 64 do
|
||||
self.tq[n] = love.graphics.newQuad((n % 5) * 8, math.floor(n / 5) * 8,
|
||||
8, 8, self.bg:getDimensions())
|
||||
end
|
||||
self.hpq = newQuad(HP_LABEL, self.bg)
|
||||
end
|
||||
if self.ob then
|
||||
self.bq = {}
|
||||
@@ -122,11 +287,53 @@ function SurfingMinigame.new(game, onDone)
|
||||
return self
|
||||
end
|
||||
|
||||
-- crest height at screen x for the current scroll (two sines so the
|
||||
-- wave rolls instead of looping visibly)
|
||||
-- SurfingMinigame_ChooseNextWaveSequence: past section $16 the finale is
|
||||
-- forced, otherwise a nonzero Random picks one of eight sequence starts.
|
||||
-- Either way this column itself is flat water.
|
||||
function SurfingMinigame:chooseSequence()
|
||||
if math.floor(self.distance / 128) >= 0x16 then
|
||||
self.waveFn = 0x6a
|
||||
else
|
||||
local r = math.random(0, 255)
|
||||
if r ~= 0 then self.waveFn = SEQ_STARTS[((r - 1) % 8) + 1] end
|
||||
end
|
||||
return WAVE_PATTERNS[0x00], FLAT_WATER_Y, FLAT_WATER_Y
|
||||
end
|
||||
|
||||
-- one 16px metatile column, appended on the right as the sea scrolls
|
||||
function SurfingMinigame:pushColumn()
|
||||
local pat, hl, hr
|
||||
if self.waveFn == 0 then
|
||||
pat, hl, hr = self:chooseSequence()
|
||||
else
|
||||
local step = WAVE_STEPS[self.waveFn]
|
||||
if not step then
|
||||
self.waveFn = 0
|
||||
pat, hl, hr = WAVE_PATTERNS[0x00], FLAT_WATER_Y, FLAT_WATER_Y
|
||||
else
|
||||
pat, hl, hr = WAVE_PATTERNS[step[1]], step[2], step[3]
|
||||
if step[4] == ADV then self.waveFn = self.waveFn + 1
|
||||
elseif step[4] == RESET then self.waveFn = 0 end
|
||||
end
|
||||
end
|
||||
self.colTail = self.colTail + 1
|
||||
self.cols[self.colTail] = { pat = pat, hl = hl, hr = hr }
|
||||
self.cols[self.colTail - 24] = nil -- columns behind the viewport
|
||||
end
|
||||
|
||||
-- keep the generated columns covering the viewport plus the lookahead
|
||||
function SurfingMinigame:generateAhead()
|
||||
while self.colTail * 16 < self.distance + 176 do self:pushColumn() end
|
||||
end
|
||||
|
||||
-- Pikachu's screen y for a screen x, from the per-tile-column ride
|
||||
-- heights the wave states hand back (SurfingMinigame_SetPikachuHeight
|
||||
-- samples the same array either side of the scroll's low bit).
|
||||
function SurfingMinigame:seaY(x)
|
||||
local s = self.distance + x
|
||||
return 92 - 14 * math.sin(s / 26) - 6 * math.sin(s / 9.5)
|
||||
local tile = math.floor((self.distance + x) / 8)
|
||||
local col = self.cols[math.floor(tile / 2)]
|
||||
if not col then return FLAT_WATER_Y - 16 end
|
||||
return (tile % 2 == 0 and col.hl or col.hr) - 16
|
||||
end
|
||||
|
||||
function SurfingMinigame:finishRun()
|
||||
@@ -146,6 +353,13 @@ function SurfingMinigame:update()
|
||||
if self.banner.frames <= 0 then self.banner = nil end
|
||||
end
|
||||
if self.phase == "results" then
|
||||
-- the sea keeps sliding under the card for a beat so the beach
|
||||
-- run-out (states $72..$7b) actually crosses the screen, like
|
||||
-- SurfingMinigame_WaitToShowResults scrolling to the sand
|
||||
if self.resultShown < 150 then
|
||||
self.distance = self.distance + 1.2
|
||||
self:generateAhead()
|
||||
end
|
||||
self.resultShown = self.resultShown + 1
|
||||
if self.resultShown > 30
|
||||
and (input:wasPressed("a") or input:wasPressed("b")) then
|
||||
@@ -162,9 +376,13 @@ function SurfingMinigame:update()
|
||||
|
||||
-- the wave scrolls by the current speed; the beach ends the run
|
||||
self.distance = self.distance + 0.8 + self.speed * 0.35
|
||||
self:generateAhead()
|
||||
if self.distance >= RUN_DISTANCE then
|
||||
-- rode it all the way in: distance bonus like the original's goal
|
||||
self.score = self.score + 500
|
||||
-- SurfingMinigame_WaitToShowResults hands the generator state $72 so
|
||||
-- the sand runs out under the coast-in
|
||||
self.waveFn = 0x72
|
||||
self:finishRun()
|
||||
return
|
||||
end
|
||||
@@ -218,16 +436,11 @@ function SurfingMinigame:update()
|
||||
end
|
||||
end
|
||||
|
||||
-- draw one 8x8 sheet tile quad at x, y
|
||||
function SurfingMinigame:tile(q, x, y)
|
||||
love.graphics.draw(self.bg, self.aq[q], x, y)
|
||||
end
|
||||
|
||||
function SurfingMinigame:sgbPalettes()
|
||||
local P = require("src.render.PaletteFX")
|
||||
local zones = { P.whole(SEA_PAL) }
|
||||
if self.phase ~= "wipeout" and self.phase ~= "results" then
|
||||
local tx = math.floor((PIKA_X - 12) / 8)
|
||||
local tx = math.floor(PIKA_X / 8)
|
||||
local ty = math.floor(math.max(0, self.pikaScreenY or 60) / 8)
|
||||
zones[#zones + 1] = P.zone(PIKA_PAL, tx, ty, tx + 3, ty + 3)
|
||||
end
|
||||
@@ -242,87 +455,52 @@ function SurfingMinigame:drawScore(x, y, n)
|
||||
end
|
||||
end
|
||||
|
||||
-- the BG map: metatile columns from the wave generator, scrolled by
|
||||
-- distance (SurfingMinigame_ScrollAndGenerateBGMap)
|
||||
function SurfingMinigame:drawBackground()
|
||||
local scx = math.floor(self.distance)
|
||||
local first = math.floor(scx / 16)
|
||||
for c = first, first + 10 do
|
||||
local col = self.cols[c]
|
||||
if col then
|
||||
local x = c * 16 - scx
|
||||
for i = 1, 8 do
|
||||
local mt = BG_METATILES[col.pat[i]]
|
||||
if mt then
|
||||
local y = (i - 1) * 16
|
||||
love.graphics.draw(self.bg, self.tq[mt[1]], x, y)
|
||||
love.graphics.draw(self.bg, self.tq[mt[2]], x + 8, y)
|
||||
love.graphics.draw(self.bg, self.tq[mt[3]], x, y + 8)
|
||||
love.graphics.draw(self.bg, self.tq[mt[4]], x + 8, y + 8)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function SurfingMinigame:draw()
|
||||
local haveSheets = self.bg and self.ob
|
||||
-- sky
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle("fill", 0, 0, 160, 144)
|
||||
if not haveSheets then
|
||||
-- cache predates the surf sheets: plain shapes keep it playable
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
Font.draw(Strings("SCORE %d", self.score), 4, 4)
|
||||
love.graphics.rectangle("fill", PIKA_X - 8,
|
||||
self:seaY(PIKA_X) - 16 - self.y, 16, 16)
|
||||
love.graphics.rectangle("fill", PIKA_X, self:seaY(PIKA_X) - self.y, 16, 16)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
return
|
||||
end
|
||||
|
||||
self:drawBackground()
|
||||
|
||||
-- cloud in the sky strip
|
||||
love.graphics.draw(self.ob, self.bq.cloud, 112, 8)
|
||||
|
||||
-- open water: the scalloped pattern tiles the whole sea, phase-locked
|
||||
-- to the scroll so the surface slides
|
||||
local shift = math.floor(self.distance) % 8
|
||||
for ty = HORIZON, 136, 8 do
|
||||
local alt = (ty / 8) % 2 == 0
|
||||
for tx = -8, 160, 8 do
|
||||
self:tile(alt and "scallop" or "scallop2", tx - shift, ty)
|
||||
end
|
||||
end
|
||||
|
||||
-- the wave face: a white patch hugging the ride line (the original
|
||||
-- carved it with per-scanline scroll; the ellipse stands in), with a
|
||||
-- few scallops floating inside and the foam lip along its upper edge
|
||||
local faceY = self:seaY(56) + 10
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.ellipse("fill", 56, faceY, 46, 30)
|
||||
love.graphics.ellipse("fill", 100, faceY + 16, 40, 22)
|
||||
for _, spot in ipairs({ { 30, 8 }, { 70, 16 }, { 48, 22 } }) do
|
||||
self:tile("scallop", 56 - 46 + spot[1] - shift, faceY - 24 + spot[2])
|
||||
end
|
||||
local pikaY = self:seaY(PIKA_X) - 20 - self.y
|
||||
for a = 205, 335, 18 do
|
||||
local r = math.rad(a)
|
||||
local lx = 56 + math.cos(r) * 44 - 4
|
||||
local ly = faceY + math.sin(r) * 28 - 4
|
||||
-- foam that would land inside Pikachu's SGB zone comes out orange;
|
||||
-- leave that patch to the spray ellipse instead
|
||||
if math.abs(lx - PIKA_X) > 28 or math.abs(ly - (pikaY + 12)) > 26 then
|
||||
self:tile("lip", lx, ly)
|
||||
end
|
||||
end
|
||||
self:tile("shade", 92 - shift, faceY + 20)
|
||||
self:tile("shade", 116 - shift, faceY + 24)
|
||||
|
||||
-- beach slides through at the start and again before the goal
|
||||
local beachX
|
||||
if self.distance < 160 then
|
||||
beachX = -self.distance
|
||||
elseif self.distance > RUN_DISTANCE - 200 then
|
||||
beachX = 160 - (self.distance - (RUN_DISTANCE - 200))
|
||||
end
|
||||
if beachX then
|
||||
for tx = 0, 32, 8 do
|
||||
self:tile("beach", beachX + tx, 128)
|
||||
self:tile("beach", beachX + tx, 136)
|
||||
end
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.rectangle("fill", beachX + 9, 118, 2, 10)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
self:tile("palm", beachX + 6, 112)
|
||||
self:tile("hut", beachX + 20, 118)
|
||||
end
|
||||
|
||||
-- Pikachu. The white spray patch under him doubles as the yellow SGB
|
||||
-- zone's backdrop: shade 0 maps to white in both palettes, so the
|
||||
-- attribute-block bleed never shows on the water pattern.
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
local py = self:seaY(PIKA_X) - 20 - self.y
|
||||
-- Pikachu rides at the height his tile column reports
|
||||
local py = self:seaY(PIKA_X) - self.y
|
||||
self.pikaScreenY = py -- the yellow SGB zone tracks this
|
||||
love.graphics.ellipse("fill", PIKA_X, py + 12, 25, 21)
|
||||
if self.phase == "wipeout" then
|
||||
love.graphics.draw(self.ob, self.bq.splash, PIKA_X - 16,
|
||||
self:seaY(PIKA_X) - 16)
|
||||
love.graphics.draw(self.ob, self.bq.splash, PIKA_X - 4, py)
|
||||
else
|
||||
local quad
|
||||
if self.phase == "ride" and self.speed <= 2
|
||||
@@ -332,7 +510,7 @@ function SurfingMinigame:draw()
|
||||
local bucket = math.floor(((self.rot % 360) + 22.5) / 45) % 8 * 45
|
||||
quad = self.bq.poses[bucket] or self.bq.poses[0]
|
||||
end
|
||||
love.graphics.draw(self.ob, quad, PIKA_X - 12, py)
|
||||
love.graphics.draw(self.ob, quad, PIKA_X, py)
|
||||
end
|
||||
|
||||
-- banner beats: GOOD! / YEAH- / Oh no..
|
||||
@@ -340,11 +518,12 @@ function SurfingMinigame:draw()
|
||||
love.graphics.draw(self.ob, self.bq[self.banner.quad], 60, 40)
|
||||
end
|
||||
|
||||
-- score strip, bottom right: HP: + sheet digits
|
||||
-- the HP window sits under the BG rows ($7e into hWY puts it at y 126;
|
||||
-- tile-aligned here): "HP:" plus the sheet digits over plain white
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
love.graphics.rectangle("fill", 100, 134, 60, 10)
|
||||
love.graphics.draw(self.bg, self.aq.hp, 102, 135)
|
||||
self:drawScore(126, 135, self.score)
|
||||
love.graphics.rectangle("fill", 0, BG_HEIGHT, 160, 144 - BG_HEIGHT)
|
||||
love.graphics.draw(self.bg, self.hpq, 8, BG_HEIGHT + 4)
|
||||
self:drawScore(32, BG_HEIGHT + 4, self.score)
|
||||
|
||||
if self.phase == "results" then
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
|
||||
+35
-20
@@ -10,8 +10,16 @@ local TradeAnim = {}
|
||||
TradeAnim.__index = TradeAnim
|
||||
TradeAnim.isOpaque = true
|
||||
|
||||
-- Trade_LoadMonSprite runs SET_PAL_POKEMON_WHOLE_SCREEN for the mon it puts
|
||||
-- on screen; every other step of the sequence runs SET_PAL_GENERIC, which is
|
||||
-- PAL_MEWMON (data/sgb/sgb_packets.asm PalPacket_Generic). #750
|
||||
function TradeAnim:sgbPalettes(game)
|
||||
return require("src.render.PaletteFX").wholeNamed(game.data, "MEWMON")
|
||||
local P = require("src.render.PaletteFX")
|
||||
local mon = (self.phase == "show_player" and self.sent)
|
||||
or (self.phase == "show_enemy" and self.received)
|
||||
local colors = mon and P.monPal(game.data, mon.species)
|
||||
if colors then return { P.whole(colors) } end
|
||||
return P.wholeNamed(game.data, "MEWMON")
|
||||
end
|
||||
|
||||
local DEFAULT_ART = {
|
||||
@@ -350,17 +358,17 @@ function TradeAnim:drawMonInfo(mon, ot, otId, boxTy)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
|
||||
function TradeAnim:drawIconInBubble(sprite, x, y)
|
||||
local spr = sprite
|
||||
if spr then
|
||||
local sw, sh = spr:getDimensions()
|
||||
local s = 16 / math.max(sw, sh)
|
||||
love.graphics.draw(spr, x, y, 0, s, s)
|
||||
else
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.rectangle("fill", x + 4, y + 4, 8, 8)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
-- Trade_WriteCircledMonOAM: the mon crosses the cable as its party-menu
|
||||
-- sprite (wMonPartySpriteSpecies -> WriteMonPartySpriteOAMBySpecies), not as
|
||||
-- its battle pic, and Trade_AnimCircledMon flips both it and the ring to
|
||||
-- their second frame every step. The ring is four OAM blocks --
|
||||
-- Trade_CircleOAMBlocks .OAMBlock0-3 at (8,8) (24,8) (8,24) (24,24) with the
|
||||
-- X/Y flips -- so the 16x32 bubble sheet holds one quadrant per frame and the
|
||||
-- circle it makes is 32x32 around the 16x16 icon. The icon rides OAM
|
||||
-- block 0 and the circle blocks 1-4 (Trade_WriteCircleOAMBlock counts a up
|
||||
-- from 1), and the lower OAM index wins overlap on DMG, so the icon draws
|
||||
-- on top of the circle's filled interior. #750
|
||||
function TradeAnim:drawIconInBubble(mon, x, y)
|
||||
if self.img.bubble then
|
||||
if not self.bubbleQuad then
|
||||
local iw, ih = self.img.bubble:getDimensions()
|
||||
@@ -370,7 +378,19 @@ function TradeAnim:drawIconInBubble(sprite, x, y)
|
||||
or self.bubbleQuad
|
||||
end
|
||||
local q = self.cableFlash and self.bubbleQuadAlt or self.bubbleQuad
|
||||
love.graphics.draw(self.img.bubble, q, x - 8, y - 8)
|
||||
local left, top = x - 8, y - 8
|
||||
local right, bottom = left + 32, top + 32
|
||||
love.graphics.draw(self.img.bubble, q, left, top)
|
||||
love.graphics.draw(self.img.bubble, q, right, top, 0, -1, 1)
|
||||
love.graphics.draw(self.img.bubble, q, left, bottom, 0, 1, -1)
|
||||
love.graphics.draw(self.img.bubble, q, right, bottom, 0, -1, -1)
|
||||
end
|
||||
local drawn = mon and require("src.ui.PartyMenu").drawIcon(
|
||||
self.game, mon, x, y, false, 0, self.cableFlash)
|
||||
if not drawn then
|
||||
love.graphics.setColor(0, 0, 0, 1)
|
||||
love.graphics.rectangle("fill", x + 4, y + 4, 8, 8)
|
||||
love.graphics.setColor(1, 1, 1, 1)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -471,13 +491,8 @@ function TradeAnim:draw()
|
||||
love.graphics.translate(160, 0)
|
||||
self:drawRightGB()
|
||||
love.graphics.pop()
|
||||
local sprite
|
||||
if p == "transfer_lr" then
|
||||
sprite = self.sentSprite
|
||||
else
|
||||
sprite = self.recvSprite
|
||||
end
|
||||
self:drawIconInBubble(sprite, self.monX, self.monY)
|
||||
local mon = p == "transfer_lr" and self.sent or self.received
|
||||
self:drawIconInBubble(mon, self.monX, self.monY)
|
||||
if self.cableFlash then
|
||||
love.graphics.setColor(1, 1, 1, 0.15)
|
||||
love.graphics.rectangle("fill", 0, 32, 160, 8)
|
||||
|
||||
Reference in New Issue
Block a user