Replace the Freeze status with Frostbite - pret/pokecrystal GitHub Wiki

Pokémon: Legends Arceus made new changes to the battle system, like the implementation of Agile/Strong style moves; among those changes, the Freeze status was replaced with "Frostbite", which acts similarly to the Burn status, but it halves the opponent's Special Atttack instead of its Attack.

Just like in Legends Arceus, this tutorial will teach you how to replace Freeze with Frostbite.

Contents

  1. Edit the battle text
  2. Frostbite deals damage every turn
  3. Halve Special Attack when frostbitten
  4. Don't immobilize a Pokémon suffering from Frostbite
  5. Change more Freeze behavior
  6. Adjust the AI properly
  7. Edit more miscellaneous text

1. Edit the battle text

Before creating the necessary routines, let's make some preparation work by creating, editing and deleting the battle text related to Freeze/Frostbite. Go to data/text/battle.asm:

 HurtByBurnText:
	text "<USER>'s"
	line "hurt by its burn!"
	prompt

+HurtByFrostbiteText:
+	text "<USER>'s"
+	line "hurt by frostbite!"
+	prompt

    ...
    
-FrozenSolidText:
-	text "<USER>"
-	line "is frozen solid!"
-	prompt
 
    ...
 
-WasFrozenText:
+GotAFrostbiteText:
	text "<TARGET>"
-	line "was frozen solid!"
+	line "got a frostbite!"
	prompt

We added two new texts for when the opponent is inflicted with Frostbite and when it's hurt by it, along with removing the FrozenSolidText. Now it's time to program the status per se.

2. Frostbite deals damage every turn

Just like the Burn status, Frostbite damages the inflicted Pokémon every turn, and so we have to edit the responsible routine for it. Let's go to engine/battle/core.asm:

 ResidualDamage:
 ; Return z if the user fainted before
 ; or as a result of residual damage.
 ; For Sandstorm damage, see HandleWeather.

	call HasUserFainted
	ret z

	ld a, BATTLE_VARS_STATUS
	call GetBattleVar
-	and 1 << PSN | 1 << BRN
+	and 1 << PSN | 1 << BRN | 1 << FRZ
	jr z, .did_psn_brn

	ld hl, HurtByPoisonText
	ld de, ANIM_PSN
-	and 1 << BRN
-	jr z, .got_anim
+	bit PSN, a
+	jr nz, .got_anim

	ld hl, HurtByBurnText
	ld de, ANIM_BRN
+	bit BRN, a
+	jr nz, .got_anim
+
+	ld hl, HurtByFrostbiteText
+	ld de, ANIM_FRZ
 .got_anim
	...

Now when checking the user's status it'll also check for Frostbite and load the corresponding "Hurt by" text along with its animation by checking which status bit is set. In this example, Frostbite is using the same animation as Freeze since we haven't made a new one (creating entirely new animations is out of the scope of this tutorial).

3. Halve Special Attack when frostbitten

Another trait of Frostbite is also halving the inflicted Pokémon's Special Attack (unlike Burn which halves Attack), so we'll create a new routine for it. In the same file as the previous step:

+ApplyFrbEffectOnSpclAttack:
+	ldh a, [hBattleTurn]
+	and a
+	jr z, .enemy
+	ld a, [wBattleMonStatus]
+	and 1 << FRZ
+	ret z
+	ld hl, wBattleMonSpclAtk + 1
+	jr .proceed
+
+.enemy
+	ld a, [wEnemyMonStatus]
+	and 1 << FRZ
+	ret z
+	ld hl, wEnemyMonSpclAtk + 1
+.proceed
+	ld a, [hld]
+	ld b, a
+	ld a, [hl]
+	srl a
+	rr b
+	ld [hli], a
+	or b
+	jr nz, .ok
+	ld b, $1 ; min special attack
+
+.ok
+	ld [hl], b
+	ret

This routine is based on ApplyBrnEffectOnAttack (but slightly optimized), with the difference of affecting Special Attack instead of Attack. It checks whether the user is affected by Frostbite and, if so, applies the debuff.

Note: For organization reasons I suggest placing it after ApplyBrnEffectOnAttack, but you can also place it at the end of the file.

Anyways, it's time to start using our newly made routine! First, we need to use it whenever the status effects are applied on both the player's and opponent's stats, so let's edit ApplyStatusEffectOnStats in engine/battle/core.asm:

 ApplyStatusEffectOnStats:
	ldh [hBattleTurn], a
	call ApplyPrzEffectOnSpeed
+	call ApplyFrbEffectOnSpclAttack
	jp ApplyBrnEffectOnAttack

