CTP2 Bureau : SLIC as an events-driven language
CTP2 Bureau
The Modding
Knowledgebase
WePlayCiv: CtP2 Downloads
WePlayCiv: Call To Power forum
Apolyton Call To Power forums
Civilization Fanatics: Call To Power

SLIC as an events-driven language

TURN WORD-WRAP ON :)
This guide is probably only useful to some people, others will find it positivly unhelpful. Some parts are less well explained. I think these are the bits that Locutus has already done ("Locutus' Ultimate Guide To SLIC").

by Ben Weaver aka The Immortal Wombat
24/11/01

1.0 Intro
 1.1 Stuff
2.0 Handlers
 2.1 Events as Handler Triggers
 2.2 Structure of Handlers
 2.3 Example Code
3.0 Variables
 3.1 Defining variables
 3.2 Built-in variables
 3.3 Use of variables in functions
4.0 if, for, while       
 4.1 Boolean Logic
 4.2 If, Elseif, and Else Statements
 4.3 For Loops
 4.4 While Loops
5.0 Functions
 5.1 Example Function Code
 5.2 Nested functions
 5.3 User-made functions
 5.4 Function-Based Coding
 5.5 Events as Functions
6.0 Messageboxes
7.0 mod_ functions
8.0 End Note

1.0 Intro

SLIC is a scripting language for CtP2. This means basically that it is a simplified programming language designed for use in CtP2. This allows you to make subtle, or not-so-subtle changes to the way the game plays, how some of the game rules act, and other such interesting things. It is based on a C-like syntax, but I see it very differently from C, because primarily it is events-driven language. Basically, think of SLIC as a reaction-language ie When x happens, SLIC makes y happen.
This guide should help you learn the basics of SLIC from the point of view as making y happen when x does, for many different and increasingly complex types of x and y.
Very important when SLICing: Have a goal in mind, and know how you are going to acheive this. This may mean getting very well aquainted with all the functions and events, or at least having a list of them available at any time.

Notes:
Most examples of SLIC coding in this guide do not come from Activision scenarios, as the SLIC in those is fairly complicated, and better examples are available. Consequently, if I refer to "SLIC files", I mean SLIC files other than Actision's *_func.slc and *_msg.slc files.

() - parentheses
[] - square brackets
{} - brackets

I am essentially very lazy, so a lot of the stuff in Locutus' SLIC guide, about Boolean logic, if, for, while, loops and number crunching is not included. It would probably be easier to read Locutus' guide first.

Also, I have put the events and handlers section first. This is a dubious idea, as an understanding of variables is needed for almost all other SLICing, but variables are essentially boring, so flick to section 3 as much as you like whilst reading section two, and you will still feel like you're learning how to do it, rather than the whole dull understanding it first.

1.1 - Stuff

At this stage I will make some comments about the C structure of SLIC, as you shouldn't need to go delving in C tutorials to learn SLIC. Every bracket that is opened '{' opens a new block of code. This will be dealt with more fully in chapter 4 with the if, for and while loops, but for now, every handler and loop that is opened, needs to be closed, so the game does not give you an error when it goes onto the next handler.
Note that when the bracket is opened, the next line is tabbed forward. This is so that the code is easier to follow. These two examples work eactly the same as ech other:

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 KillUnit(unit[0]);
}
[/php]
[php]
HandleEvent(MoveUnits) 'kill_the_unit' pre {
KillUnit(unit[0]);
}

but when you get into really complicated code with 20 or 30 different loops and brackets, then getting the right thing closed at the right time is very important.

Also, every line that does not end in '{' should end with a semi-colon; this is a marker to SLIC that the line has ended.

Oh yeah, and a double forward slash acts to signify a comment:

// everything beyond those slashes isn't read by SLIC.
KillUnit(unit[0]);   // this also works after lines of code have been ended with a semi-colon

This may seem like a lot to remember, but when you're writing the code, it becomes second-nature mostly, and most of the time its easier to remember than forget.

2.0 Handlers

