Fix Type‐Based Status Immunities - pret/pokered GitHub Wiki
Introduction
Did you know that in Gen1, Fire-types aren't actually immune to the Burn status? Similarly, Ice-types aren't technically immune to Freeze, either! The way these status immunities are handled is a bit quirky:
- The game checks the type of the move being used against the type of its target
- If the types match, any status-based secondary effect will never activate on that target
This works decently well in the vanilla games. After all, the only moves that can cause burn are Fire-type moves, and the only ones that can Freeze are Ice-type. Hence, Fire-type Pokemon never get burned, and Ice-type Pokemon never get Frozen. Seems fine, right?
Not entirely. Not only is this system the source of the infamous "Body Slam can't paralyze Normal-types" oversight, but it makes it impossible for us ROMhackers to implement certain later-gen moves like Scald (which, being a Water-type move, would be able to burn Fire-types while incidentally being unable to burn Water-types). These fixes eliminate these problems entirely, leaving us free to assign whatever status effect we want to any move we want!
Implement True Burn- and Freeze-Immunity
Open engine\battle\effects.asm and find the section headed FreezeBurnParalyzeEffect:. Delete this section of code; we won't be needing it anymore:
FreezeBurnParalyzeEffect:
xor a
ld [wAnimationType], a
call CheckTargetSubstitute
ret nz ; return if they have a substitute, can't effect them
ldh a, [hWhoseTurn]
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
...
Now move down to .opponentAttacker and make the same deletion:
...
.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
...
Now that we've deleted the faulty status-immunity check, let's put in one that's a bit more sophisticated. Back up just a tiny bit to .burn1 and insert these type-checks:
...
.burn1
+ ld a, [wEnemyMonType1]
+ cp FIRE
+ ret z
+ ld a, [wEnemyMonType2]
+ cp FIRE
+ ret z
ld a, 1 << BRN
ld [wEnemyMonStatus], a
call HalveAttackDueToBurn ; halve attack of affected mon
ld a, ENEMY_HUD_SHAKE_ANIM
call PlayAlternativeAnimation
ld hl, BurnedText
jp PrintText
.freeze1
+ ld a, [wEnemyMonType1]
+ cp ICE
+ ret z
+ ld a, [wEnemyMonType2]
+ cp ICE
+ ret z
call ClearHyperBeam ; resets hyper beam (recharge) condition from target
ld a, 1 << FRZ
...
Now our moves won't inflict inappropriate Burns and Freezes on opponents. Next we make sure we have the same protection from enemy status. Move down to .burn2 and make these changes:
...
.burn2
+ ld a, [wBattleMonType1]
+ cp FIRE
+ ret z
+ ld a, [wBattleMonType2]
+ cp FIRE
+ ret z
ld a, 1 << BRN
ld [wBattleMonStatus], a
call HalveAttackDueToBurn
ld hl, BurnedText
jp PrintText
.freeze2
+ ld a, [wBattleMonType1]
+ cp ICE
+ ret z
+ ld a, [wBattleMonType2]
+ cp ICE
+ ret z ; the following fix is OPTIONAL
-; hyper beam bits aren't reset for opponent's side
+; OPTIONAL: While we're here, let's fix an unrelated bug. If your Mon gets Frozen while recharging from Hyper Beam,
+; you (the player) will be completely locked out of taking any action should the recharging Mon be defrosted.
+; This bug only affects the player, not the NPC trainer.
+ call ClearHyperBeam ; OPTIONAL
...
Now secondary Burn and Freeze effects will never be applied to Fire-types and Ice-types, respectively. But what about dedicated status moves, like Will-o-Wisp, whose sole function is to cause Burn? The above fix doesn't cover that, but don't despair! Just a bit further down I'll show you how to (OPTIONALLY) prevent Electric-types from EVER being paralyzed. You can probably copy and adapt that following update to suit your Fire- and Ice-type immunity needs. But first...
Make Electric-Types Immune to Paralysis Side Effects
In the vanilla games, Electric-types cannot be paralyzed as a side effect of damaging Electric moves, so we'll implement the same immunity here. We'll be staying in the same file for now... engine\battle\effects.asm. Find where .paralyze1 is commented and insert the following:
...
; .paralyze1
+ ld a, [wEnemyMonType1]
+ cp ELECTRIC
+ ret z
+ ld a, [wEnemyMonType2]
+ cp ELECTRIC
+ ret z
ld a, 1 << PAR
ld [wEnemyMonStatus], a
call QuarterSpeedDueToParalysis ; quarter speed of affected mon
ld a, ENEMY_HUD_SHAKE_ANIM
call PlayAlternativeAnimation
jp PrintMayNotAttackText ; print paralysis text
.burn1
...
Now go down to where .paralyze2 is commented and do the same:
...
; .paralyze2
+ ld a, [wBattleMonType1]
+ cp ELECTRIC
+ ret z
+ ld a, [wBattleMonType2]
+ cp ELECTRIC
+ ret z
ld a, 1 << PAR
ld [wBattleMonStatus], a
call QuarterSpeedDueToParalysis
jp PrintMayNotAttackText
.burn2
...
Now Electric-types are protected from paralysis resulting from secondary effects.
(OPTIONAL) Make Electric-Types Entirely Immune to Paralysis
RBY purists may scoff at this, but I'll also show how to implement modern day "Electric-type Paralysis immunity." This covers paralysis from Thunder Wave, Stun Spore, and Glare. Go to engine\battle\move_effects\paralyze.asm and insert the following, right at the beginning of the file:
ParalyzeEffect_:
+ ld hl, wEnemyMonType1
+ ldh a, [hWhoseTurn]
+ and a
+ jr z, .isTargetImmune
+ ld hl, wBattleMonType1
+.isTargetImmune
+ ld a, [hli]
+ cp ELECTRIC
+ jp z, .doesntAffect
+ ld a, [hl]
+ cp ELECTRIC
+ jr z, .doesntAffect
ld hl, wEnemyMonStatus
ld de, wPlayerMoveType
ldh a, [hWhoseTurn]
and a
jp z, .next
...
This grants full blanket-immunity for all Electric-types to any paralyzing status move.
NOTE: If you're looking to implement the same full immunity for Fire-types against stuff like Will-o-Wisp, this is probably what you want to take and adapt (subbing FIRE in place of ELECTRIC, of course)... I'm assuming that, if you've implemented a move like Will-o-Wisp into your ROMhack, you probably created a file like engine\battle\move_effects\burn.asm or similar, to handle dedicated "Burn" status moves.
(OPTIONAL) Make Grass-Types Immune to Powder Moves
Here's another fix that Genwunners might shun... in modern games Grass-types are completely unaffected by powder moves. For our purposes, that means we need to code a Grass-type immunity against Poisonpowder, Stun Spore, Sleep Powder, and Spore. The way we'll do this is basically to create an exception for each move in its respective status effect routine. Fortunately, Sleep Powder and Spore are both Grass-type Sleep-inducing moves, so they can be handled together, meaning we only have to do this three times.
Go to engine\battle\move_effects\paralyze.asm (if you're not there already) and modify the routine like this:
...
.next
ld a, [hl]
and a ; does the target already have a status ailment?
jr nz, .didntAffect
; check if the target is immune due to types
ld a, [de]
cp ELECTRIC
- jr nz, .hitTest
- ld b, h
- ld c, l
- inc bc
- ld a, [bc]
- cp GROUND
- jr z, .doesntAffect
- inc bc
- ld a, [bc]
- cp GROUND
- jr z, .doesntAffect
+ jr z, .checkThunderWaveImmunity
+ cp GRASS
+ jr z, .checkStunSporeImmunity
+ jr .hitTest
+.checkThunderWaveImmunity
+ ld b, h
+ ld c, l
+ inc bc
+ ld a, [bc]
+ cp GROUND
+ jr z, .doesntAffect
+ inc bc
+ ld a, [bc]
+ cp GROUND
+ jr z, .doesntAffect
+ jr .hitTest
+.checkStunSporeImmunity
+ ld b, h
+ ld c, l
+ inc bc
+ ld a, [bc]
+ cp GRASS
+ jr z, .doesntAffect
+ inc bc
+ ld a, [bc]
+ cp GRASS
+ jr z, .doesntAffect
.hitTest
...
Now Grass-types are immune to Stun Spore.
Next, we'll make them immune to Sleep Powder and Spore. Go to engine\battle\effects.asm and find the SleepEffect: routine (near the top):
SleepEffect:
+ ld hl, wPlayerSelectedMove
+ ldh a, [hWhoseTurn]
+ and a
+ jr z, .checkIfPowderMove
+ inc hl ; 'hl' now equals wEnemySelectedMove
+.checkIfPowderMove
+ ld a, [hl]
+ cp SLEEP_POWDER
+ jr z, .loadTargetMonType
+ cp SPORE
+ jr nz, .loadTargetMonStatus
+.loadTargetMonType
+ ld hl, wEnemyMonType1
+ ldh a, [hWhoseTurn]
+ and a
+ jr z, .checkPowderImmunity
+ ld hl, wBattleMonType1
+.checkPowderImmunity
+ ld a, [hli]
+ cp GRASS
+ jr z, .doesntAffect
+ ld a, [hl]
+ cp GRASS
+ jr z, .doesntAffect
+.loadTargetMonStatus
ld de, wEnemyMonStatus
ld bc, wEnemyBattleStatus2
ldh a, [hWhoseTurn]
and a
jp z, .sleepEffect
ld de, wBattleMonStatus
ld bc, wPlayerBattleStatus2
.sleepEffect
...
Next, go to the end of the SleepEffect: section and insert:
...
.didntAffect
jp PrintDidntAffectText
+.doesntAffect
+ ld c, 50
+ call DelayFrames
+ jpfar PrintDoesntAffectText
FellAsleepText:
...
Now go to PoisonEffect: just a bit further down:
PoisonEffect:
+ ld hl, wPlayerSelectedMove
+ ldh a, [hWhoseTurn]
+ and a
+ jr z, .checkIfPoisonpowder
+ inc hl
+.checkIfPoisonpowder
+ ld a, [hl]
+ cp POISONPOWDER
+ jr nz, .loadTargetMonStatus
+ ld hl, wEnemyMonType1
+ ldh a, [hWhoseTurn]
+ and a
+ jr z, .checkPowderImmunity
+ ld hl, wBattleMonType1
+.checkPowderImmunity
+ ld a, [hli]
+ cp GRASS
+ jp z, .doesntAffect
+ ld a, [hl]
+ cp GRASS
+ jp z, .doesntAffect
+.loadTargetMonStatus
ld hl, wEnemyMonStatus
ld de, wPlayerMoveEffect
ldh a, [hWhoseTurn]
and a
jr z, .poisonEffect
ld hl, wBattleMonStatus
ld de, wEnemyMoveEffect
.poisonEffect
...
Lastly, go to the end of the PoisonEffect section and add:
...
.didntAffect
ld c, 50
call DelayFrames
jp PrintDidntAffectText
+.doesntAffect
+ ld c, 50
+ call DelayFrames
+ jpfar PrintDoesntAffectText
PoisonedText:
...
That should do it!
Conclusion
This should fix any type immunity discrepancies when it comes to status and allow you to update status immunities to their modern-day iterations, if you so choose. And for all you Gen1 competitive players, now you can give that Body Slamming, para-hax-spreading Tauros a taste of its own medicine. Ha!
Good luck!