BOS

Last updated:  12/20/98 21:27

armbats.gif (4947 bytes)The majority of the information contained in this section was originally taken from the "BOS Scripting Help File" written by Alakbar, and was originally downloaded from the TAMMO web site.

The BOS file is a text file describing the animation sequences of a unit.  The BOS file is then compiled into a COB file using a utility such as Cobbler, which can be found at the TADD Files/3rd Party Utilities web page (among other places).  Both the BOS and the COB files are found in the scripts directory of your UFO or HPI file.  The COB file is the actual file that is used by Total Annihilation, so you may omit the presence of the BOS file from your final UFO or HPI file if you wish to keep the guts of your unit a secret.

As with everything in Total Annihilation, the best way to learn how something works or how to do something is to use a tool like HPIView, and look at existing units for examples.  I've added the wireframe pictures of the ARM Millenium and the Core Hellfire to remind you of what they look like for reference.

A Few Important Notes

The X-Axis in Total Annihilation is the Left/Right axis.  For linear movements (i.e. "move"), a positive value is "" and a negative value is "".  For angular movements (i.e. "turn"), a positive value is a "" rotation around the X-Axis, and a negative value is a "" rotation.  Note that for angular movements, the value used is typically a "pitch" value that is passed into the appropriate subroutine to indicate the verticle angle at which an enemy unit is attacking.

hellfire.gif (3339 bytes)The Y-Axis in Total Annihilation is the Up/Down axis.  For linear movements (i.e. "move"), a positive value is "up" and a negative value is "down".  (A good example of Y-Axis linear movement is in the raising and lowering of a big gun, such as the Annihilator).   For angular movements (i.e. "turn"), a positive value is a "" rotation around the Y-Axis, and a negative value is a "" rotation.   Note that for angular movements, the value used is typically a "heading" value that is passed into the appropriate subroutine to indicate the "compass direction" at which an enemy unit is attacking.

The Z-Axis in Total Annihilation unit terms is the Front/Back axis, according to whatever direction the unit, or piece of a unit, is facing.  For linear movements (i.e. move) positive value is "forward" and a negative value is "backwards" with respect to whatever the piece is facing.  (A good example of Z-Axis linear movement is in gun barrel recoil).  For angular movements (i.e. "turn"), a positive value is a "" rotation around the Z-Axis, and a negative value is a "" rotation.

Numbers in brackets [] seem to denote linear measurements, both in terms of direction and speed.  Thus, a statement such as "move CannonR to z-axis [-2.4] speed [500];" would indicate that the piece "CannonR" would be moved backwards 2.4 units at a linear speed of 500.  A unit value of [0] (zero) would indicate the "home" location of the object (i.e. wherever it started).

Numbers in angled brackets <> seem to denote angular measurements.  A unit value of <0> (zero) would indicate the "home" orientation of the object along the specified axis.

Very few of the subroutines described in this section must be re-defined for your.   If you do not define a subroutine of your own, a default subroutine with the same name is used in its place (?), or you may use some of the pre-defined routines that can be found in the .h files.

File Format

Firstly let me break down the BOS file into its componets here. For all you C programmers, this is very much like C but in its basic form. This is a general format for the BOS file, including a list of most of the functions that have been found in the BOS files in totala.hpi.  Mainly I will be using the Cavedog's ARM Battleship's (the Millenium) BOS file, and TADD's Core Hellfire's BOS file for examples, but I will try and cover the ones here as best as I can.  I will also try to cover 2 topics here. The building functions and the transport functions.

piece <piece list>;
static-var <static variable list>;
#define <constant> <value>
#include <filename>

// This is a comment line
Create() {}
StartMoving() {}
StopMoving() {}
MotionControl() {}
AimPrimary(heading,pitch) {}  	  // See also stdtank.h 
AimSecondary(heading,pitch) {}
AimTertiary(heading,pitch) {}       
AimFromPrimary(piecenum) {}
AimFromSecondary(piecenum) {}
AimFromTertiary(piecenum) {}        
QueryPrimary(piecenum) {}
QuerySecondary(piecenum) {}  
QueryTertiary(piecenum) {}
FirePrimary() {}		  // See also stdtank.h
FireSecondary() {}
FireTertiary() {}
Activate() {}			  // See also stdscrpt.h
Deactivate() {}
SetMaxReloadTime(time) {}
RestoreAfterDelay() {}
HitByWeapon(anglex, anglez) {}	  // See also Hitweap.h
StartBuilding() {}
StopBuilding() {}
TargetHeading( heading ) {}
QueryNanoPiece(piecenum) {}
QueryLandingPad(piece1, piece2) {}
QueryBuildInfo(piecenum) {}
QueryTransport(piecenum) {}
Demo() {}
BeginTransport(height) {}
EndTransport() {}
SweetSpot(piecenum) {}    
Killed() {}
TargetCleared(which) {}
RockUnit(anglex, anglez) {}	// See also Rockunit.h

// Miscellaneous pre-defined subroutines
Help() {}		        // From help.h
HelpScale( scale ) {}           // From help.h   
SmokeUnit() {}		        // From SmokeUnit.h
InitState() {}		        // From statechg.h
RequestState(requestedstate) {} // From statechg.h
OpenYard() {}			// From yard.h
CloseYard() {}			// From yard.h                         

Most of the syntax within a BOS file follows the same semantic and syntactic conventions as the C programming language, so most of the information here will assume you know about "if" and "while" statements, as well as the use of the semicolons, assignment ("="), equals ("=="), etc.