Open any SLIC file, and somewhere in it will be an event handler. Event handlers are what 'see' the event happening, and trigger the code that you want to react to that event (They recognise a specific <i>x</i> and trigger your code for <i>y</i>.

2.1 - Events as Handler Triggers

Events are things that happen in the game. Most are fairly self-explanatory. BeginTurn 'fires' every time a turn begins (once for each player, not once for every human turn). MoveUnit fires every time a unit is moved. Activision provided a list of the events that SLIC recognises (>> SLIC-Functions).
Note that for each event, there are one or two (and occasionally three) variables listed after the event. for example:

GetExpelledOrder(army_t, location_t, int_t<player>)

This is an event that will trigger a handler for example, you want to make it so that whenever an army is expelled, the player who owns the army will be told about it.
The variables after the event are automatically generated into the built-in arrays so that when you trigger a handler on the GetExpelledOrder event, army[0] will be the "name" for that army in context, location[0] will be the army's location in context, and player[0] will be the army's owner (I think).

In diplomacy events in particular, you often get 2 players listed. These will be stored as player[0] (the person making the proposal for instance) and player[1] (the recipient). This is different from player 0 (barbarians) and player 1 (Usually the human. Almost always the civ in blue)

2.2 - Structure of Handlers

Event Handlers will all look something like this:

HandleEvent(BeginTurn) 'handler_name' post {
  • The first bit 'HandleEvent' is always there. It defines the next block of code as an event handler.
  • Then the parentheses open, and you have to fill in an event that you want the code to fire on. (ie. define your x)
  • Between the ' ' marks, fill in a handler name. To avoid confusion (for yourself and the game) this shoud be named differently to every other handler, and not have the same name as any variables you are using.
  • Then comes the pre/post bit. Most of the time it doesn't matter whether the code fires before or after the in-game stuff has been taken care of, but occasionally if you want one trigger to fire before the other, then set one to pre (before) and one to post (after).
  • Finally, the brackets open, to signify that everything inside them is part of the event handler.

If you are using EditPlus2 with the SLIC addon, you should get a colourful line of blue-red-pink-blue, and then you know you didn't make any typos or whatever.

2.3 - Example

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 KillUnit(unit[0]);
}

This is a very simple event handler. It consists of one event (the MoveUnit event), and one reaction, a function. It kills the unit that has just moved.

3.0 Variables

Variables in algebraic mathematics are numbers assigned a name, because their actual values change. In SLIC, this is much the same, only as well as integer variables (NB not decimals, SLIC can't use them), SLIC also uses unit variables, army variables, location variables and city variables. Player variables are treated as integers.

In the above example of an event, the event stores three variables, and these are denoted by army_t, location_t, int_t&lt;player&gt; The &lt;player&gt; bit is irrelevant, the player is just a number to the game. In functions, to get them to work properly, you have to fill into them the correct variables that you want the functions to work on (see chapter 5).

3.1 - Defining Variables

To make SLIC recognise the variables for what they are, so that it doesn't start treating a unit as a city, or a location for an integer, you have to define what each variable that you use is. This can be done inside the handler, or outside the code, on its own. The former is a local variable. It applies only to that handler. If you use it in another handler, you must redefine it. The latter is a global variable, it applies to all functions and handlers in the code, and can be used in messagebox text (see chapter 6).
NB. If you are mixing your SLIC with that of other peoples, chances are you may have used the same variables somewhere, so it is recommended to define variables locally, unless you need them global for some reason, in which case name them with really obscure names.

To define a variable, you need a new line, to tell the game what each variable is.
eg.

int_t tmpNum;   // tmpNum (temporary number) will be treated as an integer
unit_t tmpUnit;  // tmpUnit (temporary unit) will be treated as a unit
location_t tmpLoc;  // tmpLoc will be treated as a location
army_t  tmpArmy; // tmpArmy will be treated as an army
city_t tmpCity;  // tmpCity will be treated as a city
   // note semicolons at the end of each line

Once they are defined, variables can be used below that point. Most SLIC files keep global variables at the very top, so they apply to the whole code.

3.2 - Built-in Variables

>> SLIC Variables

As mentioned in section 2.1, and in Locutus' guide, SLIC comes with built-in variables that are already defined. They can be used as a suffix to any other defined variable eg. if tmpPlayer is defined as a player, tmpPlayer.cities will automatically be the number of cities that tmpPlayer owns.

These built-in variables are very useful to save time doing a CityCount(tmpPlayer) and similar functions all the time.

For some reason, these variables seem to work better (for me at least) if the player (or city, location, army, unit etc) is stored into the context array of player[0] (or city[0], location[0]...). If it isn't, then you can easily just do:

player[0] = tmpPlayer;

Which puts tmpPlayer into the player[0] array.

3.3 - Using Variables in Functions

Really related to chapter 5, but functions need to have the correct variables put into them.

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;  // define variable
 tmpUnit = unit[0]; // define what it actually <i>is</i>
 KillUnit(tmpUnit); // the KillUnit function needs to have a unit_t in it.
}

Using the >> function reference, you can see which functions need which variables, and make sure you have the right ones available and defined.

4.0 Boolean Logic and if, for and while loops

4.1 - Boolean Logic

For me to do a guide to Boolean logic and calculations in SLIC would be foolish, when Locutus did such a comprehensive write-up in "Locutus' Ultimate Guide To SLIC".

Reminder:
= make equal to
== is equal to
!= is not equal to
> is greater than
< is less than
>= greater than or equal to
<= less than or equal to

4.2 - If, Elseif, and Else Statements

Pretty much the same as if statements in any programming language, and even in any spoken language.

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;    // define variable
 tmpUnit = unit[0];   // define what it actually <i>is</i>
 player[0] = unit[0].owner;  // name the unit's owner (it is not implicit in the event)
 if(player[0].cities > 10){ // if statement. IF the unit's owner has 10 cities...
  KillUnit(tmpUnit);  // if 'yes', then do function. Kill the unit.
 }    // close if statement
}      // close handler

