Tug of War Tutorial

THE MAP FILE

        This tutorial shows you how to set up your map for TOW.


        First you need a multiplayer map built so you can place in the TOW objectives. Once you have a map ready and your spawn points in lets begin placing the TOW objective. Your .map and .scr file names must be the same and placed in the “/maps/obj/” folder. The filename must be in this form in order for the game to read them as a TOW map: “MP_filename_TOW.map”. filename can be anything you like, but the MP_ at the beginning and the _TOW at the end are necessary.
        You can place up to 5 objectives for your TOW map. For this example we are going to place 5 objectives in the level, one that destroys the axis spawn point, one that destroys the allied spawn point and three more that each side will have to fight over controlling. Objective 1 will be the allied spawn point and Objective 5 will be the axis spawn point. Objectives 2, 3 and 4 are what we are going to set up first. In our “MP_tutorial_TOW.map” we have three light switches that indicated who has control of the objective(Red light = axis, Green light = allies).
        In your map file, create a brush that will be your light switch box. Now create another brush surrounding the switch. This will be your trigger. With the brush selected, right click on the grid and select “trigger” -> “use”. Press the “n” key to bring up the entities window and give your trigger a targetname by enter the below values into the Key and Value fields.
                Key: setthread
                Value: switch1

Diagram 1-1 (Side View, Entity Window and 3D window)

        We have to create a TOW objective and link it to our trigger. Right click on the grid and select “func” -> “TOWObjective”. Press “n” to bring up the “Entity” window and enter the following TOW values into the Key and Value fields.

        Key: $targetname
        Value: switchObj1

        Key: AlliesObjNum
        Value: 2

        Key: AxisObjNum
        Value: 4

        Key: ControlledBy
        Value: 1

        Key: ObjectiveNbr
        value: 2

        Select the trigger that you created while the TOWObjective is still selected and press (ctrl + k) to link the two together. Now build three more Objectives following the same steps and using these values for trigger and TOWObjectives:

Trigger
Setthread		switch2

TOWobjective
$targetname		switchObj2
AlliesObjNum		3
AxisObjNum		3
ControlledBy		2
ObjectiveNbr		3

Trigger
Setthread		switch3

TOWobjective
$targetname		switchObj3
AlliesObjNum		4
AxisObjNum		2
ControlledBy		0
ObjectiveNbr		4

Diagram 1-2 (Top view)

        Now we have to add the last two TOW Objectives, the allied spawn and the axis spawn. First we will create a bomb and then we will set the TOW objective. Right click on the grid and select “script” -> “object”. Enter the following lines into the key and value:

	Key			Value
	$explosion_fx		fx/fx_flak88_explosion.tik
	$explosion_sound	explode_aagun
	$targetname		axis_bomb
	$trigger_name		allie_bomb_trigger
	model			items//pulse_explosive.tik

This will create your bomb. Around it create a trigger with the following values:

	Key			Value
	$targetname		allie_bomb_trigger

Now create a TOWObject and give it the following values:

	Key			Value
	$targetname		Obj_alliespawnpoint
	AlliesObjNum		1
	AxisObjNum		5
	ControlledBy		1
	ObjectiveNbr		1

Link the TOWobjective to the trigger. Now you have to set up the axis spawn point. Follow the same steps above but change all the targetnames and trigger_names to the opposite team. (allie = axis)

	Key			Value
	$explosion_fx		fx/fx_flak88_explosion.tik
	$explosion_sound	explode_aagun
	$targetname		allie_bomb
	$trigger_name		axis_bomb_trigger
	model			items//pulse_explosive.tik

This will create your bomb. Around it create a trigger with the following values:

	Key			Value
	$targetname		axis_bomb_trigger

Now create a TOWObject and give it the following values:

	Key			Value
	$targetname		Obj_axisspawnpoint
	AlliesObjNum		5
	AxisObjNum		1
	ControlledBy		0
	ObjectiveNbr		5