BOS Functions

The contents of this section originated at the PWD web site's PWD/DCS Cobbler Tutorial web page.

Note:  Distances are in meters and Angles are in degrees.

Statement and Syntax Description
Attach-Unit [Unit] To [Object]; Attach a Unit to selected object
Cache [Object]; Disable texture animation ( default = disabled )
Call-Script [Script]([Parameters]); Call a script
Dont-Cache [Object]; Enable texture animation ( default = disabled )
Dont-Shade [Object]; Disable shading ( default for building = enabled; default for moving units = disabled )
Drop-Unit [Unit]; Drop unit
Emit-Sfx [Type] from [Object]; Emit a SFX ( fire or bubbles ) from a point ( pointbased ) or from a vertex ( vectorbased ).  The different types of effects are defined in the SFXtype.h file.
Explode [Object] type [Type]; Explode an object in a particular manner.
Get [SysVar]; Get a sysvar (system variable) like BUILD_PERCENT_LEFT
Hide [Object]; Hide an object ( default = all objects shown )
Jump [Destination];  
Jump if ([Value] == False) [Destination];  
Move [Object] to [Axis] [Position] Speed [Speed]; Move an object along a selected axis ( ?-axis ) with selected speed
Move [Object] to [Axis] [Position] Now; Move an object immediatly
Rand([Min], [Max]); Randomize a static-var value
Return [Value]; Return to selected value
Set [SysVar] To [Value]; Set a sysvar (system variable) to a value ( ex : SET ARMORED TO TRUE )
Set-Signal-Mask [Signal]; Set an ID ( 0 , 2 , 4 , 8 , etc.. ) for the signal mask. this is how the script can be killed.
Shade [Object]; Enable shading ( default for building = enabled; default for moving units = disabled )
Show [Object]; Show an object ( default = all objects shown )
Signal [Signal]; Emit a signal ( 0 , 2 , 4 , 8 , etc.. ) to kill script with same signal-mask
Sleep [Time]; make a little pause between 2 or more events
Spin [Object] Around [Axis] Speed [Speed] Accelerate [Acceleration]; start spinning a object
Spin [Object] Around [Axis] Speed [Speed]; spin an object directly to max speed
Start-Script [Script]([Parameters]); Start a script
Stop-Spin [Object] Around [Axis] Decelerate [Deceleration]; stop spinning an object with deceleration
Stop-Spin [Object] Around [Axis]; stop spinning an object immediatly
Turn [Object] to [Axis] [Bearing] Speed [Speed]; turn an object
Turn [Object] to [Axis] [Bearing] Now; turn an object immediatly
Wait-For-Move [Object] Along [Axis]; Wait until selected object has finished moving along selected axis
Wait-For-Turn [Object] Around [Axis]; Wait until selected object has finished turning around selected axis

BOS System Variables

See the file exptype.h for the actual definitions of these variables.

Variable Action/Values Description
ACTIVATION Set or Get:   0/1 Used to indicate whether or not the unit is currently activated.  For example, after being attacked, an ARM Solar Collector folds up, deactivates, and does not produce any energy during that time.
ARMORED Set or Get: TRUE/FALSE Used to indicate whether the unit is in a protected (or "armored") state or not.  For example, the ARM Solar Collector, when folded up and deactivated, is armored, and therefor able to withstand more damage before being destroyed.
ATAN Get Only:   ordinary two-parameter atan
BUGGER_OFF Set or Get: (ask other units to clear the area)
BUILD_PERCENT_LEFT Get Only:   0-100 Returns a measure of how far along the unit is in the build process.  Typically used to indicate when the unit is fully constructed and ready to be activated.  A value of zero indicates the unit is built and ready
BUSY Set or Get: (used by misc. special case missions like transport ships)
GROUND_HEIGHT Get Only:    
HEALTH Get Only:    
HYPOT Get Only:   ordinary two-parameter hypot
INBULIDSTANCE Set or Get:  
PIECE_XZ Get Only:    
PIECE_Y Get Only:    
STANDINGFIREORDERS Set or Get:  
STANDINGMOVEORDERS Set or Get:  
UNIT_HEIGHT Get Only:    
UNIT_XZ Get Only:    
UNIT_Y Get Only:    
XZ_ATAN Get Only:   atan of packed x,z coords
XZ_HYPOT Get Only:   hypot of packed x,z coords
YARD_OPEN Set or Get: (change which plots we occupy when building opens and closes)

piece

Anyway, lets get started with the first one, "piece". This one tells TA which objects will be affected by the script. Such as "base" for the sweetspot() or "wake" for the bubble animations for ships and of course "beam" for the nano sprays. A general rule of thumb is any object that is used to aim with, emits a special effect (like smoke, bubbles, vtol flame, or nano spray), or creates that flash effect from cannon barrels gets put in here. The format for this is:

piece base, flare, wake1, wake2, turret;

Dont forget the semicolon. Its a C thing that symbolized end of statement.  As you can see the names after "piece" are objects from the 3DO file.

static-var

This item is where you would put variables that change in the BOS file. Say you wanted to alter fire between 3 barrels on a turret like how the battleship does, you would put that variable name here. That way it gets initialized and tells TA that this variable is going to store variables. Let me run through an example of the armbats.bos file for its primary turret.

static_var next_barrel1;

