init
This commit is contained in:
129
maps/mp/agents/_agent_common.gsc
Normal file
129
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;
|
||||
}
|
447
maps/mp/agents/_agent_utility.gsc
Normal file
447
maps/mp/agents/_agent_utility.gsc
Normal file
@ -0,0 +1,447 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
//========================================================
|
||||
// agentFunc
|
||||
//========================================================
|
||||
agentFunc( func_name )
|
||||
{
|
||||
assert( IsAgent( self ) );
|
||||
assert( IsDefined(func_name) );
|
||||
assert( isDefined(self.agent_type) );
|
||||
assert( isDefined(level.agent_funcs[self.agent_type]) );
|
||||
assert( isDefined(level.agent_funcs[self.agent_type][func_name]) );
|
||||
|
||||
return level.agent_funcs[self.agent_type][func_name];
|
||||
}
|
||||
|
||||
//========================================================
|
||||
// set_agent_team
|
||||
//========================================================
|
||||
set_agent_team( team, optional_owner )
|
||||
{
|
||||
// since an agent entity has both a "sentient" and an "agent", we need both
|
||||
// these to understand the team the entity is on (much as client entities
|
||||
// have a "sentient" and a "client"). The "team" field sets the "sentient"
|
||||
// team and the "agentteam" field sets the "agent" team.
|
||||
self.team = team;
|
||||
self.agentteam = team;
|
||||
self.pers["team"] = team;
|
||||
|
||||
self.owner = optional_owner;
|
||||
self SetOtherEnt( optional_owner );
|
||||
self SetEntityOwner( optional_owner );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// initAgentScriptVariables
|
||||
//=======================================================
|
||||
initAgentScriptVariables()
|
||||
{
|
||||
self.agent_type = "player"; // TODO: communicate this to code?
|
||||
self.pers = [];
|
||||
self.hasDied = false;
|
||||
self.isActive = false;
|
||||
self.isAgent = true;
|
||||
self.wasTI = false;
|
||||
self.isSniper = false;
|
||||
self.spawnTime = 0;
|
||||
self.entity_number = self GetEntityNumber();
|
||||
self.agent_teamParticipant = false;
|
||||
self.agent_gameParticipant = false;
|
||||
self.canPerformClientTraces = false;
|
||||
self.agentname = undefined;
|
||||
|
||||
self DetachAll();
|
||||
|
||||
self initPlayerScriptVariables( false );
|
||||
}
|
||||
|
||||
|
||||
//========================================================
|
||||
// initPlayerScriptVariables
|
||||
//========================================================
|
||||
initPlayerScriptVariables( asPlayer )
|
||||
{
|
||||
if ( !asPlayer )
|
||||
{
|
||||
// Not as a player
|
||||
self.class = undefined;
|
||||
self.lastClass = undefined;
|
||||
self.moveSpeedScaler = undefined;
|
||||
self.avoidKillstreakOnSpawnTimer = undefined;
|
||||
self.guid = undefined;
|
||||
self.name = undefined;
|
||||
self.saved_actionSlotData = undefined;
|
||||
self.perks = undefined;
|
||||
self.weaponList = undefined;
|
||||
self.omaClassChanged = undefined;
|
||||
self.objectiveScaler = undefined;
|
||||
self.touchTriggers = undefined;
|
||||
self.carryObject = undefined;
|
||||
self.claimTrigger = undefined;
|
||||
self.canPickupObject = undefined;
|
||||
self.killedInUse = undefined;
|
||||
self.sessionteam = undefined;
|
||||
self.sessionstate = undefined;
|
||||
self.lastSpawnTime = undefined;
|
||||
self.lastspawnpoint = undefined;
|
||||
self.disabledWeapon = undefined;
|
||||
self.disabledWeaponSwitch = undefined;
|
||||
self.disabledOffhandWeapons = undefined;
|
||||
self.disabledUsability = undefined;
|
||||
self.shieldDamage = undefined;
|
||||
self.shieldBulletHits = undefined;
|
||||
self.recentShieldXP = undefined;
|
||||
}
|
||||
else
|
||||
{
|
||||
// As a player
|
||||
self.moveSpeedScaler = 1;
|
||||
self.avoidKillstreakOnSpawnTimer = 5;
|
||||
self.guid = self getUniqueId();
|
||||
self.name = self.guid;
|
||||
self.sessionteam = self.team;
|
||||
self.sessionstate = "playing";
|
||||
self.shieldDamage = 0;
|
||||
self.shieldBulletHits = 0;
|
||||
self.recentShieldXP = 0;
|
||||
self.agent_gameParticipant = true; // If initialized as a player, always make agent a game participant
|
||||
|
||||
self maps\mp\gametypes\_playerlogic::setupSavedActionSlots();
|
||||
self thread maps\mp\perks\_perks::onPlayerSpawned();
|
||||
|
||||
if ( IsGameParticipant( self ) )
|
||||
{
|
||||
self.objectiveScaler = 1;
|
||||
self maps\mp\gametypes\_gameobjects::init_player_gameobjects();
|
||||
self.disabledWeapon = 0;
|
||||
self.disabledWeaponSwitch = 0;
|
||||
self.disabledOffhandWeapons = 0;
|
||||
}
|
||||
}
|
||||
|
||||
self.disabledUsability = 1;
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// getFreeAgent
|
||||
//===========================================
|
||||
getFreeAgent( agent_type )
|
||||
{
|
||||
freeAgent = undefined;
|
||||
|
||||
if( IsDefined( level.agentArray ) )
|
||||
{
|
||||
foreach( agent in level.agentArray )
|
||||
{
|
||||
if( !IsDefined( agent.isActive ) || !agent.isActive )
|
||||
{
|
||||
if ( IsDefined(agent.waitingToDeactivate) && agent.waitingToDeactivate )
|
||||
continue;
|
||||
|
||||
freeAgent = agent;
|
||||
|
||||
freeAgent initAgentScriptVariables();
|
||||
|
||||
if ( IsDefined( agent_type ) )
|
||||
freeAgent.agent_type = agent_type; // TODO: communicate this to code?
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return freeAgent;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// activateAgent
|
||||
//=======================================================
|
||||
activateAgent()
|
||||
{
|
||||
/#
|
||||
if ( !self.isActive )
|
||||
{
|
||||
// Activating this agent, ensure that he has connected on the same frame
|
||||
AssertEx(self.connectTime == GetTime(), "Agent spawn took too long - there should be no waits in between connectNewAgent and spawning the agent");
|
||||
}
|
||||
#/
|
||||
self.isActive = true;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//=======================================================
|
||||
// deactivateAgent
|
||||
//=======================================================
|
||||
deactivateAgent()
|
||||
{
|
||||
self thread deactivateAgentDelayed();
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// deactivateAgentDelayed
|
||||
//=======================================================
|
||||
deactivateAgentDelayed()
|
||||
{
|
||||
self notify("deactivateAgentDelayed");
|
||||
self endon("deactivateAgentDelayed");
|
||||
|
||||
// During the 0.05s wait in deactivateAgentDelayed, the agent's script variables are all cleared out
|
||||
// So we need to do this now while IsGameParticipant can still be checked
|
||||
if ( IsGameParticipant(self) )
|
||||
self maps\mp\gametypes\_spawnlogic::removeFromParticipantsArray();
|
||||
|
||||
self maps\mp\gametypes\_spawnlogic::removeFromCharactersArray();
|
||||
|
||||
// Wait till next frame before we "disconnect"
|
||||
// That way things waiting on "death" but have endon("disconnect") will still function
|
||||
// e.g. maps\mp\killstreaks\_juggernaut::juggRemover()
|
||||
wait 0.05;
|
||||
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
self.owner = undefined;
|
||||
self.connectTime = undefined;
|
||||
self.waitingToDeactivate = undefined;
|
||||
|
||||
// Clear this agent from any other character's attackers array
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
if ( IsDefined( character.attackers ) )
|
||||
{
|
||||
foreach ( index, attacker in character.attackers )
|
||||
{
|
||||
if ( attacker == self )
|
||||
character.attackers[index] = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( self.headModel ) )
|
||||
{
|
||||
self Detach( self.headModel );
|
||||
self.headModel = undefined;
|
||||
}
|
||||
|
||||
self notify("disconnect");
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getNumActiveAgents
|
||||
//===========================================
|
||||
getNumActiveAgents( type )
|
||||
{
|
||||
if ( !IsDefined(type) )
|
||||
type = "all";
|
||||
|
||||
agents = getActiveAgentsOfType(type);
|
||||
return agents.size;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getActiveAgentsOfType
|
||||
//===========================================
|
||||
getActiveAgentsOfType( type )
|
||||
{
|
||||
Assert(IsDefined(type));
|
||||
agents = [];
|
||||
|
||||
if ( !IsDefined( level.agentArray ) )
|
||||
return agents;
|
||||
|
||||
foreach ( agent in level.agentArray )
|
||||
{
|
||||
if ( IsDefined( agent.isActive ) && agent.isActive )
|
||||
{
|
||||
if ( type == "all" || agent.agent_type == type )
|
||||
agents[agents.size] = agent;
|
||||
}
|
||||
}
|
||||
|
||||
return agents;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// getNumOwnedActiveAgents
|
||||
//===========================================
|
||||
getNumOwnedActiveAgents( player )
|
||||
{
|
||||
return getNumOwnedActiveAgentsByType( player, "all" );
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// getNumOwnedActiveAgentsByType
|
||||
//===========================================
|
||||
getNumOwnedActiveAgentsByType( player, type )
|
||||
{
|
||||
Assert(IsDefined(type));
|
||||
numOwnedActiveAgents = 0;
|
||||
|
||||
if( !IsDefined(level.agentArray) )
|
||||
{
|
||||
return numOwnedActiveAgents;
|
||||
}
|
||||
|
||||
foreach( agent in level.agentArray )
|
||||
{
|
||||
if( IsDefined( agent.isActive ) && agent.isActive )
|
||||
{
|
||||
if ( IsDefined(agent.owner) && (agent.owner == player) )
|
||||
{
|
||||
// Adding exclusion for "alien" type from a request for "all" to prevent the Seeker killstreak in mp_dome_ns from overloading the max allowable agents per player.
|
||||
if ( ( type == "all" && agent.agent_type != "alien" ) || agent.agent_type == type )
|
||||
numOwnedActiveAgents++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return numOwnedActiveAgents;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// getValidSpawnPathNodeNearPlayer
|
||||
//=======================================================
|
||||
getValidSpawnPathNodeNearPlayer( bDoPhysicsTraceToPlayer, bDoPhysicsTraceToValidateNode ) // self = player
|
||||
{
|
||||
assert( isPlayer( self ) );
|
||||
|
||||
nodeArray = GetNodesInRadius( self.origin, 350, 64, 128, "Path" );
|
||||
|
||||
if( !IsDefined(nodeArray) || (nodeArray.size == 0) )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
if ( IsDefined(level.waterDeleteZ) && IsDefined(level.trigUnderWater) )
|
||||
{
|
||||
// Ignore any nodes where the agent would die immediately upon spawning
|
||||
nodeArrayOld = nodeArray;
|
||||
nodeArray = [];
|
||||
foreach( node in nodeArrayOld )
|
||||
{
|
||||
if ( node.origin[ 2 ] > level.waterDeleteZ || !IsPointInVolume( node.origin, level.trigUnderWater ) )
|
||||
nodeArray[nodeArray.size] = node;
|
||||
}
|
||||
}
|
||||
|
||||
playerDirection = AnglesToForward( self.angles );
|
||||
bestDot = -10;
|
||||
|
||||
playerHeight = maps\mp\gametypes\_spawnlogic::getPlayerTraceHeight( self );
|
||||
zOffset = ( 0, 0, playerHeight );
|
||||
|
||||
if ( !IsDefined(bDoPhysicsTraceToPlayer) )
|
||||
bDoPhysicsTraceToPlayer = false;
|
||||
|
||||
if ( !IsDefined(bDoPhysicsTraceToValidateNode) )
|
||||
bDoPhysicsTraceToValidateNode = false;
|
||||
|
||||
pathNodeSortedByDot = [];
|
||||
pathNodeDotValues = [];
|
||||
foreach( pathNode in nodeArray )
|
||||
{
|
||||
if ( !pathNode DoesNodeAllowStance("stand") || isDefined ( pathnode.no_agent_spawn) )
|
||||
continue;
|
||||
|
||||
|
||||
directionToNode = VectorNormalize( pathNode.origin - self.origin );
|
||||
dot = VectorDot( playerDirection, directionToNode );
|
||||
|
||||
i = 0;
|
||||
for ( ; i < pathNodeDotValues.size; i++ )
|
||||
{
|
||||
if ( dot > pathNodeDotValues[i] )
|
||||
{
|
||||
for ( j = pathNodeDotValues.size; j > i; j-- )
|
||||
{
|
||||
pathNodeDotValues[j] = pathNodeDotValues[j-1];
|
||||
pathNodeSortedByDot[j] = pathNodeSortedByDot[j-1];
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
pathNodeSortedByDot[i] = pathNode;
|
||||
pathNodeDotValues[i] = dot;
|
||||
}
|
||||
|
||||
// pick a path node in the player's view
|
||||
for ( i = 0; i < pathNodeSortedByDot.size; i++ )
|
||||
{
|
||||
pathNode = pathNodeSortedByDot[i];
|
||||
|
||||
traceStart = self.origin + zOffset;
|
||||
traceEnd = pathNode.origin + zOffset;
|
||||
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
// prevent selecting a node that the player cannot see
|
||||
if( !SightTracePassed( traceStart, traceEnd, false, self ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bDoPhysicsTraceToValidateNode )
|
||||
{
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
hitPos = PlayerPhysicsTrace( pathNode.origin + zOffset, pathNode.origin );
|
||||
if ( DistanceSquared( hitPos, pathNode.origin ) > 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bDoPhysicsTraceToPlayer )
|
||||
{
|
||||
if ( i > 0 )
|
||||
wait(0.05); // Spread out the traces across multiple frames
|
||||
|
||||
hitPos = PhysicsTrace( traceStart, traceEnd );
|
||||
if ( DistanceSquared( hitPos, traceEnd ) > 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
return pathNode;
|
||||
}
|
||||
|
||||
// always return a node for safeguard
|
||||
if( (pathNodeSortedByDot.size > 0) && IsDefined(level.isHorde) )
|
||||
return pathNodeSortedByDot[0];
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// killAgent
|
||||
//=======================================================
|
||||
killAgent( agent )
|
||||
{
|
||||
// do enough damage to kill the agent regardless of any damage mitigation
|
||||
agent DoDamage( agent.health + 500000, agent.origin );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// killDog
|
||||
//=======================================================
|
||||
killDog() // self == dog
|
||||
{
|
||||
self [[ self agentFunc( "on_damaged" ) ]](
|
||||
level, // eInflictor The entity that causes the damage.(e.g. a turret)
|
||||
undefined, // eAttacker The entity that is attacking.
|
||||
self.health + 1, // iDamage Integer specifying the amount of damage done
|
||||
0, // iDFlags Integer specifying flags that are to be applied to the damage
|
||||
"MOD_CRUSH", // sMeansOfDeath Integer specifying the method of death
|
||||
"none", // sWeapon The weapon number of the weapon used to inflict the damage
|
||||
( 0, 0, 0 ), // vPoint The point the damage is from?
|
||||
(0, 0, 0), // vDir The direction of the damage
|
||||
"none", // sHitLoc The location of the hit
|
||||
0 // psOffsetTime The time offset for the damage
|
||||
);
|
||||
}
|
502
maps/mp/agents/_agents.gsc
Normal file
502
maps/mp/agents/_agents.gsc
Normal file
@ -0,0 +1,502 @@
|
||||
#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();
|
||||
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
|
||||
|
||||
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 );
|
||||
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 maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
agent Suicide();
|
||||
drop_agent_players--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 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(use_randomized_personality) && use_randomized_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" );
|
||||
}
|
||||
|
||||
if ( IsDefined( difficulty ) )
|
||||
self maps\mp\bots\_bots_util::bot_set_difficulty( difficulty );
|
||||
|
||||
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::giveLoadout( 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();
|
||||
|
||||
self.hasDied = false;
|
||||
|
||||
self thread maps\mp\gametypes\_weapons::onPlayerSpawned();
|
||||
self thread maps\mp\gametypes\_healthoverlay::playerHealthRegen();
|
||||
self thread maps\mp\gametypes\_battlechatter_mp::onPlayerSpawned();
|
||||
|
||||
level notify( "spawned_agent_player", self );
|
||||
level notify( "spawned_agent", 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);
|
||||
self FinishAgentDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset, 0.0 );
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// 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;
|
||||
|
||||
// JC-ToDo: - Kept this here in case I bring back mine logic for the mk32
|
||||
// if ( sWeapon == "xm25_mp" && sMeansOfDeath == "MOD_IMPACT" )
|
||||
// iDamage = 95;
|
||||
|
||||
// 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 = ter_op( iDamage >= self.health, "hitkill" ,"standard" ); // adds final kill hitmarker to dogs
|
||||
|
||||
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) )
|
||||
{
|
||||
// TODO: should play vo for killing the agent
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_squad_mate" );
|
||||
}
|
||||
|
||||
self 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.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 [[level.weaponDropFunction]]( eAttacker, sMeansOfDeath );
|
||||
|
||||
// ragdoll
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
thread delayStartRagdoll( self.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
|
||||
|
||||
self 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";
|
||||
}
|
||||
}
|
193
maps/mp/agents/_agents_civ_hvt.gsc
Normal file
193
maps/mp/agents/_agents_civ_hvt.gsc
Normal file
@ -0,0 +1,193 @@
|
||||
#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\gametypes\_damage;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["civ_hvt"] = [];
|
||||
|
||||
level.agent_funcs["civ_hvt"]["spawn"] = ::onSpawn;
|
||||
level.agent_funcs["civ_hvt"]["think"] = ::agentThink;
|
||||
level.agent_funcs["civ_hvt"]["on_killed"] = ::onAgentKilled;
|
||||
level.agent_funcs["civ_hvt"]["on_damaged"] = maps\mp\agents\_agents::on_agent_player_damaged;
|
||||
level.agent_funcs["civ_hvt"]["on_damaged_finished"] = maps\mp\agents\_agents::agent_damage_finished;
|
||||
}
|
||||
|
||||
onSpawn( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty )
|
||||
{
|
||||
self.hvtIsFollowing = false;
|
||||
|
||||
self maps\mp\agents\_agents::spawn_agent_player( optional_spawnOrigin, optional_spawnAngles, optional_owner, use_randomized_personality, respawn_on_death, difficulty );
|
||||
|
||||
self thread handlePlayerUse();
|
||||
}
|
||||
|
||||
onAgentKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
self.defendNode = undefined;
|
||||
self.hvtTrigger MakeUnusable();
|
||||
self.hvtTrigger = undefined;
|
||||
|
||||
// ragdoll
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
thread delayStartRagdoll( self.body, sHitLoc, vDir, sWeapon, eInflictor, sMeansOfDeath );
|
||||
|
||||
if ( IsDefined( self.onKilledCallback ) )
|
||||
{
|
||||
self [[ self.onKilledCallback ]]( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration );
|
||||
}
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
|
||||
// send a message to owner
|
||||
self.owner notify( "hvt_killed" );
|
||||
}
|
||||
|
||||
agentThink()
|
||||
{
|
||||
self notify( "agent_think" );
|
||||
self endon( "agent_think" );
|
||||
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( self.hvtIsFollowing )
|
||||
{
|
||||
self followThink();
|
||||
}
|
||||
else
|
||||
{
|
||||
self waitThink( 150 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
waitThink( radius )
|
||||
{
|
||||
self BotSetStance("none");
|
||||
self BotClearScriptGoal();
|
||||
self bot_disable_tactical_goals();
|
||||
|
||||
defendNode = self.owner getValidSpawnPathNodeNearPlayer();
|
||||
|
||||
self.cur_defend_node = undefined;
|
||||
self.bot_defending = true;
|
||||
self.bot_defending_center = defendNode.origin;
|
||||
self.bot_defending_radius = radius;
|
||||
self.cur_defend_stance = "crouch";
|
||||
self.bot_defending_type = "protect";
|
||||
|
||||
result = "";
|
||||
while( result != "goal" )
|
||||
{
|
||||
self.cur_defend_node = defendNode;
|
||||
|
||||
self BotSetScriptGoalNode( self.cur_defend_node, "tactical" );
|
||||
result = self waittill_any_return( "goal", "bad_path" );
|
||||
|
||||
self.node_closest_to_defend_center = defendNode;
|
||||
|
||||
self.cur_defend_node = undefined;
|
||||
}
|
||||
|
||||
self childthread defense_watch_entrances_at_goal();
|
||||
|
||||
self waittill( "hvt_toggle" );
|
||||
}
|
||||
|
||||
followThink() // self == agent
|
||||
{
|
||||
self BotClearScriptGoal();
|
||||
self bot_disable_tactical_goals();
|
||||
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
{
|
||||
self bot_guard_player( self.owner, 250 );
|
||||
}
|
||||
|
||||
self waittill( "hvt_toggle" );
|
||||
}
|
||||
|
||||
handlePlayerUse() // self == agent
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
if ( !IsDefined( self.hvtTrigger ) )
|
||||
{
|
||||
self.hvtTrigger = Spawn( "script_model", self.origin );
|
||||
self.hvtTrigger LinkTo( self );
|
||||
}
|
||||
|
||||
self.hvtTrigger MakeUsable();
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player != self.owner )
|
||||
{
|
||||
self.hvtTrigger DisablePlayerUse( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.hvtTrigger EnablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
|
||||
self thread waitForPlayerConnect();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self setFollowerHintString();
|
||||
|
||||
self.hvtTrigger waittill ( "trigger", player );
|
||||
|
||||
assert( player == self.owner );
|
||||
|
||||
self.hvtIsFollowing = !self.hvtIsFollowing;
|
||||
// do something with the AI
|
||||
|
||||
print( "Is Following: " + self.hvtIsFollowing );
|
||||
|
||||
self notify( "hvt_toggle" );
|
||||
}
|
||||
}
|
||||
|
||||
setFollowerHintString()
|
||||
{
|
||||
hintString = &"MP_HVT_FOLLOW";
|
||||
if ( self.hvtIsFollowing )
|
||||
{
|
||||
hintString = &"MP_HVT_WAIT";
|
||||
}
|
||||
self.hvtTrigger setHintString( hintString );
|
||||
}
|
||||
|
||||
waitForPlayerConnect()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
self.hvtTrigger disablePlayerUse( player );
|
||||
}
|
||||
}
|
7
maps/mp/agents/_agents_gametype_aliens.gsc
Normal file
7
maps/mp/agents/_agents_gametype_aliens.gsc
Normal file
@ -0,0 +1,7 @@
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
14
maps/mp/agents/_agents_gametype_blitz.gsc
Normal file
14
maps/mp/agents/_agents_gametype_blitz.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
68
maps/mp/agents/_agents_gametype_conf.gsc
Normal file
68
maps/mp/agents/_agents_gametype_conf.gsc
Normal file
@ -0,0 +1,68 @@
|
||||
#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;
|
||||
|
||||
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;
|
||||
}
|
14
maps/mp/agents/_agents_gametype_cranked.gsc
Normal file
14
maps/mp/agents/_agents_gametype_cranked.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
14
maps/mp/agents/_agents_gametype_dm.gsc
Normal file
14
maps/mp/agents/_agents_gametype_dm.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
48
maps/mp/agents/_agents_gametype_dom.gsc
Normal file
48
maps/mp/agents/_agents_gametype_dom.gsc
Normal file
@ -0,0 +1,48 @@
|
||||
#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;
|
||||
|
||||
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;
|
||||
}
|
20
maps/mp/agents/_agents_gametype_grind.gsc
Normal file
20
maps/mp/agents/_agents_gametype_grind.gsc
Normal file
@ -0,0 +1,20 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = maps\mp\agents\_agents_gametype_conf::agent_squadmember_conf_think;
|
||||
level.agent_funcs["player"]["think"] = maps\mp\agents\_agents_gametype_conf::agent_player_conf_think;
|
||||
}
|
14
maps/mp/agents/_agents_gametype_gun.gsc
Normal file
14
maps/mp/agents/_agents_gametype_gun.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
550
maps/mp/agents/_agents_gametype_horde.gsc
Normal file
550
maps/mp/agents/_agents_gametype_horde.gsc
Normal file
@ -0,0 +1,550 @@
|
||||
#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\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_horde_util;
|
||||
#include maps\mp\gametypes\_horde_crates;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
|
||||
/#
|
||||
CONST_FORCE_DOG_SPAWN = false;
|
||||
CONST_FORCE_PLAYER_ENEMY_SPAWN = false;
|
||||
CONST_DISABLE_AUTO_AI_REMOVAL = false;
|
||||
#/
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
level thread runRoundSpawning();
|
||||
}
|
||||
|
||||
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_INFECTED";
|
||||
self.horde_type = "";
|
||||
}
|
||||
|
||||
runRoundSpawning()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "start_round" );
|
||||
|
||||
if( isSpecialRound() )
|
||||
{
|
||||
runSpecialRound();
|
||||
}
|
||||
else
|
||||
{
|
||||
runNormalRound();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
runSpecialRound()
|
||||
{
|
||||
runLootDrop();
|
||||
}
|
||||
|
||||
runNormalRound()
|
||||
{
|
||||
level childthread highlightLastEnemies();
|
||||
|
||||
while( level.currentEnemyCount < level.maxEnemyCount )
|
||||
{
|
||||
while( level.currentAliveEnemyCount < level.maxAliveEnemyCount )
|
||||
{
|
||||
createEnemy();
|
||||
|
||||
if( level.currentEnemyCount == level.maxEnemyCount )
|
||||
break;
|
||||
}
|
||||
|
||||
level waittill( "enemy_death" );
|
||||
}
|
||||
}
|
||||
|
||||
createEnemy()
|
||||
{
|
||||
/#
|
||||
if( CONST_FORCE_DOG_SPAWN )
|
||||
{
|
||||
createDogEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
/#
|
||||
if( CONST_FORCE_PLAYER_ENEMY_SPAWN )
|
||||
{
|
||||
createHumanoidEnemy();
|
||||
return;
|
||||
}
|
||||
#/
|
||||
|
||||
if( isDogRound() && (RandomIntRange(1, 101) < level.chanceToSpawnDog) )
|
||||
{
|
||||
createDogEnemy();
|
||||
}
|
||||
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") ]]();
|
||||
|
||||
level.currentEnemyCount++;
|
||||
level.currentAliveEnemyCount++;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
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( player in level.characters )
|
||||
{
|
||||
if( isOnHumanTeam(player) )
|
||||
continue;
|
||||
|
||||
if( isReallyAlive(player) )
|
||||
{
|
||||
player HudOutlineEnable( level.enemyOutlineColor, false );
|
||||
player.outlineColor = level.enemyOutlineColor;
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
hordeEnemyKilled( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
AssertEx( (level.currentAliveEnemyCount > 0), "currentAliveEnemyCount is below zero" );
|
||||
|
||||
level.currentAliveEnemyCount--;
|
||||
trackIntelKills( sWeapon, sMeansOfDeath );
|
||||
|
||||
level thread maps\mp\gametypes\horde::chanceToSpawnPickup( self );
|
||||
level notify( "enemy_death" );
|
||||
|
||||
// player attacker
|
||||
if( IsPlayer(eAttacker) )
|
||||
{
|
||||
awardHordeKill( eAttacker );
|
||||
|
||||
if( eAttacker _hasPerk("specialty_triggerhappy") )
|
||||
{
|
||||
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 );
|
||||
}
|
||||
}
|
||||
|
||||
trackIntelKills( sWeapon, sMeansOfDeath )
|
||||
{
|
||||
if( level.isTeamIntelComplete )
|
||||
return;
|
||||
|
||||
if( sWeapon == "none" )
|
||||
return;
|
||||
|
||||
if( sMeansOfDeath == "MOD_MELEE" )
|
||||
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) )
|
||||
{
|
||||
outlineStuckAI( self );
|
||||
|
||||
if( !bot_in_combat(240 * 1000) )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 240, 480 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
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 )
|
||||
{
|
||||
outlineStuckAI( self );
|
||||
|
||||
if( positionTime > 55 )
|
||||
break;
|
||||
}
|
||||
|
||||
if( checkExpireTime( spawnTime, 120, 240 ) )
|
||||
break;
|
||||
}
|
||||
|
||||
killAgent( self );
|
||||
}
|
||||
|
||||
checkExpireTime( spawnTime, highLightTime, expireTime )
|
||||
{
|
||||
aliveTime = (GetTime() - spawnTime) / 1000;
|
||||
|
||||
if( aliveTime > highLightTime )
|
||||
{
|
||||
outlineStuckAI( self );
|
||||
|
||||
if( aliveTime > expireTime )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
outlineStuckAI( agent )
|
||||
{
|
||||
agent HudOutlineEnable( level.enemyOutlineColor, false );
|
||||
agent.outlineColor = level.enemyOutlineColor;
|
||||
|
||||
/#
|
||||
agent HudOutlineEnable( 2, 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";
|
||||
|
||||
// pathing variables
|
||||
self.lasSetGoalPos = (0,0,0);
|
||||
self.bHasNoPath = false;
|
||||
self.randomPathStopTime = 0;
|
||||
|
||||
maps\mp\gametypes\horde::setEnemyAgentHealth( self );
|
||||
}
|
||||
|
||||
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 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);
|
||||
}
|
||||
}
|
||||
|
||||
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 );
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
14
maps/mp/agents/_agents_gametype_infect.gsc
Normal file
14
maps/mp/agents/_agents_gametype_infect.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
75
maps/mp/agents/_agents_gametype_mugger.gsc
Normal file
75
maps/mp/agents/_agents_gametype_mugger.gsc
Normal file
@ -0,0 +1,75 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"]["gametype_update"] = ::agent_squadmember_mugger_think;
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_mugger_think;
|
||||
}
|
||||
|
||||
agent_player_mugger_think()
|
||||
{
|
||||
self thread maps\mp\bots\_bots_gametype_mugger::bot_mugger_think();
|
||||
}
|
||||
|
||||
agent_squadmember_mugger_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
|
||||
if (IsBot( self.owner ) )
|
||||
{
|
||||
current_player_fov = self BotGetFovDot();
|
||||
}
|
||||
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_mugger::bot_find_visible_tags_mugger( 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;
|
||||
}
|
26
maps/mp/agents/_agents_gametype_sd.gsc
Normal file
26
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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
setup_callbacks();
|
||||
}
|
||||
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["player"]["think"] = ::agent_player_sd_think;
|
||||
}
|
||||
|
||||
agent_player_sd_think()
|
||||
{
|
||||
self _enableUsability();
|
||||
|
||||
self thread maps\mp\bots\_bots_gametype_sd::bot_sd_think();
|
||||
}
|
14
maps/mp/agents/_agents_gametype_siege.gsc
Normal file
14
maps/mp/agents/_agents_gametype_siege.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// Place Holder
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
14
maps/mp/agents/_agents_gametype_sotf.gsc
Normal file
14
maps/mp/agents/_agents_gametype_sotf.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
14
maps/mp/agents/_agents_gametype_sotf_ffa.gsc
Normal file
14
maps/mp/agents/_agents_gametype_sotf_ffa.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
14
maps/mp/agents/_agents_gametype_sr.gsc
Normal file
14
maps/mp/agents/_agents_gametype_sr.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
14
maps/mp/agents/_agents_gametype_war.gsc
Normal file
14
maps/mp/agents/_agents_gametype_war.gsc
Normal file
@ -0,0 +1,14 @@
|
||||
#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;
|
||||
|
||||
//=======================================================
|
||||
// main
|
||||
//=======================================================
|
||||
main()
|
||||
{
|
||||
// nothing for now...
|
||||
}
|
247
maps/mp/agents/_scriptedagents.gsc
Normal file
247
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 );
|
||||
}
|
590
maps/mp/agents/alien/_alien_agents.gsc
Normal file
590
maps/mp/agents/alien/_alien_agents.gsc
Normal file
@ -0,0 +1,590 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\gametypes\_gamelogic;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\alien\_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 maps\mp\agents\_agent_common::init();
|
||||
|
||||
level.spitter_last_cloud_time = 0;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// initAliens
|
||||
//=======================================================
|
||||
setup_callbacks()
|
||||
{
|
||||
if ( !IsDefined( level.agent_funcs ) )
|
||||
level.agent_funcs = [];
|
||||
|
||||
level.agent_funcs["alien"] = [];
|
||||
|
||||
level.agent_funcs["alien"]["spawn"] = ::alienAgentSpawn;
|
||||
|
||||
level.agent_funcs["alien"]["think"] = ::alienAgentThink;
|
||||
level.agent_funcs["alien"]["on_killed"] = maps\mp\alien\_death::onAlienAgentKilled;
|
||||
level.agent_funcs["alien"]["on_damaged"] = maps\mp\alien\_damage::onAlienAgentDamaged;
|
||||
level.agent_funcs["alien"]["on_damaged_finished"] = maps\mp\agents\alien\_alien_think::onDamageFinish;
|
||||
|
||||
level.alien_funcs["goon"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["minion"]["approach"] = maps\mp\agents\alien\_alien_minion::minion_approach;
|
||||
level.alien_funcs["spitter"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["elite"]["approach"] = maps\mp\agents\alien\_alien_elite::elite_approach;
|
||||
level.alien_funcs["brute"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["locust"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
level.alien_funcs["leper"]["approach"] = maps\mp\agents\alien\_alien_think::default_approach;
|
||||
|
||||
level.alien_funcs["goon"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["minion"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["spitter"]["combat"] = maps\mp\agents\alien\_alien_spitter::spitter_combat;
|
||||
level.alien_funcs["elite"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["brute"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["locust"]["combat"] = maps\mp\agents\alien\_alien_think::default_alien_combat;
|
||||
level.alien_funcs["leper"]["combat"] = maps\mp\agents\alien\_alien_leper::leper_combat;
|
||||
|
||||
level.alien_funcs["goon"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["minion"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["spitter"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["elite"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["brute"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["locust"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
level.alien_funcs["leper"]["badpath"] = maps\mp\agents\alien\_alien_think::handle_badpath;
|
||||
|
||||
level.used_nodes = [];
|
||||
level.used_nodes_list_size = 20;
|
||||
level.used_nodes_list_index = 0;
|
||||
|
||||
level.alien_jump_melee_speed = 1.05;
|
||||
level.alien_jump_melee_gravity = 900;
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// alienAgentThink
|
||||
//=======================================================
|
||||
alienAgentThink()
|
||||
{
|
||||
}
|
||||
|
||||
//=======================================================
|
||||
// alienAgentSpawn
|
||||
//=======================================================
|
||||
alienAgentSpawn( spawnOrigin, spawnAngles, alienType, introVignetteAnim )
|
||||
{
|
||||
if ( !isDefined( alienType ) )
|
||||
alienType = "wave goon";
|
||||
|
||||
alien_type = remove_spawn_type( alienType );
|
||||
|
||||
if ( !isDefined( spawnOrigin ) || !isDefined( spawnAngles ) )
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
}
|
||||
|
||||
self set_alien_model( alien_type );
|
||||
|
||||
// escape sequence, move aliens closer to players post spawn
|
||||
if ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) && self.agentteam == "axis" )
|
||||
{
|
||||
if ( !flag_exist( "nuke_went_off" ) || !flag( "nuke_went_off" ) )
|
||||
{
|
||||
self.noTriggerHurt = true;
|
||||
port_failed = false;
|
||||
|
||||
tokens = strtok( alienType, " " );
|
||||
type = tokens[ 0 ];
|
||||
if ( tokens.size > 1 )
|
||||
type = tokens[ 1 ];
|
||||
|
||||
prof_begin( "port_to_player_loc" );
|
||||
if ( alien_type == "spitter" && IsDefined( level.escape_spitter_target_node ) )
|
||||
{
|
||||
spawnOrigin = self maps\mp\alien\_spawnlogic::port_to_escape_spitter_location();
|
||||
}
|
||||
else
|
||||
{
|
||||
port_to_data = self maps\mp\alien\_spawnlogic::port_to_player_loc( type );
|
||||
if ( !isdefined( port_to_data ) )
|
||||
{
|
||||
port_failed = true;
|
||||
/#
|
||||
if ( GetDvarInt( "alien_debug_escape" ) > 0 )
|
||||
IPrintLnBold( "^1Failed to port alien" );
|
||||
#/
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnOrigin = port_to_data[ 0 ];
|
||||
spawnAngles = port_to_data[ 1 ];
|
||||
}
|
||||
}
|
||||
prof_end( "port_to_player_loc" );
|
||||
|
||||
if ( !port_failed )
|
||||
{
|
||||
spawnOrigin = GetGroundPosition( spawnOrigin, 16 );
|
||||
spawnOrigin -= ( 0, 0, 90 );
|
||||
|
||||
introVignetteAnim = level.cycle_data.spawn_node_info[ "queen_test" ].vignetteInfo[ type ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self spawn_alien_agent( spawnOrigin, spawnAngles, alien_type );
|
||||
level notify( "spawned_agent", self );
|
||||
|
||||
self set_alien_attributes( alienType );
|
||||
|
||||
self set_code_fields( alien_type );
|
||||
|
||||
self set_script_fields( spawnOrigin );
|
||||
|
||||
self set_threat_bias_group( alien_type );
|
||||
|
||||
self type_specific_init();
|
||||
|
||||
self setup_watcher();
|
||||
|
||||
self misc_setup();
|
||||
|
||||
if ( isDefined ( introVignetteAnim ) )
|
||||
self doIntroVignetteAnim( introVignetteAnim );
|
||||
|
||||
if ( isdefined( self.noTriggerHurt ) )
|
||||
self.noTriggerHurt = undefined;
|
||||
|
||||
self maps\mp\alien\_ffotd::onSpawnAlien();
|
||||
|
||||
//self ScrAgentUseModelCollisionBounds();
|
||||
|
||||
self thread maps\mp\agents\alien\_alien_think::main();
|
||||
}
|
||||
|
||||
set_code_fields( alien_type )
|
||||
{
|
||||
self.allowJump = true;
|
||||
self.allowladders = 1;
|
||||
self.moveMode = self get_default_movemode();
|
||||
self.radius = 15;
|
||||
self.height = 72;
|
||||
self.turnrate = 0.3;
|
||||
self.sharpTurnNotifyDist = 48;
|
||||
self.traverseSoonNotifyDist = level.alienAnimData.jumpLaunchArrival_maxMoveDelta;
|
||||
self.stopSoonNotifyDist = level.alienAnimData.stopSoon_NotifyDist;
|
||||
self.jumpCost = level.alien_types[ alien_type ].attributes[ "jump_cost" ];
|
||||
|
||||
// escape mode = jumps more
|
||||
if ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) )
|
||||
self.jumpCost = max( 0.85, self.jumpCost * 0.66 );
|
||||
|
||||
self.traverseCost = level.alien_types[ alien_type ].attributes[ "traverse_cost" ];
|
||||
self.runCost = level.alien_types[ alien_type ].attributes[ "run_cost" ];
|
||||
if ( IsDefined( level.alien_types[ alien_type ].attributes[ "wall_run_cost" ] ) )
|
||||
self ScrAgentSetWallRunCost( level.alien_types[ alien_type ].attributes[ "wall_run_cost" ] );
|
||||
}
|
||||
|
||||
get_default_movemode()
|
||||
{
|
||||
alien_type = self get_alien_type();
|
||||
switch( alien_type )
|
||||
{
|
||||
case "minion":
|
||||
return "walk";
|
||||
default:
|
||||
return "run";
|
||||
}
|
||||
}
|
||||
|
||||
set_threat_bias_group( alien_type )
|
||||
{
|
||||
if ( !can_attack_drill( alien_type ) )
|
||||
{
|
||||
self SetThreatBiasGroup( "dontattackdrill" );
|
||||
return;
|
||||
}
|
||||
|
||||
self SetThreatBiasGroup( "other_aliens" );
|
||||
}
|
||||
|
||||
can_attack_drill( alien_type )
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_can_attack_drill_override_func ) )
|
||||
{
|
||||
canAttackDrill = [[level.dlc_alien_can_attack_drill_override_func]]( alien_type );
|
||||
if ( IsDefined( canAttackDrill ) )
|
||||
return canAttackDrill;
|
||||
}
|
||||
|
||||
switch( alien_type )
|
||||
{
|
||||
case "elite":
|
||||
case "minion":
|
||||
case "locust":
|
||||
case "gargoyle":
|
||||
case "mammoth":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
set_script_fields( spawnOrigin )
|
||||
{
|
||||
self.species = "alien";
|
||||
self.enableStop = true;
|
||||
self activateAgent();
|
||||
self.spawnTime = GetTime();
|
||||
self.attacking_player = false;
|
||||
self.spawnOrigin = spawnOrigin;
|
||||
self.recentDamages = [];
|
||||
self.damageListIndex = 0;
|
||||
self.swipeChance = 0.5;
|
||||
self.leapEndPos = undefined;
|
||||
self.trajectoryActive = false;
|
||||
self.melee_in_move_back = false;
|
||||
self.melee_in_posture = false;
|
||||
}
|
||||
|
||||
remove_spawn_type( alienType )
|
||||
{
|
||||
// if spawn type is passed in, it is the first token delimited by a space, second token is alien type
|
||||
spawnTypeConfig = strtok( alienType, " " );
|
||||
if ( isdefined( spawnTypeConfig ) && spawnTypeConfig.size == 2 )
|
||||
return spawnTypeConfig[ 1 ];
|
||||
else
|
||||
return alienType;
|
||||
}
|
||||
|
||||
set_alien_model( alien_type )
|
||||
{
|
||||
// During kraken fight
|
||||
if ( isDefined( level.get_alien_model_func ) )
|
||||
{
|
||||
alien_model = [[level.get_alien_model_func]]( alien_type );
|
||||
}
|
||||
else
|
||||
{
|
||||
alien_model = level.alien_types[ alien_type ].attributes[ "model" ];
|
||||
}
|
||||
|
||||
self SetModel( alien_model );
|
||||
self show();
|
||||
self MotionBlurHQEnable();
|
||||
}
|
||||
|
||||
spawn_alien_agent( spawnOrigin, spawnAngles, alien_type )
|
||||
{
|
||||
// the self.OnEnterAnimState field needs to be set before SpawnAgent
|
||||
self.OnEnterAnimState = maps\mp\agents\alien\_alien_think::onEnterAnimState;
|
||||
anim_class = get_anim_class( alien_type );
|
||||
self SpawnAgent( spawnOrigin, spawnAngles, anim_class, 15, 50 );
|
||||
}
|
||||
|
||||
get_anim_class( alien_type )
|
||||
{
|
||||
return level.alien_types[ alien_type ].attributes[ "animclass" ];
|
||||
}
|
||||
|
||||
set_alien_attributes( alienType )
|
||||
{
|
||||
self maps\mp\alien\_spawnlogic::assign_alien_attributes( alienType );
|
||||
}
|
||||
|
||||
type_specific_init()
|
||||
{
|
||||
switch ( maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "elite":
|
||||
maps\mp\agents\alien\_alien_elite::elite_init();
|
||||
break;
|
||||
case "minion":
|
||||
maps\mp\agents\alien\_alien_minion::minion_init();
|
||||
break;
|
||||
case "spitter":
|
||||
maps\mp\agents\alien\_alien_spitter::spitter_init();
|
||||
break;
|
||||
case "leper":
|
||||
maps\mp\agents\alien\_alien_leper::leper_init();
|
||||
break;
|
||||
default:
|
||||
// Check for level specific override
|
||||
if( isDefined( level.dlc_alien_init_override_func ))
|
||||
{
|
||||
[[level.dlc_alien_init_override_func]]();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
misc_setup()
|
||||
{
|
||||
self ScrAgentSetClipMode( "agent" );
|
||||
self TakeAllWeapons();
|
||||
//self maps\mp\agents\alien\_alien_anim_utils::calculateAnimData();
|
||||
}
|
||||
|
||||
setup_watcher()
|
||||
{
|
||||
self thread maps\mp\agents\alien\_alien_think::watch_for_scripted();
|
||||
self thread maps\mp\agents\alien\_alien_think::watch_for_badpath();
|
||||
self thread maps\mp\agents\alien\_alien_think::watch_for_insolid();
|
||||
self thread maps\mp\_flashgrenades::MonitorFlash();
|
||||
self thread maps\mp\agents\alien\_alien_think::MonitorFlash();
|
||||
|
||||
/#
|
||||
if ( GetDvarInt( "scr_aliendebugvelocity" ) == 1 )
|
||||
self thread maps\mp\alien\_debug::alienDebugVelocity();
|
||||
#/
|
||||
}
|
||||
|
||||
doIntroVignetteAnim( vignetteAnimInfo )
|
||||
{
|
||||
CONST_ANIM_STATE_INDEX = 0;
|
||||
CONST_ANIM_INDEX_ARRAY_INDEX = 1;
|
||||
CONST_LABEL_INDEX = 2;
|
||||
CONST_END_NOTETRACK_INDEX = 3;
|
||||
CONST_FX_INDEX = 4;
|
||||
CONST_SCRIPTABLE_TARGETNAME_INDEX = 5;
|
||||
CONST_SCRIPTABLE_STATE_INDEX = 6;
|
||||
CONST_SPAWN_NODE_ID_INDEX = 7;
|
||||
|
||||
self ScrAgentSetScripted( true );
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
vignetteAnimInfo = StrTok( vignetteAnimInfo, ";" );
|
||||
|
||||
self.vignetteAnimInfo = [];
|
||||
self.vignetteAnimInfo["FX"] = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_FX_INDEX] );
|
||||
self.vignetteAnimInfo["scriptableName"] = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SCRIPTABLE_TARGETNAME_INDEX] ), "," );
|
||||
self.vignetteAnimInfo["scriptableState"] = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SCRIPTABLE_STATE_INDEX] ), "," );
|
||||
self.vignetteAnimInfo["spawnNodeID"] = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_SPAWN_NODE_ID_INDEX] );
|
||||
|
||||
animState = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_ANIM_STATE_INDEX] );
|
||||
indexArray = StrTok( replaceNoneWithEmptyString( vignetteAnimInfo[CONST_ANIM_INDEX_ARRAY_INDEX] ), "," );
|
||||
animIndex = int( indexArray [ randomInt ( indexArray.size ) ] );
|
||||
animLabel = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_LABEL_INDEX] );
|
||||
endNotetrack = replaceNoneWithEmptyString( vignetteAnimInfo[CONST_END_NOTETRACK_INDEX] );
|
||||
|
||||
animEntry = self GetAnimEntry( animState, animIndex );
|
||||
|
||||
if ( shouldDoGroundLerp( animEntry ) )
|
||||
doLerpToEndOnGround( animState, animIndex );
|
||||
|
||||
if ( willPlayScriptables( animEntry ) )
|
||||
resetAllScriptables( self.vignetteAnimInfo["scriptableName"], self.origin );
|
||||
|
||||
result = maps\mp\agents\alien\_alien_traverse::needFlexibleHeightSupport( animEntry );
|
||||
|
||||
if( result.need_support )
|
||||
doSpawnVignetteWithFlexibleHeight( animState, animIndex, animLabel, animEntry, result.start_notetrack, result.end_notetrack, ::vignetteNotetrackHandler );
|
||||
else
|
||||
maps\mp\agents\_scriptedAgents::PlayAnimNUntilNotetrack( animState, animIndex, animLabel, endNotetrack, ::vignetteNotetrackHandler );
|
||||
|
||||
self ScrAgentSetScripted( false );
|
||||
}
|
||||
|
||||
shouldDoGroundLerp( animEntry )
|
||||
{
|
||||
return !( AnimHasNotetrack ( animEntry, "skip_ground_lerp" ) );
|
||||
}
|
||||
|
||||
willPlayScriptables( animEntry )
|
||||
{
|
||||
return ( AnimHasNotetrack( animEntry, "play_scriptable" ) && can_play_scriptable( self.vignetteAnimInfo["spawnNodeID"], self.vignetteAnimInfo["scriptableName"] ) );
|
||||
}
|
||||
|
||||
doSpawnVignetteWithFlexibleHeight( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, notetrackHandlerFunc )
|
||||
{
|
||||
maps\mp\agents\_scriptedAgents::PlayAnimNUntilNotetrack( animState, animIndex, animLabel, startNotetrack, notetrackHandlerFunc );
|
||||
|
||||
ground_pos = getEndLocOnGround( animEntry );
|
||||
maps\mp\agents\alien\_alien_traverse::doTraversalWithFlexibleHeight_internal( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, ground_pos , 1, ::vignetteNotetrackHandler );
|
||||
}
|
||||
|
||||
getEndLocOnGround( animEntry )
|
||||
{
|
||||
DROP_TO_GROUND_UP_DIST = 32;
|
||||
DROP_TO_GROUND_DOWN_DIST = -300;
|
||||
|
||||
AnimEndLoc = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( animEntry, self.origin, self.angles, GetAnimLength( animEntry ) );
|
||||
return drop_to_ground( AnimEndLoc, DROP_TO_GROUND_UP_DIST, DROP_TO_GROUND_DOWN_DIST );
|
||||
}
|
||||
|
||||
replaceNoneWithEmptyString( string )
|
||||
{
|
||||
if( string == "NONE" )
|
||||
return "";
|
||||
|
||||
return string;
|
||||
}
|
||||
|
||||
vignetteNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "alien_drone_spawn_underground":
|
||||
case "play_fx":
|
||||
if ( !is_empty_string( self.vignetteAnimInfo["FX"] ) )
|
||||
playSpawnVignetteFX( self.vignetteAnimInfo["FX"] );
|
||||
break;
|
||||
|
||||
case "play_scriptable":
|
||||
if ( can_play_scriptable( self.vignetteAnimInfo["spawnNodeID"], self.vignetteAnimInfo["scriptableName"] ) )
|
||||
{
|
||||
playAnimOnAllScriptables( self.vignetteAnimInfo["scriptableName"], self.origin, self.vignetteAnimInfo["scriptableState"] );
|
||||
|
||||
if ( is_one_off_scriptable( self.vignetteAnimInfo["spawnNodeID"] ) )
|
||||
inactivate_scriptable_for_node( self.vignetteAnimInfo["spawnNodeID"] );
|
||||
}
|
||||
break;
|
||||
|
||||
case "play_earthquake":
|
||||
Earthquake( 0.5, 1.5, self.origin, 800 );
|
||||
break;
|
||||
|
||||
case "delete_spawn_clip":
|
||||
if ( isDefined( self.intro_clips ) )
|
||||
delete_items( self.intro_clips );
|
||||
break;
|
||||
|
||||
case "frontal_cone_knock_player_back":
|
||||
frontal_cone_knock_player_back();
|
||||
break;
|
||||
|
||||
case "apply_physics":
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
can_play_scriptable( node_id, scriptable_name_list )
|
||||
{
|
||||
return ( ( is_scriptable_status( node_id, "always_on" ) || is_scriptable_status( node_id, "one_off" ) ) && scriptable_name_list.size > 0 );
|
||||
}
|
||||
|
||||
is_scriptable_status( node_id, state )
|
||||
{
|
||||
return ( level.cycle_data.spawn_node_info[node_id].scriptableStatus == state );
|
||||
}
|
||||
|
||||
is_one_off_scriptable( node_id )
|
||||
{
|
||||
return is_scriptable_status( node_id, "one_off" );
|
||||
}
|
||||
|
||||
inactivate_scriptable_for_node( node_id )
|
||||
{
|
||||
level.cycle_data.spawn_node_info[node_id].scriptableStatus = "inactive";
|
||||
}
|
||||
|
||||
delete_items( item_array )
|
||||
{
|
||||
foreach( item in item_array )
|
||||
{
|
||||
if ( isDefined( item ) )
|
||||
item delete();
|
||||
}
|
||||
}
|
||||
|
||||
frontal_cone_knock_player_back()
|
||||
{
|
||||
KNOCK_BACK_ACTIVATION_DIST_SQ = 22500; // 150 * 150
|
||||
KNOCK_BACK_FORCE_MAGNITUDE = 650;
|
||||
FRONT_CONE_LIMIT = 0.2588; //cos( 70 )
|
||||
|
||||
self_forward = anglesToForward( self.angles);
|
||||
|
||||
foreach ( player in level.players)
|
||||
{
|
||||
self_to_player = vectorNormalize ( player.origin - self.origin );
|
||||
|
||||
if( VectorDot( self_to_player, self_forward ) > FRONT_CONE_LIMIT && distanceSquared( player.origin , self.origin ) <= KNOCK_BACK_ACTIVATION_DIST_SQ )
|
||||
{
|
||||
player SetVelocity( VectorNormalize( player.origin - self.origin ) * KNOCK_BACK_FORCE_MAGNITUDE );
|
||||
player DoDamage( ( player.health / 10 ) , self.origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
resetAllScriptables( scriptable_name_list, position )
|
||||
{
|
||||
for( i = 0; i < scriptable_name_list.size; i++ )
|
||||
maps\mp\agents\alien\_alien_anim_utils::resetScriptable( scriptable_name_list[i], position );
|
||||
}
|
||||
|
||||
playAnimOnAllScriptables( scriptable_name_list, position, scriptable_state_list )
|
||||
{
|
||||
/# AssertEx( scriptable_name_list.size == scriptable_state_list.size, "The scriptable name lists and state lists have mismatch with their size near position ( " + position + " )." ); #/
|
||||
|
||||
for( i = 0; i < scriptable_name_list.size; i++ )
|
||||
maps\mp\agents\alien\_alien_anim_utils::playAnimOnScriptable( scriptable_name_list[i], position, int( scriptable_state_list[i] ) );
|
||||
}
|
||||
|
||||
is_empty_string( string )
|
||||
{
|
||||
return ( string == "" );
|
||||
}
|
||||
|
||||
playSpawnVignetteFX( effect_key )
|
||||
{
|
||||
effect_id = level._effect[effect_key];
|
||||
AssertEx( isDefined( effect_id ), "'" + effect_key + "' is not a valid key for the spawn vignette FX. Load the effect in alien type specific script." );
|
||||
ground_position = GetGroundPosition( self.origin + ( 0, 0, 100 ), 16 ); // play fx on ground surface
|
||||
PlayFX( effect_id, ground_position, (0,0,1) );
|
||||
}
|
||||
|
||||
doLerpToEndOnGround( animState, animIndex )
|
||||
{
|
||||
VERTICAL_DELTA_BUFFER = 2;
|
||||
|
||||
anime = self GetAnimEntry( animState, animIndex );
|
||||
|
||||
lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( anime );
|
||||
lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( anime, self.origin, self.angles, lerp_time );
|
||||
|
||||
z_delta = getVerticalDeltaToEndGroud( anime );
|
||||
lerp_target_pos += ( 0, 0, z_delta + VERTICAL_DELTA_BUFFER );
|
||||
|
||||
thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time );
|
||||
}
|
||||
|
||||
getVerticalDeltaToEndGroud( anime )
|
||||
{
|
||||
GET_GROUND_DROP_HEIGHT = 100;
|
||||
AI_PHYSICS_TRACE_RADIUS = 32;
|
||||
AI_PHYSICS_TRACE_HEIGHT = 72;
|
||||
|
||||
anime_delta = GetMoveDelta( anime, 0, 1 );
|
||||
anime_delta = RotateVector( anime_delta, self.angles );
|
||||
anime_height = anime_delta[2];
|
||||
|
||||
anime_end_pos = self.origin + anime_delta;
|
||||
trace_start_pos = anime_end_pos + ( 0, 0, GET_GROUND_DROP_HEIGHT );
|
||||
trace_end_pos = anime_end_pos - ( 0, 0, GET_GROUND_DROP_HEIGHT );
|
||||
ground_pos = self AIPhysicsTrace( trace_start_pos, trace_end_pos, AI_PHYSICS_TRACE_RADIUS, AI_PHYSICS_TRACE_HEIGHT );
|
||||
self_to_ground_height = ( ground_pos - self.origin )[2];
|
||||
|
||||
return ( self_to_ground_height - anime_height );
|
||||
}
|
829
maps/mp/agents/alien/_alien_anim_utils.gsc
Normal file
829
maps/mp/agents/alien/_alien_anim_utils.gsc
Normal file
@ -0,0 +1,829 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
//=======================================================
|
||||
// initAlienAnims
|
||||
//=======================================================
|
||||
initAlienAnims()
|
||||
{
|
||||
level.alienAnimData = SpawnStruct();
|
||||
|
||||
initAlienCannedTraverses( level.alienAnimData );
|
||||
|
||||
initAlienJumpTraverses( level.alienAnimData );
|
||||
|
||||
initAlienPain( level.alienAnimData );
|
||||
|
||||
initAlienDeath( level.alienAnimData );
|
||||
|
||||
initMoveBackAnims();
|
||||
|
||||
//<NOTE JC> The following values are hard-coded here. Need to run calculateAnimData() if the specific animation assets are
|
||||
// changed.
|
||||
level.alienAnimData.jumpLaunchArrival_maxMoveDelta = 107.659;
|
||||
level.alienAnimData.stopSoon_NotifyDist = 99.4488;
|
||||
}
|
||||
|
||||
calculateAnimData()
|
||||
{
|
||||
//<NOTE JC> The reason for this function is that we can only GetAnimEntry running on an agent. Only need to run this when
|
||||
// specific animation set is updated
|
||||
|
||||
calculate_jumpLaunchArrivalMaxMoveDelta();
|
||||
calculate_stopSoonNotifyDist();
|
||||
}
|
||||
|
||||
calculate_jumpLaunchArrivalMaxMoveDelta()
|
||||
{
|
||||
iprintln( "level.alienAnimData.jumpLaunchArrival_maxMoveDelta = " + calculate_maxMoveDeltaInAnimState( "jump_launch_arrival" ) );
|
||||
}
|
||||
|
||||
calculate_stopSoonNotifyDist()
|
||||
{
|
||||
iprintln( "level.alienAnimData.stopSoon_NotifyDist = " + calculate_maxMoveDeltaInAnimState( "run_stop" ) );
|
||||
}
|
||||
|
||||
calculate_maxMoveDeltaInAnimState( animState )
|
||||
{
|
||||
maxMoveDeltaSq = 0;
|
||||
animCount = self GetAnimEntryCount( animState );
|
||||
|
||||
for ( i = 0; i < animCount; i++ )
|
||||
{
|
||||
animEntry = self GetAnimEntry( animState, i );
|
||||
moveDelta = GetMoveDelta( animEntry, 0, 1 );
|
||||
|
||||
deltaDistSq = LengthSquared( moveDelta );
|
||||
if ( deltaDistSq > maxMoveDeltaSq )
|
||||
maxMoveDeltaSq = deltaDistSq;
|
||||
}
|
||||
|
||||
return sqrt( maxMoveDeltaSq );
|
||||
}
|
||||
|
||||
initAlienCannedTraverses( alienAnimData )
|
||||
{
|
||||
alienAnimData.cannedTraverseAnims = [];
|
||||
|
||||
// Canned traversals
|
||||
// Group 1 --- Each group can hold 32 animations
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_door" ] = registerTraverseData( "traverse_group_1", [ 0 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_sidewall_l" ] = registerTraverseData( "traverse_group_1", [ 1 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_sidewall_r" ] = registerTraverseData( "traverse_group_1", [ 2 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_leap_clear_height_54" ] = registerTraverseData( "traverse_group_1", [ 3 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_drone_traverse_corner_wall_crawl" ] = registerTraverseData( "traverse_group_1", [ 4 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_leap_clear_height_36" ] = registerTraverseData( "traverse_group_1", [ 5 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_leap_tree" ] = registerTraverseData( "traverse_group_1", [ 6 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_under_car" ] = registerTraverseData( "traverse_group_1", [ 7 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_on_car" ] = registerTraverseData( "traverse_group_1", [ 8 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_step_up_56" ] = registerTraverseData( "traverse_group_1", [ 9 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_step_down_56" ] = registerTraverseData( "traverse_group_1", [ 10 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_deadtree" ] = registerTraverseData( "traverse_group_1", [ 11 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_back_humvee" ] = registerTraverseData( "traverse_group_1", [ 12 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_car" ] = registerTraverseData( "traverse_group_1", [ 13 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_humvee" ] = registerTraverseData( "traverse_group_1", [ 14 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_sidecar" ] = registerTraverseData( "traverse_group_1", [ 15 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_sidehumvee" ] = registerTraverseData( "traverse_group_1", [ 16 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_under_fence" ] = registerTraverseData( "traverse_group_1", [ 17,24 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_spiral_tree" ] = registerTraverseData( "traverse_group_1", [ 18 ], true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_gutter_L" ] = registerTraverseData( "traverse_group_1", [ 19 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_gutter_R" ] = registerTraverseData( "traverse_group_1", [ 20 ], false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_over_fence_112" ] = registerTraverseData( "traverse_group_1", [ 21,22,23 ], false );
|
||||
|
||||
// Group 2 --- This group supports the flexible height notetrack. Does not have 'restart' on the animstate. Hold 32 animations max
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_36" ] = registerTraverseData( "traverse_group_2", [ 0 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_drone_traverse_climb_vault_8" ] = registerTraverseData( "traverse_group_2", [ 1 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_drone_traverse_climb_over_fence" ] = registerTraverseData( "traverse_group_2", [ 2 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_crawl_rail_vault_lodge" ] = registerTraverseData( "traverse_group_2", [ 3 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_rail_lodge" ] = registerTraverseData( "traverse_group_2", [ 4 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_roof_to_ceiling" ] = registerTraverseData( "traverse_group_2", [ 5 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_over_fence_88" ] = registerTraverseData( "traverse_group_2", [ 6 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_100" ] = registerTraverseData( "traverse_group_2", [ 7 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_200" ] = registerTraverseData( "traverse_group_2", [ 8 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_70" ] = registerTraverseData( "traverse_group_2", [ 9 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_200" ] = registerTraverseData( "traverse_group_2", [ 10 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_straight" ] = registerTraverseData( "traverse_group_2", [ 11 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_roof_to_ground" ] = registerTraverseData( "traverse_group_2", [ 12 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_128_rail_32" ] = registerTraverseData( "traverse_group_2", [ 13 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_128_rail_36" ] = registerTraverseData( "traverse_group_2", [ 14 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_128_rail_48" ] = registerTraverseData( "traverse_group_2", [ 15 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_rail_32_idle" ] = registerTraverseData( "traverse_group_2", [ 16 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_rail_32_run" ] = registerTraverseData( "traverse_group_2", [ 17 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_32" ] = registerTraverseData( "traverse_group_2", [ 18 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_48" ] = registerTraverseData( "traverse_group_2", [ 19 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_128_rail_32" ] = registerTraverseData( "traverse_group_2", [ 20 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_128_rail_36" ] = registerTraverseData( "traverse_group_2", [ 21 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_128_rail_48" ] = registerTraverseData( "traverse_group_2", [ 22 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_down_128_rail_36" ] = registerTraverseData( "traverse_group_2", [ 23 ], true, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_crate_48" ] = registerTraverseData( "traverse_group_2", [ 24 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_mantle_crate_64" ] = registerTraverseData( "traverse_group_2", [ 25 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_56_idle" ] = registerTraverseData( "traverse_group_2", [ 26 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_56_run" ] = registerTraverseData( "traverse_group_2", [ 27 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_56_idle" ] = registerTraverseData( "traverse_group_2", [ 28 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_56_run" ] = registerTraverseData( "traverse_group_2", [ 29 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_88_enter_scale" ] = registerTraverseData( "traverse_group_2", [ 30 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_88_exit_scale" ] = registerTraverseData( "traverse_group_2", [ 31 ], false, true );
|
||||
|
||||
// Group 3 --- This group supports the flexible height notetrack. Does not have 'restart' on the animstate. Hold 32 animations max
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_up_90_rail_32" ] = registerTraverseData( "traverse_group_3", [ 0 ], false, false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_high_to_low" ] = registerTraverseData( "traverse_group_3", [ 1 ], false,false );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_fence_low_to_high" ] = registerTraverseData( "traverse_group_3", [ 2 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_straight_forward_56" ] = registerTraverseData( "traverse_group_3", [ 3 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_jump_down_straight_360_dlc" ] = registerTraverseData( "traverse_group_3", [ 4 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_rail_32_jump_down_idle_dlc" ] = registerTraverseData( "traverse_group_3", [ 5 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_rail_36_jump_down_idle_dlc" ] = registerTraverseData( "traverse_group_3", [ 6 ], false, true );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_rail_48_jump_down_idle_dlc" ] = registerTraverseData( "traverse_group_3", [ 7 ], false, true );
|
||||
|
||||
// Special traversals
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up" ] = registerTraverseData( "traverse_climb_up" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_down" ] = registerTraverseData( "traverse_climb_down" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_over_56" ] = registerTraverseData( "traverse_climb_up_over_56" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_over_56_down" ] = registerTraverseData( "traverse_climb_over_56_down" );
|
||||
alienAnimData.cannedTraverseAnims[ "climb_up_end_jump_side_l" ] = registerTraverseData( "climb_up_end_jump_side_l" );
|
||||
alienAnimData.cannedTraverseAnims[ "climb_up_end_jump_side_r" ] = registerTraverseData( "climb_up_end_jump_side_r" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_ledge_18_run" ] = registerTraverseData( "traverse_climb_up_ledge_18_run" );
|
||||
alienAnimData.cannedTraverseAnims[ "alien_climb_up_ledge_18_idle" ] = registerTraverseData( "traverse_climb_up_ledge_18_idle" );
|
||||
|
||||
alienAnimData.cannedTraverseAnims[ "alien_wall_run" ] = registerTraverseData( "run" );
|
||||
}
|
||||
|
||||
initAlienJumpTraverses( alienAnimData )
|
||||
{
|
||||
level.alienAnimData.jumpGravity = 20.0 / 0.02205; // cScrAgent_JumpGravity
|
||||
|
||||
level.alienAnimData.jumpLaunchGroundDelta = 16.8476;
|
||||
level.alienAnimData.jumpLaunchInAirAnimLength = 0.111111;
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection = [];
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ] = [];
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ] = [];
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ] = [];
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ][0] = (0.338726, 0, 0.940885);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ][1] = (0.688542, 0, 0.725196);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_up" ][2] = (0.906517, 0, 0.422169);
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ][0] = (0.248516, 0, 0.968628);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ][1] = (0.579155, 0, 0.815218);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_level" ][2] = (0.906514, 0, 0.422177);
|
||||
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ][0] = (0.333125, 0, 0.942883);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ][1] = (0.518112, 0, 0.855313);
|
||||
level.alienAnimData.jumpLaunchDirection[ "jump_launch_down" ][2] = (0.892489, 0, 0.451068);
|
||||
|
||||
level.alienAnimData.inAirAnimEntry = [];
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ] = [];
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ] = [];
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ] = [];
|
||||
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_up" ] = 0;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_level" ] = 1;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_down" ] = 2;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_up" ] = 3;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_level" ] = 4;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_down" ] = 5;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_up" ] = 6;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_level" ] = 7;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_down" ] = 8;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_sidewall_high" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_sidewall_high" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_sidewall_high" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_up" ][ "jump_land_sidewall_low" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_level" ][ "jump_land_sidewall_low" ] = 9;
|
||||
level.alienAnimData.inAirAnimEntry[ "jump_launch_down" ][ "jump_land_sidewall_low" ] = 9;
|
||||
}
|
||||
|
||||
initAlienPain( alienAnimData )
|
||||
{
|
||||
alienAnimData.painAnims = [];
|
||||
|
||||
//Idle, anim states are: idle_pain_light, idle_pain_heavy
|
||||
idlePainAnims = [];
|
||||
idlePainAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "front" ][ "low_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "front" ][ "up_body_L" ] = [ 1 ];
|
||||
idlePainAnims [ "front" ][ "up_body_R" ] = [ 2 ];
|
||||
idlePainAnims [ "front" ][ "low_body_L" ] = [ 2 ];
|
||||
idlePainAnims [ "front" ][ "low_body_R" ] = [ 2 ];
|
||||
idlePainAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
idlePainAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
idlePainAnims [ "right" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "right" ][ "up_chest" ] = [ 3 ];
|
||||
idlePainAnims [ "right" ][ "low_chest" ] = [ 3 ];
|
||||
idlePainAnims [ "right" ][ "up_body_L" ] = [ 3 ];
|
||||
idlePainAnims [ "right" ][ "up_body_R" ] = [ 2 ];
|
||||
idlePainAnims [ "right" ][ "low_body_L" ] = [ 4 ];
|
||||
idlePainAnims [ "right" ][ "low_body_R" ] = [ 4 ];
|
||||
idlePainAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
idlePainAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
idlePainAnims [ "left" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "left" ][ "up_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "left" ][ "low_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
idlePainAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
idlePainAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
idlePainAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
idlePainAnims [ "left" ][ "armor" ] = [ 2 ];
|
||||
idlePainAnims [ "left" ][ "soft" ] = [ 2 ];
|
||||
idlePainAnims [ "back" ][ "head" ] = [ 0 ];
|
||||
idlePainAnims [ "back" ][ "up_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "back" ][ "low_chest" ] = [ 1 ];
|
||||
idlePainAnims [ "back" ][ "up_body_L" ] = [ 1 ];
|
||||
idlePainAnims [ "back" ][ "up_body_R" ] = [ 7 ];
|
||||
idlePainAnims [ "back" ][ "low_body_L" ] = [ 7 ];
|
||||
idlePainAnims [ "back" ][ "low_body_R" ] = [ 7 ];
|
||||
idlePainAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
idlePainAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "idle" ] = idlePainAnims;
|
||||
|
||||
//Run, anim states are: run_stumble_light, run_stumble_heavy
|
||||
/*
|
||||
0 alien_drone_run_pain_upbody_f_light
|
||||
1 alien_drone_run_pain_upbody_f_medium
|
||||
2 alien_drone_run_pain_upbody_v2_f_medium
|
||||
3 alien_drone_run_pain_Lshoulder_f_heavy
|
||||
4 alien_drone_run_pain_Rshoulder_f_heavy
|
||||
5 alien_drone_run_pain_Lupbody_l_light
|
||||
6 alien_drone_run_pain_Llowbody_l_light
|
||||
7 alien_drone_run_pain_Rupbody_r_light
|
||||
8 alien_drone_run_pain_Lupbody_f_light
|
||||
9 alien_drone_run_pain_Rupbody_f_light
|
||||
10 alien_drone_run_pain_lowbody_f_light
|
||||
11 alien_drone_run_pain_Rlowbody_r_light
|
||||
12 alien_drone_run_pain_upbody_b_light
|
||||
13 alien_drone_run_pain_lowbody_b_light
|
||||
*/
|
||||
runPainAnims = [];
|
||||
runPainAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
runPainAnims [ "front" ][ "up_chest" ] = [ 9 ];
|
||||
runPainAnims [ "front" ][ "low_chest" ] = [ 8 ];
|
||||
runPainAnims [ "front" ][ "up_body_L" ] = [ 8 ];
|
||||
runPainAnims [ "front" ][ "up_body_R" ] = [ 9 ];
|
||||
runPainAnims [ "front" ][ "low_body_L" ] = [ 10 ];
|
||||
runPainAnims [ "front" ][ "low_body_R" ] = [ 10 ];
|
||||
runPainAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
runPainAnims [ "right" ][ "head" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "up_chest" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "low_chest" ] = [ 11 ];
|
||||
runPainAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
runPainAnims [ "right" ][ "low_body_L" ] = [ 11 ];
|
||||
runPainAnims [ "right" ][ "low_body_R" ] = [ 11 ];
|
||||
runPainAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
runPainAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
runPainAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
runPainAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
runPainAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
runPainAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
runPainAnims [ "back" ][ "head" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "up_chest" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "low_chest" ] = [ 13 ];
|
||||
runPainAnims [ "back" ][ "up_body_L" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "up_body_R" ] = [ 12 ];
|
||||
runPainAnims [ "back" ][ "low_body_L" ] = [ 13 ];
|
||||
runPainAnims [ "back" ][ "low_body_R" ] = [ 13 ];
|
||||
runPainAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
runPainAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "run" ] = runPainAnims;
|
||||
|
||||
//Jump, anim states are: jump_pain_light, jump_pain_heavy
|
||||
jumpPainAnims = [];
|
||||
jumpPainAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
jumpPainAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
jumpPainAnims [ "front" ][ "low_chest" ] = [ 1 ];
|
||||
jumpPainAnims [ "front" ][ "up_body_L" ] = [ 2 ];
|
||||
jumpPainAnims [ "front" ][ "up_body_R" ] = [ 3 ];
|
||||
jumpPainAnims [ "front" ][ "low_body_L" ] = [ 4 ];
|
||||
jumpPainAnims [ "front" ][ "low_body_R" ] = [ 4 ];
|
||||
jumpPainAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
jumpPainAnims [ "right" ][ "head" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "up_chest" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "low_chest" ] = [ 8 ];
|
||||
jumpPainAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
jumpPainAnims [ "right" ][ "low_body_L" ] = [ 8 ];
|
||||
jumpPainAnims [ "right" ][ "low_body_R" ] = [ 8 ];
|
||||
jumpPainAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
jumpPainAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
jumpPainAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
jumpPainAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
jumpPainAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
jumpPainAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
jumpPainAnims [ "back" ][ "head" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "up_chest" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "low_chest" ] = [ 10 ];
|
||||
jumpPainAnims [ "back" ][ "up_body_L" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "up_body_R" ] = [ 9 ];
|
||||
jumpPainAnims [ "back" ][ "low_body_L" ] = [ 10 ];
|
||||
jumpPainAnims [ "back" ][ "low_body_R" ] = [ 10 ];
|
||||
jumpPainAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
jumpPainAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "jump" ] = jumpPainAnims;
|
||||
|
||||
//Push back, anim state is: pain_pushback
|
||||
pushbackPainAnims = [];
|
||||
pushbackPainAnims [ "front" ] = [ 0, 1 ];
|
||||
pushbackPainAnims [ "right" ] = [ 2 ];
|
||||
pushbackPainAnims [ "left" ] = [ 3 ];
|
||||
pushbackPainAnims [ "back" ] = [ 4 ];
|
||||
alienAnimData.painAnims[ "push_back" ] = pushbackPainAnims;
|
||||
|
||||
//Move back, anim state are: move_back_pain_light, move_back_pain_heavy
|
||||
movebackPainAnims = [];
|
||||
movebackPainAnims [ "front" ] = [ 0 ];
|
||||
movebackPainAnims [ "right" ] = [ 0 ];
|
||||
movebackPainAnims [ "left" ] = [ 0 ];
|
||||
movebackPainAnims [ "back" ] = [ 0 ];
|
||||
alienAnimData.painAnims[ "move_back" ] = movebackPainAnims;
|
||||
|
||||
//Melee, anim state are: melee_pain_light, melee_pain_heavy
|
||||
meleePainAnims = [];
|
||||
meleePainAnims [ "front" ] = [ 0, 1, 2 ];
|
||||
meleePainAnims [ "right" ] = [ 0, 1, 2 ];
|
||||
meleePainAnims [ "left" ] = [ 0, 1, 2 ];
|
||||
meleePainAnims [ "back" ] = [ 0, 1, 2 ];
|
||||
alienAnimData.painAnims[ "melee" ] = meleePainAnims;
|
||||
|
||||
combinedHitLoc = [];
|
||||
combinedHitLoc [ "head" ] = "head";
|
||||
combinedHitLoc [ "neck" ] = "head";
|
||||
combinedHitLoc [ "torso_upper" ] = "up_chest";
|
||||
combinedHitLoc [ "none" ] = "up_chest";
|
||||
combinedHitLoc [ "torso_lower" ] = "low_chest";
|
||||
combinedHitLoc [ "left_arm_upper" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_arm_lower" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_hand" ] = "up_body_L";
|
||||
combinedHitLoc [ "right_arm_upper" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_arm_lower" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_hand" ] = "up_body_R";
|
||||
combinedHitLoc [ "left_leg_upper" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_leg_lower" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_foot" ] = "low_body_L";
|
||||
combinedHitLoc [ "right_leg_upper" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_leg_lower" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_foot" ] = "low_body_R";
|
||||
combinedHitLoc [ "armor" ] = "armor";
|
||||
combinedHitLoc [ "soft" ] = "soft";
|
||||
alienAnimData.painAnims[ "hitLoc" ] = combinedHitLoc;
|
||||
|
||||
hitDirection = [];
|
||||
hitDirection[ 0 ] = "back";
|
||||
hitDirection[ 1 ] = "back";
|
||||
hitDirection[ 2 ] = "right";
|
||||
hitDirection[ 3 ] = "right";
|
||||
hitDirection[ 4 ] = "front";
|
||||
hitDirection[ 5 ] = "left";
|
||||
hitDirection[ 6 ] = "left";
|
||||
hitDirection[ 7 ] = "back";
|
||||
hitDirection[ 8 ] = "back";
|
||||
alienAnimData.painAnims[ "hitDirection" ] = hitDirection;
|
||||
|
||||
jumpPainIdleToImpactMap = [];
|
||||
// *idle index *Impact index
|
||||
jumpPainIdleToImpactMap[ 0 ] = [ 0 ];
|
||||
jumpPainIdleToImpactMap[ 1 ] = [ 1 ];
|
||||
jumpPainIdleToImpactMap[ 2 ] = [ 2 ];
|
||||
jumpPainIdleToImpactMap[ 3 ] = [ 3 ];
|
||||
jumpPainIdleToImpactMap[ 4 ] = [ 4 ];
|
||||
jumpPainIdleToImpactMap[ 5 ] = [ 5 ];
|
||||
jumpPainIdleToImpactMap[ 6 ] = [ 6 ];
|
||||
jumpPainIdleToImpactMap[ 7 ] = [ 7 ];
|
||||
jumpPainIdleToImpactMap[ 8 ] = [ 8 ];
|
||||
jumpPainIdleToImpactMap[ 9 ] = [ 9 ];
|
||||
jumpPainIdleToImpactMap[ 10 ] = [ 10 ];
|
||||
alienAnimData.painAnims[ "idleToImpactMap" ] = jumpPainIdleToImpactMap;
|
||||
}
|
||||
|
||||
initAlienDeath( alienAnimData )
|
||||
{
|
||||
alienAnimData.deathAnims = [];
|
||||
|
||||
//Idle, anim states are: idle_death_light, idle_death_heavy
|
||||
idleDeathAnims = [];
|
||||
idleDeathAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "front" ][ "low_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "front" ][ "up_body_L" ] = [ 1 ];
|
||||
idleDeathAnims [ "front" ][ "up_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "front" ][ "low_body_L" ] = [ 2 ];
|
||||
idleDeathAnims [ "front" ][ "low_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
idleDeathAnims [ "right" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "right" ][ "up_chest" ] = [ 4 ];
|
||||
idleDeathAnims [ "right" ][ "low_chest" ] = [ 3 ];
|
||||
idleDeathAnims [ "right" ][ "up_body_L" ] = [ 4 ];
|
||||
idleDeathAnims [ "right" ][ "up_body_R" ] = [ 4 ];
|
||||
idleDeathAnims [ "right" ][ "low_body_L" ] = [ 2 ];
|
||||
idleDeathAnims [ "right" ][ "low_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
idleDeathAnims [ "left" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "left" ][ "up_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "left" ][ "low_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "left" ][ "up_body_L" ] = [ 1 ];
|
||||
idleDeathAnims [ "left" ][ "up_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "left" ][ "low_body_L" ] = [ 5 ];
|
||||
idleDeathAnims [ "left" ][ "low_body_R" ] = [ 5 ];
|
||||
idleDeathAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
idleDeathAnims [ "back" ][ "head" ] = [ 0 ];
|
||||
idleDeathAnims [ "back" ][ "up_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "back" ][ "low_chest" ] = [ 1 ];
|
||||
idleDeathAnims [ "back" ][ "up_body_L" ] = [ 1 ];
|
||||
idleDeathAnims [ "back" ][ "up_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "back" ][ "low_body_L" ] = [ 2 ];
|
||||
idleDeathAnims [ "back" ][ "low_body_R" ] = [ 2 ];
|
||||
idleDeathAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
idleDeathAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.deathAnims[ "idle" ] = idleDeathAnims;
|
||||
|
||||
//Run, anim states are: run_death_light, run_death_heavy
|
||||
runDeathAnims = [];
|
||||
runDeathAnims [ "front" ][ "head" ] = [ 0 ];
|
||||
runDeathAnims [ "front" ][ "up_chest" ] = [ 1 ];
|
||||
runDeathAnims [ "front" ][ "low_chest" ] = [ 3 ];
|
||||
runDeathAnims [ "front" ][ "up_body_L" ] = [ 4 ];
|
||||
runDeathAnims [ "front" ][ "up_body_R" ] = [ 9 ];
|
||||
runDeathAnims [ "front" ][ "low_body_L" ] = [ 4 ];
|
||||
runDeathAnims [ "front" ][ "low_body_R" ] = [ 3 ];
|
||||
runDeathAnims [ "front" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "front" ][ "soft" ] = [ 0 ];
|
||||
runDeathAnims [ "right" ][ "head" ] = [ 2 ];
|
||||
runDeathAnims [ "right" ][ "up_chest" ] = [ 1 ];
|
||||
runDeathAnims [ "right" ][ "low_chest" ] = [ 0 ];
|
||||
runDeathAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
runDeathAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
runDeathAnims [ "right" ][ "low_body_L" ] = [ 3 ];
|
||||
runDeathAnims [ "right" ][ "low_body_R" ] = [ 4 ];
|
||||
runDeathAnims [ "right" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "right" ][ "soft" ] = [ 0 ];
|
||||
runDeathAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
runDeathAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
runDeathAnims [ "left" ][ "low_body_L" ] = [ 8 ];
|
||||
runDeathAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
runDeathAnims [ "left" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "left" ][ "soft" ] = [ 0 ];
|
||||
runDeathAnims [ "back" ][ "head" ] = [ 1 ];
|
||||
runDeathAnims [ "back" ][ "up_chest" ] = [ 5 ];
|
||||
runDeathAnims [ "back" ][ "low_chest" ] = [ 4 ];
|
||||
runDeathAnims [ "back" ][ "up_body_L" ] = [ 3 ];
|
||||
runDeathAnims [ "back" ][ "up_body_R" ] = [ 2 ];
|
||||
runDeathAnims [ "back" ][ "low_body_L" ] = [ 1 ];
|
||||
runDeathAnims [ "back" ][ "low_body_R" ] = [ 4 ];
|
||||
runDeathAnims [ "back" ][ "armor" ] = [ 0 ];
|
||||
runDeathAnims [ "back" ][ "soft" ] = [ 0 ];
|
||||
alienAnimData.deathAnims[ "run" ] = runDeathAnims;
|
||||
|
||||
//Jump, anim states are: jump_death_light, jump_death_heavy
|
||||
jumpDeathAnims = [];
|
||||
jumpDeathAnims [ "front" ][ "head" ] = [ 1 ];
|
||||
jumpDeathAnims [ "front" ][ "up_chest" ] = [ 0 ];
|
||||
jumpDeathAnims [ "front" ][ "low_chest" ] = [ 0 ];
|
||||
jumpDeathAnims [ "front" ][ "up_body_L" ] = [ 2 ];
|
||||
jumpDeathAnims [ "front" ][ "up_body_R" ] = [ 3 ];
|
||||
jumpDeathAnims [ "front" ][ "low_body_L" ] = [ 4 ];
|
||||
jumpDeathAnims [ "front" ][ "low_body_R" ] = [ 4 ];
|
||||
jumpDeathAnims [ "front" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "front" ][ "soft" ] = [ 1 ];
|
||||
jumpDeathAnims [ "right" ][ "head" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "up_chest" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "low_chest" ] = [ 8 ];
|
||||
jumpDeathAnims [ "right" ][ "up_body_L" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "up_body_R" ] = [ 7 ];
|
||||
jumpDeathAnims [ "right" ][ "low_body_L" ] = [ 8 ];
|
||||
jumpDeathAnims [ "right" ][ "low_body_R" ] = [ 8 ];
|
||||
jumpDeathAnims [ "right" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "right" ][ "soft" ] = [ 1 ];
|
||||
jumpDeathAnims [ "left" ][ "head" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "up_chest" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "low_chest" ] = [ 6 ];
|
||||
jumpDeathAnims [ "left" ][ "up_body_L" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "up_body_R" ] = [ 5 ];
|
||||
jumpDeathAnims [ "left" ][ "low_body_L" ] = [ 6 ];
|
||||
jumpDeathAnims [ "left" ][ "low_body_R" ] = [ 6 ];
|
||||
jumpDeathAnims [ "left" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "left" ][ "soft" ] = [ 1 ];
|
||||
jumpDeathAnims [ "back" ][ "head" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "up_chest" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "low_chest" ] = [ 10 ];
|
||||
jumpDeathAnims [ "back" ][ "up_body_L" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "up_body_R" ] = [ 9 ];
|
||||
jumpDeathAnims [ "back" ][ "low_body_L" ] = [ 10 ];
|
||||
jumpDeathAnims [ "back" ][ "low_body_R" ] = [ 10 ];
|
||||
jumpDeathAnims [ "back" ][ "armor" ] = [ 1 ];
|
||||
jumpDeathAnims [ "back" ][ "soft" ] = [ 1 ];
|
||||
alienAnimData.deathAnims[ "jump" ] = jumpDeathAnims;
|
||||
|
||||
combinedHitLoc = [];
|
||||
combinedHitLoc [ "head" ] = "head";
|
||||
combinedHitLoc [ "neck" ] = "head";
|
||||
combinedHitLoc [ "torso_upper" ] = "up_chest";
|
||||
combinedHitLoc [ "none" ] = "up_chest";
|
||||
combinedHitLoc [ "torso_lower" ] = "low_chest";
|
||||
combinedHitLoc [ "left_arm_upper" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_arm_lower" ] = "up_body_L";
|
||||
combinedHitLoc [ "left_hand" ] = "up_body_L";
|
||||
combinedHitLoc [ "right_arm_upper" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_arm_lower" ] = "up_body_R";
|
||||
combinedHitLoc [ "right_hand" ] = "up_body_R";
|
||||
combinedHitLoc [ "left_leg_upper" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_leg_lower" ] = "low_body_L";
|
||||
combinedHitLoc [ "left_foot" ] = "low_body_L";
|
||||
combinedHitLoc [ "right_leg_upper" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_leg_lower" ] = "low_body_R";
|
||||
combinedHitLoc [ "right_foot" ] = "low_body_R";
|
||||
combinedHitLoc [ "armor" ] = "armor";
|
||||
combinedHitLoc [ "soft" ] = "soft";
|
||||
alienAnimData.deathAnims[ "hitLoc" ] = combinedHitLoc;
|
||||
|
||||
hitDirection = [];
|
||||
hitDirection[ 0 ] = "back";
|
||||
hitDirection[ 1 ] = "back";
|
||||
hitDirection[ 2 ] = "right";
|
||||
hitDirection[ 3 ] = "right";
|
||||
hitDirection[ 4 ] = "front";
|
||||
hitDirection[ 5 ] = "left";
|
||||
hitDirection[ 6 ] = "left";
|
||||
hitDirection[ 7 ] = "back";
|
||||
hitDirection[ 8 ] = "back";
|
||||
alienAnimData.deathAnims[ "hitDirection" ] = hitDirection;
|
||||
|
||||
//Special death
|
||||
specialDeathAnims = [];
|
||||
specialDeathAnims [ "electric_shock_death" ] = [ 0 ];
|
||||
specialDeathAnims [ "traverse" ] = [ 1 ];
|
||||
alienAnimData.deathAnims[ "special" ] = specialDeathAnims;
|
||||
}
|
||||
|
||||
initMoveBackAnims()
|
||||
{
|
||||
level.alienAnimData.alienMoveBackAnimChance[ 0 ] = 40;
|
||||
level.alienAnimData.alienMoveBackAnimChance[ 1 ] = 40;
|
||||
level.alienAnimData.alienMoveBackAnimChance[ 2 ] = 20;
|
||||
}
|
||||
|
||||
registerTraverseData( animState, animIndexArray, endInOriented, flexHeightEndAtTraverseEnd, traverseSound, traverseAnimScale )
|
||||
{
|
||||
assertEx( isDefined( animState));
|
||||
|
||||
traverseData = [];
|
||||
traverseData [ "animState" ] = animState;
|
||||
|
||||
if ( isDefined ( animIndexArray ) )
|
||||
traverseData [ "animIndexArray" ] = animIndexArray;
|
||||
|
||||
if ( isDefined ( endInOriented ) )
|
||||
traverseData [ "endInOriented" ] = endInOriented;
|
||||
|
||||
if ( isDefined ( flexHeightEndAtTraverseEnd ) )
|
||||
traverseData [ "flexHeightEndAtTraverseEnd" ] = flexHeightEndAtTraverseEnd;
|
||||
|
||||
if ( isDefined ( traverseSound ) )
|
||||
traverseData [ "traverseSound" ] = traverseSound;
|
||||
|
||||
if ( isDefined ( traverseAnimScale ) )
|
||||
traverseData [ "traverseAnimScale" ] = traverseAnimScale;
|
||||
|
||||
return traverseData;
|
||||
}
|
||||
|
||||
turnTowardsEntity( entity )
|
||||
{
|
||||
targetVector = entity.origin - self.origin;
|
||||
return turnTowardsVector( targetVector );
|
||||
}
|
||||
|
||||
turnTowardsVector( targetVector )
|
||||
{
|
||||
turnIndex = getTurnInPlaceIndex( AnglesToForward( self.angles ), targetVector, AnglesToUp( self.angles ) );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
if ( turnIndex != 4 )
|
||||
{
|
||||
self.stateLocked = true;
|
||||
if( self.oriented )
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
else
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
animState = getTurnInPlaceAnimState();
|
||||
self PlayAnimNUntilNotetrack( animState, turnIndex, "turn_in_place", "code_move" );
|
||||
|
||||
if ( !self maps\mp\alien\_utility::is_idle_state_locked() )
|
||||
self.stateLocked = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
getTurnInPlaceAnimState()
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_turn_in_place_anim_state_override_func ) )
|
||||
{
|
||||
animState = [[level.dlc_alien_turn_in_place_anim_state_override_func]]();
|
||||
if ( IsDefined( animState ) )
|
||||
return animState;
|
||||
}
|
||||
|
||||
return "turn_in_place";
|
||||
}
|
||||
|
||||
getTurnInPlaceIndex ( inVector, outVector, surfaceNormal )
|
||||
{
|
||||
turnAnim = undefined;
|
||||
index = undefined;
|
||||
|
||||
projData = getProjectionData( inVector, outVector, surfaceNormal );
|
||||
rotatedYaw = projData.rotatedYaw;
|
||||
projInToOutRight = projData.projInToOutRight;
|
||||
|
||||
// favor underturning, unless you're within <threshold> degrees of the next one up.
|
||||
threshold = 10;
|
||||
|
||||
if ( projInToOutRight > 0 ) //Entering from the right
|
||||
{
|
||||
index = int( ceil( ( 180 - rotatedYaw - threshold ) / 45 ) );
|
||||
}
|
||||
else //Entering from the left
|
||||
{
|
||||
index = int( floor( ( 180 + rotatedYaw + threshold ) / 45 ) );
|
||||
}
|
||||
index = int( clamp( index, 0, 8 )); //the threshold in getTurnInPlaceIndex might cause the index to be out of range
|
||||
return index;
|
||||
}
|
||||
|
||||
getProjectionData( inVector, outVector, surfaceNormal )
|
||||
{
|
||||
projectionData = SpawnStruct();
|
||||
|
||||
inVectorNoNormal = vectorNormalize( projectVectorToPlane( inVector, surfaceNormal));
|
||||
outVectorNoNormal = vectorNormalize( projectVectorToPlane( outVector, surfaceNormal ));
|
||||
|
||||
outVectorRight = VectorCross ( outVectorNoNormal, surfaceNormal );
|
||||
outVectorRightNoNormal = VectorNormalize ( projectVectorToPlane( outVectorRight, surfaceNormal ) );
|
||||
projInToOutRight = VectorDot ( inVectorNoNormal * -1, outVectorRightNoNormal );
|
||||
|
||||
ratio = vectorDot( outVectorNoNormal, inVectorNoNormal );
|
||||
|
||||
// Need to make sure the value is within the domain of Acos
|
||||
ratio = clamp( ratio, -1, 1 );
|
||||
|
||||
rotatedYaw = Acos ( ratio );
|
||||
|
||||
projectionData.rotatedYaw = rotatedYaw;
|
||||
projectionData.projInToOutRight = projInToOutRight;
|
||||
|
||||
return projectionData;
|
||||
}
|
||||
|
||||
projectVectorToPlane( vector, planeUp )
|
||||
{
|
||||
dotResult = VectorDot( vector, planeUp );
|
||||
projVector = vector - ( planeUp * dotResult );
|
||||
return projVector;
|
||||
}
|
||||
|
||||
pain_getCombinedHitLoc( hitLoc )
|
||||
{
|
||||
return ( level.alienAnimData.painAnims[ "hitLoc" ][ hitLoc ] );
|
||||
}
|
||||
|
||||
pain_getIncomingDirection( direction )
|
||||
{
|
||||
directionIndex = maps\mp\agents\_scriptedagents::GetAngleIndexFromSelfYaw( direction );
|
||||
return ( level.alienAnimData.painAnims[ "hitDirection" ][ directionIndex ] );
|
||||
}
|
||||
|
||||
death_getCombinedHitLoc( hitLoc )
|
||||
{
|
||||
return ( level.alienAnimData.deathAnims[ "hitLoc" ][ hitLoc ] );
|
||||
}
|
||||
|
||||
death_getIncomingDirection( direction )
|
||||
{
|
||||
directionIndex = maps\mp\agents\_scriptedagents::GetAngleIndexFromSelfYaw( direction );
|
||||
return ( level.alienAnimData.deathAnims[ "hitDirection" ][ directionIndex ] );
|
||||
}
|
||||
|
||||
getPainAnimState( state, iDamage, is_stun )
|
||||
{
|
||||
secondaryState = getDamageDegree( iDamage, is_stun );
|
||||
return ( state + "_" + secondaryState );
|
||||
}
|
||||
|
||||
getDamageDegree( iDamage, is_stun )
|
||||
{
|
||||
alienType = self maps\mp\alien\_utility::get_alien_type();
|
||||
damageThreshold = level.alien_types[ alienType ].attributes[ "heavy_damage_threshold" ];
|
||||
|
||||
if ( iDamage < damageThreshold && !is_stun )
|
||||
return "light";
|
||||
else
|
||||
return "heavy";
|
||||
}
|
||||
|
||||
getPainAnimIndex( state, damageDirection, hitLoc )
|
||||
{
|
||||
damageDirection = pain_getIncomingDirection( damageDirection * -1 );
|
||||
|
||||
if ( isDefined ( hitLoc ) )
|
||||
hitLoc = pain_getCombinedHitLoc( hitLoc );
|
||||
|
||||
return getPainDeathAnimIndex_Internal( state, damageDirection, hitLoc, level.alienAnimData.painAnims );
|
||||
}
|
||||
|
||||
GetImpactPainAnimIndex( jump_pain_index )
|
||||
{
|
||||
available_impact = level.alienAnimData.painAnims[ "idleToImpactMap" ][ jump_pain_index ];
|
||||
random_impact_index = randomIntRange( 0, available_impact.size );
|
||||
return available_impact [ random_impact_index ];
|
||||
}
|
||||
|
||||
getDeathAnimState( state, iDamage )
|
||||
{
|
||||
secondaryState = getDamageDegree( iDamage, false );
|
||||
return ( state + "_" + secondaryState );
|
||||
}
|
||||
|
||||
getDeathAnimIndex( state, damageDirection, hitLoc )
|
||||
{
|
||||
damageDirection = death_getIncomingDirection( damageDirection * -1 );
|
||||
hitLoc = death_getCombinedHitLoc( hitLoc );
|
||||
return getPainDeathAnimIndex_Internal( state, damageDirection, hitLoc, level.alienAnimData.deathAnims );
|
||||
}
|
||||
|
||||
getPainDeathAnimIndex_Internal( state, damageDirection, hitLoc, animArray )
|
||||
{
|
||||
if ( isDefined ( hitLoc ) )
|
||||
availableIndexList = animArray[ state ][ damageDirection ][ hitLoc ];
|
||||
else
|
||||
availableIndexList = animArray[ state ][ damageDirection ];
|
||||
|
||||
return ( availableIndexList[ randomInt ( availableIndexList.size ) ] );
|
||||
}
|
||||
|
||||
getSpecialDeathAnimIndex( state )
|
||||
{
|
||||
availableIndexList = level.alienAnimData.deathAnims[ "special" ][ state ];
|
||||
return ( availableIndexList[ randomInt ( availableIndexList.size ) ] );
|
||||
}
|
||||
|
||||
resetScriptable( scriptableName, endPos )
|
||||
{
|
||||
scriptable_obj = GetEnt( scriptableName, "targetname" );
|
||||
AssertEx( isDefined( scriptable_obj ), "Unable to find a scriptable object with targetname: " + scriptableName + " at location " + endPos );
|
||||
scriptable_obj SetScriptablePartState( 0, 0 );
|
||||
}
|
||||
|
||||
playAnimOnScriptable( scriptableName, endPos, scriptableState )
|
||||
{
|
||||
scriptable_obj = GetEnt( scriptableName, "targetname" );
|
||||
AssertEx( isDefined( scriptable_obj ), "Unable to find a scriptable object with targetname: " + scriptableName + " at location " + endPos );
|
||||
|
||||
if ( !isDefined( scriptableState ) )
|
||||
scriptableState = 1;
|
||||
|
||||
scriptable_obj SetScriptablePartState( 0, scriptableState );
|
||||
|
||||
level notify( "scriptable",scriptableName );
|
||||
}
|
||||
|
||||
getLerpTime( startAnim )
|
||||
{
|
||||
startAnimLength = GetAnimLength( startAnim );
|
||||
return min( 0.2, startAnimLength );
|
||||
}
|
||||
|
||||
getPosInSpaceAtAnimTime( anime, start_pos, start_angles, time )
|
||||
{
|
||||
animLength = GetAnimLength( anime );
|
||||
animDelta = GetMoveDelta( anime, 0, time / animLength );
|
||||
offsetFromStart = RotateVector( animDelta, start_angles );
|
||||
|
||||
return ( start_pos + offsetFromStart );
|
||||
}
|
||||
|
||||
doLerp( lerp_target_pos, lerp_time )
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self ScrAgentDoAnimLerp( self.origin, lerp_target_pos, lerp_time );
|
||||
wait lerp_time;
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
}
|
649
maps/mp/agents/alien/_alien_elite.gsc
Normal file
649
maps/mp/agents/alien/_alien_elite.gsc
Normal file
@ -0,0 +1,649 @@
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
ALIEN_CHARGE_ATTACK_DISTANCE_MAX = 500;
|
||||
ALIEN_CHARGE_ATTACK_DISTANCE_MIN = 350;
|
||||
ALIEN_CHARGE_COOLDOWN_MSEC = 12000;
|
||||
|
||||
ALIEN_SLAM_MIN_DISTANCE = 175;
|
||||
ALIEN_SLAM_RADIUS = 250;
|
||||
ELITE_SWIPE_OFFSET_XY = 125; // how far in front of player we want to swipe (so the player can actually see it.)
|
||||
ELITE_MAX_SWIPE_DAMAGE_DIST = 175;
|
||||
|
||||
ANGERED_DAMAGE_SCALAR = 1.25;
|
||||
|
||||
ELITE_ATTACK_START_SOUND = "";
|
||||
CHARGE_HIT_SOUND = "";
|
||||
CHARGE_ATTACK_START_SOUND = "";
|
||||
ELITE_REGEN_START_SOUND = "";
|
||||
|
||||
elite_approach( enemy, attack_counter )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "elite_approach" ); #/
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_attacker_state( "attacking" ); #/
|
||||
|
||||
// Run near enemy
|
||||
if ( DistanceSquared( enemy.origin, self.origin ) > ALIEN_CHARGE_ATTACK_DISTANCE_MAX * ALIEN_CHARGE_ATTACK_DISTANCE_MAX )
|
||||
self maps\mp\agents\alien\_alien_think::run_near_enemy( ALIEN_CHARGE_ATTACK_DISTANCE_MAX, enemy );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( can_do_charge_attack( enemy ) )
|
||||
{
|
||||
return "charge";
|
||||
}
|
||||
else if ( run_to_slam( enemy ) )
|
||||
{
|
||||
return "slam";
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
}
|
||||
|
||||
run_to_slam( enemy )
|
||||
{
|
||||
self thread monitor_charge_range( enemy );
|
||||
self thread run_to_enemy( enemy );
|
||||
|
||||
msg = self common_scripts\utility::waittill_any_return( "run_to_slam_complete", "in_charge_range", "enemy", "bad_path" );
|
||||
if ( !self AgentCanSeeSentient( enemy ) )
|
||||
return false;
|
||||
|
||||
return ( msg == "run_to_slam_complete" );
|
||||
}
|
||||
|
||||
run_to_enemy( enemy )
|
||||
{
|
||||
enemy endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "bad_path" );
|
||||
|
||||
startTime = GetTime();
|
||||
|
||||
self maps\mp\agents\alien\_alien_think::run_near_enemy( ALIEN_SLAM_MIN_DISTANCE, enemy );
|
||||
|
||||
// need to make sure a frame passes before we send the notify
|
||||
if ( startTime == GetTime() )
|
||||
wait 0.05;
|
||||
|
||||
self notify( "run_to_slam_complete" );
|
||||
}
|
||||
|
||||
monitor_charge_range( enemy )
|
||||
{
|
||||
self endon( "goal_reached" );
|
||||
enemy endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "bad_path" );
|
||||
|
||||
chargeRangeSquared = ALIEN_CHARGE_ATTACK_DISTANCE_MIN * ALIEN_CHARGE_ATTACK_DISTANCE_MIN;
|
||||
wait 0.05;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, enemy.origin ) >= chargeRangeSquared )
|
||||
break;
|
||||
|
||||
wait 0.2;
|
||||
}
|
||||
|
||||
self notify( "in_charge_range" );
|
||||
}
|
||||
|
||||
can_do_charge_attack( enemy )
|
||||
{
|
||||
if ( gettime() < self.last_charge_time + ALIEN_CHARGE_COOLDOWN_MSEC )
|
||||
return false;
|
||||
|
||||
if ( DistanceSquared( self.origin, enemy.origin ) < ALIEN_CHARGE_ATTACK_DISTANCE_MIN * ALIEN_CHARGE_ATTACK_DISTANCE_MIN )
|
||||
return false;
|
||||
|
||||
if ( !maps\mp\agents\_scriptedagents::CanMovePointToPoint( self.origin, enemy.origin) )
|
||||
return false;
|
||||
|
||||
return self maps\mp\alien\_utility::is_normal_upright( anglesToUp( self.angles ) );
|
||||
}
|
||||
|
||||
ground_slam( enemy )
|
||||
{
|
||||
self.melee_type = "slam";
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
}
|
||||
|
||||
ALIEN_ELITE_GROUND_SLAM_IMPULSE = 800;
|
||||
|
||||
do_ground_slam( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( enemy );
|
||||
self ScrAgentSetOrientMode( "face enemy" );
|
||||
|
||||
self maps\mp\agents\alien\_alien_melee::try_preliminary_swipes( "swipe", enemy, ELITE_SWIPE_OFFSET_XY, ELITE_MAX_SWIPE_DAMAGE_DIST );
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimNUntilNotetrack( "attack_melee_swipe", 2, "attack_melee", "alien_slam_big" );
|
||||
|
||||
min_damage = level.alien_types[ self.alien_type ].attributes[ "slam_min_damage" ];
|
||||
max_damage = level.alien_types[ self.alien_type ].attributes[ "slam_max_damage" ];
|
||||
|
||||
if ( IsDefined( self.elite_angered ) )
|
||||
{
|
||||
min_damage *= get_angered_damage_scalar();
|
||||
max_damage *= get_angered_damage_scalar();
|
||||
}
|
||||
self area_damage_and_impulse( ALIEN_SLAM_RADIUS, min_damage, max_damage, ALIEN_ELITE_GROUND_SLAM_IMPULSE );
|
||||
self maps\mp\agents\_scriptedagents::WaitUntilNotetrack( "attack_melee", "end" );
|
||||
|
||||
if ( !isDefined( self.elite_angered ) )
|
||||
meleeSuccess = self maps\mp\agents\alien\_alien_melee::move_back( enemy, true );
|
||||
self set_alien_emissive_default( 0.2 );
|
||||
}
|
||||
|
||||
charge_attack( enemy )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "charge_attack" ); #/
|
||||
|
||||
if ( enemy being_charged() )
|
||||
{
|
||||
wait 0.2;
|
||||
return;
|
||||
}
|
||||
|
||||
self.melee_type = "charge";
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
enemy.being_charged = false;
|
||||
}
|
||||
|
||||
angered( enemy )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "health_regen" ); #/
|
||||
|
||||
self.melee_type = "angered";
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
}
|
||||
|
||||
do_charge_attack( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
enemy.being_charged = true;
|
||||
self.last_charge_time = gettime();
|
||||
self set_alien_emissive( 0.2, 1.0 );
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( enemy );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetOrientMode( "face enemy" );
|
||||
|
||||
charge_start_index = get_charge_start_index();
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimNAtRateUntilNotetrack( "charge_attack_start", charge_start_index, 1.15, "charge_attack_start", "end", ::chargeStartNotetrackHandler );
|
||||
|
||||
if ( isAlive( enemy ) && can_see_enemy( enemy ) )
|
||||
{
|
||||
self thread track_enemy( enemy );
|
||||
self SetAnimState( "charge_attack", charge_start_index, 1.0);
|
||||
|
||||
result = watch_charge_hit( enemy, charge_start_index );
|
||||
self notify( "charge_complete" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
if ( !IsDefined( result ) ) // enemy died mid charge, play stop anim
|
||||
result = "fail";
|
||||
|
||||
switch ( result )
|
||||
{
|
||||
case "success":
|
||||
self maps\mp\agents\_scriptedagents::SafelyPlayAnimNAtRateUntilNotetrack( "charge_attack_bump", charge_start_index, 1.0, "charge_attack_bump", "end", ::chargeEndNotetrackHandler );
|
||||
break;
|
||||
case "fail":
|
||||
self play_stop_anim( charge_start_index );
|
||||
break;
|
||||
default:
|
||||
assertmsg( "Unknown charge hit result: " + result );
|
||||
break;
|
||||
}
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
|
||||
self set_alien_emissive_default( 0.2 );
|
||||
}
|
||||
|
||||
can_see_enemy( enemy )
|
||||
{
|
||||
return SightTracePassed( self getEye(), enemy getEye(), false, self );
|
||||
}
|
||||
|
||||
track_enemy( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "charge_complete" );
|
||||
|
||||
STOP_TRACKING_DISTANCE_SQ = 325 * 325;
|
||||
self.charge_tracking_enemy = true;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, enemy.origin ) < STOP_TRACKING_DISTANCE_SQ )
|
||||
break;
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self.charge_tracking_enemy = false;
|
||||
}
|
||||
|
||||
play_stop_anim( anim_index )
|
||||
{
|
||||
FORWARD_CLEARANCE = 120;
|
||||
|
||||
if ( hit_geo( FORWARD_CLEARANCE ) )
|
||||
go_hit_geo();
|
||||
else
|
||||
self maps\mp\agents\_scriptedagents::SafelyPlayAnimNAtRateUntilNotetrack( "charge_attack_stop", anim_index, 1.0, "charge_attack_stop", "end", ::chargeEndNotetrackHandler );
|
||||
}
|
||||
|
||||
go_hit_geo()
|
||||
{
|
||||
hit_geo_index = get_hit_geo_index();
|
||||
hit_geo_anim = self GetAnimEntry( "charge_hit_geo", hit_geo_index );
|
||||
notetrack_time = GetNotetrackTimes( hit_geo_anim, "forward_end" );
|
||||
forward_delta = length( GetMoveDelta( hit_geo_anim, 0.0, notetrack_time[ 0 ] ) );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( hit_geo( forward_delta ) )
|
||||
break;
|
||||
|
||||
common_scripts\utility::waitframe();
|
||||
}
|
||||
self maps\mp\agents\_scriptedagents::SafelyPlayAnimNAtRateUntilNotetrack( "charge_hit_geo", hit_geo_index, 1.0, "charge_hit_geo", "end", ::chargeEndNotetrackHandler );
|
||||
}
|
||||
|
||||
watch_charge_hit( enemy, anim_index )
|
||||
{
|
||||
self endon( "death" );
|
||||
enemy endon( "death" );
|
||||
|
||||
MIN_CHARGE_TIME = 3.0;
|
||||
MAX_CHARGE_TIME = 6.0;
|
||||
FRAME_TIME = 0.05;
|
||||
|
||||
chargeStopAnim = self GetAnimEntry( "charge_attack_stop", anim_index );
|
||||
num_loops = int( randomFloatRange( MIN_CHARGE_TIME, MAX_CHARGE_TIME ) / FRAME_TIME );
|
||||
animDistance = Length( GetMoveDelta( chargeStopAnim ) );
|
||||
animLength = GetAnimLength( chargeStopAnim );
|
||||
shortLookAheadDistance = (animDistance / animLength) * FRAME_TIME * 3;
|
||||
|
||||
for ( i = 0; i < num_loops; i++ )
|
||||
{
|
||||
if ( hit_player() )
|
||||
return "success";
|
||||
|
||||
if ( self.charge_tracking_enemy )
|
||||
lookAheadDistance = Distance( enemy.origin, self.origin);
|
||||
else
|
||||
lookAheadDistance = shortlookAheadDistance;
|
||||
|
||||
if ( hit_geo( lookAheadDistance ) )
|
||||
return "fail";
|
||||
|
||||
if ( !self.charge_tracking_enemy && missed_enemy( enemy ) )
|
||||
return "fail";
|
||||
|
||||
common_scripts\utility::waitframe();
|
||||
}
|
||||
return "fail"; //time out
|
||||
}
|
||||
|
||||
ALIEN_ELITE_CHARGE_IMPULSE = 1200;
|
||||
|
||||
hit_player()
|
||||
{
|
||||
CHARGE_HIT_DIST = 140;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( distanceSquared ( self.origin, player.origin ) < CHARGE_HIT_DIST * CHARGE_HIT_DIST
|
||||
&& might_hit_enemy( player )
|
||||
)
|
||||
{
|
||||
self maps\mp\agents\alien\_alien_melee::melee_DoDamage( player, "charge" );
|
||||
player player_fly_back( ALIEN_ELITE_CHARGE_IMPULSE, vectorNormalize( player.origin - self.origin ));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
hit_geo( lookAheadDistance )
|
||||
{
|
||||
OFFSET_HEIGHT = 18.0;
|
||||
COS_30 = 0.866;
|
||||
|
||||
traceStart = self.origin + ( 0, 0, OFFSET_HEIGHT );
|
||||
traceEnd = traceStart + AnglesToForward(self.angles ) * lookAheadDistance;
|
||||
|
||||
hitInfo = self AIPhysicsTrace( traceStart, traceEnd, self.radius, self.height - OFFSET_HEIGHT, true, true );
|
||||
return hitInfo["fraction"] < 1.0 && hitInfo["normal"][2] < COS_30;
|
||||
}
|
||||
|
||||
player_fly_back( impulse, direction )
|
||||
{
|
||||
MAX_SPEED = 600.0;
|
||||
original_velocity = self GetVelocity();
|
||||
impluse_velocity = direction * impulse;
|
||||
|
||||
final_velocity = ( original_velocity + impluse_velocity ) * ( 1, 1, 0 );
|
||||
speed = Length( final_velocity );
|
||||
|
||||
if ( speed >= 400.0 )
|
||||
{
|
||||
final_velocity = VectorNormalize( final_velocity ) * 400.0;
|
||||
}
|
||||
|
||||
self SetVelocity( final_velocity );
|
||||
}
|
||||
|
||||
might_hit_enemy( enemy )
|
||||
{
|
||||
CONE_LIMIT = 0.866; //cos( 30 )
|
||||
|
||||
can_see_enemy = can_see_enemy( enemy );
|
||||
|
||||
self_to_enemy = vectorNormalize ( enemy.origin - self.origin );
|
||||
self_forward = anglesToForward( self.angles);
|
||||
enemy_in_front_cone = VectorDot( self_to_enemy, self_forward ) > CONE_LIMIT;
|
||||
|
||||
return ( can_see_enemy && enemy_in_front_cone );
|
||||
}
|
||||
|
||||
missed_enemy( enemy )
|
||||
{
|
||||
pastEnemyDistance = -256;
|
||||
can_see_enemy = can_see_enemy( enemy );
|
||||
|
||||
if ( !can_see_enemy )
|
||||
return true;
|
||||
|
||||
self_to_enemy = enemy.origin - self.origin;
|
||||
self_forward = anglesToForward( self.angles);
|
||||
distancePast = VectorDot( self_to_enemy, self_forward );
|
||||
|
||||
if ( distancePast > 0 )
|
||||
return false;
|
||||
|
||||
return distancePast < pastEnemyDistance;
|
||||
}
|
||||
|
||||
being_charged()
|
||||
{
|
||||
return ( isDefined( self.being_charged ) && self.being_charged );
|
||||
}
|
||||
|
||||
get_charge_start_index()
|
||||
{
|
||||
animWeights = [ 40 /*Entry 0: ex. alien_queen_charge_start*/,
|
||||
30 /*Entry 1: ex. alien_queen_charge_start_v2*/,
|
||||
30 /*Entry 2: ex. alien_queen_charge_start_v3*/
|
||||
];
|
||||
return get_weighted_index( "charge_attack_start", animWeights );
|
||||
}
|
||||
|
||||
get_hit_geo_index()
|
||||
{
|
||||
animWeights = [ 15 /*alien_drone_run_bump_heavy*/,
|
||||
25 /*alien_drone_run_bump_medium*/,
|
||||
60 /*alien_drone_run_bump_light*/
|
||||
];
|
||||
return get_weighted_index( "charge_hit_geo", animWeights );
|
||||
}
|
||||
|
||||
get_weighted_index( animState, animWeights )
|
||||
{
|
||||
nEntries = self GetAnimEntryCount( animState );
|
||||
assert( animWeights.size == nEntries );
|
||||
return maps\mp\alien\_utility::GetRandomIndex( animWeights );
|
||||
}
|
||||
|
||||
load_queen_fx()
|
||||
{
|
||||
level._effect[ "queen_shield_impact" ] = Loadfx( "fx/impacts/large_metalhit_1" );
|
||||
level._effect[ "queen_ground_spawn" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_elite_ground_spawn" );
|
||||
}
|
||||
|
||||
elite_init()
|
||||
{
|
||||
self.next_health_regen_time = getTime();
|
||||
self.last_charge_time = gettime();
|
||||
if ( !isPlayingSolo() )
|
||||
{
|
||||
self.elite_angered = true;
|
||||
self.moveplaybackrate = 1.2;
|
||||
}
|
||||
}
|
||||
|
||||
activate_angered_state()
|
||||
{
|
||||
prepare_to_regenerate();
|
||||
|
||||
CONST_HEALTH_REGEN_TIME = 10.0; // in sec
|
||||
CONST_HEALTH_REGEN_COOL_DOWN = 60000; // in ms
|
||||
|
||||
self.elite_angered = true; // Regen is now an "angered" state
|
||||
self.moveplaybackrate = 1.2;
|
||||
|
||||
activate_health_regen_shield();
|
||||
}
|
||||
|
||||
activate_health_regen()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
prepare_to_regenerate();
|
||||
|
||||
CONST_HEALTH_REGEN_TIME = 10.0; // in sec
|
||||
CONST_HEALTH_REGEN_COOL_DOWN = 60000; // in ms
|
||||
|
||||
self.next_health_regen_time = getTime() + CONST_HEALTH_REGEN_COOL_DOWN;
|
||||
|
||||
thread play_health_regen_anim();
|
||||
|
||||
activate_health_regen_shield();
|
||||
thread queen_health_regen( CONST_HEALTH_REGEN_TIME );
|
||||
|
||||
self common_scripts\utility::waittill_any_timeout( CONST_HEALTH_REGEN_TIME, "stop_queen_health_regen" );
|
||||
|
||||
disable_health_regen_shield();
|
||||
}
|
||||
|
||||
ALIEN_ELITE_REGEN_IMPULSE_RADIUS = 200;
|
||||
ALIEN_ELITE_REGEN_IMPULSE = 800;
|
||||
|
||||
prepare_to_regenerate()
|
||||
{
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
maps\mp\agents\_scriptedagents::PlayAnimNAtRateUntilNotetrack( "prepare_to_regen", 0, 2.0, "prepare_to_regen", "end" );
|
||||
|
||||
// TODO: Play the FX off of a notetrack when we have the real prepare_to_regen anim
|
||||
// maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "prepare_to_regen", "prepare_to_regen", "impulse", ::handle_pre_regen_notetracks );
|
||||
// PlayFX( level._effect[ "queen_regen_AoE" ], self.origin, AnglesToForward( self.angles ), AnglesToUp( self.angles ) );
|
||||
|
||||
min_damage = level.alien_types[ self.alien_type ].attributes[ "explode_min_damage" ];
|
||||
max_damage = level.alien_types[ self.alien_type ].attributes[ "explode_max_damage" ];
|
||||
|
||||
if ( IsDefined( self.elite_angered ) )
|
||||
{
|
||||
min_damage *= get_angered_damage_scalar();
|
||||
max_damage *= get_angered_damage_scalar();
|
||||
}
|
||||
area_damage_and_impulse( ALIEN_ELITE_REGEN_IMPULSE_RADIUS, min_damage, max_damage, ALIEN_ELITE_REGEN_IMPULSE );
|
||||
}
|
||||
|
||||
play_health_regen_anim()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "stop_queen_health_regen" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
anim_state = "regen";
|
||||
|
||||
while ( true )
|
||||
{
|
||||
maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( anim_state, anim_state, "end" );
|
||||
}
|
||||
}
|
||||
|
||||
queen_health_regen( CONST_HEALTH_REGEN_TIME )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "stop_queen_health_regen" );
|
||||
|
||||
CONST_HEALTH_REGEN_INTERVAL = 1.0; // in sec
|
||||
|
||||
num_of_regen = int ( CONST_HEALTH_REGEN_TIME / CONST_HEALTH_REGEN_INTERVAL );
|
||||
total_health_to_regen = ( self.maxhealth - self.health ) / 2; // regen only to up the midpoint between current health and max health
|
||||
health_each_regen = int ( total_health_to_regen / num_of_regen );
|
||||
|
||||
for ( i = 0; i < num_of_regen; i++ )
|
||||
{
|
||||
wait ( CONST_HEALTH_REGEN_INTERVAL );
|
||||
self.health += health_each_regen;
|
||||
}
|
||||
}
|
||||
|
||||
activate_health_regen_shield()
|
||||
{
|
||||
/*self.shield_model = deploy_health_regen_shield();
|
||||
self.shield_FX = PlayLoopedFX ( level._effect[ "queen_shield" ], 10.0, self.origin,0, AnglesToForward(self.angles), (0,0,1) );
|
||||
|
||||
self.shield_model thread clean_up_on_owner_death( self );
|
||||
self.shield_FX thread clean_up_on_owner_death( self );*/
|
||||
}
|
||||
|
||||
disable_health_regen_shield()
|
||||
{
|
||||
self SetScriptablePartState( "body", "normal" );
|
||||
/*
|
||||
self.shield_model delete();
|
||||
self.shield_FX delete();*/
|
||||
}
|
||||
|
||||
clean_up_on_owner_death( owner )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
owner endon ( "stop_queen_health_regen" );
|
||||
|
||||
owner waittill ( "death" );
|
||||
self delete();
|
||||
}
|
||||
|
||||
deploy_health_regen_shield()
|
||||
{
|
||||
shield = spawn ( "script_model", self.origin );
|
||||
shield setModel ( "alien_shield_bubble_distortion" );
|
||||
shield linkTo ( self, "tag_origin" );
|
||||
shield setCanDamage ( true );
|
||||
|
||||
return shield;
|
||||
}
|
||||
|
||||
//<TODO JC> Remove this as the impact effect will eventually be played from the character model's surface type
|
||||
play_shield_impact_fx( vPoint, vDir )
|
||||
{
|
||||
if ( isDefined ( vDir ) )
|
||||
forward_vector = vDir * -1;
|
||||
else
|
||||
forward_vector = anglesToForward( self.angles );
|
||||
|
||||
up_vector = anglesToUp ( vectorToAngles ( forward_vector ) );
|
||||
PlayFX( level._effect[ "queen_shield_impact" ], vPoint, forward_vector, up_vector );
|
||||
}
|
||||
|
||||
ALIEN_ELITE_EXPLOSIVE_RESISTANCE = 0.5;
|
||||
|
||||
eliteDamageProcessing( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
// Explosive resistance
|
||||
switch ( sMeansOfDeath )
|
||||
{
|
||||
case "MOD_EXPLOSIVE":
|
||||
case "MOD_GRENADE_SPLASH":
|
||||
case "MOD_GRENADE":
|
||||
case "MOD_PROJECTILE":
|
||||
case "MOD_PROJECTILE_SPLASH":
|
||||
iDamage *= ALIEN_ELITE_EXPLOSIVE_RESISTANCE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return iDamage;
|
||||
}
|
||||
|
||||
ALIEN_ELITE_JUMP_IMPULSE = 500;
|
||||
|
||||
on_jump_impact()
|
||||
{
|
||||
DAMAGE_RADIUS = 256;
|
||||
MAX_DAMAGE = 30;
|
||||
MIN_DAMAGE = 10;
|
||||
|
||||
alienUp = anglesToUp( self.angles );
|
||||
if ( !maps\mp\alien\_utility::is_normal_upright( alienUp ) )
|
||||
return;
|
||||
|
||||
area_damage_and_impulse( DAMAGE_RADIUS, MIN_DAMAGE, MAX_DAMAGE, ALIEN_ELITE_JUMP_IMPULSE );
|
||||
}
|
||||
|
||||
area_damage_and_impulse( damage_radius, min_damage, max_damage, impulse )
|
||||
{
|
||||
RadiusDamage( self.origin, damage_radius, max_damage, min_damage, self, "MOD_EXPLOSIVE", "alienrhinoslam_mp" );
|
||||
damage_radius_squared = damage_radius * damage_radius;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( DistanceSquared( self.origin, player.origin ) > damage_radius_squared )
|
||||
continue;
|
||||
|
||||
pushDirection = VectorNormalize(player.origin - self.origin );
|
||||
player player_fly_back( impulse, pushDirection );
|
||||
}
|
||||
}
|
||||
|
||||
get_angered_damage_scalar()
|
||||
{
|
||||
return ANGERED_DAMAGE_SCALAR;
|
||||
}
|
||||
|
||||
chargeStartNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "queen_roll_start":
|
||||
self playLoopSound( "queen_roll" );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
chargeEndNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "queen_roll_stop":
|
||||
self stopLoopSound( "queen_roll" );
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
199
maps/mp/agents/alien/_alien_idle.gsc
Normal file
199
maps/mp/agents/alien/_alien_idle.gsc
Normal file
@ -0,0 +1,199 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include common_scripts\utility;
|
||||
|
||||
MIN_IDLE_REPEAT_TIMES = 2;
|
||||
MAX_POSTURE_REPEAT_TIMES = 2;
|
||||
IDLE_POSTURE_VOICE = "alien_voice";
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self init_alien_idle();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( IsDefined( self.attractor_flare ) )
|
||||
{
|
||||
play_attractor_idle();
|
||||
}
|
||||
else if ( IsDefined( self.enemy_downed ) && self.enemy_downed )
|
||||
{
|
||||
play_enemy_downed_idle();
|
||||
|
||||
if ( level.gameEnded )
|
||||
self.enemy_downed = false; // Make sure play_enemy_downed_idle() is played only once when game ends
|
||||
}
|
||||
else
|
||||
{
|
||||
play_idle();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init_alien_idle()
|
||||
{
|
||||
self.idle_anim_counter = 0;
|
||||
self.consecutive_posture_counter = 0;
|
||||
if ( isDefined( self.xyanimscale ) )
|
||||
self ScrAgentSetAnimScale( self.xyanimscale, 1.0 );
|
||||
|
||||
if ( IsDefined( self.idle_state_locked ) && self.idle_state_locked )
|
||||
self.stateLocked = true;
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.previousAnimState = "idle";
|
||||
if ( IsDefined( self.idle_state_locked ) && self.idle_state_locked )
|
||||
{
|
||||
self.stateLocked = false;
|
||||
self.idle_state_locked = false;
|
||||
}
|
||||
}
|
||||
|
||||
play_enemy_downed_idle()
|
||||
{
|
||||
self faceTarget();
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "posture", "posture", "end" );
|
||||
}
|
||||
|
||||
play_attractor_idle()
|
||||
{
|
||||
facingAngles = VectorToAngles( self.attractor_flare.origin - self.origin );
|
||||
facingAngles = ( self.angles[0], facingAngles[1], self.angles[2] );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", facingAngles );
|
||||
attactorIndex = GetRandomAnimEntry( "idle_flare" );
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimNUntilNotetrack( "idle_flare", attactorIndex, "idle_flare", "end" );
|
||||
}
|
||||
|
||||
play_idle()
|
||||
{
|
||||
self faceTarget();
|
||||
idleState = selectIdleAnimState();
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self PlayAnimNUntilNotetrack( idleState, undefined, idleState, "end" );
|
||||
}
|
||||
|
||||
selectIdleAnimState()
|
||||
{
|
||||
if( isDefined( level.dlc_idle_anim_state_override_func ))
|
||||
{
|
||||
animState = self [[level.dlc_idle_anim_state_override_func]]( self.enemy );
|
||||
if ( IsDefined( animState ) )
|
||||
return animState;
|
||||
}
|
||||
|
||||
if ( isAlive( self.enemy ) )
|
||||
{
|
||||
// Possibly posture
|
||||
if ( cointoss() && self.consecutive_posture_counter < MAX_POSTURE_REPEAT_TIMES )
|
||||
{
|
||||
// <TODO J.C.> Need to move this into notetrack
|
||||
//self PlaySound( IDLE_POSTURE_VOICE );
|
||||
self.consecutive_posture_counter++;
|
||||
return "idle_posture";
|
||||
}
|
||||
}
|
||||
|
||||
self.consecutive_posture_counter = 0;
|
||||
|
||||
if ( self.idle_anim_counter < MIN_IDLE_REPEAT_TIMES + RandomIntRange ( 0, 1 ) )
|
||||
{
|
||||
resultState = "idle_default";
|
||||
self.idle_anim_counter += 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
resultState = "idle";
|
||||
self.idle_anim_counter = 0;
|
||||
}
|
||||
|
||||
return resultState;
|
||||
}
|
||||
|
||||
faceTarget()
|
||||
{
|
||||
faceTarget = undefined;
|
||||
if ( IsAlive( self.enemy ) && DistanceSquared( self.enemy.origin, self.origin ) < 1600 * 1600 )
|
||||
faceTarget = self.enemy;
|
||||
else if ( IsDefined( self.owner ) )
|
||||
faceTarget = self.owner;
|
||||
|
||||
if ( IsDefined( faceTarget ) )
|
||||
{
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( faceTarget );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onDamage( eInflictor, eAttacker, iThatDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( IsDefined( level.dlc_can_do_pain_override_func ) )
|
||||
{
|
||||
painAllowed = [[level.dlc_can_do_pain_override_func]]( "idle" );
|
||||
if ( !painAllowed )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( maps\mp\alien\_utility::is_pain_available( eAttacker,sMeansOfDeath ) )
|
||||
self DoPain( iDFlags, vDir, sHitLoc, iThatDamage, sMeansOfDeath );
|
||||
}
|
||||
|
||||
DoPain( iDFlags, damageDirection, hitLocation, iDamage, sMeansOfDeath )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
is_stun = ( iDFlags & level.iDFLAGS_STUN );
|
||||
|
||||
if ( sMeansOfDeath == "MOD_MELEE" || is_stun )
|
||||
{
|
||||
animState = "pain_pushback";
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "push_back", damageDirection );
|
||||
pain_notify = "pain_pushback";
|
||||
}
|
||||
else
|
||||
{
|
||||
baseAnimState = getBasePainAnimState();
|
||||
animState = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( baseAnimState, iDamage, is_stun );
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "idle", damageDirection, hitLocation );
|
||||
pain_notify = "idle_pain";
|
||||
}
|
||||
|
||||
anime = self GetAnimEntry( animState, animIndex );
|
||||
self maps\mp\alien\_utility::always_play_pain_sound( anime );
|
||||
self maps\mp\alien\_utility::register_pain( anime );
|
||||
self.stateLocked = true;
|
||||
|
||||
if ( IsDefined( self.oriented ) && self.oriented )
|
||||
{
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
}
|
||||
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, pain_notify );
|
||||
|
||||
if ( !isdefined(self.idle_state_locked) || !self.idle_state_locked )
|
||||
self.stateLocked = false;
|
||||
|
||||
self SetAnimState( "idle" );
|
||||
}
|
||||
|
||||
getBasePainAnimState()
|
||||
{
|
||||
if ( IsDefined( level.dlc_alien_pain_anim_state_override_func ) )
|
||||
{
|
||||
animState = [[level.dlc_alien_pain_anim_state_override_func]]( "idle" );
|
||||
if ( IsDefined( animState ) )
|
||||
return animState;
|
||||
}
|
||||
|
||||
return "idle_pain";
|
||||
}
|
776
maps/mp/agents/alien/_alien_jump.gsc
Normal file
776
maps/mp/agents/alien/_alien_jump.gsc
Normal file
@ -0,0 +1,776 @@
|
||||
//
|
||||
// _alien_jump.gsc
|
||||
//
|
||||
// Common jump functionality.
|
||||
// Jump is called from multiple states. It does not correspond to any particular anim state.
|
||||
//
|
||||
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
Jump( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName )
|
||||
{
|
||||
|
||||
maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( endPos - startPos );
|
||||
oldTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
self thread JumpInternal( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName );
|
||||
self waittill( "jump_finished" );
|
||||
self JumpCleanup( oldTurnRate, endAngles );
|
||||
}
|
||||
|
||||
JumpCleanup( oldTurnRate, endAngles )
|
||||
{
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
self ScrAgentSetMaxTurnSpeed( oldTurnRate );
|
||||
if ( self maps\mp\alien\_utility::is_normal_upright( AnglesToUp( endAngles ) ) )
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self.oriented = false;
|
||||
self.ignoreme = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self.oriented = true;
|
||||
self.ignoreme = true;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
JumpInternal( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon ("killanimscript" );
|
||||
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( endPos - startPos );
|
||||
|
||||
if ( isDefined ( scriptableName ) )
|
||||
maps\mp\agents\alien\_alien_anim_utils::resetScriptable( scriptableName, endPos );
|
||||
|
||||
self.trajectoryActive = false;
|
||||
|
||||
// figure out initial jump stuff
|
||||
jumpAnimStates = SpawnStruct();
|
||||
|
||||
jumpInfo = self GetJumpInfo( startPos, startAngles, endPos, endAngles, nextPos );
|
||||
|
||||
self GetJumpAnimStates( jumpInfo, jumpAnimStates );
|
||||
|
||||
if ( IsDefined( jumpCBs ) && IsDefined( jumpCBs.fnSetAnimStates ) )
|
||||
self [[ jumpCBs.fnSetAnimStates ]]( jumpInfo, jumpAnimStates );
|
||||
|
||||
anglesToEnd = GetJumpStartAngles( startPos, startAngles, endPos );
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToEnd );
|
||||
|
||||
t = 0;
|
||||
|
||||
beginAnim = self GetAnimEntry( jumpAnimStates.launchAnimState, jumpAnimStates.launchAnimEntry );
|
||||
|
||||
/////////////
|
||||
// calculate the landing point such that the final position is at our goal
|
||||
endAnim = self GetAnimEntry( jumpAnimStates.landAnimState, jumpAnimStates.landAnimEntry );
|
||||
|
||||
endFinish = GetNotetrackTimes( endAnim, "finish" );
|
||||
if ( endFinish.size > 0 )
|
||||
{
|
||||
endAnimLength = endFinish[0] * GetAnimLength( endAnim );
|
||||
}
|
||||
else
|
||||
{
|
||||
endAnimLength = GetAnimLength( endAnim );
|
||||
}
|
||||
|
||||
endAnimTime = endAnimLength / jumpAnimStates.playbackRate;
|
||||
|
||||
endLandMoveFrame = floor( endAnimTime * 20.0 );
|
||||
endLandMoveTime = (endLandMoveFrame / 20.0) / endAnimTime;
|
||||
|
||||
endLand = GetNotetrackTimes( endAnim, "stop_teleport" );
|
||||
if ( endLand.size > 0 )
|
||||
{
|
||||
endLandTime = endLand[0] * endAnimTime;
|
||||
beginLandMoveFrame = ceil( endLandTime * 20.0 );
|
||||
beginLandMoveTime = (beginLandMoveFrame / 20.0) / endAnimTime;
|
||||
|
||||
endLandMove = GetMoveDelta( endAnim, beginLandMoveTime, endLandMoveTime );
|
||||
}
|
||||
else
|
||||
{
|
||||
endLandTime = 0.8 * endAnimTime;
|
||||
beginLandMoveFrame = ceil( endLandTime * 20.0 );
|
||||
beginLandMoveTime = (beginLandMoveFrame / 20.0) / endAnimTime;
|
||||
|
||||
endLandMove = GetMoveDelta( endAnim, beginLandMoveTime, endLandMoveTime );
|
||||
}
|
||||
|
||||
endAngles = GetJumpEndAngles( startPos, endPos, endAngles );
|
||||
endLandMoveRot = RotateVector( endLandMove, endAngles );
|
||||
endLandOrigin = endPos - endLandMoveRot;
|
||||
|
||||
/////////////
|
||||
// jump begin
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self PlaySoundOnMovingEnt( get_jump_SFX_alias() );
|
||||
|
||||
if ( AnimHasNotetrack( beginAnim, "start_teleport" ) )
|
||||
{
|
||||
self PlayAnimNAtRateUntilNotetrack(jumpAnimStates.launchAnimState,
|
||||
jumpAnimStates.launchAnimEntry,
|
||||
jumpAnimStates.playbackRate,
|
||||
"jump_launch",
|
||||
"start_teleport" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self PlayAnimNAtRateForTime(jumpAnimStates.launchAnimState,
|
||||
jumpAnimStates.launchAnimEntry,
|
||||
jumpAnimStates.playabackRate,
|
||||
0.5 * GetAnimLength( beginAnim ) / jumpAnimStates.playbackRate );
|
||||
}
|
||||
|
||||
/////////////
|
||||
// do the trajectory
|
||||
startTime = gettime();
|
||||
t = self ScrAgentDoTrajectory( self.origin, endLandOrigin, jumpInfo.jumpSpeed2D );
|
||||
self.trajectoryActive = true;
|
||||
|
||||
/////////////
|
||||
// Handle pain
|
||||
self endon( "jump_pain_interrupt" );
|
||||
self thread JumpPain( t, endPos );
|
||||
|
||||
self notify( "jump_launching" ); // for cloaker jump
|
||||
|
||||
/////////////
|
||||
// orient the agent to the plane of the landing position
|
||||
oldTurnRate = self ScrAgentGetMaxTurnSpeed();
|
||||
self thread jumpOrient( jumpInfo, endAngles, oldTurnRate, t );
|
||||
|
||||
/////////////
|
||||
// finish the launch animation
|
||||
self WaitUntilNotetrack( "jump_launch", "end" );
|
||||
beginTime = ( gettime() - startTime ) / 1000;
|
||||
|
||||
|
||||
/////////////
|
||||
// calculate the time to spend in the air
|
||||
loopTime = t - beginTime - endLandTime;
|
||||
|
||||
/////////////
|
||||
// jump loop
|
||||
|
||||
if ( loopTime > 0 )
|
||||
{
|
||||
self PlayAnimNAtRateForTime(jumpAnimStates.inAirAnimState,
|
||||
jumpAnimStates.inAirAnimEntry,
|
||||
jumpAnimStates.playbackRate,
|
||||
loopTime );
|
||||
}
|
||||
|
||||
/////////////
|
||||
// jump land
|
||||
|
||||
// Allow a last minute animState change
|
||||
if ( IsDefined( jumpCBs ) && IsDefined( jumpCBs.fnLandAnimStateChoice ) )
|
||||
self [[ jumpCBs.fnLandAnimStateChoice ]]( jumpInfo, jumpAnimStates );
|
||||
|
||||
self SetAnimState( jumpAnimStates.landAnimState, jumpAnimStates.landAnimEntry, jumpAnimStates.playbackRate );
|
||||
self waittill( "traverse_complete" );
|
||||
self.trajectoryActive = false;
|
||||
|
||||
if ( isDefined ( scriptableName ) )
|
||||
maps\mp\agents\alien\_alien_anim_utils::playAnimOnScriptable( scriptableName, endPos );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 0.0 );
|
||||
|
||||
self ScrAgentSetMaxTurnSpeed( 20.28318 ); // 2 pi
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
// make sure our rotation ends up at endAngles.
|
||||
// this would be even better if it was accounting for rotating entirely to the
|
||||
// direction of the next negotiation node
|
||||
self ScrAgentSetOrientMode( "face angle abs", endAngles );
|
||||
|
||||
self thread waitForLandImpact( "jump_land" );
|
||||
self WaitUntilNotetrack( "jump_land", "end" );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
/////////////
|
||||
// yay!
|
||||
|
||||
//delta_from_goal = endPos - self.origin;
|
||||
//iprintln( "Jump Delta: " + delta_from_goal );
|
||||
|
||||
self SetOrigin( endPos, false ); // Boo!
|
||||
|
||||
self notify( "jump_finished" );
|
||||
|
||||
}
|
||||
|
||||
waitForLandImpact( animName )
|
||||
{
|
||||
alienType = self maps\mp\alien\_utility::get_alien_type();
|
||||
|
||||
switch( alienType )
|
||||
{
|
||||
case "elite":
|
||||
self WaitUntilNotetrack( animName, "jump_land_impact" );
|
||||
maps\mp\agents\alien\_alien_elite::on_jump_impact();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
jumpOrient( jumpInfo, endAngles, oldTurnRate, timeInAir )
|
||||
{
|
||||
self endon( "death" );
|
||||
UPRIGHT_VECTOR = ( 0, 0, 1 );
|
||||
UPRIGHT_DOT = 0.85;
|
||||
startUpright = maps\mp\alien\_utility::is_normal_upright( jumpInfo.startUpVector );
|
||||
endUpright = maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector );
|
||||
|
||||
if ( startUpright && !endUpright )
|
||||
{
|
||||
start_orient_time = 0.5;
|
||||
end_orient_time = 1.0;
|
||||
}
|
||||
else if ( !startUpright && endUpright )
|
||||
{
|
||||
start_orient_time = 0.0;
|
||||
end_orient_time = 0.5;
|
||||
}
|
||||
else
|
||||
{
|
||||
start_orient_time = 0.0;
|
||||
end_orient_time = 1.0;
|
||||
}
|
||||
|
||||
total_orient_time = end_orient_time - start_orient_time;
|
||||
|
||||
if ( start_orient_time > 0 )
|
||||
{
|
||||
wait ( timeInAir * start_orient_time );
|
||||
}
|
||||
|
||||
threshold = 1.0;
|
||||
if ( DistanceSquared( self.angles, endAngles ) > threshold )
|
||||
{
|
||||
anglesDelta = AnglesDelta( self.angles, endAngles );
|
||||
|
||||
turnRate = anglesDelta / ( timeInAir * total_orient_time );
|
||||
turnRate = turnRate * 3.1415926 / 180.0; // deg to rad
|
||||
turnRate = turnRate / 20; // rads per frame
|
||||
self ScrAgentSetMaxTurnSpeed( turnRate );
|
||||
}
|
||||
self ScrAgentSetOrientMode( "face angle abs", endAngles );
|
||||
}
|
||||
|
||||
GetJumpInfo( startPos, startAngles, endPos, endAngles, nextPos )
|
||||
{
|
||||
jumpInfo = SpawnStruct();
|
||||
|
||||
startToEnd = endPos - startPos;
|
||||
startToEnd2D = startToEnd * ( 1, 1, 0 );
|
||||
startToEnd2D = VectorNormalize( startToEnd2D );
|
||||
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchGroundDelta ), "Jump launch table has not been initialized" );
|
||||
|
||||
jumpInfo.launchOrigin = startPos + startToEnd2D * level.alienAnimData.jumpLaunchGroundDelta;
|
||||
jumpInfo.landOrigin = endPos;
|
||||
|
||||
jumpInfo.jumpVector = jumpInfo.landOrigin - jumpInfo.launchOrigin;
|
||||
jumpInfo.jumpVector2D = jumpInfo.jumpVector * ( 1, 1, 0 );
|
||||
jumpInfo.jumpDistance2D = Length( jumpInfo.jumpVector2D );
|
||||
AssertEx( jumpInfo.jumpDistance2D != 0, "Trying to jump vertically. This is not handled." );
|
||||
|
||||
jumpInfo.jumpDirection2D = jumpInfo.jumpVector2D / jumpInfo.jumpDistance2D;
|
||||
|
||||
if ( IsDefined( nextPos ) )
|
||||
jumpInfo.landVector = nextPos - endPos;
|
||||
else if ( IsDefined( self.enemy ) )
|
||||
jumpInfo.landVector = self.enemy.origin - endPos;
|
||||
else
|
||||
jumpInfo.landVector = AnglesToForward( self.angles );
|
||||
|
||||
jumpInfo.startAngles = GetJumpAngles( jumpInfo.jumpVector, AnglesToUp( startAngles ) );
|
||||
jumpInfo.endAngles = GetJumpAngles( jumpInfo.jumpVector, AnglesToUp( endAngles ) );
|
||||
|
||||
jumpInfo.startUpVector = AnglesToUp( jumpInfo.startAngles );
|
||||
jumpInfo.endUpVector = AnglesToUp( jumpInfo.endAngles );
|
||||
|
||||
GetJumpVelocity( jumpInfo );
|
||||
|
||||
return jumpInfo;
|
||||
}
|
||||
|
||||
GetJumpAngles( jumpVector, vUp )
|
||||
{
|
||||
forwardVector = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpVector, vUp );
|
||||
right = VectorCross( forwardVector, vUp );
|
||||
angles = AxisToAngles( forwardVector, right, vUp );
|
||||
return angles;
|
||||
}
|
||||
|
||||
//GetLaunchAngle( speed, gravity, x, y )
|
||||
//{
|
||||
// // From: http://en.wikipedia.org/wiki/Trajectory_of_a_projectile
|
||||
// val = speed * speed * speed * speed - gravity * ( gravity * x * x + 2 * y * speed * speed );
|
||||
// AssertEx ( val >= 0, "The given velocity is unable to reach the target. Increase the velocity." );
|
||||
//
|
||||
// //requiredAngleHigh = ATan( ( speed * speed + Sqrt( val ) ) / ( gravity * x ) );
|
||||
// requiredAngleLow = ATan( ( speed * speed - Sqrt( val ) ) / ( gravity * x ) );
|
||||
//
|
||||
// return requiredAngleLow;
|
||||
//}
|
||||
|
||||
//GetMinimumLaunchSpeed( gravity, x, y )
|
||||
//{
|
||||
// // From: http://en.wikipedia.org/wiki/Range_of_a_projectile
|
||||
// // This code calculates the minimum speed required to reach a point by assuming
|
||||
// // the point being reached is the maximum range of the projectile fired at the
|
||||
// // desired velocity. The result is derived from the equation for the maximum
|
||||
// // range of a projectile: Range = ( vel / g ) * sqrt( vel ^ 2 + 2 * g * height )
|
||||
// a = 2 * gravity * y;
|
||||
// b = gravity * gravity * x * x;
|
||||
//
|
||||
// result = Sqrt( (a + Sqrt( a * a + 4 * b ) ) / 2 );
|
||||
//
|
||||
// return result;
|
||||
//}
|
||||
|
||||
GetJumpVelocity( jumpInfo )
|
||||
{
|
||||
x = jumpInfo.jumpDistance2D;
|
||||
y = jumpInfo.jumpVector[ 2 ];
|
||||
isWallJump = !maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector );
|
||||
g = GetJumpGravity( isWallJump );
|
||||
MIN_JUMP_SPEED_MULTIPLIER = 1.01;
|
||||
|
||||
minJumpSpeed = TrajectoryCalculateMinimumVelocity( jumpInfo.launchOrigin, jumpInfo.landOrigin, g );//GetMinimumLaunchSpeed( g, x, y );
|
||||
jumpSpeedMultiplier = GetJumpSpeedMultiplier( isWallJump );
|
||||
jumpSpeed = minJumpSpeed * MIN_JUMP_SPEED_MULTIPLIER * jumpSpeedMultiplier;
|
||||
AssertEx( jumpSpeed != 0, "Trying to jump but the jump doesn't go anywhere." );
|
||||
|
||||
jumpAngle = TrajectoryCalculateExitAngle( jumpSpeed, g, x, y );//GetLaunchAngle( jumpSpeed, g, x, y );
|
||||
|
||||
jumpAngleCos = Cos( jumpAngle );
|
||||
AssertEx( jumpAngleCos != 0, "Trying to jump vertically. This is not handled." );
|
||||
|
||||
jumpInfo.jumpTime = jumpInfo.jumpDistance2D / ( jumpSpeed * jumpAngleCos );
|
||||
|
||||
gravityVector = g * ( 0, 0, -1 );
|
||||
jumpInfo.launchVelocity = TrajectoryCalculateInitialVelocity( jumpInfo.launchOrigin, jumpInfo.landOrigin, gravityVector, jumpInfo.jumpTime ); //(jumpInfo.jumpVector - 0.5 * gravityVector * jumpInfo.jumpTime * jumpInfo.jumpTime) / jumpInfo.jumpTime;
|
||||
jumpInfo.launchVelocity2D = jumpInfo.launchVelocity * ( 1, 1, 0 );
|
||||
jumpInfo.jumpSpeed2D = Length( jumpInfo.launchVelocity2D );
|
||||
}
|
||||
|
||||
GetJumpSpeedMultiplier( is_wall_jump )
|
||||
{
|
||||
if ( IsDefined( self.melee_jumping ) && self.melee_jumping )
|
||||
{
|
||||
AssertEx( IsDefined( level.alien_jump_melee_speed, "Alien jump speed is not defined" ) );
|
||||
return level.alien_jump_melee_speed;
|
||||
}
|
||||
else if ( is_wall_jump )
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpWallSpeed" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpSpeed" );
|
||||
}
|
||||
}
|
||||
|
||||
GetJumpGravity( is_wall_jump )
|
||||
{
|
||||
if ( IsDefined( self.melee_jumping ) && self.melee_jumping )
|
||||
{
|
||||
AssertEx( IsDefined( level.alien_jump_melee_gravity, "Alien jump gravity is not defined" ) );
|
||||
return level.alien_jump_melee_gravity;
|
||||
}
|
||||
else if ( is_wall_jump )
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpWallGravity" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return GetDvarFloat( "agent_jumpGravity" );
|
||||
}
|
||||
}
|
||||
|
||||
GetJumpPlaybackRate( jumpInfo, animStates )
|
||||
{
|
||||
AssertEx( jumpInfo.jumpTime != 0);
|
||||
|
||||
launchAnim = self GetAnimEntry( animStates.launchAnimState, animStates.launchAnimEntry );
|
||||
inAirAnim = self GetAnimEntry( animStates.inAirAnimState, animStates.inAirAnimEntry );
|
||||
landAnim = self GetAnimEntry( animStates.landAnimState, animStates.landAnimEntry );
|
||||
|
||||
launchAnimLength = GetAnimLength( launchAnim );
|
||||
launchAnimInAirTime = launchAnimLength * 0.5;
|
||||
|
||||
launchAnimTakeoff = GetNotetrackTimes( launchAnim, "start_teleport" );
|
||||
if ( IsDefined( launchAnimTakeoff ) && launchAnimTakeoff.size > 0 )
|
||||
launchAnimInAirTime = launchAnimLength - launchAnimTakeoff[0] * launchAnimLength;
|
||||
Assert( launchAnimInAirTime < launchAnimLength );
|
||||
|
||||
landAnimLength = GetAnimLength( landAnim );
|
||||
landAnimInAirTime = landAnimLength * 0.5;
|
||||
|
||||
landAnimArrive = GetNotetrackTimes( landAnim, "stop_teleport" );
|
||||
if ( IsDefined( landAnimArrive ) && landAnimArrive.size > 0 )
|
||||
landAnimInAirTime = landAnimArrive[0] * landAnimLength;
|
||||
Assert( landAnimInAirTime < landAnimLength );
|
||||
|
||||
inAirAnimLength = GetAnimLength( inAirAnim );
|
||||
Assert( inAirAnimLength > 0 );
|
||||
|
||||
// calculate how long the physics needs to run and round up to the nearest frame
|
||||
// this ensures that the trajectory finishes before we move out of the trajectory
|
||||
// anim mode
|
||||
trajectoryFrameCount = ceil( jumpInfo.jumpTime * 20.0 );
|
||||
trajectoryPhysicsTime = trajectoryFrameCount / 20.0;
|
||||
|
||||
// calculate the amount to scale all animations to achieve the required time in the air
|
||||
trajectoryAnimTime = inAirAnimLength + launchAnimInAirTime + landAnimInAirTime;
|
||||
trajectoryAnimScale = trajectoryAnimTime / trajectoryPhysicsTime;
|
||||
|
||||
// add a two frames of trim since the in air animation will play for a fixed time
|
||||
// and that time may straddle a frame boundary on both ends
|
||||
inAirAnimTime = inAirAnimLength / trajectoryAnimScale + 0.1;
|
||||
inAirAnimScale = inAirAnimLength / inAirAnimTime;
|
||||
|
||||
return inAirAnimScale;
|
||||
}
|
||||
|
||||
GetJumpAnimStates( jumpInfo, animStates )
|
||||
{
|
||||
animStates.launchAnimState = GetLaunchAnimState( jumpInfo );
|
||||
animStates.launchAnimEntry = GetLaunchAnimEntry( jumpInfo, animStates.launchAnimState );
|
||||
|
||||
animStates.landAnimState = GetLandAnimState( jumpInfo );
|
||||
animStates.landAnimEntry = GetLandAnimEntry( jumpInfo, animStates.landAnimState );
|
||||
|
||||
animStates.inAirAnimState = GetInAirAnimState( jumpInfo, animStates.launchAnimState, animStates.landAnimState );
|
||||
animStates.inAirAnimEntry = GetInAirAnimEntry( jumpInfo, animStates.launchAnimState, animStates.landAnimState );
|
||||
|
||||
animStates.playbackRate = self GetJumpPlaybackRate( jumpInfo, animStates );
|
||||
}
|
||||
|
||||
GetJumpStartAngles( startPos, startAngles, endPos )
|
||||
{
|
||||
startUp = AnglesToUp( startAngles );
|
||||
startForward = VectorNormalize( endPos - startPos );
|
||||
if ( VectorDot( startUp, startForward ) > 0.98 )
|
||||
startForward = ( 0, 0, 1 );
|
||||
startLeft = VectorCross( startUp, startForward );
|
||||
startForward = VectorCross( startLeft, startUp );
|
||||
return AxisToAngles( startForward, -1 * startLeft, startUp );
|
||||
}
|
||||
|
||||
GetLaunchAnimState( jumpInfo )
|
||||
{
|
||||
LEVEL_DEGREE_RANGE = 20;
|
||||
cosLimitForLevel = Cos( 90 - LEVEL_DEGREE_RANGE );
|
||||
|
||||
startToEnd = VectorNormalize( jumpInfo.jumpVector );
|
||||
startToEndDotUp = VectorDot( startToEnd, jumpInfo.startUpVector );
|
||||
|
||||
if ( abs( startToEndDotUp ) <= cosLimitForLevel )
|
||||
{
|
||||
return "jump_launch_level";
|
||||
}
|
||||
else if ( startToEndDotUp > 0 )
|
||||
{
|
||||
return "jump_launch_up";
|
||||
}
|
||||
else if ( startToEndDotUp < 0 )
|
||||
{
|
||||
return "jump_launch_down";
|
||||
}
|
||||
}
|
||||
|
||||
GetLaunchAnimEntry( jumpInfo, launchAnimState )
|
||||
{
|
||||
launchDirection = VectorNormalize( jumpInfo.launchVelocity );
|
||||
launchDirection = RotateVector( launchDirection, jumpInfo.startAngles );
|
||||
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection ), "Alien jump table has not been initialized" );
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ] ),
|
||||
"Alien jump table has not been initialized for launch state " + launchAnimState );
|
||||
|
||||
launchEntryCount = self GetAnimEntryCount( launchAnimState );
|
||||
AssertEx( launchEntryCount > 0, "Alien launch state " + launchAnimState + " as no animations." );
|
||||
|
||||
launchEntry = 0;
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ launchEntry ] ),
|
||||
"Alien launch entry " + launchEntry + " for state " + launchAnimState + " has no direction." );
|
||||
|
||||
launchEntryDot = VectorDot( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ launchEntry ], launchDirection );
|
||||
|
||||
for ( nextLaunchEntry = 1; nextLaunchEntry < launchEntryCount; nextLaunchEntry++ )
|
||||
{
|
||||
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ nextLaunchEntry ] ),
|
||||
"Alien launch entry " + nextLaunchEntry + " for state " + launchAnimState + " has no direction." );
|
||||
|
||||
nextLaunchEntryDot = VectorDot( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ nextLaunchEntry ], launchDirection );
|
||||
if ( nextLaunchEntryDot > launchEntryDot )
|
||||
{
|
||||
launchEntry = nextLaunchEntry;
|
||||
launchEntryDot = nextLaunchEntryDot;
|
||||
}
|
||||
}
|
||||
|
||||
return launchEntry;
|
||||
}
|
||||
|
||||
GetInAirAnimState( jumpInfo, launchAnimState, landAnimState )
|
||||
{
|
||||
return "jump_in_air";
|
||||
}
|
||||
|
||||
GetInAirAnimEntry( jumpInfo, launchAnimState, landAnimState )
|
||||
{
|
||||
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry ), "Alien in air table has not been initialized" );
|
||||
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry[ launchAnimState ] ),
|
||||
"Alien in air table has not been initialized for launch state " + launchAnimState );
|
||||
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry[ launchAnimState ][ landAnimState ] ),
|
||||
"Alien in air table has not been initialized for launch state " + launchAnimState + " and land anim state " + landAnimState );
|
||||
|
||||
return level.alienAnimData.inAirAnimEntry[ launchAnimState ][ landAnimState ];
|
||||
}
|
||||
|
||||
GetJumpEndAngles( startPos, endPos, endAngles )
|
||||
{
|
||||
endUp = AnglesToUp( endAngles );
|
||||
endForward = VectorNormalize( endPos - startPos );
|
||||
if ( VectorDot( endUp, endForward ) > 0.98 )
|
||||
endForward = ( 0, 0, 1 );
|
||||
endLeft = VectorCross( endUp, endForward );
|
||||
endForward = VectorCross( endLeft, endUp );
|
||||
return AxisToAngles( endForward, -1 * endLeft, endUp );
|
||||
}
|
||||
|
||||
GetLandAnimState( jumpInfo )
|
||||
{
|
||||
jumpVectorLength = length( jumpInfo.jumpVector );
|
||||
PITCH_THRESHOLD = 0.342; // sin(20)
|
||||
|
||||
if ( !maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector ) )
|
||||
{
|
||||
WORLD_UP = ( 0, 0, 1 );
|
||||
pitch = VectorDot( jumpInfo.jumpVector, WORLD_UP ) / jumpVectorLength;
|
||||
|
||||
if ( pitch > PITCH_THRESHOLD )
|
||||
{
|
||||
return "jump_land_sidewall_low";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "jump_land_sidewall_high";
|
||||
}
|
||||
}
|
||||
|
||||
pitch = VectorDot( jumpInfo.jumpVector, jumpInfo.endUpVector ) / jumpVectorLength;
|
||||
|
||||
if ( pitch > PITCH_THRESHOLD )
|
||||
{
|
||||
return "jump_land_down";
|
||||
}
|
||||
else if ( pitch < ( PITCH_THRESHOLD * -1 ) )
|
||||
{
|
||||
return "jump_land_up";
|
||||
}
|
||||
else
|
||||
{
|
||||
return "jump_land_level";
|
||||
}
|
||||
}
|
||||
|
||||
GetLandAnimEntry( jumpInfo, landAnimState )
|
||||
{
|
||||
incomingVectorWithoutNormal = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpInfo.jumpVector, jumpInfo.endUpVector );
|
||||
outgoingVectorWithoutNormal = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpInfo.landVector, jumpInfo.endUpVector );
|
||||
thirdVector = incomingVectorWithoutNormal - outgoingVectorWithoutNormal;
|
||||
|
||||
outgoingRightVector = VectorCross( outgoingVectorWithoutNormal, jumpInfo.endUpVector );
|
||||
outgoingRightVectorWithoutNormal = VectorNormalize( maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( outgoingRightVector, jumpInfo.endUpVector ) ) * 100;
|
||||
|
||||
projectionIncomingToOutgoingRight = VectorDot ( incomingVectorWithoutNormal * -1, outgoingRightVectorWithoutNormal );
|
||||
|
||||
//Law of cosine
|
||||
a = Length( incomingVectorWithoutNormal );
|
||||
b = Length( outgoingVectorWithoutNormal );
|
||||
c = Length( thirdVector );
|
||||
|
||||
MIN_LENGTH = 0.001;
|
||||
|
||||
// Edge case: Return forward;
|
||||
if ( a < MIN_LENGTH || b < MIN_LENGTH )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
|
||||
ratio = ( a * a + b * b - c * c ) / ( 2 * a * b );
|
||||
if ( ratio <= -1 )
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
else if ( ratio >= 1 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
rotatedYaw = Acos( ratio );
|
||||
if ( projectionIncomingToOutgoingRight > 0 ) //Entering from the right
|
||||
{
|
||||
if ( 0 <= rotatedYaw && rotatedYaw < 22.5 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if ( 22.5 <= rotatedYaw && rotatedYaw < 67.5 )
|
||||
{
|
||||
return 2;
|
||||
}
|
||||
else if ( 67.5 <= rotatedYaw && rotatedYaw < 112.5 )
|
||||
{
|
||||
return 4;
|
||||
}
|
||||
else if ( 112.5 <= rotatedYaw && rotatedYaw < 157.5 )
|
||||
{
|
||||
return 7;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
else //Entering from the left
|
||||
{
|
||||
if ( 0 <= rotatedYaw && rotatedYaw < 22.5 )
|
||||
{
|
||||
return 1;
|
||||
}
|
||||
else if ( 22.5 <= rotatedYaw && rotatedYaw < 67.5 )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
else if ( 67.5 <= rotatedYaw && rotatedYaw < 112.5 )
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
else if ( 112.5 <= rotatedYaw && rotatedYaw < 157.5 )
|
||||
{
|
||||
return 5;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 6;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
JumpPain( duration, endPos )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "jump_finished" );
|
||||
|
||||
start_time = gettime();
|
||||
duration_msec = duration*1000;
|
||||
self waittill( "jump_pain", damageDirection, hitLocation, iDamage, stun );
|
||||
|
||||
// Make sure we're still jumping
|
||||
if ( !self.trajectoryActive )
|
||||
{
|
||||
return; // Too late!
|
||||
}
|
||||
|
||||
// Stop normal jump animations and play the pain
|
||||
self notify( "jump_pain_interrupt" );
|
||||
|
||||
jump_pain_state = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( "jump_pain", iDamage, stun );
|
||||
jump_pain_index = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "jump", damageDirection, hitLocation );
|
||||
damage_degree = self maps\mp\agents\alien\_alien_anim_utils::getDamageDegree( iDamage, stun );
|
||||
jump_end_time_sec = start_time * 0.001 + duration;
|
||||
PlayInAirJumpPainAnims( jump_pain_state, jump_pain_index, jump_end_time_sec, damage_degree );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 0.0 );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
// this would be even better if it was accounting for rotating entirely to the
|
||||
// direction of the next negotiation node
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
impact_pain_anim = self GetImpactPainAnimState( damage_degree );
|
||||
impact_pain_index = self maps\mp\agents\alien\_alien_anim_utils::GetImpactPainAnimIndex( jump_pain_index );
|
||||
self SetAnimState( impact_pain_anim, impact_pain_index, 1.0 );
|
||||
self WaitUntilNotetrack( impact_pain_anim, "code_move" );
|
||||
|
||||
self notify ( "jump_finished" );
|
||||
}
|
||||
|
||||
PlayInAirJumpPainAnims( jump_pain_state, jump_pain_entry, jump_end_time_sec, damage_degree )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "killanimscript" );
|
||||
self endon( "jump_finished" );
|
||||
|
||||
self SetAnimState( jump_pain_state, jump_pain_entry, 1.0 );
|
||||
msg = self common_scripts\utility::waittill_any_return( "jump_pain", "traverse_complete" );
|
||||
if ( msg == "traverse_complete" )
|
||||
return;
|
||||
|
||||
idle_time_remaining = jump_end_time_sec - GetTime() * 0.001;
|
||||
|
||||
if ( idle_time_remaining > 0 )
|
||||
{
|
||||
MAX_RATE_SCALE = 2.0;
|
||||
jump_pain_idle_state = self GetJumpPainIdleAnimState( damage_degree );
|
||||
jump_pain_idle_anim = self GetAnimEntry( jump_pain_idle_state, jump_pain_entry );
|
||||
jump_pain_idle_anim_length = GetAnimLength( jump_pain_idle_anim );
|
||||
jump_pain_idle_rate = Min( MAX_RATE_SCALE, jump_pain_idle_anim_length / idle_time_remaining );
|
||||
|
||||
self SetAnimState( jump_pain_idle_state, jump_pain_entry, jump_pain_idle_rate );
|
||||
}
|
||||
|
||||
self waittill( "traverse_complete" );
|
||||
}
|
||||
|
||||
GetJumpPainIdleAnimState( damage_degree )
|
||||
{
|
||||
return ( "jump_pain_idle_" + damage_degree );
|
||||
}
|
||||
|
||||
GetImpactPainAnimState( damage_degree )
|
||||
{
|
||||
return ( "jump_impact_pain_" + damage_degree );
|
||||
}
|
||||
|
||||
get_jump_SFX_alias()
|
||||
{
|
||||
switch( maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "elite":
|
||||
return "null"; // "No sound is better than the wrong sound" - Tim S.
|
||||
|
||||
case "spitter":
|
||||
return "spitter_jump";
|
||||
|
||||
case "seeder":
|
||||
return "seed_jump";
|
||||
|
||||
case "gargoyle":
|
||||
return "gg_jump";
|
||||
|
||||
default:
|
||||
return "alien_jump";
|
||||
}
|
||||
}
|
190
maps/mp/agents/alien/_alien_leper.gsc
Normal file
190
maps/mp/agents/alien/_alien_leper.gsc
Normal file
@ -0,0 +1,190 @@
|
||||
// _alien_leper
|
||||
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
LEPER_NODE_WAIT = 5.0;
|
||||
LEPER_SPAWN_DURATION = 35000; // Lifetime
|
||||
LEPER_MIN_SAFE_PLAYER_DIST_SQR = 1048576; // A player entering this distance triggers searching for a new node
|
||||
LEPER_DAMAGE_MOVE_DELAY = 1.5; // Waits this time after being damaged before choosing a new node
|
||||
|
||||
leper_init()
|
||||
{
|
||||
self.leperDespawnTime = getTime() + LEPER_SPAWN_DURATION;
|
||||
self thread handle_favorite_enemy();
|
||||
|
||||
}
|
||||
|
||||
leper_combat( enemy )
|
||||
{
|
||||
self endon( "death" );
|
||||
enemy endon( "death" );
|
||||
|
||||
self leper_retreat( enemy );
|
||||
}
|
||||
|
||||
leper_retreat( enemy )
|
||||
{
|
||||
while ( 1 )
|
||||
{
|
||||
self leper_approach( enemy );
|
||||
self leper_wait_at_node( enemy );
|
||||
}
|
||||
}
|
||||
|
||||
leper_challenge_despawn( despawn_time )
|
||||
{
|
||||
self endon( "leper_despawn" );
|
||||
self endon( "death" );
|
||||
|
||||
wait despawn_time;
|
||||
self leper_despawn();
|
||||
}
|
||||
|
||||
|
||||
handle_favorite_enemy()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
// Our favorite enemy should always be the closest player
|
||||
while ( 1 )
|
||||
{
|
||||
self.favoriteenemy = self get_closest_living_player();
|
||||
wait 5;
|
||||
}
|
||||
}
|
||||
|
||||
leper_despawn()
|
||||
{
|
||||
self endon( "death" );
|
||||
self.health = 30000;
|
||||
self.maxhealth = 30000;
|
||||
|
||||
self ScrAgentSetGoalPos( self.origin );
|
||||
self ScrAgentSetGoalRadius( 2048 );
|
||||
PlayFXOnTag( level._effect[ "alien_teleport" ], self, "tag_origin" );
|
||||
wait 1.0;
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
|
||||
leper_approach( enemy )
|
||||
{
|
||||
retreat_node = self get_leper_retreat_node( enemy );
|
||||
if ( !isDefined( retreat_node ))
|
||||
{
|
||||
wait 1;
|
||||
return;
|
||||
}
|
||||
|
||||
self ScrAgentSetGoalNode( retreat_node );
|
||||
self ScrAgentSetGoalRadius( 64 );
|
||||
self waittill( "goal_reached" );
|
||||
}
|
||||
|
||||
leave_node_on_distance_breach( enemy )
|
||||
{
|
||||
enemy endon( "death" );
|
||||
self endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "alien_main_loop_restart" );
|
||||
self endon( "leave_node ");
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
if ( DistanceSquared( enemy.origin, self.origin ) < LEPER_MIN_SAFE_PLAYER_DIST_SQR )
|
||||
{
|
||||
// go away
|
||||
self notify( "leave_node" );
|
||||
}
|
||||
wait 1;
|
||||
}
|
||||
}
|
||||
|
||||
leave_node_on_attacked( enemy )
|
||||
{
|
||||
enemy endon( "death" );
|
||||
self endon( "death" );
|
||||
self endon( "enemy" );
|
||||
self endon( "alien_main_loop_restart" );
|
||||
self endon( "leave_node ");
|
||||
|
||||
self waittill( "damage" );
|
||||
wait LEPER_DAMAGE_MOVE_DELAY;
|
||||
|
||||
self notify( "leave_node" );
|
||||
}
|
||||
|
||||
leper_wait_at_node( enemy )
|
||||
{
|
||||
self endon( "leave_node" );
|
||||
|
||||
self thread leave_node_on_attacked( enemy );
|
||||
self thread leave_node_on_distance_breach( enemy );
|
||||
wait LEPER_NODE_WAIT;
|
||||
}
|
||||
|
||||
get_leper_retreat_node( enemy )
|
||||
{
|
||||
retreat_nodes = get_named_retreat_nodes();
|
||||
if ( !isDefined( retreat_nodes ) )
|
||||
retreat_nodes = get_possible_retreat_nodes();
|
||||
|
||||
filters = [];
|
||||
filters[ "direction" ] = "override";
|
||||
filters[ "direction_override" ] = get_direction_away_from_players();
|
||||
filters[ "direction_weight" ] = 2.0;
|
||||
filters[ "min_height" ] = 64.0;
|
||||
filters[ "max_height" ] = 500.0;
|
||||
filters[ "height_weight" ] = 2.0;
|
||||
filters[ "enemy_los" ] = false;
|
||||
filters[ "enemy_los_weight" ] = 2.0;
|
||||
filters[ "min_dist_from_enemy" ] = 500.0;
|
||||
filters[ "max_dist_from_enemy" ] = 2048.0;
|
||||
filters[ "desired_dist_from_enemy" ] = 1500.0;
|
||||
filters[ "dist_from_enemy_weight" ] = 3.0;
|
||||
filters[ "min_dist_from_all_enemies" ] = 800.0;
|
||||
filters[ "min_dist_from_all_enemies_weight" ] = 5.0;
|
||||
filters[ "not_recently_used_weight" ] = 4.0;
|
||||
filters[ "random_weight" ] = 1.5;
|
||||
|
||||
result = maps\mp\agents\alien\_alien_think::get_retreat_node_rated( enemy, filters, retreat_nodes );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get_possible_retreat_nodes()
|
||||
{
|
||||
jump_nodes = GetNodesInRadius( self.origin, 1024, 400, 500, "jump" );
|
||||
return jump_nodes;
|
||||
}
|
||||
|
||||
get_direction_away_from_players()
|
||||
{
|
||||
if ( level.players.size == 0)
|
||||
return self.origin + AnglesToForward( self.angles ) * 100;
|
||||
|
||||
centralLocation = ( 0, 0, 0 );
|
||||
|
||||
foreach ( player in level.players )
|
||||
centralLocation += player.origin;
|
||||
|
||||
centralLocation = centralLocation / level.players.size;
|
||||
|
||||
return self.origin - centralLocation;
|
||||
}
|
||||
|
||||
// Lepers don't attack!
|
||||
leper_attack()
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
get_named_retreat_nodes()
|
||||
{
|
||||
current_area = get_current_area_name();
|
||||
possible_nodes = getnodearray( current_area + "_leper_location","targetname" );
|
||||
if ( isDefined( possible_nodes ) && possible_nodes.size > 0 )
|
||||
return possible_nodes;
|
||||
|
||||
return undefined;
|
||||
}
|
1201
maps/mp/agents/alien/_alien_melee.gsc
Normal file
1201
maps/mp/agents/alien/_alien_melee.gsc
Normal file
File diff suppressed because it is too large
Load Diff
87
maps/mp/agents/alien/_alien_minion.gsc
Normal file
87
maps/mp/agents/alien/_alien_minion.gsc
Normal file
@ -0,0 +1,87 @@
|
||||
#include maps\mp\agents\alien\_alien_think;
|
||||
#include maps\mp\alien\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
ALIEN_MINION_EXPLODE_DISTANCE = 80;
|
||||
ALIEN_MINION_EXPLODE_RADIUS = 200;
|
||||
EXPLODE_ATTACK_START_SOUND = "alien_minion_attack";
|
||||
ALIEN_MINION_CHARGE_SOUND = "alien_minion_alert";
|
||||
ALIEN_MINION_CHATTER_SOUND = "alien_minion_idle";
|
||||
CHATTER_MIN_INTERVAL = 8.0;
|
||||
CHATTER_MAX_INTERVAL = 15.0;
|
||||
|
||||
minion_init()
|
||||
{
|
||||
self thread minion_chatter_monitor();
|
||||
}
|
||||
|
||||
minion_chatter_monitor()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
chatterInterval = RandomFloatRange( CHATTER_MIN_INTERVAL, CHATTER_MAX_INTERVAL );
|
||||
wait chatterInterval;
|
||||
self PlaySoundOnMovingEnt( ALIEN_MINION_CHATTER_SOUND );
|
||||
}
|
||||
}
|
||||
|
||||
minion_approach( enemy, attack_counter )
|
||||
{
|
||||
/# debug_alien_ai_state( "default_approach" ); #/
|
||||
/# debug_alien_attacker_state( "attacking" ); #/
|
||||
|
||||
self.attacking_player = true;
|
||||
self.bypass_max_attacker_counter = false;
|
||||
|
||||
swipe_chance = 0.0; //self.swipeChance;
|
||||
should_swipe = ( RandomFloat( 1.0 ) < swipe_chance );
|
||||
|
||||
if ( should_swipe )
|
||||
{
|
||||
return go_for_swipe( enemy );
|
||||
}
|
||||
|
||||
self PlaySoundOnMovingEnt( ALIEN_MINION_CHARGE_SOUND );
|
||||
approach_node = approach_enemy( ALIEN_MINION_EXPLODE_DISTANCE, enemy, 3 );
|
||||
return "explode";
|
||||
}
|
||||
|
||||
explode_attack( enemy )
|
||||
{
|
||||
/# debug_alien_ai_state( "swipe_melee" ); #/
|
||||
self.melee_type = "explode";
|
||||
alien_melee( enemy );
|
||||
}
|
||||
|
||||
explode( enemy )
|
||||
{
|
||||
self set_alien_emissive( 0.2, 1.0 );
|
||||
self PlaySoundOnMovingEnt( EXPLODE_ATTACK_START_SOUND );
|
||||
playFxOnTag( level._effect[ "alien_minion_preexplode" ], self, "tag_origin" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
|
||||
anim_rate = 1.25;
|
||||
|
||||
self SetAnimState( "minion_explode", 0, anim_rate );
|
||||
wait GetAnimLength( self GetAnimEntry( "minion_explode", 0 ) ) * ( 1 / anim_rate );
|
||||
self Suicide();
|
||||
}
|
||||
|
||||
load_minion_fx()
|
||||
{
|
||||
level._effect[ "alien_minion_explode" ] = Loadfx( "vfx/gameplay/alien/vfx_alien_minion_explode" );
|
||||
level._effect[ "alien_minion_preexplode" ] = loadfx( "vfx/gameplay/alien/vfx_alien_minion_preexplosion");
|
||||
}
|
||||
|
||||
minion_explode_on_death( loc )
|
||||
{
|
||||
waitframe(); //<NOTE J.C.> Prevent script stack overflow
|
||||
|
||||
PlayFx( level._effect[ "alien_minion_explode" ], loc + (0,0,32) );
|
||||
PlaySoundAtPos( loc, "alien_minion_explode" );
|
||||
|
||||
RadiusDamage( loc, ALIEN_MINION_EXPLODE_RADIUS, level.alien_types[ "minion" ].attributes[ "explode_max_damage" ], level.alien_types[ "minion" ].attributes[ "explode_min_damage" ],undefined,"MOD_EXPLOSIVE","alien_minion_explosion" );
|
||||
}
|
959
maps/mp/agents/alien/_alien_move.gsc
Normal file
959
maps/mp/agents/alien/_alien_move.gsc
Normal file
@ -0,0 +1,959 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_utility;
|
||||
|
||||
ALLOW_SLIDE_DIST_SQR = 20.0 * 20.0;
|
||||
DODGE_CHANCE_ENEMY_FACING_TOLERANCE = 0.985; // cos 10, how closely our enemy has to be looking at us
|
||||
DODGE_CHANCE_ALIEN_FACING_TOLERANCE = 0.766; // cos 40, how closely we have to be facing our enemy
|
||||
DODGE_CHANCE_LOOK_AT_TIME_MIN = 1000; // Time our enemy has to look at us before dodging
|
||||
DODGE_CHANCE_LOOK_AT_TIME_MAX = 2000;
|
||||
|
||||
DODGE_CHANCE_MAX_DISTANCE_SQ = 640000.0; // 800.0 * 800.0
|
||||
MIN_DODGE_DIST_SQUARED = 65536.0; // 256 * 256
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self EnterMove();
|
||||
self StartMove();
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
EnterMove()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self.playing_pain_animation = false;
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
|
||||
StartMove()
|
||||
{
|
||||
if ( canDoStartMove())
|
||||
{
|
||||
switch( getStartMoveType() )
|
||||
{
|
||||
case "run-start":
|
||||
self doRunStart();
|
||||
break;
|
||||
case "walk-start":
|
||||
self doWalkStart();
|
||||
break;
|
||||
case "leap-to-run":
|
||||
self doLeapToRunStart();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
self.playing_pain_animation = false;
|
||||
self CancelAllBut( undefined );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self.previousAnimState = "move";
|
||||
}
|
||||
|
||||
SetupMovement()
|
||||
{
|
||||
self.enableStop = true;
|
||||
self thread WaitForMovemodeChange();
|
||||
self thread WaitForJumpSoon();
|
||||
self thread WaitForSharpTurn();
|
||||
self thread WaitForStop();
|
||||
self thread WaitForStuck();
|
||||
|
||||
if ( self canDodge() )
|
||||
{
|
||||
self thread WaitForNearMiss();
|
||||
self thread WaitForDodgeChance();
|
||||
}
|
||||
}
|
||||
|
||||
ContinueMovement()
|
||||
{
|
||||
self SetupMovement();
|
||||
|
||||
// Oriented agents should maintain their orientation
|
||||
if( self.oriented )
|
||||
{
|
||||
forward = self GetLookaheadDir();
|
||||
up = AnglesToUp( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
self ScrAgentSetAnimMode( "code_move_slide" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self ScrAgentSetAnimMode( "code_move" );
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( self.xyanimscale, 1.0 );
|
||||
self SetMoveAnim( self.moveMode );
|
||||
}
|
||||
|
||||
WaitForMovemodeChange()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_runwalk" );
|
||||
curMovement = self.moveMode;
|
||||
while ( true )
|
||||
{
|
||||
if ( curMovement != self.moveMode )
|
||||
{
|
||||
self SetMoveAnim( self.moveMode );
|
||||
curMovement = self.moveMode;
|
||||
}
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
WaitForSharpTurn()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_sharpturn" );
|
||||
|
||||
self waittill( "path_dir_change", newDir );
|
||||
|
||||
angleIndex = GetAngleIndexFromSelfYaw( newDir );
|
||||
|
||||
if ( angleIndex == 4 ) // 4 means this turn wasn't sharp enough for me to care. (angle ~= 0)
|
||||
{
|
||||
self thread WaitForSharpTurn();
|
||||
return;
|
||||
}
|
||||
|
||||
shouldMoveStraightAhead = !( self should_do_sharp_turn() );
|
||||
|
||||
if ( shouldMoveStraightAhead )
|
||||
angleIndex = 0;
|
||||
|
||||
//Try run-turn
|
||||
animState = "run_turn";
|
||||
turnAnim = self GetAnimEntry( animState, angleIndex );
|
||||
canDoTurn = shouldMoveStraightAhead || CanDoTurnAnim( turnAnim );
|
||||
|
||||
if ( !canDoTurn )
|
||||
{
|
||||
self thread WaitForSharpTurn();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "sharpturn" );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
self.enableStop = false;
|
||||
|
||||
if ( shouldMoveStraightAhead )
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( self GetLookaheadDir() );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, angleIndex, self.moveplaybackrate, animState, "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
self.bLockGoalPos = false;
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForStop()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_stop" );
|
||||
|
||||
self waittill( "stop_soon" );
|
||||
|
||||
if ( !self shouldDoStopAnim() || self.movemode == "walk" )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
goalPos = self GetPathGoalPos();
|
||||
//assert( IsDefined( goalPos ) );
|
||||
|
||||
if ( !isDefined( goalPos ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
meToStop = goalPos - self.origin;
|
||||
finalFaceDir = getStopEndFaceDir( goalPos );
|
||||
|
||||
animState = getStopAnimState();
|
||||
|
||||
if ( self should_move_straight_ahead() )
|
||||
{
|
||||
animIndex = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
animIndex = getStopAnimIndex( animState, finalFaceDir );
|
||||
}
|
||||
|
||||
stopAnim = self GetAnimEntry( animState, animIndex );
|
||||
stopDelta = GetMoveDelta( stopAnim );
|
||||
stopAngleDelta = GetAngleDelta( stopAnim );
|
||||
|
||||
// not enough room left to play the animation. abort. (i'm willing to squish/scale the anim up to 48 units.)
|
||||
if ( Length( meToStop ) + 48 < Length( stopDelta ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
stopData = self GetStopData( goalPos );
|
||||
stopStartPos = self CalcAnimStartPos( stopData.pos, stopData.angles[1], stopDelta, stopAngleDelta );
|
||||
stopStartPosDropped = self DropPosToGround( stopStartPos );
|
||||
|
||||
if ( !IsDefined( stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !self CanMovePointToPoint( stopData.pos, stopStartPosDropped ) )
|
||||
{
|
||||
self thread WaitForStop();
|
||||
return;
|
||||
}
|
||||
|
||||
self CancelAllBut( "stop", "sharpturn" );
|
||||
|
||||
self thread WaitForPathSet( "alienmove_endwait_pathsetwhilestopping", "alienmove_endwait_stop" );
|
||||
|
||||
// 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", VectorToAngles( meToStop ) );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNUntilNotetrack( animState, animIndex, animState, "end" );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
if ( self should_move_straight_ahead() )
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( self GetLookaheadDir() );
|
||||
|
||||
// Make sure we made it
|
||||
goalPos = self GetPathGoalPos();
|
||||
if ( DistanceSquared( self.origin, goalPos ) < ALLOW_SLIDE_DIST_SQR )
|
||||
{
|
||||
// Success
|
||||
self ScrAgentSetAnimMode( "code_move_slide" );
|
||||
self SetAnimState( "idle" ); // if all went well, idle state should kick in without this. if all didn't... cover it up.
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Failure - return to move
|
||||
StartMove();
|
||||
ContinueMovement();
|
||||
}
|
||||
}
|
||||
|
||||
getStopEndFaceDir( goalPos )
|
||||
{
|
||||
if ( isDefined ( self.enemy ) )
|
||||
return ( self.enemy.origin - goalPos );
|
||||
|
||||
return ( goalPos - self.origin );
|
||||
}
|
||||
|
||||
getStopAnimState()
|
||||
{
|
||||
switch( self.movemode )
|
||||
{
|
||||
case "run":
|
||||
case "jog":
|
||||
return "run_stop";
|
||||
case "walk":
|
||||
return "walk_stop";
|
||||
default:
|
||||
AssertMsg( "Trying to get stop animState for unknown movemode: " + self.movemode );
|
||||
}
|
||||
}
|
||||
|
||||
getStopAnimIndex( animState, meToStop )
|
||||
{
|
||||
switch( animState )
|
||||
{
|
||||
case "walk_stop":
|
||||
return 0;
|
||||
case "run_stop":
|
||||
return GetAngleIndexFromSelfYaw( meToStop );
|
||||
}
|
||||
}
|
||||
|
||||
WaitForPathSet( endOnNotify, killParentNotify )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( endOnNotify );
|
||||
|
||||
oldGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
self waittill( "path_set" );
|
||||
|
||||
newGoalPos = self ScrAgentGetGoalPos();
|
||||
|
||||
if ( DistanceSquared( oldGoalPos, newGoalPos ) < 1 )
|
||||
{
|
||||
self thread WaitForPathSet( endOnNotify, killParentNotify );
|
||||
return;
|
||||
}
|
||||
|
||||
self notify( killParentNotify );
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
WaitForJumpSoon()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_jumpsoon" );
|
||||
|
||||
self waittill( "traverse_soon" );
|
||||
self CancelAllBut( "jumpsoon" );
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
|
||||
// Check if alien should do the run-to-leap animations
|
||||
targetVector = endNode.origin - startNode.origin;
|
||||
angleIndex = GetAngleIndexFromSelfYaw( endNode.origin - startNode.origin );
|
||||
if ( !shouldDoLeapArrivalAnim( startNode, angleIndex ) )
|
||||
{
|
||||
self ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if alien can move from anim start position to the start node
|
||||
arrivalAnimState = "jump_launch_arrival";
|
||||
arrivalAnim = self GetAnimEntry( arrivalAnimState, angleIndex );
|
||||
moveDelta = GetMoveDelta( arrivalAnim );
|
||||
angleYawDelta = GetAngleDelta( arrivalAnim );
|
||||
if ( !self CanMovePointToPoint( self.origin, startNode.origin ) && !self.oriented )
|
||||
{
|
||||
self ContinueMovement();
|
||||
return;
|
||||
}
|
||||
|
||||
self thread WaitForPathSet( "alienmove_endwait_pathsetwhilejumping", "alienmove_endwait_jumpsoon" );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
scaleFactors = GetAnimScaleFactors( startNode.origin - self.origin, moveDelta );
|
||||
self ScrAgentSetAnimScale( scaleFactors.xy, scaleFactors.z );
|
||||
self PlayAnimNAtRateUntilNotetrack( arrivalAnimState, angleIndex, self.moveplaybackrate, "jump_launch_arrival", "anim_will_finish" );
|
||||
|
||||
forward = targetVector;
|
||||
up = AnglesToUp( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
if ( isDefined( startNode ) && distanceSquared( self.origin, startNode.origin ) < ALLOW_SLIDE_DIST_SQR || self.oriented )
|
||||
self ScrAgentSetAnimMode( "code_move_slide" ); // hope that we enter the traverse state at some point...
|
||||
else
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
SetMoveAnim( moveMode )
|
||||
{
|
||||
if ( moveMode == "run" )
|
||||
{
|
||||
nEntries = self GetAnimEntryCount( "run" );
|
||||
animWeights = [ /*01*/ 20, /*03*/ 80 ];
|
||||
assert( animWeights.size == nEntries );
|
||||
randIndex = maps\mp\alien\_utility::GetRandomIndex( animWeights );
|
||||
assert( randIndex < nEntries );
|
||||
self SetAnimState( "run", randIndex, self.moveplaybackrate );
|
||||
}
|
||||
else if ( moveMode == "jog" )
|
||||
{
|
||||
self SetAnimState( "jog", undefined, self.moveplaybackrate );
|
||||
}
|
||||
else if ( moveMode == "walk" )
|
||||
{
|
||||
self SetAnimState( "walk", undefined, self.moveplaybackrate );
|
||||
}
|
||||
else
|
||||
{
|
||||
assertmsg( "unimplemented move mode " + moveMode );
|
||||
}
|
||||
}
|
||||
|
||||
CancelAllBut( doNotCancel, doNotCancel2 )
|
||||
{
|
||||
cleanups = [ "runwalk", "sharpturn", "stop", "pathsetwhilestopping", "jumpsoon", "pathsetwhilejumping", "pathset", "nearmiss", "dodgechance", "stuck" ];
|
||||
|
||||
bCheckDoNotCancel = IsDefined( doNotCancel );
|
||||
bCheckDoNotCancel2 = IsDefined( doNotCancel2 );
|
||||
|
||||
foreach ( cleanup in cleanups )
|
||||
{
|
||||
if ( bCheckDoNotCancel && cleanup == doNotCancel )
|
||||
continue;
|
||||
if ( bCheckDoNotCancel2 && cleanup == doNotCancel2 )
|
||||
continue;
|
||||
self notify( "alienmove_endwait_" + cleanup );
|
||||
}
|
||||
}
|
||||
|
||||
GetStopData( goalPos )
|
||||
{
|
||||
stopData = SpawnStruct();
|
||||
|
||||
if ( IsDefined( self.node ) )
|
||||
{
|
||||
stopData.pos = self.node.origin;
|
||||
stopData.angles = self.node.angles;
|
||||
}
|
||||
else if ( isDefined( self.enemy ) )
|
||||
{
|
||||
stopData.pos = goalPos;
|
||||
stopData.angles = vectorToAngles( self.enemy.origin - goalPos );
|
||||
}
|
||||
else
|
||||
{
|
||||
stopData.pos = goalPos;
|
||||
stopData.angles = self.angles;
|
||||
}
|
||||
|
||||
return stopData;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
||||
onFlashbanged()
|
||||
{
|
||||
self DoStumble();
|
||||
}
|
||||
|
||||
onDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if ( IsDefined( level.dlc_can_do_pain_override_func ) )
|
||||
{
|
||||
painAllowed = [[level.dlc_can_do_pain_override_func]]( "move" );
|
||||
if ( !painAllowed )
|
||||
return;
|
||||
}
|
||||
|
||||
if ( maps\mp\alien\_utility::is_pain_available( eAttacker,sMeansOfDeath ) )
|
||||
self DoStumble( iDFlags, vDir, sHitLoc, iDamage, sMeansOfDeath, eAttacker );
|
||||
}
|
||||
|
||||
DoStumble( iDFlags, damageDirection, hitLocation, iDamage, sMeansOfDeath, eAttacker )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
if ( self.playing_pain_animation )
|
||||
return;
|
||||
|
||||
self CancelAllBut( undefined );
|
||||
self.stateLocked = true;
|
||||
self.playing_pain_animation = true;
|
||||
|
||||
is_stun = ( iDFlags & level.iDFLAGS_STUN );
|
||||
|
||||
if ( sMeansOfDeath == "MOD_MELEE" || is_stun )
|
||||
{
|
||||
animState = "pain_pushback";
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "push_back", damageDirection );
|
||||
pain_notify = "pain_pushback";
|
||||
}
|
||||
else
|
||||
{
|
||||
animState = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( "run_stumble", iDamage, is_stun );
|
||||
animIndex = maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "run", damageDirection, hitLocation );
|
||||
pain_notify = "run_stumble";
|
||||
}
|
||||
|
||||
anime = self GetAnimEntry( animState, animIndex );
|
||||
self maps\mp\alien\_utility::always_play_pain_sound( anime );
|
||||
self maps\mp\alien\_utility::register_pain( anime );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, animIndex, self.movePlaybackRate, pain_notify, "code_move" );
|
||||
|
||||
self.playing_pain_animation = false;
|
||||
self.stateLocked = false;
|
||||
|
||||
if ( shouldStartMove() )
|
||||
self StartMove();
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
|
||||
WaitForNearMiss( enemy )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_nearmiss" );
|
||||
|
||||
DODGE_CHANCE = 0.5;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill_any( "bulletwhizby", "damage" );
|
||||
|
||||
if( RandomFloat( 1.0 ) < DODGE_CHANCE )
|
||||
continue;
|
||||
|
||||
if ( !self.playing_pain_animation )
|
||||
{
|
||||
DoDodge();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WaitForDodgeChance()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_dodgechance" );
|
||||
|
||||
currentLookAtDuration = 0.0;
|
||||
currentDodgeTime = RandomIntRange( DODGE_CHANCE_LOOK_AT_TIME_MIN, DODGE_CHANCE_LOOK_AT_TIME_MAX );
|
||||
lastTimeStamp = GetTime();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
wait 0.1;
|
||||
|
||||
if ( IsAlive( self.enemy ) )
|
||||
{
|
||||
currentTime = GetTime();
|
||||
enemyToMe = VectorNormalize( self.origin - self.enemy.origin );
|
||||
enemyFacing = AnglesToForward( self.enemy.angles );
|
||||
|
||||
// Fail if enemy isn't looking at us
|
||||
if ( VectorDot( enemytoMe, enemyFacing ) < DODGE_CHANCE_ENEMY_FACING_TOLERANCE )
|
||||
{
|
||||
currentLookAtDuration = 0.0;
|
||||
continue;
|
||||
}
|
||||
|
||||
currentLookAtDuration += currentTime - lastTimeStamp;
|
||||
|
||||
// Fail if enemy is too far away
|
||||
if ( DistanceSquared( self.origin, self.enemy.origin ) > DODGE_CHANCE_MAX_DISTANCE_SQ )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Fail if we're not navigating towards the enemy
|
||||
meToEnemy = enemyToMe * -1.0;
|
||||
myFacing = AnglesToForward( self.angles );
|
||||
if ( VectorDot( meToEnemy, myFacing ) < DODGE_CHANCE_ALIEN_FACING_TOLERANCE )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( currentLookAtDuration >= currentDodgeTime && !self.playing_pain_animation )
|
||||
{
|
||||
DoDodge( "dodgechance" );
|
||||
currentLookAtDuration = 0.0;
|
||||
currentDodgeTime = RandomIntRange( DODGE_CHANCE_LOOK_AT_TIME_MIN, DODGE_CHANCE_LOOK_AT_TIME_MAX );
|
||||
}
|
||||
|
||||
lastTimeStamp = currentTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
canDodge()
|
||||
{
|
||||
switch( self maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "elite":
|
||||
case "mammoth":
|
||||
case "spitter":
|
||||
case "seeder":
|
||||
return false;
|
||||
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DoDodge( endwait )
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
DODGE_FREQUENCY = 1000; // 1 second
|
||||
|
||||
if( IsDefined( self.last_dodge_time ) && GetTime() - self.last_dodge_time < DODGE_FREQUENCY )
|
||||
return;
|
||||
|
||||
if ( IsAlive( self.enemy ) && DistanceSquared( self.origin, self.enemy.origin ) < MIN_DODGE_DIST_SQUARED )
|
||||
return;
|
||||
|
||||
primary_dodge_anim_state = get_primary_dodge_anim_state();
|
||||
|
||||
if ( cointoss() )
|
||||
{
|
||||
if ( !TryDodge( primary_dodge_anim_state + "_left", endwait ) )
|
||||
TryDodge( primary_dodge_anim_state + "_right", endwait );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !TryDodge( primary_dodge_anim_state + "_right", endwait ) )
|
||||
TryDodge( primary_dodge_anim_state + "_left", endwait );
|
||||
}
|
||||
}
|
||||
|
||||
get_primary_dodge_anim_state()
|
||||
{
|
||||
switch( self.movemode )
|
||||
{
|
||||
case "jog":
|
||||
return "jog_dodge";
|
||||
|
||||
default:
|
||||
return "run_dodge";
|
||||
}
|
||||
}
|
||||
|
||||
TryDodge( dodgeState, endwait )
|
||||
{
|
||||
MIN_DODGE_SCALE = 0.5;
|
||||
|
||||
dodgeEntry = self GetRandomAnimEntry( dodgeState );
|
||||
dodgeAnim = self GetAnimEntry( dodgeState, dodgeEntry );
|
||||
moveScale = GetSafeAnimMoveDeltaPercentage( dodgeAnim );
|
||||
moveScale = min( moveScale, self.xyanimscale );
|
||||
|
||||
if ( moveScale < MIN_DODGE_SCALE )
|
||||
return false;
|
||||
|
||||
self.last_dodge_time = GetTime();
|
||||
|
||||
self CancelAllBut( endwait );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
self ScrAgentSetAnimScale( moveScale, 1.0 );
|
||||
self PlayAnimUntilNotetrack( dodgeState, dodgeState, "end" );
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self ContinueMovement();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
WaitForStuck()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_stuck" );
|
||||
|
||||
STUCK_DURATION = 2000.0;
|
||||
nextStuckTime = GetTime() + STUCK_DURATION;
|
||||
lastPos = self.origin;
|
||||
STUCK_TOLERANCE = 1.0;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
currentTime = GetTime();
|
||||
LastDistance = Length( self.origin - lastPos );
|
||||
if ( LastDistance > STUCK_TOLERANCE )
|
||||
nextStuckTime = currentTime + STUCK_DURATION;
|
||||
|
||||
if ( nextStuckTime <= currentTime )
|
||||
{
|
||||
stuckLerp();
|
||||
nextStuckTime = currentTime + STUCK_DURATION;
|
||||
break;
|
||||
}
|
||||
|
||||
lastPos = self.origin;
|
||||
wait 0.1;
|
||||
}
|
||||
|
||||
self ContinueMovement();
|
||||
}
|
||||
|
||||
stuckLerp()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
self endon( "alienmove_endwait_stuck" );
|
||||
self endon( "death" );
|
||||
|
||||
LERP_TIME = 0.2;
|
||||
|
||||
CancelAllBut( "stuck" );
|
||||
|
||||
currentAnim = self GetAnimEntry();
|
||||
currentAnimLength = GetAnimLength( currentAnim );
|
||||
currentAnimDistance = Length( GetMoveDelta( currentAnim ) );
|
||||
lerpDistance = ( LERP_TIME / currentAnimLength ) * currentAnimDistance;
|
||||
|
||||
lerpDirection = self GetLookaheadDir();
|
||||
endPos = self.origin + lerpDirection * lerpDistance;
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", VectorToAngles( lerpDirection ) );
|
||||
self ScrAgentDoAnimLerp( self.origin, endPos, LERP_TIME );
|
||||
wait LERP_TIME;
|
||||
|
||||
self SetOrigin( self.origin );
|
||||
}
|
||||
|
||||
doWalkStart()
|
||||
{
|
||||
animState = "walk_start";
|
||||
animIndex = GetRandomAnimEntry( animState );
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, animIndex, self.movePlaybackRate, animState, "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
}
|
||||
|
||||
doRunStart()
|
||||
{
|
||||
negStartNode = self GetNegotiationStartNode();
|
||||
if ( IsDefined( negStartNode ) )
|
||||
goalPos = negStartNode.origin;
|
||||
else
|
||||
goalPos = self GetPathGoalPos();
|
||||
|
||||
// GetPathGoalPos will return undefined if i don't have a path
|
||||
if ( !IsDefined( goalPos ) )
|
||||
return;
|
||||
|
||||
// don't play start if i have no room for the start.
|
||||
if ( DistanceSquared( goalPos, self.origin ) < 100 * 100 )
|
||||
return;
|
||||
|
||||
lookaheadDir = self GetLookaheadDir();
|
||||
|
||||
myVelocity = self GetVelocity();
|
||||
if ( LengthSquared( myVelocity ) > 16 )
|
||||
{
|
||||
// don't need a start if i'm wallrunning and about to turn a corner onto another plane.
|
||||
myUp = AnglesToUp( self.angles );
|
||||
if ( VectorDot( myUp, (0,0,1) ) < 0.707 )
|
||||
{
|
||||
angleCos = VectorDot( myUp, lookaheadDir );
|
||||
if ( angleCos > 0.707 || angleCos < -0.707 )
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self doStartMoveAnim( "run_start" );
|
||||
}
|
||||
|
||||
doLeapToRunStart()
|
||||
{
|
||||
self doStartMoveAnim( "leap_to_run_start" );
|
||||
}
|
||||
|
||||
should_move_straight_ahead()
|
||||
{
|
||||
switch ( self maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "spitter":
|
||||
case "seeder":
|
||||
case "minion":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
should_do_sharp_turn()
|
||||
{
|
||||
switch ( self maps\mp\alien\_utility::get_alien_type() )
|
||||
{
|
||||
case "spitter":
|
||||
case "seeder":
|
||||
case "minion":
|
||||
case "elite":
|
||||
case "mammoth":
|
||||
return false;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
doStartMoveAnim( animState )
|
||||
{
|
||||
if ( self should_move_straight_ahead() )
|
||||
{
|
||||
angleIndex = 0;
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( self GetLookaheadDir() );
|
||||
}
|
||||
else
|
||||
{
|
||||
angleIndex = getStartMoveAngleIndex();
|
||||
}
|
||||
|
||||
// JohnW: Disabling trace check - mostly redundant from sharpturn traces in code
|
||||
//startAnim = self GetAnimEntry( animState, angleIndex );
|
||||
//startAnimTranslation = GetMoveDelta( startAnim );
|
||||
//endPos = RotateVector( startAnimTranslation, self.angles ) + self.origin;
|
||||
//if ( !self CanMovePointToPoint( self.origin, endPos ) )
|
||||
//return;
|
||||
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
self PlayAnimNAtRateUntilNotetrack( animState, angleIndex, self.movePlaybackRate, animState, "code_move" );
|
||||
self ScrAgentSetOrientMode( "face motion" );
|
||||
|
||||
self.bLockGoalPos = false;
|
||||
|
||||
}
|
||||
|
||||
canDoStartMove()
|
||||
{
|
||||
if ( !isdefined( self.traverseComplete )
|
||||
&& !isdefined( self.skipStartMove )
|
||||
&& ( !isdefined( self.disableExits ) || self.disableExits == false ))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
getStartMoveType()
|
||||
{
|
||||
previousAnimState = self.previousAnimState;
|
||||
switch( previousAnimState )
|
||||
{
|
||||
case "traverse_jump":
|
||||
return "leap-to-run";
|
||||
default:
|
||||
switch( self.movemode )
|
||||
{
|
||||
case "run":
|
||||
return "run-start";
|
||||
case "walk":
|
||||
return "walk-start";
|
||||
default:
|
||||
return "run-start";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shouldDoStopAnim()
|
||||
{
|
||||
return ( isDefined( self.enableStop ) && self.enableStop == true );
|
||||
}
|
||||
|
||||
shouldDoLeapArrivalAnim( startNode, angleIndex )
|
||||
{
|
||||
if ( startNode.type == "Jump" || startNode.type == "Jump Attack" ) // For jump nodes, always do run-to-leap animation
|
||||
return true;
|
||||
else if ( traversalStartFromIdle( startNode.animscript ) ) // If the traversal animation starts at the idle position, need to play run-to-leap
|
||||
return true;
|
||||
else if ( incomingAngleStraightAhead( self maps\mp\alien\_utility::get_alien_type(), angleIndex ) ) // For other traversals, do not play when the incoming angle is either 45 or 0
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
incomingAngleStraightAhead( alienType, angleIndex )
|
||||
{
|
||||
switch( alienType )
|
||||
{
|
||||
case "elite":
|
||||
case "mammoth":
|
||||
return ( angleIndex == 4 );
|
||||
|
||||
default:
|
||||
return ( angleIndex == 3 || angleIndex == 4 || angleIndex == 5 );
|
||||
}
|
||||
}
|
||||
|
||||
traversalStartFromIdle( anim_script )
|
||||
{
|
||||
switch( anim_script )
|
||||
{
|
||||
case "alien_climb_up":
|
||||
case "alien_climb_up_over_56":
|
||||
case "climb_up_end_jump_side_l":
|
||||
case "climb_up_end_jump_side_r":
|
||||
case "alien_climb_up_ledge_18_run":
|
||||
case "alien_climb_up_ledge_18_idle":
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
CanDoTurnAnim( turnAnim )
|
||||
{
|
||||
HEIGHT_OFFSET = 16;
|
||||
RADIUS_OFFSET = 10;
|
||||
HEIGHT_OFFSET_COOR = ( 0, 0, 16 );
|
||||
|
||||
if ( !IsDefined( self GetPathGoalPos()) )
|
||||
return false;
|
||||
|
||||
assert( isDefined( turnAnim ));
|
||||
|
||||
codeMoveTimes = GetNotetrackTimes( turnAnim, "code_move" );
|
||||
assert( codeMoveTimes.size == 1 );
|
||||
|
||||
codeMoveTime = codeMoveTimes[ 0 ];
|
||||
assert( codeMoveTime <= 1 );
|
||||
|
||||
moveDelta = GetMoveDelta( turnAnim, 0, codeMoveTime );
|
||||
codeMovePoint = self LocalToWorldCoords( moveDelta );
|
||||
codeMovePoint = GetGroundPosition( codeMovePoint, self.radius );
|
||||
if ( !isDefined( codeMovePoint ) )
|
||||
return false;
|
||||
|
||||
trace_passed = self AIPhysicsTracePassed( self.origin + HEIGHT_OFFSET_COOR, codeMovePoint + HEIGHT_OFFSET_COOR, self.radius - RADIUS_OFFSET, self.height - HEIGHT_OFFSET );
|
||||
if ( trace_passed )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
shouldStartMove()
|
||||
{
|
||||
angleIndex = getStartMoveAngleIndex();
|
||||
|
||||
return ( angleIndex < 3 || angleIndex > 5 ); //We do not want to do start move if the look ahead direction is straight ahead or 45 degree to either side
|
||||
}
|
||||
|
||||
getStartMoveAngleIndex()
|
||||
{
|
||||
return GetAngleIndexFromSelfYaw( self GetLookaheadDir() );
|
||||
}
|
842
maps/mp/agents/alien/_alien_spitter.gsc
Normal file
842
maps/mp/agents/alien/_alien_spitter.gsc
Normal file
@ -0,0 +1,842 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\alien\_utility;
|
||||
|
||||
ALIEN_SPIT_ATTACK_DISTANCE_MAX_SQ = 1440000; // 1200 * 1200
|
||||
ALIEN_ESCAPE_SPIT_ATTACK_DISTANCE_MAX_SQ = 3240000; // 1800 * 1800
|
||||
MIN_SPIT_TIMES = 3;
|
||||
MAX_SPIT_TIMES = 6;
|
||||
|
||||
SPITTER_NODE_DURATION = 10; // Max length of time they stay at one spit node
|
||||
SPITTER_FIRE_INTERVAL_MIN = 1.5; // Min amount of time in between a projectile fire
|
||||
SPITTER_FIRE_INTERVAL_MAX = 3.0; // Max amount of time in between a projectile fire
|
||||
SPITTER_PROJECTILE_BARRAGE_SIZE_MIN = 2; // Number of small projectiles to shoot at a time when not shooting a gas cloud
|
||||
SPITTER_PROJECTILE_BARRAGE_SIZE_MAX = 3; // Number of small projectiles to shoot at a time when not shooting a gas cloud
|
||||
|
||||
SPITTER_GAS_CLOUD_FIRE_INTERVAL_MIN = 10.0; // Min amount of time in between a gas cloud projectile fire
|
||||
SPITTER_GAS_CLOUD_FIRE_INTERVAL_MAX = 15.0; // Max amount of time in between a gas cloud projectile fire
|
||||
SPITTER_GAS_CLOUD_MAX_COUNT = 3; // Max number of active gas clouds in a level
|
||||
|
||||
SPITTER_NODE_DAMAGE_DELAY = 0.1; // How long they wait to move from a spit node after getting damaged
|
||||
SPITTER_MIN_PLAYER_DISTANCE_SQ = 90000.0; // If player gets within 300 units, they'll move to a new spit node
|
||||
SPITTER_MOVE_MIN_PLAYER_DISTANCE_SQ = 40000.0; // If player gets within 200 units while alien is moving, they'll stop and spit a projectile at them
|
||||
SPITTER_NODE_INITIAL_FIRE_DELAY_SCALE = 0.5; // Initial scale on delay before a spitter can spit after getting to a node
|
||||
SPITTER_NO_TARGET_NODE_MOVE_TIME = 1.0; // If no targets at current node for this time, spitter will move
|
||||
|
||||
SPITTER_AOE_HEIGHT = 128; // Height of gas cloud
|
||||
SPITTER_AOE_RADIUS = 150; // Radius of gas cloud
|
||||
SPITTER_AOE_DURATION = 10.0; // How long the gas cloud lasts
|
||||
SPITTER_AOE_DELAY = 2.0; // How long after projectile explodes before gas cloud damage is applied
|
||||
SPITTER_AOE_DAMAGE_PER_SECOND = 12.0; // Damage per second at center of gas cloud
|
||||
|
||||
SPITTER_TIME_BETWEEN_SPITS = 3.33; // SPITTER_AOE_DURATION / SPITTER_GAS_CLOUD_MAX_COUNT
|
||||
|
||||
SPITTER_LOOK_AHEAD_PERCENTAGE = 0.5; // how accurately the spitters lead the players
|
||||
SPITTER_ESCAPE_LOOK_AHEAD_PERCENTAGE = 1.0; // how accurately the spitters lead the players during escape sequence
|
||||
|
||||
load_spitter_fx()
|
||||
{
|
||||
level._effect[ "spit_AOE" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_spitter_gas_cloud" );
|
||||
level._effect[ "spit_AOE_small" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_spitter_gas_cloud_64" );
|
||||
}
|
||||
|
||||
spitter_init()
|
||||
{
|
||||
self.gas_cloud_available = true;
|
||||
}
|
||||
|
||||
spitter_death()
|
||||
{
|
||||
release_spit_node();
|
||||
}
|
||||
|
||||
is_escape_sequence_active()
|
||||
{
|
||||
return ( flag_exist( "hives_cleared" ) && flag( "hives_cleared" ) );
|
||||
}
|
||||
|
||||
get_max_spit_distance_squared()
|
||||
{
|
||||
if ( is_escape_sequence_active() )
|
||||
return ALIEN_ESCAPE_SPIT_ATTACK_DISTANCE_MAX_SQ;
|
||||
|
||||
return ALIEN_SPIT_ATTACK_DISTANCE_MAX_SQ;
|
||||
}
|
||||
|
||||
get_lookahead_percentage()
|
||||
{
|
||||
if ( is_escape_sequence_active() )
|
||||
return SPITTER_ESCAPE_LOOK_AHEAD_PERCENTAGE;
|
||||
|
||||
return SPITTER_LOOK_AHEAD_PERCENTAGE;
|
||||
}
|
||||
|
||||
spit_projectile( enemy )
|
||||
{
|
||||
if ( self.spit_type == "gas_cloud" )
|
||||
{
|
||||
level.spitter_last_cloud_time = gettime();
|
||||
}
|
||||
|
||||
self.melee_type = "spit";
|
||||
|
||||
self.spit_target = enemy;
|
||||
maps\mp\agents\alien\_alien_think::alien_melee( enemy );
|
||||
}
|
||||
|
||||
spit_attack( enemy )
|
||||
{
|
||||
/# maps\mp\agents\alien\_alien_think::debug_alien_ai_state( "spit_attack" ); #/
|
||||
self endon( "melee_pain_interrupt" );
|
||||
isEnemyChopper = isdefined( enemy ) && isdefined( enemy.code_classname ) && enemy.code_classname == "script_vehicle";
|
||||
|
||||
if ( isEnemyChopper )
|
||||
targetedEnemy = enemy;
|
||||
else
|
||||
targetedEnemy = self.spit_target;
|
||||
|
||||
targetedEnemy endon( "death" );
|
||||
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsEntity( targetedEnemy );
|
||||
|
||||
if ( IsAlive( targetedEnemy ) )
|
||||
{
|
||||
self.spit_target = targetedEnemy;
|
||||
|
||||
if ( isEnemyChopper )
|
||||
{
|
||||
aim_ahead_factor = 5; // factor of speed MPH
|
||||
aim_ahead_unit_vec = VectorNormalize( AnglesToForward( targetedEnemy.angles ) ); // direction
|
||||
aim_ahead_speed_mag = Length( targetedEnemy Vehicle_GetVelocity() ) * aim_ahead_factor; // scaler
|
||||
aim_ahead_vec = aim_ahead_unit_vec * aim_ahead_speed_mag; // aim ahead offset vector
|
||||
|
||||
self.spit_target_location = targetedEnemy.origin + aim_ahead_vec + ( 0, 0, 32 ); // offset by 32 down from origin as origin is at rotor
|
||||
}
|
||||
else
|
||||
{
|
||||
self.spit_target_location = targetedEnemy.origin;
|
||||
}
|
||||
|
||||
self.looktarget = targetedEnemy;
|
||||
|
||||
self set_alien_emissive( 0.2, 1.0 );
|
||||
|
||||
if ( IsDefined ( self.current_spit_node ) && !maps\mp\alien\_utility::is_normal_upright( AnglesToUp( self.current_spit_node.angles ) ) )
|
||||
{
|
||||
up = AnglesToUp( self.current_spit_node.angles );
|
||||
forward = AnglesToForward( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
}
|
||||
else if ( IsDefined( self.enemy ) && targetedEnemy == self.enemy )
|
||||
{
|
||||
self ScrAgentSetOrientMode( "face enemy" );
|
||||
}
|
||||
else
|
||||
{
|
||||
forward = VectorNormalize( targetedEnemy.origin - self.origin );
|
||||
if ( IsDefined( self.current_spit_node ) )
|
||||
up = AnglesToUp( self.current_spit_node.angles );
|
||||
else
|
||||
up = AnglesToUp( self.angles );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
anglesToFace = AxisToAngles( forward, right, up );
|
||||
self ScrAgentSetOrientMode( "face angle abs", anglesToFace );
|
||||
}
|
||||
|
||||
if( self.oriented )
|
||||
self ScrAgentSetAnimMode( "anim angle delta" );
|
||||
else
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
play_spit_anim();
|
||||
}
|
||||
|
||||
self set_alien_emissive_default( 0.2 );
|
||||
self.looktarget = undefined;
|
||||
self.spit_target = undefined;
|
||||
self.spit_target_location = undefined;
|
||||
self.spit_type = undefined;
|
||||
}
|
||||
|
||||
play_spit_anim()
|
||||
{
|
||||
switch ( self.spit_type )
|
||||
{
|
||||
case "close_range":
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "close_spit_attack", "spit_attack", "end", ::handleAttackNotetracks );
|
||||
break;
|
||||
case "gas_cloud":
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "gas_spit_attack", "spit_attack", "end", ::handleAttackNotetracks );
|
||||
break;
|
||||
case "long_range":
|
||||
barrage_count = RandomIntRange( SPITTER_PROJECTILE_BARRAGE_SIZE_MIN, SPITTER_PROJECTILE_BARRAGE_SIZE_MAX );
|
||||
for ( spitIndex = 0; spitIndex < barrage_count; spitIndex++ )
|
||||
self maps\mp\agents\_scriptedagents::PlayAnimUntilNotetrack( "long_range_spit_attack", "spit_attack", "end", ::handleAttackNotetracks );
|
||||
break;
|
||||
default:
|
||||
AssertMsg( self.spit_type + " is an invalid spit type!" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
get_best_spit_target( targeted_enemy )
|
||||
{
|
||||
if ( cointoss() && get_alien_type() != "seeder" )
|
||||
{
|
||||
griefTargets = get_grief_targets();
|
||||
|
||||
foreach ( griefTarget in griefTargets )
|
||||
{
|
||||
if ( is_valid_spit_target( griefTarget, false ) )
|
||||
return griefTarget;
|
||||
}
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
if ( IsDefined( targeted_enemy ) )
|
||||
{
|
||||
if ( IsAlive( targeted_enemy ) && is_valid_spit_target( targeted_enemy, false ) )
|
||||
return targeted_enemy;
|
||||
}
|
||||
|
||||
possibleTargets = self get_current_possible_targets();
|
||||
MAX_TARGET_TESTS_PER_FRAME = 4;
|
||||
currentTestsThisFrame = 0;
|
||||
|
||||
foreach ( possibleTarget in possibleTargets )
|
||||
{
|
||||
if ( !IsAlive( possibleTarget ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( targeted_enemy ) && possibleTarget == targeted_enemy )
|
||||
continue;
|
||||
|
||||
if ( is_valid_spit_target( possibleTarget, true ) )
|
||||
return possibleTarget;
|
||||
|
||||
currentTestsThisFrame++;
|
||||
if ( currentTestsThisFrame >= MAX_TARGET_TESTS_PER_FRAME )
|
||||
{
|
||||
waitframe();
|
||||
currentTestsThisFrame = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
get_grief_targets()
|
||||
{
|
||||
griefTargets = [];
|
||||
if ( !can_spit_gas_cloud( ) || is_pet())
|
||||
return griefTargets;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( !IsAlive( player ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( player.inLastStand ) && player.inLastStand )
|
||||
griefTargets[griefTargets.size] = player;
|
||||
}
|
||||
|
||||
if ( IsDefined( level.drill ) && IsDefined( level.drill.state ) && level.drill.state == "offline" )
|
||||
griefTargets[griefTargets.size] = level.drill;
|
||||
|
||||
return array_randomize( griefTargets );
|
||||
}
|
||||
|
||||
is_valid_spit_target( spit_target, check_attacker_values )
|
||||
{
|
||||
if ( !isAlive( spit_target ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( check_attacker_values && IsPlayer( spit_target ) && !has_attacker_space( spit_target ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
maxValidDistanceSq = get_max_spit_distance_squared();
|
||||
|
||||
flatDistanceToTargetSquared = Distance2DSquared( self.origin, spit_target.origin );
|
||||
if ( flatDistanceToTargetSquared > maxValidDistanceSq )
|
||||
return false;
|
||||
|
||||
self.looktarget = spit_target;
|
||||
|
||||
if ( !isAlive( spit_target ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (( isPlayer( spit_target ) || IsSentient( spit_target )) && !IsDefined( spit_target.usingRemote ) )
|
||||
endPos = spit_target getEye();
|
||||
else
|
||||
endPos = spit_target.origin;
|
||||
|
||||
spitFirePos = self GetTagOrigin( "TAG_BREATH" );
|
||||
return BulletTracePassed( spitFirePos, endPos, false, self );
|
||||
}
|
||||
|
||||
get_spit_fire_pos( spit_target )
|
||||
{
|
||||
return self GetTagOrigin( "TAG_BREATH" );
|
||||
}
|
||||
|
||||
has_attacker_space( player )
|
||||
{
|
||||
maxValidAttackerValue = level.maxAlienAttackerDifficultyValue - level.alien_types[ self.alien_type ].attributes[ "attacker_difficulty" ];
|
||||
|
||||
targetedAttackerScore = maps\mp\agents\alien\_alien_think::get_current_attacker_value( player );
|
||||
|
||||
return ( targetedAttackerScore <= maxValidAttackerValue );
|
||||
}
|
||||
|
||||
handleAttackNotetracks( note, animState, animIndex, animTime )
|
||||
{
|
||||
if( isDefined( level.dlc_attacknotetrack_override_func ))
|
||||
{
|
||||
self [[level.dlc_attacknotetrack_override_func]]( note, animState, animIndex, animTime );
|
||||
return;
|
||||
}
|
||||
|
||||
if ( note == "spit" )
|
||||
return self fire_spit_projectile();
|
||||
}
|
||||
|
||||
fire_spit_projectile()
|
||||
{
|
||||
if ( !IsDefined( self.spit_target ) && !IsDefined( self.spit_target_location ) )
|
||||
return;
|
||||
|
||||
hasValidTarget = IsAlive( self.spit_target );
|
||||
isTargetChopper = isdefined( self.spit_target.code_classname ) && self.spit_target.code_classname == "script_vehicle";
|
||||
if ( hasValidTarget && !isTargetChopper )
|
||||
targetLocation = self.spit_target.origin;
|
||||
else
|
||||
targetLocation = self.spit_target_location;
|
||||
|
||||
if ( self.spit_type == "gas_cloud" )
|
||||
{
|
||||
spit_gas_cloud_projectile( targetLocation );
|
||||
}
|
||||
else if ( hasValidTarget )
|
||||
{
|
||||
PROJECTILE_SPEED = 1400;
|
||||
targetLocation = get_lookahead_target_location( PROJECTILE_SPEED, self.spit_target, false );
|
||||
if ( !BulletTracePassed( targetLocation, get_spit_fire_pos( targetLocation ), false, self ) )
|
||||
targetLocation = get_lookahead_target_location( PROJECTILE_SPEED, self.spit_target, true );
|
||||
|
||||
spit_basic_projectile( targetLocation );
|
||||
}
|
||||
}
|
||||
|
||||
get_lookahead_target_location( projectile_speed, target, use_eye_location )
|
||||
{
|
||||
if ( !IsPlayer( target ) )
|
||||
return target.origin;
|
||||
|
||||
lookAheadPercentage = get_lookahead_percentage();
|
||||
|
||||
if ( use_eye_location && !IsDefined( target.usingRemote ))
|
||||
targetLocation = target GetEye();
|
||||
else
|
||||
targetLocation = target.origin;
|
||||
|
||||
distanceToTarget = Distance( self.origin, targetLocation);
|
||||
timeToImpact = distanceToTarget / projectile_speed;
|
||||
targetVelocity = target GetVelocity();
|
||||
|
||||
return targetLocation + targetVelocity * lookAheadPercentage * timeToImpact;
|
||||
}
|
||||
|
||||
can_spit_gas_cloud()
|
||||
{
|
||||
if ( !self.gas_cloud_available )
|
||||
return false;
|
||||
|
||||
if ( isdefined( self.enemy ) && isdefined( self.enemy.no_gas_cloud_attack ) && self.enemy.no_gas_cloud_attack )
|
||||
return false;
|
||||
|
||||
time_since_last_spit = (gettime() - level.spitter_last_cloud_time) * 0.001;
|
||||
|
||||
return level.spitter_gas_cloud_count < SPITTER_GAS_CLOUD_MAX_COUNT && time_since_last_spit > SPITTER_TIME_BETWEEN_SPITS;
|
||||
}
|
||||
|
||||
spit_basic_projectile( targetLocation )
|
||||
{
|
||||
spitFirePos = get_spit_fire_pos( targetLocation );
|
||||
spitProjectile = MagicBullet( "alienspit_mp", spitFirePos, targetLocation, self );
|
||||
spitProjectile.owner = self;
|
||||
|
||||
if ( IsDefined( spitProjectile ) )
|
||||
spitProjectile thread spit_basic_projectile_impact_monitor( self );
|
||||
}
|
||||
|
||||
spit_basic_projectile_impact_monitor( owner )
|
||||
{
|
||||
self waittill( "explode", explodeLocation );
|
||||
|
||||
if ( !IsDefined( explodeLocation ) )
|
||||
return;
|
||||
|
||||
PlayFx( level._effect[ "spit_AOE_small" ], explodeLocation + (0,0,8), (0,0,1), (1,0,0) );
|
||||
}
|
||||
|
||||
spit_gas_cloud_projectile( targetLocation )
|
||||
{
|
||||
spitFirePos = get_spit_fire_pos( targetLocation );
|
||||
spitProjectile = MagicBullet( "alienspit_gas_mp", spitFirePos, targetLocation, self );
|
||||
spitProjectile.owner = self;
|
||||
|
||||
if ( IsDefined( spitProjectile ) )
|
||||
spitProjectile thread spit_gas_cloud_projectile_impact_monitor( self );
|
||||
|
||||
self thread gas_cloud_available_timer();
|
||||
}
|
||||
|
||||
gas_cloud_available_timer()
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self.gas_cloud_available = false;
|
||||
cloudInterval = RandomFloatRange( SPITTER_GAS_CLOUD_FIRE_INTERVAL_MIN, SPITTER_GAS_CLOUD_FIRE_INTERVAL_MAX );
|
||||
wait cloudInterval;
|
||||
self.gas_cloud_available = true;
|
||||
}
|
||||
|
||||
spit_gas_cloud_projectile_impact_monitor( owner )
|
||||
{
|
||||
self waittill( "explode", explodeLocation );
|
||||
|
||||
if ( !IsDefined( explodeLocation ) )
|
||||
return;
|
||||
|
||||
trigger = Spawn( "trigger_radius", explodeLocation, 0, SPITTER_AOE_RADIUS, SPITTER_AOE_HEIGHT );
|
||||
// sanity check. Need to come up with more robust fallback
|
||||
if ( !IsDefined( trigger ) )
|
||||
return;
|
||||
|
||||
level.spitter_gas_cloud_count++;
|
||||
trigger.onPlayer = true;
|
||||
PlayFx( level._effect[ "spit_AOE" ], explodeLocation + (0,0,8),(0,0,1), (1,0,0) );
|
||||
thread spit_aoe_cloud_damage( explodeLocation, trigger );
|
||||
level notify( "spitter_spit",explodeLocation );
|
||||
|
||||
wait SPITTER_AOE_DURATION;
|
||||
trigger Delete();
|
||||
level.spitter_gas_cloud_count--;
|
||||
}
|
||||
|
||||
spit_aoe_cloud_damage( impact_location, trigger )
|
||||
{
|
||||
trigger endon( "death" );
|
||||
|
||||
wait SPITTER_AOE_DELAY;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
trigger waittill( "trigger", player );
|
||||
|
||||
if ( !IsPlayer( player ) )
|
||||
continue;
|
||||
|
||||
if ( !IsAlive( player ) )
|
||||
continue;
|
||||
|
||||
disorient_player( player );
|
||||
damage_player( player, trigger );
|
||||
}
|
||||
}
|
||||
|
||||
damage_player( player, trigger )
|
||||
{
|
||||
DAMAGE_INTERVAL = 0.5;
|
||||
|
||||
currentTime = GetTime();
|
||||
|
||||
if ( !IsDefined( player.last_spitter_gas_damage_time ) )
|
||||
{
|
||||
elapsedTime = DAMAGE_INTERVAL;
|
||||
}
|
||||
else if (player.last_spitter_gas_damage_time + DAMAGE_INTERVAL * 1000.0 > currentTime )
|
||||
{
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
elapsedTime = Min( DAMAGE_INTERVAL, (currentTime - player.last_spitter_gas_damage_time) * 0.001 );
|
||||
}
|
||||
|
||||
gas_damage_scalar = player maps\mp\alien\_perk_utility::perk_GetGasDamageScalar();
|
||||
damageAmount = int(SPITTER_AOE_DAMAGE_PER_SECOND * elapsedTime * gas_damage_scalar );
|
||||
if ( damageAmount > 0 )
|
||||
{
|
||||
player thread [[ level.callbackPlayerDamage ]]( trigger, trigger, damageAmount, 0, "MOD_SUICIDE", "alienspit_gas_mp", trigger.origin, ( 0,0,0 ), "none", 0 );
|
||||
}
|
||||
player.last_spitter_gas_damage_time = currentTime;
|
||||
}
|
||||
|
||||
disorient_player( player )
|
||||
{
|
||||
if ( is_chaos_mode() && player maps\mp\alien\_perk_utility::perk_GetGasDamageScalar() == 0 )
|
||||
return;
|
||||
else if( !player maps\mp\alien\_perk_utility::has_perk( "perk_medic", [ 1,2,3,4 ] ) )
|
||||
{
|
||||
if ( isDefined( level.shell_shock_override ))
|
||||
player [[level.shell_shock_override]]( 0.5 );
|
||||
else
|
||||
player ShellShock( "alien_spitter_gas_cloud", 0.5 );
|
||||
}
|
||||
}
|
||||
|
||||
get_RL_toward( target )
|
||||
{
|
||||
//Return the right/left vector toward target
|
||||
self_to_target_angles = VectorToAngles( target.origin - self.origin );
|
||||
target_direction = anglesToRight( self_to_target_angles );
|
||||
|
||||
if ( common_scripts\utility::cointoss())
|
||||
target_direction *= -1;
|
||||
|
||||
return target_direction;
|
||||
}
|
||||
|
||||
spitter_combat( enemy )
|
||||
{
|
||||
self endon( "bad_path" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
attackNode = find_spitter_attack_node( self.enemy );
|
||||
|
||||
if ( IsDefined( attackNode ) )
|
||||
{
|
||||
move_to_spitter_attack_node( attackNode );
|
||||
spitter_attack( self.enemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
release_spit_node()
|
||||
{
|
||||
if ( IsDefined( self.current_spit_node ) )
|
||||
{
|
||||
self ScrAgentRelinquishClaimedNode( self.current_spit_node );
|
||||
self.current_spit_node.claimed = false;
|
||||
self.current_spit_node = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
claim_spit_node( spit_node )
|
||||
{
|
||||
self.current_spit_node = spit_node;
|
||||
spit_node.claimed = true;
|
||||
self ScrAgentClaimNode( spit_node );
|
||||
}
|
||||
|
||||
move_to_spitter_attack_node( attack_node )
|
||||
{
|
||||
self endon( "player_proximity_during_move" );
|
||||
|
||||
release_spit_node();
|
||||
claim_spit_node( attack_node );
|
||||
|
||||
self ScrAgentSetGoalNode( attack_node );
|
||||
self ScrAgentSetGoalRadius( 64 );
|
||||
self thread enemy_proximity_during_move_monitor();
|
||||
self waittill( "goal_reached" );
|
||||
}
|
||||
|
||||
enemy_proximity_during_move_monitor()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "goal_reached" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
if ( !is_normal_upright( AnglesToUp( self.angles ) ) )
|
||||
continue;
|
||||
|
||||
if ( !self maps\mp\agents\alien\_alien_think::melee_okay() )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( self.valid_moving_spit_attack_time ) && GetTime() < self.valid_moving_spit_attack_time )
|
||||
continue;
|
||||
|
||||
closePlayer = find_player_within_distance( SPITTER_MOVE_MIN_PLAYER_DISTANCE_SQ );
|
||||
if ( IsDefined( closePlayer ) )
|
||||
break;
|
||||
}
|
||||
|
||||
release_spit_node();
|
||||
self notify( "player_proximity_during_move" );
|
||||
self ScrAgentSetGoalEntity( closePlayer );
|
||||
self ScrAgentSetGoalRadius( 2048.0 );
|
||||
self waittill( "goal_reached" );
|
||||
}
|
||||
|
||||
get_possible_spitter_attack_nodes( target_entity )
|
||||
{
|
||||
if( get_alien_type() == "seeder" )
|
||||
attackNodes = GetNodesInRadius( target_entity.origin, 768, 128, 512, "jump attack" );
|
||||
else
|
||||
attackNodes = GetNodesInRadius( target_entity.origin, 1000, 300, 512, "jump attack" );
|
||||
|
||||
validNodes = [];
|
||||
|
||||
foreach( attackNode in attackNodes )
|
||||
{
|
||||
if ( IsDefined( attackNode.claimed) && attackNode.claimed )
|
||||
continue;
|
||||
|
||||
validNodes[validNodes.size] = attackNode;
|
||||
}
|
||||
|
||||
return validNodes;
|
||||
|
||||
}
|
||||
|
||||
is_pet()
|
||||
{
|
||||
return ( IsDefined( self.pet ) && self.pet );
|
||||
}
|
||||
|
||||
get_current_possible_targets()
|
||||
{
|
||||
if ( is_pet() )
|
||||
return level.agentArray;
|
||||
else
|
||||
return level.players;
|
||||
}
|
||||
|
||||
find_spitter_attack_node( target_enemy )
|
||||
{
|
||||
nearbySpitNodes = [];
|
||||
|
||||
if ( is_escape_sequence_active() && IsDefined( level.escape_spitter_target_node ) )
|
||||
{
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( level.escape_spitter_target_node );
|
||||
if ( nearbySpitNodes.size > 0 )
|
||||
target_enemy = level.escape_spitter_target_node;
|
||||
}
|
||||
|
||||
if ( nearbySpitNodes.size == 0 && IsDefined( target_enemy ) )
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( target_enemy );
|
||||
|
||||
if ( nearbySpitNodes.size == 0 )
|
||||
{
|
||||
possibleTargets = self get_current_possible_targets();
|
||||
foreach ( possibleTarget in possibleTargets )
|
||||
{
|
||||
wait 0.05;
|
||||
|
||||
if ( !IsAlive( possibleTarget ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( target_enemy ) && possibleTarget == target_enemy )
|
||||
continue;
|
||||
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( possibleTarget );
|
||||
if ( nearbySpitNodes.size > 0 )
|
||||
{
|
||||
target_enemy = possibleTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( nearbySpitNodes.size == 0 )
|
||||
nearbySpitNodes = get_possible_spitter_attack_nodes( self );
|
||||
|
||||
if ( nearbySpitNodes.size == 0 )
|
||||
return undefined;
|
||||
|
||||
filters = [];
|
||||
|
||||
if ( IsDefined( target_enemy ) )
|
||||
{
|
||||
filters[ "dist_from_enemy_weight" ] = 8.0;
|
||||
filters[ "enemy_los_weight" ] = 6.0;
|
||||
filters[ "height_weight" ] = 4.0;
|
||||
target_direction = get_RL_toward( target_enemy );
|
||||
target_enemy endon( "death" );
|
||||
}
|
||||
else
|
||||
{
|
||||
filters[ "dist_from_enemy_weight" ] = 0.0;
|
||||
filters[ "enemy_los_weight" ] = 0.0;
|
||||
filters[ "height_weight" ] = 0.0;
|
||||
target_direction = get_central_enemies_direction();
|
||||
}
|
||||
|
||||
filters[ "direction" ] = "override";
|
||||
filters[ "direction_override" ] = target_direction;
|
||||
filters[ "direction_weight" ] = 1.0;
|
||||
filters[ "min_height" ] = 64.0;
|
||||
filters[ "max_height" ] = 400.0;
|
||||
filters[ "enemy_los" ] = true;
|
||||
filters[ "min_dist_from_enemy" ] = 300.0;
|
||||
filters[ "max_dist_from_enemy" ] = 800.0;
|
||||
filters[ "desired_dist_from_enemy" ] = 600.0;
|
||||
filters[ "min_dist_from_all_enemies" ] = 300.0;
|
||||
filters[ "min_dist_from_all_enemies_weight" ] = 5.0;
|
||||
filters[ "not_recently_used_weight" ] = 10.0;
|
||||
filters[ "recently_used_time_limit" ] = 30.0;
|
||||
filters[ "random_weight" ] = 1.0;
|
||||
|
||||
result = maps\mp\agents\alien\_alien_think::get_retreat_node_rated( target_enemy, filters, nearbySpitNodes );
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
get_central_enemies_direction()
|
||||
{
|
||||
possibleTargets = self get_current_possible_targets();
|
||||
|
||||
if ( possibleTargets.size == 0)
|
||||
return self.origin + AnglesToForward( self.angles ) * 100;
|
||||
|
||||
centralLocation = ( 0, 0, 0 );
|
||||
|
||||
foreach ( possibleTarget in possibleTargets )
|
||||
centralLocation += possibleTarget.origin;
|
||||
|
||||
centralLocation = centralLocation / possibleTargets.size;
|
||||
|
||||
return centralLocation - self.origin;
|
||||
}
|
||||
|
||||
spitter_attack( enemy )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
|
||||
if ( !IsDefined( self.current_spit_node ) )
|
||||
{
|
||||
choose_spit_type( "close_range" );
|
||||
spit_projectile( enemy );
|
||||
self.valid_moving_spit_attack_time = GetTime() + RandomFloatRange( SPITTER_FIRE_INTERVAL_MIN, SPITTER_FIRE_INTERVAL_MAX ) * 1000.0;
|
||||
return;
|
||||
}
|
||||
|
||||
set_up_attack_node_watchers();
|
||||
|
||||
if ( !is_escape_sequence_active() )
|
||||
wait RandomFloatRange( SPITTER_FIRE_INTERVAL_MIN, SPITTER_FIRE_INTERVAL_MAX ) * SPITTER_NODE_INITIAL_FIRE_DELAY_SCALE;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
targetedEnemy = undefined;
|
||||
no_target_time = 0.0;
|
||||
while ( !IsDefined( targetedEnemy ) )
|
||||
{
|
||||
no_target_time += 0.2;
|
||||
if ( no_target_time >= SPITTER_NO_TARGET_NODE_MOVE_TIME )
|
||||
{
|
||||
return;
|
||||
}
|
||||
wait 0.2;
|
||||
|
||||
if ( IsDefined( enemy ) && IsDefined( enemy.code_classname ) && enemy.code_classname == "script_vehicle" )
|
||||
{
|
||||
targetedEnemy = enemy;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetedEnemy = get_best_spit_target( enemy );
|
||||
}
|
||||
}
|
||||
|
||||
choose_spit_type( "long_range" );
|
||||
spit_projectile( targetedEnemy );
|
||||
wait RandomFloatRange( SPITTER_FIRE_INTERVAL_MIN, SPITTER_FIRE_INTERVAL_MAX );
|
||||
}
|
||||
}
|
||||
|
||||
choose_spit_type( default_type )
|
||||
{
|
||||
if ( !is_pet() && can_spit_gas_cloud() )
|
||||
self.spit_type = "gas_cloud";
|
||||
else
|
||||
self.spit_type = default_type;
|
||||
}
|
||||
|
||||
set_up_attack_node_watchers()
|
||||
{
|
||||
self thread spitter_node_duration_monitor( SPITTER_NODE_DURATION );
|
||||
self thread spitter_node_attacked_monitor( SPITTER_NODE_DAMAGE_DELAY );
|
||||
|
||||
if ( !is_pet() )
|
||||
self thread spitter_node_player_proximity( SPITTER_MIN_PLAYER_DISTANCE_SQ );
|
||||
}
|
||||
|
||||
spitter_node_duration_monitor( duration )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
wait duration;
|
||||
|
||||
self notify( "spitter_node_move_requested" );
|
||||
}
|
||||
|
||||
spitter_node_attacked_monitor( damage_delay )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
self waittill( "damage" );
|
||||
wait damage_delay;
|
||||
|
||||
self notify( "spitter_node_move_requested" );
|
||||
}
|
||||
|
||||
spitter_node_player_proximity( min_player_distances_sq )
|
||||
{
|
||||
self endon( "spitter_node_move_requested" );
|
||||
self endon( "death" );
|
||||
self endon ( "alien_main_loop_restart" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
closePlayer = find_player_within_distance( min_player_distances_sq );
|
||||
if ( IsDefined( closePlayer ) )
|
||||
break;
|
||||
|
||||
wait 0.2;
|
||||
}
|
||||
|
||||
self notify( "spitter_node_move_requested" );
|
||||
}
|
||||
|
||||
find_player_within_distance( distance_sq )
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( !IsAlive( player ) )
|
||||
continue;
|
||||
|
||||
flatDistanceToPlayerSq = Distance2DSquared( self.origin, player.origin );
|
||||
if ( flatDistanceToPlayerSq < distance_sq )
|
||||
return player;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
2949
maps/mp/agents/alien/_alien_think.gsc
Normal file
2949
maps/mp/agents/alien/_alien_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
681
maps/mp/agents/alien/_alien_traverse.gsc
Normal file
681
maps/mp/agents/alien/_alien_traverse.gsc
Normal file
@ -0,0 +1,681 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
self.bLockGoalPos = true;
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
assert( IsDefined( startNode ) && IsDefined( endNode ) );
|
||||
|
||||
if ( startNode.type == "Jump" || startNode.type == "Jump Attack" )
|
||||
{
|
||||
nextNode = self GetNegotiationNextNode();
|
||||
|
||||
if ( IsDefined( startNode.target ) && IsDefined( endNode.targetname ) && startNode.target == endNode.targetname )
|
||||
{
|
||||
self.traverseType = "canned";
|
||||
self DoTraverse( startNode, endNode );
|
||||
return;
|
||||
}
|
||||
|
||||
attackableEnemy = find_attackable_enemy_at_node( endNode );
|
||||
if ( IsDefined( attackableEnemy ) )
|
||||
{
|
||||
self.traverseType = "jump_attack";
|
||||
self.leapEndPos = endNode.origin;
|
||||
|
||||
self maps\mp\agents\alien\_alien_melee::melee_leap( attackableEnemy );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.traverseType = "jump";
|
||||
self Jump( startNode, endNode, nextNode );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self.traverseType = "canned";
|
||||
self doTraverse( startNode, endNode );
|
||||
}
|
||||
}
|
||||
|
||||
find_attackable_enemy_at_node( nodeToCheck )
|
||||
{
|
||||
if (( self maps\mp\alien\_utility::get_alien_type() == "spitter" ) ||
|
||||
( self maps\mp\alien\_utility::get_alien_type() == "seeder" ))
|
||||
return undefined;
|
||||
|
||||
CLOSE_PLAYER_DIST_SQ = 128 * 128;
|
||||
COS_45 = 0.707;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( DistanceSquared( player.origin, nodeToCheck.origin ) > CLOSE_PLAYER_DIST_SQ )
|
||||
continue;
|
||||
|
||||
playerToNode = VectorNormalize( nodeToCheck.origin - player.origin );
|
||||
playerForward = AnglesToForward( player.angles );
|
||||
forwardDot = VectorDot( playerToNode, playerForward );
|
||||
|
||||
if ( forwardDot > COS_45 )
|
||||
return player;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
end_script()
|
||||
{
|
||||
self.bLockGoalPos = false;
|
||||
if ( self.traverseType == "jump" )
|
||||
{
|
||||
self.previousAnimState = "traverse_jump";
|
||||
}
|
||||
else if ( self.traverseType == "jump_attack" )
|
||||
{
|
||||
self.previousAnimState = "traverse_jump_attack";
|
||||
}
|
||||
else
|
||||
{
|
||||
self.previousAnimState = "traverse_canned";
|
||||
}
|
||||
self.traverseType = undefined;
|
||||
}
|
||||
|
||||
|
||||
Jump( startNode, endNode, nextNode )
|
||||
{
|
||||
nextPos = undefined;
|
||||
if ( IsDefined( nextNode ) )
|
||||
nextPos = nextNode.origin;
|
||||
|
||||
if ( isDefined( level.dlc_alien_jump_override ) )
|
||||
{
|
||||
[[level.dlc_alien_jump_override]]( startNode, endNode, nextNode, nextPos );
|
||||
return;
|
||||
}
|
||||
|
||||
self maps\mp\agents\alien\_alien_jump::Jump( startNode.origin, startNode.angles, endNode.origin, endNode.angles, nextPos, undefined, endNode.script_noteworthy );
|
||||
}
|
||||
|
||||
doTraverse( startNode, endNode )
|
||||
{
|
||||
traverseData = level.alienAnimData.cannedTraverseAnims[ startNode.animscript ];
|
||||
AssertEx( isDefined( traverseData ), "Traversal '" + startNode.animscript + "' is not supported" );
|
||||
|
||||
animState = traverseData [ "animState" ];
|
||||
AssertEx( isDefined( animState ), "No animState specified for traversal '" + startNode.animscript + "'" );
|
||||
|
||||
self.startNode = startNode;
|
||||
self.endNode = endNode;
|
||||
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self ScrAgentSetOrientMode( "face angle abs", startNode.angles );
|
||||
self ScrAgentSetAnimMode( "anim deltas" );
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
|
||||
if ( isdefined( traverseData[ "traverseSound" ] ) )
|
||||
self thread maps\mp\_utility::play_sound_on_tag( traverseData[ "traverseSound" ] );
|
||||
|
||||
if ( isdefined( traverseData[ "traverseAnimScale" ] ) )
|
||||
self ScrAgentSetAnimScale( traverseData[ "traverseAnimScale" ], traverseData[ "traverseAnimScale" ] );
|
||||
|
||||
switch ( animState )
|
||||
{
|
||||
case "traverse_climb_up":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up", self GetAnimEntry( "traverse_climb_up", 4 ) );
|
||||
break;
|
||||
|
||||
case "traverse_climb_up_over_56":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up_over_56" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_up_ledge_18_run":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up_ledge_18_run" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_up_ledge_18_idle":
|
||||
alienClimbUp( startNode, endNode, "traverse_climb_up_ledge_18_idle" );
|
||||
break;
|
||||
|
||||
case "climb_up_end_jump_side_l":
|
||||
alienClimbUp( startNode, endNode, "climb_up_end_jump_side_l" );
|
||||
break;
|
||||
|
||||
case "climb_up_end_jump_side_r":
|
||||
alienClimbUp( startNode, endNode, "climb_up_end_jump_side_r" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_down":
|
||||
alienClimbDown( startNode, endNode, "traverse_climb_down" );
|
||||
break;
|
||||
|
||||
case "traverse_climb_over_56_down":
|
||||
alienClimbDown( startNode, endNode, "traverse_climb_over_56_down" );
|
||||
break;
|
||||
case "run":
|
||||
alienWallRun( startNode, endNode, "run" );
|
||||
break;
|
||||
|
||||
default:
|
||||
alienRegularTraversal( startNode, animState, traverseData [ "animIndexArray" ], traverseData [ "endInOriented" ], traverseData [ "flexHeightEndAtTraverseEnd" ] );
|
||||
break;
|
||||
}
|
||||
|
||||
self.startNode = undefined;
|
||||
self.endNode = undefined;
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
}
|
||||
|
||||
alienRegularTraversal( startNode, animState, animIndexArray, endInOriented, flexHeightEndAtTraverseEnd )
|
||||
{
|
||||
animIndex = animIndexArray [ RandomInt ( animIndexArray.size ) ];
|
||||
animEntry = self GetAnimEntry( animState, animIndex );
|
||||
result = needFlexibleHeightSupport( animEntry );
|
||||
animTime = GetAnimLength( animEntry );
|
||||
|
||||
self traverseAnimLerp( animEntry, startNode );
|
||||
|
||||
// If we have an apex, move us away from our wall on death
|
||||
if ( AnimHasNotetrack( animEntry, "highest_point" ) )
|
||||
self.apexTraversalDeathVector = VectorNormalize( self.startNode.origin - self.endNode.origin );
|
||||
|
||||
// If we are pointing to an entity, assume it's a scriptable
|
||||
scriptable = GetEnt( startnode.target, "targetname" );
|
||||
if ( IsDefined( scriptable ) )
|
||||
{
|
||||
scriptable thread runScriptableTraverse( animTime );
|
||||
}
|
||||
|
||||
if( result.need_support )
|
||||
doTraversalWithFlexibleHeight( animState, animIndex, animEntry, result.start_notetrack, result.end_notetrack, flexHeightEndAtTraverseEnd, ::alienTraverseNotetrackHandler );
|
||||
else
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, "canned_traverse", "end", ::alienTraverseNotetrackHandler );
|
||||
|
||||
endRegularTraversal( endInOriented );
|
||||
}
|
||||
|
||||
runScriptableTraverse( animTime )
|
||||
{
|
||||
self notify( "stop_previous_traversal" );
|
||||
self endon( "stop_previous_traversal" );
|
||||
self SetScriptablePartState( 0, 1 );//plays the animation
|
||||
wait animTime;
|
||||
self SetScriptablePartState( 0, 0 );//resets the scriptable state
|
||||
}
|
||||
|
||||
endRegularTraversal( endInOriented )
|
||||
{
|
||||
if( endInOriented )
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "noclip" );
|
||||
self.oriented = true;
|
||||
self.ignoreme = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self.oriented = false;
|
||||
self.ignoreme = false;
|
||||
}
|
||||
}
|
||||
|
||||
needFlexibleHeightSupport( animEntry )
|
||||
{
|
||||
result = spawnStruct();
|
||||
|
||||
if ( AnimHasNotetrack ( animEntry, "traverse_up" ) )
|
||||
{
|
||||
result.need_support = true;
|
||||
result.start_notetrack = "traverse_up";
|
||||
result.end_notetrack = "traverse_up_end";
|
||||
return result;
|
||||
}
|
||||
|
||||
if ( AnimHasNotetrack ( animEntry, "traverse_drop" ) )
|
||||
{
|
||||
result.need_support = true;
|
||||
result.start_notetrack = "traverse_drop";
|
||||
result.end_notetrack = "traverse_drop_end";
|
||||
return result;
|
||||
}
|
||||
|
||||
result.need_support = false;
|
||||
return result;
|
||||
}
|
||||
|
||||
doTraversalWithFlexibleHeight( animState, animIndex, animEntry, startNotetrack, endNotetrack, flexHeightEndAtTraverseEnd, notetrackHandlerFunc )
|
||||
{
|
||||
CONST_TRAVERSAL_ANIM_LABEL = "canned_traverse";
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, CONST_TRAVERSAL_ANIM_LABEL, startNotetrack, notetrackHandlerFunc );
|
||||
|
||||
if ( flexHeightEndAtTraverseEnd )
|
||||
{
|
||||
flex_height_end_pos = self.endNode.origin;
|
||||
flex_height_anim_end_time = 1;
|
||||
}
|
||||
else
|
||||
{
|
||||
AssertEx( isDefined( self.endNode.target ), "Traversal " + animState + " " + animIndex + " at " + self.origin + ". Need to link a script struct from the traversal end point to mark the apex point for the animation" );
|
||||
flex_height_end_pos = common_scripts\utility::getstruct( self.endNode.target, "targetname" );
|
||||
AssertEx( isDefined( flex_height_end_pos ), "Traversal " + animState + " " + animIndex + " at " + self.origin + ". Unable to find the apex point struct" );
|
||||
flex_height_end_pos = flex_height_end_pos.origin;
|
||||
apexNotetrackTimes = GetNotetrackTimes( animEntry, "highest_point" );
|
||||
flex_height_anim_end_time = apexNotetrackTimes[ 0 ];
|
||||
AssertEx( isDefined( flex_height_anim_end_time ), "Traversal " + animState + " " + animIndex + " at " + self.origin + ". Missing 'highest_point' notetrack" );
|
||||
}
|
||||
|
||||
doTraversalWithFlexibleHeight_internal( animState, animIndex, CONST_TRAVERSAL_ANIM_LABEL, animEntry, startNotetrack, endNotetrack, flex_height_end_pos, flex_height_anim_end_time, notetrackHandlerFunc );
|
||||
}
|
||||
|
||||
doTraversalWithFlexibleHeight_internal( animState, animIndex, animLabel, animEntry, startNotetrack, endNotetrack, flexHeightEndPos, flexHeightAnimEndTime, notetrackHandlerFunc )
|
||||
{
|
||||
remaining_height = abs( self.origin[ 2 ] - flexHeightEndPos[ 2 ] );
|
||||
|
||||
startNotetrackTimes = GetNotetrackTimes( animEntry, startNotetrack );
|
||||
start_time = startNotetrackTimes[ 0 ];
|
||||
|
||||
endNotetrackTimes = GetNotetrackTimes( animEntry, endNotetrack );
|
||||
end_time = endNotetrackTimes[ 0 ];
|
||||
AssertEx( end_time > start_time, "Traversal " + animState + " " + animIndex + " has incorrectly placed flexible height notetracks." );
|
||||
|
||||
remaining_anim_delta = GetMoveDelta( animEntry, start_time, flexHeightAnimEndTime );
|
||||
remaining_anim_height = abs( remaining_anim_delta[ 2 ] );
|
||||
|
||||
anim_delta_between = GetMoveDelta( animEntry, start_time, end_time );
|
||||
scaled_anim_height = abs( anim_delta_between[ 2 ] );
|
||||
AssertEx( scaled_anim_height > 0.0, "Traversal " + animState + " " + animIndex + " has bad traverse notetracks." );
|
||||
not_scaled_anim_height = remaining_anim_height - scaled_anim_height;
|
||||
|
||||
//<TODO J.C.> When we have time, we need to investigate why this is happening on certain traversals
|
||||
//AssertEx( ( remaining_height - not_scaled_anim_height ) > 0, "Traversal " + animState + " " + animIndex + " at " + self.origin + " has no vertical space to do flexible height." );
|
||||
|
||||
if ( remaining_height <= not_scaled_anim_height )
|
||||
anim_scale = 1;
|
||||
else
|
||||
anim_scale = ( remaining_height - not_scaled_anim_height ) / scaled_anim_height;
|
||||
|
||||
anim_rate = 1 / anim_scale;
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, anim_scale );
|
||||
PlayAnimNAtRateUntilNotetrack( animState, animIndex, anim_rate, animLabel, endNotetrack, notetrackHandlerFunc );
|
||||
|
||||
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
||||
PlayAnimNUntilNotetrack( animState, animIndex, animLabel, "end", notetrackHandlerFunc );
|
||||
|
||||
self.apexTraversalDeathVector = undefined;
|
||||
}
|
||||
|
||||
alienTraverseNotetrackHandler( note, animState, animIndex, animTime )
|
||||
{
|
||||
switch ( note )
|
||||
{
|
||||
case "apply_physics":
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
break;
|
||||
|
||||
case "highest_point":
|
||||
if ( isDefined( self.apexTraversalDeathVector ) )
|
||||
self.apexTraversalDeathVector *= -1;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// Special traversals
|
||||
//===========================================
|
||||
|
||||
//////////////////
|
||||
// Climb up
|
||||
|
||||
alienClimbUp( startNode, endNode, animState, longerEndAnim )
|
||||
{
|
||||
startAnim = self GetAnimEntry( animState, 0 );
|
||||
scrabbleAnim = self GetAnimEntry( animState, 1 );
|
||||
loopAnim = self GetAnimEntry( animState, 2 );
|
||||
endAnim = self GetAnimEntry( animState, 3 );
|
||||
|
||||
totalHeight = endNode.origin[ 2 ] - startnode.origin[ 2 ];
|
||||
startAnimHeight = GetMoveDelta( startAnim, 0, 1 )[ 2 ];
|
||||
scrabbleAnimHeight = GetMoveDelta( scrabbleAnim, 0, 1 )[ 2 ];
|
||||
loopAnimHeight = GetMoveDelta( loopAnim, 0, 1 )[ 2 ];
|
||||
endAnimHeight = GetMoveDelta( endAnim, 0, 1 )[ 2 ];
|
||||
longerEndAnimHeight = undefined;
|
||||
|
||||
climbUpNotetrackTime = getNoteTrackTimes( startAnim, "climb_up_teleport" ) [ 0 ];
|
||||
climbUpAnimDeltaBeforeNotetrack = GetMoveDelta( startAnim, 0, climbUpNotetrackTime );
|
||||
climbUpAnimDeltaAfterNotetrack = GetMoveDelta( startAnim, climbUpNotetrackTime, 1 );
|
||||
startAnimHeightAfterNotetrack = climbUpAnimDeltaAfterNotetrack[ 2 ];
|
||||
|
||||
if ( totalHeight < ( startAnimHeight + endAnimHeight ) )
|
||||
Println( "ERROR: Height is too short for " + animState + ". Modify the geo or use another traversal." );
|
||||
|
||||
distForScrabbleAndLoop = totalHeight - ( startAnimHeight + endAnimHeight );
|
||||
canDoScrabble = false;
|
||||
numOfLoop = 0;
|
||||
if ( distForScrabbleAndLoop > 0 )
|
||||
{
|
||||
canDoScrabble = ( distForScrabbleAndLoop - scrabbleAnimHeight ) > 0;
|
||||
numOfLoop = max ( 0, floor ( ( distForScrabbleAndLoop - canDoScrabble * scrabbleAnimHeight ) / loopAnimHeight ) );
|
||||
}
|
||||
|
||||
teleportAnimHeight = canDoScrabble * scrabbleAnimHeight + numOfLoop * loopAnimHeight + startAnimHeightAfterNotetrack;
|
||||
teleportRealHeight = totalHeight - endAnimHeight - ( startAnimHeight - startAnimHeightAfterNotetrack );
|
||||
animScalerZ = teleportRealHeight / teleportAnimHeight;
|
||||
|
||||
canDoLongerEndAnim = false;
|
||||
if ( isDefined ( longerEndAnim ))
|
||||
{
|
||||
longerEndAnimHeight = GetMoveDelta( longerEndAnim, 0, 1 )[ 2 ];
|
||||
endAnimHeightDiff = longerEndAnimHeight - endAnimHeight;
|
||||
canDoLongerEndAnim = ( teleportRealHeight - teleportAnimHeight ) > endAnimHeightDiff;
|
||||
animScalerZ = ( teleportRealHeight - canDoLongerEndAnim * endAnimHeightDiff )/ teleportAnimHeight;
|
||||
}
|
||||
|
||||
selectedEndAnim = endAnim;
|
||||
if ( canDoLongerEndAnim )
|
||||
selectedEndAnim = longerEndAnim;
|
||||
|
||||
stopTeleportNotetrack = getNoteTrackTimes( selectedEndAnim, "stop_teleport" ) [ 0 ];
|
||||
endAnimHeightBeforeNotetrack = GetMoveDelta( selectedEndAnim, 0, stopTeleportNotetrack )[ 2 ];
|
||||
stopToEndAnimDelta = GetMoveDelta( selectedEndAnim, stopTeleportNotetrack, 1 );
|
||||
stopToEndAnimDeltaXY = length( stopToEndAnimDelta * ( 1, 1, 0 ) );
|
||||
|
||||
// startAnim: Play the anim normally until climb_up_teleport notetrack
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self traverseClimbUpLerp( startAnim, startNode );
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, 0, "canned_traverse", "climb_up_teleport" );
|
||||
|
||||
// startAnim: Initial horizontal scaling to make up for any XY displacement. Start to scale to Z.
|
||||
self ScrAgentSetAnimScale( 1, animScalerZ );
|
||||
self WaitUntilNotetrack( "canned_traverse", "end" );
|
||||
|
||||
// scrabble and loop animation: Continue the Z scaling.
|
||||
self ScrAgentSetAnimScale( 1, animScalerZ );
|
||||
if ( canDoScrabble )
|
||||
PlayAnimNUntilNotetrack( animState, 1, "canned_traverse", "finish" );
|
||||
|
||||
for ( i = 0; i < numOfLoop; i++ )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( animState, 2, "canned_traverse", "end" );
|
||||
}
|
||||
|
||||
//Final height adjustment, making sure alien reach enough height and will not end up inside geo when finish the traversal
|
||||
selfToEndHeight = endNode.origin[ 2 ] - self.origin[ 2 ] - stopToEndAnimDelta[ 2 ];
|
||||
animScalerZ = 1.0;
|
||||
if ( selfToEndHeight > endAnimHeightBeforeNotetrack )
|
||||
animScalerZ = selfToEndHeight / endAnimHeightBeforeNotetrack;
|
||||
|
||||
self ScrAgentSetAnimScale( 1, animScalerZ );
|
||||
|
||||
if ( canDoLongerEndAnim )
|
||||
PlayAnimNUntilNotetrack( animState, 4, "canned_traverse", "stop_teleport", ::alienTraverseNotetrackHandler );
|
||||
else
|
||||
PlayAnimNUntilNotetrack( animState, 3, "canned_traverse", "stop_teleport", ::alienTraverseNotetrackHandler );
|
||||
|
||||
//Final horizontal adjustment, making sure alien will end at the traverse End node
|
||||
selfToEndXY = distance2D( self.origin, endNode.origin );
|
||||
animScalerXY = selfToEndXY / stopToEndAnimDeltaXY;
|
||||
|
||||
self ScrAgentSetAnimScale( animScalerXY, 1 );
|
||||
self WaitUntilNotetrack( "canned_traverse", "end" );
|
||||
}
|
||||
|
||||
/////////////////////
|
||||
// Climb down
|
||||
|
||||
alienClimbDown( startNode, endNode, animState )
|
||||
{
|
||||
startAnim = self GetAnimEntry( animState, 0 );
|
||||
loopAnim = self GetAnimEntry( animState, 1 );
|
||||
slideAnim = self GetAnimEntry( animState, 2 );
|
||||
endAnim = self GetAnimEntry( animState, 3 );
|
||||
jumpOffEndAnim = self GetAnimEntry( animState, 4 );
|
||||
|
||||
totalHeight= startNode.origin[ 2 ] - endNode.origin[ 2 ];
|
||||
startAnimHeight = -1 * GetMoveDelta( startAnim, 0, 1 )[ 2 ];
|
||||
slideAnimHeight = -1 * GetMoveDelta( slideAnim, 0, 1 )[ 2 ];
|
||||
loopAnimHeight = -1 * GetMoveDelta( loopAnim, 0, 1 )[ 2 ];
|
||||
endAnimHeight = -1 * GetMoveDelta( endAnim, 0, 1 )[ 2 ];
|
||||
jumpOffEndAnimHeight = -1 * GetMoveDelta( jumpOffEndAnim, 0, 1 )[ 2 ];
|
||||
|
||||
if ( totalHeight < ( startAnimHeight + endAnimHeight ) )
|
||||
Println( "ERROR: Height is too short for " + animState + ". Modify the geo or use another traversal." );
|
||||
|
||||
endAnimToPlay = endAnim;
|
||||
endAnimToPlayHeight = endAnimHeight;
|
||||
canDoJump = false;
|
||||
|
||||
//Determine whether alien can play the jump off anim for end
|
||||
if ( self canDoJumpForEnd( startnode, endNode, startAnim, jumpOffEndAnim ))
|
||||
{
|
||||
endAnimToPlay = jumpOffEndAnim;
|
||||
endAnimToPlayHeight = jumpOffEndAnimHeight;
|
||||
canDoJump = true;
|
||||
}
|
||||
|
||||
distForSlideAndLoop = totalHeight - ( startAnimHeight + endAnimToPlayHeight );
|
||||
canDoSlide = false;
|
||||
numOfLoop = 0;
|
||||
if ( distForSlideAndLoop > 0 )
|
||||
{
|
||||
canDoSlide = ( distForSlideAndLoop - slideAnimHeight ) > 0;
|
||||
numOfLoop = max ( 0, floor (( distForSlideAndLoop - canDoSlide * slideAnimHeight ) / loopAnimHeight ));
|
||||
}
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
|
||||
self traverseClimbDownLerp( startAnim, startNode );
|
||||
|
||||
PlayAnimNUntilNotetrack( animState, 0, "canned_traverse", "end" );
|
||||
|
||||
slideAndLoopAnimHeight = canDoSlide * slideAnimHeight + numOfLoop * loopAnimHeight;
|
||||
if ( slideAndLoopAnimHeight > 0 )
|
||||
{
|
||||
animScaler = abs( ( distForSlideAndLoop )/ slideAndLoopAnimHeight );
|
||||
self ScrAgentSetAnimScale( 1, animScaler );
|
||||
}
|
||||
|
||||
//<Note J.C.>: Playing the loop and slide animation from the same anim state has caused the following issue.:
|
||||
// (1) The "will_finish_soon" notetrack will fire off immediately due to the short anim length for the slide anim,
|
||||
// causing the slide animation to not play
|
||||
// (2) When this happens, the alien will keep playing the loop animation even when the jump-off state is activated.
|
||||
// If time permits, we need to look into how situations like this should be prevented.
|
||||
for ( i = 0; i < numOfLoop; i++ )
|
||||
{
|
||||
PlayAnimNUntilNotetrack( "traverse_climb_down_loop", 0, "traverse_climb_down_loop", "end" );
|
||||
}
|
||||
if ( canDoSlide )
|
||||
PlayAnimNUntilNotetrack( "traverse_climb_down_slide", 0, "traverse_climb_down_slide", "end" );
|
||||
|
||||
//Final height adjustment, making sure alien ends up on the ground when finish
|
||||
teleportStartTime = getNoteTrackTimes( endAnimToPlay, "climb_down_teleport" ) [ 0 ];
|
||||
teleportEndTime = getNoteTrackTimes( endAnimToPlay, "stop_teleport" ) [ 0 ];
|
||||
animHeightAfterNotetrack = -1 * GetMoveDelta( endAnimToPlay, teleportStartTime, teleportEndTime )[ 2 ];
|
||||
heightAdjustment = abs( self.origin[ 2 ] - endNode.origin[ 2 ] - abs ( GetMoveDelta( endAnimToPlay, teleportEndTime, 1 )[ 2 ] ) );
|
||||
animScaler = heightAdjustment / animHeightAfterNotetrack;
|
||||
|
||||
self ScrAgentSetAnimScale( 1, animScaler );
|
||||
|
||||
if ( canDoJump )
|
||||
PlayAnimNUntilNotetrack( animState, 4, "canned_traverse", "stop_teleport" );
|
||||
else
|
||||
PlayAnimNUntilNotetrack( animState, 3, "canned_traverse", "stop_teleport" );
|
||||
|
||||
self ScrAgentSetAnimScale( 1, 1 );
|
||||
self ScrAgentSetPhysicsMode( "gravity" );
|
||||
self WaitUntilNotetrack( "canned_traverse", "end" );
|
||||
}
|
||||
|
||||
traverseAnimLerp( startAnim, startNode )
|
||||
{
|
||||
// Make sure we're oriented exactly with the node -
|
||||
// lerp to the correct position for the first part of the anim
|
||||
|
||||
lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( startAnim );
|
||||
lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( startAnim, startNode.origin, startNode.angles, lerp_time );
|
||||
|
||||
thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time );
|
||||
}
|
||||
|
||||
traverseClimbDownLerp( startAnim, startNode )
|
||||
{
|
||||
VERTICAL_DROP = -30; // For climb down, go down when attempt to locate the vertical edge
|
||||
HORIZONTAL_EXTENSION = 60; // Further extend horizontally for nodes places close to the vertical edge
|
||||
|
||||
doTraverseClimbLerp( startAnim, startNode, VERTICAL_DROP, HORIZONTAL_EXTENSION, true );
|
||||
}
|
||||
|
||||
traverseClimbUpLerp( startAnim, startNode )
|
||||
{
|
||||
VERTICAL_RAISE = 0; // For climb up, go up when attempt to locate the vertical edge
|
||||
HORIZONTAL_EXTENSION = 50; // Further extend horizontally for nodes places placed far from the vertical edge
|
||||
|
||||
doTraverseClimbLerp( startAnim, startNode, VERTICAL_RAISE, HORIZONTAL_EXTENSION, false );
|
||||
}
|
||||
|
||||
doTraverseClimbLerp( startAnim, startNode, verticalProbeDis, horizontalProbeDis, probeForward )
|
||||
{
|
||||
lerp_time = maps\mp\agents\alien\_alien_anim_utils::getLerpTime( startAnim );
|
||||
lerp_target_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( startAnim, startNode.origin, startNode.angles, lerp_time );
|
||||
|
||||
if ( probeForward )
|
||||
horizontal_probe_direction = ( lerp_target_pos - startNode.origin ) * ( 1, 1, 0 );
|
||||
else
|
||||
horizontal_probe_direction = ( startNode.origin - lerp_target_pos ) * ( 1, 1, 0 );
|
||||
|
||||
horizontal_offset = vectorNormalize( horizontal_probe_direction );
|
||||
horizontal_offset *= horizontalProbeDis;
|
||||
|
||||
anim_end_pos = maps\mp\agents\alien\_alien_anim_utils::getPosInSpaceAtAnimTime( startAnim, startNode.origin, startNode.angles, GetAnimLength( startAnim ) );
|
||||
end_pos_aligned = alignToVerticalEdge( anim_end_pos, verticalProbeDis, horizontal_offset );
|
||||
|
||||
xy_displacement = end_pos_aligned - anim_end_pos;
|
||||
lerp_target_pos += xy_displacement;
|
||||
|
||||
thread maps\mp\agents\alien\_alien_anim_utils::doLerp( lerp_target_pos, lerp_time );
|
||||
}
|
||||
|
||||
alignToVerticalEdge( lerp_target_pos, vertical_displacement, horizontal_offset )
|
||||
{
|
||||
BACKWARD_SCALAR = 3.0; // When doing a backward trace toward the lerp_target_pos, extend the horizontal_offset further to
|
||||
// make sure we hit the vertical edge
|
||||
|
||||
lerp_target_pos += horizontal_offset;
|
||||
lerp_target_pos += ( 0, 0, vertical_displacement );
|
||||
|
||||
trace_end_pos = lerp_target_pos - horizontal_offset * BACKWARD_SCALAR;
|
||||
trace = bulletTrace( lerp_target_pos, trace_end_pos, false );
|
||||
lerp_target_pos = trace["position"];
|
||||
lerp_target_pos += ( 0, 0, -1 * vertical_displacement );
|
||||
|
||||
return lerp_target_pos;
|
||||
}
|
||||
|
||||
canDoJumpForEnd( startnode, endNode, startAnim, jumpAnim )
|
||||
{
|
||||
TRACE_START_FORWARD_PADDING = 10;
|
||||
TRACE_END_UP_PADDING = ( 0, 0, 10 );
|
||||
TRACE_CAPSULE_RADIUS = 5;
|
||||
TRACE_CAPSULE_HEIGHT = self.height;
|
||||
|
||||
startAnimDelta = GetMoveDelta( startAnim, 0, 1 );
|
||||
startAnimDeltaXY = Length2D ( startAnimDelta );
|
||||
startAnimDeltaZ = startAnimDelta [ 2 ] * -1;
|
||||
|
||||
jumpAnimDelta = GetMoveDelta( jumpAnim, 0, 1 );
|
||||
jumpAnimDeltaXY = Length2D ( jumpAnimDelta );
|
||||
jumpAnimDeltaZ = jumpAnimDelta[ 2 ] * -1;
|
||||
|
||||
startToEndXY = VectorNormalize (( endNode.origin - startnode.origin ) * ( 1, 1, 0 ));
|
||||
startAnimEndPos = startnode.origin + startToEndXY * startAnimDeltaXY - ( 0, 0, startAnimDeltaZ );
|
||||
|
||||
startAnimEndGroundPos = PhysicsTrace( startAnimEndPos, startAnimEndPos + ( 0, 0, -2000 ) );
|
||||
startAnimEndAboveGround = ( startAnimEndPos - startAnimEndGroundPos ) [ 2 ];
|
||||
|
||||
if ( startAnimEndAboveGround < jumpAnimDeltaZ )
|
||||
return false;
|
||||
|
||||
jumpStartPos = startAnimEndGroundPos + ( 0, 0, jumpAnimDeltaZ );
|
||||
jumpEndPos = startAnimEndGroundPos + startToEndXY * jumpAnimDeltaXY;
|
||||
|
||||
traceStartPos = jumpStartPos + startToEndXY * TRACE_START_FORWARD_PADDING;
|
||||
traceEndPos = jumpEndPos + TRACE_END_UP_PADDING;
|
||||
|
||||
return ( self AIPhysicsTracePassed( traceStartPos, traceEndPos, TRACE_CAPSULE_RADIUS, TRACE_CAPSULE_HEIGHT, false ) );
|
||||
}
|
||||
|
||||
alienWallRun( startNode, endNode, animState )
|
||||
{
|
||||
self.oriented = true;
|
||||
|
||||
startToEnd = endNode.origin - startNode.origin;
|
||||
up = AnglesToUp( endNode.angles );
|
||||
forward = VectorNormalize( startToEnd );
|
||||
left = VectorCross( up, forward );
|
||||
forward = VectorCross( left, up );
|
||||
right = (0,0,0) - left;
|
||||
|
||||
startToEndAngles = AxisToAngles( forward, right, up );
|
||||
|
||||
self ScrAgentSetOrientMode( "face angle abs", startToEndAngles );
|
||||
|
||||
animEntry = self GetAnimEntry( animState, 0 );
|
||||
time = GetAnimLength( animEntry );
|
||||
moveDelta = GetMoveDelta( animEntry );
|
||||
dist = Length( moveDelta );
|
||||
distToEnd = Length( endNode.origin - self.origin );
|
||||
lerpTime = time * ( distToEnd / dist );
|
||||
self ScrAgentDoAnimLerp( self.origin, endNode.origin, lerpTime );
|
||||
|
||||
self SetAnimState( animState, 0 );
|
||||
|
||||
wait( lerpTime );
|
||||
|
||||
self alienWallRun_WaitForAngles( startToEndAngles );
|
||||
}
|
||||
|
||||
alienWallRun_AnglesAlmostEqual( angles1, angles2, diff )
|
||||
{
|
||||
if ( abs( angleClamp180( angles2[0] - angles1[0] ) > diff ) )
|
||||
return false;
|
||||
if ( abs( angleClamp180( angles2[1] - angles1[1] ) > diff ) )
|
||||
return false;
|
||||
if ( abs( angleClamp180( angles2[2] - angles1[2] ) > diff ) )
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
// do a little extra wait, time out after 0.5s in case something changes.
|
||||
// make sure we're within 5 degrees of our desired angles, but also cancel
|
||||
// out if we're not actually closing in our desired angles because maybe
|
||||
// something changed out our desired angles.
|
||||
// *hocus-pocus-handwavey-insurance*
|
||||
alienWallRun_WaitForAngles( desiredAngles )
|
||||
{
|
||||
previousDiff = 360;
|
||||
waitTime = 0.5;
|
||||
|
||||
while ( waitTime > 0 )
|
||||
{
|
||||
// do an extra check, in addition to the anglesdelta, to see if the angles are close to
|
||||
// each other, because anglesdelta SREs if they're too close. which seems rather unuseful.
|
||||
if ( alienWallRun_AnglesAlmostEqual( self.angles, desiredAngles, 1 ) )
|
||||
break;
|
||||
|
||||
diff = AnglesDelta( desiredAngles, self.angles );
|
||||
if ( diff < 5 || diff >= previousDiff )
|
||||
break;
|
||||
previousDiff = diff;
|
||||
wait( 0.05 );
|
||||
waitTime -= 0.05;
|
||||
}
|
||||
}
|
304
maps/mp/agents/dog/_dog_idle.gsc
Normal file
304
maps/mp/agents/dog/_dog_idle.gsc
Normal file
@ -0,0 +1,304 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include common_scripts\utility;
|
||||
|
||||
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( ter_op( self.bIsWolf, "anml_wolf_pants_mp_fast", "anml_dog_pants_mp_fast" ) );
|
||||
else
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_pants_mp_med", "anml_dog_pants_mp_med" ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( GetTime() > self.timeOfNextSound )
|
||||
{
|
||||
if ( RandomInt(10) < 4 )
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_whine", "anml_dog_whine" ) );
|
||||
else
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "anml_wolf_pants_mp_med", "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 );
|
||||
}
|
351
maps/mp/agents/dog/_dog_melee.gsc
Normal file
351
maps/mp/agents/dog/_dog_melee.gsc
Normal file
@ -0,0 +1,351 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\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( ter_op( self.bIsWolf, "mp_wolf_attack_quick_back_npc", "mp_dog_attack_quick_back_npc" ) );
|
||||
else
|
||||
self PlaySoundOnMovingEnt( ter_op( self.bIsWolf, "mp_wolf_attack_short_npc", "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 ( IsDefined( enemy.hasRiotShield ) && enemy.hasRiotShield )
|
||||
{
|
||||
enemyToMe = self.origin - enemy.origin;
|
||||
meToEnemy = VectorNormalize( ( enemyToMe[0], enemyToMe[1], 0 ) );
|
||||
|
||||
enemyFacing = AnglesToForward( enemy.angles );
|
||||
angleToMe = VectorDot( enemyFacing, enemyToMe );
|
||||
|
||||
if ( enemy.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
maps/mp/agents/dog/_dog_move.gsc
Normal file
490
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
maps/mp/agents/dog/_dog_think.gsc
Normal file
1198
maps/mp/agents/dog/_dog_think.gsc
Normal file
File diff suppressed because it is too large
Load Diff
228
maps/mp/agents/dog/_dog_traverse.gsc
Normal file
228
maps/mp/agents/dog/_dog_traverse.gsc
Normal file
@ -0,0 +1,228 @@
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
|
||||
main()
|
||||
{
|
||||
self endon( "killanimscript" );
|
||||
|
||||
if ( !IsDefined( level.dogTraverseAnims ) )
|
||||
InitDogTraverseAnims();
|
||||
|
||||
startNode = self GetNegotiationStartNode();
|
||||
endNode = self GetNegotiationEndNode();
|
||||
assert( IsDefined( startNode ) && IsDefined( endNode ) );
|
||||
|
||||
animState = undefined;
|
||||
|
||||
animState = level.dogTraverseAnims[ startNode.animscript ];
|
||||
|
||||
if ( !IsDefined( animState ) )
|
||||
{
|
||||
assertmsg( "no animation for traverse " + startNode.animscript );
|
||||
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[ "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";
|
||||
}
|
||||
|
Reference in New Issue
Block a user