If Statements return 1 or 0 , true or false. If the if statement is true (1) then the code inside inside the brackets is executed, otherwise it is skipped over.

This leads to elseif and else statements.

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;    // define variable
 tmpUnit = unit[0];   // define what it actually <i>is</i>
 player[0] = unit[0].owner;  // name the unit's owner (it is not implicit in the event)
 if(player[0].cities > 10){  // if statement. IF the unit's owner has 10 cities...
  KillUnit(tmpUnit);  // if 'yes', then do function. Kill the unit.
 }     // close if statement
 elseif(player[0].cities < 3){  // ELSE, IF (elseif) the unit's owner has less than 3 cities...
  KillUnit(tmpUnit);  // if 'yes', then do function. Kill the unit.
 }     // close elseif statement
 else{     // ELSE. (none of the above)...
  Heal(tmpUnit);   // if you haven't killed it in an if loop above, then heal the unit.
 }    // close else loop
}      // close handler

4.2 - For Loops

This is a way of executing a bit of code many times, without having to write it out many times.
Usually, this is done by having a counter which counts the number of times the loop has been done, and when it reaches the upper boundary you set, it stops.

The basic structure of a for loop is:

// define counter, usually i for integer
int_t i;
// for statement ( starting value-semicolon-value is less than value-incremental increase) bracket {
for(i = 0; i < 8; i = i + 1){
 // code
//close loop
}

Note, for loops can run in reverse, starting high, and decreasing to 0. See some of Martin Guehmann's code, as he uses it far more frequently that anyone else.
eg.

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;      // define variable
 tmpUnit = unit[0];     // define what it actually <i>is</i>
 player[0] = unit[0].owner;    // name the unit's owner (it is not implicit in the event)
 if(player[0].cities > 10){    // if statement. IF the unit's owner has 10 cities...
  KillUnit(tmpUnit);    // if 'yes', then do function. Kill the unit.
 }       // close if statement
 elseif(player[0].cities < 3){    // ELSE, IF (elseif) the unit's owner has less than 3 cities...
  KillUnit(tmpUnit);    // if 'yes', then do function. Kill the unit.
  int_t i;    // define counter variable
  for(i = 0; i < player[0].cities; i = i + 1){ // for each of player[0]'s cities...
   AddGold(player[0], 5000);  // give 5000 compensation
  }      // close for loop
 }       // close elseif statement
 else{       // ELSE. (none of the above)...
  Heal(tmpUnit);     // if you haven't killed it in an if loop above, then heal the unit.
 }       // close else loop
}        // close handler

4.3 - While Loops

Can run similar to for loops, so that you run some code a certain number of times. eg.

