diff options
author | Idain <luiscarlosholguinperez@outlook.com> | 2021-04-17 05:14:11 -0400 |
---|---|---|
committer | Idain <luiscarlosholguinperez@outlook.com> | 2021-04-17 05:14:11 -0400 |
commit | 9d77119bd08b1a97e56656ea4df2239dda12f255 (patch) | |
tree | d87f93b8f4e402106a15f968e4f43001bc4ee3b0 | |
parent | 15eef4abf147941dc0df10387aaa6ad9bdb78b30 (diff) |
Created Grant Grass-type Pokémon immunity to Powder/Spore based moves (markdown)
-rw-r--r-- | Grant-Grass-type-Pokémon-immunity-to-Powder-Spore-based-moves.md | 219 |
1 files changed, 219 insertions, 0 deletions
diff --git a/Grant-Grass-type-Pokémon-immunity-to-Powder-Spore-based-moves.md b/Grant-Grass-type-Pokémon-immunity-to-Powder-Spore-based-moves.md new file mode 100644 index 0000000..c766e33 --- /dev/null +++ b/Grant-Grass-type-Pokémon-immunity-to-Powder-Spore-based-moves.md @@ -0,0 +1,219 @@ +Generation 6 introduced an interesting mechanic where Grass-type Pokémon are unaffected by moves based on powder/spores, and there are five moves in Generation 2 which meet this criteria: Cotton Spore, Poison Powder, Sleep Powder, Spore and Stun Spore. In this tutorial, we'll implement this immunity and also make the AI discourages these moves if the opponent is Grass-type. + +## Contents + +1. [Create the Powder/Spore move list](#1-create-the-powderspore-move-list) +2. [Create `IsInByteArray`](#2-create-isinbytearray) +3. [Add a new Battle Command](#3-add-a-new-battle-command) +4. [Make the AI Discourage Powder/Spore moves if the opponent is Grass-type](#4-make-the-ai-discourage-powderspore-moves-if-the-opponent-is-grass-type) + +## 1. Create the Powder/Spore move list + +Let's create **home/powder_moves.asm**: +```diff ++PowderMoves:: ++ db POISONPOWDER ++ db SLEEP_POWDER ++ db SPORE ++ db COTTON_SPORE ++ db STUN_SPORE ++ db -1 +``` + +And include it in [home.asm](../blob/master/home.asm): + +```diff +SECTION "Home", ROM0 +... ++INCLUDE "home/powder_moves.asm" +``` + +You might be wondering why we're adding this list in ROM0 instead of another bank, this is going to be explained in further steps. For now let's proceed with the next one. + +## 2. Create `IsInByteArray` + +Go to [home/arrays.asm](../blob/master/home/arrays.asm) and + +```diff ++IsInByteArray:: ++ ld de, 1 ++; fallthrough + IsInArray:: + ; Find value a for every de bytes in array hl. + ; Return index in b and carry if found. + ... +``` +As the commented lines say, the job of `IsInArray` is to find the value of register `a` in an array loaded in `hl`, and you advance `de` bytes forward in the array. We made this small addition so that we have a function that always advances one byte forward in a given array, which we'll use later. + +## 3. Add a new Battle Command + +Now we need a battle command that checks whether the used move is powder/spore based or not and apply the immunity depending on the opponent's types, but it doesn't exist yet so we'll have create it. First edit [macros/scripts/battle_commands.asm](../blob/master/macros/scripts/battle_commands.asm): + +```diff +; BattleCommandPointers indexes (see data/battle/effect_command_pointers.asm) + const_def 1 + command checkturn ; 01 + ... + command traptarget ; 3b +- command effect0x3c ; 3c ++ command checkpowder ; 3c + command rampage ; 3d + ... +``` + +We could've also put the command at the end of the list, but for the sake of saving space we're replacing `effect0x3c` since it's unused. Now let's edit [data/battle/effect_command_pointers.asm](../blob/master/data/battle/effect_command_pointers.asm): + +```diff + BattleCommandPointers: + ; entries correspond to macros/scripts/battle_commands.asm + table_width 2, BattleCommandPointers + dw BattleCommand_CheckTurn + ... + dw BattleCommand_TrapTarget +- dw BattleCommand_Unused3C ++ dw BattleCommand_CheckPowder + dw BattleCommand_Rampage + ... +``` + +And then edit [engine/battle/effect_commands.asm](../blob/master/engine/battle/effect_commands.asm) + +```diff ++BattleCommand_CheckPowder: ++; Checks if the move is powder/spore-based and ++; if the opponent is Grass-type ++ ld a, BATTLE_VARS_MOVE_ANIM ++ call GetBattleVar ++ ld hl, PowderMoves ++ call IsInByteArray ++ ret nc ++ ++; If the opponent is Grass-type, the move fails. ++ ld hl, wEnemyMonType1 ++ ldh a, [hBattleTurn] ++ and a ++ jr z, .checkgrasstype ++ ld hl, wBattleMonType1 ++ ++.checkgrasstype: ++ ld a, [hli] ++ cp GRASS ++ jp z, .Immune ++ ld a, [hl] ++ cp GRASS ++ ret nz ++ ;fallthrough ++.Immune: ++ ld a, 1 ++ ld [wAttackMissed], a ++ ret +``` +As the comments explain, `BattleCommand_CheckPowder` first checks if the move is in the `PowderMoves` list and then checks the opponent's types to determine whether to hit or miss. + +Let's explain something important. To access the `PowderMoves` list it needs to be included either in the same bank as engine/battle/effect_commands.asm ("Effect Commands") or in the ROM0 bank. We decided to opt for the latter because we're going to check this list from another file that is located in another bank, and ROM0 can be accessed by any file located in any bank (also, the list is pretty short, so it won't fill the bank). **There are more practical workarounds**, so don't get used to this. + +Anyways, now that we've finally created our new battle command, let's add it to some already existing move effects that are used by Powder/Spore moves. Go to [data/moves/effects.asm](../blob/master/data/moves/effects.asm): + +```diff +MoveEffects: ; used only for BANK(MoveEffects) + ... + DoSleep: + checkobedience + usedmovetext + doturn + checkhit ++ checkpowder + checksafeguard + sleeptarget + endmove + ... + + SpeedDown2: + checkobedience + usedmovetext + doturn + checkhit ++ checkpowder + speeddown2 + lowersub + statdownanim + raisesub + statdownmessage + statdownfailtext + endmove +... + + DoPoison: + checkobedience + usedmovetext + doturn + checkhit ++ checkpowder + stab + checksafeguard + poison + endmove + + DoParalyze: + checkobedience + usedmovetext + doturn + stab + checkhit ++ checkpowder + checksafeguard + paralyze + endmove +... +``` + +`DoSleep` is used by both Sleep Powder and Spore, `SpeedDown2` is used by Cotton Spore, `DoPoison` by Poison Powder, and `DoParalyze` by Stun Spore. + +Now the effect is completely functional! But remember that I told you we're gonna use the `PowderMoves` list in another file? If you paid attention, I said at the start of the tutorial we're going to modify the AI to discourage these moves depending on the opponent's types. So let's go to our final step. + +## 4. Make the AI discourage Powder/Spore moves if the opponent is Grass-type + +Go to [engine/battle/ai/scoring.asm](../blob/master/engine/battle/ai/scoring.asm) and edit `AI_Status`: + +```diff + AI_Status: + ... + inc de + call AIGetEnemyMove + ++; Check if the opponent is immune to powder/spore moves. ++ ld a, [wEnemyMoveStruct + MOVE_ANIM] ++ push bc ++ push de ++ push hl ++ ld hl, PowderMoves ++ call IsInByteArray ++ pop hl ++ pop de ++ pop bc ++ jr nc, .normal_status_check ++ ++ ld a, [wBattleMonType1] ++ cp GRASS ++ jr z, .immune ++ ld a, [wBattleMonType2] ++ cp GRASS ++ jr z, .immune ++ ++.normal_status_check + ld a, [wEnemyMoveStruct + MOVE_EFFECT] + cp EFFECT_TOXIC + jr z, .poisonimmunity + cp EFFECT_POISON + jr z, .poisonimmunity + cp EFFECT_SLEEP + jr z, .typeimmunity + cp EFFECT_PARALYZE + jr z, .typeimmunity + +... +``` + +Explanation time! The logic behind this is very similar to `BattleCommand_CheckPowder`, where we store the move in `a` and do our powder check; if it's not powder/spore based, we proceed with the original effect check, but if it is then we check the opponent's types. If the final check succeds, we jump to `.immune` where it discourages the move from being used. The `push`s and `pop`s are used to preserve `bc, `de` and `hl` since AI_Status use the original values often. + +And that's it! Now the AI will discourage Powder/Based moves correctly depending on the situation. With this, the tutorial is finally complete! |