summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Remove-Pokémon-sprite-animations.md614
-rw-r--r--Tutorials.md7
2 files changed, 619 insertions, 2 deletions
diff --git a/Remove-Pokémon-sprite-animations.md b/Remove-Pokémon-sprite-animations.md
new file mode 100644
index 0000000..0063f63
--- /dev/null
+++ b/Remove-Pokémon-sprite-animations.md
@@ -0,0 +1,614 @@
+One of Pokémon Crystal's improvements to Gold and Silver was animating the Pokémon sprites. There are various reasons why you might want to remove this feature:
+
+- You're changing a lot of Pokémon and don't want to design animations for them all
+- You want the game to more closely resemble R/B/Y or G/S
+- You need the space for something else
+
+If you just want some of the Pokémon to not be animated, then as mentioned in the [new Pokémon tutorial](Add-a-new-Pokémon), you can just put `endanim` as the full contents of anim.asm and anim_idle.asm for those particular Pokémon. But if none if them will be animated, then you can save considerable ROM space by removing the entire animation system.
+
+
+## Contents
+
+1. [Delete the sprite animation system and data](#1-delete-the-sprite-animation-system-and-data)
+2. [Remove references to deleted files](#2-remove-references-to-deleted-files)
+3. [Remove code and data that is no longer used](#3-remove-code-and-data-that-is-no-longer-used)
+4. [Don't load animation graphics](#4-dont-load-animation-graphics)
+5. [Play cries instead of animations](#5-play-cries-instead-of-animations)
+6. [Fix the Pokémon stats screen](#6-fix-the-pokémon-stats-screen)
+7. [Remove the unused animation tile graphics](#7-remove-the-unused-animation-tile-graphics)
+
+
+## 1. Delete the sprite animation system and data
+
+Delete all of these files:
+
+- [engine/gfx/pic_animation.asm](../blob/master/engine/gfx/pic_animation.asm)
+- [gfx/pokemon/anim_pointers.asm](../blob/master/gfx/pokemon/anim_pointers.asm)
+- [gfx/pokemon/anims.asm](../blob/master/gfx/pokemon/anims.asm)
+- [gfx/pokemon/idle_pointers.asm](../blob/master/gfx/pokemon/idle_pointers.asm)
+- [gfx/pokemon/idles.asm](../blob/master/gfx/pokemon/idles.asm)
+- [gfx/pokemon/bitmask_pointers.asm](../blob/master/gfx/pokemon/bitmask_pointers.asm)
+- [gfx/pokemon/bitmasks.asm](../blob/master/gfx/pokemon/bitmasks.asm)
+- [gfx/pokemon/frame_pointers.asm](../blob/master/gfx/pokemon/frame_pointers.asm)
+- [gfx/pokemon/johto_frames.asm](../blob/master/gfx/pokemon/johto_frames.asm)
+- [gfx/pokemon/kanto_frames.asm](../blob/master/gfx/pokemon/kanto_frames.asm)
+- [gfx/pokemon/unown_anim_pointers.asm](../blob/master/gfx/pokemon/unown_anim_pointers.asm)
+- [gfx/pokemon/unown_anims.asm](../blob/master/gfx/pokemon/unown_anims.asm)
+- [gfx/pokemon/unown_idle_pointers.asm](../blob/master/gfx/pokemon/unown_idle_pointers.asm)
+- [gfx/pokemon/unown_idles.asm](../blob/master/gfx/pokemon/unown_idles.asm)
+- [gfx/pokemon/unown_bitmask_pointers.asm](../blob/master/gfx/pokemon/unown_bitmask_pointers.asm)
+- [gfx/pokemon/unown_bitmasks.asm](../blob/master/gfx/pokemon/unown_bitmasks.asm)
+- [gfx/pokemon/unown_frame_pointers.asm](../blob/master/gfx/pokemon/unown_frame_pointers.asm)
+- [gfx/pokemon/unown_frames.asm](../blob/master/gfx/pokemon/unown_frames.asm)
+- [gfx/pokemon/unown/bitmask.asm](../blob/master/gfx/pokemon/unown/bitmask.asm)
+- [gfx/pokemon/unown/frames.asm](../blob/master/gfx/pokemon/unown/frames.asm)
+
+Then run these commands to delete files that might have been created by `make`:
+
+```bash
+rm -f gfx/pokemon/*/front.animated.2bpp
+rm -f gfx/pokemon/*/front.animated.2bpp.lz
+rm -f gfx/pokemon/*/bitmask.asm
+rm -f gfx/pokemon/*/frames.asm
+```
+
+
+## 2. Remove references to deleted files
+
+Edit [main.asm](../blob/master/main.asm):
+
+```diff
+-SECTION "Pic Animations 1", ROMX
+-
+-INCLUDE "engine/gfx/pic_animation.asm"
+-...
+-INCLUDE "gfx/pokemon/unown_bitmasks.asm"
+-
+-
+-SECTION "Pic Animations 2", ROMX
+-
+-INCLUDE "gfx/pokemon/frame_pointers.asm"
+-INCLUDE "gfx/pokemon/kanto_frames.asm"
+-
+-
+ SECTION "Font Inversed", ROMX
+
+ FontInversed:
+ INCBIN "gfx/font/font_inversed.1bpp"
+-
+-
+-SECTION "Pic Animations 3", ROMX
+-
+-INCLUDE "gfx/pokemon/johto_frames.asm"
+-INCLUDE "gfx/pokemon/unown_frame_pointers.asm"
+-INCLUDE "gfx/pokemon/unown_frames.asm"
+```
+
+And edit [pokecrystal.link](../blob/master/pokecrystal.link):
+
+```diff
+-ROMX $34
+- "Pic Animations 1"
+-ROMX $35
+- "Pic Animations 2"
+-ROMX $36
+ "Font Inversed"
+- "Pic Animations 3"
+```
+
+
+## 3. Remove code and data that is no longer used
+
+Edit [wram.asm](../blob/master/wram.asm):
+
+```diff
+-; PokeAnim data
+-wPokeAnimSceneIndex:: db
+-...
+-wPokeAnimStructEnd::
+```
+
+Edit [constants/gfx_constants.asm](../blob/master/constants/gfx_constants.asm):
+
+```diff
+-; PokeAnims indexes (see engine/gfx/pic_animation.asm)
+- const_def
+- const ANIM_MON_SLOW
+- ...
+- const ANIM_MON_EGG2
+```
+
+Edit [data/predef_pointers.asm](../blob/master/data/predef_pointers.asm):
+
+```diff
+ PredefPointers::
+ ...
+- add_predef Unused_AnimateMon_Slow_Normal
+ add_predef PlaceStatusString
+- add_predef LoadMonAnimation
+- add_predef AnimateFrontpic
+- add_predef Unused_HOF_AnimateAlignedFrontpic ; $48
+- add_predef HOF_AnimateFrontpic
+- dbw -1, InexplicablyEmptyFunction ; ???
+```
+
+
+## 4. Don't load animation graphics
+
+Edit [engine/gfx/load_pics.asm](../blob/master/engine/gfx/load_pics.asm):
+
+```diff
+ GetAnimatedFrontpic:
+ ld a, [wCurPartySpecies]
+ ld [wCurSpecies], a
+ call IsAPokemon
+ ret c
+ ld a, [rSVBK]
+ push af
+ xor a
+ ld [hBGMapMode], a
+ call _GetFrontpic
+- call GetAnimatedEnemyFrontpic
+ pop af
+ ld [rSVBK], a
+ ret
+
+...
+
+-GetAnimatedEnemyFrontpic:
+- ...
+- ret
+-
+-LoadFrontpicTiles:
+- ...
+- ret
+```
+
+
+## 5. Play cries instead of animations
+
+If you read [engine/gfx/pic_animation.asm](../blob/master/engine/gfx/pic_animation.asm) before deleting it, you'll see how the animation system works. Usually the `AnimateFrontpic` routine is called with an `ANIM_MON_*` value loaded into `e`. It then runs a series of subroutines based on the value of `e`, selected from the `PokeAnims` array:
+
+```
+PokeAnims:
+; entries correspond to ANIM_MON_* constants
+ dw .Slow
+ dw .Normal
+ dw .Menu
+ dw .Trade
+ dw .Evolve
+ dw .Hatch
+ dw .Unused ; same as .Menu
+ dw .Egg1
+ dw .Egg2
+
+.Slow: pokeanim StereoCry, Setup2, Play
+.Normal: pokeanim StereoCry, Setup, Play
+.Menu: pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
+.Trade: pokeanim Idle, Play2, Idle, Play, SetWait, Wait, Cry, Setup, Play
+.Evolve: pokeanim Idle, Play, SetWait, Wait, CryNoWait, Setup, Play
+.Hatch: pokeanim Idle, Play, CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
+.Unused: pokeanim CryNoWait, Setup, Play, SetWait, Wait, Idle, Play
+.Egg1: pokeanim Setup, Play
+.Egg2: pokeanim Idle, Play
+```
+
+Notice that not all of the subroutines are related to graphics: `Cry`, `CryNoWait`, and `StereoCry` play the audio cries. So since we've deleted the whole implementation of `AnimateFrontpic`, we have to restore the cry-playing functionality to wherever `AnimateFrontpic` was called.
+
+Edit [engine/gfx/trademon_frontpic.asm](../blob/master/engine/gfx/trademon_frontpic.asm):
+
+```diff
+ AnimateTrademonFrontpic:
+ ...
+ ld a, [wOTTrademonSpecies]
+ ld [wCurPartySpecies], a
+- hlcoord 7, 2
+- ld d, $0
+- ld e, ANIM_MON_TRADE
+- predef AnimateFrontpic
++ call PlayMonCry
+ ret
+```
+
+Edit [engine/movie/evolution_animation.asm](../blob/master/engine/movie/evolution_animation.asm):
+
+```diff
+ EvolutionAnimation:
+ ...
+
+ ld a, [wPlayerHPPal]
+ ld [wCurPartySpecies], a
+- hlcoord 7, 2
+- ld d, $0
+- ld e, ANIM_MON_EVOLVE
+- predef AnimateFrontpic
++ call PlayMonCry2
+
+ pop af
+ ld [wCurPartySpecies], a
+ pop af
+ ld [wBoxAlignment], a
+ ret
+```
+
+Edit [engine/pokemon/breeding.asm](../blob/master/engine/pokemon/breeding.asm):
+
+```diff
+ EggHatch_AnimationSequence:
+ ...
+ ld a, [wJumptableIndex]
+ ld [wCurPartySpecies], a
+- hlcoord 6, 3
+- ld d, $0
+- ld e, ANIM_MON_HATCH
+- predef AnimateFrontpic
++ call PlayMonCry2
+ pop af
+ ld [wCurSpecies], a
+ ret
+```
+
+Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm):
+
+```diff
+ Function_SetEnemyMonAndSendOutAnimation:
+ ...
+
+ ld bc, wTempMonSpecies
+ farcall CheckFaintedFrzSlp
+ jr c, .skip_cry
+- farcall CheckBattleScene
+- jr c, .cry_no_anim
+- hlcoord 12, 0
+- ld d, $0
+- ld e, ANIM_MON_SLOW
+- predef AnimateFrontpic
+- jr .skip_cry
+-
+-.cry_no_anim
+ ld a, $f
+ ld [wCryTracks], a
+ ld a, [wTempEnemyMonSpecies]
+ call PlayStereoCry
+
+ .skip_cry
+ call UpdateEnemyHUD
+ ld a, $1
+ ld [hBGMapMode], a
+ ret
+
+ ...
+
+BattleStartMessage:
+ ...
+
+.not_shiny
+ farcall CheckSleepingTreeMon
+ jr c, .skip_cry
+
+ farcall CheckBattleScene
+ jr c, .cry_no_anim
+
+- hlcoord 12, 0
+- ld d, $0
+- ld e, ANIM_MON_NORMAL
+- predef AnimateFrontpic
+- jr .skip_cry ; cry is played during the animation
+-
+-.cry_no_anim
+ ld a, $0f
+ ld [wCryTracks], a
+ ld a, [wTempEnemyMonSpecies]
+ call PlayStereoCry
+
+ .skip_cry
+ ...
+```
+
+Turning the "Battle Scene" option off would already disable sprite animations here, so all we had to do was make its case the default and only one.
+
+Edit [engine/events/halloffame.asm](../blob/master/engine/events/halloffame.asm):
+
+```diff
+ AnimateHallOfFame:
+ ...
+
+ .DisplayNewHallOfFamer:
+ call DisplayHOFMon
+ ld de, .String_NewHallOfFamer
+ hlcoord 1, 2
+ call PlaceString
+ call WaitBGMap
+- decoord 6, 5
+- ld c, $6
+- predef HOF_AnimateFrontpic
+- ld c, 60
++ call HOF_PlayCry
++ ld c, 180
+ call DelayFrames
+ and a
+ ret
+
+ ...
+
+ _HallOfFamePC:
+ ...
+
+ .finish
+ ld de, .EmptyString
+ call PlaceString
+ call WaitBGMap
+ ld b, SCGB_PLAYER_OR_MON_FRONTPIC_PALS
+ call GetSGBLayout
+ call SetPalettes
+- decoord 6, 5
+- ld c, $6
+- predef HOF_AnimateFrontpic
++ call HOF_PlayCry
+ and a
+ ret
+
+ ...
+
++HOF_PlayCry::
++ ld a, [wCurPartySpecies]
++ cp EGG
++ jr z, .fail
++ call IsAPokemon
++ jr c, .fail
++ ld a, [wCurPartySpecies]
++ call PlayMonCry2
++ ret
++
++.fail
++ ld a, 1
++ ld [wCurPartySpecies], a
++ ret
+```
+
+The Hall of Fame uses its own `HOF_AnimateFrontpic` instead of the usual `AnimateFrontpic`, so we copied its extra behavior.
+
+Edit [mobile/mobile_42.asm](../blob/master/mobile/mobile_42.asm):
+
+```diff
+ Function108219:
+ ld [wCurPartySpecies], a
+- hlcoord 7, 2
+- ld d, $0
+- ld e, ANIM_MON_TRADE
+- predef AnimateFrontpic
++ call PlayMonCry
+ ret
+
+ Function108229:
+ ld [wCurPartySpecies], a
+- hlcoord 7, 2
+- ld d, $0
+- ld e, ANIM_MON_TRADE
+- predef LoadMonAnimation
++ call PlayMonCry
+ ret
+
+...
+
+ Function1082db:
+ .loop
+ farcall PlaySpriteAnimations
+- farcall SetUpPokeAnim
+- farcall HDMATransferTileMapToWRAMBank3
+ jr nc, .loop
+ ret
+```
+
+Finally, edit [mobile/mobile_5f.asm](../blob/master/mobile/mobile_5f.asm):
+
+```diff
+ Function17d93a:
+ ...
+ decoord 0, 0
+ add hl, de
+ ld e, l
+ ld d, h
+- farcall HOF_AnimateFrontpic
++ farcall HOF_PlayCry
+ pop af
+ ld [rSVBK], a
+ call Function17e349
+ ret
+```
+
+
+## 6. Fix the Pokémon stats screen
+
+The stats screen doesn't call `AnimateFrontpic` *or* `HOF_AnimateFrontpic`. Instead, it calls individual low-level subroutines of the sprite animation engine. So fixing it to just play cries is a bit more tricky than with the other subsystems.
+
+Edit [engine/pokemon/stats_screen.asm](../blob/master/engine/pokemon/stats_screen.asm):
+
+```diff
+ StatsScreen_WaitAnim:
+ ld hl, wcf64
+ bit 6, [hl]
+ jr nz, .try_anim
+ bit 5, [hl]
+ jr nz, .finish
+ call DelayFrame
+ ret
+
+ .try_anim
+- farcall SetUpPokeAnim
+- jr nc, .finish
+ ld hl, wcf64
+ res 6, [hl]
+ .finish
+ ld hl, wcf64
+ res 5, [hl]
+ farcall HDMATransferTileMapToWRAMBank3
+ ret
+
+ ...
+
+ StatsScreen_PlaceFrontpic:
+ ld hl, wTempMonDVs
+ predef GetUnownLetter
+ call StatsScreen_GetAnimationParam
+ jr c, .egg
+ and a
+ jr z, .no_cry
+ jr .cry
+ ...
+
+ .get_animation
+ ld a, [wCurPartySpecies]
+ call IsAPokemon
+ ret c
+ call StatsScreen_LoadTextBoxSpaceGFX
+ ld de, vTiles2 tile $00
+ predef GetAnimatedFrontpic
+- hlcoord 0, 0
+- ld d, $0
+- ld e, ANIM_MON_MENU
+- predef LoadMonAnimation
+ ld hl, wcf64
+ set 6, [hl]
+ ret
+
+ StatsScreen_GetAnimationParam:
+ ld a, [wMonType]
+ ld hl, .Jumptable
+ rst JumpTable
+ ret
+
+ .Jumptable:
+ dw .PartyMon
+ dw .OTPartyMon
+ dw .BoxMon
+ dw .Tempmon
+ dw .Wildmon
+
+ ...
+
+ .CheckEggFaintedFrzSlp:
+ ld a, [wCurPartySpecies]
+ cp EGG
+ jr z, .egg
+ call CheckFaintedFrzSlp
+ jr c, .FaintedFrzSlp
++ jr .Wildmon
++
+ .egg
+ xor a
+ scf
+ ret
+
+ .Wildmon:
+ ld a, $1
+ and a
+ ret
+
+ .FaintedFrzSlp:
+ xor a
+ ret
+
+ ...
+
+ StatsScreen_AnimateEgg:
+ ...
+- hlcoord 0, 0
+- ld d, $0
+- predef LoadMonAnimation
+ ld hl, wcf64
+ set 6, [hl]
+ ret
+```
+
+Before, the `farcall SetUpPokeAnim` in `StatsScreen_WaitAnim` would play the Pokémon cry. Since we deleted that, we have to fix `StatsScreen_GetAnimationParam.CheckEggFaintedFrzSlp` so that it will return `nz` and `nc` for Pokémon that are neither Eggs, nor fainted, nor frozen, nor asleep. That way `StatsScreen_PlaceFrontpic` will correctly play cries for those Pokémon.
+
+
+## 7. Remove the unused animation tile graphics
+
+If we stopped here, the animations would be gone and the cries would play correctly, but the ROM would still have leftover graphics data for the sprite animations.
+
+One way to fix this would be to edit all 276 [gfx/pokemon/\*/front.png](../tree/master/gfx/pokemon/) files (251 Pokémon, with 26 forms for Unown), cropping each one to just be the first frame. But doing this manually is tedious, and doing it automatically (a) requires installing [GraphicsMagick](http://www.graphicsmagick.org/) and (b) risks messing up the images' [indexed color palettes](https://en.wikipedia.org/wiki/Indexed_color).
+
+Another way is to crop the sprites *after* they've been converted from .png to .2bpp format. That's what we're going to do.
+
+(If you're replacing the 251 Gen 2 Pokémon with your own set, then skip this step. And of course when you're drawing front sprites, just draw them as square still sprites, without any animated frames.)
+
+Create **tools/trim_animation.sh**:
+
+```diff
++#!/bin/sh
++# Usage: trim_animation.sh front.animated.2bpp front.dimensions
++
++case $(cat $2) in
++ U) bytes=400;; # $55="U"; 5*5*16=400
++ f) bytes=576;; # $66="f"; 6*6*16=576
++ w) bytes=784;; # $77="w"; 7*7*16=784
++ *) bytes=-0;; # invalid size; don't trim
++esac
++
++temp_file=$(mktemp)
++head -c $bytes $1 > $temp_file
++mv $temp_file $1
+```
+
+Then edit the [Makefile](../blob/master/Makefile):
+
+```diff
+-### Terrible hacks to match animations. Delete these rules if you don't care about matching.
+-
+-# Dewgong has an unused tile id in its last frame. The tile itself is missing.
+-gfx/pokemon/dewgong/frames.asm: gfx/pokemon/dewgong/front.animated.tilemap gfx/pokemon/dewgong/front.dimensions
+- tools/pokemon_animation -f $^ > $@
+- echo " db \$$4d" >> $@
+-
+-# Lugia has two unused tile ids in its last frame. The tiles themselves are missing.
+-gfx/pokemon/lugia/frames.asm: gfx/pokemon/lugia/front.animated.tilemap gfx/pokemon/lugia/front.dimensions
+- tools/pokemon_animation -f $^ > $@
+- echo " db \$$5e, \$$59" >> $@
+-
+-# Girafarig has a redundant tile after the end. It is used in two frames, so it must be injected into the generated graphics.
+-# This is more involved, so it's hacked into pokemon_animation_graphics.
+-gfx/pokemon/girafarig/front.animated.2bpp: gfx/pokemon/girafarig/front.2bpp gfx/pokemon/girafarig/front.dimensions
+- tools/pokemon_animation_graphics --girafarig -o $@ $^
+-gfx/pokemon/girafarig/front.animated.tilemap: gfx/pokemon/girafarig/front.2bpp gfx/pokemon/girafarig/front.dimensions
+- tools/pokemon_animation_graphics --girafarig -t $@ $^
+-
+
+### Pokemon pic graphics rules
+
+gfx/pokemon/%/front.dimensions: gfx/pokemon/%/front.png
+ tools/png_dimensions $< $@
+gfx/pokemon/%/normal.pal: gfx/pokemon/%/normal.gbcpal
+ tools/palette -p $< > $@
+gfx/pokemon/%/normal.gbcpal: gfx/pokemon/%/front.png
+ $(RGBGFX) -p $@ $<
+gfx/pokemon/%/back.2bpp: gfx/pokemon/%/back.png
+ $(RGBGFX) -h -o $@ $<
+-gfx/pokemon/%/bitmask.asm: gfx/pokemon/%/front.animated.tilemap gfx/pokemon/%/front.dimensions
+- tools/pokemon_animation -b $^ > $@
+-gfx/pokemon/%/frames.asm: gfx/pokemon/%/front.animated.tilemap gfx/pokemon/%/front.dimensions
+- tools/pokemon_animation -f $^ > $@
+gfx/pokemon/%/front.animated.2bpp: gfx/pokemon/%/front.2bpp gfx/pokemon/%/front.dimensions
+ tools/pokemon_animation_graphics -o $@ $^
++ tools/trim_animation.sh $@ $(word 2,$^)
+-gfx/pokemon/%/front.animated.tilemap: gfx/pokemon/%/front.2bpp gfx/pokemon/%/front.dimensions
+- tools/pokemon_animation_graphics -t $@ $^
+```
+
+(Be sure to use tabs, not spaces, for indenting commands in the Makefile!)
+
+Let's go over what trim_animation.sh is doing. Before, this was the workflow when building the ROM:
+
+1. `tools/gfx` converts each front.png to **front.2bpp**
+2. `tools/png_dimensions` reads each front.png and writes its size as a single byte in **front.dimensions**
+3. `tools/pokemon_animation_graphics` takes each pair of front.2bpp and front.dimensions and creates **front.animated.2bpp**, which is modified from front.2bpp in two ways:
+ - The first frame is transposed so its tiles are in column order, not row order
+ - The subsequent frames are treated as a series of *animation tiles*, and duplicate tiles are left out
+4. `tools/lzcomp` compresses each front.animated.2bpp into **front.animated.2bpp.lz**
+
+(Actually, if a **front.animated.2bpp.lz.XXXXXXXX** file exists, and the [MD5 hash](https://en.wikipedia.org/wiki/MD5) of front.animated.2bpp is XXXXXXXX, then it will just copy front.animated.2bpp.lz.XXXXXXXX as the compressed file. This is done because `tools/lzcomp` can't always reproduce the exact same compression as the original Pokémon Crystal ROM.)
+
+Anyway, trim_animation.sh takes each front.animated.2bpp file and removes the animation tiles, keeping only the transposed tiles for the first frame. It reads front.dimensions to know how many tiles the first frames uses. Then each tile uses 16 bytes, so it keeps that many bytes of the head of the file.
+
+We're done! This change frees up 102 KB of a 2 MB ROM: nearly 5% of the total space. Considering that an original Crystal ROM has 20% of its space unused, we've increased the free space by nearly 25%.
diff --git a/Tutorials.md b/Tutorials.md
index d3fe360..2234830 100644
--- a/Tutorials.md
+++ b/Tutorials.md
@@ -42,10 +42,14 @@ Tutorials may use diff syntax to show edits:
- [Allow tiles to have different attributes in different blocks (including X and Y flip)](Allow-tiles-to-have-different-attributes-in-different-blocks-\(including-X-and-Y-flip\))
- [Increase Pokémon sprite animation size](Increase-Pokémon-sprite-animation-size)
- [Allow more trainer parties, with individual DVs, stat experience, and nicknames](Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames)
-- [Remove the 25% failure chance for AI status moves](Remove-the-25%25-failure-chance-for-AI-status-moves)
- [Colored trainer card badges](Colored-trainer-card-badges)
- [Show the tops of leaders' heads on the trainer card](Show-the-tops-of-leaders-heads-on-the-trainer-card)
- [Correct grammar for plural trainers like Twins](Correct-grammar-for-plural-trainers-like-Twins)
+
+**Removing features:**
+
+- [Remove Pokémon sprite animations](Remove-Pokémon-sprite-animations)
+- [Remove the 25% failure chance for AI status moves](Remove-the-25%25-failure-chance-for-AI-status-moves)
- [Remove the redundant move grammar table](Remove-the-redundant-move-grammar-table)
**Features from later generations:**
@@ -75,7 +79,6 @@ Tutorials may use diff syntax to show edits:
- More daily and weekly events
- More than 15 `object_event`s per map
- Third trainer card page for Kanto badges
-- Remove all Pokémon sprite animations
- Don't print experience gain at level 100
- Colored party menu icons
- Option to show shiny colors in Pokédex