mirror of
https://github.com/bryanthaboi/gen1recomp.git
synced 2026-08-12 16:31:05 +02:00
129 lines
5.6 KiB
Lua
129 lines
5.6 KiB
Lua
-- The Silph Scope unveil in the POKEMON_TOWER_6F battle (#492).
|
|
--
|
|
-- PrintBeginningBattleText .isMarowak (engine/battle/common_text.asm:49-60):
|
|
-- with the scope in the bag the battle still ENTERS disguised -- InitWildBattle
|
|
-- takes its .isGhost branch on wCurOpponent == RESTLESS_SOUL either way -- and
|
|
-- the scope only buys the unveil that is played over the disguise:
|
|
-- EnemyAppearedText, UnveiledGhostText, LoadEnemyMonData, MarowakAnim, then
|
|
-- WildMonAppearedText. The port used to hand the scope-carrying player a
|
|
-- battle that opened with MAROWAK already on screen and no unveil at all.
|
|
--
|
|
-- The story3 dispatch half (which branch the trigger picks, and that the ball
|
|
-- dodge survives the unveil) is tests/parity_marowak.lua and
|
|
-- tests/parity_marowak_ball.lua.
|
|
package.path = "./?.lua;./?/init.lua;" .. package.path
|
|
|
|
local T = require("tests.modkit")
|
|
local BattleState = require("src.battle.BattleState")
|
|
|
|
T.check(type(BattleState.makeUnveiledGhost) == "function",
|
|
"BattleState:makeUnveiledGhost exists")
|
|
T.check(BattleState.makeUnveiledGhost ~= BattleState.makeGhost,
|
|
"the unveiled battle is not just the scope-less ghost battle")
|
|
|
|
local REAL_SPRITE = { marker = "the MAROWAK front pic" }
|
|
|
|
local function newBattle()
|
|
return setmetatable({
|
|
kind = "wild",
|
|
queue = {},
|
|
frame = 0,
|
|
data = { text = {}, pokemon = {} },
|
|
enemy = { name = "MAROWAK", sprite = REAL_SPRITE,
|
|
mon = { species = "MAROWAK", level = 30 } },
|
|
}, BattleState)
|
|
end
|
|
|
|
-- ------------------------------------------------------- entering disguised
|
|
local b = newBattle()
|
|
b:makeUnveiledGhost()
|
|
T.eq(b.enemy.name, "GHOST",
|
|
"with the scope the battle still OPENS as the GHOST (#492)")
|
|
T.check(b.enemy.sprite ~= REAL_SPRITE,
|
|
"and wears the ghost pic, not the MAROWAK one")
|
|
T.check(b.scopeReveal == true, "the unveil is armed")
|
|
T.check(not b.ghost,
|
|
"but IsGhostBattle stays false: the mon can be attacked and flees normally")
|
|
T.eq(b.ghostReal.name, "MAROWAK", "the real nick is kept for LoadEnemyMonData")
|
|
T.eq(b.ghostReal.sprite, REAL_SPRITE, "...and the real pic with it")
|
|
|
|
local ghostSprite = b.enemy.sprite
|
|
|
|
-- ------------------------------------------------------------- the row order
|
|
-- .isMarowak prints over the "GHOST appeared!" box enter() already queued: the
|
|
-- unveil line, MarowakAnim (a wait row parked over the fx state machine), then
|
|
-- "Wild MAROWAK appeared!" under the restored name.
|
|
b:queueScopeReveal()
|
|
T.eq(#b.queue, 4, "the unveil is four queue rows")
|
|
T.check(type(b.queue[1].text) == "string"
|
|
and b.queue[1].text:find("SILPH SCOPE", 1, true) ~= nil,
|
|
"row 1 is UnveiledGhostText")
|
|
T.check(type(b.queue[2].fn) == "function", "row 2 starts MarowakAnim")
|
|
T.eq(b.queue[3].wait, BattleState.GHOST_REVEAL_FRAMES,
|
|
"row 3 holds the queue for the length of the animation")
|
|
T.check(type(b.queue[4].text) == "string"
|
|
and b.queue[4].text:find("MAROWAK", 1, true) ~= nil,
|
|
"row 4 is WildMonAppearedText under the real name")
|
|
T.check(b.queue[4].text:find("GHOST", 1, true) == nil,
|
|
"...and not under the disguise")
|
|
|
|
-- ------------------------------------------------------------ the animation
|
|
-- MarowakAnim (engine/battle/ghost_marowak_anim.asm): FlashSprite8Times, the
|
|
-- rOBP1 fade-out, the pic swap, the fade-in. The whole point of #492 is that
|
|
-- the swap comes AFTER the ghost has faded off the paper, so every frame up to
|
|
-- there still shows the GHOST.
|
|
b.queue[2].fn()
|
|
T.check(b.ghostReveal ~= nil, "the fn arms the reveal state")
|
|
|
|
local FLASH = BattleState.GHOST_FLASH_FRAMES
|
|
local FADE_OUT = BattleState.GHOST_FADE_OUT_FRAMES
|
|
local swapFrame, fades, stillGhost = nil, {}, true
|
|
for frame = 1, BattleState.GHOST_REVEAL_FRAMES do
|
|
b:updateFx()
|
|
local pf = b.picFx and b.picFx[b.enemy]
|
|
fades[frame] = pf and pf.fade
|
|
if not swapFrame and b.enemy.name ~= "GHOST" then swapFrame = frame end
|
|
if frame <= FLASH + FADE_OUT and b.enemy.name ~= "GHOST" then
|
|
stillGhost = false
|
|
end
|
|
end
|
|
|
|
T.check(stillGhost,
|
|
"the GHOST is on screen for the whole flash and fade-out (#492)")
|
|
T.eq(swapFrame, FLASH + FADE_OUT + 1,
|
|
"the pic and nick swap the frame after the ghost has faded out")
|
|
T.eq(b.enemy.name, "MAROWAK", "the real nick is back by the end")
|
|
T.eq(b.enemy.sprite, REAL_SPRITE, "and the real pic with it")
|
|
T.check(b.enemy.sprite ~= ghostSprite, "the ghost pic is gone")
|
|
|
|
-- FlashSprite8Times xors rOBP1 with $80 every 10 frames, so the body alternates
|
|
-- between two shades rather than sitting opaque
|
|
T.eq(fades[5], 1, "the flash opens on the ghost at full strength")
|
|
T.eq(fades[15], 0.5, "...drops a shade 10 frames in")
|
|
T.eq(fades[25], 1, "...and comes back")
|
|
T.check(fades[FLASH + FADE_OUT] == 0,
|
|
"the ghost is fully faded out when the swap lands")
|
|
T.check(fades[FLASH + FADE_OUT + 1] > 0
|
|
and fades[FLASH + FADE_OUT + 1] < 1,
|
|
"the MAROWAK fades IN rather than popping on")
|
|
T.eq(fades[BattleState.GHOST_REVEAL_FRAMES - 1], 1,
|
|
"and reaches full strength before the box turns")
|
|
|
|
T.check(b.ghostReveal == nil, "the reveal state clears itself")
|
|
T.check(b.scopeReveal == nil, "so does the flag that gated the intro cry")
|
|
local pf = b.picFx and b.picFx[b.enemy]
|
|
T.check(pf == nil or pf.fade == nil,
|
|
"no leftover alpha on the pic for the rest of the battle")
|
|
|
|
-- ------------------------------------------------ the scope-less ghost still
|
|
-- Nothing above may change the battle you get WITHOUT the scope: still a real
|
|
-- ghost battle, still no unveil queued.
|
|
local noScope = newBattle()
|
|
noScope:makeGhost()
|
|
T.check(noScope.ghost == true, "without the scope IsGhostBattle is true")
|
|
T.eq(noScope.enemy.name, "GHOST", "and the disguise is the same one")
|
|
T.check(noScope.scopeReveal == nil, "with no unveil armed")
|
|
T.eq(#noScope.queue, 0, "and no unveil rows queued")
|
|
|
|
T.finish("ghost unveil")
|