The PokéCommunity Forums Fan Games ROM Hacking Research & Development
Research Trainer AI Command Research

Research & Development Got a well-founded knack with ROM hacking? Love reverse-engineering the Pokémon games? Or perhaps you love your assembly language. This is the spot for polling and gathering your ideas, and then implementing them! Share your hypothesis, get ideas from others, and collaborate to create!


Reply
 
Thread Tools
  #1    
Old November 25th, 2017 (1:41 PM). Edited 4 Weeks Ago by AkameTheBulbasaur.
AkameTheBulbasaur's Avatar
AkameTheBulbasaur AkameTheBulbasaur is offline
Akame Marukawa of Iyotono
     
    Join Date: May 2013
    Location: A place :D
    Age: 20
    Gender: Male
    Nature: Gentle
    Posts: 334
    Acknowledgments
    First and foremost I have to thank Knizz for his previous research on the Trainer AI Scripting system! Because of his 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 he relied heavily on that as well.
    22
    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.

    Spoiler:

    00: RandomJumpIfLessThan
    Arguments: 1 byte, 1 word
    Generates a random number between 0 and 255. If less than the given number, the script will jump to the address given.

    01: RandomJumpIfMoreThan
    Arguments: 1 byte, 1 word
    Same as above except if more than the given number, the script will jump to the address given.

    02: RandomJumpOneIn256
    Arguments: 1 word
    Generates a random number between 0 and 255. Has a one in 256 (about .4%) chance of jumping to the address given.

    03: RandomJump255In256
    Arguments: 1 word
    Same as above but has a 255 in 256 (about 99.6%) chance of jumping to the address given.

    04: AddToViabilityScore
    Arguments: 1 byte
    Adds the given amount to the viability score of the move currently being considered. 01, 02, 03… add 1, 2, 3… FF, FE, FD… subtract 1, 2, 3… It is currently unknown where one switches to the other but it can be presumed to be at 7F, halfway between 1 and FF.

    05: JumpIfHealthLessThan
    Arguments: 2 bytes, 1 word
    Looks at a given target (options: Attacker or Defender. All commands with targets will henceforth be presumed to only accept these two unless otherwise stated). Compares the percentage of their health remaining to a given number. Jumps to the address given if health is strictly less than the percentage.

    06: JumpIfHealthMoreThan
    Arguments: 2 bytes, 1 word
    Same as the command above except jumping if the value obtained is strictly more than the value given.

    07: JumpIfHealthEquals
    Arguments: 2 bytes, 1 word
    Same as the command above except jumping if the value obtained is strictly equal to the value given.

    08: JumpIfHealthNotEqual
    Arguments: 2 bytes, 1 word
    Same as the command above except jumping if the value obtained is strictly not equal to the value given.

    09: JumpIfStatus1Equals
    Arguments: 1 byte, 2 words
    Looks at a given target and compares the bits in the RAM for status 1 to a certain amount, and if equal it will jump to the given address.

    It will jump if a bit matches. This means if you want to check for more than one condition at once, add the values of the conditions you want together. (For example, if you want to check for either sleep or poison, enter 00 00 00 0F).

    This uses or logic, so if you enter in more than one condition at a time, the target only needs to have one for the command to return true.

    Values are stored in the game backward, so if you want AA BB CC DD, the word needed is DD CC BB AA.

    List of Status 1 Values:
    00 00 00 07 —> Sleep (up to 7 turns)
    00 00 00 08 —> Poison
    00 00 00 10 —> Burn
    00 00 00 20 —> Freeze
    00 00 00 40 —> Paralyze
    00 00 00 80 —> Badly poisoned

    0A: JumpIfStatus1NotEqual
    Arguments: 1 byte, 2 words
    Same as above except it checks if the target does not have any of the statuses. The same principle for checking for more than one condition applies here too. I believe the same or logic applies here too, so if you check for more than one condition, the target only needs to not have one of them to jump.

    0B: JumpIfStatus2Equals
    Arguments: 1 byte, 2 words
    Similar to Status 1, except with different conditions. Status 2 includes volatile conditions such as confusion or substitutes.

    List of Status 2 Values:
    00 00 00 07 —> Confusion
    00 00 00 08 —> Flinch
    00 00 00 70 —> Uproar
    00 00 0F 00 —> Bide
    00 00 10 00 —> Multi-turn attack
    00 00 E0 00 —> Wrap
    00 0F 00 00 —> Infatuation
    00 10 00 00 —> CHR raised
    00 20 00 00 —> Transformed
    00 40 00 00 —> Recharge
    00 80 00 00 —> Rage
    01 00 00 00 —> Substitute
    02 00 00 00 —> Destiny Bond
    04 00 00 00 —> prevent escape
    08 00 00 00 —> Nightmare
    10 00 00 00 —> Cursed
    20 00 00 00 —> Foresight
    40 00 00 00 —> Defense Curl
    80 00 00 00 —> Tormented

    0C: JumpIfStatus2NotEqual
    Arguments: 1 byte, 2 words
    Same as the previous command, except in the negative.

    0D: JumpIfStatus3Equals
    Arguments: 1 byte, 2 words
    Same as Status 1 and Status 2. Status 3 includes conditions such as Leech Seed, Ingrain, or the semi-invulnerable turns of moves such as Fly.

    List of Status 3 Values:
    00 00 00 04 —> Leech Seed
    00 00 00 10 —> Lock On
    00 00 00 20 —> Perish Song
    00 00 00 40 —> In the air
    00 00 00 80 —> Underground
    00 00 01 00 —> Minimized
    00 00 02 00 —> Charge
    00 00 04 00 —> Ingrain
    00 00 08 00 —> Yawn (start sleep)
    00 00 10 00 —> Yawn (sleep next turn)
    00 00 20 00 —> Imprison
    00 00 40 00 —> Grudge
    00 01 00 00 —> Mud Sport
    00 02 00 00 —> Water Sport
    00 04 00 00 —> Underwater

    0E: JumpIfStatus3NotEqual
    Arguments: 1 byte, 2 words
    Same as the above command but in the negative.

    0F: JumpIfStatus4Equals
    Arguments: 1 byte, 2 words
    Same as the previous Status commands, but with Status 4. Status 4 includes conditions such as having Light Screen or Reflect set up.

    List of Status 4 Values:
    00 00 00 01 —> Reflect
    00 00 00 02 —> Light Screen
    00 00 00 10 —> Spikes
    00 00 00 20 —> Safeguard
    00 00 01 00 —> Mist

    10: JumpIfStatus4NotEqual
    Arguments: 1 byte, 2 words
    Finally, the last of the Status commands. This one is the same as the previous one except in the negative.

    11: JumpIfByteLessThan
    Arguments: 1 byte, 1 word
    Compares the byte in the free variable to a given value, and jumps to the given address if the free variable is less than it.

    12: JumpIfByteMoreThan
    Arguments: 1 byte, 1 word
    Same as the previous command except jumping if the free variable is more than the given byte.

    13: JumpIfByteEquals
    Arguments: 1 byte, 1 word
    Same as the previous command, except jumping if the free variable and the given byte are equal.

    14: JumpIfByteNotEqual
    Arguments: 1 byte, 1 word
    Same as the previous command, except jumping if the free variable and the given byte are not equal.

    15: JumpIfWordLessThan
    Argument: 2 words
    Compares the given word to the word in the free variable. If the free variable is less than the given word, it jumps to the given address.

    16: JumpIfWordMoreThan
    Argument: 2 words
    Same as the previous command except jumping if the free variable is more than the given word.

    17: JumpIfWordEquals
    Arguments: 2 words
    Same as the previous command except jumping if the free variable and the given word are equal.

    18: JumpIfWordNotEqual
    Arguments: 2 words
    Same as the previous command except jumping if the free variable and the given word are not equal.

    19: JumpIfMoveIDEquals
    Arguments: 1 halfword, 1 word
    If the move being considered while the script is running is equal to the halfword given, then it will jump to the given address.

    1A: JumpIfMoveIDNotEqual
    Arguments: 1 halfword, 1 word
    This is the negative of the previous command. As you’ve surely noticed by now, lots of these commands are the negative versions of previous ones.

    1B: JumpIfByteInList
    Arguments: 2 words
    Compares the free variable to every entry in the list at the given address. If the byte can be found in there, then the script will jump to the given address. Useful for doing things such as comparing a target’s ability to a list (after using the GetAbility command discussed later).

    1C: JumpIfByteNotInList
    Arguments: 2 words
    The negative version of the previous command.

    1D: JumpIfHalfwordInList
    Arguments: 2 words
    Compare the free variable to a list of halfwords. Jumps to the given address if any of those halfwords match the free variable. Useful for comparing to a list of moves.

    1E: JumpIfHalfwordNotInList
    Arguments: 2 words
    Negative of the previous command. Moving right along.

    1F: JumpIfDamagingMoveInMoveset
    Arguments: 1 word / 1 byte, 1 word
    Checks the target’s move set and jumps if there is a move that does damage. In default FireRed, this command only checks the attacker’s moves. I rewrote this one because I like to make the AI a cheater who knows which moves you have. Some call it sadistic, but I like to call it making the game more fun. Anyway, all I did in the rewrite is add the ability to choose which target’s move set you can check.

    20: JumpIfNoDamagingMoveInMoveset
    Arguments: 1 word / 1 byte, 1 word
    Same as the last command but in the negative.

    21: GetBattleTurnCounter
    Arguments: none
    Gets the current turn of battle and stores it into the free variable. This is a total counter, so it counts the number of turns since the battle started, not since a Pokemon entered the field (that is a different command that we’ll find later).

    22: GetType
    Arguments: 1 byte
    Gets the type of the Pokemon or move and stores it into the free variable. The arguments accepted as follows:

    0 = Defender’s Primary Type
    1 = Attacker’s Primary Type
    2 = Defender’s Secondary Type
    3 = Attacker’s Secondary Type
    4 = Type of the considered move

    23: GetPowerofConsideredMove
    Arguments: none
    Gets the power of the move considered and stores the result into the free variable.

    24: GetPowerOfStrongestMove
    Arguments: none
    Gets the power of the strongest move in the attacker’s moves.

    25: GetMoveLastUsed
    Arguments: Target
    Gets the move that was used most recently by the given target. Useful for scripting Mirror Move scenarios. This is most commonly used in conjunction with the GetKindOfMove and GetBasePower commands.

    26: JumpIfFreeVarEquals
    Arguments: 1 byte, 1 word
    Jumps to the given address if the free variable equals the given value. The only difference I can find between this and JumpIfByteEquals is that in JumpIfByteEquals the free variable is loaded before the AI Cursor (the AI Cursor gets the byte to compare). This appears to only be used with GetType.

    27: JumpIfFreeVarNotEqual
    Arguments: 1 byte, 1 word
    Same as the last command except in the negative.

    28: JumpIfMoveWouldHitFirst
    Arguments: 1 byte, 1 word
    If the target would outspeed the opponent, then the script will jump to the given address.

    29: JumpIfMoveWouldHitSecond
    Arguments: 1
    byte, 1 word

    If the target is slower than the opponent, then the script will jump to the given address.

    2C: CountViablePokemonOnTeam
    Arguments: 1 byte
    Checks how many Pokemon that haven't fainted are left on the target’s team and puts that number into the free variable.

    2D: GetMoveID
    Arguments: none
    Gets the index number of the move being considered and stores it into the free variable. Useful for use with the JumpIfByteInList command.

    2E: GetMoveScriptID
    Arguments: none
    Gets the move script number of the move being considered and stores it into the free variable.

    2F: GetAbility
    Arguments: 1 byte
    Gets the ability of the target and stores it into the free variable.

    30: SimulateDamageBonusFourTimes
    Arguments: none
    I honestly have no idea what this command is meant to do. It involves damage calculations. This name comes from Knizz’s research way back when, but I don’t think he gave a full description of this command (he just had the list of names). I think it’s supposed to set all damage bonuses to zero but I really don’t know.

    31: JumpIfDamageBonusEquals
    Arguments: 1 byte, 1 word
    Gets the effectiveness of the move considered against the opponent and stores a value into the free variable. Jumps to the given address if the free variable matches the value given.

    0 = Immune
    A = 1/4 effectiveness (A = 14 / 2 = 28 / 4, 10 = 20 / 2 = 40 / 4)
    14 = 1/2 effectiveness (14 = 28 / 2, 20 = 40 / 2)
    28 = Neutral (40)
    50 = 2x effectiveness (50 = 28 * 2, 80 = 40 * 2)
    A0 = 4x effectiveness (A0 = 50 * 2 = 28 * 4, 160 = 80 * 2 = 40 * 4)

    34: JumpIfAnyPokemonHasStatus
    Arguments: 1 byte, 2 words
    Jumps to a given address if any party member on the target side has a given status. This checks for anything in the Status 1 category (talked about in the JumpIfStatus1Equals/NotEqual commands) since that is the only kind of status which persists after switching out.

    35: JumpIfNoPokemonHasStatus
    Arguments: 1 byte, 2 words
    Exactly the same as the last command, but jumping if nobody in the party has the given status. This was apparently bugged in default FireRed, so I rewrote it (and also rewrote the previous command while I was at it).

    36: GetWeather
    Arguments: none
    I rewrote this command because I thought it was written dumbly. It gave no way to check specifically for clear weather. In either case, this command returns a value into the free variable based on the current weather.

    Values for default FireRed
    0 = Sun
    1 = Rain
    2 = Sandstorm
    3 = Hail

    Values for Rewrite
    0 = Clear
    1 = Rain
    2 = Sandstorm
    3 = Sun
    4 = Hail

    37: JumpIfMoveScriptEquals
    Arguments: 1 byte, 1 word
    Jumps to the address given if the move script of the move being considered matches the given value.

    38: JumpIfMoveScriptNotEqual
    Arguments: 1 byte, 1 word
    Negative of the previous command. Not much else to say here. Moving along.

    39: JumpIfStatBuffLessThan
    Arguments: 3 bytes, 1 word
    This takes four arguments. The first is the target wanted, the second is the stat to compare, the third is the value to compare it to, and the final argument is the address to jump to if the value of the stat is strictly less than the value given.

    Values for stats
    1 = Attack
    2 = Defense
    3 = Speed
    4 = Special Attack
    5 = Special Defense
    6 = Accuracy
    7 = Evasion

    Values for buffs
    0 = -6
    1 = -5
    2 = -4
    3 = -3
    4 = -2
    5 = -1
    6 = No buffs
    7 = +1
    8 = +2
    9 = +3
    A = +4
    B = +5
    C = +6

    3A: JumpIfStatBuffMoreThan
    Arguments: 3 bytes, 1 word
    Same as the last command except jumping if the stat is strictly more than the value.

    3B: JumpIfStatBuffEqual
    Arguments: 3 bytes, 1 word
    Same as the last command, except jumping if the stat is equal to the given value.

    3C: JumpIfStatBuffNotEqual
    Arguments: 3 bytes, 1 word
    Same as the last command except jumping if the stat is not equal to the given value.

    3D: JumpIfMoveKnocksOut
    Argument: 1 word
    I believe this calculates the damage an attack will do damage equal to the opponent’s HP before adding damage modification or critical-hit multipliers, and jumps if the attack will knock out the opponent.

    3E: JumpIfMoveDoesntKnockOut
    Arguments: 1 word
    The same as the previous command except in the negative.

    3F: JumpIfMoveInMoveSet
    Arguments: 1, byte, 1 halfword, 1 word
    The original I was not very fond of so I rewrote it. In the original, 1 checked the moves of the Attacker and 0 did something else involving both the attacker and defender.

    In the new version, the target values are as follows:

    0 = Just the Defender
    1 = Just the Attacker
    2 = Whole Attacking side (if in a Double Battle)
    3 = Whole Defending side

    In both versions, the next halfword is the ID of the move to check for. As with most other things in the game, if your halfword is AABB, the game stores it as BBAA.

    If the move requested is in the target’s move set, then it will jump to the provided address.

    40: JumpIfMoveNotInMoveSet
    Arguments: 1 byte, 1 halfword, 1 word
    The inverse of the command above. I have rewritten this one too using the same target values as the previous one.

    41: JumpIfMoveScriptInMoveSet
    Argument: 1 byte, 1 halfword, 1 word / 2 bytes, 1 word
    I rewrote this one too. In the original, the command takes a halfword, which made little sense to me since move scripts only go up to FF (1 byte). So I just cut out that extra byte. Otherwise, the rules are the same as in Command 3F with the same target values.

    42: JumpIfMoveScriptNotInMoveSet
    Arguments: 1 byte, 1 halfword, 1 word / 2 bytes, 1 word
    The inverse of the previous command. This was also rewritten with the same syntax as before. Nothing much else to say.

    43: JumpIfMoveSetRestricted
    Arguments: 2 bytes, 1 word
    Checks if the target is either Disabled (0) or Encored (1), and jumps to the given address if they are.

    44: JumpIfEncoreIs
    Arguments: 1 byte, 1 word
    Checks if the active side is in Encore or not. To check if they are, use 0 as the argument. To check if they are not, use 1 instead. If the argument you picked matches the state of the active side, then the script will jump to the address given.

    45: RunAway
    Arguments: none
    This makes the Pokemon flee on the next turn. This is used with the roaming Pokemon scripts. There’s not terribly much use for it in Trainer Battles but it could be useful for a battle where the opponent leaves halfway through or when certain conditions are met (such as fleeing when health is under half).

    46: JumpRandomUnknown
    Arguments: 1 word
    This generates a random number and then jumps if some condition is met. Further research needed.

    47: SafariZone
    Arguments: none
    This triggers the Safari Zone text of “NAME is watching carefully.” That’s it.

    48: GetHeldItemEffect
    Arguments: 1 byte
    Gets the held item effect byte of the given target and stores it into the free variable. Returns 0 if there is no item.

    49: GetGender
    Arguments: 1 byte
    Gets the gender of the target and stores it into the free variable. Returns 0 for male, FE for female and FF for genderless.

    4A: CheckIfFirstTurn
    Arguments: 1 byte
    Checks if it is the first turn that the target has been on the field. Returns 1 if yes, 0 if no. This is different from the total turns of battle, this is the individual number of turns. So when a Pokemon switches out, this is reset.

    4B: GetStockpileCounter
    Arguments: 1 byte
    Gets the Stockpile counter for the given target and returns it into the free variable.

    4C: CheckIfDoubleBattle
    Arguments: none
    Checks the battle type and sees if it is a double battle. If yes, then it returns 1, if not then it returns 0.

    4D: GetDP08Item
    Arguments: 1 byte
    Gets what is at the DP08 Pointer for the given target.

    4E: GetKindOfMove
    Arguments: none
    I rewrote this command for the DPSS patch. Before this was seemingly identical to the GetType command. Now this checks the padding byte used by the DPSS patch to determine whether a move is physical, special or status.

    Values used
    0 = Physical
    1 = Special
    2 = Status

    To apply the “rewrite” (it’s not really a rewrite, it’s byte change) change the bytes at 0xC945A from 80 78 to 40 7A.

    4F: GetBasePower
    Arguments: none
    Gets the base power of the considered move and puts it into the free variable.

    50: GetMoveRange
    Arguments: none
    In default FireRed, this command got the move script ID of the move being considered. So it did the exact same thing as the GetMoveScriptID command, thus rendering it utterly pointless.

    I changed one little thing so that it was more useful. Now it gets the range of the move being considered. The range is how many Pokemon the move affects.

    (Put this in the game by changing the bytes at 0xC94B2 to C0 79)

    Most common values
    0 = Single selected target
    1 = Depends on the attack (i.e.moves like Counter target the last foe to hit them)
    4 = Random target
    8 = Both foes
    10 = User
    20 = Everyone but the user
    40 = Opponent’s field

    51: GetProtectActivity
    Arguments: 1 byte
    Checks if the target has Endure or Protect (or any variation of those moves) active. Returns 1 if true, 0 if false.

    58: Call
    Arguments: 1 word
    Calls another script at the address given and will return from where the previous script left off when finished. Returns with the ReturnToBattle command.

    59: Jump
    Arguments: 1 word
    Jumps to another script at the given address. This does not return to the first script unless you jump back to it again.

    5A: ReturnToBattle
    Arguments: none
    Stops the script and returns to normal battle. The game will then go to the next script in the AI Script table (or if this was the last one, then it will go to the next move in the attacker’s move set and loop through the table again.) If this was the attacker’s last move, then battle will resume (it resumes again at the start of the player’s turn before they select an option).

    5B: JumpIfBattlerLevelsAre
    Arguments: 1 byte, 1 word
    This command compares the attacker and the defender’s levels to each other. I rewrote this one to have a few more options.

    Original arguments
    0 = Jump if the Attacker’s level is higher than the Defender's
    1 = Jump if the Attacker’s level is higher than or equal to the Defender’s
    2 = Jump if the Attacker’s level equals the Defender’s

    Rewrite’s arguments
    0 = Jump if the Attacker’s Level equals the Defender’s
    1 = Jump if the Attacker’s level does not equal the Defender’s
    2 = Jump if the Attacker’s level is less than the Defender’s
    3 = Jump if the Attacker’s level is more than the Defender’s

    5C: JumpIfTauntTurnsNotZero
    Arguments: 1 word
    Jumps to a given address if the amount of turns left in Taunt is not zero, (from my understanding this is when the defender has been Taunted in the first place). I said defender because this only checks the defender for some reason.

    5D: JumpIfTauntTurnsZero
    Arguments: 1 word
    Same as the last command, except jumping when the amount of turns remaining in Taunt is zero (so this is probably when the foe has not been Taunted or is on the final turn of Taunt’s activity).


    Rewritten Commands

    As promised, here are the routines for the commands that I rewrote. You are free to use these if you think they will work for you.

    Spoiler:

    1F: JumpIfDamagingMoveInMoveset
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    GetTarget:
    push {r4-r6, r14}
    mov r3, #0x0
    ldr r4, .AICursor
    ldr r5, [r4]
    ldrb r0, [r5, #0x1]
    cmp r0, #0x0
    beq Defender
    b Attacker

    Defender:
    ldr r6, .Defender
    b Moves

    Attacker:
    ldr r6, .Attacker

    Moves:
    ldrb r2, [r6]
    ldr r5, .AttackData
    ldr r1, .UserData
    mov r0, #0x58
    mul r0, r2
    add r1, r1, #0xC
    add r2, r0, r1
    ldrh r0, [r2]
    cmp r0, #0x0
    beq Loop
    lsl r1, r0, #0x1
    add r1, r1, r0
    lsl r1, r1, #0x2
    add r1, r1, r5
    ldrb r0, [r1, #0x1]
    cmp r0, #0x0
    bne Jump

    Loop:
    add r2, r2, #0x2
    add r3, r3, #0x1
    cmp r3, #0x3
    ble Moves
    b Return

    Jump:
    ldr r0, [r4]
    add r0, r0, #0x5
    str r0, [r4]
    ldr r2, [r4]
    ldrb r1, [r2, #0x1]
    ldrb r0, [r2, #0x2]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x3]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r4]

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

    .align 2
    .AICursor: .word 0x2039A00
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .UserData: .word 0x2023BE4
    .AttackData: .word 0x8250C04


    20: JumpIfDamagingMoveNotInMoveset
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    GetTarget:
    push {r4-r6, r14}
    mov r3, #0x0
    ldr r4, .AICursor
    ldr r5, [r4]
    ldrb r0, [r5, #0x1]
    cmp r0, #0x0
    beq Defender
    b Attacker

    Defender:
    ldr r6, .Defender
    b Moves

    Attacker:
    ldr r6, .Attacker

    Moves:
    ldrb r2, [r6]
    ldr r5, .AttackData
    ldr r1, .UserData
    mov r0, #0x58
    mul r0, r2
    add r1, r1, #0xC
    add r2, r0, r1
    ldrh r0, [r2]
    cmp r0, #0x0
    beq Loop
    lsl r1, r0, #0x1
    add r1, r1, r0
    lsl r1, r1, #0x2
    add r1, r1, r5
    ldrb r0, [r1, #0x1]
    cmp r0, #0x0
    bne Return

    Loop:
    add r2, r2, #0x2
    add r3, r3, #0x1
    cmp r3, #0x3
    ble Moves

    Jump:
    ldr r0, [r4]
    add r0, r0, #0x5
    str r0, [r4]
    ldr r2, [r4]
    ldrb r1, [r2, #0x1]
    ldrb r0, [r2, #0x2]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x3]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r4]

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

    .align 2
    .AICursor: .word 0x2039A00
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .UserData: .word 0x2023BE4
    .AttackData: .word 0x8250C04


    22: GetType
    (I added the ability to get the Types of the Attacker and Defender's partners in Double Battles)
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    GetTarget:
    push {r4, r14}
    ldr r1, .AICursor
    ldr r0, [r1]
    ldrb r4, [r0, #0x1]
    cmp r4, #0x0
    beq Defender1
    cmp r4, #0x1
    beq Attacker1
    cmp r4, #0x2
    beq Defender2
    cmp r4, #0x3
    beq Attacker2
    cmp r4, #0x4
    beq MoveVar
    cmp r4, #0x5
    beq FreeVar
    cmp r4, #0x6
    beq APartner1
    cmp r4, #0x7
    beq DPartner1
    cmp r4, #0x8
    beq APartner2
    cmp r4, #0x9
    beq DPartner2
    b Return

    Attacker1:
    ldr r0, .Attacker
    mov r1, #0x21
    b GetTypePokemon

    Defender1:
    ldr r0, .Defender
    mov r1, #0x21
    b GetTypePokemon

    APartner1:
    ldr r0, .APartner
    mov r1, #0x21
    b GetTypePokemon

    DPartner1:
    ldr r0, .DPartner
    mov r1, #0x21
    b GetTypePokemon

    Attacker2:
    ldr r0, .Attacker
    mov r1, #0x22
    b GetTypePokemon

    Defender2:
    ldr r0, .Defender
    mov r1, #0x22
    b GetTypePokemon

    APartner2:
    ldr r0, .APartner
    mov r1, #0x22
    b GetTypePokemon

    DPartner2:
    ldr r0, .DPartner
    mov r1, #0x22
    b GetTypePokemon

    MoveVar:
    ldr r0, .Resources

    ldr r0, [r0]

    add r0, r0, #0x14

    ldr r0, [r0]

    add r0, r0, #0x2
    ldrh r1, [r0]
    add r0, r0, #0x8
    mov r4, r0
    b GetTypeMove

    FreeVar:
    ldr r0, .Resources

    ldr r0, [r0]

    add r0, r0, #0x14

    ldr r0, [r0]

    add r0, r0, #0x8
    ldrh r1, [r0]
    mov r4, r0
    b GetTypeMove

    GetTypePokemon:
    /*r0 is the Target*/
    /*r1 is the Type (0x21 for 1st 0x22 for 2nd)*/
    ldrb r0, [r0]
    mov r2, #0x58
    mul r0, r2
    ldr r3, .UserData
    add r3, r3, r0
    add r3, r3, r1
    ldrb r3, [r3]
    b Store

    GetTypeMove:
    /*Move is in r1*/
    mov r2, #0xC
    mul r1, r2
    ldr r3, .AttackData
    add r3, r3, r1
    ldrb r3, [r3]
    b Store

    Store:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    str r3, [r4]

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

    .align 2
    .AICursor: .word 0x2039A00
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .APartner: .word 0x2023D60
    .DPartner: .word 0x2023D61
    .Resources: .word 0x2023FF4
    .UserData: .word 0x2023BE4
    .AttackData: .word 0x8250C04


    2E: GetMoveScriptID
    Arguments: 1 byte
    So I noticed something lacking with some of these commands. They either worked only with the current move, or only with the free var (so they could only be used with the GetMoveLastUsed command). So I added the ability to choose which one you want to use. 0 is the current move, 1 is the free var.
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    CheckArgument:
    push {r4-r5, lr}
    ldr r5, .AICursor
    ldr r4, [r5]
    ldrb r4, [r4, #0x1]
    cmp r4, #0x1
    beq FreeVar
    b ConsideredMove

    FreeVar:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    ldrh r4, [r4]
    b GetScript

    ConsideredMove:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x2
    ldrh r4, [r4]
    b GetScript

    GetScript:
    ldr r2, .AttackData
    lsl r0, r4, #0x1
    add r0, r0, r4
    lsl r0, r0, #0x2
    add r0, r0, r2
    ldrb r0, [r0, #0x0]

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

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x2
    str r4, [r5]
    pop {r4-r5}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x2039A00
    .Resources: .word 0x2023FF4
    .AttackData: .word 0x8250C04


    2F: GetAbility
    (I added the ability to get the ability of the Attacker and Defender's partners here too).
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    GetTarget:
    push {r4-r7, r14}
    ldr r0, .AICursor
    ldr r0, [r0]
    ldrb r0, [r0, #0x1]
    cmp r0, #0x0
    beq Defender
    cmp r0, #0x1
    beq Attacker
    cmp r0, #0x2
    beq AttackerPartner
    cmp r0, #0x3
    beq DefenderPartner
    b Return

    Defender:
    ldr r0, .Defender
    b GetAbility

    Attacker:
    ldr r0, .Attacker
    b GetAbility

    AttackerPartner:
    ldr r0, .AttackerPartner
    b GetAbility

    DefenderPartner:
    ldr r0, .DefenderPartner
    b GetAbility

    GetAbility:
    ldr r4, .UserBank
    ldr r5, .Resources
    ldr r5, [r5]
    add r5, r5, #0x14
    ldr r5, [r5]
    ldrb r0, [r0]
    mov r6, #0x58
    mul r0, r6
    add r0, r0, r4
    add r0, r0, #0x20
    ldrb r0, [r0]
    add r5, r5, #0x8
    str r0, [r5]
    b Return

    Return:
    ldr r4, .AICursor
    ldr r5, [r4]
    add r5, r5, #0x2
    str r5, [r4]
    pop {r4-r7}
    pop {r0}
    bx r0

    .align 2
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .AttackerPartner: .word 0x2023D6D
    .DefenderPartner: .word 0x2023D6E
    .AICursor: .word 0x2039A00
    .UserBank: .word 0x2023BE4
    .Resources: .word 0x2023FF4


    31: JumpIfDamageBonusEquals
    Arguments: Type/Move (1 byte), Value (1 byte), Address (1 word)
    I added a new argument before the effectiveness value. Now you can tell if a specific Type of move would be super effective or not. This is useful for things like Normalize or Glaciate, because the AI will still think that the move is whatever Type it what originally instead of what it gets turned into.

    Values (for Types)
    0 = Normal
    1 = Fighting
    2 = Flying
    3 = Poison
    4 = Ground
    5 = Rock
    6 = Bug
    7 = Ghost
    8 = Steel
    9 = Fairy
    A = Fire
    B = Water
    C = Grass
    D = Electric
    E = Psychic
    F = Ice
    10 = Dragon
    11 = Dark
    12 = Current Move

    Values for effectiveness
    0 = Immune
    A = 1/4 effectiveness (A = 14 / 2 = 28 / 4, 10 = 20 / 2 = 40 / 4)
    14 = 1/2 effectiveness (14 = 28 / 2, 20 = 40 / 2)
    28 = Neutral (40)
    50 = 2x effectiveness (50 = 28 * 2, 80 = 40 * 2)
    A0 = 4x effectiveness (A0 = 50 * 2 = 28 * 4, 160 = 80 * 2 = 40 * 4)
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    SetUp:
    push {r4-r5, lr}
    ldr r0, .PowerOverride
    mov r1, #0x0
    strh r1, [r0]
    ldr r0, .DP08
    ldr r0, [r0]
    strb r1, [r0, #0x13]
    ldr r0, .AttackMulti
    mov r2, #0x1
    strb r2, [r0, #0xE]
    ldr r5, .Effective
    strb r1, [r5]
    ldr r0, .Critical
    strb r2, [r0]
    ldr r4, .Damage
    mov r0, #0x28
    str r0, [r4]

    CheckArguments:
    ldr r0, .AICursor
    ldr r0, [r0]
    ldrb r0, [r0, #0x1]
    cmp r0, #0x0
    beq Normal
    cmp r0, #0x1
    beq Fighting
    cmp r0, #0x2
    beq Flying
    cmp r0, #0x3
    beq Poison
    cmp r0, #0x4
    beq Ground
    cmp r0, #0x5
    beq Rock
    cmp r0, #0x6
    beq Bug
    cmp r0, #0x7
    beq Ghost
    cmp r0, #0x8
    beq Steel
    cmp r0, #0x9
    beq Fairy
    cmp r0, #0xA
    beq Fire
    cmp r0, #0xB
    beq Water
    cmp r0, #0xC
    beq Grass
    cmp r0, #0xD
    beq Electric
    cmp r0, #0xE
    beq Psychic
    cmp r0, #0xF
    beq Ice
    cmp r0, #0x10
    beq Dragon
    cmp r0, #0x11
    beq Dark
    cmp r0, #0x12
    beq Move
    b Move

    Normal:
    ldr r1, .MoveToExecute
    mov r0, #0xF /*Cut*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Fighting:
    ldr r1, .MoveToExecute
    mov r0, #0x2 /*KarateChop*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Flying:
    ldr r1, .MoveToExecute
    mov r0, #0x13 /*Fly*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Poison:
    ldr r1, .MoveToExecute
    mov r0, #0x28 /*Poison Sting*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Ground:
    ldr r1, .MoveToExecute
    mov r0, #0x1C /*Sand Attack*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Rock:
    ldr r1, .MoveToExecute
    mov r0, #0x58 /*Rock Throw*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Bug:
    ldr r1, .MoveToExecute
    mov r0, #0x2A /*Pin Missle*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Ghost:
    ldr r1, .MoveToExecute
    mov r0, #0xF7 /*Shadow Ball*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Steel:
    ldr r1, .MoveToExecute
    mov r0, #0xD3 /*Steel Wing*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Fairy:
    ldr r1, .MoveToExecute
    mov r0, #0x76 /*Metronome*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Fire:
    ldr r1, .MoveToExecute
    mov r0, #0x7E /*Fire Blast*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Water:
    ldr r1, .MoveToExecute
    mov r0, #0x91 /*Bubble*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Grass:
    ldr r1, .MoveToExecute
    mov r0, #0x48 /*Mega Drain*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Electric:
    ldr r1, .MoveToExecute
    mov r0, #0x55 /*Thunderbolt*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Psychic:
    ldr r1, .MoveToExecute
    mov r0, #0x3C /*Psybeam*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Ice:
    ldr r1, .MoveToExecute
    mov r0, #0x3A /*Ice Beam*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Dragon:
    ldr r1, .MoveToExecute
    mov r0, #0x20 /*Dragon Rush*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Dark:
    ldr r1, .MoveToExecute
    mov r0, #0x2C /*Bite*/
    strh r0, [r1]
    ldrh r0, [r1]
    b GetEffectivity

    Move:
    ldr r1, .MoveToExecute
    ldr r0, .Resources
    ldr r0, [r0]
    add r0, r0, #0x14
    ldr r0, [r0]
    add r0, r0, #0x8
    strh r0, [r1]
    ldrh r0, [r1]

    GetEffectivity:
    ldr r1, .Attacker
    ldrb r1, [r1]
    ldr r2, .Defender
    ldrb r2, [r2]
    bl DamageCalc

    Compare:
    ldr r4, .Damage
    ldr r0, [r4]
    ldr r3, .AICursor
    ldr r2, [r3]
    ldrb r1, [r2, #0x2]
    cmp r0, r1
    bne Return

    Jump:
    ldrb r1, [r2, #0x3]
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x5]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x6]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r3]
    b Return

    Return:
    add r0, r2, #0x7
    str r0, [r3]
    pop {r4-r5}
    pop {r0}
    bx r0

    DamageCalc:
    ldr r5, .Get
    bx r5

    .align 2
    .MoveToExecute: .word 0x02023D4A
    .AICursor: .word 0x02039A00
    .Resources: .word 0x02023FF4
    .GetEffectivity: .word 0x080C8651
    .PowerOverride: .word 0x02023F50
    .DP08: .word 0x02023FE8
    .AttackMulti: .word 0x02023FC4
    .Effective: .word 0x02023DDC
    .Critical: .word 0x02023D71
    .Damage: .word 0x02023D50
    .Get: .word 0x0801EDF5
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C

    Note: this works by substituting the current move var for a different move of the desired Type. If you changed the Type of the move used in the routinel then you will obviously have to change that move to something else of the desired type.

    34: JumpIfPokemonHasStatus
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    SetUp:
    push {r4-r7, r14}
    mov r7, r10
    mov r6, r9
    mov r5, r8

    GetTarget:
    push {r5-r7}
    ldr r0, .AICursor
    ldr r1, [r0]
    ldrb r1, [r1, #0x1]
    cmp r1, #0x1
    bne Defender
    ldr r1, .FirstPokeAttacker
    b GetStatus

    Defender:
    ldr r1, .FirstPokeDefender

    GetStatus:
    mov r9, r1
    ldr r1, [r0]
    ldrb r7, [r1, #0x2]
    ldrb r7, [r1, #0x3]
    lsl r0, r0, #0x8
    orr r7, r0
    ldrb r0, [r1, #0x4]
    lsl r0, r0, #0x10
    orr r7, r0
    ldrb r0, [r1, #0x5]
    lsl r0, r0, #0x18
    orr r7, r0

    PartyLoop:
    mov r0, #0x64
    mov r4, r8
    mul r4, r0
    add r4, r9
    mov r0, r4

    GetSpecies:
    mov r1, #0xB
    bl Decrypt
    lsl r0, r0, #0x10
    lsr r5, r0 #0x10
    mov r0, r4

    GetHP:
    mov r1, #0x39
    bl Decrypt
    lsl r0, r0, #0x10
    lsr r6, r0, #0x10
    mov r0, r4

    GetStatus:
    mov r1, #0x37
    bl Decrypt

    Compare:
    cmp r5, #0x0 /*Checks if slot is empty*/
    beq Loop
    mov r1, #0xFF
    add r1, r1, #0x9D
    cmp r5, r1 /*Checks if slot is egg*/
    beq Loop
    cmp r6, r0 /*Checks if slot is fainted*/
    beq Loop
    cmp r0, r7
    beq Jump

    Loop:
    mov r0, #0x1
    mov r8, r0
    mov r1, r8
    cmp r1, #0x5
    ble PartyLoop
    b ReturnPrep

    Jump:
    ldr r3, .AICursor
    ldr r2, [r3]
    ldrb r1, [r2, #0x6]
    ldrb r0, [r2, #0x7]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x8]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x9]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r3]
    b Return

    ReturnPrep:
    ldr r1, .AICursor
    ldr r0, [r1]
    add r0, r0, #0xA
    str r0, [r1]

    Return:
    pop {r3-r5}
    mov r8, r3
    mov r9, r4
    mov r10, r5
    pop {r4-r7}
    pop {r0}
    bx r0

    Decrypt:
    ldr r2, .Decrypter
    bx r2

    .align 2
    .AICursor: .word 0x02039A00
    .FirstPokeAttacker: .word 0x0202402C
    .FirstPokeDefender: .word 0x02024284
    .Decrypter: .word 0x0803FBE9


    35: JumpIfNoPokemonHasStatus
    (Apparently, the original of this command doesn't work. Not sure if that's true but this one definitely should).
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    SetUp:
    push {r4-r7, r14}
    mov r7, r10
    mov r6, r9
    mov r5, r8

    GetTarget:
    push {r5-r7}
    ldr r0, .AICursor
    ldr r1, [r0]
    ldrb r1, [r1, #0x1]
    cmp r1, #0x1
    bne Defender
    ldr r1, .FirstPokeAttacker
    b GetStatus

    Defender:
    ldr r1, .FirstPokeDefender

    GetStatus:
    mov r9, r1
    ldr r1, [r0]
    ldrb r7, [r1, #0x2]
    ldrb r7, [r1, #0x3]
    lsl r0, r0, #0x8
    orr r7, r0
    ldrb r0, [r1, #0x4]
    lsl r0, r0, #0x10
    orr r7, r0
    ldrb r0, [r1, #0x5]
    lsl r0, r0, #0x18
    orr r7, r0

    PartyLoop:
    mov r0, #0x64
    mov r4, r8
    mul r4, r0
    add r4, r9
    mov r0, r4

    GetSpecies:
    mov r1, #0xB
    bl Decrypt
    lsl r0, r0, #0x10
    lsr r5, r0 #0x10
    mov r0, r4

    GetHP:
    mov r1, #0x39
    bl Decrypt
    lsl r0, r0, #0x10
    lsr r6, r0, #0x10
    mov r0, r4

    GetStatus:
    mov r1, #0x37
    bl Decrypt

    Compare:
    cmp r5, #0x0 /*Checks if slot is empty*/
    beq Loop
    mov r1, #0xFF
    add r1, r1, #0x9D
    cmp r5, r1 /*Checks if slot is egg*/
    beq Loop
    cmp r6, r0 /*Checks if slot is fainted*/
    beq Loop
    cmp r0, r7
    beq ReturnPrep

    Loop:
    mov r0, #0x1
    mov r8, r0
    mov r1, r8
    cmp r1, #0x5
    ble PartyLoop
    b Jump

    Jump:
    ldr r3, .AICursor
    ldr r2, [r3]
    ldrb r1, [r2, #0x6]
    ldrb r0, [r2, #0x7]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x8]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x9]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r3]
    b Return

    ReturnPrep:
    ldr r1, .AICursor
    ldr r0, [r1]
    add r0, r0, #0xA
    str r0, [r1]

    Return:
    pop {r3-r5}
    mov r8, r3
    mov r9, r4
    mov r10, r5
    pop {r4-r7}
    pop {r0}
    bx r0

    Decrypt:
    ldr r2, .Decrypter
    bx r2

    .align 2
    .AICursor: .word 0x02039A00
    .FirstPokeAttacker: .word 0x0202402C
    .FirstPokeDefender: .word 0x02024284
    .Decrypter: .word 0x0803FBE9


    36: GetWeather
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    LoadWeather:
    push {r14}
    ldr r2, .WeatherRAM

    CheckRain:
    ldrh r1, [r2]
    mov r0, #0x7
    and r0, r1
    cmp r0, #0x0
    beq CheckSandstorm
    ldr r0, .Resources
    ldr r0, [r0]
    ldr r1, [r0, #0x14]
    mov r0, #0x1
    str r0, [r1, #0x8]
    b Return

    CheckSandstorm:
    ldrh r1, [r2]
    mov r0, #0x18
    and r0, r1
    cmp r0, #0x0
    beq CheckSun
    ldr r0, .Resources
    ldr r0, [r0]
    ldr r1, [r0, #0x14]
    mov r0, #0x2
    str r0, [r1, #0x8]
    b Return

    CheckSun:
    ldrh r1, [r2]
    mov r0, #0x60
    and r0, r1
    cmp r0, #0x0
    beq CheckHail
    ldr r0, .Resources
    ldr r0, [r0]
    ldr r1, [r0, #0x14]
    mov r0, #0x3
    str r0, [r1, #0x8]
    b Return

    CheckHail:
    ldrh r1, [r2]
    mov r0, #0x80
    and r0, r1
    cmp r0, #0x0
    beq ClearWeather
    ldr r0, .Resources
    ldr r0, [r0]
    ldr r1, [r0, #0x14]
    mov r0, #0x4
    str r0, [r1, #0x8]
    b Return

    ClearWeather:
    ldr r0, .Resources
    ldr r0, [r0]
    ldr r1, [r0, #0x14]
    mov r0, #0x0
    str r0, [r1, #0x8]
    b Return

    Return:
    ldr r1, .AICursor
    ldr r0, [r1]
    add r0, #0x1
    str r0, [r1]
    pop r0
    bx r0

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


    3F: JumpIfMoveInMoveset
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckTarget:
    push {r4-r7, r14}
    ldr r1, .AICursor
    ldr r0, [r1]
    ldrb r0, [r0, #0x1]

    MoveToCompare:
    ldr r6, [r1]
    ldrb r3, [r6, #0x3]
    ldrb r6, [r6, #0x2]
    lsl r3, r3, #0x8
    orr r6, r3
    add r5, r1, #0x0

    Compare:
    cmp r0, #0x0 /*Just Opponent*/
    beq OneDefender
    cmp r0, #0x1 /*Just User*/
    beq OneAttacker
    cmp r0, #0x2 /*Both Attackers*/
    beq BothAttackers
    cmp r0, #0x3 /*Both Defenders*/
    beq BothDefenders
    b Return

    OneDefender:
    push {r5}
    mov r5, #0x0
    b Defender

    OneAttacker:
    push {r5}
    mov r5, #0x1
    b Attacker

    BothAttackers:
    push {r5}
    mov r5, #0x2
    b Attacker

    BothDefenders:
    push {r5}
    mov r5, #0x3
    b Defender

    Attacker:
    mov r3, #0x0
    ldr r2, .UserBank
    ldr r4, .Attacker
    b GetMove

    AttackerPartner:
    mov r5, #0x4
    ldr r2, .UserBank
    ldr r4, .AttackerPartner
    b GetMove

    Defender:
    mov r3, #0x0
    ldr r2, .UserBank
    ldr r4, .Defender
    b GetMove

    DefenderPartner:
    mov r5, #0x4
    ldr r2, .UserBank
    ldr r4, .DefenderPartner
    b GetMove

    GetMove:
    ldrb r1, [r4]
    ldr r2, .UserBank
    mov r0, #0x58
    mul r0, r1
    add r2, r2, #0xC
    add r0, r0, r2
    add r0, r0, r3
    add r0, r0, r3
    ldrh r0, [r0]
    cmp r0, r6
    beq Jump
    b Loop

    Loop:
    add r3, r3, #0x1
    cmp r3, #0x3
    ble GetMove
    mov r3, #0x0
    cmp r5, #0x2
    bge CheckPartner
    b Pop

    CheckPartner:
    cmp r5, #0x2
    beq AttackerPartner
    cmp r5, #0x3
    beq DefenderPartner
    b Pop

    Jump:
    pop {r5}
    ldr r2, [r5]
    ldrb r1, [r2, #0x3]
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x5]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x6]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r5]
    b Return

    Pop:
    pop {r5}
    ldr r1, [r5]
    add r1, #0x8
    str r1, [r5]

    Return:
    pop {r4-r7}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x02039A00
    .UserBank: .word 0x02023BE4
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C
    .AttackerPartner: .word 0x02023D6D
    .DefenderPartner: .word 0x02023D6E


    40: JumpIfMoveNotInMoveset
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckTarget:
    push {r4-r7, r14}
    ldr r1, .AICursor
    ldr r0, [r1]
    ldrb r0, [r0, #0x1]

    MoveToCompare:
    ldr r6, [r1]
    ldrb r3, [r6, #0x3]
    ldrb r6, [r6, #0x2]
    lsl r3, r3, #0x8
    orr r6, r3
    add r5, r1, #0x0

    Compare:
    cmp r0, #0x0 /*Just Opponent*/
    beq OneDefender
    cmp r0, #0x1 /*Just User*/
    beq OneAttacker
    cmp r0, #0x2 /*Both Attackers*/
    beq BothAttackers
    cmp r0, #0x3 /*Both Defenders*/
    beq BothDefenders
    b Return

    OneDefender:
    push {r5}
    mov r5, #0x0
    b Defender

    OneAttacker:
    push {r5}
    mov r5, #0x1
    b Attacker

    BothAttackers:
    push {r5}
    mov r5, #0x2
    b Attacker

    BothDefenders:
    push {r5}
    mov r5, #0x3
    b Defender

    Attacker:
    mov r3, #0x0
    ldr r2, .UserBank
    ldr r4, .Attacker
    b GetMove

    AttackerPartner:
    mov r5, #0x4
    ldr r2, .UserBank
    ldr r4, .AttackerPartner
    b GetMove

    Defender:
    mov r3, #0x0
    ldr r2, .UserBank
    ldr r4, .Defender
    b GetMove

    DefenderPartner:
    mov r5, #0x4
    ldr r2, .UserBank
    ldr r4, .DefenderPartner
    b GetMove

    GetMove:
    ldrb r1, [r4]
    ldr r2, .UserBank
    mov r0, #0x58
    mul r0, r1
    add r2, r2, #0xC
    add r0, r0, r2
    add r0, r0, r3
    add r0, r0, r3
    ldrh r0, [r0]
    cmp r0, r6
    bne Jump
    b Loop

    Loop:
    add r3, r3, #0x1
    cmp r3, #0x3
    ble GetMove
    mov r3, #0x0
    cmp r5, #0x2
    bge CheckPartner
    b Pop

    CheckPartner:
    cmp r5, #0x2
    beq AttackerPartner
    cmp r5, #0x3
    beq DefenderPartner
    b Pop

    Jump:
    pop {r5}
    ldr r2, [r5]
    ldrb r1, [r2, #0x3]
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x5]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x6]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r5]
    b Return

    Pop:
    pop {r5}
    ldr r1, [r5]
    add r1, #0x8
    str r1, [r5]

    Return:
    pop {r4-r7}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x02039A00
    .UserBank: .word 0x02023BE4
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C
    .AttackerPartner: .word 0x02023D6D
    .DefenderPartner: .word 0x02023D6E


    41: JumpIfMoveScriptInMoveset
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckTarget:
    push {r4-r7, r14}
    ldr r1, .AICursor
    ldr r0, [r1]
    ldrb r0, [r0, #0x1]
    add r5, r1, #0x0

    CompareTarget:
    cmp r0, #0x0 /*Just Opponent*/
    beq OneDefender
    cmp r0, #0x1 /*Just User*/
    beq OneAttacker
    cmp r0, #0x2 /*Both Attackers*/
    beq BothAttackers
    cmp r0, #0x3 /*Both Defenders*/
    beq BothDefenders
    b Return

    OneDefender:
    push {r5}
    mov r5, #0x0
    b Defender

    OneAttacker:
    push {r5}
    mov r5, #0x1
    b Attacker

    BothAttackers:
    push {r5}
    mov r5, #0x2
    b Attacker

    BothDefenders:
    push {r5}
    mov r5, #0x3
    b Defender

    Attacker:
    mov r3, #0x0
    ldr r1, .UserBank
    ldr r7, .Attacker
    b GetMove

    AttackerPartner:
    mov r5, #0x4
    ldr r1, .UserBank
    ldr r7, .AttackerPartner
    b GetMove

    Defender:
    mov r3, #0x0
    ldr r1, .UserBank
    ldr r7, .Defender
    b GetMove

    DefenderPartner:
    mov r5, #0x4
    ldr r1, .UserBank
    ldr r7, .DefenderPartner
    b GetMove

    GetMove:
    /*Loads Position # into r2*/
    /*Multiplies it by 0x58*/
    /*Adds 12 to 23BE4 to get 23BF0*/
    ldrb r2, [r7]
    ldr r1, .UserBank
    mov r0, #0x58
    mul r0, r2
    add r1, r1, #0xC
    add r0, r0, r1
    add r0, r0, r3
    add r0, r0, r3
    ldrh r0, [r0]
    b GetScript

    GetScript:
    ldr r6, .AttackTable
    mov r2, #0xC
    mul r0, r2
    add r6, r6, r0
    ldrb r0, [r6]
    ldr r4, .AICursor
    ldr r4, [r4]
    ldrb r2, [r4, #0x2]
    cmp r0, r2
    beq Jump

    Loop:
    add r3, r3, #0x1
    cmp r3, #0x3
    ble GetMove
    mov r3, #0x0
    cmp r5, #0x2
    bge CheckPartner
    b Return

    CheckPartner:
    cmp r5, #0x2
    beq AttackerPartner
    cmp r5, #0x3
    beq DefenderPartner
    b Return

    Jump:
    pop {r5}
    ldr r2, [r5]
    ldrb r1, [r2, #0x3]
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x5]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x6]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r5]

    Return:
    pop {r5}
    ldr r1, [r5]
    add r1, r1, #0x7
    str r1, [r5]
    pop {r4-r7}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x02039A00
    .UserBank: .word 0x02023BE4
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C
    .AttackerPartner: .word 0x02023D6D
    .DefenderPartner: .word 0x02023D6E
    .AttackTable: .word 0x08250C04


    42: JumpIfMoveScriptNotInMoveset
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckTarget:
    push {r4-r7, r14}
    ldr r1, .AICursor
    ldr r0, [r1]
    ldrb r0, [r0, #0x1]
    add r5, r1, #0x0

    CompareTarget:
    cmp r0, #0x0 /*Just Opponent*/
    beq OneDefender
    cmp r0, #0x1 /*Just User*/
    beq OneAttacker
    cmp r0, #0x2 /*Both Attackers*/
    beq BothAttackers
    cmp r0, #0x3 /*Both Defenders*/
    beq BothDefenders
    b Return

    OneDefender:
    push {r5}
    mov r5, #0x0
    b Defender

    OneAttacker:
    push {r5}
    mov r5, #0x1
    b Attacker

    BothAttackers:
    push {r5}
    mov r5, #0x2
    b Attacker

    BothDefenders:
    push {r5}
    mov r5, #0x3
    b Defender

    Attacker:
    mov r3, #0x0
    ldr r1, .UserBank
    ldr r7, .Attacker
    b GetMove

    AttackerPartner:
    mov r5, #0x4
    ldr r1, .UserBank
    ldr r7, .AttackerPartner
    b GetMove

    Defender:
    mov r3, #0x0
    ldr r1, .UserBank
    ldr r7, .Defender
    b GetMove

    DefenderPartner:
    mov r5, #0x4
    ldr r1, .UserBank
    ldr r7, .DefenderPartner
    b GetMove

    GetMove:
    /*Loads Position # into r2*/
    /*Multiplies it by 0x58*/
    /*Adds 12 to 23BE4 to get 23BF0*/
    ldrb r2, [r7]
    ldr r1, .UserBank
    mov r0, #0x58
    mul r0, r2
    add r1, r1, #0xC
    add r0, r0, r1
    add r0, r0, r3
    add r0, r0, r3
    ldrh r0, [r0]
    b GetScript

    GetScript:
    ldr r6, .AttackTable
    mov r2, #0xC
    mul r0, r2
    add r6, r6, r0
    ldrb r0, [r6]
    ldr r4, .AICursor
    ldr r4, [r4]
    ldrb r2, [r4, #0x2]
    cmp r0, r2
    bne Jump

    Loop:
    add r3, r3, #0x1
    cmp r3, #0x3
    ble GetMove
    mov r3, #0x0
    cmp r5, #0x2
    bge CheckPartner
    b Return

    CheckPartner:
    cmp r5, #0x2
    beq AttackerPartner
    cmp r5, #0x3
    beq DefenderPartner
    b Return

    Jump:
    pop {r5}
    ldr r2, [r5]
    ldrb r1, [r2, #0x3]
    ldrb r0, [r2, #0x4]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r2, #0x5]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r2, #0x6]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r5]

    Return:
    pop {r5}
    ldr r1, [r5]
    add r1, r1, #0x7
    str r1, [r5]
    pop {r4-r7}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x02039A00
    .UserBank: .word 0x02023BE4
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C
    .AttackerPartner: .word 0x02023D6D
    .DefenderPartner: .word 0x02023D6E
    .AttackTable: .word 0x08250C04


    4E: GetKindOfMove
    Arguments: 1 byte
    This is the rewrite of the GetKindOfMove command so that it actually can use the current move as an argument.
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    CheckArgument:
    push {r4-r5, lr}
    ldr r5, .AICursor
    ldr r4, [r5]
    ldrb r4, [r4, #0x1]
    cmp r4, #0x1
    beq FreeVar
    b ConsideredMove

    FreeVar:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    ldrh r4, [r4]
    b GetScript

    ConsideredMove:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x2
    ldrh r4, [r4]
    b GetScript

    GetScript:
    ldr r2, .AttackData
    lsl r0, r4, #0x1
    add r0, r0, r4
    lsl r0, r0, #0x2
    add r0, r0, r2
    ldrb r0, [r0, #0xA]

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

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x2
    str r4, [r5]
    pop {r4-r5}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x2039A00
    .Resources: .word 0x2023FF4
    .AttackData: .word 0x8250C04


    50: GetMoveRange
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    CheckArgument:
    push {r4-r5, lr}
    ldr r5, .AICursor
    ldr r4, [r5]
    ldrb r4, [r4, #0x1]
    cmp r4, #0x1
    beq FreeVar
    b ConsideredMove

    FreeVar:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    ldrh r4, [r4]
    b GetScript

    ConsideredMove:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x2
    ldrh r4, [r4]
    b GetScript

    GetScript:
    ldr r2, .AttackData
    lsl r0, r4, #0x1
    add r0, r0, r4
    lsl r0, r0, #0x2
    add r0, r0, r2
    ldrb r0, [r0, #0x6]

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

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x2
    str r4, [r5]
    pop {r4-r5}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x2039A00
    .Resources: .word 0x2023FF4
    .AttackData: .word 0x8250C04


    5B: JumpIfBattlerLevelsAre
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    GetTarget:
    push {r4-r5, r14}
    mov r5, r8
    push {r5}
    ldr r5, .AICursor
    ldr r4, [r5]
    ldrb r0, [r4, #0x1]
    mov r8, r0
    b GetLevels

    GetLevels:
    ldr r3, .UserData
    ldr r0, .Attacker
    ldrb r0, [r0]
    mov r2, #0x58
    mov r1, r0
    mul r1, r2
    add r1, r1, r3
    add r1, #0x2A
    ldr r0, .Defender
    ldrb r0, [r0]
    mul r0, r2
    add r0, r0, r3
    add r0, #0x2A
    ldrb r1, [r1] /*Player's Pokemon*/
    ldrb r0, [r0] /*Trainer's Pokemon*/
    mov r5, r8

    CheckArgument:
    cmp r5, #0x0
    beq Equal
    cmp r5, #0x1
    beq NotEqual
    cmp r5, #0x2
    beq LessThan
    cmp r5, #0x3
    beq MoreThan
    b Skip

    Equal:
    cmp r1, r0
    beq Jump
    b Skip

    NotEqual:
    cmp r1, r0
    bne Jump
    b Skip

    LessThan:
    cmp r1, r0
    bls Jump
    b Skip

    MoreThan:
    cmp r1, r0
    bgt Jump
    b Skip

    Jump:
    ldrb r1, [r4, #0x2]
    ldrb r0, [r4, #0x3]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r4, #0x4]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r4, #0x5]
    lsl r0, r0, #0x18
    orr r1, r0
    ldr r0, .AICursor
    str r1, [r0]
    b Return

    Skip:
    add r0, r4, #0x6
    ldr r1, .AICursor
    str r0, [r1]

    Return:
    pop {r5}
    mov r8, r5
    pop {r4, r5}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x2039A00
    .UserData: .word 0x2023BE4
    .Attacker: .word 0x2023D6C
    .Defender: .word 0x2023D6B




    New Commands
    Now, if you read that whole long list of commands carefully, you will have noticed that there are some numbers missing. This is because, for some unknown reason, there are 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 softlocked the game.

    This is useful because it means you can add about ten new commands without replacing or expanding the table!

    I wrote three so far, which I will include and explain. If I write more which I think will be useful, I'll post them here later. The numbers I use here are the command numbers I gave them, but they don't have to be what you use.

    Spoiler:

    2A: GetPerishCounter
    Argument: 1 byte
    It takes the target given and obtains the Perish Count (amount of turns left until Perish Song takes effect and the target faints). This can be used for having certain moves have priority on the turn before fainting or other such things.

    Here’s list of bytes returned:
    32 = Perish Count 3
    31 = Perish Count 2
    30 = Perish Count 1 (turn before fainting)
    00 = Not affected by Perish Song

    Routine:
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckTarget:
    push {r4, r14}
    ldr r0, .AICursor
    ldr r1, [r0]
    ldrb r1, [r1, #0x1]
    mov r4, r0
    cmp r1, #0x1
    bne Defender
    ldr r0, .Attacker
    b GetPerishCount

    Defender:
    ldr r0, .Defender

    GetPerishCount:
    ldrb r3, [r0]
    ldr r0, .Resources
    ldr r0, [r0]
    add r0, r0, #0x14
    ldr r2, [r0]
    ldr r1, .DisableData
    lsl r0, r3, #0x3
    sub r0, r0, r3
    lsl r0, r0, #0x2
    add r0, r0, r1
    ldrb r0, [r0, #0xF] /*Perish Song Timer*/

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

    .align 2
    .AICursor: .word 0x2039A00
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .DisableData: .word 0x2023E0C
    .Resources: .word 0x2023FF4


    2B: GetSpikesLayer
    Arguments: 1 byte
    So the way the AI checks for Spikes in default FireRed is stupid. The way Spikes works is you can have up to three layers set up. Depending on how many layers are set up, a certain amount of damage is done to non-levitating foes when they switch in. The more layers, the higher the damage.

    Well the way default FireRed is programmed it is impossible for the AI to know when to stop using the move, and also use the move to its full potential at the same time. This is because the game checks the RAM address that has the “Spikes exist on this side of the field” byte instead of the “This is how many layers of spikes there are” byte.

    Thus I wrote this command so you can check for all three layers of Spikes before decreasing the move’s priority. So now the AI will be able to set up Spikes properly.

    Routine
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckTarget:
    push {r4, r14}
    ldr r0, .AICursor
    ldr r1, [r0]
    ldrb r1, [r1, #0x1]
    mov r4, r0
    cmp r1, #0x1
    bne Defender
    ldr r0, .Attacker
    b GetSpikesLayer

    Defender:
    ldr r0, .Defender

    GetSpikesLayer:
    ldrb r3, [r0]
    mov r0, #0xC
    mul r3, r0
    ldr r0, .Resources
    ldr r0, [r0]
    add r0, r0, #0x14
    ldr r2, [r0]
    ldr r1, .PartyTimersFoe
    add r1, r1, r3
    add r1, r1, #0xA
    ldrb r0, [r1] /*Layer of Spikes*/

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

    .align 2
    .AICursor: .word 0x2039A00
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .PartyTimersFoe: .word 0x2023DE4
    .Resources: .word 0x2023FF4


    32: JumpIfAnyOrAllStatsAre
    Arguments: 4 bytes, 1 word
    This command has a whopping five arguments! That sounds daunting, but the command itself is not too complicated.

    The first argument is the target (Attacker or Defender).

    The second argument is whether you want to check Any or All stats. All stats is every single stat for the target. Any just means if any of the target’s stats fits the next argument, then the script will jump. Any is 0, All is 1.

    The third argument is which comparison you want to make.

    Values
    0 = Equal
    1 = NotEqual
    2 = LessThan
    3 = MoreThan

    The fourth argument is the value you want to compare the stats too.

    The final argument is the address you want to jump to.

    Routine
    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    GetTarget:
    push {r4-r7, r14}
    ldr r0, .AICursor
    ldr r1, [r0]
    ldrb r0, [r1, #0x1]
    ldrb r5, [r1, #0x2]
    ldrb r6, [r1, #0x3]
    ldrb r7, [r1, #0x4]
    cmp r0, #0x1
    beq Attacker
    cmp r0, #0x0
    beq Defender
    b Return

    Attacker:
    ldr r0, .Attacker
    b Argument

    Defender:
    ldr r0, .Defender
    b Argument

    Argument:
    ldr r4, .UserBank
    ldrb r0, [r0]
    mov r1, #0x58
    mul r0, r1
    add r4, r0, r4
    mov r2, #0x19
    cmp r5, #0x0
    beq Any
    cmp r5, #0x1
    beq All
    b Return

    Any:
    cmp r6, #0x0
    beq AnyLessThan
    cmp r6, #0x1
    beq AnyMoreThan
    cmp r6, #0x2
    beq AnyEqual
    cmp r6, #0x3
    beq AnyNotEqual
    b Return

    All:
    cmp r6, #0x0
    beq AllLessThan
    cmp r6, #0x1
    beq AllMoreThan
    cmp r6, #0x2
    beq AllEqual
    cmp r6, #0x3
    beq AllNotEqual
    b Return

    AnyLessThan:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    blt Jump
    add r2, r2, #0x1
    cmp r2, #0x1f
    ble AnyLessThan
    b Return

    AnyMoreThan:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    bgt Jump
    add r2, r2, #0x1
    cmp r2, #0x1f
    ble AnyMoreThan
    b Return

    AnyEqual:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    beq Jump
    add r2, r2, #0x1
    cmp r2, #0x1f
    ble AnyEqual
    b Return

    AnyNotEqual:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    bne Jump
    add r2, r2, #0x1
    cmp r2, #0x1f
    ble AnyNotEqual
    b Return

    AllMoreThan:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    ble Return
    add r2, r2, #0x1
    cmp r2, #0x1F
    ble AllMoreThan
    b Jump

    AllLessThan:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    bge Return
    add r2, r2, #0x1
    cmp r2, #0x1F
    ble AllLessThan
    b Jump

    AllNotEqual:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    beq Return
    add r2, r2, #0x1
    cmp r2, #0x1F
    ble AllNotEqual
    b Jump

    AllEqual:
    add r0, r2, r4
    ldrb r1, [r0]
    cmp r1, r7
    bne Return
    add r2, r2, #0x1
    cmp r2, #0x1F
    ble AllEqual
    b Jump

    Jump:
    ldr r5, .AICursor
    ldr r4, [r5]
    ldrb r1, [r4, #0x5]
    ldrb r0, [r4, #0x6]
    lsl r0, r0, #0x8
    orr r1, r0
    ldrb r0, [r4, #0x7]
    lsl r0, r0, #0x10
    orr r1, r0
    ldrb r0, [r4, #0x8]
    lsl r0, r0, #0x18
    orr r1, r0
    str r1, [r5]
    pop {r4-r7}
    pop {r0}
    bx r0

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x9
    str r4, [r5]
    pop {r4-r7}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x2039A00
    .Attacker: .word 0x2023D6B
    .Defender: .word 0x2023D6C
    .UserBank: .word 0x2023BE4


    33: DoesTeeterDanceWork
    Arguments: None
    This one is self-explanatory. This checks if any of the other Pokemon are confused and if they have Own Tempo. If no Pokemon on the field will be affected by Teeter Dance, this returns a 0, otherwise a 1. I wrote this because there was no real way to truly determine whether the move would work in Double Battles.
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    GetStatus:
    push {r4-r6, r14}
    ldr r0, .AttackerPartner
    ldrb r0, [r0]
    ldr r1, .Defender
    ldrb r1, [r1]
    ldr r2, .DefenderPartner
    ldrb r2, [r2]
    mov r3, #0x58
    mul r0, r3
    mul r1, r3
    mul r2, r3
    ldr r4, .UserBank
    add r0, r0, r4
    add r1, r1, r4
    add r2, r2, r4
    add r0, r0, #0x50
    add r1, r1, #0x50
    add r2, r2, #0x50
    ldrb r0, [r0]
    ldrb r1, [r1]
    ldrb r2, [r2]

    Check:
    cmp r0, #0x7
    bne OwnTempo
    cmp r1, #0x7
    bne OwnTempo
    cmp r2, #0x7
    bne OwnTempo
    b WontWork

    OwnTempo:
    ldr r0, .AttackerPartner
    ldrb r0, [r0]
    ldr r1, .Defender
    ldrb r1, [r1]
    ldr r2, .DefenderPartner
    ldrb r2, [r2]
    mov r3, #0x58
    mul r0, r3
    mul r1, r3
    mul r2, r3
    ldr r4, .UserBank
    add r0, r0, r4
    add r1, r1, r4
    add r2, r2, r4
    add r0, r0, #0x20
    add r1, r1, #0x20
    add r2, r2, #0x20
    ldrb r0, [r0]
    ldrb r1, [r1]
    ldrb r2, [r2]

    Check2:
    cmp r0, #0x14
    bne Work
    cmp r1, #0x14
    bne Work
    cmp r2, #0x14
    bne Work
    b WontWork

    Work:
    ldr r6, .Resources
    ldr r6, [r6]
    add r6, r6, #0x14
    ldr r6, [r6]
    add r6, r6, #0x8
    mov r0, #0x1
    str r0, [r6]
    b Return

    WontWork:
    ldr r6, .Resources
    ldr r6, [r6]
    add r6, r6, #0x14
    ldr r6, [r6]
    add r6, r6, #0x8
    mov r0, #0x0
    str r0, [r6]
    b Return

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x1
    str r4, [r5]
    pop {r4-r6}
    pop {r0}
    bx r0

    .align 2
    .AttackerPartner: .word 0x2023D6D
    .Defender: .word 0x2023D6C
    .DefenderPartner: .word 0x2023D6E
    .Resources: .word 0x2023FF4
    .AICursor: .word 0x2039A00
    .UserBank: .word 0x2023BE4


    52: GetTarget
    Arguments: None
    This gets the target. This was made mainly for Double Battles where you actually have options for who your target is. If you are in a Single Battle, it will simply return 0. If you are in a Double Battle, it will return either 0 or 2 if your target is an opponent (0/2 being the position number). If your target was your partner,
    it will return a 1.

    Small But Important Side Note: The Target in Double Battles is determined in a routine starting at 0xC6E2E. It essentially generates it randomly. Yaaaaay. I wanted to change that routine to have a bit more thought put into choosing the target, and I'm currently in the process of doing so. I will post that later when I'm done writing/testing it.

    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckDoubleBattle:
    push {r4-r5, lr}
    ldr r4, .BattleType
    ldr r4, [r4]
    mov r1, #0x1
    and r1, r4
    cmp r1, #0x0
    beq Single
    b Double

    Single:
    mov r0, #0x0
    b Store

    Double:
    ldr r0, .Defender
    ldrb r0, [r0]
    ldr r1, .APartner
    ldrb r1, [r1]
    cmp r0, r1
    beq TargetPartner
    cmp r0, #0x0
    beq Single
    mov r0, #0x2
    b Store

    TargetPartner:
    mov r0, #0x1
    b Store

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

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

    .align 2
    .BattleType: .word 0x02022B4C
    .Defender: .word 0x02023D6C
    .APartner: .word 0x02023D6D
    .Resources: .word 0x02023FF4
    .AICursor: .word 0x02039A00


    53: GetSubHealthRatio
    Arguments: 1 byte
    This gets the health percentage of the Substitute of a given target, just in case that is ever something somebody else besides me would ever want to do.
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    Main:
    push {r4-r5, r14}
    ldr r0, .AICursor
    ldr r1, [r0]
    ldrb r1, [r1, #0x1]
    cmp r1, #0x0
    beq Defender
    cmp r1, #0x1
    beq Attacker
    cmp r1, #0x2
    beq DPartner
    cmp r1, #0x3
    beq APartner
    b Return

    Defender:
    ldr r1, .Defender
    ldrb r1, [r1]
    b GetMaxSubHP

    Attacker:
    ldr r1, .Attacker
    ldrb r1, [r1]
    b GetMaxSubHP

    DPartner:
    ldr r1, .DPartner
    ldrb r1, [r1]
    b GetMaxSubHP

    APartner:
    ldr r1, .APartner
    ldrb r1, [r1]
    b GetMaxSubHP

    GetMaxSubHP:
    /*02023BE4 in r2*/
    /*58 in r3*/
    /*r1 * r3 + r2 = offset*/
    /*Puts max HP in r4*/
    ldr r2, .UserData
    mov r3, #0x58
    mul r3, r1
    add r2, r2, r3
    add r2, r2, #0x2C
    ldrh r4, [r2]
    cmp r4, #0xFF
    ble GetSubHP
    mov r4, #0xFF

    GetSubHP:
    ldr r2, .Disable
    lsl r0, r1, #0x3
    sub r0, r0, r1
    lsl r0, r0, #0x2
    add r0, r0, r2
    ldrb r5, [r0, #0xA]

    Divide:
    mov r2, #0x64
    mul r5, r2
    mov r0, r5
    mov r1, r4
    bl Divide2

    Store:
    ldr r3, .Resources
    ldr r3, [r3]
    add r3, r3, #0x14
    ldr r3, [r3]
    add r3, r3, #0x8
    str r0, [r3]
    b Return

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x2
    str r4, [r5]
    pop {r4-r5}
    pop {r0}
    bx r0

    Divide2:
    ldr r2, .Divider
    bx r2

    .align 2
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C
    .APartner: .word 0x02023D6D
    .DPartner: .word 0x02023D6E
    .UserData: .word 0x02023BE4
    .Disable: .word 0x02023E0C
    .AICursor: .word 0x02039A00
    .Divider: .word 0x081E4019
    .Resources: .word 0x02023FF4


    54: CheckIfMoveFlagSet
    Arguments: 1 byte
    This checks if the move flags for the considered move (or free var. Use 0 for current move, and 1 for the free var) have a bit in common with the byte given. If they do, then it returns 1.

    Values:
    0x1 —> Direct contact
    0x2 —> Affected by Protect
    0x4 —> Affected by Magic Coat
    0x8 —> Affected by Snatch
    0x10 —> Can be used by Mirror Move
    0x20 —> Affected by King’s Rock
    0x3F —> All of the above

    Spoiler:

    .text
    .align 2
    .thumb
    .thumb_func

    CheckArgument:
    push {r4-r5, lr}
    ldr r5, .AICursor
    ldr r4, [r5]
    ldrb r4, [r4, #0x1]
    cmp r4, #0x1
    beq FreeVar
    b ConsideredMove

    FreeVar:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    ldrh r1, [r4]
    b GetFlags

    ConsideredMove:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x2
    ldrh r1, [r4]
    b GetFlags

    GetFlags:
    ldr r2, .AttackTable
    lsl r0, r1, #0x1
    add r0, r0, r1
    lsl r0, r0, #0x2
    add r0, r0, r2
    ldrb r0, [r0, #0x8]

    GetCompare:
    ldr r1, .AICursor
    ldr r1, [r1]
    ldrb r1, [r1, #0x2]
    and r0, r1
    cmp r0, #0x0
    beq SetZero
    mov r0, #0x1
    b Store

    SetZero:
    mov r0, #0x0

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

    Return:
    ldr r1, .AICursor
    ldr r0, [r1]
    add r0, r0, #0x3
    str r0, [r1]
    pop {r4-r5}
    pop {r0}
    bx r0

    .align 2
    .AICursor: .word 0x2039A00
    .AttackTable: .word 0x8250C04
    .Resources: .word 0x2023FF4


    55: CheckIfInverseBattle
    Arguments: None
    This is to be used if you inserted this routine by DoesntKnowHowToPlay. This checks if you set flag 0x23D (the Inverse Battle flag used in that routine. It returns 1 if you've set the flag (and therefore the battle is an Inverse one).
    This is useful because the AI doesn't automatically know if you're in an Inverse Battle, so it won't know automatically that Super Effective and Not Very Effective moves have been switched.

    With this command, you could just do the switching yourself by checking if you're in an Inverse Battle, and if you are then checking for a Not Very Effective move when you mean a Super Effective one and vice versa.

    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    CheckFlag:
    push {r4-r5, lr}
    mov r0, #0x2
    lsl r0, #0x8
    mov r1, #0x3D
    add r0, r1
    bl FlagCheck
    cmp r0, #0x0
    bne Set
    mov r0, #0x0
    b Store

    Set:
    mov r0, #0x1

    Store:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    str r0, [r4]
    b Return

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


    FlagCheck:
    ldr r4, .Flag
    bx r4

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


    56: CheckIfStatsAre
    Arguments: Target (1 byte), Stat 1 ( byte), Stat 2 (1 byte), Comparison (1 byte)
    Compares two stats for the given target. Returns 1 if the given comparison is true.


    Values (Stats):
    Attack = 1
    Defense = 2
    Speed = 3
    Sp. Attack = 4
    Sp. Defense = 5

    Values (Comparison)
    Equal = 0
    NotEqual = 1
    LessThan = 2
    MoreThan = 3
    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    Main:
    push {r4-r5, lr}
    ldr r0, .AICursor
    ldr r0, [r0]
    ldrb r0, [r0, #0x1]
    cmp r0, #0x0
    beq Defender
    cmp r0, #0x1
    beq Attacker
    cmp r0, #0x2
    beq APartner
    cmp r0, #0x3
    beq DPartner

    Attacker:
    ldr r1, .Attacker
    b Stats

    Defender:
    ldr r1, .Defender
    b Stats

    APartner:
    ldr r1, .APartner
    b Stats

    DPartner:
    ldr r1, .DPartner

    Stats:
    /*Puts the stats to compare into r4 and r5*/
    ldr r2, .UserData
    ldrb r1, [r1]
    mov r3, #0x58
    mul r1, r3
    ldr r0, .AICursor
    ldr r0, [r0]
    ldrb r4, [r0, #0x2]
    ldrb r5, [r0, #0x3]
    add r2, r2, r1
    mov r3, #0x2
    mul r4, r3
    mul r5, r3
    add r4, r2, r4
    add r5, r2, r5
    ldrh r4, [r4]
    ldrh r5, [r5]

    Comparison:
    ldr r0, .AICursor
    ldr r0, [r0]
    ldrb r3, [r0, #0x4]
    cmp r3, #0x0
    beq Equal
    cmp r3, #0x1
    beq NotEqual
    cmp r3, #0x2
    beq LessThan
    cmp r3, #0x3
    beq MoreThan

    Equal:
    cmp r4, r5
    beq True
    b False

    NotEqual:
    cmp r4, r5
    bne True
    b False

    LessThan:
    cmp r4, r5
    blt True
    b False

    MoreThan:
    cmp r4, r5
    bgt True
    b False

    True:
    mov r2, #0x1
    b Store

    False:
    mov r2, #0x0
    b Store

    Store:
    ldr r4, .Resources
    ldr r4, [r4]
    add r4, r4, #0x14
    ldr r4, [r4]
    add r4, r4, #0x8
    str r2, [r4]

    Return:
    ldr r5, .AICursor
    ldr r4, [r5]
    add r4, r4, #0x5
    str r4, [r5]
    pop {r4-r5}
    pop {r0}
    bx r0

    .align 2
    .UserData: .word 0x02023BE4
    .Resources: .word 0x02023FF4

    .AICursor: .word 0x02039A00
    .Attacker: .word 0x02023D6B
    .Defender: .word 0x02023D6C
    .APartner: .word 0x02023D6D
    .DPartner: .word 0x02023D6E


    57: GetDataAtRAM
    Arguments: 1 byte, 1 word
    The first argument is the size of the data you want.

    0 = byte
    1 = half word
    2 = word

    The second argument is the RAM Address you want to get that data from.

    Spoiler:
    .text
    .align 2
    .thumb
    .thumb_func

    GetAddress:
    push {r4-r5, lr}
    ldr r5, .AICursor
    ldr r0, [r5]
    ldrb r1, [r0, #0x2]
    ldrb r2, [r0, #0x3]
    lsl r2, r2, #0x8
    orr r1, r2
    ldrb r2, [r0, #0x4]
    lsl r2, r2, #0x10
    orr r1, r2
    ldrb r2, [r0, #0x5]
    lsl r2, r2, #0x18
    orr r1, r2

    GetArgument:
    ldrb r3, [r0, #0x1]
    cmp r3, #0x0
    beq Byte
    cmp r3, #0x1
    beq HWord
    cmp r3, #0x2
    beq Word

    Byte:
    ldrb r4, [r1]
    b Store

    HWord:
    ldrh r4, [r1]
    b Store

    Word:
    ldr r4, [r1]

    Store:
    ldr r3, .Resources
    ldr r3, [r3]
    add r3, r3, #0x14
    ldr r3, [r3]
    add r3, r3, #0x8
    str r4, [r3]

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

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




    ASM Macro Files

    I have one more present for the masses at Pokecommunity. I wrote ASM macros for all the commands, and for a few other things like Types. Now you can use them to easily and quickly write your own scripts without the need for making a whole editor!

    Spoiler:

    Default Commands
    (This works for the AI Commands as they originally were, no rewrites)
    Spoiler:

    .text
    .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 JumpIfStatus1NotEqualTarget 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 Target Address
    .byte 0x28
    .byte \Target
    .word \Address
    .endm

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

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

    .macro GetMoveID
    .byte 0x2D
    .endm

    .macro GetMoveScriptID
    .byte 0x2E
    .endm

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

    .macro SimulateDamageBonusFourTimes
    .byte 0x30
    .endm

    .macro JumpIfDamageBonusEquals Value Address
    .byte 0x31
    .byte \Value
    .word \Address
    .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

    .macro GetWeather
    .byte 0x36
    .endm

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

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

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

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

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

    .macro JumpIfStatNotEqual 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
    .hword \Script
    .word \Address
    .endm

    .macro JumpIfMoveScriptNotInMoveSet Target Script Address
    .byte 0x42
    .byte \Target
    .hword \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 JumpRandomUnknown Address
    .byte 0x46
    .word \Address
    .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 GetDP08Item Target
    .byte 0x4D
    .byte \Target
    .endm

    .macro GetKindOfMove
    .byte 0x4E
    .endm

    .macro GetBasePower
    .byte 0x4F
    .endm

    .macro GetMoveRange
    .byte 0x50
    .endm

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

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

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

    .macro ReturnToBattle
    .byte 0x5A
    .endm

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

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

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


    Modified Commands
    (Only two commands changed their syntax when I modified them, so here's the new macros for those two)
    Spoiler:

    .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


    New Commands
    (These are the macros for the new commands I included above)
    Spoiler:

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

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

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


    Types
    Spoiler:

    .text
    .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 ???, 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


    Misc. Arguments
    Spoiler:

    .text

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

    .equ ASide, 0x2
    .equ DSide, 0x3

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

    .equ Partner, 0x1

    .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, 0xA0
    .equ Weak, 0x50
    .equ Neutral, 0x28
    .equ Resist, 0x14
    .equ DoubleResist, 0xA
    .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 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



    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.
    __________________
    "The human sacrificed himself, to save the Pokemon. I pitted them against each other, but not until they set aside their differences did I see the true power they all share deep inside. I see now that the circumstances of one's birth are irrelevant; it is what you do with the gift of life that determines who you are." -Mewtwo
    Reply With Quote

    Relevant Advertising!

      #2    
    Old November 26th, 2017 (10:26 AM).
    ghoulslash's Avatar
    ghoulslash ghoulslash is offline
       
      Join Date: Mar 2016
      Gender: Male
      Posts: 89
      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.
      Reply With Quote
        #3    
      Old November 26th, 2017 (2:56 PM). Edited November 26th, 2017 by AkameTheBulbasaur.
      AkameTheBulbasaur's Avatar
      AkameTheBulbasaur AkameTheBulbasaur is offline
      Akame Marukawa of Iyotono
         
        Join Date: May 2013
        Location: A place :D
        Age: 20
        Gender: Male
        Nature: Gentle
        Posts: 334
        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.
        __________________
        "The human sacrificed himself, to save the Pokemon. I pitted them against each other, but not until they set aside their differences did I see the true power they all share deep inside. I see now that the circumstances of one's birth are irrelevant; it is what you do with the gift of life that determines who you are." -Mewtwo
        Reply With Quote
          #4    
        Old November 27th, 2017 (8:45 PM).
        ghoulslash's Avatar
        ghoulslash ghoulslash is offline
           
          Join Date: Mar 2016
          Gender: Male
          Posts: 89
          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.
          Reply With Quote
            #5    
          Old November 28th, 2017 (6:00 PM). Edited November 28th, 2017 by AkameTheBulbasaur.
          AkameTheBulbasaur's Avatar
          AkameTheBulbasaur AkameTheBulbasaur is offline
          Akame Marukawa of Iyotono
             
            Join Date: May 2013
            Location: A place :D
            Age: 20
            Gender: Male
            Nature: Gentle
            Posts: 334
            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.
            __________________
            "The human sacrificed himself, to save the Pokemon. I pitted them against each other, but not until they set aside their differences did I see the true power they all share deep inside. I see now that the circumstances of one's birth are irrelevant; it is what you do with the gift of life that determines who you are." -Mewtwo
            Reply With Quote
              #6    
            Old December 7th, 2017 (10:21 PM). Edited January 12th, 2018 by Skeli Unbound.
            Skeli Unbound's Avatar
            Skeli Unbound Skeli Unbound is offline
            Lord of the Rings
               
              Join Date: Apr 2014
              Location: Canada
              Age: 19
              Gender: Male
              Nature: Adamant
              Posts: 138
              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
              __________________
              Pokemon Unbound

              Reply With Quote
                #7    
              Old December 8th, 2017 (7:53 AM).
              DizzyEgg's Avatar
              DizzyEgg DizzyEgg is offline
                 
                Join Date: Feb 2014
                Location: Poland
                Age: 20
                Gender: Male
                Nature: Quiet
                Posts: 716
                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.
                __________________
                Support Pokeruby!

                My works:
                Battle Engine Upgrade
                Pokemon Expansion
                Items/TMs/Tutors Expansion
                Various Features(Evo Methods, BW Repel, Levels Above 100, Trainers with EV, Nature Colored Stats)

                Emerald Features:
                Form Changes (KDS)
                Reply With Quote
                  #8    
                Old December 8th, 2017 (8:29 AM).
                Skeli Unbound's Avatar
                Skeli Unbound Skeli Unbound is offline
                Lord of the Rings
                   
                  Join Date: Apr 2014
                  Location: Canada
                  Age: 19
                  Gender: Male
                  Nature: Adamant
                  Posts: 138
                  Quote:
                  Originally Posted by DizzyEgg View Post
                  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.
                  __________________
                  Pokemon Unbound

                  Reply With Quote
                    #9    
                  Old December 26th, 2017 (8:49 PM). Edited January 12th, 2018 by AkameTheBulbasaur.
                  AkameTheBulbasaur's Avatar
                  AkameTheBulbasaur AkameTheBulbasaur is offline
                  Akame Marukawa of Iyotono
                     
                    Join Date: May 2013
                    Location: A place :D
                    Age: 20
                    Gender: Male
                    Nature: Gentle
                    Posts: 334
                    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.
                    __________________
                    "The human sacrificed himself, to save the Pokemon. I pitted them against each other, but not until they set aside their differences did I see the true power they all share deep inside. I see now that the circumstances of one's birth are irrelevant; it is what you do with the gift of life that determines who you are." -Mewtwo
                    Reply With Quote
                      #10    
                    Old January 8th, 2018 (6:46 PM). Edited January 10th, 2018 by Skeli Unbound.
                    Skeli Unbound's Avatar
                    Skeli Unbound Skeli Unbound is offline
                    Lord of the Rings
                       
                      Join Date: Apr 2014
                      Location: Canada
                      Age: 19
                      Gender: Male
                      Nature: Adamant
                      Posts: 138
                      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}
                      __________________
                      Pokemon Unbound

                      Reply With Quote
                        #11    
                      Old January 12th, 2018 (11:17 AM). Edited 3 Weeks Ago by AkameTheBulbasaur.
                      AkameTheBulbasaur's Avatar
                      AkameTheBulbasaur AkameTheBulbasaur is offline
                      Akame Marukawa of Iyotono
                         
                        Join Date: May 2013
                        Location: A place :D
                        Age: 20
                        Gender: Male
                        Nature: Gentle
                        Posts: 334
                        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, .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 0x80C6E7D
                        .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 SetDefender

                        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 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, .ReturnS
                        bx r0


                        .align 2
                        .ReturnD: .word 0x080C6E85
                        .ReturnS: .word 0x080C6E7D
                        .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

                        /*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*/
                        /*6EBBD8*/
                        .text
                        .align 2
                        .thumb
                        .thumb_func

                        Main:
                        cmp r2, #0x0
                        beq Player
                        cmp r2, #0x2
                        beq Player
                        b AI

                        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, #0x8 /*Double*/
                        and r0, r1
                        cmp r0, #0x0
                        beq Player

                        /*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 /*Change this to the score threshold*/
                        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.
                        __________________
                        "The human sacrificed himself, to save the Pokemon. I pitted them against each other, but not until they set aside their differences did I see the true power they all share deep inside. I see now that the circumstances of one's birth are irrelevant; it is what you do with the gift of life that determines who you are." -Mewtwo
                        Reply With Quote
                        Reply

                        Quick Reply

                        Join the conversation!

                        Create an account to post a reply in this thread, participate in other discussions, and more!

                        Create a PokéCommunity Account

                        Sponsored Links
                        Thread Tools

                        Posting Rules
                        You may not post new threads
                        You may not post replies
                        You may not post attachments
                        You may not edit your posts

                        BB code is On
                        Smilies are On
                        [IMG] code is On
                        HTML code is Off

                        Forum Jump


                        All times are GMT -8. The time now is 1:06 PM.