FirePrimary() 
{ 
    if (next_barrel1==1) .
    { 
        ..... 
    } 
    if (next_barrel1==2) 
    { 
        ..... 
    }
    if (next_barrel1==3) 
    { 
        ..... 
    } 
    next_barrel1 = next_barrel1 + 1; 
    if (next_barrel1==4) 
    { 
        nextbarrel1=1; 
    }
}                                              

In the previous example, the next_barrel1 static variable is used as a counter to determine which of the 3 gun barrels is the next gun to be fired by the battleship.

static-var only holds variables. Thats all this does. TA can distinguish which variable is assigned to which unit so dont worry about having 30 battleships in the game and it being confused as to which turret is being assigned a variable and stuff.

#define

This confusing little thing is what you would use to define signals and animation variables. Yea confusing isnt it. Heres an example, again of the armbats.bos file:

#define SIG_AIM1    2
#define SIG_AIM2    4 
#define SIG_WAKE    8 
#define SMOKEPIECE1 base                                              

For C programmers, you'll recognise that #define works just the way you expect it to.  For those who do not know C, #define simply declares constant values that cannot change.  Actually, it's not really like a variable...what it actually means to the COB compiler is "whereever you see X, pretend it's Y instead".  For example, in the above code, whenever the COB compiler sees the variable SIG_AIM1, it replaces it with a value of 2.  Some #define constants have special meaning within a BOS file, as you will soon learn.

Looking at the armbats.bot file, this is going to be confusing so I will start with the first 3 items above. SIG is a signal which uses a value  to seperate them so they are independant, like how these 3 are.  What a signal does is stop a function from doing an action so that it can be restarted or stopped. The reason this is done is because say a battleship is aiming its turrets at a target, if  the SIG isnt there then the turrets wont be able to re-aim at another target until the first target is dead. I believe this is what it does. I havent tested a unit without them so I am uncertain as to if this is true.  Also the numbers next to them are what make the signals independant to one another. So say if SIG_AIM1 and SIG_AIM2 were both 2, then when the signal was sent to SIG_AIM1 to stop aiming, SIG_AIM2 would also be halted, even if it wasnt finished aiming (again I think this is what happens on overlapping signal numbers).

"SMOKEPIECE1 base" means that the object name "base" is where the smoke will eminate from when the unit is badly damaged. You can have more pieces smoke by telling it like say you want the turret to smoke also. It would be "#define SMOKEPIECE1 base" then below that "#define SMOKEPIECE2 turret1".

Another #define item is ANIM_VARIABLE TRUE. This tells TA that within the BOS file there are statements dealing with how the item is deactived and activated. Like how the Brawler tucks in its wings when it lands or how the buildings open and close themselves up for building stuff. Again these are just signals that allow a process to stop and be restarted.

Note that there are no semicolons used with #define statements.

#include

Basicly this just includes other scripts that are to go along with the regular bos file.  The cobbler has a hard time, I think, in using path names. My suggestion is to change them to just a simple file name and keep the BOS file, all the .h files and the Cobbler in the same directory. The format for this is:

#include "SFXtype.h"
#include "smokeunit.h"
#include "exptype.h"

Thats all the #include does is include files when the Cobbler compiles the BOS script.  Also notice that the file names are surrounded by a " and that there is no ; at the end of these either. They are called using the statement "start-script <function-name>();" like, for example, the function inside smokeunit.h would be "start-script SmokeUnit();".

Create()

Create() is a function and what it does is tell TA what to do when the unit is created. Any variable names that were used in static-var get initialized in here and any animations like the radar on top of the carrier and aircraft plants get placed in here as well. Its also a good idea to put the "start-script Smokeunit();" call in here as well so its ready to go when the unit is damaged, even while it was being built.

Create()
{
    hide flare1a;
    hide flare1b;
    hide flare1c;
    hide flare2a;
    hide flare2b;
    hide flare2c;
    next_barrel1=1;  
    next_barrel2=1;
    start-script SmokeUnit();
}                                             

As you can see the armbats.bos file here hides all the flares that are shown when the cannons are fired and the next_barrelX variables are initialized in here as well.  Every unit must have this function defined for it, even if there isnt anything done to the unit like the dragon teeth. It still needs this part.

Create() 
{    
    padflip = 0; 
    start-script SmokeUnit();   
    while(get BUILD_PERCENT_LEFT) 
    {
        sleep 1000;
    }    
    spin radar around y-axis speed <60>;
}                                              

As another example, look at the Create() function above.  This is the Create() function for the armcarry.bos file. Again the variable padflip is initialized, the "start-script SmokeUnit();" is called and below that a "while" condition is called. This "while" condition prevents the next line, which spins the radar, from starting until the BUILD_PERCENT_LEFT is completed. If you didnt have this, the radar would be spinning while the unit was being built.  The "padflip = 0;" statement is just a static-var that is just being initialized.

StartMoving()

Generally, k-bots, ships, and subs use this function to either show the wakes from ships and subs, or tell TA that the k-bot is walking. I havent seen any use of these in vehicles or aircraft in their BOS files. Anyways for vessels a typical setup would be this:

StartMoving()
{
    signal SIG_WAKE;
    set-signal-mask SIG_WAKE;
    while (TRUE)
    {
        emit-sfx SFXTYPE_WAKE1 from wake1;
        emit-sfx SFXTYPE_WAKE1 from wake2;
        sleep 300; 
    }
}                                             