HandleEvent(BeginTurn) 'while_loop' pre {
 int_t i;    // define counter variable
 i = 0;      // starting value
 while(i < player[0].cities){   // upper boundary
  AddGold(player[0], 5000);  // give 5000 gold
  i = i + 1;    // incremental increase to i
 }      // close while loop      
}       // close handler

Another way while loops can work is when you want to identify one thing from many. This is equivalent to running a for loop until you find the one you want, and then breaking the cycle.

This code is taken and adapted from Locutus' piracy code:
(I should mention, random(xxx) gets a random number between 0 and xxx)

HandleEvent(BeginTurn) 'while_loop' pre {
int_t xcoord;
int_t ycoord;
location_t tmpLoc;
 xcoord = Random(GetMapWidth());    // 
 ycoord = Random(GetMapHeight());   // Initiate the numbers
 MakeLocation(tmpLoc, xcoord, ycoord);   // and the location
 while (TerrainType(tmpLoc) < 10) {   // IF the terrain at tmpLoc is below 10 (plains -> mountains)
  xcoord = Random(GetMapWidth());   // reinitiate
  ycoord = Random(GetMapHeight());  // the numbers,
  MakeLocation(tmpLoc, xcoord, ycoord);  // and keep going, until the logic statement does not hold true
        // then you end up with the location you want
 }      // close while loop      
}       // close handler

5.0 - Functions

>> Function reference

Functions come in two types, as, I beleive they do in C. Integer functions (int_f) return a number, and void functions (void_f) don't return anything, they just execute some action.
I think in essence, with SLIC as a way to code reactions to events, the void_f functios will be the reaction, and the int_f functions will be a way of narrowing down where the reaction occurs.

5.1 - Code with Functions In

In reality, it is nearly impossible to code anything without using functions, but some code is more based on functions than others.
Going back to this example:

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;       // define variable
 tmpUnit = unit[0];      // define what it actually <i>is</i>
 player[0] = unit[0].owner;     // name the unit's owner (it is not implicit in the event)
 if(player[0].cities > 10){     // if statement. IF the unit's owner has 10 cities...
  KillUnit(tmpUnit);     // if 'yes', then do function. Kill the unit.
 }        // close if statement
 elseif(player[0].cities < 3){     // ELSEIF the unit's owner has less than 3 cities...
  KillUnit(tmpUnit);     // if 'yes', then do function. Kill the unit.
  int_t i;      // define counter variable
  for(i = 0; i < player[0].cities; i = i + 1){  // for each of player[0]'s cities...
   city_t tmpCity;   // define
   GetCityByIndex(player[0], i, tmpCity);  // Find the city currently being dealt with
   if(CityHasBuilding(tmpCity, BuildingDB(IMPROVE_SILO))){  // if city has silo
    AddGold(player[0], 5000);  // give 5000 compensation 
   }
   elseif(CityHasBulding(tmpCity, BuildingDB(IMPROVE_GRANARY))){ // otherwise if it has a granary
    
    AddGold(player[0], 3000);  // give 3000 gold
   }
   else {
    AddGold(player[0], 1000);  // otherwise, give just 1000
   }
  }       // close for loop
 }        // close elseif statement
 else{        // ELSE. (none of the above)...
  Heal(tmpUnit);      // if its still there, then heal the unit.
 }        // close else loop
}         // close handler

In the emboldened section are the functions GetCityByIndex, CityHasBuilding and AddGold.
GetCityByIndex and AddGold are void_f functions, they do not return any integers, they just react.

AddGold is a simple action function, it adds gold to the player's treasury.

