1976 lines
54 KiB
Plaintext
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();
|
|
}
|
|
}
|
|
|
|
|