init
This commit is contained in:
129
raw/maps/mp/agents/_agent_common.gsc
Normal file
129
raw/maps/mp/agents/_agent_common.gsc
Normal file
@ -0,0 +1,129 @@
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentAdded
|
||||
//=======================================================
|
||||
CodeCallback_AgentAdded()
|
||||
{
|
||||
self initAgentScriptVariables();
|
||||
|
||||
agentTeam = "axis";
|
||||
|
||||
if( (level.numagents % 2) == 0 )
|
||||
{
|
||||
agentTeam = "allies";
|
||||
}
|
||||
|
||||
level.numagents++;
|
||||
self set_agent_team( agentTeam );
|
||||
|
||||
level.agentArray[ level.agentArray.size ] = self;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentDamaged
|
||||
//=======================================================
|
||||
CodeCallback_AgentDamaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
eAttacker = _validateAttacker( eAttacker );
|
||||
|
||||
self [[ self agentFunc( "on_damaged" ) ]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// CodeCallback_AgentKilled
|
||||
//=======================================================
|
||||
CodeCallback_AgentKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
eAttacker = _validateAttacker( eAttacker );
|
||||
|
||||
self thread [[ self agentFunc("on_killed") ]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration);
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// init
|
||||
//========================================================
|
||||
init()
|
||||
{
|
||||
initAgentLevelVariables();
|
||||
|
||||
// add all the agents we're supposed to have in the game with us
|
||||
level thread add_agents_to_game();
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// connectNewAgent
|
||||
//=======================================================
|
||||
connectNewAgent( agent_type, team, class )
|
||||
{
|
||||
agent = getFreeAgent( agent_type );
|
||||
|
||||
if ( IsDefined( agent ) )
|
||||
{
|
||||
agent.connectTime = GetTime();
|
||||
|
||||
if ( IsDefined( team ) )
|
||||
agent set_agent_team( team );
|
||||
else
|
||||
agent set_agent_team( agent.team );
|
||||
|
||||
if ( IsDefined( class ) )
|
||||
agent.class_override = class;
|
||||
|
||||
if( IsDefined(level.agent_funcs[agent_type]["onAIConnect"]) )
|
||||
agent [[ agent agentFunc("onAIConnect") ]]();
|
||||
|
||||
agent maps\mp\gametypes\_spawnlogic::addToCharactersArray();
|
||||
|
||||
AssertEx(agent.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in connectNewAgent");
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// initAgentLevelVariables
|
||||
//========================================================
|
||||
initAgentLevelVariables()
|
||||
{
|
||||
level.agentArray = [];
|
||||
level.numagents = 0;
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// add_agents_to_game
|
||||
//========================================================
|
||||
add_agents_to_game()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
level waittill("connected", player);
|
||||
|
||||
maxagents = GetMaxAgents();
|
||||
|
||||
while( level.agentArray.size < maxagents )
|
||||
{
|
||||
agent = AddAgent();
|
||||
|
||||
if( !IsDefined( agent) )
|
||||
{
|
||||
waitframe();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// set_agent_health
|
||||
//========================================================
|
||||
set_agent_health( health )
|
||||
{
|
||||
self.agenthealth = health;
|
||||
self.health = health;
|
||||
self.maxhealth = health;
|
||||
}
|
458
raw/maps/mp/agents/_agent_utility.gsc
Normal file
458
raw/maps/mp/agents/_agent_utility.gsc
Normal file
@ -0,0 +1,458 @@
|
||||
#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.ignoreall = false;
|
||||
self.ignoreme = false;
|
||||
|
||||
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;
|
||||
}
|
||||
else
|
||||
{
|
||||
// As a player
|
||||
self.moveSpeedScaler = level.basePlayerMoveScale;
|
||||
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.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 ) && ( !IsDefined( agent.isReserved ) || !agent.isReserved ) )
|
||||
{
|
||||
if ( IsDefined(agent.waitingToDeactivate) && agent.waitingToDeactivate )
|
||||
continue;
|
||||
|
||||
if ( IsDefined(level.despawning_agents) && array_contains(level.despawning_agents,agent) )
|
||||
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");
|
||||
|
||||
if ( !IsDefined(level.despawning_agents) )
|
||||
level.despawning_agents = [];
|
||||
|
||||
if ( !array_contains(level.despawning_agents,self) )
|
||||
level.despawning_agents = array_add(level.despawning_agents,self);
|
||||
|
||||
// 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();
|
||||
|
||||
//println("** " + GetTime() + " About to clear agent script vars (1) for agent " + self GetEntityNumber() + " of type " + self.agent_type );
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.headModel = undefined;
|
||||
self DetachAll();
|
||||
|
||||
self notify("disconnect");
|
||||
|
||||
//println("** " + GetTime() + " About to clear agent script vars (2) for agent " + self GetEntityNumber() );
|
||||
self AgentClearScriptVars(); // This needs to be the last line, anything after this won't execute correctly because the entity has been cleared
|
||||
|
||||
level.despawning_agents = array_remove(level.despawning_agents,self);
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// 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) )
|
||||
{
|
||||
if ( type == "all" || 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") )
|
||||
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
|
||||
);
|
||||
}
|
557
raw/maps/mp/agents/_agents.gsc
Normal file
557
raw/maps/mp/agents/_agents.gsc
Normal file
@ -0,0 +1,557 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
//=======================================================================
|
||||
// main
|
||||
// This is functions is called directly from native code on game startup
|
||||
// The particular gametype's main() is called from native code afterward
|
||||
//=======================================================================
|
||||
main()
|
||||
{
|
||||
if( IsDefined( level.createFX_enabled ) && level.createFX_enabled )
|
||||
return;
|
||||
|
||||
setup_callbacks();
|
||||
|
||||
// Enable badplaces in destructibles
|
||||
level.badplace_cylinder_func = ::badplace_cylinder;
|
||||
level.badplace_delete_func = ::badplace_delete;
|
||||
|
||||
/#
|
||||
level thread monitor_scr_agent_players();
|
||||
#/
|
||||
|
||||
level thread maps\mp\agents\_agent_common::init();
|
||||
if ( !InVirtualLobby() )
|
||||
{
|
||||
level thread maps\mp\killstreaks\_agent_killstreak::init();
|
||||
level thread maps\mp\killstreaks\_dog_killstreak::init();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// setup_callbacks
|
||||
//========================================================
|
||||
setup_callbacks()
|
||||
{
|
||||
if ( !IsDefined( level.agent_funcs ) )
|
||||
level.agent_funcs = [];
|
||||
|
||||
level.agent_funcs["player"] = [];
|
||||
|
||||
level.agent_funcs["player"]["spawn"] = ::spawn_agent_player;
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_war::bot_war_think;
|
||||
level.agent_funcs["player"]["on_killed"] = ::on_agent_player_killed;
|
||||
level.agent_funcs["player"]["on_damaged"] = ::on_agent_player_damaged;
|
||||
level.agent_funcs["player"]["on_damaged_finished"] = ::agent_damage_finished;
|
||||
|
||||
maps\mp\killstreaks\_agent_killstreak::setup_callbacks();
|
||||
maps\mp\killstreaks\_dog_killstreak::setup_callbacks();
|
||||
}
|
||||
|
||||
wait_till_agent_funcs_defined()
|
||||
{
|
||||
while( !IsDefined(level.agent_funcs) )
|
||||
wait(0.05);
|
||||
}
|
||||
|
||||
|
||||
/#
|
||||
//=======================================================
|
||||
// new_scr_agent_team
|
||||
//=======================================================
|
||||
new_scr_agent_team()
|
||||
{
|
||||
teamCounts = [];
|
||||
teamCounts["allies"] = 0;
|
||||
teamCounts["axis"] = 0;
|
||||
minTeam = undefined;
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if ( !IsDefined( teamCounts[player.team] ) )
|
||||
teamCounts[player.team] = 0;
|
||||
if ( IsTeamParticipant( player ) )
|
||||
teamCounts[player.team]++;
|
||||
}
|
||||
foreach ( team, count in teamCounts )
|
||||
{
|
||||
if ( (team != "spectator") && (!IsDefined(minTeam) || teamCounts[minTeam] > count) )
|
||||
minTeam = team;
|
||||
}
|
||||
|
||||
return minTeam;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// monitor_scr_agent_players
|
||||
//=======================================================
|
||||
monitor_scr_agent_players()
|
||||
{
|
||||
SetDevDvarIfUninitialized( "scr_agent_players_add", "0" );
|
||||
SetDevDvarIfUninitialized( "scr_agent_players_drop", "0" );
|
||||
|
||||
while(level.players.size == 0)
|
||||
wait(0.05); // Agents don't exist until a player connects
|
||||
|
||||
while(!IsDefined(level.bot_loadouts_initialized))
|
||||
wait(0.05); // Agents need to wait until bot loadouts have been initialized
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait(0.1);
|
||||
|
||||
add_agent_players = getdvarInt("scr_agent_players_add");
|
||||
drop_agent_players = getdvarInt("scr_agent_players_drop");
|
||||
|
||||
if ( add_agent_players != 0 )
|
||||
SetDevDvar( "scr_agent_players_add", 0 );
|
||||
|
||||
if ( drop_agent_players != 0 )
|
||||
SetDevDvar( "scr_agent_players_drop", 0 );
|
||||
|
||||
for ( i = 0; i < add_agent_players; i++ )
|
||||
{
|
||||
agent = add_humanoid_agent( "player", new_scr_agent_team(), undefined, undefined, undefined, undefined, true, true, "follow_code_and_dev_dvar" );
|
||||
if ( IsDefined( agent ) )
|
||||
agent.agent_teamParticipant = true;
|
||||
}
|
||||
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( !IsDefined( agent.isActive ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( agent.isActive ) && agent.isActive && agent.agent_type == "player" )
|
||||
{
|
||||
if ( drop_agent_players > 0 )
|
||||
{
|
||||
agent.respawn_on_death = undefined;
|
||||
agent Suicide();
|
||||
Assert(agent.health == 0); // Ensure that Suicide worked
|
||||
drop_agent_players--;
|
||||
wait(0.1);
|
||||
Assert(!IsDefined(agent.isActive) || !agent.isActive); // Agent should have been deactivated already
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
//=======================================================
|
||||
// add_humanoid_agent
|
||||
//=======================================================
|
||||
add_humanoid_agent( agent_type, team, class, optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( agent_type, team, class );
|
||||
|
||||
if( IsDefined( agent ) )
|
||||
{
|
||||
agent thread [[ agent agentFunc("spawn") ]]( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty );
|
||||
}
|
||||
|
||||
return agent;
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// spawn_agent_player
|
||||
//========================================================
|
||||
spawn_agent_player( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while( !IsDefined(level.getSpawnPoint) )
|
||||
{
|
||||
waitframe();
|
||||
}
|
||||
|
||||
if( self.hasDied )
|
||||
{
|
||||
wait( RandomIntRange(6, 10) );
|
||||
}
|
||||
|
||||
self initPlayerScriptVariables( true );
|
||||
|
||||
// allow killstreaks to pass in specific spawn locations
|
||||
if( IsDefined(optional_spawnOrigin) && IsDefined(optional_spawnAngles) )
|
||||
{
|
||||
spawnOrigin = optional_spawnOrigin;
|
||||
spawnAngles = optional_spawnAngles;
|
||||
|
||||
self.lastSpawnPoint = SpawnStruct();
|
||||
self.lastSpawnPoint.origin = spawnOrigin;
|
||||
self.lastSpawnPoint.angles = spawnAngles;
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
|
||||
// Player specific variables needed in damage processing
|
||||
self.lastSpawnPoint = spawnpoint;
|
||||
}
|
||||
self activateAgent();
|
||||
self.lastSpawnTime = GetTime();
|
||||
self.spawnTime = GetTime();
|
||||
|
||||
phys_trace_start = spawnOrigin + (0,0,25);
|
||||
phys_trace_end = spawnOrigin;
|
||||
newSpawnOrigin = PlayerPhysicsTrace(phys_trace_start, phys_trace_end);
|
||||
if ( DistanceSquared( newSpawnOrigin, phys_trace_start ) > 1 )
|
||||
{
|
||||
// If the result from the physics trace wasn't immediately in solid, then use it instead
|
||||
spawnOrigin = newSpawnOrigin;
|
||||
}
|
||||
|
||||
// called from code when an agent is done initializing after AddAgent is called
|
||||
// this should set up any state specific to this agent and game
|
||||
self SpawnAgent( spawnOrigin, spawnAngles );
|
||||
|
||||
if ( IsDefined( difficulty ) )
|
||||
self.agent_override_difficulty = difficulty;
|
||||
|
||||
if ( IsDefined( self.agent_override_difficulty ) )
|
||||
{
|
||||
if ( self.agent_override_difficulty == "follow_code_and_dev_dvar" )
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( self BotGetDifficulty(), true );
|
||||
else
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( difficulty );
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( self BotGetDifficulty() );
|
||||
}
|
||||
|
||||
if ( IsDefined(use_randomized_personality) && use_randomized_personality )
|
||||
self.use_randomized_personality = true;
|
||||
|
||||
if ( IsDefined ( self.use_randomized_personality) && self.use_randomized_personality )
|
||||
{
|
||||
if ( !self.hasDied )
|
||||
{
|
||||
// If this is the first time this agent has spawned, balance personalities unless we are restricting them based on difficulty
|
||||
allowAdvPersonality = self BotGetDifficultySetting( "advancedPersonality" );
|
||||
if ( IsDefined( allowAdvPersonality ) && allowAdvPersonality != 0 )
|
||||
self maps\mp\bots\_bots_personality::bot_balance_personality();
|
||||
}
|
||||
|
||||
/#
|
||||
self maps\mp\bots\_bots::bot_set_personality_from_dev_dvar();
|
||||
#/
|
||||
self maps\mp\bots\_bots_personality::bot_assign_personality_functions(); // Randomized personality was already set, so just need to setup functions
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\bots\_bots_util::bot_set_personality( "default" );
|
||||
}
|
||||
|
||||
self initPlayerClass();
|
||||
|
||||
self maps\mp\agents\_agent_common::set_agent_health( 100 );
|
||||
if ( IsDefined(respawn_on_death) && respawn_on_death )
|
||||
self.respawn_on_death = true;
|
||||
|
||||
// must set the team after SpawnAgent to fix a bug with weapon crosshairs and nametags
|
||||
if( IsDefined(optional_owner) )
|
||||
self set_agent_team( optional_owner.team, optional_owner );
|
||||
|
||||
if( isDefined( self.owner ) )
|
||||
self thread destroyOnOwnerDisconnect( self.owner );
|
||||
|
||||
self thread maps\mp\_flashgrenades::monitorFlash();
|
||||
|
||||
// switch to agent bot mode and wipe all AI info clean
|
||||
self EnableAnimState( false );
|
||||
|
||||
self [[level.onSpawnPlayer]]();
|
||||
self maps\mp\gametypes\_class::giveAndApplyLoadout( self.team, self.class, true );
|
||||
|
||||
self thread maps\mp\bots\_bots::bot_think_watch_enemy( true );
|
||||
self thread maps\mp\bots\_bots::bot_think_crate();
|
||||
if ( self.agent_type == "player" )
|
||||
self thread maps\mp\bots\_bots::bot_think_level_actions();
|
||||
else if ( self.agent_type == "odin_juggernaut" )
|
||||
self thread maps\mp\bots\_bots::bot_think_level_actions( 128 );
|
||||
self thread maps\mp\bots\_bots_strategy::bot_think_tactical_goals();
|
||||
self thread [[ self agentFunc("think") ]]();
|
||||
|
||||
if ( !self.hasDied )
|
||||
self maps\mp\gametypes\_spawnlogic::addToParticipantsArray();
|
||||
|
||||
if ( !self.hasDied )
|
||||
{
|
||||
// These functions don't need to be restarted if an agent is respawning, since they are infinite while loops
|
||||
self thread maps\mp\gametypes\_weapons::onPlayerSpawned();
|
||||
self thread maps\mp\gametypes\_battlechatter_mp::onPlayerSpawned();
|
||||
}
|
||||
|
||||
self.hasDied = false;
|
||||
|
||||
self thread maps\mp\gametypes\_healthoverlay::playerHealthRegen();
|
||||
|
||||
if ( IsDefined(self.use_randomized_personality) && self.use_randomized_personality && IsDefined(self.respawn_on_death) && self.respawn_on_death )
|
||||
{
|
||||
// Devgui agents need a costume (otherwise they'll be invisible)
|
||||
self SetAgentCostumeIndex( 1 );
|
||||
}
|
||||
|
||||
level notify( "spawned_agent_player", self );
|
||||
level notify( "spawned_agent", self );
|
||||
level notify( "player_spawned", self );
|
||||
self notify( "spawned_player" );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// destroyOnOwnerDisconnect
|
||||
//========================================================
|
||||
destroyOnOwnerDisconnect( owner )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
owner waittill( "killstreak_disowned" );
|
||||
|
||||
self notify( "owner_disconnect" );
|
||||
|
||||
// Wait till host migration finishes before suiciding
|
||||
if ( maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone() )
|
||||
wait 0.05;
|
||||
|
||||
// kill the agent
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// agent_damage_finished
|
||||
//========================================================
|
||||
agent_damage_finished( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if( IsDefined( eInflictor ) || IsDefined( eAttacker ) )
|
||||
{
|
||||
if( !IsDefined( eInflictor ) )
|
||||
eInflictor = eAttacker;
|
||||
|
||||
if( isdefined(self.allowVehicleDamage) && !self.allowVehicleDamage )
|
||||
{
|
||||
if( IsDefined( eInflictor.classname ) && eInflictor.classname == "script_vehicle" )
|
||||
return false;
|
||||
}
|
||||
|
||||
if( IsDefined( eInflictor.classname ) && eInflictor.classname == "auto_turret" )
|
||||
eAttacker = eInflictor;
|
||||
|
||||
if( IsDefined( eAttacker ) && sMeansOfDeath != "MOD_FALLING" && sMeansOfDeath != "MOD_SUICIDE" )
|
||||
{
|
||||
if( level.teamBased )
|
||||
{
|
||||
if( IsDefined( eAttacker.team ) && eAttacker.team != self.team )
|
||||
{
|
||||
self SetAgentAttacker( eAttacker );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self SetAgentAttacker( eAttacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Assert(IsDefined(self.isActive) && self.isActive);
|
||||
result = self FinishAgentDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset, 0.0 );
|
||||
if ( IsDefined(result) )
|
||||
self thread FinishAgentDamage_ImpactFXWrapper( result[0], result[1], result[2], result[3], result[4], result[5], result[6] );
|
||||
|
||||
if ( !IsDefined(self.isActive) )
|
||||
{
|
||||
// Agent just died and cleared out all his script variables
|
||||
// So don't allow this agent to be freed up until he is properly deactivated in deactivateAgentDelayed
|
||||
self.waitingToDeactivate = true;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
FinishAgentDamage_ImpactFXWrapper( attacker, mod, weapon, hitloc, point, dir, localdir )
|
||||
{
|
||||
waittillframeend; // Need to wait till the end of the frame here, so that we can spawn the impact fx at a more accurate location
|
||||
|
||||
if ( !isDefined(self) || !IsDefined(attacker) )
|
||||
return;
|
||||
|
||||
self FinishAgentDamage_ImpactFX( attacker, mod, weapon, hitloc, point, dir, localdir );
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// on_agent_generic_damaged
|
||||
//=======================================================
|
||||
on_agent_generic_damaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
attckerIsOwner = IsDefined(eAttacker) && IsDefined(self.owner) && (self.owner == eAttacker);
|
||||
attackerIsTeammate = attackerIsHittingTeam( self.owner, eAttacker ) || attckerIsOwner;
|
||||
|
||||
// ignore friendly fire damage for team based modes
|
||||
if( level.teambased && attackerIsTeammate && !level.friendlyfire )
|
||||
return false;
|
||||
|
||||
// ignore damage from owner in non team based modes
|
||||
if( !level.teambased && attckerIsOwner )
|
||||
return false;
|
||||
|
||||
// don't let helicopters and other vehicles crush a player, if we want it to then put in a special case here
|
||||
if( IsDefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_CRUSH" && IsDefined( eInflictor ) && IsDefined( eInflictor.classname ) && eInflictor.classname == "script_vehicle" )
|
||||
return false;
|
||||
|
||||
if ( !IsDefined( self ) || !isReallyAlive( self ) )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( eAttacker ) && eAttacker.classname == "script_origin" && IsDefined( eAttacker.type ) && eAttacker.type == "soft_landing" )
|
||||
return false;
|
||||
|
||||
if ( sWeapon == "killstreak_emp_mp" )
|
||||
return false;
|
||||
|
||||
if ( sWeapon == "bouncingbetty_mp" && !maps\mp\gametypes\_weapons::mineDamageHeightPassed( eInflictor, self ) )
|
||||
return false;
|
||||
|
||||
// ensure throwing knife death
|
||||
if ( ( sWeapon == "throwingknife_mp" || sWeapon == "throwingknifejugg_mp" ) && sMeansOfDeath == "MOD_IMPACT" )
|
||||
iDamage = self.health + 1;
|
||||
|
||||
// ensures stuck death
|
||||
if ( IsDefined( eInflictor ) && IsDefined( eInflictor.stuckEnemyEntity ) && eInflictor.stuckEnemyEntity == self )
|
||||
iDamage = self.health + 1;
|
||||
|
||||
if( iDamage <= 0 )
|
||||
return false;
|
||||
|
||||
if ( IsDefined( eAttacker ) && eAttacker != self && iDamage > 0 && ( !IsDefined( sHitLoc ) || sHitLoc != "shield" ) )
|
||||
{
|
||||
if( iDFlags & level.iDFLAGS_STUN )
|
||||
typeHit = "stun";
|
||||
else if( !shouldWeaponFeedback( sWeapon ) )
|
||||
typeHit = "none";
|
||||
else
|
||||
typeHit = "standard";
|
||||
|
||||
eAttacker thread maps\mp\gametypes\_damagefeedback::updateDamageFeedback( typeHit );
|
||||
}
|
||||
|
||||
if ( IsDefined( level.modifyPlayerDamage ) )
|
||||
iDamage = [[level.modifyPlayerDamage]]( self, eAttacker, iDamage, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc );
|
||||
|
||||
return self [[ self agentFunc( "on_damaged_finished" ) ]]( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// on_agent_player_damaged
|
||||
//========================================================
|
||||
on_agent_player_damaged( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
attckerIsOwner = IsDefined(eAttacker) && IsDefined(self.owner) && (self.owner == eAttacker);
|
||||
|
||||
// ignore damage from owner in non team based modes
|
||||
if( !level.teambased && attckerIsOwner )
|
||||
return false;
|
||||
|
||||
Callback_PlayerDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_player_killed
|
||||
//=======================================================
|
||||
on_agent_player_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
self on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, true);
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && (!isDefined(self.owner) || eAttacker != self.owner) && (!IsDefined(self.nonKillstreakAgent) || !self.nonKillstreakAgent) )
|
||||
{
|
||||
// TODO: should play vo for killing the agent
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_squad_mate" );
|
||||
}
|
||||
|
||||
if( IsDefined(level.on_agent_player_killed) )
|
||||
[[level.on_agent_player_killed]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration);
|
||||
|
||||
self thread maps\mp\gametypes\_weapons::dropScavengerForDeath( eAttacker );
|
||||
|
||||
if ( self.isActive )
|
||||
{
|
||||
self.hasDied = true;
|
||||
|
||||
if ( getGametypeNumLives() != 1 && ( IsDefined(self.respawn_on_death) && self.respawn_on_death ) )
|
||||
{
|
||||
self thread [[ self agentFunc("spawn") ]]();
|
||||
}
|
||||
else
|
||||
{
|
||||
self deactivateAgent();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_humanoid_agent_killed_common
|
||||
//=======================================================
|
||||
on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, dropWeapons )
|
||||
{
|
||||
// Things that happen on every type of humanoid agent that dies
|
||||
|
||||
if ( self maps\mp\_riotshield::hasRiotShieldEquipped() )
|
||||
{
|
||||
self LaunchShield( iDamage, sMeansofDeath );
|
||||
|
||||
if ( !dropWeapons )
|
||||
{
|
||||
// If not dropping weapons, need to make sure we at least drop the riot shield
|
||||
item = self dropItem( self GetCurrentWeapon() );
|
||||
|
||||
if( IsDefined(item) )
|
||||
{
|
||||
item thread maps\mp\gametypes\_weapons::deletePickupAfterAWhile();
|
||||
item.owner = self;
|
||||
item.ownersattacker = eAttacker;
|
||||
item MakeUnusable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( dropWeapons )
|
||||
self thread [[level.weaponDropFunction]]( eAttacker, sMeansOfDeath );
|
||||
|
||||
// ragdoll
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
thread delayStartRagdoll( self.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
|
||||
|
||||
self maps\mp\_riotshield::riotShield_clear();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// initPlayerClass
|
||||
//===========================================
|
||||
initPlayerClass()
|
||||
{
|
||||
// Must be called AFTER agent has been spawned as a bot agent
|
||||
if ( IsDefined(self.class_override) )
|
||||
{
|
||||
self.class = self.class_override;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( self maps\mp\bots\_bots_loadout::bot_setup_loadout_callback() )
|
||||
self.class = "callback";
|
||||
else
|
||||
self.class = "class1";
|
||||
}
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_ball.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_ball.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
69
raw/maps/mp/agents/_agents_gametype_conf.gsc
Normal file
69
raw/maps/mp/agents/_agents_gametype_conf.gsc
Normal file
@ -0,0 +1,69 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = ::agent_squadmember_conf_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_conf_think;
|
||||
}
|
||||
|
||||
agent_player_conf_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_conf::bot_conf_think();
|
||||
}
|
||||
|
||||
agent_squadmember_conf_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
if ( !IsDefined(self.tags_seen_by_owner) )
|
||||
self.tags_seen_by_owner = [];
|
||||
|
||||
if ( !IsDefined(self.next_time_check_tags) )
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
if ( GetTime() > self.next_time_check_tags )
|
||||
{
|
||||
self.next_time_check_tags = GetTime() + 500;
|
||||
|
||||
current_player_fov = 0.78; // approximation
|
||||
nearest_node_to_player = self.owner GetNearestNode();
|
||||
if ( IsDefined(nearest_node_to_player) )
|
||||
{
|
||||
new_visible_tags_to_player = self.owner maps\mp\bots\_bots_gametype_conf::bot_find_visible_tags( true, nearest_node_to_player, current_player_fov );
|
||||
self.tags_seen_by_owner = maps\mp\bots\_bots_gametype_conf::bot_combine_tag_seen_arrays( new_visible_tags_to_player, self.tags_seen_by_owner );
|
||||
}
|
||||
}
|
||||
|
||||
self.tags_seen_by_owner = self maps\mp\bots\_bots_gametype_conf::bot_remove_invalid_tags( self.tags_seen_by_owner );
|
||||
best_tag = self maps\mp\bots\_bots_gametype_conf::bot_find_best_tag_from_array( self.tags_seen_by_owner, false );
|
||||
|
||||
if ( IsDefined(best_tag) )
|
||||
{
|
||||
if ( !IsDefined(self.tag_getting) || DistanceSquared(best_tag.curorigin, self.tag_getting.curorigin) > 1 )
|
||||
{
|
||||
self.tag_getting = best_tag;
|
||||
self bot_defend_stop();
|
||||
self BotSetScriptGoal( self.tag_getting.curorigin, 0, "objective", undefined, level.bot_tag_obj_radius );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
else if ( IsDefined(self.tag_getting) )
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
self.tag_getting = undefined;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
17
raw/maps/mp/agents/_agents_gametype_ctf.gsc
Normal file
17
raw/maps/mp/agents/_agents_gametype_ctf.gsc
Normal file
@ -0,0 +1,17 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_ctf::bot_ctf_think;
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_dm.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_dm.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
49
raw/maps/mp/agents/_agents_gametype_dom.gsc
Normal file
49
raw/maps/mp/agents/_agents_gametype_dom.gsc
Normal file
@ -0,0 +1,49 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"]= ::agent_squadmember_dom_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_dom_think;
|
||||
}
|
||||
|
||||
agent_player_dom_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_dom::bot_dom_think();
|
||||
}
|
||||
|
||||
agent_squadmember_dom_think()
|
||||
{
|
||||
// Returning true means the "think" was handled here. "False" means use the default think
|
||||
|
||||
owner_flag = undefined;
|
||||
foreach( trigger in self.owner.touchTriggers )
|
||||
{
|
||||
if ( trigger.useobj.id == "domFlag" )
|
||||
owner_flag = trigger;
|
||||
}
|
||||
|
||||
if ( IsDefined(owner_flag) )
|
||||
{
|
||||
owner_flag_team = owner_flag maps\mp\gametypes\dom::getFlagTeam();
|
||||
if ( owner_flag_team != self.team )
|
||||
{
|
||||
if ( !self maps\mp\bots\_bots_gametype_dom::bot_is_capturing_flag( owner_flag ) )
|
||||
self maps\mp\bots\_bots_gametype_dom::capture_flag(owner_flag, "critical", true);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_gun.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_gun.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
702
raw/maps/mp/agents/_agents_gametype_horde.gsc
Normal file
702
raw/maps/mp/agents/_agents_gametype_horde.gsc
Normal file
@ -0,0 +1,702 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_horde_util;
|
||||
#include maps\mp\gametypes\_horde_crates;
|
||||
#include maps\mp\gametypes\_horde_drones;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
/#
|
||||
CONST_DISABLE_SPAWNING = false;
|
||||
CONST_FORCE_DOG_SPAWN = false;
|
||||
CONST_FORCE_DRONE_SPAWN = false;
|
||||
CONST_FORCE_PLAYER_ENEMY_SPAWN = false;
|
||||
CONST_DISABLE_AUTO_AI_REMOVAL = false;
|
||||
#/
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
level thread runRoundSpawning();
|
||||
|
||||
//for spot underneath the trailer where players can exploit goliaths
|
||||
if ( getMapName() == "mp_detroit" )
|
||||
level.goliathExploitTrigger = Spawn( "trigger_radius", ( -1662, -72, 582.5 ), 0, 86, 64 );
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["player"]["think"] = ::enemyAgentThink;
|
||||
level.agent_funcs["player"]["on_killed"] = ::onAgentKilled;
|
||||
|
||||
level.agent_funcs["squadmate"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["squadmate"]["think"] = ::allyAgentThink;
|
||||
|
||||
level.agent_funcs["dog"]["onAIConnect"] = ::onAIConnect;
|
||||
level.agent_funcs["dog"]["think"] = ::agentDogThink;
|
||||
level.agent_funcs["dog"]["on_killed"] = ::onDogKilled;
|
||||
}
|
||||
|
||||
onAIConnect()
|
||||
{
|
||||
self.gameModefirstSpawn = true;
|
||||
self.agentname = &"HORDE_GRUNT";
|
||||
self.horde_type = "";
|
||||
}
|
||||
|
||||
runRoundSpawning()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "start_round" );
|
||||
|
||||
/#
|
||||
if ( CONST_DISABLE_SPAWNING )
|
||||
continue;
|
||||
#/
|
||||
|
||||
wait 2; //give a little extra time for enemy weapons to load
|
||||
|
||||
if ( getMapName() == "mp_prison_z" && level.currentRoundNumber > 10 )
|
||||
runZombieRound();
|
||||
else
|
||||
runNormalRound();
|
||||
}
|
||||
}
|
||||
|
||||
runNormalRound()
|
||||
{
|
||||
level childthread highlightLastEnemies();
|
||||
|
||||
while( level.currentEnemyCount < level.maxEnemyCount )
|
||||
{
|
||||
while( level.currentAliveEnemyCount < level.maxAliveEnemyCount )
|
||||
{
|
||||
createEnemy();
|
||||
|
||||
if( level.currentEnemyCount == level.maxEnemyCount )
|
||||
break;
|
||||
}
|
||||
|
||||
level.waveFirstSpawn = false;
|
||||
|
||||
level waittill( "enemy_death" );
|
||||
}
|
||||
}
|
||||
|
||||
runZombieRound()
|
||||
{
|
||||
|
||||
level.zombiesDead = 0;
|
||||
level waittill ( "beginZombieSpawn" ); //delay start of spawning for VO - event, etc.
|
||||
|
||||
while( level.currentEnemyCount < level.maxEnemyCount )
|
||||
{
|
||||
while( level.currentAliveEnemyCount < level.maxAliveEnemyCount )
|
||||
{
|
||||
createEnemy();
|
||||
wait 0.1;
|
||||
}
|
||||
|
||||
level.waveFirstSpawn = false;
|
||||
|
||||
level waittill_any ( "enemy_death", "go_zombie" );
|
||||
|
||||
level.zombiesDead++;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
createEnemy()
|
||||
{
|
||||
/#
|
||||
if( CONST_FORCE_DOG_SPAWN )
|
||||
{
|
||||
createDogEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
/#
|
||||
if( CONST_FORCE_PLAYER_ENEMY_SPAWN )
|
||||
{
|
||||
createHumanoidEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
/#
|
||||
if( CONST_FORCE_DRONE_SPAWN )
|
||||
{
|
||||
createDroneEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
if( level.maxDogCount > 1 && level.dogsAlive < level.maxDogCount ) //no dogs at the same time as drones for now
|
||||
{
|
||||
createDogEnemy();
|
||||
}
|
||||
else
|
||||
{
|
||||
if( level.maxWarbirdCount > 0 )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( isOnHumanTeam( player ) && IsAlive( player ) )
|
||||
{
|
||||
player createWarbirdEnemy(); //use player's origin as a starting point.
|
||||
level.maxWarbirdCount--;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if ( level.maxDroneCount > 0 )
|
||||
{
|
||||
createDroneEnemy();
|
||||
level.maxDroneCount --;
|
||||
}
|
||||
else
|
||||
createHumanoidEnemy();
|
||||
}
|
||||
}
|
||||
|
||||
createHumanoidEnemy()
|
||||
{
|
||||
agent = undefined;
|
||||
|
||||
while( !IsDefined(agent) )
|
||||
{
|
||||
agent = maps\mp\agents\_agents::add_humanoid_agent( "player", level.enemyTeam, "class1" );
|
||||
|
||||
if( IsDefined(agent) )
|
||||
{
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
createDogEnemy()
|
||||
{
|
||||
agent = undefined;
|
||||
|
||||
while( !IsDefined(agent) )
|
||||
{
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "dog", level.enemyTeam );
|
||||
if( IsDefined(agent) )
|
||||
{
|
||||
agent thread [[ agent agentFunc("spawn") ]]();
|
||||
agent.awardpoints = 100;
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
level.dogsAlive++;
|
||||
}
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
createDroneEnemy()
|
||||
{
|
||||
// Wait until an empty vehicle slot is available to spawn this drone
|
||||
thread waitingToSpawnDrone();
|
||||
|
||||
// Claim the enemy spot for our future drone
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitingToSpawnDrone()
|
||||
{
|
||||
level.numDronesWaitingToSpawn++;
|
||||
|
||||
// Leave 2 open vehicle slots for players to use
|
||||
while( currentActiveVehicleCount( 2 ) >= maxVehiclesAllowed() )
|
||||
{
|
||||
wait 1;
|
||||
}
|
||||
|
||||
level.numDronesWaitingToSpawn--;
|
||||
|
||||
waitframe();
|
||||
drone = hordeCreateDrone ( level.players[0], "assault_uav_horde", level.hordeDroneModel );
|
||||
|
||||
drone HudOutlineEnable( level.enemyOutlineColor, true );
|
||||
drone.droneturret HudOutlineEnable ( level.enemyOutlineColor, true );
|
||||
drone.outlineColor = level.enemyOutlineColor;
|
||||
}
|
||||
|
||||
createWarbirdEnemy()
|
||||
{
|
||||
self thread maps\mp\gametypes\_horde_warbird::hordeCreateWarbird();
|
||||
}
|
||||
|
||||
playAISpawnEffect()
|
||||
{
|
||||
PlayFX( level._effect["spawn_effect"], self.origin );
|
||||
}
|
||||
|
||||
highlightLastEnemies()
|
||||
{
|
||||
level endon( "round_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "enemy_death" );
|
||||
|
||||
if( level.currentEnemyCount != level.maxEnemyCount )
|
||||
continue;
|
||||
|
||||
if( level.currentAliveEnemyCount < 3 )
|
||||
{
|
||||
foreach ( drone in level.flying_attack_drones )
|
||||
{
|
||||
drone HudOutlineEnable ( level.enemyOutlineColor, false );
|
||||
drone.droneTurret HudOutlineEnable ( level.enemyOutlineColor, false );
|
||||
drone.lastTwoEnemies = true;
|
||||
}
|
||||
foreach( player in level.characters )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
continue;
|
||||
|
||||
if( isReallyAlive(player) && !player IsCloaked() )
|
||||
{
|
||||
player HudOutlineEnable( level.enemyOutlineColor, false );
|
||||
player.outlineColor = level.enemyOutlineColor;
|
||||
}
|
||||
}
|
||||
setdvar ( "bg_compassShowEnemies", 1 );
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if( !isOnHumanTeam(self) )
|
||||
self hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
self HudOutlineDisable();
|
||||
self maps\mp\agents\_agents::on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, false);
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
}
|
||||
|
||||
onDogKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
if( !isOnHumanTeam(self) )
|
||||
self hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
self HudOutlineDisable();
|
||||
self maps\mp\killstreaks\_dog_killstreak::on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
|
||||
//level.dogsAlive--;
|
||||
}
|
||||
|
||||
hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
AssertEx( (level.currentAliveEnemyCount > 0), "currentAliveEnemyCount is below zero" );
|
||||
|
||||
level.currentAliveEnemyCount--;
|
||||
level.killsSinceIntelDrop ++;
|
||||
level.killsSinceAmmoDrop ++;
|
||||
|
||||
if ( level.objDefend )
|
||||
maps\mp\gametypes\horde::checkDefendKill( self, eAttacker );
|
||||
|
||||
trackIntelKills( sWeapon, sMeansOfDeath );
|
||||
|
||||
level thread maps\mp\gametypes\horde::chanceToSpawnPickup( self );
|
||||
level notify( "enemy_death", eAttacker, self );
|
||||
|
||||
//for enemies left UI in HordeHud.lua
|
||||
level.enemiesLeft--;
|
||||
if ( !level.zombiesStarted ) //don't update the UI after zombies has started
|
||||
SetOmnvar ( "ui_horde_enemies_left", level.enemiesLeft );
|
||||
|
||||
// player attacker
|
||||
if ( IsPlayer( eAttacker ) && !level.zombiesStarted )
|
||||
{
|
||||
awardHordeKill( eAttacker );
|
||||
eAttacker thread maps\mp\gametypes\_rank::xpPointsPopup( "kill", self.awardPoints );
|
||||
level thread hordeUpdateScore( eAttacker, self.awardPoints );
|
||||
|
||||
if( eAttacker _hasPerk("specialty_triggerhappy") )
|
||||
{
|
||||
// lost with port to blacksmith
|
||||
//eAttacker thread maps\mp\perks\_perkfunctions::setTriggerHappyInternal();
|
||||
}
|
||||
}
|
||||
|
||||
// killstreak entity attacker
|
||||
if( IsDefined(eAttacker) && IsDefined(eAttacker.owner) && IsPlayer(eAttacker.owner) && IsDefined(eAttacker.owner.killz) )
|
||||
{
|
||||
awardHordeKill( eAttacker.owner );
|
||||
eAttacker.owner thread maps\mp\gametypes\_rank::xpPointsPopup( "kill", self.awardPoints );
|
||||
level thread hordeUpdateScore( eAttacker.owner, self.awardPoints );
|
||||
}
|
||||
}
|
||||
|
||||
trackIntelKills( sWeapon, sMeansOfDeath )
|
||||
{
|
||||
if( level.isTeamIntelComplete )
|
||||
return;
|
||||
|
||||
if( sWeapon == "none" )
|
||||
return;
|
||||
|
||||
if( isMeleeMOD(sMeansOfDeath) )
|
||||
level.numMeleeKillsIntel++;
|
||||
|
||||
if( !isKillstreakWeapon( sWeapon ) && (sMeansOfDeath == "MOD_HEAD_SHOT") )
|
||||
level.numHeadShotsIntel++;
|
||||
|
||||
if( isKillstreakWeapon( sWeapon ) && (sWeapon != level.intelMiniGun) )
|
||||
level.numKillStreakKillsIntel++;
|
||||
|
||||
if( maps\mp\gametypes\_class::isValidEquipment( sWeapon, false ) || maps\mp\gametypes\_class::isValidOffhand( sWeapon, false ) )
|
||||
level.numEquipmentKillsIntel++;
|
||||
}
|
||||
|
||||
enemyAgentThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self BotSetFlag("no_enemy_search", true);
|
||||
|
||||
self thread monitorBadHumanoidAI();
|
||||
self thread locateEnemyPositions();
|
||||
}
|
||||
|
||||
monitorBadHumanoidAI()
|
||||
{
|
||||
/#
|
||||
if( CONST_DISABLE_AUTO_AI_REMOVAL )
|
||||
return;
|
||||
#/
|
||||
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
spawnTime = GetTime();
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait( 5.0 );
|
||||
|
||||
if( !bot_in_combat(120 * 1000) )
|
||||
{
|
||||
|
||||
if( !bot_in_combat(240 * 1000) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 240, 480 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
//removing this for the time being. Will see if problems arise
|
||||
//killAgent( self );
|
||||
}
|
||||
|
||||
monitorBadDogAI()
|
||||
{
|
||||
/#
|
||||
if( CONST_DISABLE_AUTO_AI_REMOVAL )
|
||||
return;
|
||||
#/
|
||||
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
spawnTime = GetTime();
|
||||
lastPosition = self.origin;
|
||||
lastPositionTime = spawnTime;
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait( 5.0 );
|
||||
|
||||
positionDelta = DistanceSquared( self.origin, lastPosition );
|
||||
positionTime = (GetTime() - lastPositionTime) / 1000;
|
||||
|
||||
if( positionDelta > (128 * 128) )
|
||||
{
|
||||
lastPosition = self.origin;
|
||||
lastPositionTime = GetTime();
|
||||
}
|
||||
else if( positionTime > 25 )
|
||||
{
|
||||
if( positionTime > 55 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 120, 240 ) )
|
||||
break;
|
||||
}
|
||||
killAgent( self );
|
||||
}
|
||||
|
||||
checkExpireTime( spawnTime, highLightTime, expireTime )
|
||||
{
|
||||
aliveTime = (GetTime() - spawnTime) / 1000;
|
||||
|
||||
if( aliveTime > highLightTime )
|
||||
{
|
||||
|
||||
if( aliveTime > expireTime )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SCR_CONST_ALLY_AGENT_LOW_HEALTH_BEHAVIOR = 0.6;
|
||||
SCR_CONST_PLAYER_LOW_HEALTH_BEHAVIOR = 0.5;
|
||||
|
||||
allyAgentThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
self BotSetFlag("force_sprint",true);
|
||||
holding_till_health_regen = false;
|
||||
next_time_protect_player = 0;
|
||||
|
||||
while(1)
|
||||
{
|
||||
if ( float(self.owner.health) / self.owner.maxhealth < SCR_CONST_PLAYER_LOW_HEALTH_BEHAVIOR && GetTime() > next_time_protect_player )
|
||||
{
|
||||
nodes = GetNodesInRadiusSorted(self.owner.origin, 256, 0);
|
||||
if ( nodes.size >= 2 )
|
||||
{
|
||||
self.defense_force_next_node_goal = nodes[1]; // Send agent to the second-closest node to the player
|
||||
self notify("defend_force_node_recalculation");
|
||||
next_time_protect_player = GetTime() + 1000;
|
||||
}
|
||||
}
|
||||
else if ( float(self.health) / self.maxhealth >= SCR_CONST_ALLY_AGENT_LOW_HEALTH_BEHAVIOR )
|
||||
{
|
||||
holding_till_health_regen = false;
|
||||
}
|
||||
else if ( !holding_till_health_regen )
|
||||
{
|
||||
// Pick node on the opposite side of the player and hide at it
|
||||
node = self bot_find_node_to_guard_player( self.owner.origin, 350, true );
|
||||
if ( IsDefined(node) )
|
||||
{
|
||||
self.defense_force_next_node_goal = node;
|
||||
self notify("defend_force_node_recalculation");
|
||||
|
||||
holding_till_health_regen = true;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
{
|
||||
optional_params["override_goal_type"] = "critical";
|
||||
optional_params["min_goal_time"] = 20;
|
||||
optional_params["max_goal_time"] = 30;
|
||||
self bot_guard_player( self.owner, 350, optional_params );
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
hordeSetupDogState()
|
||||
{
|
||||
//self _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
self.enableExtendedKill = false;
|
||||
self.agentname = &"HORDE_QUAD";
|
||||
self.horde_type = "Quad";
|
||||
self thread maps\mp\gametypes\horde::hordeApplyAIModifiers();
|
||||
// pathing variables
|
||||
self.lasSetGoalPos = (0,0,0);
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = 0;
|
||||
|
||||
self.maxhealth = 60;
|
||||
self.health = self.maxhealth;
|
||||
}
|
||||
|
||||
agentDogThink()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
self maps\mp\agents\dog\_dog_think::setupDogState();
|
||||
self hordeSetupDogState();
|
||||
|
||||
self thread locateEnemyPositions();
|
||||
self thread [[self.watchAttackStateFunc]]();
|
||||
self thread WaitForBadPathHorde();
|
||||
self thread monitorBadDogAI();
|
||||
self thread agentDogBark();
|
||||
|
||||
/#
|
||||
self thread maps\mp\agents\dog\_dog_think::debug_dog();
|
||||
#/
|
||||
|
||||
while ( true )
|
||||
{
|
||||
/#
|
||||
if ( self maps\mp\agents\dog\_dog_think::ProcessDebugMode() )
|
||||
continue;
|
||||
#/
|
||||
|
||||
if ( self.aiState != "melee" && !self.stateLocked && self maps\mp\agents\dog\_dog_think::readyToMeleeTarget() && !self maps\mp\agents\dog\_dog_think::DidPastMeleeFail() )
|
||||
self ScrAgentBeginMelee( self.curMeleeTarget );
|
||||
|
||||
if( self.randomPathStopTime > GetTime() )
|
||||
{
|
||||
wait(0.05);
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !IsDefined(self.enemy) || self.bHasNoPath )
|
||||
{
|
||||
pathNodes = GetNodesInRadiusSorted( self.origin, 1024, 256, 128, "Path" );
|
||||
|
||||
if( pathNodes.size > 0 )
|
||||
{
|
||||
nodeNum = RandomIntRange(int(pathNodes.size*0.9), pathNodes.size); //Pick from the furthest 10%
|
||||
self ScrAgentSetGoalPos( pathNodes[nodeNum].origin );
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = GetTime() + 2500;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
attackPoint = self maps\mp\agents\dog\_dog_think::GetAttackPoint( self.enemy );
|
||||
self.curMeleeTarget = self.enemy;
|
||||
self.moveMode = "sprint";
|
||||
self.bArrivalsEnabled = false;
|
||||
|
||||
if( DistanceSquared(attackPoint, self.lasSetGoalPos) > (64 * 64) )
|
||||
{
|
||||
self ScrAgentSetGoalPos( attackPoint );
|
||||
self.lasSetGoalPos = attackPoint;
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
agentDogBark()
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
while ( !isdefined ( self.curmeleetarget ) )
|
||||
wait 0.25;
|
||||
|
||||
while ( isdefined ( self.curMeleeTarget ) && distance ( self.origin, self.curMeleeTarget.origin ) > 200 )
|
||||
{
|
||||
wait randomfloatrange ( 0, 2 );
|
||||
self playsound ( "anml_doberman_bark" );
|
||||
|
||||
}
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
WaitForBadPathHorde()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "bad_path", badGoalPos );
|
||||
self.bHasNoPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
locateEnemyPositions()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
{
|
||||
self GetEnemyInfo( player );
|
||||
if ( isdefined ( player.hordeDrone ) )
|
||||
self GetEnemyInfo ( player.hordeDrone );
|
||||
}
|
||||
}
|
||||
|
||||
wait(0.5);
|
||||
}
|
||||
}
|
||||
|
||||
findClosestPlayer()
|
||||
{
|
||||
closestPlayer = undefined;
|
||||
closestDistance = 100000 * 100000;
|
||||
|
||||
// find the nearest player
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( isReallyAlive(player) && isOnHumanTeam(player) && !isPlayerInLastStand(player) )
|
||||
{
|
||||
distSquared = DistanceSquared( player.origin, self.origin );
|
||||
|
||||
if ( distSquared < closestDistance )
|
||||
{
|
||||
closestPlayer = player;
|
||||
closestDistance = distSquared;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return closestPlayer;
|
||||
}
|
||||
|
||||
handleDetroitGoliathTrailerExploit()
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
wait 1;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( isdefined ( self.enemy ) && self.enemy IsTouching ( level.goliathExploitTrigger ) )
|
||||
{
|
||||
//IPrintLnBold ( "In Exploit Trigger" );
|
||||
self BotSetScriptGoal ( ( -1696, -408, 608.5 ), 32, "critical", 200 );
|
||||
|
||||
while ( isdefined ( self.enemy ) && isReallyAlive ( self.enemy ) && !isPlayerInLastStand ( self.enemy ) && self.enemy istouching ( level.goliathExploitTrigger ) )
|
||||
wait 0.25;
|
||||
|
||||
self BotClearScriptGoal();
|
||||
}
|
||||
|
||||
wait 1;
|
||||
}
|
||||
}
|
17
raw/maps/mp/agents/_agents_gametype_hp.gsc
Normal file
17
raw/maps/mp/agents/_agents_gametype_hp.gsc
Normal file
@ -0,0 +1,17 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = maps\mp\bots\_bots_gametype_hp::bot_hp_think;
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_infect.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_infect.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
26
raw/maps/mp/agents/_agents_gametype_sd.gsc
Normal file
26
raw/maps/mp/agents/_agents_gametype_sd.gsc
Normal file
@ -0,0 +1,26 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_sd_think;
|
||||
}
|
||||
|
||||
agent_player_sd_think()
|
||||
{
|
||||
self _enableUsability();
|
||||
foreach( bombzone in level.bombZones )
|
||||
bombzone.trigger EnablePlayerUse( self );
|
||||
|
||||
self thread maps\mp\bots\_bots_gametype_sd::bot_sd_think();
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_sr.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_sr.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
maps\mp\agents\_agents_gametype_sd::setup_callbacks();
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_twar.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_twar.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
4
raw/maps/mp/agents/_agents_gametype_vlobby.gsc
Normal file
4
raw/maps/mp/agents/_agents_gametype_vlobby.gsc
Normal file
@ -0,0 +1,4 @@
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
12
raw/maps/mp/agents/_agents_gametype_war.gsc
Normal file
12
raw/maps/mp/agents/_agents_gametype_war.gsc
Normal file
@ -0,0 +1,12 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
#include maps\mp\bots\_bots_personality;
|
||||
#include maps\mp\bots\_bots_gametype_common;
|
||||
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
247
raw/maps/mp/agents/_scriptedagents.gsc
Normal file
247
raw/maps/mp/agents/_scriptedagents.gsc
Normal file
@ -0,0 +1,247 @@
|
||||
//
|
||||
// Scripted agent common functions.
|
||||
//
|
||||
|
||||
// called from code when animation state changes.
|
||||
OnEnterState( prevState, nextState )
|
||||
{
|
||||
if ( IsDefined( self.OnEnterAnimState ) )
|
||||
self [[ self.OnEnterAnimState ]]( prevState, nextState );
|
||||
}
|
||||
|
||||
// called from code when the agent is freed.
|
||||
OnDeactivate()
|
||||
{
|
||||
self notify( "killanimscript" );
|
||||
}
|
||||
|
||||
|
||||
// util function
|
||||
PlayAnimUntilNotetrack( animState, animLabel, notetrack, customFunction )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( animState, 0, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex );
|
||||
|
||||
if ( !IsDefined( notetrack ) )
|
||||
notetrack = "end";
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction );
|
||||
}
|
||||
|
||||
PlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
|
||||
if ( !IsDefined( notetrack ) )
|
||||
notetrack = "end";
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction );
|
||||
}
|
||||
|
||||
WaitUntilNotetrack( animLabel, notetrack, animState, animIndex, customFunction )
|
||||
{
|
||||
startTime = getTime();
|
||||
animTime = undefined;
|
||||
animLength = undefined;
|
||||
|
||||
if ( isDefined ( animState ) && isDefined ( animIndex ) )
|
||||
animLength = getAnimLength( self GetAnimEntry( animState, animIndex ));
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( animLabel, note );
|
||||
|
||||
if ( isDefined ( animLength ) )
|
||||
animTime = ( getTime() - startTime ) * 0.001 / animLength;
|
||||
|
||||
if ( !isDefined( animLength ) || animTime > 0 )
|
||||
{
|
||||
if ( note == notetrack || note == "end" || note == "anim_will_finish" || note == "finish" )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( customFunction ) )
|
||||
[[ customFunction ]]( note, animState, animIndex, animTime );
|
||||
}
|
||||
}
|
||||
|
||||
PlayAnimForTime( animState, time )
|
||||
{
|
||||
PlayAnimNForTime( animState, 0, time );
|
||||
}
|
||||
|
||||
PlayAnimNForTime( animState, animIndex, time )
|
||||
{
|
||||
self SetAnimState( animState, animIndex );
|
||||
wait( time );
|
||||
}
|
||||
|
||||
PlayAnimNAtRateForTime( animState, animIndex, animRate, time )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
wait( time );
|
||||
}
|
||||
|
||||
GetAnimScaleFactors( delta, animDelta, bAnimInWorldSpace )
|
||||
{
|
||||
distXY = Length2D( delta );
|
||||
distZ = delta[2];
|
||||
animXY = Length2D( animDelta );
|
||||
animZ = animDelta[2];
|
||||
|
||||
scaleXY = 1;
|
||||
scaleZ = 1;
|
||||
if ( IsDefined( bAnimInWorldSpace ) && bAnimInWorldSpace )
|
||||
{
|
||||
animDelta2D = ( animDelta[0], animDelta[1], 0 );
|
||||
animDeltaDir = VectorNormalize( animDelta2D );
|
||||
if ( VectorDot( animDeltaDir, delta ) < 0 )
|
||||
scaleXY = 0;
|
||||
else if ( animXY > 0 )
|
||||
scaleXY = distXY / animXY;
|
||||
}
|
||||
else if ( animXY > 0 )
|
||||
scaleXY = distXY / animXY;
|
||||
|
||||
assert( scaleXY >= 0 );
|
||||
|
||||
if ( abs(animZ) > 0.001 && animZ * distZ >= 0 ) // animZ & distZ have to be same sign.
|
||||
scaleZ = distZ / animZ;
|
||||
|
||||
assert( scaleZ >= 0 );
|
||||
|
||||
scaleFactors = SpawnStruct();
|
||||
scaleFactors.xy = scaleXY;
|
||||
scaleFactors.z = scaleZ;
|
||||
|
||||
return scaleFactors;
|
||||
}
|
||||
|
||||
// -180, -135, -90, -45, 0, 45, 90, 135, 180
|
||||
// favor underturning, unless you're within <threshold> degrees of the next one up.
|
||||
GetAngleIndex( angle, threshold )
|
||||
{
|
||||
if ( !IsDefined( threshold ) )
|
||||
threshold = 10;
|
||||
|
||||
if ( angle < 0 )
|
||||
return int( ceil( ( 180 + angle - threshold ) / 45 ) );
|
||||
else
|
||||
return int( floor( ( 180 + angle + threshold ) / 45 ) );
|
||||
}
|
||||
|
||||
|
||||
DropPosToGround( position, drop_distance )
|
||||
{
|
||||
//droppedPos = GetGroundPosition( position, radius, 64, 64 );
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
if ( !IsDefined( drop_distance ) )
|
||||
drop_distance = 18;
|
||||
|
||||
startPos = position + (0, 0, drop_distance);
|
||||
endPos = position + (0, 0, drop_distance * -1 );
|
||||
|
||||
droppedPos = self AIPhysicsTrace( startPos, endPos, self.radius, self.height, true );
|
||||
|
||||
if ( abs( droppedPos[2] - startPos[2] ) < 0.1 )
|
||||
return undefined;
|
||||
|
||||
if ( abs( droppedPos[2] - endPos[2] ) < 0.1 )
|
||||
return undefined;
|
||||
|
||||
return droppedPos;
|
||||
}
|
||||
|
||||
|
||||
TRACE_RADIUS_BUFFER = 4;
|
||||
CanMovePointToPoint( startPos, endPos, stepSize )
|
||||
{
|
||||
if ( !isDefined( stepSize ) )
|
||||
{
|
||||
stepSize = 6;
|
||||
}
|
||||
|
||||
step_offset = (0, 0, 1) * stepSize;
|
||||
startPosRaised = startPos + step_offset;
|
||||
endPosRaised = endPos + step_offset;
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
assert( stepSize < self.height );
|
||||
|
||||
return self AIPhysicsTracePassed( startPosRaised, endPosRaised, self.radius, self.height - stepSize, true );
|
||||
}
|
||||
|
||||
GetValidPointToPointMoveLocation( startPos, endPos, stepSize )
|
||||
{
|
||||
if ( !isDefined( stepSize ) )
|
||||
{
|
||||
stepSize = 6;
|
||||
}
|
||||
|
||||
step_offset = (0, 0, 1) * stepSize;
|
||||
startPosRaised = startPos + step_offset;
|
||||
endPosRaised = endPos + step_offset;
|
||||
|
||||
assert( IsDefined( self.radius ) && IsDefined( self.height ) );
|
||||
assert( stepSize < self.height );
|
||||
|
||||
return self AIPhysicsTrace( startPosRaised, endPosRaised, self.radius + TRACE_RADIUS_BUFFER, self.height - stepSize, true );
|
||||
}
|
||||
|
||||
GetSafeAnimMoveDeltaPercentage( moveAnim )
|
||||
{
|
||||
animTranslation = GetMoveDelta( moveAnim );
|
||||
endPos = self LocalToWorldCoords( animTranslation );
|
||||
validMovePosition = GetValidPointToPointMoveLocation( self.origin, endPos );
|
||||
validMoveDistance = Distance( self.origin, validMovePosition );
|
||||
desiredMoveDistance = Distance( self.origin, endPos );
|
||||
|
||||
return Min( 1.0, validMoveDistance / desiredMoveDistance );
|
||||
}
|
||||
|
||||
SafelyPlayAnimUntilNotetrack( animState, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimAtRateUntilNotetrack( animState, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
SafelyPlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimNAtRateUntilNotetrack( animState, animIndex, animRate, animLabel, notetrack, customFunction )
|
||||
{
|
||||
self SetAnimState( animState, animIndex, animRate );
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
}
|
||||
|
||||
SafelyPlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction )
|
||||
{
|
||||
animToPlay = self GetAnimEntry( animState, animIndex );
|
||||
moveScale = GetSafeAnimMoveDeltaPercentage( animToPlay );
|
||||
self ScrAgentSetAnimScale( moveScale, 1.0 );
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, animLabel, notetrack, customFunction );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
}
|
||||
|
||||
GetRandomAnimEntry( state )
|
||||
{
|
||||
count = self GetAnimEntryCount( state );
|
||||
return RandomInt( count );
|
||||
}
|
||||
|
||||
GetAngleIndexFromSelfYaw( targetVector )
|
||||
{
|
||||
targetAngles = VectorToAngles( targetVector );
|
||||
angleDiff = AngleClamp180( targetAngles[1] - self.angles[1] );
|
||||
return GetAngleIndex( angleDiff );
|
||||
}
|
303
raw/maps/mp/agents/dog/_dog_idle.gsc
Normal file
303
raw/maps/mp/agents/dog/_dog_idle.gsc
Normal file
@ -0,0 +1,303 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self.animSubstate = "none";
|
||||
|
||||
self SetTimeOfNextSound();
|
||||
self.timeOfNextSound += 2000;
|
||||
|
||||
self.bIdleHitReaction = false;
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
|
||||
self UpdateState();
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
if ( IsDefined( self.prevTurnRate ) )
|
||||
{
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
UpdateState()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "cancelidleloop" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
prevState = self.animSubstate;
|
||||
nextState = self DetermineState();
|
||||
if ( nextState != self.animSubstate )
|
||||
self EnterState( nextState );
|
||||
|
||||
self UpdateAngle();
|
||||
|
||||
switch ( self.animSubstate )
|
||||
{
|
||||
case "idle_combat":
|
||||
wait( 0.2 );
|
||||
break;
|
||||
case "idle_noncombat":
|
||||
if ( prevState == "none" )
|
||||
{
|
||||
//if ( self.moveMode == "run" || self.moveMode == "sprint" )
|
||||
// self PlaySoundOnMovingEnt( "anml_dog_pants_mp_fast" );
|
||||
//else
|
||||
//self PlaySoundOnMovingEnt( "anml_dog_pants_mp_med" );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetTime() > self.timeOfNextSound )
|
||||
{
|
||||
//if ( RandomInt(10) < 4 )
|
||||
//self PlaySoundOnMovingEnt( "anml_doberman_bark" );
|
||||
//else
|
||||
//self PlaySoundOnMovingEnt( "anml_dog_pants_mp_med" );
|
||||
self SetTimeOfNextSound();
|
||||
}
|
||||
}
|
||||
wait ( 0.5 );
|
||||
break;
|
||||
default:
|
||||
assertmsg( "unknown dog stop state " + self.animSubstate );
|
||||
wait( 1 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
DetermineState()
|
||||
{
|
||||
if ( ShouldAttackIdle() )
|
||||
return "idle_combat";
|
||||
else
|
||||
return "idle_noncombat";
|
||||
}
|
||||
|
||||
|
||||
EnterState( state )
|
||||
{
|
||||
self ExitState( self.animSubstate );
|
||||
self.animSubstate = state;
|
||||
|
||||
PlayIdleAnim();
|
||||
}
|
||||
|
||||
|
||||
ExitState( prevState )
|
||||
{
|
||||
if ( IsDefined( self.prevTurnRate ) )
|
||||
{
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
PlayIdleAnim()
|
||||
{
|
||||
if ( self.animSubstate == "idle_combat" )
|
||||
self SetAnimState( "attack_idle" );
|
||||
else
|
||||
self SetAnimState( "casual_idle" );
|
||||
}
|
||||
|
||||
|
||||
UpdateAngle()
|
||||
{
|
||||
faceTarget = undefined;
|
||||
if ( IsDefined( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) < 1024 * 1024 )
|
||||
faceTarget = self.enemy;
|
||||
else if ( IsDefined( self.owner ) && DistanceSquared( self.owner.origin, self.origin ) > 24 * 24 )
|
||||
faceTarget = self.owner;
|
||||
|
||||
if ( IsDefined( faceTarget ) )
|
||||
{
|
||||
meToTarget = faceTarget.origin - self.origin;
|
||||
meToTargetAngles = VectorToAngles( meToTarget );
|
||||
|
||||
if ( abs( AngleClamp180( meToTargetAngles[1] - self.angles[1] ) ) > 1 )
|
||||
self TurnToAngle( meToTargetAngles[1] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
ShouldAttackIdle()
|
||||
{
|
||||
return isdefined( self.enemy )
|
||||
&& maps\mp\_utility::IsReallyAlive( self.enemy )
|
||||
&& distanceSquared( self.origin, self.enemy.origin ) < 1000000;
|
||||
//&& self SeeRecently( self.enemy, 5 );
|
||||
}
|
||||
|
||||
GetTurnAnimState( angleDiff )
|
||||
{
|
||||
if ( self ShouldAttackIdle() )
|
||||
{
|
||||
if ( angleDiff < -135 || angleDiff > 135 )
|
||||
return "attack_turn_180";
|
||||
else if ( angleDiff < 0 )
|
||||
return "attack_turn_right_90";
|
||||
else
|
||||
return "attack_turn_left_90";
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angleDiff < -135 || angleDiff > 135 )
|
||||
return "casual_turn_180";
|
||||
else if ( angleDiff < 0 )
|
||||
return "casual_turn_right_90";
|
||||
else
|
||||
return "casual_turn_left_90";
|
||||
}
|
||||
}
|
||||
|
||||
TurnToAngle( desiredAngle )
|
||||
{
|
||||
currentAngle = self.angles[1];
|
||||
angleDiff = AngleClamp180( desiredAngle - currentAngle );
|
||||
|
||||
if ( -0.5 < angleDiff && angleDiff < 0.5 )
|
||||
return;
|
||||
|
||||
if ( -10 < angleDiff && angleDiff < 10 )
|
||||
{
|
||||
RotateToAngle( desiredAngle, 2 );
|
||||
return;
|
||||
}
|
||||
|
||||
animState = GetTurnAnimState( angleDiff );
|
||||
|
||||
turnAnim = self GetAnimEntry( animState, 0 );
|
||||
|
||||
animLength = GetAnimLength( turnAnim );
|
||||
animAngleDelta = GetAngleDelta3D( turnAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
|
||||
if ( AnimHasNotetrack( turnAnim, "turn_begin" ) && AnimHasNotetrack( turnAnim, "turn_end" ) )
|
||||
{
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "turn_in_place" );
|
||||
|
||||
beginTimes = GetNotetrackTimes( turnAnim, "turn_begin" );
|
||||
endTimes = GetNotetrackTimes( turnAnim, "turn_end" );
|
||||
turnTime = (endTimes[0] - beginTimes[0]) * animLength;
|
||||
|
||||
turnAdjust = AngleClamp180( angleDiff - animAngleDelta[1] );
|
||||
|
||||
turnSpeed = abs(turnAdjust) / turnTime / 20;
|
||||
turnSpeed = turnSpeed * 3.14159 / 180; // radians per frame.
|
||||
|
||||
angles = ( 0, AngleClamp180( self.angles[1] + turnAdjust ), 0 );
|
||||
|
||||
self.prevTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( turnSpeed );
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
self WaitUntilNotetrack( "turn_in_place", "turn_end" );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
|
||||
self WaitUntilNotetrack( "turn_in_place", "end" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.prevTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
|
||||
turnSpeed = abs( AngleClamp180(angleDiff-animAngleDelta[1]) ) / animLength / 20;
|
||||
turnSpeed = turnSpeed * 3.14159 / 180;
|
||||
self ScrAgentSetMaxTurnSpeed( turnSpeed ); // radians per frame.
|
||||
|
||||
angles = ( 0, AngleClamp180( desiredAngle - animAngleDelta[1] ), 0 );
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "turn_in_place" );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( self.prevTurnRate );
|
||||
self.prevTurnRate = undefined;
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
self PlayIdleAnim();
|
||||
}
|
||||
|
||||
RotateToAngle( desiredAngle, tolerance )
|
||||
{
|
||||
if ( abs( AngleClamp180( desiredAngle - self.angles[1] ) ) <= tolerance )
|
||||
return;
|
||||
|
||||
angles = ( 0, desiredAngle, 0 );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", angles );
|
||||
|
||||
while ( AngleClamp180( desiredAngle - self.angles[1] ) > tolerance )
|
||||
wait ( 0.1 );
|
||||
}
|
||||
|
||||
SetTimeOfNextSound()
|
||||
{
|
||||
self.timeOfNextSound = GetTime() + 8000 + RandomInt( 5000 );
|
||||
}
|
||||
|
||||
DoHitReaction( hitAngle )
|
||||
{
|
||||
self.bLockGoalPos = true;
|
||||
self.stateLocked = true;
|
||||
self.bIdleHitReaction = true;
|
||||
|
||||
// hitAngle is angle from me to damage
|
||||
angleDiff = AngleClamp180( hitAngle - self.angles[1] );
|
||||
|
||||
if ( angleDiff > 0 )
|
||||
animIndex = 1; // left
|
||||
else
|
||||
animIndex = 0; // right
|
||||
|
||||
self notify( "cancelidleloop" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( "stand_pain", animIndex, "stand_pain" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self.stateLocked = false;
|
||||
self.bIdleHitReaction = false;
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.animSubstate = "none";
|
||||
self thread UpdateState();
|
||||
}
|
||||
|
||||
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( self.bIdleHitReaction )
|
||||
return;
|
||||
|
||||
hitDirToAngles = VectorToAngles( vDir );
|
||||
hitAngle = hitDirToAngles[1] - 180;
|
||||
|
||||
self DoHitReaction( hitAngle );
|
||||
}
|
||||
|
||||
OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
|
||||
{
|
||||
if ( self.bIdleHitReaction )
|
||||
return;
|
||||
|
||||
DoHitReaction( self.angles[1] + 180 );
|
||||
}
|
350
raw/maps/mp/agents/dog/_dog_melee.gsc
Normal file
350
raw/maps/mp/agents/dog/_dog_melee.gsc
Normal file
@ -0,0 +1,350 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
|
||||
assert( IsDefined( self.curMeleeTarget ) );
|
||||
|
||||
self.curMeleeTarget endon( "disconnect" );
|
||||
|
||||
// get desired end pos.
|
||||
meToTarget = self.curMeleeTarget.origin - self.origin;
|
||||
distMeToTarget = Length( meToTarget );
|
||||
|
||||
bTestCanMove = true;
|
||||
if ( distMeToTarget < self.attackOffset )
|
||||
{
|
||||
attackPos = self.origin;
|
||||
bTestCanMove = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = meToTarget / distMeToTarget;
|
||||
attackPos = self.curMeleeTarget.origin - meToTarget * self.attackOffset;
|
||||
}
|
||||
|
||||
bLerp = false;
|
||||
|
||||
startPos = self.origin + (0,0,30);
|
||||
endPos = self.curMeleeTarget.origin + (0,0,30);
|
||||
hitPos = PhysicsTrace( startPos, endPos );
|
||||
if ( DistanceSquared( hitPos, endPos ) > 1 )
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( bTestCanMove )
|
||||
bCanMoveToAttackPos = self CanMovePointToPoint( self.origin, attackPos );
|
||||
else
|
||||
bCanMoveToAttackPos = true;
|
||||
|
||||
animEntry = undefined;
|
||||
if ( !bCanMoveToAttackPos )
|
||||
{
|
||||
bShouldDoExtendedKill = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
animEntry = self ShouldDoExtendedKill( self.curMeleeTarget );
|
||||
bShouldDoExtendedKill = IsDefined( animEntry );
|
||||
}
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
if ( bShouldDoExtendedKill )
|
||||
{
|
||||
assert( IsDefined( animEntry ) );
|
||||
self DoExtendedKill( animEntry );
|
||||
}
|
||||
else
|
||||
{
|
||||
self DoStandardKill( attackPos, bCanMoveToAttackPos );
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetMeleeAnimState()
|
||||
{
|
||||
return "attack_run_and_jump";
|
||||
}
|
||||
|
||||
// returns kill direction, if any.
|
||||
ShouldDoExtendedKill( victim )
|
||||
{
|
||||
if( !self.enableExtendedKill )
|
||||
return undefined;
|
||||
|
||||
cMaxHeightDiff = 4;
|
||||
|
||||
if ( !IsGameParticipant( victim ) ) // humans only.
|
||||
return undefined;
|
||||
if ( self IsProtectedByRiotshield( victim ) )
|
||||
return undefined;
|
||||
if ( victim IsJuggernaut() )
|
||||
return undefined;
|
||||
|
||||
victimToMe = self.origin - victim.origin;
|
||||
if ( abs( victimToMe[2] ) > cMaxHeightDiff )
|
||||
return undefined;
|
||||
|
||||
victimToMe2D = VectorNormalize( (victimToMe[0], victimToMe[1], 0) );
|
||||
victimFacing = AnglesToForward( victim.angles );
|
||||
angleToMe = VectorDot( victimFacing, victimToMe2D );
|
||||
|
||||
if ( angleToMe > 0.707 )
|
||||
{
|
||||
animEntry = 0; // front
|
||||
snappedVictimToMe = RotateVector( ( 1, 0, 0 ), victim.angles );
|
||||
}
|
||||
else if ( angleToMe < -0.707 )
|
||||
{
|
||||
animEntry = 1; // back
|
||||
snappedVictimToMe = RotateVector( (-1, 0, 0), victim.angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
cross = maps\mp\agents\dog\_dog_think::cross2D( victimToMe, victimFacing );
|
||||
if ( cross > 0 )
|
||||
{
|
||||
animEntry = 3; // right
|
||||
snappedVictimToMe = RotateVector( (0, -1, 0), victim.angles );
|
||||
}
|
||||
else
|
||||
{
|
||||
animEntry = 2; // left
|
||||
snappedVictimToMe = RotateVector( (0, 1, 0), victim.angles );
|
||||
}
|
||||
}
|
||||
|
||||
if ( animEntry == 1 )
|
||||
cClearanceRequired = 128;
|
||||
else
|
||||
cClearanceRequired = 96;
|
||||
landPos = victim.origin - cClearanceRequired * snappedVictimToMe;
|
||||
|
||||
landPosDropped = self DropPosToGround( landPos );
|
||||
if ( !IsDefined( landPosDropped ) )
|
||||
return undefined;
|
||||
|
||||
if ( abs( landPosDropped[2] - landPos[2] ) > cMaxHeightDiff )
|
||||
return undefined;
|
||||
|
||||
if ( !self AIPhysicsTracePassed( victim.origin + (0,0,4), landPosDropped + (0,0,4), self.radius, self.height ) )
|
||||
return undefined;
|
||||
|
||||
return animEntry;
|
||||
}
|
||||
|
||||
DoExtendedKill( animEntry )
|
||||
{
|
||||
meleeAnimState = "attack_extended";
|
||||
|
||||
self DoMeleeDamage( self.curMeleeTarget, self.curMeleeTarget.health, "MOD_MELEE_DOG" );
|
||||
|
||||
attackAnim = self GetAnimEntry( meleeAnimState, animEntry );
|
||||
self thread ExtendedKill_StickToVictim( attackAnim, self.curMeleeTarget.origin, self.curMeleeTarget.angles );
|
||||
|
||||
// if ( animEntry == 1 ) // back
|
||||
// self PlaySoundOnMovingEnt( "mp_dog_attack_quick_back_npc" );
|
||||
// else
|
||||
// self PlaySoundOnMovingEnt( "mp_dog_attack_short_npc" );
|
||||
|
||||
self PlayAnimNUntilNotetrack( meleeAnimState, animEntry, "attack", "end" );
|
||||
|
||||
self notify( "kill_stick" );
|
||||
self.curMeleeTarget = undefined;
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self Unlink();
|
||||
}
|
||||
|
||||
ExtendedKill_StickToVictim( attackAnim, targetOrigin, targetAngles )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "kill_stick" );
|
||||
|
||||
wait( 0.05 ); // must wait for anim to kick in before we can properly calculate our offsets.
|
||||
assert( IsDefined( self.curMeleeTarget ) );
|
||||
|
||||
if ( IsAlive( self.curMeleeTarget ) ) // godmode, etc.
|
||||
return;
|
||||
|
||||
corpse = self.curMeleeTarget GetCorpseEntity();
|
||||
assert( IsDefined( corpse ) );
|
||||
self LinkTo( corpse );
|
||||
|
||||
self ScrAgentDoAnimRelative( attackAnim, targetOrigin, targetAngles );
|
||||
}
|
||||
|
||||
DoStandardKill( attackPos, bCanMoveToAttackPos )
|
||||
{
|
||||
meleeAnimState = self GetMeleeAnimState();
|
||||
|
||||
bLerp = false;
|
||||
|
||||
if ( !bCanMoveToAttackPos )
|
||||
{
|
||||
if ( self AgentCanSeeSentient( self.curMeleeTarget ) )
|
||||
{
|
||||
groundPos = self DropPosToGround( self.curMeleeTarget.origin );
|
||||
if ( IsDefined( groundPos ) )
|
||||
{
|
||||
bLerp = true;
|
||||
attackPos = groundPos; // i'm going to clip the heck through him, but i need a guaranteed safe spot.
|
||||
}
|
||||
else
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self MeleeFailed();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self.lastMeleeFailedMyPos = undefined;
|
||||
self.lastMeleeFailedPos = undefined;
|
||||
|
||||
attackAnim = self GetAnimEntry( meleeAnimState, 0 );
|
||||
animLength = GetAnimLength( attackAnim );
|
||||
meleeNotetracks = GetNotetrackTimes( attackAnim, "dog_melee" );
|
||||
if ( meleeNotetracks.size > 0 )
|
||||
lerpTime = meleeNotetracks[0] * animLength;
|
||||
else
|
||||
lerpTime = animLength;
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, attackPos, lerpTime );
|
||||
|
||||
self thread UpdateLerpPos( self.curMeleeTarget, lerpTime, bCanMoveToAttackPos );
|
||||
|
||||
self PlayAnimNUntilNotetrack( meleeAnimState, 0, "attack", "dog_melee" );
|
||||
|
||||
self notify( "cancel_updatelerppos" );
|
||||
|
||||
damageDealt = 0;
|
||||
if( IsDefined( self.curMeleeTarget ) )
|
||||
damageDealt = self.curMeleeTarget.health;
|
||||
if( IsDefined( self.meleeDamage ) )
|
||||
damageDealt = self.meleeDamage;
|
||||
|
||||
if( IsDefined( self.curMeleeTarget ) )
|
||||
self DoMeleeDamage( self.curMeleeTarget, damageDealt, "MOD_IMPACT" );
|
||||
|
||||
self.curMeleeTarget = undefined; // dude's dead now, or soon will be.
|
||||
|
||||
if ( bLerp )
|
||||
self ScrAgentSetAnimScale( 0, 1 );
|
||||
else
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
self WaitUntilNotetrack( "attack", "end" );
|
||||
}
|
||||
|
||||
UpdateLerpPos( enemy, lerpTime, bCanMoveToAttackPos )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "death" );
|
||||
self endon( "cancel_updatelerppos" );
|
||||
enemy endon( "disconnect" );
|
||||
enemy endon( "death" );
|
||||
|
||||
timeRemaining = lerpTime;
|
||||
interval = 0.05;
|
||||
while ( true )
|
||||
{
|
||||
wait( interval );
|
||||
timeRemaining -= interval;
|
||||
|
||||
if ( timeRemaining <= 0 )
|
||||
break;
|
||||
|
||||
attackPos = GetUpdatedAttackPos( enemy, bCanMoveToAttackPos );
|
||||
if ( !IsDefined( attackPos ) )
|
||||
break;
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, attackPos, timeRemaining );
|
||||
}
|
||||
}
|
||||
|
||||
GetUpdatedAttackPos( enemy, bCanMove )
|
||||
{
|
||||
if ( !bCanMove )
|
||||
{
|
||||
droppedPos = self DropPosToGround( enemy.origin );
|
||||
return droppedPos;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = enemy.origin - self.origin;
|
||||
distMeToTarget = Length( meToTarget );
|
||||
|
||||
if ( distMeToTarget < self.attackOffset )
|
||||
{
|
||||
return self.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
meToTarget = meToTarget / distMeToTarget;
|
||||
attackPos = enemy.origin - meToTarget * self.attackOffset;
|
||||
if ( self CanMovePointToPoint( self.origin, attackPos ) )
|
||||
return attackPos;
|
||||
else
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IsProtectedByRiotshield( enemy )
|
||||
{
|
||||
if ( enemy maps\mp\_riotshield::hasRiotShield() )
|
||||
{
|
||||
enemyToMe = self.origin - enemy.origin;
|
||||
meToEnemy = VectorNormalize( ( enemyToMe[0], enemyToMe[1], 0 ) );
|
||||
|
||||
enemyFacing = AnglesToForward( enemy.angles );
|
||||
angleToMe = VectorDot( enemyFacing, enemyToMe );
|
||||
|
||||
if ( enemy maps\mp\_riotshield::hasRiotShieldEquipped() )
|
||||
{
|
||||
if ( angleToMe > 0.766 )
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( angleToMe < -0.766 )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DoMeleeDamage( enemy, damage, meansOfDeath )
|
||||
{
|
||||
if ( self IsProtectedByRiotshield( enemy ) )
|
||||
return;
|
||||
|
||||
enemy DoDamage( damage, self.origin, self, self, meansOfDeath );
|
||||
}
|
||||
|
||||
MeleeFailed()
|
||||
{
|
||||
self.lastMeleeFailedMyPos = self.origin;
|
||||
self.lastMeleeFailedPos = self.curMeleeTarget.origin;
|
||||
}
|
490
raw/maps/mp/agents/dog/_dog_move.gsc
Normal file
490
raw/maps/mp/agents/dog/_dog_move.gsc
Normal file
@ -0,0 +1,490 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
|
||||
self StartMove();
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self CancelAllBut( undefined );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
|
||||
SetupMovement()
|
||||
{
|
||||
self thread WaitForRunWalkChange();
|
||||
self thread WaitForSharpTurn();
|
||||
self thread WaitForStop();
|
||||
//self thread WaitForStopEarly();
|
||||
//self thread HandleMoveLoopFootsteps();
|
||||
}
|
||||
|
||||
ContinueMovement()
|
||||
{
|
||||
self SetupMovement();
|
||||
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self SetMoveAnim( self.moveMode );
|
||||
}
|
||||
|
||||
SetMoveAnim( moveMode )
|
||||
{
|
||||
self SetAnimState( moveMode );
|
||||
}
|
||||
|
||||
WaitForRunWalkChange()
|
||||
{
|
||||
self endon( "dogmove_endwait_runwalk" );
|
||||
curMovement = self.moveMode;
|
||||
while ( true )
|
||||
{
|
||||
if ( curMovement != self.moveMode )
|
||||
{
|
||||
self SetMoveAnim( self.moveMode );
|
||||
curMovement = self.moveMode;
|
||||
}
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
DoSharpTurn( newDir )
|
||||
{
|
||||
lookaheadAngles = VectorToAngles( newDir );
|
||||
angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
|
||||
if ( angleIndex == 4 ) // 4 means this turn wasn't sharp enough for me to care. (angle ~= 0)
|
||||
{
|
||||
ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
animState = "sharp_turn";
|
||||
|
||||
turnAnim = self GetAnimEntry( animState, angleIndex );
|
||||
animAngleDelta = GetAngleDelta( turnAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - animAngleDelta ), 0 ) );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, angleIndex, "sharp_turn" );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForSharpTurn()
|
||||
{
|
||||
self endon( "dogmove_endwait_sharpturn" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
self CancelAllBut( "sharpturn" );
|
||||
|
||||
self DoSharpTurn( newDir );
|
||||
}
|
||||
|
||||
WaitForStop()
|
||||
{
|
||||
self endon( "dogmove_endwait_stop" );
|
||||
|
||||
self waittill( "stop_soon" );
|
||||
|
||||
if ( IsDefined( self.bArrivalsEnabled ) && !self.bArrivalsEnabled )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopState = self GetStopAnimState();
|
||||
|
||||
stopAnim = self GetAnimEntry( stopState.state, stopState.index );
|
||||
stopDelta = GetMoveDelta( stopAnim );
|
||||
stopAngleDelta = GetAngleDelta( stopAnim );
|
||||
|
||||
goalPos = self GetPathGoalPos();
|
||||
assert( IsDefined( goalPos ) );
|
||||
|
||||
meToStop = goalPos - self.origin;
|
||||
// not enough room left to play the animation. abort. (i'm willing to squish/scale the anim up to 12 units.)
|
||||
if ( Length( meToStop ) + 12 < Length( stopDelta ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopData = self GetStopData();
|
||||
stopStartPos = self CalcAnimStartPos( stopData.pos, stopData.angles[1], stopDelta, stopAngleDelta );
|
||||
stopStartPosDropped = DropPosToGround( stopStartPos );
|
||||
|
||||
if ( !IsDefined( stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self CanMovePointToPoint( stopData.pos, stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "stop" );
|
||||
|
||||
self thread WaitForPathSetWhileStopping();
|
||||
self thread WaitForSharpTurnWhileStopping();
|
||||
if ( DistanceSquared( stopStartPos, self.origin ) > 4 )
|
||||
{
|
||||
self ScrAgentSetWaypoint( stopStartPos );
|
||||
self thread WaitForBlockedWhileStopping();
|
||||
self waittill( "waypoint_reached" );
|
||||
self notify( "dogmove_endwait_blockedwhilestopping" );
|
||||
}
|
||||
|
||||
facingDir = goalPos - self.origin;
|
||||
facingAngles = VectorToAngles( facingDir );
|
||||
facingYaw = ( 0, facingAngles[1] - stopAngleDelta, 0 );
|
||||
|
||||
// scale the anim if necessary, to make sure we end up where we wanted to end up.
|
||||
scaleFactors = GetAnimScaleFactors( goalPos - self.origin, stopDelta );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", facingYaw, ( 0, facingAngles[1], 0 ) );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( stopState.state, stopState.index, "move_stop" );
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin ); // whether i got where i was going, get where i got.
|
||||
}
|
||||
|
||||
WaitForPathSetWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_pathsetwhilestopping" );
|
||||
|
||||
oldGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
self waittill( "path_set" );
|
||||
|
||||
newGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
if ( DistanceSquared( oldGoalPos, newGoalPos ) < 1 )
|
||||
{
|
||||
self thread WaitForPathSetWhileStopping();
|
||||
return;
|
||||
}
|
||||
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
self notify( "dogmove_endwait_sharpturnwhilestopping" );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForSharpTurnWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_sharpturnwhilestopping" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
|
||||
self notify( "dogmove_endwait_pathsetwhilestopping" );
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
|
||||
self DoSharpTurn( newDir );
|
||||
}
|
||||
|
||||
WaitForBlockedWhileStopping()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_blockedwhilestopping" );
|
||||
|
||||
self waittill( "path_blocked" );
|
||||
self notify( "dogmove_endwait_stop" );
|
||||
self ScrAgentSetWaypoint( undefined );
|
||||
}
|
||||
|
||||
WaitForStopEarly()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "dogmove_endwait_stopearly" );
|
||||
|
||||
stopAnim = self GetAnimEntry( "move_stop_4", 0 );
|
||||
stopAnimTranslation = GetMoveDelta( stopAnim );
|
||||
stoppingDistance = Length( stopAnimTranslation );
|
||||
offset = self.preferredOffsetFromOwner + stoppingDistance;
|
||||
offsetSq = offset * offset;
|
||||
|
||||
if ( DistanceSquared( self.origin, self.owner.origin ) <= offsetSq )
|
||||
return;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( !IsDefined( self.owner ) )
|
||||
break;
|
||||
|
||||
if ( DistanceSquared( self.origin, self.owner.origin ) < offsetSq )
|
||||
{
|
||||
stopPos = self LocalToWorldCoords( stopAnimTranslation );
|
||||
self ScrAgentSetGoalPos( stopPos );
|
||||
break;
|
||||
}
|
||||
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
CancelAllBut( doNotCancel )
|
||||
{
|
||||
cleanups = [ "runwalk", "sharpturn", "stop", "pathsetwhilestopping", "blockedwhilestopping", "sharpturnwhilestopping", "stopearly" ];
|
||||
|
||||
bCheckDoNotCancel = IsDefined( doNotCancel );
|
||||
|
||||
foreach ( cleanup in cleanups )
|
||||
{
|
||||
if ( bCheckDoNotCancel && cleanup == doNotCancel )
|
||||
continue;
|
||||
self notify( "dogmove_endwait_" + cleanup );
|
||||
}
|
||||
}
|
||||
|
||||
StartMove()
|
||||
{
|
||||
negStartNode = self GetNegotiationStartNode();
|
||||
if ( IsDefined( negStartNode ) )
|
||||
goalPos = negStartNode.origin;
|
||||
else
|
||||
goalPos = self GetPathGoalPos();
|
||||
|
||||
// don't play start if i have no room for the start.
|
||||
if ( DistanceSquared( goalPos, self.origin ) < 100 * 100 )
|
||||
return;
|
||||
|
||||
lookaheadDir = self GetLookaheadDir();
|
||||
lookaheadAngles = VectorToAngles( lookaheadDir );
|
||||
|
||||
myVelocity = self GetVelocity();
|
||||
if ( Length2DSquared( myVelocity ) > 16 )
|
||||
{
|
||||
myVelocity = VectorNormalize( myVelocity );
|
||||
if ( VectorDot( myVelocity, lookaheadDir ) > 0.707 )
|
||||
return; // don't need a start if i'm already moving in the direction i want to move.
|
||||
}
|
||||
|
||||
angleDiff = AngleClamp180( lookaheadAngles[1] - self.angles[1] );
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
|
||||
startAnim = self GetAnimEntry( "move_start", angleIndex );
|
||||
startAnimTranslation = GetMoveDelta( startAnim );
|
||||
|
||||
endPos = RotateVector( startAnimTranslation, self.angles ) + self.origin;
|
||||
if ( !self CanMovePointToPoint( self.origin, endPos ) )
|
||||
return;
|
||||
|
||||
startAnimAngles = GetAngleDelta3D( startAnim );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
if ( 3 <= angleIndex && angleIndex <= 5 )
|
||||
self ScrAgentSetOrientMode( "face angle abs", ( 0, AngleClamp180( lookaheadAngles[1] - startAnimAngles[1] ), 0 ) );
|
||||
else
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNUntilNotetrack( "move_start", angleIndex, "move_start" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetStopData()
|
||||
{
|
||||
stopData = SpawnStruct();
|
||||
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
stopData.pos = self.node.origin;
|
||||
stopData.angles = self.node.angles;
|
||||
}
|
||||
else
|
||||
{
|
||||
pathGoalPos = self GetPathGoalPos();
|
||||
assert( IsDefined( pathGoalPos ) );
|
||||
stopData.pos = pathGoalPos;
|
||||
//stopData.angles = self.angles;
|
||||
stopData.angles = VectorToAngles( self GetLookaheadDir() );
|
||||
}
|
||||
|
||||
return stopData;
|
||||
}
|
||||
|
||||
GetStopAnimState( angle )
|
||||
{
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
angleDiff = self.node.angles[1] - self.angles[1];
|
||||
angleIndex = GetAngleIndex( angleDiff );
|
||||
}
|
||||
else
|
||||
{
|
||||
angleIndex = 4;
|
||||
}
|
||||
|
||||
result = SpawnStruct();
|
||||
result.state = "move_stop";
|
||||
result.index = angleIndex;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
CalcAnimStartPos( stopPos, stopAngle, animDelta, animAngleDelta )
|
||||
{
|
||||
dAngle = stopAngle - animAngleDelta;
|
||||
angles = ( 0, dAngle, 0 );
|
||||
vForward = AnglesToForward( angles );
|
||||
vRight = AnglesToRight( angles );
|
||||
|
||||
forward = vForward * animDelta[0];
|
||||
right = vRight * animDelta[1];
|
||||
|
||||
return stopPos - forward + right;
|
||||
}
|
||||
|
||||
Dog_AddLean()
|
||||
{
|
||||
leanFrac = Clamp( self.leanAmount / 25.0, -1, 1 );
|
||||
if ( leanFrac > 0 )
|
||||
{
|
||||
// set lean left( leanFrac );
|
||||
// set lean right( 0 );
|
||||
}
|
||||
else
|
||||
{
|
||||
// set lean left( 0 );
|
||||
// set lean right( 0 - leanFrac );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
HandleFootstepNotetracks( note, animState, animIndex, animTime )
|
||||
{
|
||||
if ( true )
|
||||
return false;
|
||||
|
||||
switch ( note )
|
||||
{
|
||||
case "footstep_front_left_small":
|
||||
case "footstep_front_right_small":
|
||||
case "footstep_back_left_small":
|
||||
case "footstep_back_right_small":
|
||||
case "footstep_front_left_large":
|
||||
case "footstep_front_right_large":
|
||||
case "footstep_back_left_large":
|
||||
case "footstep_back_right_large":
|
||||
{
|
||||
surfaceType = undefined;
|
||||
if ( IsDefined( self.surfaceType ) )
|
||||
{
|
||||
surfaceType = self.surfaceType;
|
||||
self.lastSurfaceType = surfaceType;
|
||||
}
|
||||
else if ( IsDefined( self.lastSurfaceType ) )
|
||||
{
|
||||
surfaceType = self.lastSurfaceType;
|
||||
}
|
||||
else
|
||||
{
|
||||
surfaceType = "dirt";
|
||||
}
|
||||
|
||||
if ( surfaceType != "dirt" && surfaceType != "concrete" && surfaceType != "wood" && surfaceType != "metal" )
|
||||
surfaceType = "dirt";
|
||||
|
||||
if ( surfaceType == "concrete" ) // code == concrete, sound == cement.
|
||||
surfaceType = "cement";
|
||||
|
||||
//moveType = self.sound_animMoveType;
|
||||
//if ( !IsDefined( moveType ) )
|
||||
// moveType = "run";
|
||||
if ( self.aiState == "traverse" )
|
||||
moveType = "land";
|
||||
else if ( self.moveMode == "sprint" )
|
||||
moveType = "sprint";
|
||||
else if ( self.moveMode == "fastwalk" )
|
||||
moveType = "walk";
|
||||
else
|
||||
moveType = "run";
|
||||
|
||||
self PlaySoundOnMovingEnt( "dogstep_" + moveType + "_" + surfaceType );
|
||||
|
||||
if ( IsSubStr( note, "front_left" ) )
|
||||
{
|
||||
soundAlias1 = "anml_dog_mvmt_accent";
|
||||
soundAlias2 = "anml_dog_mvmt_vest";
|
||||
|
||||
if ( moveType == "walk" )
|
||||
suffix = "_npc";
|
||||
else
|
||||
suffix = "_run_npc";
|
||||
|
||||
self PlaySoundOnMovingEnt( soundAlias1 + suffix );
|
||||
self PlaySoundOnMovingEnt( soundAlias2 + suffix );
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
DoHitReaction( hitAngle )
|
||||
{
|
||||
self CancelAllBut( undefined );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
self.stateLocked = true;
|
||||
|
||||
// hitAngle is angle from me to damage
|
||||
angleDiff = AngleClamp180( hitAngle - self.angles[1] );
|
||||
|
||||
if ( angleDiff > 0 )
|
||||
animIndex = 1; // left
|
||||
else
|
||||
animIndex = 0; // right
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNUntilNotetrack( "run_pain", animIndex, "run_pain" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
self.stateLocked = false;
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( self.stateLocked )
|
||||
return;
|
||||
|
||||
hitDirToAngles = VectorToAngles( vDir );
|
||||
hitAngle = hitDirToAngles[1] - 180;
|
||||
|
||||
self DoHitReaction( hitAngle );
|
||||
}
|
||||
|
||||
OnFlashbanged( origin, percent_distance, percent_angle, attacker, teamName, extraDuration )
|
||||
{
|
||||
if ( self.stateLocked )
|
||||
return;
|
||||
|
||||
DoHitReaction( self.angles[1] + 180 );
|
||||
}
|
1198
raw/maps/mp/agents/dog/_dog_think.gsc
Normal file
1198
raw/maps/mp/agents/dog/_dog_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
242
raw/maps/mp/agents/dog/_dog_traverse.gsc
Normal file
242
raw/maps/mp/agents/dog/_dog_traverse.gsc
Normal file
@ -0,0 +1,242 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
CONST_BOT_WALK_FORWARD_UNITS_PER_SECOND = 256;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
if ( !IsDefined( level.dogTraverseAnims ) )
|
||||
InitDogTraverseAnims();
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
assert( IsDefined( startNode ) && IsDefined( endNode ) );
|
||||
|
||||
if( startNode.animscript == "bot_walk_forward" )
|
||||
{
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
lerpTime = Length(startToEnd) / CONST_BOT_WALK_FORWARD_UNITS_PER_SECOND;
|
||||
startToEnd2D = ( startToEnd[0], startToEnd[1], 0);
|
||||
anglestoEnd = VectorToAngles( startToEnd2D );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglestoEnd );
|
||||
self ScrAgentDoAnimLerp( startNode.origin, endNode.origin, lerpTime );
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self PlayAnimForTime( "run", lerpTime );
|
||||
return;
|
||||
}
|
||||
|
||||
animState = undefined;
|
||||
animState = level.dogTraverseAnims[ startNode.animscript ];
|
||||
|
||||
if ( !IsDefined( animState ) )
|
||||
{
|
||||
assertmsg( "no animation for traverse " + startNode.animscript + "@ " + startNode.origin );
|
||||
return;
|
||||
}
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
startToEnd2D = ( startToEnd[0], startToEnd[1], 0 );
|
||||
anglesToEnd = VectorToAngles( startToEnd2D );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToEnd );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
traverseAnim = self GetAnimEntry( animState, 0 );
|
||||
|
||||
codeMoveNotetracks = GetNotetrackTimes( traverseAnim, "code_move" );
|
||||
if ( codeMoveNotetracks.size > 0 )
|
||||
moveDelta = GetMoveDelta( traverseAnim, 0, codeMoveNotetracks[0] );
|
||||
else
|
||||
moveDelta = GetMoveDelta( traverseAnim, 0, 1 );
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( startToEnd, moveDelta );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
|
||||
// the end node is higher than the start node.
|
||||
if ( startToEnd[2] > 0 )
|
||||
{
|
||||
if ( moveDelta[2] > 0 )
|
||||
{
|
||||
jumpStartNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_start" );
|
||||
if ( jumpStartNotetracks.size > 0 )
|
||||
{
|
||||
xyScale = 1;
|
||||
zScale = 1;
|
||||
if ( Length2DSquared( startToEnd2D ) < 0.8 * 0.8 * Length2DSquared( moveDelta ) )
|
||||
xyScale = 0.4;
|
||||
if ( startToEnd[2] < 0.75 * moveDelta[2] )
|
||||
zScale = 0.5;
|
||||
|
||||
self ScrAgentSetAnimScale( xyScale, zScale );
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "traverse_jump_start" );
|
||||
jumpEndNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_end" );
|
||||
assert( jumpEndNotetracks.size > 0 );
|
||||
jumpStartMoveDelta = GetMoveDelta( traverseAnim, 0, jumpStartNotetracks[0] );
|
||||
jumpEndMoveDelta = GetMoveDelta( traverseAnim, 0, jumpEndNotetracks[0] );
|
||||
|
||||
xyScale = 1;
|
||||
zScale = 1;
|
||||
currentToEnd = endNode.origin - self.origin;
|
||||
animToEnd = moveDelta - jumpStartMoveDelta;
|
||||
if ( Length2DSquared( currentToEnd ) < 0.75 * 0.75 * Length2DSquared( animToEnd ) )
|
||||
xyScale = 0.75;
|
||||
if ( currentToEnd[2] < 0.75 * animToEnd[2] )
|
||||
zScale = 0.75;
|
||||
|
||||
animJumpEndToEnd = moveDelta - jumpEndMoveDelta;
|
||||
scaledAnimJumpEndToEnd = ( animJumpEndToEnd[0] * xyScale, animJumpEndToEnd[1] * xyScale, animJumpEndToEnd[2] * zScale );
|
||||
worldAnimJumpEndToEnd = RotateVector( scaledAnimJumpEndToEnd, anglesToEnd );
|
||||
nodeJumpEndPos = endNode.origin - worldAnimJumpEndToEnd;
|
||||
|
||||
animJumpStartToJumpEnd = jumpEndMoveDelta - jumpStartMoveDelta;
|
||||
worldAnimJumpStartToJumpEnd = RotateVector( animJumpStartToJumpEnd, anglesToEnd );
|
||||
currentToNodeJumpEnd = nodeJumpEndPos - self.origin;
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( currentToNodeJumpEnd, worldAnimJumpStartToJumpEnd, true);
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self WaitUntilNotetrack( "traverse", "traverse_jump_end" );
|
||||
|
||||
self ScrAgentSetAnimScale( xyScale, zScale );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{ // can't do negative scale. use lerp.
|
||||
gravityOnNotetracks = GetNotetrackTimes( traverseAnim, "gravity on" );
|
||||
if ( gravityOnNotetracks.size > 0 )
|
||||
{
|
||||
targetEntPos = startNode GetTargetEntPos();
|
||||
if ( IsDefined( targetEntPos ) )
|
||||
{
|
||||
startToTarget = targetEntPos - self.origin;
|
||||
targetToEnd = endNode.origin - targetEntPos;
|
||||
|
||||
startDelta = GetMoveDelta( traverseAnim, 0, gravityOnNotetracks[0] );
|
||||
scaleFactors = self GetAnimScaleFactors( startToTarget, startDelta );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "gravity on" );
|
||||
|
||||
endDelta = GetMoveDelta( traverseAnim, gravityOnNotetracks[0], 1 );
|
||||
scaleFactors = self GetAnimScaleFactors( targetToEnd, endDelta );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
animLength = GetAnimLength( traverseAnim );
|
||||
self ScrAgentDoAnimLerp( startNode.origin, endNode.origin, animLength );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
gravityOnNotetracks = GetNotetrackTimes( traverseAnim, "gravity on" );
|
||||
if ( gravityOnNotetracks.size > 0 )
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, 1 );
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse", "gravity on" );
|
||||
|
||||
gravityOnMoveDelta = GetMoveDelta( traverseAnim, 0, gravityOnNotetracks[0] );
|
||||
zAnimDelta = gravityOnMoveDelta[2] - moveDelta[2];
|
||||
|
||||
if ( abs( zAnimDelta ) > 0 )
|
||||
{
|
||||
zMeToEnd = self.origin[2] - endNode.origin[2];
|
||||
|
||||
zScale = zMeToEnd / zAnimDelta;
|
||||
assert( zScale > 0 );
|
||||
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, zScale );
|
||||
|
||||
animrate = Clamp( 2 / zScale, 0.5, 1 );
|
||||
|
||||
norestart = animState + "_norestart";
|
||||
self SetAnimState( norestart, 0, animrate );
|
||||
}
|
||||
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
|
||||
animrate = Clamp( 2 / scaleFactors.z, 0.5, 1 );
|
||||
|
||||
jumpEndNotetracks = GetNotetrackTimes( traverseAnim, "traverse_jump_end" );
|
||||
if ( jumpEndNotetracks.size > 0 )
|
||||
{
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, 0, animrate, "traverse", "traverse_jump_end" );
|
||||
norestart = animState + "_norestart";
|
||||
self SetAnimState( norestart, 0, 1 );
|
||||
self WaitUntilNotetrack( "traverse", "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayAnimNUntilNotetrack( animState, 0, "traverse" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
GetTargetEntPos()
|
||||
{
|
||||
if ( IsDefined( self.targetEntPos ) )
|
||||
return self.targetEntPos;
|
||||
|
||||
targetEnt = GetEnt( self.target, "targetname" );
|
||||
if ( !IsDefined( targetEnt ) )
|
||||
return undefined;
|
||||
|
||||
self.targetEntPos = targetEnt.origin;
|
||||
targetEnt delete();
|
||||
return self.targetEntPos;
|
||||
}
|
||||
|
||||
InitDogTraverseAnims()
|
||||
{
|
||||
level.dogTraverseAnims = [];
|
||||
|
||||
level.dogTraverseAnims[ "hjk_tree_hop" ] = "traverse_jump_over_24";
|
||||
level.dogTraverseAnims[ "jump_across_72" ] = "traverse_jump_over_24";
|
||||
level.dogTraverseAnims[ "wall_hop" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_2" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "wall_over_40" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "wall_over" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_divethrough_36" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_over_40" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "window_over_quick" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "jump_up_80" ] = "traverse_jump_up_70";
|
||||
level.dogTraverseAnims[ "jump_standing_80" ] = "traverse_jump_up_70";
|
||||
level.dogTraverseAnims[ "jump_down_80" ] = "traverse_jump_down_70";
|
||||
level.dogTraverseAnims[ "jumpdown_96" ] = "traverse_jump_down_70";
|
||||
level.dogTraverseAnims[ "jump_up_40" ] = "traverse_jump_up_40";
|
||||
level.dogTraverseAnims[ "jump_down_40" ] = "traverse_jump_down_40";
|
||||
level.dogTraverseAnims[ "step_up" ] = "traverse_jump_up_24";
|
||||
level.dogTraverseAnims[ "step_up_24" ] = "traverse_jump_up_24";
|
||||
level.dogTraverseAnims[ "step_down" ] = "traverse_jump_down_24";
|
||||
level.dogTraverseAnims[ "jump_down" ] = "traverse_jump_down_24";
|
||||
level.dogTraverseAnims[ "jump_across" ] = "traverse_jump_over_36";
|
||||
level.dogTraverseAnims[ "jump_across_100" ] = "traverse_jump_over_36";
|
||||
}
|
||||
|
1265
raw/maps/mp/agents/dog/_instinct_dog_think.gsc
Normal file
1265
raw/maps/mp/agents/dog/_instinct_dog_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user