Left 4 Dead 2 Guide

Custom Scripted Weapons

Sharing is temporarily disabled

Left 4 Dead 2 Guide

This guide describes how to create a custom weapon with new functionality, using a control vscript and a custom melee weapon as a base.


The guide assumes general L4D2 modding experience, knowledge in creating custom weapons, and some basic familiarity with scripting and the Squirrel language.

Please ask in the comments if you have any questions.

Template Files
The example and template files are available from the item below. Use GCFScape to browse and extract the files.


How it works
Left 4 Dead does not allow you to create custom weapons, with one exception; melee weapons. All melee weapons are based of the same entity, with a melee weapon script defining the individual weapon behavior. New melee weapons can be created by simply creating a new script for them, and including it in the mission script for each campaign that the weapon should spawn in.

The 2013 EMS update added various new scripting features for the VScript system, including the ability to spawn new entities, get callbacks on game events and monitor player key presses.

Using these features, new weapons (and maybe other kinds of items) can be created.

Limitations and caveats

The functionality of the scripted weapon is limited to what can be done with VScript functions and what entities that can be spawned in during gameplay. 

In particular damaging enemies and attributing damage to the user works badly. There is no way to damage enemies directly, so damage has to be done though entities like point_hurt or script_trigger_hurt. These entities don't count the user to be the damage source so they don't alert witches or add to friendly fire counters.

The First Person viewmodels have their animations limited to how melee weapons animate, so no reload animations or related ones are possible. There is no way to modify the viewmodels body groups or skins dynamically either. 

Detecting user button presses is limited to polling once every 0.1 second frame, so there can be a firing delay and quick taps to buttons may go unnoticed. This makes it difficult to do precision weapons well, so it is recommended to make weapons that encourage players to hold down the fire button instead.

Useful features

* Entity Spawning - Most networked point based entities can be spawned in by the scripting system, and some brush based ones can be converted to simple point based ones by the EMS entity group exporter.
* Ray tracing - Allows you to do hitscan weapon behavior, using the TraceLine() function.
* Particle effects - Can be added with entities to display many kinds of effects, including bullet tracers.

Creating the Melee Weapon

The first part we need is a custom melee weapon to track. 

Melee Weapon Scripts
A Melee weapon is defined by adding a melee weapon script under the \scripts\melee\ directory. Copy and modify one of the official scripts or the one for the provided template weapon. The file name of the script file determines the internal weapon name, so for example a weapon with the script \scripts\melee\my_weapon.txt will be called my_weapon.

The template weapon script is called template_weapon.txt

The important thing to set in the script is the viewmodel value. The weapon control script tracks weapons by their viewmodel, so it must be unique from other weapons and props. In the example template_weapon script the viewmodel is set to a recompiled version of the baseball bat model.

It does not seem possible to completely disable the melee weapon functionality, but you can set both the "starttime" and "endtime" values of each attack animation to minimize the time the attack is performed, and set both "startdir" and "enddir" to "N" to aim the attack away.

Mission Scripts
To make the weapons spawn in a campaign or map, they need to be added to it's mission script. For the official campaigns, the mission script for each campaign will need to be overridden. Extract the scripts from the game .vpks or use the provided templates.

Only the meleeweapons setting needs to be modified, adding the weapon's internal name to the semicolon separated list.

 "meleeweapons"	"fireaxe;crowbar;cricket_bat;katana;baseball_bat;knife;my_weapon"

This will make the mod incompatible with others that modify the mission scripts, like melee weapon unlockers.
For custom campaigns the mission script cannot be overridden cleanly, so it is only advisable to include support for the weapon directly in the campaign addon.

Getting the Custom Weapon Scripts to run

The custom weapons are tracked by the control script custom_weapon_controller.nut that is included in the template files.

The controller can be loaded in with a few lines of script:
[code] ::g_WeaponController <- {} DoIncludeScript("custom_weapon_controller", g_WeaponController) if(g_WeaponController.AddCustomWeapon("models/weapons/melee/v_my_weapon.mdl", "my_weapon_script")) { g_WeaponController.SetReplacePercentage("my_weapon", 15) } [/code]
The first two lines loads in the controller script. 

The next line tells the controller to start tracking a weapon. It needs the viewmodel file to track and the custom weapon VScript (more on that next chapter). The custom weapon script used for the template is called custom_template.nut. The if clause is only needed if the replacement function is used.

g_WeaponController.AddCustomWeapon(string viewModel, string script)

The function checks if the custom weapon script can be loaded, so other weapons not included in the mod can be added to provide intercompatibility.

The last line is an optional feature to replace a portion of the melee weapon spawns with the custom weapon. 

g_WeaponController.SetReplacePercentage(string weapon, int percentage)

It takes the internal name of the weapon as well as a percentage of weapons to replace.

Official Campaigns
There is no official way of loading scripts work in to every campaign, so we are going to need to override a sytem script. The best one to modify is scriptedmode.nut. In the ScriptMode_Init() function, add the code to load the controller after line 74 where the sm_spawn script has been loaded, or use and modify the included template.

