s1-scripts-dev/raw/maps/mp/_explosive_drone.gsc
2025-05-21 16:23:17 +02:00

1976 lines
54 KiB
Plaintext

/*
Explosive Drone
Based on Tracking Drone and Tridrone code
Description: A piece of equipment the sticks to a surface and tracks then explodes near a player when he gets close
TASK: The model is a variable grenade, but the FX are based on an old drone model. Look at _variable_grenade.gsc -> tracking_grenade_think( attacker ) to start to attack the problem. It uses a different tracking algorthm,
so there will be some work to merge that into this.
*/
#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 = 0;
BALL_DRONE_SIDE_OFFSET = 0;
CONST_ACTIVATION_TIME = 3;
CONST_Mine_TriggerRadius = 192;
CONST_Mine_TriggerHeight = 192;
CONST_TRACKING_TIME = 3000;
CONST_EXPLODE_PAUSE = 0.5;
CONST_REMOTE_DETONATE_HOLD_TIME = 1;
message_alpha = 1;
message_fade_time = 0.1;
explosive_drone_pickup_string = &"MP_PICKUP_EXPLOSIVE_DRONE";
watchExplosiveDroneUsage() // self == player
{
self endon( "spawned_player" );
self endon( "disconnect" );
self endon( "death" );
self endon( "faux_spawn" );
if ( !IsDefined( level.explosiveDroneSettings ) )
{
explosiveDroneInit();
}
while ( true )
{
self waittill( "grenade_fire", grenade, weapname );
shortWeaponName = maps\mp\_utility::strip_suffix( weapname, "_lefthand" );
if ( shortWeaponName == "explosive_drone_mp" )
{
grenade.team = self.team;
if(!IsDefined(grenade.owner))
{
grenade.owner = self;
}
if(!IsDefined(grenade.weaponname))
{
grenade.weaponname = weapname;
}
grenade thread explosiveDroneLink();
}
}
}
explosiveDroneLink() // self == grenade
{
self thread watchForStick();
// grenade may not have its model immediately after "grenade_fire" so wait before linking to it
wait( 0.1 );
if ( IsDefined ( self ) )
{
self.explosiveDrone = Spawn( "script_model", self.origin );
self.explosiveDrone.targetname = "explosive_drone_head_model";
self.explosiveDrone SetModel( level.explosiveDroneSettings.modelBase );
self.explosiveDrone.oldContents = self.explosiveDrone SetContents( 0 );
self.explosiveDrone LinkTo( self, "tag_spike", (0,0,0), (0,0,0) );
self.explosiveDrone.owner = self.owner;
explosive_drone_model = self.explosiveDrone;
explosive_drone_model thread cleanup_on_grenade_death( self );
self thread monitorSpikeDestroy();
self thread monitorHeadDestroy();
}
}
cleanup_on_grenade_death( grenade )
{
grenade waittill( "death" );
if ( IsDefined( self ) )
self Delete();
}
explosiveGrenadeDeath( attacker, weapon, meansOfDeath, damage )
{
if ( IsDefined( self ) )
{
self notify("death");
// play explosion effect and do radius damage if attacker is defined
// play spark effect if no attacker
if (IsDefined (self.explosiveDrone) )
{
self.explosiveDrone deleteExplosiveDrone();
}
self delete();
}
}
explosiveHeadDeath( attacker, weapon, meansOfDeath, damage )
{
if (IsDefined (self) )
{
self delete();
}
}
explosiveDroneInit()
{
level.explosiveDroneMaxPerPlayer = 1;
level.explosiveDroneSettings = SpawnStruct();
level.explosiveDroneSettings.timeOut = 20.0;
level.explosiveDroneSettings.ExplosiveTimeOut = 30.0;
level.explosiveDroneSettings.health = 60; // keep it from dying anywhere in code
level.explosiveDroneSettings.maxHealth = 60; // this is what we check against for death
level.explosiveDroneSettings.vehicleInfo = "vehicle_tracking_drone_mp";
level.explosiveDroneSettings.modelBase = "npc_drone_explosive_main";
level.explosiveDroneSettings.fxId_sparks = Loadfx( "vfx/sparks/direct_hack_stun" );
level.explosiveDroneSettings.fxId_laser_glow = LoadFx( "vfx/lights/tracking_drone_laser_blue" );
level.explosiveDroneSettings.fxId_explode = LoadFX( "vfx/explosion/explosive_drone_explosion" );
level.explosiveDroneSettings.fxId_LethalExplode = LoadFX( "vfx/explosion/explosive_drone_explosion" );
// level.explosiveDroneSettings.fxId_warning = LoadFX( "vfx/lights/light_tracking_drone_blink_warning" );
level.explosiveDroneSettings.fxId_enemy_light = LoadFX( "vfx/lights/light_explosive_drone_beacon_enemy" );
level.explosiveDroneSettings.fxId_friendly_light = LoadFX( "vfx/lights/light_explosive_drone_beacon_friendly" );
// level.explosiveDroneSettings.fxId_thruster_down = LoadFX( "vfx/distortion/tracking_drone_distortion_down" );
// level.explosiveDroneSettings.fxId_thruster_up = LoadFX( "vfx/distortion/tracking_drone_distortion_up" );
level.explosiveDroneSettings.fxId_engine_distort = LoadFX( "vfx/distortion/tracking_drone_distortion_hemi" );
level.explosiveDroneSettings.fxId_launch_thruster = LoadFX( "vfx/trail/explosive_drone_thruster_large" );
level.explosiveDroneSettings.fxId_position_thruster = LoadFX( "vfx/trail/explosive_drone_thruster_small");
level.explosiveDroneSettings.sound_explode = "wpn_explosive_drone_exp";
level.explosiveDroneSettings.sound_lock = "wpn_explosive_drone_lock";
level.explosiveDroneSettings.sound_launch = "wpn_explosive_drone_open";
//level.explosiveDroneSettings.dome = LoadFX( "vfx/unique/orbital_dome_ground_friendly" );
foreach ( player in level.players )
{
player.is_being_tracked = false;
}
level thread onExplosivePlayerConnect();
level.explosiveDroneTimeout = level.explosiveDroneSettings.timeOut;
level.explosiveDroneTimeout = level.explosiveDroneSettings.ExplosiveTimeOut;
level.explosiveDroneDebugPosition = 0;
level.explosiveDroneDebugPositionForward = BALL_DRONE_FORWARD_OFFSET;
level.explosiveDroneDebugPositionHeight = BALL_DRONE_SIDE_OFFSET;
/*
/#
SetDevDvarIfUninitialized( "scr_explosivedrone_timeout", 20.0 );
SetDevDvarIfUninitialized( "scr_explosivedrone_debug_position", 0 );
SetDevDvarIfUninitialized( "scr_explosivedrone_debug_position_forward", 70.0 );
SetDevDvarIfUninitialized( "scr_explosivedrone_debug_position_height", 70.0 );
SetDevDvarIfUninitialized( "scr_explosivedrone_debug_spawn_position_forward", 70.0 );
SetDevDvarIfUninitialized( "scr_explosivedrone_debug_spawn_position_height", 70.0 );
SetDevDvarIfUninitialized( "scr_explosivedrone_max_per_player", 1 );
level thread checkDvars();
*/
}
/*
checkDvars()
{
while(true)
{
level.explosiveDroneTimeout = GetDvarFloat( "scr_explosivedrone_timeout", level.explosiveDroneSettings.timeOut );
level.explosiveDroneDebugPosition = GetDvarInt( "scr_explosiveDrone_debug_position" );
level.explosiveDroneDebugPositionForward = GetDvarFloat( "scr_explosiveDrone_debug_position_forward" );
level.explosiveDroneDebugPositionHeight = GetDvarFloat( "scr_explosiveDrone_debug_position_height" );
level.explosiveDroneDebugSpawnPositionForward = GetDvarFloat( "scr_explosiveDrone_debug_spawn_position_forward" );
level.explosiveDroneDebugSpawnPositionHeight = GetDvarFloat( "scr_explosiveDrone_debug_spawn_position_height" );
level.explosiveDroneMaxPerPlayer = GetDvarInt( "scr_explosiveDrone_debug_position" );
wait( 1.0 );
}
}
*/
tryUseExplosiveDrone( grenade ) // self == player
{
numIncomingVehicles = 1;
if ( self isUsingRemote() )
{
return false;
}
else if ( exceededMaxExplosiveDrones( ) )
{
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.explosiveDroneArray ) )
{
self.explosiveDroneArray = [];
}
// Limit the player to a max of 2 drones
if ( self.explosiveDroneArray.size )
{
self.explosiveDroneArray = array_removeUndefined( self.explosiveDroneArray );
if ( self.explosiveDroneArray.size >= level.explosiveDroneMaxPerPlayer )
{
if ( IsDefined( self.explosiveDroneArray[0] ) )
{
self.explosiveDroneArray[0] thread explosiveDrone_leave();
}
}
}
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
incrementFauxVehicleCount();
explosiveDrone = grenade createExplosiveDrone();
if( !IsDefined( explosiveDrone ) )
{
// decrement the faux vehicle count since this failed to spawn
decrementFauxVehicleCount();
return false;
}
self playsound( level.explosiveDroneSettings.sound_launch );
self playsound( level.explosiveDroneSettings.sound_lock );
self.explosiveDroneArray[ self.explosiveDroneArray.size ] = explosiveDrone;
self thread startExplosiveDrone( explosiveDrone );
PlayFXOnTag(level.explosiveDroneSettings.fxId_launch_thruster, explosiveDrone, "TAG_THRUSTER_BTM");
grenade notify( "mine_selfdestruct" );
return explosiveDrone;
}
createExplosiveDrone( bRecreate, startPosition, startAngles, DroneType ) // self == grenade
{
if ( !IsDefined( bRecreate ) )
{
bRecreate = false;
}
if( !bRecreate )
{
startAng = self.angles;
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;
/#
if( level.explosiveDroneDebugPosition )
{
targetOffset = (0, 0, level.explosiveDroneDebugSpawnPositionHeight );
targetOffset += forward * level.explosiveDroneDebugSpawnPositionForward;
}
#/
if ( IsDefined( self.explosiveDrone ) )
{
startPos = self.explosiveDrone.origin;
startAng = self.explosiveDrone.angles;
self.explosiveDrone deleteExplosiveDrone();
self addToDeleteSpike();
}
else
{
startPos = self.origin;
}
/*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;
}
dir = AnglesToUp( self.angles );
startPos += ( dir * 10 );
drone = SpawnHelicopter( self.owner, startPos, startAng, level.explosiveDroneSettings.vehicleInfo, level.explosiveDroneSettings.modelBase );
if( !IsDefined( drone ) )
return;
// TEST: insert launch FX here to possibly deal with FX launch not being straight down
drone.type = "explosive_drone";
//drone EnableAimAssist();
// make expendable if it is non-lethal drone
drone make_entity_sentient_mp( self.owner.team);
drone MakeVehicleNotCollideWithPlayers( true );
drone addToExplosiveDroneList();
drone thread removeFromExplosiveDroneListOnDeath();
drone.health = level.explosiveDroneSettings.health;
drone.maxHealth = level.explosiveDroneSettings.maxHealth;
drone.damageTaken = 0; // how much damage has it taken
drone.speed = 20;
drone.followSpeed = 20;
drone.owner = self.owner;
drone.team = self.owner.team;
drone Vehicle_SetSpeed( drone.speed, 10, 10 );
drone SetYawSpeed( 120, 90 );
drone SetNearGoalNotifyDist( 64 );
drone SetHoverParams( 20, 5, 5 );
drone.fx_tag0 = undefined;
if(IsDefined(drone.type))
{
if(drone.type == "explosive_drone")
{
//drone.fx_tag0 = "fx_joint_0";
//drone.fx_tag0 = "tag_fx";
}
/*
else if(drone.type == "explosive_drone")
{
drone.fx_tag0 = "TAG_EYE";
}
*/
}
// set icon
/*
if ( level.teamBased )
drone maps\mp\_entityheadicons::setTeamHeadIcon( self.owner.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, ::onExplosiveDroneDeath, undefined, false );
drone thread explosiveDrone_watchDisable();
drone thread explosiveDrone_watchDeath();
drone thread explosiveDrone_watchTimeout();
drone thread explosiveDrone_watchOwnerLoss();
drone thread explosiveDrone_watchOwnerDeath();
drone thread explosiveDrone_watchRoundEnd();
drone thread explosiveDrone_watchHostMigration();
drone thread ExplosiveDrone_enemy_lightFX();
drone thread ExplosiveDrone_friendly_lightFX();
drone thread drone_thrusterFXExplosive();
return drone;
}
addToDeleteSpike()
{
MAX_SPIKE_LIST = 5;
if ( !IsDefined( level.spikeList ) )
{
level.spikeList = [];
level.spikeListIndex = 0;
}
if ( level.spikeList.size >= MAX_SPIKE_LIST )
{
if ( IsDefined( level.spikeList[ level.spikeListIndex ] ) )
{
level.spikeList[ level.spikeListIndex ] Delete();
}
}
level.spikeList[ level.spikeListIndex ] = self;
level.spikeListIndex = ( level.spikeListIndex + 1 ) % MAX_SPIKE_LIST;
}
idleTargetMoverExplosive( 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 );
}
}
explosiveDrone_enemy_lightFX() // self == drone
{
// looping fx
self endon( "death" );
self.owner endon( "faux_spawn" );
foreach( player in level.players )
{
if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team != self.team )
{
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
}
}
}
explosiveDrone_friendly_lightFX() // self == drone
{
// looping fx
self endon( "death" );
self.owner endon( "faux_spawn" );
foreach( player in level.players )
{
if( IsDefined( player ) && IsSentient( player ) && IsSentient( self ) && player.team == self.team )
{
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
}
}
self thread watchConnectedPlayFXExplosive();
self thread watchJoinedTeamPlayFXExplosive();
}
// Currently not working well. Need to grab from _variable_grenade.gsc -> tracking_grenade_think( attacker ) -> tracking_grenade_thrust_effect when this is ready for primetime.
drone_thrusterFXExplosive()
{
self endon( "death" );
self endon( "disconnect" );
self.owner endon( "faux_spawn" );
while ( true )
{
foreach( player in level.players )
{
self thread drone_thrusterFX_bottom_threaded( player );
self thread drone_thrusterFX_side_threaded( player );
}
wait(1.1);
}
// This might not be necesscary since the new players should register in the level.players above
// while( true )
// {
// level waittill( "connected", player );
// player waittill( "spawned_player" );
//
// wait(0.1);
// if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
// {
// PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
// }
// wait(0.1);
// if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
// {
// PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
// }
// wait(0.1);
// if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
// {
// PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
// }
// wait(0.1);
// {
// PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
// }
// wait(0.25);
// }
}
drone_thrusterFX_side_threaded( player )
{
self endon( "death" );
self endon( "disconnect" );
self.owner endon( "faux_spawn" );
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_X_nY_Z", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_X_nY_nZ", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_nX_nY_Z", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_nX_nY_nZ", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_nX_Y_nZ", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_nX_Y_Z", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_X_Y_Z", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUST_SIDE_X_Y_nZ", player );
}
}
drone_thrusterFX_bottom_threaded( player )
{
self endon( "death" );
self endon( "disconnect" );
self.owner endon( "faux_spawn" );
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_engine_distort))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
}
wait(0.10);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_engine_distort))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
}
wait(0.10);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_engine_distort))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
}
wait(0.10);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_engine_distort))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
}
wait(0.10);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_engine_distort))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
}
wait(0.10);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_position_thruster))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_position_thruster, self, "TAG_THRUSTER_BTM", player );
}
wait(0.1);
if(isDefined(player) && isDefined(self) && isdefined(level.explosiveDroneSettings.fxId_engine_distort))
{
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_engine_distort, self, "TAG_THRUSTER_BTM", player );
}
}
watchConnectedPlayFXExplosive() // self == drone
{
self endon( "death" );
self.owner endon( "faux_spawn" );
// 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.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
}
}
}
watchJoinedTeamPlayFXExplosive() // self == drone
{
self endon( "death" );
self.owner endon( "faux_spawn" );
// 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.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_friendly_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
wait 0.15;
PlayFXOnTagForClients( level.explosiveDroneSettings.fxId_enemy_light, self, "TAG_BEACON", player );
}
}
}
startExplosiveDrone( drone ) // self == player
{
level endon( "game_ended" );
drone endon( "death" );
// begin following player
drone thread explosiveDrone_followTarget();
drone thread createKillCamEntity();
if(IsDefined(drone.type))
{
if(drone.type == "explosive_drone")
{
drone thread CheckForExplosiveGoalExplosive();
/*
result = self waittill_any_return( "goal", "near_goal", "hit_goal" );
*/
}
}
}
CheckForExplosiveGoalExplosive() // self == drone
{
level endon( "game_ended" );
level endon( "host_migration_begin" );
self endon( "death" );
self endon( "leaving" );
starttime = gettime();
self thread blowUpAtEndofTrackingTime(starttime);
//
// self waittill_any( "goal", "near_goal", "hit_goal");
//
// wait(CONST_TRACKING_TIME);
//
// self notify("exploding");
// self thread BlowUpDroneSequenceExplosive();
// while((gettime () - starttime) < CONST_TRACKING_TIME)
// {
//
// if( IsDefined(self.trackedPlayer) && self.trackedPlayer != self.owner && isReallyAlive(self.trackedPlayer))
// {
// DistanceToTargetSq = DistanceSquared(self.trackedPlayer.origin, self.origin);
// if(DistanceToTargetSq <= 16384)
// {
// self notify("exploding");
// self thread BlowUpDroneSequenceExplosive();
// starttime = gettime() + CONST_TRACKING_TIME;
// }
// else if ((gettime () - starttime) > CONST_TRACKING_TIME)
// {
// self notify("exploding");
// self thread BlowUpDroneSequenceExplosive();
// }
// }
//
// wait(0.1);
// }
}
blowUpAtEndofTrackingTime(starttime)
{
level endon( "game_ended" );
level endon( "host_migration_begin" );
self endon( "death" );
self endon( "leaving" );
while ((gettime () - starttime) < CONST_TRACKING_TIME)
{
waitframe();
}
if(IsDefined(self))
{
self notify("exploding");
self thread BlowUpDroneSequenceExplosive();
}
}
BlowUpDroneSequenceExplosive()
{
// play sound
// blink lights
StoredOwner = undefined;
if ( IsDefined( self ) )
{
if(isdefined(self.owner))
{
StoredOwner = self.owner;
}
//self thread TurnOnDangerLightsExplosive();
self playsound( level.explosiveDroneSettings.sound_lock );
//explosiveDrone_stopMovement();
wait(CONST_EXPLODE_PAUSE);
}
if ( IsDefined( self ) )
{
self PlaySound( "wpn_explosive_drone_exp" );
up_v = AnglesToUp(self.angles);
forward_v = AnglesToForward(self.angles);
PlayFx( level.explosiveDroneSettings.fxId_LethalExplode, self.origin, forward_v, up_v );
if(isdefined(StoredOwner))
{
self RadiusDamage(self.origin, 256, 130, 55, StoredOwner, "MOD_EXPLOSIVE", "explosive_drone_mp");
}
else
{
self RadiusDamage(self.origin, 256, 130, 55, undefined, "MOD_EXPLOSIVE", "explosive_drone_mp");
}
self notify("death");
}
}
TurnOnDangerLightsExplosive()
{
if( IsDefined( self ))
{
//StopFXOnTag( level.explosiveDroneSettings.fxId_enemy_light, self, "tag_fx");
//StopFXOnTag( level.explosiveDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_0");
//StopFXOnTag( level.explosiveDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_1");
//StopFXOnTag( level.explosiveDroneSettings.fxId_enemy_light, self, "tag_fx_beacon_2");
//StopFXOnTag( level.explosiveDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_0");
//StopFXOnTag( level.explosiveDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_1");
//StopFXOnTag( level.explosiveDroneSettings.fxId_friendly_light, self, "tag_fx_beacon_2");
}
wait(0.05);
if( IsDefined( self ))
{
//StopFXOnTag( level.explosiveDroneSettings.fxId_enemy_light, self, "tag_fx");
//PlayFXOnTag( level.explosiveDroneSettings.fxId_warning, self, "tag_fx_beacon_0");
//PlayFXOnTag( level.explosiveDroneSettings.fxId_warning, self, "tag_fx_beacon_1");
}
wait(0.15);
if( IsDefined( self ))
{
//PlayFXOnTag( level.explosiveDroneSettings.fxId_warning, self, "tag_fx_beacon_2");
}
}
explosiveDrone_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 explosiveDrone_leave();
return;
}
self.owner endon( "disconnect" );
self endon( "owner_gone" );
self Vehicle_SetSpeed( self.followSpeed, 10, 10 );
self.previousTrackedPlayer = self.owner;
self.trackedPlayer = undefined;
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(self.trackedPlayer) || self.trackedPlayer == self.owner)
{
foreach( player in level.players )
{
if( IsDefined( player ) && IsAlive(player) && player != self.owner && ( !level.teamBased || player.team != self.team ) && !player _hasPerk( "specialty_blindeye" ) )
{
currentDistanceSquared = DistanceSquared(self.origin, player.origin);
if(currentDistanceSquared < closestDistanceSquared)
{
closestDistanceSquared = currentDistanceSquared;
self.trackedPlayer = player;
self thread watchPlayerDeathDisconnectExplosive(player);
}
}
}
}
// Destroy explosive drone if there are no current enemies to track
if ( !IsDefined( self.trackedPlayer ) )
{
self thread explosiveDroneExplode();
}
// Track current target
if( IsDefined( self.trackedPlayer ))
{
explosiveDrone_moveToPlayer(self.trackedPlayer);
}
// Set previous tracked target
if(self.trackedPlayer != self.previousTrackedPlayer )
{
stopHighlightingPlayerExplosive(self.previousTrackedPlayer );
self.previousTrackedPlayer = self.trackedPlayer;
}
}
wait( 1 );
}
}
watchPlayerDeathDisconnectExplosive(trackedPlayer) // self == drone
{
trackedPlayer waittill_any( "death", "disconnect", "faux_spawn", "joined_team" );
if(trackedPlayer.is_being_tracked == true)
{
self thread explosiveDrone_leave();
}
else
{
self.trackedPlayer = undefined;
}
}
explosiveDrone_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( "explosiveDrone_moveToPlayer" );
self endon( "explosiveDrone_moveToPlayer" );
// collect the ideal offsets from the player
forwardOffset = -1 * BALL_DRONE_FORWARD_OFFSET;
sideOffset = BALL_DRONE_SIDE_OFFSET;
heightOffset = BALL_DRONE_STAND_UP_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.explosiveDroneDebugPosition )
{
targetOffset = (0, -1*level.explosiveDroneDebugPositionForward, level.explosiveDroneDebugPositionHeight );
}
#/
// 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 explosiveDrone_watchForGoal();
self thread explosiveDrone_watchTargetDisconnect();
}
explosiveDrone_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;
}
explosiveDrone_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();
explosiveDrone = newOwner createExplosiveDrone( true, self.origin, self.angles );
if( !IsDefined( explosiveDrone ) )
{
// decrement the faux vehicle count since this failed to spawn
decrementFauxVehicleCount();
return false;
}
// Create array if it does not exist
if(!IsDefined(newOwner.explosiveDroneArray))
{
newOwner.explosiveDroneArray = [];
}
newOwner.explosiveDroneArray[ newOwner.explosiveDroneArray.size ] = explosiveDrone;
newOwner thread startExplosiveDrone( explosiveDrone );
if( IsDefined( level.explosiveDroneSettings.fxId_sparks ) )
{
//StopFXOnTag( level.explosiveDroneSettings.fxId_sparks, self, self.fx_tag0 );
}
self removeExplosiveDrone();
return true;
}
explosiveDrone_highlightTarget() // self == drone
{
level endon( "game_ended" );
self endon( "death" );
self endon( "leaving" );
if( !IsDefined( self.owner ) )
{
self thread explosiveDrone_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)
{
startHighlightingPlayerExplosive(self.trackedPlayer);
}
}
else
{
if(IsDefined(self.trackedPlayer) && self.trackedPlayer.is_being_tracked == true)
{
stopHighlightingPlayerExplosive(self.trackedPlayer);
}
}
wait( 0.05 );
}
}
startHighlightingPlayerExplosive(playerToStart) // self == drone
{
self.laserTag LaserOn("explosive_drone_laser");
playfxontag(level.explosiveDroneSettings.fxId_laser_glow, self.laserTag, "tag_laser");
if( IsDefined( level.explosiveDroneSettings.sound_lock ) )
{
self PlaySound( level.explosiveDroneSettings.sound_lock );
}
playerToStart setPerk( "specialty_radararrow", true, false );
if ( playerToStart.is_being_tracked == false )
{
playerToStart.is_being_tracked = true;
playerToStart.TrackedByPlayer = self.owner;
}
}
stopHighlightingPlayerExplosive(playerToStop) // self == drone.trackedPlayer
{
if( IsDefined( self.laserTag ) )
{
self.laserTag LaserOff();
stopfxontag(level.explosiveDroneSettings.fxId_laser_glow, self.laserTag, "tag_laser");
}
if(IsDefined(playerToStop))
{
if( IsDefined( level.explosiveDroneSettings.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;
}
}
onExplosivePlayerConnect()
{
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 so we don't choke/SRE in explosiveDrone_highlightTarget()
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 );
}
}
*/
explosiveDrone_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( "explosiveDrone_watchForGoal" );
self endon( "explosiveDrone_watchForGoal" );
result = self waittill_any_return( "goal", "near_goal", "hit_goal" );
self.inTransit = false;
self.inactive = false;
self notify( "hit_goal" );
}
/* ============================
State Trackers
============================ */
explosiveDrone_watchDeath() // self == drone
{
level endon( "game_ended" );
self endon( "gone" );
self waittill( "death" );
self thread explosiveDroneDestroyed();
}
explosiveDrone_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.explosiveDroneTimeout;
if(self.type == "explosive_drone")
{
timeout = level.explosiveDroneTimeout;
}
wait( timeout );
self thread explosiveDrone_leave();
}
explosiveDrone_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 explosiveDrone_leave();
}
explosiveDrone_watchOwnerDeath() // self == drone
{
level endon ( "game_ended" );
self endon( "death" );
self endon( "leaving" );
while( true )
{
self.owner waittill( "death" );
self thread explosiveDrone_leave();
}
}
explosiveDrone_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( "explosiveDrone_watchTargetDisconnect" );
self endon( "explosiveDrone_watchTargetDisconnect" );
self.trackedPlayer waittill( "disconnect" );
stopHighlightingPlayerExplosive( self.trackedPlayer );
explosiveDrone_moveToPlayer( self.owner );
}
explosiveDrone_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 explosiveDrone_leave();
}
explosiveDrone_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" );
stopHighlightingPlayerExplosive( self.trackedPlayer );
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
self thread explosiveDrone_changeOwner( self.owner );
}
explosiveDrone_leave() // self == drone
{
self endon( "death" );
self notify( "leaving" );
stopHighlightingPlayerExplosive( self.trackedPlayer );
explosiveDroneExplode();
}
/* ============================
End State Trackers
============================ */
/* ============================
Damage and Death Monitors
============================ */
onExplosiveDroneDeath( attacker, weapon, meansOfDeath, damage )
{
self notify ( "death" );
}
explosiveDrone_grenade_watchDisable()
{
self endon( "death" );
self.owner endon( "disconnect" );
level endon( "game_ended" );
self.stunned = false;
while( true )
{
self waittill( "emp_damage", attacker, duration );
self thread explosiveDrone_grenade_stunned();
}
}
explosiveDrone_watchDisable()
{
self endon( "death" );
self.owner endon( "disconnect" );
level endon( "game_ended" );
while( true )
{
self waittill( "emp_damage", attacker, duration );
self thread explosiveDrone_stunned();
}
}
explosiveDrone_grenade_stunned() // self == grenade
{
self notify( "explosiveDrone_stunned" );
self endon( "explosiveDrone_stunned" );
self endon( "death" );
self.owner endon( "disconnect" );
level endon( "game_ended" );
explosiveDrone_grenade_stunBegin();
wait( STUNNED_TIME );
explosiveDrone_grenade_stunEnd();
}
explosiveDrone_stunned() // self == drone
{
self notify( "explosiveDrone_stunned" );
self endon( "explosiveDrone_stunned" );
self endon( "death" );
self.owner endon( "disconnect" );
level endon( "game_ended" );
explosiveDrone_stunBegin();
wait( STUNNED_TIME );
explosiveDrone_stunEnd();
}
explosiveDrone_grenade_stunBegin() // self == grenade
{
if(self.stunned)
return;
self.stunned = true;
if( IsDefined( level.explosiveDroneSettings.fxId_sparks ) )
{
PlayFXOnTag( level.explosiveDroneSettings.fxId_sparks, self, "TAG_BEACON" );
}
}
explosiveDrone_stunBegin()
{
if(self.stunned)
return;
self.stunned = true;
if( IsDefined( level.explosiveDroneSettings.fxId_sparks ) )
{
PlayFXOnTag( level.explosiveDroneSettings.fxId_sparks, self, "TAG_BEACON" );
}
thread stopHighlightingPlayerExplosive(self.trackedPlayer);
self.trackedPlayer = undefined;
self.previousTrackedPlayer = self.owner;
self thread explosiveDrone_stopMovement();
}
explosiveDrone_grenade_stunEnd() // self == grenade
{
if( IsDefined( level.explosiveDroneSettings.fxId_sparks ) )
{
KillFXOnTag( level.explosiveDroneSettings.fxId_sparks, self, "TAG_BEACON" );
}
self.stunned = false;
self.inactive = false;
}
explosiveDrone_stunEnd()
{
if( IsDefined( level.explosiveDroneSettings.fxId_sparks ) )
{
KillFXOnTag( level.explosiveDroneSettings.fxId_sparks, self, "TAG_BEACON" );
}
self.stunned = false;
self.inactive = false;
}
explosiveDroneDestroyed() // self == drone
{
if( !IsDefined( self ) )
return;
//self.owner IPrintLnBold( &"TRACKING_DRONE_DESTROYED" );
// Turn off the highlighting
stopHighlightingPlayerExplosive(self.trackedPlayer);
// stop stuned behavior and vfx
explosiveDrone_stunEnd();
// TODO: could put some drama here as it crashes
explosiveDroneExplode();
}
explosiveDroneExplode() // self == drone
{
if( IsDefined( level.explosiveDroneSettings.fxId_explode ) )
{
PlayFX( level.explosiveDroneSettings.fxId_explode, self.origin );
}
if( IsDefined( level.explosiveDroneSettings.sound_explode ) )
{
self PlaySound( level.explosiveDroneSettings.sound_explode );
}
self notify( "exploding" );
self removeExplosiveDrone();
}
deleteExplosiveDrone() // self == drone
{
if( IsDefined( self.attractor ) )
{
Missile_DeleteAttractor( self.attractor );
}
self removeKillCamEntity();
self delete();
}
removeExplosiveDrone() // 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.explosiveDrone ) )
{
self.owner.explosiveDrone = undefined;
}
self deleteExplosiveDrone();
}
/* ============================
End Damage and Death Monitors
============================ */
/* ============================
List and Count Management
============================ */
addToExplosiveDroneList()
{
level.explosiveDrones[ self GetEntityNumber() ] = self;
}
removeFromExplosiveDroneListOnDeath()
{
entNum = self GetEntityNumber();
self waittill ( "death" );
level.explosiveDrones[ entNum ] = undefined;
level.explosiveDrones = array_removeUndefined( level.explosiveDrones );
}
exceededMaxExplosiveDrones( )
{
if( IsDefined(level.explosiveDrones) && level.explosiveDrones.size >= maxVehiclesAllowed() )
return true;
else
return false;
}
/* ============================
End List and Count Management
============================ */
ExplosiveDroneProximityTrigger() // self == grenade
{
self endon( "mine_destroyed" );
self endon( "mine_selfdestruct" );
self endon( "death" );
self.owner endon( "disconnect" );
self.owner endon( "faux_spawn" );
wait( CONST_ACTIVATION_TIME ); //arming time
if ( IsDefined ( self ) && IsDefined( self.explosiveDrone ) )
{
// set icon
teamIconoffset = self.explosiveDrone GetTagOrigin("TAG_BEACON") - self GetTagOrigin("TAG_BEACON") + (0,0,10);
if ( level.teamBased )
self maps\mp\_entityheadicons::setTeamHeadIcon( self.owner.team, teamIconoffset, "TAG_BEACON" );
else
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, teamIconoffset, "TAG_BEACON" );
trigger = Spawn( "trigger_radius", (self.origin+(0,0,((CONST_Mine_TriggerHeight/2)*-1))), 0, CONST_Mine_TriggerRadius, CONST_Mine_TriggerHeight );
trigger.owner = self;
self thread ExplosiveDroneDeleteTrigger( trigger );
self thread watchForPickup( trigger );
player = undefined;
while ( IsDefined( self ) && IsDefined( self.explosiveDrone) )
{
trigger waittill( "trigger", player );
if ( !IsDefined( player ) )
{
wait( 0.1 );
continue;
}
if ( player _hasPerk( "specialty_blindeye" ))
{
wait( 0.1 );
continue;
}
if( isDefined( self.explosiveDrone) && !player SightConeTrace( self.explosiveDrone GetTagOrigin( "TAG_BEACON" ), self.explosiveDrone ) )
{
wait( 0.1 );
continue;
}
if( IsDefined( self.explosiveDrone ) )
{
start = self.explosiveDrone GetTagOrigin( "TAG_BEACON" );
end = player GetEye();
if ( !BulletTracePassed( start, end, false, self.explosiveDrone ) )
{
wait( 0.1 );
continue;
}
}
if ( isReallyAlive( player ) && player != self.owner && ( !level.teamBased || player.team != self.owner.team ) && !self.stunned)
{
player tryUseExplosiveDrone( self );
}
}
}
}
ExplosiveDroneDeleteTrigger( trigger )
{
self waittill_any( "mine_triggered", "mine_destroyed", "mine_selfdestruct", "death" );
if( IsDefined( self.entityHeadIcon ) )
{
self notify( "kill_entity_headicon_thread" );
self.entityHeadIcon destroy();
}
trigger delete();
}
showDebugRadius(owner)
{
effect = SpawnFx( level.explosiveDroneSettings.dome, owner.origin );
TriggerFx( effect );
self waittill( "death" );
effect delete();
}
endOnPlayerSpawn()
{
self.owner waittill_any("spawned_player", "faux_spawn", "delete_explosive_drones");
self explosiveGrenadeDeath();
}
// The spike in the ground counts as a "grenade", so we clean up when appropriate
monitorSpikeDestroy()
{
self.owner endon( "death" );
self.owner endon( "disconnect" );
self.owner endon( "faux_spawn" );
//self waittill_any("mine_selfdestruct", "mp_exo_repulsor_repel");
self waittill_any("mine_selfdestruct");
self explosiveGrenadeDeath();
}
monitorHeadDestroy()
{
self.owner endon( "death" );
self.owner endon( "disconnect" );
self.owner endon( "faux_spawn" );
while(IsDefined(self.explosiveDrone))
{
wait(0.15);
}
if(isDefined(self))
{
self PlaySound( "wpn_explosive_drone_exp" );
up_v = AnglesToUp(self.angles);
forward_v = AnglesToForward(self.angles);
PlayFx(level.explosiveDroneSettings.fxId_LethalExplode, self.origin, forward_v, up_v);
self RadiusDamage(self.origin, 256, 130, 55, self.owner, "MOD_EXPLOSIVE", "explosive_drone_mp");
self notify("death");
}
self explosiveGrenadeDeath();
}
startGrenadeLightFX() // self = grenade/spike
{
// looping fx, threaded so it does not change if we get more players on the server
self endon( "death" );
self.owner endon( "death" );
self.owner endon( "disconnect" );
ExplosiveDroneFXLoopWaitTime = 4 * 0.15;
while ( IsDefined( self.explosiveDrone ) )
{
foreach( player in level.players )
{
if( IsDefined( player ) && IsSentient( player ) && player.team == self.team && IsDefined(self.explosiveDrone))
{
self thread FXblink( level.explosiveDroneSettings.fxId_friendly_light, self.explosiveDrone, "TAG_BEACON", player );
}
if( IsDefined( player ) && IsSentient( player ) && player.team != self.team && IsDefined(self.explosiveDrone))
{
self thread FXblink( level.explosiveDroneSettings.fxId_enemy_light, self.explosiveDrone, "TAG_BEACON", player );
}
}
wait ExplosiveDroneFXLoopWaitTime;
}
}
FXblink( blink_fx, explosiveDrone, tag, player_to_show ) // self == grenade/spike
{
for(i=0 ; i <= 4 && IsDefined(explosiveDrone) ; i++)
{
if ( IsDefined(player_to_show) && IsDefined(explosiveDrone) && IsDefined( self.stunned ) && !self.stunned)
{
PlayFXOnTagForClients( blink_fx, explosiveDrone, tag, player_to_show );
wait 0.15;
}
}
}
watchForStick() // self == grenade
{
self endon( "death" );
self.owner endon( "death" );
self.owner endon( "disconnect" );
stickReturnParms = undefined;
stickReturnParms = self waittill_any_return_parms( "missile_stuck","mp_exo_repulsor_repel" );
while ( !IsDefined( self.explosiveDrone ) )
{
waitframe();
}
if (IsDefined(stickReturnParms[1])) //handle sticking to drones and moving objects
{
if( stickReturnParms[1].classname == "script_model")
{
self PlaySound( "wpn_explosive_drone_exp" );
up_v = AnglesToUp(self.angles);
forward_v = AnglesToForward(self.angles);
PlayFx(level.explosiveDroneSettings.fxId_LethalExplode, self.origin, forward_v, up_v);
self RadiusDamage(self.origin, 256, 130, 55, self.owner, "MOD_EXPLOSIVE", "explosive_drone_mp");
self thread maps\mp\_explosive_drone::explosiveGrenadeDeath();
//self delete();
}
}
if( IsDefined(self) ) // in case mp_exo_repulsor_repel got us here, or we destroyed ourselves above.
{
self.explosiveDrone SetContents( self.explosiveDrone.oldContents );
self thread ExplosiveDroneProximityTrigger();
self thread endOnPlayerSpawn();
self thread explosiveDrone_grenade_watchDisable();
self thread startGrenadeLightFX();
self thread maps\mp\gametypes\_damage::setEntityDamageCallback( 100, undefined, ::explosiveGrenadeDeath, undefined, false );
self.explosiveDrone thread maps\mp\gametypes\_damage::setEntityDamageCallback( 100, undefined, ::explosiveHeadDeath, undefined, false );
self thread maps\mp\gametypes\_weapons::stickyHandleMovers( "mine_selfdestruct" );
}
}
createKillCamEntity() //self == drone
{
killCamOffset = (0,0,0);
self.killCamEnt = Spawn( "script_model", self.origin );
self.killCamEnt SetScriptMoverKillCam( "explosive" );
self.killCamEnt LinkTo ( self, "TAG_THRUSTER_BTM", killCamOffset, (0,0,0) );
self.killCamEnt SetContents( 0 );
self.killCamEnt.startTime = getTime();
}
removeKillCamEntity() //self == drone
{
if( IsDefined( self.killCamEnt ) )
{
self.killCamEnt delete();
}
}
watchForPickup(trigger) // self == grenade
{
self.owner endon( "disconnect" );
self.owner endon( "faux_spawn" );
level endon( "game_ended" );
self endon( "death" );
self.owner endon( "death" );
self.explosiveDrone MakeUsable();
self.explosiveDrone SetHintString( explosive_drone_pickup_string );
self.explosiveDrone SetHintStringVisibleOnlyToOwner( true );
pickupRadiusSq = GetDvarFloat( "player_useRadius", 128 );
pickupRadiusSq = pickupRadiusSq * pickupRadiusSq;
while ( true )
{
if ( !IsDefined( self ) || !IsDefined( trigger ) )
{
break;
}
inRange = IsDefined( self.explosiveDrone ) && DistanceSquared( self.owner geteye(), self.explosiveDrone.origin ) <= pickupRadiusSq;
if( self.owner IsTouching( trigger ) && inRange )
{
timeUsed = 0;
while ( self.owner UseButtonPressed() )
{
if( !isReallyAlive( self.owner ))
break;
if ( !self.owner IsTouching( trigger ) )
break;
if ( self.owner FragButtonPressed() || self.owner SecondaryOffhandButtonPressed() || IsDefined( self.owner.throwingGrenade ) )
break;
if ( self.owner IsUsingTurret() || self.owner isUsingRemote() )
break;
if ( IsDefined( self.owner.isCapturingCrate ) && self.owner.isCapturingCrate )
break;
if ( IsDefined( self.owner.empGrenaded ) && self.owner.empGrenaded )
break;
if( IsDefined( self.owner.using_remote_turret ) && self.owner.using_remote_turret )
break;
timeUsed += 0.05;
if( timeUsed > 0.75 )
{
self.owner SetWeaponAmmoStock( self.weaponname, self.owner GetWeaponAmmoStock( self.weaponname ) + 1 );
self.explosiveDrone deleteExplosiveDrone();
self delete();
break;
}
waitframe();
}
}
waitframe();
}
}