|
|
 |

SLIC scripting language
By Joe Rumsey
About the author: CTP Programmer, a.k.a Mr Ogre on the forums
Date: June 18, 1998
Last Updated: April 13, 1999
Contents
SLIC Language Specification
SLIC uses a C-like syntax for most things. There a few large
differences, and many features of C don't exist in SLIC, but if you
can read C, you can understand most everything in SLIC.
The basic components of the SLIC language are:
Message Boxes
A MessageBox is a section of SLIC code that defines a dialog
box to be displayed to the user. The general format is:
MessageBox 'name' {
commands
}
The name can be any string, and may contain spaces, but may not
continue past the end of a line.
A typical command might be:
Text(ID_THIS_IS_A_MESSAGE_BOX);
Which would cause the dialog box to display the text pointed at by the
string ID "THIS_IS_A_MESSAGE_BOX". Note that string IDs in SLIC are
always preceded by ID_. There are other possible commands for message
boxes:
Alert Boxes
An AlertBox is exactly the same as a MessageBox, the
differences being that a MessageBox starts as an icon, while an AlertBox
opens immediately, has a smaller window, and must contain at least one
button. AlertBoxes are modal, meaning the user has to respond to them
before the game can proceed.
Buttons
Messages and alerts can have buttons attached. A button can have any
text on it, and can execute any valid slic code when clicked. Inside
the code for a messagebox or alertbox, a button is specified like
this:
...
Button (stringid) {
code
}
...
code is the code to be run when the button is clicked. As an example, here is a message box with an "OK" button and a "Tell me more" button:
messageBox 'MSampleBox' {
Text(ID_THIS_IS_A_SAMPLE);
Button(ID_OK) {
Kill(); // Close this message
}
Button(ID_TELL_ME_MORE) {
// Provide another messagebox with more info.
// Does not close this box, since there is no Kill() in this button.
Message(g.player, 'MSampleTellMeMore');
}
}
There is also a special button-like statement called OnClose which
doesn't take a name, and runs when the message is closed. Here is an
example of how to send another message when one message is closed (no
matter how the first message was closed)
messageBox 'MFirstMessage' {
Text(ID_CLOSING_SENDS_ANOTHER_MESSAGE);
OnClose {
Message(g.player, 'MSecondMessage');
}
}
messageBox 'MSecondMessage' {
Text(ID_SENT_WHEN_FIRST_MESSAGE_CLOSED);
}
Triggers
A trigger is a section of SLIC code that is run when specific game
events occur. A trigger has this format:
Trigger 'name' when (expression) {
commands
}
There is also a special format for triggering by UI components:
Trigger 'name' on "UI Component Name" when (expression) {
commans
}
It behaves in the same way as the normal format, except that it is only evaluated when the UI component is used.
The name follows the same rules as for message boxes.
The body of the trigger is executed only when the expression is
true (not equal to 0).
A SLIC expression is a C-style mathematical expression. These
are all valid expressions:
1 + 1
a + b
a * (b + c)
(a * b) + c
a && b
a || b
a < b
a > 2
a <= b
a >= b
a != b
!a
(a && !b) || c
The variables used in the expression for a trigger determine when the
trigger might be run. For example, if the built-in variable
unit.built is used, the trigger condition will be checked
whenever a unit is built. However, any given trigger is never placed
in more than one list of triggers to be checked. So while a trigger
whose only built-in variable in the conditional is g.player
will be checked whenever a player's turn begins, one that uses both
g.player and unit.built will only be checked when a unit
is built. The author has tried to make the order of which variables
takes precedence as logical as possible. Some variables make no sense
when used in the same expression (E.G. city.built and
unit.sighted can't be fired at the same time). In other cases,
the list the trigger is added to should be the one that will fire more
often.
Built-in Variables
The SLIC engine contains many predefined variables with many
different meanings. See Built-in
variables and trigger events for a discussion of variable concepts
and a full list of builtin variables.
Trigger Priorities
As mentioned in Built-in variables and
trigger events,, the built in variables used in a trigger's
conditional determine when that trigger is evaluated. A trigger is
never added to more than one event list, however. There is a preset
prioritization of trigger lists, defined below. The list the trigger
is added to is the lowest list here. For example, if a trigger
has both g.year and unit.built, it is added to UNIT_BUILT. The name
of the lists below are the event type the list is associated with.
All the triggers in a list will be checked when that event occurs.
Functions
SLIC provides a number of functions for performing operations or
checking values that would otherwise be difficult or impossible. Here
is a list. VOID functions do not return values and cannot be used in
expressions. INT functions do and can.
Regions
There are a number of functions above which act on regions. A region is a parallelogram or union of parallelograms on the map. They can be defined in two ways.
A simple region:
Region name [ x1,y2, x2, y2];
Or a complex region built from a union of any number of simple regions:
Region name = name1 + name2 [+ ... + namen]
WARNING: Regions have not been thoroughly tested, they may not
work as advertised!
User variables
In addition to the built-in variables, any number of user variables
may be used. Integer variables do not need to be declared prior to
use, and are always initialized to 0. You may assign the result of
any expression to a user variable:
my_var = 1 + 1;
have_flanker = IsFlankingUnit(unit);
counter = counter + 1;
turns_to_go = turns_to_go - 1;
last_checked_year = g.year;
And so on. Their values are global and persistent - they will not
change between various slic objects or when execution stops.
In addition to user variables, there are Unit, City, and Location
variables. These must be declared outside of any object prior to
use. They cannot be used in expressions EXCEPT with the == operator.
That is, you can check to see if a unit is the same as another unit:
if(myUnitVariable == unit.1) {
// they are the same unit
}
The same works for cities and locations.
Here is an example that creates a unit in a city at the beginning of
that city's turn, then as soon as that unit moves, kills it.
TypeUnit myExampleUnit;
trigger 'CreateAUnit' when(city.beginturn && g.player == 1) {
CreateUnit(g.player, UnitType("UNIT_WARRIOR"), city.location, 0, myExampleUnit);
DisableTrigger('CreateAUnit');
}
trigger 'KillAUnit' when(unit.moved && unit == myExampleUnit) {
KillUnit(myExampleUnit);
DisableTrigger('KillAUnit');
}
Miscellany
What? No Functions?
While SLIC does not directly support user defined functions, there is
a workaround. Using the Abort() function, you can create a MessageBox
object that never displays a message. So call Abort() as the first
line of a MessageBox object, then write any code you want inside the
object, and call that MessageBox from anywhere you'd like with
Message(0, 'NameOfYourMessageBox'); The first argument to
message is a player index, but since this message isn't going to
display anything, it doesn't matter what index you use.
Loading your script
You can cause your SLIC file to be loaded by using a
#include "yourfile.slc" in script.slc. Your file should be
placed in default/gamedata, the same place as script.slc.
Joe Rumsey
Back to main Modification page
Apolyton Civilization Site Copyright © Daniel Quick
All trademarks and trade names are the properties of their respective owners.
|