Left 4 Dead 2

Left 4 Dead 2

Not enough ratings
How to make your own Mutation
By Mono
This guide is intended for starters, while also includes some advanced references.
I also wrote a Mutation Creator that can generate a mutation with many configurable options in GUI, you can find its Github Repo in the References section. However, it's only available in Chinese right now, but if you guys demand it, I will do the translation.
(To proceed, you need a basic knowledge of object-oriented programming)
   
Award
Favorite
Favorited
Unfavorite
Fundamentals: VScript System
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.
The official Squirrel documentation can be found here[squirrel-lang.org]

Generally, VScripts can be divided into 4 types, according to their usage:
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.

(This guide will not cover this kind of VScripts)

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. We will focus on this kind of scripts later.

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.

--------------------------------
The location of VScripts
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.

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
  • left 4 dead 2/left4dead2_dlc2/pak01_dir.vpk
  • left 4 dead 2/left4dead2_dlc3/pak01_dir.vpk
  • left 4 dead 2/update/pak01_dir.vpk
  • left 4 dead 2/sdk_content/scripting/scripts/vscripts/
These official scripts are coming with encrypted .nuc files, you can download the plaintext format here: Decompiled VScripts[www.dropbox.com]

Get Prepared: VSLib & Editor
VSLib is a simple, powerful VScript library for L4D2, which packs many APIs of VScript. This library will make your modding so much easier. Create superior mutations, interactive maps, and much more. Actually, the famous Admin System is developed using VSLib.

Download VSLib here: L4D2Scripters/vslib[github.com]

You can choose an editor for editing .nut files, as you like. Here I am using Sublime Text 3, which has a plug-in that supports Squirrel language highlighting:


Framework: Construct our Mutaion
Make Proper Directories
  • Navigate to Left 4 Dead 2/left4dead2/addons/ (just where you store some addon campaigns), creating a folder named mymutation;
  • Head into mymutation, this will be used as a reference to later relative paths;
  • Create directories: modes/scripts/vscripts/, and decompress VSLib to the later;
To avoid ambiguity, I will describe it in Shell Script syntax:
cd "Left 4 Dead 2/left4dead2/addons/" mkdir mymutation && cd $_ mkdir -p modes/ scripts/vscripts/

Until now, your scripts/vscripts/ should be like this:


--------------------------------------
Build Essential Files
  • Create file: addoninfo.txt in current folder;
  • Create file: mymutation.txt under modes/
  • Create file: mymutation.nut under scripts/vscripts/

Write following contents to ./addoninfo.txt:
"AddonInfo" { addonSteamAppID 550 addontitle "My Mutation" addonContent_Script 1 addonauthor "myself" addonDescription "This is my first mutation game" }

Write following contents to modes/mymutation.txt:
"mymutation" { "base" "coop" "maxplayers" "4" "DisplayTitle" "My Mutation" "Description" "This is my first mutation game" "Image" "maps/any" "Author" "myself" }


Start Coding: From Scratch
This file: scripts/vscripts/mymutation.nut, is where we will write code into.

Previously, I published a simple Mutation to Workshop: Malicious Boomer.
You can get the source code on my Github: typowritter/L4D2-Mutations[github.com]

To make it short, the rule is:
  1. No common infected, normally you can only see Boomers.
  2. Boomer vomits can attract a mob of Special Infected, which includes all SI but Boomer themselves and witches

Now, I will demonstrate how to code it from scratch:

At the very first, we must include VSLib:
IncludeScript( "VSLib" );

