From e13271fc8501645bb3c12ea99142c907b017ca86 Mon Sep 17 00:00:00 2001 From: bryanthaboi Date: Fri, 21 Aug 2026 21:33:42 -0400 Subject: [PATCH] a little more --- src/battle/gen2/Battle.lua | 46 ++++++++++++++++++++++++++------------ tests/gen2_battle_test.lua | 20 +++++++++++++++++ 2 files changed, 52 insertions(+), 14 deletions(-) diff --git a/src/battle/gen2/Battle.lua b/src/battle/gen2/Battle.lua index 667cfe62..15abbe14 100644 --- a/src/battle/gen2/Battle.lua +++ b/src/battle/gen2/Battle.lua @@ -908,7 +908,11 @@ Battle.SLEEP_BYPASS_MOVES = { SNORE = true, SLEEP_TALK = true } -- Can this mon act? Returns true, or false plus the message the cart prints. -- `moveId` is wCurPlayerMove / wCurEnemyMove (effect_commands.asm:193). -function Battle:canAct(mon, moveId) +local function clearBide(state) + state.bideTurns, state.bideStored, state.bideMove = nil, nil, nil +end + +local function checkTurn(self, mon, moveId) local name = self:monName(mon) -- SUBSTATUS_RECHARGE, and it is checked BEFORE status: CheckPlayerTurn reads -- it first, clears it, prints MustRechargeText and jumps to EndTurn, so a mon @@ -967,6 +971,14 @@ function Battle:canAct(mon, moveId) return true end +-- CantMove (engine/battle/effect_commands.asm:344-353) clears BIDE on every +-- arm of CheckPlayerTurn / CheckEnemyTurn that spends the turn. +function Battle:canAct(mon, moveId) + local acted = checkTurn(self, mon, moveId) + if not acted then clearBide(self:volatile(mon)) end + return acted +end + -- STRUGGLE, the move a mon with nothing left to spend falls back to -- (engine/battle/core.asm `.CheckPlayerHasUsableMoves` for the player and -- `.struggle` for the enemy). It lives in the move table like any other move @@ -3854,26 +3866,30 @@ end -- engine/battle/core.asm:627-629 function Battle:cancelBide(mon) - local state = self:volatile(mon) - state.bideTurns, state.bideStored, state.bideMove = nil, nil, nil + clearBide(self:volatile(mon)) end -- Encore forces the move; Disable forbids one. Both are read by the screen -- (to grey out the move list) and by the enemy's own choice below. +-- engine/battle/core.asm:561-566 +local function encoredMove(state, mon) + if not state.encore then return nil end + for _, move in ipairs(mon.moves or {}) do + if move.id == state.encore and (move.pp or 0) > 0 then + return state.encore + end + end + state.encore, state.encoreTurns = nil, nil + return nil +end + function Battle:forcedMove(mon) local locked = self:lockedInMove(mon) if locked then return locked end - local state = self:volatile(mon) -- ParsePlayerAction reads SUBSTATUS_ENCORED ahead of the bide arm -- (engine/battle/core.asm:561-566). - if state.encore then - for _, move in ipairs(mon.moves or {}) do - if move.id == state.encore and (move.pp or 0) > 0 then - return state.encore - end - end - state.encore, state.encoreTurns = nil, nil - end + local encored = encoredMove(self:volatile(mon), mon) + if encored then return encored end return self:fightLockedMove(mon) end @@ -4188,8 +4204,10 @@ function Battle:vanillaEnemyMove() local charged = enemyState.chargeMove if charged then return charged end - -- engine/battle/core.asm:5650, :5532-5533. Straight off the volatile, the - -- way `charged` above is, so the charge-lock test's bare stub still drives it. + -- engine/battle/core.asm:5524-5533: the encore arm runs ahead of + -- CheckEnemyLockedIn (:5650). + local encored = encoredMove(enemyState, self.enemy) + if encored then return encored end if enemyState.bideTurns then return enemyState.bideMove end -- Encore and Disable narrow the pool before the AI ever scores it. diff --git a/tests/gen2_battle_test.lua b/tests/gen2_battle_test.lua index 76a29eda..ff60c311 100644 --- a/tests/gen2_battle_test.lua +++ b/tests/gen2_battle_test.lua @@ -2871,6 +2871,26 @@ end)() b:takeEvents() check("using an item cancels the Bide", b:forcedMove(player), nil) check("and drops the bank", b:volatile(player).bideStored, nil) + + -- CantMove (engine/battle/effect_commands.asm:344-353) clears SUBSTATUS_BIDE + -- on every arm that spends the turn, so a flinch ends the Bide. + b:useMove(player, wild, "BIDE") + b:takeEvents() + check("locked once more", b:forcedMove(player), "BIDE") + b:volatile(player).flinched = true + check("a flinch spends the turn", b:canAct(player, "BIDE"), false) + b:takeEvents() + check("and CantMove ends the Bide", b:forcedMove(player), nil) + check("bank dropped with it", b:volatile(player).bideStored, nil) + + -- .not_linked reads SUBSTATUS_ENCORED before CheckEnemyLockedIn + -- (engine/battle/core.asm:5524-5533), so an encored foe obeys the Encore. + es.bideTurns, es.bideMove, es.bideStored = 2, "BIDE", 0 + es.encore, es.encoreTurns = "TACKLE", 3 + check("Encore outranks the foe's Bide lock", b:enemyMove(), "TACKLE") + es.encore, es.encoreTurns = nil, nil + check("without it the Bide lock holds", b:enemyMove(), "BIDE") + es.bideTurns, es.bideMove, es.bideStored = nil, nil, nil end)() -- --------------------------------------------------- Snore and Sleep Talk