GetCityByIndex is more complicated, though it doesn't return a number, it doesn't act as such. What it does is return a city, in the form of a variable defined beforehand, in this case tmpCity. tmpCity can now be used inside the for loop to act on that city, (after the for loop, it will only apply to the last city found in the for loop (ie when i = player[0].cities - 1).

CityHasBuilding is an int_f function, so is usually used in a if statement. IF(CityHasBuilding(tmpCity, BuildingDB(IMPROVE_GRANARY))) THEN { do something. IF it doesn't have that building, then that reaction does not apply in this case. The int_f has limited the number of cases where the reaction applies, as carried out by the void_f function AddGold.

5.2 - Nested Functions

It is possible to use functions inside other functions to save the trouble of defining and using variables all the time.
eg.

city_t tmpCity;
GetCityByIndex(player[0], random(PlayerCityCount(player[0])), tmpCity);

This will get a city belonging to player[0] with a random index number. The random number is chosen from between 0 and player[0]'s amount of cities, so will always return a valid city. The city is then stored as tmpCity.
This saved the bother of storing PlayerCityCount(player[0]) as a variable, then finding random(player[0].cities) and storing that, and then plugging that into the GetCity... function.

5.3 - User-made Functions

As well as the built-in functions, SLIC allows you to define your own functions and use them in handlers. These had me really confused at the start, as I could see loads of code with no event to trigger it, and then functions in handlers which were not listed. A good example of a custom function is Dale GetNearestCity function.

int_f GetDistanceNearestCity(location_t theLoc, int_t thePlayer) { 
    // this is an int_f function needing an input of location and player
    // definitions cut for space...
   
 tmpPlayer = thePlayer;
 tmpLoc = theLoc;
 min = 10000;
 city = 0;    
 cities = Cities(tmpPlayer);
 for(i = 0; i < cities; i = i + 1) {
  GetCityByIndex(tmpPlayer, i, tmpCity);
  val = SquaredDistance(tmpCity.location, tmpLoc);
  if(val < min) {
   min = val;
   city = i;
  }
 }
 return city;     // this is the integer that the function returns at the end.
}       // It is the index of thePlayer's closest city.

This function is used inside the main Withdraw script handler to find the nearest city that the aeroplane should return to.
Inside a handler, the title of the fnction becomes a function, so putting it into the example handler from above:

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;       
 tmpUnit = unit[0];      
 player[0] = unit[0].owner;     
 if(player[0].cities > 10){     
  KillUnit(tmpUnit);     
 }        
 elseif(player[0].cities < 3){     
  KillUnit(tmpUnit);     
  int_t i;      
  for(i = 0; i < player[0].cities; i = i + 1){  
   city_t tmpCity;   
   GetCityByIndex(player[0], i, tmpCity);  
   if(CityHasBuilding(tmpCity, BuildingDB(IMPROVE_SILO))){  
    AddGold(player[0], 5000);  
   }
   else {
    AddGold(player[0], 1000);  
   }
  }        // Once all cities have been done..
  int_t tmpIndex;
  tmpIndex = GetNearestCity(unit[0].location, player[0]);  // find the nearest home city to the unit
  GetCityByIndex(player[0], tmpIndex, tmpCity);   // store that city
  CreateUnit(player[0], unit[0].type, tmpCity.location, 0); // create a unit of the same type there
 }        
 else{        
  Heal(tmpUnit);      
 }        
}

That is an example of using a new function as a time and space saving device inside a handler. Often the function will be a small part of the code that is primarily the reaction to the event.
Occassionally though, the function is more important, and the handlers are needed only to trigger it at certain times.

5.4 - Function-Based Code

One of the simplest example of this type of code is Locutus Capitol Code for the MedMod.
He uses a function to rebuild a capitol, and the handlers merely as triggers to run the function.

// function that actually finds a replacement city for the capital and makes it build a new one
int_f MM2_CreateCapital(int_t thePlayer) {
city_t tmpCity;
city_t largestCity;
int_t i;
int_t size;
int_t tmpPlayer;
 tmpPlayer = thePlayer;
 player[0] = tmpPlayer;
 if (player[0].cities > 1) {    // if player has more cities
  size = -1;
  for (i = 0; i < player[3].cities; i = i + 1) { // cycle through all cities
   GetCityByIndex(tmpPlayer, 0, tmpCity);
   if (tmpCity.population > size && !CityHasBuilding(tmpCity, "IMPROVE_CAPITOL")) {
   // check for capitol needed since this is executed pre capture, so old capital is evaluated as well
    if (IsWonderAtHead(tmpCity) == -1) {
    // check for wonder at head of build queue is needed since ClearBuildQueue doesn't clear queues with wonders at head
     largestCity = tmpCity; // store city
     size = largestCity.population;
    }
   }
  }
  if (size > -1) {    // if a city was found
   ClearBuildQueue(largestCity);  // clear existing buildqueue
   AddBuildingToBuildList(largestCity, BuildingDB(IMPROVE_CAPITOL)); // start building a Capitol
   Event:BuyFront(largestCity);  // (try to) rush buy it
  }
 }
}

and the handlers that fire it when a capitol is destroyed:

// detect the capture of a Capital and call CreateCapital function
HandleEvent(CaptureCity) 'MM2_CapitalCaptured' pre {
int_t tmpPlayer;
city_t tmpCity;
 tmpCity = city[0];
 tmpPlayer = tmpCity.owner;
 player[3] = tmpPlayer;
 if (CityHasBuilding(tmpCity, "IMPROVE_CAPITOL")) { // if city is capital/has capitol
  if (!IsHumanPlayer(tmpPlayer)) {  // if player isn't human
   MM2_CreateCapital(tmpPlayer);  // find new capital
  }
 }
}
//ditto for KillCity event, GiveCity event, DisbandCity event, and CreatePark event.

Using functions separate from the main code means that the code is only written out once, rather than once for each event. This makes the file shorter, easier to manage and debug, and not as spacious for uploading or downloading.

5.5 - Events as Functions

As well as using events to trigger reaction code, events can be forced to happen, just as functions can cause things to happen.
eg. Taken a bit from example handler.

int_t tmpIndex;
 tmpIndex = GetNearestCity(unit[0].location, player[0]);  // find the nearest home city to the unit
 GetCityByIndex(player[0], tmpIndex, tmpCity);   // store that city
 CreateUnit(player[0], unit[0].type, tmpCity.location, 0, tmpUnit); // create tmpUnit of the same type there.
 Event:EntrenchUnit(tmpUnit);      // Use entrench event to fortify it.

This forces the game to recognise that the fortify event has happened, as if someone pressed the F button.
Most events in the events listing can be used like this. The ones that can't are the ones where one of the variables has a GEA_ prefix in the list. These variables are paths, and are too complicated to define in SLIC (>> SLIC Events).

6.0 MessageBoxes

Messageboxes come in two forms. Mesageboxes and alertboxes. Messageboxes can be left open, while the game continues around them. Alertboxes force play to be stopped until you have responded to them.
A typical messagebox in SLIC looks like this:

messagebox 'kill_unit_or_not' {   // definition messagebox, name, open bracket
 Show();    // otherwise it wouldn't show
 Title(ID_TITLE);  // message title
 Text(ID_TEXT);   // message text
 Button(ID_YES){   // button
  Kill();   // effect button has. Kills text box
  KillUnit(unit[0]) // kills the unit 
 }    // end button effect
 Button(ID_NO){   // other button
  Kill();   // only kills the text box
 }    // end button effect
}     // close

Alertboxes are exactly the same as messageboxes, except instead of having the word messagebox where it is bold, have the word alertbox.

Title, text and button text should be stored in scen_str.txt in ctp2_data/<i>language</i>/gamedata directory (Without the ID_ prefix, that is just to show the SLIC what it is)

To make a message appear, use the message function in the handler.

HandleEvent(MoveUnits) 'kill_the_unit' pre {
 unit_t tmpUnit;       
 tmpUnit = unit[0];      
 player[0] = unit[0].owner;     
 if(player[0].cities > 10){     
  KillUnit(tmpUnit);     
 }        
 elseif(player[0].cities < 3){     
  KillUnit(tmpUnit);     
  int_t i;      
  for(i = 0; i < player[0].cities; i = i + 1){  
   city_t tmpCity;   
   GetCityByIndex(player[0], i, tmpCity);  
   if(CityHasBuilding(tmpCity, BuildingDB(IMPROVE_SILO))){  
    AddGold(player[0], 5000);  
   }
   else {
    AddGold(player[0], 1000);  
   }
  }        // Once all cities have been done..
  int_t tmpIndex;
  tmpIndex = GetNearestCity(unit[0].location, player[0]);  // find the nearest home city to the unit
  GetCityByIndex(player[0], tmpIndex, tmpCity);   // store that city
  CreateUnit(player[0], unit[0].type, tmpCity.location, 0); // create a unit of the same type there
 }        
 else{        
  message(player[0], 'kill_unit_or_not');   // give a choice whether or not to kill it      
 }        
}

7.0 Mod_ functions

The wierdest of all SLIC, these are functions that do not have to be triggered, but apply all the time in the game. There are theoretically eight, but only four have obvious effects. These are:

int_f mod_CanPlayerHaveAdvance(int_t thePlayer, int_t theAdvance)
{
int_f mod_CanCityBuildBuilding(city_t theCity, int_t theBuilding)
{
int_f mod_CanCityBuildUnit(city_t theCity, int_t theUnit)
{
int_f mod_CanCityBuildWonder(city_t theCity, int_t theWonder)
{

They do exactly as they suggest, they limit which techs, wonders, buildings and units are available to players and cities.
You can use code (mostly if statements I should think) to limit to where the rules apply. If in doubt, copy a known format. The Alexander the Great scenario has a basic example of limitations to a tech tree, and the MedMod2 restricts the use of some units. I think the only place that uses the mod_CanCityBuildBuilding function is my Mars 2020 scenario (currently in beta testing), so here is the code for that.

int_f mod_CanCityBuildBuilding(city_t theCity, int_t theBuilding)
{
 
 int_t tmpBuilding;
 tmpBuilding = theBuilding;
 tmpCity = theCity;
 city[0] = tmpCity;
 if(tmpBuilding == BuildingDB(IMPROVE_SOLAR_PLANT)){
  if(g.year >= 48){
   return 1;
  }
  else {
   return 0;
  }
 } elseif(tmpBuilding == BuildingDB(IMPROVE_HOSPITAL) || tmpBuilding == BuildingDB(IMPROVE_PUBLISHING_HOUSE)) {
  if(G.YEAR >= 36){
   return 1;
  }
  else {
   return 0;
  }
 } elseif(tmpBuilding == BuildingDB(IMPROVE_ORBITAL_LABORATORY)
|| tmpBuilding == BuildingDB(IMPROVE_RECYCLING_PLANT)
|| tmpBuilding == BuildingDB(IMPROVE_UNIVERSITY)) {
  if(G.YEAR >= 24){
   return 1;
  }
  else {
   return 0;
  }
 } elseif(tmpBuilding == BuildingDB(IMPROVE_ACADEMY)){
  if(g.year >= 12){
   return 1;
  }
  else {
   return 0;
  }
 } elseif(tmpBuilding == BuildingDB(IMPROVE_AQUEDUCT)){
  tmpNum = city[0].population;
  if(tmpNum >= 3){
   return 1;
  }
  else {
   return 0;
  }
 } elseif(tmpBuilding == BuildingDB(IMPROVE_COMPUTER_CENTER)){
  tmpNum = city[0].population;
  if(tmpNum >= 3 && g.year >= 6){
   return 1;
  }
  else {
   return 0;
  }
 } else {
  return 1;
 }
}

Start with deciding which building you want to limit the use of, then decide who you are limiting the building to or from, and why.

If anyone wants to experiment with the other four, they are:

mod_CityHappiness
mod_UnitAttack
mod_UnitRangedAttack
mod_UnitDefense

8.0 End Note

As I said at the beginning, this is not necessarily a good way to learn SLIC, but it is in theory the way I learned. In actuality, I learned by doing. Hack as much code from other places as you can. Some of the functions are buggy, some don't work, some have limitations. Read around the Apolyton Mods forum right back to the beginning, and download everything you can. (If you can understand BlueO's city expansion then you're there!) Read as much SLIC as you can, and try to figure out why their code works. If your's doesn't, try for a workaround, model your code on other people's, and start a help thread in the forum, we the SLICers will help, 'cos we all had difficulties to start with.

So...
Before you code, have a method
Work though the method, be flexible, and willing to mix methods to get a result.
The best debugger is the game itself. Put DebugSlic to Yes in userprofile.txt to get rid of errors.
Alt-Tab out, fix the bug, and /reloadslic. Repeat <i>ad nauseum</i>
When it goes right, no matter how clumsy it looks in relation to other code, its a great feeling :)
And have fun!

  Administration
This site is currently maintained by: BureauBert (Webmaster)  Maquiladora (Manager)  Legal Notice: Legal Notice Statistics: 71193 unique users since 02.05.2004 (» Site Statistics) Hosted by: WebhostOne
Share this page:  VK Facebook Twitter LiveJournal OK MR Google+ LinkedIn tumblr Pinterest Blogger Digg Evernote