See the "signal SIG_WAKE;" call? This tells the unit that if its changing its direction to restart the script. The function "StopMoving()" will have the call to stop the wake bubble animation. "set-signal-mask" is what allows itself to be killed by another "signal" call.  The emit-sfx statements create the wake bubbles from a ship while its moving

The statement "while (TRUE)" means that if the signal hasnt been killed off, to continue with the following statements below it that are enclosed in brackets "{ }".  "emit-sxf SXFTYPE_WAKE1 from wake1;" is what makes the bubbles and "wake1" is the object name in the 3do file. The delay, "sleep 300" means to not repeat the above statements for 300 ticks (I think) so that the bubbles arnt overlapping themselves and stuff.

For the k-bots, the only thing I've found is a boolean expression to indicate if the unit is moving or not. Its used in another function called "MotionControl()" which houses all the information for aiming certain parts like the nanobeams and such. Anyways, what k-bots use is:

static-var bMoving; 
.....
StartMoving()
{
    bMoving = TRUE;
}                                             

Pretty straight forward here. Its just using the static-var of bMoving to show the "MotionControl()" function that the unit is moving.  You could pass this to another function, say the "Deactivate()" function by giving it a condition like "if (bMoving)".

StopMoving()

This is just the opposite of "StartMoving()" and it is used the same way as the "StartMoving()" function call but what its used for is to send signals, like this:

StopMoving()
{ 
    signal SIG_WAKE;
}                                             

For ships and subs, all this is doing is killing off the animation bubbles that were started in the "StartMoving()" function.  You dont need the "set-signal-mask" statement in here because it's already set by the "StartMoving()" function.

The Kbot script would use:

StopMoving() 
{
    bMoving = FALSE;
}                                             

No signal call here, just the variable set to FALSE so the variable can be compaired in another function.

MotionControl()

The MotionControl() subroutine is where some of the magic lies when it comes to the complex animations of a Kbot walking and targeting its enemy.  A good unit to look at for an example is the Arm Eraser.

AimPrimary(heading,pitch)
AimSecondary(heading,pitch)
AimTertiary(heading,pitch)

Turret aiming galore! Yup thats what this function does is aim the turret and the barrels (if any) towards their target. Turreted based weapons need this. If you are unsure about needing this, check the "weapons.tdf" file and look for the item "turret=1" for the weapon you want to use. If thats true then you definatly will need this.

Using the armbats.bos file for an example, the format is:

AimPrimary(heading,pitch)
{
    signal SIG_AIM1;
    set-signal-mask SIG_AIM1;
    turn turret1 to y-axis heading speed <55>;
    turn turret1 to x-axis (0-pitch) speed <30>;
    wait-for-turn turret1 around y-axis;
    wait-for-turn turret1 around x-axis;
    return(TRUE);
}                                             

Again like in the "StartMoving()" function we have the signal calls here but for SIG_AIM1 instead. There isnt another function that stops the SIG_AIM1, its done in here because the weapon is constantly being aimed and it can be re-aimed at another target because it has the "set-signal-mask" statement in here.  I think the last statement, "return(TRUE);" is kinda like a condition where the unit wont stop aiming until it has the proper aim towards its target, then it goes on to the next phase...FIRING!!!

Anyways the line "turn turret1 to y-axis heading speed <55>;". "turn", of course, means turn or rotate around an axis. "turret1" is the object as named in the 3do file. "to y-axis" means around y-axis. Thats confusing. "heading" is the number that is passed from the function "AimPrimary(heading,pitch)", its the direction of the target to "turret1". Or in naval terms, bearing. "speed <55>" is the speed setting of the object that will rotate in this statement.

A little note on "passing". This is a C thing where in a function name (such as in "AimPrimary(heading,pitch)") a value or set of values can be sent from the main program, through this function and used in the statements inside of it. Since the main program of TA already initializes and uses the variables "heading" and "pitch", you dont need to define them in the static-var part of your bos file.

You will notice here that the next line "turn turret1 to x-axis (0-pitch) speed <30>;" is using the "turret1" object to elevate the barrels of the turret along the x-axis. This I dont like because all the barrels are being aimed at the same time but I guess in the game you  cant tell the difference. Everything is the same here exept for the (0-pitch) number. It gets this number exactly like how it got the "heading" number, passed from the function name of "AimPrimary(heading,pitch)". The (0-pitch) sets the angle to where a projectile weapon will fire and since gravity is an issue in this game, it uses this format. I think this is how it will turn negative numbers into positive elevations. Again we have the speed setting by "speed <30>".

In the units .fbi file, AimPrimary is Weapon1, AimSeconday is Weapon2, and AimTertiary is Weapon3. I dont know what Weapon4 is though.

AimFromPrimary(piecenum)
AimFromSecondary(piecenum)
AimFromTertiary(piecenum)

All this function does is set which object is going to be used to aim with. The format for this is:

AimFromPrimary(piecenum)
{
    piecenum=turret1;
}

AimFromSecondary(piecenum)
{
    piecenum=turret2;
}                                             

Again, turreted weapons need to have this so TA knows what object to use to get the "heading" and "pitch" angles from.  We have the "passed" variables here but they are going the other way, "piecenum" is being defined here and in the TA main program it will have that information from this function.  Thats all this function does.

QueryPrimary(piecenum)
QuerySecondary(piecenum)
QueryTertiary(piecenum)

Dont let the name fool you. These functions are what actually fires the weapon. TA asks "what object do I use to launch the weapon from?" and they are usually the objects named FLARE. The format for this is:

QueryPrimary(piecenum)
{
    piecenum=flare1a;.
}

