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

  1. Prepare the move itself
  2. Define an effect constant
  3. Update the effect pointer table
  4. Write the effect script
  5. Define any new battle commands
  6. 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!

Screenshot