7.1 Mods and Rulesets

Getting Started
This section references assumes basic knowledge of the Dark Reign 2 scripting language learned in the Single Player section, and it is highly recommended you read through that section before proceeding.

Scripting and making rulesets for multiplayer has quite a few differences than single player, and involves thinking from every player's viewpoint rather than an individual team's. In SP, most of your scripts explicitly call a team by their name, which, while possible in MP, is not recommended and usually leads to making separate configs for each team. Instead, you should be calling teams using Relation() and @.engine.name, two things we'll discuss below. Another difference in SP vs. MP is compatibility with allies. This involves using the Relation() tag quite a bit, as well as making sure the scripts, especially triggers, work correctly for ALL allies. These will all be covered in this document.

It is recommended you unpack (using Drpack) a ruleset, such as Control Freak, and look at the files as we go along.

How Scripts Run
As mentioned earlier, SP and MP scripting differs quite a bit. SP is much more focused on specific teams, while an MP script must be duplicated for every team that is playing, and thus the way you use variables, Team(), Relation(), and a few other things is a bit different. StartActionAvailable() is used to define what variables and objectives are given to the available players. These objectives are located in whatever types file you have defined.

Multilanguage tools & rulesets
In all of DR2's rulesets and SP missions, you will find messages like #game.message.whatever. These are references to a Multilanguage file, called "[language].cfg" where language is what language it is in (such as English.cfg). The purpose of this is to make all of DR2 have no references to one language. This file is the only file that should not be modified, and not have a special version in a ruleset's directory. You should just write in plain text, and have somebody help convert your mod to other languages if necessary.

File Structure
Before you begin making your ruleset, you should have a good idea of how the files are laid out, so that you know what is available to you. Rulesets are broken down into four different config file types:

Mod.cfg
The mod.cfg file contains the basic information about the ruleset, as well as what variables are needed by the players, the system, and what files to include in the ruleset.

Types file
Generally named after the ruleset, such as 'control_types.cfg', the types file can contain all the objectives of the particular ruleset, as well as special object configs, particle configs, etc, that you don't want to store in another file. It's basically the Jack-of-all-trades file. You can include many types files if you feel the need to organize them that way (for instance, an objects file, a particle file, etc.)

Exec file
Exec files are used for manipulating variables, interface controls, keybindings, and other functions not in the types file.

AI file
If necessary, you may include AI files into the game. This is mostly used in Personalities, but can be used in rulesets as well.

Mod.cfg Breakdown
Here is a quick overview of the mod.cfg file:
Description("#mods.ruleset.assault.title");
Description is to give your ruleset a name when selecting it. Unfortunately, you cannot use the Multilanguage tool to put in your description, so you just have to type in the name here.

Author("Pandemic");
Self Explanatory. Put your name in here.

Homepage("http://www.pandemicstudios.com");
Another easy one - if you have a homepage, put it here.

Download("http://www.pandemicstudios.com/dr2/addons/assault.zwp");
This is the exact URL where people will be able to download this file.

Private(1);
If you are creating a ruleset that you want to work only on specific maps, this should be set to 1. If you are creating just a ruleset that can work on any map, set this to 0.

Files("Type")
This includes all Type files into the ruleset.


Files("Type")
{
 Add("assault_types.cfg");
 Add("types_objects_special_togranprops.cfg");
}


Files("Exec")
This includes any exec files into the ruleset.
Files("Exec")
{
 Add("assault_exec.cfg");
}



Files("AI")
This includes any AI settings you want.
Files("AI")
{
 Add("assault_AI.cfg");
}


StartActionClient()
Action given to the client team, used for global mission variables. This StartAction only gets created once. Notice Global variables have a ~. In front of them instead of @.
StartActionClient()
{
 CreateVarFloat("~.assault.time", 3600);
 CreateVarInteger("~.assault.minutes", 60);
}


StartActionTeam("Team")
Action given to a team based on name (used to specify a particular team to get objectives). Generally this is used to specify special teams that are not controlled by a player. In Assault, this was the special AI team.
StartActionTeam("worship")
{
 NewObjective("assault.countdown");
 NewObjective("assault.counttimer");
}


StartActionAvailable()
Action given to a team based on the 'Available To Players' button pressed in the team setup.
StartActionAvailable()
{
 CreateVarInteger("@.assault.hqchecker", 0);
 CreateVarInteger("@.assault.timeron", 0);
 CreateVarInteger("@.assault.hqneed", 0);
 NewObjective("assault.spawn");
}

StartActionAll()
Action given to all teams

