Scriptedmode.nut
Jump to navigation
Jump to search
This is the official scriptedmode.nuc decrypted with Vice3 on Left 4 Dead 2.2.2.0 Update 3 and may be updated at any time.
///////////////////////////////////////////////////////////////////////////////
// Scriptedmode.nut
// this holds the "control logic" for all scriptmodes, and loads up the Mode
// and Map specific subscripts thus it has various initialization,
// default/override table merging, and a "root" manager class
///////////////////////////////////////////////////////////////////////////////
//=========================================================
// remedial little debug system - set this to 1 or 2 for warnings, or verbose feedback
// though only is systems that participate/used this call, which isnt many
smDebug <- 0
// the basic print
function smDbgPrint( string )
{
if ( smDebug )
printl( "scriptedmode: " + string )
}
// a loud/extra verbose print
function smDbgLoud( string )
{
if ( smDebug == 2 )
printl( "scriptedmode: " + string )
}
//-------------------------------------
// this is merged with any mode or map specific info to become DirectorOptions
//-------------------------------------
BaseScriptedDOTable <-
{
ScriptedStageType = STAGE_NONE
ScriptedStageValue = 1000
SpawnSetRule = SPAWN_SURVIVORS
SpawnDirectionCount = 0
SpawnDirectionMask = 0
}
//---------------------------------------------------------
// For AllowTakeDamage() - this table (filled in) will be passed to you - return false to ignore damage.
// You are welcome to change DamageDone and DamageType if you'd like, but other changes will be ignored
//---------------------------------------------------------
ScriptedDamageInfo <-
{
Attacker = null // hscript of the entity that attacked
Victim = null // hscript of the entity that was hit
Inflictor = null // hscript of the entity that was the inflictor
DamageDone = 0 // how much damage done
DamageType = -1 // of what type
Location = Vector(0,0,0) // where
Weapon = null // by what - often Null (say if attacker was a common)
}
//=========================================================
// called from C++ when you try and kick off a mode to
// decide whether scriptmode wants to handle it
//=========================================================
function ScriptMode_Init( modename, mapname )
{
ClearGameEventCallbacks()
::g_RoundState <- {}
::g_MapScript <- getroottable().DirectorScript.MapScript
::g_ModeScript <- getroottable().DirectorScript.MapScript.ChallengeScript
// Just for convenience, so we don't have to keep checking if it's there
if ( !("MapState" in g_MapScript) )
{
g_MapScript.MapState <- {}
}
IncludeScript( "sm_utilities", g_MapScript ) // so we have constants and such when we load the map...
IncludeScript( "sm_spawn", g_MapScript ) // and the spawning system
// printl("In scripted Init, gonna try with for map " + mapname + " in mode " + modename )
local bScriptedModeValid = false
if ( IncludeScript( modename, g_ModeScript ) )
{
printl( "ScriptMode loaded " + modename + " and now Initializing" )
IncludeScript( mapname + "_" + modename, g_MapScript )
// Add to the spawn array
MergeSessionSpawnTables()
MergeSessionStateTables()
SessionState.MapName <- mapname
SessionState.ModeName <- modename
// If not specified, start active by default
if ( !( "StartActive" in SessionState ) )
{
SessionState.StartActive <- true
}
if ( SessionState.StartActive )
{
MergeSessionOptionTables()
}
// Sanitize the map
if ( "SanitizeTable" in this )
{
SanitizeMap( SanitizeTable )
}
bScriptedModeValid = true;
}
if ( !bScriptedModeValid )
return false
if ( "SessionSpawns" in getroottable() )
{
EntSpawn_DoIncludes( ::SessionSpawns )
}
// include all helper stuff before building the help
IncludeScript( "sm_stages", g_MapScript )
// check for any scripthelp_<funcname> strings and create help entries for them
AddToScriptHelp( getroottable() )
AddToScriptHelp( g_MapScript )
AddToScriptHelp( g_ModeScript )
// go ahead and call all the precache elements - the MapSpawn table ones then any explicit OnPrecache's
ScriptedPrecache()
ScriptMode_SystemCall("Precache")
return true
}
// Make a call to MapScript and ModeScript, returns whether any calls were made
// NOTE: sadly only does no-argument calls - which makes this way less useful
scripthelp_ScriptMode_SystemCall <- "Makes the passed in call to g_MapScript and g_ModeScript, but it must have Zero args"
function ScriptMode_SystemCall( callname )
{
local calls = 0
if ( callname in g_MapScript ) // do we allow returning False to say "dont call Mode"? ugh...
{
g_MapScript[callname]()
calls++
}
if ( callname in g_ModeScript )
{
g_ModeScript[callname]()
calls++
}
return calls > 0
}
//=========================================================
// this is called from C++ when the gameplay start happens
//=========================================================
function ScriptMode_OnGameplayStart( modename, mapname )
{
if ( "SessionSpawns" in getroottable() )
EntSpawn_DoTheSpawns( ::SessionSpawns )
ScriptMode_SystemCall("OnGameplayStart")
return SessionState.StartActive
}
//=========================================================
// this is called from C++ when the mode activates
//=========================================================
function ScriptMode_OnActivate( modename, mapname )
{
if ( g_ModeScript == null )
{
// Need to reload the mode script
// TODO: Should know exactly which script we want to load here
g_ModeScript = getroottable().DirectorScript.MapScript.ChallengeScript
if ( IncludeScript( modename, g_ModeScript ) )
{
printl( "ScriptMode loaded " + modename + " and now Initializing" )
}
}
if ( !SessionState.StartActive )
{
MergeSessionOptionTables()
}
if ( "SetupModeHUD" in g_ModeScript )
{
g_ModeScript.SetupModeHUD()
}
ScriptMode_SystemCall("OnActivate")
}
//=========================================================
// called as the mutation shuts down
//=========================================================
function ScriptMode_OnShutdown( reason, nextmap )
{
SessionState.ShutdownReason <- reason
SessionState.NextMap <- nextmap
ScriptMode_SystemCall( "OnShutdown" )
g_ModeScript = null
}
//=========================================================
// Called by RR system to add any global criteria to queries based on mode or map state
//=========================================================
function ScriptMode_AddCriteria( )
{ // @todo: is there a varargs for syscall? - if so swap this to a syscall
local criteria = {}
if ( "AddCriteria" in g_ModeScript) // does this break and falsely delegate
g_ModeScript.AddCriteria( criteria )
if ( "AddCriteria" in g_MapScript)
g_MapScript.AddCriteria( criteria )
return criteria
}
//=========================================================
// this is for the fake "Event" style intra-script communication - so it really is just a wrapped call
//=========================================================
function FireEvent( eventname )
{ // yea, this is kinda weird - due to legacy really
ScriptMode_SystemCall(eventname)
}
//=========================================================
// Session level table merging - so our Mode and Map configuration can be queried in a unified way
// For State and Options, the base is merged into the Mode, then Map last, so Map value "wins"
// For Spawn tables we do the opposit (@todo Why?)
// Then in all 3 cases we then set a global ::Session<thing> variable for unified access
//=========================================================
function MergeSessionSpawnTables()
{
AddDefaultsToArray( "ModeSpawns", g_ModeScript, "MapSpawns", g_MapScript )
::SessionSpawns <- g_MapScript.MapSpawns
}
function MergeSessionStateTables()
{
AddDefaultsToTable( "MapState", g_MapScript, "MutationState", g_ModeScript )
::SessionState <- g_ModeScript.MutationState
}
function MergeSessionOptionTables()
{
AddDefaultsToTable( "BaseScriptedDOTable", this, "DirectorOptions", g_ModeScript )
AddDefaultsToTable( "MutationOptions", g_ModeScript, "DirectorOptions", g_ModeScript )
AddDefaultsToTable( "MapOptions", g_MapScript, "DirectorOptions", g_ModeScript )
::SessionOptions <- g_ModeScript.DirectorOptions
}
///////////////////////////////////////////////////////////////////////////////
//
// tick support via the "SlowPoll"
// - allowing you to get an occasional persistent callback from C++
// - keeps a list from which you can add and remove your own SlowPolls
//
// the "system" is remedial, just keeps a list, checks time delta, calls the list
// and updates last call time - nothing too fancy
//
///////////////////////////////////////////////////////////////////////////////
scriptedModeLastSlowPoll <- 0
scriptedModeUpdateFuncs <- []
scriptedModeSlowPollFuncs <- []
scriptedModeSlowPollInterval <- 3
scriptedNextUpdateCalls <- []
//=========================================================
// called from C++ on the main script update clock
function Update()
{
local frame = GetFrameCount()
local defer = []
foreach (idx, val in scriptedNextUpdateCalls)
{
if (val.frame < frame )
val.func()
else
defer.append(val)
}
scriptedNextUpdateCalls = defer
foreach (idx, val in scriptedModeUpdateFuncs)
val()
if ( Time() - scriptedModeLastSlowPoll > scriptedModeSlowPollInterval )
{
foreach (idx, val in scriptedModeSlowPollFuncs)
val()
scriptedModeLastSlowPoll += scriptedModeSlowPollInterval
}
}
scripthelp_ScriptedMode_CallNextUpdate <- "Pass a lambda to this, it will get called once as part of next Update, for doing something 'soon but not now'"
function ScriptedMode_CallNextUpdate( callnext )
{
scriptedNextUpdateCalls.append( { frame = GetFrameCount(), func = callnext } )
}
//=========================================================
// Adds a function to the update list
function ScriptedMode_AddUpdate( updateFunc )
{
scriptedModeUpdateFuncs.append( updateFunc )
}
//=========================================================
// Adds a function to the slowpoll list
function ScriptedMode_AddSlowPoll( updateFunc )
{
if (!scriptedModeSlowPollFuncs.find(updateFunc))
scriptedModeSlowPollFuncs.append( updateFunc )
else
printl("You already have a SlowPoll for " + updateFunc.tostring())
}
//=========================================================
// Removes a function from the update list
function ScriptedMode_RemoveUpdate( updateFunc )
{
local idx = scriptedModeUpdateFuncs.find( updateFunc )
if (idx == null)
return false
scriptedModeUpdateFuncs.remove( idx )
return true
}
//=========================================================
// removes a function from the slowpoll list
function ScriptedMode_RemoveSlowPoll( updateFunc )
{
local idx = scriptedModeSlowPollFuncs.find( updateFunc )
if (idx == null)
return false
scriptedModeSlowPollFuncs.remove( idx )
return true
}
///////////////////////////////////////////////////////////////////////////////
//
// called by C++ to update the HUD table - i.e. run any data functions, etc
// WARNING: this is called quite often, dont be silly in what you do in your closures/updates!
//
///////////////////////////////////////////////////////////////////////////////
function UpdateHUDTable( hudTable )
{
if ("PreCallback" in hudTable) // so you can put a custom callback to make before any hud setup
hudTable.PreCallback()
foreach (key, val in hudTable.Fields)
if ("datafunc" in val)
val.dataval <- val.datafunc();
if ("PostCallback" in hudTable) // so you can put a custom
hudTable.PostCallback()
}
/////////////////////////
// Misc Helper
_entHelper <- function ( ent, funcname )
{
if (ent == null)
printl("No entity!")
else
{
if (typeof(funcname) == "function")
{
funcname(ent)
}
else if (typeof(funcname) == "string")
{
if (funcname in ent)
ent[funcname]()
else if (funcname in g_ModeScript)
g_ModeScript[funcname](ent)
else if (funcname in g_MapScript)
g_MapScript[funcname](ent)
else
printl("No " + funcname + " in " + ent.GetName() + " or in map/mode script either")
}
else
printl("Need to pass a string of a function name or a lambda function, not a " + typeof(funcname))
}
}
scripthelp_Ent <- "Takes an entity index name or classname, calls the passed function name on all that match"
::EntCall <- function ( idxorname, funcname )
{
local hEnt = null
if ( typeof(idxorname) == "string" )
{
local foundany = false
while ( hEnt = Entities.FindByName( hEnt, idxorname ) )
{
foundany = true
g_ModeScript._entHelper( hEnt, funcname )
}
if (!foundany)
{
while ( hEnt = Entities.FindByClassname( hEnt, idxorname ) )
{
foundany = true
g_ModeScript._entHelper( hEnt, funcname )
}
}
if (!foundany)
printl("Never saw anything that matched " + idxorname )
}
else if ( typeof(idxorname) == "integer" )
{
hEnt = EntIndexToHScript( idxorname )
g_ModeScript._entHelper( hEnt, funcname )
}
}
scripthelp_Ent <- "Takes an entity index or name, returns the entity"
::Ent <- function( idxorname )
{
local hEnt = null
if ( typeof(idxorname) == "string" )
hEnt = Entities.FindByName( null, idxorname )
else if ( typeof(idxorname) == "integer" )
hEnt = EntIndexToHScript( idxorname )
if (hEnt)
return hEnt
printl( "Hey! no entity for " + idxorname )
}