These files represent scripted actions. .bcs
files are scripts attached to anything other than the player characters. .bs
files are scripts which drive a characters actions automatically (AI scripts) based on criteria in the environment.
Just as the files from which they are compiled, these script files make use of the concept of "triggers" and "responses". A trigger is a condition which causes a response with a certain probability. A response is one or more calls to functions they have exposed to the script. I believe this to be the difference between .bcs
and .bs
files. (They are assigned different resource types in the resource management code, too.)
The format will be given in a top-down sense: i.e. I will describe the formatting of top-level blocks, and then I will describe the contents of the blocks. The process will proceed recursively until the whole format is described. The top-level block is the
script file.
First, a brief word on "function parameters". Both
triggers and
actions are essentially calls to functions inside the Infinity Engine. Triggers can take up to 7 arguments, and actions can take up to 10 arguments. There are three allowable forms of arguments: strings, integers, and objects. The different function calls are defined in TRIGGER.IDS and ACTION.IDS. There are also functions defined in SVTRIOBJ.IDS, which are a {sub|super} set of of the function calls defined in TRIGGER.IDS. They are probably used for {insert spiel here}. String arguments are simply quoted strings (i.e. ASCII strings delimited by the double quote character "). The format of these descriptions is given below, by way of an example (from BG's TRIGGER.IDS):
0x401D Specifics(O:Object*,I:Specifics*Specific)
The first thing on the line is the ID (in hex. IDs in scripts are typically in decimal). Next is the name of the function. Inside the parentheses, similarly to C/C++, is a comma-delimited list of argument type and argument name. The argument types are:
S: string
O: object
I: integer
P: point
A: action
There is always a tag after the
:
and before the
*
. I believe the tag is used only for expository function -- i.e. simply an argument name to help discern the purpose. There is, however, one minor complication. Actions only have space for 2 string parameters. There are actions taking anywhere from 0 to 4 strings. Some of the actions which take strings (usually either 2 or 4 strings) actually concatenate the strings. In this case, it is always an "Area" and a "Name" parameter, (though the parameter names vary somewhat). The only surefire way to tell which is which is to hardcode the values of the actions which concatenate strings. When the strings are concatenated, the "Area" is always the first part of the resulting string, and always takes exactly 6 characters. It works, in most respects, just like a namespace qualifier in, for instance,
C++
. An aside: The functions which actually concatenate the strings are typically the ones which access "global variables", i.e. Global, SetGlobal, IncrementGlobal, et cetera. I am not certain, at present, how "action" type parameters are stored. This will require some investigation.
The final detail in the above example is the bit following the
*
. This occurs (I believe) only in integer arguments; the string following the asterisk is the name of an IDS file. The values in the IDS file are the only allowed values for that parameter; moreover, it is extremely probable that the parameter can be accessed using the symbolic names given in the IDS file, though this is merely speculation. Each trigger can use up to 2 (3?) integer arguments, up to 2 string arguments, and one object argument. These seven arguments are always specified in each trigger, even if they are not all used. If an argument is not used, it is assigned a dummy value. Finally, the arguments are used in order when they are listed in the scripts. For instance, two integer parameters always occupy the first two "integer parameter" slots in the trigger or action. A trigger has an additional "flags" field, in which, for instance, a bit is either set or cleared to indicate whether the trigger is to be negated or not. (i.e. whether the success of the trigger should return true or false).
This can be interpreted as "if condition, then response set". A response set is a set of actions, each of which is performed with a certain probability.
This should be interpreted as the AND of all the "trigger" conditions. The condition is true iff all triggers inside are true.
This format is slightly hairier. First, it has a "trigger ID", which is an ID in the TRIGGER.IDS file. Essentially, each trigger corresponds to a call to one of the functions listed in there. See the section on parameters for details.
TR
(newline)
- trigger ID from TRIGGER.IDS (no newline)
- 1 integer parameter (no newline)
- 1 flags dword (no newline):
- bit 0: negate condition flag (if true, this trigger is negated -- i.e. success=>false, failure=>true)
- 1 integer parameter (no newline)
- 1 integer. Unknown purpose.
- 2 string parameters (no newline)
- 1 object parameter (newline)
TR
(newline)
Each response in a reponse set has a certain weight associated with it. Usually, this is 100%, but if not, then the response is only played with a certain probability. To find the chance of a particular response being chosen, sum the probabilities of all the responses in the response set. Each response Rnhas a probability
Pn/Sum(P)
of being chosen, if the response set is to be played.
A response is simply the concatenation of a probability and an
ACTION.
RE
(newline)
weight. i.e. how likely this response is to occur, given that the response set is to be run (no newline -- often no whitespace, though that may not be important).
action (newline)
RE
(newline)
This format is slightly hairier. First, it has a "action ID", which is an ID in the action.IDS file. Essentially, each action corresponds to a call to one of the functions listed in there. See the section on
parametersfor details.
AC
(newline)
action ID from ACTION.IDS (no newline)
3 object parameters (newlines after each)
1 integer parameters (no newline)
1 point parameter (formatted as two integers x y
) (no newline)
2 integer parameters (no newline)
2 string parameters (no newline)
AC
(newline)
Objects represent things (i.e. characters) in the game. An object has several parameters. These parameters have enumerated values which can be looked up in .IDS files. Planescape: Torment has more parameters than BG did.
- TEAM (Planescape: Torment only)
- Every object in Torment can have a TEAM. Most objects do not have a team specified.
- FACTION (Planescape: Torment only)
- Every object in Torment can belong to a FACTION. Most creatures do, in fact, belong to a faction.
- EA (Enemy-Ally)
- Whether the character is friendly to your party. Values include "INANIMATE" (=1), for inanimate objects, "PC" (=2) for characters belonging to the player, "CHARMED" (=6) for characters who have been charmed, and hence are under friendly control, or "ENEMY" (=255) for characters who are hostile towards the character. Two special values of EA exist: "GOODCUTOFF" (=30) and "EVILCUTOFF" (=200). Characters who are below the good cutoff are always hostile towards characters over the evil cutoff, and vice versa. To this end, you can use GOODCUTOFF and EVILCUTOFF as sort of "wildcards". EVILCUTOFF specifies all characters who are "evil", and GOODCUTOFF specifies all characters who are "good". Note that this has little to do with the alignment.
- GENERAL
- The general type of an object. This includes "HUMANOID", "UNDEAD", "ANIMAL" et cetera, but also "HELMET", "KEY", POTION", "GLOVES", et cetera.
- RACE
- The race of an object. Creatures obviously have a race, but items also have "race", which can include a slightly more specific description of the type of item than was given in the GENERAL field. For instance, for armor items, this includes the "type" of the armor -- leather, chain, plate, etc.
- CLASS
- The class of a creature or item. Again, the class notion makes more sense for creatures, but gives some information about the specific type of an item. For a "sword" or "bow", for instance, the class can be "LONG_SWORD" or "LONG_BOW". As another example, the different types of spiders (phase,wraith,etc) are differentiated by the class field.
- SPECIFIC
- The specific type of an item. BG only defines three specific types: NORMAL, MAGIC, and NO_MAGIC. Dunno. Torment uses this field much more extensively to differentiate precise individuals matching a description from everyone else.
- GENDER
- Gender field. There are, mind-boggling as it may seem, five possible values for this in BG, including the expected MALE and FEMALE. There's also "NIETHER" (sic), which I presume was meant as NEITHER. Finally, there are OTHER and BOTH.
- ALIGNMENT
- This field is fairly obvious. The high nybble is the Chaotic-Lawful axis, and the low nybble is the Good-Evil axis. The values for this are specified in ALIGNMEN.IDS. The only nuance to this is that there are several values MASK_CHAOTIC, MASK_LCNEUTRAL, MASK_LAWFUL, MASK_EVIL, MASK_GENEUTRAL, MASK_GOOD which are wildcards, meaning, respectively, all chaotic objects, all neutral (on the lawful-chaotic axis) objects, all lawful objects, all evil objects, all neutral (good-evil axis) objects, and all good objects.
- IDENTIFIERS
- The 5 identifiers for an object allow functional specification of an object (LastAttackedBy(Myself), etc.) These values are looked up in OBJECT.IDS. Any unused bytes are set to 0.
- NAME
- This is a string parameter, and is only used for characters who have specific names. Objects can be looked up by name, or referenced by name in scripts. That is the purpose of this field.
The specific format of an object is as follows:
OB
(newline)
integer: enemy-ally field (EA.IDS)
integer: (torment only) faction (FACTION.IDS)
integer: (torment only) team (TEAM.IDS)
integer: general (GENERAL.IDS)
integer: race (RACE.IDS)
integer: class (CLASS.IDS)
integer: specific (SPECIFIC.IDS)
integer: gender (GENDER.IDS)
integer: alignment (ALIGNMEN.IDS)
integer: identifiers (OBJECT.IDS)
(Not in BG1) object coordinates
string: name
OB
(newline)
Object coordinates must be specified as a point. Coordinate values which are
-1
indicate that the specified part of the coordinate is not used.
A point is represented as:
[x.y]