iw6-scripts-dev/maps/mp/agents/_agent_utility.gsc
2024-12-11 11:28:08 +01:00

447 lines
12 KiB
Plaintext

#include common_scripts\utility;
#include maps\mp\_utility;
//========================================================
// agentFunc
//========================================================
agentFunc( func_name )
{
assert( IsAgent( self ) );
assert( IsDefined(func_name) );
assert( isDefined(self.agent_type) );
assert( isDefined(level.agent_funcs[self.agent_type]) );
assert( isDefined(level.agent_funcs[self.agent_type][func_name]) );
return level.agent_funcs[self.agent_type][func_name];
}
//========================================================
// set_agent_team
//========================================================
set_agent_team( team, optional_owner )
{
// since an agent entity has both a "sentient" and an "agent", we need both
// these to understand the team the entity is on (much as client entities
// have a "sentient" and a "client"). The "team" field sets the "sentient"
// team and the "agentteam" field sets the "agent" team.
self.team = team;
self.agentteam = team;
self.pers["team"] = team;
self.owner = optional_owner;
self SetOtherEnt( optional_owner );
self SetEntityOwner( optional_owner );
}
//=======================================================
// initAgentScriptVariables
//=======================================================
initAgentScriptVariables()
{
self.agent_type = "player"; // TODO: communicate this to code?
self.pers = [];
self.hasDied = false;
self.isActive = false;
self.isAgent = true;
self.wasTI = false;
self.isSniper = false;
self.spawnTime = 0;
self.entity_number = self GetEntityNumber();
self.agent_teamParticipant = false;
self.agent_gameParticipant = false;
self.canPerformClientTraces = false;
self.agentname = undefined;
self DetachAll();
self initPlayerScriptVariables( false );
}
//========================================================
// initPlayerScriptVariables
//========================================================
initPlayerScriptVariables( asPlayer )
{
if ( !asPlayer )
{
// Not as a player
self.class = undefined;
self.lastClass = undefined;
self.moveSpeedScaler = undefined;
self.avoidKillstreakOnSpawnTimer = undefined;
self.guid = undefined;
self.name = undefined;
self.saved_actionSlotData = undefined;
self.perks = undefined;
self.weaponList = undefined;
self.omaClassChanged = undefined;
self.objectiveScaler = undefined;
self.touchTriggers = undefined;
self.carryObject = undefined;
self.claimTrigger = undefined;
self.canPickupObject = undefined;
self.killedInUse = undefined;
self.sessionteam = undefined;
self.sessionstate = undefined;
self.lastSpawnTime = undefined;
self.lastspawnpoint = undefined;
self.disabledWeapon = undefined;
self.disabledWeaponSwitch = undefined;
self.disabledOffhandWeapons = undefined;
self.disabledUsability = undefined;
self.shieldDamage = undefined;
self.shieldBulletHits = undefined;
self.recentShieldXP = undefined;
}
else
{
// As a player
self.moveSpeedScaler = 1;
self.avoidKillstreakOnSpawnTimer = 5;
self.guid = self getUniqueId();
self.name = self.guid;
self.sessionteam = self.team;
self.sessionstate = "playing";
self.shieldDamage = 0;
self.shieldBulletHits = 0;
self.recentShieldXP = 0;
self.agent_gameParticipant = true; // If initialized as a player, always make agent a game participant
self maps\mp\gametypes\_playerlogic::setupSavedActionSlots();
self thread maps\mp\perks\_perks::onPlayerSpawned();
if ( IsGameParticipant( self ) )
{
self.objectiveScaler = 1;
self maps\mp\gametypes\_gameobjects::init_player_gameobjects();
self.disabledWeapon = 0;
self.disabledWeaponSwitch = 0;
self.disabledOffhandWeapons = 0;
}
}
self.disabledUsability = 1;
}
//===========================================
// getFreeAgent
//===========================================
getFreeAgent( agent_type )
{
freeAgent = undefined;
if( IsDefined( level.agentArray ) )
{
foreach( agent in level.agentArray )
{
if( !IsDefined( agent.isActive ) || !agent.isActive )
{
if ( IsDefined(agent.waitingToDeactivate) && agent.waitingToDeactivate )
continue;
freeAgent = agent;
freeAgent initAgentScriptVariables();
if ( IsDefined( agent_type ) )
freeAgent.agent_type = agent_type; // TODO: communicate this to code?
break;
}
}
}
return freeAgent;
}
//=======================================================
// activateAgent
//=======================================================
activateAgent()
{
/#
if ( !self.isActive )
{
// Activating this agent, ensure that he has connected on the same frame
AssertEx(self.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in between connectNewAgent and spawning the agent");
}
#/
self.isActive = true;
}
//=======================================================
// deactivateAgent
//=======================================================
deactivateAgent()
{
self thread deactivateAgentDelayed();
}
//=======================================================
// deactivateAgentDelayed
//=======================================================
deactivateAgentDelayed()
{
self notify("deactivateAgentDelayed");
self endon("deactivateAgentDelayed");
// During the 0.05s wait in deactivateAgentDelayed, the agent's script variables are all cleared out
// So we need to do this now while IsGameParticipant can still be checked
if ( IsGameParticipant(self) )
self maps\mp\gametypes\_spawnlogic::removeFromParticipantsArray();
self maps\mp\gametypes\_spawnlogic::removeFromCharactersArray();
// Wait till next frame before we "disconnect"
// That way things waiting on "death" but have endon("disconnect") will still function
// e.g. maps\mp\killstreaks\_juggernaut::juggRemover()
wait 0.05;
self.isActive = false;
self.hasDied = false;
self.owner = undefined;
self.connectTime = undefined;
self.waitingToDeactivate = undefined;
// Clear this agent from any other character's attackers array
foreach ( character in level.characters )
{
if ( IsDefined( character.attackers ) )
{
foreach ( index, attacker in character.attackers )
{
if ( attacker == self )
character.attackers[index] = undefined;
}
}
}
if ( IsDefined( self.headModel ) )
{
self Detach( self.headModel );
self.headModel = undefined;
}
self notify("disconnect");
}
//===========================================
// getNumActiveAgents
//===========================================
getNumActiveAgents( type )
{
if ( !IsDefined(type) )
type = "all";
agents = getActiveAgentsOfType(type);
return agents.size;
}
//===========================================
// getActiveAgentsOfType
//===========================================
getActiveAgentsOfType( type )
{
Assert(IsDefined(type));
agents = [];
if ( !IsDefined( level.agentArray ) )
return agents;
foreach ( agent in level.agentArray )
{
if ( IsDefined( agent.isActive ) && agent.isActive )
{
if ( type == "all" || agent.agent_type == type )
agents[agents.size] = agent;
}
}
return agents;
}
//===========================================
// getNumOwnedActiveAgents
//===========================================
getNumOwnedActiveAgents( player )
{
return getNumOwnedActiveAgentsByType( player, "all" );
}
//===========================================
// getNumOwnedActiveAgentsByType
//===========================================
getNumOwnedActiveAgentsByType( player, type )
{
Assert(IsDefined(type));
numOwnedActiveAgents = 0;
if( !IsDefined(level.agentArray) )
{
return numOwnedActiveAgents;
}
foreach( agent in level.agentArray )
{
if( IsDefined( agent.isActive ) && agent.isActive )
{
if ( IsDefined(agent.owner) && (agent.owner == player) )
{
// Adding exclusion for "alien" type from a request for "all" to prevent the Seeker killstreak in mp_dome_ns from overloading the max allowable agents per player.
if ( ( type == "all" && agent.agent_type != "alien" ) || agent.agent_type == type )
numOwnedActiveAgents++;
}
}
}
return numOwnedActiveAgents;
}
//=======================================================
// getValidSpawnPathNodeNearPlayer
//=======================================================
getValidSpawnPathNodeNearPlayer( bDoPhysicsTraceToPlayer, bDoPhysicsTraceToValidateNode ) // self = player
{
assert( isPlayer( self ) );
nodeArray = GetNodesInRadius( self.origin, 350, 64, 128, "Path" );
if( !IsDefined(nodeArray) || (nodeArray.size == 0) )
{
return undefined;
}
if ( IsDefined(level.waterDeleteZ) && IsDefined(level.trigUnderWater) )
{
// Ignore any nodes where the agent would die immediately upon spawning
nodeArrayOld = nodeArray;
nodeArray = [];
foreach( node in nodeArrayOld )
{
if ( node.origin[ 2 ] > level.waterDeleteZ || !IsPointInVolume( node.origin, level.trigUnderWater ) )
nodeArray[nodeArray.size] = node;
}
}
playerDirection = AnglesToForward( self.angles );
bestDot = -10;
playerHeight = maps\mp\gametypes\_spawnlogic::getPlayerTraceHeight( self );
zOffset = ( 0, 0, playerHeight );
if ( !IsDefined(bDoPhysicsTraceToPlayer) )
bDoPhysicsTraceToPlayer = false;
if ( !IsDefined(bDoPhysicsTraceToValidateNode) )
bDoPhysicsTraceToValidateNode = false;
pathNodeSortedByDot = [];
pathNodeDotValues = [];
foreach( pathNode in nodeArray )
{
if ( !pathNode DoesNodeAllowStance("stand") || isDefined ( pathnode.no_agent_spawn) )
continue;
directionToNode = VectorNormalize( pathNode.origin - self.origin );
dot = VectorDot( playerDirection, directionToNode );
i = 0;
for ( ; i < pathNodeDotValues.size; i++ )
{
if ( dot > pathNodeDotValues[i] )
{
for ( j = pathNodeDotValues.size; j > i; j-- )
{
pathNodeDotValues[j] = pathNodeDotValues[j-1];
pathNodeSortedByDot[j] = pathNodeSortedByDot[j-1];
}
break;
}
}
pathNodeSortedByDot[i] = pathNode;
pathNodeDotValues[i] = dot;
}
// pick a path node in the player's view
for ( i = 0; i < pathNodeSortedByDot.size; i++ )
{
pathNode = pathNodeSortedByDot[i];
traceStart = self.origin + zOffset;
traceEnd = pathNode.origin + zOffset;
if ( i > 0 )
wait(0.05); // Spread out the traces across multiple frames
// prevent selecting a node that the player cannot see
if( !SightTracePassed( traceStart, traceEnd, false, self ) )
{
continue;
}
if ( bDoPhysicsTraceToValidateNode )
{
if ( i > 0 )
wait(0.05); // Spread out the traces across multiple frames
hitPos = PlayerPhysicsTrace( pathNode.origin + zOffset, pathNode.origin );
if ( DistanceSquared( hitPos, pathNode.origin ) > 1 )
continue;
}
if ( bDoPhysicsTraceToPlayer )
{
if ( i > 0 )
wait(0.05); // Spread out the traces across multiple frames
hitPos = PhysicsTrace( traceStart, traceEnd );
if ( DistanceSquared( hitPos, traceEnd ) > 1 )
continue;
}
return pathNode;
}
// always return a node for safeguard
if( (pathNodeSortedByDot.size > 0) && IsDefined(level.isHorde) )
return pathNodeSortedByDot[0];
}
//=======================================================
// killAgent
//=======================================================
killAgent( agent )
{
// do enough damage to kill the agent regardless of any damage mitigation
agent DoDamage( agent.health + 500000, agent.origin );
}
//=======================================================
// killDog
//=======================================================
killDog() // self == dog
{
self [[ self agentFunc( "on_damaged" ) ]](
level, // eInflictor The entity that causes the damage.(e.g. a turret)
undefined, // eAttacker The entity that is attacking.
self.health + 1, // iDamage Integer specifying the amount of damage done
0, // iDFlags Integer specifying flags that are to be applied to the damage
"MOD_CRUSH", // sMeansOfDeath Integer specifying the method of death
"none", // sWeapon The weapon number of the weapon used to inflict the damage
( 0, 0, 0 ), // vPoint The point the damage is from?
(0, 0, 0), // vDir The direction of the damage
"none", // sHitLoc The location of the hit
0 // psOffsetTime The time offset for the damage
);
}