init
This commit is contained in:
921 files changed
+337975
No files matched your search
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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!
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
}
|
||||
@@ -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 );
|
||||
}
|
||||
@@ -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");
|
||||
}
|
||||
|
||||
Whitespace-only changes.
Reference in new issue
Block a user