QuerySecondary(piecenum)
{
    piecenum=flare1b;
}                                             

Again like in the "AimFromPrimary()" function, its passing variables, or object names, to TA giving it the object names to use as firing positions for that particular weapon.

Heres a rotating firing sequence for a triple barrel turret as like the one on the Arms Battleship:

QueryPrimary(piecenum) 
{ 
    if (next_barrel1==1) { 
        piecenum=flare1a; } 
    if (next_barrel1==2) { 
        piecenum=flare1b; } 
    if (next_barrel1==3) { 
        piecenum=flare1c; } 
}                                             

Back above in the "static-var" section, the example shows the "next_barrel1" variable being incremented and down here its being compaired to see which flare should be used. It would look really odd to have the center barrel firing all the time while the left and right barrels show their flares. Thats why this is done like this.  Thats all this function does.

FirePrimary(piecenum)
FireSecondary(piecenum)
FireTertiary(piecenum)

Yup another name mixup here. What this function does is show animation for firing.    Turrets use this to move their barrels back and to show flares, like this:

FirePrimary()
{
    if (next_barrel1==1)
    {
        move barrel1a to z-axis [-2.4] speed [500];
        wait-for-move barrel1a along z-axis; 
        move barrel1a to z-axis [0] speed [3.0];
        show flare1a; 
        sleep 150;  
        hide flare1a;
    }
    .....
}                                             

After the "if" comparison is done, a barrel is moved along the z-axis to a position [-2.4] AWAY from the center of its z-axis at the speed of [500].  Watch the brackets and where they are used. The bottom of this file has a list as to their occurance.  The "wait-for-move" is a pause that will wait for the specified object to actually move
to its required length along a specified axis before proceeding to the next statement. The "along" is a good addition because it allows you to have multi axial movements (say a barrel that is raising out of a ground or whatever).

The next statement is the barrel returning to the ready position but moving slowly because of the speed setting of [3.0]. Also notice that z-axis is [0] and not [2.4]. Thats because its returning the object barrel1a to its original position. If you had said [2.4] instead of [0], the barrel would appear 2.4 units away from the center of its z-axis.

The "show flare1a;" does just that, it shows the flare1a object, sleeps for 150 ticks (I think its in ticks which I think is 1/100th of a second?), and then hides the flare1a object again. This gives it that flash effect.

Activate()
Deactivate()

These are the state requests of a particular unit. The Brawler, Missile ship, MERL, Construction Yards, etc... use them. What they do is make a unit useable or un-usable until it goes into the ready status. Like how you cant fire a Nuke until its done loading it on the pad or why the MERL wont fire until the rocket is in a position to fire.

An important note here is that the #include "Statechg.h" line needs to be added twice in the bos file. Once before the #define ACTIVATECMD/DEACTIVATECMD and once after it. Lemme show you an example of the arm's missile silo bos file:

.....
#define ANIM_VARIABLE TRUE
#include "StateChg.h"
#include "activatescr.bos"
#include "deactivatescr.bos"
#include "smokeunit.h"
#include "exptype.h"

..... // ("Go()" and "Stop()" functions here) 

#define ACTIVATECMD call-script Go();   
#define DEACTIVATECMD call-script Stop(); 
#include "StateChg.h" -    
.....                                             

See how "Statechg.h" was added twice here? Remember, once before and once after the "#define ACTIVATECMD" and "#define DEACTIVATECMD" definitions. Also notice after the ACTIVATECMD there is the segment "call-script Go();". This is the function used to show the animation of the unit being readied to shoot. The same with the DEACTIVATECMD but to deactivate the unit.  Take note of the "#define ANIM_VARIABLE TRUE" definition here as well. It will be needed.

..... 
Go()
    {
    dont-cache door1; 
    dont-cache door2;
    dont-cache missile; 
    dont-cache plate; 
    dont-cache arm;
    show missile; 
    call-script activatescr(); 
    ready = TRUE; 
    } 

Stop() 
    { 
    ready = FALSE;
    sleep 4000;
    call-script deactivatescr();
    cache door1;
    cache door2;
    cache missile;
    cache plate;
    cache arm;
    }
.....                                             

Here is where the functions "Go()" and "Stop()" are at. I am not sure what the "dont-cache" and "cache" do but by following this, a rule of thumb can be made. Use "dont-cache" for objects that will be animated by the function "activatescr()" and "cache" for objects animated by the function "deactivatescr()".

You can see that "Go()" and "Stop()" are defined at the top by "#define ACTIVATECMD call-script Go();" and #define DEACTIVATECMD call-script Stop();". What this says is when TA gets the ACTIVATECMD from the user (you wanting the unit to do something) to start running the function "Go()". And within that "Go()" function is a "call-script activatescr();" statement. And below that the variable "ready" which can be used for comparison in another function.

// Activate.bos file //

activatescr()
{
    If ( ANIM_VARIABLE )
    {
        move plate to y-axis <0.00> now;
        move arm to x-axis <0.00> now;
        move arm to z-axis <0.00> now;
        turn door1 around z-axis <0> now;
        turn door1 to z-axis [-89.55] speed <47.13>;
        turn door2 around z-axis <0> now;
        turn door2 to z-axis [90.00] speed <47.37>;
        sleep <1900>;
    }
    If ( ANIM_VARIABLE )
    {
        move door1 to y-axis [-6.10] speed <3.13>;
        move door2 to y-axis [-6.10] speed <3.13>;
        sleep <1950>;
    }
    If ( ANIM_VARIABLE )
    { 
        move plate to y-axis [8.00] speed <4.06>;
        sleep <1970>;
    } 
    If ( ANIM_VARIABLE )
    { 
        turn arm to x-axis [-90.00] speed <45.54>;
        sleep <1976>;
    }
    sleep <114>;
    return ( 0 );
}                                              

