View Single Post
  #1    
Old March 25th, 2013 (2:14 AM). Edited May 26th, 2013 by Jambo51.
Jambo51's Avatar
Jambo51 Jambo51 is offline
Glory To Arstotzka
     
    Join Date: Jun 2009
    Gender: Male
    Nature: Quiet
    Posts: 732
    Hello there, as you may have noticed recently, I released a Battle Script Editor called Battle Script Pro. The problem is, apart from myself, no-one really knows how to use it, or even what most of the commands do!

    Here, I will walk through the basics of making basic attacks, and how to get basic added effects working.

    Before we get started, I should make this clear. Battle Script Pro will support, and parse, any number formatted in Binary, Octal, Decimal, Hexadecimal and Thornal.
    Spoiler:
    Binary (Base 2) Formatting:
    Code:
    [XXXXXXXX]
    {XXXXXXXX}
    0bXXXXXXXX
    XXXXXXXXb
    Octal (Base 8) Formatting:
    Code:
    0oXX
    Decimal (Base 10) Formatting:
    Code:
    XX
    Hexadecimal (Base 16) Formatting:
    Code:
    0xXX
    $XX
    &HXX
    XXh
    Thornal (Base 32) Formatting:
    Code:
    0tXX
    XXt
    Where the XX's stand for any given digit valid in the number set you are using. The maximum length of any value you put in is 32 bits long (0xFFFFFFFF), so don't use a value higher than that.

    Code:
    0b1000001000 = 0o1010 = 520 = 0x208 = 0tG8
    For reference on using thornal and octal, if you're unsure and are curious, Wikipedia has some good articles about them. Thornal in BSP uses the "Extended Hex Character Set".


    Basic Damaging Attack:
    Spoiler:
    So, with no further ado, I'll stick a basic script here!

    Code:
    #dynamic 0x720000
    #freespacebyte 0xFF
    
    #include moves.bsh
    
    #org @start
    attackcanceler
    accuracycheck 0x1D695E 0x0
    attackstring
    ppreduce
    calculatedamage
    attackanimation
    waitanimation
    cmd5c 0x0
    waitstate
    datahpupdate 0x0
    graphicalhpupdate 0x0
    critmessage
    waitmessage 0x40
    resultmessage
    waitmessage 0x40
    goto 0x1D6947
    Now, that looks really nasty, so lets break it down line by line and explain why each line is in the script and why (in some cases) it is where it is.

    Code:
    #dynamic 0x720000
    Just like XSE, you can define a dynamic position to search for free space from. The tool will search for enough free space for each script fragment (explained later) and insert it into the free space. Defines the start location for the search to 0x720000. Speaking of free space...

    Code:
    #freespacebyte 0xFF
    This defines what is regarded as free space by the tool. If you skip this definition, the tool assumes the Free Space Byte is 0xFF.

    Code:
    #include moves.bsh
    Includes the header file moves.bsh. This header file is full of definitions for the moves, which lets you refer to a move by "MOVE_POUND" instead of having to remember its index number.

    The header files which come bundled with the tool are:
    moves.bsh
    pokemon.bsh
    abilities.bsh
    items.bsh

    Note: there is a special header file called commands.bsh which I will get to later.
    Note 2: You can define custom header files if you so desire.
    Note 3: std.bsh is included (that is, the definitions in it are loaded into memory) by default, you don't need to #include it

    Code:
    #org @start
    This line contains 2 important things. Firstly, the #org, which defines that this is the start of a script fragment. What is a script fragment, you ask? Well, it's simply the part of a script between the opening command and the "ending" command.

    A whole script is usually made up of several script fragments which occur due to conditional branches.

    Secondly, @start. This, like XSE, is a dynamic pointer, in that it allows you to dynamically assign the offsets to script fragments and branches. Certainly makes writing scripts easier, IMO.

    Code:
    attackcanceler
    The name given to this command is very unfair as it really underplays what it actually does. Long story short, it checks for attack canceling statuses (like paralysis, being frozen, being asleep and such) and runs the calculations which cancel the attack. It silently branches to a completely separate script if needed.

    It ALSO checks things like Protect/Detect, Turns of sleep (waking the user up if there are 0 turns of sleep left) and runs a chanced calculation on whether or not to defrost a frozen user.

    As you can see, it does a LOT. Due to its nature, you MUST only include this command once in the script's lifetime, as by its nature, if you have it twice, it becomes twice as likely to be paralysed, in love etc.

    Code:
    accuracycheck 0x1D695E 0x0
    This command does what it says on the tin. If you want your attack to never miss, you must NOT put this command in. There is a completely alternative method for moves which never miss (except for missing when the target is in the semi-invulnerable states).

    0x1D695E is the address of the alternate script which gets branched to IF the attack misses.

    0x0 is an unknown half-word. It may control the likelihood of missing or something, I dunno.

    Code:
    attackstring
    This prints "Pokémon used Attack!" to the screen. Does nothing else.

    Code:
    ppreduce
    As the name implies, reduces PP of used attack. Note that the effect of Pressure is calculated here.

    Code:
    damagecalculation
    This is an alias/super command. What that means is that on compilation, it compiles out to several commands which have the effect named. As the name suggests, it calculates the damage the attack will do to the opponent, including type effectiveness, special/physical effects, base damage (excluding only a handful of attacks), and stat increases/decreases.

    Code:
    attackanimation
    Plays the animation of the attack. Simple as that.

    Code:
    waitanimation
    Waits for the animation started above to complete.

    Code:
    cmd5c 0x0
    Not entirely sure what this command does. I think it applies the effectiveness noise plus the little flash to indicate that damage has happened.

    The 0x0 signifies which banked Pokémon to apply this to. (Wait, what? What does "Banked Pokémon mean?)

    In battle scripting there are several banks, used to refer to the battling Pokémon. That is, there are a handful of memory locations which contain references to the battling Pokémon. The banks (as used here) refer to those memory locations. Convoluted, eh?

    Anyway, to use them, know that:
    Spoiler:
    0x0 - Target
    0x1 - User



    So what this means is "Apply the damage noise/animation to the target".

    Code:
    waitstate
    Simply pauses script execution and waits for the previous command to finish. Only works for certain commands.

    Code:
    datahpupdate 0x0
    Physically updates the data behind to have the new HP value.
    The 0x0 is the bank again.

    Code:
    graphicalhpupdate 0x0
    Updates the HP bar on screen, scrolling it down towards the value stored in the data. Note that this MUST go AFTER the datahpupdate command (or equivalent ASM), otherwise, nothing will happen.
    The 0x0 is, once again, the bank.

    Code:
    critmessage
    Displays a message saying the attack was a critical hit if the attack was a critical hit.

    Code:
    waitmessage 0x40
    Without this, the message would flash on screen and then the script would continue. This simply delays the script's execution so that the message can be read.

    The 0x40 equates to about 1 second in real time.

    Code:
    resultmessage
    This picks the relevant message to display and displays it.
    Messages can be like:
    "It had no effect!"
    "It wasn't very effective..."
    and so on.

    Code:
    goto 0x1D6947
    Now, you might be wondering why I didn't end the script with an "end" command like most scripting languages would. There is a very simple reason for this, and that is, everything you need to handle after this point can be handled by the "regular" script.

    In fact, this whole script is pointless (as the "regular" script has all of these commands and more), but for demonstration purposes, it's a good start.

    Anyway, the goto does what its name implies, it simply jumps, no questions asked, to the given address.

    0x1D6947 is (in FireRed) where you would want to jump to in order to have an additional effect executed. Note that, if you supply a hardcoded address like this, the tool will add 0x08000000 to it IFF (if and only if) the address you supply is LESS than 0x02000000.

    This means that ROM addresses can be referenced by the 6 digit location and do not require the 8 in front of them. However, if you include the 8 it still works. It also means that RAM addresses are unaffected by this logic, so if you refer to 0x02023D4A, then it compiles it verbatim.

    So, I've walked you through the basic damage structure, but we haven't done anything particularly interesting yet!



    Status Inflicting Moves:
    Spoiler:
    This is actually relatively simple compared to doing damage, especially as I've already explained a lot of the commands above!

    Code:
    #dynamic 0x720000
    #freespacebyte 0xFF
    
    #include moves.bsh
    
    #org @start
    attackcanceler
    accuracycheck 0x1D695E 0x0
    setbyte 0x02023E85 0xX
    attackstring
    ppreduce
    attackanimation
    waitanimation
    seteffectwithchancetarget
    goto 0x1D694E
    So, what's changed? Well, obviously, Damage calculation has been removed, as the move does no direct damage, as have the HP updating commands. However, there are two brand new commands here, as well as the branch having changed.

    Code:
    setbyte 0x02023E85 0xX
    This command is equivalent to strb in ASM. It literally stores the byte (0xX) into memory location 0x02023E85. Needless to say, it will store any given byte into any given RAM Address, not just the ones I used.

    0x02023E85 is the effect chooser in FireRed. The byte stored in here is used later to determine the move's primary effect.

    The 0xX is what you change! You put here the relevant byte for inflicting a status on the target. Possible bytes are:
    Spoiler:
    0x1 - Sleep
    0x2 - Poison
    0x3 - Burn
    0x4 - Freeze
    0x5 - Paralysis
    0x6 - Badly Poison


    Note, I need to check these, but I do know that sleep is right.


    Code:
    seteffectwithchancetarget
    Catchy name for a command, huh? This, as the name implies, runs a random calculation against the likelihood stored in the move's data for the effect to happen, and if that check passes, checks if the target can get the status, and if so, applies the status to the target. Note, however, that it is better practice to explicitly check earlier in the script for whether it can get the status or not!

    It reads 0x02023E85 to get which effect to execute, hence our storing of a value to there earlier in the script.

    This command takes care of calling the scripts necessary to display the symbol on the HP bars, and putting text on the screen, so don't concern yourself with that!

    The location of the goto has changed once again, this is simply because we are skipping a few commands from the "regular" script as they are unnecessary.



    Damage AND Status Inflicting Moves:
    Spoiler:
    This is quite simple, as it's just the first script plus two commands!

    Code:
    #dynamic 0x720000
    #freespacebyte 0xFF
    
    #include moves.bsh
    
    #org @start
    setbyte 0x02023E85 0xX
    attackcanceler
    accuracycheck 0x1D695E 0x0
    attackstring
    ppreduce
    calculatedamage
    attackanimation
    waitanimation
    cmd5c 0x0
    waitstate
    datahpupdate 0x0
    graphicalhpupdate 0x0
    critmessage
    waitmessage 0x40
    resultmessage
    waitmessage 0x40
    seteffectwithchancetarget
    goto 0x1D694E
    Incredibly easy, eh?

    Note that you can replace seteffectwithchancetarget with seteffecttarget and seteffectuser. The commands have the same effect (giving a Pokémon a status), it simply changes whether there's random chance involved, and who the status will actually effect.

    OK, so far, I've equipped you with the very basics of battle scripting. There's still a long way to go, though, as I've only covered about 20 commands, when there are nearly 250!


    Attacks That Don't Miss and Special Status Flags:
    Spoiler:
    This is quite easy, as you're simply replacing a single command with another, less obvious, command. I'll use the basic attack as an example.

    Code:
    #dynamic 0x720000
    #freespacebyte 0xFF
    
    #include moves.bsh
    
    #org @start
    attackcanceler
    jumpifspecialstatusflag 0x0 0x400C0 0x0 0x1D695E
    attackstring
    ppreduce
    calculatedamage
    attackanimation
    waitanimation
    cmd5c 0x0
    waitstate
    datahpupdate 0x0
    graphicalhpupdate 0x0
    critmessage
    waitmessage 0x40
    resultmessage
    waitmessage 0x40
    goto 0x1D6947
    You probably noticed, I directly replaced accuracycheck with something called jumpifspecialstatusflag. Confused? Can't say I blame you if you are! So, some background information is probably required here.

    A Special Status bit is simply a bit in memory assigned to a given Pokémon which indicates that the Pokémon is currently undergoing some form of special status. This includes things like the semi-invulnerable bits (which I am checking here). A full list of the known bits is quoted at the end of this section.

    Now that we know that, we understand why we are checking the special status, yeah?

    Now, to explain the numbers used.

    The first 0x0 is the bank, once again.

    The 0x400C0 isn't particularly obvious, unless you understand bitwise checks. In 32-bit binary, that number is:
    Code:
    00000000000001000000000011000000
    What we are actually doing with this command is checking if any of these bits I specified are set. If they ARE set, then we do something. That something is defined by the next parameter.
    This command ANDs the special status bits for the Pokémon with the value you supply, hence why we have to think in Binary!

    The second 0x0 is a byte telling the command what to do after it does the bitwise operation.
    Spoiler:
    0x0 - Branch if ANY set
    0x1 - Branch if none set


    The final parameter is where to branch to if the command returns true.

    By removing the accuracycheck command, we have removed any chance of the attack missing (ignoring things like Protect and such). Now, this move would only miss if the target was in the middle phase of Dive, Fly, Bounce or Dig.

    If you wanted to leave one of those out, say you wanted to have the move still hit Pokémon in the middle phase of Dive, you would simply zero the relevant bit in the special status check.

    Code:
    jumpifspecialstatusflag 0x0 0xC0 0x0 0x1D695E
    This above would hit a Pokémon in the middle phase of dive, but miss those in the middle phase of Fly, Bounce and Dig, understand?

    This is a list of the known Special Status Flags, quoted verbatim from JPAN from his thread on here about Battle Scripts:
    Quote:
    Special (status) flags
    Code:
    0x00000001 --> ??
    0x00000002 --> ??
    0x00000004 --> leech seed affected
    0x00000010 --> always hit 
    0x00000020 --> perish song affected 
    0x00000040 --> on air (fly, bounce) 
    0x00000080 --> underground (dig)
    0x00000100 --> minimized
    0x00000200 --> charged up
    0x00000400 --> rooted 
    0x00000800 --> sleep this turn(yawn)
    0x00001000 --> sleep in one turn(yawn)
    0x00002000 --> cannot use attacks from target(imprision)
    0x00004000 --> foe loses all pp on last used atk if user faints (grudge)
    0x00010000 --> elec attacks cause half damage (mud sport)
    0x00020000 --> fire attacks cause half damage (water sport)
    0x00040000 --> underwater (dive)



    Scalar Multiplication of Attack Damage and Using Keywords:
    Spoiler:
    We're doubling the attack power of a move if used on a Pokémon which is in the middle phase of Dive!

    Code:
    #org @start
    jumpifspecialstatusflag 0x0 0x40000 0x1 @later
    setbyte damagemultiplier 0x2
    goto @later
    
    #org @later
    //script continues
    Whoa! Whoa! Whoa! "damagemultiplier"? I thought we had to refer to RAM addresses by their actual numerical value!

    Well, thankfully not. I put a bunch of the main ram locations used in Battle Scripting and made them into keywords which refer to the RAM Location. It's much easier than trying to remember the location, wouldn't you agree?

    Ignoring that, the script is fairly self explanatory.

    Note, you can use any scalar multiplier up to 0xFF. You can only use whole numbers here, so no manipulation of damage for more clever purposes.



    Raising/Lowering Stat Buffs:
    Spoiler:
    Spoiler:
    You'll have noticed I have danced around the more common status moves and their effects so far, that is, the effects which simply lower/raise the stat levels.

    Well, here's some background to it to begin with. There are 8 stat buffs for each Pokémon in battle. They are located at (Base Location + 0x18) + (0x58 * battler ID). However, we don't need to refer to them directly, as Battle Scripting has a helpful command which refers to them for us!

    As you may have guessed by now, it follows the same bank structure as many of these such commands. The command is as follows:

    Code:
    jumpifstat 0x0 0x0 0x1 0x6 @later
    The parameters are:

    0x0 - The bank

    0x0 - The second 0x0 is a byte telling the command what to do after it does the comparison

    0x1 - Stat Index
    Spoiler:
    0x0 - HP (Note, this has no actual effect in game)
    0x1 - Attack
    0x2 - Defence
    0x3 - Speed
    0x4 - Special Attack
    0x5 - Special Defence
    0x6 - Accuracy
    0x7 - Evasion


    0x6 - What to compare the requested stat against


    @later - the address to jump to if the command returns true.

    So far, though, all I've told you is how to check the stats, not how to manipulate them. Well, the thing is, they go hand in hand. You don't want to be raising an already maxed stat, or lowering an already minimised stat. Or perhaps you only want it to work if the target/user has a certain stat level? Either way, the stat checks are a good place to start.

    Now, to actually raise or lower stats is relatively simple.

    Here is a script fragment that will actually change a stat:
    Code:
    playstatchangeanimation 0x0 0x2 0x0
    statbuffchange 0x1 true @alt
    jumpifbyte 0x0 statchange 0x2 @alt
    printfromtable 0x83FE57C
    waitmessage 0x40
    
    #org @alt
    //rest of code
    Wait, what's this madness? TRUE? A word that is NOT a keyword as a parameter?

    Well, let's break down the new commands and all will be explained!

    Code:
    playstatchangeanimation 0x0 0x2 0x0
    The name is pretty self explanatory, to be honest, so I won't bother explaining that.

    The first 0x0 is, once again, the bank (see how that crops up all the time?)

    The 0x2 is the colour of the animation. This parameter is treated as a bitfield by the game's engine, and what's more, it works out which ones it should and should not show! That is to say, if you were to pass it a value of 0x6 (which in binary is 0000 0110), it will check each stat you have identified and work out if that part of the animation is at all necessary. If it isn't, it only displays the ones which are necessary.

    Spoiler:
    0x1 - HP
    0x2 - Attack
    0x4 - Defence
    0x8 - Speed
    0x10 - Special Attack
    0x20 - Special Defence
    0x40 - Accuracy
    0x80 - Evasion


    The second 0x0 is a byte defining which direction the animation should go. I am unsure what the least significant bits stand for, but the most significant bit in the lower nibble stands for the direction of the animation.

    Spoiler:
    0000 0XXX - Up
    0000 1XXX - Down


    Code:
    statbuffchange 0x0 TRUE 0x0
    Well, this is the command which physically executes the stat change, if it can.

    The first parameter defines a lot. It defines the target, as well as various other unknown things.
    What you place here can be:
    Spoiler:
    0x0 - Affects target (Opponent, or yourself, depends what the move targets)
    0x80 - Affects user (Always yourself)



    Now, the interesting and unusual one. The ONLY command in the entire engine which takes a word as a parameter. Note that keywords simply stand for numbers, and don't count as words.

    This stands for the possibility for the stat change to fail. There are certain circumstances which can cause a stat change to fail, and this word (TRUE or FALSE) can override that.

    Note that this parameter does NOT exist after compilation, as it gets merged with the previous parameter.

    The 0x0 is meant to be a ROM address to jump to if the stat change fails. However, in my honest opinion, it looks better to check the failure conditions before you ever reach this point, as the stat change animation will already have played by this point.

    Now, the one thing I skipped over was the 0xX in the setbyte command. Well, here's a list of what bytes do what when stored into the stat change ram location.
    Spoiler:
    Raising One Level:
    0x11 - Attack
    0x12 - Defense
    0x13 - Speed
    0x14 - Special Attack
    0x15 - Special Defense
    0x16 - Accuracy
    0x17 - Evasion

    Raising Two Levels:
    0x21 - Attack
    0x22 - Defense
    0x23 - Speed
    0x24 - Special Attack
    0x25 - Special Defense
    0x26 - Accuracy
    0x27 - Evasion

    Lowering One Level:
    0x91 - Attack
    0x92 - Defense
    0x93 - Speed
    0x94 - Special Attack
    0x95 - Special Defense
    0x96 - Accuracy
    0x97 - Evasion

    Lowering Two Levels:
    0xA1 - Attack
    0xA2 - Defense
    0xA3 - Speed
    0xA4 - Special Attack
    0xA5 - Special Defense
    0xA6 - Accuracy
    0xA7 - Evasion


    You should note that, while the game isn't designed to deal with triple stat increases, it is capable of doing so. All that happens is that you don't get an amount sensitive message after the stat change. I know this is the case, as I have such a triple stat increase in one of my moves. I haven't tried anything higher than that, but you certainly can't go any higher than a change of +/- 7, because the highest bit controls whether the stat rises or falls.


    More to follow.
    __________________
    Hey guys, please check out my recreations of the gen 1 and 2 music on my custom engine at my SoundCloud! - Here!
    Reply With Quote