Add a new AI layer - pret/pokecrystal GitHub Wiki
As explained in this larger AI tutorial, the behavior of computer trainers is broken up into "layers," allowing different layers to be given to different trainer classes without each layer messing up the others. There exist some empty slots that can be filled with new layers, so here's how to do that.
Contents
- Add a new AI_ constant
- Add a new AI pointer
- Write the code for the new layer
- Apply the new layer to trainer classes
1. Add a new AI_ constant
In constants/trainer_data_constants.asm:
...
; TRNATTR_AI_MOVE_WEIGHTS bit flags (wEnemyTrainerAIFlags)
; AIScoringPointers indexes (see engine/battle/ai/move.asm)
const_def
shift_const AI_BASIC
shift_const AI_SETUP
shift_const AI_TYPES
shift_const AI_OFFENSIVE
shift_const AI_SMART
shift_const AI_OPPORTUNIST
shift_const AI_AGGRESSIVE
shift_const AI_CAUTIOUS
shift_const AI_STATUS
shift_const AI_RISKY
+ shift_const AI_IMMUNITIES
DEF NO_AI EQU 0
...
Name the new constant whatever you want based on what it's going to do.
2. Add a new AI pointer
...
AIScoringPointers:
; entries correspond to AI_* constants
dw AI_Basic
dw AI_Setup
dw AI_Types
dw AI_Offensive
dw AI_Smart
dw AI_Opportunist
dw AI_Aggressive
dw AI_Cautious
dw AI_Status
dw AI_Risky
- dw AI_None
+ dw AI_Immunities
dw AI_None
dw AI_None
dw AI_None
dw AI_None
dw AI_None
This will point to a function label in engine/battle/ai/scoring.asm. There are six slots pointing to AI_None, a label which does nothing. Therefore, all six of these slots can potentially be filled with new layers. You don't need to make sure to leave one filled with AI_None; it's not used as a terminator, so it doesn't need to appear here at all.
The order of these pointers needs to be the same as the order of the AI_* constants in constants/trainer_data_constants.asm. Beyond that, however, their order doesn't matter. The layers are executed in this order, but the idea of "layers" is that they can function independently from each other, so they can be run in any order. Of course, if you write bad code, then that might not be the case.
If you look higher up in engine/battle/ai/move.asm...
AIChooseMove:
...
.CheckLayer:
pop hl
pop bc
ld a, c
cp 16 ; up to 16 scoring layers
jr z, .DecrementScores
...
...you'll see that the number of layers, 16, is explicitly defined. 16 bit flags require 2 bytes to be stored. If you stroll on over to ram/wram.asm and search for the label wEnemyTrainerAIFlags...
...
wEnemyTrainerItem1:: db
wEnemyTrainerItem2:: db
wEnemyTrainerBaseReward:: db
wEnemyTrainerAIFlags:: ds 3
wOTClassName:: ds TRAINER_CLASS_NAME_LENGTH
...
...you'll see that 3 bytes are actually reserved for it (ds 3). However, the third is used for flags pertaining to the computer using items or switching out Pokémon, so it's not necessarily free to use for more layers. The item and switch flags do only use 6 out of 8 bits, one of which is garbage data, so if you're clever you might be able to squeeze out 3 more layers, but that'll be on you to figure out.
3. Write the code for the new layer
Anywhere in engine/battle/ai/scoring.asm, create a new function with the same name as the pointer from the last step. Then write code for whatever you want that layer to do. A full explanation of how the AI works is outside the scope of this tutorial, so we'll just cover one example.
The existing AI_Types layer does a basic check of the computer Pokémon's moves for type effectiveness. It "dismisses" any move that the player is immune to (meaning the computer will basically never pick that move), and does some lighter "encouragement" and "discouragement" for moves that are super effective or not very effective. However, the AI_Types layer is only used by weaker trainer classes, and gets replaced with AI_Aggressive on stronger ones. AI_Aggressive does a more complex check to see which of the computer's moves will do the most damage to the player, taking into account not only type effectiveness, but base power and so on. The thing is, all that it does with this information is discourage any move that isn't the best one, and if it can't find a best one, it does nothing at all. As a result, since it doesn't also run AI_Types, there's a chance that the computer could end up picking a move that the player is immune to. You've probably never seen this happen during play, which is because other layers are also doing other things, but this is just an example of what can be done with new layers. What we're going to do is break off the immunity check from inside of AI_Types to create a new layer, AI_Immunities, and then run AI_Immunities alongside both AI_Types and AI_Aggressive, thus solving our hypothetical problem.
...
AI_Types:
-; Dismiss any move that the player is immune to.
; Encourage super-effective moves.
; Discourage not very effective moves unless
; all damaging moves are of the same type.
ld hl, wEnemyAIMoveScores - 1
ld de, wEnemyMonMoves
ld b, NUM_MOVES + 1
.checkmove
dec b
ret z
inc hl
ld a, [de]
and a
ret z
inc de
call AIGetEnemyMove
push hl
push bc
push de
ld a, 1
ldh [hBattleTurn], a
callfar BattleCheckTypeMatchup
pop de
pop bc
pop hl
ld a, [wTypeMatchup]
- and a
- jr z, .immune
cp EFFECTIVE
jr z, .checkmove
jr c, .noteffective
; effective
ld a, [wEnemyMoveStruct + MOVE_POWER]
and a
jr z, .checkmove
dec [hl]
jr .checkmove
.noteffective
; Discourage this move if there are any moves
; that do damage of a different type.
push hl
push de
push bc
ld a, [wEnemyMoveStruct + MOVE_TYPE]
ld d, a
ld hl, wEnemyMonMoves
ld b, NUM_MOVES + 1
ld c, 0
.checkmove2
dec b
jr z, .movesdone
ld a, [hli]
and a
jr z, .movesdone
call AIGetEnemyMove
ld a, [wEnemyMoveStruct + MOVE_TYPE]
cp d
jr z, .checkmove2
ld a, [wEnemyMoveStruct + MOVE_POWER]
and a
jr nz, .damaging
jr .checkmove2
.damaging
ld c, a
.movesdone
ld a, c
pop bc
pop de
pop hl
and a
jr z, .checkmove
inc [hl]
jr .checkmove
-.immune
- call AIDiscourageMove
- jr .checkmove
+
+AI_Immunities:
+; Dismiss any move that the player is immune to.
+
+ ld hl, wEnemyAIMoveScores - 1
+ ld de, wEnemyMonMoves
+ ld b, NUM_MOVES + 1
+.checkmove
+ dec b
+ ret z
+
+ inc hl
+ ld a, [de]
+ and a
+ ret z
+
+ inc de
+ call AIGetEnemyMove
+ ld a, [wEnemyMoveStruct + MOVE_POWER]
+ and a
+ jr z, .checkmove
+
+ push hl
+ push bc
+ push de
+ ld a, 1
+ ldh [hBattleTurn], a
+ callfar BattleCheckTypeMatchup
+ pop de
+ pop bc
+ pop hl
+
+ ld a, [wTypeMatchup]
+ and a
+ jr z, .immune
+ jr .checkmove
+
+.immune
+ call AIDiscourageMove
+ jr .checkmove
...
I've added a check so that this new layer only dismisses damaging moves, because there are a lot of cases where a non-damaging move ignores type immunities. AI_Types does do a check for non-damaging moves, but not until after checking for immunities. This is probably because AI_Types is meant to create a less intelligent trainer, but since we're going to use AI_Immunities together with AI_Aggressive as well as AI_Types, we'll err on the smarter side. Some other layers contain handling for non-damaging type immunities, so it should work out.
You may notice that this particular approach has caused us to repeat a lot of code. The pointer table limits us to 16 layers, but the true limiting factor is that engine/battle/ai/scoring.asm needs to fit within a single bank. There are many ways to optimize its contents, and/or it can be split across multiple banks, but such factors are outside the scope of this tutorial, as it is possible to fit in some new code without overflowing.
4. Apply the new layer to trainer classes
Now that we've created a new layer, we need to give it to whichever trainer classes whom we want to have it. This is done in data/trainers/attributes.asm. In our example, we've taken the immunity check out of AI_TYPES, so we'll need to add AI_IMMUNITIES to any trainer class that has AI_TYPES in order to restore their proper behavior, and then we'll also add AI_IMMUNITIES to any class that has AI_AGGRESSIVE, since that was what we set out to do.
TrainerClassAttributes:
; entries correspond to trainer classes (see constants/trainer_constants.asm)
table_width NUM_TRAINER_ATTRIBUTES
; Falkner
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Whitney
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Bugsy
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Morty
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Pryce
db HYPER_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Jasmine
db HYPER_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Chuck
db FULL_HEAL, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Clair
db FULL_HEAL, HYPER_POTION ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Rival1
db NO_ITEM, NO_ITEM ; items
db 15 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Pokemon Prof
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_AGGRESSIVE | AI_STATUS
+ dw AI_BASIC | AI_AGGRESSIVE | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Will
db MAX_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Cal
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Bruno
db MAX_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Karen
db FULL_HEAL, MAX_POTION ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Koga
db FULL_HEAL, FULL_RESTORE ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Champion
db FULL_HEAL, FULL_RESTORE ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Brock
db HYPER_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Misty
db FULL_HEAL, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Lt Surge
db HYPER_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Scientist
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Erika
db HYPER_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Schoolboy
db NO_ITEM, NO_ITEM ; items
db 8 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_OFTEN
; Bird Keeper
db NO_ITEM, NO_ITEM ; items
db 6 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OFFENSIVE | AI_OPPORTUNIST | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OFFENSIVE | AI_OPPORTUNIST | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Janine
db DIRE_HIT, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Cooltrainerm
db NO_ITEM, NO_ITEM ; items
db 12 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Cooltrainerf
db NO_ITEM, NO_ITEM ; items
db 12 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Beauty
db NO_ITEM, NO_ITEM ; items
db 22 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Pokemaniac
db NO_ITEM, NO_ITEM ; items
db 15 ; base reward
- dw AI_BASIC | AI_SETUP | AI_OFFENSIVE | AI_AGGRESSIVE | AI_STATUS
+ dw AI_BASIC | AI_SETUP | AI_OFFENSIVE | AI_AGGRESSIVE | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Gruntm
db NO_ITEM, NO_ITEM ; items
db 10 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Gentleman
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_SETUP | AI_AGGRESSIVE | AI_STATUS
+ dw AI_BASIC | AI_SETUP | AI_AGGRESSIVE | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Skier
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Teacher
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_OPPORTUNIST | AI_AGGRESSIVE | AI_STATUS
+ dw AI_BASIC | AI_OPPORTUNIST | AI_AGGRESSIVE | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Sabrina
db HYPER_POTION, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Fisher
db NO_ITEM, NO_ITEM ; items
db 10 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_OFTEN
; Swimmerm
db NO_ITEM, NO_ITEM ; items
db 2 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_OFFENSIVE | AI_STATUS
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_OFFENSIVE | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Swimmerf
db NO_ITEM, NO_ITEM ; items
db 5 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Super Nerd
db NO_ITEM, NO_ITEM ; items
db 8 ; base reward
- dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Rival2
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Guitarist
db NO_ITEM, NO_ITEM ; items
db 8 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Biker
db NO_ITEM, NO_ITEM ; items
db 8 ; base reward
- dw AI_BASIC | AI_TYPES | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_TYPES | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Blaine
db MAX_POTION, FULL_HEAL ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Juggler
db NO_ITEM, NO_ITEM ; items
db 10 ; base reward
- dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Executivem
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_SMART | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_SMART | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Psychic T
db NO_ITEM, NO_ITEM ; items
db 8 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Executivef
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_SMART | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_SMART | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Sage
db NO_ITEM, NO_ITEM ; items
db 8 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Medium
db NO_ITEM, NO_ITEM ; items
db 10 ; base reward
- dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_TYPES | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Boarder
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Pokefanm
db NO_ITEM, NO_ITEM ; items
db 20 ; base reward
- dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Kimono Girl
db NO_ITEM, NO_ITEM ; items
db 18 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
...
; Pokefanf
db NO_ITEM, NO_ITEM ; items
db 20 ; base reward
- dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_SMART | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Red
db FULL_RESTORE, FULL_RESTORE ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Blue
db FULL_RESTORE, FULL_RESTORE ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Officer
db NO_ITEM, NO_ITEM ; items
db 10 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_STATUS
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_STATUS | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Gruntf
db NO_ITEM, NO_ITEM ; items
db 10 ; base reward
- dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_TYPES | AI_OPPORTUNIST | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
; Mysticalman
db NO_ITEM, NO_ITEM ; items
db 25 ; base reward
- dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY
+ dw AI_BASIC | AI_SETUP | AI_SMART | AI_AGGRESSIVE | AI_CAUTIOUS | AI_STATUS | AI_RISKY | AI_IMMUNITIES
dw CONTEXT_USE | SWITCH_SOMETIMES
assert_table_length NUM_TRAINER_CLASSES
It would also be possible to give AI_IMMUNITIES to a class that has neither AI_TYPES nor AI_AGGRESSIVE, creating a behavior type that's aware of immunities but otherwise ignorant of type matchups. That's the fun of layers: they can be mixed and matched for new results.
Since these constants are setting bit flags, the order they're listed in here doesn't actually matter; we're just maintaining the same order for the sake of organization. And also since they're setting bit flags, we're not adding any data space when we give trainer classes more layers than they had before, so we don't have to worry about this file overflowing a bank.
That's the gist of adding a new layer, but obviously you can do just about anything with that layer. You may have seen here that "boss" trainers all tend to have the same set of layers, which when combined will more or less result in the smartest behavior. But it would also be possible to create a layer that only gets used by one trainer class, which would be a way to make that class stand out, and thus differentiate special trainers not only from the masses but also from each other. In this way, you could craft a very specific strategy based on one trainer's party, and use a unique layer to make sure that they pull off that strategy properly. For more ideas, you can always check out any open-source hacks that you come across!