Here is the "activatescr()" function from the arm's missile silo.  Being called by the "Go()" function, and included by #include "activatescr.bos" definition, here is where the pieces are maneuvered into place that will allow the unit to fire.  You can see the "if" comparisons to see if ANIM_VARIABLE exists, which it does, so these conditions will always be true. Delays by the statements "sleep <1900>" pause the sequences here.  To me it looks like a better way of scripting this could have been done by using the "wait-on-move" command and removing the "if" comparisons, but I guess it has to be written like this.

At the very bottom is the "return ( 0 )" statement which is just returning to the "Go()" function saying its done with this and can continue on  with the rest of its statements.  The "Deactivate()" function is just the opposite of this "Activate()" function because it just replaces all pieces back into their original position.  Now all these functions are just setting up how the unit is to be activated. The actual call is from here:

// Armsilo.bos //

AimPrimary(heading,pitch)
{
    start-script RequestState( ACTIVE ); 
    signal SIG_AIM;
    set-signal-mask SIG_AIM;
    while (!ready)
    {
        sleep( 250 );
    } 
    start-script RestoreAfterDelay();
    return(TRUE); 
}                                             

So why is the statement "start-script RequestState( ACTIVE );" the one that does it?   Because in the "statechg.h" file it has an "if" comparison checking to see which state was requested. Since "ACTIVE" was passed from the "RequestState( ACTIVE );" statement, the "if" comparison is true and the statement within the brackets are executed. "ACTIVATECMD" is called and since "ACTIVATECMD" was defined to call-script "Go()", we go through that function now. 

// Segment from file statechg.h //
....
if (statechg_DesiredState == ACTIVE)
{
    ACTIVATECMD
    actualstate = ACTIVE;
}
.....                                             

Walking through the "Go()" function we move to another "call-script" but this one goes to the "activatescr()" function where it starts moving parts.  After thats done it returns to "Go()" and then back to "AimPrimary(heading,pitch)" where it then sets up the signal and goes on from there.

I know this was a big one but I had to cover a very in depth example for using this function and how. If you are lost then load up the "armsilo.bos", "activate.bos", and the "statechg.h" files so you can walk through it with this text. If you want to make units that follow this, you need to get this.

SetMaxReloadTime(time)

Not sure of this yet, but all examples of this I've seen so far look exactly like this, where "restore_delay" is a variable that is used in the RestoreAfterDelay() subroutine (described below).

SetMaxReloadTime(time)
{
    restore_delay = time * 2;
}                                             

RestoreAfterDelay()

Once a unit has been engaged in battle, some units "retract" to their original position after some period of time after the battle.  (For example, the ARM Annihilator will retract back into its base).  It is this function that folds the unit back up into its protected state.  As an example, the following subroutine is from TADD's Core Hellfire vehicle:

RestoreAfterDelay()
{
    sleep restore_delay;
    turn Turretarms to x-axis 0 speed <80>;
    turn Turret1 to y-axis 0 speed <60>;
    move Turretarms to y-axis [3] speed 40;
}                                             

In this example, after waiting for the alloted restoration time period, the "turretarms" piece is returned to the zero-position of the x-axis (that is, it is rotated so that it is no longer pointing up or down), the "turret1" piece is returned to the zero position of the y-axis (that is, it is rotated to face "forward"), and the "turretarms" piece is then lowered down the y-axis to a point 3 units above the "home" position.

HitByWeapon(anglex, anglez)

This routine comes into play when the unit is hit by a weapon.  It is best described by a small example from the ARM Solar Collector unit:

HitByWeapon(anglex,anglez)
{
    signal SIG_HIT; 
    set-signal-mask SIG_HIT;
    set ACTIVATION to 0;
    sleep 8000;
    set ACTIVATION to 1;
}                                             

In a nutshell, when the ARM Solar Collector is hit by a weapon, it deactivates itself, and goes to sleep for 8000 time units before reactivating.  If a weapon hit is taken while already deactivated, the "signal SIG_HIT" statement causes the previous execution of this routine to be interrupted, then restarted.

The paramters "anglex" and "anglez" are the X and Z coordinates of where the hit was taken.  The default HitByWeapon() subroutine found in Hitweap.h contains some sample code to show how to "rock" the unit back and forth when it takes a weapon hit.

StartBuilding()
StopBuilding()

This function just animates the construction unit or plant, depending on what it is, and can be used to call the ACTIVATECMD or state of the unit. A particular format is followed by the unit type as listed below:

StartBuilding() 
{ 
    spin pad around y-axis speed <30>; 
} 

StopBuilding()
{ 
    stop-spin pad around y-axis;
}                                             

For plants that have a pad that spins around, they use this. Ship yards dont have a pad that spins so they dont use this at all.

StartBuilding()
{
    set INBUILDSTANCE to TRUE; 
}

StopBuilding()
{
    set INBUILDSTANCE to FALSE;
}                                             

Construction aircraft use this one. TA has INBUILDSTANCE already defined and all this is doing is setting that variable as true or false.

