This commit is contained in:
Future committed 2023-04-13 17:30:38 +02:00
commit 013c6f3b4d
921 files changed
+337975

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
Line truncated
#namespace Blackboard;
function autoexec main()
{
_InitializeBlackboard();
}
function private _InitializeBlackboard()
{
level.__ai_blackboard = [];
level thread _UpdateEvents();
}
function private _UpdateEvents()
{
waitTime = 1 * .05;
updateMillis = waitTime * 1000;
while ( true )
{
foreach ( eventName, events in level.__ai_blackboard )
{
liveEvents = [];
// Reduce time to live, and only keep events that still have addition time to live.
foreach ( event in events )
{
event.ttl = event.ttl - updateMillis;
if ( event.ttl > 0 )
{
liveEvents[ liveEvents.size ] = event;
}
}
level.__ai_blackboard[ eventName ] = liveEvents;
}
wait waitTime;
}
}
function AddBlackboardEvent( eventName, data, timeToLiveInMillis )
{
/#
assert( IsString( eventName ), "Must pass in an event name when adding an event to the blackboard." );
assert( IsDefined( data ), "Must pass in some type of data to store as an event." );
assert( IsInt( timeToLiveInMillis ) && timeToLiveInMillis > 0,
"Must pass in a positive time to live value for the lifespan of the event." );
#/
event = SpawnStruct();
event.data = data;
event.timestamp = GetTime();
event.ttl = timeToLiveInMillis;
if ( !isdefined( level.__ai_blackboard[eventName] ) ) level.__ai_blackboard[eventName] = []; else if ( !IsArray( level.__ai_blackboard[eventName] ) ) level.__ai_blackboard[eventName] = array( level.__ai_blackboard[eventName] ); level.__ai_blackboard[eventName][level.__ai_blackboard[eventName].size]=event;;
}
function GetBlackboardEvents( eventName )
{
if ( IsDefined( level.__ai_blackboard[eventName] ) )
{
return level.__ai_blackboard[eventName];
}
return [];
}
function RemoveBlackboardEvents( eventName )
{
if ( IsDefined( level.__ai_blackboard[eventName] ) )
{
level.__ai_blackboard[eventName] = undefined;
}
}
+250
View File
@@ -0,0 +1,250 @@
function autoexec main()
{
/#
level.__ai_debugInterface = GetDvarInt( "ai_debugInterface" );
#/
}
function private _CheckValue( archetype, attributeName, value )
{
/#
attribute = level.__ai_interface[ archetype ][ attributeName ];
switch ( attribute[ "type" ] )
{
case "_interface_match":
possibleValues = attribute[ "values" ];
assert( !IsArray( possibleValues ) || IsInArray( possibleValues, value ),
"AI: \"" + value + "\" is not one of the allowed values for attribute \"" +
attributeName + "\"." );
break;
case "_interface_numeric":
maxValue = attribute[ "max_value" ];
minValue = attribute[ "min_value" ];
assert( IsInt( value ) || IsFloat( value ), "AI: Attribute \"" + attributeName +
"\" requires a numeric value and \"" + value + "\" does not qualify." );
assert( ( !IsDefined( maxValue ) && !IsDefined( minValue ) ) ||
( value <= maxValue && value >= minValue ),
"AI: \"" + value + "\" is outside the allowed range of (" + minValue + "," +
maxValue + ")." );
break;
case "_interface_vector":
if ( IsDefined( value ) )
assert( IsVec( value ), "AI: Attribute \"" + attributeName +
"\" requires a vector value and \"" + value + "\" does not qualify." );
break;
default:
assert( "AI: Unknown attribute type of \"" + attribute[ "type" ] +
"\" for attribute \"" + attributeName + "\"." );
break;
}
#/
}
function private _CheckPrerequisites( entity, attribute )
{
/#
assert( IsEntity( entity ), "AI: Must pass an entity to access an attribute." );
assert( IsActor( entity ) || IsVehicle( entity ), "AI: Must pass an actor or vehicle to access an attribute." );
assert( IsString( attribute ), "AI: Must pass in a valid attribute name." );
// In depth debugging, only necessary to debug if an AI or the AI Interface system has been setup properly.
if ( IsDefined( level.__ai_debugInterface ) && level.__ai_debugInterface > 0 )
{
assert( IsArray( entity.__interface ),
"AI: Entity(" + entity.archetype + ") must create an interface before accessing an " +
"attribute, see \"ai::CreateInterfaceForEntity\"." );
assert( IsArray( level.__ai_interface ),
"AI: No attributes have been registered with the AI interface system yet." );
assert( IsArray( level.__ai_interface[ entity.archetype ] ),
"AI: No attributes for archetype \"" + entity.archetype + "\" have been registered." );
assert( IsArray( level.__ai_interface[ entity.archetype ][ attribute ] ),
"AI: Attribute \"" + attribute + "\" has not been registered for archetype \"" +
entity.archetype + "\" yet." );
assert( IsString( level.__ai_interface[ entity.archetype ][ attribute ][ "type" ] ),
"AI: Attribute type is undefined for \"" + attribute + "\"." );
}
#/
}
function private _CheckRegistrationPrerequisites( archetype, attribute, callbackFunction )
{
/#
assert( IsString( archetype ), "AI: \"archetype\" value is mandatory for registration." );
assert( IsString( attribute ), "AI: \"attribute\" value is mandatory for registration." );
assert( !IsDefined( callbackFunction ) || IsFunctionPtr( callbackFunction ),
"AI: \"callbackFunction\" is optional but must be a function pointer if specified." );
#/
}
function private _InitializeLevelInterface( archetype )
{
if ( !IsDefined( level.__ai_interface ) )
{
level.__ai_interface = [];
}
if ( !IsDefined( level.__ai_interface[ archetype ] ) )
{
level.__ai_interface[ archetype ] = [];
}
}
#namespace ai;
function CreateInterfaceForEntity( entity )
{
if ( !IsDefined( entity.__interface ) )
{
entity.__interface = [];
}
}
function GetAiAttribute( entity, attribute )
{
/#
ai_interface::_CheckPrerequisites( entity, attribute );
#/
if ( !IsDefined( entity.__interface[ attribute ] ) )
{
return level.__ai_interface[ entity.archetype ][ attribute ][ "default_value" ];
}
return entity.__interface[ attribute ];
}
function HasAiAttribute( entity, attribute )
{
return
IsDefined( entity ) &&
IsDefined( attribute ) &&
IsDefined( entity.archetype ) &&
IsDefined( level.__ai_interface ) &&
IsDefined( level.__ai_interface[ entity.archetype ] ) &&
IsDefined( level.__ai_interface[ entity.archetype ][ attribute ] );
}
function RegisterMatchedInterface( archetype, attribute, defaultValue, possibleValues, callbackFunction )
{
/#
ai_interface::_CheckRegistrationPrerequisites( archetype, attribute, callbackFunction );
assert( !IsDefined( possibleValues ) || IsArray( possibleValues ),
"AI: \"possibleValues\" is optional but must be an array if specified." );
#/
ai_interface::_InitializeLevelInterface( archetype );
/#
assert( !IsDefined( level.__ai_interface[ archetype ][ attribute ] ),
"AI: \"" + attribute + "\" is already registered for archetype \"" + archetype + "\"" );
#/
level.__ai_interface[ archetype ][ attribute ] = [];
level.__ai_interface[ archetype ][ attribute ][ "callback" ] = callbackFunction;
level.__ai_interface[ archetype ][ attribute ][ "default_value" ] = defaultValue;
level.__ai_interface[ archetype ][ attribute ][ "type" ] = "_interface_match";
level.__ai_interface[ archetype ][ attribute ][ "values" ] = possibleValues;
/#
ai_interface::_CheckValue( archetype, attribute, defaultValue );
#/
}
function RegisterNumericInterface( archetype, attribute, defaultValue, minimum, maximum, callbackFunction )
{
/#
ai_interface::_CheckRegistrationPrerequisites( archetype, attribute, callbackFunction );
assert( !IsDefined( minimum ) || IsInt( minimum ) || IsFloat( minimum ),
"AI: \"minimum\" is optional but must be a numeric if specified." );
assert( !IsDefined( maximum ) || IsInt( maximum ) || IsFloat( maximum ),
"AI: \"maximum\" is optional but must be a numeric if specified." );
assert( ( !IsDefined( minimum ) && !IsDefined( maximum ) ) ||
( IsDefined( minimum ) && IsDefined( maximum ) ),
"AI: Either both a minimum and maximum must be defined or both undefined, not mixed.");
assert( ( !IsDefined( minimum ) && !IsDefined( maximum ) ) ||
( minimum <= maximum ),
"AI: Attribute \"" + attribute + "\" cannot specify a minimum greater than " +
"the attribute's maximum");
#/
ai_interface::_InitializeLevelInterface( archetype );
/#
assert( !IsDefined( level.__ai_interface[ archetype ][ attribute ] ),
"AI: \"" + attribute + "\" is already registered for archetype \"" + archetype + "\"" );
#/
level.__ai_interface[ archetype ][ attribute ] = [];
level.__ai_interface[ archetype ][ attribute ][ "callback" ] = callbackFunction;
level.__ai_interface[ archetype ][ attribute ][ "default_value" ] = defaultValue;
level.__ai_interface[ archetype ][ attribute ][ "max_value" ] = maximum;
level.__ai_interface[ archetype ][ attribute ][ "min_value" ] = minimum;
level.__ai_interface[ archetype ][ attribute ][ "type" ] = "_interface_numeric";
/#
ai_interface::_CheckValue( archetype, attribute, defaultValue );
#/
}
function RegisterVectorInterface( archetype, attribute, defaultValue, callbackFunction )
{
/#
ai_interface::_CheckRegistrationPrerequisites( archetype, attribute, callbackFunction );
#/
ai_interface::_InitializeLevelInterface( archetype );
/#
assert( !IsDefined( level.__ai_interface[ archetype ][ attribute ] ),
"AI: \"" + attribute + "\" is already registered for archetype \"" + archetype + "\"" );
#/
level.__ai_interface[ archetype ][ attribute ] = [];
level.__ai_interface[ archetype ][ attribute ][ "callback" ] = callbackFunction;
level.__ai_interface[ archetype ][ attribute ][ "default_value" ] = defaultValue;
level.__ai_interface[ archetype ][ attribute ][ "type" ] = "_interface_vector";
/#
ai_interface::_CheckValue( archetype, attribute, defaultValue );
#/
}
function SetAiAttribute( entity, attribute, value )
{
/#
ai_interface::_CheckPrerequisites( entity, attribute );
ai_interface::_CheckValue( entity.archetype, attribute, value );
#/
oldValue = entity.__interface[ attribute ];
if ( !IsDefined( oldValue ) )
{
oldValue = level.__ai_interface[ entity.archetype ][ attribute ][ "default_value" ];
}
entity.__interface[ attribute ] = value;
callback = level.__ai_interface[ entity.archetype ][ attribute ][ "callback" ];
if ( IsFunctionPtr( callback ) )
{
[[callback]]( entity, attribute, oldValue, value );
}
}
+324
View File
@@ -0,0 +1,324 @@
#using scripts\shared\ai_shared;
#using scripts\shared\array_shared;
#using scripts\shared\flag_shared;
#using scripts\shared\system_shared;
#using scripts\shared\trigger_shared;
#using scripts\shared\spawner_shared;
#using scripts\shared\util_shared;
Line truncated
#namespace AiSquads;
function autoexec __init__sytem__() { system::register("ai_squads",&__init__,undefined,undefined); }
function __init__()
{
level._squads = [];
actorSpawnerArray = GetActorSpawnerTeamArray( "axis" );
array::run_all( actorSpawnerArray, &spawner::add_spawn_function, &SquadMemberThink );
}
// ------- AI SQUAD -----------//
class Squad
{
var squadLeader;
var squadMembers;
var squadBreadCrumb;
constructor()
{
squadLeader = 0;
squadMembers = [];
squadBreadCrumb = [];
}
function addSquadBreadCrumbs( ai )
{
assert( squadLeader == ai );
if( Distance2DSquared( squadBreadCrumb, ai.origin ) >= 96 * 96 )
{
/# RecordCircle( ai.origin, 4, ( 0, 0, 1 ), "Animscript", ai ); #/
squadBreadCrumb = ai.origin;
}
}
function getSquadBreadCrumb()
{
return squadBreadCrumb;
}
function GetLeader()
{
return squadLeader;
}
function GetMembers()
{
return squadMembers;
}
function AddAIToSquad( ai )
{
if( !IsInArray( squadMembers, ai ) )
{
// TEMP : Turn off other behaviors of the robot, to make sure that he will respect and follow the squad
if( ai.archetype == "robot" )
{
ai ai::set_behavior_attribute( "move_mode", "squadmember" );
}
squadMembers[squadMembers.size] = ai;
}
}
function RemoveAIFromSqaud( ai )
{
if( IsInArray( squadMembers, ai ) )
{
ArrayRemoveValue( squadMembers, ai, false );
if( squadLeader === ai )
{
squadLeader = undefined;
}
}
}
function Think()
{
// update squad leader - pick the first one in the squadMembers list
// remove the squad if no one is left
if( (IsInt(squadLeader) && squadLeader == 0) || !IsDefined( squadLeader ) )
{
if( squadMembers.size > 0 )
{
squadLeader = squadMembers[0];
squadBreadCrumb = squadLeader.origin;
}
else
{
return false;
}
}
return true;
}
}
function private CreateSquad( squadName )
{
level._squads[squadName] = new Squad();
return level._squads[squadName];
}
function private RemoveSquad( squadName )
{
if( IsDefined( level._squads ) && IsDefined( level._squads[squadName] ) )
{
level._squads[squadName] = undefined;
}
}
function private GetSquad( squadName )
{
return level._squads[squadName];
}
function private ThinkSquad( squadName )
{
while(1)
{
if( [[ level._squads[squadName] ]]->Think() )
{
wait 0.5;
}
else
{
RemoveSquad( squadName );
break;
}
}
}
function private SquadMemberDeath()
{
self waittill( "death" );
if( IsDefined( self.squadName ) && IsDefined( level._squads[self.squadName] ) )
{
[[ level._squads[self.squadName] ]]->RemoveAIFromSqaud( self );
}
}
function private SquadMemberThink()
{
self endon("death");
if( !IsDefined( self.script_aisquadname ) )
return;
// wait for other ai systems to initialize
wait 0.5;
// Assign the squad name
self.squadName = self.script_aisquadname;
if( IsDefined( self.squadName ) )
{
// Create squad if it does not exist, then create it
if( !IsDefined( level._squads[self.squadName] ) )
{
squad = CreateSquad( self.squadName );
newSquadCreated = true;
}
else
{
squad = GetSquad( self.squadName );
}
// Add this AI as a member of the squad
[[squad]]->AddAIToSquad( self );
// Handle the death of the sqaudMember and remove him from squad
self thread SquadMemberDeath();
// start ticking this squad
if( ( isdefined( newSquadCreated ) && newSquadCreated ) )
{
level thread ThinkSquad( self.squadName );
}
// Keep updating and following the leader of the squad
while(1)
{
squadLeader = [[ level._squads[self.squadName] ]]->GetLeader();
if( IsDefined( squadLeader ) && !(IsInt(squadLeader) && squadLeader == 0) )
{
if( squadLeader == self )
{
/# RecordEntText( self.squadName + ": LEADER", self, ( 0, 1, 0 ), "Animscript" ); #/
/# RecordEntText( self.squadName + ": LEADER", self, ( 0, 1, 0 ), "Animscript" ); #/
/# RecordCircle( self.origin, 300, ( 1, .5, 0 ), "Animscript", self ); #/
if( IsDefined( self.enemy ) )
{
self SetGoal( self.enemy );
}
[[squad]]->addSquadBreadCrumbs( self );
}
else
{
/# RecordLine( self.origin, squadLeader.origin, ( 0, 1, 0 ), "Animscript", self ); #/
/# RecordEntText( self.squadName+ ": FOLLOWER", self, ( 0, 1, 0 ), "Animscript" ); #/
followPosition = [[squad]]->getSquadBreadCrumb();
followDistSq = Distance2DSquared( self.goalPos, followPosition );
if( IsDefined( squadLeader.enemy ) )
{
if( !IsDefined( self.enemy ) || ( IsDefined( self.enemy ) && self.enemy != squadLeader.enemy ) )
self SetEntityTarget( squadLeader.enemy, 1 );
}
if( IsDefined( self.goalPos ) && followDistSq >= 16 * 16 )
{
if( followDistSq >= 150 * 150 )
{
self ai::set_behavior_attribute( "sprint", true );
}
else
{
self ai::set_behavior_attribute( "sprint", false );
}
self SetGoal( followPosition, true );
}
}
}
wait 1;
}
}
}
// ------- UTILITY -----------//
function isFollowingSquadLeader( ai )
{
if( ai ai::get_behavior_attribute( "move_mode" ) != "squadmember" )
{
return false;
}
squadMember = isSquadMember( ai );
currentSquadLeader = getSquadLeader( ai ) ;
isAISquadLeader = IsDefined( currentSquadLeader ) && currentSquadLeader == ai;
if( squadMember && !isAISquadLeader )
{
return true;
}
return false;
}
function isSquadMember( ai )
{
if( IsDefined( ai.squadName ) )
{
squad = GetSquad( ai.squadName );
if( IsDefined( squad ) )
{
return IsInArray( [[squad]]->GetMembers(), ai );
}
}
return false;
}
function isSquadLeader( ai )
{
if( IsDefined( ai.squadName ) )
{
squad = GetSquad( ai.squadName );
if( IsDefined( squad ) )
{
squadLeader = [[squad]]->GetLeader();
return IsDefined( squadLeader ) && squadLeader == ai;
}
}
return false;
}
function getSquadLeader( ai )
{
if( IsDefined( ai.squadName ) )
{
squad = GetSquad( ai.squadName );
if( IsDefined( squad ) )
{
return [[squad]]->GetLeader();
}
}
return undefined;
}
@@ -0,0 +1,16 @@
#namespace AnimationSelectorTable;
function RegisterAnimationSelectorTableEvaluator( functionName, functionPtr )
{
if ( !IsDefined( level._astevaluatorscriptfunctions ) )
{
level._astevaluatorscriptfunctions = [];
}
functionName = ToLower( functionName );
Assert( IsDefined( functionName ) && IsDefined( functionPtr ) );
Assert( !IsDefined( level._astevaluatorscriptfunctions[functionName] ) );
level._astevaluatorscriptfunctions[functionName] = functionPtr;
}
Whitespace-only changes.
@@ -0,0 +1,90 @@
// ASM RUNNING STATUS FLAGS ------------------------------------------------------//
// TODO - Find a better way to not need to have this hardcoded state name.
// ASM NOTETRACK HANDLER DEFINES -----------------------------------------------//
// ASM AIM/SHOOT NODE DEFINES -------------------------------------------------//
// ASM MOCOMP ATTRIBUTES -----------------------------------------------------//
// NOTERACKS HANDLED BY ASM ---------------------------------------------------//
// AI_ANIM_MODE, DEFINED SAME AS CODE -----------------------------------------//
@@ -0,0 +1,79 @@
Line truncated
// Created and Implemented by Sumeet Jakatdar
#namespace AnimationStateNetwork;
// ------------- AnimationMocomp Initialization ----------- //
function autoexec InitAnimationMocomps()
{
level._AnimationMocomps = [];
}
// ------------- AnimationMocomp Utility ----------- //
function RunAnimationMocomp( mocompName, mocompStatus, asmEntity, mocompAnim, mocompAnimBlendOutTime, mocompDuration )
{
Assert( ( mocompStatus >= 0 && mocompStatus <= 2 ), "ASM - Unsupported MocompStatus " + mocompStatus + " for RunAnimationMocomp." );
Assert( IsDefined( level._AnimationMocomps[mocompName] ), "BT - RegisterAnimationMocomps with " + mocompName + " does not exists." );
if( mocompStatus == 0 )
mocompStatus = "asm_mocomp_start";
else if( mocompStatus == 1 )
mocompStatus = "asm_mocomp_update";
else
mocompStatus = "asm_mocomp_terminate";
animationMocompResult = asmEntity [[level._AnimationMocomps[mocompName][mocompStatus]]]( asmEntity, mocompAnim, mocompAnimBlendOutTime, "", mocompDuration );
return animationMocompResult;
}
// ------------- AnimationMocomp API ----------- //
function RegisterAnimationMocomp( mocompName, startFuncPtr, updateFuncPtr, terminateFuncPtr )
{
mocompName = ToLower( mocompName );
Assert( IsString( mocompName ), "ASM - mocompName for RegisterAnimationMocomps is not a string." );
Assert( !IsDefined(level._AnimationMocomps[mocompName]), "mocomp with name " + mocompName + " is already registered." );
level._AnimationMocomps[mocompName] = array();
// ASM_MOCOMP_START
Assert( IsDefined( startFuncPtr ) && IsFunctionPtr( startFuncPtr ), "BT - RegisterAnimationMocomps must have startFunctionPtr and it must be of type functionPtr." );
level._AnimationMocomps[mocompName]["asm_mocomp_start"] = startFuncPtr;
// ASM_MOCOMP_UPDATE
if( IsDefined( updateFuncPtr ) )
{
Assert( IsFunctionPtr( updateFuncPtr ), "BT - RegisterAnimationMocomps must have updateFuncPtr and it must be of type functionPtr." );
level._AnimationMocomps[mocompName]["asm_mocomp_update"] = updateFuncPtr;
}
else
{
level._AnimationMocomps[mocompName]["asm_mocomp_update"] = &AnimationMocompEmptyFunc;
}
// ASM_MOCOMP_UPDATE
if( IsDefined( terminateFuncPtr ) )
{
Assert( IsFunctionPtr( terminateFuncPtr ), "BT - RegisterAnimationMocomps must have terminateFuncPtr and it must be of type functionPtr." );
level._AnimationMocomps[mocompName]["asm_mocomp_terminate"] = terminateFuncPtr;
}
else
{
level._AnimationMocomps[mocompName]["asm_mocomp_terminate"] = &AnimationMocompEmptyFunc;
}
}
function AnimationMocompEmptyFunc( entity, mocompAnim, mocompAnimBlendOutTime, mocompAnimFlag, mocompDuration )
{
}
@@ -0,0 +1,80 @@
#using scripts\shared\ai\systems\blackboard;
Line truncated
#namespace AnimationStateNetwork;
// ------------- NotatrackHandler Initialization ----------- //
function autoexec InitNotetrackHandler()
{
level._NOTETRACK_HANDLER = [];
}
// ------------- NotetrackHandler internals ----------- //
function private RunNotetrackHandler( entity, notetracks )
{
assert( IsArray( notetracks ) );
for ( index = 0; index < notetracks.size; index++ )
{
HandleNotetrack( entity, notetracks[index] );
}
}
function private HandleNotetrack( entity, notetrack )
{
notetrackHandler = level._NOTETRACK_HANDLER[notetrack];
If( !IsDefined( notetrackHandler ) )
{
return;
}
if( IsFunctionPtr( notetrackHandler ) )
{
// run the notetrackHandler function
[[notetrackHandler]]( entity );
}
else
{
// assume notetrackHandler is setting a blackboard variable for entity
Blackboard::SetBlackBoardAttribute(
entity,
notetrackHandler.blackboardAttributeName,
notetrackHandler.blackBoardValue );
}
}
// ------------- NotatrackHandler Function utility ----------- //
function RegisterNotetrackHandlerFunction( notetrackName, notetrackFuncPtr )
{
Assert(
IsString( notetrackName ),
"NotetrackHandler - notetrackName must be of string type." );
Assert(
IsFunctionPtr( notetrackFuncPtr ),
"NotetrackHandler - notetrackFunc name must be of function pointer type." );
Assert(
!IsDefined( level._NOTETRACK_HANDLER[notetrackName] ),
"NotetrackHandler - notetrackName \"" + notetrackName + "\" is already registered." );
// add the notetrack handler to the global list
level._NOTETRACK_HANDLER[notetrackName] = notetrackFuncPtr;
}
// ------------- BlackBoard NotetrackHandler utility ----------- //
function RegisterBlackboardNotetrackHandler(
notetrackName, blackboardAttributeName, blackBoardValue )
{
notetrackHandler = SpawnStruct();
notetrackHandler.blackboardAttributeName = blackboardAttributeName;
notetrackHandler.blackBoardValue = blackBoardValue;
level._NOTETRACK_HANDLER[notetrackName] = notetrackHandler;
}
@@ -0,0 +1,25 @@
#using scripts\shared\ai\archetype_utility;
Line truncated
#namespace AnimationStateNetworkUtility;
// ------------------- Functions to be updated and maintained after the integration in T7 --------------------------------------//
function RequestState( entity, stateName )
{
assert( IsDefined( entity ) );
entity ASMRequestSubState( stateName );
}
function SearchAnimationMap( entity, aliasName )
{
if ( IsDefined( entity ) && IsDefined( aliasName ) )
{
animationName = entity AnimMappingSearch( IString( aliasName ));
if ( IsDefined( animationName ) )
return FindAnimByName( "generic", animationName );
}
}
+15
View File
@@ -0,0 +1,15 @@
// EMPTY ANIMSCRIPT AS WE DO NOT NEED AI TO RUN ANYTHING HERE
function main()
{
}
function end_script()
{
// This callback will be called when AI will change states from AIS_BEHAVE to AIS_SCRIPTED
// i.e when it will go from behavior state to animscripted state
if( IsDefined( self.___ArchetypeOnAnimscriptedCallback ) )
[[self.___ArchetypeOnAnimscriptedCallback]]( self );
}
+98
View File
@@ -0,0 +1,98 @@
// at 96 units distance, AI will prefer to walk instead of running to it.
// standing offset at covernodes based on the AI pose
// yaws used to check if the AI can see the enemy
// offsets used to check if the AI can see the enemy
// in sec, if AI has not seen enemy for 4 sec, then he will be considered hidden
// maximum time for idle action at cover, ir-respective of the animation length
// grenade throw ranges
// grenade throw modifiers
// Lower bound of time before a person of the same team can throw another grenade
// Upper bound of time before a person of the same team can throw another grenade
// Lower bound of time before a person that finished a scripted animation can throw a grenade
// Upper bound of time before a person that finished a scripted animation can throw a grenade
// Lower bound of time before being able to throw another grenade
// Upper bound of time before being able to throw another grenade
// New grenade throws must land atleast this far from any previous throw, within the GRENADE_THROW_TIME window.
// Don't throw a grenade if you are on team allies and it will land this close to a player (may want to change to any ally)
// Don't throw a grenade if there is a player in laststand this close to the target.
// Melee ranges and timers
// allowed 10 units to account for charge blend
// charge distance is the minimum distance required to even attempt melee
// Arrival
// Distance at which enmey is considered to be nearby
// Cover modes
//Sprint values
//delay between two sprints
//upto one more second delay between sprint
//additional compensation factor
//value between 0 and 100, probability of sprinting
// Cover shoot times
// *********** SUMEET TODO - Merge seeking and exposed reacquiring behavior together. ***********
// Reacquire/ Cover/Locomotion Seek
@@ -0,0 +1,363 @@
// Created and Implemented by Sumeet Jakatdar
Line truncated
#namespace BehaviorStateMachine;
// ------------- Behavior State Machine Script API registration ----------- //
function RegisterBSMScriptAPIInternal( functionName, scriptFunction )
{
if ( !IsDefined( level._bsmscriptfunctions ) )
{
level._bsmscriptfunctions = [];
}
// SUMEET TODO - remove the need of ToLower and have the functionNames defined in .gsh
functionName = ToLower( functionName );
Assert( IsDefined( scriptFunction ) && IsDefined( scriptFunction ), "BT - (RegisterBSMScriptAPI) No functionPtr defined or no functionName defined for BSMScript." );
Assert( !IsDefined( level._bsmscriptfunctions[functionName] ), "BT - (RegisterBSMScriptAPI) functionName is already defined for BSMScript." );
level._bsmscriptfunctions[functionName] = scriptFunction;
}
/*
class BehaviorState
{
var stateName;
var asmStateName;
var initFunc;
var updateFunc;
var terminateFunc;
var connections;
constructor()
{
connections = [];
}
destructor()
{
}
function GetConnections()
{
return connections;
}
}
class BehaviorConnection
{
var connectionName;
var toState;
var conditionFunc;
var endOfStateConnection;
var asmState;
constructor()
{
toState = NULL;
}
destructor()
{
}
}
class BSM
{
var stateMachineName;
var states;
var miscData;
var currentState;
var previousState;
constructor()
{
states = [];
}
destructor()
{
}
function AddState( stateName, asmStateName, initFunc, updateFunc, terminateFunc )
{
assert( !IsDefined( states[stateName] ), "BSM - State is already defined " + stateName );
states[stateName] = new BehaviorState();
states[stateName].initFunc = initFunc;
states[stateName].updateFunc = updateFunc;
states[stateName].terminateFunc = terminateFunc;
states[stateName].asmStateName = asmStateName;
states[stateName].stateName = stateName;
}
function AddConnection( connectionName, fromState, toState, conditionFunc, endOfStateConnection, asmState )
{
assert( IsDefined( states[fromState] ), "BSM - State is not defined " + fromState );
assert( IsDefined( states[toState] ), "BSM - State is not defined " + toState );
assert( !IsDefined( states[fromState].connections[connectionName] ), "BSM - Connection is already defined " + connectionName );
states[fromState].connections[connectionName] = new BehaviorConnection();
states[fromState].connections[connectionName].toState = toState;
states[fromState].connections[connectionName].conditionFunc = conditionFunc;
states[fromState].connections[connectionName].connectionName = connectionName;
states[fromState].connections[connectionName].endOfStateConnection = endOfStateConnection;
states[fromState].connections[connectionName].asmState = asmState;
}
function GetStates()
{
return states;
}
}
function CreateBSM( archetypeName, stateMachineName )
{
if( !IsDefined( level.__BSMs ) )
{
level.__BSMs = [];
}
if( !IsDefined( level.__BSMs[archetypeName] ) )
{
level.__BSMs[archetypeName] = [];
}
assert( !IsDefined( level.__BSMs[archetypeName][stateMachineName] ) );
newStateMachine = new BSM();
newStateMachine.stateMachineName = stateMachineName;
level.__BSMs[archetypeName][stateMachineName] = newStateMachine;
return level.__BSMs[archetypeName][stateMachineName];
}
function StartBSM( entity, stateMachineName )
{
assert( IsDefined( level.__BSMs[entity.archetype][stateMachineName] ) );
stateMachine = level.__BSMs[entity.archetype][stateMachineName];
assert( !IsDefined( stateMachine.currentState ) );
assert( !IsDefined( stateMachine.previousState ) );
// go through the states, find the first fit
foreach ( state in stateMachine.states )
{
if( !IsDefined( state.initFunc ) )
{
if( IsDefined( state.asmStateName ) )
{
newState = state;
break;
}
}
else
{
validState = [[state.initFunc]]( entity );
if( validState )
{
// found one state which has a valid ASM, set it as a current substate
if( IsDefined( state.asmStateName ) )
{
newState = state;
break;
}
}
}
}
if( IsDefined( newState ) )
{
stateMachine.currentState = newState;
AnimationStateNetworkUtility::RequestState( entity, newState.asmStateName );
return BHTN_RUNNING;
}
return BHTN_SUCCESS;
}
function UpdateBSM( entity, stateMachineName )
{
assert( IsDefined( level.__BSMs[entity.archetype][stateMachineName] ) );
stateMachine = level.__BSMs[entity.archetype][stateMachineName];
assert( IsDefined( stateMachine.currentState ) );
// run the update function for the current state
currentStateUpdatedResult = true;
if( IsDefined( stateMachine.currentState.updateFunc ) )
{
currentStateUpdatedResult = [[stateMachine.currentState.updateFunc]]( entity );
}
// We have a valid running state, go through all the connections ( if there are any )
connections = [[stateMachine.currentState]]->GetConnections();
if( !connections.size )
{
if( entity ASMGetStatus() == ASM_STATE_COMPLETE )
{
TerminateBSM( entity, stateMachineName );
return BHTN_SUCCESS;
}
}
else
{
nextValidState = undefined;
foreach ( connection in connections )
{
// execute endOfStateConnection only when current state is complete or is not valid anymore
if( IS_TRUE( connection.endOfStateConnection )
&& ( entity ASMGetStatus() != ASM_STATE_COMPLETE || !currentStateUpdatedResult )
)
{
/#RecordEntText( "EndOfStateConnection :" + connection.connectionName, entity, RED, "Animscript" );#/
continue;
}
if( IsDefined( connection.conditionFunc ) )
{
// check if the connection is valid
validConnection = [[connection.conditionFunc]]( entity );
if( !IsDefined( validConnection ) )
validConnection = true;
// Connection is valid, check if the toState has any condition associated with it, and execute it
if( validConnection )
{
/#RecordEntText( "Connection :" + connection.connectionName, entity, GREEN, "Animscript" );#/
if( !IsDefined( stateMachine.states[connection.toState].initFunc ) )
{
nextValidState = stateMachine.states[connection.toState];
break;
}
else
{
validToState = [[stateMachine.states[connection.toState].initFunc]]( entity );
if( validToState )
{
nextValidState = stateMachine.states[connection.toState];
break;
}
else
{
/#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, RED, "Animscript" );#/
}
}
}
else
{
/#RecordEntText( "Connection :" + connection.connectionName, entity, RED, "Animscript" );#/
}
}
else
{
/#RecordEntText( "Connection :" + connection.connectionName, entity, GREEN, "Animscript" );#/
if( !IsDefined( stateMachine.states[connection.toState].initFunc ) )
{
nextValidState = stateMachine.states[connection.toState];
break;
}
else
{
validToState = [[stateMachine.states[connection.toState].initFunc]]( entity );
if( validToState )
{
nextValidState = stateMachine.states[connection.toState];
break;
}
else
{
/#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, RED, "Animscript" );#/
}
}
}
}
// found a validNextState with ValidConnection, go there now
if( IsDefined( nextValidState ) )
{
// found a new valid state, execute terminateFunc of the current state
if( IsDefined( stateMachine.currentState.terminateFunc ) )
{
[[stateMachine.currentState.terminateFunc]]( entity );
}
/#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, GREEN, "Animscript" );#/
stateMachine.previousState = stateMachine.currentState;
stateMachine.currentState = nextValidState;
assert( IsDefined( nextValidState.asmStateName ) );
AnimationStateNetworkUtility::RequestState( entity, nextValidState.asmStateName );
return BHTN_RUNNING;
}
else
{
/#RecordEntText( "ToState :" + stateMachine.states[connection.toState].stateName, entity, RED, "Animscript" );#/
}
}
// if there is no better connection, just update the current substate
if( entity ASMGetStatus() == ASM_STATE_COMPLETE )
{
TerminateBSM( entity, stateMachineName );
return BHTN_SUCCESS;
}
else
{
/#RecordEntText( "CurrentState :" + stateMachine.currentState.stateName, entity, BLUE, "Animscript" );#/
}
return BHTN_RUNNING;
}
function TerminateBSM( entity, stateMachineName )
{
assert( IsDefined( level.__BSMs[entity.archetype][stateMachineName] ) );
stateMachine = level.__BSMs[entity.archetype][stateMachineName];
// state machine is terminating and returning control to behavior tree, call terminateFunc of the current state
if( IsDefined( stateMachine.currentState ) && IsDefined( stateMachine.currentState.terminateFunc ) )
{
[[stateMachine.currentState.terminateFunc]]( entity );
}
stateMachine.currentState = undefined;
stateMachine.previousState = undefined;
return BHTN_SUCCESS;
}
*/
@@ -0,0 +1,19 @@
// These enums must match with StateMachineStatus in behavior_state_machine_db.h
// HACK - just to match status codes with the behavior tree, ease of use!
// HACK - just to match status codes with the behavior tree, ease of use!
+17
View File
@@ -0,0 +1,17 @@
// return status for BehaviorTreeNodes tick functions - BHTN_STATUS, order is important, BHTN_RUNNING needs to be highest
@@ -0,0 +1,71 @@
#namespace BehaviorTreeNetwork;
// ------------- BehaviorTreeScript ----------- //
function RegisterBehaviorTreeScriptAPIInternal( functionName, functionPtr )
{
if ( !IsDefined( level._BehaviorTreeScriptFunctions ) )
{
level._BehaviorTreeScriptFunctions = [];
}
// SUMEET TODO - remove the need of ToLower and have the functionNames defined in .gsh
functionName = ToLower( functionName );
Assert( IsDefined( functionName ) && IsDefined( functionPtr ), "BT - (RegisterBehaviorTreeScriptAPI) No functionPtr defined or no functionName defined for BehaviorTreeScript." );
Assert( !IsDefined( level._BehaviorTreeScriptFunctions[functionName] ), "BT - (RegisterBehaviorTreeScriptAPI) functionName is already defined for BehaviorTreeScript." );
level._BehaviorTreeScriptFunctions[functionName] = functionPtr;
}
// ------------- BehaviorTreeAction ----------- //
function RegisterBehaviorTreeActionInternal( actionName, startFuncPtr, updateFuncPtr, terminateFuncPtr )
{
if ( !IsDefined( level._BehaviorTreeActions ) )
{
level._BehaviorTreeActions = [];
}
actionName = ToLower( actionName );
Assert( IsString( actionName ), "BT - actionName for RegisterBehaviorTreeActionInternal is not a string." );
Assert( !IsDefined(level._BehaviorTreeActions[actionName]), "BT - (RegisterBehaviorTreeActionInternal) actionName " + actionName + " is already registered." );
level._BehaviorTreeActions[actionName] = array();
// BHTN_ACTION_START
if( IsDefined( startFuncPtr ) )
{
Assert( IsFunctionPtr( startFuncPtr ), "BT - BehaviorTreeAction startFuncPtr must be of type functionPtr." );
level._BehaviorTreeActions[actionName]["bhtn_action_start"] = startFuncPtr;
}
// BHTN_ACTION_UPDATE
if( IsDefined( updateFuncPtr ) )
{
Assert( IsFunctionPtr( updateFuncPtr ), "BT - BehaviorTreeAction updateFuncPtr must be of type functionPtr." );
level._BehaviorTreeActions[actionName]["bhtn_action_update"] = updateFuncPtr;
}
// BHTN_ACTION_TERMINATE
if( IsDefined( terminateFuncPtr ) )
{
Assert( IsFunctionPtr( terminateFuncPtr ), "BT - BehaviorTreeAction terminateFuncPtr must be of type functionPtr." );
level._BehaviorTreeActions[actionName]["bhtn_action_terminate"] = terminateFuncPtr;
}
}
// ------------- utility----------- //
#namespace BehaviorTreeNetworkUtility;
function RegisterBehaviorTreeScriptAPI( functionName, functionPtr )
{
BehaviorTreeNetwork::RegisterBehaviorTreeScriptAPIInternal( functionName, functionPtr );
}
function RegisterBehaviorTreeAction( actionName, startFuncPtr, updateFuncPtr, terminateFuncPtr )
{
BehaviorTreeNetwork::RegisterBehaviorTreeActionInternal( actionName, startFuncPtr, updateFuncPtr, terminateFuncPtr );
}
+89
View File
@@ -0,0 +1,89 @@
Line truncated
#namespace Blackboard;
function RegisterBlackBoardAttribute( entity, attributeName, defaultAttributeValue, getterFunction )
{
Assert( IsDefined( entity.__blackboard ), "Blackboard - No blackboard has been created for this entity." );
Assert( !IsDefined( entity.__blackboard[attributeName] ), "BlackBoard - Attribute " + attributeName + " already registered." );
if( IsDefined( getterFunction ) )
{
// When a blackboard is registered with getterFunction, then every single time its requested, it will
// execure getterFunction and return the value.
Assert( IsFunctionPtr( getterFunction ) );
entity.__blackboard[attributeName] = getterFunction;
}
else
{
// If there is no getterFunction, then it will just store the default value
if( !IsDefined( defaultAttributeValue ) )
{
defaultAttributeValue = undefined;
}
entity.__blackboard[attributeName] = defaultAttributeValue;
}
}
function GetBlackBoardAttribute( entity, attributeName )
{
if( IsFunctionPtr( entity.__blackboard[attributeName] ) )
{
getterFunction = entity.__blackboard[attributeName];
attributeValue = entity [[getterFunction]]();
/#
if( IsActor( entity ) )
entity updatetrackedblackboardattribute( attributeName );
#/
return attributeValue;
}
else
{
/#
if( IsActor( entity ) )
entity updatetrackedblackboardattribute( attributeName );
#/
return entity.__blackboard[attributeName];
}
}
function SetBlackBoardAttribute( entity, attributeName, attributeValue )
{
if ( IsDefined( entity.__blackboard[attributeName] ) )
{
if ( !IsDefined( attributeValue ) && IsFunctionPtr( entity.__blackboard[attributeName] ) )
{
// Attempts to clear attributes with getter functions is meaningless.
return;
}
Assert( !IsFunctionPtr( entity.__blackboard[attributeName] ), "Blackboard - Attribute value can not be set explicitly, as it has a getterFunction." );
}
entity.__blackboard[attributeName] = attributeValue;
/#
if ( IsActor( entity ) )
entity updatetrackedblackboardattribute( attributeName );
#/
}
function CreateBlackBoardForEntity( entity )
{
if( !IsDefined( entity.__blackboard ) )
{
entity.__blackboard = [];
}
if( !IsDefined( level._setBlackboardAttributeFunc ) )
{
level._setBlackboardAttributeFunc = &SetBlackBoardAttribute;
}
}
+424
View File
@@ -0,0 +1,424 @@
// -------------------------------------------- BLACKBOARD ATTRIBUTES --------------------------//
// Attribute names, keep it lowercase. "_" before the attribute name indicates that its being used by both AST and BT.
// Desired values should have DESIRED and "_desired_" in their definition. There should be a associated BB attribute existing already
// Eg. ATTRIBUTE and DESIRED_ATTRIBUTE and "_attribute" and "_desired_attribute". Try to keep them together please. :)
// Robots
//Zombies
//contains all the possible knockdown animations
//only has the quick to the ground knockdown animations
// will be set to YES if AI will look good if he reacts with flanking
// -------------------------------------------- BLACKBOARD DEFINES --------------------//
// -------------------------------------------- AIM RELATED --------------------------//
// -------------------------------------------- STANCES -------------------------------//
// -------------------------------------------- WEAPONS -------------------------------//
// -------------------------------------------- DAMAGE -------------------------------//
// -------------------------------------------- TACTICAL WALK -------------------------//
// -------------------------------------------- CORNER PREDICTION ----------------------//
// -------------------------------------------- ARRIVAL -------------------------------//
// -------------------------------------------- LOCMOTION PAIN ------------------------//
// -------------------------------------------- DAMAGE DIRECTION ------------------------//
// -------------------------------------------- SPECIAL DEATH ------------------------//
// -------------------------------------------- AWARENESS ------------------------//
// -------------------------------- STAIRS ------------------------//
// skip4 Not implemented yet
// -------------------------------------------- HITLOCS ------------------------//
// same as hitLocation_t enum in q_shared.h
+414
View File
@@ -0,0 +1,414 @@
#using scripts\shared\system_shared;
#using scripts\shared\ai\archetype_utility;
Line truncated
#namespace as_debug;
/#
function autoexec __init__sytem__() { system::register("as_debug",&__init__,undefined,undefined); }
#/
/#
function __init__()
{
level thread debugDvars();
}
function debugDvars()
{
while( 1 )
{
if( GetDvarInt( "debug_ai_clear_corpses", 0 ) )
{
delete_all_ai_corpses();
}
{wait(.05);};
}
}
function isDebugOn()
{
return ( (GetDvarInt("animDebug") == 1) || ( isdefined (anim.debugEnt) && anim.debugEnt == self ) );
}
function drawDebugLineInternal(fromPoint, toPoint, color, durationFrames)
{
//println ("Drawing line, color "+color[0]+","+color[1]+","+color[2]);
//player = getent("player", "classname" );
//println ( "Point1 : "+fromPoint+", Point2: "+toPoint+", player: "+player.origin );
for (i=0;i<durationFrames;i++)
{
line (fromPoint, toPoint, color);
{wait(.05);};
}
}
function drawDebugLine(fromPoint, toPoint, color, durationFrames)
{
if (isDebugOn())
{
thread drawDebugLineInternal(fromPoint, toPoint, color, durationFrames);
}
}
function debugLine(fromPoint, toPoint, color, durationFrames)
{
for (i=0;i<durationFrames*20;i++)
{
line (fromPoint, toPoint, color);
{wait(.05);};
}
}
function drawDebugCross(atPoint, radius, color, durationFrames)
{
atPoint_high = atPoint + ( 0, 0, radius );
atPoint_low = atPoint + ( 0, 0, -1*radius );
atPoint_left = atPoint + ( 0, radius, 0 );
atPoint_right = atPoint + ( 0, -1*radius, 0 );
atPoint_forward = atPoint + ( radius, 0, 0 );
atPoint_back = atPoint + (-1*radius, 0, 0 );
thread debugLine(atPoint_high, atPoint_low, color, durationFrames);
thread debugLine(atPoint_left, atPoint_right, color, durationFrames);
thread debugLine(atPoint_forward, atPoint_back, color, durationFrames);
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
///////////////////////////////////// BEGIN ANIMSCRIPT STATE DEBUGGING /////////////////////////////////////////
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
function UpdateDebugInfo()
{
self endon("death");
self.debugInfo = SpawnStruct();
self.debugInfo.enabled = GetDvarint( "ai_debugAnimscript") > 0;
debugClearState();
while(1)
{
// the waittillframeend causes a random crash in the VM
//waittillframeend;
{wait(.05);};
UpdateDebugInfoInternal();
{wait(.05);};
}
}
function UpdateDebugInfoInternal()
{
if( isdefined(anim.debugEnt) && (anim.debugEnt==self) )
{
doInfo = true;
}
else
{
doInfo = GetDvarint( "ai_debugAnimscript") > 0;
// check if there's a selected ent
if( doInfo )
{
ai_entNum = GetDvarint( "ai_debugEntIndex");
if( ai_entNum > -1 && ai_entNum != self getEntityNumber() )
{
doInfo = false;
}
}
// clear everything out if it was just switched on to make sure we start clean
if( !self.debugInfo.enabled && doInfo )
{
self.debugInfo.shouldClearOnAnimscriptChange = true;
}
self.debugInfo.enabled = doInfo;
}
}
function drawDebugEntText( text, ent, color, channel )
{
assert( isdefined(ent) );
if( !GetDvarint( "recorder_enableRec") )
{
if( !isdefined(ent.debugAnimScriptTime) || GetTime() > ent.debugAnimScriptTime )
{
ent.debugAnimScriptLevel = 0;
ent.debugAnimScriptTime = GetTime();
}
indentLevel = VectorScale( (0,0,-10), ent.debugAnimScriptLevel );
print3d( self.origin + (0,0,70) + indentLevel, text, color );
ent.debugAnimScriptLevel++;
}
else
{
recordEntText( text, ent, color, channel );
}
}
function debugPushState(stateName, extraInfo)
{
if( !GetDvarint( "ai_debugAnimscript") )
{
return;
}
// don't do anything if it's not the selected ent
ai_entNum = GetDvarint( "ai_debugEntIndex");
if( ai_entNum > -1 && ai_entNum != self getEntityNumber() )
{
return;
}
assert( isdefined(self.debugInfo.states) );
assert( isdefined(stateName) );
//recordEntText( "push: " + stateName, self, GREEN, "Animscript" );
state = SpawnStruct();
state.stateName = stateName;
state.stateLevel = self.debugInfo.stateLevel;
state.stateTime = GetTime();
state.stateValid = true;
self.debugInfo.stateLevel++;
if( isdefined(extraInfo) )
{
state.extraInfo = extraInfo + " ";
}
self.debugInfo.states[ self.debugInfo.states.size ] = state;
}
function debugAddStateInfo(stateName, extraInfo)
{
if( !GetDvarint( "ai_debugAnimscript") )
{
return;
}
// don't do anything if it's not the selected ent
ai_entNum = GetDvarint( "ai_debugEntIndex");
if( ai_entNum > -1 && ai_entNum != self getEntityNumber() )
{
return;
}
assert( isdefined(self.debugInfo.states) );
// find the first matching state from bottom
if( isdefined(stateName) )
{
for( i = self.debugInfo.states.size - 1; i >= 0; i-- )
{
assert( isdefined( self.debugInfo.states[i] ) );
if( self.debugInfo.states[i].stateName == stateName )
{
if( !isdefined(self.debugInfo.states[i].extraInfo) )
{
self.debugInfo.states[i].extraInfo = "";
}
self.debugInfo.states[i].extraInfo += extraInfo + " ";
break;
}
}
}
else if( self.debugInfo.states.size > 0 )
{
// add to the last one
lastIndex = self.debugInfo.states.size - 1;
assert( isdefined(self.debugInfo.states[lastIndex]) );
if( !isdefined(self.debugInfo.states[lastIndex].extraInfo) )
{
self.debugInfo.states[lastIndex].extraInfo = "";
}
self.debugInfo.states[lastIndex].extraInfo += extraInfo + " ";
}
}
function debugPopState(stateName, exitReason)
{
if( !GetDvarint( "ai_debugAnimscript") || self.debugInfo.states.size <= 0 )
{
return;
}
// don't do anything if it's not the selected ent
ai_entNum = GetDvarint( "ai_debugEntIndex");
if(!isdefined(self)|| !IsAlive(self))
{
return;
}
if( ai_entNum > -1 && ai_entNum != self getEntityNumber() )
{
return;
}
assert( isdefined(self.debugInfo.states) );
if( isdefined(stateName) )
{
//recordEntText( "pop: " + stateName, self, RED, "Animscript" );
// remove elements at and after stateName
for( i=0; i < self.debugInfo.states.size; i++ )
{
if( self.debugInfo.states[i].stateName == stateName && self.debugInfo.states[i].stateValid )
{
self.debugInfo.states[i].stateValid = false;
self.debugInfo.states[i].exitReason = exitReason;
self.debugInfo.stateLevel = self.debugInfo.states[i].stateLevel;
// invalidate all states below this one
for( j=i+1; j < self.debugInfo.states.size && self.debugInfo.states[j].stateLevel > self.debugInfo.states[i].stateLevel; j++ )
{
self.debugInfo.states[j].stateValid = false;
}
break;
}
}
}
else
{
//recordEntText( "pop", self, RED, "Animscript" );
// remove the last element
for( i = self.debugInfo.states.size - 1; i >= 0; i-- )
{
if( self.debugInfo.states[ i ].stateValid )
{
self.debugInfo.states[ i ].stateValid = false;
self.debugInfo.states[ i ].exitReason = exitReason;
self.debugInfo.stateLevel--;
break;
}
}
}
}
function debugClearState()
{
self.debugInfo.states = [];
self.debugInfo.stateLevel = 0;
self.debugInfo.shouldClearOnAnimscriptChange = false;
}
function debugShouldClearState()
{
if( isdefined(self.debugInfo) && isdefined(self.debugInfo.shouldClearOnAnimscriptChange) && self.debugInfo.shouldClearOnAnimscriptChange )
{
return true;
}
return false;
}
function debugCleanStateStack()
{
newArray = [];
for( i=0; i < self.debugInfo.states.size; i++ )
{
if( self.debugInfo.states[i].stateValid )
{
newArray[ newArray.size ] = self.debugInfo.states[i];
}
}
self.debugInfo.states = newArray;
}
function indent(depth)
{
indent = "";
for( i=0; i < depth; i++ )
{
indent += " ";
}
return indent;
}
function debugDrawWeightedPoints( entity, points, weights )
{
lowestValue = 0;
highestValue = 0;
for (index = 0; index < points.size; index++)
{
if ( weights[index] < lowestValue )
{
lowestValue = weights[index];
}
if ( weights[index] > highestValue )
{
highestValue = weights[index];
}
}
for (index = 0; index < points.size; index++)
{
debugDrawWeightedPoint(entity, points[index], weights[index],lowestValue,highestValue);
}
}
function debugDrawWeightedPoint( entity, point, weight, lowestValue, highestValue )
{
deltaValue = highestValue - lowestValue;
halfDeltaValue = deltaValue / 2.0;
midValue = lowestValue + deltaValue / 2.0;
if ( halfDeltaValue == 0 )
{
halfDeltaValue = 1;
}
if ( weight <= midValue )
{
// Bad places.
redColor = 1 - abs( ( weight - lowestValue ) / halfDeltaValue );
RecordCircle( point, 2, (redColor,0,0), "Script", entity );
}
else
{
// Good places.
greenColor = 1 - abs( ( highestValue - weight ) / halfDeltaValue );
RecordCircle( point, 2, (0,greenColor,0), "Script", entity );
}
}
function delete_all_ai_corpses()
{
SetDvar( "debug_ai_clear_corpses", 0 );
corpses = GetCorpseArray();
foreach( corpse in corpses )
{
if( IsActorCorpse( corpse ) )
{
corpse delete();
}
}
}
#/
@@ -0,0 +1,167 @@
#using scripts\codescripts\struct;
#using scripts\shared\ai\systems\gib;
#using scripts\shared\clientfield_shared;
Line truncated
function autoexec main()
{
clientfield::register(
"actor",
"destructible_character_state",
1,
21,
"int",
&DestructClientUtils::_DestructHandler,
!true,
!true);
destructibles = struct::get_script_bundles( "destructiblecharacterdef" );
processedBundles = [];
// Process each destructible bundle to allow quick access to information in the future.
foreach ( destructibleName, destructible in destructibles )
{
destructBundle = SpawnStruct();
destructBundle.pieceCount = destructible.pieceCount;
destructBundle.pieces = [];
destructBundle.name = destructibleName;
for ( index = 1; index <= destructBundle.pieceCount; index++ )
{
pieceStruct = SpawnStruct();
pieceStruct.gibmodel = GetStructField( destructible, "piece" + index + "_gibmodel" );
pieceStruct.gibtag = GetStructField( destructible, "piece" + index + "_gibtag" );
pieceStruct.gibfx = GetStructField( destructible, "piece" + index + "_gibfx" );
pieceStruct.gibfxtag = GetStructField( destructible, "piece" + index + "_gibeffecttag" );
pieceStruct.gibdynentfx = GetStructField( destructible, "piece" + index + "_gibdynentfx" );
pieceStruct.gibsound = GetStructField( destructible, "piece" + index + "_gibsound" );
pieceStruct.hitlocation = GetStructField( destructible, "piece" + index + "_hitlocation" );
pieceStruct.hidetag = GetStructField( destructible, "piece" + index + "_hidetag" );
pieceStruct.detachmodel = GetStructField( destructible, "piece" + index + "_detachmodel" );
destructBundle.pieces[ destructBundle.pieces.size ] = pieceStruct;
}
processedBundles[ destructibleName ] = destructBundle;
}
// Replaces all destructible character define bundles with their processed form to free unncessary script variables.
level.scriptbundles[ "destructiblecharacterdef" ] = processedBundles;
}
#namespace DestructClientUtils;
function private _DestructHandler( localClientNum, oldValue, newValue, bNewEnt, bInitialSnap, fieldName, wasDemoJump )
{
entity = self;
destructFlags = (oldValue ^ newValue);
shouldSpawnGibs = (newValue & 1);
// Don't use the old clientfield value for new entities.
if ( bNewEnt )
{
destructFlags = (0 ^ newValue);
}
if ( !IsDefined(entity.destructibledef) )
{
return;
}
// Only look at destructible pieces, skip toggle bit.
currentDestructFlag = (1 << 1);
pieceNumber = 1;
// Handles any number of simultaneous gibbings.
while ( destructFlags >= currentDestructFlag )
{
if ( destructFlags & currentDestructFlag )
{
_DestructPiece( localClientNum, entity, pieceNumber, shouldSpawnGibs );
}
currentDestructFlag = currentDestructFlag << 1;
pieceNumber++;
}
entity._destruct_state = newValue;
}
function private _DestructPiece( localClientNum, entity, pieceNumber, shouldSpawnGibs )
{
if ( !IsDefined(entity.destructibledef) )
{
return;
}
destructBundle = struct::get_script_bundle( "destructiblecharacterdef", entity.destructibledef );
piece = destructBundle.pieces[ pieceNumber - 1 ];
if ( IsDefined( piece ) )
{
if ( shouldSpawnGibs )
{
GibClientUtils::_PlayGibFX( localClientNum, entity, piece.gibfx, piece.gibfxtag );
entity thread GibClientUtils::_GibPiece( localClientNum, entity, piece.gibmodel, piece.gibtag, piece.gibdynentfx );
GibClientUtils::_PlayGibSound( localClientNum, entity, piece.gibsound );
}
_HandleDestructCallbacks( localClientNum, entity, pieceNumber );
}
}
function private _GetDestructState( localClientNum, entity )
{
if ( IsDefined( entity._destruct_state ) )
{
return entity._destruct_state;
}
return 0;
}
function private _HandleDestructCallbacks( localClientNum, entity, pieceNumber )
{
if ( IsDefined( entity._destructCallbacks ) &&
IsDefined( entity._destructCallbacks[ pieceNumber ] ) )
{
foreach ( callback in entity._destructCallbacks[ pieceNumber ] )
{
if ( IsFunctionPtr( callback ) )
{
[[callback]]( localClientNum, entity, pieceNumber );
}
}
}
}
function AddDestructPieceCallback( localClientNum, entity, pieceNumber, callbackFunction )
{
assert( IsFunctionPtr( callbackFunction ) );
if ( !IsDefined( entity._destructCallbacks ) )
{
entity._destructCallbacks = [];
}
if ( !IsDefined( entity._destructCallbacks[ pieceNumber ] ) )
{
entity._destructCallbacks[ pieceNumber ] = [];
}
destructCallbacks = entity._destructCallbacks[ pieceNumber ];
destructCallbacks[ destructCallbacks.size ] = callbackFunction;
entity._destructCallbacks[ pieceNumber ] = destructCallbacks;
}
function IsPieceDestructed( localClientNum, entity, pieceNumber )
{
return (_GetDestructState( localClientNum, entity ) & (1 << pieceNumber));
}
// end #namespace DestructClientUtils;
@@ -0,0 +1,313 @@
#using scripts\codescripts\struct;
#using scripts\shared\clientfield_shared;
#using scripts\shared\math_shared;
#using scripts\shared\array_shared;
Line truncated
function autoexec main()
{
clientfield::register(
"actor",
"destructible_character_state",
1,
21,
"int" );
destructibles = struct::get_script_bundles( "destructiblecharacterdef" );
processedBundles = [];
// Process each destructible bundle to allow quick access to information in the future.
foreach ( destructibleName, destructible in destructibles )
{
destructBundle = SpawnStruct();
destructBundle.pieceCount = destructible.pieceCount;
destructBundle.pieces = [];
destructBundle.name = destructibleName;
for ( index = 1; index <= destructBundle.pieceCount; index++ )
{
pieceStruct = SpawnStruct();
pieceStruct.gibmodel = GetStructField( destructible, "piece" + index + "_gibmodel" );
pieceStruct.gibtag = GetStructField( destructible, "piece" + index + "_gibtag" );
pieceStruct.gibfx = GetStructField( destructible, "piece" + index + "_gibfx" );
pieceStruct.gibfxtag = GetStructField( destructible, "piece" + index + "_gibeffecttag" );
pieceStruct.gibdynentfx = GetStructField( destructible, "piece" + index + "_gibdynentfx" );
pieceStruct.gibsound = GetStructField( destructible, "piece" + index + "_gibsound" );
pieceStruct.hitlocation = GetStructField( destructible, "piece" + index + "_hitlocation" );
pieceStruct.hidetag = GetStructField( destructible, "piece" + index + "_hidetag" );
pieceStruct.detachmodel = GetStructField( destructible, "piece" + index + "_detachmodel" );
destructBundle.pieces[ destructBundle.pieces.size ] = pieceStruct;
}
processedBundles[ destructibleName ] = destructBundle;
}
// Replaces all destructible character define bundles with their processed form to free unncessary script variables.
level.scriptbundles[ "destructiblecharacterdef" ] = processedBundles;
}
#namespace DestructServerUtils;
function private _GetDestructState( entity )
{
if ( IsDefined( entity._destruct_state ) )
{
return entity._destruct_state;
}
return 0;
}
function private _SetDestructed( entity, destructFlag )
{
entity._destruct_state = (_GetDestructState( entity ) | destructFlag);
entity clientfield::set( "destructible_character_state", entity._destruct_state );
}
function CopyDestructState( originalEntity, newEntity )
{
newEntity._destruct_state = _GetDestructState( originalEntity );
ToggleSpawnGibs( newEntity, false );
ReapplyDestructedPieces( newEntity );
}
function DestructHitLocPieces( entity, hitLoc )
{
if ( IsDefined(entity.destructibledef) )
{
destructBundle = struct::get_script_bundle( "destructiblecharacterdef", entity.destructibledef );
for ( index = 1; index <= destructBundle.pieces.size; index++ )
{
piece = destructBundle.pieces[ index - 1 ];
if ( IsDefined( piece.hitlocation ) && piece.hitlocation == hitLoc )
{
DestructPiece( entity, index );
}
}
}
}
function DestructLeftArmPieces( entity )
{
DestructHitLocPieces( entity, "left_arm_upper" );
DestructHitLocPieces( entity, "left_arm_lower" );
DestructHitLocPieces( entity, "left_hand" );
}
function DestructLeftLegPieces( entity )
{
DestructHitLocPieces( entity, "left_leg_upper" );
DestructHitLocPieces( entity, "left_leg_lower" );
DestructHitLocPieces( entity, "left_foot" );
}
function DestructPiece( entity, pieceNumber )
{
/# assert( (1 <= pieceNumber && pieceNumber <= 20) ); #/
if ( IsDestructed( entity, pieceNumber ) )
{
return;
}
_SetDestructed( entity, (1 << pieceNumber) );
if ( !IsDefined(entity.destructibledef) )
{
return;
}
destructBundle = struct::get_script_bundle( "destructiblecharacterdef", entity.destructibledef );
piece = destructBundle.pieces[ pieceNumber - 1 ];
if ( IsDefined( piece.hidetag ) && entity HasPart( piece.hidetag ) )
{
entity HidePart( piece.hidetag );
}
if ( IsDefined( piece.detachmodel ) )
{
entity Detach( piece.detachmodel, "" );
}
}
// Destruct a specified number of pieces
// Use 0 to destruct all pieces
function DestructNumberRandomPieces( entity, num_pieces_to_destruct=0 )
{
// Array containing our desctructible pieces.
destructible_pieces_list = [];
// Get the total number of breakable pieces we have
destructablePieces = GetPieceCount( entity );
if (num_pieces_to_destruct == 0)
{
num_pieces_to_destruct = destructablePieces;
}
// Build the list of pieces
for ( i = 0; i < destructablePieces; i++ )
{
destructible_pieces_list[i] = i + 1;
}
// Shuffle the list of pieces so we break off pieces at random
destructible_pieces_list = array::randomize(destructible_pieces_list);
// Go through the randomized list breaking off pieces until we either run out of pieces or broke off all the pieces we needed to.
foreach(piece in destructible_pieces_list)
{
if(!IsDestructed(entity,piece))
{
DestructPiece(entity,piece);
num_pieces_to_destruct--;
if(num_pieces_to_destruct == 0 )
break;
}
}
}
function DestructRandomPieces( entity)
{
destructPieces = GetPieceCount( entity );
for ( index = 0; index < destructPieces; index++ )
{
if ( math::cointoss() )
{
DestructPiece( entity, index + 1 );
}
}
}
function DestructRightArmPieces( entity )
{
DestructHitLocPieces( entity, "right_arm_upper" );
DestructHitLocPieces( entity, "right_arm_lower" );
DestructHitLocPieces( entity, "right_hand" );
}
function DestructRightLegPieces( entity )
{
DestructHitLocPieces( entity, "right_leg_upper" );
DestructHitLocPieces( entity, "right_leg_lower" );
DestructHitLocPieces( entity, "right_foot" );
}
function GetPieceCount( entity )
{
if ( IsDefined(entity.destructibledef) )
{
destructBundle = struct::get_script_bundle( "destructiblecharacterdef", entity.destructibledef );
if ( IsDefined( destructBundle ) )
{
return destructBundle.piececount;
}
}
return 0;
}
function HandleDamage(
eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, psOffsetTime, boneIndex, modelIndex )
{
entity = self;
if ( ( isdefined( entity.skipDeath ) && entity.skipDeath ) )
{
return iDamage;
}
if ( ( isdefined( entity.skipGibs ) && entity.skipGibs ) )
{
return iDamage;
}
ToggleSpawnGibs( entity, true );
DestructHitLocPieces( entity, sHitLoc );
return iDamage;
}
function IsDestructed( entity, pieceNumber )
{
/# assert( (1 <= pieceNumber && pieceNumber <= 20) ); #/
return (_GetDestructState( entity ) & (1 << pieceNumber));
}
function ReapplyDestructedPieces( entity )
{
if ( !IsDefined(entity.destructibledef) )
{
return;
}
destructBundle = struct::get_script_bundle( "destructiblecharacterdef", entity.destructibledef );
for ( index = 1; index <= destructBundle.pieces.size; index++ )
{
if ( !IsDestructed( entity, index ) )
{
continue;
}
piece = destructBundle.pieces[ index - 1];
if ( IsDefined( piece.hidetag ) && entity HasPart( piece.hidetag ) )
{
entity HidePart( piece.hidetag );
}
}
}
function ShowDestructedPieces( entity )
{
if ( !IsDefined(entity.destructibledef) )
{
return;
}
destructBundle = struct::get_script_bundle( "destructiblecharacterdef", entity.destructibledef );
for ( index = 1; index <= destructBundle.pieces.size; index++ )
{
piece = destructBundle.pieces[ index - 1];
if ( IsDefined( piece.hidetag ) && entity HasPart( piece.hidetag ) )
{
entity ShowPart( piece.hidetag );
}
}
}
function ToggleSpawnGibs( entity, shouldSpawnGibs )
{
if ( shouldSpawnGibs )
{
entity._destruct_state = _GetDestructState( entity ) | 1;
}
else
{
entity._destruct_state = _GetDestructState( entity ) & ~1;
}
entity clientfield::set( "destructible_character_state", entity._destruct_state );
}
// end #namespace DestructServerUtils;
@@ -0,0 +1,24 @@
// 20 possible states and 1 toggle bit to determine if spawning gib models should happen on the client.
// The toggle bit is used to predamage characters when they are spawned in, without creating gib models.
// (20 + 1) states
// Pieces numbers start at 1, and stop at DESTRUCT_MAX_PIECES
+421
View File
@@ -0,0 +1,421 @@
#using scripts\shared\math_shared;
#using scripts\shared\util_shared;
Line truncated
#precache( "lui_menu", "TempDialog" );
#precache( "lui_menu_data", "dialogText" );
#precache( "lui_menu_data", "title" );
// face.gsc
// Supposed to handle all facial and dialogue animations from regular scripts.
//#using_animtree ("generic_human"); - This file doesn't call animations directly.
function SayGenericDialogue(typeString)
{
if (level.disableGenericDialog)
{
return;
}
switch (typeString)
{
case "attack":
importance = 0.5;
break;
case "swing":
importance = 0.5;
typeString = "attack";
break;
case "flashbang":
importance = 0.7;
break;
case "pain_small":
importance = 0.4;
break;
case "pain_bullet":
wait(.01); //make sure dialog system has had a chance to deal with death before we play another line.
importance = 0.4;
break;
default:
/#println ("Unexpected generic dialog string: "+typeString);#/
importance = 0.3;
break;
}
SayGenericDialogueWithImportance(typeString, importance);
}
// Makes a character say the specified line in his voice, if he's not already saying something more
// important.
function SayGenericDialogueWithImportance(typeString, importance)
{
soundAlias = "dds_";
if( isdefined( self.dds_characterID ) )
{
soundAlias += self.dds_characterID;
}
else
{
/#printLn( "this AI does not have a dds_characterID" );#/
return;
}
soundAlias += "_" + typeString;
if(SoundExists(soundAlias))
{
self thread PlayFaceThread (undefined, soundAlias, importance);
}
}
function SetIdleFaceDelayed(facialAnimationArray)
{
self.a.idleFace = facialAnimationArray;
}
// Sets the facial expression to return to when not saying dialogue.
// The array is animation1, weight1, animation2, weight2, etc. The animations will play in turn - each time
// one finishes a new one will be chosen randomly based on weight.
function SetIdleFace(facialAnimationArray)
{
if (!anim.useFacialAnims)
{
return;
}
self.a.idleFace = facialAnimationArray;
self PlayIdleFace();
}
// Makes the character play the specified sound and animation. The anim and the sound are optional - you
// can just defined one if you don't have both.
// Generally, importance should be in the range of 0.6-0.8 for scripted dialogue.
// Importance is a float, from 0 to 1.
// 0.0 - Idle expressions
// 0.1-0.5 - most generic dialogue
// 0.6-0.8 - most scripted dialogue
// 0.9 - pain
// 1.0 - death
// Importance can also be one of these strings: "any", "pain" or "death", which specfies what sounds can
// interrupt this one.
function SaySpecificDialogue(facialanim, soundAlias, importance, notifyString, waitOrNot, timeToWait, toplayer)
{
///("SaySpecificDialog, facial: ",facialanim,", sound: ",soundAlias,", importance: "+importance+", notify: ",notifyString, ", WaitOrNot: ", waitOrNot, ", timeToWait: ", timeToWait);#/
self thread PlayFaceThread(facialanim, soundAlias, importance, notifyString, waitOrNot, timeToWait, toplayer);
}
// DEAD CODE REMOVAL
// Takes an array with a set of "anim" entries and a corresponding set of "weight" entries.
//ChooseAnimFromSet( animSet )
//{
// return; // Facial animations are now part of full body aniamtions
/*
if (!anim.useFacialAnims)
return;
// First, normalize the weights.
totalWeight = 0;
numAnims = animSet["anim"].size;
for ( i=0 ; i<numAnims ; i++ )
{
totalWeight += animSet["weight"][i];
}
for ( i=0 ; i<numAnims ; i++ )
{
animSet["weight"][i] = animSet["weight"][i] / totalWeight;
}
// Now choose an animation.
rand = RandomFloat(1);
runningTotal = 0;
chosenAnim = undefined;
for ( i=0 ; i<numAnims ; i++ )
{
runningTotal += animSet["weight"][i];
if (runningTotal >= rand)
{
chosenAnim = i;
break;
}
}
assert(isdefined(chosenAnim), "Logic error in ChooseAnimFromSet. Rand is " + rand + ".");
return animSet["anim"][chosenAnim];
*/
//}
//-----------------------------------------------------
// Housekeeping functions - these are for internal use
//-----------------------------------------------------
// PlayIdleFace doesn't force an idle animation to play - it will interrupt a current idle animation, but it
// won't play over a more important animation, like dialogue.
function PlayIdleFace()
{
return; // Idle facial animations are now in the full-body animations.
}
// PlayFaceThread is the workhorse of the system - it checks the importance, and if it's high enough, it
// plays the animation and/or sound specified.
// The waitOrNot parameter specifies what to do if another animation/sound is already playing.
// Options: "wait" or undefined. TimeToWait is an optional timeout time for waiting.
// Waiting faces are queued.
function PlayFaceThread( facialanim, str_script_alias, importance, notifyString, waitOrNot, timeToWait, toplayer )
{
//NOTE most of the described functionality was removed in COD2 (!)
//Talk about misleading comments...
//This must be called in it's own thread.
//
//The behavior has been simplifed greatly
// lines never wait
// a line stops the current line if at equal or higher priority
// a line doesn't play if at lower priority
//SBM 8/3/08
self endon( "death" );
if ( !isdefined( str_script_alias ) )
{
wait 1;
self notify( notifyString );
return; //no sound, don't do anything facials are now included into the main anims
}
str_notify_alias = str_script_alias; // Save this off because the script alias gets changed for females
if(!isdefined(level.NumberOfImportantPeopleTalking))level.NumberOfImportantPeopleTalking=0;
if(!isdefined(level.TalkNotifySeed))level.TalkNotifySeed=0;
if(!isdefined(notifyString))notifyString="PlayFaceThread " + str_script_alias;
if(!isdefined(self.a))self.a=SpawnStruct();
if(!isdefined(self.a.facialSoundDone))self.a.facialSoundDone=true;
if(!isdefined(self.isTalking))self.isTalking=false;
if ( self.isTalking ) //currently talking
{
//TUEY added equal to, so that the system won't spam the same notifies
if ( IsDefined( self.a.currentDialogImportance ) )
{
if ( importance < self.a.currentDialogImportance )
{
wait 1;
self notify( notifyString );
return; //skip it cause its not important
}
else if ( importance == self.a.currentDialogImportance )
{
if ( self.a.facialSoundAlias == str_script_alias )
{
return; //playing same alias twice? probably a script error somewhere
}
/# println( "WARNING: delaying alias " + self.a.facialSoundAlias + " to play " + str_script_alias ); #/
while ( self.isTalking ) //while is to fix a race condition
{
self waittill( "done speaking" ); //wait currently playing thread to finish
}
}
}
else
{
/# PrintLn("WARNING: interrupting alias "+self.a.facialSoundAlias+" to play "+str_script_alias); #/
self StopSound( self.a.facialSoundAlias );
self notify("cancel speaking");
while ( self.isTalking ) //while is to fix a race condition
{
self waittill( "done speaking" ); //wait currently playing thread to finish
}
}
}
assert( self.a.facialSoundDone );
assert( self.a.facialSoundAlias == undefined );
assert( self.a.facialSoundNotify == undefined );
assert( self.a.currentDialogImportance == undefined );
assert( !self.isTalking );
self notify( "bc_interrupt" );
self.isTalking = true;
self.a.facialSoundDone = false;
self.a.facialSoundNotify = notifyString;
self.a.facialSoundAlias = str_script_alias;
self.a.currentDialogImportance = importance;
if ( importance == 1.0 )
{
level.NumberOfImportantPeopleTalking += 1;
}
/#
if ( level.NumberOfImportantPeopleTalking > 1 )
{
println( "WARNING: multiple high priority dialogs are happening at once " + str_script_alias );
}
#/
uniqueNotify = notifyString + " " + level.TalkNotifySeed;
level.TalkNotifySeed += 1;
if ( isdefined( level.scr_sound ) && isdefined( level.scr_sound[ "generic" ] ) )
{
str_vox_file = level.scr_sound[ "generic" ][ str_script_alias ];
}
if ( isdefined( str_vox_file ) )
{
if ( SoundExists( str_vox_file ) )
{
if ( IsPlayer( toplayer ) )
{
self thread _play_sound_to_player_with_notify( str_vox_file, toplayer, uniqueNotify );
}
else
{
if ( isdefined( self GetTagOrigin( "J_Head" ) ) )
{
self PlaySoundWithNotify( str_vox_file, uniqueNotify, "J_Head" );
}
else
{
self PlaySoundWithNotify( str_vox_file, uniqueNotify );
}
}
}
else
{
/#
PrintLn( "WARNING: DIALOG VO SOUND: '" + str_script_alias + "' does not exist." );
self thread _missing_dialog( str_script_alias, str_vox_file, uniqueNotify );
#/
}
}
else
{
self thread _temp_dialog( str_script_alias, uniqueNotify );
}
self util::waittill_any( "death", "cancel speaking", uniqueNotify );
if ( importance == 1.0 )
{
level.NumberOfImportantPeopleTalking -= 1;
level.ImportantPeopleTalkingTime = GetTime();
}
if ( isdefined( self ) )
{
self.isTalking = false;
self.a.facialSoundDone = true;
self.a.facialSoundNotify = undefined;
self.a.facialSoundAlias = undefined;
self.a.currentDialogImportance = undefined;
self.lastSayTime = GetTime();
}
self notify( "done speaking", str_notify_alias );
self notify( notifyString );
}
// HACK: there is no way to get a notify from a sound on a specific client.
// Even though SoundGetPlaybackTime isn't 100% and won't be accurate with locs,
// it's our best bet for now.
function _play_sound_to_player_with_notify( soundAlias, toplayer, uniqueNotify )
{
self endon( "death" );
toplayer endon( "death" );
self PlaySoundToPlayer( soundAlias, toplayer );
n_playbackTime = SoundGetPlaybackTime( soundAlias );
if ( n_playbackTime > 0 )
{
wait n_playbackTime * .001;
}
else
{
wait 1.0;
}
self notify( uniqueNotify );
}
function private _temp_dialog( str_line, uniqueNotify, b_missing_vo = false )
{
SetDvar( "bgcache_disablewarninghints", 1 );
if ( !b_missing_vo && isdefined( self.propername ) )
{
str_line = self.propername + ": " + str_line;
}
foreach ( player in level.players )
{
if ( !isdefined( player GetLUIMenu( "TempDialog" ) ) )
{
player OpenLuiMenu( "TempDialog" );
}
player SetLuiMenuData( player GetLUIMenu( "TempDialog" ), "dialogText", str_line );
if ( b_missing_vo )
{
player SetLuiMenuData( player GetLUIMenu( "TempDialog" ), "title", "MISSING VO SOUND" );
}
else
{
player SetLuiMenuData( player GetLUIMenu( "TempDialog" ), "title", "TEMP VO" );
}
}
// wait time based on average words spoken (2 per second) and capped at 5 seconds total, -1 for propername
n_wait_time = ( StrTok( str_line, " ").size - 1 ) / 2;
n_wait_time = math::clamp( n_wait_time, 2, 5);
util::waittill_any_timeout( n_wait_time, "death", "cancel speaking" );
foreach ( player in level.players )
{
if ( isdefined( player GetLUIMenu( "TempDialog" ) ) )
{
player CloseLUIMenu( player GetLUIMenu( "TempDialog" ) );
}
}
SetDvar( "bgcache_disablewarninghints", 0 );
self notify( uniqueNotify );
}
function private _missing_dialog( str_script_alias, str_vox_file, uniqueNotify )
{
_temp_dialog( "script id: " + str_script_alias + " sound alias: " + str_vox_file, uniqueNotify, true );
}
function PlayFace_WaitForNotify(waitForString, notifyString, killmeString)
{
self endon ("death");
self endon (killmeString);
self waittill (waitForString);
self.a.faceWaitForResult = "notify";
self notify (notifyString);
}
function PlayFace_WaitForTime(time, notifyString, killmeString)
{
self endon ("death");
self endon (killmeString);
wait (time);
self.a.faceWaitForResult = "time";
self notify (notifyString);
}
+256
View File
@@ -0,0 +1,256 @@
#using scripts\codescripts\struct;
#using scripts\shared\ai\systems\destructible_character;
#using scripts\shared\ai\systems\gib;
Line truncated
function autoexec main()
{
fxBundles = struct::get_script_bundles( "fxcharacterdef" );
processedFxBundles = [];
// Process all fx character script bundles into a direct lookup form for future access.
foreach ( fxBundleName, fxBundle in fxBundles )
{
processedFxBundle = SpawnStruct();
processedFxBundle.effectCount = fxBundle.effectCount;
processedFxBundle.fx = [];
processedFxBundle.name = fxBundleName;
for ( index = 1; index <= fxBundle.effectCount; index++ )
{
fx = GetStructField( fxBundle, "effect" + index + "_fx" );
if ( IsDefined( fx ) )
{
fxStruct = SpawnStruct();
fxStruct.attachTag = GetStructField( fxBundle, "effect" + index + "_attachtag" );
fxStruct.fx = GetStructField( fxBundle, "effect" + index + "_fx" );
fxStruct.stopOnGib = FxClientUtils::_GibPartNameToGibFlag(
GetStructField( fxBundle, "effect" + index + "_stopongib" ) );
fxStruct.stopOnPieceDestroyed = GetStructField( fxBundle, "effect" + index + "_stoponpiecedestroyed" );
processedFxBundle.fx[ processedFxBundle.fx.size ] = fxStruct;
}
}
processedFxBundles[ fxBundleName ] = processedFxBundle;
}
// Replaces all fx character script bundles with their processed form to free unncessary script variables.
level.scriptbundles[ "fxcharacterdef" ] = processedFxBundles;
}
#namespace FxClientUtils;
function private _ConfigEntity( localClientNum, entity )
{
if ( !IsDefined( entity._fxCharacter ) )
{
entity._fxCharacter = [];
handledGibs = array(
8,
16,
32,
128,
256 );
foreach ( gibFlag in handledGibs )
{
GibClientUtils::AddGibCallback( localClientNum, entity, gibFlag, &_GibHandler );
}
for ( index = 1; index <= 20; index++ )
{
DestructClientUtils::AddDestructPieceCallback( localClientNum, entity, index, &_DestructHandler );
}
}
}
function private _DestructHandler( localClientNum, entity, pieceNumber )
{
if ( !IsDefined( entity._fxCharacter ) )
{
return;
}
foreach ( fxBundleName, fxBundleInst in entity._fxCharacter )
{
fxBundle = struct::get_script_bundle( "fxcharacterdef", fxBundleName );
for ( index = 0; index < fxBundle.fx.size; index++ )
{
if ( IsDefined( fxBundleInst[ index ] ) &&
fxBundle.fx[index].stopOnPieceDestroyed === pieceNumber )
{
StopFx( localClientNum, fxBundleInst[ index ] );
fxBundleInst[ index ] = undefined;
}
}
}
}
function private _GibHandler( localClientNum, entity, gibFlag )
{
if ( !IsDefined( entity._fxCharacter ) )
{
return;
}
foreach ( fxBundleName, fxBundleInst in entity._fxCharacter )
{
fxBundle = struct::get_script_bundle( "fxcharacterdef", fxBundleName );
for ( index = 0; index < fxBundle.fx.size; index++ )
{
if ( IsDefined( fxBundleInst[ index ] ) && fxBundle.fx[index].stopOnGib === gibFlag )
{
StopFx( localClientNum, fxBundleInst[ index ] );
fxBundleInst[ index ] = undefined;
}
}
}
}
function private _GibPartNameToGibFlag( gibPartName )
{
if ( IsDefined( gibPartName ) )
{
switch ( gibPartName )
{
case "head":
return 8;
case "right arm":
return 16;
case "left arm":
return 32;
case "right leg":
return 128;
case "left leg":
return 256;
}
}
}
function private _IsGibbed( localClientNum, entity, stopOnGibFlag )
{
if ( !IsDefined( stopOnGibFlag ) )
{
return false;
}
return GibClientUtils::IsGibbed( localClientNum, entity, stopOnGibFlag );
}
function private _IsPieceDestructed( localClientNum, entity, stopOnPieceDestroyed )
{
if ( !IsDefined( stopOnPieceDestroyed ) )
{
return false;
}
return DestructClientUtils::IsPieceDestructed( localClientNum, entity, stopOnPieceDestroyed );
}
function private _ShouldPlayFx( localClientNum, entity, fxStruct )
{
if ( _IsGibbed( localClientNum, entity, fxStruct.stopOnGib ) )
{
return false;
}
if ( _IsPieceDestructed( localClientNum, entity, fxStruct.stopOnPieceDestroyed ) )
{
return false;
}
return true;
}
function PlayFxBundle( localClientNum, entity, fxScriptBundle )
{
if ( !IsDefined( fxScriptBundle ) )
{
return;
}
_ConfigEntity( localClientNum, entity );
fxBundle = struct::get_script_bundle( "fxcharacterdef", fxScriptBundle );
if ( IsDefined( entity._fxCharacter[ fxBundle.name ] ) )
{
// FxBundle is already playing, early out.
return;
}
if ( IsDefined( fxBundle ) )
{
playingFx = [];
for ( index = 0; index < fxBundle.fx.size; index++ )
{
fxStruct = fxBundle.fx[ index ];
if ( _ShouldPlayFx( localClientNum, entity, fxStruct ) )
{
playingFx[ index ] =
GibClientUtils::_PlayGibFX( localClientNum, entity, fxStruct.fx, fxStruct.attachTag );
}
}
if ( playingFx.size > 0 )
{
entity._fxCharacter[ fxBundle.name ] = playingFx;
}
}
}
function StopAllFXBundles( localClientNum, entity )
{
_ConfigEntity( localClientNum, entity );
fxBundleNames = [];
foreach ( fxBundleName, fxBundle in entity._fxCharacter )
{
fxBundleNames[ fxBundleNames.size ] = fxBundleName;
}
foreach ( fxBundleName in fxBundleNames )
{
StopFxBundle( localClientNum, entity, fxBundleName );
}
}
function StopFxBundle( localClientNum, entity, fxScriptBundle )
{
if ( !IsDefined( fxScriptBundle ) )
{
return;
}
_ConfigEntity( localClientNum, entity );
fxBundle = struct::get_script_bundle( "fxcharacterdef", fxScriptBundle );
if ( IsDefined( entity._fxCharacter[ fxBundle.name ] ) )
{
foreach ( fx in entity._fxCharacter[ fxBundle.name ] )
{
if ( IsDefined( fx ) )
{
StopFx( localClientNum, fx );
}
}
entity._fxCharacter[ fxBundle.name ] = undefined;
}
}
+1
View File
@@ -0,0 +1 @@
+792
View File
@@ -0,0 +1,792 @@
#using scripts\codescripts\struct;
#using scripts\shared\clientfield_shared;
#using scripts\shared\util_shared;
Line truncated
#namespace GibClientUtils;
function autoexec main()
{
clientfield::register(
"actor",
"gib_state",
1,
(9),
"int",
&GibClientUtils::_GibHandler,
!true,
!true);
clientfield::register(
"playercorpse",
"gib_state",
1,
(9+3+3),
"int",
&GibClientUtils::_GibHandler,
!true,
!true);
gibDefinitions = struct::get_script_bundles("gibcharacterdef");
gibPieceLookup = [];
gibPieceLookup[2] = "annihilate";
gibPieceLookup[8] = "head";
gibPieceLookup[16] = "rightarm";
gibPieceLookup[32] = "leftarm";
gibPieceLookup[128] = "rightleg";
gibPieceLookup[256] = "leftleg";
processedBundles = [];
// Process each gib bundle to allow quick access to information in the future.
foreach ( definitionName, definition in gibDefinitions )
{
gibBundle = SpawnStruct();
gibBundle.gibs = [];
gibBundle.name = definitionName;
foreach ( gibPieceFlag, gibPieceName in gibPieceLookup )
{
gibStruct = SpawnStruct();
gibStruct.gibmodel = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibmodel" );
gibStruct.gibtag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibtag" );
gibStruct.gibfx = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibfx" );
gibStruct.gibfxtag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibeffecttag" );
gibStruct.gibdynentfx = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibdynentfx" );
gibStruct.gibsound = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibsound" );
gibBundle.gibs[ gibPieceFlag ] = gibStruct;
}
processedBundles[ definitionName ] = gibBundle;
}
// Replaces all gib character define bundles with their processed form to free unncessary script variables.
level.scriptbundles[ "gibcharacterdef" ] = processedBundles;
// Thread that handles any corpse annihilate request.
level thread _AnnihilateCorpse();
}
function private _AnnihilateCorpse()
{
while( true )
{
level waittill( "corpse_explode", localClientNum, body, origin );
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
continue;
}
if ( IsDefined( body ) && _HasGibDef( body ) && body IsRagdoll() )
{
ClientEntGibHead( localClientNum, body );
ClientEntGibRightArm( localClientNum, body );
ClientEntGibLeftArm( localClientNum, body );
ClientEntGibRightLeg( localClientNum, body );
ClientEntGibLeftLeg( localClientNum, body );
}
// TODO(David Young 6-8-15): Human bodies are currently the only supported gibbable corpse.
// Need to add support to the gib definition to flag if a corpse can be annihilated instead.
if( IsDefined( body ) && _HasGibDef( body ) && body.archetype == "human" )
{
// Only allow human gibbing at a 50% rate.
if ( RandomInt( 100 ) >= 50 )
{
continue;
}
if ( IsDefined( origin ) && DistanceSquared( body.origin, origin ) <= ( 120 * 120 ) )
{
// Toggle hiding the ragdoll.
body.ignoreRagdoll = true;
body _GibEntity(
localClientNum,
2 |
16 |
32 |
(128+256),
true );
}
}
}
}
function private _CloneGibData( localClientNum, entity, clone )
{
clone.gib_data = SpawnStruct();
// Copy gib data.
clone.gib_data.gib_state = entity.gib_state;
clone.gib_data.gibdef = entity.gibdef;
// Copy all model data.
clone.gib_data.hatmodel = entity.hatmodel;
clone.gib_data.head = entity.head;
clone.gib_data.legdmg1 = entity.legdmg1;
clone.gib_data.legdmg2 = entity.legdmg2;
clone.gib_data.legdmg3 = entity.legdmg3;
clone.gib_data.legdmg4 = entity.legdmg4;
clone.gib_data.torsodmg1 = entity.torsodmg1;
clone.gib_data.torsodmg2 = entity.torsodmg2;
clone.gib_data.torsodmg3 = entity.torsodmg3;
clone.gib_data.torsodmg4 = entity.torsodmg4;
clone.gib_data.torsodmg5 = entity.torsodmg5;
}
function private _GetGibDef( entity )
{
if ( entity IsPlayer() || entity IsPlayerCorpse() )
{
return entity GetPlayerGibDef();
}
else if ( IsDefined( entity.gib_data ) )
{
return entity.gib_data.gibdef;
}
return entity.gibdef;
}
function private _GetGibbedState( localClientNum, entity )
{
if ( IsDefined( entity.gib_data ) && IsDefined( entity.gib_data.gib_state ) )
{
return entity.gib_data.gib_state;
}
else if ( IsDefined( entity.gib_state ) )
{
return entity.gib_state;
}
return 0;
}
function private _GetGibbedLegModel( localClientNum, entity )
{
gibState = _GetGibbedState( localClientNum, entity );
rightLegGibbed = (gibState & 128);
leftLegGibbed = (gibState & 256);
if ( rightLegGibbed && leftLegGibbed)
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg4:entity.legDmg4);
}
else if ( rightLegGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg2:entity.legDmg2);
}
else if ( leftLegGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg3:entity.legDmg3);
}
return (isdefined(entity.gib_data)?entity.gib_data.legDmg1:entity.legDmg1);
}
function private _GetGibExtraModel( localClientNumm, entity, gibFlag )
{
if ( gibFlag == 4 )
return (isdefined(entity.gib_data)?entity.gib_data.hatmodel:entity.hatmodel);
else if ( gibFlag == 8 )
return (isdefined(entity.gib_data)?entity.gib_data.head:entity.head);
else
AssertMsg( "Unable to find gib model." );
}
function private _GetGibbedTorsoModel( localClientNum, entity )
{
gibState = _GetGibbedState( localClientNum, entity );
rightArmGibbed = (gibState & 16);
leftArmGibbed = (gibState & 32);
if ( rightArmGibbed && leftArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg2:entity.torsoDmg2);
// TODO(David Young 5-14-14): Currently AI's don't support both arms getting blown off.
// return GIB_TORSO_NO_ARMS_MODEL( entity );
}
else if ( rightArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg2:entity.torsoDmg2);
}
else if ( leftArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg3:entity.torsoDmg3);
}
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg1:entity.torsoDmg1);
}
function private _GibPieceTag( localClientNum, entity, gibFlag )
{
if ( !_HasGibDef( self ) )
{
return;
}
gibBundle = struct::get_script_bundle("gibcharacterdef",_GetGibDef( entity ));
gibPiece = gibBundle.gibs[ gibFlag ];
if ( IsDefined( gibPiece ) )
{
return gibPiece.gibfxtag;
}
}
function private _GibEntity( localClientNum, gibFlags, shouldSpawnGibs )
{
entity = self;
if ( !_HasGibDef( entity ) )
{
return;
}
// Skip the toggle flag, GIB_TOGGLE_GIB_MODEL_FLAG.
currentGibFlag = 2;
gibDir = undefined;
if ( entity IsPlayer() || entity IsPlayerCorpse() )
{
// TODO(David Young 6-8-15): Add support for yaw bits for AI as well.
yaw_bits = ( ( gibFlags >> 9 ) & ( ( 1 << 3 ) - 1 ) );
yaw = getanglefrombits( yaw_bits, 3 );
gibDir = AnglesToForward( ( 0, yaw, 0 ) );
}
gibBundle = struct::get_script_bundle("gibcharacterdef",_GetGibDef( entity ));
// Handles any number of simultaneous gibbings.
while ( gibFlags >= currentGibFlag )
{
if ( gibFlags & currentGibFlag )
{
gibPiece = gibBundle.gibs[ currentGibFlag ];
if ( IsDefined( gibPiece ) )
{
if ( shouldSpawnGibs )
{
entity thread _GibPiece( localClientNum, entity, gibPiece.gibmodel, gibPiece.gibtag, gibPiece.gibdynentfx, gibDir );
}
_PlayGibFX( localClientNum, entity, gibPiece.gibfx, gibPiece.gibfxtag );
_PlayGibSound( localClientNum, entity, gibPiece.gibsound );
if ( currentGibFlag == 2 )
{
entity Hide();
entity.ignoreRagdoll = true;
}
}
_HandleGibCallbacks( localClientNum, entity, currentGibFlag );
}
currentGibFlag = currentGibFlag << 1;
}
}
function private _SetGibbed( localClientNum, entity, gibFlag )
{
gib_state = (_GetGibbedState( localClientNum, entity ) | ( gibFlag & ( ( 1 << 9 ) - 1 ) ));
if ( IsDefined( entity.gib_data ) )
{
entity.gib_data.gib_state = gib_state;
}
else
{
entity.gib_state = gib_state;
}
}
function private _GibClientEntityInternal( localClientNum, entity, gibFlag )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
if ( !IsDefined( entity ) || !_HasGibDef( entity ) )
{
return;
}
if ( entity.type !== "scriptmover" )
{
// Clientside gibbing currently only supports script models.
return;
}
if ( IsGibbed( localClientNum, entity, gibFlag ) )
{
return;
}
if ( !(_GetGibbedState( localClientNum, entity ) < 16) )
{
legModel = _GetGibbedLegModel( localClientNum, entity );
entity Detach( legModel, "" );
}
_SetGibbed( localClientNum, entity, gibFlag );
entity SetModel( _GetGibbedTorsoModel( localClientNum, entity ) );
entity Attach( _GetGibbedLegModel( localClientNum, entity ), "" );
entity _GibEntity( localClientNum, gibFlag, true );
}
function private _GibClientExtraInternal( localClientNum, entity, gibFlag )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
if ( !IsDefined( entity ) )
{
return;
}
if ( entity.type !== "scriptmover" )
{
// Clientside gibbing currently only supports script models.
return;
}
if ( IsGibbed( localClientNum, entity, gibFlag ) )
{
return;
}
gibModel = _GetGibExtraModel( localClientNum, entity, gibFlag );
if ( IsDefined( gibModel ) && entity IsAttached( gibModel, "" ) )
{
entity Detach( gibModel, "" );
}
if ( gibFlag == 8 )
{
if ( IsDefined( (isdefined(entity.gib_data)?entity.gib_data.torsoDmg5:entity.torsoDmg5) ) )
{
entity Attach( (isdefined(entity.gib_data)?entity.gib_data.torsoDmg5:entity.torsoDmg5), "" );
}
}
_SetGibbed( localClientNum, entity, gibFlag );
entity _GibEntity( localClientNum, gibFlag, true );
}
function private _GibHandler( localClientNum, oldValue, newValue, bNewEnt, bInitialSnap, fieldName, wasDemoJump )
{
entity = self;
if ( entity IsPlayer() || entity IsPlayerCorpse() )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
}
else
{
if ( IsDefined( entity.maturegib ) && entity.maturegib && !util::is_mature() )
{
return;
}
if ( IsDefined( entity.restrictedgib ) && entity.restrictedgib && !IsShowGibsEnabled() )
{
return;
}
}
gibFlags = (oldValue ^ newValue);
shouldSpawnGibs = (!(newValue & 1));
// Don't use the old clientfield value for new entities.
if ( bNewEnt )
{
gibFlags = (0 ^ newValue);
}
entity _GibEntity( localClientNum, gibFlags, shouldSpawnGibs );
entity.gib_state = newValue;
}
function _GibPiece( localClientNum, entity, gibModel, gibTag, gibFx, gibDir )
{
if ( !IsDefined( gibTag ) || !IsDefined( gibModel ) )
{
return;
}
startPosition = entity GetTagOrigin( gibTag );
startAngles = entity GetTagAngles( gibTag );
endPosition = startPosition;
endAngles = startAngles;
forwardVector = undefined;
if ( !IsDefined( startPosition ) || !IsDefined( startAngles ) )
{
return false;
}
if ( IsDefined( gibDir ) )
{
startPosition = (0,0,0);
forwardVector = gibDir;
// TODO: define the scale on the server from the damage
forwardVector *= RandomFloatRange( 100.0, 500.0 );
}
else
{
// Let a frame pass to approximate the linear and angular velocity of the tag.
{wait(.016);};
if ( IsDefined( entity ) )
{
endPosition = entity GetTagOrigin( gibTag );
endAngles = entity GetTagAngles( gibTag );
}
else
{
// Entity already removed.
endPosition = startPosition + ( AnglesToForward( startAngles ) * 10 );
endAngles = startAngles;
}
if ( !IsDefined( endPosition ) || !IsDefined( endAngles ) )
{
return false;
}
forwardVector = VectorNormalize( endPosition - startPosition );
forwardVector *= RandomFloatRange( 0.6, 1.0 );
forwardVector += ( RandomFloatRange( 0, 0.2 ), RandomFloatRange( 0, 0.2 ), RandomFloatRange( 0.2, 0.7 ) );
}
if( IsDefined( entity ) )
{
gibEntity = CreateDynEntAndLaunch( localClientNum, gibModel, endPosition, endAngles, startPosition, forwardVector, gibFx, true );
if ( IsDefined( gibEntity ) )
{
SetDynEntBodyRenderOptionsPacked( gibEntity, entity GetBodyRenderOptionsPacked() );
}
}
}
function private _HandleGibCallbacks( localClientNum, entity, gibFlag )
{
if ( IsDefined( entity._gibCallbacks ) &&
IsDefined( entity._gibCallbacks[gibFlag] ) )
{
foreach ( callback in entity._gibCallbacks[gibFlag] )
{
[[callback]]( localClientNum, entity, gibFlag );
}
}
}
function private _HandleGibAnnihilate( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib_annihilate" );
GibClientUtils::ClientEntGibAnnihilate( localClientNum, entity );
}
function private _HandleGibHead( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"head\"" );
GibClientUtils::ClientEntGibHead( localClientNum, entity );
}
function private _HandleGibRightArm( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"arm_right\"" );
GibClientUtils::ClientEntGibRightArm( localClientNum, entity );
}
function private _HandleGibLeftArm( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"arm_left\"" );
GibClientUtils::ClientEntGibLeftArm( localClientNum, entity );
}
function private _HandleGibRightLeg( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"leg_right\"" );
GibClientUtils::ClientEntGibRightLeg( localClientNum, entity );
}
function private _HandleGibLeftLeg( localClientNum )
{
entity = self;
entity endon( "entityshutdown" );
entity waittillmatch( "_anim_notify_", "gib = \"leg_left\"" );
GibClientUtils::ClientEntGibLeftLeg( localClientNum, entity );
}
function private _HasGibDef( entity )
{
return ( IsDefined( entity.gib_data ) && IsDefined( entity.gib_data.gibdef ) ) ||
IsDefined( entity.gibdef ) ||
( ( entity GetPlayerGibDef() ) != "unknown" );
}
function _PlayGibFX( localClientNum, entity, fxFileName, fxTag )
{
if ( IsDefined( fxFileName ) && IsDefined( fxTag ) && entity hasDobj(localClientNum) )
{
fx = PlayFxOnTag( localClientNum, fxFileName, entity, fxTag );
if(isDefined(fx))
{
if(isDefined(entity.team))
{
SetFxTeam( localClientNum, fx, entity.team );
}
if( ( isdefined( level.SetGibFXToIgnorePause ) && level.SetGibFXToIgnorePause ) )
{
SetFXIgnorePause( localClientNum, fx, true );
}
}
return fx;
}
}
function _PlayGibSound( localClientNum, entity, soundAlias )
{
if ( IsDefined( soundAlias ) )
{
PlaySound( localClientNum, soundAlias, entity.origin );
}
}
/@
"Name: AddGibCallback( localClientNum, entity, gibFlag, callbackFunction )"
"Summary: Register a function callback that is called when the corresponding piece is gibbed."
"MandatoryArg: <num> : Client number."
"MandatoryArg: <entity> : Entity to add callbacks to."
"MandatoryArg: <num> : Gib piece to register for."
"MandatoryArg: <function> : Function to call, function is passed the localClientNum, entity, and gibFlag."
"Module: Gib"
@/
function AddGibCallback( localClientNum, entity, gibFlag, callbackFunction )
{
assert( IsFunctionPtr( callbackFunction ) );
if ( !IsDefined( entity._gibCallbacks ) )
{
entity._gibCallbacks = [];
}
if ( !IsDefined( entity._gibCallbacks[gibFlag] ) )
{
entity._gibCallbacks[gibFlag] = [];
}
gibCallbacks = entity._gibCallbacks[gibFlag];
gibCallbacks[gibCallbacks.size] = callbackFunction;
entity._gibCallbacks[gibFlag] = gibCallbacks;
}
function ClientEntGibAnnihilate( localClientNum, entity )
{
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
return;
}
// Toggle hiding the ragdoll.
entity.ignoreRagdoll = true;
entity _GibEntity(
localClientNum,
2 |
16 |
32 |
(128+256),
true );
}
function ClientEntGibHead( localClientNum, entity )
{
_GibClientExtraInternal( localClientNum, entity, 4 );
_GibClientExtraInternal( localClientNum, entity, 8 );
}
function ClientEntGibLeftArm( localClientNum, entity )
{
if ( IsGibbed( localClientNum, entity, 16 ) )
{
return;
}
_GibClientEntityInternal( localClientNum, entity, 32 );
}
function ClientEntGibRightArm( localClientNum, entity )
{
if ( IsGibbed( localClientNum, entity, 32 ) )
{
return;
}
_GibClientEntityInternal( localClientNum, entity, 16 );
}
function ClientEntGibLeftLeg( localClientNum, entity )
{
_GibClientEntityInternal( localClientNum, entity, 256 );
}
function ClientEntGibRightLeg( localClientNum, entity )
{
_GibClientEntityInternal( localClientNum, entity, 128 );
}
function CreateScriptModelOfEntity( localClientNum, entity )
{
clone = Spawn( localClientNum, entity.origin, "script_model" );
clone.angles = entity.angles;
_CloneGibData( localClientNum, entity, clone );
gibState = _GetGibbedState( localClientNum, clone );
if ( !util::is_mature() || util::is_gib_restricted_build() )
{
// Don't display gibbed entities for non-mature clients.
gibState = 0;
}
if ( !(_GetGibbedState( localClientNum, entity ) < 16) )
{
// Attach torso or full body model.
clone SetModel( _GetGibbedTorsoModel( localClientNum, entity ) );
// Only attach separate legs if the body is damage already.
clone Attach( _GetGibbedLegModel( localClientNum, entity ), "" );
}
else
{
// Attach full body model.
clone SetModel( entity.model );
}
if ( (gibState & 8) )
{
// Attach head stump.
if ( IsDefined( (isdefined(clone.gib_data)?clone.gib_data.torsoDmg5:clone.torsoDmg5) ) )
{
clone Attach( (isdefined(clone.gib_data)?clone.gib_data.torsoDmg5:clone.torsoDmg5), "" );
}
}
else
{
// Attach head.
if ( IsDefined( (isdefined(clone.gib_data)?clone.gib_data.head:clone.head) ) )
{
clone Attach( (isdefined(clone.gib_data)?clone.gib_data.head:clone.head), "" );
}
if ( !(gibState & 4) && IsDefined( (isdefined(clone.gib_data)?clone.gib_data.hatmodel:clone.hatmodel) ) )
{
clone Attach( (isdefined(clone.gib_data)?clone.gib_data.hatmodel:clone.hatmodel), "" );
}
}
return clone;
}
function IsGibbed( localClientNum, entity, gibFlag )
{
return (_GetGibbedState( localClientNum, entity ) & gibFlag);
}
function IsUndamaged( localClientNum, entity )
{
return _GetGibbedState( localClientNum, entity ) == 0;
}
function GibEntity( localClientNum, gibFlags )
{
self _GibEntity( localClientNum, gibFlags, true );
self.gib_state = (_GetGibbedState( localClientNum, self ) | ( gibFlags & ( ( 1 << 9 ) - 1 ) ));
}
function HandleGibNotetracks( localClientNum )
{
entity = self;
entity thread _HandleGibAnnihilate( localClientNum );
entity thread _HandleGibHead( localClientNum );
entity thread _HandleGibRightArm( localClientNum );
entity thread _HandleGibLeftArm( localClientNum );
entity thread _HandleGibRightLeg( localClientNum );
entity thread _HandleGibLeftLeg( localClientNum );
}
function PlayerGibLeftArm( localClientNum )
{
self GibEntity( localClientNum, 32 );
}
function PlayerGibRightArm( localClientNum )
{
self GibEntity( localClientNum, 16 );
}
function PlayerGibLeftLeg( localClientNum )
{
self GibEntity( localClientNum, 256 );
}
function PlayerGibRightLeg( localClientNum )
{
self GibEntity( localClientNum, 128 );
}
function PlayerGibLegs( localClientNum )
{
self GibEntity( localClientNum, 128 );
self GibEntity( localClientNum, 256 );
}
function PlayerGibTag( localClientNum, gibFlag )
{
return _GibPieceTag( localClientNum, self, gibFlag );
}
+663
View File
@@ -0,0 +1,663 @@
#using scripts\codescripts\struct;
#using scripts\shared\clientfield_shared;
#using scripts\shared\throttle_shared;
#using scripts\shared\ai\systems\destructible_character;
#using scripts\shared\ai\systems\shared;
Line truncated
function private fields_equal( field_a, field_b)
{
if ( !isDefined(field_a) && !isDefined(field_b))
return true;
if ( isDefined(field_a) && isDefined(field_b) && field_a == field_b )
return true;
return false;
}
function private _IsDefaultPlayerGib( gibPieceFlag, gibStruct )
{
if ( !fields_equal( level.playerGibBundle.gibs[gibPieceFlag].gibdynentfx , gibStruct.gibdynentfx ) )
{
return false;
}
if ( !fields_equal( level.playerGibBundle.gibs[gibPieceFlag].gibfxtag, gibStruct.gibfxtag ) )
{
return false;
}
if ( !fields_equal( level.playerGibBundle.gibs[gibPieceFlag].gibfx, gibStruct.gibfx ) )
{
return false;
}
if ( !fields_equal( level.playerGibBundle.gibs[gibPieceFlag].gibtag, gibStruct.gibtag ) )
{
return false;
}
return true;
}
function autoexec main()
{
clientfield::register(
"actor",
"gib_state",
1,
(9),
"int" );
clientfield::register(
"playercorpse",
"gib_state",
1,
(9+3+3),
"int" );
gibDefinitions = struct::get_script_bundles("gibcharacterdef");
gibPieceLookup = [];
gibPieceLookup[2] = "annihilate";
gibPieceLookup[8] = "head";
gibPieceLookup[16] = "rightarm";
gibPieceLookup[32] = "leftarm";
gibPieceLookup[128] = "rightleg";
gibPieceLookup[256] = "leftleg";
processedBundles = [];
if ( SessionModeIsMultiplayerGame() )
{
level.playerGibBundle = SpawnStruct();
level.playerGibBundle.gibs = [];
level.playerGibBundle.name = "default_player";
level.playerGibBundle.gibs[2] = SpawnStruct();
level.playerGibBundle.gibs[8] = SpawnStruct();
level.playerGibBundle.gibs[32] = SpawnStruct();
level.playerGibBundle.gibs[256] = SpawnStruct();
level.playerGibBundle.gibs[16] = SpawnStruct();
level.playerGibBundle.gibs[128] = SpawnStruct();
level.playerGibBundle.gibs[2].gibfxtag = "j_spinelower";
level.playerGibBundle.gibs[2].gibfx = "blood/fx_blood_impact_exp_body_lg";
level.playerGibBundle.gibs[32].gibmodel = "c_t7_mp_battery_mpc_body1_s_larm";
level.playerGibBundle.gibs[32].gibdynentfx = "blood/fx_blood_gib_limb_trail_emitter";
level.playerGibBundle.gibs[32].gibfxtag = "j_elbow_le";
level.playerGibBundle.gibs[32].gibfx = "blood/fx_blood_gib_arm_sever_burst";
level.playerGibBundle.gibs[32].gibtag = "j_elbow_le";
level.playerGibBundle.gibs[256].gibmodel = "c_t7_mp_battery_mpc_body1_s_lleg";
level.playerGibBundle.gibs[256].gibdynentfx = "blood/fx_blood_gib_limb_trail_emitter";
level.playerGibBundle.gibs[256].gibfxtag = "j_knee_le";
level.playerGibBundle.gibs[256].gibfx = "blood/fx_blood_gib_leg_sever_burst";
level.playerGibBundle.gibs[256].gibtag = "j_knee_le";
level.playerGibBundle.gibs[16].gibmodel = "c_t7_mp_battery_mpc_body1_s_rarm";
level.playerGibBundle.gibs[16].gibdynentfx = "blood/fx_blood_gib_limb_trail_emitter";
level.playerGibBundle.gibs[16].gibfxtag = "j_elbow_ri";
level.playerGibBundle.gibs[16].gibfx = "blood/fx_blood_gib_arm_sever_burst_rt";
level.playerGibBundle.gibs[16].gibtag = "j_elbow_ri";
level.playerGibBundle.gibs[128].gibmodel = "c_t7_mp_battery_mpc_body1_s_rleg";
level.playerGibBundle.gibs[128].gibdynentfx = "blood/fx_blood_gib_limb_trail_emitter";
level.playerGibBundle.gibs[128].gibfxtag = "j_knee_ri";
level.playerGibBundle.gibs[128].gibfx = "blood/fx_blood_gib_leg_sever_burst_rt";
level.playerGibBundle.gibs[128].gibtag = "j_knee_ri";
}
// Process each gib bundle to allow quick access to information in the future.
foreach ( definitionName, definition in gibDefinitions )
{
gibBundle = SpawnStruct();
gibBundle.gibs = [];
gibBundle.name = definitionName;
default_player = false;
foreach ( gibPieceFlag, gibPieceName in gibPieceLookup )
{
gibStruct = SpawnStruct();
gibStruct.gibmodel = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibmodel" );
gibStruct.gibtag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibtag" );
gibStruct.gibfx = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibfx" );
gibStruct.gibfxtag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibeffecttag" );
gibStruct.gibdynentfx = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibdynentfx" );
gibStruct.gibsound = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibsound" );
gibStruct.gibhidetag = GetStructField( definition, gibPieceLookup[ gibPieceFlag ] + "_gibhidetag" );
if ( SessionModeIsMultiplayerGame() && _IsDefaultPlayerGib( gibPieceFlag, gibStruct ) )
{
default_player = true;
}
gibBundle.gibs[ gibPieceFlag ] = gibStruct;
}
if ( SessionModeIsMultiplayerGame() && default_player )
{
processedBundles[ definitionName ] = level.playerGibBundle;
}
else
{
processedBundles[ definitionName ] = gibBundle;
}
}
// Replaces all gib character define bundles with their processed form to free unncessary script variables.
level.scriptbundles[ "gibcharacterdef" ] = processedBundles;
if ( !IsDefined( level.gib_throttle ) )
{
level.gib_throttle = new Throttle();
// Two gibs every 0.2 seconds.
[[ level.gib_throttle ]]->Initialize( 2, 0.2 );
}
}
#namespace GibServerUtils;
function private _Annihilate( entity )
{
if ( IsDefined( entity ) )
{
entity NotSolid();
}
}
function private _GetGibExtraModel( entity, gibFlag )
{
if ( gibFlag == 4 )
return (isdefined(entity.gib_data)?entity.gib_data.hatmodel:entity.hatmodel);
else if ( gibFlag == 8 )
return (isdefined(entity.gib_data)?entity.gib_data.head:entity.head);
else
AssertMsg( "Unable to find gib model." );
}
// Used to solely gib equipment and the actor's head.
// Does not change the torso or leg models.
function private _GibExtra( entity, gibFlag )
{
if ( IsGibbed( entity, gibFlag ) )
{
return false;
}
if ( !_HasGibDef( entity ) )
{
return false;
}
entity thread _GibExtraInternal( entity, gibFlag );
return true;
}
function private _GibExtraInternal( entity, gibFlag )
{
// Allow simulatenous gibs to happen on the same frame, since the network
// cost is negligible if the gib is for the same entity.
if ( entity.gib_time !== GetTime() )
{
[[ level.gib_throttle ]]->WaitInQueue( entity );
}
if ( !IsDefined( entity ) )
{
return;
}
entity.gib_time = GetTime();
if ( IsGibbed( entity, gibFlag ) )
{
return false;
}
if ( gibFlag == 8 )
{
if ( IsDefined( (isdefined(entity.gib_data)?entity.gib_data.torsoDmg5:entity.torsoDmg5) ) )
{
entity Attach( (isdefined(entity.gib_data)?entity.gib_data.torsoDmg5:entity.torsoDmg5), "", true );
}
}
_SetGibbed( entity, gibFlag, undefined );
DestructServerUtils::ShowDestructedPieces( entity );
ShowHiddenGibPieces( entity );
gibModel = _GetGibExtraModel( entity, gibFlag );
if ( IsDefined( gibModel ) )
{
entity Detach( gibModel, "" );
}
DestructServerUtils::ReapplyDestructedPieces( entity );
ReapplyHiddenGibPieces( entity );
}
// Used to gib torso or leg pieces, not including equipment or the actor's head.
// Changes the torso and leg models.
function private _GibEntity( entity, gibFlag )
{
if ( IsGibbed( entity, gibFlag ) || !_HasGibPieces( entity, gibFlag ) )
{
return false;
}
if ( !_HasGibDef( entity ) )
{
return false;
}
entity thread _GibEntityInternal( entity, gibFlag );
return true;
}
function private _GibEntityInternal( entity, gibFlag )
{
// Allow simulatenous gibs to happen on the same frame, since the network
// cost is negligible if the gib is for the same entity.
if ( entity.gib_time !== GetTime() )
{
[[ level.gib_throttle ]]->WaitInQueue( entity );
}
if ( !IsDefined( entity ) )
{
return;
}
entity.gib_time = GetTime();
if ( IsGibbed( entity, gibFlag ) )
{
return;
}
DestructServerUtils::ShowDestructedPieces( entity );
ShowHiddenGibPieces( entity );
if ( !(_GetGibbedState( entity ) < 16) )
{
legModel = _GetGibbedLegModel( entity );
entity Detach( legModel );
}
_SetGibbed( entity, gibFlag, undefined );
entity SetModel( _GetGibbedTorsoModel( entity ) );
entity Attach( _GetGibbedLegModel( entity ) );
DestructServerUtils::ReapplyDestructedPieces( entity );
ReapplyHiddenGibPieces( entity );
}
function private _GetGibbedLegModel( entity )
{
gibState = _GetGibbedState( entity );
rightLegGibbed = (gibState & 128);
leftLegGibbed = (gibState & 256);
if ( rightLegGibbed && leftLegGibbed)
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg4:entity.legDmg4);
}
else if ( rightLegGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg2:entity.legDmg2);
}
else if ( leftLegGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.legDmg3:entity.legDmg3);
}
return (isdefined(entity.gib_data)?entity.gib_data.legDmg1:entity.legDmg1);
}
function private _GetGibbedState( entity )
{
if ( IsDefined( entity.gib_state ) )
{
return entity.gib_state;
}
return 0;
}
function private _GetGibbedTorsoModel( entity )
{
gibState = _GetGibbedState( entity );
rightArmGibbed = (gibState & 16);
leftArmGibbed = (gibState & 32);
if ( rightArmGibbed && leftArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg2:entity.torsoDmg2);
// TODO(David Young 5-14-14): Currently AI's don't support both arms getting blown off.
// return GIB_TORSO_NO_ARMS_MODEL( entity );
}
else if ( rightArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg2:entity.torsoDmg2);
}
else if ( leftArmGibbed )
{
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg3:entity.torsoDmg3);
}
return (isdefined(entity.gib_data)?entity.gib_data.torsoDmg1:entity.torsoDmg1);
}
function private _HasGibDef( entity )
{
return IsDefined( entity.gibdef );
}
function private _HasGibPieces( entity, gibFlag )
{
hasGibPieces = false;
gibState = _GetGibbedState( entity );
entity.gib_state = (gibState | ( gibFlag & ( ( 1 << 9 ) - 1 ) ));
if ( IsDefined( _GetGibbedTorsoModel( entity ) ) &&
IsDefined( _GetGibbedLegModel( entity ) ) )
{
hasGibPieces = true;
}
entity.gib_state = gibState;
return hasGibPieces;
}
function private _SetGibbed( entity, gibFlag, gibDir )
{
if ( IsDefined( gibDir ) )
{
angles = VectortoAngles( gibDir );
yaw = angles[1];
yaw_bits = getbitsforangle( yaw, 3 );
entity.gib_state = (( _GetGibbedState( entity )|gibFlag & ( ( 1 << 9 ) - 1 ) ) + ( yaw_bits << 9 ) );
}
else
{
entity.gib_state = (_GetGibbedState( entity ) | ( gibFlag & ( ( 1 << 9 ) - 1 ) ));
}
entity.gibbed = true;
entity clientfield::set( "gib_state", entity.gib_state );
}
function Annihilate( entity )
{
if ( !_HasGibDef( entity ) )
{
return false;
}
gibBundle = struct::get_script_bundle("gibcharacterdef",entity.gibdef);
if ( !IsDefined( gibBundle ) || !IsDefined( gibBundle.gibs ) )
{
return false;
}
gibPieceStruct = gibBundle.gibs[ 2 ];
// Make sure there is some sort of FX to play if we're annihilating the AI.
if ( IsDefined( gibPieceStruct ) )
{
if ( IsDefined( gibPieceStruct.gibfx ) )
{
_SetGibbed( entity, 2, undefined );
entity thread _Annihilate( entity );
return true;
}
}
return false;
}
function CopyGibState( originalEntity, newEntity )
{
newEntity.gib_state = _GetGibbedState( originalEntity );
ToggleSpawnGibs( newEntity, false );
ReapplyHiddenGibPieces( newEntity );
}
function IsGibbed( entity, gibFlag )
{
return (_GetGibbedState( entity ) & gibFlag);
}
function GibHat( entity )
{
return _GibExtra( entity, 4 );
}
function GibHead( entity )
{
GibHat( entity );
return _GibExtra( entity, 8 );
}
function GibLeftArm( entity )
{
// TODO(David Young 5-14-14): Currently AI's don't support both arms getting blown off.
if ( IsGibbed( entity, 16 ) )
{
return false;
}
if ( _GibEntity( entity, 32 ) )
{
DestructServerUtils::DestructLeftArmPieces( entity );
return true;
}
return false;
}
function GibRightArm( entity )
{
// TODO(David Young 5-14-14): Currently AI's don't support both arms getting blown off.
if ( IsGibbed( entity, 32 ) )
{
return false;
}
if ( _GibEntity( entity, 16 ) )
{
DestructServerUtils::DestructRightArmPieces( entity );
entity thread shared::DropAIWeapon();
return true;
}
return false;
}
function GibLeftLeg( entity )
{
if ( _GibEntity( entity, 256 ) )
{
DestructServerUtils::DestructLeftLegPieces( entity );
return true;
}
return false;
}
function GibRightLeg( entity )
{
if ( _GibEntity( entity, 128 ) )
{
DestructServerUtils::DestructRightLegPieces( entity );
return true;
}
return false;
}
function GibLegs( entity )
{
if ( _GibEntity( entity, (128+256) ) )
{
DestructServerUtils::DestructRightLegPieces( entity );
DestructServerUtils::DestructLeftLegPieces( entity );
return true;
}
return false;
}
function PlayerGibLeftArm( entity )
{
if ( IsDefined( entity.body ) )
{
dir = (1,0,0);
_SetGibbed( entity.body, 32, dir );
}
}
function PlayerGibRightArm( entity )
{
if ( IsDefined( entity.body ) )
{
dir = (1,0,0);
_SetGibbed( entity.body, 16, dir );
}
}
function PlayerGibLeftLeg( entity )
{
if ( IsDefined( entity.body ) )
{
dir = (1,0,0);
_SetGibbed( entity.body, 256, dir );
}
}
function PlayerGibRightLeg( entity )
{
if ( IsDefined( entity.body ) )
{
dir = (1,0,0);
_SetGibbed( entity.body, 128, dir );
}
}
function PlayerGibLegs( entity )
{
if ( IsDefined( entity.body ) )
{
dir = (1,0,0);
_SetGibbed( entity.body, 128, dir );
_SetGibbed( entity.body, 256, dir );
}
}
function PlayerGibLeftArmVel( entity, dir )
{
if ( IsDefined( entity.body ) )
{
_SetGibbed( entity.body, 32, dir );
}
}
function PlayerGibRightArmVel( entity, dir )
{
if ( IsDefined( entity.body ) )
{
_SetGibbed( entity.body, 16, dir );
}
}
function PlayerGibLeftLegVel( entity, dir )
{
if ( IsDefined( entity.body ) )
{
_SetGibbed( entity.body, 256, dir );
}
}
function PlayerGibRightLegVel( entity, dir )
{
if ( IsDefined( entity.body ) )
{
_SetGibbed( entity.body, 128, dir );
}
}
function PlayerGibLegsVel( entity, dir )
{
if ( IsDefined( entity.body ) )
{
_SetGibbed( entity.body, 128, dir );
_SetGibbed( entity.body, 256, dir );
}
}
function ReapplyHiddenGibPieces( entity )
{
if ( !_HasGibDef( entity ) )
{
return;
}
gibBundle = struct::get_script_bundle("gibcharacterdef",entity.gibdef);
foreach ( gibFlag, gib in gibBundle.gibs )
{
if ( !IsGibbed( entity, gibFlag ) )
{
continue;
}
if ( IsDefined( gib.gibhidetag ) && IsAlive( entity ) && entity HasPart( gib.gibhidetag ) )
{
if ( !( isdefined( entity.skipDeath ) && entity.skipDeath ) )
{
// Gighidetag's are only used for hiding hitlocations. If the entity is already dead or skipping death animations don't apply them.
entity HidePart( gib.gibhidetag, "", true );
}
}
}
}
function ShowHiddenGibPieces( entity )
{
if ( !_HasGibDef( entity ) )
{
return;
}
gibBundle = struct::get_script_bundle("gibcharacterdef",entity.gibdef);
foreach ( gibFlag, gib in gibBundle.gibs )
{
if ( IsDefined( gib.gibhidetag ) && entity HasPart( gib.gibhidetag ) )
{
entity ShowPart( gib.gibhidetag, "", true );
}
}
}
function ToggleSpawnGibs( entity, shouldSpawnGibs )
{
if ( !shouldSpawnGibs )
{
entity.gib_state = _GetGibbedState( entity ) | 1;
}
else
{
entity.gib_state = _GetGibbedState( entity ) & ~1;
}
entity clientfield::set( "gib_state", entity.gib_state );
}
+57
View File
@@ -0,0 +1,57 @@
// Server side only
// Server side only
// Server side only
// 7 Damage states and 1 toggle bit to determine if gib models should be spawned on the client.
// Less than GIB_TORSO_RIGHT_ARM_FLAG
// Client/Server scriptbundle
+414
View File
@@ -0,0 +1,414 @@
#using scripts\shared\gameskill_shared;
#using scripts\shared\name_shared;
#using scripts\shared\util_shared;
#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\systems\shared;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\systems\weaponList;
Line truncated
#using_animtree("generic");
//--------------------------------------------------------------------------------
// Weapon Initialization
//--------------------------------------------------------------------------------
function initWeapon( weapon )
{
self.weaponInfo[weapon.name] = SpawnStruct();
self.weaponInfo[weapon.name].position = "none";
self.weaponInfo[weapon.name].hasClip = true;
if ( IsDefined( weapon.ClipModel ) )
self.weaponInfo[weapon.name].useClip = true;
else
self.weaponInfo[weapon.name].useClip = false;
}
//--------------------------------------------------------------------------------
// Main
//--------------------------------------------------------------------------------
function main()
{
self.a = SpawnStruct();
self.a.weaponPos = [];
// Init weapons to "none"
// This is done here for now, but might be better to
// just have the converter set the empty weapon slots to "none" instead of ""
if ( self.weapon == level.weaponNone )
self AiUtility::setCurrentWeapon( level.weaponNone );
// primary weapon
self AiUtility::setPrimaryWeapon(self.weapon);
// secondary weapon
if ( self.secondaryweapon == level.weaponNone )
self AiUtility::setSecondaryWeapon( level.weaponNone );
self AiUtility::setSecondaryWeapon( self.secondaryweapon );
self AiUtility::setCurrentWeapon(self.primaryweapon);
self.initial_primaryweapon = self.primaryweapon;
self.initial_secondaryweapon = self.secondaryweapon;
self initWeapon( self.primaryweapon );
self initWeapon( self.secondaryweapon );
self initWeapon( self.sidearm );
self.weapon_positions = array( "left", "right", "chest", "back" );
for (i = 0; i < self.weapon_positions.size; i++)
{
self.a.weaponPos[self.weapon_positions[i]] = level.weaponNone;
}
self.lastWeapon = self.weapon;
self thread beginGrenadeTracking();
self thread globalGrenadeTracking();
firstInit();
// AI_TODO - Proper ammo tracking for rocketlauncher AI's
self.a.rockets = 3;
self.a.rocketVisible = true;
// Set initial states for poses
self.a.pose = "stand";
self.a.prevPose = self.a.pose;
self.a.movement = "stop";
self.a.special = "none";
self.a.gunHand = "none"; // Initialize so that PutGunInHand works properly.
shared::placeWeaponOn( self.primaryweapon, "right" );
if ( isdefined( self.secondaryweaponclass ) && self.secondaryweaponclass != "none" && self.secondaryweaponclass != "pistol" )
shared::placeWeaponOn( self.secondaryweapon, "back");
self.a.combatEndTime = GetTime();
self.a.nextGrenadeTryTime = 0;
// set up all the aiming stuff
self.a.isAiming = false;
self.rightAimLimit = 45;
self.leftAimLimit = -45;
self.upAimLimit = 45;
self.downAimLimit = -45;
// setup the speed variables
self.walk = false;
self.sprint = false;
self.a.postScriptFunc = undefined;
// use the GDT settings to start with
self.baseAccuracy = self.accuracy;
// set default accuracy mod
if( !isdefined(self.script_accuracy) )
self.script_accuracy = 1;
// scale baseAccuracy based on number of coop players
if( self.team == "axis" || self.team == "team3" )
self thread gameskill::axisAccuracyControl();
else if (self.team == "allies")
self thread gameskill::alliesAccuracyControl();
self.a.missTime = 0;
self.bulletsInClip = self.weapon.clipSize;
// state tracking
self.lastEnemySightTime = 0; // last time we saw our current enemy
self.combatTime = 0; // how long we've been in/out of combat
self.suppressed = false; // if we're currently suppressed
self.suppressedTime = 0; // how long we've been in/out of suppression
if ( self.team == "allies" )
self.suppressionThreshold = 0.75;
else
self.suppressionThreshold = 0.5;
// Random range makes the grenades less accurate and do less damage, but also makes it difficult to throw back.
if ( self.team == "allies" )
self.randomGrenadeRange = 0;
else
self.randomGrenadeRange = 128;
self.reacquire_state = 0;
}
function setNameAndRank()
{
self endon ( "death" );
self name::get();
}
function DoNothing()
{
}
function set_anim_playback_rate()
{
self.animplaybackrate = 0.9 + RandomFloat( 0.2 );
self.moveplaybackrate = 1;
}
function trackVelocity()
{
self endon ("death");
for (;;)
{
self.oldOrigin = self.origin;
wait (0.2);
}
}
/#
function checkApproachAngles( transTypes )
{
idealTransAngles[1] = 45;
idealTransAngles[2] = 0;
idealTransAngles[3] = -45;
idealTransAngles[4] = 90;
idealTransAngles[6] = -90;
idealTransAngles[7] = 135;
idealTransAngles[8] = 180;
idealTransAngles[9] = -135;
wait .05;
for ( i = 1; i <= 9; i++ )
{
for ( j = 0; j < transTypes.size; j++ )
{
trans = transTypes[j];
idealAdd = 0;
if ( trans == "left" || trans == "left_crouch" )
{
idealAdd = 90;
}
else if ( trans == "right" || trans == "right_crouch" )
{
idealAdd = -90;
}
if ( isdefined( anim.coverTransAngles[ trans ][i] ) )
{
correctAngle = AngleClamp180( idealTransAngles[i] + idealAdd );
actualAngle = AngleClamp180( anim.coverTransAngles[ trans ][i] );
if ( AbsAngleClamp180( actualAngle - correctAngle ) > 7 )
{
println( "^1Cover approach animation has bad yaw delta: anim.coverTrans[\"" + trans + "\"][" + i + "]; is ^2" + actualAngle + "^1, should be closer to ^2" + correctAngle + "^1." );
}
}
}
}
for ( i = 1; i <= 9; i++ )
{
for ( j = 0; j < transTypes.size; j++ )
{
trans = transTypes[j];
idealAdd = 0;
if ( trans == "left" || trans == "left_crouch" )
{
idealAdd = 90;
}
else if ( trans == "right" || trans == "right_crouch" )
{
idealAdd = -90;
}
if ( isdefined( anim.coverExitAngles[ trans ][i] ) )
{
correctAngle = AngleClamp180( -1 * (idealTransAngles[i] + idealAdd + 180) );
actualAngle = AngleClamp180( anim.coverExitAngles[ trans ][i] );
if ( AbsAngleClamp180( actualAngle - correctAngle ) > 7 )
{
println( "^1Cover exit animation has bad yaw delta: anim.coverTrans[\"" + trans + "\"][" + i + "]; is ^2" + actualAngle + "^1, should be closer to ^2" + correctAngle + "^1." );
}
}
}
}
}
#/
function getExitSplitTime( approachType, dir )
{
return anim.coverExitSplit[ approachType ][ dir ];
}
function getTransSplitTime( approachType, dir )
{
return anim.coverTransSplit[ approachType ][ dir ];
}
function firstInit()
{
// Initialization that should happen once per level
if ( IsDefined(anim.NotFirstTime) ) // Use this to trigger the first init
{
return;
}
anim.NotFirstTime = true;
anim.grenadeTimers["player_frag_grenade_sp"] = randomIntRange( 1000, 20000 );
anim.grenadeTimers["player_flash_grenade_sp"] = randomIntRange( 1000, 20000 );
anim.grenadeTimers["player_double_grenade"] = randomIntRange( 10000, 60000 );
anim.grenadeTimers["AI_frag_grenade_sp"] = randomIntRange( 0, 20000 );
anim.grenadeTimers["AI_flash_grenade_sp"] = randomIntRange( 0, 20000 );
anim.numGrenadesInProgressTowardsPlayer = 0;
anim.lastGrenadeLandedNearPlayerTime = -1000000;
anim.lastFragGrenadeToPlayerStart = -1000000;
thread setNextPlayerGrenadeTime();
if ( !isdefined( level.flag ) )
{
level.flag = [];
}
level.painAI = undefined;
anim.coverCrouchLeanPitch = -55;
}
function onPlayerConnect()
{
player = self;
firstInit();
player.invul = false;
}
function setNextPlayerGrenadeTime()
{
waittillframeend;
// might not be defined if load::main() wasn't called
if ( isdefined( anim.playerGrenadeRangeTime ) )
{
maxTime = int( anim.playerGrenadeRangeTime * 0.7 );
if ( maxTime < 1 )
{
maxTime = 1;
}
anim.grenadeTimers["player_frag_grenade_sp"] = randomIntRange( 0, maxTime );
anim.grenadeTimers["player_flash_grenade_sp"] = randomIntRange( 0, maxTime );
}
if ( isdefined( anim.playerDoubleGrenadeTime ) )
{
maxTime = int( anim.playerDoubleGrenadeTime );
minTime = int( maxTime / 2 );
if ( maxTime <= minTime )
{
maxTime = minTime + 1;
}
anim.grenadeTimers["player_double_grenade"] = randomIntRange( minTime, maxTime );
}
}
function AddToMissiles(grenade)
{
if(!isdefined(level.missileEntities))level.missileEntities=[];
if ( !isdefined( level.missileEntities ) ) level.missileEntities = []; else if ( !IsArray( level.missileEntities ) ) level.missileEntities = array( level.missileEntities ); level.missileEntities[level.missileEntities.size]=grenade;;
while ( IsDefined(grenade) )
{
{wait(.05);};
}
ArrayRemoveValue(level.missileEntities,grenade);
}
function globalGrenadeTracking()
{
if(!isdefined(level.missileEntities))level.missileEntities=[];
self endon ( "death" );
self thread globalGrenadeLauncherTracking();
self thread globalMissileTracking();
for ( ;; )
{
self waittill ( "grenade_fire", grenade, weapon );
grenade.owner=self;
grenade.weapon = weapon;
level thread AddToMissiles(grenade);
}
}
function globalGrenadeLauncherTracking()
{
self endon ( "death" );
for ( ;; )
{
self waittill ( "grenade_launcher_fire", grenade, weapon );
grenade.owner=self;
grenade.weapon = weapon;
level thread AddToMissiles(grenade);
}
}
function globalMissileTracking()
{
self endon ( "death" );
for ( ;; )
{
self waittill ( "missile_fire", grenade, weapon );
grenade.owner=self;
grenade.weapon = weapon;
level thread AddToMissiles(grenade);
}
}
function beginGrenadeTracking()
{
self endon ( "death" );
for ( ;; )
{
self waittill ( "grenade_fire", grenade, weapon );
grenade thread grenade_earthQuake();
}
}
function endOnDeath()
{
self waittill( "death" );
waittillframeend;
self notify ( "end_explode" );
}
function grenade_earthQuake() //self == grenade
{
self thread endOnDeath();
self endon( "end_explode" );
self waittill( "explode", position );
PlayRumbleOnPosition( "grenade_rumble", position );
earthquake( 0.3, 0.5, position, 400 );
}
//--------------------------------------------------------------------------------
// end script
//--------------------------------------------------------------------------------
function end_script()
{
}
+42
View File
@@ -0,0 +1,42 @@
Line truncated
// Note that this script is called from the level script command animscripted, only for AI. If animscripted
// is done on a script model, this script is not called - startscriptedanim is called directly.
#using_animtree("generic");
function main()
{
self endon ("death");
self notify ("killanimscript");
self notify ("clearSuppressionAttack");
self.codeScripted["root"] = %body; // TEMP!
self endon ("end_sequence");
self.a.script = "scripted";
self waittill("killanimscript");
}
#using_animtree("generic");
function init(notifyName, origin, angles, theAnim, AnimMode, root, rate, goalTime, lerpTime)
{
}
//--------------------------------------------------------------------------------
// end script
//--------------------------------------------------------------------------------
function end_script()
{
// This callback will be called when AI will change states from AIS_SCRIPTED to AIS_BEHAVE
// i.e when it will go from animscripted state to behavior state
if( IsDefined( self.___ArchetypeOnBehaveCallback ) )
[[self.___ArchetypeOnBehaveCallback]]( self );
}
+532
View File
@@ -0,0 +1,532 @@
#using scripts\shared\ai_shared;
#using scripts\shared\math_shared;
#using scripts\shared\sound_shared;
#using scripts\shared\throttle_shared;
#using scripts\shared\util_shared;
#using scripts\shared\ai\systems\debug;
#using scripts\shared\ai\systems\init;
#using scripts\shared\ai\archetype_utility;
#using scripts\shared\ai\systems\weaponList;
Line truncated
#using_animtree("generic");
function autoexec main()
{
level.ai_weapon_throttle = new Throttle();
[[ level.ai_weapon_throttle ]]->Initialize( 1, 0.1 );
}
function private _throwStowedWeapon( entity, weapon, weaponModel )
{
entity waittill( "death" );
if(IsDefined(entity))
{
weaponModel Unlink();
entity shared::ThrowWeapon( weapon, getTagForPos( "back" ), false );
}
weaponModel Delete();
}
function StowWeapon( weapon, positionOffset, orientationOffset )
{
entity = self;
if ( !IsDefined( positionOffset ) )
{
positionOffset = (0, 0, 0);
}
if ( !IsDefined( orientationOffset ) )
{
orientationOffset = (0, 0, 0);
}
weaponModel = Spawn( "script_model", (0, 0, 0) );
weaponModel SetModel( weapon.worldmodel );
weaponModel LinkTo( entity, "tag_stowed_back", positionOffset, orientationOffset );
entity thread _throwStowedWeapon( entity, weapon, weaponModel );
}
function placeWeaponOn( weapon, position )
{
self notify("weapon_position_change");
if ( IsString( weapon ) )
{
weapon = GetWeapon( weapon );
}
if (!isdefined(self.weaponInfo[weapon.name]))
self init::initWeapon( weapon );
curPosition = self.weaponInfo[weapon.name].position;
// make sure we're not out of sync
assert( curPosition == "none" || self.a.weaponPos[curPosition] == weapon );
if ( !IsArray( self.a.weaponPos ) )
{
self.a.weaponPos = [];
}
assert( IsArray( self.a.weaponPos ) );
assert( position == "none" || IsDefined( self.a.weaponPos[position] ), "Weapon position \"" + position + "\"" );
assert( IsWeapon( weapon ) );
// weapon already in place
if ( position != "none" && self.a.weaponPos[position] == weapon )
return;
//println("detach all (" + weapon.name + ", " + position + ")");
self detachAllWeaponModels();
// detach if we're already in a position
if ( curPosition != "none" )
self detachWeapon( weapon );
// nothing more to do
if ( position == "none" )
{
//println("update(1) all (" + weapon.name + ", " + position + ")");
self updateAttachedWeaponModels();
self AiUtility::setCurrentWeapon( level.weaponNone );
return;
}
if ( self.a.weaponPos[position] != level.weaponNone )
self detachWeapon( self.a.weaponPos[position] );
// to ensure that the correct tags for the active weapon are used, we need to make sure it gets attached first
if ( position == "left" || position == "right" )
{
self updateScriptWeaponInfoAndPos( weapon, position );
self AiUtility::setCurrentWeapon(weapon);
}
else
{
self updateScriptWeaponInfoAndPos( weapon, position );
}
self updateAttachedWeaponModels();
// make sure we don't have a weapon in each hand
assert( self.a.weaponPos["left"] == level.weaponNone || self.a.weaponPos["right"] == level.weaponNone );
}
function detachWeapon( weapon )
{
self.a.weaponPos[self.weaponInfo[weapon.name].position] = level.weaponNone;
self.weaponInfo[weapon.name].position = "none";
}
function updateScriptWeaponInfoAndPos( weapon, position )
{
self.weaponInfo[weapon.name].position = position;
self.a.weaponPos[position] = weapon;
}
function detachAllWeaponModels()
{
if( isdefined(self.weapon_positions) )
{
for ( index = 0; index < self.weapon_positions.size; index++ )
{
weapon = self.a.weaponPos[self.weapon_positions[index]];
if ( weapon == level.weaponNone )
continue;
self SetActorWeapon( level.weaponNone, self GetActorWeaponOptions() );
}
}
}
function updateAttachedWeaponModels()
{
if( isdefined(self.weapon_positions) )
{
for ( index = 0; index < self.weapon_positions.size; index++ )
{
weapon = self.a.weaponPos[self.weapon_positions[index]];
if ( weapon == level.weaponNone )
continue;
// weapon should only be assigned (means self.weapon will be set) to the weapon if its set to go to "right"
if( self.weapon_positions[index] != "right" )
continue;
self SetActorWeapon( weapon, self GetActorWeaponOptions() );
if ( self.weaponInfo[weapon.name].useClip && !self.weaponInfo[weapon.name].hasClip )
self hidepart( "tag_clip" );
}
}
}
function getTagForPos( position )
{
switch ( position )
{
case "chest":
return "tag_weapon_chest";
case "back":
return "tag_stowed_back";
case "left":
return "tag_weapon_left";
case "right":
return "tag_weapon_right";
case "hand":
return "tag_inhand";
default:
assertMsg( "unknown weapon placement position: " + position );
break;
}
}
function ThrowWeapon( weapon, positionTag, scavenger )
{
waitTime = 0.1;
linearScalar = 2.0;
angularScalar = 10;
// Calculate the linear and angular velocity to launch the weapon.
startPosition = self GetTagOrigin( positionTag );
startAngles = self GetTagAngles( positionTag );
// Wait two server frames to calculate velocity.
wait( waitTime );
if ( IsDefined( self ) )
{
endPosition = self GetTagOrigin( positionTag );
endAngles = self GetTagAngles( positionTag );
linearVelocity = ( endPosition - startPosition ) * ( 1.0 / waitTime ) * linearScalar;
angularVelocity = VectorNormalize( endAngles - startAngles ) * angularScalar;
throwWeapon = self DropWeapon( weapon, positionTag, linearVelocity, angularVelocity, scavenger );
if ( IsDefined( throwWeapon ) )
{
throwWeapon SetContents( throwWeapon SetContents( 0 ) & ~( (1 << 15) | (1 << 26) | (1 << 23) | (1 << 25) ) );
}
return throwWeapon;
}
}
function DropAIWeapon()
{
self endon("death");
if( self.weapon == level.weaponNone )
{
return;
}
if (( isdefined( self.script_nodropsecondaryweapon ) && self.script_nodropsecondaryweapon ) && (self.weapon == self.initial_secondaryweapon))
{
/#PrintLn("Not dropping secondary weapon '" + self.weapon.name + "'");#/
return;
}
else if (( isdefined( self.script_nodropsidearm ) && self.script_nodropsidearm ) && (self.weapon == self.sidearm))
{
/#PrintLn("Not dropping sidearm '" + self.weapon.name + "'");#/
return;
}
[[ level.ai_weapon_throttle ]]->WaitInQueue( self );
current_weapon = self.weapon;
dropWeaponName = player_weapon_drop( current_weapon );
position = self.weaponInfo[ current_weapon.name ].position;
shouldDropWeapon = !IsDefined( self.dontDropWeapon ) || self.dontDropWeapon === false;
if( current_weapon.isScavengable == false )
{
shouldDropWeapon = false;
}
if ( shouldDropWeapon && self.dropWeapon )
{
self.dontDropWeapon = true;
positionTag = getTagForPos( position );
ThrowWeapon( dropWeaponName, positionTag, false );
}
if ( self.weapon != level.weaponNone )
{
shared::placeWeaponOn( current_weapon, "none" );
if( self.weapon == self.primaryweapon )
{
self AiUtility::setPrimaryWeapon( level.weaponNone );
}
else if( self.weapon == self.secondaryweapon )
{
self AiUtility::setSecondaryWeapon( level.weaponNone );
}
}
self AiUtility::setCurrentWeapon( level.weaponNone );
}
function DropAllAIWeapons()
{
if (( isdefined( self.a.dropping_weapons ) && self.a.dropping_weapons ))
{
// already called
return;
}
if( !self.dropweapon )
{
if( self.weapon != level.weaponNone )
{
shared::placeWeaponOn( self.weapon, "none" );
self AiUtility::setCurrentWeapon( level.weaponNone );
}
return;
}
self.a.dropping_weapons = true;
self detachAllWeaponModels();
droppedSideArm = false;
if( isdefined(self.weapon_positions) )
{
for ( index = 0; index < self.weapon_positions.size; index++ )
{
weapon = self.a.weaponPos[ self.weapon_positions[ index ] ];
if ( weapon != level.weaponNone )
{
self.weaponInfo[ weapon.name ].position = "none";
self.a.weaponPos[ self.weapon_positions[ index ] ] = level.weaponNone;
if (( isdefined( self.script_nodropsecondaryweapon ) && self.script_nodropsecondaryweapon ) && (weapon == self.initial_secondaryweapon))
{
/#PrintLn("Not dropping secondary weapon '" + weapon.name + "'");#/
}
else if (( isdefined( self.script_nodropsidearm ) && self.script_nodropsidearm ) && (weapon == self.sidearm))
{
/#PrintLn("Not dropping sidearm '" + weapon.name + "'");#/
}
else
{
velocity = self GetVelocity();
speed = Length( velocity ) * 0.5;
weapon = player_weapon_drop(weapon);
droppedWeapon = self DropWeapon( weapon, self.weapon_positions[ index ], speed );
if ( self.sideArm != level.weaponNone )
{
if ( weapon == self.sideArm )
droppedSideArm = true;
}
}
}
}
}
if( !droppedSideArm && self.sideArm != level.weaponNone )
{
// 10% chance of dropping sidearm
if( RandomInt(100) <= 10 )
{
velocity = self GetVelocity();
speed = Length( velocity ) * 0.5;
droppedWeapon = self DropWeapon( self.sideArm, "chest", speed );
}
}
self AiUtility::setCurrentWeapon( level.weaponNone );
self.a.dropping_weapons = undefined;
}
function player_weapon_drop( weapon )
{
if ( IsSubStr( weapon.name, "rpg" ) )
{
return GetWeapon( "rpg_player" );
}
return weapon;
}
function HandleNoteTrack( note, flagName, customFunction, var1 )
{
}
// DoNoteTracks waits for and responds to standard noteTracks on the animation, returning when it gets an "end" or a "finish"
// For level scripts, a pointer to a custom function should be passed as the second argument, which handles notetracks not
// already handled by the generic function. This call should take the form DoNoteTracks(flagName,&customFunction);
// The custom function will be called for each notetrack not recognized, and will pass the notetrack name. Note that this
// function could be called multiple times for a single animation.
function DoNoteTracks( flagName, customFunction, debugIdentifier, var1 ) // debugIdentifier isn't even used. we should get rid of it.
{
for (;;)
{
self waittill (flagName, note);
if ( !isdefined( note ) )
{
note = "undefined";
}
val = self HandleNoteTrack( note, flagName, customFunction, var1 );
if ( isdefined( val ) )
{
return val;
}
}
}
function DoNoteTracksIntercept( flagName, interceptFunction, debugIdentifier ) // debugIdentifier isn't even used. we should get rid of it.
{
assert( isdefined( interceptFunction ) );
for (;;)
{
self waittill ( flagName, note );
if ( !isdefined( note ) )
{
note = "undefined";
}
intercepted = [[interceptFunction]]( note );
if ( isdefined( intercepted ) && intercepted )
{
continue;
}
val = self HandleNoteTrack( note, flagName );
if ( isdefined( val ) )
{
return val;
}
}
}
function DoNoteTracksPostCallback( flagName, postFunction )
{
assert( isdefined( postFunction ) );
for (;;)
{
self waittill ( flagName, note );
if ( !isdefined( note ) )
{
note = "undefined";
}
val = self HandleNoteTrack( note, flagName );
[[postFunction]]( note );
if ( isdefined( val ) )
{
return val;
}
}
}
// Don't call this function except as a thread you're going to kill - it lasts forever.
function DoNoteTracksForever(flagName, killString, customFunction, debugIdentifier)
{
DoNoteTracksForeverProc(&DoNoteTracks, flagName, killString, customFunction, debugIdentifier);
}
function DoNoteTracksForeverIntercept(flagName, killString, interceptFunction, debugIdentifier)
{
DoNoteTracksForeverProc(&DoNoteTracksIntercept, flagName, killString, interceptFunction, debugIdentifier );
}
function DoNoteTracksForeverProc( notetracksFunc, flagName, killString, customFunction, debugIdentifier )
{
if (isdefined (killString))
{
self endon (killString);
}
self endon ("killanimscript");
if (!isdefined(debugIdentifier))
{
debugIdentifier = "undefined";
}
for (;;)
{
time = GetTime();
returnedNote = [[notetracksFunc]](flagName, customFunction, debugIdentifier);
timetaken = GetTime() - time;
if ( timetaken < 0.05)
{
time = GetTime();
returnedNote = [[notetracksFunc]](flagName, customFunction, debugIdentifier);
timetaken = GetTime() - time;
if ( timetaken < 0.05)
{
/#println (GetTime()+" "+debugIdentifier+" shared::DoNoteTracksForever is trying to cause an infinite loop on anim "+flagName+", returned "+returnedNote+".");#/
wait ( 0.05 - timetaken );
}
}
//(GetTime()+" "+debugIdentifier+" DoNoteTracksForever returned in "+timetaken+" ms.");#/
}
}
// Designed for using DoNoteTracks on looping animations, so you can wait for a time instead of the "end" parameter
function DoNoteTracksForTime(time, flagName, customFunction, debugIdentifier)
{
ent = SpawnStruct();
ent thread doNoteTracksForTimeEndNotify(time);
DoNoteTracksForTimeProc(&DoNoteTracksForever, time, flagName, customFunction, debugIdentifier, ent);
}
function DoNoteTracksForTimeIntercept( time, flagName, interceptFunction, debugIdentifier)
{
ent = SpawnStruct();
ent thread doNoteTracksForTimeEndNotify(time);
DoNoteTracksForTimeProc(&DoNoteTracksForeverIntercept, time, flagName, interceptFunction, debugIdentifier, ent);
}
function DoNoteTracksForTimeProc( doNoteTracksForeverFunc, time, flagName, customFunction, debugIdentifier, ent)
{
ent endon ("stop_notetracks");
[[doNoteTracksForeverFunc]](flagName, undefined, customFunction, debugIdentifier);
}
function doNoteTracksForTimeEndNotify(time)
{
wait (time);
self notify ("stop_notetracks");
}
View File
Whitespace-only changes.