This makes the mod incompatible with a variety of other mods.

Custom Campaigns
If you are authoring a custom map/campaign, this will be a lot easier. Create a logic_script entity and add a script name to it's Entity Vscripts keyvalue, for example custom_weapon_loader. Then create a custom_weapon_loader.nut vscript, and paste the code above into it.

The Custom Weapon Script

Now we come to the part your all here for, adding all the cool features to the weapon.

Open the template script custom_template.nut and save it in the vscripts directory, for example as my_weapon_script.nut

The Custom Weapon VScript is an Entity Script that gets attached to the custom weapon_melee entity by the controller when a player pick it up for the first time.

Like all entity scripts, the handle of the backing entity can be accessed through the self variable. When a player has the weapon equipped in hand, the player and viewmodel can be accessed through the currentPlayer and viewmodel variables that are set from the OnEquipped() callback function.

Callback Functions
The controller makes callbacks to functions in the script, allowing the script to react to game events like the player equipping the weapon and firing.

* OnInitialize() - Called after the script has loaded
Put any initialization code here, for example precaching assets and spawning persistent entities.
Unlike normally loading entity scripts, the variables and functions are not fully available until the script has loaded completely, so it's easier to put initialization code here than directly in the script scope.
* OnEquipped(handle player, handle viewmodel) - Called when player switches to the weapon
Supplies handles to the player and viewmodel. In the template these are copied to script scope variables.
* OnUnEquipped() - Called when the player puts away the weapon or drops it
* OnStartFiring() - Called when the player starts firing the weapon
* OnFireTick(int playerButtonMask) - Called every 0.1 seconds while the player is firing
The argument is the result of CTerrorPlayer::GetButtonMask()
* OnEndFiring() - Called when the player releases the fire button

The template weapon script contains a basic usage example, a magic staff that shoots explosions.

The Explosion
The explosion consists of an env_explosion entity for damage and effects, and a separate sound played through the script.

The env_explosion entity is persistent through the weapons life. It is spawned in OnInitialize() through the g_ModeScript.CreateSingleSimpleEntityFromTable() utility function, from a keyvlaue table at the end of the script. The handle of the entity is stored in the script scope. Note that the keyvalues in the table after set up the same way is in Hammer, with the exception of vectors and angles. The classname key defines the entity type.

Note that if you want to spawn models, they need to be precahced first with the  self.PrecacheModel(string modelName) method.

The explosion is triggered from the ShootFireBall() function, by first tracing where the player is looking, moving the explosion entity to the end of the trace using the CBaseEntity::SetOrigin() method, and then triggering the explosion with an entity output using the DoEntFire() function.

Playing Sounds
The explosion sound is played on the explosion entity using the EmitSoundOn() function. In addition, a looping heartbeat sound is played the same way when the weapon is equipped, and stopped when unequipped using the StopSoundOn() function.

Sounds can also be played for specific players using the EmitsoundOnClient(string soundScript, handle player) function.

Note that sounds have to be precahed before they are played. This can be done in the OnInitialize() function using the self.PrecacheScriptSound(string soundScript) method.

Advanced Examples

Attaching objects to the viewmodel
Entities can be parented to attachment points on the viewmodel. This has some issues though. The attachment will only be visible on the client viewing the viewmodel, and the server console will be spammed with bone access errors.

Here is an example of attaching a small flame to a flamethrower nozzle.
local flamethrower_flame = { classname = "info_particle_system" angles = Vector(30, 0, 0) effect_name = "weapon_molotov_fp" render_in_front = "0" start_active = "1" targetname = "flamethrower_fire" origin = Vector(0, 0, 0) } flame <- g_ModeScript.CreateSingleSimpleEntityFromTable(flamethrower_flame) DoEntFire("!self", "SetParent", "!activator", 0, viewmodel, flame) DoEntFire("!self", "SetParentAttachment", "attach_nozzle", 0.01, flame, flame)


Guide Credits

You may like

  • Mod
    Coach tlou2

    Reuploaded before/after be deleted

  • Mod

    a special assault rifle.A mod never done before.an assault rifle with knockback effect for the lols. makes ak 47 to knockback enemis like the shotgun does.and to do that i have to change some status too ,so the weapon dont be broken, ...

    • New
  • Mod
    Rayman1103's Mutation Mod

    Adds a lot of Custom Mutations created by me and others. The main-menu has been edited so all Mutations can be created via lobbies, and can be played online at anytime. To play Custom Mutations online, they'll have to be played via a ...

  • 5 Maps
    Dark Wood (Extended)

    ------------------------------------------------------------------------------ 05.11.2022 Update v1.8: - Added/redesigned lighting on all maps - Redesigned several areas with new geometry and/or new textures - Sewer hallways ex...

  • Mod
    The Demolition Gunners

    "I don't know where I am. I don't know why I'm here. All I know is I must destroy...and call forth Explosion...!!" Once upon a time in a parallel world, all survivors lived each of their daily life in peace. One day, a huge explosion ...

    • Updated