And let's go to engine/battle/effect_commands.asm to use it whenever the player's and opponent's stats are calculated:

 CalcPlayerStats:

	...

	ld hl, ApplyPrzEffectOnSpeed
	call CallBattleCore

	ld hl, ApplyBrnEffectOnAttack
	call CallBattleCore

+	ld hl, ApplyFrbEffectOnSpclAttack
+	call CallBattleCore

	jp BattleCommand_SwitchTurn


 CalcEnemyStats:

	...

	ld hl, ApplyPrzEffectOnSpeed
	call CallBattleCore

	ld hl, ApplyBrnEffectOnAttack
	call CallBattleCore

+	ld hl, ApplyFrbEffectOnSpclAttack
+	call CallBattleCore

	jp BattleCommand_SwitchTurn

Finally, we'll have to use the routine when initially applying the Frostbite status on the opponent. Still, in engine/battle/effect_commands.asm:

 BattleCommand_FreezeTarget:
 
 	...
	
 	set FRZ, [hl]
	call UpdateOpponentInParty
+	ld hl, ApplyFrbEffectOnSpclAttack
+	call CallBattleCore
	ld de, ANIM_FRZ
	call PlayOpponentBattleAnim
	call RefreshBattleHuds

Here ApplyFrbEffectOnSpclAttack is being used similarly to how BattleCommand_BurnTarget uses ApplyBrnEffectOnAttack, calling it from the "Battle Core" bank (where engine/battle/core.asm) is placed.

4. Don't immobilize a Pokémon suffering from Frostbite

There are various routines responsible for applying the Freeze status, restarting some substatus when it happens (like flying and digging) and thawing you with a 10% chance every turn.

First we'll get rid of the one which stops both the inflicted Pokémon from moving. Let's edit engine/battle/effect_commands.asm:

 BattleCommand_CheckTurn:
 
 	...
	
.not_asleep
-
-	ld hl, wBattleMonStatus
-	bit FRZ, [hl]
-	jr z, .not_frozen
-
-	; Flame Wheel and Sacred Fire thaw the user.
-	ld a, [wCurPlayerMove]
-	cp FLAME_WHEEL
-	jr z, .not_frozen
-	cp SACRED_FIRE
-	jr z, .not_frozen
-
-	ld hl, FrozenSolidText
-	call StdBattleTextbox
-
-	call CantMove
-	jp EndTurn
-
-.not_frozen

	ld hl, wPlayerSubStatus3
	bit SUBSTATUS_FLINCHED, [hl]
	jr z, .not_flinched
	...
 CheckEnemyTurn:
 
 	...
 
 .not_asleep
-
-	ld hl, wEnemyMonStatus
-	bit FRZ, [hl]
-	jr z, .not_frozen
-
-	; Flame Wheel and Sacred Fire thaw the user.
-	ld a, [wCurEnemyMove]
-	cp FLAME_WHEEL
-	jr z, .not_frozen
-	cp SACRED_FIRE
-	jr z, .not_frozen
-
-	ld hl, FrozenSolidText
-	call StdBattleTextbox
-	call CantMove
-	jp EndTurn
-
-.not_frozen

	ld hl, wEnemySubStatus3
	bit SUBSTATUS_FLINCHED, [hl]
	jr z, .not_flinched
	...

Now the frostbitten Pokémon can move, but some of their substatus will reset when inflicted by it, which makes sense for a frozen Pokémon but not for a frostbitten one. Let's change this behavior:

 BattleCommand_FreezeTarget:
 
 	...

-	ld hl, WasFrozenText
+	ld hl, GotAFrostbiteText
	call StdBattleTextbox

	farcall UseHeldStatusHealingItem
-	ret nz
-
-	call OpponentCantMove
-	call EndRechargeOpp
-	ld hl, wEnemyJustGotFrozen
-	ldh a, [hBattleTurn]
-	and a
-	jr z, .finish
-	ld hl, wPlayerJustGotFrozen
-.finish
-	ld [hl], $1
	ret

We also used the chance to replace the old WasFrozenText with the new one we edited.

Great! Frostbite works! But there are some leftover routines from the old Freeze status that we need to clean up; to be more specific, HandleDefrost is a routine that acts between turns to determine whether the frozen Pokémon should defrost itself or not. We don't need it anymore, so we can safely delete it along with the instances where it's called. For this, go to engine/battle/core.asm:

 HandleBetweenTurnEffects:
 	...
 .NoMoreFaintingConditions:
	call HandleLeftovers
	call HandleMysteryberry
-	call HandleDefrost
	call HandleSafeguard
	
 ...
	
