Difference between revisions of "L4D2 Vscripts"

From Valve Developer Community
Jump to: navigation, search
(Decrypting NUC files: removed outdated link and added link to decrypted mutation vscripts)
 
(219 intermediate revisions by 17 users not shown)
Line 1: Line 1:
{{sq}}{{l4d2}} '''Left 4 Dead 2 vscripts''' are Squirrel language-based scripts that can be run in-game to perform various in-game tasks and changes.
+
{{otherlang2
{{stub}}
+
|noborder=true
{{Note|Information on vscripts is currently incomplete. Descriptions, accuracy, and information are subject to change.}}
+
|zh-cn=L4D2_Vscripts:zh-cn
 +
|zh-tw=L4D2_Vscripts:zh-cn
 +
}}
  
==Description==
+
{{toc-right}}
Left 4 Dead 2 vscripts are written in [http://squirrel-lang.org/ Squirrel], a compilable scripting language similar to [http://www.lua.org/ Lua].
+
{{sq}}{{l4d2}} '''Left 4 Dead 2 [[VScript|VScripts]]''' are server-side scripts that are run in an in-game virtual machine. They are written in [[Squirrel]], a compiled scripting language similar to [http://www.lua.org/ Lua]. It currently runs Squirrel version 3.0.4.
  
Some uses of vscripts include:
 
  
*Mini-games found in Dark Carnival - counters, timers, prop spawning
+
== Uses ==
*Some Scavenge Logic
 
*Director manipulation - onslaughts, reserved wanderers, complete emptiness/silence, [[L4D2_Level_Design/Boss_Prohibition|prohibition of boss infected]] (tanks and witches), direction of mobs
 
*Model manipulation - green diesel cans attached to survivors in Hard Rain
 
*Much more!
 
  
The file extensions of vscripts are .nut and .nuc, where .nut files are human readable using a text editor. Official .nuc script files are located in '''scripts/vscripts''' within pak01_dir.vpk (you can open this file with third party programs like [[GCFScape]]).
+
=== [[L4D2_Director_Scripts|Director Scripts]] ===
 +
The most common use of [[VScript|VScripts]] in Left 4 Dead 2 is to influence the behavior of the AI Director. These scripts can range from simple adjustments of infected spawning and [[L4D2_Level_Design/Boss_Prohibition|prohibiting boss infected]], to custom events like onslaughts and gauntlets, and even complex staged panic events and fully [[L4D2_Level_Design/Custom_Finale|custom finales]]. Most of the events in the official campaigns are mainly implemented this way.
  
Some entities that accept vscripts as inputs or properties include [[Info_director|info_director]], [[Logic_script|logic_script]], and [[Trigger_finale|trigger_finale]].
+
Director Scripts work mainly by writing overriding values of the various variables used by the Director into a <code>DirectorOptions</code> table.
  
===Decompiling nuc-files===
+
Only one Director Script can be running at a time. Executing a new one will terminate any previous running one and remove any values it set in <code>DirectorOptions</code>.
.Nuc files are ICE encrypted .nut files. The encryption key is '''SDhfi878'''. You can use [[VICE]] to decode them or download already deciphered scripts [http://www.multiupload.com/OXO3T2S1YA here] (includes all scripts from version 2.0.1.8 and all mutations).  
 
  
==Practicing the Squirrel language==
+
=== [[#Entity Scripts 3|Entity Scripts]] ===
Developers that would like to practice Squirrel can quickly set up an environment to do so. It can be as simple as opening up L4D2 and executing scripts from the console. One can also set up a light-weight squirrel interpretor from Windows Command Prompt:
+
Another common use is to attach a script to an entity. The script provides easy access to read and modify many of the entity's properties, and even to write new [[KeyValues]]. This allows for controlling entities in ways that would be very complicated or impossible to with the entity I/O system in Hammer.
===Windows Command Prompt===
 
#Download and extract the [http://modulatum.wordpress.com/2008/03/20/squirrel-binary-for-windows/ Squirrel interpretor binary (Version 2.2, 32-Bit)] to any folder. sq.exe is the interpretor. sq.exe can also be created by compiling from source code.
 
#Any scripts within that folder are executed with the command <code>sq ''filename''.nut</code> via Windows Command Prompt {{tip|There are sample scripts included with the compiled binary. Try running something simple first, like the "Hello, World!" script. <code>sq hello.nut</code>}}
 
#Optional: Set up a path to sq.exe within the Environment Variables in Windows. This allows convenient execution of .NUT scripts from any folder from the Command Prompt. Go to System Properties via the Control Panel, select the Advanced tab, and click the Environmental Variables button. Under System Variables select the Variable "Path" and click on the Edit button. Add the full path to sq.exe (for example, C:\Squirrel, where sq.exe is located in C:\Squirrel) to the other paths to the Variable Value. Restart the Command Prompt and <code>sq ''filename''.nut</code> should be able to work from any folder.
 
  
Documentation and generic sample scripts can always be found at the [http://squirrel-lang.org/ official Squirrel Language site].
+
Any entity is capable of running a script, and has the ability to set a specified ''think'' function to run every 0.1 seconds, as well as executing script code as an entity output.
===L4D2 Client===
 
*[[info_director]]
 
**Loads DirectorOptions such as onslaughts, panic events, infected limits, etc.
 
**Can be loaded in console with ent_fire command <code>ent_fire <targetname of info_director> BeginScript <name of script></code>
 
*[[logic_script]]
 
**The vscript that is set up in the logic_script will load automatically on load
 
**The vscript can be reloaded with console command <code>script_reload_code</code>
 
*Game Modes {{todo|see if it also applies to coop, versus, etc.}}
 
**Scavenge
 
***A per-map script is loaded automatically during Scavenge Mode
 
***Name the script <map name>_scavenge.nut.
 
***For example, c1m4_atrium_scavenge.nut.
 
**Mutators
 
***Will automatically run the script with the same name as the game mode.
 
***For example, running a map <code>map <map name> realismversus</code> will automatically load realismversus script.
 
*Events
 
**Finale
 
***"Script" KeyValue of trigger_finale does not need to be set up
 
***Automatically loads <map name>_finale.nut script on finale trigger
 
***Known to work in this fashion in Custom and Scavenge finale type (set up in trigger_finale)
 
  
==Scripting samples==
+
Some entities also have specialized functions for VScripts, with the most prominent being [[point_script_use_target]], which allows for turning other entities into fully programmable timed buttons.  
{{Warning|}}NEVER use the '''=''' operator when trying to influence the director. Use <-. The difference is semantics. If you use =, it will throw an error if the table slot isn't defined (like if a previous script didn't define it). <-, on the other hand, will create the variable if it does not exist. Some of these samples are only portions of a full script, so make sure that the variables are created beforehand.
 
===Targeting entities with a director script===
 
Add something like this in the script for your minifinale when your horde wave gets triggered:
 
<pre>{
 
EntFire( "church_bell_relay", "Trigger", 0 )
 
}</pre>
 
Then place a logic_relay with that name to send an output to your ambient_generic.
 
  
===Triggering a panic event though a script===
+
=== [[#Global Scripts 3|Global Scripts]] ===
If you want to start a panic event through a script (not an onslaught) then use this:
+
Scripts can also be made to run on map load, based on game mode and optionally map name. These scripts are commonly used to create scripting and modify global Director options for [[Mutation_Gametype_(L4D2)|mutations]], but can also be used in custom maps.
<pre>
 
//-----------------------------------------------------
 
local PANIC = 0
 
//-----------------------------------------------------
 
  
DirectorOptions <-
+
Global scripts can have [[List_of_L4D2_Script_Functions#Scripted_Mode_hooks|script hook functions]] added that get called from the game at certain events, like when players/objects take damage.  
{
 
A_CustomFinale1 = PANIC
 
A_CustomFinaleValue1 = 2
 
}</pre>
 
The number refers to the number of waves that you want in your minifinale. This is what was used at the ferry in the first map of Swamp Fever. This script needs to be executed via scriptedpanicevent input to the director. Should also be initialized on a battlefield or finale nav mesh.
 
 
 
===Handling a survivor===
 
<pre>/***************************************
 
Get a handle on a specific survivor
 
- This is pretty hackish, I can't just say Entities.FindByName(null, "playername"), (YET) because
 
I don't know what it's expecting internally. And I can't "learn" it's name by saying survivior.GetName(),
 
because what that returns is just some a function ref, it's probably the C++ side wrapped in a closure, OPAQUE, bummer.
 
***************************************/
 
 
 
//Get Nick
 
Nick <- Entities.FindByModel(null,"models/survivors/survivor_gambler.mdl");
 
// Nick is class "player" (which must be related or superset of CBaseEntity)
 
Nick.SetHealth(0); // Ouch but he won't incap until hit again, even below zero
 
//or Super Nick!
 
Nick.SetHealth(50000); // Tank gonna get slapped to death.</pre>
 
 
 
{{note|'''"!survivorname"''' can also be used as the entity name. For example, the health of the survivor can be reduced with
 
 
 
<pre>EntFire("!nick", "SetHealth", 1)</pre>
 
 
 
'''!nick''', '''!rochelle''', '''!coach''', '''!ellis''' point to the corresponding player class entities in game. This was discovered by looking at the passing campaign decompiled levels where the designers used '''!zoey''', '''!louis''' and '''!francis''' in the actual level to teleport the corresponding L4D1 npcs around the map.}}
 
 
 
==="Detecting"/Finding Survivors===
 
[http://forums.steampowered.com/forums/showthread.php?t=1374401] Just a sample you can use in your own code to find survivors. You need to call if after a delay of 10 seconds or so with a logic_auto
 
 
 
Something like
 
 
 
logic_auto > onmapspawn "logic_script_name" runscriptcode with a value of "FindSurvivors()"
 
 
 
Then you can iterate (loop) through the survivors like so...
 
<pre>/*
 
findsurvivors.nut
 
author: Lee Pumphret
 
http://www.leeland.net
 
 
 
The survivors table, once initialized, holds an entity reference to all survivors
 
To reference a specific survivor, you can say
 
survivors.nick (etc...)
 
*/
 
 
 
survivors <-{
 
  coach = "models/survivors/survivor_coach.mdl",
 
  ellis = "models/survivors/survivor_mechanic.mdl",
 
  nick = "models/survivors/survivor_gambler.mdl",
 
  rochelle = "models/survivors/survivor_producer.mdl"
 
}
 
 
 
survivors_found <- 0 // flag set to true once survivors are found
 
 
 
/*
 
Find survivors, this needs to be called after a delay. If you call it immediately,
 
it will fail as they have not been loaded yet, 10 sec after map load should be good.
 
You can call it with a logic_auto output, runscriptcode FindSurvivors()
 
*/
 
function FindSurvivors(){
 
  foreach(s,m in survivors){
 
    printl ("looking for "+s+" mdl:"+m);
 
    survivor <- Entities.FindByModel(null, m)
 
    if (survivor){
 
      printl(s+" found: "+survivor);
 
      survivors[s] = survivor
 
      survivors_found++
 
    }else{
 
      printl(s+" NOT FOUND!: "+survivor);
 
      survivors[s] = null
 
    }
 
  }
 
}</pre>
 
  
*[http://www.leeland.net/gehaonsusu.html Getting a handle on survivors - findsurvivors.nut] - Author's website
+
There are many utility functions and features readily available for these scripts, including a resource and building system, and custom panic wave spawning. Please see Valve's [[L4D2_EMS|Expanded Mutation System tutorial]] for more information.
  
===Find Survivors Closest to Entity===
+
=== Other uses ===
This builds off multiple scripts. Refer to the [http://www.leeland.net/survivorclosest-nut.html author's website] for a explanation and complete code. [http://forums.steampowered.com/forums/showthread.php?t=1401390]
+
All scripts can access functions for many features, including [[L4D2_EMS/Appendix:_Spawning|spawning entities]] either from precompiled lists or programmatically, [[L4D2_EMS/Appendix:_Spawning_Infected|spawning infected]], a [[L4D2_EMS/Appendix:_HUD|HUD system]], [[L4D2_EMS/Appendix:_Table_Save_Restore|storing data]] across levels and to disk, and much more.
  
<pre>IncludeScript("survivorclosest.nut", this);
+
Please see [[L4D2 Vscript Examples]] for more examples and ideas.
  
ent <- Entities.FindByName(null, "entname");
 
  
printl("Found entity "+ent);
+
== Script files ==
 +
The scripts are loaded from text files with the file extensions <code>.nut</code> and <code>.nuc</code>, where <code>.nuc</code> denotes encryption of plain text <code>.nut</code>. Custom scripts are read from '''\left 4 dead 2\left4dead2\scripts\vscripts\''', as well as '''\scripts\vscripts\''' when packed into a .vpk file.
  
 +
=== Location ===
 +
;Official .nuc script files are located in '''scripts/vscripts''' in multiple locations
 +
{{note|Browse and extract [[VPK]] files with third-party programs like [[GCFScape]].}}
 +
*'''left 4 dead 2\left4dead2\pak01_dir.vpk'''
 +
;*'''left 4 dead 2\left4dead2_dlc1\pak01_dir.vpk'''
 +
:April 22, 2010 The Passing update
 +
;*'''left 4 dead 2\left4dead2_dlc2\pak01_dir.vpk'''
 +
:October 5, 2010 The Sacrifice update
 +
;*'''left 4 dead 2\left4dead2_dlc3\pak01_dir.vpk'''
 +
:March 22, 2011 Cold Stream Beta / L4D1 Transition Project update
 +
;*'''left 4 dead 2\update\pak01_dir.vpk'''
 +
:This is where mutations were updated/replaced bi-weekly.
 +
;*'''left 4 dead 2\sdk_content\scripting\scripts\vscripts\'''
 +
:Plaintext versions of many of the scripts introduced in the EMS update.
  
FindSurvivors()  // make sure this script isn't called right away or they won't be found yet
 
  
who <- FindSurvivorClosestToEnt(ent); // returns entity reference to survivor
+
=== Decrypting NUC files ===
if (who){
+
.nuc files are ICE encrypted .nut files. The encryption key is '''SDhfi878'''. You can use [[VICE]] to decode them.<br>
  printl(who);
+
You may find decrypted mutation vscripts [[L4D2_Decrypted_mutations|here]]
  printl(who+" is closest to "+ent.GetName()+" at "+who.GetOrigin());
 
  // do something here
 
}else{
 
  printl("Nothing found");
 
}
 
  
 +
== Loading vscripts ==
 +
VScripts are loaded in different ways depending on what they are used for. They can also be manually loaded with the console command <code>script ''filename''</code>
  
/* or, if you want the name returned (the key into the
+
=== Director Scripts ===
survivors table), add a true value as a second param */
 
  
who <- FindSurvivorClosestToEnt(ent, 1); // returns entity reference to survivor
+
;Using the [[info_director]] entity using the following inputs:
if (who){
+
:<code>BeginScript <''script name''></code>
  printl(who);
+
::Executes a generic Director script, for example for onslaught events or changing spawning behavior.
  printl(who+" is closest to "+ent.GetName()+" at "+survivors[who].GetOrigin());
+
:<code>EndScript</code>
  // do something here
+
::Ends the running script and resets the Director options to the map specific values.
}else{
+
:<code>BeginScriptedPanicEvent <''script name''></code>
  printl("Nothing found");
+
::Begins a Scripted Panic event.
}</pre>
 
  
==="Hovering Chopper Support" Example===
+
{{note|Scripts used with <code>BeginScriptedPanicEvent</code> will not work if they are in a subdirectory, even though you can use subdirectories in other script contexts. They must reside under the vscripts folder, or they will simply act as a 1 stage 1 second delay.}}
[http://www.leeland.net/hochsuch.html] If you want to parent a func_tracktrain to something, so it moves relative, you don't parent the path, you parent the func_tracktrain itsef to the item, and the path will move relative to this. So here's what this does.
 
*You make your track as normal, your "train" heli, whatever.
 
*Place an info_target in the center of your path with a name ex: chopper_follow
 
*Then you parent that func_tracktrain to the target (chopper_follow).
 
*Pass that target as the first entitygroup to this script.
 
*Then what happens is that the script computes the average position of the survivors ever 1/10 of a second, moves the info_target there (chopper_follow), and the path_track will follow.
 
*Have your track the height you want, relative to the info_target.
 
{{note|There are a couple caveats, you'll need to call FindSurvivors() after a delay of map load, or it won't work. They won't be loaded yet, a logic_auto output onmapspawn with a delay of 10 seconds should do it.}}
 
*[http://www.leeland.net/hochsuch.html Hovering chopper support - choppercover.nut] - Author's Website. Contains full code, video of the example, map source to an example, and description.
 
*[http://www.youtube.com/watch?v=orm1SJwaijE l4d2 vscript tracktrain hovertest] - Video of prototype
 
  
===Changing The Skybox On Map Load===
+
;Finale scripts
[http://www.leeland.net/chskonmaplow.html] Save the file to your scripts/vscripts folder as whichskybox.nut
+
:The <code><map name>_finale.nut</code> script is atumatically loaded when a finale map starts. The script is triggered when the [[trigger_finale]] is used, either manually by the player, or with the <code>ForceFinaleStart</code> input.
 +
:{{todo|[[trigger_finale]] also has an option to specify a finale script, but it doesn't seem to work}}
  
Add a logic_script entity to your map, turn off smartedit and add a "vscripts" key with a value of whichskybox.nut
 
  
Obviously you would have to adjust fog and lighting entities as well for it to not look terrible.
+
All Director Scripts are placed in the script scope <code>DirectorScript</code>
  
<pre>Skyboxes <- [
+
=== Entity Scripts ===
  "sky_l4d_c1_1_hdr",
+
;Automatic loading at map start
  "sky_l4d_c1_2_hdr",
+
:Uses the script set in the '''Entity Scripts''' KeyValue of the entity.
  "sky_l4d_c2m1_hdr",
+
;Manual loading
  "sky_l4d_night02_hdr",
+
:Using the <code>RunScriptFile</code> input.
  "sky_l4d_predawn02_hdr",
 
  "sky_l4d_c4m1_hdr",
 
  "sky_l4d_c4m4_hdr",
 
  "sky_l4d_c5_1_hdr",
 
  "sky_l4d_c6m1_hdr"
 
]
 
  
worldspawn <- Entities.FindByClassname (null, "worldspawn");
+
Entity Scripts are placed in the script scope <code>_<unique ID>_<entity name></code>
local i = RandomInt(0,Skyboxes.len()-1);
 
printl("Skybox is "+Skyboxes[i]);
 
printl( worldspawn.__KeyValueFromString("skyname",Skyboxes[i]) );</pre>
 
  
===Changing Witch Movement Type On Map Load===
+
=== Global Scripts ===
This was based off of the code found here: [http://www.leeland.net/chskonmaplow.html] Save the two files to your scripts/vscripts folder as daywitch.nut and nightwitch.nut
+
;Mode Specific Scripts
 +
:Will automatically run the script with the same name as the [[Mutation_Gametype_(L4D2)|game mode]]. For example, running a map <code>map <map name> mutation12</code> will automatically load the Realism Versus script, <code>mutation12.nuc</code>.
 +
:They are placed in the script scope <code>g_ModeScript</code>
  
Add two logic_script entities to your map. Give "Name" a value of daywitchscript, turn off smartedit and add a "vscripts" key with a value of daywitch.nut.
+
{{note|Adding a script for a mode will enable Scripted Mode on it.}}
  
Repeat for the other with a "Name" value of nightwitchscript, turn off smartedit again and add a "vscripts" key with a value of nightwitch.nut.
+
;Map Specific Scripts (only available in Scripted Mode)
 +
:Per-map scripts can be created that run when the map is loaded in the specified game mode, using the syntax <code><map name>_<mode name>.nut</code>, for example, <code>c1m4_atrium_mutation12.nut</code>.
 +
:They are placed in the script scope <code>g_MapScript</code>
  
There are two scripts involved here, one for day witches and one for night. These scripts run on map load and select the numerical equivialant of Midnight (0) or Morning (2) found in worldspawn. I seperated the scripts so entity logic can tie into them. More on that later.
 
  
daywitch.nut:
 
  
<pre>Skyboxes <- [
+
== Scripting environment ==
  "2"
+
'''Please see [[List of L4D2 Script Functions]] for built in classes and functions.'''
]
 
  
worldspawn <- Entities.FindByClassname (null, "worldspawn");
+
When a script is loaded, it is placed into a table, or Script Scope. Mode, map and Director Scripts are put into set scopes, while a unique scope is generated for each Entity Script. Please see [[Vscript Fundamentals]] for more information.
local i = RandomInt(0,Skyboxes.len()-1);
 
printl("Skybox is "+Skyboxes[i]);
 
printl( worldspawn.__KeyValueFromString("timeofday",Skyboxes[i]) );</pre>
 
  
nightwitch.nut:
+
=== Table structure ===
 
+
<source lang=cpp>
<pre>Skyboxes <- [
+
DirectorScript = // Base Director Scope.
  "0"
 
]
 
 
 
worldspawn <- Entities.FindByClassname (null, "worldspawn");
 
local i = RandomInt(0,Skyboxes.len()-1);
 
printl("Skybox is "+Skyboxes[i]);
 
printl( worldspawn.__KeyValueFromString("timeofday",Skyboxes[i]) );</pre>
 
 
 
This is the line of entity logic I used in my test map to achieve random day or night witches. Note I start the logic_auto with a delay of 10 seconds so the AI can get ready. It doesn't seem to work if I don't, as noted in the "Hovering Chopper Support Example" above.
 
 
 
<pre>Class: logic_auto
 
My output named: OnMapSpawn
 
Targets entities named: daynightwitchcase
 
Via this input: PickRandomShuffle
 
Delay: 10.00
 
 
 
Class: logic_case
 
My output named: OnCase01
 
Targets entities named: daywitchtemplate
 
Via this input: ForceSpawn
 
Delay: 0.00
 
 
 
My output named: OnCase02
 
Targets entities named: nightwitchtemplate
 
Via this input: ForceSpawn
 
Delay: 0.00
 
 
 
Class: point_template
 
Name: daywitchscripttemplate
 
Template 1: daywitchscript
 
 
 
Class: point_template
 
Name: nightwitchscripttemplate
 
Template 1: nightwitchscript
 
 
 
Class: logic_script
 
Name: daywitchscript
 
vscripts: daywitch.nut
 
 
 
Class: logic_script
 
Name: nightwitchscript
 
vscripts: nightwitch.nut</pre>
 
 
 
Compile your map with this logic in place, and both named scripts in the scripts/vscripts folder in your main game directory and you should see witches respond and behave differently, depending on which script the game chose randomly.
 
 
 
 
 
{{sq}}{{l4d2}} '''Left 4 Dead 2 vscripts''' are Squirrel language-based scripts that can be run in-game to perform various in-game tasks and changes.
 
{{stub}}
 
{{Note|Information on vscripts is currently incomplete. Descriptions, accuracy, and information are subject to change.}}
 
 
 
==Description==
 
Left 4 Dead 2 vscripts are written in [http://squirrel-lang.org/ Squirrel], a compilable scripting language similar to [http://www.lua.org/ Lua].
 
 
 
Some uses of vscripts include:
 
 
 
*Mini-games found in Dark Carnival - counters, timers, prop spawning
 
*Some Scavenge Logic
 
*Director manipulation - onslaughts, reserved wanderers, complete emptiness/silence, [[L4D2_Level_Design/Boss_Prohibition|prohibition of boss infected]] (tanks and witches), direction of mobs
 
*Model manipulation - green diesel cans attached to survivors in Hard Rain
 
*Much more!
 
 
 
The file extensions of vscripts are .nut and .nuc, where .nut files are human readable using a text editor. Official .nuc script files are located in '''scripts/vscripts''' within pak01_dir.vpk (you can open this file with third party programs like [[GCFScape]]).
 
 
 
Some entities that accept vscripts as inputs or properties include [[Info_director|info_director]], [[Logic_script|logic_script]], and [[Trigger_finale|trigger_finale]].
 
 
 
===Decompiling nuc-files===
 
.Nuc files are ICE encrypted .nut files. The encryption key is '''SDhfi878'''. You can use [[VICE]] to decode them or download already deciphered scripts [http://www.multiupload.com/OXO3T2S1YA here] (includes all scripts from version 2.0.1.8 and all mutations).
 
 
 
==Practicing the Squirrel language==
 
Developers that would like to practice Squirrel can quickly set up an environment to do so. It can be as simple as opening up L4D2 and executing scripts from the console. One can also set up a light-weight squirrel interpretor from Windows Command Prompt:
 
===Windows Command Prompt===
 
#Download and extract the [http://modulatum.wordpress.com/2008/03/20/squirrel-binary-for-windows/ Squirrel interpretor binary (Version 2.2, 32-Bit)] to any folder. sq.exe is the interpretor. sq.exe can also be created by compiling from source code.
 
#Any scripts within that folder are executed with the command <code>sq ''filename''.nut</code> via Windows Command Prompt {{tip|There are sample scripts included with the compiled binary. Try running something simple first, like the "Hello, World!" script. <code>sq hello.nut</code>}}
 
#Optional: Set up a path to sq.exe within the Environment Variables in Windows. This allows convenient execution of .NUT scripts from any folder from the Command Prompt. Go to System Properties via the Control Panel, select the Advanced tab, and click the Environmental Variables button. Under System Variables select the Variable "Path" and click on the Edit button. Add the full path to sq.exe (for example, C:\Squirrel, where sq.exe is located in C:\Squirrel) to the other paths to the Variable Value. Restart the Command Prompt and <code>sq ''filename''.nut</code> should be able to work from any folder.
 
 
 
Documentation and generic sample scripts can always be found at the [http://squirrel-lang.org/ official Squirrel Language site].
 
===L4D2 Client===
 
*[[info_director]]
 
**Loads DirectorOptions such as onslaughts, panic events, infected limits, etc.
 
**Can be loaded in console with ent_fire command <code>ent_fire <targetname of info_director> BeginScript <name of script></code>
 
*[[logic_script]]
 
**The vscript that is set up in the logic_script will load automatically on load
 
**The vscript can be reloaded with console command <code>script_reload_code</code>
 
*Game Modes {{todo|see if it also applies to coop, versus, etc.}}
 
**Scavenge
 
***A per-map script is loaded automatically during Scavenge Mode
 
***Name the script <map name>_scavenge.nut.
 
***For example, c1m4_atrium_scavenge.nut.
 
**Mutators
 
***Will automatically run the script with the same name as the game mode.
 
***For example, running a map <code>map <map name> realismversus</code> will automatically load realismversus script.
 
*Events
 
**Finale
 
***"Script" KeyValue of trigger_finale does not need to be set up
 
***Automatically loads <map name>_finale.nut script on finale trigger
 
***Known to work in this fashion in Custom and Scavenge finale type (set up in trigger_finale)
 
 
 
==Scripting samples==
 
{{Warning|}}NEVER use the '''=''' operator when trying to influence the director. Use <-. The difference is semantics. If you use =, it will throw an error if the table slot isn't defined (like if a previous script didn't define it). <-, on the other hand, will create the variable if it does not exist. Some of these samples are only portions of a full script, so make sure that the variables are created beforehand.
 
===Targeting entities with a director script===
 
Add something like this in the script for your minifinale when your horde wave gets triggered:
 
<pre>{
 
EntFire( "church_bell_relay", "Trigger", 0 )
 
}</pre>
 
Then place a logic_relay with that name to send an output to your ambient_generic.
 
 
 
===Triggering a panic event though a script===
 
If you want to start a panic event through a script (not an onslaught) then use this:
 
<pre>
 
//-----------------------------------------------------
 
local PANIC = 0
 
//-----------------------------------------------------
 
 
 
DirectorOptions <-
 
 
{
 
{
A_CustomFinale1 = PANIC
+
DirectorOptions // Base DirectorOptions table.
A_CustomFinaleValue1 = 2
+
MapScript = // Map Script scope.
}</pre>
+
{
The number refers to the number of waves that you want in your minifinale. This is what was used at the ferry in the first map of Swamp Fever. This script needs to be executed via scriptedpanicevent input to the director. Should also be initialized on a battlefield or finale nav mesh.
+
BaseScriptedDOTable // Hardcoded DirectorOptions.
 
+
ChallengeScript = // Mode Script scope.
===Handling a survivor===
+
{
<pre>/***************************************
+
MutationState // Initial values for SessionState.
Get a handle on a specific survivor
+
MutationOptions // Initial values for SessionOptions.
- This is pretty hackish, I can't just say Entities.FindByName(null, "playername"), (YET) because
+
}
I don't know what it's expecting internally. And I can't "learn" it's name by saying survivior.GetName(),
+
LocalScript = // Script Scope Director Scripts are placed in.
because what that returns is just some a function ref, it's probably the C++ side wrapped in a closure, OPAQUE, bummer.
+
{
***************************************/
+
DirectorOptions // DirectorOptions for current Director Script (like onslaughts). Only availabe when a script is active.
 
+
}
//Get Nick
+
MapOptions // Initial values for SessionOptions.
Nick <- Entities.FindByModel(null,"models/survivors/survivor_gambler.mdl");
+
MapState // Initial values for SessionState.
// Nick is class "player" (which must be related or superset of CBaseEntity)
+
}
Nick.SetHealth(0); // Ouch but he won't incap until hit again, even below zero
 
//or Super Nick!
 
Nick.SetHealth(50000); // Tank gonna get slapped to death.</pre>
 
 
 
{{note|'''"!survivorname"''' can also be used as the entity name. For example, the health of the survivor can be reduced with
 
 
 
<pre>EntFire("!nick", "SetHealth", 1)</pre>
 
 
 
'''!nick''', '''!rochelle''', '''!coach''', '''!ellis''' point to the corresponding player class entities in game. This was discovered by looking at the passing campaign decompiled levels where the designers used '''!zoey''', '''!louis''' and '''!francis''' in the actual level to teleport the corresponding L4D1 npcs around the map.}}
 
 
 
==="Detecting"/Finding Survivors===
 
[http://forums.steampowered.com/forums/showthread.php?t=1374401] Just a sample you can use in your own code to find survivors. You need to call if after a delay of 10 seconds or so with a logic_auto
 
 
 
Something like
 
 
 
logic_auto > onmapspawn "logic_script_name" runscriptcode with a value of "FindSurvivors()"
 
 
 
Then you can iterate (loop) through the survivors like so...
 
<pre>/*
 
findsurvivors.nut
 
author: Lee Pumphret
 
http://www.leeland.net
 
 
 
The survivors table, once initialized, holds an entity reference to all survivors
 
To reference a specific survivor, you can say
 
survivors.nick (etc...)
 
*/
 
 
 
survivors <-{
 
  coach = "models/survivors/survivor_coach.mdl",
 
  ellis = "models/survivors/survivor_mechanic.mdl",
 
  nick = "models/survivors/survivor_gambler.mdl",
 
  rochelle = "models/survivors/survivor_producer.mdl"
 
 
}
 
}
 +
g_MapScript // Global reference to the Map Script scope (DirectorScript.MapScript).
 +
g_ModeScript // Global reference to the Mode Script scope (DirectorScript.MapScript.ChallengeScript).
 +
g_rr // Scope holding the Response Rule system.
 +
g_RoundState // TODO
 +
SessionOptions //Global Director options (Scripted mode only).
 +
SessionState  //State variables for game modes (Scripted mode only).
 +
</source>
  
survivors_found <- 0 // flag set to true once survivors are found
 
 
/*
 
Find survivors, this needs to be called after a delay. If you call it immediately,
 
it will fail as they have not been loaded yet, 10 sec after map load should be good.
 
You can call it with a logic_auto output, runscriptcode FindSurvivors()
 
*/
 
function FindSurvivors(){
 
  foreach(s,m in survivors){
 
    printl ("looking for "+s+" mdl:"+m);
 
    survivor <- Entities.FindByModel(null, m)
 
    if (survivor){
 
      printl(s+" found: "+survivor);
 
      survivors[s] = survivor
 
      survivors_found++
 
    }else{
 
      printl(s+" NOT FOUND!: "+survivor);
 
      survivors[s] = null
 
    }
 
  }
 
}</pre>
 
 
*[http://www.leeland.net/gehaonsusu.html Getting a handle on survivors - findsurvivors.nut] - Author's website
 
  
===Find Survivors Closest to Entity===
+
=== Delegation ===
This builds off multiple scripts. Refer to the [http://www.leeland.net/survivorclosest-nut.html author's website] for a explanation and complete code. [http://forums.steampowered.com/forums/showthread.php?t=1401390]
+
Some of the tables have [http://squirrel-lang.org/doc/squirrel3.html#d0e2038 delegation] set up, so that if a key isn't present in it, it is read from its parent table instead.
  
<pre>IncludeScript("survivorclosest.nut", this);
+
The tables are delegated like this (parent tables on the right):
 +
; The Director Scope
 +
: <code>(DirectorScript.MapScript.ChallengeScript; DirectorScript.MapScript.LocalScript) < DirectorScript.MapScript < DirectorScript < ::</code>
 +
;DirectorOptions
 +
:<code>DirectorScript.MapScript.LocalScript.DirectorOptions (When Director Script active) < DirectorScript.MapScript.ChallengeScript.DirectorOptions (Scripted mode only) < DirectorScript.DirectorOptions</code>
  
ent <- Entities.FindByName(null, "entname");
+
== Entity Scripts ==
 +
Use script scope <code>_<unique ID>_<entity name></code>
  
printl("Found entity "+ent);
+
Adding a script to the '''Entity Scripts''' KeyValue of a server-side entity loads the script as an Entity Script. The script is executed when the entity spawns, and loads into a unique script scope made up of an unique identifier followed by the entity name or class name.
  
 +
A think function can be set with the <code>thinkfunction</code> [[Keyvalue|KeyValue]], the specified script function every 0.1 seconds. While it has the potential to become expensive, a programmer is able to limit the amount of code executed. Functions can also be manually called through the I/O system with the input <code>RunScriptCode ''function_name(argument, ...)</code>.
  
FindSurvivors()  // make sure this script isn't called right away or they won't be found yet
+
Entity Scripts have a <code>self</code> reference to their owning entity handle, allowing the script easy access to control the entity through its class methods. There are also hook functions available depending on the entity class. Both methods and hooks are documented [[List_of_L4D2_Script_Functions#Classes|here]].
  
who <- FindSurvivorClosestToEnt(ent); // returns entity reference to survivor
+
Some entities have additional script functionality:
if (who){
+
*[[point_script_use_target]] - Has both methods and hooks for controlling the button.
  printl(who);
+
*[[logic_script]] - Can pass multiple entity names to the script as an array called <code>EntityGroup</code>.
  printl(who+" is closest to "+ent.GetName()+" at "+who.GetOrigin());
+
*[[point_template]]
  // do something here
+
*[[env_entity_maker]]
}else{
+
*[[info_item_position]]
  printl("Nothing found");
 
}
 
  
  
/* or, if you want the name returned (the key into the
+
The script can be reloaded with console command <code>ent_fire <name of entity> runscriptfile ''<relative vscript path>''</code>. This is useful for quick script reloading.
survivors table), add a true value as a second param */
 
  
who <- FindSurvivorClosestToEnt(ent, 1); // returns entity reference to survivor
 
if (who){
 
  printl(who);
 
  printl(who+" is closest to "+ent.GetName()+" at "+survivors[who].GetOrigin());
 
  // do something here
 
}else{
 
  printl("Nothing found");
 
}</pre>
 
  
==="Hovering Chopper Support" Example===
+
=== [[Inputs_and_Outputs|I/O system]] interaction ===
[http://www.leeland.net/hochsuch.html] If you want to parent a func_tracktrain to something, so it moves relative, you don't parent the path, you parent the func_tracktrain itsef to the item, and the path will move relative to this. So here's what this does.
+
Any script can use the <code>EntFire()</code> and <code>DoEntFire()</code> functions to fire outputs to map entities. Since the ''<code>activator</code>'' and ''<code>caller</code>'' arguments in <code>DoEntFire()</code> take a script handle, it can be used to fire an output to an entity using the "!self" or "!activator" keyword, even without having to know its name, as long as the entity handle is available.
*You make your track as normal, your "train" heli, whatever.
 
*Place an info_target in the center of your path with a name ex: chopper_follow
 
*Then you parent that func_tracktrain to the target (chopper_follow).
 
*Pass that target as the first entitygroup to this script.
 
*Then what happens is that the script computes the average position of the survivors ever 1/10 of a second, moves the info_target there (chopper_follow), and the path_track will follow.
 
*Have your track the height you want, relative to the info_target.
 
{{note|There are a couple caveats, you'll need to call FindSurvivors() after a delay of map load, or it won't work. They won't be loaded yet, a logic_auto output onmapspawn with a delay of 10 seconds should do it.}}
 
*[http://www.leeland.net/hochsuch.html Hovering chopper support - choppercover.nut] - Author's Website. Contains full code, video of the example, map source to an example, and description.
 
*[http://www.youtube.com/watch?v=orm1SJwaijE l4d2 vscript tracktrain hovertest] - Video of prototype
 
  
===Changing The Skybox On Map Load===
+
<source lang=cpp>
[http://www.leeland.net/chskonmaplow.html] Save the file to your scripts/vscripts folder as whichskybox.nut
+
EntFire( "church_bell_relay", "Trigger", 0 ); // Fires an output to the Trigger input of the named entity.
  
Add a logic_script entity to your map, turn off smartedit and add a "vscripts" key with a value of whichskybox.nut
+
player <- null;
 
+
while(player = Entities.FindByClassname(player, "player"))  // Iterate through the script handles of the players.
Obviously you would have to adjust fog and lighting entities as well for it to not look terrible.
 
 
 
<pre>Skyboxes <- [
 
  "sky_l4d_c1_1_hdr",
 
  "sky_l4d_c1_2_hdr",
 
  "sky_l4d_c2m1_hdr",
 
  "sky_l4d_night02_hdr",
 
  "sky_l4d_predawn02_hdr",
 
  "sky_l4d_c4m1_hdr",
 
  "sky_l4d_c4m4_hdr",
 
  "sky_l4d_c5_1_hdr",
 
  "sky_l4d_c6m1_hdr"
 
]
 
 
 
worldspawn <- Entities.FindByClassname (null, "worldspawn");
 
local i = RandomInt(0,Skyboxes.len()-1);
 
printl("Skybox is "+Skyboxes[i]);
 
printl( worldspawn.__KeyValueFromString("skyname",Skyboxes[i]) );</pre>
 
 
 
===Changing Witch Movement Type On Map Load===
 
This was based off of the code found here: [http://www.leeland.net/chskonmaplow.html] Save the two files to your scripts/vscripts folder as daywitch.nut and nightwitch.nut
 
 
 
Add a logic_script entity to your map, turn off smartedit and add a "vscripts" key with a value of daywitch.nut, then repeat for the other with a value of nightwitch.nut
 
 
 
There are two scripts involved here, one for day witches and one for night. These scripts run on map load and select the numerical equivialant of Midnight (0) or Morning (2) found in worldspawn. I seperated the scripts so entity logic can tie into them. More on that below.
 
 
 
daywitch.nut:
 
 
 
<pre>Skyboxes <- [
 
  "2"
 
]
 
 
 
worldspawn <- Entities.FindByClassname (null, "worldspawn");
 
local i = RandomInt(0,Skyboxes.len()-1);
 
printl("Skybox is "+Skyboxes[i]);
 
printl( worldspawn.__KeyValueFromString("timeofday",Skyboxes[i]) );;</pre>
 
 
 
nightwitch.nut:
 
 
 
<pre>Skyboxes <- [
 
  "0"
 
]
 
 
 
worldspawn <- Entities.FindByClassname (null, "worldspawn");
 
local i = RandomInt(0,Skyboxes.len()-1);
 
printl("Skybox is "+Skyboxes[i]);
 
printl( worldspawn.__KeyValueFromString("timeofday",Skyboxes[i]) );</pre>
 
 
 
This is the line of entity logic I used in my test map to achieve random day or night witches. Note I start the logic_auto with a delay of 10 seconds so the AI can get ready. It didn't seem to work if I didn't, as noted in the "Hovering Chopper Support Example" above.
 
 
 
<pre>Class: logic_auto
 
My output named: OnMapSpawn
 
Targets entities named: daynightwitchcase
 
Via this input: PickRandomShuffle
 
Delay: 10.00
 
 
 
Class: logic_case
 
My output named: OnCase01
 
Targets entities named: daywitchtemplate
 
Via this input: ForceSpawn
 
Delay: 0.00
 
 
 
My output named: OnCase02
 
Targets entities named: nightwitchtemplate
 
Via this input: ForceSpawn
 
Delay: 0.00
 
 
 
Class: point_template
 
Name: daywitchscripttemplate
 
Template 1: daywitchscript
 
 
 
Class: point_template
 
Name: nightwitchscripttemplate
 
Template 1: nightwitchscript
 
 
 
Class: logic_script
 
Name: daywitchscript
 
vscripts: daywitch.nut
 
 
 
Class: logic_script
 
Name: nightwitchscript
 
vscripts: nightwitch.nut</pre>
 
 
 
Compile your map with this logic in place, and both named scripts in the scripts/vscripts folder in your main game directory and you should see witches respond and behave differently, randomly.
 
 
 
The advantages of tying it into map logic and thus using two seperate script files are many. By randomly changing the skybox in the same way, substituting script files, you could theoretically have a map that for all intents and purposes could switch from day to night or vice versa on map load. Of course, you'd need to adjust fog and lighting entities as well for it to look good, but this is only one step of many in converting a map.
 
 
 
===Some General DirectorOptions===
 
You can start a script with an input to the director "BeginScript" and then the name of the script in the parameters field. Place the script as a ".nut" file in your vscripts directory. To end the script, send an input to the director "EndScript".
 
 
 
Here are some examples:
 
<pre>DirectorOptions <-
 
{
 
ProhibitBosses = 1 (default is 0)
 
AlwaysAllowWanderers = 1 (default is 0)
 
MobMinSize = 10 (default)
 
MobMaxSize = 30 (default)
 
SustainPeakMinTime = 3 (default)
 
SustainPeakMaxTime = 5 (default)
 
IntensityRelaxThreshold = 0.9 (default)
 
RelaxMinInterval = 30 (default)
 
RelaxMaxInterval = 45 (default)
 
RelaxMaxFlowTravel = 3000 (default)
 
SpecialRespawnInterval = 45.0 (default)
 
NumReservedWanderers = 10 (default is 0)
 
}</pre>
 
 
 
You can play around with some of these numbers for specific events in your levels. For example, some of our scripts reduce the SpecialRespawnInterval to 30 seconds or we have some that reduce the RelaxMaxFlowTravel to 1000 so that the director won't stay relaxed if the survivors have continued far enough.
 
 
 
===Prohibiting Tank and Witch spawns===
 
Left4Dead 2 no longer relies on the mission.txt file to prohibit Tanks and Witches on maps. This is now done with a script file that you should place in your left4dead2/scripts/vscripts folder (you may need to add the vscripts subfolder yourself). Add the following to a text file:
 
 
 
<pre>DirectorOptions <-
 
{
 
   ProhibitBosses = true
 
}</pre>
 
 
 
Save the textfile with the extention '''.nut''' in the vscripts folder. In your map, place a '''logic_auto''' entity and add an output. The output should target the AI Director entity and make it fire the script by using the BeginScript action. In the parameter field, you set the name of your script (without the '''.nut''' extention)
 
 
 
===Some Scavenge Gamemode DirectorOptions (for Coop/Versus Finales with Scavenge)===
 
For a finale that is using scavenge as the gameplay (such as in the Mall Atrium finale), you'll need a separate scavenge script.  Here's c1m4's (name the script "[the name of the map]_scavenge.nut"):
 
<pre>DirectorOptions <-
 
{
 
PreferredMobDirection = SPAWN_LARGE_VOLUME
 
PreferredSpecialDirection = SPAWN_LARGE_VOLUME
 
ShouldConstrainLargeVolumeSpawn = false
 
MobSpawnMinTime = 45
 
MobSpawnMaxTime = 90
 
CommonLimit = 15
 
ZombieSpawnRange = 3000
 
}
 
NavMesh.UnblockRescueVehicleNav()
 
Director.ResetMobTimer()</pre>
 
 
 
===Dark Carnival Onslaught Script===
 
Here's the onslaught script Valve used for the 4th map of Dark Carnival:
 
 
 
<pre>Msg("Initiating Onslaught\n");
 
 
 
DirectorOptions <-
 
{
 
// This turns off tanks and witches.
 
ProhibitBosses = false
 
 
 
//LockTempo = true
 
MobSpawnMinTime = 3
 
MobSpawnMaxTime = 7
 
MobMinSize = 30
 
MobMaxSize = 30
 
MobMaxPending = 30
 
SustainPeakMinTime = 5
 
SustainPeakMaxTime = 10
 
IntensityRelaxThreshold = 0.99
 
RelaxMinInterval = 1
 
RelaxMaxInterval = 5
 
RelaxMaxFlowTravel = 50
 
SpecialRespawnInterval = 1.0
 
PreferredMobDirection = SPAWN_IN_FRONT_OF_SURVIVORS
 
ZombieSpawnRange = 2000
 
}
 
 
 
Director.ResetMobTimer()</pre>
 
 
 
===Maximum number of simultaneous specials===
 
The following script not only limits the number of specials (using <code>MaxSpecials</code>), but also limits the number of specific specials allowed simultaneously and increases the maximum number of common infected:
 
{{note|Developers should keep in mind that in-game and '''network''' performance may suffer as more infected are introduced to the player at the same time.}}
 
<pre>Msg("Preparing to own the Survivors");
 
local Dopts = DirectorScript.DirectorOptions; // get a reference to the options
 
Dopts.MaxSpecials <- 20;
 
Dopts.BoomerLimit <- 5;
 
Dopts.SmokerLimit <- 5;
 
Dopts.HunterLimit <- 5;
 
Dopts.ChargerLimit <- 5;
 
Dopts.SpitterLimit <- 5;
 
Dopts.JockeyLimit <- 5;
 
Dopts.CommonLimit <- 120;
 
Dopts.SpecialRespawnInterval <- 1.0;</pre>
 
 
 
===Dead Center Chapter 3 Special Infected Limit===
 
For [http://forums.steampowered.com/forums/showthread.php?t=1155050 multiple SI spawns] like in the 3rd chapter of Dead Center:
 
<pre>
 
BoomerLimit = 0
 
SmokerLimit = 3
 
HunterLimit = 1
 
ChargerLimit = 2
 
</pre>
 
 
 
You can also limit Spitters and Jockeys with
 
<pre>
 
SpitterLimit = 0
 
JockeyLimit = 0
 
</pre>
 
 
 
===c1m4_atrium.nut (or <map_name>.nut)===
 
c1m4_atrium_finale (or <map name>_finale) also relies on this script in certain cases.
 
{{note|The description of the finale script was derived from the given c1m4_atrium_finale script comment: "// this is too late. Moved to c1m4_atrium.nut"}}
 
 
 
<pre>
 
Msg(" atrium map script "+"\n")
 
 
// number of cans needed to escape.
 
 
if ( Director.IsSinglePlayerGame() )
 
 
{
 
{
                NumCansNeeded <- 8
+
    DoEntFire("!self", "speakresponseconcept", "PlayerLaugh", 0, null, player); // Make each player laugh.
}
 
else
 
{
 
                NumCansNeeded <- 13
 
}
 
 
 
DirectorOptions <-
 
{
 
             
 
CommonLimit = 15
 
 
}
 
 
NavMesh.UnblockRescueVehicleNav()
 
 
EntFire( "progress_display", "SetTotalItems", NumCansNeeded )
 
 
 
function GasCanPoured(){}
 
</pre>
 
 
 
===Tic-Tac-Toe mini-game===
 
This example uses and vscripts to manipulate entities "registered" to a [[logic_script]] entity.[http://forums.steampowered.com/forums/showthread.php?t=1242468]
 
*[http://www.leeland.net/l4d2-scripts-play-tic-tac-toe.html Writing a Mini Game - Tic Tac Toe - Part One] - Author's Website
 
*[http://www.youtube.com/watch?v=B9hASsoHK54 l4d2 - Vscript example - Tic-Tac-Toe] - Video of early Prototype
 
*[http://www.youtube.com/watch?v=mvDFEoA0ib0 l4d2 - Vscript example - Tic-Tac-Toe - updated] - Video of current version with "brutally misanthropic AI"
 
 
 
===Pushing a Player Around===
 
[http://www.leeland.net/puplarpu.html][http://forums.steampowered.com/forums/showthread.php?t=1399520]
 
<pre>/*--------------------------------------------
 
author: http://leeland.net
 
file:pushplayer.nut
 
description:
 
Allows you to add a velocity vector to any player via trigger.
 
Call it from a trigger with something like
 
OnStartTouch !activator runscriptcode OurPushPlayer(0,0,400)
 
 
 
A note on values, 4096 is the maximum velocity, values below 250 don't move the player at all.
 
Z Values around 700 will incap, 1000 or so will prove fatal
 
 
 
 
 
--------------------------------------------*/
 
 
 
function OurPushPlayer(x,y,z) {
 
  local addv = Vector(x,y,z); // 4096 max velocity, anything higher is clamped
 
  local v = self.GetVelocity()
 
  self.SetVelocity(v+addv);
 
 
}
 
}
 +
</source>
  
  
// slip in a reference to our function in the global table so we can access from all objects,
+
Conversely, the <code>[[List_of_L4D2_Script_Functions#CBaseEntity|CBaseEntity]]::ConnectOutput()</code> and <code>DisconnectOutput()</code> functions can be used to call a script function when the specified entity output fires.
// *technique to use sparingly! Your function could overwrite another. Name uniquely to try and avoid conflicts*
+
In addition, arbitrary VScript code can be run from the I/O system, using the <code>RunScriptCode</code> input available in all entities. The code will run in the current entities script scope.
::OurPushPlayer <- OurPushPlayer;</pre>
+
{{warning|Never use double-quotation marks in any Hammer Output, since it will corrupt the map file. This means that strings cannot be passed with <code>RunScriptCode</code>.}}
{{note|Negative Z values do not work. A player cannot be pounded into the ground.}}
 
  
==Director options==
 
This is by no means an exaustive list of all the director options. Additions and updates can be found in the [http://forums.steampowered.com/forums/showthread.php?p=16557881 Steam Forums]
 
  
*'''function OnBeginCustomFinaleStage()'''
+
== Global Scripts ==
:If defined, will be called on every stage change with the number, and type, this is how you would change director options between stages (spawn directions, etc)
+
Naming a script file with a game mode name a mode specific script, and makes it execute every time a map is loaded in the specified mode. If a Mode Script exists, a map specific script with the map name followed by an underscore and the mode name can also be loaded.
Code:
 
<pre>function OnBeginCustomFinaleStage( num, type )
 
{
 
      printl( "Beginning custom finale stage " + num + " of type " + type );
 
      // set options as wanted for the stage
 
}</pre>
 
  
*'''function Update()'''
+
{{todo|Add information about the features and utility functions that get loaded into these.}}
:If you define an Update() function in your vscript, it will run repeatedly much like a Think() function, but ONLY after a trigger_finale is triggered, if you have multiple scripts that are running that define it, they all will be called.  
 
  
*'''Director.L4D1SurvivorGiveItem()'''
+
=== Scripted Mode ===
:This makes l4d1 survivors give an item
+
Adding a Mode Script will enable Scripted Mode for the game mode. This works on the base game modes included in the game as well.
  
*'''AlwaysAllowWanderers''' = true|false
+
Scripted Mode loads utility functions into the <code>g_ModeScript</code> scope, disables the hardcoded setting of 2 on the <code>MaxSpecials</code> Director Option, and enables [[L4D2_EMS/Appendix:_Game_Events|game event callbacks]] and [[List_of_L4D2_Script_Functions#Scripted_Mode_hooks|script hook functions]]. It also seems to break the finale tank music in Dark Carnival.
*'''BileMobSize''' = NUM ? Doesn't seem to do anything
 
*'''BoomerLimit''' = maximum number of boomers allowed
 
*'''ChargerLimit''' =maximum number of chargers allowed
 
*'''CommonLimit''' = maximum number of commons allowed
 
*'''DisallowThreatType''' = ZOMBIE_WITCH, ZOMBIE_TANK (other values???)
 
*'''HordeEscapeCommonLimit''' = number of commons allowed when the escape vehicle has arrived
 
*'''HunterLimit''' = maximum number of hunters allowed
 
*'''IntensityRelaxThreshold''' = All survivors must be below this intensity before a Peak is allowed to switch to Relax (in addition to the normal peak timer)
 
*'''JockeyLimit''' = maximum number of jockeys allowed
 
*'''LockTempo''' = 0 ???
 
*'''MaxSpecials''' = number of specials allowed at once
 
*'''MegaMobMaxSize''' = maximum megamob size
 
*'''MegaMobMinSize''' = minimum megamob size
 
*'''MobMaxPending''' = not sure
 
*'''MobMaxSize''' = max mob size
 
*'''MobMinSize''' = min mob size
 
*'''MobRechargeRate''' = Guessing it's the speed at which a mob regenerates (ie next mob)
 
*'''MobSpawnMaxTime''' = max time in seconds for mob spawn
 
*'''MobSpawnMinTime''' = min time in seconds? for mob spawn
 
*'''MusicDynamicMobScanStopSize''' = When see less than this many of a mob, music stops
 
*'''MusicDynamicMobSpawnSize''' = ???Spawning a mob this large can play music
 
*'''MusicDynamicMobStopSize''' = When a mob gets to this size we think about stopping the music
 
*'''NumReservedWanderers''' = the number of infected that cannot be absorbed
 
  
*'''PreferredMobDirection''' = SPAWN_ABOVE_SURVIVORS, SPAWN_ANYWHERE, SPAWN_BEHIND_SURVIVORS, SPAWN_FAR_AWAY_FROM_SURVIVORS, SPAWN_IN_FRONT_OF_SURVIVORS , SPAWN_LARGE_VOLUME, SPAWN_NEAR_IT_VICTIM, SPAWN_NO_PREFERENCE
+
{{todo|Does enabling Scripted Mode have any other consequences?}}
  
{{note|SPAWN_NEAR_IT_VICTIM does not exist before a finale and will cause an error, so I'm assuming the director picks someone as IT when the finale starts. SPAWN_LARGE_VOLUME is what makes you be a mile away on DC finale.}}
+
=== Shared Tables ===
 +
;<code>SessionState</code>
 +
:A table for storing session specific variables. Not preserved across level transitions.
 +
;<code>SessionOptions</code>
 +
:Base [[L4D2_Director_Scripts#DirectorOptions|Director Options]] for the game mode.
  
*'''PreferredSpecialDirection '''
+
=== Available Functions ===
{{note|The same values for PreferredMobDirection appear to work, BUT I've also seen the following, I don't know if it's just redundancy or what.
+
{{todo|Document the available utility features.}}
SPAWN_SPECIALS_ANYWHERE
 
SPAWN_SPECIALS_IN_FRONT_OF_SURVIVORS}}
 
  
*'''ProhibitBosses''' = true|false - prohibit tanks/witches
 
*'''RelaxMaxFlowTravel''' = 600
 
*'''RelaxMaxInterval''' = 5
 
*'''RelaxMinInterval''' = 5
 
*'''ShouldAllowMobsWithTank''' = true|false
 
*'''ShouldAllowSpecialsWithTank''' = true|false
 
*'''ShouldConstrainLargeVolumeSpawn''' = true|false
 
*'''SmokerLimit''' = maximum number of smokers allowed
 
*'''SpecialRespawnInterval''' = time in seconds for special respawns
 
*'''SpitterLimit''' = maximum number of spitters allowed
 
*'''SustainPeakMaxTime''' = in minutes
 
*'''SustainPeakMinTime''' = in minutes
 
*'''TempHealthDecayRate''' = 0.27 // pain_pills_decay_rate default, higher values equals quicker decay
 
*'''ZombieSpawnRange''' = How far away can zombies spawn?
 
*'''ZombieSpawnInFog''' = true|false
 
  
==Finale scripts==
+
=== Mode Scripts ===
Some variables in DirectorOptions can only be used during finales.
+
Use script scope <code>g_ModeScript</code>
  
A normal finale consists of X number of stages
+
==== Tables ====
 +
;<code>MutationState</code>
 +
:Initial values for the <code>SessionState</code> table.
 +
;<code>MutationOptions</code>
 +
:Initial values for the <code>SessionOptions</code> table.
  
A stage can be one of 4 types (other values will break the finale and go right to ESCAPE)
 
  
*PANIC - a panic event, the value is the number of them (ie 2 would be 2 panic events in a row)
+
=== Map Scripts ===
*TANK - spawn a tank(s), the value is the number of tanks to spawn
+
Use script scope <code>g_MapScript</code>
*DELAY - a delay, the value is the number of seconds to wait before proceeding to the next stage
 
*ONSLAUGHT - The value should be the name of a vscript to call or "" (which will do nothing), any bad value here will crash you to the desktop.
 
  
Your onslaught script is responsible for sending an EndCustomScriptedStage to the director (a goal of your choice, like a certain trigger volume, timer, random value, etc.). Otherwise, the '''onslaught will not end.'''
+
==== Tables ====
 +
;<code>MapState</code>
 +
:Map specific values for the <code>SessionState</code> table.
 +
;<code>MapOptions</code>
 +
:Map specific values for the <code>SessionOptions</code> table.
  
You can also specify custom game sounds for a stage by adding a key, for instance <code>A_CustomFinaleMusic1 = "C2M5.BadManTank2"</code>
 
An example custom finale script:
 
<pre>PANIC <- 0
 
TANK <- 1
 
DELAY <- 2
 
ONSLAUGHT <- 3
 
  
DirectorOptions <-
+
== Glossary ==
{
+
;DirectorOptions
//-----------------------------------------------------
+
:A table of named variables that override the AI Director behavior.
CommonLimit = 10
+
;Entity handle
A_CustomFinale_StageCount =
+
:Also known as EHANDLE. {{todo|Looks like some sort of pointer reference to an entity.}}
+
;Script handle
A_CustomFinale1 = PANIC
+
:An entity instance with accessors and mutators to the C++ entity object. Also known as HScript.
A_CustomFinaleValue1 = 2  // two panic events
+
;Script scope
+
:The table where the variables, functions and classes of a VScript are placed.
A_CustomFinale2 = DELAY
 
A_CustomFinaleValue2 = 12  // delay for twelve seconds in addition to stage delay
 
 
A_CustomFinale3 = TANK
 
A_CustomFinaleValue3 = 3  // 3 tanks!
 
 
A_CustomFinale4 = DELAY
 
A_CustomFinaleValue4 = 12 // wait some more
 
 
A_CustomFinale5 = ONSLAUGHT
 
A_CustomFinaleValue5 = "my_onslaught_script.nut" // run our onslaught script
 
 
A_CustomFinale6 = DELAY
 
A_CustomFinaleValue6 = 15 // wait 15 seconds
 
 
A_CustomFinale7 = TANK
 
A_CustomFinaleValue7 = 1  // one more tank
 
  
A_CustomFinale8 = DELAY
 
A_CustomFinaleValue8 = 10 // wait ten seconds ... rescue!
 
 
SpecialRespawnInterval = 25
 
  
//-----------------------------------------------------
+
== Director options ==
}
+
'''Please see [[L4D2 Director Scripts#DirectorOptions|L4D2 Director Scripts]] for a table of available options.'''
  
function OnBeginCustomFinaleStage( num, type )
 
{
 
      printl( "Beginning custom finale stage " + num + " of type " + type );
 
      MapScript.DirectorOptions.CommonLimit = num * 10 // increase commons by 10 linearly with stages
 
}</pre>
 
  
==Overview of Squirrel in L4D2==
+
== Third party tools ==
The following are available in L4D2 by default.[http://forums.steampowered.com/forums/showthread.php?t=1238461][http://www.leeland.net/vscript-faq.html]
 
{{Warning|}}NEVER use the = operator when trying to influence the director. Use <-. The difference is semantics. If you use =, it will throw an error if the table slot isn't defined (like if a previous script didn't define it). <-, on the other hand, will create the variable if it does not exist.
 
===Variables===
 
{{todo|Add Variables}}
 
===Classes===
 
====CDirector====
 
Game Instance: Director
 
*Class Methods
 
**GetAveragedSurvivorSpan() - Get the distance between the lead and trailing survivors, smoothed over time
 
**GetAveragedSurvivorSpeed() - Get the rate at which the lead survivor is moving along the flow, smoothed over time
 
**GetFurthestSurvivorFlow() - Get the maximum distance along the flow that the survivors have reached
 
**GetGameMode() -  Get the current game mode "versus", "coop", etc.
 
**HasAnySurvivorLeftSafeArea()
 
**IsPlayingOnConsole() - Returns true if player is running the client on a console like Xbox 360
 
**IsSinglePlayerGame() - Return true if game is in single player
 
**IsValid()
 
**L4D1SurvivorGiveItem()
 
**PlayMegaMobWarningSounds() - Plays a horde scream sound and asks survivors to speak 'incoming horde' lines
 
**ResetMobTimer() - Trigger a mob as soon as possible when in BUILD_UP (refer to <code>director_debug 1</code>)
 
*Director Enumerations {{note|These are (or some are) '''script specific''', hence the duplicate values.}}
 
**FINALE_CUSTOM_DELAY = 10
 
**FINALE_CUSTOM_PANIC = 7
 
**FINALE_CUSTOM_SCRIPTED = 9
 
**FINALE_CUSTOM_TANK = 8
 
**FINALE_FINAL_BOSS = 5
 
**FINALE_GAUNTLET_1 = 0
 
**FINALE_GAUNTLET_2 = 3
 
**FINALE_GAUNTLET_BOSS = 15
 
**FINALE_GAUNTLET_BOSS_INCOMING = 14
 
**FINALE_GAUNTLET_ESCAPE = 16
 
**FINALE_GAUNTLET_HORDE = 12
 
**FINALE_GAUNTLET_HORDE_BONUSTIME = 13
 
**FINALE_GAUNTLET_START = 11
 
**FINALE_HALFTIME_BOSS = 2
 
**FINALE_HORDE_ATTACK_1 = 1
 
**FINALE_HORDE_ATTACK_2 = 4
 
**FINALE_HORDE_ESCAPE = 6
 
**SPAWN_ABOVE_SURVIVORS = 6
 
**SPAWN_ANYWHERE = 0
 
**SPAWN_BEHIND_SURVIVORS = 1
 
**SPAWN_FAR_AWAY_FROM_SURVIVORS = 5
 
**SPAWN_IN_FRONT_OF_SURVIVORS = 7
 
**SPAWN_LARGE_VOLUME = 9
 
**SPAWN_NEAR_IT_VICTIM = 2
 
**SPAWN_NO_PREFERENCE = -1
 
**SPAWN_SPECIALS_ANYWHERE = 4
 
**SPAWN_SPECIALS_IN_FRONT_OF_SURVIVORS = 3
 
**SPAWN_VERSUS_FINALE_DISTANCE = 8
 
**ZOMBIE_TANK = 8
 
**ZOMBIE_WITCH = 7
 
  
====CEntities====
+
===VSLib===
Game Instance: Entities
+
'''VSLib''' (''VS''cript ''Lib''rary) is a simple, lightweight library for L4D2's VScript mutation system. It greatly simplifies coding VScripts by taking care of a lot of tedious work for you. It is used in several popular mods like [http://steamcommunity.com/sharedfiles/filedetails/?id=214630948 Rayman1103's Admin System] and [http://steamcommunity.com/sharedfiles/filedetails/?id=157525096 Stranded]. Read more about [[VSLib]].
*Class Methods
 
**.FindByClassname (null|prev,classname) - Find entities by class name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search Continue an iteration over the list of entities, providing reference to a previously found entity
 
**.FindByClassnameNearest (classname, vector) - Find entities by class name nearest to a point
 
**.FindByClassnameWithin (null|prev,classname) - Find entities by class name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search
 
**.FindByModel (null|prev, modelname) - Find entities by model name. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search
 
**.FindByName (classname, vector) - Find entities by class name nearest to a point
 
**.FindByNameNearest (name, vector) ??? - Find entities by name nearest to a point
 
**.FindByNameWithin (null|prev, name, radius) ??? - Find entities by name within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search
 
**.FindByTarget (null|prev, targetname) - Find entities by targetname. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search
 
**.FindInSphere (null|prev, vector,radius) ??? - Find entities within a radius. Pass 'null' to start an iteration, or reference to a previously found entity to continue a search
 
**.IsValid() - whether entity is valid
 
**.First() - Begin an iteration over the list of entities
 
**.Next() - Returns the next entity in the Entities list
 
  
====CBaseEntity====
+
== See also ==
Game Instance: N/A
+
*[[L4D2 Vscript Examples]]
*Class Methods
+
*[[List of L4D2 Script Functions]]
**.__KeyValueFromInt(key,int) - Sets a keyvalue from an integer, the keys available for all __KeyValue are from the DEFINE_KEYFIELD in baseentity.cpp (classname,rendermode, renderfx, etc.)
+
*[[L4D2 EMS|Extended Mutation System]]
**.__KeyValueFromString(key,string) - Sets a keyvalue from a string, the keys available for all __KeyValue are from the DEFINE_KEYFIELD in baseentity.cpp (classname,rendermode, renderfx, etc.)
 
**.__KeyValueFromVector(key,vector) - Sets a keyvalue from a vector, the keys available for all __KeyValue are from the DEFINE_KEYFIELD in baseentity.cpp (classname,rendermode, renderfx, etc.)
 
**.ConnectOutput(outputname,functioname) - Adds an I/O connection that will call the named function when the specified output fires
 
**.DisconnectOutput(event) - Removes a connected script function from an I/O event
 
**.FirstMoveChild() - returns entity's first move child if exists
 
**.GetClassname() - returns classname
 
**.GetForwardVector() - returns Vector instance
 
**.GetHealth() - returns entity health
 
**.GetMoveParent() - If in hierarchy, retrieves the entity's parent
 
**.GetName() - returns targetname of entity (if entity is named)
 
**.GetOrigin() - returns origin vector
 
**.GetPreTemplateName - Get the entity name stripped of template unique decoration. ie myitem&0125 returns myitem
 
**.GetRootMoveParent() - If in hierarchy, walks up the hierarchy to find the root parent
 
**.GetScriptId() - Retrieve the unique identifier used to refer to the entity within the scripting system
 
**.GetScriptScope() - Retrieve the script-side data associated with an entity
 
**.GetVelocity() - returns velocity vector
 
**.IsValid() - whether entity is valid
 
**.NextMovePeer - returns next child entity
 
**.SetForwardVector(vector)
 
**.SetHealth(int) - sets entity health
 
**.SetOrigin(vector) - sets entity position in world
 
**.SetVelocity(vector) - sets entity velocity
 
**.ValidateScriptScope - Ensure that an entity's script scope has been created
 
 
 
====CBaseAnimating====
 
'''Extends''' CBaseEntity
 
*Class Methods
 
**.__KeyValueFromInt(key,int) - Sets a keyvalue from an integer, the keys available for all __KeyValue are from the DEFINE_KEYFIELD in baseentity.cpp (classname,rendermode, renderfx, etc)
 
**.__KeyValueFromString(key,string) - Sets a keyvalue from a string, the keys available for all __KeyValue are from the DEFINE_KEYFIELD in baseentity.cpp (classname,rendermode, renderfx, etc)
 
**.__KeyValueFromVector(key,vector) - Sets a keyvalue from a vector, the keys available for all __KeyValue are from the DEFINE_KEYFIELD in baseentity.cpp (classname,rendermode, renderfx, etc)
 
**.ConnectOutput(outputname,functioname) - Adds an I/O connection that will call the named function when the specified output fires
 
**.DisconnectOutput(event) - Removes a connected script function from an I/O event
 
**.FirstMoveChild() - returns entity's first move child if exists
 
**.GetClassname() - returns classname
 
**.GetForwardVector() - returns Vector instance
 
**.GetHealth() - returns entity health
 
**.GetMoveParent() - If in hierarchy, retrieves the entity's parent
 
**.GetName() - returns named (if entity is named)
 
**.GetOrigin() - returns origin vector
 
**.GetPreTemplateName - Get the entity name stripped of template unique decoration. ie myitem&0125 returns myitem
 
**.GetRootMoveParent() - If in hierarchy, walks up the hierarchy to find the root parent
 
**.GetScriptId() - Retrieve the unique identifier used to refer to the entity within the scripting system
 
**.GetScriptScope() - Retrieve the script-side data associated with an entity
 
**.GetVelocity() - returns velocity vector
 
**.IsValid() - whether entity is valid
 
**.NextMovePeer - returns next child entity
 
**.SetForwardVector(vector)
 
**.SetHealth(int) - sets entity health
 
**.SetOrigin(vector) - sets entity position in world
 
**.SetVelocity(vector) - sets entity velocity
 
**.ValidateScriptScope - Ensure that an entity's script scope has been created
 
 
 
====CNavMesh====
 
Game Instance: NavMesh
 
*Class Methods
 
**.IsValid
 
**.UnblockRescueVehicleNav() - Removes the block for rescue vehicle. Normally is blocked before the finale is finished, but is necessary for bots to access like in the Atrium finale, c1m4_atrium.bsp.
 
 
 
====CCallChainer====
 
Game Instance: N/A
 
*Class Methods
 
**.chains
 
**.constructor
 
**.PostScriptExecute
 
**.prefix
 
**.scope
 
====CSimpleCallChainer====
 
*Class Methods
 
**.Call
 
**.chain
 
**.constructor
 
**.exactMatch
 
**.PostScriptExecute
 
**.prefix
 
**.scope
 
====regxp====
 
{{todo|}}
 
Game Instance:
 
*Class Methods
 
====Vector====
 
Game Instance: None
 
*Class Methods
 
**.constructor
 
**.Cross
 
**.Dot
 
**.Length
 
**.Length2D
 
**.Length2DSqr
 
**.LengthSqr
 
**.Norm
 
**.ToKVString
 
===Functions===
 
*Assert(value, "optional message") - test value and if not true, throws exception, optionally with messsage
 
*ConnectOutputs(table) - sets output functions for entity by table TODO
 
*DebugDrawBox
 
*DebugDrawLine
 
*Developer() - returns 1 or TRUE when in developer mode
 
*DoEntFire( target.tostring(), action, value, delay, activator, caller ) - Fire an event
 
*EntFire("ent_fire target event") - wrapper for DoEntFire() that sets activator and caller to null
 
*DoIncludeScript
 
*IncludeScript - Wrapper for DoIncludeScript
 
*GetFunctionSignature
 
*Msg("message here") - prints message to console
 
*printl("message") - prints message with carriage return
 
*RandomFloat() - Returns a random float
 
*RandomInt() - Returns a random int between 0 and 32767
 
*RetrieveNativeSignature
 
*SendToConsole("string")- send a string to the console as a command
 
*ShowMessage("message") - Print a hud message on all clients
 
*Time() - Get the current server time
 
*DoUniqueString - Called by UniqueString, prob want to call that instead
 
*UniqueString() - Generate a string guaranteed to be unique across the life of the script VM, with an optional root string. Useful for adding data to tables when not sure what keys are already in use in that table.
 
 
 
{{sq}}Standard Squirrel library functions (consult official Squirrel language league for more details):
 
abs,
 
acos,
 
array,
 
asin ,
 
assert,
 
atan ,
 
atan2,
 
ceil,
 
collectgarbage,
 
compilestring,
 
cos,
 
exp ,
 
fabs ,
 
floor,
 
format,
 
getconsttable,
 
getroottable,
 
getstackinfos,
 
log,
 
log10,
 
lstrip,
 
pow,
 
print,
 
rand,
 
rstrip,
 
setconsttable,
 
seterrorhandler,
 
setroottable,
 
type,
 
sin,
 
split,
 
sqrt,
 
srand,
 
strip,
 
suspend,
 
tan
 
 
 
===Constants===
 
{{todo|Add Constants}}
 
 
 
==See also==
 
 
*[[L4D2_Level_Design/Boss_Prohibition|L4D2 Level Design/Boss Prohibition]]
 
*[[L4D2_Level_Design/Boss_Prohibition|L4D2 Level Design/Boss Prohibition]]
 
*[[Left_4_Dead_2_Tool_Updates|Left 4 Dead 2 Tool Updates]]
 
*[[Left_4_Dead_2_Tool_Updates|Left 4 Dead 2 Tool Updates]]
 
*[[Mutation Gametype (L4D2)]]
 
*[[Mutation Gametype (L4D2)]]
 +
*[[L4D2_Decrypted_mutations|Decrypted Mutation Vscripts]]
 
*[[Trigger_finale|trigger_finale]]
 
*[[Trigger_finale|trigger_finale]]
 
*[[Info_director|info_director]]
 
*[[Info_director|info_director]]
 
*[[Logic_script|logic_script]]
 
*[[Logic_script|logic_script]]
 +
*[[VScript|vscripts]]
 +
*[[VSLib]]
  
==External links==
+
== External links ==
 +
;Alternative Tool
 +
* [http://www.mediafire.com/?q4dqbs554p494vk (mediafire)L4D2 VScript Editor Beta 0.5 by Cynick:support nut (de)compile for l4d2]
 +
;Alternative Documentation
 
* [http://forums.steampowered.com/forums/showthread.php?t=1128303 Director Scripts - .nuc files (Steam forums)]
 
* [http://forums.steampowered.com/forums/showthread.php?t=1128303 Director Scripts - .nuc files (Steam forums)]
 
* [http://forums.steampowered.com/forums/showthread.php?t=1238461 It's the vscript'ing documentation FAQ! (Steam forums)]
 
* [http://forums.steampowered.com/forums/showthread.php?t=1238461 It's the vscript'ing documentation FAQ! (Steam forums)]
 
* [http://forums.steampowered.com/forums/showthread.php?t=1242468 Tutorial - Writing a Mini Game - Tic Tac Toe - Part One (Steam Forums)]
 
* [http://forums.steampowered.com/forums/showthread.php?t=1242468 Tutorial - Writing a Mini Game - Tic Tac Toe - Part One (Steam Forums)]
**[http://www.leeland.net/l4d2-scripts-play-tic-tac-toe.html Writing a Mini Game - Tic Tac Toe - Part One] - Author's Website
+
**[http://leeland.stores.yahoo.net/l4d2-scripts-play-tic-tac-toe.html Writing a Mini Game - Tic Tac Toe - Part One] - Author's Website
 
**[http://www.youtube.com/watch?v=B9hASsoHK54 l4d2 - Vscript example - Tic-Tac-Toe] - Video of early Prototype
 
**[http://www.youtube.com/watch?v=B9hASsoHK54 l4d2 - Vscript example - Tic-Tac-Toe] - Video of early Prototype
 
**[http://www.youtube.com/watch?v=mvDFEoA0ib0 l4d2 - Vscript example - Tic-Tac-Toe - updated] - Video of current version with "brutally misanthropic AI"
 
**[http://www.youtube.com/watch?v=mvDFEoA0ib0 l4d2 - Vscript example - Tic-Tac-Toe - updated] - Video of current version with "brutally misanthropic AI"
Line 1,114: Line 278:
 
* {{sq}}[http://squirrel-lang.org/ Squirrel: The Programming Language - Documentation and Sample Code]
 
* {{sq}}[http://squirrel-lang.org/ Squirrel: The Programming Language - Documentation and Sample Code]
 
* [http://www.valvesoftware.com/publications/2009/ai_systems_of_l4d_mike_booth.pdf The AI Systems of Left 4 Dead by Michael Booth (PDF)]
 
* [http://www.valvesoftware.com/publications/2009/ai_systems_of_l4d_mike_booth.pdf The AI Systems of Left 4 Dead by Michael Booth (PDF)]
 +
* {{as}}[http://swarmarmory.com/forums/viewtopic.php?f=19&t=541 "Creating a "Money"/Point System"] - Swarm Armory
 +
;Deciphered Official Scripts:
 +
*[http://www.mediafire.com/?k280fhwjsp21sn8 Version 2.1.3.6 and all mutations]
 +
*[http://ata4.info/downloads/left4dead2/scriptsrc.zip Version 2.0.8.4 (August 16, 2011)]
 
<div style="text-align: right; direction: ltr; margin-left: 1em;">{{sq}}+{{l4d2}} = [[image:pnkhrt-16px.png]]</div>
 
<div style="text-align: right; direction: ltr; margin-left: 1em;">{{sq}}+{{l4d2}} = [[image:pnkhrt-16px.png]]</div>
 +
 
[[Category:Left_4_Dead_2]]
 
[[Category:Left_4_Dead_2]]
 +
[[Category:Scripting]]

Latest revision as of 10:32, 26 February 2021

简体中文 简体中文

<Squirrel Language><Left 4 Dead 2> Left 4 Dead 2 VScripts are server-side scripts that are run in an in-game virtual machine. They are written in Squirrel, a compiled scripting language similar to Lua. It currently runs Squirrel version 3.0.4.


Uses

Director Scripts

The most common use of VScripts in Left 4 Dead 2 is to influence the behavior of the AI Director. These scripts can range from simple adjustments of infected spawning and prohibiting boss infected, to custom events like onslaughts and gauntlets, and even complex staged panic events and fully custom finales. Most of the events in the official campaigns are mainly implemented this way.

Director Scripts work mainly by writing overriding values of the various variables used by the Director into a DirectorOptions table.

Only one Director Script can be running at a time. Executing a new one will terminate any previous running one and remove any values it set in DirectorOptions.

Entity Scripts

Another common use is to attach a script to an entity. The script provides easy access to read and modify many of the entity's properties, and even to write new KeyValues. This allows for controlling entities in ways that would be very complicated or impossible to with the entity I/O system in Hammer.

Any entity is capable of running a script, and has the ability to set a specified think function to run every 0.1 seconds, as well as executing script code as an entity output.

Some entities also have specialized functions for VScripts, with the most prominent being point_script_use_target, which allows for turning other entities into fully programmable timed buttons.

Global Scripts

Scripts can also be made to run on map load, based on game mode and optionally map name. These scripts are commonly used to create scripting and modify global Director options for mutations, but can also be used in custom maps.

Global scripts can have script hook functions added that get called from the game at certain events, like when players/objects take damage.

There are many utility functions and features readily available for these scripts, including a resource and building system, and custom panic wave spawning. Please see Valve's Expanded Mutation System tutorial for more information.

Other uses

All scripts can access functions for many features, including spawning entities either from precompiled lists or programmatically, spawning infected, a HUD system, storing data across levels and to disk, and much more.

Please see L4D2 Vscript Examples for more examples and ideas.


Script files

The scripts are loaded from text files with the file extensions .nut and .nuc, where .nuc denotes encryption of plain text .nut. Custom scripts are read from \left 4 dead 2\left4dead2\scripts\vscripts\, as well as \scripts\vscripts\ when packed into a .vpk file.

Location

Official .nuc script files are located in scripts/vscripts in multiple locations
Note.png Note: Browse and extract VPK files with third-party programs like GCFScape.
  • left 4 dead 2\left4dead2\pak01_dir.vpk
  • left 4 dead 2\left4dead2_dlc1\pak01_dir.vpk
April 22, 2010 The Passing update
  • left 4 dead 2\left4dead2_dlc2\pak01_dir.vpk
October 5, 2010 The Sacrifice update
  • left 4 dead 2\left4dead2_dlc3\pak01_dir.vpk
March 22, 2011 Cold Stream Beta / L4D1 Transition Project update
  • left 4 dead 2\update\pak01_dir.vpk
This is where mutations were updated/replaced bi-weekly.
  • left 4 dead 2\sdk_content\scripting\scripts\vscripts\
Plaintext versions of many of the scripts introduced in the EMS update.


Decrypting NUC files

.nuc files are ICE encrypted .nut files. The encryption key is SDhfi878. You can use VICE to decode them.
You may find decrypted mutation vscripts here

Loading vscripts

VScripts are loaded in different ways depending on what they are used for. They can also be manually loaded with the console command script filename

Director Scripts

Using the info_director entity using the following inputs
BeginScript <script name>
Executes a generic Director script, for example for onslaught events or changing spawning behavior.
EndScript
Ends the running script and resets the Director options to the map specific values.
BeginScriptedPanicEvent <script name>
Begins a Scripted Panic event.
Note.png Note: Scripts used with BeginScriptedPanicEvent will not work if they are in a subdirectory, even though you can use subdirectories in other script contexts. They must reside under the vscripts folder, or they will simply act as a 1 stage 1 second delay.
Finale scripts
The <map name>_finale.nut script is atumatically loaded when a finale map starts. The script is triggered when the trigger_finale is used, either manually by the player, or with the ForceFinaleStart input.
To do: trigger_finale also has an option to specify a finale script, but it doesn't seem to work


All Director Scripts are placed in the script scope DirectorScript

Entity Scripts

Automatic loading at map start
Uses the script set in the Entity Scripts KeyValue of the entity.
Manual loading
Using the RunScriptFile input.

Entity Scripts are placed in the script scope _<unique ID>_<entity name>

Global Scripts

Mode Specific Scripts
Will automatically run the script with the same name as the game mode. For example, running a map map <map name> mutation12 will automatically load the Realism Versus script, mutation12.nuc.
They are placed in the script scope g_ModeScript
Note.png Note: Adding a script for a mode will enable Scripted Mode on it.
Map Specific Scripts (only available in Scripted Mode)
Per-map scripts can be created that run when the map is loaded in the specified game mode, using the syntax <map name>_<mode name>.nut, for example, c1m4_atrium_mutation12.nut.
They are placed in the script scope g_MapScript


Scripting environment

Please see List of L4D2 Script Functions for built in classes and functions.

When a script is loaded, it is placed into a table, or Script Scope. Mode, map and Director Scripts are put into set scopes, while a unique scope is generated for each Entity Script. Please see Vscript Fundamentals for more information.

Table structure

DirectorScript = 			// Base Director Scope.
{
	DirectorOptions 		// Base DirectorOptions table.
	MapScript =  			// Map Script scope.
	{
		BaseScriptedDOTable 	// Hardcoded DirectorOptions.
		ChallengeScript = 	// Mode Script scope.
		{
			MutationState 	// Initial values for SessionState.
			MutationOptions // Initial values for SessionOptions.
		}
		LocalScript =		// Script Scope Director Scripts are placed in.
		{
			DirectorOptions // DirectorOptions for current Director Script (like onslaughts). Only availabe when a script is active.
		}
		MapOptions 		// Initial values for SessionOptions.
		MapState 		// Initial values for SessionState.
	}
}
g_MapScript 	// Global reference to the Map Script scope (DirectorScript.MapScript).
g_ModeScript 	// Global reference to the Mode Script scope (DirectorScript.MapScript.ChallengeScript).
g_rr		// Scope holding the Response Rule system.
g_RoundState	// TODO
SessionOptions 	//Global Director options (Scripted mode only).
SessionState  	//State variables for game modes (Scripted mode only).


Delegation

Some of the tables have delegation set up, so that if a key isn't present in it, it is read from its parent table instead.

The tables are delegated like this (parent tables on the right):

The Director Scope
(DirectorScript.MapScript.ChallengeScript; DirectorScript.MapScript.LocalScript) < DirectorScript.MapScript < DirectorScript < ::
DirectorOptions
DirectorScript.MapScript.LocalScript.DirectorOptions (When Director Script active) < DirectorScript.MapScript.ChallengeScript.DirectorOptions (Scripted mode only) < DirectorScript.DirectorOptions

Entity Scripts

Use script scope _<unique ID>_<entity name>

Adding a script to the Entity Scripts KeyValue of a server-side entity loads the script as an Entity Script. The script is executed when the entity spawns, and loads into a unique script scope made up of an unique identifier followed by the entity name or class name.

A think function can be set with the thinkfunction KeyValue, the specified script function every 0.1 seconds. While it has the potential to become expensive, a programmer is able to limit the amount of code executed. Functions can also be manually called through the I/O system with the input RunScriptCode function_name(argument, ...).

Entity Scripts have a self reference to their owning entity handle, allowing the script easy access to control the entity through its class methods. There are also hook functions available depending on the entity class. Both methods and hooks are documented here.

Some entities have additional script functionality:


The script can be reloaded with console command ent_fire <name of entity> runscriptfile <relative vscript path>. This is useful for quick script reloading.


I/O system interaction

Any script can use the EntFire() and DoEntFire() functions to fire outputs to map entities. Since the activator and caller arguments in DoEntFire() take a script handle, it can be used to fire an output to an entity using the "!self" or "!activator" keyword, even without having to know its name, as long as the entity handle is available.

EntFire( "church_bell_relay", "Trigger", 0 ); // Fires an output to the Trigger input of the named entity.

player <- null;
while(player = Entities.FindByClassname(player, "player"))   // Iterate through the script handles of the players.
{
    DoEntFire("!self", "speakresponseconcept", "PlayerLaugh", 0, null, player); // Make each player laugh.
}


Conversely, the CBaseEntity::ConnectOutput() and DisconnectOutput() functions can be used to call a script function when the specified entity output fires. In addition, arbitrary VScript code can be run from the I/O system, using the RunScriptCode input available in all entities. The code will run in the current entities script scope.

Warning.png Warning: Never use double-quotation marks in any Hammer Output, since it will corrupt the map file. This means that strings cannot be passed with RunScriptCode.


Global Scripts

Naming a script file with a game mode name a mode specific script, and makes it execute every time a map is loaded in the specified mode. If a Mode Script exists, a map specific script with the map name followed by an underscore and the mode name can also be loaded.

To do: Add information about the features and utility functions that get loaded into these.

Scripted Mode

Adding a Mode Script will enable Scripted Mode for the game mode. This works on the base game modes included in the game as well.

Scripted Mode loads utility functions into the g_ModeScript scope, disables the hardcoded setting of 2 on the MaxSpecials Director Option, and enables game event callbacks and script hook functions. It also seems to break the finale tank music in Dark Carnival.

To do: Does enabling Scripted Mode have any other consequences?

Shared Tables

SessionState
A table for storing session specific variables. Not preserved across level transitions.
SessionOptions
Base Director Options for the game mode.

Available Functions

To do: Document the available utility features.


Mode Scripts

Use script scope g_ModeScript

Tables

MutationState
Initial values for the SessionState table.
MutationOptions
Initial values for the SessionOptions table.


Map Scripts

Use script scope g_MapScript

Tables

MapState
Map specific values for the SessionState table.
MapOptions
Map specific values for the SessionOptions table.


Glossary

DirectorOptions
A table of named variables that override the AI Director behavior.
Entity handle
Also known as EHANDLE. To do: Looks like some sort of pointer reference to an entity.
Script handle
An entity instance with accessors and mutators to the C++ entity object. Also known as HScript.
Script scope
The table where the variables, functions and classes of a VScript are placed.


Director options

Please see L4D2 Director Scripts for a table of available options.


Third party tools

VSLib

VSLib (VScript Library) is a simple, lightweight library for L4D2's VScript mutation system. It greatly simplifies coding VScripts by taking care of a lot of tedious work for you. It is used in several popular mods like Rayman1103's Admin System and Stranded. Read more about VSLib.

See also

External links

Alternative Tool
Alternative Documentation
Deciphered Official Scripts
<Squirrel Language>+<Left 4 Dead 2> = Pnkhrt-16px.png