Check base power before skipping damage calculation - pret/pokered GitHub Wiki

DISCLAIMER: As with any changes to the battle engine, one should always make sure to test and check for bugs; especially if these changes will conflict with preexisting changes that you have already made. Please make a note in this tutorial or let me know on Discord (@Xillicis) if you discover any issues or realize a more efficient implementation.

WHY? The main motivation for this tutorial is to make it easier to implement new moves such as Mud Slap, Fire Lash, Chilling Water, Low Sweep, Mud Shot, etc...

In particular, this applies to any move with a move effect belonging to the list ResidualEffects2 which is found in data/battle/residual_effects_2.asm. The ResidualEffects2 include all stat up and stat down moves along with BIDE_EFFECT and SLEEP_EFFECT which correspond to the moves BIDE and SPORE, respectively. Currently, the changes outlined in this tutorial will not cover damaging moves that have a 100% chance of causing a status effect on the opponent; e.g. Zap Cannon.

Let's consider an example move: Mud Slap. The base power is 20 and the move always lowers the targets accuracy (assuming the move didn't miss). In generation 1, moves that always lower a stat like Sand Attack or Tail Whip will automatically skip the damage calculation. In particular, any move with the move effect belonging to the list residual_effects_2.asm will skip the damage calculation. Therefore, if we try to implement Mud Slap following this tutorial Add a New Move and use the ACCURACY_DOWN1_EFFECT as the move effect, then the move will never deal damage.

We solve this issue by checking that the base power is not zero. Open up engine/battle/core.asm and make the following change to the subroutine MirrorMoveCheck:,

...
.next
	ld a, [wPlayerMoveEffect]
	ld hl, ResidualEffects2
	ld de, 1
	call IsInArray
-	jp c, JumpMoveEffect ; done here after executing effects of ResidualEffects2
+	jr nc, .notResidual2Effect
+	ld a, [wPlayerMovePower]
+	and a ; check if zero base power
+	jp z, JumpMoveEffect
+.notResidual2Effect
	ld a, [wMoveMissed]
	and a
	jr z, .moveDidNotMiss
	call PrintMoveFailureText
	ld a, [wPlayerMoveEffect]
	cp EXPLODE_EFFECT ; even if Explosion or Selfdestruct missed, its effect still needs to be activated
	jr z, .notDone
	jp ExecutePlayerMoveDone ; otherwise, we're done if the move missed
.moveDidNotMiss
	call ApplyAttackToEnemyPokemon
...

Notice that old code checks if the move effect belongs to the list ResidualEffects2 and if so it jumps to the move effect which effectively leaves the current subroutine hence skipping the ApplyAttackToEnemyPokemon. (Note: IsInArray sets the carry flag c if the value stored in register a belongs to the array pointed to by hl.) So instead, we change the code to check if the move effect is in ResidualEffects2 and check if the base power is zero before jumping to the move effect.

This code is only for the players move, so we also need to make a similar change for the enemy's move. Head down a bit farther to the subroutine EnemyCheckIfMirrorMoveEffect: make the following change,

...
	ld hl, ResidualEffects2
	ld de, $1
	call IsInArray
-	jp c, JumpMoveEffect
+	jr nc, .notResidual2EffectEnemy
+	ld a, [wEnemyMovePower]
+	and a ; Check if zero base power
+	jp z, JumpMoveEffect
+.notResidual2EffectEnemy
	ld a, [wMoveMissed]
	and a
	jr z, .moveDidNotMiss
...

Damage will now be applied properly; however, if you try the move out, you'll find that the animation will play twice. Once for the damage and once for the stat reduction. There are two corrections that have to be made for this. The first, is if the move increased the user's stat, e.g. Flame Charge. The second is if the move lowered the opponent's stat, e.g. Mud Slap. To correct this, open up engine/battle/effects.asm and modify the subroutine UpdateStat: with the following changes,

...
        call nz, Bankswitch
        pop de
.notMinimize
+       ldh a, [hWhoseTurn]
+       and a
+       ld a, [wPlayerMovePower]
+       jr z, .gotUsersPower1
+       ld a, [wEnemyMovePower]
+.gotUsersPower1
+       and a ; Skip animation if damage dealing move
+       jr nz, .skipAnimation
        call PlayCurrentMoveAnimation
+.skipAnimation
        ld a, [de]
        cp MINIMIZE
        jr nz, .applyBadgeBoostsAndStatusPenalties
...

Go down a bit further and make the following changes to the subroutine UpdateLoweredStatDone:,

...
        ld a, [de]
        cp $44
        jr nc, .ApplyBadgeBoostsAndStatusPenalties
+       ldh a, [hWhoseTurn] ; check who is using the move
+       and a
+	ld a, [wPlayerMovePower]
+       jr z, .gotUsersPower2
+       ld a, [wEnemyMovePower]
+.gotUsersPower2
+	and a ; Skip animation if damage dealing move
+	jr nz, .ApplyBadgeBoostsAndStatusPenalties
        call PlayCurrentMoveAnimation2
.ApplyBadgeBoostsAndStatusPenalties
...

That's it. Note that these changes should work for any of the stat up or down effects listed in data/battle/residual_effects_2.asm