summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Add-a-new-move.md228
-rw-r--r--Tutorials.md3
2 files changed, 228 insertions, 3 deletions
diff --git a/Add-a-new-move.md b/Add-a-new-move.md
index 6178550..d0ca8b8 100644
--- a/Add-a-new-move.md
+++ b/Add-a-new-move.md
@@ -1,4 +1,4 @@
-This tutorial is for how to add a new move, allowing up to 254 moves. As an example, we'll add Nasty Plot.
+This tutorial is for how to add a new move, allowing up to 255 moves. As an example, we'll add Nasty Plot.
## Contents
@@ -8,6 +8,12 @@ This tutorial is for how to add a new move, allowing up to 254 moves. As an exam
3. [Define its battle properties](#3-define-its-battle-properties)
4. [Define its animation](#4-define-its-animation)
5. [Let Pokémon learn the move](#5-let-pokémon-learn-the-move)
+6. [Adding a 255th move](#6-adding-a-255th-move)
+ 1. [Prepare move $FF](#61-prepare-move-ff)
+ 2. [Swap move $FF with Struggle](#62-swap-move-ff-with-struggle)
+ 3. [Remove Struggle from the `MetronomeExcepts` list](#63-remove-struggle-from-the-metronomeexcepts-list)
+ 4. [Get rid of `NUM_ATTACKS` checks](#64-get-rid-of-num_attacks-checks)
+ 5. [Don't make move $FF end the turn early](#65-dont-make-move-ff-end-the-turn-early)
## 1. Define a move constant
@@ -175,3 +181,223 @@ I added `NASTY_PLOT` to these sets, based on their canon ones in later generatio
- [data/trainers/parties.asm](../blob/master/data/trainers/parties.asm): Karen's Houndoom
![Screenshot](screenshots/nasty-plot.png)
+
+
+## 6. Adding a 255th move
+
+It's pretty easy to replace the unused moves 252, 253, and 254 ($FC, $FD, and $FE) using the steps above. But move 255 ($FF) is trickier. The value $FF is often used as a special case in the code: it's the maximum value a single byte can have, it marks the end of lists\* (note that $FF can be 255 or −1 depending on context, due to [two's complement](https://en.wikipedia.org/wiki/Two%27s_complement) arithmetic, so lists ending with `db -1` actually end with $FF), and using it like any other value can be difficult to impossible.
+
+
+### 6.1. Prepare move $FF
+
+Let's say the 255th move will be Fake Out. First, follow the steps as usual. Define `FAKE_OUT` after `MOVE_OR_ANIM_FE`; give it a name, description, and battle properties (`FAKE_OUT, EFFECT_FAKE_OUT, 40, NORMAL, 100, 10, 0`); give it an animation (it can share `BattleAnim_Tackle`); and add it to Pokémon learnsets.
+
+Remember, this time we're introducing a new constant, not replacing an old one. So `ANIM_SWEET_SCENT_2` will be shifted from $FF to $100, `ANIM_THROW_POKE_BALL` from $100 to $101, and so on.
+
+
+### 6.2. Swap move $FF with Struggle
+
+Because $FF (aka 255, aka −1) is treated specially, it's not actually suitable as a move ID. Luckily, Struggle is a move that's treated specially itself: no Pokémon can learn it naturally. So go back to all the files you edited in the previous step, and swap the lines for Struggle ($A5) with the lines for Fake Out ($FF).
+
+
+### 6.3. Remove Struggle from the `MetronomeExcepts` list
+
+Now that Struggle is move $FF, including it in a list would end the list early. Usually moves would show up in various lists, but since Struggle is not a typical learnable move, it only shows up in one: `MetronomeExcepts`, the list of moves Metronome cannot copy.
+
+Edit [data/moves/metronome_exception_moves.asm](../blob/master/data/moves/metronome_exception_moves.asm):
+
+```diff
+ MetronomeExcepts:
+ db NO_MOVE
+ db METRONOME
+- db STRUGGLE
+ db SKETCH
+ db MIMIC
+ db COUNTER
+ db MIRROR_COAT
+ db PROTECT
+ db DETECT
+ db ENDURE
+ db DESTINY_BOND
+ db SLEEP_TALK
+ db THIEF
+ db -1
+```
+
+We still don't want Metronome to copy Struggle, so edit [engine/battle/move_effects/metronome.asm](../blob/master/engine/battle/move_effects/metronome.asm):
+
+```diff
+ ; No invalid moves.
+ cp NUM_ATTACKS + 1
+ jr nc, .GetMove
+
++; No Struggle.
++ cp STRUGGLE
++ jr z, .GetMove
++
+ ; None of the moves in MetronomeExcepts.
+ push af
+ ld de, 1
+ ld hl, MetronomeExcepts
+ call IsInArray
+ pop bc
+ jr c, .GetMove
+```
+
+
+### 6.4. Get rid of `NUM_ATTACKS + 1` checks
+
+Some places in the code do `cp NUM_ATTACKS + 1` to check if a move ID is not too high. If we have 255 moves, then `NUM_ATTACKS` + 1 will be 256 ($100), which won't fit in one byte, so these checks will cause a build error. However, the checks will also be redundant since every move ID is valid, so we can remove them.
+
+Edit [engine/battle/move_effects/metronome.asm](../blob/master/engine/battle/move_effects/metronome.asm) again:
+
+```diff
+-; No invalid moves.
+- cp NUM_ATTACKS + 1
+- jr nc, .GetMove
+```
+
+Edit [engine/events/battle_tower/battle_tower.asm](../blob/master/engine/events/battle_tower/battle_tower.asm):
+
+```diff
+ ValidateBTParty:
+ ; Check for and fix errors in party data
+ ...
+
+ .dont_load
+ ld [wCurPartyLevel], a
+ ld hl, MON_MOVES
+ add hl, bc
+ ld d, NUM_MOVES - 1
+ ld a, [hli]
+ and a
+- jr z, .not_move
+- cp NUM_ATTACKS + 1
+- jr nc, .not_move
+- jr .valid_move
++ jr nz, .valid_move
+
+-.not_move
+ dec hl
+ ld a, POUND
+ ld [hli], a
+ xor a
+ ld [hli], a
+ ld [hli], a
+ ld [hl], a
+ jr .done_moves
+
+ .valid_move
+- ld a, [hl]
+- cp NUM_ATTACKS + 1
+- jr c, .next
+- ld [hl], $0
+-
+-.next
+- inc hl
++ ld a, [hli]
+ dec d
+ jr nz, .valid_move
+```
+
+Edit [engine/pokemon/correct_party_errors.asm](../blob/master/engine/pokemon/correct_party_errors.asm):
+
+```diff
+ ld hl, wPartyMon1Moves
+ ld a, [wPartyCount]
+ ld b, a
+ .loop5
+ push hl
+ ld c, NUM_MOVES
+ ld a, [hl]
+ and a
+- jr z, .invalid_move
+- cp NUM_ATTACKS + 1
+- jr c, .moves_loop
++ jr nz, .moves_loop
+-.invalid_move
+ ld [hl], POUND
+
+ .moves_loop
+ ld a, [hl]
+ and a
+- jr z, .fill_invalid_moves
+- cp NUM_ATTACKS + 1
+- jr c, .next_move
++ jr nz, .next_move
+
+ .fill_invalid_moves
+ ...
+```
+
+And edit [engine/mobile/mobile_5c.asm](../blob/master/engine/mobile/mobile_5c.asm):
+
+```diff
+ CheckBTMonMovesForErrors:
+ ld c, BATTLETOWER_PARTY_LENGTH
+ ld hl, wBT_OTTempMon1Moves
+ .loop
+ push hl
+- ld a, [hl]
+- cp NUM_ATTACKS + 1
+- jr c, .okay
+- ld a, POUND
+- ld [hl], a
+-
+-.okay
+- inc hl
++ ld a, [hli]
+ ld b, NUM_MOVES - 1
+ .loop2
+ ld a, [hl]
+ and a
+- jr z, .loop3
+- cp NUM_ATTACKS + 1
+- jr c, .next
++ jr nz, .next
+
+ .loop3
+ ...
+```
+
+
+### 6.5. Don't make move $FF end the turn early
+
+There's one more way that $FF as a move ID is special. When Pursuit attacks a Pokémon that's switching out, the value $FF is used to end that attack early. Now that $FF is a valid move, we'll need to use a different value; `NO_MOVE` ($00) works.
+
+Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
+
+```diff
+ PursuitSwitch:
+ ...
+
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVarAddr
+- ld a, $ff
++ xor a ; NO_MOVE
+ ld [hl], a
+
+ ...
+```
+
+And edit [engine/battle/effect_commands.asm](../blob/master/engine/battle/effect_commands.asm):
+
+```diff
+ CheckTurn:
+ BattleCommand_CheckTurn:
+ ; checkturn
+
+ ; Repurposed as hardcoded turn handling. Useless as a command.
+
+-; Move $ff immediately ends the turn.
++; NO_MOVE immediately ends the turn.
+ ld a, BATTLE_VARS_MOVE
+ call GetBattleVar
+- inc a
++ and a ; NO_MOVE?
+ jp z, EndTurn
+
+ ...
+```
+
+Now you can have 255 moves, as long as the last one is Struggle!
diff --git a/Tutorials.md b/Tutorials.md
index c8a13c6..2fbac27 100644
--- a/Tutorials.md
+++ b/Tutorials.md
@@ -14,7 +14,7 @@ Tutorials may use diff syntax to show edits:
- [Pokémon species (up to 253)](Add-a-new-Pokémon)
- [Trainer class](Add-a-new-trainer-class)
- [Type (Fairy)](Add-a-new-type)
-- [Move (up to 254)](Add-a-new-move)
+- [Move (up to 255)](Add-a-new-move)
- [Move effect](Add-a-new-move-effect)
- [Field move effect](Add-a-new-field-move-effect)
- [Item (up to 254, with various effects)](Add-a-new-item)
@@ -92,7 +92,6 @@ Tutorials may use diff syntax to show edits:
- Gen 3+ experience growth rates ([Fluctuating and Erratic](https://bulbapedia.bulbagarden.net/wiki/Experience))
- Colored party menu icons (overworld or battle sprite colors)
- Option to show shiny colors in Pokédex
-- Add 255 moves, not just 254
- Automatic rain/sun/sandstorm on certain maps
- SGB-style colors for overworld maps and sprites
- Implement dynamic overhead+underfoot bridges