Diagram 1-3 (Top View and 3D View)

        Your TOW map will now work if you add the script provided to it. If you would like to add lights so you know who has control of the objective, then continue reading.
        Lets create the green light, that will be on when the Allies control an objective. First we will create the light bulb and then we will make the corona.
        Create a brush and then press “n” to open your “Entity” window. Select from the “Entity” window “addon_static_lamp_lightbulb-caged-green” and rotate it to face the direction you want.
        Next we add the corona. Create a brush and press “n” to open your “Entity” window. Select from the “Entity” window “addon_fx_corona_green-bright” and rotate it to face the direction you want. Give it a $targetname obj2_light_allie. The number after obj should correspond with the objective number you are working on(2, 3 or 4). Place the corona on top of your lightbulb and you are all set to go. Top make the red light bulb follow the same directions above but select the “red” objects instead of “green”. Also change the $targetname to obj2_light_axis. Make three sets of red and green lights and place them over their corresponding objective. Now lets move on to the script.


THE SCRIPT FILE

        To get an understanding on how the scripting for TOW is read the Tug of War doc. Here we will just go over the things not covered in that doc.


main:	

	level.script="maps/obj/MP_Tutorial_TOW.scr"		
	exec global/tow_dm.scr

	level.gametype = int( getcvar( g_gametype ) )

	//gametype 5 = Tug of War
	if( level.gametype == 5 )
	{			
		setcvar "g_obj_alliedtext1" "Protect Spawn Point"
		setcvar "g_obj_alliedtext2" "Control Switch 1"
		setcvar "g_obj_alliedtext3" "Control Switch 2"
		setcvar "g_obj_alliedtext4" "Control Switch 3"
		setcvar "g_obj_alliedtext5" "Detonate Axis Spawn Point"

		setcvar "g_obj_axistext1" "Protect Spawn Point"
		setcvar "g_obj_axistext2" "Control Switch 1"
		setcvar "g_obj_axistext3" "Control Switch 2"
		setcvar "g_obj_axistext4" "Control Switch 3"
		setcvar "g_obj_axistext5" "Destroy Allied Spawn Point"
	}
	else
	{
		//Not a TOW game remove the bombs
		$allie_bomb remove
		$axis_bomb remove


		// set scoreboard messages
		setcvar "g_obj_alliedtext1" "Tow Tutorial" 
		setcvar "g_obj_alliedtext2" ""
		setcvar "g_obj_alliedtext3" ""
		setcvar "g_obj_axistext1" ""
		setcvar "g_obj_axistext2" ""
		setcvar "g_obj_axistext3" ""

	}
	
	//////////////////////////
	level waittill prespawn
	//////////////////////////
	

	//////////////////////////
	level waittill spawn
	//////////////////////////

	level.bRoundStarted			= 0

//	level.bswitch1SwitchUp	= 1
//	level.bswitch2SwitchUp	= 1
//	level.bswitch3SwitchUp	= 1
	level.axisFire				= 1
	
	// set the parameters for this round based match	
	level.dmrespawning	= 1			// 1 or 0
	level.dmroundlimit	= 15			// round time limit in minutes
	level.clockside		= draw			// set to axis, allies, kills, or draw
	level.numObjectives	= 5			// Number of objectives needed to win
	level.numAxisObjectives	= 0				// Number of objectives taken by the axis
	level.numAlliedObjectives	= 0				// Number of objectives taken by the allies


	//////////////////////////
//	level waittill roundstart
	//////////////////////////		

	//If this is a tug of war game then we init all the TOW stuff
	if( level.gametype == 5 )
	{
		//init the objectives
		thread init_objectives
		
		thread init_spawner_bombs
		thread init_switch_lights

		//Setup the starting team objectives	
		thread set_objectives

		level.bRoundStarted = 1
	}

	
end


//----------------------------------------------------------
//Destroy the allied spawner here
//----------------------------------------------------------
alliesspawnpoint:	
	
	iprintln "The Allied Spawn Point has been Destroyed!"

	//Take over the objective
	$Obj_alliespawnpoint TakeOver 0
	level.numAxisObjectives++
	level.numAlliedObjectives--

	iprintln "The Allied Team Can No Longer Respawn!"

	waitthread Check_End_Match

end

//----------------------------------------------------------
//Destroy the axis spawner here
//----------------------------------------------------------
axisspawnpoint:
	
	iprintln "The Axis Spawn Point has been Destroyed!"
	
	$Obj_axisspawnpoint TakeOver 1	
	level.numAlliedObjectives++
	level.numAxisObjectives-- 

	iprintln "The Axis Team Can No Longer Respawn!"

	waitthread Check_End_Match