-HandleDefrost:
-	ldh a, [hSerialConnectionStatus]
-	cp USING_EXTERNAL_CLOCK
-	jr z, .enemy_first
-	call .do_player_turn
-	jr .do_enemy_turn
- 	...
-.wild
-
-	call UpdateBattleHuds
-	call SetPlayerTurn
-	ld hl, DefrostedOpponentText
-	jp StdBattleTextbox

Also, wPlayerJustGotFrozen and wEnemyJustGotFrozen were labels used to store a value to indicate that the player/enemy was recently frozen and stop HandleDefrost from thawing them in the same turn they were statused, but they're not useful anymore, so we can also delete them. Still in engine/battle/core.asm:

 BattleTurn:
 .loop
	call Stubbed_Increments5_a89a
	call CheckContestBattleOver
	jp c, .quit

	xor a
	ld [wPlayerIsSwitching], a
	ld [wEnemyIsSwitching], a
	ld [wBattleHasJustStarted], a
-	ld [wPlayerJustGotFrozen], a
-	ld [wEnemyJustGotFrozen], a
	ld [wCurDamage], a
	ld [wCurDamage + 1], a

And now let's delete the labels from ram/wram.asm and allocate empty bytes instead (this is to avoid shifting all the other memory addresses and desync data):

 wAmuletCoin:: db

 wSomeoneIsRampaging:: db

-wPlayerJustGotFrozen:: db
-wEnemyJustGotFrozen:: db
+	ds 2
+
 wBattleEnd::

5. Change more Freeze behavior

A frozen/sleeping wild Pokémon can't run from battle, but it doesn't make sense for frostbitten Pokémon, so we'll edit this in engine/battle/core.asm:

 TryEnemyFlee:
	ld a, [wBattleMode]
	dec a
	jr nz, .Stay

	ld a, [wPlayerSubStatus5]
	bit SUBSTATUS_CANT_RUN, a
	jr nz, .Stay

	ld a, [wEnemyWrapCount]
	and a
	jr nz, .Stay

	ld a, [wEnemyMonStatus]
-	and 1 << FRZ | SLP_MASK
+	and SLP_MASK
	jr nz, .Stay

Now it'll only check for the Sleep status instead of also checking for Freeze (or Frostbite in this case).

In the next case, you cannot flinch a target that's either frozen or sleeping, but again, it doesn't make sense for frostbitten Pokémon. Go to engine/battle/effect_commands.asm:

 BattleCommand_FakeOut:
	ld a, [wAttackMissed]
	and a
	ret nz

	call CheckSubstituteOpp
	jr nz, .fail

	ld a, BATTLE_VARS_STATUS_OPP
	call GetBattleVar
-	and 1 << FRZ | SLP_MASK
+	and SLP_MASK
	jr nz, .fail

	call CheckOpponentWentFirst
	jr z, FlinchTarget

 .fail
	ld a, 1
	ld [wAttackMissed], a
	ret

 BattleCommand_FlinchTarget:
	call CheckSubstituteOpp
	ret nz

	ld a, BATTLE_VARS_STATUS_OPP
	call GetBattleVar
-	and 1 << FRZ | SLP_MASK
+	and SLP_MASK
	ret nz

	call CheckOpponentWentFirst
	ret nz

	ld a, [wEffectFailed]
	and a
	ret nz

CheckFaintedFrzSlep is used for muting a Pokémon's cry whenever they're sent into battle or seen in the status screen. When evolving, it won't animate the Pokémon either. Just like before, we'll edit the check in engine/pokemon/stats_screen.asm:

 CheckFaintedFrzSlp:
	ld hl, MON_HP
	add hl, bc
	ld a, [hli]
	or [hl]
-	jr z, .fainted_frz_slp
+	jr z, .fainted_slp
	ld hl, MON_STATUS
	add hl, bc
	ld a, [hl]
-	and 1 << FRZ | SLP_MASK
-	jr nz, .fainted_frz_slp
+	and SLP _MASK
+	jr nz, .fainted_slp
	and a
	ret

-.fainted_frz_slp
+.fainted_slp
	scf
	ret

6. Adjust the AI properly

We've been working from the player's perspective, but now we need to modify the AI to act properly.

If the AI has the CONTEXT_USE_F item flag, possesses either a Full Heal or Full Restore, and their Pokémon is frozen/sleeping, they'll always use their item. It makes sense for frozen Pokémon since they can't move and can only heal themselves by using either Flame Wheel or Sacred Fire, but a frostbitten Pokémon can still act, so it's not a high priority anymore. Let's go to engine/battle/ai/items.asm:

 AI_Items:
	...
 .FailToxicCheck:
	ld a, [wEnemyMonStatus]