StartActionSide("Side")
Action given to a team based on side. Mostly used in AI personalities, it can also be used in rulesets if need be. 'Side' should be Sprawler or JDA (unless you've created your own!)

Standard Rulesets vs. Map Specific Rulesets
One of the first things to decide when making a Ruleset is if you want it to work on ANY map or only maps that are made specifically for it. Map Specific rulesets give you much more flexibility to create, but you sacrifice having many maps to play on. There are quite a few ways of using different map features work well with a ruleset:

Regions
These are probably used the most in Map Specific Rulesets. Using the InRegion() function, you can trigger events when any specific type of unit, enemy, ally or self, moves in a region. You can also spawn units in regions. For an example of a complex InRegion() check, look at Control Freak's control_types.cfg file.

Tags
Tags are useful for attaching to units or other assets that you want to control later on in your mod. To create them, see Making Good Maps

Assets
Using certain assets can help in any part of a ruleset. For instance, Control Freak has 5 Control Points on each map that you must take over. Assault has a large portal you must go through to win, as well as a fully fortified AI base.

Strategies & Design
See Making Good Maps

Files that can be in a ruleset
Just about any file can be placed in a ruleset's directory, from textures, configs and audio clips to 3d objects and more. If you want to override specific files the game uses, naming files the same name and placing them in the ruleset directory should work perfectly. There are certain exceptions (such as Multilanguage configs.)

Variables
Variables are covered a bit in SP, but are used much more in MP. They're really essential to rulesets and let you do just about everything.

Global Variables
A global variable is something that every player's objectives can affect and see. Defining a global variables is done on StartActionClient(). For instance, in Control Freak, five global variables are set, one for each control point. All other teams can see these five variables, and if a team takes a control point, the objective records that teams name into the variable. These are the best way to get each team's scripts to communicate with one another. (Ex: ~.control.CP-A)

Local Variables
Local variables are variables only seen by the one team running a script. All other teams cannot see what one team's local variables are. These are used mostly for triggers, timers and the like. (Ex: @.control.CP-A)

Special Variables
If you want a team's specific name, listen up: @.engine.name gives the name of the team. All maps currently have teams named 'TeamX' where X is a number 1-8, except for assault, which has the team 'assault.' This variable can come in handy in all sorts of situations, such as Control Freak, which gives @.engine.name to a global variable, and checks to see if all five global variables for each control point are controlled by allies. In Assault, @.engine.name is used for the regions which a Dezigner piece is spawned.

CreateVarInteger
This creates an integer variable that stores whole numbers.

CreateVarFloat
This creates a floating-point variable, that can store any number or decimal.

CreateVarString
This creates a string variable that you can store text and/or numbers in.

NOTE: Variables can only have TWO periods in them, no more. (i.e. no @.ab.bc.cd, but @.ab.bc-cd will work)

More Information on variables is available in Section 5.7

Common Objectives
Dark Reign 2 has a few common functions that you can call using NewObjective() These are all defined in types_common.cfg (in case you want to look at them directly.) They are as follows:

Common.deathcheck
This checks to see if a player's units have been destroyed, at which point that player will get common.eliminate.

Common.killallenemy
This checks to see if all other player's have been destroyed. If so, player gets common.timer.win.

Common.timer.win
Win condition (triggers end of game). This gives a 3 second delay between satisfying the victory conditions and getting the win condition (delay is there to allow for self destruction of units and other win condition objectives you want completed, such as earthquakes or whatnot.)

Common.aivictory
This checks to see if an AI player has won, and if so, triggers common.timer.win.

Common.eliminate
This eliminates a specific player (and thus their team), but allows the game to continue. 4 second delay.

Common.timer.lose
Lose condition (triggers end of game). This also has a 3 second delay. If you feel it's easier to have teams lose than have the other team(s) win, you can use this.

Logging Types (cash, kills, etc)
In rulesets such as Bloodbath and Gluttony, a condition known as 'Tally' will allow you to load up a variable with information about how many units a player constructed, destroyed, etc, how much taelon they collected, etc. These can be very useful not only for simple rulesets using one of these as a win condition, but for advanced ruleset/map combinations that involve a series of tallies to trigger actions.
Condition("Tally")
{
  Statistic("KillsSelf");
  Amount(10);
  Operator(">=");
  Var("@.self");
}


Radio Events & Objective Text
An important part in a ruleset is to make sure players know what they are supposed to do, and if they've done something right or wrong. Combining messages seen in the console and at the top of the screen with Objective Text in it's own pop up box will let you get your message across clearly.

Two types of Radio Events exist, Location Messages and Game Messages. You can attach a variable to a message if you need.

Often times the naming convention of messages usually follows what your mod is called. In Control Freak, for example, all objectives and radio events began with 'control.', such as 'control.taken.' This is not required, but is recommended to help you debug.

As we've learned, variables play a very big part in rulesets. You can use variables in Radio Events as shown below:


ConfigureRadioEvent("modname.radio")
{
  Messages()
  {
    LocationMessage::Self("modname.selfhelp", "@.engine.name");
  }
}

ConfigureLocationMessage("modname.selfhelp")
{
  Interval();
  Sound();
  Message()
  {
    Type("Message::Console");
    Add("You, {1:s}, need some serious help.");
  }
}



Notice that a variable was called in the message using {1:s}. Since @.engine.name will show up as a STRING, you must used {1:s}. If you had an integer, it would be {1:i} and if you had a float it would be {1.0:f}.

If you want to call a specific player or team in a message, using #player and #team in the variable section in GameMessage:: and LocationMessage:: will give you the player's name (if CoOp'd, only the first player will show), and if you use #team, the player's team will show (not used at all in mods, but it's there.)

NOTE: If a string variable is used in a message, no other variables will work in it.

Also, if you JUST want a blip to show up, you do not have to fill in the Message() at all, instead you can add 'BlipColor()' with an appropriate RGBA color. Only Location Messages give blips, so this is not useful for a Game Message.


ConfigureLocationMessage("modname.beep")
{
  Interval();
  Sound();
  Message();
  BlipColor(255, 100, 0, 100);
}


Remember, Location Messages require a region to give a blip. So when you are calling a location message, you MUST give a region (that has been defined in the studio) for that message to show up on.
TriggerTeamRadio("modname.beep")
{
  Region("beepland");
}