[an error occurred while processing this directive]
[an error occurred while processing this directive]
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

  • Language
  • Message Boxes
  • Alert Boxes
  • Buttons
  • Triggers
  • Built-in variables
  • Trigger priorities
  • Functions
  • Regions
  • User variables
  • Miscellany
  • 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:
  • Variables - SLIC supports integer variables, as well as special variables to contain units, cities, and locations, and a large set of special built in variables (described in Built-in variables) Integer variables do not need to be declared prior to use and are always initialized to 0. Unit, City, and Location variables DO need to be declared outside of any object and before they are used in your code. They can be declared with the TypeUnit, TypeCity, and TypeLocation directives. All variables are global - if you change them in one object, they will have the same value in every other object. Builtin variables cannot be modified, only examined.
  • Statements - function calls and expressions (including assignments). Examples:
        Message(g.player, 'AMessage');
    	Text(ID_SOME_TEXT);
    	a = b;
    	a = b * c + (d / e);
    	a = b + UnitsInCell(city.location);
    	
  • If/ElseIf/Else clauses - much like C, except that ElseIf is used instead of "else if". Note that SLIC is case insensitive, thus If, if, elseif, ElseIf, ELSEIF, else, eLsE, and so on, are all valid. Example:
        if(a) {
    	  Text(ID_TEXT_WHEN_A_IS_TRUE);
        } elseif (b) {
          Text(ID_TEXT_WHEN_B_IS_TRUE);
        } else {
    	  Text(ID_TEXT_WHEN_NEITHER_A_NOR_B_IS_TRUE);
        }
      
  • While Loops - again, these work like C. This loop sends one message for every city the given player owns:
      aCity = 0;
      while(aCity < player.cities) {
         Message(player, 'AMessageAboutACity');
         aCity = aCity + 1;
      }
      
  • Messages - Described below
  • Triggers - Described below
  • Buttons - One of SLIC's primary functions is handling the in game messages. So a method of specifying the buttons to place on messages is provided. See the section below on buttons
  • 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:

  • EyePoint(location) - Any variable that contains a location (unit, city, unit.1.location, etc.) can be used with this function to provide an "EyePoint" button that centers the map on that location.
  • EyeDropdown(startIndex, list variable) - Any builtin variable that can be used as a list (currently unit, city, and discovery) can be used with this function to provide a dropdown list of eyepoint locations to zoom to, or in discovery's case of things to research. Discovery is really only useful for the choose a discovery dialog. The startIndex parameter is useful to make the dropdown list start at an index other than 1. For example, when the discovery messagebox is invoked, discovery.1 is the discovery that was just discovered, and discoveries that can now be researched start at discovery.2.
  • 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.
  • YEARLY - g.year, g.pollution, g.globalwarming, g.ozone
  • PLAYER - g.player, player.gold, player.cities, player.goods, player.discoveries, player.capitol, player.pollution, player.betrayedalliances
  • DISCOVERY - discovery.type
  • DISCOVERY_TRADED - discovery.traded
  • CITY_BUILT - city.built
  • CITY_PRODUCTION - city.building, city.buildingunit, city.buildingwonder, city.buildingpop
  • CITY - city.population, city.food, city.owner, city.happiness, city.combatunits
  • UNIT_BEGIN_TURN - unit.beginturn
  • WONDER_STARTED - wonder.started
  • WONDER_FINISHED - wonder.finished
  • CLICKED_UNIT - clicked.unit
  • IDLE - idle (NYI)
  • ATTACK - special.attacked
  • UNIT_MOVED - unit.moved
  • UNIT_DEAD - unit.dead
  • CITY_CAPTURED - city.captured
  • CONTACT - special.contact, unit.sighted
  • UNIT_BUILT - unit.built
  • SAME_GOOD - special.samegood
  • SAME_GOOD_AS_TRADED - special.samegoodastraded
  • TRADE_SCREEN - special.tradescreen
  • DIPLOMATIC_SCREEN - special.diplomaticscreen
  • UNIT_QUEUE - special.unitqueue
  • PRODUCTION_QUEUE - special.productionqueue
  • CREATE_STACK - special.createstack
  • CREATE_MIXED_STACK - special.createmixedstack
  • AUTO_ARRANGE_OFF - special.autoarrangeoff
  • BOMBARDMENT - special.bombarded
  • COUNTER_BOMBARDMENT - special.counterbombarded
  • ACTIVE_DEFENSE - special.activedefense
  • INDULGENCES - special.indulgences
  • TERRORISM - special.terrorism
  • CONVERSION - special.conversion
  • UNIT_LAUNCHED - special.unitlaunched
  • ENSLAVEMENT - special.citizenenslaved
  • POP_MOVED - special.popmoved
  • BUILD_FARM - special.buildfarm
  • BUILD_ROAD - special.buildroad
  • BUILD_MINE - special.buildmine
  • BUILD_INSTALLATION - special.buildinstallation
  • BUILD_TRANSFORM - special.buildtransform
  • SCIENCE_RATE - player.sciencerate
  • TRADE_OFFER - special.tradeoffer
  • TREATY_BROKEN - agreement.broken
  • 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.

    Function reference

    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

    [an error occurred while processing this directive]