diff options
-rw-r--r-- | Add-a-new-move.md | 228 | ||||
-rw-r--r-- | Tutorials.md | 3 |
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  + + +## 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 |