L4D2 Level Design/Gauntlet Finale: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
m (WIP, usiing base text!!)
 
m (→‎director_gauntlet.nuc: Removed redundancy)
 
(24 intermediate revisions by 7 users not shown)
Line 1: Line 1:
Boot lockers holding item stashes were introduced into Left 4 Dead 2 with the "Passing" update. Rather than being separate entities, such as the ammo crates in Half-Life 2, these stashes are built up from many entities already available in the game; in fact, the only new factors used for these lockers are the model and opening sound. This article will demonstrate how to build different types of lockers; from static, persistent lockers, to lockers spawnable at different points, to lockers that can contain different types of items.
{{LanguageBar}}
{{L4D2 level intro menu}}
{{update}}
{{stub}}
{{l4d2}} Gauntlet Finales were introduced in The Parish campaign. Rather than having the player holdout in an enclosed arena, gauntlets force players to run to the end of the level, where the rescue vehicle is already waiting for them. In The Parish, the players must run across a bridge to make it to a rescue helicopter.


You may wish to follow along with the [[Decompiling Maps|decompiled map]] this article is based off of, c5m5_bridge.vmf.


The article assumes that you have a good grasp of working in Hammer and are familliar with the way Left 4 Dead entities work.
== Overview ==
A gauntlet mainly consists of [[L4D Level Design/Finale Events Part 1|standard finale components]] with a few new additions and modifications:


*The [[trigger_finale]] uses "Gauntlet" as Finale Type setting.
*A [[info_target]], named "nav_flow_target" at the end of the map close to the rescue vehicle, but not on navmesh marked with [[List_of_L4D_Series_Nav_Mesh_Attributes|Rescue_Vehicle]]. Else it will be blocked and Nav Flow won't generate.
*Rescue closets are usually absent but should work if they are added. Consider adding one if there is combat before reaching the finale area.
*Finales depend on director_gauntlet.nuc vscript by default, even without setting the "Script File" field.<br>
{{note|Defining a different script in the "Script File" field will first run the default director_gauntlet and second it will launch whatever script you defined.}}


The VMF instances used in this article can be found here (link coming soon).
The gauntlet finale structure is not very flexible and workarounds should be taken into consideration. Only one tank is allowed before the escape vehicle is ready. director_debug 1 shows that it is considered an escape sequence by the second time GauntletStopPanic is fired at trigger_finale.


==Static Locker==
== director_gauntlet.nuc ==
This is the original Gauntlet Finale script, should you want to tweak it for your own purposes.
<source lang=js>
Msg("Initiating Gauntlet\n");


