Overview
This article is intended to help those new to SMOD understand the structure of its "mapadd" system, especially those unfamiliar with programming or Hammer. If at any time, you still need help or have questions, ask! The same goes for any suggestions.
Mapadds are scripts used by SMOD to add or modify entities in a map as it loads, without actually changing the map file itself. For example, a mapadd script could:
- Make a dropship pick up an APC and then drop it down elsewhere.
- Change the player's starting position and give them extra equipment.
- Spawn a fireteam of Overwatch soldiers and send them searching for the player, or
- Send a barrage of headcrab shells into a rebel outpost.
It's possible to do nearly anything with enough knowledge and work put in. Along with basic TXT mapadd scripts, it's possible to use LUA code for more complex scripting. TXT and LUA scripts rest in the mapadd folder and share the name of the map they apply to (ex: smod\mapadd\dm_plaza17.txt will apply to maps\dm_plaza17.bsp).
Principles
The Basic Script
As said before, mapadd scripts add or modify "entities". One helpful way to understand entities is to think of everything in the game world that the player interacts with. Entities are the lights you see, the doors you open, the weapons you shoot, the crates you hide behind, the soldiers you hide from, and so on.
There are also other entities that are less tangible, such as triggers that activate events. A trigger may monitor whether the player's completed an objective, if their squad has been killed off, or if they've simply picked up a certain watermelon. Anything can happen depending on what the trigger is set up to do, from detonating a bomb to starting a music track.
With that in mind, let's break down how a typical mapadd script block is written.
|
|
Line 1 names the type of code block to use. In this case, Entities will be used to spawn something right at the start of the map.
Line 2 has an opening brace, {. This signals the start of our code block, which will continue until we hit its closing brace, }.
Line 3 names the entity we're going to spawn. The entity's classname is npc_combine_s, the standard Combine soldier.
Line 4 has another opening brace, beginning a new block where the soldier's characteristics can be set.
Lines 5 and 6 set the origin and angle values of the soldier. The origin determines what position in the level the soldier will be, using XYZ coordinates. For example, if the third number was 1000, the soldier would start 1000 units upwards in the air before gravity took its toll. The angle value works similarly, but changes what direction the soldier is facing.
Lines 7 and 8 begin another block named keyvalues, which SMOD mostly uses for entity values other than origin, angle, and some special SMOD-specific ones like longrange.
• Line 9 sets an additionalequipment value for the soldier. In this case, the value's set so the soldier will begin the map by carrying an AR2. For examples of other keyvalues, here's a list for the soldier from the VDC wiki.
• Line 10 sets a spawn flag value, somewhat similar to other keyvalues. In this case, it makes the soldier see farther than normal. Multiple spawn flags are used by combining their values; if we wanted him to see farther and drop a health kit once he died, we would combine 256 above and 8 to make it 264.
• The next three lines (11, 12, 13) are all closing braces for the keyvalues block, the npc_combine_s block, and the Entities block, in that order. Every opening brace needs a matching closing brace or problems will pop up, like all or part of the script being ignored by the game. Note how the three closing braces are set apart a number of spaces, just like the opening braces. It's a good habit that you do something like this to keep your braces organized. This will make your code easier to read, which can help with finding and fixing problems later such as missing or double braces.
Randomized Spawns
Like it says on the tin, this section spawns entities in the map and places them at randomly selected navigation nodes. The format here is a bit different from Entities but much of the above still applies. A small example:
|
|
• Lines 1 and 2 begin the Randomspawn section, and lines 3-4 begin a block for some random Combine soldiers.
• Line 5's count sets the number of soldiers this block will spawn to 3.
• Line 6 uses values to set the targetname, squad name, and number of grenades each soldier has, similar to
the Entities section's keyvalues block.
• Line 7 uses weapon to arm the soldier with an AR2. Certain commands/keyvalues like this can be written outside of the
keyvalues block.
• Lines 8-10 end the soldier block and begin a removenodes block. This kind is used to ignore some nodes when
selecting random nodes to spawn entities at.
• Line 11 picks the coordinates [ 100, 50, 0 ] as a starting point. Line 12 says to exclude all the nodes within 1000 units of this starting point from being a spawn location. Lines 13 and 14 close the removenodes block, and then close the randomspawn block.
Inputs and Outputs
When creating scripts that do more than just spawn things, inputs and outputs play a large part.
If you're unfamiliar with them, think of "inputs" as commands or scripts that entities use. For example, a Combine soldier's Ignite input will make him burst into flames, or the same soldier's BeginRappel input will make him drop down from his zipline, if he's set up to use one.
In a similar vein, "outputs" are events in the game world that can be used to fire off inputs. For example, a soldier's OnFoundPlayer output can trigger an alarm when the soldier spots the player.
To illustrate, below is a zombie that's been rigged to die when struck by a single bullet or other unfortunate circumstance.
"Entities"
{
"npc_zombie"
{
"origin" "1000 200 64"
"keyvalues"
{
"OnDamaged" "!self,SetHealth,0,2,-1"
}
}
}
Above, the OnDamaged output will jump into action once the zombie suffers some kind of damage. It will then wait for 2 seconds before giving the zombie a SetHealth command, setting its health to 0 and killing it.
The format for outputs is <entity name>,<input>,<parameter>,<delay>,<# of times>:
Entity name is where to put the classname or targetname of an entity or group of entities.
Input is the type of command that should be given to whatever is selected by Entity Name.
Parameter is extra data required for the input. In this example, SetHealth requires a number to determine the new health amount. If a value isn't needed, this should be left blank with no spaces between the two commas.
Delay sets how many seconds until the command is actually carried out.
# of times determines how many times this output can be triggered. A value of 2 would allow the output to be triggered once, then once again. A value of -1 means this output is infinitely repeatable.
Labels
The next section type to be covered is the labeled one. Label sections are virtually identical in format to the Entities section, but are used to
manipulate entities some time after the map's start. Commands under a label will only be executed once the label is triggered by a timer
or an event in the game, which can make optimizing the script for play that much easier. You may also just use labels to organize your script, which can make editing easier down the road. For instance:
"Entities"
{
"instant_trig"
{
"keyvalues"
{
"Label" "CSpawn"
"Timer" ".01"
}
}
}
"Entities:CSpawn"
{
"npc_citizen"
{
"origin" "1000 200 64"
"angle" "0 180 0"
}
}
The above creates a labeled section named CSpawn, along with a trigger that executes the label immediately, spawning a citizen.
Curiously, some entities may act strangely or not work inside labels, but will do their jobs perfectly in the Entities section. One example is point_teleport, which will not do anything useful if placed in a label.
Precaching
You've probably had it happen before - while plowing through some supply crates, the screen stutters just before a new weapon pops out. After picking it up and turning on its laser sight, another stuttering fit occurs. This is caused by on-the-fly loading of models, textures, sounds and so on into the game's memory cache. While not a serious issue, this can be fixed by loading in advance things the mapadd will use, thanks to a precache block like that shown below.
"Precache"
{
"entity" "npc_citizen" // Loads an entity and its associated assets (sounds, models, etc)
"model" "models/weapons/v_mp5.mdl" // Loads a specific model.
"model" "models/weapons/w_mp5.mdl"
"sound" "buttons\button17.wav" // Loads a sound through its filename path.
"sound" "Weapon_SVD.Fire" // Loads a sound through its SoundID.
}
This isn't necessary for things spawned at the very start of the map with the Entities block. It's also usually not necessary to load an entity's models or other assets if you've precached the entity. One exception would be where you precache the citizen entity, but spawn a citizen with a rebel model later on, since precaching the entity only loads the blue uniform models.
Sounds that will be played through sound events must be loaded beforehand or they'll refuse to play. You can normally skip loading other sounds; those will be at least partially loaded already.
Other notes
- Take advantage of labels to optimize the script! Even a particularly unlucky player may be engaged with a dozen soldiers at most, so there's no need to have another five dozen soldiers elsewhere hogging up resources and slowing the game down. Always try to reasonably limit how many entities, especially NPCs, are active at a time. If a horde is called for, make use of npc_maker or other means to create a steady stream of trouble.
- Don't expect all entities to work as advertised. Certain entities like point_teleport will act in an unusual way or not work at all, especially outside the Entities section.
- The template flag will not work properly on an entity created from a mapadd script. This means npc_template_maker, env_entity_maker, and other entities that rely on templates are pretty useless in mapadding. However, npc_maker and labels can be used for similar effects.
- Because it's tricky to use most brush entities within mapadd scripts, triggers like trigger_multiple are often avoided. Interaction with some brush entities, like func_tank (used for mounted AR2 in Route Kanal), should be avoided entirely or the game can crash. However, instant_trig is a trigger type specially made for SMOD as a substitute for brush-based triggers. You can read more about instant_trig here.
Further reading
For those needing a hands-on exercise to really "get" what's on this page, there's one such exercise here that will walk you through using each section type talked about on this page. Once you've run through, you should be comfortable enough in the how-to of things to understand the other tutorials and examples.
There is also a mapadd command reference here which describes the various commands and keyvalues used in mapadd scripts.
Customization |