diff options
-rw-r--r-- | Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md | 778 | ||||
-rw-r--r-- | Tutorials.md | 4 | ||||
-rw-r--r-- | screenshots/enemy-dvs.png | bin | 0 -> 2101 bytes | |||
-rw-r--r-- | screenshots/enemy-nicknames.png | bin | 0 -> 2249 bytes |
4 files changed, 779 insertions, 3 deletions
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 new file mode 100644 index 0000000..70b2edf --- /dev/null +++ b/Allow-more-trainer-parties,-with-individual-DVs,-stat-experience,-and-nicknames.md @@ -0,0 +1,778 @@ +The NPC trainer teams in pokecrystal are fairly limited: their Pokémon can hold items and have custom movesets, but they cannot have nicknames, custom DVs (so enemy Pokémon can't be shiny or have well-typed Hidden Power), or custom stat experience. The trainer party data is also stored in a single ROM bank, which limits how many teams you can have; and the code for reading teams is repetitive and hard to edit. + +This tutorial will fix all of those problems. + + +## TOC + + +## 1. Refactor trainer types to use bit flags + +Each enemy trainer has one to six Pokémon, with individual data depending on the trainer type: + +- `TRAINERTYPE_NORMAL`: level, species +- `TRAINERTYPE_MOVES`: level, species, four moves +- `TRAINERTYPE_ITEM`: level, species, held item +- `TRAINERTYPE_ITEM_MOVES`: level, species, held item, four moves + +Clearly there's a lot of shared data across those four types. But if you look at `ReadTrainerParty` in [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm), you'll see that each trainer type has a totally separate routine for reading their data, so there are four identical chunks of code for reading the level and species, two for the moves, and two for the items. + +An alternative is to treat the trainer type byte as a set of *bit flags*. Without any bits set, their Pokémon will just have a level and species; but then one bit will toggle reading of held items, one for movesets, and six more bits will be available for other new kinds of data. + +Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_data_constants.asm): + +```diff +-; TrainerTypes indexes (see engine/battle/read_trainer_party.asm) +- const_def +- const TRAINERTYPE_NORMAL +- const TRAINERTYPE_MOVES +- const TRAINERTYPE_ITEM +- const TRAINERTYPE_ITEM_MOVES ++; TrainerTypes bits (see engine/battle/read_trainer_party.asm) ++ const_def ++ const TRAINERTYPE_MOVES_F ; 0 ++ const TRAINERTYPE_ITEM_F ; 1 ++ ++; Trainer party types (see data/trainers/parties.asm) ++TRAINERTYPE_NORMAL EQU 0 ++TRAINERTYPE_MOVES EQU 1 << TRAINERTYPE_MOVES_F ++TRAINERTYPE_ITEM EQU 1 << TRAINERTYPE_ITEM_F ++TRAINERTYPE_ITEM_MOVES EQU TRAINERTYPE_MOVES | TRAINERTYPE_ITEM +``` + +Ironically, the numeric values of the `TRAINERTYPE_*` constants haven't even changed. They still go from 0 to 3. + +Edit [wram.asm](../blob/master/wram.asm): + +```diff +- ds 3 ++wOtherTrainerType:: db ++ ++ ds 2 + + wLinkBattleRNs:: ds 10 ; d1fa + + ... + + wOtherTrainerClass:: ; d22f + ; class (Youngster, Bug Catcher, etc.) of opposing trainer + ; 0 if opponent is a wild Pokémon, not a trainer + db +``` + +The `wOtherTrainerType` byte will store the trainer type while their data is being read. + +Finally, edit [engine/battle/read_trainer_party.asm](../blob/master/engine/battle/read_trainer_party.asm): + +```diff + ReadTrainerParty: + ... + + ld a, [hli] +- ld c, a +- ld b, 0 ++ ld [wOtherTrainerType], a + ld d, h + ld e, l +- ld hl, TrainerTypes +- add hl, bc +- add hl, bc +- ld a, [hli] +- ld h, [hl] +- ld l, a +- ld bc, .done +- push bc +- jp hl + call ReadTrainerPartyPieces +- + .done + jp ComputeTrainerReward + + .cal2 + ld a, BANK(sMysteryGiftTrainer) + call GetSRAMBank ++ ld a, TRAINERTYPE_MOVES ++ ld [wOtherTrainerType], a + ld de, sMysteryGiftTrainer +- call TrainerType2 ++ call ReadTrainerPartyPieces + call CloseSRAM + jr .done + +-TrainerTypes: +-; entries correspond to TRAINERTYPE_* constants +- dw TrainerType1 ; level, species +- dw TrainerType2 ; level, species, moves +- dw TrainerType3 ; level, species, item +- dw TrainerType4 ; level, species, item, moves +- +-TrainerType1: +-; normal (level, species) +- ld h, d +- ld l, e +-.loop +- ... +- jr .loop +- +-TrainerType2: +-; moves +- ld h, d +- ld l, e +-.loop +- ... +- jr .loop +- +-TrainerType3: +-; item +- ld h, d +- ld l, e +-.loop +- ... +- jr .loop +- +-TrainerType4: +-; item + moves +- ld h, d +- ld l, e +-.loop +- ... +- jr .loop ++ReadTrainerPartyPieces: ++ ld h, d ++ ld l, e ++ ++.loop ++; end? ++ ld a, [hli] ++ cp -1 ++ ret z ++ ++; level ++ ld [wCurPartyLevel], a ++ ++; species ++ ld a, [hli] ++ ld [wCurPartySpecies], a ++ ++; add to party ++ ld a, OTPARTYMON ++ ld [wMonType], a ++ push hl ++ predef TryAddMonToParty ++ pop hl ++ ++; item? ++ ld a, [wOtherTrainerType] ++ bit TRAINERTYPE_ITEM_F, a ++ jr z, .no_item ++ ++ push hl ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1Item ++ call GetPartyLocation ++ ld d, h ++ ld e, l ++ pop hl ++ ++ ld a, [hli] ++ ld [de], a ++.no_item ++ ++; moves? ++ ld a, [wOtherTrainerType] ++ bit TRAINERTYPE_MOVES_F, a ++ jr z, .no_moves ++ ++ push hl ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1Moves ++ call GetPartyLocation ++ ld d, h ++ ld e, l ++ pop hl ++ ++ ld b, NUM_MOVES ++.copy_moves ++ ld a, [hli] ++ ld [de], a ++ inc de ++ dec b ++ jr nz, .copy_moves ++ ++ push hl ++ ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1 ++ call GetPartyLocation ++ ld d, h ++ ld e, l ++ ld hl, MON_PP ++ add hl, de ++ ++ push hl ++ ld hl, MON_MOVES ++ add hl, de ++ pop de ++ ++ ld b, NUM_MOVES ++.copy_pp ++ ld a, [hli] ++ and a ++ jr z, .copied_pp ++ ++ push hl ++ push bc ++ dec a ++ ld hl, Moves + MOVE_PP ++ ld bc, MOVE_LENGTH ++ call AddNTimes ++ ld a, BANK(Moves) ++ call GetFarByte ++ pop bc ++ pop hl ++ ++ ld [de], a ++ inc de ++ dec b ++ jr nz, .copy_pp ++.copied_pp ++ ++ pop hl ++.no_moves ++ ++ jp .loop +``` + +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. + +If we stopped here, the code would be cleaner and smaller, but would not do anything new. So let's continue. + + +## 2. Add a trainer type flag for nicknames + +This will allow enemy trainer parties to define nicknames for their Pokémon. + +Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_data_constants.asm) again: + +```diff + ; TrainerTypes bits (see engine/battle/read_trainer_party.asm) + const_def + const TRAINERTYPE_MOVES_F ; 0 + const TRAINERTYPE_ITEM_F ; 1 ++ const TRAINERTYPE_NICKNAME_F ; 2 + + ; Trainer party types (see data/trainers/parties.asm) + TRAINERTYPE_NORMAL EQU 0 + TRAINERTYPE_MOVES EQU 1 << TRAINERTYPE_MOVES_F + TRAINERTYPE_ITEM EQU 1 << TRAINERTYPE_ITEM_F + TRAINERTYPE_ITEM_MOVES EQU TRAINERTYPE_MOVES | TRAINERTYPE_ITEM ++TRAINERTYPE_NICKNAME EQU 1 << TRAINERTYPE_NICKNAME_F +``` + +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: + +```diff + ; add to party + ld a, OTPARTYMON + ld [wMonType], a + push hl + predef TryAddMonToParty + pop hl ++ ++; nickname? ++ ld a, [wOtherTrainerType] ++ bit TRAINERTYPE_NICKNAME_F, a ++ jr z, .no_nickname ++ ++ push de ++ ++ ld de, wStringBuffer2 ++.copy_nickname ++ ld a, [hli] ++ ld [de], a ++ inc de ++ cp "@" ++ jr nz, .copy_nickname ++ ++ push hl ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMonNicknames ++ ld bc, MON_NAME_LENGTH ++ call AddNTimes ++ ld d, h ++ ld e, l ++ ld hl, wStringBuffer2 ++ ld bc, MON_NAME_LENGTH ++ call CopyBytes ++ pop hl ++ ++ pop de ++.no_nickname + + ; item? + ... +``` + +Then edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm): + +```diff +LoadEnemyMon: + ... + + ld a, [wTempEnemyMonSpecies] + ld [wd265], a + +- call GetPokemonName +- + ; Did we catch it? + ld a, [wBattleMode] + and a + ret z + + ; Update enemy nick ++ ld a, [wBattleMode] ++ dec a ; WILD_BATTLE? ++ jr z, .no_nickname ++ ld a, [wOtherTrainerType] ++ bit TRAINERTYPE_NICKNAME_F, a ++ jr z, .no_nickname ++ ld a, [wCurPartyMon] ++ ld hl, wOTPartyMonNicknames ++ ld bc, MON_NAME_LENGTH ++ call AddNTimes ++ jr .got_nickname ++.no_nickname ++ call GetPokemonName + ld hl, wStringBuffer1 ++.got_nickname + ld de, wEnemyMonNick + ld bc, MON_NAME_LENGTH + call CopyBytes +``` + +Now you can give nicknames to enemy Pokémon. Be sure to keep the data in order: level, species, nickname, held item, moves. + +For example, these are parties for your rival that give him nicknames, held items, and a new Pokémon: + +``` +Rival1Group: + ; RIVAL1 (1) + db "?@", TRAINERTYPE_NICKNAME | TRAINERTYPE_ITEM + db 3, RATTATA, "RATTATA@", NO_ITEM + db 5, CHIKORITA, "ROOT@", BERRY + db -1 ; end + + ; RIVAL1 (2) + db "?@", TRAINERTYPE_NICKNAME | TRAINERTYPE_ITEM + db 3, RATTATA, "RATTATA@", NO_ITEM + db 5, CYNDAQUIL, "BLAZE@", BERRY + db -1 ; end + + ; RIVAL1 (3) + db "?@", TRAINERTYPE_NICKNAME | TRAINERTYPE_ITEM + db 3, RATTATA, "RATTATA@", NO_ITEM + db 5, TOTODILE, "JAWS@", BERRY + db -1 ; end + + ... +``` + +Which successfully loads in battle: + + + +Note that since −1 ($FF) is the end-of-party marker, you can't use the digit "9" in nicknames because it's equal to $FF (as seen in [charmap.asm](../blob/master/charmap.asm)). If you really need a nickname with "9" in it, you can add a duplicate character that looks just like "9", for example by editing the "『" in [gfx/font/font_battle_extra.png](../blob/master/gfx/font/font_battle_extra.png) (since "『" is unused character $72). + + +## 3. Add a trainer type flag for DVs + +This will allow enemy trainer parties to define individual DVs for their Pokémon, which not only affects their stats, but also gender, shininess, and Hidden Power type. + +Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_data_constants.asm) again: + +```diff + ; TrainerTypes bits (see engine/battle/read_trainer_party.asm) + const_def + const TRAINERTYPE_MOVES_F ; 0 + const TRAINERTYPE_ITEM_F ; 1 + const TRAINERTYPE_NICKNAME_F ; 2 ++ const TRAINERTYPE_DVS_F ; 3 + + ; Trainer party types (see data/trainers/parties.asm) + TRAINERTYPE_NORMAL EQU 0 + TRAINERTYPE_MOVES EQU 1 << TRAINERTYPE_MOVES_F + TRAINERTYPE_ITEM EQU 1 << TRAINERTYPE_ITEM_F + TRAINERTYPE_ITEM_MOVES EQU TRAINERTYPE_MOVES | TRAINERTYPE_ITEM + TRAINERTYPE_NICKNAME EQU 1 << TRAINERTYPE_NICKNAME_F ++TRAINERTYPE_DVS EQU 1 << TRAINERTYPE_DVS_F ++ ++PERFECT_DV EQU $11 ; treated as $FF in enemy party data +``` + +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: + +```diff + ; add to party + ld a, OTPARTYMON + ld [wMonType], a + push hl + predef TryAddMonToParty + pop hl + + ; nickname? + ld a, [wOtherTrainerType] + bit TRAINERTYPE_NICKNAME_F, a + jr z, .no_nickname + ... + .no_nickname ++ ++; dvs? ++ ld a, [wOtherTrainerType] ++ bit TRAINERTYPE_DVS_F, a ++ jr z, .no_dvs ++ ++ push hl ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1DVs ++ call GetPartyLocation ++ ld d, h ++ ld e, l ++ pop hl ++ ++; When reading DVs, treat PERFECT_DV as $ff ++ ld a, [hli] ++ cp PERFECT_DV ++ jr nz, .atk_def_dv_ok ++ ld a, $ff ++.atk_def_dv_ok ++ ld [de], a ++ inc de ++ ld a, [hli] ++ cp PERFECT_DV ++ jr nz, .spd_spc_dv_ok ++ ld a, $ff ++.spd_spc_dv_ok ++ ld [de], a ++.no_dvs + + ; item? + ... + + .no_moves ++ ++; Custom DVs affect stats, so recalculate them after TryAddMonToParty ++ ld a, [wOtherTrainerType] ++ and TRAINERTYPE_DVS ++ jr z, .no_stat_recalc ++ ++ push hl ++ ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1MaxHP ++ call GetPartyLocation ++ ld d, h ++ ld e, l ++ ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1StatExp - 1 ++ call GetPartyLocation ++ ++; recalculate stats ++ ld b, TRUE ++ push de ++ predef CalcMonStats ++ pop hl ++ ++; copy max HP to current HP ++ inc hl ++ ld c, [hl] ++ dec hl ++ ld b, [hl] ++ dec hl ++ ld [hl], c ++ dec hl ++ ld [hl], b ++ ++ pop hl ++.no_stat_recalc + + jp .loop +``` + +Then edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm) again: + +```diff + .InitDVs: +-; Trainer DVs +- +-; All trainers have preset DVs, determined by class +-; See GetTrainerDVs for more on that +- farcall GetTrainerDVs +-; These are the DVs we'll use if we're actually in a trainer battle + ld a, [wBattleMode] + dec a +- jr nz, .UpdateDVs ++ jr z, .WildDVs ++ ++; Trainer DVs ++ ld a, [wCurPartyMon] ++ ld hl, wOTPartyMon1DVs ++ call GetPartyLocation ++ ld b, [hl] ++ inc hl ++ ld c, [hl] ++ jr .UpdateDVs + ++.WildDVs: + ; Wild DVs + ... +``` + +Now you can give custom DVs to enemy Pokémon. Be sure to keep the data in order: level, species, nickname, DVs, held item, moves. + +DVs are specified as `$AD, $SP`, where *A* = attack, *D* = defense, *S* = speed, and *P* = special, with each one going from $0 to $F (15). + +For example, these are parties for your rival that give him custom DVs and held items: + +``` +Rival1Group: + ; RIVAL1 (1) + db "?@", TRAINERTYPE_DVS | TRAINERTYPE_ITEM + db 3, RATTATA, PERFECT_DV, PERFECT_DV, NO_ITEM ; top percentage + db 5, CHIKORITA, ATKDEFDV_SHINY, SPDSPCDV_SHINY, BERRY + db -1 ; end + + ; RIVAL1 (2) + db "?@", TRAINERTYPE_DVS | TRAINERTYPE_ITEM + db 3, RATTATA, PERFECT_DV, PERFECT_DV, NO_ITEM ; top percentage + db 5, CYNDAQUIL, ATKDEFDV_SHINY, SPDSPCDV_SHINY, BERRY + db -1 ; end + + ; RIVAL1 (3) + db "?@", TRAINERTYPE_DVS | TRAINERTYPE_ITEM + db 3, RATTATA, PERFECT_DV, PERFECT_DV, NO_ITEM ; top percentage + db 5, TOTODILE, ATKDEFDV_SHINY, SPDSPCDV_SHINY, BERRY + db -1 ; end + + ... +``` + +Which successfully loads in battle: + + + +Note that since −1 ($FF) is the end-of-party marker, you can't use $FF for any DVs. That's why `PERFECT_DV` gets turned into $FF, as explained in the comments. It's defined as $11 since you're unlikely to want those specific DVs, but you can use any value for it. If you want to use $00, you should also replace the two `cp PERFECT_DV` lines with `and a` since that's a more efficient way to check for zero. + + +## 4. Add a trainer type flag for stat experience + +This will allow enemy trainer parties to define individual stat experience for their Pokémon, which lets you increase the difficulty better than just raising levels. + +Edit [constants/trainer_data_constants.asm](../blob/master/constants/trainer_data_constants.asm) again: + +```diff + ; TrainerTypes bits (see engine/battle/read_trainer_party.asm) + const_def + const TRAINERTYPE_MOVES_F ; 0 + const TRAINERTYPE_ITEM_F ; 1 + const TRAINERTYPE_NICKNAME_F ; 2 + const TRAINERTYPE_DVS_F ; 3 ++ const TRAINERTYPE_STAT_EXP_F ; 4 + + ; Trainer party types (see data/trainers/parties.asm) + TRAINERTYPE_NORMAL EQU 0 + TRAINERTYPE_MOVES EQU 1 << TRAINERTYPE_MOVES_F + TRAINERTYPE_ITEM EQU 1 << TRAINERTYPE_ITEM_F + TRAINERTYPE_ITEM_MOVES EQU TRAINERTYPE_MOVES | TRAINERTYPE_ITEM + TRAINERTYPE_NICKNAME EQU 1 << TRAINERTYPE_NICKNAME_F + TRAINERTYPE_DVS EQU 1 << TRAINERTYPE_DVS_F ++TRAINERTYPE_STAT_EXP EQU 1 << TRAINERTYPE_STAT_EXP_F + + PERFECT_DV EQU $11 ; treated as $FF in enemy party data ++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: + +```diff + ; add to party + ld a, OTPARTYMON + ld [wMonType], a + push hl + predef TryAddMonToParty + pop hl + + ; nickname? + ld a, [wOtherTrainerType] + bit TRAINERTYPE_NICKNAME_F, a + jr z, .no_nickname + ... + .no_nickname + + ; dvs? + ld a, [wOtherTrainerType] + bit TRAINERTYPE_DVS_F, a + jr z, .no_dvs + ... + .no_dvs ++ ++; stat exp? ++ ld a, [wOtherTrainerType] ++ bit TRAINERTYPE_STAT_EXP_F, a ++ jr z, .no_stat_exp ++ ++ push hl ++ ld a, [wOTPartyCount] ++ dec a ++ ld hl, wOTPartyMon1StatExp ++ call GetPartyLocation ++ ld d, h ++ ld e, l ++ pop hl ++ ++ ld c, NUM_EXP_STATS ++.stat_exp_loop ++; When reading stat experience, treat PERFECT_STAT_EXP as $FFFF ++ ld a, [hl] ++ cp LOW(PERFECT_STAT_EXP) ++ jr nz, .not_perfect_stat_exp ++ inc hl ++ ld a, [hl] ++ cp HIGH(PERFECT_STAT_EXP) ++ dec hl ++ jr nz, .not_perfect_stat_exp ++ ld a, $ff ++rept 2 ++ ld [de], a ++ inc de ++ inc hl ++endr ++ jr .continue_stat_exp ++ ++.not_perfect_stat_exp ++rept 2 ++ ld a, [hli] ++ ld [de], a ++ inc de ++endr ++.continue_stat_exp ++ dec c ++ jr nz, .stat_exp_loop ++.no_stat_exp + + ; item? + ... + + .no_moves + +-; Custom DVs affect stats, so recalculate them after TryAddMonToParty ++; Custom DVs or stat experience affect stats, ++; so recalculate them after TryAddMonToParty + ld a, [wOtherTrainerType] +- and TRAINERTYPE_DVS ++ and TRAINERTYPE_DVS | TRAINERTYPE_STAT_EXP + jr z, .no_stat_recalc + ... + .no_stat_recalc + + jp .loop +``` + +(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`.) + +Then edit [engine/battle/core.asm](../blob/master/engine/battle/core.asm) again: + +```diff + LoadEnemyMon: + ... + + ; Fill stats + ld de, wEnemyMonMaxHP + ld b, FALSE + ld hl, wEnemyMonDVs - (MON_DVS - MON_STAT_EXP + 1) ; wLinkBattleRNs + 7 ; ? ++ ld a, [wBattleMode] ++ cp TRAINER_BATTLE ++ jr nz, .no_stat_exp ++ ld a, [wCurPartyMon] ++ ld hl, wOTPartyMon1StatExp - 1 ++ call GetPartyLocation ++ ld b, TRUE ++.no_stat_exp + predef CalcMonStats +``` + +Now you can give custom stat experience to enemy Pokémon. Be sure to keep the data in order: level, species, nickname, DVs, stat experience, held item, moves. + +Stat experience is specified as five *words*, not bytes, because each of the five stats (HP, Attack, Defense, Speed, and Special) has two-byte experience going from $0000 to $FFFF (65,535). + +For example, these are parties for your rival that give him custom DVs, stat experience, held items, and moves: + +``` +Rival1Group: + ; RIVAL1 (1) + db "?@", TRAINERTYPE_DVS | TRAINERTYPE_STAT_EXP | TRAINERTYPE_ITEM | TRAINERTYPE_MOVES + db 3, RATTATA + db PERFECT_DV, PERFECT_DV ; atk|def, spd|spc + dw PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP ; hp, atk, def, spd, spc + db NO_ITEM + db TACKLE, TAIL_WHIP, BITE, NO_MOVE ; Bite is an egg move + db 5, CHIKORITA + db ATKDEFDV_SHINY, SPDSPCDV_SHINY ; atk|def, spd|spc + dw $0000, $0000, $0000, $0000, $0000 ; hp, atk, def, spd, spc + db BERRY + db TACKLE, GROWL, NO_MOVE, NO_MOVE + db -1 ; end + + ; RIVAL1 (2) + db "?@", TRAINERTYPE_DVS | TRAINERTYPE_STAT_EXP | TRAINERTYPE_ITEM | TRAINERTYPE_MOVES + db 3, RATTATA + db PERFECT_DV, PERFECT_DV ; atk|def, spd|spc + dw PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP ; hp, atk, def, spd, spc + db NO_ITEM + db TACKLE, TAIL_WHIP, BITE, NO_MOVE ; Bite is an egg move + db 5, CYNDAQUIL + db ATKDEFDV_SHINY, SPDSPCDV_SHINY ; atk|def, spd|spc + dw $0000, $0000, $0000, $0000, $0000 ; hp, atk, def, spd, spc + db BERRY + db TACKLE, LEER, NO_MOVE, NO_MOVE + db -1 ; end + + ; RIVAL1 (3) + db "?@", TRAINERTYPE_DVS | TRAINERTYPE_STAT_EXP | TRAINERTYPE_ITEM | TRAINERTYPE_MOVES + db 3, RATTATA + db PERFECT_DV, PERFECT_DV ; atk|def, spd|spc + dw PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP, PERFECT_STAT_EXP ; hp, atk, def, spd, spc + db NO_ITEM + db TACKLE, TAIL_WHIP, BITE, NO_MOVE ; Bite is an egg move + db 5, TOTODILE + db ATKDEFDV_SHINY, SPDSPCDV_SHINY ; atk|def, spd|spc + dw $0000, $0000, $0000, $0000, $0000 ; hp, atk, def, spd, spc + db BERRY + db SCRATCH, LEER, NO_MOVE, NO_MOVE + db -1 ; end + + ... +``` + +Which successfully loads in battle (no screenshot because stat experience isn't visible). + + +## 5. Allow trainer data to be stored in multiple banks + +TODO + + +## 6. Add a trainer type flag for variable parties + +[TODO](https://hax.iimarckus.org/topic/7137/) diff --git a/Tutorials.md b/Tutorials.md index 17ea718..a7c31bd 100644 --- a/Tutorials.md +++ b/Tutorials.md @@ -40,6 +40,7 @@ Tutorials may use diff syntax to show edits: - [Allow map tiles to appear above sprites (so NPCs can walk behind tiles) with `PRIORITY` colors](Allow-map-tiles-to-appear-above-sprites-\(so-NPCs-can-walk-behind-tiles\)-with-PRIORITY-colors) - [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) @@ -73,13 +74,10 @@ Tutorials may use diff syntax to show edits: - Evolution methods (location, held item, move, [etc](https://gitgud.io/pfero/axyllagame/commit/81914d43eb89195734caee724c0a40d4686a0bab)) - More daily and weekly events - Third trainer card page for Kanto badges -- Allow multiple banks of trainer party data -- Custom DVs for individual trainer teams (allows trainers to have shiny Pokémon) - Remove all Pokémon sprite animations - Implement dynamic overhead+underfoot bridges - Pan the camera for cutscenes by making the player invisible - Gain experience from catching Pokémon - Evolution moves from Gen 7 -- A trainer type that has a different party for all 16 badge counts (useful for open world games) ([via](https://hax.iimarckus.org/topic/7137/)) - Nuzlocke mode (an in-game enforced [Nuzlocke Challenge](https://bulbapedia.bulbagarden.net/wiki/Nuzlocke_Challenge)) - Useful unused content (`COLL_CURRENT_*`, `HELD_PREVENT_*`, `ENVIRONMENT_5`, `TRADE_GENDER_MALE`, `GROWTH_SLIGHTLY_*`, `SPRITE_UNUSED_GUY`, `PAL_NPC_PINK`, etc) diff --git a/screenshots/enemy-dvs.png b/screenshots/enemy-dvs.png Binary files differnew file mode 100644 index 0000000..bd44aab --- /dev/null +++ b/screenshots/enemy-dvs.png diff --git a/screenshots/enemy-nicknames.png b/screenshots/enemy-nicknames.png Binary files differnew file mode 100644 index 0000000..4201479 --- /dev/null +++ b/screenshots/enemy-nicknames.png |