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!