end

//----------------------------------------------------------
//control switch 1 green/red
//----------------------------------------------------------
switch1:	
	
	if( level.bRoundStarted == 1 )
	{
		if( parm.other.dmteam == axis )
		{
			if( $Obj_switch1.ControlledBy != 0 )
			{			
				$Obj_switch1 TakeOver 0				
				level.numAxisObjectives++
				level.numAlliedObjectives--
				$obj2_light_allie hide	//Turns the allied light off
				$obj2_light_axis show	//Turns the axis light on
				iprintln "The Axis Control Switch 1!"

				waitthread Check_End_Match
			}
		}
		else if( parm.other.dmteam == allies )
		{
			if( $Obj_switch1.ControlledBy != 1 )
			{		
				$Obj_switch1 TakeOver 1				
				level.numAlliedObjectives++
				level.numAxisObjectives--
				$obj2_light_axis hide	//Turns the Axis light off
				$obj2_light_allie show	//Turns the allied light on

				iprintln "The Allies Control Switch 1!"

				waitthread Check_End_Match
			}
		}
		
		//Update team current objectives
		thread set_objectives
	}

end

//----------------------------------------------------------
//control switch 2 green/red
//----------------------------------------------------------
switch2:
	
	if( level.bRoundStarted == 1 )
	{
		if( parm.other.dmteam == axis )
		{
			if( $Obj_switch2.ControlledBy != 0 )
			{		
				$Obj_switch2 TakeOver 0
				level.numAxisObjectives++
				level.numAlliedObjectives--
				$obj3_light_allie hide
				$obj3_light_axis show

				iprintln "The Axis Control Switch 2!"

				waitthread Check_End_Match
			}
		}
		else if( parm.other.dmteam == allies )
		{		
			if( $Obj_switch2.ControlledBy != 1 )
			{		
				$Obj_switch2 TakeOver 1			
				level.numAlliedObjectives++
				level.numAxisObjectives--
				$obj3_light_axis hide
				$obj3_light_allie show

				iprintln "The Allies Control Switch 2!"	

				waitthread Check_End_Match
			}
		}

		//Update team current objectives
		thread set_objectives
	}

end

//----------------------------------------------------------
//control switch 3 green/red
//----------------------------------------------------------
switch3:

	if( level.bRoundStarted == 1 )
	{
		if( parm.other.dmteam == axis )
		{
			if( $Obj_switch3.ControlledBy != 0 )
			{
				$Obj_switch3 TakeOver 0				
				level.numAxisObjectives++
				level.numAlliedObjectives--
				$obj4_light_allie hide
				$obj4_light_axis show

				iprintln "The Axis Control Switch 3!"

				waitthread Check_End_Match
			}
		}
		else if( parm.other.dmteam == allies )
		{
			if( $Obj_switch3.ControlledBy != 1 )
			{
				$Obj_switch3 TakeOver 1				
				level.numAlliedObjectives++
				level.numAxisObjectives--
				$obj4_light_axis hide
				$obj4_light_allie show
				
				iprintln "The Allies Control Switch 3!"

				waitthread Check_End_Match
			}

		}

		//Update team current objectives
		thread set_objectives
	}

end


//----------------------------------------------------------
//Set the teams current objectives
//----------------------------------------------------------
set_objectives:	
	
	//First lets do the allies
	if( $Obj_switch3.ControlledBy == 0 )
	{			
		$Obj_switch3 SetCurrent 1
	}	
	else if( $Obj_switch2.ControlledBy == 0 )
	{
		$Obj_switch2 SetCurrent 1
	}
	else if( $Obj_switch1.ControlledBy == 0 )
	{	
		$Obj_switch1 SetCurrent 1
	}
	else if( $Obj_axisspawnpoint.ControlledBy == 0 )
	{
		$Obj_axisspawnpoint SetCurrent 1
	}

	//Now the Axis	
	if( $Obj_switch1.ControlledBy == 1 )
	{			
		$Obj_switch1 SetCurrent 0
	}
	else if( $Obj_switch2.ControlledBy == 1 )
	{
		$Obj_switch2 SetCurrent 0
	}
	else if( $Obj_switch3.ControlledBy == 1 )
	{	
		$Obj_switch3 SetCurrent 0
	}
	else if( $Obj_alliespawnpoint.ControlledBy == 1 )
	{
		$Obj_alliespawnpoint SetCurrent 0			
	}	

end

//--------------------------------------------------------------
//init the spawner bombs
//--------------------------------------------------------------
init_spawner_bombs:

	//Allied spawner bomb
	$axis_bomb thread global/tow_dm.scr::bomb_thinker "axis" maps/obj/MP_Tutorial_TOW.scr::alliesspawnpoint
	$allie_bomb thread global/tow_dm.scr::bomb_thinker "allies" maps/obj/MP_Tutorial_TOW.scr::axisspawnpoint
end

//-----------------------------------------------
// Blow up Allied Spawner
//-----------------------------------------------
axis_bomb_relay_explode:

	self waittill trigger
	thread alliesspawnpoint	
end

//-----------------------------------------------
// Blow up Axis Spawner
//-----------------------------------------------
allie_bomb_relay_explode:
	
	self waittill trigger
	thread axisspawnpoint
end

//----------------------------------------------------------
//Initialize the switch lights (start all lights off)
//----------------------------------------------------------
init_switch_lights:

	$obj2_light_allie hide
	$obj2_light_axis hide
	$obj3_light_allie hide
	$obj3_light_axis hide
	$obj4_light_allie hide
	$obj4_light_axis hide

end

//-----------------------------------------------
// init the objectives
//-----------------------------------------------
init_objectives:
	
	//Allied spawn point
	if( $Obj_alliespawnpoint.ControlledBy == 0 )
	{
		level.numAxisObjectives++
	}
	else if( $Obj_alliespawnpoint.ControlledBy == 1 )
	{
		level.numAlliedObjectives++
	}

	//Axis spawn point
	if( $Obj_axisspawnpoint.ControlledBy == 0 )
	{
		level.numAxisObjectives++
	}
	else if( $Obj_axisspawnpoint.ControlledBy == 1 )
	{
		level.numAlliedObjectivse++
	}

	//switch 1
	if( $Obj_switch1.ControlledBy == 0 )
	{
		level.numAxisObjectives++
	}
	else if( $Obj_switch1.ControlledBy == 1 )
	{
		level.numAlliedObjectives++
	}

	//switch 2
	if( $Obj_switch2.ControlledBy == 0 )
	{
		level.numAxisObjectives++
	}
	else if( $Obj_switch2.ControlledBy == 1 )
	{
		level.numAlliedObjectives++
	}

	//switch 3
	if( $Obj_switch3.ControlledBy == 0 )
	{
		level.numAxisObjectives++
	}
	else if( $Obj_switch3.ControlledBy == 1 )
	{
		level.numAlliedObjectives--
	}

end

//-----------------------------------------------
// init the objectives
//-----------------------------------------------
Check_End_Match:
	
	//Allies first
	if( level.numAlliedObjectives == level.numObjectives )
	{
		//ignore the clock
		level ignoreclock 1

		//if there is a bomb ticking stop it.
		waitthread global/tow_dm.scr::StopBomb

		//Allies win play the movie
		teamwin allies		
	}
	else if( level.numAxisObjectives == level.numObjectives )
	{
		//ignore the clock
		level ignoreclock 1

		//if there is a bomb ticking stop it.
		waitthread global/tow_dm.scr::StopBomb

		//Axis win do their movie and end the map
		teamwin axis
	}

end






Here are some general rules and tips when creating a TOW map.


How to Setup a Tug-of-War Map

func_TOWObjective:  

This new entity type allows easy setup of the new Tug-of-War Objectives.  This section will document the settings for this new entity type.           

$targetname                 -Use this to give the objective a descriptive name.  

AlliesObjNum             - Assign which objective number this is for the allies.  This is important because it will affect how the checkboxes work in the new tab score screen.  Each objective can be a different number for each team.  For instance the first objective on the map may be the allied spawner.  This should be assigned as the first allied objective but the last objective for the axis side.  

AxisObjNum               - Assign which objective number this is for the axis.  See AlliesObjNum for more information.     

ControlledBy              - This should be assigned to 0, 1, or 2.  When the map starts this setting will determine which objectives are owned by whom, and which are neutral.  0 = Axis, 1 = Allies, 2 = Neutral.  

ObjectiveNbr              - This just needs to be a unique number that does not conflict with the other objectives.  Typically use 0 – 5.  

There are also 2 check boxes that can be checked.  StartOn, and Respawner.  Check the Respawner box if this objective is to halt the team’s ability to respawn in when killed.  For now always check the StartOn box.  

Setting Up the Objectives in MOHRadiant:  

Here are the steps needed to create a working Tug-of-War Entity in MOHRadiant:  

bullet Create a trigger that will be used to set off the objective.  All the triggers for these maps should be trigger_multiple types.
bullet Give your trigger descriptive $targetname, and setthread names.
bullet Create an entity and use the func_TOWObjective entity type.
bullet Setup the entity variables described above.
bullet Connect the Objective entity to the trigger.  Not the other way around.  This is VERY important.

Scripting Tug-of-War Objectives:  

Every Tug-of-War map is going to have different scripting requirements.  I’ll document the basic things that need to be setup and show some examples.  

Scoreboard Text:

setcvar "g_obj_alliedtext1" "Allied Objective Text 1"

setcvar "g_obj_alliedtext2" "Allied Objective Text 2"

setcvar "g_obj_alliedtext3" "Allied Objective Text 3"

setcvar "g_obj_alliedtext4" "Allied Objective Text 4"

setcvar "g_obj_alliedtext5" "Allied Objective Text 5"  

setcvar "g_obj_axistext1" "Axis Objective Text 1"

setcvar "g_obj_axistext2" "Axis Objective Text 2"

setcvar "g_obj_axistext3" "Axis Objective Text 3"

setcvar "g_obj_axistext4" "Axis Objective Text 4"

setcvar "g_obj_axistext5" "Axis Objective Text 5"  

There are 5 objective text entries for each team.   The example above shows how to setup this text.  

Func_TOWObjective Variables and Functions:  

Variables:

ControlledBy  - This is the team number that controls the objective. 

Example:  

if( $ObjectiveName.ControlledBy == 0 )

{

            println “This objective is owned by the axis team.”

}  

Will check to see if the objective named $ObjectiveName to test if it is controlled by the axis team and if it is will print out a message indicating as much.  

Functions:

TakeOver – Call this to have a team take over the objective.  Pass it 0, 1, or 2 which represent the team taking it over.  0 = axis, 1 = allies, 2 = neutral.  Example:  

$ObjectiveName TakeOver 0  

Will cause the objective named $ObjectiveName to be taken over by the axis team.  

SetCurrent – Call this to make this the current objective for the passed in team.  Pass it 0, 1, or 2 to represent the team that sets this as the priority objective.  This will cause the compass for the passed in team to point at this objective.

Example:  

$ObjectiveName SetCurrent 0  

This will cause the objective named $ObjectiveName to be set as the current objective on the compass for the axis team.  

Scripting Objective Take Over:  

When an opposing team takes an objective there should be a triggered setthread that gets called in script.  This is where the handling should take place.  Here is an example from Sub Pen:  

toggle_subpen_doors:             

            if( parm.other.dmteam == axis )

            {

                        if( $obj_subpen_doors.ControlledBy != 0 )

                        {                                 

                                    $obj_subpen_doors TakeOver 0

                                    iprintln "The Axis have closed the pen doors!"

                        }

            }

            else if( parm.other.dmteam == allies )

            {

                        if( $obj_subpen_doors.ControlledBy != 1 )

                        {                     

                                    $obj_subpen_doors TakeOver 1

                                    iprintln "The Allies have opened the pen doors!"

                        }

            }  

            //Update team current objectives

            thread set_objectives  

end

This function gets triggered when a team hits the sub pen doors trigger.  Here is a breakdown of this function:  

