L4D2 Level Design/Custom Finale: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
No edit summary
Line 1: Line 1:
{{L4D2 level intro menu}}
{{L4D2 level intro menu}}__toc__
A custom [[L4D Level Design/Finale Events Part 1|finale]] reads automatically off of a [[VScript|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.
A custom [[L4D Level Design/Finale Events Part 1|finale]] reads automatically off of a [[VScript|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.


Line 7: Line 7:
=== VScript ===
=== VScript ===
As discussed in the [[L4D2_Vscripts#Finale_Specific.2FRelated|L4D2 vscript article]], there are four stage types, additional custom finale-specific director options, and special functions available.
As discussed in the [[L4D2_Vscripts#Finale_Specific.2FRelated|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 ====
{{ScrollBox|<source lang=lua>
//-----------------------------------------------------------------------------
// Enumerations of stage types
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
{
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" ) )
{
Msg( "\n*****\nLocalScript.DirectorOptions:\n" );
foreach( key, value in LocalScript.DirectorOptions )
{
Msg( "    " + key + " = " + value + "\n" );
}
}
printl("========================================================");
}
}</source>}}


=== Map ===
=== Map ===
At the very least, all that needs to be changed is trigger_finale Finale Type, from <code>Standard</code> to <code>Custom</code>. There are other options and details you should consider:
At the very least, all that needs to be changed is trigger_finale Finale Type, from <code>Standard</code> to <code>Custom</code>. There are other options and details you should consider:
* The onslaught stage type does end unless the [[info_director|director]] is given the input EndCustomScriptedStage via script or direct in-game I/O.
* The onslaught stage type does not end unless the [[info_director|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().
* 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.
* trigger_finale: AdvanceFinaleState input is available.

Revision as of 09:20, 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

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
	{
		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" ) )
		{
			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.

Todo: break it down, explain, tutorialize
L4D2 Level Design/Gauntlet Finale Return to L4D2 Level Design