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

1101 lines
26 KiB
Plaintext

#include common_scripts\utility;
#include maps\mp\agents\_scriptedAgents;
#include maps\mp\agents\_agent_utility;
#include maps\mp\gametypes\_damage;
main()
{
self setupAlienState();
self thread think();
self thread watchOwnerDamage();
self thread watchOwnerDeath();
self thread watchOwnerTeamChange();
self thread WaitForBadPath();
self thread WaitForPathSet();
/#
self thread debug_dog();
#/
}
setupAlienState()
{
self.bLockGoalPos = false;
self.ownerRadiusSq = 144 * 144;
self.meleeRadiusSq = 90*90; //128 * 128;
self.attackOffset = 25 + self.radius;
self.attackRadiusSq = 450 * 450;
self.warningRadiusSq = 550 * 550;
self.warningZHeight = 128 * 0.75; //min floor to ceiling in a map is 128 units
self.attackZHeight = 54; //how high we'll allow the alien to jump to attack its target.
self.attackZHeightDown = -64; //how low we'll allow the alien to jump to attack its target.
self.ownerDamagedRadiusSq = 1500 * 1500; //if the owner takes damage and the attacker is within this radius
self.dogDamagedRadiusSq = 6500 * 6500; //if the alien takes damage and the attacker is within this radius. Dog = 1000 * 1000
self.keepPursuingTargetRadiusSq = 6000 * 6000; //stop pursuing the target after the target gets this far away from my owner, unless he's my favorite enemy.
self.preferredOffsetFromOwner = 76;
self.minOffsetFromOwner = 50; //need to keep this above owner.radius + self.radius.
self.forceAttack = true; //if we want to send after a target. Dog = false
self.ignoreCloseFoliage = true;
self.moveMode = "run";
self.enableExtendedKill = true;
self.attackState = "idle"; // self.aiState comes from code, so we need something to tell us what attack state we're in
self.moveState = "idle";
self.bHasBadPath = false;
self.timeOfLastDamage = 0;
// self.PathNodeArrayIndex = 0;
// Send the alien to the closest node on the pathnodearray
self.Alien_GoalPos = get_closest( self.origin , self.PathNodeArray );
self.allowCrouch = true;
self ScrAgentSetGoalRadius(24); // alien will get real twitchy about stopping if this falls below 16.
}
init()
{
//IPrintLnBold ("running INIT - settig up function calls");
self.animCBs = SpawnStruct();
self.animCBs.OnEnter = [];
self.animCBs.OnEnter[ "idle" ] = maps\mp\mp_dome_ns_alien_idle::main;
self.animCBs.OnEnter[ "move" ] = maps\mp\mp_dome_ns_alien_move::main;
self.animCBs.OnEnter[ "traverse" ] = maps\mp\mp_dome_ns_alien_traverse::main;
self.animCBs.OnExit = [];
self.animCBs.OnExit[ "idle" ] = maps\mp\mp_dome_ns_alien_idle::end_script;
self.animCBs.OnExit[ "move" ] = maps\mp\mp_dome_ns_alien_move::end_script;
self.animCBs.OnExit[ "traverse" ] = maps\mp\mp_dome_ns_alien_traverse::end_script;
self Suicide();
self.watchAttackStateFunc = ::watchAttackState;
self.aiState = "idle";
self.moveMode = "fastwalk";
self.radius = 15;
self.height = 40;
}
onEnterAnimState( prevState, nextState )
{
self notify( "killanimscript" );
if ( !IsDefined( self.animCBs.OnEnter[ nextState ] ) )
return;
if ( prevState == nextState && ( nextState != "alien_traverse" ) )
return;
if ( IsDefined( self.animCBs.OnExit[ prevState ] ) )
self [[ self.animCBs.OnExit[ prevState ] ]] ();
ExitAIState( self.aiState );
self.aiState = nextState;
EnterAIState( nextState );
self [[ self.animCBs.OnEnter[ nextState ] ]]();
}
think()
{
//IPrintLnBold ("start thinking");
self endon( "death" );
level endon( "game_ended" );
if ( IsDefined( self.owner ) )
{
self endon( "owner_disconnect" );
self thread destroyOnOwnerDisconnect( self.owner );
}
self thread [[self.watchAttackStateFunc]]();
self thread MonitorFlash();
while ( true )
{
/#
if ( self ProcessDebugMode() )
continue;
#/
if ( !self.stateLocked && self readyToMeleeTarget() )
{
self mp_dome_ns_alien_explode ( self.curMeleeTarget );
return;
}
switch ( self.aiState )
{
case "idle":
self updateIdle();
break;
case "move":
self updateMove();
break;
case "melee":
self updateMelee();
break;
}
wait( 0.05 );
}
}
// self = the alien seeker
mp_dome_ns_alien_explode( explosion_target, maxDamage, blastRadius, attacker )
{
//self set_alien_emissive( 0.2, 1.0 );
self EmissiveBlend( 0.2, 1.0 );
playfx (level._effect[ "vfx_alien_minion_explode_dome" ], self.origin);
phyExpMagnitude = 2;
minDamage = 1;
if ( !isDefined ( maxDamage ))
maxDamage = 400;
if ( !isDefined ( blastRadius ))
blastRadius = 380;
// The seeker was killed by an attacker, so the attacker owns the explosions
if ( isDefined ( attacker ) )
{
attacker radiusDamage(self.origin, blastRadius, maxDamage, minDamage, attacker, "MOD_EXPLOSIVE", "killstreak_level_event_mp");
}
// The seeker blew itself up and the owner exists, so the owner owns the explosions
else if(IsDefined (self.owner))
{
self radiusDamage(self.origin, blastRadius, maxDamage, minDamage, self.owner, "MOD_EXPLOSIVE", "killstreak_level_event_mp");
}
// The seeker blew itself up and the owner does not exist, so nobody owns the explosion
else
{
self radiusDamage(self.origin, blastRadius, maxDamage, minDamage, undefined, "MOD_EXPLOSIVE", "killstreak_level_event_mp");
}
self playsound ("alien_minion_explode");
physicsExplosionSphere( self.origin, blastRadius, blastRadius/2, phyExpMagnitude );
self maps\mp\gametypes\_shellshock::barrel_earthQuake();
// If the alien was killed by someone and didn't just blow themselves up
// if ( !isDefined ( attacker ))
// {
self notify( "killanimscript" );
self SetAnimState( "explode", 0, 1 );
//}
wait GetAnimLength( self GetAnimEntry( "explode", 0 ) );
if ( IsDefined ( self ))
self suicide();
}
DidPastPursuitFail( enemy )
{
assert( IsDefined( enemy ) );
if ( IsDefined( self.curMeleeTarget ) && enemy != self.curMeleeTarget )
return false;
if ( !IsDefined( self.lastPursuitFailedPos ) || !IsDefined( self.lastPursuitFailedMyPos ) )
return false;
if ( Distance2DSquared( enemy.origin, self.lastPursuitFailedPos ) > 4 )
return false;
if ( self.bLastPursuitFailedPosBad )
return true;
if ( DistanceSquared( self.origin, self.lastPursuitFailedMyPos ) > 64 * 64 && GetTime() - self.lastPursuitFailedTime > 2000 )
return false;
return true;
}
DidPastMeleeFail()
{
assert( IsDefined( self.curMeleeTarget ) );
if ( IsDefined( self.lastMeleeFailedPos ) && IsDefined( self.lastMeleeFailedMyPos )
&& Distance2DSquared( self.curMeleeTarget.origin, self.lastMeleeFailedPos ) < 4
&& DistanceSquared( self.origin, self.lastMeleeFailedMyPos ) < 50 * 50 )
return true;
if ( self WantToAttackTargetButCant( false ) )
return true;
return false;
}
enterAIState( state )
{
self ExitAIState( self.aiState );
self.aiState = state;
switch ( state )
{
case "idle":
self.moveState = "idle";
self.bHasBadPath = false;
break;
case "move":
self.moveState = "follow";
break;
case "melee":
break;
default:
break;
}
}
ExitAIState( state )
{
switch ( state )
{
case "move":
self.ownerPrevPos = undefined;
break;
default:
break;
}
}
updateIdle()
{
self UpdateMoveState();
}
updateMove()
{
self UpdateMoveState();
}
updateMelee()
{
self ScrAgentSetGoalPos( self.origin );
}
UpdateMoveState()
{
//IPrintLnBold ("updating move state");
if ( self.bLockGoalPos )
return;
self.prevMoveState = self.moveState;
attackPoint = undefined;
bRefreshGoal = false;
bWantedPursuitButFollowInstead = false;
cBadPathTimeOut = 500;
if ( self.bHasBadPath && GetTime() - self.lastBadPathTime < cBadPathTimeOut )
{
self.moveState = "follow";
bRefreshGoal = true;
}
else
{
self.moveState = self GetMoveState();
}
if ( self.moveState == "pursuit" )
{
attackPoint = self GetAttackPoint( self.enemy );
bLastBadMeleeTarget = false;
if ( IsDefined( self.lastBadPathTime ) && ( GetTime() - self.lastBadPathTime < 3000 ) )
{
if ( Distance2DSquared( attackPoint, self.lastBadPathGoal ) < 16 )
bLastBadMeleeTarget = true;
else if ( IsDefined( self.lastBadPathMoveState ) && self.lastBadPathMoveState == "pursuit" && Distance2DSquared( self.lastBadPathUltimateGoal, self.enemy.origin ) < 16 )
bLastBadMeleeTarget = true;
}
if ( bLastBadMeleeTarget )
{
self.moveState = "follow";
bWantedPursuitButFollowInstead = true;
}
else if ( self wantToAttackTargetButCant( true ) )
{
self.moveState = "follow";
bWantedPursuitButFollowInstead = true;
}
else if ( self DidPastPursuitFail( self.enemy ) )
{
self.moveState = "follow";
bWantedPursuitButFollowInstead = true;
}
}
self SetPastPursuitFailed( bWantedPursuitButFollowInstead );
if ( self.moveState == "follow" )
{
self.curMeleeTarget = undefined;
self.moveMode = self GetFollowMoveMode( self.moveMode );
self.bArrivalsEnabled = true;
myPos = self GetPathGoalPos();
if ( !IsDefined( myPos ) )
myPos = self.origin;
if ( GetTime() - self.timeOfLastDamage < 5000 )
bRefreshGoal = true;
distFromGoalPos = Distance2DSquared( self.origin, self.Alien_GoalPos.origin );
if ( ( distFromGoalPos < 800 ) )
{
self PickNewLocation();
}
self ScrAgentSetGoalPos( self.Alien_GoalPos.origin );
if ( bRefreshGoal == true )
{
self ScrAgentSetGoalPos( self.origin );
}
}
else if ( self.moveState == "pursuit" )
{
self.curMeleeTarget = self.enemy;
self.moveMode = "sprint";
self.bArrivalsEnabled = false;
assert( IsDefined( attackPoint ) );
self ScrAgentSetGoalPos( attackPoint );
}
}
PickNewLocation()
{
// The new goal is the one targeted by the current goal
self.Alien_GoalPos = GetStruct ( self.Alien_GoalPos.target, "targetname" );
}
GetMoveState( prevState )
{
if ( IsDefined( self.enemy ) )
{
// To ensure aliens don't gather around a dead remote uav user
if ( !maps\mp\_utility::IsReallyAlive( self.enemy ) )
return "follow";
if ( IsDefined( self.favoriteEnemy ) && self.enemy == self.favoriteEnemy )
return "pursuit";
if ( abs( self.origin[2] - self.enemy.origin[2] ) < self.warningZHeight && Distance2DSquared( self.enemy.origin, self.origin ) < self.attackRadiusSq )
return "pursuit";
if ( IsDefined( self.curMeleeTarget ) && self.curMeleeTarget == self.enemy )
{
if ( Distance2DSquared( self.curMeleeTarget.origin, self.origin ) < self.keepPursuingTargetRadiusSq )
return "pursuit";
}
}
return "follow";
}
SetPastPursuitFailed( bWantedPursuitButFollowInstead )
{
if ( bWantedPursuitButFollowInstead )
{
if ( !IsDefined( self.lastPursuitFailedPos ) )
{
self.lastPursuitFailedPos = self.enemy.origin;
self.lastPursuitFailedMyPos = self.origin;
groundPos = self DropPosToGround( self.enemy.origin );
self.bLastPursuitFailedPosBad = !IsDefined( groundPos );
self.lastPursuitFailedTime = GetTime();
}
}
else
{
self.lastPursuitFailedPos = undefined;
self.lastPursuitFailedMyPos = undefined;
self.bLastPursuitFailedPosBad = undefined;
self.lastPursuitFailedTime = undefined;
}
}
WaitForBadPath()
{
self endon( "death" );
level endon( "game_ended" );
while ( true )
{
self waittill( "bad_path", badGoalPos );
self.bHasBadPath = true;
self.lastBadPathTime = GetTime();
self.lastBadPathGoal = badGoalPos;
self.lastBadPathMoveState = self.moveState;
if ( self.moveState == "follow" && IsDefined( self.owner ) )
self.lastBadPathUltimateGoal = self.owner.origin;
else if ( self.moveState == "pursuit" && IsDefined( self.enemy ) )
self.lastBadPathUltimateGoal = self.enemy.origin;
}
}
WaitForPathSet()
{
self endon( "death" );
level endon( "game_ended" );
while ( true )
{
self waittill( "path_set" );
self.bHasBadPath = false;
}
}
GetFollowMoveMode( currentMoveMode )
{
cRunToFastWalkDistSq = 350 * 350;
cFastWalkToRunDistSq = 400 * 400;
pathGoalPos = self GetPathGoalPos();
if ( IsDefined( pathGoalPos ) )
{
distSq = DistanceSquared( pathGoalPos, self.origin );
if ( currentMoveMode == "run" || currentMoveMode == "sprint" )
{
if ( distSq < cRunToFastWalkDistSq )
return "fastwalk";
else if ( currentMoveMode == "sprint" )
return "run";
}
else if ( currentMoveMode == "fastwalk" )
{
if ( distSq > cFastWalkToRunDistSq )
return "run";
}
}
return currentMoveMode;
}
IsWithinAttackHeight( targetPos )
{
zDiff = targetPos[2] - self.origin[2];
return zDiff <= self.attackZHeight && zDiff >= self.attackZHeightDown;
}
WantToAttackTargetButCant( bCheckSight )
{
if ( !IsDefined( self.curMeleeTarget ) )
return false;
return !self IsWithinAttackHeight( self.curMeleeTarget.origin )
&& Distance2DSquared( self.origin, self.curMeleeTarget.origin ) < self.meleeRadiusSq * 0.75 * 0.75
&& ( !bCheckSight || self AgentCanSeeSentient( self.curMeleeTarget ) );
}
readyToMeleeTarget()
{
if ( !IsDefined( self.curMeleeTarget ) )
return false;
if ( !maps\mp\_utility::IsReallyAlive( self.curMeleeTarget ) )
return false;
if ( self.aiState == "traverse" )
return false;
if ( Distance2DSquared( self.origin, self.curMeleeTarget.origin ) > self.meleeRadiusSq )
return false;
if ( !self IsWithinAttackHeight( self.curMeleeTarget.origin ) )
return false;
return true;
}
wantsToGrowlAtTarget()
{
if ( !IsDefined( self.enemy ) )
return false;
// first make sure the enemy is within the same height as the alien, or i can see them (which means their not on a different 'floor')
if( abs( self.origin[ 2 ] - self.enemy.origin[ 2 ] ) <= self.warningZHeight || self AgentCanSeeSentient( self.enemy ) )
{
// now see if the enemy is within the warning radius
distSq = Distance2DSquared( self.origin, self.enemy.origin );
if ( distSq < self.warningRadiusSq )
return true;
}
return false;
}
getAttackPoint( enemy )
{
meToTarget = enemy.origin - self.origin;
meToTarget = VectorNormalize( meToTarget );
pathGoalPos = self GetPathGoalPos();
closeEnough = self.attackOffset + 4;
if ( IsDefined( pathGoalPos ) && Distance2DSquared( pathGoalPos, enemy.origin ) < closeEnough * closeEnough && self CanMovePointToPoint( enemy.origin, pathGoalPos ) )
return pathGoalPos;
attackPoint = enemy.origin - meToTarget * self.attackOffset;
attackPoint = self DropPosToGround( attackPoint );
if ( !IsDefined( attackPoint ) )
return enemy.origin;
if ( !self CanMovePointToPoint( enemy.origin, attackPoint ) )
{
enemyFacing = AnglesToForward( enemy.angles );
attackPoint = enemy.origin + enemyFacing * self.attackOffset;
if ( !self CanMovePointToPoint( enemy.origin, attackPoint ) )
return enemy.origin;
}
return attackPoint;
}
// > 0 right
cross2D( a, b )
{
return a[0] * b[1] - b[0] * a[1];
}
destroyOnOwnerDisconnect( owner )
{
self endon( "death" );
owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
self notify( "owner_disconnect" );
if ( maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone() )
wait 0.05;
self notify( "killanimscript" );
if ( IsDefined( self.animCBs.OnExit[ self.aiState ] ) )
self [[ self.animCBs.OnExit[ self.aiState ] ]] ();
self mp_dome_ns_alien_explode( undefined, 1, 0 );
}
watchAttackState() // self == alien
{
self endon( "death" );
level endon( "game_ended" );
while ( true )
{
if ( self.aiState == "melee" )
{
if ( self.attackState != "melee" )
{
self.attackState = "melee";
self SetSoundState( undefined );
}
}
else if ( self.moveState == "pursuit" ) //( self wantsToAttackTarget() )
{
if ( self.attackState != "attacking" )
{
self.attackState = "attacking";
self SetSoundState( "bark", "attacking" );
}
}
else //if( !self wantsToAttackTarget() )
{
if ( self.attackState != "warning" )
{
if ( self wantsToGrowlAtTarget() )
{
self.attackState = "warning";
self SetSoundState( "growl", "warning" );
}
else
{
self.attackState = self.aiState;
self SetSoundState( "pant" );
}
}
else
{
if ( !self wantsToGrowlAtTarget() )
{
self.attackState = self.aiState;
self SetSoundState( "pant" );
}
}
}
wait( 0.05 );
}
}
SetSoundState( state, attackState )
{
if ( !IsDefined( state ) )
{
self notify( "end_dog_sound" );
self.soundState = undefined;
return;
}
if ( !IsDefined( self.soundState ) || self.soundState != state )
{
self notify( "end_dog_sound" );
self.soundState = state;
if ( state == "bark" )
{
self thread playBark( attackState );
}
else if ( state == "growl" )
{
self thread playGrowl( attackState );
}
else if ( state == "pant" )
{
self thread playPanting();
}
else
{
assertmsg( "unknown sound state " + state );
}
}
}
playBark( state ) // self == alien
{
self endon( "death" );
level endon( "game_ended" );
self endon( "end_dog_sound" );
if( !isDefined( self.barking_sound ) )
{
self PlaySoundOnMovingEnt( "alien_minion_attack" );
self.barking_sound = true;
self thread watchBarking();
}
}
watchBarking() // self == alien
{
self endon( "death" );
level endon( "game_ended" );
self endon( "end_dog_sound" );
wait( RandomIntRange( 5, 10 ) );
self.barking_sound = undefined;
}
playGrowl( state ) // self == alien
{
self endon( "death" );
level endon( "game_ended" );
self endon( "end_dog_sound" );
if ( IsDefined( self.lastGrowlPlayedTime ) && GetTime() - self.lastGrowlPlayedTime < 3000 )
wait( 3 );
// while the alien is in this state randomly play growl
while ( true )
{
self.lastGrowlPlayedTime = GetTime();
self PlaySoundOnMovingEnt( "alien_minion_attack" );
wait( RandomIntRange( 3, 6 ) );
}
}
playPanting( state )
{
self endon( "death" );
level endon( "game_ended" );
self endon( "end_dog_sound" );
if ( IsDefined( self.lastPantPlayedTime ) && GetTime() - self.lastPantPlayedTime < 3000 )
wait( 3 );
self.lastPantPlayedTime = GetTime();
while ( true )
{
// idle can handle panting on its own. just check periodically.
if ( self.aiState == "idle" )
{
wait ( 3 );
continue;
}
self.lastPantPlayedTime = GetTime();
if ( self.moveMode == "run" || self.moveMode == "sprint" )
self PlaySoundOnMovingEnt( "alien_minion_idle" );
else
self PlaySoundOnMovingEnt( "alien_minion_idle" );
wait( RandomIntRange( 6, 8 ) );
}
}
watchOwnerDamage() // self == alien
{
self endon( "death" );
level endon( "game_ended" );
while( true )
{
if( !IsDefined( self.owner ) )
return;
self.owner waittill( "damage", damage, attacker );
if ( IsPlayer( attacker ) && attacker != self.owner )
{
// is the alien already attacking?
if ( self.attackState == "attacking" )
continue;
// is the alien close to the owner?
if ( DistanceSquared( self.owner.origin, self.origin ) > self.ownerDamagedRadiusSq )
continue;
// is the attacker within the owner damaged range?
if ( DistanceSquared( self.owner.origin, attacker.origin ) > self.ownerDamagedRadiusSq )
continue;
self.favoriteEnemy = attacker;
self.forceAttack = true;
self thread watchFavoriteEnemyDeath();
}
}
}
watchOwnerDeath() // self == alien
{
self endon( "death" );
level endon( "game_ended" );
while( true )
{
if( !IsDefined( self.owner ) )
return;
self.owner waittill( "death" );
switch( level.gameType )
{
case "sd":
// the alien needs to die when the owner dies because killstreaks go away when the owner dies in sd
killDog();
break;
case "sr":
// the alien needs to die when the owner is eliminated because killstreaks go away when the owner dies in sr
result = level waittill_any_return( "sr_player_eliminated", "sr_player_respawned" );
if( IsDefined( result ) && result == "sr_player_eliminated" )
killDog();
break;
}
}
}
watchOwnerTeamChange() // self == alien
{
self endon( "death" );
level endon( "game_ended" );
while( true )
{
if( !IsDefined( self.owner ) )
return;
result = self.owner waittill_any_return_no_endon_death( "joined_team", "joined_spectators" );
if( IsDefined( result ) && ( result == "joined_team" || result == "joined_spectators" ) )
killDog();
}
}
watchFavoriteEnemyDeath() // self == alien
{
self notify( "watchFavoriteEnemyDeath" );
self endon( "watchFavoriteEnemyDeath" );
self endon( "death" );
self.favoriteEnemy waittill_any_timeout( 5.0, "death", "disconnect" );
self.favoriteEnemy = undefined;
self.forceAttack = false;
}
OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
{
self.timeOfLastDamage = GetTime();
if ( IsDefined( self.owner ) )
self.damagedOwnerToMe = VectorNormalize( self.origin - self.owner.origin );
if ( self ShouldPlayHitReaction( iDamage, sWeapon, sMeansOfDeath ) )
{
switch ( self.aiState )
{
case "idle":
self thread maps\mp\mp_dome_ns_alien_idle::OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
break;
case "move":
self thread maps\mp\mp_dome_ns_alien_move::OnDamage( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
break;
}
}
}
ShouldPlayHitReaction( iDamage, sWeapon, sMeansOfDeath )
{
// being specific on certain damage to do the damage reaction
if ( IsDefined( sWeapon ) && WeaponClass( sWeapon ) == "sniper" )
return true;
if ( IsDefined( sMeansOfDeath ) && IsExplosiveDamageMOD( sMeansOfDeath ) && iDamage >= 10 )
return true;
if ( IsDefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_MELEE" )
return true;
if ( IsDefined( sWeapon ) && sWeapon == "concussion_grenade_mp" )
return true;
// flash grenade is monitored using MonitorFlash()
return false;
}
MonitorFlash()
{
self endon( "death" );
while ( true )
{
self waittill( "flashbang", origin, percent_distance, percent_angle, attacker, teamName, extraDuration );
if ( IsDefined( attacker ) && attacker == self.owner )
continue;
switch ( self.aiState )
{
case "idle":
self maps\mp\mp_dome_ns_alien_idle::onFlashbanged();
break;
case "move":
self maps\mp\mp_dome_ns_alien_move::onFlashbanged();
break;
}
}
}
get_closest( origin, points, maxDist )
{
Assert( points.size );
closestPoint = points[ 0 ];
dist = Distance( origin, closestPoint.origin );
for ( index = 0; index < points.size; index++ )
{
testDist = Distance( origin, points[ index ].origin );
if ( testDist >= dist )
continue;
dist = testDist;
closestPoint = points[ index ];
}
if ( !isDefined( maxDist ) || dist <= maxDist )
return closestPoint;
return undefined;
}
/#
debug_dog() // self == alien
{
self endon( "death" );
level endon( "game_ended" );
while( true )
{
if( GetDvarInt( "scr_debugdog" ) > 0 )
{
start = self.origin;
end = self.origin;
if( IsDefined( self.enemy ) )
end = self.enemy.origin;
color = [ 1, 1, 1 ];
switch( self.attackState )
{
case "idle":
color = [ 1, 1, 1 ];
break;
case "move":
color = [ 0, 1, 0 ];
break;
case "traverse":
color = [ 0.5, 0.5, 0.5 ];
break;
case "melee":
case "attacking":
color = [ 1, 0, 0 ];
break;
case "warning":
color = [ 0.8, 0.8, 0 ];
break;
default:
break;
}
Print3d( self.origin + ( 0, 0, 10 ), self.attackState, ( color[0], color[1], color[2] ) );
Line( start, end, ( color[0], color[1], color[2] ) );
}
wait( 0.05 );
}
}
#/
/#
ProcessDebugMode()
{
if ( getdvarint( "scr_alienDebugMode" ) == 1 )
{
if ( !IsDefined( self.bDebugMode ) || !self.bDebugMode )
self thread DoDebugMode();
self.bDebugMode = true;
wait( 0.05 );
return true;
}
else
{
if ( IsDefined( self.bDebugMode ) && self.bDebugMode )
self EndDebugMode();
self.bDebugMode = false;
return false;
}
}
DoDebugPrint3D( str, color )
{
self notify( "enddebugprint3D" );
level endon( "game_ended" );
self endon( "death" );
self endon( "enddebugprint3D" );
nTimes = 0;
while ( nTimes < 60 )
{
printPos = self.origin + (0,0,48);
print3D( printPos, str, color, 1, 1 );
nTimes++;
wait( 0.05 );
}
}
DebugDrawHitPos( pos, normal, color )
{
self notify( "enddebugdrawhitpos" );
level endon( "game_ended" );
self endon( "death" );
self endon( "enddebugdrawhitpos" );
lineEnd = pos + normal * 100;
while ( true )
{
Line( pos, lineEnd, color, 1, true, 1 );
wait( 0.05 );
}
}
DoDebugMode()
{
self endon( "death" );
level endon( "game_ended" );
self endon( "enddebugmode" );
if ( IsDefined( self.owner ) && IsPlayer( self.owner ) )
player = self.owner;
else
player = level.players[0];
if ( IsAI( player ) )
return;
red = (255,0,0);
green = (0,255,0);
while ( true )
{
player NotifyOnPlayerCommand( "debug_setgoal", "+usereload" );
player waittill( "debug_setgoal" );
viewAngles = player GetPlayerAngles();
viewEye = player GetEye();
playerForward = AnglesToForward( viewAngles );
traceEndPos = viewEye + playerForward * 2048;
trace = self AIPhysicsTrace( viewEye, traceEndPos, 1, 2, true, true );
if ( trace[ "fraction" ] < 1 )
{
hitNormal = trace[ "normal" ];
hitPos = trace[ "position" ];
drawColor = red;
if ( hitNormal[2] > 0.707 )
{
goalPos = self DropPosToGround( hitPos );
if ( IsDefined( goalPos ) )
{
closestNode = GetClosestNodeInSight( goalPos );
if ( IsDefined( closestNode ) )
{
self ScrAgentSetGoalPos( goalPos );
drawColor = green;
self notify("enddebugprint3D");
hitPos = goalPos;
}
else
{
thread DoDebugPrint3D( "unable to find closest node to pos. (cannot path.)", red );
self ScrAgentSetGoalPos( self.origin );
}
}
else
{
thread DoDebugPrint3D( "alien cannot stand there.", red );
self ScrAgentSetGoalPos( self.origin );
}
}
else
{
thread DoDebugPrint3D( "hit pos too steep.", red );
self ScrAgentSetGoalPos( self.origin );
}
thread DebugDrawHitPos( hitPos, hitNormal, drawColor );
}
else
{
thread DoDebugPrint3D( "trace did not hit anything.", red );
self ScrAgentSetGoalPos( self.origin );
self notify( "enddebugdrawhitpos" );
}
}
}
EndDebugMode()
{
self notify("enddebugprint3D");
self notify("enddebugdrawhitpos");
self notify("enddebugmode");
}
#/