The PokéCommunity Forums

The PokéCommunity Forums (https://www.pokecommunity.com/index.php)
-   Binary Hack Research & Development (https://www.pokecommunity.com/forumdisplay.php?f=195)
-   -   Research Trainer AI Command Research (https://www.pokecommunity.com/showthread.php?t=403682)

AkameTheBulbasaur November 25th, 2017 1:41 PM

Trainer AI Command Research
 
Acknowledgments
First and foremost I have to thank Knizz for her previous research on the Trainer AI Scripting system! Because of her hard work, I was able to have a foundation for all the research I've done. By extension, I also have to thank JPAN, because Knizz said she relied heavily on that as well.

If you want to see Knizz's original work, it is in this thread.

I should say that this particular research is for FireRed. I think that Emerald uses the same sort of system (with different offsets) but I haven't checked. Don't sue me if it's different!

And with that, let's get on with the show!

Introduction
There have been a few attempts in the past of improving the difficulty of ROM Hacks, such as DoesntKnowHowToPlay's Trainer EV hack (here), but I wasn't quite satisfied with that.

I used that hack, and JPAN's dynamic trainer level hack (to make trainer levels match your own), and that helped genuinely increase the difficulty, but I found it wasn't enough. I wanted to really control how the AI works from its core.

So I found Knizz's earlier thread from 2014, which detailed a sort of scripting system for the AI, theoretically making it possible to write your own scripts and customize the AI. It was a really good start, but I couldn't find very much information outside of that thread.

The last post in that thread is from 2015. The development on that front seemed to stagnate.

So I decided I would try to research the commands myself, just for fun, and see how far I got. I was originally intending to post what I found to that thread after I was done. Five months later and the post I'd written down was so long I thought it would be easier to give it it's own thread.

The Commands
There are two main variables in Trainer AI Scripting. The first and foremost is the Free Variable. This is like LASTRESULT in NPC scripting. Many of the commands store their results here, and you can then use this result for other commands.

Many commands jump to a given address if the free variable compares to something.

The second variable is the Considered Move Variable. This variable has the index number of the move being considered while the script is running. You can compare it to things or get Move data from it.

Useful numbers:
Attacker is 1, Defender is 0.
True is 1, False is 0.

Basic syntax rules:
The target (if applicable) always comes first, and the address to jump to (if applicable always comes last). If any of the below commands have more than two arguments, this is how the order will almost always go.

Some of the existing commands I thought were either written in a clunky way, or I thought of something I could add to them (I think one of them was bugged to begin with) so I rewrote them. I'll include the routines for the rewritten versions after the description of the commands.

I should note that there are, for some unknown reason, commands in default FireRed that do nothing. I don't mean they have a redundant or pointless effect, I mean they literally do nothing. I tried using one and it soft locked the game. This is useful because it means you can add about ten new commands without replacing or expanding the table!

In fact, because of the CallASM command (which I wrote recently and have included at the very bottom) you don't need to expand the table at all if you don't want to!

DISCLAIMER: Adding in the new or rewritten commands that will follow won't make the AI smarter. Only creating new scripts using the commands will.

WARNING ABOUT CERTAIN REWRITTEN COMMANDS!
The first iteration of this thread included a warning about how certain rewritten commands are incompatible with default AI scripts because the amount of arguments in them is different. Somehow, this warning was deleted, so I'm putting it back because it's kinda important lol.

JumpIfDamageBonusEquals has extra arguments, and thus if you put it in and do not change the default script, the game will crash.

This really shouldn't be an issue, as the modified command is really intended to be used with modified scripts. However, if for some reason you want to use the default scripts, follow the instructions below:
  1. Download (or make your own) this file. It is 16 MB of just FF.
  2. Open this in a THUMB editor.
    Spoiler:
    .macro RandomJumpIfLessThan Value Address
    .byte 0x0
    .byte \Value
    .word \Address
    .endm

    .macro RandomJumpIfMoreThan Value Address
    .byte 0x1
    .byte \Value
    .word \Address
    .endm

    .macro RandomJumpOneIn256 Address
    .byte 0x2
    .word \Address
    .endm

    .macro RandomJump255In256 Address
    .byte 0x3
    .word \Address
    .endm

    .macro AddToViabilityScore Value
    .byte 0x4
    .byte \Value
    .endm

    .macro JumpIfHealthLessThan Target Value Address
    .byte 0x5
    .byte \Target
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfHealthMoreThan Target Value Address
    .byte 0x6
    .byte \Target
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfHealthEquals Target Value Address
    .byte 0x7
    .byte \Target
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfHealthNotEqual Target Value Address
    .byte 0x8
    .byte \Target
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfStatus1Equals Target Status Address
    .byte 0x9
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus1NotEqual Target Status Address
    .byte 0xA
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus2Equals Target Status Address
    .byte 0xB
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus2NotEqual Target Status Address
    .byte 0xC
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus3Equals Target Status Address
    .byte 0xD
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus3NotEqual Target Status Address
    .byte 0xE
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus4Equals Target Status Address
    .byte 0xF
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfStatus4NotEqual Target Status Address
    .byte 0x10
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfByteLessThan Value Address
    .byte 0x11
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfByteMoreThan Value Address
    .byte 0x12
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfByteEquals Value Address
    .byte 0x13
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfByteNotEqual Value Address
    .byte 0x14
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfWordLessThan Value Address
    .byte 0x15
    .word \Value
    .word \Address
    .endm

    .macro JumpIfWordMoreThan Value Address
    .byte 0x16
    .word \Value
    .word \Address
    .endm

    .macro JumpIfWordEquals Value Address
    .byte 0x17
    .word \Value
    .word \Address
    .endm

    .macro JumpIfWordNotEqual Value Address
    .byte 0x18
    .word \Value
    .word \Address
    .endm

    .macro JumpIfMoveIDEquals Move Address
    .byte 0x19
    .hword \Move
    .word \Address
    .endm

    .macro JumpIfMoveIDNotEqual Move Address
    .byte 0x1A
    .hword \Move
    .word \Address
    .endm

    .macro JumpIfByteInList List Address
    .byte 0x1B
    .word \List
    .word \Address
    .endm

    .macro JumpIfByteNotInList List Address
    .byte 0x1C
    .word \List
    .word \Address
    .endm

    .macro JumpIfHalfwordInList List Address
    .byte 0x1D
    .word \List
    .word \Address
    .endm

    .macro JumpIfHalfwordNotInList List Address
    .byte 0x1E
    .word \List
    .word \Address
    .endm

    .macro JumpIfAttackerHasDamagingMoves Address
    .byte 0x1F
    .word \Address
    .endm

    .macro JumpIfAttackerHasNoDamagingMoves Address
    .byte 0x20
    .word \Address
    .endm

    .macro GetBattleTurnCounter
    .byte 0x21
    .endm

    .macro GetType Argument
    .byte 0x22
    .byte \Argument
    .endm

    .macro GetPowerOfConsideredMove
    .byte 0x23
    .endm

    .macro GetPowerOfStrongestMove
    .byte 0x24
    .endm

    .macro GetMoveLastUsed Target
    .byte 0x25
    .byte \Target
    .endm

    .macro JumpIfFreeVarEquals Value Address
    .byte 0x26
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfFreeVarNotEqual Value Address
    .byte 0x27
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfMoveWouldHitFirst Priority Address
    .byte 0x28
    .byte \Priority
    .word \Address
    .endm

    .macro JumpIfMoveWouldHitSecond Priority Address
    .byte x29
    .byte \Priority
    .word \Address
    .endm

    .macro GetPerishCount Target
    .byte 0x2A
    .byte \Target
    .endm

    .macro GetSpikesLayer Target
    .byte 0x2B
    .byte \Target
    .endm

    .macro CountViablePokemonOnTeam Target
    .byte 0x2C
    .byte \Target
    .endm

    .macro GetMoveID
    .byte 0x2D
    .endm

    .macro GetMoveScriptID Move
    .byte 0x2E
    .byte \Move
    .endm

    .macro GetAbility Target
    .byte 0x2F
    .byte \Target
    .endm

    .macro CallASM Address
    .byte 0x30
    .word \Address
    .endm

    .macro JumpIfDamageBonusEquals Move Value Address
    .byte 0x31
    .byte \Move
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfAnyOrAllStatsAre Target Argument Compare Value Address
    .byte 0x32
    .byte \Target
    .byte \Argument
    .byte \Compare
    .byte \Value
    .word \Address
    .endm

    .macro DoesTeeterDanceWork
    .byte 0x33
    .endm

    .macro JumpIfAnyPokemonHasStatus Target Status Address
    .byte 0x34
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro JumpIfNoPokemonHasStatus Target Status Address
    .byte 0x35
    .byte \Target
    .word \Status
    .word \Address
    .endm

    .macro GetWeather
    .byte 0x36
    .endm

    .macro JumpIfMoveScriptEquals Value Address
    .byte 0x37
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfMoveScriptNotEqual Value Address
    .byte 0x38
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfStatBuffLessThan Target Stat Value Address
    .byte 0x39
    .byte \Target
    .byte \Stat
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfStatBuffMoreThan Target Stat Value Address
    .byte 0x3A
    .byte \Target
    .byte \Stat
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfStatBuffEquals Target Stat Value Address
    .byte 0x3B
    .byte \Target
    .byte \Stat
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfStatBuffNotEqual Target Stat Value Address
    .byte 0x3C
    .byte \Target
    .byte \Stat
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfMoveKnocksOut Address
    .byte 0x3D
    .word \Address
    .endm

    .macro JumpIfMoveDoesntKnockOut Address
    .byte 0x3E
    .word \Address
    .endm

    .macro JumpIfMoveInMoveSet Target Move Address
    .byte 0x3F
    .byte \Target
    .hword \Move
    .word \Address
    .endm

    .macro JumpIfMoveNotInMoveSet Target Move Address
    .byte 0x40
    .byte \Target
    .hword \Move
    .word \Address
    .endm

    .macro JumpIfMoveScriptInMoveSet Target Script Address
    .byte 0x41
    .byte \Target
    .byte \Script
    .word \Address
    .endm

    .macro JumpIfMoveScriptNotInMoveSet Target Script Address
    .byte 0x42
    .byte \Target
    .byte \Script
    .word \Address
    .endm

    .macro JumpIfMoveSetRestricted Target Restriction Address
    .byte 0x43
    .byte \Target
    .byte \Restriction
    .word \Address
    .endm

    .macro JumpIfEncoreIs Value Address
    .byte 0x44
    .byte \Value
    .byte \Address
    .endm

    .macro RunAway
    .byte 0x45
    .endm

    .macro GetMovePriority Move
    .byte 0x46
    .byte \Move
    .endm

    .macro SafariZone
    .byte 0x47
    .endm

    .macro GetHeldItemEffect Target
    .byte 0x48
    .byte \Target
    .endm

    .macro GetGender Target
    .byte 0x49
    .byte \Target
    .endm

    .macro CheckIfFirstTurn Target
    .byte 0x4A
    .byte \Target
    .endm

    .macro GetStockpileCounter Target
    .byte 0x4B
    .byte \Target
    .endm

    .macro CheckIfDoubleBattle
    .byte 0x4C
    .endm

    .macro GetItemID Target
    .byte 0x4D
    .byte \Target
    .endm

    .macro GetKindOfMove Move
    .byte 0x4E
    .byte \Move
    .endm

    .macro GetMoveAccuracy
    .byte 0x4F
    .endm

    .macro GetMoveRange Move
    .byte 0x50
    .byte \Move
    .endm

    .macro GetProtectActivity Target
    .byte 0x51
    .byte \Target
    .endm

    .macro GetTarget
    .byte 0x52
    .endm

    .macro GetSubHealthRatio Target
    .byte 0x53
    .byte \Target
    .endm

    .macro CheckIfMoveFlagSet Move Flag
    .byte 0x54
    .hword \Move
    .byte \Flag
    .endm

    .macro CheckIfInverseBattle
    .byte 0x55
    .endm

    .macro CheckIfStatsAre Target Stat1 Stat2 Compare
    .byte 0x56
    .byte \Target
    .byte \Stat1
    .byte \Stat2
    .byte \Compare
    .endm

    .macro GetDataAtRAM Size Address
    .byte 0x57
    .byte \Size
    .word \Address
    .endm

    .macro Call Address
    .byte 0x58
    .word \Address
    .endm

    .macro Jump Address
    .byte 0x59
    .word \Address
    .endm

    .macro ReturnToBattle
    .byte 0x5A
    .endm

    .macro JumpIfBattlerLevelsAre Value Address
    .byte 0x5B
    .byte \Value
    .word \Address
    .endm

    .macro JumpIfTauntTurnsNotZero Address
    .byte 0x5C
    .word \Address
    .endm

    .macro JumpIfTauntTurnsZero Address
    .byte 0x5D
    .word \Address
    .endm

    .equ Defender, 0x0
    .equ Attacker, 0x1
    .equ APartner, 0x2
    .equ DPartner, 0x3

    .equ Priority, 0x1
    .equ NoPriority, 0x0

    .equ Done, 0xFFFF
    .equ Finished, 0xFF

    .equ CurrentMove, 0x12
    .equ MoveVar, 0x0
    .equ FreeVar, 0x1

    .equ Partner, 0x1

    .equ Byte, 0x0
    .equ HalfWord, 0x1
    .equ Word, 0x2

    .equ True, 0x1
    .equ False, 0x0

    .equ DType1, 0x0
    .equ AType1, 0x1
    .equ DType2, 0x2
    .equ AType2, 0x3
    .equ Move, 0x4
    .equ Var, 0x5

    .equ APType1, 0x6
    .equ DPType1, 0x7
    .equ APType2, 0x8
    .equ DPType2, 0x9

    .equ DoubleWeak, 0xF0
    .equ Weak, 0x78
    .equ Neutral, 0x28
    .equ Resist, 0x1E
    .equ DoubleResist, 0xF
    .equ Immune, 0x0

    .equ Equal, 0x0
    .equ NotEqual, 0x1
    .equ LessThan, 0x2
    .equ MoreThan, 0x3

    .equ AllFoes, 0x8
    .equ AllButUser, 0x20
    .equ User, 0x10
    .equ Target, 0x0
    .equ Random, 0x4
    .equ Everyone, 0x30

    .equ Any, 0x0
    .equ All, 0x1

    .equ Clear, 0x0
    .equ Rain_Storm, 0x1
    .equ Sun_Shine, 0x2
    .equ Sand_Storm, 0x3
    .equ Hail_Storm, 0x4

    .equ Sleep, 0x00000007
    .equ Poisoned, 0x00000008
    .equ Burn, 0x00000010
    .equ Freeze, 0x00000020
    .equ Paralyze, 0x00000040
    .equ BadPoison, 0x00000080
    .equ MajorStatus, 0x000000FF

    .equ Confused, 0x00000007
    .equ CantSleep, 0x00000070
    .equ Trapped, 0x0400E000
    .equ InLove, 0x000F0000
    .equ RechargeUp, 0x00400000
    .equ SubActive, 0x01000000
    .equ DBondUp, 0x02000000
    .equ NMareUp, 0x08000000
    .equ CurseUp, 0x10000000
    .equ DCurlUp, 0x40000000
    .equ TormentUp, 0x80000000

    .equ LSeedUp, 0x00000004
    .equ LockOnUp, 0x00000010
    .equ PanicModeUp, 0x00000020
    .equ SInvulnerable, 0x000400C0
    .equ ChargeUp, 0x00000200
    .equ IngrainUp, 0x00000400
    .equ Flying, 0x00000040
    .equ Digging, 0x00000080
    .equ GrudgeUp, 0x00004000
    .equ Diving, 0x00040000

    .equ ReflectUp, 0x00000001
    .equ LScreenUp, 0x00000002
    .equ SpikesUp, 0x00000010
    .equ SGuardUp, 0x00000020
    .equ MistUp, 0x00000100

    .equ Attack, 0x1
    .equ Defense, 0x2
    .equ Speed, 0x3
    .equ SpAttack, 0x4
    .equ SpDefense, 0x5
    .equ Accuracy, 0x6
    .equ Evasion, 0x7

    .equ Physical, 0x0
    .equ Special, 0x1
    .equ Status, 0x2

    .equ DirectContact, 0x1
    .equ ProtectBlocked, 0x2
    .equ MagicCoatBlock, 0x4
    .equ SnatchBlock, 0x8
    .equ MirrorMoveUsable, 0x10
    .equ KingsRockBlock, 0x20
    .equ AllMoveFlags, 0x3F

    .equ Stench, 0x1
    .equ Drizzle, 0x2
    .equ SpeedBoost, 0x3
    .equ BattleArmor, 0x4
    .equ Sturdy, 0x5
    .equ Damp, 0x6
    .equ Limber, 0x7
    .equ SandVeil, 0x8
    .equ Static, 0x9
    .equ VoltAbsorb, 0xA
    .equ WaterAbsorb, 0xB
    .equ Oblivious, 0xC
    .equ CloudNine, 0xD
    .equ CompoundEyes, 0xE
    .equ Insomnia, 0xF
    .equ ColorChange, 0x10
    .equ Immunity, 0x11
    .equ FlashFire, 0x12
    .equ ShieldDust, 0x13
    .equ OwnTempo, 0x14
    .equ SuctionCups, 0x15
    .equ Intimidate, 0x16
    .equ ShadowTag, 0x17
    .equ RoughSkin, 0x18
    .equ WonderGuard, 0x19
    .equ Levitate, 0x1A
    .equ EffectSpore, 0x1B
    .equ Synchronize, 0x1C
    .equ ClearBody, 0x1D
    .equ NaturalCure, 0x1E
    .equ LightningRod, 0x1F
    .equ SereneGrace, 0x20
    .equ SwiftSwim, 0x21
    .equ Chlorophyll, 0x22
    .equ Illuminate, 0x23
    .equ Trace, 0x24
    .equ HugePower, 0x25
    .equ PoisonPoint, 0x26
    .equ InnerFocus, 0x27
    .equ MagmaArmor, 0x28
    .equ WaterVeil, 0x29
    .equ MagnetPull, 0x2A
    .equ FrostVeil, 0x2B
    .equ RainDish, 0x2C
    .equ SandStream, 0x2D
    .equ Pressure, 0x2E
    .equ ThickFat, 0x2F
    .equ EarlyBird, 0x30
    .equ FlameBody, 0x31
    .equ RunAway, 0x32
    .equ KeenEye, 0x33
    .equ HyperCutter, 0x34
    .equ PickUp, 0x35
    .equ Truant, 0x36
    .equ Hustle, 0x37
    .equ CuteCharm, 0x38
    .equ Plus, 0x39
    .equ Minus, 0x3A
    .equ Forecast, 0x3B
    .equ StickyHold, 0x3C
    .equ ShedSkin, 0x3D
    .equ Guts, 0x3E
    .equ MarvelScale, 0x3F
    .equ LiquidOoze, 0x40
    .equ Overgrow, 0x41
    .equ Blaze, 0x42
    .equ Torrent, 0x43
    .equ Swarm, 0x44
    .equ RockHead, 0x45
    .equ Drought, 0x46
    .equ ArenaTrap, 0x47
    .equ VitalSpirit, 0x48
    .equ WhiteSmoke, 0x49
    .equ PurePower, 0x4A
    .equ ShellArmor, 0x4B
    .equ SnowWarning, 0x4C
    .equ AirLock, 0x4D

    .equ None, 0x0
    .equ MasterBall, 0x1
    .equ UltraBall, 0x2
    .equ GreatBall, 0x3
    .equ PokeBall, 0x4
    .equ SafariBall, 0x5
    .equ NetBall, 0x6
    .equ DiveBall, 0x7
    .equ NestBall, 0x8
    .equ RepeatBall, 0x9
    .equ TimerBall, 0xA
    .equ LuxuryBall, 0xB
    .equ QuickBall, 0xC
    .equ Potion, 0xD
    .equ Banitsa, 0xE
    .equ Flan, 0xF
    .equ Gyuvech, 0x10
    .equ OmanyteCake, 0x11
    .equ CrabCake, 0x12
    .equ FullRestore, 0x13
    .equ MaxPotion, 0x14
    .equ HyperPotion, 0x15
    .equ SuperPotion, 0x16
    .equ Tarator, 0x17
    .equ Revive, 0x18
    .equ MaxRevive, 0x19
    .equ FreshWater, 0x1A
    .equ SodaPop, 0x1B
    .equ Lemonade, 0x1C
    .equ MooMooMilk, 0x1D
    .equ EnergyPowder, 0x1E
    .equ EnergyRoot, 0x1F
    .equ HealPowder, 0x20
    .equ RevivalHerb, 0x21
    .equ Tea, 0x22
    .equ MaxEther, 0x23
    .equ Elixir, 0x24
    .equ MaxElixir, 0x25
    .equ LavaCookie, 0x26
    .equ BlueFlute, 0x27
    .equ YellowFlute, 0x28
    .equ RedFlute, 0x29
    .equ BlackFlute, 0x2A
    .equ WhiteFlute, 0x2B
    .equ BerryJuice, 0x2C
    .equ SacredAsh, 0x2D
    .equ MizuSalt, 0x2E
    .equ MizuShell, 0x2F
    .equ RedShard, 0x30
    .equ BlueShard, 0x31
    .equ YellowShard, 0x32
    .equ GreenShard, 0x33
    .equ Iyoko, 0x34
    .equ Quesadilla, 0x35
    .equ PikaCookie, 0x36
    .equ Triagel, 0x37
    .equ HealthWing, 0x38
    .equ MuscleWing, 0x39
    .equ ResistWing, 0x3A
    .equ CleverWing, 0x3B
    .equ GeniusWing, 0x3C
    .equ SwiftWing, 0x3D
    .equ Yonjin, 0x3E
    .equ HPUp, 0x3F
    .equ Protein, 0x40
    .equ Iron, 0x41
    .equ Carbos, 0x42
    .equ Calcium, 0x43
    .equ RareCandy, 0x44
    .equ PPUp, 0x45
    .equ Zinc, 0x46
    .equ PPMax, 0x47
    .equ Maitzei, 0x48
    .equ AlterStone, 0x49
    .equ Electirizer, 0x4A
    .equ XAttack, 0x4B
    .equ XDefend, 0x4C
    .equ XSpeed, 0x4D
    .equ XAccuracy, 0x4E
    .equ XSpecial, 0x4F
    .equ ClefairyDoll, 0x50
    .equ FluffyTail, 0x51
    .equ Ambonyan, 0x52
    .equ NanabCake, 0x53
    .equ Lobster, 0x54
    .equ EscapeRope, 0x55
    .equ VeranoCone, 0x56
    .equ Apple, 0x57
    .equ GreenApple, 0x58
    .equ GoldenApple, 0x59
    .equ RainbowApple, 0x5A
    .equ Banana, 0x5B
    .equ SpotPizza, 0x5C
    .equ SunStone, 0x5D
    .equ MoonStone, 0x5E
    .equ FireStone, 0x5F
    .equ ThunderStone, 0x60
    .equ WaterStone, 0x61
    .equ LeafStone, 0x62
    .equ IceCream, 0x63
    .equ MewMacaroni, 0x64
    .equ Paella, 0x65
    .equ Pancakes, 0x66
    .equ TinyMushroom, 0x67
    .equ BigMushroom, 0x68
    .equ Kanaba, 0x69
    .equ Pearl, 0x6A
    .equ BigPearl, 0x6B
    .equ Stardust, 0x6C
    .equ StarPiece, 0x6D
    .equ Nugget, 0x6E
    .equ HeartScale, 0x6F
    .equ LaggingTail, 0x70
    .equ CheesePizza, 0x71
    .equ ChocoPizza, 0x72
    .equ ExpertBelt, 0x73
    .equ GrillCheese, 0x74
    .equ Hamburger, 0x75
    .equ Invention, 0x76
    .equ WhitePizza, 0x77
    .equ Magmarizer, 0x78
    .equ OrangeMail, 0x79
    .equ HarborMail, 0x7A
    .equ GlitterMail, 0x7B
    .equ MechMail, 0x7C
    .equ WoodMail, 0x7D
    .equ WaveMail, 0x7E
    .equ BeadMail, 0x7F
    .equ ShadowMail, 0x80
    .equ TropicMail, 0x81
    .equ DreamMail, 0x82
    .equ FabMail, 0x83
    .equ RetroMail, 0x84
    .equ CheriBerry, 0x85
    .equ ChestoBerry, 0x86
    .equ PechaBerry, 0x87
    .equ RawstBerry, 0x88
    .equ AspearBerry, 0x89
    .equ LeppaBerry, 0x8A
    .equ OranBerry, 0x8B
    .equ PersimBerry, 0x8C
    .equ LumBerry, 0x8D
    .equ SitrusBerry, 0x8E
    .equ FigyBerry, 0x8F
    .equ WikiBerry, 0x90
    .equ MagoBerry, 0x91
    .equ AguavBerry, 0x92
    .equ IapapaBerry, 0x93
    .equ RazzBerry, 0x94
    .equ BlukBerry, 0x95
    .equ NanabBerry, 0x96
    .equ WepearBerry, 0x97
    .equ PinapBerry, 0x98
    .equ PomegBerry, 0x99
    .equ KelpsyBerry, 0x9A
    .equ QualotBerry, 0x9B
    .equ HondewBerry, 0x9C
    .equ GrepaBerry, 0x9D
    .equ TamatoBerry, 0x9E
    .equ CornnBerry, 0x9F
    .equ MagostBerry, 0xA0
    .equ RabutaBerry, 0xA1
    .equ NomelBerry, 0xA2
    .equ SpelonBerry, 0xA3
    .equ PamtreBerry, 0xA4
    .equ WatmelBerry, 0xA5
    .equ DurinBerry, 0xA6
    .equ BelueBerry, 0xA7
    .equ LiechiBerry, 0xA8
    .equ GanlonBerry, 0xA9
    .equ SalacBerry, 0xAA
    .equ PetayaBerry, 0xAB
    .equ ApicotBerry, 0xAC
    .equ LansatBerry, 0xAD
    .equ StarfBerry, 0xAE
    .equ EnigmaBerry, 0xAF
    .equ BlackSludge, 0xB0
    .equ AssaultVest, 0xB1
    .equ RazorClaw, 0xB2
    .equ BrightPowder, 0xB3
    .equ WhiteHerb, 0xB4
    .equ MachoBrace, 0xB5
    .equ ExpBrace, 0xB6
    .equ QuickClaw, 0xB7
    .equ SootheBell, 0xB8
    .equ MentalHerb, 0xB9
    .equ ChoiceBand, 0xBA
    .equ KingsRock, 0xBB
    .equ SilverPowder, 0xBC
    .equ AmuletCoin, 0xBD
    .equ CleanseTag, 0xBE
    .equ SoulDew, 0xBF
    .equ DeepSeaTooth, 0xC0
    .equ DeepSeaScale, 0xC1
    .equ SmokeBall, 0xC2
    .equ Everstone, 0xC3
    .equ FocusBand, 0xC4
    .equ LuckyEgg, 0xC5
    .equ ScopeLens, 0xC6
    .equ MetalCoat, 0xC7
    .equ Leftovers, 0xC8
    .equ DragonScale, 0xC9
    .equ LightBall, 0xCA
    .equ SoftSand, 0xCB
    .equ HardStone, 0xCC
    .equ MiracleSeed, 0xCD
    .equ Blackglasses, 0xCE
    .equ BlackBelt, 0xCF
    .equ Magnet, 0xD0
    .equ MysticWater, 0xD1
    .equ SharpBeak, 0xD2
    .equ PoisonBarb, 0xD3
    .equ NeverMeltIce, 0xD4
    .equ SpellTag, 0xD5
    .equ TwistedSpoon, 0xD6
    .equ Charcoal, 0xD7
    .equ DragonFang, 0xD8
    .equ SilkScarf, 0xD9
    .equ UpGrade, 0xDA
    .equ ShellBell, 0xDB
    .equ SeaIncense, 0xDC
    .equ MindIncense, 0xDD
    .equ LuckyPunch, 0xDE
    .equ MetalPowder, 0xDF
    .equ ThickClub, 0xE0
    .equ Stick, 0xE1
    .equ SmoothRock, 0xE2
    .equ IcyRock, 0xE3
    .equ DampRock, 0xE4
    .equ HeatRock, 0xE5
    .equ WideLens, 0xE6
    .equ LightClay, 0xE7
    .equ PowerWeight, 0xE8
    .equ PowerBracer, 0xE9
    .equ PowerBelt, 0xEA
    .equ PowerAnklet, 0xEB
    .equ PowerLens, 0xEC
    .equ PowerBand, 0xED
    .equ BigNugget, 0xEE
    .equ ShinyCharm, 0xEF
    .equ RelicCrown, 0xF0
    .equ RelicStatue, 0xF1
    .equ RelicVase, 0xF2
    .equ MicleBerry, 0xF3
    .equ CustapBerry, 0xF4
    .equ JabocaBerry, 0xF5
    .equ RazorRang, 0xF6
    .equ Eviolite, 0xF7
    .equ FloatStone, 0xF8
    .equ BindingVine, 0xF9
    .equ IronBall, 0xFA
    .equ CellBattery, 0xFB
    .equ FlameOrb, 0xFC
    .equ PixieOrb, 0xFD
    .equ RedScarf, 0xFE
    .equ BlueScarf, 0xFF
    .equ PinkScarf, 0x100
    .equ GreenScarf, 0x101
    .equ YellowScarf, 0x102
    .equ SnowCloak, 0x103
    .equ CoinCase, 0x104
    .equ ItemFinder, 0x105
    .equ OldRod, 0x106
    .equ GoodRod, 0x107
    .equ SuperRod, 0x108
    .equ SSPostcard, 0x109
    .equ BowPostcard, 0x10A
    .equ BooPostcard, 0x10B
    .equ WailmerPail, 0x10C
    .equ Suitcase, 0x10D
    .equ ZapPostcard, 0x10E
    .equ ClosetKey, 0x10F
    .equ SunCloak, 0x110
    .equ SandCloak, 0x111
    .equ Letter, 0x112
    .equ EonPostcard, 0x113
    .equ Orb, 0x114
    .equ SapphireEgg, 0x115
    .equ RareBone, 0x116
    .equ PearlEgg, 0x117
    .equ Meteorite, 0x118
    .equ PowPostcard, 0x119
    .equ BronzeEgg, 0x11A
    .equ SilverEgg, 0x11B
    .equ GoldenEgg, 0x11C
    .equ RubyEgg, 0x11D
    .equ RootFossil, 0x11E
    .equ ClawFossil, 0x11F
    .equ FunPostcard, 0x120
    .equ TM01, 0x121
    .equ TM02, 0x122
    .equ TM03, 0x123
    .equ TM04, 0x124
    .equ TM05, 0x125
    .equ TM06, 0x126
    .equ TM07, 0x127
    .equ TM08, 0x128
    .equ TM09, 0x129
    .equ TM10, 0x12A
    .equ TM11, 0x12B
    .equ TM12, 0x12C
    .equ TM13, 0x12D
    .equ TM14, 0x12E
    .equ TM15, 0x12F
    .equ TM16, 0x130
    .equ TM17, 0x131
    .equ TM18, 0x132
    .equ TM19, 0x133
    .equ TM20, 0x134
    .equ TM21, 0x135
    .equ TM22, 0x136
    .equ TM23, 0x137
    .equ TM24, 0x138
    .equ TM25, 0x139
    .equ TM26, 0x13A
    .equ TM27, 0x13B
    .equ TM28, 0x13C
    .equ TM29, 0x13D
    .equ TM30, 0x13E
    .equ TM31, 0x13F
    .equ TM32, 0x140
    .equ TM33, 0x141
    .equ TM34, 0x142
    .equ TM35, 0x143
    .equ TM36, 0x144
    .equ TM37, 0x145
    .equ TM38, 0x146
    .equ TM39, 0x147
    .equ TM40, 0x148
    .equ TM41, 0x149
    .equ TM42, 0x14A
    .equ TM43, 0x14B
    .equ TM44, 0x14C
    .equ TM45, 0x14D
    .equ TM46, 0x14E
    .equ TM47, 0x14F
    .equ TM48, 0x150
    .equ TM49, 0x151
    .equ TM50, 0x152
    .equ HM01, 0x153
    .equ HM02, 0x154
    .equ HM03, 0x155
    .equ HM04, 0x156
    .equ HM05, 0x157
    .equ HM06, 0x158
    .equ HM07, 0x159
    .equ HM08, 0x15A
    .equ AmethystEgg, 0x15B
    .equ GarnetEgg, 0x15C
    .equ Package, 0x15D
    .equ LostFlute, 0x15E
    .equ FocusSash, 0x15F
    .equ BikePostcard, 0x160
    .equ ToxicOrb, 0x161
    .equ OldAmber, 0x162
    .equ SandIncense, 0x163
    .equ RockyHelmet, 0x164
    .equ HelixFossil, 0x165
    .equ DomeFossil, 0x166
    .equ ShedShell, 0x167
    .equ Bicycle, 0x168
    .equ TownMap, 0x169
    .equ RainCloak, 0x16A
    .equ OddIncense, 0x16B
    .equ TMCase, 0x16C
    .equ BerryPouch, 0x16D
    .equ SheepyTV, 0x16E
    .equ TriPostcard, 0x16F
    .equ PictureBook, 0x170
    .equ Snowball, 0x171
    .equ MysticTicket, 0x172
    .equ PoryPhone, 0x173
    .equ WiseGlasses, 0x174
    .equ Ruby, 0x175
    .equ Sapphire, 0x176

    .equ QuiverDance, 0x1
    .equ KarateChop, 0x2
    .equ DoubleSlap, 0x3
    .equ CometPunch, 0x4
    .equ MegaPunch, 0x5
    .equ PayDay, 0x6
    .equ FirePunch, 0x7
    .equ IcePunch, 0x8
    .equ Thunderpunch, 0x9
    .equ Scratch, 0xA
    .equ ViceGrip, 0xB
    .equ Guillotine, 0xC
    .equ RazorWind, 0xD
    .equ SwordsDance, 0xE
    .equ Cut, 0xF
    .equ Gust, 0x10
    .equ WingAttack, 0x11
    .equ Whirlwind, 0x12
    .equ Fly, 0x13
    .equ Bind, 0x14
    .equ Slam, 0x15
    .equ VineWhip, 0x16
    .equ Stomp, 0x17
    .equ DoubleKick, 0x18
    .equ MegaKick, 0x19
    .equ JumpKick, 0x1A
    .equ RollingKick, 0x1B
    .equ SandAttack, 0x1C
    .equ Headbutt, 0x1D
    .equ HornAttack, 0x1E
    .equ FuryAttack, 0x1F
    .equ DragonRush, 0x20
    .equ Tackle, 0x21
    .equ BodySlam, 0x22
    .equ Wrap, 0x23
    .equ TakeDown, 0x24
    .equ Thrash, 0x25
    .equ DoubleEdge, 0x26
    .equ TailWhip, 0x27
    .equ PoisonSting, 0x28
    .equ Twineedle, 0x29
    .equ PinMissisle, 0x2A
    .equ Leer, 0x2B
    .equ Bite, 0x2C
    .equ Growl, 0x2D
    .equ Roar, 0x2E
    .equ Sing, 0x2F
    .equ Supersonic, 0x30
    .equ SonicBoom, 0x31
    .equ Disable, 0x32
    .equ Acid, 0x33
    .equ Ember, 0x34
    .equ Flamethrower, 0x35
    .equ Mist, 0x36
    .equ WaterGun, 0x37
    .equ HydroPump, 0x38
    .equ Surf, 0x39
    .equ IceBeam, 0x3A
    .equ Blizzard, 0x3B
    .equ Psybeam, 0x3C
    .equ BubbleBeam, 0x3D
    .equ AuroraBeam, 0x3E
    .equ HyperBeam, 0x3F
    .equ Peck, 0x40
    .equ DrillPeck, 0x41
    .equ Recycle, 0x42
    .equ LowKick, 0x43
    .equ Counter, 0x44
    .equ SeismicToss, 0x45
    .equ Strength, 0x46
    .equ Absorb, 0x47
    .equ MegaDrain, 0x48
    .equ LeechSeed, 0x49
    .equ Growth, 0x4A
    .equ RazorLeaf, 0x4B
    .equ SolarBeam, 0x4C
    .equ Poisonpowder, 0x4D
    .equ StunSpore, 0x4E
    .equ SleepPowder, 0x4F
    .equ PetalDance, 0x50
    .equ StringShot, 0x51
    .equ Snatch, 0x52
    .equ FireSpin, 0x53
    .equ Thundershock, 0x54
    .equ Thunderbolt, 0x55
    .equ ThunderWave, 0x56
    .equ Thunder, 0x57
    .equ RockThrow, 0x58
    .equ Earthquake, 0x59
    .equ Fissure, 0x5A
    .equ Dig, 0x5B
    .equ Toxic, 0x5C
    .equ Confusion, 0x5D
    .equ Psychic, 0x5E
    .equ Hypnosis, 0x5F
    .equ Meditate, 0x60
    .equ Agility, 0x61
    .equ QuickAttack, 0x62
    .equ Rage, 0x63
    .equ Teleport, 0x64
    .equ NightShade, 0x65
    .equ Mimic, 0x66
    .equ Screech, 0x67
    .equ DoubleTeam, 0x68
    .equ Recover, 0x69
    .equ Harden, 0x6A
    .equ Minimize, 0x6B
    .equ Smokescreen, 0x6C
    .equ ConfuseRay, 0x6D
    .equ Withdraw, 0x6E
    .equ DefenseCurl, 0x6F
    .equ Barrier, 0x70
    .equ LightScreen, 0x71
    .equ Haze, 0x72
    .equ Reflect, 0x73
    .equ IceBurn, 0x74
    .equ OminousWind, 0x75
    .equ Metronome, 0x76
    .equ MirrorMove, 0x77
    .equ PowerGem, 0x78
    .equ EggBomb, 0x79
    .equ Lick, 0x7A
    .equ Smog, 0x7B
    .equ Sludge, 0x7C
    .equ BoneClub, 0x7D
    .equ FireBlast, 0x7E
    .equ Waterfall, 0x7F
    .equ Clamp, 0x80
    .equ Swift, 0x81
    .equ SkullBash, 0x82
    .equ SpikeCannon, 0x83
    .equ Constrict, 0x84
    .equ Amnesia, 0x85
    .equ Kinesis, 0x86
    .equ SoftBoiled, 0x87
    .equ HiJumpKick, 0x88
    .equ Glare, 0x89
    .equ DreamEater, 0x8A
    .equ PoisonGas, 0x8B
    .equ Barrage, 0x8C
    .equ LeechLife, 0x8D
    .equ LovelyKiss, 0x8E
    .equ SkyAttack, 0x8F
    .equ Transform, 0x90
    .equ Bubble, 0x91
    .equ DizzyPunch, 0x92
    .equ Spore, 0x93
    .equ SwitchMove, 0x94
    .equ SkillSwap, 0x95
    .equ PoisonJab, 0x96
    .equ AcidArmor, 0x97
    .equ Crabhammer, 0x98
    .equ SeedBomb, 0x99
    .equ FurySwipes, 0x9A
    .equ Bonemerang, 0x9B
    .equ Rest, 0x9C
    .equ RockSlide, 0x9D
    .equ HyperFang, 0x9E
    .equ Sharpen, 0x9F
    .equ Conversion, 0xA0
    .equ TriAttack, 0xA1
    .equ SuperFang, 0xA2
    .equ Slash, 0xA3
    .equ Substitute, 0xA4
    .equ Struggle, 0xA5
    .equ Sketch, 0xA6
    .equ TripleKick, 0xA7
    .equ Thief, 0xA8
    .equ SpiderWeb, 0xA9
    .equ MindReader, 0xAA
    .equ Nightmare, 0xAB
    .equ FlameWheel, 0xAC
    .equ Snore, 0xAD
    .equ Curse, 0xAE
    .equ Flail, 0xAF
    .equ Conversion2, 0xB0
    .equ Aeroblast, 0xB1
    .equ CottonGuard, 0xB2
    .equ Reversal, 0xB3
    .equ Spite, 0xB4
    .equ PowderSnow, 0xB5
    .equ Protect, 0xB6
    .equ MachPunch, 0xB7
    .equ ScaryFace, 0xB8
    .equ FeintAttack, 0xB9
    .equ SweetKiss, 0xBA
    .equ BellyDrum, 0xBB
    .equ SludgeBomb, 0xBC
    .equ MudSlap, 0xBD
    .equ Octazooka, 0xBE
    .equ Spikes, 0xBF
    .equ ZapCannon, 0xC0
    .equ Moonblast, 0xC1
    .equ DestinyBond, 0xC2
    .equ PerishSong, 0xC3
    .equ IcyWind, 0xC4
    .equ Detect, 0xC5
    .equ BoneRush, 0xC6
    .equ LockOn, 0xC7
    .equ Outrage, 0xC8
    .equ Sandstorm, 0xC9
    .equ GigaDrain, 0xCA
    .equ Endure, 0xCB
    .equ Charm, 0xCC
    .equ Rollout, 0xCD
    .equ FalseSwipe, 0xCE
    .equ Swagger, 0xCF
    .equ MilkDrink, 0xD0
    .equ Spark, 0xD1
    .equ FuryCutter, 0xD2
    .equ SteelWing, 0xD3
    .equ MeanLook, 0xD4
    .equ Attract, 0xD5
    .equ SleepTalk, 0xD6
    .equ HealBell, 0xD7
    .equ Return, 0xD8
    .equ Present, 0xD9
    .equ Roost, 0xDA
    .equ Safeguard, 0xDB
    .equ PainSplit, 0xDC
    .equ SacredFire, 0xDD
    .equ Magnitude, 0xDE
    .equ DynamicPunch, 0xDF
    .equ Megahorn, 0xE0
    .equ Dragonbreath, 0xE1
    .equ BatonPass, 0xE2
    .equ Encore, 0xE3
    .equ Pursuit, 0xE4
    .equ RapidSpin, 0xE5
    .equ SweetScent, 0xE6
    .equ IronTail, 0xE7
    .equ MetalClaw, 0xE8
    .equ VitalThrow, 0xE9
    .equ MorningSun, 0xEA
    .equ Synthesis, 0xEB
    .equ Moonlight, 0xEC
    .equ HiddenPower, 0xED
    .equ CrossChop, 0xEE
    .equ Twister, 0xEF
    .equ RainDance, 0xF0
    .equ SunnyDay, 0xF1
    .equ Crunch, 0xF2
    .equ MirrorCoat, 0xF3
    .equ PsychUp, 0xF4
    .equ Extremespeed, 0xF5
    .equ Ancientpower, 0xF6
    .equ ShadowBall, 0xF7
    .equ FutureSight, 0xF8
    .equ RockSmash, 0xF9
    .equ Whirlpool, 0xFA
    .equ BeatUp, 0xFB
    .equ FakeOut, 0xFC
    .equ Uproar, 0xFD
    .equ Stockpile, 0xFE
    .equ SpitUp, 0xFF
    .equ Swallow, 0x100
    .equ HeatWave, 0x101
    .equ Hail, 0x102
    .equ Torment, 0x103
    .equ Flatter, 0x104
    .equ WillOWisp, 0x105
    .equ FlameCharge, 0x106
    .equ Facade, 0x107
    .equ FocusPunch, 0x108
    .equ Smellingsalt, 0x109
    .equ DracoMeteor, 0x10A
    .equ NaturePower, 0x10B
    .equ Charge, 0x10C
    .equ CrossPoison, 0x10D
    .equ DazzleGleam, 0x10E
    .equ Trick, 0x10F
    .equ EarthPower, 0x110
    .equ Wish, 0x111
    .equ Assist, 0x112
    .equ Ingrain, 0x113
    .equ Superpower, 0x114
    .equ MagicCoat, 0x115
    .equ XScissor, 0x116
    .equ Revenge, 0x117
    .equ BrickBreak, 0x118
    .equ Yawn, 0x119
    .equ KnockOff, 0x11A
    .equ Endeavor, 0x11B
    .equ Eruption, 0x11C
    .equ LightOfRuin, 0x11D
    .equ DrainKiss, 0x11E
    .equ Refresh, 0x11F
    .equ Grudge, 0x120
    .equ FairyWind, 0x121
    .equ SecretPower, 0x122
    .equ Dive, 0x123
    .equ ArmThrust, 0x124
    .equ Camouflage, 0x125
    .equ TailGlow, 0x126
    .equ LusterPurge, 0x127
    .equ MistBall, 0x128
    .equ Featherdance, 0x129
    .equ TeeterDance, 0x12A
    .equ BlazeKick, 0x12B
    .equ ShadowClaw, 0x12C
    .equ IceBall, 0x12D
    .equ NeedleArm, 0x12E
    .equ SlackOff, 0x12F
    .equ HyperVoice, 0x130
    .equ PoisonFang, 0x131
    .equ CrushClaw, 0x132
    .equ BlastBurn, 0x133
    .equ HydroCannon, 0x134
    .equ MeteorMash, 0x135
    .equ Astonish, 0x136
    .equ WeatherBall, 0x137
    .equ Aromatherapy, 0x138
    .equ FakeTears, 0x139
    .equ AirSlash, 0x13A
    .equ Overheat, 0x13B
    .equ PlayRough, 0x13C
    .equ RockTomb, 0x13D
    .equ SilverWind, 0x13E
    .equ MetalSound, 0x13F
    .equ Grasswhistle, 0x140
    .equ Tickle, 0x141
    .equ CosmicPower, 0x142
    .equ WaterSpout, 0x143
    .equ SignalBeam, 0x144
    .equ ShadowPunch, 0x145
    .equ Extrasensory, 0x146
    .equ SkyUppercut, 0x147
    .equ SandTomb, 0x148
    .equ SheerCold, 0x149
    .equ MuddyWater, 0x14A
    .equ BulletSeed, 0x14B
    .equ AerialAce, 0x14C
    .equ IcicleSpear, 0x14D
    .equ IronDefense, 0x14E
    .equ Block, 0x14F
    .equ Howl, 0x150
    .equ DragonClaw, 0x151
    .equ FrenzyPlant, 0x152
    .equ BulkUp, 0x153
    .equ Bounce, 0x154
    .equ MudShot, 0x155
    .equ PoisonTail, 0x156
    .equ Covet, 0x157
    .equ VoltTackle, 0x158
    .equ MagicalLeaf, 0x159
    .equ Scald, 0x15A
    .equ CalmMind, 0x15B
    .equ LeafBlade, 0x15C
    .equ DragonDance, 0x15D
    .equ RockBlast, 0x15E
    .equ ShockWave, 0x15F
    .equ WaterPulse, 0x160
    .equ DoomDesire, 0x161
    .equ PsychoBoost, 0x162

    .equ Normal, 0x0
    .equ Fighting, 0x1
    .equ Flying, 0x2
    .equ Poison, 0x3
    .equ Ground, 0x4
    .equ Rock, 0x5
    .equ Bug, 0x6
    .equ Ghost, 0x7
    .equ Steel, 0x8
    .equ Fairy, 0x9
    .equ Fire, 0xA
    .equ Water, 0xB
    .equ Grass, 0xC
    .equ Electric, 0xD
    .equ Psychic, 0xE
    .equ Ice, 0xF
    .equ Dragon, 0x10
    .equ Dark, 0x11

    .macro Pointer Address
    .word \Address
    .endm

    .equ ROM, 0x8000000
    .equ Offset, 0x

    .org 0x1D9BF4, 0xFF
    Pointer MoveCheck + ROM

    .org 0x1D9C97, 0xFF
    Pointer WonderGuardCheck + ROM

    .org 0x1D9D2F, 0xFF
    Pointer DampCheck + ROM

    .org 0x1D9D35, 0xFF
    Pointer DreamEaterCheck + ROM

    .org 0x1D9DAD, 0xFF
    Pointer OHKO + ROM

    .org 0x1D9E31, 0xFF
    Pointer ParalyseCheck + ROM

    .org 0x1D9ECD, 0xFF
    Pointer MagnitudeCheck + ROM

    .org 0x1DA44D, 0xFF
    Pointer AbsorbCheck + ROM

    .org 0x1DA459, 0xFF
    Pointer DreamEaterCheck2 + ROM

    .org Offset, 0xFF
    AbsorbCheck:
    JumpIfDamageBonusEquals 0x0 0x14 0x81DA749
    JumpIfDamageBonusEquals 0x0 0xA 0x81DA749
    Jump 0x81DA751

    .org Offset+0x13, 0xFF
    DreamEaterCheck2:
    JumpIfDamageBonusEquals 0x0 0xA 0x81DA7C1
    JumpIfDamageBonusEquals 0x0 0x14 0x81DA7C1
    Jump 0x81DA7C3

    .org Offset+0x26, 0xFF
    MoveCheck:
    GetPowerOfStrongestMove
    JumpIfByteEquals 0x0 0x81D9CE0
    JumpIfDamageBonusEquals 0x0 0x0 0x81DA433
    GetAbility Defender
    JumpIfByteEquals 0xA 0x81D9CA6
    JumpIfByteEquals 0xB 0x81D9CB3
    JumpIfByteEquals 0x12 0x81D9CC0
    JumpIfByteEquals 0x19 0x81D9CCD
    JumpIfByteEquals 0x1A 0x81D9CD8
    Jump 0x81D9CE0

    .org Offset+0x59, 0xFF
    WonderGuardCheck:
    JumpIfDamageBonusEquals 0x0 0x50 0x81D9CE0
    Jump 0x81DA433

    .org Offset+0x65, 0xFF
    DampCheck:
    JumpIfDamageBonusEquals 0x0 0x0 0x81DA433
    GetAbility Defender
    JumpIfByteEquals 0x6 0x81DA433
    CountViablePokemonOnTeam Attacker
    JumpIfByteNotEqual 0x0 0x81D9FF2
    CountViablePokemonOnTeam Defender
    JumpIfByteNotEqual 0x0 0x81DA433
    Jump 0x81DA424
    ReturnToBattle

    .org Offset+0x8A, 0xFF
    DreamEaterCheck:
    JumpIfStatus1NotEqual Defender 0x7 0x81DA430
    JumpIfDamageBonusEquals 0x0 0x0 0x81DA433
    ReturnToBattle

    .org Offset+0x9C, 0xFF
    OHKO:
    JumpIfDamageBonusEquals 0x0 0x0 0x81DA433
    GetAbility Defender
    JumpIfByteEquals 0x5 0x81DA433
    JumpIfBattlerLevelsAre 0x1 0x81DA433
    ReturnToBattle

    .org Offset+0xB2, 0xFF
    MagnitudeCheck:
    GetAbility Defender
    JumpIfByteEquals 0x1A 0x81DA433
    JumpIfDamageBonusEquals 0x0 0x0 0x81DA433
    GetAbility Defender
    JumpIfByteNotEqual 0x19 0x81DA1CB
    JumpIfDamageBonusEquals 0x0 0x50 0x81DA1CB
    Jump 0x81DA433

    .org Offset+0xD5, 0xFF
    ParalyseCheck:
    JumpIfDamageBonusEquals 0x0 0x0 0x81DA433
    GetAbility Defender
    JumpIfByteEquals 0x7 0x81DA433
    JumpIfStatus1Equals Defender 0xFF 0x81DA433
    ReturnToBattle

    .org 0xFFFFFF, 0xFF
    .byte 0xFF
  3. Go to the part that says ".equ Offset, 0x" and add after the 0x the offset of some free space. You will need 0xEF (239) bytes worth.
  4. Compile to a binary file with your favourite THUMB compiler.
  5. Create an ips patch by using the blank file as the unmodified file and your new binary file as the modified one.
  6. Now you can patch a FireRed ROM with it.

Documents
Since I keep updating this because I either add new commands or update old ones, I decided it would be way easier to have a Google Doc with all the information on it. It's easier for me to update that.

There are three. The first is the command description list. Please note that these descriptions are of the updated commands. So if a command was rewritten or updated, the rewritten/updated version is used. It also includes descriptions of new commands.

The second is a folder that has all the rewritten command routines in it.

The third is another folder that has all the new command routines in it.

Links
1. AI Command Descriptions
2. Rewritten Commands
3. New Commands


Callasm Command
I am putting this command separately because it is a big deal. With it, you can have as many commands as you need regardless of how big or small your command table is. You can literally use this to do pretty much anything you want.

The syntax looks like this

Callasm OFFSET + 1
Arguments for asm routine


Put the arguments for the command (if any) immediately after the pointer to the command routine. Then, just format the routine as if it were a normal command called from the table. The AI Cursor is updated in the routine it calls, so you can have as many or as little arguments as you need.

Spoiler:
/*Put XX XX XX 08 at 0x3F5664*/
.text
.align 2
.thumb
.thumb_func

Main:
push {r4, lr}
ldr r4, .AICursor
ldr r3, [r4]
add r3, r3, #0x4
str r3, [r4]
sub r3, r3, #0x4
ldrb r1, [r3, #0x1]
ldrb r0, [r3, #0x2]
lsl r0, r0, #0x8
orr r1, r0
ldrb r0, [r3, #0x3]
lsl r0, r0, #0x10
orr r1, r0
ldrb r0, [r3, #0x4]
lsl r0, r0, #0x18
orr r1, r0
bl DoRoutine
pop {r4}
pop {r0}
bx r0

DoRoutine:
bx r1

.align 2
.AICursor: .word 0x02039A00


Macros
The final gift I would like to impart on the masses at Pokecommunity is this. I've created a few macros to use to help compile scripts.

The link to view all of them is here.

The way to use them is to include the relevant macros at the top of the script (and by that I mean copy and paste them into the beginning of your script), and then just type the script using this sort of form:

.equ ROM, 0x8

Main:
JumpIfMoveScriptEquals 0x1 CauseSleep+ROM
ReturnToBattle

CauseSleep:
JumpIfStatus1Equals Defender 0x000000FF Decrease32+ROM
ReturnToBattle

Decrease32:
AddToViabilityScore 0xE0
ReturnToBattle

That way, you can just compile this with any sort of THUMB assembler. Remember to put the offset that you're inserting the script at after the ".equ ROM, 0x8". If you don't the pointers in your script will be all wrong.

I recommend

Conclusion
This thread is the culmination of over five months worth of research and work. My hope with all of this is to give ROM hackers the ability to truly customize the AI. This can be used not just to raise the difficulty/challenge of games, but also to add support to the ever-growing list of new moves and abilities being added to the gen III games.

ghoulslash November 26th, 2017 10:26 AM

Awesome research! I had been interested in pursuing this as well.

It looks like each AI script is run from a table starting at 0x1D9BF4, where the AI bitfield determines which scripts to run in succession (eg. 0x7 is 0111 in binary which would run scripts 0, 1, and 2 from said table)

There are 18 or 19 scripts that do nothing, so we should be able to easily create our own AI scripts with knizz's and your contributions!

My one question is, do we know where the wild battle AI is set? For example, safari pokemon and roaming Pokemon have a separate AI bitfield; it would awesome to find this location and add in more flag checks for legendary battles, etc.

AkameTheBulbasaur November 26th, 2017 2:56 PM

There are two scripts all the way at the very end of the table that seem to be for Wild Pokemon.

The first is at 0x1DBCA8 and it appears to check for conditions that would stop it from fleeing (Wrap, Mean Look, abilities etc.) and if they aren't there then it will run away. I think this is for roaming Pokemon who flee on the first turn.

Spoiler:

0x81DBCA8
Main:
JumpIfStatus2Equals Attacker 0000E000 Return1
JumpIfStatsu2Equals Attacker 04000000 Return1
GetAbility Defender
JumpIfByteEquals ShadowTag Return1
GetAbility Attacker
JumpIfByteEquals Levitate RunAway1
GetAbility Defender
JumpIfByteEquals ArenaTrap Return1

0x81DBCD4
RunAway1:
RunAway

0x8DBCD5
Return1:
ReturnToBattle


The second one was at 0x1DBCD6 and this appears to be for the Safari Zone, where the Pokemon is more likely to flee if it's at low health (less than 20%). The RunAway command is used twice, so maybe using it more means a higher chance of fleeing?
Spoiler:

0x81DBCD6
Main:
JumpRandomUnknown RunAway1
SafariZone

0x81DBCDC
RunAway1:
RunAway
JumpIfHealthEquals Defender 0x14 RunAway2
JumpIfHealthLessThan Defender 0x14 RunAway2
ReturnToBattle

0x81DBCEC
RunAway2:
RunAway
ReturnToBattle


There aren't any sort of move rating scripts here because Wild Pokemon just use random moves. Possibly editing these could make Wild Pokemon smarter, but I don't know how to make it work for only specific Pokemon (like legendaries and such). That would be interesting to look into though!

I'm not sure where the bitfield is set. The bitfield for each trainer is set in the data for each specific trainer (so gym leaders/E4 have different difficulty than some random Youngster). The bitfield for Safari Zone/Roamers might be set in some other routine that activates those battle types.

ghoulslash November 27th, 2017 8:45 PM

Yeah I tried looking into where an AI script might be loaded for a roaming pokemon but didn't have any luck yet.

There is a function call at 0801e1b6 that runs through the type effectiveness table/levitate/etc checks and returns a bitfield value to r6 (0x0 is regular effectiveness, 0x1 for super effective, and 0x2 for not very effective). It does an OR operation so you could return 0x3 if the attack is super effective on the target's first type and resisted by the 2nd type.

I might be wrong but it looks like the JumpIfDamageBonusEquals calls a different function, so this routine could be used by wild pokemon as well rather than choosing a move completely at random? What is strange is that it is called within the accuracy check battle command.

AkameTheBulbasaur November 28th, 2017 6:00 PM

I was messing around with the debugger yesterday because I was trying to see if changing the Battle Type to the Safari bit (0x80) would activate the script.

Long story short, it didn't. What it did was make the Safari Ball counter and menu options come up, but the Pokemon still behaves like it would have normally.

Which did end in the somewhat silly result of a wild Wurmple attacking my trainer sprite instead of my Ditto, but ultimately didn't quite give me what I was looking for.

So the script is probably called elsewhere. The AI Script table is only pointed to once in the ROM however.

Skeli December 7th, 2017 10:21 PM

I rewrote the database for Jambo's Battle Script Pro to incorporate the commands for AI scripting. I've decompiled all of the vanilla scripts so it looks like I've worked out all the bugs. I still need to manually enter all of the descriptions for the commands, but that's not my top priority right now. Anyways, please enjoy AI Script Pro!

-Edit: It turns out that at this point the tool can only compile the first 1F commands or so. I need to finish the commands.bsh file so it actually compiles all the commands. Decompilation works fine though.

--Edit 2: It should be ready now.

--Edit 3: I've modified the source code to make it more compatible with AI scripting. As long as you don't devote any lines to just comments, or leave blank spaces in the middle of your #orgs (in between #org and #org is fine) it should work properly.
Spoiler:
Eg. You can do this:
Code:

#org @Start
something
returntobattle

#org @Whatever
returntobattle


But not this:
Code:

#org @start
whatever
something
                                NOOO!
returntobattle

//@@@@@@@@@@ NOOO!

#org @somethingelse
returntobattle



DizzyEgg December 8th, 2017 7:53 AM

I hope you guys are aware that the commands have been known for a very long time and are decompiled in pokeruby/pokeemerald. All AI scripts have also been dumped in pokeruby.

Skeli December 8th, 2017 8:29 AM

Quote:

Originally Posted by DizzyEgg (Post 9806059)
I hope you guys are aware that the commands have been known for a very long time and are decompiled in pokeruby/pokeemerald. All AI scripts have also been dumped in pokeruby.

Yes, but this thread will make it easier for any casual ROM hacker to make their own AI scripts without having to learn C.

AkameTheBulbasaur December 26th, 2017 8:49 PM

I should probably clarify that the point of this thread isn't to introduce the concept of AI scripting or the commands. I'm aware they've been known for a while because the thread I referenced is from 2014. :D

The point was more to describe how they work in more detail (since I couldn't find anything else that really described how to use them in a script. That thread from 2014 was it to my knowledge), and also to introduce the new commands I'd written.

IMPORTANT! I have merged this post with the original one!

I should also announce that some of the commands (GetKindOfMove for example) ONLY WORK WITH THE FREE VAR INSTEAD OF THE MOVE VAR!!!!

I rewrote them so that they can use the move var, which is what is far more useful to check. They are under the Rewritten Commands tab in the original post.

Skeli January 8th, 2018 6:46 PM

I've written a few new routines as well. They're rather simple, but they do the trick. Tbh I haven't tested them yet, but unless I made a mistake, they should work. I plan on testing them hopefully within the next week or so.

5E: GetPersonalCounter
Arguments: 1 Byte, 1 Word
This command takes the bank and the counter you wish to check (Magnet Rise, Telekinesis, etc.) for that Pokemon, and returns whatever number is in the counter. Basically it tells the AI if the counter isn't at 0, don't use the move.
Spoiler:
Code:

.thumb
.global AI_GetPersonalCounter

@GetPersonalCounter BANK COUNTER
Main:
push {r4-r5,lr}
ldr r5, .AICursor
ldr r0, [r5]
ldrb r0, [r0, #0x1]
bl GetBattleSide

GetCounter:
ldr r1, [r5]
ldrb r2, [r1, #0x3]
ldrb r3, [r1, #0x4]
ldrb r4, [r1, #0x5]
ldrb r1, [r1, #0x2]
lsl r2, #0x8
lsl r3, #0x10
lsl r4, #0x18
orr r1, r2
orr r1, r3
orr r1, r4

LoadCounter:
add r1, r0
ldrb r1, [r1]

Store:
ldr r0, .Resources
ldr r0, [r0]
ldr r0, [r0, #0x14]
str r1, [r0, #0x8]

Return:
ldr r0, [r5]
add r0, #0x6
str r0, [r5]
pop {r4-r5,pc}

GetBattleSide:
ldr r1, =0x8016E25
bx r1

.align 2
.Resources:        .word 0x02023FF4
.AICursor:        .word 0x02039A00




5F: GetTeamCounter
Arguments: 1 Byte, 1 Word
This command takes the bank and the team counter you wish to check (Tailwind, Lucky Chant, etc.) for that Pokemon's team, and returns whatever number is in the counter.
Spoiler:
Code:

.thumb
.global AI_GetTeamCounter

@GetTeamCounter BANK COUNTER
Main:
push {r4-r5,lr}
ldr r5, .AICursor
ldr r0, [r5]
ldrb r0, [r0, #0x1]
bl GetBattleSide
mov r1, #0x1
and r0, r1

GetCounter:
ldr r1, [r5]
ldrb r2, [r1, #0x3]
ldrb r3, [r1, #0x4]
ldrb r4, [r1, #0x5]
ldrb r1, [r1, #0x2]
lsl r2, #0x8
lsl r3, #0x10
lsl r4, #0x18
orr r1, r2
orr r1, r3
orr r1, r4

LoadCounter:
add r1, r0
ldrb r1, [r1]

Store:
ldr r0, .Resources
ldr r0, [r0]
ldr r0, [r0, #0x14]
str r1, [r0, #0x8]

Return:
ldr r0, [r5]
add r0, #0x6
str r0, [r5]
pop {r4-r5,pc}

GetBattleSide:
ldr r1, =0x8016E25
bx r1

.align 2
.Resources:        .word 0x02023FF4
.AICursor:        .word 0x02039A00




60: GetFieldCounter
Arguments: 1 Word
This command takes the field counter you wish to check (Gravity, Trick Room, etc.) and returns whatever number is in the counter.
Spoiler:
Code:

.thumb
.global AI_GetFieldCounter

@GetFieldCounter COUNTER
GetCounter:
push {r4,lr}
ldr r4, .AICursor
ldr r0, [r4]
ldrb r1, [r0, #0x2]
ldrb r2, [r0, #0x3]
ldrb r3, [r0, #0x4]
ldrb r0, [r0, #0x1]
lsl r1, #0x8
lsl r2, #0x10
lsl r3, #0x18
orr r0, r1
orr r0, r2
orr r0, r3

LoadByte:
ldrb r1, [r0]

Store:
ldr r0, .Resources
ldr r0, [r0]
ldr r0, [r0, #0x14]
str r1, [r0, #0x8]

Return:
ldr r0, [r4]
add r0, #0x5
str r0, [r4]
pop {r4,pc}

.align 2
.Resources:        .word 0x02023FF4
.AICursor:        .word 0x02039A00




61: GetTerrain
Arguments: None
This command returns the terrain type (Electric, Grassy, etc.). Make sure you replace the terrain offset in the code with the ram loc of your terrain type (if you've implemented terrains of course).
Spoiler:
Code:

.thumb
.global AI_GetTerrain

LoadTerrain:
push {r4,lr}
ldr r0, .TerrainLoc
ldrb r0, [r0]

Store:
ldr r1, .Resources
ldr r1, [r1]
ldr r1, [r1, #0x14]
str r0, [r1, #0x8]

Return:
ldr r4, .AICursor
ldr r0, [r4]
add r0, #0x1
str r0, [r4]
pop {r4,pc}

.align 2
.TerrainLoc:        .word 0x02AAAAAA
.Resources:        .word 0x02023FF4
.AICursor:        .word 0x02039A00




62: GetSpecies
Arguments: 1 Byte
This command takes the bank and returns the species in the bank. 0 is the target, 1 is the user, 2 is the target's partner, and 3 is the user's partner. This can be used, for example, to see if the target is a Minior in Shield Form, and if it is, don't use any status 1 inducing moves.
Spoiler:
Code:

.thumb
.global AI_GetSpecies

@GetSpecies BANK
LoadBank:
push {r4,lr}
ldr r4, .AICursor
ldr r0, [r4]
ldrb r0, [r0, #0x1]
cmp r0, #0x2
bge PartnerCheck
bl GetBattleSide
b LoadBattleData

PartnerCheck:
sub r0, #0x2
bl GetBattleSide
bl GetPartnerBank

LoadBattleData:
mov r1, r0
ldr r0, .BattleData
mov r2, #0x58
mul r1, r2
add r0, r1
ldrh r0, [r0]

Store:
ldr r1, .Resources
ldr r1, [r1]
ldr r1, [r1, #0x14]
str r0, [r1, #0x8]

Return:
ldr r0, [r4]
add r0, #0x2
str r0, [r4]
pop {r4,pc}

GetBattleSide:
ldr r1, =0x8016E25
bx r1

GetPartnerBank:
ldr r1, =0x8XXXXXX + 1
bx r1

.align 2
.BattleData:        .word 0x2023BE4
.Resources:        .word 0x2023FF4
.AICursor:        .word 0x2039A00




63: GetHeldItem
Arguments: 1 Byte
This command takes the bank and returns the held item of the Pokemon in that bank. 0 is the target, 1 is the user, 2 is the target's partner, and 3 is the user's partner. This can be used for Fling or Acrobatics to see if the attacker is holding an item.
Spoiler:
Code:

.thumb
.global AI_GetHeldItem

@GetHeldItem BANK
LoadBank:
push {r4,lr}
ldr r4, .AICursor
ldr r0, [r4]
ldrb r0, [r0, #0x1]
cmp r0, #0x2
bge PartnerCheck
bl GetBattleSide
b LoadBattleData

PartnerCheck:
sub r0, #0x2
bl GetBattleSide
bl GetPartnerBank

LoadBattleData:
mov r1, r0
ldr r0, .BattleData
mov r2, #0x58
mul r1, r2
add r0, r1
ldrh r0, [r0]

Store:
ldr r1, .Resources
ldr r1, [r1]
ldr r1, [r1, #0x14]
str r0, [r1, #0x8]

Return:
ldr r0, [r4]
add r0, #0x2
str r0, [r4]
pop {r4,pc}

GetBattleSide:
ldr r1, =0x8016E25
bx r1

GetPartnerBank:
ldr r1, =0x8XXXXXX + 1
bx r1

.align 2
.BattleData:        .word 0x2023C12
.Resources:        .word 0x2023FF4
.AICursor:        .word 0x2039A00




64: GetHeldItemPocket
Arguments: 1 Byte
This command takes the bank and returns the pocket of the held item of the Pokemon in that bank. 0 is the target, 1 is the user, 2 is the target's partner, and 3 is the user's partner. This is basically used to check if the Pokemon is holding a berry.
Spoiler:
Code:

.thumb
.global AI_GetHeldItemPocket

@GetHeldItemPocket BANK
LoadBank:
push {r4,lr}
ldr r4, .AICursor
ldr r0, [r4]
ldrb r0, [r0, #0x1]
cmp r0, #0x2
bge PartnerCheck
bl GetBattleSide
b LoadBattleData

PartnerCheck:
sub r0, #0x2
bl GetBattleSide
bl GetPartnerBank

LoadBattleData:
mov r1, r0
ldr r0, .BattleData
mov r2, #0x58
mul r1, r2
add r0, r1
ldrh r0, [r0]

GetPocketNumber:
bl GetPocket

Store:
ldr r1, .Resources
ldr r1, [r1]
ldr r1, [r1, #0x14]
str r0, [r1, #0x8]

Return:
ldr r0, [r4]
add r0, #0x2
str r0, [r4]
pop {r4,pc}

GetBattleSide:
ldr r1, =0x8016E25
bx r1

GetPocket:
ldr r1, =0x809A9D9
bx r1

GetPartnerBank:
ldr r1, =0x8XXXXXX + 1
bx r1

.align 2
.BattleData:        .word 0x2023C12
.Resources:        .word 0x2023FF4
.AICursor:        .word 0x2039A00




Helper Function: GetPartnerBank
I'm going to be calling on this a lot so you should insert it somewhere and make a record of where you've inserted it.
Spoiler:
Code:

.thumb
.global GetPartnerBankBL

GetPartnerBank:
        push {lr}
        cmp r0, #0x0
        beq P1
        cmp r0, #0x1
        beq O1
        cmp r0, #0x2
        beq P2
        cmp r0, #0x3
        beq O2
P1:        mov r0, #0x2
        pop {pc}
O1:        mov r0, #0x3
        pop {pc}
P2:        mov r0, #0x0
        pop {pc}
O2:        mov r0, #0x1
        pop {pc}



AkameTheBulbasaur January 12th, 2018 11:17 AM

ATTENSHOON!!!!

Here I am including two things that are not scripting related but are important for the AI. The first is a Double Battle Logic routine (actually two of them), and the second is a method for getting the AI to switch out in a controlled manner.

Double Battle Target Logic + RAM Setting
First of all, any of the above routines which use the RAM addresses 02023D6D or 02023D6E need to be changed!!!

I was under the impression that those were the Attacker Partner and Defender Partner banks respectively

THEY ARE NOT!

Instead, you need to use a free RAM address to store those banks.

Here is a branch from the normal routine that sets the Attacker Partner and Defender Partner RAM, and nothing else. If you don't want any kind of target select logic then use this.

Spoiler:
/*Put 00 48 00 47 XX XX XX 08 at 0xC6E28*/
.text
.align 2
.thumb
.thumb_func

SetAttacker:
ldr r1, .Attacker
ldr r0, .Buffer
ldrb r2, [r0]
strb r2, [r1]

SetPartner:
ldr r1, .APartner
cmp r2, #0x1
beq Set3
mov r0, #0x1
strb r0, [r1]
b DoubleCheck

Set3:
mov r0, #0x3
strb r0, [r1]

DoubleCheck:
ldr r0, .BattleType
ldr r0, [r0]
mov r1, #0x1
and r0, r1
cmp r0, #0x0
beq Single

Double:
ldr r4, .Defender
bl RNG
mov r3, #0x2
and r0, r3
strb r0, [r4]
push {r4}
ldr r4, .DPartner
cmp r0, #0x0
beq SetTwo
cmp r0, #0x1
beq SetThree
cmp r0, #0x2
beq SetZero
cmp r0, #0x3
beq SetOne

SetTwo:
mov r0, #0x2
b Return

SetZero:
mov r0, #0x0
b Return

SetOne:
mov r0, #0x1
b Return

SetThree:
mov r0, #0x3
b Return

Return:
strb r0, [r4]
pop {r4}
ldr r0, .Return
bx r0

RNG:
ldr r3, .GetRNG
bx r3

Single:
ldr r0, .Defender1
mov r1, #0x1
eor r1, r2
strb r1, [r0]
ldr r0, .DPartner1
cmp r1, #0x0
beq SetTwo
cmp r1, #0x1
beq SetThree
cmp r1, #0x2
beq SetZero
mov r1, #0x1
strb r1, [r0]
b Return

SetTwo:
mov r1, #0x2
strb r1, [r0]
b Return

SetThree:
mov r1, #0x3
strb r1, [r0]
b Return

SetZero:
mov r1, #0x0
strb r1, [r0]
b Return

ReturnS:
ldr r0, .BattleType
ldr r1, [r0]
ldr r0, .Return2
bx r0

.align 2
.Attacker: .word 0x2023D6B
.Defender: .word 0x2023D6C
.APartner: .word 0x2023D60
.DPartner: .word 0x2023D61

.Buffer: .word 0x2023BC4
.Return: .word 0x80C6E47
.Return2: .word 0x80C6E89
.BattleType: .word 0x2022B4C
.GetRNG: .word 0x8044EC9


Replace 0x2023D60 and 0x2023D61 with your choice of RAM if you don't want to/can't use those.

The next part is the same routine but with extra logic so that under certain conditions in Double Battles, the AI will target the partner. For example, of the partner has Volt Absorb, is low on health and the Attacker has a damaging Electric Type move, they will target the partner. This only sets the target, you have to get it to use the right move in an AI script!!! That is easy enough to do, however.

Fair warning! This is super duper long! I tried to shorten it as much as possible and it's still really long. Sorry about that!

Spoiler:

/*Insert 00 48 00 47 XX XX XX 08 at 0xC6E28*/
.text
.align 2
.thumb
.thumb_func

SetAttacker:
ldr r1, .Attacker1
ldr r0, .Buffer
ldrb r2, [r0]
strb r2, [r1]

SetPartner:
ldr r1, .APartner1
cmp r2, #0x1
beq Set3
mov r0, #0x1
strb r0, [r1]
b GetBattleType

Set3:
mov r0, #0x3
strb r0, [r1]

GetBattleType:
ldr r0, .BattleType
ldr r0, [r0]
mov r1, #0x1 /*Double*/
and r0, r1
cmp r0, #0x0
beq Single

Double:
push {r0-r7}
ldr r0, .Attacker1
ldrb r0, [r0]
bl GetHealth
cmp r3, #0x32
ble SetDefender

Checks:
bl CheckHealingMove
cmp r4, #0x1
beq TargetPartner
cmp r4, #0x2
beq SkipHealingAbilities

CheckAbilities:
ldr r0, .APartner1
ldrb r0, [r0]
mov r1, #0x20
bl UserDataByte
cmp r2, #0xA
beq VoltAbsorb
cmp r2, #0xB
beq WaterAbsorb

SkipHealingAbilities:
ldr r0, .APartner1
ldrb r0, [r0]
mov r1, #0x20
bl UserDataByte
cmp r2, #0x12
beq FlashFire
cmp r2, #0x14
beq OwnTempo
cmp r2, #0x1F
beq Lightningrod
cmp r2, #0x3E
beq Guts
cmp r2, #0x3F
beq Guts
b CheckYourAbility


CheckYourAbility:
mov r5, #0x1
ldr r0, .Attacker1
ldrb r0, [r0]
bl GetHealth
cmp r3, #0x4B
bge CMPFF2
ldr r0, .Attacker1
ldrb r0, [r0]
mov r1, #0x20
bl UserDataByte
cmp r2, #0xA
beq VoltAbsorb
cmp r2, #0xB
beq WaterAbsorb

CMPFF2:
ldr r0, .Attacker1
ldrb r0, [r0]
mov r1, #0x20
bl UserDataByte
cmp r2, #0x12
beq FlashFire
cmp r2, #0x1F
beq Lightningrod
b CheckItems

VoltAbsorb:
mov r7, #0xD
bl TypeCheck
b Next

WaterAbsorb:
mov r7, #0xD
bl TypeCheck
b Next

FlashFire:
bl FireCheck
b Next

Guts:
bl Guts2
b Next

Lightningrod:
bl RodCheck
b Next

OwnTempo:
bl Tempo

Next:
cmp r4, #0x1
beq TargetPartner
b CheckItems

CheckItems:
bl ItemCheck
cmp r4, #0x1
beq TargetPartner
b SetDefender

TargetPartner:
ldr r1, .APartner1
ldrb r1, [r1]
ldr r0, .Defender1
strb r1, [r0]
ldr r1, .Attacker1
ldrb r1, [r1]
ldr r0, .DPartner1
strb r1, [r0]
b Return

SetDefender:
bl RNG
mov r3, #0x2
and r0, r3
mov r4, r0
ldr r0, .Defender1
strb r4, [r0]
ldr r0, .DPartner1
cmp r4, #0x0
beq SetTwo
cmp r4, #0x2
beq SetZero

SetTwo:
mov r4, #0x2
strb r4, [r0]
b Return

SetZero:
mov r4, #0x0
strb r4, [r0]

Return:
pop {r0-r7}
ldr r0, .ReturnD
bx r0

Single:
ldr r0, .Defender1
mov r1, #0x1
eor r1, r2
strb r1, [r0]
ldr r0, .DPartner1
cmp r1, #0x0
beq SetTwo
cmp r1, #0x1
beq SetThree
cmp r1, #0x2
beq SetZero
mov r1, #0x1
strb r1, [r0]
b Return

SetTwo:
mov r1, #0x2
strb r1, [r0]
b Return

SetThree:
mov r1, #0x3
strb r1, [r0]
b Return

SetZero:
mov r1, #0x0
strb r1, [r0]
b Return

ReturnS:
ldr r0, .BattleType
ldr r1, [r0]
ldr r0, .ReturnS
bx r0


.align 2
.ReturnD: .word 0x080C6E85
.ReturnS: .word 0x080C6E89
.BattleType: .word 0x02022B4C
.Attacker1: .word 0x02023D6B
.Defender1: .word 0x02023D6C
.APartner1: .word 0x02023D60
.DPartner1: .word 0x02023D61
.Buffer: .word 0x02023BC4

/*Checks For Healing Move 0x9D*/

.align 2

CheckHealingMove:
push {r0-r3, r6, lr}
ldr r0, .APartner2
ldrb r0, [r0]
bl GetHealth
cmp r3, #0x32
bgt SetR4Two
mov r6, #0x1

GetMoveScripts:
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker2
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x0
bl GetAttackData
cmp r2, #0x9D
beq CheckPP1

Loop:
add r6, r6, #0x1
cmp r6, #0x4
bge SetR4Zero1
b GetMoveScripts

CheckPP1:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker2
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero1
b SetR4One1

SetR4One1:
mov r4, #0x1
b Return1

SetR4Two:
mov r4, #0x2
b Return1

SetR4Zero1:
mov r4, #0x0

Return1:
pop {r0-r3, r6, pc}

.align 2
.Attacker2: .word 0x02023D6B
.APartner2: .word 0x02023D60

/*Volt and Water Absorb Checks*/
.align 2

TypeCheck:
push {r0-r3, r6, lr}
mov r6, #0x1

MoveChecks:
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker3
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x2
bl GetAttackData
cmp r2, r7
bne Loop2
mov r1, #0x6
bl GetAttackData
cmp r2, #0x0
bne Loop2
cmp r7, #0xD
beq ParCheck

Loop2:
add r6, r6, #0x1
cmp r6, #0x4
bgt SetR4Zero2
b MoveChecks

CheckPP2:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker3
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero2
b SetR4One2

ParCheck:
mov r1, #0x0
bl GetAttackData
cmp r2, #0x43
bne CheckPP2
cmp r5, #0x1
beq CheckYou3
b TypeChecking3

CheckYou3:
ldr r0, .Attacker3
ldrb r0, [r0]
b THING3

TypeChecking3:
ldr r0, .APartner3
ldrb r0, [r0]

THING3:
mov r1, #0x21
bl UserDataByte
cmp r2, r7
beq Loop2
mov r1, #0x22
bl UserDataByte
cmp r2, r7
beq Loop2
b CheckPP2

SetR4Zero2:
mov r4, #0x0
b Return2

SetR4One2:
mov r4, #0x1

Return2:
pop {r0-r3, r6, pc}

.align 2
.Attacker3: .word 0x02023D6B
.APartner3: .word 0x02023D60

/*Flash Fire Check*/
.align 2

FireCheck:
push {r0-r3, r6, lr}
mov r1, #0x4C
bl UserDataWord
mov r1, #0x20
and r2, r1
cmp r2, #0x0
bne SetR4Zero4
ldr r2, .FlashFire
lsl r1, r0, #0x2
add r2, r2, r1
ldr r2, [r2]
mov r1, #0x1
and r1, r2
cmp r1, #0x0
bne SetR4Zero4
mov r7, #0xA
mov r6, #0x1
b MoveChecks2

MoveChecks2:
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker4
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x2
bl GetAttackData
cmp r2, #0xA
bne Loop4
mov r1, #0x6
bl GetAttackData
cmp r2, #0x0
bne Loop4
b Burn

Loop4:
add r6, r6, #0x1
cmp r6, #0x4
bgt SetR4Zero4
b MoveChecks2

CheckPP4:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker4
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero4
b SetR4One4

Burn:
mov r1, #0x0
bl GetAttackData
cmp r2, #0xA7
cmp r5, #0x1
beq CheckYou4
b TypeChecking4

CheckYou4:
ldr r0, .Attacker4
ldrb r0, [r0]
b THING4

TypeChecking4:
ldr r0, .APartner4
ldrb r0, [r0]

THING4:
mov r1, #0x21
bl UserDataByte
cmp r2, #0xA
beq Loop4
mov r1, #0x22
bl UserDataByte
cmp r2, #0xA
beq Loop4
b CheckPP4

SetR4Zero4:
mov r4, #0x0
b Return4

SetR4One4:
mov r4, #0x1

Return4:
pop {r0-r3, r6, pc}

.align 2
.FlashFire: .word 0x02000300
.Attacker4: .word 0x02023D6B
.APartner4: .word 0x02023D60

/*Lightningrod Check*/
.align 2

RodCheck:
push {r0-r3, r6, lr}
mov r1, #0x19
bl UserDataByte
mov r3, r2
mov r1, #0x1C
bl UserDataByte
cmp r3, #0xC
bne Next2
cmp r2, #0xC
beq SetR4Zero9
b Next2

Next2:
mov r6, #0x1

MoveChecks8:
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker8
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x2
bl GetAttackData
cmp r2, #0xD
bne Loop8
mov r1, #0x6
bl GetAttackData
cmp r2, #0x0
bne Loop8
b ParCheck2

Loop8:
add r6, r6, #0x1
cmp r6, #0x4
bge SetR4Zero9
b MoveChecks8


CheckPP8:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker8
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero9
b SetR4One8

ParCheck2:
mov r1, #0x0
bl GetAttackData
cmp r2, #0x43
bne CheckPP8
cmp r5, #0x1
beq CheckYou8
b TypeChecking8

CheckYou8:
ldr r0, .Attacker8
ldrb r0, [r0]
b THING8

TypeChecking8:
ldr r0, .APartner8
ldrb r0, [r0]

THING8:
mov r1, #0x21
bl UserDataByte
cmp r2, #0xD
beq Loop8
mov r1, #0x22
bl UserDataByte
cmp r2, #0xD
beq Loop8
b CheckPP8

SetR4Zero9:
mov r4, #0x0
b Return8

SetR4One8:
mov r4, #0x1

Return8:
pop {r0-r3, r6, pc}

.align 2
.Attacker8: .word 0x02023D6B
.APartner8: .word 0x02023D60

/*Guts Checks*/
.align 2

Guts2:
push {r0-r3, r6, lr}
mov r1, #0x4C
bl UserDataWord
mov r1, #0xFF
and r2, r1
cmp r2, #0x0
bne SetR4Zero5
ldr r0, .Attacker5
ldrb r0, [r0]
bl GetSide
mov r2, #0x1
and r2, r0
ldr r0, .Status4
lsl r2, r2, #0x1
add r2, r2, r0
ldrh r0, [r2]
mov r1, #0x20
and r1, r0
cmp r0, #0x0
bne SetR4Zero5
mov r6, #0x1

StatusMoveLoop:
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker5
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x0
bl GetAttackData
cmp r2, #0x21
beq PoisonCheck
cmp r2, #0x42
beq PoisonCheck
cmp r2, #0x43
beq ParalyzeCheck
cmp r2, #0xA7
beq BurnCheck
b Loop5

Loop5:
add r6, r6, #0x1
cmp r6, #0x4
bgt SetR4Zero5
b StatusMoveLoop

PoisonCheck:
ldr r0, .APartner5
ldrb r0, [r0]
mov r1, #0x21
bl UserDataByte
cmp r2, #0x3
beq Loop5
cmp r2, #0x8
beq Loop5
mov r1, #0x22
bl UserDataByte
cmp r2, #0x3
beq Loop5
cmp r2, #0x8
beq Loop5
mov r1, #0x2E
bl UserDataHWord
cmp r2, #0x87
beq Loop5
cmp r2, #0x56
beq Loop5
cmp r2, #0x8D
beq Loop5
b CheckPP5

BurnCheck:
ldr r0, .APartner5
ldrb r0, [r0]
mov r1, #0x21
bl UserDataByte
cmp r2, #0xA
beq Loop5
mov r1, #0x22
bl UserDataByte
cmp r2, #0xA
beq Loop5
mov r1, #0x2E
bl UserDataHWord
cmp r2, #0x88
beq Loop5
cmp r2, #0x56
beq Loop5
cmp r2, #0x8D
beq Loop5
b CheckPP5

ParalyzeCheck:
ldr r0, .APartner5
ldrb r0, [r0]
mov r1, #0x21
bl UserDataByte
cmp r2, #0xD
beq Loop5
mov r1, #0x22
bl UserDataByte
cmp r2, #0xD
beq Loop5
mov r1, #0x2E
bl UserDataHWord
cmp r2, #0x85
beq Loop5
cmp r2, #0x8D
beq Loop5
cmp r2, #0x56
beq Loop5
b CheckPP5

CheckPP5:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker5
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero5
b SetR4One5

SetR4Zero5:
mov r4, #0x0
b Return5

SetR4One5:
mov r4, #0x1

Return5:
pop {r0-r3, r6, pc}

.align 2
.Attacker5: .word 0x02023D6B
.APartner5: .word 0x02023D60
.Status4: .word 0x02023DDE

/*Own Tempo Check*/
.align 2

Tempo:
push {r0-r3, r6, lr}
mov r1, #0x50
bl UserDataWord
mov r1, #0x7
And r2, r1
cmp r2, #0x0
bne SetR4Zero6
mov r6, #0x1
b ConfuseMoveLoop

ConfuseMoveLoop:
mov r1, #0xB
Add r1, r6, r6
ldr r0, .Attacker6
ldrb r0, [r0]
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker6
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x0
bl GetAttackData
cmp r2, #0xA6
beq SetSpAttack
cmp r2, #0x76
beq SetAttack
b Loop6

SetSpAttack:
mov r1, #0x1C
b StatBuffCheck
SetAttack:
mov r1, #0x19

StatBuffCheck:
ldr r0, .Attacker6
ldrb r0, [r0]
bl UserDataByte
cmp r0, #0xC
beq SetR4Zero6
b CheckPP6

Loop6:
add r6, r6, #0x1
cmp r6, #0x4
bgt SetR4Zero6
b ConfuseMoveLoop

CheckPP6:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker6
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero6
b SetR4One6

TypeChecking6:
ldr r0, .Attacker6
ldrb r0, [r0]
mov r1, #0x21
bl UserDataByte
cmp r2, #0xA
beq Loop6
mov r1, #0x22
bl UserDataByte
cmp r2, #0xA
beq Loop6
b CheckPP6

SetR4Zero6:
mov r4, #0x0
b Return6

SetR4One6:
mov r4, #0x1

Return6:
pop {r0-r3, r6, pc}

.align 2
.Attacker6: .word 0x02023D6B

/*Item Checks*/
.align 2

ItemCheck:
push {r0-r3, r6, lr}
ldr r0, .Attacker7
ldrb r0, [r0]
bl GetSide
mov r2, #0x1
and r2, r0
ldr r0, .Status42
lsl r2, r2, #0x1
add r2, r2, r0
ldrh r0, [r2]
mov r1, #0xFF
add r1, r1, #0x1
and r1, r0
cmp r0, #0x0
bne SetR4Zero8
ldr r0, .APartner7
ldrb r0, [r0]
mov r1, #0x2E
bl UserDataHWord
mov r1, #0xFF
add r1, r1, #0x16
cmp r2, r1
beq Sapphire
add r1, r1, #0x2
cmp r2, r1
beq Pearl
add r1, r1, #0x6
cmp r2, r1
beq Ruby
add r1, r1, #0x3E
cmp r2, r1
beq Amethyst
add r1, r1, #0x1
cmp r2, r1
beq Garnet
b SetR4Zero8

Sapphire:
mov r1, #0x1C
b FullStats

Ruby:
mov r1, #0x19
b FullStats

Pearl:
mov r1, #0x1B
b FullStats

Amethyst:
mov r1, #0x1A
b FullStats

Garnet:
mov r1, #0x1D
b FullStats

FullStats:
mov r7, r1
bl UserDataByte
cmp r2, #0xC
beq SetR4Zero8
mov r6, #0x1
b StatLowering

SetR4Zero8:
mov r4, #0x0
b Return10

Return10:
pop {r0-r3, r6, pc}

StatLowering:
mov r1, #0xB
Add r1, r6, r6
ldr r0, .Attacker7
ldrb r0, [r0]
mov r1, #0xB
add r1, r1, r6
ldr r0, .Attacker7
ldrb r0, [r0]
bl UserDataHWord
mov r0, r2
mov r1, #0x0
bl GetAttackData
cmp r7, #0x19
beq NoAttack
cmp r7, #0x1A
beq NoDefense
cmp r7, #0x1B
beq NoSpeed
cmp r7, #0x1C
beq NoSpAttack
cmp r7, #0x1D
beq NoSpDefense

NoAttack:
cmp r2, #0x13
beq CheckPP7
cmp r2, #0x14
beq CheckPP7
cmp r2, #0x15
beq CheckPP7
cmp r2, #0x16
beq CheckPP7
cmp r2, #0x17
beq CheckPP7
cmp r2, #0x3B
beq CheckPP7
cmp r2, #0x3C
beq CheckPP7
cmp r2, #0x3D
beq CheckPP7
cmp r2, #0x3E
beq CheckPP7
b Loop7

NoDefense:
cmp r2, #0x12
beq CheckPP7
cmp r2, #0x14
beq CheckPP7
cmp r2, #0x15
beq CheckPP7
cmp r2, #0x16
beq CheckPP7
cmp r2, #0x17
beq CheckPP7
cmp r2, #0x3A
beq CheckPP7
cmp r2, #0x3C
beq CheckPP7
cmp r2, #0x3D
beq CheckPP7
cmp r2, #0x3E
beq CheckPP7
b Loop7

NoSpeed:
cmp r2, #0x12
beq CheckPP7
cmp r2, #0x13
beq CheckPP7
cmp r2, #0x15
beq CheckPP7
cmp r2, #0x16
beq CheckPP7
cmp r2, #0x17
beq CheckPP7
cmp r2, #0x3A
beq CheckPP7
cmp r2, #0x3B
beq CheckPP7
cmp r2, #0x3D
beq CheckPP7
cmp r2, #0x3E
beq CheckPP7
cmp r2, #0xCD
beq CheckPP7
b Loop7

NoSpAttack:
cmp r2, #0x12
beq CheckPP7
cmp r2, #0x13
beq CheckPP7
cmp r2, #0x14
beq CheckPP7
cmp r2, #0x16
beq CheckPP7
cmp r2, #0x17
beq CheckPP7
cmp r2, #0x3A
beq CheckPP7
cmp r2, #0x3B
beq CheckPP7
cmp r2, #0x3C
beq CheckPP7
cmp r2, #0x3E
beq CheckPP7
cmp r2, #0xCD
beq CheckPP7
b Loop7

NoSpDefense:
cmp r2, #0x12
beq CheckPP7
cmp r2, #0x13
beq CheckPP7
cmp r2, #0x14
beq CheckPP7
cmp r2, #0x15
beq CheckPP7
cmp r2, #0x17
beq CheckPP7
cmp r2, #0x3A
beq CheckPP7
cmp r2, #0x3B
beq CheckPP7
cmp r2, #0x3C
beq CheckPP7
cmp r2, #0x3D
beq CheckPP7
cmp r2, #0xCD
beq CheckPP7
b Loop7

Loop7:
add r6, r6, #0x1
cmp r6, #0x4
bgt SetR4Zero7
b StatLowering

CheckPP7:
mov r1, #0x23
add r1, r1, r6
ldr r0, .Attacker7
ldrb r0, [r0]
bl UserDataByte
cmp r2, #0x0
beq SetR4Zero7
b SetR4One7

SetR4Zero7:
mov r4, #0x0
b Return7

SetR4One7:
mov r4, #0x1

Return7:
pop {r0-r3, r6, pc}

.align 2
.Attacker7: .word 0x2023D6B
.APartner7: .word 0x2023D60
.Status42: .word 0x2023DDE

/*BL Functions*/
.align 2

GetHealth:
/*Target is in r0*/
/*Stores to r3*/
push {r1-r2, lr}
mov r1, #0x28
bl UserDataHWord
mov r3, r2
mov r1, #0x2C
bl UserDataHWord
mov r0, r3
mov r1, r2
mov r2, #0x64
mul r0, r2
bl Divide
mov r3, r0
pop {r1-r2, pc}

UserDataByte:
/*Put the target number in r0*/
/*Put the byte for the thing you want in r1*/
/*Returns to r2*/
push {r0-r1, r3-r4, lr}
mov r4, #0x58
ldr r3, .UserData
mul r0, r4
add r3, r3, r0
add r3, r3, r1
ldrb r2, [r3]
pop {r0-r1, r3-r4, pc}

UserDataHWord:
/*Put the target number in r0*/
/*Put the byte for the thing you want in r1*/
/*Returns to r2*/
push {r0-r1, r3-r4, lr}
mov r4, #0x58
ldr r3, .UserData
mul r0, r4
add r3, r3, r0
add r3, r3, r1
ldrh r2, [r3]
pop {r0-r1, r3-r4, pc}

UserDataWord:
/*Put the target number in r0*/
/*Put the byte for the thing you want in r1*/
/*Returns to r2*/
push {r0-r1, r3-r4, lr}
mov r4, #0x58
ldr r3, .UserData
mul r0, r4
add r3, r3, r0
add r3, r3, r1
ldr r2, [r3]
pop {r0-r1, r3-r4, pc}

GetAttackData:
/*Move is in r0*/
/*Data you want is in r1*/
/*Result in r2*/
push {r0-r1, r3-r4, lr}
ldr r3, .AttackData
mov r4, #0xC
mul r0, r4
add r0, r0, r3
add r0, r0, r1
ldrb r2, [r0]
pop {r0-r1, r3-r4, pc}

Divide:
ldr r3, .Divider
bx r3

GetSide:
ldr r3, .GetSide
bx r3

RNG:
ldr r3, .GetRNG
bx r3

.align 2
.UserData: .word 0x02023BE4
.AttackData: .word 0x08250C04
.Attacker: .word 0x02023D6B
.Defender: .word 0x02023D6C
.APartner: .word 0x02023D60
.DPartner: .word 0x02023D61
.GetRNG: .word 0x08044EC9
.Divider: .word 0x081E4019
.GetSide: .word 0x080751D9


I bolded the parts with the RAM for the Attacker and Defender Partners. You can change this to any piece of free RAM you want.

AI Switch Logic
So, there may be a better way to do this, but this is the way that I have come up with an gotten to work. It involves a little work, but once it's done it should work seamlessly.

I will go over this step by step:

1. Find a move to replace or make a new move. Remember its index number in hex.

2. Find a Battle Script you're not using, or branch off of an existing one. How you get the move to use the script is unimportant, as long as that specific move uses this script:
Spoiler:
#dynamic 0x740000
#freespacebyte 0xFF

#org @Switch
jumpifcannotswitch 0x81 @NoSwitch
attackanimation
waitanimation
openpartyscreen 0x1 @NoSwitch
cmde2 0x1
waitstate
cmd51 0x1 0x2
cmd58 0x1
switch1 0x1
switch2 0x1
cmd73 0x1
printstring 0x3
switch3 0x1 0x1
waitstate
cmd52 0x1
goto 0x81D694E

#org @NoSwitch
pause 0x20
orbyte 0x2023DCC 0x20
goto 0x81D694E


3. Insert this routine:
Spoiler:
/*Put 00 48 00 47 XX XX XX 08 at 0x15EE0*/
.text
.align 2
.thumb
.thumb_func

Main:
ldr r1, .MoveIndex
ldr r0, .MoveThing
cmp r2, #0x94
beq Switch
strh r3, [r0]
strh r3, [r1]
b Return

Switch:
strh r2, [r0]
strh r2, [r1]

Return:
ldr r0, .Return
bx r0

.align 2
.MoveIndex: .word 0x02023D4A
.MoveThing: .word 0x02023D4C
.Return: .word 0x08015EE9


4. Then insert this routine:
Spoiler:
/*Put 01 49 08 47 00 00 00 00 XX XX XX 08 at 0x148A4*/
/*6F82D4*/
.text
.align 2
.thumb
.thumb_func

Main:
push {r0}
ldr r0, .BattleType
ldr r0, [r0]
mov r1, #0x8
and r0, r1
cmp r0, #0x0
beq PopR0
pop {r0}
cmp r2, #0x0
beq Player
cmp r2, #0x2
beq Player
b AI

PopR0:
pop {r0}

Player:
mov r1, #0x58
mul r1, r2
add r0, r0, r1
add r3, #0xC
add r0, r0, r3
ldrh r0, [r0]
strh r0, [r5]
ldr r3, .Return
bx r3

AI:
push {r0-r4}
mov r4, #0x0

TrainerBattle:
ldr r0, .BattleType
ldr r0, [r0]
mov r1, #0x1 /*Double*/
and r0, r1
cmp r0, #0x0
bne NoSwitch

/*Check If Switching Is Possible*/

HowManyInParty:
ldr r0, .FirstPoke
mov r1, #0x64
mul r1, r4
add r0, r0, r1
mov r1, #0x41
bl Decrypt
cmp r0, #0x0
beq BattleType
mov r1, #0xCE
lsl r1, r1, #0x1
cmp r0, r1
beq BattleType
ldr r0, .FirstPoke
mov r1, #0x64
mul r1, r4
add r0, r0, r1
mov r1, #0x39
bl Decrypt
cmp r0, #0x0
beq BattleType
b Loop

Loop:
add r4, r4, #0x1
cmp r4, #0x5
ble HowManyInParty
b AbilityCheck

BattleType:
ldr r0, .BattleType
ldr r0, [r0]
mov r1, #0x1 /*Double*/
and r0, r1
cmp r0, #0x0
beq Single

Double:
cmp r4, #0x2
ble NoSwitch
b AbilityCheck

Single:
cmp r4, #0x1
ble NoSwitch

AbilityCheck:
mov r0, #0x0
mov r1, #0x20
bl GetUserData
cmp r2, #0x17
beq NoSwitch
cmp r2, #0x47
beq ATCheck
cmp r2, #0x2A
beq MPCheck
mov r0, #0x2
mov r1, #0x20
bl GetUserData
cmp r2, #0x17
beq NoSwitch
cmp r2, #0x47
beq ATCheck
cmp r2, #0x2A
beq MPCheck
b Status

ATCheck:
ldr r0, .Attacker
ldrb r0, [r0]
mov r1, #0x21
bl GetUserData
cmp r2, #0x2
beq Status
mov r1, #0x22
bl GetUserData
cmp r2, #0x2
beq Status
mov r1, #0x20
bl GetUserData
cmp r2, #0x1A
beq Status
b NoSwitch

MPCheck:
ldr r0, .Attacker
ldrb r0, [r0]
mov r1, #0x21
bl GetUserData
cmp r2, #0x8
beq NoSwitch
mov r1, #0x22
bl GetUserData
cmp r2, #0x8
beq NoSwitch
b Status

Status:
ldr r0, .Attacker
ldrb r0, [r0]
bl GetStatus2
ldr r1, .Compare
and r2, r1
cmp r2, #0x0
bne NoSwitch
ldr r2, .Status3
lsl r0, r0, #0x2
add r0, r0, r2
ldr r0, [r0]
mov r0, #0x80
lsl r0, r0, #0x3
and r2, r0
cmp r2, #0x0
bne NoSwitch
mov r1, #0x0

MoveViability:
ldr r0, .MoveScore
ldrb r2, [r0]
cmp r2, #0xFF
beq SetMove

Loop2:
add r1, r1, #0x1
cmp r1, #0x3
bge NoGoodMoves
mov r1, #0x0
mov r3, #0x0
b MoveViability

NoGoodMoves:
ldr r0, .MoveScore
ldrb r2, [r0]
cmp r2, #0x4E
ble SetScore
b Loop3

SetScore:
add r3, r3, #0x1

Loop3:
add r1, r1, #0x1
cmp r1, #0x3
bge CmpScore
b NoGoodMoves

CmpScore:
cmp r3, #0x4
bge SetMove
b NoSwitch

SetMove:
mov r0, #0x94
strh r0, [r5]
pop {r0-r4}
ldr r3, .Return
bx r3

NoSwitch:
pop {r0-r4}
b Player

/*BL Functions*/

GetUserData:
push {r0, r3-r4, lr}
mov r4, #0x58
ldr r3, .UserData
mul r0, r4
add r3, r3, r0
add r3, r3, r1
ldrb r2, [r3]
pop {r0, r3-r4, pc}

GetStatus2:
push {r0, r3-r4, lr}
mov r4, #0x58
ldr r3, .UserData
mul r0, r4
add r3, r3, r0
add r3, r3, #0x50
ldr r2, [r3]
pop {r0, r3-r4, pc}

Decrypt:
ldr r3, .Decrypt
bx r3

.align 2
.Return: .word 0x080148B3
.MoveScore: .word 0x020003A8
.FirstPoke: .word 0x0202402C
.Decrypt: .word 0x0803FBE9
.BattleType: .word 0x02022B4C
.Compare: .word 0x0400E000
.Attacker: .word 0x02023D6B
.Status3: .word 0x02023DFC
.UserData: .word 0x02023BE4


Essentially what you're doing here is having a move that simply makes the AI switch out. It doesn't announce "Pokemon used Move!" or anything, so it doesn't look like an attack when it secretly is. The advantages of using this method were, of course, that I didn't have to learn how the preexisting routine works because I'm doing something completely different.

More importantly than me being lazy, this method lets you decide when the AI should switch within an AI script. Here's how to set that up.

1. Insert this modified version of the AddToViabilityScore routine:
Spoiler:
.text
.align 2
.thumb
.thumb_func

Main:
push {r4, lr}
ldr r2, .Resources
ldr r0, [r2]
add r0, r0, #0x14
ldr r0, [r0]
add r1, r0, #0x4 /*This is 020003A5 which is the move number RAM*/
ldrb r0, [r0, #0x1] /*This is the move number*/
add r1, r1, r0

GetValue:
ldr r3, .AICursor
ldr r0, [r3]
ldrb r0, [r0, #0x1]
cmp r0, #0x5E /*Change this to set the Switch Score*/
beq Set255

AddToScore:
ldrb r4, [r1]
cmp r4, #0xFF
beq Return
add r0, r0, r4
strb r0, [r1]

AddToScoreNegativeCheck:
ldr r0, [r2]
add r0, r0, #0x5
ldr r1, [r0]
add r0, r1, #0x4
ldrb r1, [r1, #0x1]
add r1, r0, r1
mov r0, #0x0
ldsb r0, [r1, r0]
cmp r0, #0x0
bge Return
mov r0, #0x0
strb r0, [r1]

Set255:
mov r0, #0xFF
strb r0, [r1]

Return:
ldr r0, [r3]
add r0, r0, #0x2
str r0, [r3]
pop {r4}
pop {r0}
bx r0

.align 2
.Resources: .word 0x02023FF4
.AICursor: .word 0x02039A00


2. When you want to AI to automatically switch, just write "AddToViabilityScore 0x5E" and no matter what, it will switch. You can change 0x5E to something else in the routine if you want.

The above routine will also switch if all of the Pokemon's moves are below a certain viability score (so it will switch if all of its moves have no effect, for instance). I bolded the line to change.

It will not switch if it is unable to. For example, if the opponent has Shadow Tag, then the AI won't use the switch move even if you set the Switch Score. It will just use one of its actual moves instead.

Here are a few more things you will probably want to do:
1. Edit the Metronome Ban List: The Metronome Ban List is located at 0x2507E8. You'll probably have to replace a move that's already there, but to be honest, that's not too big of a deal. Does Protect really need to be banned from Metronome? Anyway, I digress. You won't want Metronome to call the switch move, so you should add it to the Ban List somehow.

2. Sketch: You also don't want Sketch to be able to learn the move, so insert this routine so that Sketch will fail if the Switch Move was the one last used. Not sure if it would fail anyway because the Pokemon switched out, but better safe than sorry.
Spoiler:
.text
.align 2
.thumb
.thumb_func

Main:
cmp r1, #0xA5

Compare:
pop {r1}
cmp r1, #0xA5
beq Skip
cmp r1, #0x0
beq Skip
cmp r1, r5
beq Skip
cmp r1, #0xA6
beq Skip
cmp r1, #0x94
beq Skip
b Branch

Skip:
ldr r3, .Return
bx r3

Branch:
ldr r0, .Return2
bx r0

.align 2
.Return: .word 0x0802A191
.Return2: .word 0x0802A065


You'll probably also want to do a similar thing to the above with Mimic.

xizqu March 17th, 2018 10:41 AM

Hey akame, I'm somewhat new to hacking to I'm trying to understand everything going on here.

I was able to get the inverse battle checker to work but that's a callasm outside of the battle.

How do I get spikes layer or really anything else to work? How does the ai check before using a move? Also, what are macros used for and what are the arguments you speak of? If this is too much to explain, do you have a thread that I can learn about all this? I'm trying to make a difficult fire red hack so I'd like to utilize your research and work. Thank you!

AkameTheBulbasaur May 14th, 2018 1:15 PM

Quote:

Originally Posted by xizqu (Post 9855184)
Hey akame, I'm somewhat new to hacking to I'm trying to understand everything going on here.
!

Whoops! Sorry I totally didn't see this until now!

Basically each of the opponent's moves has a score of 100 by default. The purpose of the AI scripts is to add or subtract from that score based on certain conditions. You use the commands in the scripts (kind of like you would in normal NPC scripting).

So for example, if you wanted to make a script that would prevent the AI from using Hypnosis when the foe already has a status, it would look like this.

Main:
JumpIfMoveScriptEquals 0x1 CauseSleep
ReturnToBattle

CauseSleep:
JumpIfStatus1Equals Defender 0x000000FF Decrease32
ReturnToBattle

Decrease32:
AddToViabilityScore 0xE0
ReturnToBattle


Each script is run for each move (so each script is run a total of four times maximum). Once all the scripts are run, the AI picks the move that has the highest score (I presume that if one or more have the same score, it picks one randomly between them).

Which scripts are run is determined by a value in the Trainer Data. It's a bitfield, so if you want script 1, the value is 0x1 (1 in binary). If you want scripts 1-3, the value is 0x7 (111 in binary).

Hopefully this is helpful. If you have other questions feel free to PM me.

jirachiwishmaker May 23rd, 2018 12:44 AM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9824923)
ATTENSHOON!!!!

Here I am including two things that are not scripting related but are important for the AI. The first is a Double Battle Logic routine (actually two of them), and the second is a method for getting the AI to switch out in a controlled manner.

Double Battle Target Logic + RAM Setting
First of all, any of the above routines which use the RAM addresses 02023D6D or 02023D6E need to be changed!!!

I was under the impression that those were the Attacker Partner and Defender Partner banks respectively

THEY ARE NOT!

Instead, you need to use a free RAM address to store those banks.

Do you mind to tell me that are the Attacker Partner and Defender Partner same with the APartner and DPartner? I am very confusing about it.

AkameTheBulbasaur May 23rd, 2018 1:27 PM

Quote:

Originally Posted by jirachiwishmaker (Post 9879956)
Do you mind to tell me that are the Attacker Partner and Defender Partner same with the APartner and DPartner? I am very confusing about it.

If you mean whether or not APartner and Attacker Partner mean the same thing, then yes they do. APartner is just easier to write.

Lance Koijer 2.0 May 23rd, 2018 3:12 PM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9880190)
If you mean whether or not APartner and Attacker Partner mean the same thing, then yes they do. APartner is just easier to write.

Hello Akame! I found this research very interesting and got most of it. However, I am confused on the AI Switching. Does this include player being able to force switch? Same concept is applied. I'm sorry if this has been mentioned on your post already, I might have missed it.

EDIT: Another question, let's say I'm going to create an AI Script based on the commands listed above, in what particular part of the ROM I'm going to insert it? Does it have a table of scripts that will be checked by a routine somewhere in ROM then generates all possible script or it needs a hook?

jirachiwishmaker May 24th, 2018 2:48 AM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9880190)
If you mean whether or not APartner and Attacker Partner mean the same thing, then yes they do. APartner is just easier to write.

Oh, I see. Are all the routines that use 0x02023D6D and 0x02023D6E or 0x02023D60 and 0x02023D61 as the APartner and DPartner RAM respectively must replace with the free RAM addresses?

AkameTheBulbasaur May 24th, 2018 5:02 PM

Quote:

Originally Posted by jirachiwishmaker (Post 9880409)
Oh, I see. Are all the routines that use 0x02023D6D and 0x02023D6E or 0x02023D60 and 0x02023D61 as the APartner and DPartner RAM respectively must replace with the free RAM addresses?

Correct. I used 0x02023D60 and 0x02023D61 as my free RAM, but you can pick anything as long as it's free. If I remember correctly, Mr. DollSteak used those two RAM addresses I mentioned previously in something, so if you're using that then you will need to pick new ones. If you're not using anything else that uses those, then those two are fine.

AkameTheBulbasaur May 24th, 2018 5:22 PM

Quote:

Originally Posted by Lance Koijer 2.0 (Post 9880222)
Hello Akame! I found this research very interesting and got most of it. However, I am confused on the AI Switching. Does this include player being able to force switch? Same concept is applied. I'm sorry if this has been mentioned on your post already, I might have missed it.

EDIT: Another question, let's say I'm going to create an AI Script based on the commands listed above, in what particular part of the ROM I'm going to insert it? Does it have a table of scripts that will be checked by a routine somewhere in ROM then generates all possible script or it needs a hook?

I'm assuming what you mean by force switch is setting up a situation where the AI has to switch and will always switch. There are two ways to do that. The easiest way is to use the modified AddToViabilityScore command and write "AddToViabilityScore 0x5E." This will guarantee that the AI will switch (provided it actually can switch that is). This is the way you actually have control over in AI scripts.

The other way that the AI will switch with this is if all of their moves are ineffective. You can kind of influence this through AI scripts but it's easier to just use "AddToViabilityScore 0x5E" if you want a guaranteed switch.

For example, if you want the AI to always switch out if they are badly poisoned:

PoisonCheck:
JumpIfStatus1Equals Attacker0x00000080 Switch1
ReturnToBattle

Switch1:
AddToViabilityScore 0x5E
ReturnToBattle

As for your second question, there is a table at 0x1D9BF4 that has a series of pointers to all the AI scripts. The way you get trainers to use these scripts is determined by a value in the trainer data. I forget where it is in other editors, but in HopelessTrainerEditor, it's right underneath the name (if I remember correctly).

The way it works is that the game reads that number in binary and uses the script numbers indicated by the positions of the ones in that number. So for example, if you put 7 for that value (which for a while was assumed to be the maximum value), that trainer would use scripts 1, 2 and 3 (because 7 in binary is 111).

Personally, I have had the most luck using 7 scripts and no more (so a trainer value of 127), but theoretically you could have up to 16(? I think this is the actual maximum since if I remember right the AI value is a halfword). The table I gave you has 32 entries though, so I don't know.

jirachiwishmaker May 24th, 2018 5:31 PM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9880685)
Correct. I used 0x02023D60 and 0x02023D61 as my free RAM, but you can pick anything as long as it's free. If I remember correctly, Mr. DollSteak used those two RAM addresses I mentioned previously in something, so if you're using that then you will need to pick new ones. If you're not using anything else that uses those, then those two are fine.

Okay. Thanks for answering. In the JumpIfDamageBonusEquals AI commands, why Fairy doesn't use the own Fairy move instead of the Metronome?

AkameTheBulbasaur May 24th, 2018 5:38 PM

Quote:

Originally Posted by jirachiwishmaker (Post 9880695)
Okay. Thanks for answering. In the JumpIfDamageBonusEquals AI commands, why Fairy doesn't use the own Fairy move instead of the Metronome?

Because in my game (where I originally developed the routine) Metronome was a Fairy Type move. If it's not for you you'll want to change that to a move that is Fairy Type for you.

jirachiwishmaker May 24th, 2018 5:49 PM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9880700)
Because in my game (where I originally developed the routine) Metronome was a Fairy Type move. If it's not for you you'll want to change that to a move that is Fairy Type for you.

Okay. I get it. Thanks a million.

Lance Koijer 2.0 May 25th, 2018 3:55 AM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9880700)
Because in my game (where I originally developed the routine) Metronome was a Fairy Type move. If it's not for you you'll want to change that to a move that is Fairy Type for you.


Wow thanks for the response...

anyway.. What I was asking is if it's possible to use AI Script on player not just on aoppomemt trainer...

Another question, would it be possible to make it in wild battles? Not the wildbattle command in XSE but the actual wild battle where you will emcounter Pokémon in grass and water areas and etcetera.

Thanks man.

AkameTheBulbasaur May 25th, 2018 5:57 PM

Quote:

Originally Posted by Lance Koijer 2.0 (Post 9880832)
Wow thanks for the response...

anyway.. What I was asking is if it's possible to use AI Script on player not just on aoppomemt trainer...

Another question, would it be possible to make it in wild battles? Not the wildbattle command in XSE but the actual wild battle where you will emcounter Pokémon in grass and water areas and etcetera.

Thanks man.

The AI Scripts are only activated for the AI, not any manual input (like the player). It is possible for wild battles, but only in certain circumstances. The last two scripts in the table are used for wild Pokemon battles. One is the script that makes the Pokemon run away in Safari battles, and the other I believe is the one that makes Entei/Raikou/Suicune run away. I don't know how they are activated though, so I'm not sure how to make them work for normal wild battles.

Lance Koijer 2.0 May 25th, 2018 6:21 PM

Quote:

Originally Posted by AkameTheBulbasaur (Post 9880920)
The AI Scripts are only activated for the AI, not any manual input (like the player). It is possible for wild battles, but only in certain circumstances. The last two scripts in the table are used for wild Pokemon battles. One is the script that makes the Pokemon run away in Safari battles, and the other I believe is the one that makes Entei/Raikou/Suicune run away. I don't know how they are activated though, so I'm not sure how to make them work for normal wild battles.

Thanks man. That really is a good finding. Kudos


All times are GMT -8. The time now is 8:41 AM.


Like our Facebook Page Follow us on Twitter © 2002 - 2018 The PokéCommunity™, pokecommunity.com.
Pokémon characters and images belong to The Pokémon Company International and Nintendo. This website is in no way affiliated with or endorsed by Nintendo, Creatures, GAMEFREAK, The Pokémon Company or The Pokémon Company International. We just love Pokémon.
All forum styles, their images (unless noted otherwise) and site designs are © 2002 - 2016 The PokéCommunity / PokéCommunity.com.
PokéCommunity™ is a trademark of The PokéCommunity. All rights reserved. Sponsor advertisements do not imply our endorsement of that product or service. User generated content remains the property of its creator.

Acknowledgements
Use of PokéCommunity Assets
vB Optimise by DragonByte Technologies Ltd © 2023.