diff options
author | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-06-23 17:50:50 -0400 |
---|---|---|
committer | Rangi <remy.oukaour+rangi42@gmail.com> | 2020-06-23 17:50:50 -0400 |
commit | 0a62d48df2d73c11ca13e4c3015d3d3abfa20292 (patch) | |
tree | 530f89782772bb76a76fbdd28247f1c6e3248483 /engine/battle/effects.asm | |
parent | bdeb494add78beb8d00375f352a2c49e9f7fd32b (diff) |
Move more tables from engine/ to data/
This also splits the end of engine/battle/core.asm into engine/battle/effects.asm.
Diffstat (limited to 'engine/battle/effects.asm')
-rw-r--r-- | engine/battle/effects.asm | 1493 |
1 files changed, 1493 insertions, 0 deletions
diff --git a/engine/battle/effects.asm b/engine/battle/effects.asm new file mode 100644 index 00000000..8bb22a1c --- /dev/null +++ b/engine/battle/effects.asm @@ -0,0 +1,1493 @@ +JumpMoveEffect: + call _JumpMoveEffect + ld b, $1 + ret + +_JumpMoveEffect: + ld a, [H_WHOSETURN] + and a + ld a, [wPlayerMoveEffect] + jr z, .next1 + ld a, [wEnemyMoveEffect] +.next1 + dec a ; subtract 1, there is no special effect for 00 + add a ; x2, 16bit pointers + ld hl, MoveEffectPointerTable + ld b, 0 + ld c, a + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a + jp hl ; jump to special effect handler + +INCLUDE "data/effects_pointers.asm" + +SleepEffect: + ld de, wEnemyMonStatus + ld bc, wEnemyBattleStatus2 + ld a, [H_WHOSETURN] + and a + jp z, .sleepEffect + ld de, wBattleMonStatus + ld bc, wPlayerBattleStatus2 + +.sleepEffect + ld a, [bc] + bit NEEDS_TO_RECHARGE, a ; does the target need to recharge? (hyper beam) + res NEEDS_TO_RECHARGE, a ; target no longer needs to recharge + ld [bc], a + jr nz, .setSleepCounter ; if the target had to recharge, all hit tests will be skipped + ; including the event where the target already has another status + ld a, [de] + ld b, a + and $7 + jr z, .notAlreadySleeping ; can't affect a mon that is already asleep + ld hl, AlreadyAsleepText + jp PrintText +.notAlreadySleeping + ld a, b + and a + jr nz, .didntAffect ; can't affect a mon that is already statused + push de + call MoveHitTest ; apply accuracy tests + pop de + ld a, [wMoveMissed] + and a + jr nz, .didntAffect +.setSleepCounter +; set target's sleep counter to a random number between 1 and 7 + call BattleRandom + and $7 + jr z, .setSleepCounter + ld [de], a + call PlayCurrentMoveAnimation2 + ld hl, FellAsleepText + jp PrintText +.didntAffect + jp PrintDidntAffectText + +FellAsleepText: + TX_FAR _FellAsleepText + db "@" + +AlreadyAsleepText: + TX_FAR _AlreadyAsleepText + db "@" + +PoisonEffect: + ld hl, wEnemyMonStatus + ld de, wPlayerMoveEffect + ld a, [H_WHOSETURN] + and a + jr z, .poisonEffect + ld hl, wBattleMonStatus + ld de, wEnemyMoveEffect +.poisonEffect + call CheckTargetSubstitute + jr nz, .noEffect ; can't poison a substitute target + ld a, [hli] + ld b, a + and a + jr nz, .noEffect ; miss if target is already statused + ld a, [hli] + cp POISON ; can't poison a poison-type target + jr z, .noEffect + ld a, [hld] + cp POISON ; can't poison a poison-type target + jr z, .noEffect + ld a, [de] + cp POISON_SIDE_EFFECT1 + ld b, $34 ; ~20% chance of poisoning + jr z, .sideEffectTest + cp POISON_SIDE_EFFECT2 + ld b, $67 ; ~40% chance of poisoning + jr z, .sideEffectTest + push hl + push de + call MoveHitTest ; apply accuracy tests + pop de + pop hl + ld a, [wMoveMissed] + and a + jr nz, .didntAffect + jr .inflictPoison +.sideEffectTest + call BattleRandom + cp b ; was side effect successful? + ret nc +.inflictPoison + dec hl + set 3, [hl] ; mon is now poisoned + push de + dec de + ld a, [H_WHOSETURN] + and a + ld b, ANIM_C7 + ld hl, wPlayerBattleStatus3 + ld a, [de] + ld de, wPlayerToxicCounter + jr nz, .ok + ld b, ANIM_A9 + ld hl, wEnemyBattleStatus3 + ld de, wEnemyToxicCounter +.ok + cp TOXIC + jr nz, .normalPoison ; done if move is not Toxic + set BADLY_POISONED, [hl] ; else set Toxic battstatus + xor a + ld [de], a + ld hl, BadlyPoisonedText + jr .continue +.normalPoison + ld hl, PoisonedText +.continue + pop de + ld a, [de] + cp POISON_EFFECT + jr z, .regularPoisonEffect + ld a, b + call PlayBattleAnimation2 + jp PrintText +.regularPoisonEffect + call PlayCurrentMoveAnimation2 + jp PrintText +.noEffect + ld a, [de] + cp POISON_EFFECT + ret nz +.didntAffect + ld c, 50 + call DelayFrames + jp PrintDidntAffectText + +PoisonedText: + TX_FAR _PoisonedText + db "@" + +BadlyPoisonedText: + TX_FAR _BadlyPoisonedText + db "@" + +DrainHPEffect: + jpab DrainHPEffect_ + +ExplodeEffect: + ld hl, wBattleMonHP + ld de, wPlayerBattleStatus2 + ld a, [H_WHOSETURN] + and a + jr z, .faintUser + ld hl, wEnemyMonHP + ld de, wEnemyBattleStatus2 +.faintUser + xor a + ld [hli], a ; set the mon's HP to 0 + ld [hli], a + inc hl + ld [hl], a ; set mon's status to 0 + ld a, [de] + res SEEDED, a ; clear mon's leech seed status + ld [de], a + ret + +FreezeBurnParalyzeEffect: + xor a + ld [wAnimationType], a + call CheckTargetSubstitute ; test bit 4 of d063/d068 flags [target has substitute flag] + ret nz ; return if they have a substitute, can't effect them + ld a, [H_WHOSETURN] + and a + jp nz, opponentAttacker + ld a, [wEnemyMonStatus] + and a + jp nz, CheckDefrost ; can't inflict status if opponent is already statused + ld a, [wPlayerMoveType] + ld b, a + ld a, [wEnemyMonType1] + cp b ; do target type 1 and move type match? + ret z ; return if they match (an ice move can't freeze an ice-type, body slam can't paralyze a normal-type, etc.) + ld a, [wEnemyMonType2] + cp b ; do target type 2 and move type match? + ret z ; return if they match + ld a, [wPlayerMoveEffect] + cp PARALYZE_SIDE_EFFECT1 + 1 ; 10% status effects are 04, 05, 06 so 07 will set carry for those + ld b, $1a ; 0x1A/0x100 or 26/256 = 10.2%~ chance + jr c, .next1 ; branch ahead if this is a 10% chance effect.. + ld b, $4d ; else use 0x4D/0x100 or 77/256 = 30.1%~ chance + sub $1e ; subtract $1E to map to equivalent 10% chance effects +.next1 + push af + call BattleRandom ; get random 8bit value for probability test + cp b + pop bc + ret nc ; do nothing if random value is >= 1A or 4D [no status applied] + ld a, b ; what type of effect is this? + cp BURN_SIDE_EFFECT1 + jr z, .burn + cp FREEZE_SIDE_EFFECT + jr z, .freeze +; .paralyze + ld a, 1 << PAR + ld [wEnemyMonStatus], a + call QuarterSpeedDueToParalysis ; quarter speed of affected mon + ld a, ANIM_A9 + call PlayBattleAnimation + jp PrintMayNotAttackText ; print paralysis text +.burn + ld a, 1 << BRN + ld [wEnemyMonStatus], a + call HalveAttackDueToBurn ; halve attack of affected mon + ld a, ANIM_A9 + call PlayBattleAnimation + ld hl, BurnedText + jp PrintText +.freeze + call ClearHyperBeam ; resets hyper beam (recharge) condition from target + ld a, 1 << FRZ + ld [wEnemyMonStatus], a + ld a, ANIM_A9 + call PlayBattleAnimation + ld hl, FrozenText + jp PrintText +opponentAttacker: + ld a, [wBattleMonStatus] ; mostly same as above with addresses swapped for opponent + and a + jp nz, CheckDefrost + ld a, [wEnemyMoveType] + ld b, a + ld a, [wBattleMonType1] + cp b + ret z + ld a, [wBattleMonType2] + cp b + ret z + ld a, [wEnemyMoveEffect] + cp PARALYZE_SIDE_EFFECT1 + 1 + ld b, $1a + jr c, .next1 + ld b, $4d + sub $1e +.next1 + push af + call BattleRandom + cp b + pop bc + ret nc + ld a, b + cp BURN_SIDE_EFFECT1 + jr z, .burn + cp FREEZE_SIDE_EFFECT + jr z, .freeze + ld a, 1 << PAR + ld [wBattleMonStatus], a + call QuarterSpeedDueToParalysis + jp PrintMayNotAttackText +.burn + ld a, 1 << BRN + ld [wBattleMonStatus], a + call HalveAttackDueToBurn + ld hl, BurnedText + jp PrintText +.freeze +; hyper beam bits aren't reseted for opponent's side + ld a, 1 << FRZ + ld [wBattleMonStatus], a + ld hl, FrozenText + jp PrintText + +BurnedText: + TX_FAR _BurnedText + db "@" + +FrozenText: + TX_FAR _FrozenText + db "@" + +CheckDefrost: +; any fire-type move that has a chance inflict burn (all but Fire Spin) will defrost a frozen target + and 1 << FRZ ; are they frozen? + ret z ; return if so + ld a, [H_WHOSETURN] + and a + jr nz, .opponent + ;player [attacker] + ld a, [wPlayerMoveType] + sub FIRE + ret nz ; return if type of move used isn't fire + ld [wEnemyMonStatus], a ; set opponent status to 00 ["defrost" a frozen monster] + ld hl, wEnemyMon1Status + ld a, [wEnemyMonPartyPos] + ld bc, wEnemyMon2 - wEnemyMon1 + call AddNTimes + xor a + ld [hl], a ; clear status in roster + ld hl, FireDefrostedText + jr .common +.opponent + ld a, [wEnemyMoveType] ; same as above with addresses swapped + sub FIRE + ret nz + ld [wBattleMonStatus], a + ld hl, wPartyMon1Status + ld a, [wPlayerMonNumber] + ld bc, wPartyMon2 - wPartyMon1 + call AddNTimes + xor a + ld [hl], a + ld hl, FireDefrostedText +.common + jp PrintText + +FireDefrostedText: + TX_FAR _FireDefrostedText + db "@" + +StatModifierUpEffect: + ld hl, wPlayerMonStatMods + ld de, wPlayerMoveEffect + ld a, [H_WHOSETURN] + and a + jr z, .statModifierUpEffect + ld hl, wEnemyMonStatMods + ld de, wEnemyMoveEffect +.statModifierUpEffect + ld a, [de] + sub ATTACK_UP1_EFFECT + cp EVASION_UP1_EFFECT + $3 - ATTACK_UP1_EFFECT ; covers all +1 effects + jr c, .incrementStatMod + sub ATTACK_UP2_EFFECT - ATTACK_UP1_EFFECT ; map +2 effects to equivalent +1 effect +.incrementStatMod + ld c, a + ld b, $0 + add hl, bc + ld b, [hl] + inc b ; increment corresponding stat mod + ld a, $d + cp b ; can't raise stat past +6 ($d or 13) + jp c, PrintNothingHappenedText + ld a, [de] + cp ATTACK_UP1_EFFECT + $8 ; is it a +2 effect? + jr c, .ok + inc b ; if so, increment stat mod again + ld a, $d + cp b ; unless it's already +6 + jr nc, .ok + ld b, a +.ok + ld [hl], b + ld a, c + cp $4 + jr nc, UpdateStatDone ; jump if mod affected is evasion/accuracy + push hl + ld hl, wBattleMonAttack + 1 + ld de, wPlayerMonUnmodifiedAttack + ld a, [H_WHOSETURN] + and a + jr z, .pointToStats + ld hl, wEnemyMonAttack + 1 + ld de, wEnemyMonUnmodifiedAttack +.pointToStats + push bc + sla c + ld b, $0 + add hl, bc ; hl = modified stat + ld a, c + add e + ld e, a + jr nc, .checkIf999 + inc d ; de = unmodified (original) stat +.checkIf999 + pop bc + ld a, [hld] + sub 999 % $100 ; check if stat is already 999 + jr nz, .recalculateStat + ld a, [hl] + sbc 999 / $100 + jp z, RestoreOriginalStatModifier +.recalculateStat ; recalculate affected stat + ; paralysis and burn penalties, as well as badge boosts are ignored + push hl + push bc + ld hl, StatModifierRatios + dec b + sla b + ld c, b + ld b, $0 + add hl, bc + pop bc + xor a + ld [H_MULTIPLICAND], a + ld a, [de] + ld [H_MULTIPLICAND + 1], a + inc de + ld a, [de] + ld [H_MULTIPLICAND + 2], a + ld a, [hli] + ld [H_MULTIPLIER], a + call Multiply + ld a, [hl] + ld [H_DIVISOR], a + ld b, $4 + call Divide + pop hl +; cap at 999 + ld a, [H_PRODUCT + 3] + sub 999 % $100 + ld a, [H_PRODUCT + 2] + sbc 999 / $100 + jp c, UpdateStat + ld a, 999 / $100 + ld [H_MULTIPLICAND + 1], a + ld a, 999 % $100 + ld [H_MULTIPLICAND + 2], a + +UpdateStat: + ld a, [H_PRODUCT + 2] + ld [hli], a + ld a, [H_PRODUCT + 3] + ld [hl], a + pop hl +UpdateStatDone: + ld b, c + inc b + call PrintStatText + ld hl, wPlayerBattleStatus2 + ld de, wPlayerMoveNum + ld bc, wPlayerMonMinimized + ld a, [H_WHOSETURN] + and a + jr z, .asm_3f4e6 + ld hl, wEnemyBattleStatus2 + ld de, wEnemyMoveNum + ld bc, wEnemyMonMinimized +.asm_3f4e6 + ld a, [de] + cp MINIMIZE + jr nz, .asm_3f4f9 + ; if a substitute is up, slide off the substitute and show the mon pic before + ; playing the minimize animation + bit HAS_SUBSTITUTE_UP, [hl] + push af + push bc + ld hl, HideSubstituteShowMonAnim + ld b, BANK(HideSubstituteShowMonAnim) + push de + call nz, Bankswitch + pop de +.asm_3f4f9 + call PlayCurrentMoveAnimation + ld a, [de] + cp MINIMIZE + jr nz, .applyBadgeBoostsAndStatusPenalties + pop bc + ld a, $1 + ld [bc], a + ld hl, ReshowSubstituteAnim + ld b, BANK(ReshowSubstituteAnim) + pop af + call nz, Bankswitch +.applyBadgeBoostsAndStatusPenalties + ld a, [H_WHOSETURN] + and a + call z, ApplyBadgeStatBoosts ; whenever the player uses a stat-up move, badge boosts get reapplied again to every stat, + ; even to those not affected by the stat-up move (will be boosted further) + ld hl, MonsStatsRoseText + call PrintText + +; these shouldn't be here + call QuarterSpeedDueToParalysis ; apply speed penalty to the player whose turn is not, if it's paralyzed + jp HalveAttackDueToBurn ; apply attack penalty to the player whose turn is not, if it's burned + +RestoreOriginalStatModifier: + pop hl + dec [hl] + +PrintNothingHappenedText: + ld hl, NothingHappenedText + jp PrintText + +MonsStatsRoseText: + TX_FAR _MonsStatsRoseText + TX_ASM + ld hl, GreatlyRoseText + ld a, [H_WHOSETURN] + and a + ld a, [wPlayerMoveEffect] + jr z, .playerTurn + ld a, [wEnemyMoveEffect] +.playerTurn + cp ATTACK_DOWN1_EFFECT + ret nc + ld hl, RoseText + ret + +GreatlyRoseText: + TX_DELAY + TX_FAR _GreatlyRoseText +; fallthrough +RoseText: + TX_FAR _RoseText + db "@" + +StatModifierDownEffect: + ld hl, wEnemyMonStatMods + ld de, wPlayerMoveEffect + ld bc, wEnemyBattleStatus1 + ld a, [H_WHOSETURN] + and a + jr z, .statModifierDownEffect + ld hl, wPlayerMonStatMods + ld de, wEnemyMoveEffect + ld bc, wPlayerBattleStatus1 + ld a, [wLinkState] + cp LINK_STATE_BATTLING + jr z, .statModifierDownEffect + call BattleRandom + cp $40 ; 1/4 chance to miss by in regular battle + jp c, MoveMissed +.statModifierDownEffect + call CheckTargetSubstitute ; can't hit through substitute + jp nz, MoveMissed + ld a, [de] + cp ATTACK_DOWN_SIDE_EFFECT + jr c, .nonSideEffect + call BattleRandom + cp $55 ; 85/256 chance for side effects + jp nc, CantLowerAnymore + ld a, [de] + sub ATTACK_DOWN_SIDE_EFFECT ; map each stat to 0-3 + jr .decrementStatMod +.nonSideEffect ; non-side effects only + push hl + push de + push bc + call MoveHitTest ; apply accuracy tests + pop bc + pop de + pop hl + ld a, [wMoveMissed] + and a + jp nz, MoveMissed + ld a, [bc] + bit INVULNERABLE, a ; fly/dig + jp nz, MoveMissed + ld a, [de] + sub ATTACK_DOWN1_EFFECT + cp EVASION_DOWN1_EFFECT + $3 - ATTACK_DOWN1_EFFECT ; covers all -1 effects + jr c, .decrementStatMod + sub ATTACK_DOWN2_EFFECT - ATTACK_DOWN1_EFFECT ; map -2 effects to corresponding -1 effect +.decrementStatMod + ld c, a + ld b, $0 + add hl, bc + ld b, [hl] + dec b ; dec corresponding stat mod + jp z, CantLowerAnymore ; if stat mod is 1 (-6), can't lower anymore + ld a, [de] + cp ATTACK_DOWN2_EFFECT - $16 ; $24 + jr c, .ok + cp EVASION_DOWN2_EFFECT + $5 ; $44 + jr nc, .ok + dec b ; stat down 2 effects only (dec mod again) + jr nz, .ok + inc b ; increment mod to 1 (-6) if it would become 0 (-7) +.ok + ld [hl], b ; save modified mod + ld a, c + cp $4 + jr nc, UpdateLoweredStatDone ; jump for evasion/accuracy + push hl + push de + ld hl, wEnemyMonAttack + 1 + ld de, wEnemyMonUnmodifiedAttack + ld a, [H_WHOSETURN] + and a + jr z, .pointToStat + ld hl, wBattleMonAttack + 1 + ld de, wPlayerMonUnmodifiedAttack +.pointToStat + push bc + sla c + ld b, $0 + add hl, bc ; hl = modified stat + ld a, c + add e + ld e, a + jr nc, .noCarry + inc d ; de = unmodified stat +.noCarry + pop bc + ld a, [hld] + sub $1 ; can't lower stat below 1 (-6) + jr nz, .recalculateStat + ld a, [hl] + and a + jp z, CantLowerAnymore_Pop +.recalculateStat +; recalculate affected stat +; paralysis and burn penalties, as well as badge boosts are ignored + push hl + push bc + ld hl, StatModifierRatios + dec b + sla b + ld c, b + ld b, $0 + add hl, bc + pop bc + xor a + ld [H_MULTIPLICAND], a + ld a, [de] + ld [H_MULTIPLICAND + 1], a + inc de + ld a, [de] + ld [H_MULTIPLICAND + 2], a + ld a, [hli] + ld [H_MULTIPLIER], a + call Multiply + ld a, [hl] + ld [H_DIVISOR], a + ld b, $4 + call Divide + pop hl + ld a, [H_PRODUCT + 3] + ld b, a + ld a, [H_PRODUCT + 2] + or b + jp nz, UpdateLoweredStat + ld [H_MULTIPLICAND + 1], a + ld a, $1 + ld [H_MULTIPLICAND + 2], a + +UpdateLoweredStat: + ld a, [H_PRODUCT + 2] + ld [hli], a + ld a, [H_PRODUCT + 3] + ld [hl], a + pop de + pop hl +UpdateLoweredStatDone: + ld b, c + inc b + push de + call PrintStatText + pop de + ld a, [de] + cp $44 + jr nc, .ApplyBadgeBoostsAndStatusPenalties + call PlayCurrentMoveAnimation2 +.ApplyBadgeBoostsAndStatusPenalties + ld a, [H_WHOSETURN] + and a + call nz, ApplyBadgeStatBoosts ; whenever the player uses a stat-down move, badge boosts get reapplied again to every stat, + ; even to those not affected by the stat-up move (will be boosted further) + ld hl, MonsStatsFellText + call PrintText + +; These where probably added given that a stat-down move affecting speed or attack will override +; the stat penalties from paralysis and burn respectively. +; But they are always called regardless of the stat affected by the stat-down move. + call QuarterSpeedDueToParalysis + jp HalveAttackDueToBurn + +CantLowerAnymore_Pop: + pop de + pop hl + inc [hl] + +CantLowerAnymore: + ld a, [de] + cp ATTACK_DOWN_SIDE_EFFECT + ret nc + ld hl, NothingHappenedText + jp PrintText + +MoveMissed: + ld a, [de] + cp $44 + ret nc + jp ConditionalPrintButItFailed + +MonsStatsFellText: + TX_FAR _MonsStatsFellText + TX_ASM + ld hl, FellText + ld a, [H_WHOSETURN] + and a + ld a, [wPlayerMoveEffect] + jr z, .playerTurn + ld a, [wEnemyMoveEffect] +.playerTurn +; check if the move's effect decreases a stat by 2 + cp BIDE_EFFECT + ret c + cp ATTACK_DOWN_SIDE_EFFECT + ret nc + ld hl, GreatlyFellText + ret + +GreatlyFellText: + TX_DELAY + TX_FAR _GreatlyFellText +; fallthrough +FellText: + TX_FAR _FellText + db "@" + +PrintStatText: + ld hl, StatsTextStrings + ld c, "@" +.findStatName_outer + dec b + jr z, .foundStatName +.findStatName_inner + ld a, [hli] + cp c + jr z, .findStatName_outer + jr .findStatName_inner +.foundStatName + ld de, wcf4b + ld bc, $a + jp CopyData + +INCLUDE "text/stat_names.asm" + +INCLUDE "data/battle/stat_modifiers.asm" + +BideEffect: + ld hl, wPlayerBattleStatus1 + ld de, wPlayerBideAccumulatedDamage + ld bc, wPlayerNumAttacksLeft + ld a, [H_WHOSETURN] + and a + jr z, .bideEffect + ld hl, wEnemyBattleStatus1 + ld de, wEnemyBideAccumulatedDamage + ld bc, wEnemyNumAttacksLeft +.bideEffect + set STORING_ENERGY, [hl] ; mon is now using bide + xor a + ld [de], a + inc de + ld [de], a + ld [wPlayerMoveEffect], a + ld [wEnemyMoveEffect], a + call BattleRandom + and $1 + inc a + inc a + ld [bc], a ; set Bide counter to 2 or 3 at random + ld a, [H_WHOSETURN] + add XSTATITEM_ANIM + jp PlayBattleAnimation2 + +ThrashPetalDanceEffect: + ld hl, wPlayerBattleStatus1 + ld de, wPlayerNumAttacksLeft + ld a, [H_WHOSETURN] + and a + jr z, .thrashPetalDanceEffect + ld hl, wEnemyBattleStatus1 + ld de, wEnemyNumAttacksLeft +.thrashPetalDanceEffect + set THRASHING_ABOUT, [hl] ; mon is now using thrash/petal dance + call BattleRandom + and $1 + inc a + inc a + ld [de], a ; set thrash/petal dance counter to 2 or 3 at random + ld a, [H_WHOSETURN] + add ANIM_B0 + jp PlayBattleAnimation2 + +SwitchAndTeleportEffect: + ld a, [H_WHOSETURN] + and a + jr nz, .handleEnemy + ld a, [wIsInBattle] + dec a + jr nz, .notWildBattle1 + ld a, [wCurEnemyLVL] + ld b, a + ld a, [wBattleMonLevel] + cp b ; is the player's level greater than the enemy's level? + jr nc, .playerMoveWasSuccessful ; if so, teleport will always succeed + add b + ld c, a + inc c ; c = sum of player level and enemy level +.rejectionSampleLoop1 + call BattleRandom + cp c ; get a random number between 0 and c + jr nc, .rejectionSampleLoop1 + srl b + srl b ; b = enemyLevel / 4 + cp b ; is rand[0, playerLevel + enemyLevel) >= (enemyLevel / 4)? + jr nc, .playerMoveWasSuccessful ; if so, allow teleporting + ld c, 50 + call DelayFrames + ld a, [wPlayerMoveNum] + cp TELEPORT + jp nz, PrintDidntAffectText + jp PrintButItFailedText_ +.playerMoveWasSuccessful + call ReadPlayerMonCurHPAndStatus + xor a + ld [wAnimationType], a + inc a + ld [wEscapedFromBattle], a + ld a, [wPlayerMoveNum] + jr .playAnimAndPrintText +.notWildBattle1 + ld c, 50 + call DelayFrames + ld hl, IsUnaffectedText + ld a, [wPlayerMoveNum] + cp TELEPORT + jp nz, PrintText + jp PrintButItFailedText_ +.handleEnemy + ld a, [wIsInBattle] + dec a + jr nz, .notWildBattle2 + ld a, [wBattleMonLevel] + ld b, a + ld a, [wCurEnemyLVL] + cp b + jr nc, .enemyMoveWasSuccessful + add b + ld c, a + inc c +.rejectionSampleLoop2 + call BattleRandom + cp c + jr nc, .rejectionSampleLoop2 + srl b + srl b + cp b + jr nc, .enemyMoveWasSuccessful + ld c, 50 + call DelayFrames + ld a, [wEnemyMoveNum] + cp TELEPORT + jp nz, PrintDidntAffectText + jp PrintButItFailedText_ +.enemyMoveWasSuccessful + call ReadPlayerMonCurHPAndStatus + xor a + ld [wAnimationType], a + inc a + ld [wEscapedFromBattle], a + ld a, [wEnemyMoveNum] + jr .playAnimAndPrintText +.notWildBattle2 + ld c, 50 + call DelayFrames + ld hl, IsUnaffectedText + ld a, [wEnemyMoveNum] + cp TELEPORT + jp nz, PrintText + jp ConditionalPrintButItFailed +.playAnimAndPrintText + push af + call PlayBattleAnimation + ld c, 20 + call DelayFrames + pop af + ld hl, RanFromBattleText + cp TELEPORT + jr z, .printText + ld hl, RanAwayScaredText + cp ROAR + jr z, .printText + ld hl, WasBlownAwayText +.printText + jp PrintText + +RanFromBattleText: + TX_FAR _RanFromBattleText + db "@" + +RanAwayScaredText: + TX_FAR _RanAwayScaredText + db "@" + +WasBlownAwayText: + TX_FAR _WasBlownAwayText + db "@" + +TwoToFiveAttacksEffect: + ld hl, wPlayerBattleStatus1 + ld de, wPlayerNumAttacksLeft + ld bc, wPlayerNumHits + ld a, [H_WHOSETURN] + and a + jr z, .twoToFiveAttacksEffect + ld hl, wEnemyBattleStatus1 + ld de, wEnemyNumAttacksLeft + ld bc, wEnemyNumHits +.twoToFiveAttacksEffect + bit ATTACKING_MULTIPLE_TIMES, [hl] ; is mon attacking multiple times? + ret nz + set ATTACKING_MULTIPLE_TIMES, [hl] ; mon is now attacking multiple times + ld hl, wPlayerMoveEffect + ld a, [H_WHOSETURN] + and a + jr z, .setNumberOfHits + ld hl, wEnemyMoveEffect +.setNumberOfHits + ld a, [hl] + cp TWINEEDLE_EFFECT + jr z, .twineedle + cp ATTACK_TWICE_EFFECT + ld a, $2 ; number of hits it's always 2 for ATTACK_TWICE_EFFECT + jr z, .saveNumberOfHits +; for TWO_TO_FIVE_ATTACKS_EFFECT 3/8 chance for 2 and 3 hits, and 1/8 chance for 4 and 5 hits + call BattleRandom + and $3 + cp $2 + jr c, .gotNumHits +; if the number of hits was greater than 2, re-roll again for a lower chance + call BattleRandom + and $3 +.gotNumHits + inc a + inc a +.saveNumberOfHits + ld [de], a + ld [bc], a + ret +.twineedle + ld a, POISON_SIDE_EFFECT1 + ld [hl], a ; set Twineedle's effect to poison effect + jr .saveNumberOfHits + +FlinchSideEffect: + call CheckTargetSubstitute + ret nz + ld hl, wEnemyBattleStatus1 + ld de, wPlayerMoveEffect + ld a, [H_WHOSETURN] + and a + jr z, .flinchSideEffect + ld hl, wPlayerBattleStatus1 + ld de, wEnemyMoveEffect +.flinchSideEffect + ld a, [de] + cp FLINCH_SIDE_EFFECT1 + ld b, $1a ; ~10% chance of flinch + jr z, .gotEffectChance + ld b, $4d ; ~30% chance of flinch +.gotEffectChance + call BattleRandom + cp b + ret nc + set FLINCHED, [hl] ; set mon's status to flinching + call ClearHyperBeam + ret + +OneHitKOEffect: + jpab OneHitKOEffect_ + +ChargeEffect: + ld hl, wPlayerBattleStatus1 + ld de, wPlayerMoveEffect + ld a, [H_WHOSETURN] + and a + ld b, XSTATITEM_ANIM + jr z, .chargeEffect + ld hl, wEnemyBattleStatus1 + ld de, wEnemyMoveEffect + ld b, ANIM_AF +.chargeEffect + set CHARGING_UP, [hl] + ld a, [de] + dec de ; de contains enemy or player MOVENUM + cp FLY_EFFECT + jr nz, .notFly + set INVULNERABLE, [hl] ; mon is now invulnerable to typical attacks (fly/dig) + ld b, TELEPORT ; load Teleport's animation +.notFly + ld a, [de] + cp DIG + jr nz, .notDigOrFly + set INVULNERABLE, [hl] ; mon is now invulnerable to typical attacks (fly/dig) + ld b, ANIM_C0 +.notDigOrFly + xor a + ld [wAnimationType], a + ld a, b + call PlayBattleAnimation + ld a, [de] + ld [wChargeMoveNum], a + ld hl, ChargeMoveEffectText + jp PrintText + +ChargeMoveEffectText: + TX_FAR _ChargeMoveEffectText + TX_ASM + ld a, [wChargeMoveNum] + cp RAZOR_WIND + ld hl, MadeWhirlwindText + jr z, .gotText + cp SOLARBEAM + ld hl, TookInSunlightText + jr z, .gotText + cp SKULL_BASH + ld hl, LoweredItsHeadText + jr z, .gotText + cp SKY_ATTACK + ld hl, SkyAttackGlowingText + jr z, .gotText + cp FLY + ld hl, FlewUpHighText + jr z, .gotText + cp DIG + ld hl, DugAHoleText +.gotText + ret + +MadeWhirlwindText: + TX_FAR _MadeWhirlwindText + db "@" + +TookInSunlightText: + TX_FAR _TookInSunlightText + db "@" + +LoweredItsHeadText: + TX_FAR _LoweredItsHeadText + db "@" + +SkyAttackGlowingText: + TX_FAR _SkyAttackGlowingText + db "@" + +FlewUpHighText: + TX_FAR _FlewUpHighText + db "@" + +DugAHoleText: + TX_FAR _DugAHoleText + db "@" + +TrappingEffect: + ld hl, wPlayerBattleStatus1 + ld de, wPlayerNumAttacksLeft + ld a, [H_WHOSETURN] + and a + jr z, .trappingEffect + ld hl, wEnemyBattleStatus1 + ld de, wEnemyNumAttacksLeft +.trappingEffect + bit USING_TRAPPING_MOVE, [hl] + ret nz + call ClearHyperBeam ; since this effect is called before testing whether the move will hit, + ; the target won't need to recharge even if the trapping move missed + set USING_TRAPPING_MOVE, [hl] ; mon is now using a trapping move + call BattleRandom ; 3/8 chance for 2 and 3 attacks, and 1/8 chance for 4 and 5 attacks + and $3 + cp $2 + jr c, .setTrappingCounter + call BattleRandom + and $3 +.setTrappingCounter + inc a + ld [de], a + ret + +MistEffect: + jpab MistEffect_ + +FocusEnergyEffect: + jpab FocusEnergyEffect_ + +RecoilEffect: + jpab RecoilEffect_ + +ConfusionSideEffect: + call BattleRandom + cp $19 ; ~10% chance + ret nc + jr ConfusionSideEffectSuccess + +ConfusionEffect: + call CheckTargetSubstitute + jr nz, ConfusionEffectFailed + call MoveHitTest + ld a, [wMoveMissed] + and a + jr nz, ConfusionEffectFailed + +ConfusionSideEffectSuccess: + ld a, [H_WHOSETURN] + and a + ld hl, wEnemyBattleStatus1 + ld bc, wEnemyConfusedCounter + ld a, [wPlayerMoveEffect] + jr z, .confuseTarget + ld hl, wPlayerBattleStatus1 + ld bc, wPlayerConfusedCounter + ld a, [wEnemyMoveEffect] +.confuseTarget + bit CONFUSED, [hl] ; is mon confused? + jr nz, ConfusionEffectFailed + set CONFUSED, [hl] ; mon is now confused + push af + call BattleRandom + and $3 + inc a + inc a + ld [bc], a ; confusion status will last 2-5 turns + pop af + cp CONFUSION_SIDE_EFFECT + call nz, PlayCurrentMoveAnimation2 + ld hl, BecameConfusedText + jp PrintText + +BecameConfusedText: + TX_FAR _BecameConfusedText + db "@" + +ConfusionEffectFailed: + cp CONFUSION_SIDE_EFFECT + ret z + ld c, 50 + call DelayFrames + jp ConditionalPrintButItFailed + +ParalyzeEffect: + jpab ParalyzeEffect_ + +SubstituteEffect: + jpab SubstituteEffect_ + +HyperBeamEffect: + ld hl, wPlayerBattleStatus2 + ld a, [H_WHOSETURN] + and a + jr z, .hyperBeamEffect + ld hl, wEnemyBattleStatus2 +.hyperBeamEffect + set NEEDS_TO_RECHARGE, [hl] ; mon now needs to recharge + ret + +ClearHyperBeam: + push hl + ld hl, wEnemyBattleStatus2 + ld a, [H_WHOSETURN] + and a + jr z, .playerTurn + ld hl, wPlayerBattleStatus2 +.playerTurn + res NEEDS_TO_RECHARGE, [hl] ; mon no longer needs to recharge + pop hl + ret + +RageEffect: + ld hl, wPlayerBattleStatus2 + ld a, [H_WHOSETURN] + and a + jr z, .player + ld hl, wEnemyBattleStatus2 +.player + set USING_RAGE, [hl] ; mon is now in "rage" mode + ret + +MimicEffect: + ld c, 50 + call DelayFrames + call MoveHitTest + ld a, [wMoveMissed] + and a + jr nz, .mimicMissed + ld a, [H_WHOSETURN] + and a + ld hl, wBattleMonMoves + ld a, [wPlayerBattleStatus1] + jr nz, .enemyTurn + ld a, [wLinkState] + cp LINK_STATE_BATTLING + jr nz, .letPlayerChooseMove + ld hl, wEnemyMonMoves + ld a, [wEnemyBattleStatus1] +.enemyTurn + bit INVULNERABLE, a + jr nz, .mimicMissed +.getRandomMove + push hl + call BattleRandom + and $3 + ld c, a + ld b, $0 + add hl, bc + ld a, [hl] + pop hl + and a + jr z, .getRandomMove + ld d, a + ld a, [H_WHOSETURN] + and a + ld hl, wBattleMonMoves + ld a, [wPlayerMoveListIndex] + jr z, .playerTurn + ld hl, wEnemyMonMoves + ld a, [wEnemyMoveListIndex] + jr .playerTurn +.letPlayerChooseMove + ld a, [wEnemyBattleStatus1] + bit INVULNERABLE, a + jr nz, .mimicMissed + ld a, [wCurrentMenuItem] + push af + ld a, $1 + ld [wMoveMenuType], a + call MoveSelectionMenu + call LoadScreenTilesFromBuffer1 + ld hl, wEnemyMonMoves + ld a, [wCurrentMenuItem] + ld c, a + ld b, $0 + add hl, bc + ld d, [hl] + pop af + ld hl, wBattleMonMoves +.playerTurn + ld c, a + ld b, $0 + add hl, bc + ld a, d + ld [hl], a + ld [wd11e], a + call GetMoveName + call PlayCurrentMoveAnimation + ld hl, MimicLearnedMoveText + jp PrintText +.mimicMissed + jp PrintButItFailedText_ + +MimicLearnedMoveText: + TX_FAR _MimicLearnedMoveText + db "@" + +LeechSeedEffect: + jpab LeechSeedEffect_ + +SplashEffect: + call PlayCurrentMoveAnimation + jp PrintNoEffectText + +DisableEffect: + call MoveHitTest + ld a, [wMoveMissed] + and a + jr nz, .moveMissed + ld de, wEnemyDisabledMove + ld hl, wEnemyMonMoves + ld a, [H_WHOSETURN] + and a + jr z, .disableEffect + ld de, wPlayerDisabledMove + ld hl, wBattleMonMoves +.disableEffect +; no effect if target already has a move disabled + ld a, [de] + and a + jr nz, .moveMissed +.pickMoveToDisable + push hl + call BattleRandom + and $3 + ld c, a + ld b, $0 + add hl, bc + ld a, [hl] + pop hl + and a + jr z, .pickMoveToDisable ; loop until a non-00 move slot is found + ld [wd11e], a ; store move number + push hl + ld a, [H_WHOSETURN] + and a + ld hl, wBattleMonPP + jr nz, .enemyTurn + ld a, [wLinkState] + cp LINK_STATE_BATTLING + pop hl ; wEnemyMonMoves + jr nz, .playerTurnNotLinkBattle +; .playerTurnLinkBattle + push hl + ld hl, wEnemyMonPP +.enemyTurn + push hl + ld a, [hli] + or [hl] + inc hl + or [hl] + inc hl + or [hl] + and $3f + pop hl ; wBattleMonPP or wEnemyMonPP + jr z, .moveMissedPopHL ; nothing to do if all moves have no PP left + add hl, bc + ld a, [hl] + pop hl + and a + jr z, .pickMoveToDisable ; pick another move if this one had 0 PP +.playerTurnNotLinkBattle +; non-link battle enemies have unlimited PP so the previous checks aren't needed + call BattleRandom + and $7 + inc a ; 1-8 turns disabled + inc c ; move 1-4 will be disabled + swap c + add c ; map disabled move to high nibble of wEnemyDisabledMove / wPlayerDisabledMove + ld [de], a + call PlayCurrentMoveAnimation2 + ld hl, wPlayerDisabledMoveNumber + ld a, [H_WHOSETURN] + and a + jr nz, .printDisableText + inc hl ; wEnemyDisabledMoveNumber +.printDisableText + ld a, [wd11e] ; move number + ld [hl], a + call GetMoveName + ld hl, MoveWasDisabledText + jp PrintText +.moveMissedPopHL + pop hl +.moveMissed + jp PrintButItFailedText_ + +MoveWasDisabledText: + TX_FAR _MoveWasDisabledText + db "@" + +PayDayEffect: + jpab PayDayEffect_ + +ConversionEffect: + jpab ConversionEffect_ + +HazeEffect: + jpab HazeEffect_ + +HealEffect: + jpab HealEffect_ + +TransformEffect: + jpab TransformEffect_ + +ReflectLightScreenEffect: + jpab ReflectLightScreenEffect_ + +NothingHappenedText: + TX_FAR _NothingHappenedText + db "@" + +PrintNoEffectText: + ld hl, NoEffectText + jp PrintText + +NoEffectText: + TX_FAR _NoEffectText + db "@" + +ConditionalPrintButItFailed: + ld a, [wMoveDidntMiss] + and a + ret nz ; return if the side effect failed, yet the attack was successful + +PrintButItFailedText_: + ld hl, ButItFailedText + jp PrintText + +ButItFailedText: + TX_FAR _ButItFailedText + db "@" + +PrintDidntAffectText: + ld hl, DidntAffectText + jp PrintText + +DidntAffectText: + TX_FAR _DidntAffectText + db "@" + +IsUnaffectedText: + TX_FAR _IsUnaffectedText + db "@" + +PrintMayNotAttackText: + ld hl, ParalyzedMayNotAttackText + jp PrintText + +ParalyzedMayNotAttackText: + TX_FAR _ParalyzedMayNotAttackText + db "@" + +CheckTargetSubstitute: + push hl + ld hl, wEnemyBattleStatus2 + ld a, [H_WHOSETURN] + and a + jr z, .next1 + ld hl, wPlayerBattleStatus2 +.next1 + bit HAS_SUBSTITUTE_UP, [hl] + pop hl + ret + +PlayCurrentMoveAnimation2: +; animation at MOVENUM will be played unless MOVENUM is 0 +; plays wAnimationType 3 or 6 + ld a, [H_WHOSETURN] + and a + ld a, [wPlayerMoveNum] + jr z, .notEnemyTurn + ld a, [wEnemyMoveNum] +.notEnemyTurn + and a + ret z + +PlayBattleAnimation2: +; play animation ID at a and animation type 6 or 3 + ld [wAnimationID], a + ld a, [H_WHOSETURN] + and a + ld a, $6 + jr z, .storeAnimationType + ld a, $3 +.storeAnimationType + ld [wAnimationType], a + jp PlayBattleAnimationGotID + +PlayCurrentMoveAnimation: +; animation at MOVENUM will be played unless MOVENUM is 0 +; resets wAnimationType + xor a + ld [wAnimationType], a + ld a, [H_WHOSETURN] + and a + ld a, [wPlayerMoveNum] + jr z, .notEnemyTurn + ld a, [wEnemyMoveNum] +.notEnemyTurn + and a + ret z + +PlayBattleAnimation: +; play animation ID at a and predefined animation type + ld [wAnimationID], a + +PlayBattleAnimationGotID: +; play animation at wAnimationID + push hl + push de + push bc + predef MoveAnimation + pop bc + pop de + pop hl + ret |