Then we configure the Director:
MutationOptions <- { CommonLimit = 0 // Prohibit commons MegaMobSize = 0 WanderingZombieDensityModifier = 0 MaxSpecials = 30 // Max 30 SI at once, although the actual value will be far less TankLimit = 5 // Up to 5 tanks WitchLimit = 0 BoomerLimit = 30 ChargerLimit = 5 HunterLimit = 5 JockeyLimit = 5 SpitterLimit = 5 SmokerLimit = 5 SpecialRespawnInterval = 5 // Respawn a SI 5 seconds after it dies TotalSpecials = 25 // At least this many specials before director is done TotalBoomer = 15 // 15 boomers can spawn in a wave }

The code above writes some data to table MutationOptions. At map loding, the Director will check DirectorOptions first, then MutationOptions, so the configurations in later can override former ones.

I designed 3 game states to switch between them:
  • Normal:Only Boomers will respawn.
  • Peak:Get vomited,stopping the Boomer respawn and starting other SI respawn, and holding 20 seconds.
  • Relax:After 20 seconds' Peak, enter Relax for 10s, stop all SI respawn. During this 10s the Boomer vomit will not trigger any panic. 10s later go back to Normal state.

Then we configure the Director:
MutationOptions <- { CommonLimit = 0 // Prohibit commons MegaMobSize = 0 WanderingZombieDensityModifier = 0 MaxSpecials = 30 // Max 30 SI at once, although the actual value will be far less TankLimit = 5 // Up to 5 tanks WitchLimit = 0 BoomerLimit = 30 ChargerLimit = 5 HunterLimit = 5 JockeyLimit = 5 SpitterLimit = 5 SmokerLimit = 5 SpecialRespawnInterval = 5 // Respawn a SI 5 seconds after it dies TotalSpecials = 25 // At least this many specials before director is done TotalBoomer = 15 // 15 boomers can spawn in a wave }

We will need some global variables to store the current state, and Timers to control them:
::ArrSpecialInfected <- [ Z_TANK, Z_CHARGER, // the SI Array that will respawn in a Peak state Z_HUNTER, Z_JOCKEY, Z_SPITTER, Z_SMOKER ] RoundVars.DidGetVomited <- false; // has got vomited? RoundVars.DuringRelax <- false; // is in Relax state? ::BoomTimer <- -1; ::RelaxTimer <- -1;

Now the write functions to set the Timer and act as the callback:
// switch Peak to Relax ::Dropout <- function(params) { RoundVars.DidGetVomited <- false; RoundVars.DuringRelax <- true; Timers.RemoveTimer(BoomTimer); // enter Relax for 10s, then call ResetRound RelaxTimer <- Timers.AddTimer(10.0, false, ResetRound); // Utils.SayToAll("Dropout was called."); } // switch Relax to Normal ::ResetRound <- function(params) { Timers.RemoveTimer(RelaxTimer); RoundVars.DuringRelax <- false; // Utils.SayToAll("ResetRound was called."); } // This function will be triggered automatically on any player got vomited function Notifications::OnPlayerVomited::ChangeDidGetVomited(victim, boomer, params) { // if in Normal state if (RoundVars.DidGetVomited == false && RoundVars.DuringRelax == false) { RoundVars.DidGetVomited <- true; // enter Peak for 20s, then call Dropout BoomTimer <- Timers.AddTimer(20.0, false, Dropout); // Utils.SayToAll("DidGetVomited was called."); } }

Thus now we can switch between our states, we need a function to control SI respawn:
::SpawnInfected <- function (params) { // no respawn during Relax if (RoundVars.DuringRelax) { return; } // iterate on each alive survivor foreach (player in Players.AliveSurvivors()) { // if in Peak if (RoundVars.DidGetVomited) { // choose a SI randomly SI <- Utils.GetRandValueFromArray(ArrSpecialInfected); // respawn the SI near the player Utils.SpawnZombieNearPlayer(player, SI, 800, 512); } // if in Normal else { // respawn a Boomer Utils.SpawnZombieNearPlayer(player, Z_BOOMER, 800, 512); } } }

OK, we are almost done. Now we just need to bind SpawnInfected to a repeatly running Timer:
// This function will be triggered automatically when survivors left the safe area function Notifications::OnSurvivorsLeftStartArea::StartTheTimer() { // repeatly run SpawnInfected every 5s Timers.AddTimer(5.0, true, SpawnInfected); // display a message to all players Utils.SayToAll("Boomer feast has begun!"); }

Done! This is all the code we just wrote:
Last Step: Pack into a VPK file
In order for our Mutation mode to be loaded like a normal add-on, we need to package it into a common VPK format:

Windows
Find Left 4 Dead 2\bin\vpk.exe,drag&drop mymutation folder to vpk.exe, it will the generate a mymutation.vpk in the same directory as mymutation (thus addons in our example).

Just open your game and play! You can start a campaign by typing this in the console:
map xxx mymutation

Linux/macOS
Please check my another Guide: Linux Dev Toolchain Collection, in which there are some useful tools to do the work.

As we can see, the game has loaded our Mutation:


References
Valve Developer Community: Left 4 Dead 2 Level Creation

VSLib Online Docs: Simple, powerful VScript Library for L4D2[l4d2scripters.github.io]

Guides by Rayman1103: Rayman1103 - My Content[www.gamemaps.com]

Mutation Creator by me: l4d2-mutation-creator[github.com], with a demonstration video

Discord Group: Dead4Mods[discord.gg]



3 Comments
Subject_Name_Here 12 Jul, 2023 @ 11:13am 
i don't have vpk.exe in the bin folder(
Mono  [author] 23 Feb, 2021 @ 11:08pm 
Already in progress, maybe come out soon
Quiet Monarch 22 Feb, 2021 @ 11:07am 
will you make l4d2-mutation-creator with english translation?