StartBuilding(heading)
{
    buildheading = heading; 
    start-script RequestState( ACTIVE ); 
} 

StopBuilding()
{
    start-script RequestState( INACTIVE );
}                                             

C-bots, C-vehicles and C-ships use this format. Their states are requested so that the animation of their nanolathing pieces can be opened and the variable "heading" is used in another function called "TargetHeading(heading)" so that the nanolathing piece can be properly aimed.

StartBuilding(heading,pitch)
{
    bAiming = TRUE;
    while (NOT bCanAim)
    {
        sleep 100;
    }
    turn torso to y-axis heading speed <300>;
    turn luparm to x-axis (0-pitch-<30>) speed <45>;
    wait-for-turn torso around y-axis; 
    wait-for-turn luparm around x-axis; 
    set INBUILDSTANCE to TRUE; 
} 

StopBuilding() 
{ 
    set INBUILDSTANCE to FALSE; 
    signal SIG_AIM; 
    set-signal-mask SIG_AIM; 
    call-script RestorePosition(); 
}                                              

This is the layout for what the commander uses for its building process. Its using "bAiming" for comparison in other functions as well as the INBUILDSTANCE variable. The commander also uses signals because it also has regular weapons to fire with.  You would use this format if your unit had weapons and a building capacity.

TargetHeading( heading )

This is the second part for the "StartBuilding()" function that the mobile construction units (k-bots, ships, and vehicles) use. I believe this is what the nanolathing pieces use for their aiming.

TargetHeading( heading )
{
    buildheading = 0 - heading;
}                                             

QueryNanoPiece(piecenum)

This is exactly like the "QueryPrimary()" function call. It tells TA which object piece to use for the nanolathing spray.

QueryNanoPiece(piecenum) 
{
    piecenum=beam;
}                                             

This is just a simple designation for a unit that has one nanolathing piece named "beam".

QueryNanoPiece(piecenum) 
{ 
    if( spray == 0 ) 
    { 
        piecenum=beam1; 
    }  
    if( spray != 0 ) 
    { 
        piecenum=beam2; 
    } 
    spray = !spray; 
}                                              

Here is a way to get 2 nanolathing pieces to fire at the same time. The example script is from the arms advanced vehicle plant. Apparently this is a way to pass 2 pieces through the "piecenum" variable by using this series of "if" comparisons. If your building unit has 2 nanolathing pieces, this may be the way to  go.

QueryLandingPad(piece1, piece2)

???

QueryBuildInfo(piecenum)

This function defines what object piece is used to attach the unit thats being built too.  Only construction plants as well as the ship yards use this. Mobile construction units dont use this. The format is:

QueryBuildInfo(piecenum)
{ 
    piecenum=pad;
}                                             

Just defining what piece is used to locate the unit thats being  built. I believe it attaches the unit to this piece so when the pad is spinning, the unit will spin with it.

QueryTransport(piecenum)

Not sure exactly what this does but by a guess it might be the object that is used to carry the unit. Below is the fragment from the arm atlas bos file. I believe that the statement "piecenum=1;" refrences the first object in the list for the transport, the one named "base".

QueryTransport(piecenum) 
{
piecenum=1;
}                                             

BeginTransport(height)

This item sets the attachment point for the unit to be picked up by moving the object named "link" to the unit that will be picked up. The part (0-height) takes the height of the transport and turns it into a negative number and that is where "link" gets moved too. The "RequestState(ACTIVE);" handles the animation of the unit lowering because as like before in the "Activate()" function description above, it will refrence its own "Activatescr()" function which will have its own statements for lowering the unit to carry it and then raising it again to its original height.

BeginTransport(height)
{
    move link to y-axis (0 - height) now;
    start-script RequestState( ACTIVE ); 
}                                              

EndTransport()

This is the ending part for the "BeginTransport(height)" function. It also contains the "RequestState(INACTIVE);" call and does the opposite of the above function.

EndTransport()
{
    start-script RequestState( INACTIVE );
}                                             

SweetSpot(piecenum)

I believe this is the object that is used to refrence where the user needs to click in order to select it. It might also refer to where the opposing units aim for while targeting.

SweetSpot(piecenum)
{
    piecenum=base;
}                                             

Demo()

Part of CD's unit viewer call. Not all units have this but I think it allows a unit to be viewed in the unit viewer. The format for this is:

Demo() 
{ 
    unitviewer = TRUE; 
}                                              

Also in other parts in different bos files, I have found that within the "Activate()" functions, the statement "unitviewer = FALSE;" was included so its possible to just add that statement to the "Activate()" functions of all bos files.

Killed(severity, corpsetype)

This is how the unit is destroyed and what to do in a certain condition. Mainly the condition is how much damage the unit takes. Here is the arm aircraft plants "Killed()" function.