-	and 1 << FRZ | SLP_MASK
+	and SLP_MASK
	jp z, .DontUse
	jp .Use

And also let's go to engine/battle/ai/scoring.asm to adjust the AI intelligence (and edit some comments to be more accurate):

 AI_Smart_DefrostOpponent:
-; Greatly encourage this move if enemy is frozen.
+; Greatly encourage this move if enemy is frostbitten.
 ; No move has EFFECT_DEFROST_OPPONENT, so this layer is unused.
 
 ...

 AI_Smart_HealBell:
 ; Dismiss this move if none of the opponent's Pokemon is statused.
 ; Encourage this move if the enemy is statused.
-; 50% chance to greatly encourage this move if the enemy is fast asleep or frozen.
+; 50% chance to greatly encourage this move if the enemy is fast asleep.

	...
 .ok
-	and 1 << FRZ | SLP_MASK
+	and SLP_MASK
	ret z
	call AI_50_50
	ret c
	dec [hl]
	dec [hl]
	ret
		
...
	
 AI_Smart_FlameWheel:
-; Use this move if the enemy is frozen.
+; Use this move if the enemy is frostbitten.
 ...

7. Edit more miscellaneous text

Now what's left is some text related to items/moves and other things to reflect the new status. Let's go step by step. First in data/items/descriptions.asm:

 IceHealDesc:
-	db   "Defrosts frozen"
-	next "#MON.@
+	db   "Heals a frostbit-"
+	next "ten #MON.@"

 ...

 BurntBerryDesc:
 	db   "A self-cure for"
-	next "freezing. (HOLD)@"
+	next "frostbite. (HOLD)@"

Then in data/moves/descriptions.asm:

 IcePunchDescription:
 	db   "An icy punch. May"
-	next "cause freezing.@"
+	next "cause frostbite.@"

 IceBeamDescription:
 	db   "An attack that may"
- 	next "freeze the foe.@"
+	next "cause frostbite.@"

 BlizzardDescription:
 	db   "An attack that may"
-	next "freeze the foe.@"
+	next "cause frostbite.@"

 PowderSnowDescription:
 	db   "An attack that may"
-	next "cause freezing.@"
+	next "cause frostbite.@"

Next we'll edit the "FRZ" acronym in engine/pokemon/mon_stats.asm:

 PlaceNonFaintStatus:
	push de
	ld a, [de]
	ld de, PsnString
	bit PSN, a
	jr nz, .place
	ld de, BrnString
	bit BRN, a
	jr nz, .place
-	ld de, FrzString
+	ld de, FrbString
	bit FRZ, a
	...
	
 SlpString: db "SLP@"
 PsnString: db "PSN@"
 BrnString: db "BRN@"
-FrzString: db "FRZ@"
+FrbString: db "FRB@"
 ParString: db "PAR@"

Finally, there are two maps with dialogue talking about the Frozen status that we'll edit. The first map is maps/EarlsPokemonAcademy.asm:

 AcademyBlackboard:
	opentext
	writetext AcademyBlackboardText
 .Loop:
	loadmenu .BlackboardMenuHeader
	_2dmenu
	closewindow
	ifequal 1, .Poison
	ifequal 2, .Paralysis
	ifequal 3, .Sleep
	ifequal 4, .Burn
-	ifequal 5, .Freeze
+	ifequal 5, .Frostbite
	closetext
	end
	
	...
	
-.Freeze:
-	writetext AcademyFreezeText
+.Frostbite:
+	writetext AcademyFrostbiteText
	waitbutton
	sjump .Loop
	
 ...
 
-AcademyFreezeText:
+AcademyFrostbiteText:
	text "If your #MON is"
-	line "frozen, it can't"
-	cont "do a thing."
+	line "frostbitten, it'll"
+	cont "gradually lose HP."

-	para "It remains frozen"
-	line "after battle."
+	para "Its SPCL.ATK will"
+	line "also be halved."

	para "Thaw it out with"
	line "an ICE HEAL."
	done

And the second one is maps/MahoganyGym.asm:

 BoarderRonaldSeenText:
-	text "I'll freeze your"
-	line "#MON, so you"
+	text "I'll frostbite"
+	line "your #MON, so you"
	cont "can't do a thing!"
	done

 ...

 BoarderRonaldAfterBattleText:
	text "I think there's a"
-	line "move a #MON"
-
-	para "can use while it's"
-	line "frozen."
+	line "move a frostbitten"
+
+	para "#MON can use to"
+	line "cure itself."
	done

And with this we're finally done! Frostbite is completely implemented into the game!

⚠️ **GitHub.com Fallback** ⚠️