Left 4 Dead 2/Scripting/Expanded Mutation System/Appendix: Functions
This documents some of the functions provided within the new Mutation system. As you have probably figured out if you've gotten this far, the tools provide a scattered collection of helpers and utilities, not a complete set of accessors. Hopefully the docs below will at least provide some ideas on what sorts of things you can do, and how to do them.
Note there are also plenty of other helper elements - Spawning Objects and Infects, the Simple HUD, Saving and Restoring data tables, and so on. They can be reached from the Appendix sidebar.
Script Utilities
sm_utilities holds utility functions used by scripted mode. Notable functions are:
- TeleportPlayersToStartPoints( spawnpointName ) : This function takes a targetname of spawn points in a map. The spawn points do not need unique names but there must be four of them. When the function is called the players will move from wherever they are to the spawn points. The function will return false if the spawn points are not found.
- SpawnStartBox( centerobjname, useFloating, width, depth, barriermodel, min_gap ) : This function creates a bounding rectangle of a specified size that will trigger a callback when touched by players. The only required parameter is centerobjname, which is the targetname of the point to center the start box around. By default the box is visible in game - it appears as a white striped plane. If you pass false for 'usefloating' it will use yellow striped ground markers. Or custom models can be specified with barriermodel. The function will return false if it does not find the startbox origin targetname.
- When the first survivor exits the startbox, we look for a function called SurvivorLeftStartBox defined at Map or Mode Scope. If we find it, we call it - otherwise we Director.ForceNextStage. NOTE: if you provide your own callback, you have to ForceNextStage yourself if that is what you want - we do _not_ automatically call it if you add a custom callback.
- In either case, we clear the StartBox (i.e. remove the callback checking for leaving, and if it is a floating startbox, we also remove the startbox objects).
- SanitizeMap( sanitizeTable, sanitizeFlags ) : fires outputs based on contents of sanitize table. The sanitize table is specified in a .nut file in the vscript folders named sanitize_<mapname>.nut. The SanitizeMap function is useful for killing entities on map spawn. This is particularly useful to do when you want to create a game mode for existing L4D maps but the map has a bunch of map entities that would interfere with your custom game mode. Check out sanitize_c3m1_plankcountry.nut for an example of how all the c3m1 entities (choreo entities, coop entities, etc) are killed on map spawn to prepare the map for holdout mode.
HUDManageTimers( id, timer_command, val): Call used to manage timers on hud elements. Valid command enums are:
- TIMER_DISABLE - disable the timer
- TIMER_COUNTUP - count up
- TIMER_COUNTDOWN - count down
- TIMER_STOP - stop the timer
- TIMER_SET - set the timer. val is the initial time to set.
- Misc helpers and simple things
- DeepPrintTable(table): prints out an entire table to the console in an indented/as one would build it kind of way, useful for debugging or for generating something to cut and paste to somewhere else.
- Ent( idxorname ) Takes an entity index or name, returns the entity. Example - You could issue this command from the console to set Rochelle's health to 1:
script Ent("!rochelle").SetHealth(1)
- EntCall( idxorname, funcname ): Takes an entity index, name, or class name and calls the passed function name on all the entities that match. Example - you could use this command from the console to kill Rochelle:
script EntCall( "!rochelle", "Kill")
- EntCall also supports lambda functions, so you could issue this command from the console to print all the player names:
script EntCall("player",@(ent) printl(ent.GetPlayerName()))
Some of the exposed C++ connections
Functions in this section are called directly by the C++ code within Left4Dead2.
EyeAngles(): Returns a QAngle of where the target is looking.
Vector(x,y,z ): Create a vector with x,y,z coordinates.
Vector methods:
.Length()
.LengthSqr()
.Length2D()
.Length2DSqr()
.Dot()
.Cross()
.Norm()
.Scale( val )
QAngle( x,y,z): Create a QAngle with x,y,z angles.
QAngle methods:
.Pitch()
.Yaw()
.Roll()
.Forward()
.Left()
.Up()
.ToQuat()
- AllowTakeDamage(damageTable) : If you place a function with this name in your script, C++ will call it on all damage events. The damageTable is actually defined in scriptedmode.nut and filled in as appropriate before each call.
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)
}
- If you return "false" the damage will be stopped and nothing will be done to the target (though animation/bloodstains/decals will probably still happen, sorry about that). The only field read back by the C++ is DamageDone, so if you want you can use it to change the damage value before it is applied. The Type field is a bitfield, of which several key #def's are exported to script (DMG_HEADSHOT, DMG_BULLET, DMG_BUCKSHOT, DMG_MELEE, DMG_STUMBLE, DMG_BLAST, DMG_BLAST_SURFACE, DMG_BURN ).
- InterceptChat( str, srcEnt ) : If you put a function with this name in your script, C++ will call it on all chat messages. Passing in the (annotated) chat string and the HSCRIPT of the speaker.
- TODO: Add "annotation parser" to sm_utilities, figure out what happens if chat is from spectator/server.
- Say( srcScript, string, bTeamOnly ) : Calling this will have player srcScript send string to chat, either to teamonly (true) or to everyone.
- TODO: A bad srcScript should "just work" by coming from NULL, but not yet thoroughly tested.
- Physics Stuff: There are several gets and sets you can do with physics. Remember the Apply's are impulses, not setting an absolute new value.
vVel = GetPhysVelocity( CurEnt )
vAng = GetPhysAngularVelocity( CurEnt )
CurEnt.ApplyAbsVelocityImpulse( throwImp )
CurEnt.ApplyLocalAngularVelocityImpulse( angImp )
- GetInvTable( playerScript, invTable ) : Fills invTable with the player's inventory. The table has
key: SlotX value: Entity in slot (0-4, i think? Main Weap/Hand/Thrown/HealthPack/Pills)
key: Held value: Entity being carried, otherwise Held will not be in the table
- GetInfectedStats( statTable ) : Fills statTable with some infected counts/etc... Right now the list is Witches, Tanks, Specials, Commons, though we may add more as requested going forward.
- PickupObject( playerScript, entity ) : Object from world is put into the "Held" slot of the player. Warning: it will smoothly interpolate from where it is to the players hand - which is a bit goofy if it is on other side of level.
- TODO: Add helper for "move object near me and pickup", better testing on bad/immobile objects
- UserConsoleCommand( playerScript, arg ) : when a user does a <scripted_user_func argument> at console (or bound to a key) this function is called (if it exists). The playerscript is which players console it came from. You can pass strings or whatever, of course. So could do a switch statement off <arg> to give players special controls, etc.
- AddThinkToEnt( ent, FuncName ) : this will put a think function onto an entity, or pass null to remove it. This is NOT chained, so be careful. Mostly used when you want to Inject a set of functions onto a spawned entity without putting a script on it yourself. Adding a "Think" pre-spawn will have it auto-connect, but post-spawn you need to by hand attach it. i.e. you might do something like the following (note in this case you could have one actual think func that all your spawned objects used, in practice.
function InjectThink( scrEnt, thinkFunc )
{
scrEnt.ValidateScriptScope()
local scrScope = scrEnt.GetScriptScope()
scrScope["InjectedThink"] <- thinkFunc
AddThinkToEnt( scrEnt, "InjectedThink" )
}
// and then you could call it like so to have a spawned entity call a common think passing itself as an argument
InjectThink( myEnt, @() g_MapScript.InjectedThink(self))
// or of course whatever function you want/local thing, etc
- TODO: Just put a set of these in sm_utilities so people dont mess them up/etc
- CanPickupObject( object ) : This lets you decide whether a given object should be pickupable. Right now this is a single function in the Options Table - however we have a TODO to move it to MapScript and perhaps later to make it a table you can create with a callback so you can just list pickupable model names, plus have a fancier callback, plus have entries for both mode and map.
- ZSpawn( spawnTable ) : This is essentially the z_spawn console command, except (a) you pass a table and (b) no raycast, you give it the position vector. The table entries used are:
key: type value: ZOMBIE_WITCH, etc - or the special ZSPAWN_MOB, or WITCHBRIDE, etc
key: pos value: Vector position at which to spawn
key: ang value: QAngle for spawn, optional (defaults to 0,0,0)
- TraceLine(traceTable): uses a configuration table to do a raytrace, puts return information into the table for return usage. Returns a bool indicating that the trace was successful.
Inputs:
.start - start vector for the trace
.end – end vector for the trace
.ignore – entity handle to ignore as part of the trace
.mask – optional masks: TRACE_MASK_VISIBLE_AND_NPCS (default), TRACE_MASK_ALL, TRACE_MASK_VISION, TRACE_MASK_SHOT, TRACE_MASK_PLAYER_SOLID, TRACE_MASK_NPC_SOLID
Outputs:
.hit – did we hit something
.pos – where the traceline ended
.fraction – where the hit occurred
.enthit – handle for entity hit if there is one
.startsolid – did we start in solid
The only required information to make the trace is providing the start and end point.
Example:
traceTable <-
{
start = Vector( 0,0,0 )
end = Vector( 0,0,256 )
}
- CommandABot( commandTable ): Issues commands to bots based on a table configuration.
Inputs:
.cmd - required field, must specify one of these commands: BOT_CMD_ATTACK, BOT_CMD_MOVE, BOT_CMD_RETREAT, BOT_CMD_RESET
NOTE: in order to use the BOT_CMD enums you must be operating in the mode script scope. If you aren't you can pass an integer instead of the enum (e.g., set cmd to 1 instead of BOT_CMD_MOVE)
.pos - a position, used only in BOT_CMD_MOVE
.bot - which bot you are commanding (or NULL for all bots)
.target - for attack and retreat - what entity is the target of the attack or retreat.
Here is an example of how to create a table that commands all bots to run to position 0,0,0:
commands <-
{
cmd = 1 // BOT_CMD_MOVE
pos = Vector( 0,0,0 )
}
----
CommandABot( commands )
Alternatively, you could send the data inline, like so:
CommandABot( { cmd = 1, pos = Vector( 0,0,0 ) } )
ent.SetSenseFlags( flags ): This entity class method will allow setting of the BOT_CANT_SEE flag on a target infected. Pass 0 to clear the flag and allow the target entity to resume seeing.
ent.GetSenseFlags( ): Get the current bits for the bot sense flags
(Flags: "BOT_CANT_SEE", "BOT_CANT_HEAR", "BOT_CANT_FEEL")
Notable Player class methods:
- .GetHealthBuffer(): returns the current temp health buffer
- .SetHealthBuffer( float ): sets the health buffer
- .IsIncapacitated(): returns a bool indicating incapacitated state
- .IsGhost(): returns a bool indicating ghost state
- .IsHangingFromLedge(): returns a bool indicating ledge hang state
- .GetActiveWeapon(): Get the player's active weapon
- .GetZombieType(): If an infected, find out what type. Recognized ZombieTypes are:
- Smoker = 1, Boomer = 2, Hunter = 3, Spitter = 4, Jockey = 5, Charger = 6, Witch = 7, Tank = 8, Survivor = 9
- .IsSurvivor(): On the survivor team (Otherwise, infected)
- .IsPlayer(): Is this entity a player class
- .GiveAmmo( amount ): Give ammo for the player's primary weapon
- .GetSurvivorSlot(): Get the player's slot
- .GetPlayerUserId(): Get the player's userID.
- .UseAdrenaline( duration ): Causes Adrenaline's speed effect, no change to health
- .TryGetPathableLocationWithin( radius ): Get a location on the nav the player can path to within the desired radius
- .GetEntityIndex():
- .EyePosition(): Try and get the current eye position
- .GetButtonMask(): returns a bitfield of currently pressed buttons. Recognized buttons are:
- IN_ATTACK, IN_ATTACK2, IN_JUMP, IN_DUCK, IN_FORWARD, IN_BACK, IN_USE, IN_MOVELEFT, IN_MOVERIGHT, IN_RELOAD
- .GiveItem( itemname ): Give an item/weapon by name. ("health", "katana", "rifle_ak47", etc.)
- .GetOwnerEntity(): return an entity's owner
- .SetReviveCount( count ): set the number of times a survivor has been revived, and update third-strike state and effects
- .ReviveFromIncap(): revive an incapped player
- .ReviveByDefib(): revive a dead player by defib
- .HitWithVomit(): target a player with a vomit attack
- .Stagger( position ): stagger a player away from a position. Use Vector(0,0,0) to just stagger forward.
- .IsDead(): returns a bool indicating dead state
- .GiveUpgrade( upgrade ): give a primary weapon upgrade
- .RemoveUpgrade( upgrade ): remove a primary weapon upgrade
- Upgrades: UPGRADE_INCENDIARY_AMMO, UPGRADE_EXPLOSIVE_AMMO, UPGRADE_LASER_SIGHT
- .IsDying(): returns a bool indicating dying state
- .IsOnFire(): returns a bool indicating if a player is on fire
- .Extinguish(): Extinguish a burning player
- IsPlayerABot( player ): Is this player/entity a bot
- OnEntText() : if ent_text is used on an entity and the function OnEntText() exists it will be called every tick. Returns a string that gets displayed as part of the ent_text overlay. This is a handy feature for displaying script data on objects when you ent_text the object, or running arbitrary code (such as turning on additional debugging visualizations) is using ent_text. NOTE: ent_text_allow_script 1 must be set in order for this feature to be active.
- AllowBash( basher, bashee ): called whenever melee bash is used. Returns these values:
- ALLOW_BASH_ALL - normal melee behavior
- ALLOW_BASH_NONE - do nothing at all
- ALLOW_BASH_PUSHONLY - applies physics push but deals no damage (including prevention of insta-kill ambush behavior)
- EmitSoundOnClient( soundName, player ): Play named sound only on the client for the specified player
- EmitSoundOn( soundName, entity ): Play named sound on an entity
- StopSoundOn( soundName, entity ): Stop named sound on an entity
- GetFrameCount(): Returns the engine's current frame count
- GetCurrentFlowDistanceForPlayer( player ):
- TODO: Add a description
- GetCurrentFlowPercentForPlayer( player ):
- TODO: Add a description
- GetFlowDistanceForPosition( position ):
- TODO: Add a description
- GetFlowPercentForPosition( position, flowtype ):
- TODO: Add a description
- GetMaxFlowDistance():
- TODO: Add a description
- GetAverageSurvivorFlowDistance():
- TODO: Add a description
- DropSpit( location ): drop a spit pool from the specified vector location
- DropFire( location ): drop a fire pool from the specified vector location
- Convars.GetStr( name ): returns the convar as a string. May return null if no such convar.
- Convars.GetFloat( name ): returns the convar as a float. May return null if no such convar.
- Convars.SetValue( name, value ): sets the value of the convar. Supported types are bool, int, float, string.
- Convars.GetClientConvarValue( name, entindex ): returns the convar value for the entindex as a string. Only works with client convars with the FCVAR_USERINFO flag.
Values returned from shutdown functions:
SCRIPT_SHUTDOWN_MANUAL, SCRIPT_SHUTDOWN_ROUND_RESTART, SCRIPT_SHUTDOWN_TEAM_SWAP, SCRIPT_SHUTDOWN_LEVEL_TRANSITION, SCRIPT_SHUTDOWN_EXIT_GAME