Add a new move effect - pret/pokecrystal GitHub Wiki
This tutorial is for how to add a new move effect. As an example, we'll add Hex.
Contents
- Prepare the move itself
- Define an effect constant
- Update the effect pointer table
- Write the effect script
- Define any new battle commands
- Teach the AI to use the effect well
1. Prepare the move itself
Hex is an attack from Gen 5 that doubles in power if the target has a status condition. No effect like that existed in Gen 2, so we're going to add one; but first, we have to add the move Hex, following this tutorial.
Add the constant HEX
; give it a name, description, and battle properties (HEX, EFFECT_HEX, 65, GHOST, 100, 10, 0
); give it an animation (it can share BattleAnim_Spite
); and add it to Pokémon learnsets (Vulpix, Tentacool/Tentacruel, Gastly/Haunter/Gengar, and Misdreavus by level-up; Vulpix and Dunsparce by breeding).
2. Define an effect constant
EFFECT_HEX
has not been defined yet, so we'll do that next. Edit constants/move_effect_constants.asm:
; MoveEffectsPointers indexes (see data/moves/effects_pointers.asm)
const_def
const EFFECT_NORMAL_HIT
...
const EFFECT_DEFENSE_CURL
+ const EFFECT_HEX
If you want to save space, there are six UNUSED
effects you can replace, instead of adding EFFECT_HEX
to the end of the list.
3. Update the effect pointer table
Edit data/moves/effects_pointers.asm:
MoveEffectsPointers:
; entries correspond to EFFECT_* constants
dw NormalHit
...
dw DefenseCurl
+ dw Hex
4. Write the effect script
Edit data/moves/effects.asm:
+Hex:
+ checkobedience
+ usedmovetext
+ doturn
+ critical
+ damagestats
+ damagecalc
+ stab
+ damagevariation
+ hex
+ checkhit
+ moveanim
+ failuretext
+ applydamage
+ criticaltext
+ supereffectivetext
+ checkfaint
+ buildopponentrage
+ kingsrock
+ endmove
Move effects are written in their own scripting language; see docs/move_effect_commands.md for a list of the usable commands.
If you're writing a custom effect, it's helpful to start with an existing one and modify it as needed. Here I took the NormalHit
effect and added a hex
command to double the damage if the target has a status condition. (No such command exists yet, so we'll have to add that next, but many new effects can be written just with the predefined commands.)
5. Define any new battle commands
The Hex
move effect script needs a new hex
battle command, so let's define that next. It's similar to the process of adding every other new thing: define hex
as a constant, give it an entry in a pointer table, and define the thing it's pointing to.
Edit macros/scripts/battle_commands.asm:
; BattleCommandPointers indexes (see data/battle/effect_command_pointers.asm)
const_def 1
command checkturn ; 01
...
command curl ; af
+ command hex ; b0
You can replace effect0x3c
or effect0x5d
before adding to the end of the list, if you're like me and don't want to leave unused code lying around. ;)
Anyway, edit data/battle/effect_command_pointers.asm:
BattleCommandPointers:
; entries correspond to macros/scripts/battle_commands.asm
dw BattleCommand_CheckTurn
...
dw BattleCommand_Curl
+ dw BattleCommand_Hex
Create engine/battle/move_effects/hex.asm:
+BattleCommand_Hex:
+; Get the opponent's status condition
+ ld a, BATTLE_VARS_STATUS_OPP
+ call GetBattleVar
+; Return if it's 0 (no condition)
+ and a
+ ret z
+; It's not 0, so double the damage
+ jp DoubleDamage
And edit engine/battle/effect_commands.asm:
INCLUDE "engine/battle/move_effects/rage.asm"
+
+INCLUDE "engine/battle/move_effects/hex.asm"
BattleCommand_DoubleFlyingDamage:
; doubleflyingdamage
ld a, BATTLE_VARS_SUBSTATUS3_OPP
call GetBattleVar
bit SUBSTATUS_FLYING, a
ret z
jr DoubleDamage
(We could have just written the BattleCommand_Hex
code right in engine/battle/effect_commands.asm, but it's more organized to follow pokecrystal's convention of putting individual moves' unique commands in their own files.)
Notice how similar BattleCommand_Hex
is to BattleCommand_DoubleFlyingDamage
right below it. Whether you're writing data tables, scripts, or assembly code, you don't have to start from scratch; you can find something close to what you want and modify it.
6. Teach the AI to use the effect well
Now that we successfully added Hex, it'd nice to have the enemy AI use the new move in appropriate situations.
First, look at the tables of moves and effects in data/battle/ai. They all have comments at the top explaining what they're for, like "AI_CAUTIOUS
discourages these moves after the first turn." Consider whether your new move effect should be added to any of these tables. There's nowhere we need to add HEX
or EFFECT_HEX
, though.
Next, we'll teach AI_SMART
to take advantage of EFFECT_HEX
when the player has a status condition. Edit engine/battle/ai/scoring.asm:
AI_Smart:
; Context-specific scoring.
...
AI_Smart_EffectHandlers:
dbw EFFECT_SLEEP, AI_Smart_Sleep
...
dbw EFFECT_FLY, AI_Smart_Fly
+ dbw EFFECT_HEX, AI_Smart_Hex
db -1 ; end
...
AI_Smart_DefrostOpponent:
; Greatly encourage this move if enemy is frozen.
; No move has EFFECT_DEFROST_OPPONENT, so this layer is unused.
ld a, [wEnemyMonStatus]
and 1 << FRZ
ret z
dec [hl]
dec [hl]
dec [hl]
ret
+
+AI_Smart_Hex:
+; Greatly encourage this move if the player has a status condition.
+
+ ld a, [wBattleMonStatus]
+ and a
+ ret z
+ dec [hl]
+ dec [hl]
+ dec [hl]
+ ret
Pretty self-explanatory. Find the right table, add an entry for EFFECT_HEX
, and base the new AI routine on a pre-existing one.
That's all!