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 Burn and Sunny Day interactions
  7. Adjust the AI properly
  8. Edit more miscellaneous text
  9. Appendix

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 Burn and Sunny Day interactions

By default, a Pokémon cannot be Frozen during the effects of Sunny Day. In addition, moves with a chance to Burn the target (such as Ember or Fire Punch) will cure the target of the Frozen status. Since we are recycling the code that handles how the Freeze status effect is inflicted for Frostbite, we will have to adjust both the Burn function to prevent Frostbite from being cured, and fix the Freeze function to allow for Frostbite to occur under the effects of Sunny Day. Both of these are handled under engine/battle/effect_commands.asm, so let's go there:

BattleCommand_BurnTarget:
    xor a
    ld [wBattleAfterAnim], a
    call CheckSubstituteOpp
    ret nz
    ld a, BATTLE_VARS_STATUS_OPP
    call GetBattleVarAddr
    and a
-   jp nz, Defrost
+   ret nz

The above code handles how the Burn status effect is inflicted, where jp nz, Defrost jumps to the Defrost function within the same file to Defrost the opponent during battle should you land a move with a Burn chance against a Frozen opponent. We've changed that to a ret nz so now Burn will not cure the target of Frostbite or override any other status effects.

Now let's adjust how Sunny Day behaves with Frostbite. Still in engine/battle/effect_commands.asm:

BattleCommand_FreezeTarget:
	xor a
	ld [wBattleAfterAnim], a
	call CheckSubstituteOpp
	ret nz
	ld a, BATTLE_VARS_STATUS_OPP
	call GetBattleVarAddr
	and a
	ret nz
	ld a, [wTypeModifier]
	and EFFECTIVENESS_MASK
	ret z
-	ld a, [wBattleWeather]
-	cp WEATHER_SUN
-	ret z

The three lines we removed from the function checked to see if the current weather during battle was Harsh Sunlight set up by Sunny Day and would prevent the Freeze status from happening. With these lines removed, Frostbite will now occur while Sunny Day is up.

7. 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.
 ...

8. 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

9: Appendix

The following steps are optional, but may be done at the reader's disgretion.

Removing the Defrost Functions

If the reader is inclined to do so, they may opt to delete the unused BattleCommand_DefrostOpponent battle command and the Defrost function within engine/battle/effect_commands.asm now that moves with a Burn chance don't remove Frostbite. If you go forward with this optional step however, make sure to also delete command defrostopponent in macros/scripts/battle_commands.asm and dw BattleCommand_DefrostOpponent in data/battle/effect_command_pointers.asm as well.

-Defrost:
-	ld a, [hl]
-	and 1 << FRZ
-	ret z

-	xor a
-	ld [hl], a

-	ldh a, [hBattleTurn]
-	and a
-	ld a, [wCurOTMon]
-	ld hl, wOTPartyMon1Status
-	jr z, .ok
-	ld hl, wPartyMon1Status
-	ld a, [wCurBattleMon]
- .ok

-	call GetPartyLocation
-	xor a
-	ld [hl], a
-	call UpdateOpponentInParty

-	ld hl, DefrostedOpponentText
-	jp StdBattleTextbox

   ...

-BattleCommand_DefrostOpponent:
-; Thaw the opponent if frozen, and
-; raise the user's Attack one stage.

-	call AnimateCurrentMove

-	ld a, BATTLE_VARS_STATUS_OPP
-	call GetBattleVarAddr
-	call Defrost

-	ld a, BATTLE_VARS_MOVE_EFFECT
-	call GetBattleVarAddr
-	ld a, [hl]
-	push hl
-	push af

-	ld a, EFFECT_ATTACK_UP
-	ld [hl], a
-	call BattleCommand_StatUp

-	pop af
-	pop hl
-	ld [hl], a
-	ret

Adjusting Flame Wheel and Sacred Fire Interactions

In Pokémon: Legends Arceus, if a Pokémon inflicted with Frostbite were to use Flame Wheel or Flare Blitz, it would be cured from the Frostbite. This interaction is a carry over from the other mainline Pokémon games where these moves among other moves would cure the user from the Freeze status. If you wish to remove this interaction where Flame Wheel and Sacred Fire can cure Frostbite for balancing purposes, refer to the following steps.

Once again in engine/battle/effect_commands.asm, we will need to delete the BattleCommand_Defrost function.

- BattleCommand_Defrost:
-; Thaw the user.
-
-	ld a, BATTLE_VARS_STATUS
-	call GetBattleVarAddr
-	bit FRZ, [hl]
-	ret z
-	res FRZ, [hl]
-
-; Don't update the enemy's party struct in a wild battle.
-
-	ldh a, [hBattleTurn]
-	and a
-	jr z, .party
-
-	ld a, [wBattleMode]
-	dec a
-	jr z, .done
-
- .party
-	ld a, MON_STATUS
-	call UserPartyAttr
-	res FRZ, [hl]
-
- .done
-	call RefreshBattleHuds
-	ld hl, WasDefrostedText
-	jp StdBattleTextbox

Then, delete command defrost in macros/scripts/battle_commands.asm and dw BattleCommand_Defrost in data/battle/effect_command_pointers.asm.

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

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