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 1 Week 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: 342
    Acknowledgments
    First and foremost I have to thank Knizz for her previous research on the Trainer AI Scripting system! Because of her hard work, I was able to have a foundation for all the research I've done. By extension, I also have to thank JPAN, because Knizz said she relied heavily on that as well.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    The second has all the rewritten command routines in it.

    The third has all the new command routines in it.

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

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

    The syntax looks like this

    Callasm OFFSET + 1
    Arguments for asm routine


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

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

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

    DoRoutine:
    bx r1

    .align 2
    .AICursor: .word 0x02039A00


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

    The link to view all of them is here.

    The way to use them is to include the relevant macros at the top of the script, and then just type the script using this sort of form:

    Main:
    JumpIfMoveScriptEquals 0x1 CauseSleep
    ReturnToBattle

    CauseSleep:
    JumpIfStatus1Equals Defender 0x000000FF Decrease32
    ReturnToBattle

    Decrease32:
    AddToViabilityScore 0xE0
    ReturnToBattle

    That way, you can just compile this with any sort of THUMB assembler. If you do it this way however, all the pointers in the scripts will not be in pointer form. To fix this, you'll have to go into a hex editor and put in the pointers manually. It's kind of annoying, but I've also created something that can help you with that.

    What you need to do to fix the script is find where the pointers are supposed to be. You'll find that instead of CC BB AA 08, it will look like AA BB 00 00. What this means is that the point is BBAA bytes away from the starting offset.

    Below is a short C code that you can use. Enter in the offset you inserted the script at, and then enter in the value in bytes that you find where the pointer is meant to be (unreverse it because in the hex editor it will be reversed). The program will give you the actual pointer, which you can just put into the script in the hex editor.

    Spoiler:

    #include <stdio.h>
    #include <stdlib.h>
    #include <math.h>
    #include <string.h>

    int
    main()
    {
    unsigned int Start, Offset;
    int Add;
    int Set;

    printf("Enter the starting offset:\n");
    scanf("%x", &Start);

    printf("\n");

    while(Set != 1)
    {
    printf("What's the value to add to that?: \n");
    printf("Write a negative number to quit\n");
    scanf("%x", &Add);

    if(Add < 0)
    {
    Set = 1;
    }
    else
    {
    Offset = Add + Start;
    printf("\n");

    printf("Offset: 0x%x\n", Offset);
    printf("\n");
    }
    }

    printf("\n");
    printf("Goodbye!\n");
    return 0;

    }


    Is it annoying? Yes. But it's better than trying to do the whole entire thing manually.

    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: 90
      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: 342
        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: 90
          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: 342
            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-.
            Skeli-'s Avatar
            Skeli- Skeli- is offline
            Lord of the Rings
               
              Join Date: Apr 2014
              Location: Canada
              Age: 19
              Gender: Male
              Nature: Adamant
              Posts: 161
              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: 719
                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-'s Avatar
                Skeli- Skeli- is offline
                Lord of the Rings
                   
                  Join Date: Apr 2014
                  Location: Canada
                  Age: 19
                  Gender: Male
                  Nature: Adamant
                  Posts: 161
                  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: 342
                    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-.
                    Skeli-'s Avatar
                    Skeli- Skeli- is offline
                    Lord of the Rings
                       
                      Join Date: Apr 2014
                      Location: Canada
                      Age: 19
                      Gender: Male
                      Nature: Adamant
                      Posts: 161
                      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 March 4th, 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: 342
                        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*/
                        /*6F82D4*/
                        .text
                        .align 2
                        .thumb
                        .thumb_func

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

                        PopR0:
                        pop {r0}

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

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

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

                        /*Check If Switching Is Possible*/

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

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

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

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

                        Single:
                        cmp r4, #0x1
                        ble NoSwitch

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

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

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

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

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

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

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

                        SetScore:
                        add r3, r3, #0x1

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

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

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

                        NoSwitch:
                        pop {r0-r4}
                        b Player

                        /*BL Functions*/

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

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

                        Decrypt:
                        ldr r3, .Decrypt
                        bx r3

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


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

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

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

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

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

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

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

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

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

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


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

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

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

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

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

                        Main:
                        cmp r1, #0xA5

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

                        Skip:
                        ldr r3, .Return
                        bx r3

                        Branch:
                        ldr r0, .Return2
                        bx r0

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


                        You'll probably also want to do a similar thing to the above with Mimic.
                        __________________
                        "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
                          #12    
                        Old March 17th, 2018 (10:41 AM).
                        xizqu's Avatar
                        xizqu xizqu is offline
                           
                          Join Date: Feb 2018
                          Location: California
                          Gender: Male
                          Nature: Adamant
                          Posts: 47
                          Hey akame, I'm somewhat new to hacking to I'm trying to understand everything going on here.

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

                          How do I get spikes layer or really anything else to work? How does the ai check before using a move? Also, what are macros used for and what are the arguments you speak of? If this is too much to explain, do you have a thread that I can learn about all this? I'm trying to make a difficult fire red hack so I'd like to utilize your research and work. Thank you!
                          Reply With Quote
                            #13    
                          Old 1 Week Ago (1:15 PM).
                          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: 342
                            Quote:
                            Originally Posted by xizqu View Post
                            Hey akame, I'm somewhat new to hacking to I'm trying to understand everything going on here.
                            !
                            Whoops! Sorry I totally didn't see this until now!

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

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

                            Main:
                            JumpIfMoveScriptEquals 0x1 CauseSleep
                            ReturnToBattle

                            CauseSleep:
                            JumpIfStatus1Equals Defender 0x000000FF Decrease32
                            ReturnToBattle

                            Decrease32:
                            AddToViabilityScore 0xE0
                            ReturnToBattle


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

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

                            Hopefully this is helpful. If you have other questions feel free to PM me.
                            __________________
                            "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
                              #14    
                            Old 2 Days Ago (12:44 AM). Edited 2 Days Ago by jirachiwishmaker.
                            jirachiwishmaker's Avatar
                            jirachiwishmaker jirachiwishmaker is online now
                               
                              Join Date: Jul 2007
                              Location: Forina
                              Gender: Male
                              Nature: Lonely
                              Posts: 79
                              Quote:
                              Originally Posted by AkameTheBulbasaur View Post
                              ATTENSHOON!!!!

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

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

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

                              THEY ARE NOT!

                              Instead, you need to use a free RAM address to store those banks.
                              Do you mind to tell me that are the Attacker Partner and Defender Partner same with the APartner and DPartner? I am very confusing about it.
                              Reply With Quote
                                #15    
                              Old 2 Days Ago (1:27 PM).
                              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: 342
                                Quote:
                                Originally Posted by jirachiwishmaker View Post
                                Do you mind to tell me that are the Attacker Partner and Defender Partner same with the APartner and DPartner? I am very confusing about it.
                                If you mean whether or not APartner and Attacker Partner mean the same thing, then yes they do. APartner is just easier to write.
                                __________________
                                "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
                                  #16    
                                Old 2 Days Ago (3:12 PM). Edited 2 Days Ago by Lance Koijer 2.0.
                                Lance Koijer 2.0 Lance Koijer 2.0 is offline
                                Lance Koijer
                                   
                                  Join Date: Apr 2018
                                  Location: Criscanto Town
                                  Gender: Male
                                  Nature: Adamant
                                  Posts: 14
                                  Quote:
                                  Originally Posted by AkameTheBulbasaur View Post
                                  If you mean whether or not APartner and Attacker Partner mean the same thing, then yes they do. APartner is just easier to write.
                                  Hello Akame! I found this research very interesting and got most of it. However, I am confused on the AI Switching. Does this include player being able to force switch? Same concept is applied. I'm sorry if this has been mentioned on your post already, I might have missed it.

                                  EDIT: Another question, let's say I'm going to create an AI Script based on the commands listed above, in what particular part of the ROM I'm going to insert it? Does it have a table of scripts that will be checked by a routine somewhere in ROM then generates all possible script or it needs a hook?
                                  Reply With Quote
                                    #17    
                                  Old 1 Day Ago (2:48 AM). Edited 1 Day Ago by jirachiwishmaker.
                                  jirachiwishmaker's Avatar
                                  jirachiwishmaker jirachiwishmaker is online now
                                     
                                    Join Date: Jul 2007
                                    Location: Forina
                                    Gender: Male
                                    Nature: Lonely
                                    Posts: 79
                                    Quote:
                                    Originally Posted by AkameTheBulbasaur View Post
                                    If you mean whether or not APartner and Attacker Partner mean the same thing, then yes they do. APartner is just easier to write.
                                    Oh, I see. Are all the routines that use 0x02023D6D and 0x02023D6E or 0x02023D60 and 0x02023D61 as the APartner and DPartner RAM respectively must replace with the free RAM addresses?
                                    Reply With Quote
                                      #18    
                                    Old 1 Day Ago (5:02 PM).
                                    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: 342
                                      Quote:
                                      Originally Posted by jirachiwishmaker View Post
                                      Oh, I see. Are all the routines that use 0x02023D6D and 0x02023D6E or 0x02023D60 and 0x02023D61 as the APartner and DPartner RAM respectively must replace with the free RAM addresses?
                                      Correct. I used 0x02023D60 and 0x02023D61 as my free RAM, but you can pick anything as long as it's free. If I remember correctly, Mr. DollSteak used those two RAM addresses I mentioned previously in something, so if you're using that then you will need to pick new ones. If you're not using anything else that uses those, then those two are fine.
                                      __________________
                                      "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
                                        #19    
                                      Old 1 Day Ago (5:22 PM).
                                      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: 342
                                        Quote:
                                        Originally Posted by Lance Koijer 2.0 View Post
                                        Hello Akame! I found this research very interesting and got most of it. However, I am confused on the AI Switching. Does this include player being able to force switch? Same concept is applied. I'm sorry if this has been mentioned on your post already, I might have missed it.

                                        EDIT: Another question, let's say I'm going to create an AI Script based on the commands listed above, in what particular part of the ROM I'm going to insert it? Does it have a table of scripts that will be checked by a routine somewhere in ROM then generates all possible script or it needs a hook?
                                        I'm assuming what you mean by force switch is setting up a situation where the AI has to switch and will always switch. There are two ways to do that. The easiest way is to use the modified AddToViabilityScore command and write "AddToViabilityScore 0x5E." This will guarantee that the AI will switch (provided it actually can switch that is). This is the way you actually have control over in AI scripts.

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

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

                                        PoisonCheck:
                                        JumpIfStatus1Equals Attacker0x00000080 Switch1
                                        ReturnToBattle

                                        Switch1:
                                        AddToViabilityScore 0x5E
                                        ReturnToBattle

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

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

                                        Personally, I have had the most luck using 7 scripts and no more (so a trainer value of 127), but theoretically you could have up to 16(? I think this is the actual maximum since if I remember right the AI value is a halfword). The table I gave you has 32 entries though, so I don't know.
                                        __________________
                                        "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
                                          #20    
                                        Old 1 Day Ago (5:31 PM).
                                        jirachiwishmaker's Avatar
                                        jirachiwishmaker jirachiwishmaker is online now
                                           
                                          Join Date: Jul 2007
                                          Location: Forina
                                          Gender: Male
                                          Nature: Lonely
                                          Posts: 79
                                          Quote:
                                          Originally Posted by AkameTheBulbasaur View Post
                                          Correct. I used 0x02023D60 and 0x02023D61 as my free RAM, but you can pick anything as long as it's free. If I remember correctly, Mr. DollSteak used those two RAM addresses I mentioned previously in something, so if you're using that then you will need to pick new ones. If you're not using anything else that uses those, then those two are fine.
                                          Okay. Thanks for answering. In the JumpIfDamageBonusEquals AI commands, why Fairy doesn't use the own Fairy move instead of the Metronome?
                                          Reply With Quote
                                            #21    
                                          Old 1 Day Ago (5:38 PM).
                                          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: 342
                                            Quote:
                                            Originally Posted by jirachiwishmaker View Post
                                            Okay. Thanks for answering. In the JumpIfDamageBonusEquals AI commands, why Fairy doesn't use the own Fairy move instead of the Metronome?
                                            Because in my game (where I originally developed the routine) Metronome was a Fairy Type move. If it's not for you you'll want to change that to a move that is Fairy Type for you.
                                            __________________
                                            "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
                                              #22    
                                            Old 1 Day Ago (5:49 PM).
                                            jirachiwishmaker's Avatar
                                            jirachiwishmaker jirachiwishmaker is online now
                                               
                                              Join Date: Jul 2007
                                              Location: Forina
                                              Gender: Male
                                              Nature: Lonely
                                              Posts: 79
                                              Quote:
                                              Originally Posted by AkameTheBulbasaur View Post
                                              Because in my game (where I originally developed the routine) Metronome was a Fairy Type move. If it's not for you you'll want to change that to a move that is Fairy Type for you.
                                              Okay. I get it. Thanks a million.
                                              Reply With Quote
                                                #23    
                                              Old 20 Hours Ago (3:55 AM).
                                              Lance Koijer 2.0 Lance Koijer 2.0 is offline
                                              Lance Koijer
                                                 
                                                Join Date: Apr 2018
                                                Location: Criscanto Town
                                                Gender: Male
                                                Nature: Adamant
                                                Posts: 14
                                                Quote:
                                                Originally Posted by AkameTheBulbasaur View Post
                                                Because in my game (where I originally developed the routine) Metronome was a Fairy Type move. If it's not for you you'll want to change that to a move that is Fairy Type for you.

                                                Wow thanks for the response...

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

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

                                                Thanks man.
                                                Reply With Quote
                                                  #24    
                                                Old 6 Hours Ago (5:57 PM).
                                                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: 342
                                                  Quote:
                                                  Originally Posted by Lance Koijer 2.0 View Post
                                                  Wow thanks for the response...

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

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

                                                  Thanks man.
                                                  The AI Scripts are only activated for the AI, not any manual input (like the player). It is possible for wild battles, but only in certain circumstances. The last two scripts in the table are used for wild Pokemon battles. One is the script that makes the Pokemon run away in Safari battles, and the other I believe is the one that makes Entei/Raikou/Suicune run away. I don't know how they are activated though, so I'm not sure how to make them work for normal wild battles.
                                                  __________________
                                                  "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
                                                    #25    
                                                  Old 5 Hours Ago (6:21 PM).
                                                  Lance Koijer 2.0 Lance Koijer 2.0 is offline
                                                  Lance Koijer
                                                     
                                                    Join Date: Apr 2018
                                                    Location: Criscanto Town
                                                    Gender: Male
                                                    Nature: Adamant
                                                    Posts: 14
                                                    Quote:
                                                    Originally Posted by AkameTheBulbasaur View Post
                                                    The AI Scripts are only activated for the AI, not any manual input (like the player). It is possible for wild battles, but only in certain circumstances. The last two scripts in the table are used for wild Pokemon battles. One is the script that makes the Pokemon run away in Safari battles, and the other I believe is the one that makes Entei/Raikou/Suicune run away. I don't know how they are activated though, so I'm not sure how to make them work for normal wild battles.
                                                    Thanks man. That really is a good finding. Kudos
                                                    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 12:17 AM.