L4D2 Level Design/Custom Finale: Difference between revisions
ThaiGrocer (talk | contribs) |
ThaiGrocer (talk | contribs) |
||
Line 178: | Line 178: | ||
{{todo|break it down, explain, tutorialize}} | {{todo|break it down, explain, tutorialize}} | ||
===c7m3_port_finale.nut=== | |||
This is more complex than other custom finales but essentially does so to customize the experience. The script suggests that Sacrifice finale types depend mostly on new in-game entities and entity features. | |||
{{ScrollBox|<source lang=lua> | |||
//----------------------------------------------------- | |||
// This script handles the logic for the Port / Bridge | |||
// finale in the River Campaign. | |||
// | |||
//----------------------------------------------------- | |||
Msg("Initiating c7m3_port_finale script\n"); | |||
//----------------------------------------------------- | |||
// Enumerations | |||
ERROR <- -1 | |||
PANIC <- 0 | |||
TANK <- 1 | |||
DELAY <- 2 | |||
//----------------------------------------------------- | |||
// This keeps track of the number of times the generator button has been pressed. | |||
// Init to 1, since one button press has been used to start the finale and run | |||
// this script. | |||
ButtonPressCount <- 1 | |||
// This stores the stage number that we last | |||
// played the "Press the Button!" VO | |||
LastVOButtonStageNumber <- 0 | |||
// We use this to keep from running a bunch of queued advances really quickly. | |||
// Init to true because we are starting a finale from a button press in the pre-finale script | |||
// see GeneratorButtonPressed in c7m3_port.nut | |||
PendingWaitAdvance <- true | |||
// We use three generator button presses to push through | |||
// 8 stages. We have to queue up state advances | |||
// depending on the state of the finale when buttons are pressed | |||
QueuedDelayAdvances <- 0 | |||
// Tracking current finale states | |||
CurrentFinaleStageNumber <- ERROR | |||
CurrentFinaleStageType <- ERROR | |||
// The finale is 3 phases. | |||
// We randomize the event types in the first two | |||
local RandomFinaleStage1 = 0 | |||
local RandomFinaleStage2 = 0 | |||
local RandomFinaleStage4 = 0 | |||
local RandomFinaleStage5 = 0 | |||
// PHASE 1 EVENTS | |||
if ( RandomInt( 1, 100 ) < 50 ) | |||
{ | |||
RandomFinaleStage1 = PANIC | |||
RandomFinaleStage2 = TANK | |||
} | |||
else | |||
{ | |||
RandomFinaleStage1 = TANK | |||
RandomFinaleStage2 = PANIC | |||
} | |||
// PHASE 2 EVENTS | |||
if ( RandomInt( 1, 100 ) < 50 ) | |||
{ | |||
RandomFinaleStage4 = PANIC | |||
RandomFinaleStage5 = TANK | |||
} | |||
else | |||
{ | |||
RandomFinaleStage4 = TANK | |||
RandomFinaleStage5 = PANIC | |||
} | |||
// We want to give the survivors a little of extra time to | |||
// get on their feet before the escape, since you have to fight through | |||
// the sacrifice. | |||
PreEscapeDelay <- 0 | |||
if ( Director.GetGameMode() == "coop" ) | |||
{ | |||
PreEscapeDelay <- 5 | |||
} | |||
else if ( Director.GetGameMode() == "versus" ) | |||
{ | |||
PreEscapeDelay <- 15 | |||
} | |||
DirectorOptions <- | |||
{ | |||
A_CustomFinale_StageCount = 8 | |||
// PHASE 1 | |||
A_CustomFinale1 = RandomFinaleStage1 | |||
A_CustomFinaleValue1 = 1 | |||
A_CustomFinale2 = RandomFinaleStage2 | |||
A_CustomFinaleValue2 = 1 | |||
A_CustomFinale3 = DELAY | |||
A_CustomFinaleValue3 = 9999 | |||
// PHASE 2 | |||
A_CustomFinale4 = RandomFinaleStage4 | |||
A_CustomFinaleValue4 = 1 | |||
A_CustomFinale5 = RandomFinaleStage5 | |||
A_CustomFinaleValue5 = 1 | |||
A_CustomFinale6 = DELAY | |||
A_CustomFinaleValue6 = 9999 | |||
// PHASE 3 | |||
A_CustomFinale7 = TANK | |||
A_CustomFinaleValue7 = 1 | |||
A_CustomFinale8 = DELAY | |||
A_CustomFinaleValue8 = PreEscapeDelay | |||
TankLimit = 4 | |||
WitchLimit = 0 | |||
CommonLimit = 20 | |||
HordeEscapeCommonLimit = 15 | |||
EscapeSpawnTanks = false | |||
//SpecialRespawnInterval = 80 | |||
} | |||
function OnBeginCustomFinaleStage( num, type ) | |||
{ | |||
printl( "*!* Beginning custom finale stage " + num + " of type " + type ); | |||
printl( "*!* PendingWaitAdvance " + PendingWaitAdvance + ", QueuedDelayAdvances " + QueuedDelayAdvances ); | |||
// Store off the state... | |||
CurrentFinaleStageNumber = num | |||
CurrentFinaleStageType = type | |||
// Acknowledge the state advance | |||
PendingWaitAdvance = false | |||
} | |||
function GeneratorButtonPressed() | |||
{ | |||
printl( "*!* GeneratorButtonPressed finale stage " + CurrentFinaleStageNumber + " of type " +CurrentFinaleStageType ); | |||
printl( "*!* PendingWaitAdvance " + PendingWaitAdvance + ", QueuedDelayAdvances " + QueuedDelayAdvances ); | |||
ButtonPressCount++ | |||
local ImmediateAdvances = 0 | |||
if ( CurrentFinaleStageNumber == 1 || CurrentFinaleStageNumber == 4 ) | |||
{ | |||
// First stage of a phase, so next stage is an "action" stage too. | |||
// Advance to next action stage, and then queue an advance to the | |||
// next delay. | |||
QueuedDelayAdvances++ | |||
ImmediateAdvances = 1 | |||
} | |||
else if ( CurrentFinaleStageNumber == 2 || CurrentFinaleStageNumber == 5 ) | |||
{ | |||
// Second stage of a phase, so next stage is a "delay" stage. | |||
// We need to immediately advance past the delay and into an action state. | |||
//QueuedDelayAdvances++ // NOPE! | |||
ImmediateAdvances = 2 | |||
} | |||
else if ( CurrentFinaleStageNumber == 3 || CurrentFinaleStageNumber == 6 ) | |||
{ | |||
// Wait states... (very long delay) | |||
// Advance immediately into an action state | |||
//QueuedDelayAdvances++ | |||
ImmediateAdvances = 1 | |||
} | |||
else if ( CurrentFinaleStageNumber == -1 || | |||
CurrentFinaleStageNumber == 0 ) | |||
{ | |||
// the finale is *just* about to start... | |||
// we can get this if all the buttons are hit at once at the beginning | |||
// Just queue a wait advance | |||
QueuedDelayAdvances++ | |||
ImmediateAdvances = 0 | |||
} | |||
else | |||
{ | |||
printl( "*!* Unhandled generator button press! " ); | |||
} | |||
if ( ImmediateAdvances > 0 ) | |||
{ | |||
EntFire( "generator_start_model", "Enable" ) | |||
if ( ImmediateAdvances == 1 ) | |||
{ | |||
printl( "*!* GeneratorButtonPressed Advancing State ONCE"); | |||
EntFire( "generator_start_model", "AdvanceFinaleState" ) | |||
} | |||
else if ( ImmediateAdvances == 2 ) | |||
{ | |||
printl( "*!* GeneratorButtonPressed Advancing State TWICE"); | |||
EntFire( "generator_start_model", "AdvanceFinaleState" ) | |||
EntFire( "generator_start_model", "AdvanceFinaleState" ) | |||
} | |||
EntFire( "generator_start_model", "Disable" ) | |||
PendingWaitAdvance = true | |||
} | |||
} | |||
function Update() | |||
{ | |||
// Should we advance the finale state? | |||
// 1. If we're in a DELAY state | |||
// 2. And we have queued advances.... | |||
// 3. And we haven't just tried to advance the advance the state.... | |||
if ( CurrentFinaleStageType == DELAY && QueuedDelayAdvances > 0 && !PendingWaitAdvance ) | |||
{ | |||
// If things are calm (relatively), jump to the next state | |||
if ( !Director.IsTankInPlay() && !Director.IsAnySurvivorInCombat() ) | |||
{ | |||
if ( Director.GetPendingMobCount() < 1 && Director.GetCommonInfectedCount() < 5 ) | |||
{ | |||
printl( "*!* Update Advancing State finale stage " + CurrentFinaleStageNumber + " of type " +CurrentFinaleStageType ); | |||
printl( "*!* PendingWaitAdvance " + PendingWaitAdvance + ", QueuedDelayAdvances " + QueuedDelayAdvances ); | |||
QueuedDelayAdvances-- | |||
EntFire( "generator_start_model", "Enable" ) | |||
EntFire( "generator_start_model", "AdvanceFinaleState" ) | |||
EntFire( "generator_start_model", "Disable" ) | |||
PendingWaitAdvance = true | |||
} | |||
} | |||
} | |||
// Should we fire the director event to play the "Press the button!" Nag VO? | |||
// If we're on an infinite delay stage... | |||
if ( CurrentFinaleStageType == DELAY && CurrentFinaleStageNumber > 1 && CurrentFinaleStageNumber < 7 ) | |||
{ | |||
// 1. We haven't nagged for this stage yet | |||
// 2. There are button presses remaining | |||
if ( CurrentFinaleStageNumber != LastVOButtonStageNumber && ButtonPressCount < 3 ) | |||
{ | |||
// We're not about to process a wait advance.. | |||
if ( QueuedDelayAdvances == 0 && !PendingWaitAdvance ) | |||
{ | |||
// If things are pretty calm, run the event | |||
if ( Director.GetPendingMobCount() < 1 && Director.GetCommonInfectedCount() < 1 ) | |||
{ | |||
if ( !Director.IsTankInPlay() && !Director.IsAnySurvivorInCombat() ) | |||
{ | |||
printl( "*!* Update firing event 1 (VO Prompt)" ) | |||
LastVOButtonStageNumber = CurrentFinaleStageNumber | |||
Director.UserDefinedEvent1() | |||
} | |||
} | |||
} | |||
} | |||
} | |||
} | |||
function EnableEscapeTanks() | |||
{ | |||
printl( "*!* EnableEscapeTanks finale stage " + CurrentFinaleStageNumber + " of type " +CurrentFinaleStageType ); | |||
//Msg( "\n*****\nMapScript.DirectorOptions:\n" ); | |||
//foreach( key, value in MapScript.DirectorOptions ) | |||
//{ | |||
// Msg( " " + key + " = " + value + "\n" ); | |||
//} | |||
MapScript.DirectorOptions.EscapeSpawnTanks <- true | |||
} | |||
</source>}} | |||
{{Navbar-last|:L4D2 Level Design/Gauntlet Finale|L4D2 Level Design}} | {{Navbar-last|:L4D2 Level Design/Gauntlet Finale|L4D2 Level Design}} | ||
[[Category:Left 4 Dead 2]] | [[Category:Left 4 Dead 2]] |
Revision as of 09:41, 5 October 2011
A custom finale reads automatically off of a vscript containing a list of stages, <map name>_finale.nut. The system features the ability to increment finale stages with arbitrary conditions (mainly the onslaught stage type). Something elaborate as a custom boss is possible, for example, where a certain amount of damage advances the finale to the next stage. Once the stages listed are finished, trigger_finale fires a FinaleEscapeStarted output.
None of the official L4D2 maps use the Standard
finale option. Custom finale maps include c2m5_concert, c3m4_plantation, c4m5_milltown_escape, c7m3_port, and it is assumed that L4D1 finales do the same (No Mercy is confirmed to use custom).
Components
VScript
As discussed in the L4D2 vscript article, there are four stage types, additional custom finale-specific director options, and special functions available.
The following are custom finale vscripts with additional comments:
c2m5_concert_finale.nut
//-----------------------------------------------------------------------------
// Enumerations of stage types
ERROR <- -1 //This enumeration is not always used, but what the heck!
PANIC <- 0
TANK <- 1
DELAY <- 2
ONSLAUGHT <- 3
//-----------------------------------------------------------------------------
// Initialization of tables that will be fed to DirectorOptions
SharedOptions <-
{
A_CustomFinale_StageCount = 9 //Number of stages. Used by director VS scoring, as well??
A_CustomFinale1 = PANIC
A_CustomFinaleValue1 = 1 //1 PANIC waves
A_CustomFinale2 = PANIC
A_CustomFinaleValue2 = 1
A_CustomFinale3 = DELAY
A_CustomFinaleValue3 = 15 //15 seconds of DELAY
A_CustomFinale4 = TANK
A_CustomFinaleValue4 = 1 //1 TANK
A_CustomFinaleMusic4 = "" //Custom music entry is off in script, played in-game
A_CustomFinale5 = DELAY
A_CustomFinaleValue5 = 15
A_CustomFinale6 = PANIC
A_CustomFinaleValue6 = 2
A_CustomFinale7 = DELAY
A_CustomFinaleValue7 = 10
A_CustomFinale8 = TANK
A_CustomFinaleValue8 = 1
A_CustomFinaleMusic8 = ""
A_CustomFinale9 = DELAY
A_CustomFinaleValue9 = RandomInt( 5, 10 ) //Random DELAY between 5-10 seconds
// Additional Director options
PreferredMobDirection = SPAWN_LARGE_VOLUME
PreferredSpecialDirection = SPAWN_LARGE_VOLUME
ShouldConstrainLargeVolumeSpawn = false
ZombieSpawnRange = 3000
SpecialRespawnInterval = 20
}
InitialPanicOptions <- //Table separate from SharedOptions for stage 1
{
ShouldConstrainLargeVolumeSpawn = true
}
PanicOptions <- //General panic options
{
CommonLimit = 25
}
TankOptions <- //Another separate table used when TANK in play
{
ShouldAllowSpecialsWithTank = true
SpecialRespawnInterval = 30
}
DirectorOptions <- clone SharedOptions //DirectorOptions starts off with SharedOptions
{
}
//-----------------------------------------------------------------------------
// Used frequently to copy table to another one
function AddTableToTable( dest, src )
{
foreach( key, val in src )
{
dest[key] <- val
}
}
//-----------------------------------------------------------------------------
// Manipulation of DirectorOptions with custom logic
// In this case, DirectorOptions only changes when a new stage starts
function OnBeginCustomFinaleStage( num, type ) //Special func, when every new finale stage begins
{
if ( developer() > 0 ) //If developer mode is on, -dev
{
printl("========================================================");
printl( "Beginning custom finale stage " + num + " of type " + type );
}
//Setting up / determining WAVEOPTIONS
local waveOptions = null
if ( num == 1 ) //If first stage (assumed to be PANIC)
{
waveOptions = InitialPanicOptions
}
else if ( type == PANIC ) //General PANIC
{
waveOptions = PanicOptions
/* Change MegaMobSize if MegaMobMinSize is available in PanicOptions is available.
Was this ever used?? */
if ( "MegaMobMinSize" in PanicOptions )
{
waveOptions.MegaMobSize <- RandomInt( PanicOptions.MegaMobMinSize, MegaMobMaxSize )
}
}
else if ( type == TANK ) //TANK time!
{
waveOptions = TankOptions
}
//---------------------------------
// Done determining WAVEOPTIONS. Now, actually move to DirectorOptions
MapScript.DirectorOptions.clear() //Clear all DirectorOptions
AddTableToTable( MapScript.DirectorOptions, SharedOptions ); //Bring back SharedOptions
if ( waveOptions != null ) //Finally add the stage-dependent options (WAVEOPTIONS)
{
AddTableToTable( MapScript.DirectorOptions, waveOptions );
}
//---------------------------------
if ( developer() > 0 ) //More dev outputs (-dev)
{
Msg( "\n*****\nMapScript.DirectorOptions:\n" );
foreach( key, value in MapScript.DirectorOptions )
{
Msg( " " + key + " = " + value + "\n" );
}
if ( LocalScript.rawin( "DirectorOptions" ) ) //RAWIN checks if DirectorOptions exists
{
Msg( "\n*****\nLocalScript.DirectorOptions:\n" );
foreach( key, value in LocalScript.DirectorOptions )
{
Msg( " " + key + " = " + value + "\n" );
}
}
printl("========================================================");
}
}
Map
At the very least, all that needs to be changed is trigger_finale Finale Type, from Standard
to Custom
. There are other options and details you should consider:
- The onslaught stage type does not end unless the director is given the input EndCustomScriptedStage via script EntFire (direct or indirect) or simply in-game I/O.
- info_director: OnCustomPanicStageFinished, OnPanicEventFinished (maybe just for crescendo), and OnUserDefinedScriptEvent(1-4) outputs are available, linked to vscript stage states or methods such as .UserDefinedEvent1()-.UserDefinedEvent4().
- trigger_finale: AdvanceFinaleState input is available.
Sacrifice finale
The sacrifice finale is based off of custom finale with additional hard-coded modifications introduced in The Sacrifice update.
c7m3_port_finale.nut
This is more complex than other custom finales but essentially does so to customize the experience. The script suggests that Sacrifice finale types depend mostly on new in-game entities and entity features.
//-----------------------------------------------------
// This script handles the logic for the Port / Bridge
// finale in the River Campaign.
//
//-----------------------------------------------------
Msg("Initiating c7m3_port_finale script\n");
//-----------------------------------------------------
// Enumerations
ERROR <- -1
PANIC <- 0
TANK <- 1
DELAY <- 2
//-----------------------------------------------------
// This keeps track of the number of times the generator button has been pressed.
// Init to 1, since one button press has been used to start the finale and run
// this script.
ButtonPressCount <- 1
// This stores the stage number that we last
// played the "Press the Button!" VO
LastVOButtonStageNumber <- 0
// We use this to keep from running a bunch of queued advances really quickly.
// Init to true because we are starting a finale from a button press in the pre-finale script
// see GeneratorButtonPressed in c7m3_port.nut
PendingWaitAdvance <- true
// We use three generator button presses to push through
// 8 stages. We have to queue up state advances
// depending on the state of the finale when buttons are pressed
QueuedDelayAdvances <- 0
// Tracking current finale states
CurrentFinaleStageNumber <- ERROR
CurrentFinaleStageType <- ERROR
// The finale is 3 phases.
// We randomize the event types in the first two
local RandomFinaleStage1 = 0
local RandomFinaleStage2 = 0
local RandomFinaleStage4 = 0
local RandomFinaleStage5 = 0
// PHASE 1 EVENTS
if ( RandomInt( 1, 100 ) < 50 )
{
RandomFinaleStage1 = PANIC
RandomFinaleStage2 = TANK
}
else
{
RandomFinaleStage1 = TANK
RandomFinaleStage2 = PANIC
}
// PHASE 2 EVENTS
if ( RandomInt( 1, 100 ) < 50 )
{
RandomFinaleStage4 = PANIC
RandomFinaleStage5 = TANK
}
else
{
RandomFinaleStage4 = TANK
RandomFinaleStage5 = PANIC
}
// We want to give the survivors a little of extra time to
// get on their feet before the escape, since you have to fight through
// the sacrifice.
PreEscapeDelay <- 0
if ( Director.GetGameMode() == "coop" )
{
PreEscapeDelay <- 5
}
else if ( Director.GetGameMode() == "versus" )
{
PreEscapeDelay <- 15
}
DirectorOptions <-
{
A_CustomFinale_StageCount = 8
// PHASE 1
A_CustomFinale1 = RandomFinaleStage1
A_CustomFinaleValue1 = 1
A_CustomFinale2 = RandomFinaleStage2
A_CustomFinaleValue2 = 1
A_CustomFinale3 = DELAY
A_CustomFinaleValue3 = 9999
// PHASE 2
A_CustomFinale4 = RandomFinaleStage4
A_CustomFinaleValue4 = 1
A_CustomFinale5 = RandomFinaleStage5
A_CustomFinaleValue5 = 1
A_CustomFinale6 = DELAY
A_CustomFinaleValue6 = 9999
// PHASE 3
A_CustomFinale7 = TANK
A_CustomFinaleValue7 = 1
A_CustomFinale8 = DELAY
A_CustomFinaleValue8 = PreEscapeDelay
TankLimit = 4
WitchLimit = 0
CommonLimit = 20
HordeEscapeCommonLimit = 15
EscapeSpawnTanks = false
//SpecialRespawnInterval = 80
}
function OnBeginCustomFinaleStage( num, type )
{
printl( "*!* Beginning custom finale stage " + num + " of type " + type );
printl( "*!* PendingWaitAdvance " + PendingWaitAdvance + ", QueuedDelayAdvances " + QueuedDelayAdvances );
// Store off the state...
CurrentFinaleStageNumber = num
CurrentFinaleStageType = type
// Acknowledge the state advance
PendingWaitAdvance = false
}
function GeneratorButtonPressed()
{
printl( "*!* GeneratorButtonPressed finale stage " + CurrentFinaleStageNumber + " of type " +CurrentFinaleStageType );
printl( "*!* PendingWaitAdvance " + PendingWaitAdvance + ", QueuedDelayAdvances " + QueuedDelayAdvances );
ButtonPressCount++
local ImmediateAdvances = 0
if ( CurrentFinaleStageNumber == 1 || CurrentFinaleStageNumber == 4 )
{
// First stage of a phase, so next stage is an "action" stage too.
// Advance to next action stage, and then queue an advance to the
// next delay.
QueuedDelayAdvances++
ImmediateAdvances = 1
}
else if ( CurrentFinaleStageNumber == 2 || CurrentFinaleStageNumber == 5 )
{
// Second stage of a phase, so next stage is a "delay" stage.
// We need to immediately advance past the delay and into an action state.
//QueuedDelayAdvances++ // NOPE!
ImmediateAdvances = 2
}
else if ( CurrentFinaleStageNumber == 3 || CurrentFinaleStageNumber == 6 )
{
// Wait states... (very long delay)
// Advance immediately into an action state
//QueuedDelayAdvances++
ImmediateAdvances = 1
}
else if ( CurrentFinaleStageNumber == -1 ||
CurrentFinaleStageNumber == 0 )
{
// the finale is *just* about to start...
// we can get this if all the buttons are hit at once at the beginning
// Just queue a wait advance
QueuedDelayAdvances++
ImmediateAdvances = 0
}
else
{
printl( "*!* Unhandled generator button press! " );
}
if ( ImmediateAdvances > 0 )
{
EntFire( "generator_start_model", "Enable" )
if ( ImmediateAdvances == 1 )
{
printl( "*!* GeneratorButtonPressed Advancing State ONCE");
EntFire( "generator_start_model", "AdvanceFinaleState" )
}
else if ( ImmediateAdvances == 2 )
{
printl( "*!* GeneratorButtonPressed Advancing State TWICE");
EntFire( "generator_start_model", "AdvanceFinaleState" )
EntFire( "generator_start_model", "AdvanceFinaleState" )
}
EntFire( "generator_start_model", "Disable" )
PendingWaitAdvance = true
}
}
function Update()
{
// Should we advance the finale state?
// 1. If we're in a DELAY state
// 2. And we have queued advances....
// 3. And we haven't just tried to advance the advance the state....
if ( CurrentFinaleStageType == DELAY && QueuedDelayAdvances > 0 && !PendingWaitAdvance )
{
// If things are calm (relatively), jump to the next state
if ( !Director.IsTankInPlay() && !Director.IsAnySurvivorInCombat() )
{
if ( Director.GetPendingMobCount() < 1 && Director.GetCommonInfectedCount() < 5 )
{
printl( "*!* Update Advancing State finale stage " + CurrentFinaleStageNumber + " of type " +CurrentFinaleStageType );
printl( "*!* PendingWaitAdvance " + PendingWaitAdvance + ", QueuedDelayAdvances " + QueuedDelayAdvances );
QueuedDelayAdvances--
EntFire( "generator_start_model", "Enable" )
EntFire( "generator_start_model", "AdvanceFinaleState" )
EntFire( "generator_start_model", "Disable" )
PendingWaitAdvance = true
}
}
}
// Should we fire the director event to play the "Press the button!" Nag VO?
// If we're on an infinite delay stage...
if ( CurrentFinaleStageType == DELAY && CurrentFinaleStageNumber > 1 && CurrentFinaleStageNumber < 7 )
{
// 1. We haven't nagged for this stage yet
// 2. There are button presses remaining
if ( CurrentFinaleStageNumber != LastVOButtonStageNumber && ButtonPressCount < 3 )
{
// We're not about to process a wait advance..
if ( QueuedDelayAdvances == 0 && !PendingWaitAdvance )
{
// If things are pretty calm, run the event
if ( Director.GetPendingMobCount() < 1 && Director.GetCommonInfectedCount() < 1 )
{
if ( !Director.IsTankInPlay() && !Director.IsAnySurvivorInCombat() )
{
printl( "*!* Update firing event 1 (VO Prompt)" )
LastVOButtonStageNumber = CurrentFinaleStageNumber
Director.UserDefinedEvent1()
}
}
}
}
}
}
function EnableEscapeTanks()
{
printl( "*!* EnableEscapeTanks finale stage " + CurrentFinaleStageNumber + " of type " +CurrentFinaleStageType );
//Msg( "\n*****\nMapScript.DirectorOptions:\n" );
//foreach( key, value in MapScript.DirectorOptions )
//{
// Msg( " " + key + " = " + value + "\n" );
//}
MapScript.DirectorOptions.EscapeSpawnTanks <- true
}
← L4D2 Level Design/Gauntlet Finale | Return to L4D2 Level Design |