diff --git a/docs/bugs_and_glitches.md b/docs/bugs_and_glitches.md index 4fc438b65..bf3a89068 100644 --- a/docs/bugs_and_glitches.md +++ b/docs/bugs_and_glitches.md @@ -35,9 +35,10 @@ Fixes in the [multi-player battle engine](#multi-player-battle-engine) category - [Beat Up may trigger King's Rock even if it failed](#beat-up-may-trigger-kings-rock-even-if-it-failed) - [Present damage is incorrect in link battles](#present-damage-is-incorrect-in-link-battles) - [Dragon Scale, not Dragon Fang, boosts Dragon-type moves](#dragon-scale-not-dragon-fang-boosts-dragon-type-moves) + - [Switching out or switching against a Pokémon with max HP below 4 freezes the game](#switching-out-or-switching-against-a-pokémon-with-max-HP-below-4-freezes-the-game) + - [Moves that do damage and increase your stats do not increase stats after a KO](#moves-that-do-damage-and-increase-your-stats-do-not-increase-stats-after-a-ko) - [HP bar animation is slow for high HP](#hp-bar-animation-is-slow-for-high-hp) - [HP bar animation off-by-one error for low HP](#hp-bar-animation-off-by-one-error-for-low-hp) - - [Moves that do damage and increase your stats do not increase stats after a KO](#moves-that-do-damage-and-increase-your-stats-do-not-increase-stats-after-a-ko) - [Single-player battle engine](#single-player-battle-engine) - [A Transformed Pokémon can use Sketch and learn otherwise unobtainable moves](#a-transformed-pokémon-can-use-sketch-and-learn-otherwise-unobtainable-moves) - [Catching a Transformed Pokémon always catches a Ditto](#catching-a-transformed-pokémon-always-catches-a-ditto) @@ -714,6 +715,143 @@ This bug existed for all battles in Gold and Silver, and was only fixed for sing ``` +### Switching out or switching against a Pokémon with max HP below 4 freezes the game + +This happens because switching involves calculating a percentage of maximum enemy HP. Directly calculating *HP* × 100 / *max HP* would require a two-byte denominator, so instead the game calculates *HP* × 25 / (*max HP* / 4), since even a maximum HP of 999 divided by 4 is 249, which fits in one byte. However, if the maximum HP is below 4 this will divide by 0, which enters an infinite loop in `_Divide`. + +**Fix:** First, edit `SendOutMonText` in [engine/battle/core.asm](https://github.com/pret/pokecrystal/blob/master/engine/battle/core.asm): + +```diff + ; compute enemy health remaining as a percentage + xor a + ldh [hMultiplicand + 0], a + ld hl, wEnemyMonHP + ld a, [hli] + ld [wEnemyHPAtTimeOfPlayerSwitch], a + ldh [hMultiplicand + 1], a + ld a, [hl] + ld [wEnemyHPAtTimeOfPlayerSwitch + 1], a + ldh [hMultiplicand + 2], a +- ld a, 25 +- ldh [hMultiplier], a +- call Multiply + ld hl, wEnemyMonMaxHP + ld a, [hli] + ld b, [hl] +- srl a +- rr b +- srl a +- rr b ++ ld c, 100 ++ and a ++ jr z, .shift_done ++.shift ++ rra ++ rr b ++ srl c ++ and a ++ jr nz, .shift ++.shift_done ++ ld a, c ++ ldh [hMultiplier], a ++ call Multiply + ld a, b + ld b, 4 + ldh [hDivisor], a + call Divide +``` + +Then edit `WithdrawMonText` in the same file: + +```diff + ; compute enemy health lost as a percentage + ld hl, wEnemyMonHP + 1 + ld de, wEnemyHPAtTimeOfPlayerSwitch + 1 + ld b, [hl] + dec hl + ld a, [de] + sub b + ldh [hMultiplicand + 2], a + dec de + ld b, [hl] + ld a, [de] + sbc b + ldh [hMultiplicand + 1], a +- ld a, 25 +- ldh [hMultiplier], a +- call Multiply + ld hl, wEnemyMonMaxHP + ld a, [hli] + ld b, [hl] +- srl a +- rr b +- srl a +- rr b ++ ld c, 100 ++ and a ++ jr z, .shift_done ++.shift ++ rra ++ rr b ++ srl c ++ and a ++ jr nz, .shift ++.shift_done ++ ld a, c ++ ldh [hMultiplier], a ++ call Multiply + ld a, b + ld b, 4 + ldh [hDivisor], a + call Divide +``` + +This changes both calculations to *HP* × (100 / *N*) / (*max HP* / *N*) for the smallest necessary *N*, which will be at least 1, so it avoids dividing by zero and is also more accurate. + + +### Moves that do damage and increase your stats do not increase stats after a KO + +`BattleCommand_CheckFaint` "ends the move effect if the opponent faints", and these moves attempt to raise the user's stats *after* `checkfaint`. Note that fixing this can lead to stats being increased at the end of battle, but will not have any negative effects. + +**Fix:** Edit [data/moves/effects.asm](https://github.com/pret/pokecrystal/blob/master/data/moves/effects.asm): + +```diff + DefenseUpHit: + ... + criticaltext + supereffectivetext ++ defenseup ++ statupmessage + checkfaint + buildopponentrage +- defenseup +- statupmessage + endmove + + AttackUpHit: + ... + criticaltext + supereffectivetext ++ attackup ++ statupmessage + checkfaint + buildopponentrage +- attackup +- statupmessage + endmove + + AllUpHit: + ... + criticaltext + supereffectivetext ++ allstatsup + checkfaint + buildopponentrage +- allstatsup + endmove +``` + + ### HP bar animation is slow for high HP *Fixing this cosmetic bug will* not *break link battle compatibility.* @@ -770,48 +908,6 @@ This bug existed for all battles in Gold and Silver, and was only fixed for sing jr .loop ``` -### Moves that do damage and increase your stats do not increase stats after a KO - -`BattleCommand_CheckFaint` "ends the move effect if the opponent faints", and these moves attempt to raise the user's stats *after* `checkfaint`. Note that fixing this can lead to stats being increased at the end of battle, but will not have any negative effects. - -**Fix:** Edit [data/moves/effects.asm](https://github.com/pret/pokecrystal/blob/master/data/moves/effects.asm): - -```diff - DefenseUpHit: - ... - criticaltext - supereffectivetext -+ defenseup -+ statupmessage - checkfaint - buildopponentrage -- defenseup -- statupmessage - endmove - - AttackUpHit: - ... - criticaltext - supereffectivetext -+ attackup -+ statupmessage - checkfaint - buildopponentrage -- attackup -- statupmessage - endmove - - AllUpHit: - ... - criticaltext - supereffectivetext -+ allstatsup - checkfaint - buildopponentrage -- allstatsup - endmove -``` - ## Single-player battle engine