Killed( severity, corpsetype ) 
{
    if (severity <= 25)
    { 
        corpsetype = 1; 
        explode base type   BITMAPONLY | BITMAP1;
        explode beam1 type  BITMAPONLY | BITMAP2;
        explode beam2 type  BITMAPONLY | BITMAP3;
        explode door1 type  BITMAPONLY | BITMAP4;
        explode door2 type  BITMAPONLY | BITMAP5;
        explode light type  BITMAPONLY | BITMAP1;
        explode nano1 type  BITMAPONLY | BITMAP2;
        explode nano2 type  BITMAPONLY | BITMAP3;
        explode pad type    BITMAPONLY | BITMAP4;
        explode plate1 type BITMAPONLY | BITMAP5;
        explode plate2 type BITMAPONLY | BITMAP1;
        explode post1 type  BITMAPONLY | BITMAP2;
        explode post2 type  BITMAPONLY | BITMAP3;
        explode radar type  BITMAPONLY | BITMAP4;
        return( 0 ); 
    } 
    if (severity <= 50) 
    { 
        corpsetype = 2; 
        explode base type     BITMAPONLY | BITMAP1;
        explode beam1 type    FALL | BITMAP2; 
        explode beam2 type    FALL | BITMAP3; 
        explode door1 type    BITMAPONLY | BITMAP4;
        explode door2 type    BITMAPONLY | BITMAP5;
        explode light type    FALL | BITMAP1;
        explode nano1 type    SHATTER | BITMAP2;
        explode nano2 type    BITMAPONLY | BITMAP3;
        explode pad type      BITMAPONLY | BITMAP4;
        explode plate1 type   BITMAPONLY | BITMAP5;
        explode plate2 type   BITMAPONLY | BITMAP1;
        explode post1 type    FALL | BITMAP2; 
        explode post2 type    FALL | BITMAP3; 
        explode radar type    FALL | BITMAP4;
        return( 0 );
    }
    if (severity <= 99)
    {
        corpsetype = 3;
        explode base type     BITMAPONLY | BITMAP1;
        explode beam1 type    FALL | SMOKE | FIRE | 
                              EXPLODE_ON_HIT | BITMAP2;
        explode beam2 type    FALL | SMOKE | FIRE | 
                              EXPLODE_ON_HIT | BITMAP3;
        explode door1 type    BITMAPONLY | BITMAP4;
        explode door2 type    BITMAPONLY | BITMAP5;
        explode light type    FALL | SMOKE | FIRE | 
                              EXPLODE_ON_HIT | BITMAP1;
        explode nano1 type    SHATTER | BITMAP2;
        explode nano2 type    BITMAPONLY | BITMAP3;
        explode pad type      BITMAPONLY | BITMAP4;
        explode plate1 type   BITMAPONLY | BITMAP5;
        explode plate2 type   BITMAPONLY | BITMAP1;
        explode post1 type    FALL | SMOKE | FIRE | 
                              EXPLODE_ON_HIT | BITMAP2;
        explode post2 type    FALL | SMOKE | FIRE | 
                              EXPLODE_ON_HIT | BITMAP3;
        explode radar type    FALL | SMOKE | FIRE | 
                              EXPLODE_ON_HIT | BITMAP4;
        return( 0 );
    }
}                                             

The "severity" is an indication of how badly the unit was destroyed.  As each piece of the unit is exploded, a series of flags indicates exactly how that piece explodes.  The "corpsetype" (this is currently a guess) is a measure of whether the UNITNAME_DEAD corpse is left behind (corpsetype=1), the UNITNAME_HEAP is left behind (corpsetype=2), or whether the unit is utterly destroyed, leaving no debris behind (corpsetype=3).

Explosion Type Description
BITMAP1 One type of bitmap explosion (see exptype.h)
BITMAP2 One type of bitmap explosion (see exptype.h)
BITMAP3 One type of bitmap explosion (see exptype.h)
BITMAP4 One type of bitmap explosion (see exptype.h)
BITMAP5 One type of bitmap explosion (see exptype.h)
BITMAPNUKE One type of bitmap explosion - a very big one? (see exptype.h)
BITMAPONLY The piece will not fly off or shatter or anything. Only a bitmap explosion will be rendered  (see exptype.h)
EXPLODE_ON_HIT The piece will explode when it hits the ground (see exptype.h)
FALL The piece will fall due to gravity instead of just flying off (see exptype.h)
FIRE A fire trail will follow the piece through the air (see exptype.h)
SHATTER The piece will shatter instead of remaining whole (see exptype.h)
SMOKE A smoke trail will follow the piece through the air (see exptype.h)

TargetCleared(which)

I found this one on the ARM Annihilator.  I believe this routine is called when the target that the weapon is aiming for has been destroyed.  In the case of the Annihilator, this stops the turret rotation because the rotation is so painfully slow.

TargetCleared(which)
{
    stop-spin turret around y-axis;
    stop-spin gun around x-axis;
}                                         

RockUnit(anglex, anglez)

The RockUnit() routine is called when a unit is hit by enemy fire, and can also be called when a weapon is fired from your unit.  It takes two parameters, indicating the direction from which a hit was taken (or the direction to which a weapon is being fired), and is used to "rock" the unit's model to illustrate a recoil effect.

The following example shows how this routine might be written for a water unit.   In this example, the unit first rocks away from the direction, then towards it a little slower, then back to it's normal position.  A sample can also be found in the rockunit.h file.

RockUnit(anglex,anglez)
{
	#define FIRST_SPEED 15
	#define SECOND_SPEED 12
	#define THIRD_SPEED 9

	turn base to x-axis anglex speed <FIRST_SPEED>;
	turn base to z-axis anglez speed <FIRST_SPEED>;
	wait-for-turn base around z-axis;
	wait-for-turn base around x-axis;
	turn base to x-axis (0-anglex) speed <SECOND_SPEED>;
	turn base to z-axis (0-anglez) speed <SECOND_SPEED>;
	wait-for-turn base around z-axis;
	wait-for-turn base around x-axis;
	turn base to x-axis <0> speed <THIRD_SPEED>;
	turn base to z-axis <0> speed <THIRD_SPEED>;
}