This is the most basic type of boot locker: it will always be present in the world (as opposed to lockers which are chosen to spawn at random in-game) and will include whichever type of item the level designer decides on.
DirectorOptions <-
{
PanicForever = true
PausePanicWhenRelaxing = true


[[Image:Footlocker01.png|frame|left|The locker.]]
IntensityRelaxThreshold = 0.99
{{clr}}
RelaxMinInterval = 25
 
RelaxMaxInterval = 35
Doesn't look like much from inside Hammer, does it? Here's how it's made.
RelaxMaxFlowTravel = 400
 
 
===Step 1===
----
 
[[Image:Footlocker02.png|thumb|The locker model.]]
 
The first building block is the locker model - a simple prop_dynamic. Insert one into a blank map (preferably saved with your desired instance name) and give it these properties (the rest can be left as default):
 
 
*'''World Model:''' ''models/props_waterfront/footlocker01.mdl'' - This is the model used.
*'''Name:''' ''static_locker'' - The name of the model will be fixed up as needed when the instance is inserted into your map.
*'''Start Fade Dist:''' ''1500'' - This is the general distance Valve have used to begin fading the locker out.
*'''End Fade Dist:''' ''1900'' - At this distance away, the locker will no longe be visible. Make sure that it is actually out-of-sight at this distance, otherwise you may be able to see the items inside.
*'''Collisions:''' ''Not Solid'' - The locker model does not actually have a collision mesh, so setting this option will ensure that the console doesn't complain in-game.
{{clr}}
 
 
===Step 2===
----
 
[[Image:Footlocker03.png|thumb|Collision brushes.]]
 
The second thing to do is to put in a couple of nodraw-textured brushes so that the player and objects will appear to collide with the locker. Valve left the locker with no collision model of its own probably because it would require more expensive VPhysics calculations to include one (since the locker is a concave shape), so brushes are used to act as the solid sides and base of the locker.
 
The collision brushes should be 2 units thick and follow the edges of the locker. Putting one in for the locker base is also useful. ''Don't forget'' to tie all of these brushes to a '''func_detail,''' otherwise they will play havoc with your [[visleaves]].
 
{{note|You may want to set the animation of your locker to aid in creating the collision brushes. In the properties of the model, go to the "Model" tab and select the sequence "open". Don't forget to set this back to "ref" afterwards, as changing the default animation in this tab can cause problems in Hammer.}}
{{clr}}
 
 
===Step 3===
----
 
[[Image:Footlocker04.png|thumb|Items in the locker.]]
 
Place your desired items into the locker and rotate/position them. I have used health kits and pistols here, since I designed this locker to be present at the beginning of my campaign. If you are using other items, it's best to pretty much fill the entire bottom of the locker model with a layer of items.
 
If you are using items such as pipe bombs, molotovs or boomer bile, you'll want to set their "Count" value to 10, or maybe higher. This means that players will be able to take more than one item from each spawner in-game; of course, if you just want single item pickups (like the health kits in the picture), set the "Count" value to 1.
 
{{tip|Some items, such as adrenaline, do not have a count value my default and only allow a single pickup; this can be overriden by disabling "Smart Edit" and adding a keyvaue called "count" with a value of whatever you choose.}}
 
Name ''all'' the items you have added as '''"static_locker_items"'''.
{{clr}}
 
 
===Step 4===
----
 
[[Image:Footlocker05.png|thumb|The locker opening sound.]]
 
Set your locker's animation back to "ref" (to close the lid) and add an ambient_generic somewhere near the locker. The position doesn't really matter, as we'll be specifying the origin point of the sound in the ambient_generic's properties.
 
The properties can be left at the defaults, apart from these:
 
*'''Name:''' ''static_locker_snd_open'' - This will allow us to trigger the sound later on.
*'''Sound Name:''' ''Trunk.Open'' - The soundscript entry for the locker opening sound.
*'''SourceEntityName:''' ''static_locker'' - This is the name of the prop_dynamic locker model. When triggered, the sound will originate from the locker model rather than from the position of the ambient_generic itself.
{{clr}}
 
 
===Step 5===
----
 
[[Image:Footlocker06.png|thumb|The template spawner.]]


Next, we need a point_template in order to spawn our items when we want them. Simply ticking the "Must Exist" flag on the items may cause their glow effect to be visible through the edges of the locker model, so the point_template ensures that the items are only created when they are needed. For lockers which may or may not be present, the point_template allows the spawning of the items only if the locker exists.
LockTempo = 0
SpecialRespawnInterval = 20
PreTankMobMax = 20
ZombieSpawnRange = 3000
ZombieSpawnInFog = true


Setting up the point_template is simple:
MobSpawnSize = 5
 
CommonLimit = 5
*'''Name:''' ''static_locker_template'' - The name of the point_template.
*'''Template 1:''' ''static_locker_items'' - When triggered, the point_template will spawn our ''static_locker_items'' (that we named earlier) at their appropriate spawn points.
{{clr}}


GauntletMovementThreshold = 500.0
GauntletMovementTimerLength = 5.0
GauntletMovementBonus = 2.0
GauntletMovementBonusMax = 30.0


===Step 5.5 (optional)===
// length of bridge to test progress against.
----
BridgeSpan = 20000


Add in a [[logic_game_event]] entity to tell the director that the locker has been opened. This may not be in Hammer's entity list, so add in a different entity (such as an info_target) and change its name in the drop-down box to logic_game_event. You will have to add these keyvalues manually:
MobSpawnMinTime = 5
MobSpawnMaxTime = 5


*'''Key:''' ''targetname'', '''Value:''' ''static_locker_event'' - The name of the entity.
MobSpawnSizeMin = 5
*'''Key:''' ''eventName'', '''Value:''' ''foot_locker_opened'' - The name of the game event this entity will fire when triggered.
MobSpawnSizeMax = 20
*'''Key:''' ''spawnflags'', '''Value:''' ''1'' - No idea what this flag does. I'm copying the values from Valve's maps to be on the safe side.


===Step 6===
minSpeed = 50
----
maxSpeed = 200


[[Image:Footlocker07.png|thumb|The func_button to open the locker.]]
speedPenaltyZAdds = 15


This nodraw block that's just englufed the locker is our func_button and plays the key role in getting all the locker parts functioning correctly. It should typically extend about 4 units further out from each side of the locker model (apart from the bottom, of course, where it should be in-line with the locker base).
CommonLimitMax = 30


The following properties will need to be changed:
function RecalculateLimits()
{
//Increase common limit based on progress 
    local progressPct = ( Director.GetFurthestSurvivorFlow() / BridgeSpan )
   
    if ( progressPct < 0.0 ) progressPct = 0.0;
    if ( progressPct > 1.0 ) progressPct = 1.0;
   
    MobSpawnSize = MobSpawnSizeMin + progressPct * ( MobSpawnSizeMax - MobSpawnSizeMin )


*'''Name:''' ''static_locker_button'' - In case you want to lock/unlock the button at any time though inputs.
*'''Glow Entity:''' ''static_locker'' - When the player is in range of the button and looks at it, the prop_dynamic model of the locker will glow to invite the player to open it.


These spawnflags will need to be set:
//Increase common limit based on speed 
    local speedPct = ( Director.GetAveragedSurvivorSpeed() - minSpeed ) / ( maxSpeed - minSpeed );


*'''Don't Move.'''
    if ( speedPct < 0.0 ) speedPct = 0.0;
    if ( speedPct > 1.0 ) speedPct = 1.0;


The button also needs the following outputs:
    MobSpawnSize = MobSpawnSize + speedPct * ( speedPenaltyZAdds );
   
    CommonLimit = MobSpawnSize * 1.5
   
    if ( CommonLimit > CommonLimitMax ) CommonLimit = CommonLimitMax;
   


*'''OnPressed,''' ''static_locker'' -> '''SetAnimation:''' ''opening''.
}
*'''OnPressed,''' ''self'' -> '''Kill'''.
}
*'''OnPressed,''' ''static_locker_snd_open'' -> '''PlaySound'''.
*'''OnPressed,''' ''!activator'' -> '''Speakresponseconcept:''' ''OpenLocker''.
*'''OnPressed,''' ''static_locker_template'' -> '''ForceSpawn'''.
*'''OnPressed,''' ''static_locker_event'' -> '''FireEvent'''.


