1540 lines
39 KiB
Plaintext
1540 lines
39 KiB
Plaintext
/*
|
|
Tracking Drone
|
|
Author: Ben Retan
|
|
Description: A piece of equipment that tracks and highlights enemy players
|
|
*/
|
|
|
|
#include maps\mp\_utility;
|
|
#include maps\mp\gametypes\_hud_util;
|
|
#include common_scripts\utility;
|
|
#include maps\mp\gametypes\_hostmigration;
|
|
|
|
STUNNED_TIME = 10.0;
|
|
Z_OFFSET = ( 0, 0, 90 );
|
|
|
|
BALL_DRONE_SPAWN_STAND_UP_OFFSET = 80;
|
|
BALL_DRONE_SPAWN_CROUCH_UP_OFFSET = 65;
|
|
BALL_DRONE_SPAWN_PRONE_UP_OFFSET = 37;
|
|
BALL_DRONE_SPAWN_FORWARD_OFFSET = 50;
|
|
BALL_DRONE_SPAWN_SIDE_OFFSET = 0;
|
|
|
|
BALL_DRONE_STAND_UP_OFFSET = 65;
|
|
BALL_DRONE_CROUCH_UP_OFFSET = 50;
|
|
BALL_DRONE_PRONE_UP_OFFSET = 22;
|
|
BALL_DRONE_FORWARD_OFFSET = 65;
|
|
BALL_DRONE_SIDE_OFFSET = 0;
|
|
|
|
HORDE_BALL_DRONE_STAND_UP_OFFSET = 105;
|
|
HORDE_BALL_DRONE_CROUCH_UP_OFFSET = 75;
|
|
HORDE_BALL_DRONE_PRONE_UP_OFFSET = 45;
|
|
HORDE_BALL_DRONE_FORWARD_OFFSET = 0;
|
|
HORDE_BALL_DRONE_SIDE_OFFSET = 30;
|
|
|
|
watchTrackingDroneUsage()
|
|
{
|
|
self endon( "spawned_player" );
|
|
self endon( "disconnect" );
|
|
self endon( "death" );
|
|
self endon( "faux_spawn" );
|
|
|
|
if(!IsDefined(level.trackingDroneSettings))
|
|
{
|
|
trackingDroneInit();
|
|
}
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "grenade_fire", grenade, weapname );
|
|
|
|
shortWeaponName = maps\mp\_utility::strip_suffix( weapname, "_lefthand" );
|
|
|
|
if ( shortWeaponName == "tracking_drone_mp" )
|
|
{
|
|
grenade thread destroy_tracking_drone_in_water();
|
|
|
|
wait(0.5);
|
|
|
|
if ( !IsRemovedEntity( grenade ) && IsDefined( grenade ) )
|
|
{
|
|
self.trackingDroneStartPosition = grenade.origin;
|
|
self.trackingDroneStartAngles = grenade.angles;
|
|
|
|
grenade deleteTrackingDrone();
|
|
|
|
if ( !prevent_tracking_drone_in_water( self.trackingDroneStartPosition ) )
|
|
{
|
|
tryUseTrackingDrone( weapname );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
trackingDroneInit()
|
|
{
|
|
level.trackingDroneMaxPerPlayer = 1;
|
|
|
|
level.trackingDroneSettings = SpawnStruct();
|
|
level.trackingDroneSettings.timeOut = 20.0;
|
|
level.trackingDroneSettings.ExplosiveTimeOut = 30.0;
|
|
level.trackingDroneSettings.health = 999999; // keep it from dying anywhere in code
|
|
level.trackingDroneSettings.maxHealth = 60; // this is what we check against for death
|
|
level.trackingDroneSettings.vehicleInfo = "vehicle_tracking_drone_mp";
|
|
level.trackingDroneSettings.modelBase = "npc_drone_tracking";
|
|
level.trackingDroneSettings.fxId_sparks = Loadfx( "vfx/sparks/direct_hack_stun" );
|
|
level.trackingDroneSettings.fxId_laser_glow = Loadfx( "vfx/lights/tracking_drone_laser_blue" );
|
|
level.trackingDroneSettings.fxId_explode = LoadFX( "vfx/explosion/tracking_drone_explosion" );
|
|
level.trackingDroneSettings.fxId_LethalExplode = LoadFX( "vfx/explosion/frag_grenade_default" );
|
|
level.trackingDroneSettings.fxId_warning = LoadFX( "vfx/lights/light_tracking_drone_blink_warning" );
|
|
level.trackingDroneSettings.fxId_enemy_light = LoadFX( "vfx/lights/light_tracking_drone_blink_enemy" );
|
|
level.trackingDroneSettings.fxId_friendly_light = LoadFX( "vfx/lights/light_tracking_drone_blink_friendly" );
|
|
level.trackingDroneSettings.fxId_thruster_down = LoadFX( "vfx/distortion/tracking_drone_distortion_down" );
|
|
level.trackingDroneSettings.fxId_thruster_up = LoadFX( "vfx/distortion/tracking_drone_distortion_up" );
|
|
level.trackingDroneSettings.fxId_engine_distort = LoadFX( "vfx/distortion/tracking_drone_distortion_hemi" );
|
|
level.trackingDroneSettings.sound_explode = "veh_tracking_drone_explode";
|
|
level.trackingDroneSettings.sound_lock = "veh_tracking_drone_lock_lp";
|
|
|
|
level.trackingDrones = [];
|
|
|
|
foreach(player in level.players)
|
|
{
|
|
player.is_being_tracked = false;
|
|
}
|
|
|
|
level thread onTrackingPlayerConnect();
|
|
|
|
level.trackingDroneTimeout = level.trackingDroneSettings.timeOut;
|
|
level.explosiveDroneTimeout = level.trackingDroneSettings.ExplosiveTimeOut;
|
|
level.trackingDroneDebugPosition = 0;
|
|
level.trackingDroneDebugPositionForward = BALL_DRONE_FORWARD_OFFSET;
|
|
level.trackingDroneDebugPositionHeight = BALL_DRONE_SIDE_OFFSET;
|
|
}
|
|
|
|
|
|
tryUseTrackingDrone( weaponName ) // self == player
|
|
{
|
|
numIncomingVehicles = 1;
|
|
if( self isUsingRemote() )
|
|
{
|
|
return false;
|
|
}
|
|
else if( exceededMaxTrackingDrones( ) )
|
|
{
|
|
self IPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" );
|
|
return false;
|
|
}
|
|
else if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
|
|
{
|
|
self IPrintLnBold( &"MP_AIR_SPACE_TOO_CROWDED" );
|
|
return false;
|
|
}
|
|
|
|
// Force init (this is for test bots)
|
|
if(!IsDefined(self.trackingDroneArray))
|
|
{
|
|
self.trackingDroneArray = [];
|
|
}
|
|
|
|
// Limit the player to a max of 2 drones
|
|
if ( self.trackingDroneArray.size )
|
|
{
|
|
self.trackingDroneArray = array_removeUndefined( self.trackingDroneArray );
|
|
|
|
if( self.trackingDroneArray.size >= level.trackingDroneMaxPerPlayer )
|
|
{
|
|
if(isDefined(self.trackingDroneArray[0]))
|
|
{
|
|
self.trackingDroneArray[0] thread trackingDrone_leave();
|
|
}
|
|
}
|
|
}
|
|
|
|
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
|
incrementFauxVehicleCount();
|
|
|
|
trackingDrone = createTrackingDrone( weaponName );
|
|
|
|
if( !IsDefined( trackingDrone ) )
|
|
{
|
|
// decrement the faux vehicle count since this failed to spawn
|
|
decrementFauxVehicleCount();
|
|
|
|
return false;
|
|
}
|
|
trackingDrone.weaponName = weaponName;
|
|
|
|
self.trackingDroneArray[ self.trackingDroneArray.size ] = trackingDrone;
|
|
level.trackingDrones = array_removeUndefined( level.trackingDrones );
|
|
level.trackingDrones[level.trackingDrones.size] = trackingDrone;
|
|
self thread startTrackingDrone( trackingDrone );
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
createTrackingDrone( weaponName, bRecreate, startPosition, startAngles, DroneType ) // self == player
|
|
{
|
|
if(!IsDefined(bRecreate))
|
|
{
|
|
bRecreate = false;
|
|
}
|
|
|
|
if(!bRecreate)
|
|
{
|
|
origin = self GetEye();
|
|
forward = AnglesToForward( self GetPlayerAngles() );
|
|
|
|
startAng = self GetPlayerAngles();
|
|
forward = AnglesToForward( startAng );
|
|
side = AnglesToRight(startAng );
|
|
|
|
forwardOffset = forward * BALL_DRONE_SPAWN_FORWARD_OFFSET;
|
|
|
|
sideOffset = side * BALL_DRONE_SPAWN_SIDE_OFFSET;
|
|
|
|
heightOffset = BALL_DRONE_SPAWN_STAND_UP_OFFSET;
|
|
switch( self getStance() )
|
|
{
|
|
case "stand":
|
|
heightOffset = BALL_DRONE_SPAWN_STAND_UP_OFFSET;
|
|
break;
|
|
case "crouch":
|
|
heightOffset = BALL_DRONE_SPAWN_CROUCH_UP_OFFSET;
|
|
break;
|
|
case "prone":
|
|
heightOffset = BALL_DRONE_SPAWN_PRONE_UP_OFFSET;
|
|
break;
|
|
}
|
|
|
|
targetOffset = (0, 0, heightOffset);
|
|
targetOffset += sideOffset;
|
|
targetOffset += forwardOffset;
|
|
|
|
/#
|
|
if( level.trackingDroneDebugPosition )
|
|
{
|
|
targetOffset = (0, 0, level.trackingDroneDebugSpawnPositionHeight );
|
|
targetOffset += forward * level.trackingDroneDebugSpawnPositionForward;
|
|
}
|
|
#/
|
|
|
|
if(IsDefined(self.trackingDroneStartPosition) && IsDefined(self.trackingDroneStartAngles))
|
|
{
|
|
startPos = self.trackingDroneStartPosition;
|
|
startAng = self.trackingDroneStartAngles;
|
|
}
|
|
else
|
|
{
|
|
startPos = self.origin + targetOffset;
|
|
}
|
|
|
|
/*playerStartPos = self.origin;
|
|
trace = BulletTrace( playerStartPos, startPos, false );
|
|
// make sure we aren't starting in geo
|
|
attempts = 3;
|
|
while( trace[ "surfacetype" ] != "none" && attempts > 0 )
|
|
{
|
|
startPos = self.origin + ( VectorNormalize( playerStartPos - trace[ "position" ] ) * 5 );
|
|
trace = BulletTrace( playerStartPos, startPos, false );
|
|
attempts--;
|
|
wait( 0.05 );
|
|
}
|
|
if( attempts <= 0 )
|
|
return;*/
|
|
|
|
|
|
// make sure there are path nodes in the level
|
|
/*if(!IsDefined(GetNodesOnPath(startPos, self.origin)))
|
|
{
|
|
self SetWeaponAmmoStock( weaponName, self GetWeaponAmmoStock( weaponName ) + 1 );
|
|
self IPrintLnBold( &"MP_TRACKING_DRONE_UNAVAILABLE" );
|
|
return;
|
|
}*/
|
|
}
|
|
else
|
|
{
|
|
startPos = startPosition;
|
|
targetPos = startPosition;
|
|
startAng = startAngles;
|
|
}
|
|
/*
|
|
if(DroneType == "explosive_drone")
|
|
{
|
|
drone = SpawnHelicopter( self, startPos, startAng, level.trackingDroneSettings.vehicleInfo, "vehicle_pdrone" );
|
|
if( !IsDefined( drone ) )
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
drone = SpawnHelicopter( self, startPos, startAng, level.trackingDroneSettings.vehicleInfo, level.trackingDroneSettings.modelBase );
|
|
if( !IsDefined( drone ) )
|
|
return;
|
|
}
|
|
*/
|
|
owner = self;
|
|
if ( isdefined ( level.isHorde ) && level.isHorde )
|
|
owner = level.player;
|
|
|
|
drone = SpawnHelicopter( owner, startPos, startAng, level.trackingDroneSettings.vehicleInfo, level.trackingDroneSettings.modelBase );
|
|
if( !IsDefined( drone ) )
|
|
return;
|
|
|
|
if(IsDefined(DroneType))
|
|
{
|
|
drone.type = "explosive_drone";
|
|
drone SetModel("vehicle_pdrone");
|
|
}
|
|
else
|
|
{
|
|
drone.type = "tracking_drone";
|
|
}
|
|
|
|
//drone EnableAimAssist();
|
|
|
|
// make expendable if it is non-lethal drone
|
|
drone make_entity_sentient_mp( self.team);
|
|
drone MakeVehicleNotCollideWithPlayers( true );
|
|
|
|
drone addToTrackingDroneList();
|
|
drone thread removeFromTrackingDroneListOnDeath();
|
|
|
|
drone.health = level.trackingDroneSettings.health;
|
|
drone.maxHealth = level.trackingDroneSettings.maxHealth;
|
|
drone.damageTaken = 0; // how much damage has it taken
|
|
|
|
drone.speed = 20;
|
|
drone.followSpeed = 20;
|
|
drone.owner = self;
|
|
drone.team = self.team;
|
|
|
|
drone Vehicle_SetSpeed( drone.speed, 10, 10 );
|
|
drone SetYawSpeed( 120, 90 );
|
|
drone SetNearGoalNotifyDist( 64 );
|
|
drone SetHoverParams( 4, 5, 5 );
|
|
|
|
drone.fx_tag0 = undefined;
|
|
if(IsDefined(drone.type))
|
|
{
|
|
if(drone.type == "tracking_drone")
|
|
{
|
|
drone.fx_tag0 = "fx_joint_0";
|
|
}
|
|
else if(drone.type == "explosive_drone")
|
|
{
|
|
drone.fx_tag0 = "TAG_EYE";
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// set icon
|
|
if ( level.teamBased )
|
|
drone maps\mp\_entityheadicons::setTeamHeadIcon( self.team, (0,0,25), drone.fx_tag0 );
|
|
else
|
|
drone maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,25), drone.fx_tag0);
|
|
|
|
// tracking
|
|
drone.maxTrackingRange = 2000;
|
|
drone.maxLaserRange = 300;
|
|
drone.trackedPlayer = undefined;
|
|
|
|
maxPitch = 45;
|
|
maxRoll = 45;
|
|
drone SetMaxPitchRoll( maxPitch, maxRoll );
|
|
|
|
drone.targetPos = startPos;
|
|
|
|
drone.attract_strength = 10000;
|
|
drone.attract_range = 150;
|
|
drone.attractor = Missile_CreateAttractorEnt( drone, drone.attract_strength, drone.attract_range );
|
|
|
|
drone.hasDodged = false;
|
|
drone.stunned = false;
|
|
drone.inactive = false;
|
|
|
|
drone thread maps\mp\gametypes\_damage::setEntityDamageCallback( drone.maxHealth, undefined, ::onTrackingDroneDeath, undefined, false );
|
|
drone thread trackingDrone_watchDisable();
|
|
drone thread trackingDrone_watchDeath();
|
|
drone thread trackingDrone_watchOwnerLoss();
|
|
drone thread trackingDrone_watchOwnerDeath();
|
|
drone thread trackingDrone_watchRoundEnd();
|
|
drone thread trackingDrone_watchHostMigration();
|
|
if ( !isdefined ( level.isHorde ) )
|
|
drone thread trackingDrone_watchTimeout();
|
|
// if(drone.type == "explosive_drone")
|
|
// {
|
|
// drone thread ExplosiveDrone_enemy_lightFX();
|
|
// drone thread ExplosiveDrone_friendly_lightFX();
|
|
// }
|
|
// else if(drone.type == "tracking_drone")
|
|
// {
|
|
// drone thread trackingDrone_enemy_lightFX();
|
|
// drone thread trackingDrone_friendly_lightFX();
|
|
// drone thread drone_thrusterFX();
|
|
// }
|
|
if(drone.type == "tracking_drone")
|
|
{
|
|
drone thread trackingDrone_enemy_lightFX();
|
|
drone thread trackingDrone_friendly_lightFX();
|
|
drone thread drone_thrusterFX();
|
|
}
|
|
|
|
|
|
return drone;
|
|
}
|
|
|
|
idleTargetMover( ent ) // self == player
|
|
{
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
ent endon( "death" );
|
|
|
|
// keep the idleTarget entity behind the player so the turret is always default looking back there
|
|
forward = AnglesToForward( self.angles );
|
|
while( true )
|
|
{
|
|
if( isReallyAlive( self ) && !self isUsingRemote() && AnglesToForward( self.angles ) != forward )
|
|
{
|
|
forward = AnglesToForward( self.angles );
|
|
pos = self.origin + ( forward * -100 ) + ( 0, 0, 40 );
|
|
ent MoveTo( pos, 0.5 );
|
|
}
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
trackingDrone_lightFX( fx, player ) // self == drone
|
|
{
|
|
player endon("disconnect");
|
|
|
|
PlayFXOnTagForClients( fx, self, "fx_light_1", player );
|
|
wait 0.05;
|
|
PlayFXOnTagForClients( fx, self, "fx_light_2", player );
|
|
wait 0.05;
|
|
PlayFXOnTagForClients( fx, self, "fx_light_3", player );
|
|
wait 0.05;
|
|
PlayFXOnTagForClients( fx, self, "fx_light_4", player );
|
|
}
|
|
|
|
trackingDrone_enemy_lightFX() // self == drone
|
|
{
|
|
// looping fx
|
|
|
|
self endon( "death" );
|
|
|
|
foreach( player in level.players )
|
|
{
|
|
if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team != self.team )
|
|
{
|
|
self childthread trackingDrone_lightFX( level.trackingDroneSettings.fxId_enemy_light, player );
|
|
wait(0.2);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
trackingDrone_friendly_lightFX() // self == drone
|
|
{
|
|
// looping fx
|
|
|
|
self endon( "death" );
|
|
|
|
foreach( player in level.players )
|
|
{
|
|
if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team == self.team )
|
|
{
|
|
self childthread trackingDrone_lightFX( level.trackingDroneSettings.fxId_friendly_light, player );
|
|
wait(0.2);
|
|
}
|
|
}
|
|
|
|
self thread watchConnectedPlayFX();
|
|
self thread watchJoinedTeamPlayFX();
|
|
}
|
|
|
|
drone_thrusterFX()
|
|
{
|
|
self endon( "death" );
|
|
|
|
foreach( player in level.players )
|
|
{
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_F", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_K", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_L", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_R", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_engine_distort))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_engine_distort, self, "TAG_WEAPON", player );
|
|
}
|
|
wait(0.25);
|
|
/*PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_F", player );
|
|
wait(0.1);
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_K", player );
|
|
wait(0.1);
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_L", player );
|
|
wait(0.1);
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_R", player );*/
|
|
}
|
|
|
|
while( true )
|
|
{
|
|
level waittill( "connected", player );
|
|
player waittill( "spawned_player" );
|
|
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_F", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_K", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_L", player );
|
|
}
|
|
wait(0.1);
|
|
if(isDefined(player) && isDefined(self) && isdefined(level.trackingDroneSettings.fxId_thruster_down))
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_down, self, "fx_thruster_down_R", player );
|
|
}
|
|
wait(0.1);
|
|
{
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_engine_distort, self, "TAG_WEAPON", player );
|
|
}
|
|
wait(0.25);
|
|
/*PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_F", player );
|
|
wait(0.1);
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_K", player );
|
|
wait(0.1);
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_L", player );
|
|
wait(0.1);
|
|
PlayFXOnTagForClients( level.trackingDroneSettings.fxId_thruster_up, self, "fx_thruster_up_R", player );*/
|
|
}
|
|
}
|
|
|
|
watchConnectedPlayFX() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
|
|
// play fx for late comers
|
|
while( true )
|
|
{
|
|
level waittill( "connected", player );
|
|
player waittill( "spawned_player" );
|
|
|
|
if( IsDefined( player ) && player.team == self.team )
|
|
{
|
|
self childthread trackingDrone_lightFX( level.trackingDroneSettings.fxId_friendly_light, player );
|
|
wait(0.2);
|
|
}
|
|
}
|
|
}
|
|
|
|
watchJoinedTeamPlayFX() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
|
|
// play fx for team changers
|
|
while( true )
|
|
{
|
|
level waittill( "joined_team", player );
|
|
player waittill( "spawned_player" );
|
|
|
|
if( IsDefined( player ) && player.team == self.team )
|
|
{
|
|
self childthread trackingDrone_lightFX( level.trackingDroneSettings.fxId_friendly_light, player );
|
|
wait(0.2);
|
|
}
|
|
}
|
|
}
|
|
|
|
///////////////////// TEMP FX FOR TEMP EXPLOSIVE DRONE MESH ///////////////////////////////////////////
|
|
////
|
|
//ExplosiveDrone_enemy_lightFX() // self == drone
|
|
//{
|
|
// // looping fx
|
|
//
|
|
// self endon( "death" );
|
|
//
|
|
// foreach( player in level.players )
|
|
// {
|
|
// if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team != self.team )
|
|
// {
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_0", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_1", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_2", player );
|
|
// }
|
|
//
|
|
// }
|
|
//}
|
|
//
|
|
//ExplosiveDrone_friendly_lightFX() // self == drone
|
|
//{
|
|
// // looping fx
|
|
//
|
|
// self endon( "death" );
|
|
//
|
|
// foreach( player in level.players )
|
|
// {
|
|
// if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team == self.team )
|
|
// {
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_0", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_1", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_2", player );
|
|
// }
|
|
// }
|
|
//
|
|
// self thread watchConnectedExplosivePlayFX();
|
|
// self thread watchJoinedTeamExplosivePlayFX();
|
|
//}
|
|
//
|
|
//watchConnectedExplosivePlayFX() // self == drone
|
|
//{
|
|
// self endon( "death" );
|
|
//
|
|
// // play fx for late comers
|
|
// while( true )
|
|
// {
|
|
// level waittill( "connected", player );
|
|
// player waittill( "spawned_player" );
|
|
//
|
|
// if( IsDefined( player ) && player.team == self.team )
|
|
// {
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_0", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_1", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_2", player );
|
|
// }
|
|
// }
|
|
//}
|
|
//
|
|
//watchJoinedTeamExplosivePlayFX() // self == drone
|
|
//{
|
|
// self endon( "death" );
|
|
//
|
|
// // play fx for team changers
|
|
// while( true )
|
|
// {
|
|
// level waittill( "joined_team", player );
|
|
// player waittill( "spawned_player" );
|
|
//
|
|
// if( IsDefined( player ) && player.team == self.team )
|
|
// {
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_0", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_1", player );
|
|
// wait 0.15;
|
|
// PlayFXOnTagForClients( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_2", player );
|
|
// }
|
|
// }
|
|
//}
|
|
/////
|
|
///////////////////// TEMP FX FOR TEMP EXPLOSIVE DRONE MESH ///////////////////////////////////////////
|
|
|
|
|
|
startTrackingDrone( drone ) // self == player
|
|
{
|
|
level endon( "game_ended" );
|
|
drone endon( "death" );
|
|
|
|
// begin following player
|
|
drone thread trackingDrone_followTarget();
|
|
drone thread aud_drone_start_jets();
|
|
|
|
if(IsDefined(drone.type))
|
|
{
|
|
if(drone.type == "explosive_drone")
|
|
{
|
|
drone thread CheckForExplosiveGoal();
|
|
|
|
/*
|
|
|
|
result = self waittill_any_return( "goal", "near_goal", "hit_goal" );
|
|
|
|
*/
|
|
|
|
}
|
|
// begin highlighting target
|
|
else if(drone.type == "tracking_drone" && !isdefined ( level.isHorde ) )
|
|
{
|
|
drone thread trackingDrone_highlightTarget();
|
|
}
|
|
}
|
|
}
|
|
CheckForExplosiveGoal()
|
|
{
|
|
level endon( "game_ended" );
|
|
level endon( "host_migration_begin" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
while(true)
|
|
{
|
|
self waittill_any( "goal", "near_goal", "hit_goal");
|
|
{
|
|
if(self.trackedPlayer != self.owner && isReallyAlive(self.trackedPlayer))
|
|
{
|
|
DistanceToTargetSq = DistanceSquared(self.trackedPlayer.origin, self.origin);
|
|
if(DistanceToTargetSq <= 16384)
|
|
{
|
|
self notify("exploding");
|
|
self thread BlowUpDroneSequence();
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
BlowUpDroneSequence()
|
|
{
|
|
Time = 2;
|
|
// play sound
|
|
// blink lights
|
|
StoredOwner = undefined;
|
|
if(isdefined(self.owner))
|
|
{
|
|
StoredOwner = self.owner;
|
|
}
|
|
if( IsDefined( self ))
|
|
{
|
|
self thread TurnOnDangerLights();
|
|
self playsound("drone_warning_beap");
|
|
}
|
|
|
|
|
|
wait(Time);
|
|
if(isDefined(self))
|
|
{
|
|
self PlaySound("drone_bomb_explosion");
|
|
up_v = AnglesToUp(self.angles);
|
|
forward_v = AnglesToForward(self.angles);
|
|
|
|
PlayFx(level.trackingDroneSettings.fxId_LethalExplode, self.origin, forward_v, up_v);
|
|
if(isdefined(StoredOwner))
|
|
{
|
|
self RadiusDamage(self.origin, 256, 1000, 25, StoredOwner, "MOD_EXPLOSIVE", "killstreak_missile_strike_mp");
|
|
}
|
|
else
|
|
{
|
|
self RadiusDamage(self.origin, 256, 1000, 25, undefined, "MOD_EXPLOSIVE", "killstreak_missile_strike_mp");
|
|
}
|
|
|
|
self notify("death");
|
|
}
|
|
}
|
|
TurnOnDangerLights()
|
|
{
|
|
if( IsDefined( self ))
|
|
{
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_0");
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_1");
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_2");
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_0");
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_1");
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_2");
|
|
}
|
|
wait(0.05);
|
|
if( IsDefined( self ))
|
|
{
|
|
PlayFXOnTag( level.trackingDroneSettings.fxId_warning, self, "tag_fx_beacon_0");
|
|
PlayFXOnTag( level.trackingDroneSettings.fxId_warning, self, "tag_fx_beacon_1");
|
|
}
|
|
wait(0.15);
|
|
if( IsDefined( self ))
|
|
{
|
|
PlayFXOnTag( level.trackingDroneSettings.fxId_warning, self, "tag_fx_beacon_2");
|
|
}
|
|
}
|
|
trackingDrone_followTarget() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
level endon( "host_migration_begin" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self endon( "exploding" );
|
|
|
|
if( !IsDefined( self.owner ) )
|
|
{
|
|
self thread trackingDrone_leave();
|
|
return;
|
|
}
|
|
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
self Vehicle_SetSpeed( self.followSpeed, 10, 10 );
|
|
self.previousTrackedPlayer = self.owner;
|
|
self.trackedPlayer = undefined;
|
|
if ( isdefined ( level.isHorde ) && level.isHorde )
|
|
self.trackedPlayer = self.owner;
|
|
while( true )
|
|
{
|
|
if( IsDefined( self.stunned ) && self.stunned )
|
|
{
|
|
wait( 0.5 );
|
|
continue;
|
|
}
|
|
|
|
if( IsDefined( self.owner ) && IsAlive( self.owner ) )
|
|
{
|
|
// Try to find a target to track
|
|
maxRangeSquared = self.maxTrackingRange * self.maxTrackingRange;
|
|
closestDistanceSquared = maxRangeSquared;
|
|
|
|
if ( !isdefined ( level.isHorde ) )
|
|
{
|
|
if(!IsDefined(self.trackedPlayer) || self.trackedPlayer == self.owner)
|
|
{
|
|
foreach( player in level.players )
|
|
{
|
|
if( IsDefined( player ) && IsAlive(player) && player.team != self.team && !player _hasPerk( "specialty_blindeye" ) )
|
|
{
|
|
currentDistanceSquared = DistanceSquared(self.origin, player.origin);
|
|
if(currentDistanceSquared < closestDistanceSquared)
|
|
{
|
|
closestDistanceSquared = currentDistanceSquared;
|
|
self.trackedPlayer = player;
|
|
|
|
self thread watchPlayerDeathDisconnect(player);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// Track the owner if there are no current enemies to track
|
|
if(!IsDefined(self.trackedPlayer))
|
|
{
|
|
self.trackedPlayer = self.owner;
|
|
}
|
|
|
|
// Track current target
|
|
if( IsDefined( self.trackedPlayer ))
|
|
{
|
|
trackingDrone_moveToPlayer(self.trackedPlayer);
|
|
}
|
|
|
|
// Set previous tracked target
|
|
if(self.trackedPlayer != self.previousTrackedPlayer )
|
|
{
|
|
stopHighlightingPlayer(self.previousTrackedPlayer );
|
|
self.previousTrackedPlayer = self.trackedPlayer;
|
|
}
|
|
}
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
watchPlayerDeathDisconnect(trackedPlayer) // self == drone
|
|
{
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self endon( "exploding" );
|
|
|
|
trackedPlayer waittill_any( "death", "disconnect", "faux_spawn", "joined_team" );
|
|
|
|
if( IsDefined(trackedPlayer) )
|
|
{
|
|
if(trackedPlayer.is_being_tracked == true)
|
|
{
|
|
if(!IsAlive(trackedPlayer))
|
|
trackedPlayer.died_being_tracked = true;
|
|
self thread trackingDrone_leave();
|
|
}
|
|
else
|
|
{
|
|
self.trackedPlayer = undefined;
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
trackingDrone_moveToPlayer( playerToMoveTo ) // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.owner endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
self notify( "trackingDrone_moveToPlayer" );
|
|
self endon( "trackingDrone_moveToPlayer" );
|
|
|
|
|
|
// collect the ideal offsets from the player
|
|
forwardOffset = 0;
|
|
sideOffset = 0;
|
|
heightOffset = 0;
|
|
|
|
if( isdefined ( level.isHorde ) && level.isHorde )
|
|
{
|
|
forwardOffset = -1 * HORDE_BALL_DRONE_FORWARD_OFFSET;
|
|
sideOffset = HORDE_BALL_DRONE_SIDE_OFFSET;
|
|
|
|
switch( playerToMoveTo getStance() )
|
|
{
|
|
case "stand":
|
|
heightOffset = HORDE_BALL_DRONE_STAND_UP_OFFSET;
|
|
break;
|
|
case "crouch":
|
|
heightOffset = HORDE_BALL_DRONE_CROUCH_UP_OFFSET;
|
|
break;
|
|
case "prone":
|
|
heightOffset = HORDE_BALL_DRONE_PRONE_UP_OFFSET;
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
forwardOffset = -1 * BALL_DRONE_FORWARD_OFFSET;
|
|
sideOffset = BALL_DRONE_SIDE_OFFSET;
|
|
|
|
switch( playerToMoveTo getStance() )
|
|
{
|
|
case "stand":
|
|
heightOffset = BALL_DRONE_STAND_UP_OFFSET;
|
|
break;
|
|
case "crouch":
|
|
heightOffset = BALL_DRONE_CROUCH_UP_OFFSET;
|
|
break;
|
|
case "prone":
|
|
heightOffset = BALL_DRONE_PRONE_UP_OFFSET;
|
|
break;
|
|
}
|
|
}
|
|
|
|
targetOffset = (sideOffset, forwardOffset, heightOffset);
|
|
|
|
/#
|
|
if( level.trackingDroneDebugPosition )
|
|
{
|
|
targetOffset = (0, -1*level.trackingDroneDebugPositionForward, level.trackingDroneDebugPositionHeight );
|
|
}
|
|
#/
|
|
|
|
// ask code to navigate us as close as possible to the offset from the owner, set us as in-transit and start a thread waiting for us to get to the goal
|
|
self SetDroneGoalPos( playerToMoveTo, targetOffset );
|
|
self.inTransit = true;
|
|
self thread trackingDrone_watchForGoal();
|
|
self thread trackingDrone_watchTargetDisconnect();
|
|
}
|
|
|
|
trackingDrone_stopMovement()
|
|
{
|
|
// ask code to navigate us as close as possible to the offset from the owner, set us as in-transit and start a thread waiting for us to get to the goal
|
|
self SetVehGoalPos( self.origin, 1 );
|
|
self.inTransit = false;
|
|
self.inactive = true;
|
|
}
|
|
|
|
trackingDrone_changeOwner(newOwner) // self = drone
|
|
{
|
|
// Change ownership of drone
|
|
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
|
incrementFauxVehicleCount();
|
|
|
|
trackingDrone = newOwner createTrackingDrone( self.weaponName, true, self.origin, self.angles );
|
|
if( !IsDefined( trackingDrone ) )
|
|
{
|
|
// decrement the faux vehicle count since this failed to spawn
|
|
decrementFauxVehicleCount();
|
|
|
|
return false;
|
|
}
|
|
|
|
// Create array if it does not exist
|
|
if(!IsDefined(newOwner.trackingDroneArray))
|
|
{
|
|
newOwner.trackingDroneArray = [];
|
|
}
|
|
|
|
newOwner.trackingDroneArray[ newOwner.trackingDroneArray.size ] = trackingDrone;
|
|
level.trackingDrones = array_removeUndefined( level.trackingDrones );
|
|
level.trackingDrones[level.trackingDrones.size] = trackingDrone;
|
|
newOwner thread startTrackingDrone( trackingDrone );
|
|
|
|
if( IsDefined( level.trackingDroneSettings.fxId_sparks ) )
|
|
{
|
|
StopFXOnTag( level.trackingDroneSettings.fxId_sparks, self, self.fx_tag0 );
|
|
}
|
|
|
|
self removeTrackingDrone();
|
|
|
|
return true;
|
|
}
|
|
|
|
trackingDrone_highlightTarget() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
if( !IsDefined( self.owner ) )
|
|
{
|
|
self thread trackingDrone_leave();
|
|
return;
|
|
}
|
|
|
|
self.owner endon( "disconnect" );
|
|
self.owner endon( "joined_team" );
|
|
self.owner endon( "joined_spectators" );
|
|
|
|
// Create the laser
|
|
self.laserTag = Spawn( "script_model", self.origin );
|
|
self.laserTag SetModel( "tag_laser" );
|
|
|
|
while( true )
|
|
{
|
|
if(IsDefined(self.trackedPlayer))
|
|
{
|
|
self.laserTag.origin = self GetTagOrigin("tag_weapon");
|
|
randomRange = 20;
|
|
randomOffset = ( randomFloat( randomRange ), randomFloat( randomRange ), randomFloat( randomRange ) ) - ( 10, 10, 10 );
|
|
|
|
heightOffset = BALL_DRONE_STAND_UP_OFFSET;
|
|
switch( self.trackedPlayer getStance() )
|
|
{
|
|
case "stand":
|
|
heightOffset = BALL_DRONE_STAND_UP_OFFSET;
|
|
break;
|
|
case "crouch":
|
|
heightOffset = BALL_DRONE_CROUCH_UP_OFFSET;
|
|
break;
|
|
case "prone":
|
|
heightOffset = BALL_DRONE_PRONE_UP_OFFSET;
|
|
break;
|
|
}
|
|
|
|
self.laserTag.angles = VectorToAngles( ( self.trackedPlayer.origin + ( 0, 0, heightOffset - 20 ) + randomOffset ) - self.origin );
|
|
}
|
|
|
|
if( IsDefined( self.stunned ) && self.stunned )
|
|
{
|
|
wait( 0.5 );
|
|
continue;
|
|
}
|
|
|
|
// Highlight enemies
|
|
traceEntity = undefined;
|
|
if(IsDefined(self.trackedPlayer))
|
|
{
|
|
traceData = BulletTrace( self.origin, self.trackedPlayer.origin, true, self );
|
|
traceEntity = traceData["entity"];
|
|
}
|
|
|
|
if(IsDefined(self.trackedPlayer) &&
|
|
self.trackedPlayer != self.owner &&
|
|
IsDefined(traceEntity) &&
|
|
traceEntity == self.trackedPlayer &&
|
|
DistanceSquared(self.origin, self.trackedPlayer.origin) < self.maxLaserRange * self.maxLaserRange)
|
|
{
|
|
if( self.trackedPlayer.is_being_tracked == false)
|
|
{
|
|
startHighlightingPlayer(self.trackedPlayer);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(IsDefined(self.trackedPlayer) && self.trackedPlayer.is_being_tracked == true)
|
|
{
|
|
stopHighlightingPlayer(self.trackedPlayer);
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
startHighlightingPlayer(playerToStart) // self == drone
|
|
{
|
|
self.laserTag LaserOn("tracking_drone_laser");
|
|
playfxontag(level.trackingDroneSettings.fxId_laser_glow, self.laserTag, "tag_laser");
|
|
|
|
if( IsDefined( level.trackingDroneSettings.sound_lock ) )
|
|
{
|
|
self PlayLoopSound( level.trackingDroneSettings.sound_lock );
|
|
}
|
|
|
|
playerToStart setPerk( "specialty_radararrow", true, false );
|
|
|
|
if ( playerToStart.is_being_tracked == false )
|
|
{
|
|
playerToStart.is_being_tracked = true;
|
|
playerToStart.TrackedByPlayer = self.owner;
|
|
}
|
|
}
|
|
|
|
stopHighlightingPlayer(playerToStop) // self == drone.trackedPlayer
|
|
{
|
|
if( IsDefined( self.laserTag ) )
|
|
{
|
|
self.laserTag LaserOff();
|
|
stopfxontag(level.trackingDroneSettings.fxId_laser_glow, self.laserTag, "tag_laser");
|
|
}
|
|
|
|
if(IsDefined(playerToStop))
|
|
{
|
|
if( IsDefined( level.trackingDroneSettings.sound_lock ) )
|
|
{
|
|
self StopLoopSound( );
|
|
}
|
|
|
|
if ( playerToStop HasPerk( "specialty_radararrow", true ) )
|
|
{
|
|
playerToStop unsetPerk( "specialty_radararrow", true );
|
|
}
|
|
|
|
playerToStop notify( "player_not_tracked" );
|
|
playerToStop.is_being_tracked = false;
|
|
playerToStop.TrackedByPlayer = undefined;
|
|
}
|
|
}
|
|
|
|
onTrackingPlayerConnect()
|
|
{
|
|
level endon( "game_ended" );
|
|
|
|
while ( true )
|
|
{
|
|
level waittill( "connected", player );
|
|
|
|
player.is_being_tracked = false;
|
|
|
|
// possible for multiple players to come in on one frame, so double check that we have a value for all players
|
|
foreach ( player in level.players )
|
|
{
|
|
if (!IsDefined(player.is_being_tracked))
|
|
player.is_being_tracked = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
/#
|
|
debugDrawDronePath()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "hit_goal" );
|
|
|
|
self notify( "debugDrawDronePath" );
|
|
self endon( "debugDrawDronePath" );
|
|
|
|
while( true )
|
|
{
|
|
nodePath = GetNodesOnPath( self.owner.origin, self.origin );
|
|
if( IsDefined( nodePath ) )
|
|
{
|
|
for( i = 0; i < nodePath.size; i++ )
|
|
{
|
|
if( IsDefined( nodePath[ i + 1 ] ) )
|
|
Line( nodePath[ i ].origin + Z_OFFSET, nodePath[ i + 1 ].origin + Z_OFFSET, ( 1, 0, 0 ) );
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
#/
|
|
|
|
trackingDrone_watchForGoal() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.owner endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
self notify( "trackingDrone_watchForGoal" );
|
|
self endon( "trackingDrone_watchForGoal" );
|
|
|
|
result = self waittill_any_return( "goal", "near_goal", "hit_goal" );
|
|
self.inTransit = false;
|
|
self.inactive = false;
|
|
self notify( "hit_goal" );
|
|
}
|
|
|
|
/* ============================
|
|
State Trackers
|
|
============================ */
|
|
|
|
trackingDrone_watchDeath() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "gone" );
|
|
|
|
self waittill( "death" );
|
|
|
|
self thread trackingDroneDestroyed();
|
|
}
|
|
|
|
|
|
trackingDrone_watchTimeout() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
level endon( "host_migration_begin" );
|
|
self endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
timeout = level.trackingDroneTimeout;
|
|
if(self.type == "explosive_drone")
|
|
{
|
|
timeout = level.explosiveDroneTimeout;
|
|
}
|
|
|
|
wait( timeout );
|
|
|
|
self thread trackingDrone_leave();
|
|
}
|
|
|
|
|
|
trackingDrone_watchOwnerLoss() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
|
|
|
|
self notify( "owner_gone" );
|
|
// leave
|
|
self thread trackingDrone_leave();
|
|
}
|
|
|
|
trackingDrone_watchOwnerDeath() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
while( true )
|
|
{
|
|
self.owner waittill( "death" );
|
|
|
|
self thread trackingDrone_leave();
|
|
}
|
|
}
|
|
|
|
trackingDrone_watchTargetDisconnect() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
level endon( "host_migration_begin" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.owner endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
self notify( "trackingDrone_watchTargetDisconnect" );
|
|
self endon( "trackingDrone_watchTargetDisconnect" );
|
|
|
|
self.trackedPlayer waittill( "disconnect" );
|
|
|
|
stopHighlightingPlayer( self.trackedPlayer );
|
|
trackingDrone_moveToPlayer( self.owner );
|
|
}
|
|
|
|
trackingDrone_watchRoundEnd() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
level waittill_any( "round_end_finished", "game_ended" );
|
|
|
|
// leave
|
|
self thread trackingDrone_leave();
|
|
}
|
|
|
|
trackingDrone_watchHostMigration() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
self.owner endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
level waittill( "host_migration_begin" );
|
|
|
|
stopHighlightingPlayer( self.trackedPlayer );
|
|
|
|
trackingDrone_stopMovement();
|
|
|
|
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
|
|
|
self thread trackingDrone_changeOwner( self.owner );
|
|
}
|
|
|
|
trackingDrone_leave() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
self notify( "leaving" );
|
|
|
|
stopHighlightingPlayer(self.trackedPlayer);
|
|
|
|
trackingDroneExplode();
|
|
}
|
|
|
|
/* ============================
|
|
End State Trackers
|
|
============================ */
|
|
|
|
/* ============================
|
|
Damage and Death Monitors
|
|
============================ */
|
|
|
|
onTrackingDroneDeath( attacker, weapon, meansOfDeath, damage )
|
|
{
|
|
self notify ( "death" );
|
|
}
|
|
|
|
|
|
trackingDrone_watchDisable()
|
|
{
|
|
self endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "emp_damage", attacker, duration );
|
|
|
|
self thread trackingDrone_stunned();
|
|
}
|
|
}
|
|
|
|
|
|
trackingDrone_stunned() // self == drone
|
|
{
|
|
self notify( "trackingDrone_stunned" );
|
|
self endon( "trackingDrone_stunned" );
|
|
|
|
self endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
|
|
trackingDrone_stunBegin();
|
|
|
|
wait( STUNNED_TIME );
|
|
|
|
trackingDrone_stunEnd();
|
|
|
|
}
|
|
|
|
trackingDrone_stunBegin()
|
|
{
|
|
if(self.stunned)
|
|
return;
|
|
|
|
self.stunned = true;
|
|
|
|
if( IsDefined( level.trackingDroneSettings.fxId_sparks ) )
|
|
{
|
|
PlayFXOnTag( level.trackingDroneSettings.fxId_sparks, self, self.fx_tag0 );
|
|
}
|
|
|
|
thread stopHighlightingPlayer(self.trackedPlayer);
|
|
|
|
self.trackedPlayer = undefined;
|
|
self.previousTrackedPlayer = self.owner;
|
|
|
|
self thread trackingDrone_stopMovement();
|
|
}
|
|
|
|
trackingDrone_stunEnd()
|
|
{
|
|
if( IsDefined( level.trackingDroneSettings.fxId_sparks ) )
|
|
{
|
|
KillFXOnTag( level.trackingDroneSettings.fxId_sparks, self, self.fx_tag0 );
|
|
}
|
|
|
|
self.stunned = false;
|
|
self.inactive = false;
|
|
}
|
|
|
|
trackingDroneDestroyed() // self == drone
|
|
{
|
|
if( !IsDefined( self ) )
|
|
return;
|
|
|
|
//self.owner IPrintLnBold( &"TRACKING_DRONE_DESTROYED" );
|
|
|
|
// Turn off the highlighting
|
|
stopHighlightingPlayer(self.trackedPlayer);
|
|
|
|
// stop stuned behavior and vfx
|
|
trackingDrone_stunEnd();
|
|
|
|
// TODO: could put some drama here as it crashes
|
|
|
|
trackingDroneExplode();
|
|
}
|
|
|
|
trackingDroneExplode() // self == drone
|
|
{
|
|
if( IsDefined( level.trackingDroneSettings.fxId_explode ) )
|
|
{
|
|
PlayFX( level.trackingDroneSettings.fxId_explode, self.origin );
|
|
}
|
|
|
|
if( IsDefined( level.trackingDroneSettings.sound_explode ) )
|
|
{
|
|
self PlaySound( level.trackingDroneSettings.sound_explode );
|
|
}
|
|
|
|
self notify( "explode" );
|
|
|
|
self removeTrackingDrone();
|
|
}
|
|
|
|
|
|
deleteTrackingDrone() // self == drone
|
|
{
|
|
if ( !IsRemovedEntity( self ) && IsDefined( self ) )
|
|
{
|
|
if ( IsDefined( self.attractor ) )
|
|
{
|
|
Missile_DeleteAttractor( self.attractor );
|
|
}
|
|
self delete();
|
|
}
|
|
}
|
|
|
|
removeTrackingDrone() // self == drone
|
|
{
|
|
// decrement the faux vehicle count right before it is deleted this way we know for sure it is gone
|
|
decrementFauxVehicleCount();
|
|
|
|
if( IsDefined( self.owner ) && IsDefined( self.owner.trackingDrone ) )
|
|
self.owner.trackingDrone = undefined;
|
|
|
|
if ( IsDefined( self.laserTag ) )
|
|
{
|
|
self.laserTag Delete();
|
|
}
|
|
|
|
self deleteTrackingDrone();
|
|
}
|
|
|
|
/* ============================
|
|
End Damage and Death Monitors
|
|
============================ */
|
|
|
|
/* ============================
|
|
List and Count Management
|
|
============================ */
|
|
|
|
addToTrackingDroneList()
|
|
{
|
|
level.trackingDrones[ self GetEntityNumber() ] = self;
|
|
}
|
|
|
|
removeFromTrackingDroneListOnDeath()
|
|
{
|
|
entNum = self GetEntityNumber();
|
|
|
|
self waittill ( "death" );
|
|
|
|
level.trackingDrones[ entNum ] = undefined;
|
|
|
|
level.trackingDrones = array_removeUndefined( level.trackingDrones );
|
|
}
|
|
|
|
exceededMaxTrackingDrones( )
|
|
{
|
|
if( level.trackingDrones.size >= maxVehiclesAllowed() )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* ============================
|
|
End List and Count Management
|
|
============================ */
|
|
|
|
|
|
aud_drone_start_jets()
|
|
{
|
|
self playloopsound( "veh_tracking_drone_jets_lp" );
|
|
}
|
|
|
|
destroy_tracking_drone_in_water() // self = tracking drone grenade
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( !isDefined( level.water_triggers ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while ( true )
|
|
{
|
|
foreach ( trig in level.water_triggers )
|
|
{
|
|
if ( self IsTouching( trig ) )
|
|
{
|
|
if ( IsDefined( level.trackingDroneSettings.fxId_explode ) )
|
|
{
|
|
PlayFX( level.trackingDroneSettings.fxId_explode, self.origin );
|
|
}
|
|
if ( IsDefined( level.trackingDroneSettings.sound_explode ) )
|
|
{
|
|
self PlaySound( level.trackingDroneSettings.sound_explode );
|
|
}
|
|
|
|
self deleteTrackingDrone();
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
prevent_tracking_drone_in_water( pos )
|
|
{
|
|
if ( !isDefined( level.water_triggers ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach ( trig in level.water_triggers )
|
|
{
|
|
if ( IsPointInVolume( pos, trig ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|