diff options
author | Rangi <remy.oukaour+rangi42@gmail.com> | 2018-10-27 23:52:53 -0400 |
---|---|---|
committer | Rangi <remy.oukaour+rangi42@gmail.com> | 2018-10-27 23:52:53 -0400 |
commit | ffbebeb0e5375cd4b29966ca981688351b71da04 (patch) | |
tree | 3305284468fa38903edcb201c4b5161b6d12990a | |
parent | 5610d25b1125435f68d2a5a3bf7cbea32dbf5b78 (diff) |
<details>
-rw-r--r-- | Add-a-new-item.md | 4 | ||||
-rw-r--r-- | Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md | 20 | ||||
-rw-r--r-- | Allow-tiles-to-have-different-attributes-in-different-blocks-(including-X-and-Y-flip).md | 314 | ||||
-rw-r--r-- | Expand-tilesets-from-192-to-255-tiles.md | 4 | ||||
-rw-r--r-- | Improve-the-outdoor-sprite-system.md | 425 | ||||
-rw-r--r-- | Replace-stat-experience-with-EVs.md | 4 |
6 files changed, 468 insertions, 303 deletions
diff --git a/Add-a-new-item.md b/Add-a-new-item.md index 3ce09a9..3abb682 100644 --- a/Add-a-new-item.md +++ b/Add-a-new-item.md @@ -497,7 +497,8 @@ In other words, adding an effect for `HELD_EVIOLITE` won't involve updating any Eviolite's effect is pretty similar to Metal Powder, which boosts Ditto's defenses. Searching through the battle engine code for references to `METAL_POWDER` finds that it's implemented as a `DittoMetalPowder` routine, which gets called in two places, one for the player and one for the enemy. (Note that it checks directly for whether the held item is `METAL_POWDER`, not whether the held item's effect is `HELD_METAL_POWDER`. Either check would be okay, though.) -Anyway, edit [engine/battle/effect_commands.asm](../blob/master/engine/battle/effect_commands.asm): +<details> +<summary>Anyway, edit [engine/battle/effect_commands.asm](../blob/master/engine/battle/effect_commands.asm): (click to expand)</summary> ```diff DittoMetalPowder: @@ -589,6 +590,7 @@ Anyway, edit [engine/battle/effect_commands.asm](../blob/master/engine/battle/ef and a ret ``` +</details> The implementation of `UnevolvedEviolite` is very similar to `DittoMetalPowder`, except the simple check for whether the species is `DITTO` has been replaced by a check for evolutions, similar to the check in `MoonBallMultiplier` in [engine/items/item_effects.asm](../blob/master/engine/items/item_effects.asm). (Also, instead of checking whether `[hl]` is `EVIOLITE`, we check whether `b` is `HELD_EVIOLITE`; either would be fine, since `GetOpponentItem` returns "the effect of the opponent's item in `bc`, and its id at `hl`", as explained in a comment.) `bc` gets repeatedly saved on the stack with `push` and `pop` because it contains the defense stat, and we don't want the various pre-boost checks to corrupt the original value. diff --git a/Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md b/Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md index 7590af1..1c43195 100644 --- a/Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md +++ b/Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md @@ -71,7 +71,8 @@ Edit [wram.asm](../blob/master/wram.asm): The `wOtherTrainerType` byte will store the trainer type while their data is being read. -Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm): +<details> +<summary>Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm): (click to expand)</summary> ```diff ReadTrainerParty: @@ -254,6 +255,7 @@ Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_tr + + jp .loop ``` +</details> We've replaced the four routines `TrainerType1`, `TrainerType2`, `TrainerType3`, and `TrainerType4` with a single `ReadTrainerPartyPieces` routine. If you compare them all side by side, you'll notice how the chunks of `ReadTrainerPartyPieces` are all taken from the old routines, but now they don't need repeating. @@ -334,7 +336,8 @@ Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_dat I'm not bothering to define new `TRAINERTYPE_*` constants for every combination of {moves, item, nickname}. You can just combine flag values, like `TRAINERTYPE_NICKNAME | TRAINERTYPE_ITEM` for a trainer with Pokémon that have nicknames and held items. -Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: +<details> +<summary>Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: (click to expand)</summary> ```diff ; add to party @@ -378,6 +381,7 @@ Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_tr ; item? ... ``` +</details> Then edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm): @@ -482,7 +486,8 @@ Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_dat Again, I'm not bothering to define new `TRAINERTYPE_*` constants for every combination of {moves, item, nickname, DVs}. You can just combine individual flag values. -Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: +<details> +<summary>Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: (click to expand)</summary> ```diff ; add to party @@ -574,6 +579,7 @@ Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_tr jp .loop ``` +</details> Then edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm) again: @@ -673,7 +679,8 @@ Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_dat +PERFECT_STAT_EXP EQU $1337 ; treated as $FFFF in enemy party data ``` -Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: +<details> +<summary>Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: (click to expand)</summary> ```diff ; add to party @@ -758,6 +765,7 @@ Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_tr jp .loop ``` +</details> (If you're using an older version of pokecrystal where `NUM_EXP_STATS` is not defined, then replace `ld c, NUM_EXP_STATS` with `ld c, 5`.) @@ -869,7 +877,8 @@ Edit [data/trainers/party_pointers.asm](../blob/master/data/trainers/party_point We're just replacing `dw` with `dba` everywhere. Each table entry now has a third byte to declare which bank it's in, instead of expecting all the entries to be in `BANK(Trainers)`. -Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: +<details> +<summary>Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm) again: (click to expand)</summary> ```diff +GetNextTrainerDataByte: @@ -1176,6 +1185,7 @@ Edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_tr -INCLUDE "data/trainers/parties.asm" +INCLUDE "data/trainers/party_pointers.asm" ``` +</details> That's a long series of edits, but they're all basically the same: instead of getting data directly from `[hl]` in the current ROM bank (i.e. the bank that this code is in), we call `GetNextTrainerDataByte` to switch banks while reading party data. We also have to account for the new bank byte in each `TrainerGroups` table entry. diff --git a/Allow-tiles-to-have-different-attributes-in-different-blocks-(including-X-and-Y-flip).md b/Allow-tiles-to-have-different-attributes-in-different-blocks-(including-X-and-Y-flip).md index 2e961ed..d087685 100644 --- a/Allow-tiles-to-have-different-attributes-in-different-blocks-(including-X-and-Y-flip).md +++ b/Allow-tiles-to-have-different-attributes-in-different-blocks-(including-X-and-Y-flip).md @@ -128,7 +128,8 @@ If you followed [the `PRIORITY` color tutorial](Allow-map-tiles-to-appear-above- Now that all the \*_attributes.bin files are created, delete all the \*_palette_map.asm files. Also delete [gfx/tileset_palette_maps.asm](../blob/master/gfx/tileset_palette_maps.asm). -Edit [gfx/tilesets.asm](../blob/master/gfx/tilesets.asm): +<details> +<summary>Edit [gfx/tilesets.asm](../blob/master/gfx/tilesets.asm): (click to expand)</summary> ```diff +SECTION "Tileset Data 8", ROMX @@ -261,6 +262,7 @@ Edit [gfx/tilesets.asm](../blob/master/gfx/tilesets.asm): +TilesetAerodactylWordRoomAttr: +INCBIN "data/tilesets/aerodactyl_word_room_attributes.bin" ``` +</details> All we're doing here is `INCBIN`-ing each of the \*_attributes.bin files with an appropriate label. Of course, if you've added or removed any tilesets, they'll need their own labels and `INCBIN` statements. It doesn't matter which section any of them go in, or whether you create new sections. @@ -403,6 +405,9 @@ We deleted `FarCallSwapTextboxPalettes`, so now `OverworldTextModeSwitch` just c Anyway, `LoadMapPart` calls `LoadMetatiles` to load certain data from the \*_metatiles.bin files, and we're going to define a `LoadMetatileAttributes` routine that does the same thing with the \*_attributes.bin files, so this is where it will be called. +<details> +<summary>(Click to expand the edits to `LoadMetatiles`.)</summary> + ```diff LoadMetatiles:: ; de <- wOverworldMapAnchor @@ -495,6 +500,7 @@ Anyway, `LoadMapPart` calls `LoadMetatiles` to load certain data from the \*_met jp nz, .row ret ``` +</details> This routine copies data from \*_metatiles.bin (pointed to by `[wTilesetBlocksAddress]`) into `wSurroundingTiles`. We've made two small changes to it. @@ -504,6 +510,9 @@ Two, tile IDs in \*_metatiles.bin have their high bit masked out. That used to b (Strictly speaking, we could just store values $00–$7F in \*_metatiles.bin to begin with, and rely on bit 3 of the values in \*_attributes.asm to indicate which tile range is being used ($00–$7F or $80–$FF, VRAM bank 0 or 1). But that would break compatibility with Polished Map even worse than it already is, and it would necessitate editing the \*_metatiles.bin files too.) +<details> +<summary>(Click to expand the edits to `LoadMetatileAttributes`.)</summary> + ```diff LoadMetatiles:: ... @@ -602,6 +611,7 @@ Two, tile IDs in \*_metatiles.bin have their high bit masked out. That used to b + jp nz, .row + ret ``` +</details> `LoadMetatileAttributes` is very similar to `LoadMetatiles`: it copies data from \*_attributes.bin (pointed to by `[wTilesetAttributesAddress]`) into `wSurroundingAttributes`. However, `wSurroundingAttributes` is not in the same RAM bank as `wTilesetAttributesAddress` (whereas `wSurroundingTiles` is), so we have to switch banks at one point. @@ -738,296 +748,18 @@ error: Unable to place 'Home' (ROM0 section) at $150 Turns out that `LoadMetatileAttributes` was too large to fit in ROM0. We'll need to remove something else from that bank to make room. Luckily, Game Freak left some unused code here and there, and it's all been labeled as `Unreferenced`. -Edit [home/map.asm](../blob/master/home/map.asm) again: - -```diff --Unreferenced_Function2816:: -- ld hl, wBGMapBuffer -- ld bc, wBGMapBufferEnd - wBGMapBuffer -- xor a -- call ByteFill -- ret -``` - -Edit [home/audio.asm](../blob/master/home/audio.asm): - -```diff --Unreferenced_Function3d9f:: --; Places a BCD number at the --; upper center of the screen. -- ld a, 4 * TILE_WIDTH -- ld [wVirtualOAMSprite38YCoord], a -- ld [wVirtualOAMSprite39YCoord], a -- ld a, 10 * TILE_WIDTH -- ld [wVirtualOAMSprite38XCoord], a -- ld a, 11 * TILE_WIDTH -- ld [wVirtualOAMSprite39XCoord], a -- xor a -- ld [wVirtualOAMSprite38Attributes], a -- ld [wVirtualOAMSprite39Attributes], a -- ld a, [wc296] -- cp 100 -- jr nc, .max -- add 1 -- daa -- ld b, a -- swap a -- and $f -- add "0" -- ld [wVirtualOAMSprite38TileID], a -- ld a, b -- and $f -- add "0" -- ld [wVirtualOAMSprite39TileID], a -- ret -- --.max -- ld a, "9" -- ld [wVirtualOAMSprite38TileID], a -- ld [wVirtualOAMSprite39TileID], a -- ret -``` - -Edit [home/fade.asm](../blob/master/home/fade.asm): - -```diff --Unreferenced_Function48c:: --; TimeOfDayFade -- ld a, [wTimeOfDayPal] -- ld b, a -- ld hl, IncGradGBPalTable_11 -- ld a, l -- sub b -- ld l, a -- jr nc, .okay -- dec h -- --.okay -- ld a, [hli] -- ldh [rBGP], a -- ld a, [hli] -- ldh [rOBP0], a -- ld a, [hli] -- ldh [rOBP1], a -- ret -``` - -Edit [home/lcd.asm](../blob/master/home/lcd.asm): - -```diff --Unreferenced_Function547:: -- ldh a, [hLCDCPointer] -- cp rSCX - $ff00 -- ret nz -- ld c, a -- ld a, [wLYOverrides] -- ld [$ff00+c], a -- ret -``` - -Edit [home/map_objects.asm](../blob/master/home/map_objects.asm): - -```diff --Unreferenced_Function19b8: -- call GetMapObject -- ld hl, MAPOBJECT_OBJECT_STRUCT_ID -- add hl, bc -- ld a, [hl] -- push af -- ld [hl], -1 -- inc hl -- ld bc, OBJECT_LENGTH - 1 -- xor a -- call ByteFill -- pop af -- cp -1 -- ret z -- cp $d -- ret nc -- ld b, a -- ld a, [wObjectFollow_Leader] -- cp b -- jr nz, .ok -- ld a, -1 -- ld [wObjectFollow_Leader], a -- --.ok -- ld a, b -- call GetObjectStruct -- farcall DeleteMapObject -- ret -``` - -Edit [home/menu.asm](../blob/master/home/menu.asm): - -```diff --Unreferenced_Function1f9e:: -- call GetMenuDataPointerTableEntry -- inc hl -- inc hl -- ld a, [hli] -- ld d, [hl] -- ld e, a -- ret -``` - -Edit [home/mobile.asm](../blob/master/home/mobile.asm): - -```diff --Unreferenced_Function3ed7:: -- ld [$dc02], a -- ldh a, [hROMBank] -- push af -- ld a, BANK(Function114243) -- rst Bankswitch -- -- call Function114243 -- pop bc -- ld a, b -- rst Bankswitch -- -- ld a, [$dc02] -- ret - - Function3eea:: - push hl - push bc - ld de, wAttrMap - wTileMap - add hl, de - inc b - inc b - inc c - inc c - call Function3f35 - pop bc - pop hl - call MobileHome_PlaceBox - ret - --Unreferenced_Function3efd:: -- push hl -- hlcoord 0, 12 -- ld b, 4 -- ld c, 18 -- call .fill_attr -- pop hl -- call PrintTextBoxText -- ret -- --.fill_attr -- push hl -- push bc -- ld de, wAttrMap - wTileMap -- add hl, de -- inc b -- inc b -- inc c -- inc c -- call Function3f35 -- pop bc -- pop hl -- call TextBoxBorder -- ret -``` - -Edit [home/mon_data.asm](../blob/master/home/mon_data.asm): - -```diff --Unreferenced_GetNthMove:: -- ld hl, wListMoves_MoveIndicesBuffer -- ld c, a -- ld b, 0 -- add hl, bc -- ld a, [hl] -- ret -``` - -Edit [home/mon_data_2.asm](../blob/master/home/mon_data_2.asm): - -```diff --Unreferenced_GetDexNumber:: --; Probably used in gen 1 to convert index number to dex number --; Not required in gen 2 because index number == dex number -- push hl -- ld a, b -- dec a -- ld b, 0 -- add hl, bc -- ld hl, BaseData + BASE_DEX_NO -- ld bc, BASE_DATA_SIZE -- call AddNTimes -- ld a, BANK(BaseData) -- call GetFarHalfword -- ld b, l -- ld c, h -- pop hl -- ret -``` - -Edit [home/serial.asm](../blob/master/home/serial.asm): - -```diff --Unreferenced_Function919:: -- ld a, [wLinkMode] -- and a -- ret nz -- ld a, USING_INTERNAL_CLOCK -- ldh [rSB], a -- xor a -- ldh [hSerialReceive], a -- ld a, 0 << rSC_ON -- ldh [rSC], a -- ld a, 1 << rSC_ON -- ldh [rSC], a -- ret -``` - -Edit [home/text.asm](../blob/master/home/text.asm): - -```diff --Unreferenced_Function1522:: --; TX_CRY -- push de -- ld e, [hl] -- inc hl -- ld d, [hl] -- call PlayMonCry -- pop de -- pop hl -- pop bc -- ret -``` - -Finally, edit [home.asm](../blob/master/home.asm): - -```diff --Unreferenced_CheckBPressedDebug:: --; Used in debug ROMs to walk through walls and avoid encounters. -- -- ld a, [wDebugFlags] -- bit DEBUG_FIELD_F, a -- ret z -- -- ldh a, [hJoyDown] -- bit B_BUTTON_F, a -- ret - - xor_a:: - xor a - ret - - xor_a_dec_a:: - xor a - dec a - ret - --Unreferenced_CheckFieldDebug:: -- push hl -- ld hl, wDebugFlags -- bit DEBUG_FIELD_F, [hl] -- pop hl -- ret -``` +- `Unreferenced_Function2816` in [home/map.asm](../blob/master/home/map.asm) (again) +- `Unreferenced_Function3d9f` in [home/audio.asm](../blob/master/home/audio.asm) +- `Unreferenced_Function48c` in [home/fade.asm](../blob/master/home/fade.asm) +- `Unreferenced_Function547` in [home/lcd.asm](../blob/master/home/lcd.asm) +- `Unreferenced_Function19b8` in [home/map_objects.asm](../blob/master/home/map_objects.asm) +- `Unreferenced_Function1f9e` in [home/menu.asm](../blob/master/home/menu.asm) +- `Unreferenced_Function3ed7` and `Unreferenced_Function3efd` in [home/mobile.asm](../blob/master/home/mobile.asm) +- `Unreferenced_GetNthMove` in [home/mon_data.asm](../blob/master/home/mon_data.asm) +- `Unreferenced_GetDexNumber` in [home/mon_data_2.asm](../blob/master/home/mon_data_2.asm) +- `Unreferenced_Function919` in [home/serial.asm](../blob/master/home/serial.asm) +- `Unreferenced_Function1522` in [home/text.asm](../blob/master/home/text.asm) +- `Unreferenced_CheckBPressedDebug` and `Unreferenced_CheckFieldDebug` in [home.asm](../blob/master/home.asm) *Now* we're done! `make` works, and all the maps look the same as before—but now they're capable of looking very different. diff --git a/Expand-tilesets-from-192-to-255-tiles.md b/Expand-tilesets-from-192-to-255-tiles.md index b76df85..6e945e4 100644 --- a/Expand-tilesets-from-192-to-255-tiles.md +++ b/Expand-tilesets-from-192-to-255-tiles.md @@ -51,7 +51,8 @@ Note that the textbox frames aren't present in these graphics. That's because th ## 2. Update the character set -Edit [charmap.asm](../blob/master/charmap.asm): +<details> +<summary>Edit [charmap.asm](../blob/master/charmap.asm): (click to expand)</summary> ```diff -; Actual characters (from gfx/font/font_extra.png) @@ -171,6 +172,7 @@ Edit [charmap.asm](../blob/master/charmap.asm): charmap ",", $f4 charmap "♀", $f5 ``` +</details> ## 3. Load the updated font graphics diff --git a/Improve-the-outdoor-sprite-system.md b/Improve-the-outdoor-sprite-system.md index 6eea597..7e17b06 100644 --- a/Improve-the-outdoor-sprite-system.md +++ b/Improve-the-outdoor-sprite-system.md @@ -1,16 +1,433 @@ In [the tutorial to add a new map](Add-a-new-map-and-landmark), we covered the concept of **outdoor sprite sets**. Outdoor maps—those with a `TOWN` or `ROUTE` environment—can only use sprites from their map group's set of usable sprites. -The outdoor sprite sets are defined in [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm). However, they're done in a way that's difficult to edit: each set has 23 sprites defined, even if not all of them are used. Furthermore, given the way VRAM works, there's only room for nine sprites to have walking frames, but it's not clear which nine sprites in a set will end up in VRAM bank 1 and have their walking graphics loaded. +The outdoor sprite sets are defined in [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm). For example, here's the one for Olivine City's map group: -This tutorial will improve the outdoor sprite sets by making them variable-length lists ending in 0, instead of needing to have 23 entries. The existing sprite sets for pokecrystal's maps will be optimized to work with this new format. +``` +OlivineGroupSprites: + db SPRITE_SUICUNE + db SPRITE_SILVER_TROPHY + db SPRITE_FAMICOM + db SPRITE_POKEDEX + db SPRITE_WILL + db SPRITE_KAREN + db SPRITE_NURSE + db SPRITE_OLD_LINK_RECEPTIONIST + db SPRITE_STANDING_YOUNGSTER + db SPRITE_BIG_ONIX + db SPRITE_SUDOWOODO + db SPRITE_BIG_SNORLAX + db SPRITE_OLIVINE_RIVAL + db SPRITE_POKEFAN_M + db SPRITE_LASS + db SPRITE_BUENA + db SPRITE_SWIMMER_GIRL + db SPRITE_SAILOR + db SPRITE_POKEFAN_F + db SPRITE_SUPER_NERD + db SPRITE_TAUROS + db SPRITE_FRUIT_TREE + db SPRITE_ROCK +``` + +We can see how those get loaded with [BGB](http://bgb.bircd.org/)'s VRAM viewer: + + + +VRAM is divided into six areas, each 128 tiles large. The top two areas are for sprites' standing frames. The middle-right area is for the walking frames of the sprites in the top-right. But the middle-left area is for font tiles, so the top-left sprites can't walk. (If they do, they'll appear as text tiles.) + +As they're currently implemented, these sprite sets are hard to edit. Every set has 23 sprites, and it's hard to tell which ones are needed for which map. It's also not clear which sprites get placed in VRAM bank 1 (the one on the right in the VRAM viewer). There's only enough room for nine sprites to have walking frames, but those nine are not in any particular order, nor does the order of the list correspond to the order in VRAM. + +This tutorial will improve the outdoor sprite sets by making them variable-length lists ending with 0, and with the first nine sprites being the ones to get walking frames. The existing sprite sets for pokecrystal's maps will be optimized to work with this new format. ## TOC -TODO +## 1. Make outdoor sprite sets variable-length, ending with 0 +Edit [constants/map_data_constants.asm](../blob/master/constants/map_data_constants.asm): - +```diff +-MAX_OUTDOOR_SPRITES EQU 23 ; see engine/overworld/overworld.asm +``` + +We won't need `MAX_OUTDOOR_SPRITES` any more. + +Next, edit [engine/overworld/overworld.asm](../blob/master/engine/overworld/overworld.asm): + +```diff + AddOutdoorSprites: + ld a, [wMapGroup] + dec a + ld c, a + ld b, 0 + ld hl, OutdoorSprites + add hl, bc + add hl, bc + ld a, [hli] + ld h, [hl] + ld l, a +- ld c, MAX_OUTDOOR_SPRITES + .loop +- push bc + ld a, [hli] ++ and a ++ ret z + call AddSpriteGFX +- pop bc +- dec c +- jr nz, .loop +- ret ++ jr .loop +``` + +Instead of counting `c` down from `MAX_OUTDOOR_SPRITES` to 0, now `AddOutdoorSprites` will continue until it finds a 0 list entry. (So don't forget to add them! We'll do so later.) + +Now that outdoor sprite lists can be arbitrarily long, it's more important to enforce the limit of how much RAM is even available. However, there happens to be a bug with the `LoadSpriteGFX` routine that ignores the `SPRITE_GFX_LIST_CAPACITY` limit. So be sure to [fix that](../blob/master/docs/bugs_and_glitches.md#loadspritegfx-does-not-limit-the-capacity-of-usedsprites). + + +## 2. Don't automatically sort outdoor sprite sets + +As we saw in earlier, the current sprite lists are not in any particular order. It turns out that the `LoadAndSortSprites` routine sorts the lists before loading their graphics, in order of how many tiles each one has, from most to least. Most NPC sprites have 12 tiles (four each for the front, back, and side views), so they get sorted first, and then come the still sprites like `SPRITE_POKE_BALL`, `SPRITE_FRUIT_TREE`, etc. + +(Each outdoor sprite list gets padded to 23 entries with a bunch of still sprites like `SPRITE_SILVER_TROPHY` or `SPRITE_OLD_LINK_RECEPTIONIST`. They're not used in any outdoor map, but they have to be there so the walking sprites get sorted first.) + +Anyway, edit [engine/overworld/overworld.asm](../blob/master/engine/overworld/overworld.asm) again: + +```diff + LoadAndSortSprites: + call LoadSpriteGFX +- call SortUsedSprites + call ArrangeUsedSprites + ret + + ... + +-SortUsedSprites: +-; Bubble-sort sprites by type. +- +- ... +- +-.quit +- ret +``` + +Now sprites will be loaded into VRAM in whatever order the list specifies. So if we want a certain nine sprites to have walking frames available, we'll have to put them first. + + +## 3. Update the outdoor sprite sets + +If you're replacing all of Crystal's maps with your own, you won't need these exact sets; but they're a good reference anyway for how the new sets work. + +<details> +<summary>Edit [data/maps/outdoor_sprites.asm](../blob/master/data/maps/outdoor_sprites.asm): (click to expand)</summary> + +```diff +-PalletGroupSprites: +- ... +- +-... +- +-CableClubGroupSprites: +- ... ++; Route1 and ViridianCity are connected ++; Route2 and PewterCity are connected ++; PalletTown and Route21 are connected ++PalletGroupSprites: ++; Route1, PalletTown ++ViridianGroupSprites: ++; Route2, Route22, ViridianCity ++PewterGroupSprites: ++; Route3, PewterCity ++CinnabarGroupSprites: ++; Route19, Route20, Route21, CinnabarIsland ++ db SPRITE_TEACHER ++ db SPRITE_FISHER ++ db SPRITE_YOUNGSTER ++ db SPRITE_BLUE ++ db SPRITE_GRAMPS ++ db SPRITE_BUG_CATCHER ++ db SPRITE_COOLTRAINER_F ++ db SPRITE_SWIMMER_GIRL ++ db SPRITE_SWIMMER_GUY ++ ; limit of 9 walking sprites ++ db SPRITE_POKE_BALL ++ db SPRITE_FRUIT_TREE ++ db 0 ; end ++ ++; CeruleanCity and Route5 are connected ++CeruleanGroupSprites: ++; Route4, Route9, Route10North, Route24, Route25, CeruleanCity ++SaffronGroupSprites: ++; Route5, SaffronCity ++ db SPRITE_COOLTRAINER_M ++ db SPRITE_SUPER_NERD ++ db SPRITE_COOLTRAINER_F ++ db SPRITE_FISHER ++ db SPRITE_YOUNGSTER ++ db SPRITE_LASS ++ db SPRITE_POKEFAN_M ++ db SPRITE_ROCKET ++ db SPRITE_MISTY ++ ; limit of 9 walking sprites ++ db SPRITE_POKE_BALL ++ db SPRITE_SLOWPOKE ++ db 0 ; end ++ ++VermilionGroupSprites: ++; Route6, Route11, VermilionCity ++ db SPRITE_POKEFAN_M ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_TEACHER ++ db SPRITE_SUPER_NERD ++ ; 5 of max 9 walking sprites ++ db SPRITE_BIG_SNORLAX ++ db SPRITE_MACHOP ++ db SPRITE_POKE_BALL ++ db SPRITE_FRUIT_TREE ++ db 0 ; end ++ ++CeladonGroupSprites: ++; Route7, Route16, Route17, CeladonCity ++ db SPRITE_FISHER ++ db SPRITE_TEACHER ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_LASS ++ db SPRITE_BIKER ++ ; 6 of max 9 walking sprites ++ db SPRITE_POLIWAG ++ db SPRITE_POKE_BALL ++ db SPRITE_FRUIT_TREE ++ db 0 ; end ++ ++; Route12 and Route13 are connected ++LavenderGroupSprites: ++; Route8, Route12, Route10South, LavenderTown ++FuchsiaGroupSprites: ++; Route13, Route14, Route15, Route18, FuchsiaCity ++ db SPRITE_POKEFAN_M ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_FISHER ++ db SPRITE_TEACHER ++ db SPRITE_SUPER_NERD ++ db SPRITE_BIKER ++ ; 7 of max 9 walking sprites ++ db SPRITE_POKE_BALL ++ db SPRITE_FRUIT_TREE ++ db 0 ; end ++ ++IndigoGroupSprites: ++; Route23 ++ ; 0 of max 9 walking sprites ++ db 0 ; end ++ ++; Route29 and CherrygroveCity are connected ++NewBarkGroupSprites: ++; Route26, Route27, Route29, NewBarkTown ++CherrygroveGroupSprites: ++; Route30, Route31, CherrygroveCity ++ db SPRITE_SILVER ++ db SPRITE_TEACHER ++ db SPRITE_FISHER ++ db SPRITE_COOLTRAINER_M ++ db SPRITE_YOUNGSTER ++ db SPRITE_MONSTER ++ db SPRITE_GRAMPS ++ db SPRITE_BUG_CATCHER ++ db SPRITE_COOLTRAINER_F ++ ; limit of 9 walking sprites ++ db SPRITE_POKE_BALL ++ db SPRITE_FRUIT_TREE ++ db 0 ; end ++ ++; Route37 and EcruteakCity are connected ++VioletGroupSprites: ++; Route32, Route35, Route36, Route37, VioletCity ++EcruteakGroupSprites: ++; EcruteakCity ++ db SPRITE_FISHER ++ db SPRITE_LASS ++ db SPRITE_OFFICER ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_COOLTRAINER_M ++ db SPRITE_BUG_CATCHER ++ db SPRITE_SUPER_NERD ++ ; 8 of max 9 walking sprites ++ db SPRITE_WEIRD_TREE ; variable sprite: becomes SPRITE_SUDOWOODO and SPRITE_TWIN ++ db SPRITE_POKE_BALL ++ db SPRITE_FRUIT_TREE ++ db SPRITE_SUICUNE ++ db 0 ; end ++ ++AzaleaGroupSprites: ++; Route33, AzaleaTown ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_POKEFAN_M ++ db SPRITE_TEACHER ++ db SPRITE_AZALEA_ROCKET ; variable sprite: becomes SPRITE_ROCKET and SPRITE_SILVER ++ db SPRITE_LASS ++ ; 6 of max 9 walking sprites ++ db SPRITE_FRUIT_TREE ++ db SPRITE_SLOWPOKE ++ db SPRITE_KURT_OUTSIDE ; non-walking version of SPRITE_KURT ++ db 0 ; end ++ ++GoldenrodGroupSprites: ++; Route34, GoldenrodCity ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_OFFICER ++ db SPRITE_POKEFAN_M ++ db SPRITE_COOLTRAINER_F ++ db SPRITE_ROCKET ++ db SPRITE_LASS ++ ; 7 of max 9 walking sprites ++ db SPRITE_DAY_CARE_MON_1 ++ db SPRITE_DAY_CARE_MON_2 ++ db SPRITE_POKE_BALL ++ db 0 ; end ++ ++; OlivineCity and Route40 are connected ++OlivineGroupSprites: ++; Route38, Route39, OlivineCity ++CianwoodGroupSprites: ++; Route40, Route41, CianwoodCity, BattleTowerOutside ++ db SPRITE_OLIVINE_RIVAL; variable sprite: becomes SPRITE_SILVER and SPRITE_SWIMMER_GUY ++ db SPRITE_POKEFAN_M ++ db SPRITE_LASS ++ db SPRITE_BUENA ++ db SPRITE_SWIMMER_GIRL ++ db SPRITE_SAILOR ++ db SPRITE_POKEFAN_F ++ db SPRITE_SUPER_NERD ++ ; 8 of max 9 walking sprites ++ db SPRITE_TAUROS ++ db SPRITE_FRUIT_TREE ++ db SPRITE_ROCK ++ db SPRITE_STANDING_YOUNGSTER ; non-walking version of SPRITE_YOUNGSTER ++ db SPRITE_SUICUNE ++ db 0 ; end ++ ++MahoganyGroupSprites: ++; Route42, Route44, MahoganyTown ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_LASS ++ db SPRITE_SUPER_NERD ++ db SPRITE_COOLTRAINER_M ++ db SPRITE_POKEFAN_M ++ db SPRITE_COOLTRAINER_F ++ db SPRITE_FISHER ++ ; 8 of max 9 walking sprites ++ db SPRITE_FRUIT_TREE ++ db SPRITE_POKE_BALL ++ db SPRITE_SUICUNE ++ db 0 ; end ++ ++LakeOfRageGroupSprites: ++; Route43, LakeOfRage ++ db SPRITE_LANCE ++ db SPRITE_GRAMPS ++ db SPRITE_SUPER_NERD ++ db SPRITE_COOLTRAINER_F ++ db SPRITE_FISHER ++ db SPRITE_COOLTRAINER_M ++ db SPRITE_LASS ++ db SPRITE_YOUNGSTER ++ ; 8 of max 9 walking sprites ++ db SPRITE_GYARADOS ++ db SPRITE_FRUIT_TREE ++ db SPRITE_POKE_BALL ++ db 0 ; end ++ ++BlackthornGroupSprites: ++; Route45, Route46, BlackthornCity ++ db SPRITE_GRAMPS ++ db SPRITE_YOUNGSTER ++ db SPRITE_LASS ++ db SPRITE_SUPER_NERD ++ db SPRITE_COOLTRAINER_M ++ db SPRITE_POKEFAN_M ++ db SPRITE_BLACK_BELT ++ db SPRITE_COOLTRAINER_F ++ ; 8 of max 9 walking sprites ++ db SPRITE_FRUIT_TREE ++ db SPRITE_POKE_BALL ++ db 0 ; end ++ ++SilverGroupSprites: ++; Route28, SilverCaveOutside ++ ; 0 of max 9 walking sprites ++ db 0 ; end ++ ++DungeonsGroupSprites: ++; NationalPark, NationalParkBugContest, RuinsOfAlphOutside ++ db SPRITE_LASS ++ db SPRITE_POKEFAN_F ++ db SPRITE_TEACHER ++ db SPRITE_YOUNGSTER ++ db SPRITE_POKEFAN_M ++ db SPRITE_ROCKER ++ db SPRITE_FISHER ++ db SPRITE_SCIENTIST ++ ; 8 of max 9 walking sprites ++ db SPRITE_GAMEBOY_KID ++ db SPRITE_GROWLITHE ++ db SPRITE_POKE_BALL ++ db 0 ; end ++ ++FastShipGroupSprites: ++; OlivinePort, VermilionPort, MountMoonSquare, TinTowerRoof ++ db SPRITE_SAILOR ++ db SPRITE_FISHING_GURU ++ db SPRITE_SUPER_NERD ++ db SPRITE_COOLTRAINER_F ++ db SPRITE_YOUNGSTER ++ ; 5 of max 9 walking sprites ++ db SPRITE_HO_OH ++ db SPRITE_FAIRY ++ db SPRITE_ROCK ++ db 0 ; end ++ ++CableClubGroupSprites: ++; (no outdoor maps) ++ ; 0 of max 9 walking sprites ++ db 0 ; end +``` +</details> + +Now we're done! These new sets are easier to define and debug than before. For example, here's the one for Olivine City's map group: + +``` +; OlivineCity and Route40 are connected +OlivineGroupSprites: +; Route38, Route39, OlivineCity +CianwoodGroupSprites: +; Route40, Route41, CianwoodCity, BattleTowerOutside + db SPRITE_OLIVINE_RIVAL; variable sprite: becomes SPRITE_SILVER and SPRITE_SWIMMER_GUY + db SPRITE_POKEFAN_M + db SPRITE_LASS + db SPRITE_BUENA + db SPRITE_SWIMMER_GIRL + db SPRITE_SAILOR + db SPRITE_POKEFAN_F + db SPRITE_SUPER_NERD + ; 8 of max 9 walking sprites + db SPRITE_TAUROS + db SPRITE_FRUIT_TREE + db SPRITE_ROCK + db SPRITE_STANDING_YOUNGSTER ; non-walking version of SPRITE_YOUNGSTER + db SPRITE_SUICUNE + db 0 ; end +``` + +And here's how they get loaded into VRAM:  + +The comments make it clear which maps the set applies to; and the order matches their order in VRAM, from top to bottom, right and then left. diff --git a/Replace-stat-experience-with-EVs.md b/Replace-stat-experience-with-EVs.md index ef24100..1504e78 100644 --- a/Replace-stat-experience-with-EVs.md +++ b/Replace-stat-experience-with-EVs.md @@ -213,7 +213,8 @@ That will format all the base data files correctly, but with zero EV yields. You ## 3. Gain EVs from winning battles -Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm): +<details> +<summary>Edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm): (click to expand)</summary> ```diff GiveExperiencePoints: @@ -323,6 +324,7 @@ GiveExperiencePoints: + jr .ev_loop +.evs_done ``` +</details> Now instead of gaining the enemy's base stats toward your stat experience, you'll gain their base EV yields toward your EV totals. Having Pokérus will double EV gain. |