All of these need the "Fire Once Only" box ticked. An explanation of each:
function Update()
{
DirectorOptions.RecalculateLimits();
}
</source>


:1. Sets the animation of the locker model, to open it. The animation stops automatically, so there's no need to send another input to do so.
=== Tips ===
:2. Gets rid of the func_button, so that it can't be pressed again and doesn't block the top of the box, prohibiting the player from picking up the items.
==== Bridge Span Value ====
:3. Plays the locker opening sound.
The script has a "BridgeSpan" Setting which determines how long the Gauntlet area will be, which is used to calculate progression in Versus.<br>
:4. Tells the character who "opened" the locker (the one who pressed the button) to say a line, mainly to tell the other players that they've found the items.
This Value is in units, which can easily be taken in [[Hammer]] by just creating a brush entity with one side at the [[trigger_finale]] while the other is where the rescue vehicle will be.<br>
:5. Spawns the items inside the locker.
As seen in the picture below, this brush entity will be 20096.0 units wide. So the script is set to 20000 units to make the numbers round.
:6. Fires the game event to the Director. Only include this output if you followed the optional step 5.5 above.
[[File:Gauntlet bridge span helper.png|1500px|left]]
{{clr}}
{{clr}}


==See also==
[[L4D2 Level Design]]


===Step 7===
{{NavBar|L4D2 Level Design/Scavenge Finale|L4D2 Level Design|L4D2 Level Design/Custom Finale}}
----
 
[[Image:Footlocker08.png|thumb|The info_remarkable.]]
 
The final step is to add an info_remarkable, to let the engine know that the characters should say something when they spot the locker. Set these properties:
 
*'''Name:''' ''static_locker_remarkable'' - The name of the entity.
*'''Subject Context:''' ''WorldFootLocker'' - This tells the game that the characters should say something related to spotting the locker, like "Let's open it up" or similar.
{{clr}}
 
===In-Game===
 
Your locker should look something similar to this:
 
[[Image:Footlocker09.jpg|thumb|left|640px|The locker in-game.]]
{{clr}}


{{note|This article is a work-in-progress. It is currently being updated.}}
[[Category:Left 4 Dead 2]]

