Jambo51
Glory To Arstotzka
- 736
- Posts
- 15
- Years
- Seen Jan 28, 2018
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.
Basic Damaging Attack:
Status Inflicting Moves:
Damage AND Status Inflicting Moves:
Attacks That Don't Miss and Special Status Flags:
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:
Octal (Base 8) Formatting:
Decimal (Base 10) Formatting:
Hexadecimal (Base 16) Formatting:
Thornal (Base 32) Formatting:
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.
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".
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!
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.
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...
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.
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
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.
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.
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.
This prints "Pokémon used Attack!" to the screen. Does nothing else.
As the name implies, reduces PP of used attack. Note that the effect of Pressure is calculated here.
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.
Plays the animation of the attack. Simple as that.
Waits for the animation started above to complete.
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:
So what this means is "Apply the damage noise/animation to the target".
Simply pauses script execution and waits for the previous command to finish. Only works for certain commands.
Physically updates the data behind to have the new HP value.
The 0x0 is the bank again.
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.
Displays a message saying the attack was a critical hit if the attack was a critical hit.
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.
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.
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!
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
Code:
#dynamic 0x720000
Code:
#freespacebyte 0xFF
Code:
#include moves.bsh
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
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
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
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
Code:
ppreduce
Code:
damagecalculation
Code:
attackanimation
Code:
waitanimation
Code:
cmd5c 0x0
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
0x1 - User
So what this means is "Apply the damage noise/animation to the target".
Code:
waitstate
Code:
datahpupdate 0x0
The 0x0 is the bank again.
Code:
graphicalhpupdate 0x0
The 0x0 is, once again, the bank.
Code:
critmessage
Code:
waitmessage 0x40
The 0x40 equates to about 1 second in real time.
Code:
resultmessage
Messages can be like:
"It had no effect!"
"It wasn't very effective..."
and so on.
Code:
goto 0x1D6947
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!
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.
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:
Note, I need to check these, but I do know that sleep is right.
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.
Code:
#dynamic 0x720000
#freespacebyte 0xFF
#include moves.bsh
#org @start
attackcanceler
accuracycheck 0x1D695E 0x0
setbyte 0x02023E85 0xX
attackstring
ppreduce
attackanimation
waitanimation
seteffectwithchancetarget
goto 0x1D694E
Code:
setbyte 0x02023E85 0xX
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
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
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!
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!
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
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.
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:
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.
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.
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:
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
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
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
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 is a list of the known Special Status Flags, quoted verbatim from JPAN from his thread on here about Battle Scripts:
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)[/QUOTE]
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!
Whoa! Whoa! Whoa! "damagemultiplier"? I thought we had to refer to RAM addresses by their actual numerical value!Code:#org @start jumpifspecialstatusflag 0x0 0x40000 0x1 @later setbyte damagemultiplier 0x2 goto @later #org @later //script continues
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? :P
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:
The parameters are:Code:jumpifstat 0x0 0x0 0x1 0x6 @later
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:
Wait, what's this madness? TRUE? A word that is NOT a keyword as a parameter?Code:playstatchangeanimation 0x0 0x2 0x0 statbuffchange 0x1 true @alt jumpifbyte 0x0 statchange 0x2 @alt printfromtable 0x83FE57C waitmessage 0x40 #org @alt //rest of code
Well, let's break down the new commands and all will be explained!
The name is pretty self explanatory, to be honest, so I won't bother explaining that.Code:playstatchangeanimation 0x0 0x2 0x0
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
Well, this is the command which physically executes the stat change, if it can.Code:statbuffchange 0x0 TRUE 0x0
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.
Last edited: