Counter-Strike: Global Offensive/Game Modes/Co-op Strike: Difference between revisions
(Tutorial on how to create your own Co-op Strike map) |
m (Nesciuse moved page Counter-Strike: Global Offensive/Game Modes/Co-op Strike/en to Counter-Strike: Global Offensive/Game Modes/Co-op Strike without leaving a redirect: Move en subpage to basepage) |
||
(24 intermediate revisions by 13 users not shown) | |||
Line 1: | Line 1: | ||
Co-op Strike is a cooperative game mode | {{LanguageBar}} | ||
{{back|Counter-Strike: Global Offensive Level Creation}}{{Delisted|csgo}} | |||
<noinclude></noinclude> | |||
[[File:Csgo icon coopstrike.png|left|link=]] | |||
'''Co-op Strike''' (internally: <code>'''coopmission'''</code>) is a cooperative [[game mode]] in {{csgo|4}}. | |||
It was first introduced as part of the Operation Wildfire Gemini Campaign. And now on the Steam Community Workshop. Like {{csgo mode|Guardian}}, it is exclusively available through Operations or the Steam Workshop, but can be easily accessed through the console. | |||
Creating a Co-op Strike map is long and it is expected from the readers that they have basic knowledge on how to create maps and work with entities/ | In Co-op Strike, two human players go against a staged fight against an [[AI]] controlled terrorist team in a story-driven scenario. The map prefix for these maps is <code>coop_</code>. It shares the same prefix as some custom community Co-op game modes. | ||
It is not | |||
= Entities used in Co-op Strike = | Creating a Co-op Strike map in [[Valve]]'s style is a long process and it is expected from the readers that they have basic knowledge on how to create maps and work with entities, [[I/O]], along with a basic knowledge of [[VScript]]. It is not advised to create a Co-op Strike map if there is no prior experience in mapping. | ||
* [[info_enemy_terrorist_spawn]]: The places where the | == Game Mode Description == | ||
* [[logic_script]]: The entity we connect out | If a standard map is loaded in this game mode, this game mode is technically comparable with {{csgo mode|Comp}}, but with the following differences: | ||
* [[game_coopmission_manager]]: This | * Human players are assigned to CTs and spawn at enabled [[info_player_counterterrorist]] entities as usual, whereas enemy [[bot]]s spawn at enabled [[info_enemy_terrorist_spawn]] entities. Players spawn at the round beginning but also when [[List of CS:GO Script Functions#Co-op Strike|an appropriate VScript function]] is called. During warmup, all players respawn if they die, as usual. | ||
* [[item_coop_coin]]: The secret | * The round does not end if the Terrorists are eliminated due to {{ent|mp_use_respawn_waves|2}}. | ||
* [[point_hiding_spot]]: Simply said this is | * CTs always wear an [[item_heavyassaultsuit]] that has slightly different properties in this game mode. | ||
* [[prop_exploding_barrel]]: The oh so great exploding barrels. | * CTs cannot defuse planted [[weapon_c4|C4 entities]]. | ||
* If all human players are dead, they lose the mission and a new round begins. | |||
* If the human players win the round (either by rescuing a [[hostage_entity|hostage]] or via [[game_round_end]]), they complete the mission and the next map loads. | |||
== Creating the Map == | |||
=== Entities used in Co-op Strike === | |||
These are entities introduced with the Co-op Strike game mode along with some other entities that will be used in this tutorial. | |||
* [[info_enemy_terrorist_spawn]]: The places where the enemy bots will spawn. No other spawn entities are used for them. It is the Co-op variant of [[info_player_terrorist]], so to speak. | |||
* [[logic_script]]: The entity we connect out VScript to in order to be able to use functions via [[I/O]]. | |||
* [[game_coopmission_manager]]: This entity fires outputs when special Co-op events occur, for example when a wave is completed. | |||
* [[item_coop_coin]]: The secret Co-op coin for extra points at the end of the level. At the moment, exactly 3 coins need to be in a map for the Co-op coin system to work. | |||
* [[point_hiding_spot]]: Simply said, this is an entity that tells the bots "Here is a good hiding spot". | |||
* [[prop_exploding_barrel]]: The oh so great exploding barrels. Place them a few units above the ground to prevent them from getting "sucked" into it. | |||
* [[info_hostage_spawn]]: Rescuing the hostage is one way of completing the level. | * [[info_hostage_spawn]]: Rescuing the hostage is one way of completing the level. | ||
* [[game_round_end]]: This entity can end rounds on command and make a team win. Used in | * [[game_round_end]]: This entity can end rounds on command and make a team win. Used in Co-op maps where rescuing a hostage is not the winning condition. | ||
The entities above will be the entities that will be used in | The entities above will be the entities that will be used in Co-op maps. | ||
=== Environment === | === Environment === | ||
When starting your map keep Co-op in your mind. Make sure there are plenty of hiding spots for either the | When starting your map keep Co-op in your mind. Make sure there are plenty of hiding spots for either the players or the bots. Think of places where you want to place new weapons for the players to use. Think of maybe nifty ways to use the environment to the players' advantage. Note that it is players vs. AI which is different from any other game mode. | ||
Co-op is based around a "wave" system. This means that a group of enemies will spawn, usually 5, that the players has to exterminate in order to advance to the next part of the map. So when creating the map, split your map up in multiple parts. Each part will have its own enemies by your choosing. Unless you want one big linear map with 40 enemies at once. Split your map up in different parts so that you can only advance once each wave is completed. | |||
=== Player Spawns === | |||
In this game mode, the idea is to place both human and bot spawn points across the map where needed and to only have a set of them enabled so players won't spawn in the wrong places. This will be managed by the VScript functions <code>OnSpawnsReset()</code> and <code>OnWaveCompleted()</code> that we will deal with later, see [[#Explanation of Code]]. | |||
{{note|Remember to place spawn point entities at least 8 units above the ground or players might not spawn properly.}} | |||
{{note|Player spawn entities are [[preserved entities]]. When a new round starts, their properties don't reset. That's why a Co-op VScript comes in handy.}} | |||
==== Human Player Spawns ==== | |||
The human players (re)spawn at enabled [[info_player_counterterrorist]] entities. | |||
It is a good idea to have a spawn point where the players can "relax" and setup for the fights that are about to commence. For that purpose, Valve has made spawn rooms in their Co-op maps where the players can choose weapons and grenades that they will use in the upcoming level. There can also be a way to adjust the difficulty of the bots so that the level is playable in [[#Hard Mode]]. For better gameplay, give the players "crappy" weapons at the beginning so they will look around in your map for better gear. | |||
The way you create your spawn is up to you. You can either have a simple door divide the spawn with the enemies or you could have a fancy helicopter "cutscene" take you to the actual level. Be as creative as you can be. | |||
If the human players can die in the mission (which will most likely be the case!) and if you don't want the human players to respawn at their very first spawn points of the map, you can place more CT spawns as "checkpoints" and leave them <code>Disabled</code> by default. The Co-op VScript later can access these. | |||
==== Enemy Spawns ==== | |||
=== | [[File:Csgo coop info enemy terrorist spawn.png|right]] | ||
The | The enemy bots (re)spawn at enabled [[info_enemy_terrorist_spawn]] entities. | ||
For each section in your map you will need to have these entities in order for bots to spawn. Also give all of these spawn points in a section the same name. Say for the first section of the map you will enter, you name all the [[info_enemy_terrorist_spawn]] entities <code>wave_01</code>. Do this for each section of your map in chronological order, e.g. <code>wave_02</code>, <code>wave_03</code>, and so forth. | |||
You can give the spawn points different properties. | |||
* For starters you can give them weapons. <code>glock</code> is the default weapon but you can add weapons by using the comma character <code>,</code> so you can set something like <code>glock, ak47</code> and the bot on that location will spawn with a [[weapon_glock]] and a [[weapon_ak47]]. | |||
* You can also give the bot a different player model by using the <code>Override player model</code> [[keyvalue]]. One example for this is <code>tm_phoenix_heavy.mdl</code> which is also used in ''coop_cementplant''. More model names can be found [[Choosing Player Models#Model List|here]]. | |||
After you've chosen your properties you need to set <code>Enabled by default</code> to <code>No</code>, except for the first wave of enemies that you come across those need to be <code>Enabled</code> instead of <code>Disabled</code>. | |||
=== Doorways to new Sections === | === Doorways to new Sections === | ||
Since you have to | Since you have to divide your map into different sections you will need to have doorways that connect these sections and can only be accesed once a wave is completed. Simply put, put down a [[prop_door_rotating]] between your sections, lock them so players cannot rush through the map without completing the waves and give them a proper name so you can find them later when you're working on your script. | ||
=== Collectible Coins === | === Collectible Coins === | ||
If you want players to explore your map more in order to get more points you can use the [[item_coop_coin]] | If you want players to explore your map more in order to get more points you can use the [[item_coop_coin]]. Once all 3 coins are collected in a level the players will get bonus points at the end of the level. Exactly 3 coins should be placed in a map: If you have 2 coins, the players will never be able to get the bonus points; Collecting more than 3 coins has the same effect as collecting only 3. | ||
=== Co-op | |||
=== Co-op Gear === | |||
Here is some inspiration of what you can or should supply the human players with throughout the map: | |||
;Weapons | |||
:See [[:Category: Counter-Strike: Global Offensive Weapons]] for a list of weapons. There are multiple ideas to supply players with new weapons. | |||
:*Use map-placed weapon entities, such as [[weapon_ak47]], which can only be picked up once per round. | |||
:*Equip bots with them that the players will kill and loot. | |||
:*A simple "infinite" weapon pickup can be created with an invisible, non-solid [[func_button]] and a [[game_player_equip]] entity together with a [[prop_dynamic]] of the corresponding weapon. | |||
:*[[CS:GO VScript Examples#Equip players|VScript can also handle the equipping]]. | |||
;[[weapon_healthshot]] | |||
:Players can use healthshots to heal themselves. They are good to be given before a difficult fight. Do not give too many healthshots, otherwise your map will be to easy. | |||
;[[weapon_tagrenade]] | |||
:The tagrenade grants a short "wallhack" effect: It makes enemies close to its explosion glow through walls for a brief moment. | |||
;[[point_give_ammo]] | |||
:If this entity receives the <code>GiveAmmo</code> input, it gives the activator full ammo for all his weapons. In ''coop_cementplant'' they used the ammo models <code>models\props\coop_cementplant\coop_ammo_stash\coop_ammo_stash_full.mdl</code> and <code>\coop_ammo_stash_empty.mdl</code>, when once used, the model changes from the former to the latter. Make it so that players can only take ammo from that point once so they can't run back in the level to restock on ammo again. | |||
;[[prop_ammo_box_generic]] | |||
:Each time this entity is [[+use]]d, the reserve ammo of the activator's active weapon is increased by one clip size. The entity can be used 4 times. | |||
;[[prop_weapon_refill_heavyarmor]] | |||
:Don't forget about armor: As the players will lose armor points with each fight, they will soon appreciate this entity which can be [[+use]]d by one player to replenish his armor to 200 points. | |||
;[[weapon_zone_repulsor]] | |||
:{{todo}} | |||
=== Co-op Tasks === | === Co-op Tasks === | ||
Since you are trying to make a | Since you are trying to make a ''cooperative'' map, it is fun to include simple puzzles and tasks that can only be achieved if players work together. It can go from one player standing on an elevator and the other activating this elevator or a door with 2 levers to one player being blind and the other has to guide him/her through a dark room. Be creative! | ||
=== Glowing props === | === Glowing props === | ||
Use glowing | Use glowing doors or buttons to tell the players "Go here to advance" or "Press me to advance". You can make props (the doorway to the next part of the map) glow so it's easy for players to find their way through your map. | ||
[[File:Csgo coop map example.png|center]] | |||
= Creating the Script = | == Creating the Script == | ||
Co-op Strike uses [[VScript]] to make the system function. Without the script the | Co-op Strike uses [[VScript]] written in {{sq}} [[Squirrel]] to make the system function. Without the script the game mode can't work properly, so each Co-op map needs its own script. | ||
Start by creating your script and give it a name | * Start by creating your script (text file) and give it a name, for example <code>cooptutorial.nut</code>. Important: Make sure that the extension of the file is '''.nut''', otherwise the game won't be able to read the file. | ||
* Now place this file in the correct folder inside your game files, for example <code>Steam/steamapps/common/Counter-Strike Global Offensive/csgo/scripts/vscripts/custom/</code>. Make sure it is somewhere inside the <code>vscripts</code> folder. Subdirectories are arbitrary. | |||
Once you've placed it inside the correct folder you can start editing it with notepad or any other text editor. | Once you've placed it inside the correct folder you can start editing it with notepad or any other text editor. | ||
This script will be later be used for the <code>Entity Scripts</code> property of a [[logic_script]], which means that the script will be executed each time a new round begins. Variables and functions ("table slots" to be precise) will be saved after the execution and can be called when firing the inputs <code>RunScriptCode</code> or <code>RunScriptFunction</code> to that logic_script. | |||
=== Code used in Example === | === Code used in Example === | ||
Here an example of a Co-op Script. Feel free to copy this | Here an example of a Co-op Script. Feel free to copy this into your own script file since much of it is useful for every Co-op Strike map script. | ||
An example vmf will be provided at the end of this | An example vmf will be provided at the end of this guide that works together with this script. | ||
<syntaxhighlight lang="cpp"> | {| class="mw-collapsible mw-collapsed" style="background:none" | ||
|+ style=text-align:left| | |||
| <syntaxhighlight lang="cpp"> | |||
wave <- 0; | wave <- 0; | ||
function RoundInit(){ | function RoundInit() | ||
{ | |||
//Will do this everytime you start the map/round because we call it in the OnLevelReset | //Will do this everytime you start the map/round because we call it in the OnLevelReset | ||
wave = 0; | wave = 0; | ||
Line 90: | Line 149: | ||
} | } | ||
function SpawnNextWave( amount ){ | function SpawnNextWave( amount ) | ||
{ | |||
ScriptCoopMissionSpawnNextWave( amount ); | ScriptCoopMissionSpawnNextWave( amount ); | ||
wave++; | wave++; | ||
Line 155: | Line 215: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
|} | |||
=== Explanation of Code === | === Explanation of Code === | ||
We will go over everything inside the script and explain what it all does. | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
wave <- 0; | wave <- 0; | ||
function RoundInit(){ | function RoundInit() | ||
{ | |||
//Will do this everytime you start the map/round because we call it in the OnLevelReset | //Will do this everytime you start the map/round because we call it in the OnLevelReset | ||
wave = 0; | wave = 0; | ||
Line 168: | Line 231: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
First <code>wave <- 0;</code> What we do here is initialize a variable that | First <code>wave <- 0;</code>: What we do here is define and initialize a variable that functions can call later. We have the function <code>RoundInit()</code> that sets the variable <code>wave</code> to 0. After that it tells the console to set the bot difficulty to 1, the easiest. After this it refreshes all the bot spawns. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
function ChangeGameModeToCoopIfNotCorrect() | function ChangeGameModeToCoopIfNotCorrect() | ||
Line 177: | Line 240: | ||
local map = GetMapName(); | local map = GetMapName(); | ||
if ( | if (game_type != 4 || game_mode != 1) | ||
{ | { | ||
SendToConsole("game_mode 1 | SendToConsole("game_type 4; game_mode 1; changelevel " + map); | ||
} | } | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The function <code>ChangeGameModeToCoopIfNotCorrect()</code> changes the [[game_type]] and [[game_mode]] to 4 and 1 so that the game knows its playing a Co-op map. The <code>coop_</code> map prefix is not sufficient to get the game mode right. You don't really have to know how this works; Just call this function when the map is started with the <code>OnMapSpawn</code> output of a [[logic_auto]]. More on this in [[#Making your Script work with your Map]]. | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
function SpawnFirstEnemies( amount ) | function SpawnFirstEnemies( amount ) | ||
{ | { | ||
ScriptCoopMissionSpawnFirstEnemies( amount ); | ScriptCoopMissionSpawnFirstEnemies( amount ); | ||
ScriptCoopResetRoundStartTime(); | ScriptCoopResetRoundStartTime(); // sets round time to 45 minutes | ||
wave++; | wave++; | ||
} | } | ||
function SpawnNextWave( amount ){ | function SpawnNextWave( amount ) | ||
{ | |||
ScriptCoopMissionSpawnNextWave( amount ); | ScriptCoopMissionSpawnNextWave( amount ); | ||
wave++; | wave++; | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
These are the 2 functions that will make the bots spawn. <code>SpawnFirstEnemies(amount)</code> | These are the 2 functions that will make the bots spawn. <code>SpawnFirstEnemies(amount)</code> will spawn the first wave of your level and reset the timer to 45 minutes. From within [[Hammer]] we will call this function and replace <code>amount</code> with a number of enemies to spawn. So if we want 3 bots to spawn we call <code>SpawnFirstEnemies(3)</code>. What it also does is <code>wave++</code> which means that it adds 1 to the variable <code>wave</code> so when the level starts <code>wave</code> is 0 but once a wave is spawned, it adds 1 so that <code>wave == 1</code>. <code>SpawnNextWave(amount)</code> is used in every subsequent wave. We use the variable <code>wave</code> to let the map know at which wave the players currently are. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
function OnMissionCompleted() | function OnMissionCompleted() | ||
{ | { | ||
Line 218: | Line 281: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
The names of the functions | The names of the functions say it all. As in ''coop_cementplant'', Valve made it so that your "Boss" gives a little message. | ||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
function OnRoundReset() | function OnRoundReset() | ||
Line 238: | Line 301: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<code>OnRoundReset()</code> is called when the players have died and the round "resets" | <code>OnRoundReset()</code> is called when the players have died and the round "resets". It calls the function <code>RoundInit()</code>, which is explained above. | ||
Then we have <code>OnSpawnsReset()</code>. This is called just before the round resets. This gives the map time to reset the player and enemy spawns to its original state. Here we Disable every entity whose targetname starts with <code>wave_</code> and enable only the first wave for enemies. You can do the same for CT spawns. Multiple CT spawns are used in bigger levels so that the players don't have to walk from their first spawn to the point where one player died. | |||
<code>EntFire</code> is used for [[I/O]] like in Hammer, but here we don't need to specify an output. Normally, the output defines "when" an input should be fired, but in VScript, that point in time is when the <code>EntFire</code> line is executed. For example <code>EntFire( "wave_*", "SetDisabled", "", 0 )</code> fires the input <code>SetDisabled</code> to all entities starting with "wave_", with an empty parameter override (<code>""</code>) and no delay (<code>0</code>). | |||
<syntaxhighlight lang="cpp"> | <syntaxhighlight lang="cpp"> | ||
function OnWaveCompleted() | function OnWaveCompleted() | ||
Line 266: | Line 330: | ||
} | } | ||
</syntaxhighlight> | </syntaxhighlight> | ||
Last we have OnWaveCompleted. This is called when you've killed every enemy of a wave. Because we keep track on which wave we are with the variable <code>wave</code> we can do different | Last we have <code>OnWaveCompleted</code>. This is called when you've killed every enemy of a wave. Because we keep track on which wave we are with the variable <code>wave</code>, we can do different things for different waves. Here we say: "If <code>wave</code> equals 1" then it disables all the enemy spawns but the spawn point for wave 2. We also unlock the door to the next section of the map and set the door to glow so it is easier for the players to find their way. | ||
This is all the code that is needed for a working Co-op map. If you didn't quite understand a lot of this, try copying the code from here into your own. | |||
=== Info about Code === | === Info about Code === | ||
There are a few functions that can be used to do different things. | There are a few functions that can be used to do different things. | ||
* ScriptCoopMissionSpawnFirstEnemies(#) | * <code>ScriptCoopResetRoundStartTime()</code> - Resets the roundtime to 45 minutes. | ||
* | * <code>ScriptCoopMissionSpawnFirstEnemies(#)</code> - Spawns the first <code>#</code> enemies. | ||
* <code>ScriptCoopMissionSpawnNextWave(#)</code> - Spawns a new wave, where <code>#</code> is the number of enemies you want to spawn, for example <code>ScriptCoopMissionSpawnNextWave(5)</code>. | |||
* ScriptCoopMissionRespawnDeadPlayers() | * <code>ScriptCoopMissionRespawnDeadPlayers()</code> - When this is called, it will respawn all dead CTs. | ||
{{note|There are way more, see [[List of CS:GO Script Functions]]. To learn more on how coding works, see [[VScript]] or {{sq}} [[Squirrel]] for more links.}} | |||
To learn more on how coding | |||
= Making your Script work with your Map = | == Making your Script work with your Map == | ||
We now have created a map and | We now have created a map and our script but your map won't work without connecting them. | ||
=== Setting up the entities to work with the script === | === Setting up the entities to work with the script === | ||
If you haven't already, create a [[logic_script]] and give it a simple name such as <code>logic_script</code>. | |||
{{note|The targetname <code>logic_script</code> can be disadvantageous because outputs targeting <code>logic_script</code> will target all <code>logic_script</code> entities in the map. A targetname such as <code>coop_script</code> would avoid this because it is not a classname.}} | |||
Go to the property <code>Entity Scripts</code>, click on <code>Manage...</code>, click on the <code>+</code> and search for your script which should be placed in a subfolder of <code>csgo/scripts/vscripts/</code>. Add it and the logic_script is done. | |||
[[File:Csgo coop logic script.png|center]] | |||
There should also be a [[game_coopmission_manager]] entity in the map. Go to its outputs and create one for every output that exists (except for the <code>OnUser</code> ones). | |||
[[File:Csgo coop game manager outputs.png|center]] | |||
These outputs call the script functions that we created in the scripting part. | |||
{{note|The script function names are arbitrary, it's just that they must be the same in the script file and in the parameter value of these outputs.}} | |||
=== Setting up the doorways between sections === | === Setting up the doorways between sections === | ||
Since Co-op Strike is a | Since Co-op Strike is a wave based system, we usually want to divide the map into sections with each section being unlocked when a wave is completed. Now we need to make sure that enemies will spawn once you enter a new section. So for example, we want to spawn bots when the players open the door to the next section. | ||
An output would look like this: | An output would look like this: | ||
{| {{OutputsTable}} | |||
Of course you do not have to use door you can use whatever entity you'd like. | | [[File:Io11.png]] || OnOpen || logic_script || RunScriptCode || SpawnFirstEnemies(3) || 0.00 || No | ||
= Finishing touches = | |} | ||
Do this for the first wave. | |||
For the other waves, use this: | |||
{| {{OutputsTable}} | |||
| [[File:Io11.png]] || OnOpen || logic_script || RunScriptCode || SpawnNextWave(5) || 0.00 || No | |||
|} | |||
Of course you do not have to use a door - you can use whatever entity you'd like, such as [[trigger_once]] (OnTrigger), [[func_button]] (OnPressed), etc. | |||
== Finishing touches == | |||
Just a few more things to think about to complete the map. | |||
=== Ending the Map === | === Ending the Map === | ||
If you want the map to reload in the right [[game_type]] and [[game_mode]] if they were wrong, create a [[logic_auto]] and add the output: | |||
[[File:Csgo coop change game mode.png|center]] | |||
Secondly, if you have a [[hostage_entity]] in the map, the players can win the map by bringing it to a [[func_hostage_rescue]]. | |||
If you want the players to win by getting to an end point you'll have to use a [[game_round_end]]. Create a [[trigger]] at the end of your map with the output: | |||
{| {{OutputsTable}} | |||
| [[File:Io11.png]] || OnStartTouch || game_round_end || EndRound_CounterTerroristsWin || 5 || 0.00 || No | |||
|} | |||
And voilà, your map is fully playable. | |||
=== Proper bot navigation === | === Proper bot navigation === | ||
Since you are playing against | Since you are playing against [[bot]]s you'll have to make a proper navigation file so your bots don't look as much like bots. With the commands listed at [[Nav Mesh Commands|Navigation Mesh Commands]] and [[Nav Mesh Editing|Navigation Mesh Editing]], you can do many things to improve the bot navigation. You can also put [[point_hiding_spot]]s in your map to control where bots should hide. | ||
= Testing your | |||
Now that your map is complete you'll have to test it | === Hard Mode === | ||
As with ''coop_cementplant'' and all succeeding Co-op maps by Valve, there was a secret lever to play the map in Hard Mode to get extra points at the end of the game. To do that, you'll have to set the console variable <code>mp_coopmission_bot_difficulty_offset</code> with a [[point_servercommand]]. The value 1 stands for the easiest mode; If set to 5, the game will register that it's in Hard Mode. If you really want to give your players a challenge, use an offset of 7. You can also give your [[info_enemy_terrorist_spawn]] entities a difficulty which stacks on top of the one that is set with <code>mp_coopmission_bot_difficulty_offset</code>. | |||
== Testing your Map == | |||
Now that your map is complete you'll have to test it in-game. | |||
All you have to do is open up your console, type in <code>[[map (console command)|map]] <mapname> coop</code> and it will load your map. If <code>coop</code> is omitted, the game_type and/or game_mode are most likely not correct, so if you have set up the script function <code>ChangeGameModeToCoopIfNotCorrect</code>, the map should reload itself and set those to the correct values. | |||
Make sure to check the console for VScript errors whenever code will be executed. Examples: | |||
<pre style="background:#444; color:#ddd; border-color:#666"> | |||
<filename>.nut line = (1) column = (26) : error expression expected | |||
FAILED to compile and execute script file named scripts/vscripts/<path>/<filename>.nut | |||
</pre> | |||
<pre style="background:#444; color:#ddd; border-color:#666"> | |||
AN ERROR HAS OCCURED [the index 'wave' does not exist] | |||
CALLSTACK | |||
*FUNCTION [main()] <filename>.nut line [1] | |||
LOCALS | |||
[this] TABLE | |||
</pre> | |||
{{tip|For debugging, it can make sense to add lines in the code that print a message or the current value of a variable, such as <code>println("...")</code> or <code>ScriptPrintMessageChatAll("...")</code>.}} | |||
I hope this guide will help | I hope this guide will help a lot of people. Happy Mapping :) | ||
Here is an example of a working simple | == External links == | ||
https://www.dropbox.com/s/val79djogsjzw9z/Creating%20a%20Co-op%20Strike%20Map%20Example.rar?dl=0 | Here is an example of a working simple Co-op map with its script: | ||
https://www.dropbox.com/s/val79djogsjzw9z/Creating%20a%20Co-op%20Strike%20Map%20Example.rar?dl=0{{csgo-navbox}} | |||
[[Category: Counter-Strike: Global Offensive]] | |||
[[Category: Level Design]] | |||
[[Category: Tutorials]] |
Latest revision as of 05:46, 12 July 2024


It is covered here for historical and technical reference.

Co-op Strike (internally: coopmission
) is a cooperative game mode in Counter-Strike: Global Offensive.
It was first introduced as part of the Operation Wildfire Gemini Campaign. And now on the Steam Community Workshop. Like Guardian, it is exclusively available through Operations or the Steam Workshop, but can be easily accessed through the console.
In Co-op Strike, two human players go against a staged fight against an AI controlled terrorist team in a story-driven scenario. The map prefix for these maps is coop_
. It shares the same prefix as some custom community Co-op game modes.
Creating a Co-op Strike map in Valve's style is a long process and it is expected from the readers that they have basic knowledge on how to create maps and work with entities, I/O, along with a basic knowledge of VScript. It is not advised to create a Co-op Strike map if there is no prior experience in mapping.
Game Mode Description
If a standard map is loaded in this game mode, this game mode is technically comparable with Competitive, but with the following differences:
- Human players are assigned to CTs and spawn at enabled info_player_counterterrorist entities as usual, whereas enemy bots spawn at enabled info_enemy_terrorist_spawn entities. Players spawn at the round beginning but also when an appropriate VScript function is called. During warmup, all players respawn if they die, as usual.
- The round does not end if the Terrorists are eliminated due to mp_use_respawn_waves 2.
- CTs always wear an item_heavyassaultsuit that has slightly different properties in this game mode.
- CTs cannot defuse planted C4 entities.
- If all human players are dead, they lose the mission and a new round begins.
- If the human players win the round (either by rescuing a hostage or via game_round_end), they complete the mission and the next map loads.
Creating the Map
Entities used in Co-op Strike
These are entities introduced with the Co-op Strike game mode along with some other entities that will be used in this tutorial.
- info_enemy_terrorist_spawn: The places where the enemy bots will spawn. No other spawn entities are used for them. It is the Co-op variant of info_player_terrorist, so to speak.
- logic_script: The entity we connect out VScript to in order to be able to use functions via I/O.
- game_coopmission_manager: This entity fires outputs when special Co-op events occur, for example when a wave is completed.
- item_coop_coin: The secret Co-op coin for extra points at the end of the level. At the moment, exactly 3 coins need to be in a map for the Co-op coin system to work.
- point_hiding_spot: Simply said, this is an entity that tells the bots "Here is a good hiding spot".
- prop_exploding_barrel: The oh so great exploding barrels. Place them a few units above the ground to prevent them from getting "sucked" into it.
- info_hostage_spawn: Rescuing the hostage is one way of completing the level.
- game_round_end: This entity can end rounds on command and make a team win. Used in Co-op maps where rescuing a hostage is not the winning condition.
The entities above will be the entities that will be used in Co-op maps.
Environment
When starting your map keep Co-op in your mind. Make sure there are plenty of hiding spots for either the players or the bots. Think of places where you want to place new weapons for the players to use. Think of maybe nifty ways to use the environment to the players' advantage. Note that it is players vs. AI which is different from any other game mode.
Co-op is based around a "wave" system. This means that a group of enemies will spawn, usually 5, that the players has to exterminate in order to advance to the next part of the map. So when creating the map, split your map up in multiple parts. Each part will have its own enemies by your choosing. Unless you want one big linear map with 40 enemies at once. Split your map up in different parts so that you can only advance once each wave is completed.
Player Spawns
In this game mode, the idea is to place both human and bot spawn points across the map where needed and to only have a set of them enabled so players won't spawn in the wrong places. This will be managed by the VScript functions OnSpawnsReset()
and OnWaveCompleted()
that we will deal with later, see #Explanation of Code.


Human Player Spawns
The human players (re)spawn at enabled info_player_counterterrorist entities.
It is a good idea to have a spawn point where the players can "relax" and setup for the fights that are about to commence. For that purpose, Valve has made spawn rooms in their Co-op maps where the players can choose weapons and grenades that they will use in the upcoming level. There can also be a way to adjust the difficulty of the bots so that the level is playable in #Hard Mode. For better gameplay, give the players "crappy" weapons at the beginning so they will look around in your map for better gear.
The way you create your spawn is up to you. You can either have a simple door divide the spawn with the enemies or you could have a fancy helicopter "cutscene" take you to the actual level. Be as creative as you can be.
If the human players can die in the mission (which will most likely be the case!) and if you don't want the human players to respawn at their very first spawn points of the map, you can place more CT spawns as "checkpoints" and leave them Disabled
by default. The Co-op VScript later can access these.
Enemy Spawns
The enemy bots (re)spawn at enabled info_enemy_terrorist_spawn entities.
For each section in your map you will need to have these entities in order for bots to spawn. Also give all of these spawn points in a section the same name. Say for the first section of the map you will enter, you name all the info_enemy_terrorist_spawn entities wave_01
. Do this for each section of your map in chronological order, e.g. wave_02
, wave_03
, and so forth.
You can give the spawn points different properties.
- For starters you can give them weapons.
glock
is the default weapon but you can add weapons by using the comma character,
so you can set something likeglock, ak47
and the bot on that location will spawn with a weapon_glock and a weapon_ak47. - You can also give the bot a different player model by using the
Override player model
keyvalue. One example for this istm_phoenix_heavy.mdl
which is also used in coop_cementplant. More model names can be found here.
After you've chosen your properties you need to set Enabled by default
to No
, except for the first wave of enemies that you come across those need to be Enabled
instead of Disabled
.
Doorways to new Sections
Since you have to divide your map into different sections you will need to have doorways that connect these sections and can only be accesed once a wave is completed. Simply put, put down a prop_door_rotating between your sections, lock them so players cannot rush through the map without completing the waves and give them a proper name so you can find them later when you're working on your script.
Collectible Coins
If you want players to explore your map more in order to get more points you can use the item_coop_coin. Once all 3 coins are collected in a level the players will get bonus points at the end of the level. Exactly 3 coins should be placed in a map: If you have 2 coins, the players will never be able to get the bonus points; Collecting more than 3 coins has the same effect as collecting only 3.
Co-op Gear
Here is some inspiration of what you can or should supply the human players with throughout the map:
- Weapons
- See Category: Counter-Strike: Global Offensive Weapons for a list of weapons. There are multiple ideas to supply players with new weapons.
- Use map-placed weapon entities, such as weapon_ak47, which can only be picked up once per round.
- Equip bots with them that the players will kill and loot.
- A simple "infinite" weapon pickup can be created with an invisible, non-solid func_button and a game_player_equip entity together with a prop_dynamic of the corresponding weapon.
- VScript can also handle the equipping.
- weapon_healthshot
- Players can use healthshots to heal themselves. They are good to be given before a difficult fight. Do not give too many healthshots, otherwise your map will be to easy.
- weapon_tagrenade
- The tagrenade grants a short "wallhack" effect: It makes enemies close to its explosion glow through walls for a brief moment.
- point_give_ammo
- If this entity receives the
GiveAmmo
input, it gives the activator full ammo for all his weapons. In coop_cementplant they used the ammo modelsmodels\props\coop_cementplant\coop_ammo_stash\coop_ammo_stash_full.mdl
and\coop_ammo_stash_empty.mdl
, when once used, the model changes from the former to the latter. Make it so that players can only take ammo from that point once so they can't run back in the level to restock on ammo again.
- prop_ammo_box_generic
- Each time this entity is +used, the reserve ammo of the activator's active weapon is increased by one clip size. The entity can be used 4 times.
- prop_weapon_refill_heavyarmor
- Don't forget about armor: As the players will lose armor points with each fight, they will soon appreciate this entity which can be +used by one player to replenish his armor to 200 points.
- weapon_zone_repulsor
- [Todo]
Co-op Tasks
Since you are trying to make a cooperative map, it is fun to include simple puzzles and tasks that can only be achieved if players work together. It can go from one player standing on an elevator and the other activating this elevator or a door with 2 levers to one player being blind and the other has to guide him/her through a dark room. Be creative!
Glowing props
Use glowing doors or buttons to tell the players "Go here to advance" or "Press me to advance". You can make props (the doorway to the next part of the map) glow so it's easy for players to find their way through your map.
Creating the Script
Co-op Strike uses VScript written in Squirrel to make the system function. Without the script the game mode can't work properly, so each Co-op map needs its own script.
- Start by creating your script (text file) and give it a name, for example
cooptutorial.nut
. Important: Make sure that the extension of the file is .nut, otherwise the game won't be able to read the file. - Now place this file in the correct folder inside your game files, for example
Steam/steamapps/common/Counter-Strike Global Offensive/csgo/scripts/vscripts/custom/
. Make sure it is somewhere inside thevscripts
folder. Subdirectories are arbitrary.
Once you've placed it inside the correct folder you can start editing it with notepad or any other text editor.
This script will be later be used for the Entity Scripts
property of a logic_script, which means that the script will be executed each time a new round begins. Variables and functions ("table slots" to be precise) will be saved after the execution and can be called when firing the inputs RunScriptCode
or RunScriptFunction
to that logic_script.
Code used in Example
Here an example of a Co-op Script. Feel free to copy this into your own script file since much of it is useful for every Co-op Strike map script. An example vmf will be provided at the end of this guide that works together with this script.
wave <- 0;
function RoundInit()
{
//Will do this everytime you start the map/round because we call it in the OnLevelReset
wave = 0;
//Reset the difficulty to normal at start of the round
SendToConsoleServer( "mp_coopmission_bot_difficulty_offset 1" );
ScriptCoopSetBotQuotaAndRefreshSpawns( 0 );
}
function ChangeGameModeToCoopIfNotCorrect()
{
// This will change the game mode and game type if the player has not initialized this before starting the map.
local game_mode = ScriptGetGameMode();
local game_type = ScriptGetGameType();
local map = GetMapName();
if (game_mode != 1 || game_type != 4)
{
SendToConsole("game_mode 1; game_type 4; changelevel " + map);
}
}
function SpawnFirstEnemies( amount )
{
ScriptCoopMissionSpawnFirstEnemies( amount );
ScriptCoopResetRoundStartTime();
wave++;
}
function SpawnNextWave( amount )
{
ScriptCoopMissionSpawnNextWave( amount );
wave++;
}
function OnMissionCompleted()
{
//what will happen once you've completed the mission (you could play a sound)
}
function OnRoundLostKilled()
{
//what will happen if you loose the round because you died (you could tell the players that your grandma is better than them)
}
function OnRoundLostTime()
{
//what will happen if you loose the round because the time runs out (you could tell the player that they are like turtles)
}
function OnRoundReset()
{
//called when the round resets
// IMPORTANT: you need a game_coopmission_manager that has the output 'OnLevelReset' when this is called you NEED to call this function
// in order for the level to work properly every round!
RoundInit();
}
function OnSpawnsReset()
{
//called right before the round resets (usually used for correcting stuff when on a new round other stuff is immediately called)
//enabled/disabled the correct spawns for the start. * means every group going from Terrorist_00 to infinite enemygroup_example
EntFire( "wave_*", "SetDisabled", "", 0 );
EntFire( "wave_01", "SetEnabled", "", 0 );
EntFire( "CT_*", "SetDisabled", "", 0 );
EntFire( "CT_1", "SetEnabled", "", 0 );
}
function OnWaveCompleted()
{
//Check which wave the player is and do stuff
if ( wave == 1 )
{
EntFire( "wave_*", "SetDisabled", "", 0 );
EntFire( "wave_02", "SetEnabled", "", 0 );
EntFire( "door_wave_01", "Unlock", "", 1 );
EntFire( "door_wave_01", "SetGlowEnabled", "", 1 );
}
else if ( wave == 2 )
{
EntFire( "wave_*", "SetDisabled", "", 0 );
EntFire( "wave_03", "SetEnabled", "", 0 );
EntFire( "door_wave_02", "Unlock", "", 1 );
EntFire( "door_wave_02", "SetGlowEnabled", "", 1 );
}
else if ( wave == 3 )
{
EntFire( "door_wave_03", "Unlock", "", 1 );
EntFire( "door_wave_03", "SetGlowEnabled", "", 1 );
}
}
|
Explanation of Code
We will go over everything inside the script and explain what it all does.
wave <- 0;
function RoundInit()
{
//Will do this everytime you start the map/round because we call it in the OnLevelReset
wave = 0;
//Reset the difficulty to normal at start of the round
SendToConsoleServer( "mp_coopmission_bot_difficulty_offset 1" );
ScriptCoopSetBotQuotaAndRefreshSpawns( 0 );
}
First wave <- 0;
: What we do here is define and initialize a variable that functions can call later. We have the function RoundInit()
that sets the variable wave
to 0. After that it tells the console to set the bot difficulty to 1, the easiest. After this it refreshes all the bot spawns.
function ChangeGameModeToCoopIfNotCorrect()
{
// This will change the game mode and game type if the player has not initialized this before starting the map.
local game_mode = ScriptGetGameMode();
local game_type = ScriptGetGameType();
local map = GetMapName();
if (game_type != 4 || game_mode != 1)
{
SendToConsole("game_type 4; game_mode 1; changelevel " + map);
}
}
The function ChangeGameModeToCoopIfNotCorrect()
changes the game_type and game_mode to 4 and 1 so that the game knows its playing a Co-op map. The coop_
map prefix is not sufficient to get the game mode right. You don't really have to know how this works; Just call this function when the map is started with the OnMapSpawn
output of a logic_auto. More on this in #Making your Script work with your Map.
function SpawnFirstEnemies( amount )
{
ScriptCoopMissionSpawnFirstEnemies( amount );
ScriptCoopResetRoundStartTime(); // sets round time to 45 minutes
wave++;
}
function SpawnNextWave( amount )
{
ScriptCoopMissionSpawnNextWave( amount );
wave++;
}
These are the 2 functions that will make the bots spawn. SpawnFirstEnemies(amount)
will spawn the first wave of your level and reset the timer to 45 minutes. From within Hammer we will call this function and replace amount
with a number of enemies to spawn. So if we want 3 bots to spawn we call SpawnFirstEnemies(3)
. What it also does is wave++
which means that it adds 1 to the variable wave
so when the level starts wave
is 0 but once a wave is spawned, it adds 1 so that wave == 1
. SpawnNextWave(amount)
is used in every subsequent wave. We use the variable wave
to let the map know at which wave the players currently are.
function OnMissionCompleted()
{
//what will happen once you've completed the mission (you could play a sound)
}
function OnRoundLostKilled()
{
//what will happen if you loose the round because you died (you could tell the players that your grandma is better than them)
}
function OnRoundLostTime()
{
//what will happen if you loose the round because the time runs out (you could tell the player that they are like turtles)
}
The names of the functions say it all. As in coop_cementplant, Valve made it so that your "Boss" gives a little message.
function OnRoundReset()
{
//called when the round resets
// IMPORTANT: you need a game_coopmission_manager that has the output 'OnLevelReset' when this is called you NEED to call this function
// in order for the level to work properly every round!
RoundInit();
}
function OnSpawnsReset()
{
//called right before the round resets (usually used for correcting stuff when on a new round other stuff is immediately called)
//enabled/disabled the correct spawns for the start. * means every group going from Terrorist_00 to infinite enemygroup_example
EntFire( "wave_*", "SetDisabled", "", 0 );
EntFire( "wave_01", "SetEnabled", "", 0 );
EntFire( "CT_*", "SetDisabled", "", 0 );
EntFire( "CT_1", "SetEnabled", "", 0 );
}
OnRoundReset()
is called when the players have died and the round "resets". It calls the function RoundInit()
, which is explained above.
Then we have OnSpawnsReset()
. This is called just before the round resets. This gives the map time to reset the player and enemy spawns to its original state. Here we Disable every entity whose targetname starts with wave_
and enable only the first wave for enemies. You can do the same for CT spawns. Multiple CT spawns are used in bigger levels so that the players don't have to walk from their first spawn to the point where one player died.
EntFire
is used for I/O like in Hammer, but here we don't need to specify an output. Normally, the output defines "when" an input should be fired, but in VScript, that point in time is when the EntFire
line is executed. For example EntFire( "wave_*", "SetDisabled", "", 0 )
fires the input SetDisabled
to all entities starting with "wave_", with an empty parameter override (""
) and no delay (0
).
function OnWaveCompleted()
{
//Check which wave the player is and do stuff
if ( wave == 1 )
{
EntFire( "wave_*", "SetDisabled", "", 0 );
EntFire( "wave_02", "SetEnabled", "", 0 );
EntFire( "door_wave_01", "Unlock", "", 1 );
EntFire( "door_wave_01", "SetGlowEnabled", "", 1 );
}
else if ( wave == 2 )
{
EntFire( "wave_*", "SetDisabled", "", 0 );
EntFire( "wave_03", "SetEnabled", "", 0 );
EntFire( "door_wave_02", "Unlock", "", 1 );
EntFire( "door_wave_02", "SetGlowEnabled", "", 1 );
}
else if ( wave == 3 )
{
EntFire( "door_wave_03", "Unlock", "", 1 );
EntFire( "door_wave_03", "SetGlowEnabled", "", 1 );
}
}
Last we have OnWaveCompleted
. This is called when you've killed every enemy of a wave. Because we keep track on which wave we are with the variable wave
, we can do different things for different waves. Here we say: "If wave
equals 1" then it disables all the enemy spawns but the spawn point for wave 2. We also unlock the door to the next section of the map and set the door to glow so it is easier for the players to find their way.
This is all the code that is needed for a working Co-op map. If you didn't quite understand a lot of this, try copying the code from here into your own.
Info about Code
There are a few functions that can be used to do different things.
ScriptCoopResetRoundStartTime()
- Resets the roundtime to 45 minutes.ScriptCoopMissionSpawnFirstEnemies(#)
- Spawns the first#
enemies.ScriptCoopMissionSpawnNextWave(#)
- Spawns a new wave, where#
is the number of enemies you want to spawn, for exampleScriptCoopMissionSpawnNextWave(5)
.ScriptCoopMissionRespawnDeadPlayers()
- When this is called, it will respawn all dead CTs.


Making your Script work with your Map
We now have created a map and our script but your map won't work without connecting them.
Setting up the entities to work with the script
If you haven't already, create a logic_script and give it a simple name such as logic_script
.

logic_script
can be disadvantageous because outputs targeting logic_script
will target all logic_script
entities in the map. A targetname such as coop_script
would avoid this because it is not a classname.Go to the property Entity Scripts
, click on Manage...
, click on the +
and search for your script which should be placed in a subfolder of csgo/scripts/vscripts/
. Add it and the logic_script is done.
There should also be a game_coopmission_manager entity in the map. Go to its outputs and create one for every output that exists (except for the OnUser
ones).
These outputs call the script functions that we created in the scripting part.

Setting up the doorways between sections
Since Co-op Strike is a wave based system, we usually want to divide the map into sections with each section being unlocked when a wave is completed. Now we need to make sure that enemies will spawn once you enter a new section. So for example, we want to spawn bots when the players open the door to the next section. An output would look like this:
My Output | Target Entity | Target Input | Parameter | Delay | Only Once | |
---|---|---|---|---|---|---|
![]() |
OnOpen | logic_script | RunScriptCode | SpawnFirstEnemies(3) | 0.00 | No |
Do this for the first wave. For the other waves, use this:
My Output | Target Entity | Target Input | Parameter | Delay | Only Once | |
---|---|---|---|---|---|---|
![]() |
OnOpen | logic_script | RunScriptCode | SpawnNextWave(5) | 0.00 | No |
Of course you do not have to use a door - you can use whatever entity you'd like, such as trigger_once (OnTrigger), func_button (OnPressed), etc.
Finishing touches
Just a few more things to think about to complete the map.
Ending the Map
If you want the map to reload in the right game_type and game_mode if they were wrong, create a logic_auto and add the output:
Secondly, if you have a hostage_entity in the map, the players can win the map by bringing it to a func_hostage_rescue. If you want the players to win by getting to an end point you'll have to use a game_round_end. Create a trigger at the end of your map with the output:
My Output | Target Entity | Target Input | Parameter | Delay | Only Once | |
---|---|---|---|---|---|---|
![]() |
OnStartTouch | game_round_end | EndRound_CounterTerroristsWin | 5 | 0.00 | No |
And voilà, your map is fully playable.
Since you are playing against bots you'll have to make a proper navigation file so your bots don't look as much like bots. With the commands listed at Navigation Mesh Commands and Navigation Mesh Editing, you can do many things to improve the bot navigation. You can also put point_hiding_spots in your map to control where bots should hide.
Hard Mode
As with coop_cementplant and all succeeding Co-op maps by Valve, there was a secret lever to play the map in Hard Mode to get extra points at the end of the game. To do that, you'll have to set the console variable mp_coopmission_bot_difficulty_offset
with a point_servercommand. The value 1 stands for the easiest mode; If set to 5, the game will register that it's in Hard Mode. If you really want to give your players a challenge, use an offset of 7. You can also give your info_enemy_terrorist_spawn entities a difficulty which stacks on top of the one that is set with mp_coopmission_bot_difficulty_offset
.
Testing your Map
Now that your map is complete you'll have to test it in-game.
All you have to do is open up your console, type in map <mapname> coop
and it will load your map. If coop
is omitted, the game_type and/or game_mode are most likely not correct, so if you have set up the script function ChangeGameModeToCoopIfNotCorrect
, the map should reload itself and set those to the correct values.
Make sure to check the console for VScript errors whenever code will be executed. Examples:
<filename>.nut line = (1) column = (26) : error expression expected FAILED to compile and execute script file named scripts/vscripts/<path>/<filename>.nut
AN ERROR HAS OCCURED [the index 'wave' does not exist] CALLSTACK *FUNCTION [main()] <filename>.nut line [1] LOCALS [this] TABLE

println("...")
or ScriptPrintMessageChatAll("...")
.
I hope this guide will help a lot of people. Happy Mapping :)
External links
Here is an example of a working simple Co-op map with its script:
https://www.dropbox.com/s/val79djogsjzw9z/Creating%20a%20Co-op%20Strike%20Map%20Example.rar?dl=0
|