Latest revision as of 18:53, 5 August 2025

English (en)中文 (zh)Translate (Translate)
Broom icon.png
This article or section needs to be updated to include current information regarding the subject.
Remember to check for any notes left by the tagger at this article's talk page.

Stub

This article or section is a stub. You can help by expanding it.

Left 4 Dead 2 Gauntlet Finales were introduced in The Parish campaign. Rather than having the player holdout in an enclosed arena, gauntlets force players to run to the end of the level, where the rescue vehicle is already waiting for them. In The Parish, the players must run across a bridge to make it to a rescue helicopter.

You may wish to follow along with the decompiled map this article is based off of, c5m5_bridge.vmf.

Overview

A gauntlet mainly consists of standard finale components with a few new additions and modifications:

  • The trigger_finale uses "Gauntlet" as Finale Type setting.
  • A info_target, named "nav_flow_target" at the end of the map close to the rescue vehicle, but not on navmesh marked with Rescue_Vehicle. Else it will be blocked and Nav Flow won't generate.
  • Rescue closets are usually absent but should work if they are added. Consider adding one if there is combat before reaching the finale area.
  • Finales depend on director_gauntlet.nuc vscript by default, even without setting the "Script File" field.
Note.pngNote:Defining a different script in the "Script File" field will first run the default director_gauntlet and second it will launch whatever script you defined.

The gauntlet finale structure is not very flexible and workarounds should be taken into consideration. Only one tank is allowed before the escape vehicle is ready. director_debug 1 shows that it is considered an escape sequence by the second time GauntletStopPanic is fired at trigger_finale.

director_gauntlet.nuc

This is the original Gauntlet Finale script, should you want to tweak it for your own purposes.

Msg("Initiating Gauntlet\n");

DirectorOptions <-
{
	PanicForever = true
	PausePanicWhenRelaxing = true

	IntensityRelaxThreshold = 0.99
	RelaxMinInterval = 25
	RelaxMaxInterval = 35
	RelaxMaxFlowTravel = 400

	LockTempo = 0
	SpecialRespawnInterval = 20
	PreTankMobMax = 20
	ZombieSpawnRange = 3000
	ZombieSpawnInFog = true

	MobSpawnSize = 5
	CommonLimit = 5

	GauntletMovementThreshold = 500.0
	GauntletMovementTimerLength = 5.0
	GauntletMovementBonus = 2.0
	GauntletMovementBonusMax = 30.0

	// length of bridge to test progress against.
	BridgeSpan = 20000

	MobSpawnMinTime = 5
	MobSpawnMaxTime = 5

	MobSpawnSizeMin = 5
	MobSpawnSizeMax = 20

	minSpeed = 50
	maxSpeed = 200

	speedPenaltyZAdds = 15

	CommonLimitMax = 30

	function RecalculateLimits()
	{
	//Increase common limit based on progress  
	    local progressPct = ( Director.GetFurthestSurvivorFlow() / BridgeSpan )
	    
	    if ( progressPct < 0.0 ) progressPct = 0.0;
	    if ( progressPct > 1.0 ) progressPct = 1.0;
	    
	    MobSpawnSize = MobSpawnSizeMin + progressPct * ( MobSpawnSizeMax - MobSpawnSizeMin )


	//Increase common limit based on speed   
	    local speedPct = ( Director.GetAveragedSurvivorSpeed() - minSpeed ) / ( maxSpeed - minSpeed );

	    if ( speedPct < 0.0 ) speedPct = 0.0;
	    if ( speedPct > 1.0 ) speedPct = 1.0;

	    MobSpawnSize = MobSpawnSize + speedPct * ( speedPenaltyZAdds );
	    
	    CommonLimit = MobSpawnSize * 1.5
	    
	    if ( CommonLimit > CommonLimitMax ) CommonLimit = CommonLimitMax;
	    

	}
}

function Update()
{
	DirectorOptions.RecalculateLimits();
}

Tips

Bridge Span Value

The script has a "BridgeSpan" Setting which determines how long the Gauntlet area will be, which is used to calculate progression in Versus.
This Value is in units, which can easily be taken in Hammer by just creating a brush entity with one side at the trigger_finale while the other is where the rescue vehicle will be.
As seen in the picture below, this brush entity will be 20096.0 units wide. So the script is set to 20000 units to make the numbers round.

Gauntlet bridge span helper.png

See also

L4D2 Level Design