From b5417fafec7dd37cb4be391f3bd3d4541a2a381e Mon Sep 17 00:00:00 2001 From: Remy Oukaour Date: Tue, 26 Dec 2017 17:47:05 -0500 Subject: Split battle/ into data/ and engine/ components --- engine/battle/effect_commands.asm | 10066 ++++++++++++++++++++++++++++++++++++ 1 file changed, 10066 insertions(+) create mode 100644 engine/battle/effect_commands.asm (limited to 'engine/battle/effect_commands.asm') diff --git a/engine/battle/effect_commands.asm b/engine/battle/effect_commands.asm new file mode 100644 index 000000000..5922afea3 --- /dev/null +++ b/engine/battle/effect_commands.asm @@ -0,0 +1,10066 @@ +DoPlayerTurn: ; 34000 + call SetPlayerTurn + + ld a, [wPlayerAction] + and a + ret nz + + jr DoTurn + +; 3400a + + +DoEnemyTurn: ; 3400a + call SetEnemyTurn + + ld a, [wLinkMode] + and a + jr z, DoTurn + + ld a, [wBattleAction] + cp BATTLEACTION_E + jr z, DoTurn + cp BATTLEACTION_SWITCH1 + ret nc + + ; fallthrough +; 3401d + + +DoTurn: ; 3401d +; Read in and execute the user's move effects for this turn. + + xor a + ld [wTurnEnded], a + + ; Effect command checkturn is called for every move. + call CheckTurn + + ld a, [wTurnEnded] + and a + ret nz + + call UpdateMoveData +; 3402c + + +DoMove: ; 3402c +; Get the user's move effect. + ld a, BATTLE_VARS_MOVE_EFFECT + call GetBattleVar + ld c, a + ld b, 0 + ld hl, MoveEffectsPointers + add hl, bc + add hl, bc + ld a, BANK(MoveEffectsPointers) + call GetFarHalfword + + ld de, BattleScriptBuffer + +.GetMoveEffect: + ld a, BANK(MoveEffects) + call GetFarByte + inc hl + ld [de], a + inc de + cp $ff + jr nz, .GetMoveEffect + +; Start at the first command. + ld hl, BattleScriptBuffer + ld a, l + ld [BattleScriptBufferAddress], a + ld a, h + ld [BattleScriptBufferAddress + 1], a + +.ReadMoveEffectCommand: + +; ld a, [BattleScriptBufferAddress++] + ld a, [BattleScriptBufferAddress] + ld l, a + ld a, [BattleScriptBufferAddress + 1] + ld h, a + + ld a, [hli] + + push af + ld a, l + ld [BattleScriptBufferAddress], a + ld a, h + ld [BattleScriptBufferAddress + 1], a + pop af + +; endturn_command (-2) is used to terminate branches without ending the read cycle. + cp endturn_command + ret nc + +; The rest of the commands (01-af) are read from BattleCommandPointers. + push bc + dec a + ld c, a + ld b, 0 + ld hl, BattleCommandPointers + add hl, bc + add hl, bc + pop bc + + ld a, BANK(BattleCommandPointers) + call GetFarHalfword + + call .DoMoveEffectCommand + + jr .ReadMoveEffectCommand + +.DoMoveEffectCommand: + jp hl + +; 34084 + + +CheckTurn: +BattleCommand_CheckTurn: ; 34084 +; checkturn + +; Repurposed as hardcoded turn handling. Useless as a command. + +; Move $ff immediately ends the turn. + ld a, BATTLE_VARS_MOVE + call GetBattleVar + inc a + jp z, EndTurn + + xor a + ld [AttackMissed], a + ld [EffectFailed], a + ld [wKickCounter], a + ld [AlreadyDisobeyed], a + ld [AlreadyFailed], a + ld [wSomeoneIsRampaging], a + + ld a, 10 ; 1.0 + ld [TypeModifier], a + + ld a, [hBattleTurn] + and a + jp nz, CheckEnemyTurn + + +CheckPlayerTurn: + + ld hl, PlayerSubStatus4 + bit SUBSTATUS_RECHARGE, [hl] + jr z, .no_recharge + + res SUBSTATUS_RECHARGE, [hl] + ld hl, MustRechargeText + call StdBattleTextBox + call CantMove + jp EndTurn + +.no_recharge + + + ld hl, BattleMonStatus + ld a, [hl] + and SLP + jr z, .not_asleep + + dec a + ld [BattleMonStatus], a + and SLP + jr z, .woke_up + + xor a + ld [wNumHits], a + ld de, ANIM_SLP + call FarPlayBattleAnimation + jr .fast_asleep + +.woke_up + ld hl, WokeUpText + call StdBattleTextBox + call CantMove + call UpdateBattleMonInParty + ld hl, UpdatePlayerHUD + call CallBattleCore + ld a, $1 + ld [hBGMapMode], a + ld hl, PlayerSubStatus1 + res SUBSTATUS_NIGHTMARE, [hl] + jr .not_asleep + +.fast_asleep + ld hl, FastAsleepText + call StdBattleTextBox + + ; Snore and Sleep Talk bypass sleep. + ld a, [CurPlayerMove] + cp SNORE + jr z, .not_asleep + cp SLEEP_TALK + jr z, .not_asleep + + call CantMove + jp EndTurn + +.not_asleep + + + ld hl, BattleMonStatus + bit FRZ, [hl] + jr z, .not_frozen + + ; Flame Wheel and Sacred Fire thaw the user. + ld a, [CurPlayerMove] + cp FLAME_WHEEL + jr z, .not_frozen + cp SACRED_FIRE + jr z, .not_frozen + + ld hl, FrozenSolidText + call StdBattleTextBox + + call CantMove + jp EndTurn + +.not_frozen + + + ld hl, PlayerSubStatus3 + bit SUBSTATUS_FLINCHED, [hl] + jr z, .not_flinched + + res SUBSTATUS_FLINCHED, [hl] + ld hl, FlinchedText + call StdBattleTextBox + + call CantMove + jp EndTurn + +.not_flinched + + + ld hl, PlayerDisableCount + ld a, [hl] + and a + jr z, .not_disabled + + dec a + ld [hl], a + and $f + jr nz, .not_disabled + + ld [hl], a + ld [DisabledMove], a + ld hl, DisabledNoMoreText + call StdBattleTextBox + +.not_disabled + + + ld a, [PlayerSubStatus3] + add a + jr nc, .not_confused + ld hl, PlayerConfuseCount + dec [hl] + jr nz, .confused + + ld hl, PlayerSubStatus3 + res SUBSTATUS_CONFUSED, [hl] + ld hl, ConfusedNoMoreText + call StdBattleTextBox + jr .not_confused + +.confused + ld hl, IsConfusedText + call StdBattleTextBox + xor a + ld [wNumHits], a + ld de, ANIM_CONFUSED + call FarPlayBattleAnimation + + ; 50% chance of hitting itself + call BattleRandom + cp $80 + jr nc, .not_confused + + ; clear confusion-dependent substatus + ld hl, PlayerSubStatus3 + ld a, [hl] + and 1 << SUBSTATUS_CONFUSED + ld [hl], a + + call HitConfusion + call CantMove + jp EndTurn + +.not_confused + + + ld a, [PlayerSubStatus1] + add a ; bit SUBSTATUS_ATTRACT + jr nc, .not_infatuated + + ld hl, InLoveWithText + call StdBattleTextBox + xor a + ld [wNumHits], a + ld de, ANIM_IN_LOVE + call FarPlayBattleAnimation + + ; 50% chance of infatuation + call BattleRandom + cp $80 + jr c, .not_infatuated + + ld hl, InfatuationText + call StdBattleTextBox + call CantMove + jp EndTurn + +.not_infatuated + + + ; We can't disable a move that doesn't exist. + ld a, [DisabledMove] + and a + jr z, .no_disabled_move + + ; Are we using the disabled move? + ld hl, CurPlayerMove + cp [hl] + jr nz, .no_disabled_move + + call MoveDisabled + call CantMove + jp EndTurn + +.no_disabled_move + + + ld hl, BattleMonStatus + bit PAR, [hl] + ret z + + ; 25% chance to be fully paralyzed + call BattleRandom + cp $3f + ret nc + + ld hl, FullyParalyzedText + call StdBattleTextBox + call CantMove + jp EndTurn + +; 341f0 + + +CantMove: ; 341f0 + ld a, BATTLE_VARS_SUBSTATUS1 + call GetBattleVarAddr + res SUBSTATUS_ROLLOUT, [hl] + + ld a, BATTLE_VARS_SUBSTATUS3 + call GetBattleVarAddr + ld a, [hl] + and $ff ^ (1< ReflectCount + inc bc + + ld a, 5 + ld [bc], a + ld hl, ReflectEffectText + +.good + call AnimateCurrentMove + jp StdBattleTextBox + +.failed + call AnimateFailedMove + jp PrintButItFailed + +; 3733d + + +PrintDoesntAffect: ; 3733d +; 'it doesn't affect' + ld hl, DoesntAffectText + jp StdBattleTextBox + +; 37343 + + +PrintNothingHappened: ; 37343 +; 'but nothing happened!' + ld hl, NothingHappenedText + jp StdBattleTextBox + +; 37349 + + +TryPrintButItFailed: ; 37349 + ld a, [AlreadyFailed] + and a + ret nz + + ; fallthrough +; 3734e + + +PrintButItFailed: ; 3734e +; 'but it failed!' + ld hl, ButItFailedText + jp StdBattleTextBox + +; 37354 + + +FailSnore: +FailDisable: +FailConversion2: +FailAttract: +FailForesight: +FailSpikes: + call AnimateFailedMove + ; fallthrough +; 37357 + +FailMimic: ; 37357 + ld hl, ButItFailedText ; 'but it failed!' + ld de, ItFailedText ; 'it failed!' + jp FailText_CheckOpponentProtect + +; 37360 + + +PrintDidntAffect: ; 37360 +; 'it didn't affect' + ld hl, DidntAffect1Text + jp StdBattleTextBox + +; 37366 + + +PrintDidntAffect2: ; 37366 + call AnimateFailedMove + ld hl, DidntAffect1Text ; 'it didn't affect' + ld de, DidntAffect2Text ; 'it didn't affect' + jp FailText_CheckOpponentProtect + +; 37372 + + +PrintParalyze: ; 37372 +; 'paralyzed! maybe it can't attack!' + ld hl, ParalyzedText + jp StdBattleTextBox + +; 37378 + + +CheckSubstituteOpp: ; 37378 + ld a, BATTLE_VARS_SUBSTATUS4_OPP + call GetBattleVar + bit SUBSTATUS_SUBSTITUTE, a + ret + +; 37380 + + +BattleCommand_Selfdestruct: ; 37380 + farcall TrainerRankings_Selfdestruct + ld a, BATTLEANIM_PLAYER_DAMAGE + ld [wNumHits], a + ld c, 3 + call DelayFrames + ld a, BATTLE_VARS_STATUS + call GetBattleVarAddr + xor a + ld [hli], a + inc hl + ld [hli], a + ld [hl], a + ld a, $1 + ld [wKickCounter], a + call BattleCommand_LowerSub + call LoadMoveAnim + ld a, BATTLE_VARS_SUBSTATUS4 + call GetBattleVarAddr + res SUBSTATUS_LEECH_SEED, [hl] + ld a, BATTLE_VARS_SUBSTATUS5_OPP + call GetBattleVarAddr + res SUBSTATUS_DESTINY_BOND, [hl] + call _CheckBattleScene + ret nc + farcall DrawPlayerHUD + farcall DrawEnemyHUD + call WaitBGMap + jp RefreshBattleHuds + +; 373c9 + + +INCLUDE "engine/battle/effect_commands/mirror_move.asm" + +INCLUDE "engine/battle/effect_commands/metronome.asm" + + +CheckUserMove: ; 37462 +; Return z if the user has move a. + ld b, a + ld de, BattleMonMoves + ld a, [hBattleTurn] + and a + jr z, .ok + ld de, EnemyMonMoves +.ok + + ld c, NUM_MOVES +.loop + ld a, [de] + inc de + cp b + ret z + + dec c + jr nz, .loop + + ld a, 1 + and a + ret + +; 3747b + + +ResetTurn: ; 3747b + ld hl, wPlayerCharging + ld a, [hBattleTurn] + and a + jr z, .player + ld hl, wEnemyCharging + +.player + ld [hl], 1 + xor a + ld [AlreadyDisobeyed], a + call DoMove + jp EndMoveEffect + +; 37492 + + +INCLUDE "engine/battle/effect_commands/thief.asm" + + +BattleCommand_ArenaTrap: ; 37517 +; arenatrap + +; Doesn't work on an absent opponent. + + call CheckHiddenOpponent + jr nz, .failed + +; Don't trap if the opponent is already trapped. + + ld a, BATTLE_VARS_SUBSTATUS5 + call GetBattleVarAddr + bit SUBSTATUS_CANT_RUN, [hl] + jr nz, .failed + +; Otherwise trap the opponent. + + set SUBSTATUS_CANT_RUN, [hl] + call AnimateCurrentMove + ld hl, CantEscapeNowText + jp StdBattleTextBox + +.failed + call AnimateFailedMove + jp PrintButItFailed + +; 37536 + + +INCLUDE "engine/battle/effect_commands/nightmare.asm" + + +BattleCommand_Defrost: ; 37563 +; defrost + +; Thaw the user. + + ld a, BATTLE_VARS_STATUS + call GetBattleVarAddr + bit FRZ, [hl] + ret z + res FRZ, [hl] + +; Don't update the enemy's party struct in a wild battle. + + ld a, [hBattleTurn] + and a + jr z, .party + + ld a, [wBattleMode] + dec a + jr z, .done + +.party + ld a, MON_STATUS + call UserPartyAttr + res FRZ, [hl] + +.done + call RefreshBattleHuds + ld hl, WasDefrostedText + jp StdBattleTextBox + +; 37588 + + +INCLUDE "engine/battle/effect_commands/curse.asm" + +INCLUDE "engine/battle/effect_commands/protect.asm" + +INCLUDE "engine/battle/effect_commands/endure.asm" + +INCLUDE "engine/battle/effect_commands/spikes.asm" + +INCLUDE "engine/battle/effect_commands/foresight.asm" + +INCLUDE "engine/battle/effect_commands/perish_song.asm" + +INCLUDE "engine/battle/effect_commands/sandstorm.asm" + +INCLUDE "engine/battle/effect_commands/rollout.asm" + + +BattleCommand5d: ; 37791 +; unused + ret + +; 37792 + + +BattleCommand_FuryCutter: ; 37792 +; furycutter + + ld hl, PlayerFuryCutterCount + ld a, [hBattleTurn] + and a + jr z, .go + ld hl, EnemyFuryCutterCount + +.go + ld a, [AttackMissed] + and a + jp nz, ResetFuryCutterCount + + inc [hl] + +; Damage capped at 5 turns' worth (16x). + ld a, [hl] + ld b, a + cp 6 + jr c, .checkdouble + ld b, 5 + +.checkdouble + dec b + ret z + +; Double the damage + ld hl, CurDamage + 1 + sla [hl] + dec hl + rl [hl] + jr nc, .checkdouble + +; No overflow + ld a, $ff + ld [hli], a + ld [hl], a + ret + +; 377be + + +ResetFuryCutterCount: ; 377be + + push hl + + ld hl, PlayerFuryCutterCount + ld a, [hBattleTurn] + and a + jr z, .reset + ld hl, EnemyFuryCutterCount + +.reset + xor a + ld [hl], a + + pop hl + ret + +; 377ce + + +INCLUDE "engine/battle/effect_commands/attract.asm" + +BattleCommand_HappinessPower: ; 3784b +; happinesspower + push bc + ld hl, BattleMonHappiness + ld a, [hBattleTurn] + and a + jr z, .ok + ld hl, EnemyMonHappiness +.ok + xor a + ld [hMultiplicand + 0], a + ld [hMultiplicand + 1], a + ld a, [hl] + ld [hMultiplicand + 2], a + ld a, 10 + ld [hMultiplier], a + call Multiply + ld a, 25 + ld [hDivisor], a + ld b, 4 + call Divide + ld a, [hQuotient + 2] + ld d, a + pop bc + ret + +; 37874 + + +INCLUDE "engine/battle/effect_commands/present.asm" + +BattleCommand_FrustrationPower: ; 3790e +; frustrationpower + + push bc + ld hl, BattleMonHappiness + ld a, [hBattleTurn] + and a + jr z, .got_happiness + ld hl, EnemyMonHappiness +.got_happiness + ld a, $ff + sub [hl] + ld [hMultiplicand + 2], a + xor a + ld [hMultiplicand + 0], a + ld [hMultiplicand + 1], a + ld a, 10 + ld [hMultiplier], a + call Multiply + ld a, 25 + ld [hDivisor], a + ld b, 4 + call Divide + ld a, [hQuotient + 2] + ld d, a + pop bc + ret + +; 37939 + + +BattleCommand_Safeguard: ; 37939 +; safeguard + + ld hl, PlayerScreens + ld de, PlayerSafeguardCount + ld a, [hBattleTurn] + and a + jr z, .ok + ld hl, EnemyScreens + ld de, EnemySafeguardCount +.ok + bit SCREENS_SAFEGUARD, [hl] + jr nz, .failed + set SCREENS_SAFEGUARD, [hl] + ld a, 5 + ld [de], a + call AnimateCurrentMove + ld hl, CoveredByVeilText + jp StdBattleTextBox + +.failed + call AnimateFailedMove + jp PrintButItFailed + +; 37962 + + +SafeCheckSafeguard: ; 37962 + push hl + ld hl, EnemyScreens + ld a, [hBattleTurn] + and a + jr z, .got_turn + ld hl, PlayerScreens + +.got_turn + bit SCREENS_SAFEGUARD, [hl] + pop hl + ret + +; 37972 + + +BattleCommand_CheckSafeguard: ; 37972 +; checksafeguard + ld hl, EnemyScreens + ld a, [hBattleTurn] + and a + jr z, .got_turn + ld hl, PlayerScreens +.got_turn + bit SCREENS_SAFEGUARD, [hl] + ret z + ld a, 1 + ld [AttackMissed], a + call BattleCommand_MoveDelay + ld hl, SafeguardProtectText + call StdBattleTextBox + jp EndMoveEffect + +; 37991 + + +BattleCommand_GetMagnitude: ; 37991 +; getmagnitude + + push bc + call BattleRandom + ld b, a + ld hl, .Magnitudes +.loop + ld a, [hli] + cp b + jr nc, .ok + inc hl + inc hl + jr .loop + +.ok + ld d, [hl] + push de + inc hl + ld a, [hl] + ld [wTypeMatchup], a + call BattleCommand_MoveDelay + ld hl, MagnitudeText + call StdBattleTextBox + pop de + pop bc + ret + +.Magnitudes: + ; /255, BP, magnitude + db 13, 10, 4 + db 38, 30, 5 + db 89, 50, 6 + db 166, 70, 7 + db 217, 90, 8 + db 242, 110, 9 + db 255, 150, 10 +; 379c9 + + +BattleCommand_BatonPass: ; 379c9 +; batonpass + + ld a, [hBattleTurn] + and a + jp nz, .Enemy + + +; Need something to switch to + call CheckAnyOtherAlivePartyMons + jp z, FailedBatonPass + + call UpdateBattleMonInParty + call AnimateCurrentMove + + ld c, 50 + call DelayFrames + +; Transition into switchmon menu + call LoadStandardMenuDataHeader + farcall SetUpBattlePartyMenu_NoLoop + + farcall ForcePickSwitchMonInBattle + +; Return to battle scene + call ClearPalettes + farcall _LoadBattleFontsHPBar + call CloseWindow + call ClearSprites + hlcoord 1, 0 + lb bc, 4, 10 + call ClearBox + ld b, SCGB_BATTLE_COLORS + call GetSGBLayout + call SetPalettes + call BatonPass_LinkPlayerSwitch + +; Mobile link battles handle entrances differently + farcall CheckMobileBattleError + jp c, EndMoveEffect + + ld hl, PassedBattleMonEntrance + call CallBattleCore + + call ResetBatonPassStatus + ret + + +.Enemy: + +; Wildmons don't have anything to switch to + ld a, [wBattleMode] + dec a ; WILDMON + jp z, FailedBatonPass + + call CheckAnyOtherAliveEnemyMons + jp z, FailedBatonPass + + call UpdateEnemyMonInParty + call AnimateCurrentMove + call BatonPass_LinkEnemySwitch + +; Mobile link battles handle entrances differently + farcall CheckMobileBattleError + jp c, EndMoveEffect + +; Passed enemy PartyMon entrance + xor a + ld [wEnemySwitchMonIndex], a + ld hl, EnemySwitch_SetMode + call CallBattleCore + ld hl, ResetBattleParticipants + call CallBattleCore + ld a, 1 + ld [wTypeMatchup], a + ld hl, ApplyStatLevelMultiplierOnAllStats + call CallBattleCore + + ld hl, SpikesDamage + call CallBattleCore + + jr ResetBatonPassStatus + +; 37a67 + + +BatonPass_LinkPlayerSwitch: ; 37a67 + ld a, [wLinkMode] + and a + ret z + + ld a, 1 + ld [wPlayerAction], a + + call LoadStandardMenuDataHeader + ld hl, LinkBattleSendReceiveAction + call CallBattleCore + call CloseWindow + + xor a + ld [wPlayerAction], a + ret + +; 37a82 + + +BatonPass_LinkEnemySwitch: ; 37a82 + ld a, [wLinkMode] + and a + ret z + + call LoadStandardMenuDataHeader + ld hl, LinkBattleSendReceiveAction + call CallBattleCore + + ld a, [OTPartyCount] + add BATTLEACTION_SWITCH1 + ld b, a + ld a, [wBattleAction] + cp BATTLEACTION_SWITCH1 + jr c, .baton_pass + cp b + jr c, .switch + +.baton_pass + ld a, [CurOTMon] + add BATTLEACTION_SWITCH1 + ld [wBattleAction], a +.switch + jp CloseWindow + +; 37aab + + +FailedBatonPass: ; 37aab + call AnimateFailedMove + jp PrintButItFailed + +; 37ab1 + + +ResetBatonPassStatus: ; 37ab1 +; Reset status changes that aren't passed by Baton Pass. + + ; Nightmare isn't passed. + ld a, BATTLE_VARS_STATUS + call GetBattleVar + and SLP + jr nz, .ok + + ld a, BATTLE_VARS_SUBSTATUS1 + call GetBattleVarAddr + res SUBSTATUS_NIGHTMARE, [hl] +.ok + + ; Disable isn't passed. + call ResetActorDisable + + ; Attraction isn't passed. + ld hl, PlayerSubStatus1 + res SUBSTATUS_IN_LOVE, [hl] + ld hl, EnemySubStatus1 + res SUBSTATUS_IN_LOVE, [hl] + ld hl, PlayerSubStatus5 + + ld a, BATTLE_VARS_SUBSTATUS5 + call GetBattleVarAddr + res SUBSTATUS_TRANSFORMED, [hl] + res SUBSTATUS_ENCORED, [hl] + + ; New mon hasn't used a move yet. + ld a, BATTLE_VARS_LAST_MOVE + call GetBattleVarAddr + ld [hl], 0 + + xor a + ld [wPlayerWrapCount], a + ld [wEnemyWrapCount], a + ret + +; 37ae9 + + +CheckAnyOtherAlivePartyMons: ; 37ae9 + ld hl, PartyMon1HP + ld a, [PartyCount] + ld d, a + ld a, [CurBattleMon] + ld e, a + jr CheckAnyOtherAliveMons + +; 37af6 + + +CheckAnyOtherAliveEnemyMons: ; 37af6 + ld hl, OTPartyMon1HP + ld a, [OTPartyCount] + ld d, a + ld a, [CurOTMon] + ld e, a + + ; fallthrough +; 37b01 + +CheckAnyOtherAliveMons: ; 37b01 +; Check for nonzero HP starting from partymon +; HP at hl for d partymons, besides current mon e. + +; Return nz if any are alive. + + xor a + ld b, a + ld c, a +.loop + ld a, c + cp d + jr z, .done + cp e + jr z, .next + + ld a, [hli] + or b + ld b, a + ld a, [hld] + or b + ld b, a + +.next + push bc + ld bc, PARTYMON_STRUCT_LENGTH + add hl, bc + pop bc + inc c + jr .loop + +.done + ld a, b + and a + ret + +; 37b1d + + +BattleCommand_Pursuit: ; 37b1d +; pursuit +; Double damage if the opponent is switching. + + ld hl, wEnemyIsSwitching + ld a, [hBattleTurn] + and a + jr z, .ok + ld hl, wPlayerIsSwitching +.ok + ld a, [hl] + and a + ret z + + ld hl, CurDamage + 1 + sla [hl] + dec hl + rl [hl] + ret nc + + ld a, $ff + ld [hli], a + ld [hl], a + ret + +; 37b39 + + +BattleCommand_ClearHazards: ; 37b39 +; clearhazards + + ld a, BATTLE_VARS_SUBSTATUS4 + call GetBattleVarAddr + bit SUBSTATUS_LEECH_SEED, [hl] + jr z, .not_leeched + res SUBSTATUS_LEECH_SEED, [hl] + ld hl, ShedLeechSeedText + call StdBattleTextBox +.not_leeched + + ld hl, PlayerScreens + ld de, wPlayerWrapCount + ld a, [hBattleTurn] + and a + jr z, .got_screens_wrap + ld hl, EnemyScreens + ld de, wEnemyWrapCount +.got_screens_wrap + bit SCREENS_SPIKES, [hl] + jr z, .no_spikes + res SCREENS_SPIKES, [hl] + ld hl, BlewSpikesText + push de + call StdBattleTextBox + pop de +.no_spikes + + ld a, [de] + and a + ret z + xor a + ld [de], a + ld hl, ReleasedByText + jp StdBattleTextBox + +; 37b74 + + +BattleCommand_HealMorn: ; 37b74 +; healmorn + ld b, MORN_F + jr BattleCommand_TimeBasedHealContinue + +; 37b78 + +BattleCommand_HealDay: ; 37b78 +; healday + ld b, DAY_F + jr BattleCommand_TimeBasedHealContinue + +; 37b7c + +BattleCommand_HealNite: ; 37b7c +; healnite + ld b, NITE_F + ; fallthrough +; 37b7e + +BattleCommand_TimeBasedHealContinue: ; 37b7e +; Time- and weather-sensitive heal. + + ld hl, BattleMonMaxHP + ld de, BattleMonHP + ld a, [hBattleTurn] + and a + jr z, .start + ld hl, EnemyMonMaxHP + ld de, EnemyMonHP + +.start +; Index for .Multipliers +; Default restores half max HP. + ld c, 2 + +; Don't bother healing if HP is already full. + push bc + call StringCmp + pop bc + jr z, .Full + +; Don't factor in time of day in link battles. + ld a, [wLinkMode] + and a + jr nz, .Weather + + ld a, [TimeOfDay] + cp b + jr z, .Weather + dec c ; double + +.Weather: + ld a, [Weather] + and a + jr z, .Heal + +; x2 in sun +; /2 in rain/sandstorm + inc c + cp WEATHER_SUN + jr z, .Heal + dec c + dec c + +.Heal: + ld b, 0 + ld hl, .Multipliers + add hl, bc + add hl, bc + + ld a, [hli] + ld h, [hl] + ld l, a + ld a, BANK(GetMaxHP) + rst FarCall + + call AnimateCurrentMove + call BattleCommand_SwitchTurn + + callfar RestoreHP + + call BattleCommand_SwitchTurn + call UpdateUserInParty + +; 'regained health!' + ld hl, RegainedHealthText + jp StdBattleTextBox + +.Full: + call AnimateFailedMove + +; 'hp is full!' + ld hl, HPIsFullText + jp StdBattleTextBox + +.Multipliers: + dw GetEighthMaxHP + dw GetQuarterMaxHP + dw GetHalfMaxHP + dw GetMaxHP +; 37be8 + + +BattleCommand_HiddenPower: ; 37be8 +; hiddenpower + + ld a, [AttackMissed] + and a + ret nz + farcall HiddenPowerDamage + ret + +; 37bf4 + + +BattleCommand_StartRain: ; 37bf4 +; startrain + ld a, WEATHER_RAIN + ld [Weather], a + ld a, 5 + ld [WeatherCount], a + call AnimateCurrentMove + ld hl, DownpourText + jp StdBattleTextBox + +; 37c07 + + +BattleCommand_StartSun: ; 37c07 +; startsun + ld a, WEATHER_SUN + ld [Weather], a + ld a, 5 + ld [WeatherCount], a + call AnimateCurrentMove + ld hl, SunGotBrightText + jp StdBattleTextBox + +; 37c1a + + +BattleCommand_BellyDrum: ; 37c1a +; bellydrum +; This command is buggy because it raises the user's attack +; before checking that it has enough HP to use the move. +; Swap the order of these two blocks to fix. + call BattleCommand_AttackUp2 + ld a, [AttackMissed] + and a + jr nz, .failed + + callfar GetHalfMaxHP + callfar CheckUserHasEnoughHP + jr nc, .failed + + push bc + call AnimateCurrentMove + pop bc + callfar SubtractHPFromUser + call UpdateUserInParty + ld a, 5 + +.max_attack_loop + push af + call BattleCommand_AttackUp2 + pop af + dec a + jr nz, .max_attack_loop + + ld hl, BellyDrumText + jp StdBattleTextBox + +.failed + call AnimateFailedMove + jp PrintButItFailed + +; 37c55 + + +BattleCommand_PsychUp: ; 37c55 +; psychup + + ld hl, EnemyStatLevels + ld de, PlayerStatLevels + ld a, [hBattleTurn] + and a + jr z, .pointers_correct +; It's the enemy's turn, so swap the pointers. + push hl + ld h, d + ld l, e + pop de +.pointers_correct + push hl + ld b, NUM_LEVEL_STATS +; If any of the enemy's stats is modified from its base level, +; the move succeeds. Otherwise, it fails. +.loop + ld a, [hli] + cp BASE_STAT_LEVEL + jr nz, .break + dec b + jr nz, .loop + pop hl + call AnimateFailedMove + jp PrintButItFailed + +.break + pop hl + ld b, NUM_LEVEL_STATS +.loop2 + ld a, [hli] + ld [de], a + inc de + dec b + jr nz, .loop2 + ld a, [hBattleTurn] + and a + jr nz, .calc_enemy_stats + call CalcPlayerStats + jr .merge + +.calc_enemy_stats + call CalcEnemyStats +.merge + call AnimateCurrentMove + ld hl, CopiedStatsText + jp StdBattleTextBox + +; 37c95 + + +BattleCommand_MirrorCoat: ; 37c95 +; mirrorcoat + + ld a, 1 + ld [AttackMissed], a + + ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP + call GetBattleVar + and a + ret z + + ld b, a + callfar GetMoveEffect + ld a, b + cp EFFECT_MIRROR_COAT + ret z + + call BattleCommand_ResetTypeMatchup + ld a, [wTypeMatchup] + and a + ret z + + call CheckOpponentWentFirst + ret z + + ld a, BATTLE_VARS_LAST_COUNTER_MOVE_OPP + call GetBattleVar + dec a + ld de, StringBuffer1 + call GetMoveData + + ld a, [StringBuffer1 + 2] + and a + ret z + + ld a, [StringBuffer1 + 3] + cp SPECIAL + ret c + + ld hl, CurDamage + ld a, [hli] + or [hl] + ret z + + ld a, [hl] + add a + ld [hld], a + ld a, [hl] + adc a + ld [hl], a + jr nc, .capped + ld a, $ff + ld [hli], a + ld [hl], a +.capped + + xor a + ld [AttackMissed], a + ret + +; 37ce6 + + +BattleCommand_DoubleMinimizeDamage: ; 37ce6 +; doubleminimizedamage + + ld hl, wEnemyMinimized + ld a, [hBattleTurn] + and a + jr z, .ok + ld hl, wPlayerMinimized +.ok + ld a, [hl] + and a + ret z + ld hl, CurDamage + 1 + sla [hl] + dec hl + rl [hl] + ret nc + ld a, $ff + ld [hli], a + ld [hl], a + ret + +; 37d02 + + +BattleCommand_SkipSunCharge: ; 37d02 +; mimicsuncharge + ld a, [Weather] + cp WEATHER_SUN + ret nz + ld b, charge_command + jp SkipToBattleCommand + +; 37d0d + + +BattleCommand_CheckFutureSight: ; 37d0d +; checkfuturesight + + ld hl, wPlayerFutureSightCount + ld de, wPlayerFutureSightDamage + ld a, [hBattleTurn] + and a + jr z, .ok + ld hl, wEnemyFutureSightCount + ld de, wEnemyFutureSightDamage +.ok + + ld a, [hl] + and a + ret z + cp 1 + ret nz + + ld [hl], 0 + ld a, [de] + inc de + ld [CurDamage], a + ld a, [de] + ld [CurDamage + 1], a + ld b, futuresight_command + jp SkipToBattleCommand + +; 37d34 + +BattleCommand_FutureSight: ; 37d34 +; futuresight + + call CheckUserIsCharging + jr nz, .AlreadyChargingFutureSight + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + ld b, a + ld a, BATTLE_VARS_LAST_COUNTER_MOVE + call GetBattleVarAddr + ld [hl], b + ld a, BATTLE_VARS_LAST_MOVE + call GetBattleVarAddr + ld [hl], b +.AlreadyChargingFutureSight: + ld hl, wPlayerFutureSightCount + ld a, [hBattleTurn] + and a + jr z, .GotFutureSightCount + ld hl, wEnemyFutureSightCount +.GotFutureSightCount: + ld a, [hl] + and a + jr nz, .failed + ld a, 4 + ld [hl], a + call BattleCommand_LowerSub + call BattleCommand_MoveDelay + ld hl, ForesawAttackText + call StdBattleTextBox + call BattleCommand_RaiseSub + ld de, wPlayerFutureSightDamage + ld a, [hBattleTurn] + and a + jr z, .StoreDamage + ld de, wEnemyFutureSightDamage +.StoreDamage: + ld hl, CurDamage + ld a, [hl] + ld [de], a + ld [hl], 0 + inc hl + inc de + ld a, [hl] + ld [de], a + ld [hl], 0 + jp EndMoveEffect + +.failed + pop bc + call ResetDamage + call AnimateFailedMove + call PrintButItFailed + jp EndMoveEffect + +; 37d94 + + +BattleCommand_ThunderAccuracy: ; 37d94 +; thunderaccuracy + + ld a, BATTLE_VARS_MOVE_TYPE + call GetBattleVarAddr + inc hl + ld a, [Weather] + cp WEATHER_RAIN + jr z, .rain + cp WEATHER_SUN + ret nz + ld [hl], 50 percent + 1 + ret + +.rain + ; Redundant with CheckHit guranteeing hit + ld [hl], 100 percent + ret + +; 37daa + + +CheckHiddenOpponent: ; 37daa +; BUG: This routine should account for Lock-On and Mind Reader. + ld a, BATTLE_VARS_SUBSTATUS3_OPP + call GetBattleVar + and 1 << SUBSTATUS_FLYING | 1 << SUBSTATUS_UNDERGROUND + ret + +; 37db2 + + +GetUserItem: ; 37db2 +; Return the effect of the user's item in bc, and its id at hl. + ld hl, BattleMonItem + ld a, [hBattleTurn] + and a + jr z, .go + ld hl, EnemyMonItem +.go + ld b, [hl] + jp GetItemHeldEffect + +; 37dc1 + + +GetOpponentItem: ; 37dc1 +; Return the effect of the opponent's item in bc, and its id at hl. + ld hl, EnemyMonItem + ld a, [hBattleTurn] + and a + jr z, .go + ld hl, BattleMonItem +.go + ld b, [hl] + jp GetItemHeldEffect + +; 37dd0 + + +GetItemHeldEffect: ; 37dd0 +; Return the effect of item b in bc. + ld a, b + and a + ret z + + push hl + ld hl, ItemAttributes + ITEMATTR_EFFECT + dec a + ld c, a + ld b, 0 + ld a, ITEMATTR_STRUCT_LENGTH + call AddNTimes + ld a, BANK(ItemAttributes) + call GetFarHalfword + ld b, l + ld c, h + pop hl + ret + +; 37de9 + + +AnimateCurrentMoveEitherSide: ; 37de9 + push hl + push de + push bc + ld a, [wKickCounter] + push af + call BattleCommand_LowerSub + pop af + ld [wKickCounter], a + call PlayDamageAnim + call BattleCommand_RaiseSub + pop bc + pop de + pop hl + ret + +; 37e01 + + +AnimateCurrentMove: ; 37e01 + push hl + push de + push bc + ld a, [wKickCounter] + push af + call BattleCommand_LowerSub + pop af + ld [wKickCounter], a + call LoadMoveAnim + call BattleCommand_RaiseSub + pop bc + pop de + pop hl + ret + +; 37e19 + + +PlayDamageAnim: ; 37e19 + xor a + ld [FXAnimID + 1], a + + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + and a + ret z + + ld [FXAnimID], a + + ld a, [hBattleTurn] + and a + ld a, BATTLEANIM_ENEMY_DAMAGE + jr z, .player + ld a, BATTLEANIM_PLAYER_DAMAGE + +.player + ld [wNumHits], a + + jp PlayUserBattleAnim + +; 37e36 + + +LoadMoveAnim: ; 37e36 + xor a + ld [wNumHits], a + ld [FXAnimID + 1], a + + ld a, BATTLE_VARS_MOVE_ANIM + call GetBattleVar + and a + ret z + + ; fallthrough +; 37e44 + + +LoadAnim: ; 37e44 + + ld [FXAnimID], a + + ; fallthrough +; 37e47 + + +PlayUserBattleAnim: ; 37e47 + push hl + push de + push bc + callfar PlayBattleAnim + pop bc + pop de + pop hl + ret + +; 37e54 + + +PlayOpponentBattleAnim: ; 37e54 + ld a, e + ld [FXAnimID], a + ld a, d + ld [FXAnimID + 1], a + xor a + ld [wNumHits], a + + push hl + push de + push bc + call BattleCommand_SwitchTurn + + callfar PlayBattleAnim + + call BattleCommand_SwitchTurn + pop bc + pop de + pop hl + ret + +; 37e73 + + +CallBattleCore: ; 37e73 + ld a, BANK(BattleCore) + rst FarCall + ret + +; 37e77 + + +AnimateFailedMove: ; 37e77 + call BattleCommand_LowerSub + call BattleCommand_MoveDelay + jp BattleCommand_RaiseSub + +; 37e80 + + +BattleCommand_MoveDelay: ; 37e80 +; movedelay +; Wait 40 frames. + ld c, 40 + jp DelayFrames + +; 37e85 + + +BattleCommand_ClearText: ; 37e85 +; cleartext + +; Used in multi-hit moves. + ld hl, .text + jp BattleTextBox + +.text + db "@" +; 37e8c + + +SkipToBattleCommand: ; 37e8c +; Skip over commands until reaching command b. + ld a, [BattleScriptBufferAddress + 1] + ld h, a + ld a, [BattleScriptBufferAddress] + ld l, a +.loop + ld a, [hli] + cp b + jr nz, .loop + + ld a, h + ld [BattleScriptBufferAddress + 1], a + ld a, l + ld [BattleScriptBufferAddress], a + ret + +; 37ea1 + + +GetMoveAttr: ; 37ea1 +; Assuming hl = Moves + x, return attribute x of move a. + push bc + ld bc, MOVE_LENGTH + call AddNTimes + call GetMoveByte + pop bc + ret + +; 37ead + + +GetMoveData: ; 37ead +; Copy move struct a to de. + ld hl, Moves + ld bc, MOVE_LENGTH + call AddNTimes + ld a, Bank(Moves) + jp FarCopyBytes + +; 37ebb + + +GetMoveByte: ; 37ebb + ld a, BANK(Moves) + jp GetFarByte + +; 37ec0 + + +DisappearUser: ; 37ec0 + farcall _DisappearUser + ret + +; 37ec7 + + +AppearUserLowerSub: ; 37ec7 + farcall _AppearUserLowerSub + ret + +; 37ece + + +AppearUserRaiseSub: ; 37ece + farcall _AppearUserRaiseSub + ret + +; 37ed5 + + +_CheckBattleScene: ; 37ed5 +; Checks the options. Returns carry if battle animations are disabled. + push hl + push de + push bc + farcall CheckBattleScene + pop bc + pop de + pop hl + ret + +; 37ee2 -- cgit v1.2.3