bullet First check to see which team triggered the doors.  This is accomplished by checking the parm.other.dmteam variable which will be set to either axis or allies.
bullet Next check that the team that triggered this does not already control the objective.  This is accomplished by checking that particular objectives ControlledBy variable.  The syntax for this is: $<ObjectiveName>.ControlledBy.  The ControlledBy variable will be set to 0 for axis, 1 for allies, or 2 for neutral.
bullet Now if the team that hit this trigger does not own it we call the TakeOver function.  We call this by using: $<ObjectiveName> TakeOver TeamNumber.  Where TeamNumber is 0, 1, or 2.
bullet Finally the example calls a function that updates the current objective for each team based on which the scripter feels is the most important.  Here is a listing of that function from Sub Pen:

//----------------------------------------------------------

//Set the teams current objectives

//----------------------------------------------------------

set_objectives:           

            //First lets do the allies

            if( $obj_sub_clamps.ControlledBy == 0 )

            {         

                        $obj_sub_clamps SetCurrent 1

            }

            else if( $obj_subpen_doors.ControlledBy == 0 )

            {         

                        $obj_subpen_doors SetCurrent 1

            }         

            else if( $obj_steal_sub.ControlledBy == 0 )

            {         

                        $obj_steal_sub SetCurrent 1

            }  

            //Now the Axis

            if( $obj_subpen_doors.ControlledBy == 1 )

            {         

                        $obj_subpen_doors SetCurrent 0

            }

            else if( $obj_sub_clamps.ControlledBy == 1 )

            {         

                        $obj_sub_clamps SetCurrent 0

            }

            else

            {         

                        $obj_allied_spawner SetCurrent 0

            }  

end

This function prioritizes which objectives each team needs to be set to.  This function is called everytime an objective gets taken.  This function is not automatically called but rather called from inside the script.  For the allies the priority objectives are first the sub clamps, followed by the sub pen doors and finally the sub itself.  If the clamps get taken by the axis then this function will set the clamps as the current objective for the allies.  

Respawners  

This new game type allows a team to destroy an enemy objective that will stop that team from spawning when killed.  This is surprisingly easy.  First take over the objective as normal, then use the name assigned to the spawner and call disablespawn.  Example below:  

//----------------------------------------------------------

//Destroy the allied spawner here

//----------------------------------------------------------

destroy_allied_spawner:     

            //Make sure we are the allies

            if( parm.other.dmteam != allies && $obj_allied_spawner.ControlledBy != 0 )

            {

                        iprintln "The allied transport has been destroyed!"

                        //Take over the objective

                        $obj_allied_spawner TakeOver 0

                        $Allied_Spawn_Locations disablespawn

            }           

end

Win Conditions  

The following are handled automatically:  

bullet All 5 Objectives are taken by one team.
bullet A respawner has been destroyed and the team is all subsequently killed.
bullet Time runs out.  This needs a little setup at the beginning of the script.  Here is an example:

            // set the parameters for this round based match

            level.defusing_team = "axis"                 

            level.planting_team = "allies"

            level.dmrespawning       = 1                   // 1 or 0

            level.dmroundlimit         = 5                   // round time limit in minutes

            level.clockside              = axis               // set to axis, allies, kills, or draw  

            level waittill roundstart

The defusing and planting teams need to be set but it does not matter which team.  Just make sure they have different team values set to them.  dmrespawning should always be set to 1.  dmroundlimit is the amount of time for each round.  clockside is set to determine which team wins if the clock runs out.  

Sometimes you may want to have a custom victory condition as in sub pen.  Only 3 objectives need to be taken by the allies in sub pen to win the map.  Here is the code that handles that:  

//----------------------------------------------------------

//steal the sub

//----------------------------------------------------------

steal_sub:  

            //Only the allies can actually take this objective

            if( dm_other.dmteam != axis && $obj_steal_sub.ControlledBy != 1 )

            {

if( $obj_sub_clamps.ControlledBy == 1 &&      $obj_subpen_doors.ControlledBy == 1 )

                        {

                                    $obj_steal_sub TakeOver 1

                                    iprintln "Tha Allies have stolen the U-Boat!"

                                    //Allies have won

                                    teamwin allies

                        }

            }

            //Update team current objectives

            thread set_objectives

end  

This function is called when a team hits the trigger on the top of the sub.  It checks to see if it is the allies, and if so then checks to see if the allies have opened the doors and disengaged the clamps.  If all these conditions are met the script calles teamwin and passes the winning team as the parameter.