1261 lines
36 KiB
Plaintext
1261 lines
36 KiB
Plaintext
/*
|
|
Ball Drone
|
|
Author: Aaron Eady
|
|
Description: The idea is to have a companion killstreak that stays with you and acts as a helper.
|
|
*/
|
|
|
|
#include maps\mp\_utility;
|
|
#include maps\mp\gametypes\_hud_util;
|
|
#include common_scripts\utility;
|
|
#include maps\mp\gametypes\_hostmigration;
|
|
|
|
STUNNED_TIME = 7.0;
|
|
Z_OFFSET = ( 0, 0, 90 );
|
|
|
|
BALL_DRONE_STAND_UP_OFFSET = 118;
|
|
BALL_DRONE_CROUCH_UP_OFFSET = 70;
|
|
BALL_DRONE_PRONE_UP_OFFSET = 36;
|
|
BALL_DRONE_BACK_OFFSET = 40;
|
|
BALL_DRONE_SIDE_OFFSET = 40;
|
|
|
|
init()
|
|
{
|
|
level.killStreakFuncs[ "ball_drone_radar" ] = ::tryUseBallDrone;
|
|
level.killStreakFuncs[ "ball_drone_backup" ] = ::tryUseBallDrone;
|
|
|
|
level.ballDroneSettings = [];
|
|
|
|
level.ballDroneSettings[ "ball_drone_radar" ] = SpawnStruct();
|
|
level.ballDroneSettings[ "ball_drone_radar" ].timeOut = 60.0;
|
|
level.ballDroneSettings[ "ball_drone_radar" ].health = 999999; // keep it from dying anywhere in code
|
|
level.ballDroneSettings[ "ball_drone_radar" ].maxHealth = 500; // this is what we check against for death
|
|
level.ballDroneSettings[ "ball_drone_radar" ].streakName = "ball_drone_radar";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].vehicleInfo = "ball_drone_mp";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].modelBase = "vehicle_ball_drone_iw6";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].teamSplash = "used_ball_drone_radar";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_sparks = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_ims_sparks" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_explode = LoadFX( "vfx/gameplay/explosions/vehicle/ball/vfx_exp_ball_drone" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].sound_explode = "ball_drone_explode";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].voDestroyed = "nowl_destroyed";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].voTimedOut = "nowl_gone";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].xpPopup = "destroyed_ball_drone_radar";
|
|
level.ballDroneSettings[ "ball_drone_radar" ].playFXCallback = ::radarBuddyPlayFx;
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light1 = [];
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light2 = [];
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light3 = [];
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light4 = [];
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light1[ "enemy" ] = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_light_detonator_blink" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light2[ "enemy" ] = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_light_detonator_blink" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light3[ "enemy" ] = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_light_detonator_blink" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light4[ "enemy" ] = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_light_detonator_blink" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light1[ "friendly" ] = LoadFX( "fx/misc/light_mine_blink_friendly" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light2[ "friendly" ] = LoadFX( "fx/misc/light_mine_blink_friendly" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light3[ "friendly" ] = LoadFX( "fx/misc/light_mine_blink_friendly" );
|
|
level.ballDroneSettings[ "ball_drone_radar" ].fxId_light4[ "friendly" ] = LoadFX( "fx/misc/light_mine_blink_friendly" );
|
|
|
|
level.ballDroneSettings[ "ball_drone_backup" ] = SpawnStruct();
|
|
level.ballDroneSettings[ "ball_drone_backup" ].timeOut = 90.0;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].health = 999999; // keep it from dying anywhere in code
|
|
level.ballDroneSettings[ "ball_drone_backup" ].maxHealth = 500; // this is what we check against for death
|
|
level.ballDroneSettings[ "ball_drone_backup" ].streakName = "ball_drone_backup";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].vehicleInfo = "backup_drone_mp";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].modelBase = "vehicle_drone_backup_buddy";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].teamSplash = "used_ball_drone_radar";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].fxId_sparks = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_ims_sparks" );
|
|
level.ballDroneSettings[ "ball_drone_backup" ].fxId_explode = LoadFX( "fx/explosions/bouncing_betty_explosion" );
|
|
level.ballDroneSettings[ "ball_drone_backup" ].sound_explode = "ball_drone_explode";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].voDestroyed = "vulture_destroyed";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].voTimedOut = "vulture_gone";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].xpPopup = "destroyed_ball_drone";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].weaponInfo = "ball_drone_gun_mp";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].weaponModel = "vehicle_drone_backup_buddy_gun";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].weaponTag = "tag_turret_attach";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].sound_weapon = "weap_p99_fire_npc";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].sound_targeting = "ball_drone_targeting";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].sound_lockon = "ball_drone_lockon";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].sentryMode = "sentry";
|
|
level.ballDroneSettings[ "ball_drone_backup" ].visual_range_sq = 1200 * 1200; // distance radius it will acquire targets (see)
|
|
//level.ballDroneSettings[ "ball_drone_backup" ].target_recognition = 0.5; // percentage of the player's body it sees before it labels him as a target
|
|
level.ballDroneSettings[ "ball_drone_backup" ].burstMin = 10;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].burstMax = 20;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].pauseMin = 0.15;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].pauseMax = 0.35;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].lockonTime = 0.25;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].playFXCallback = ::backupBuddyPlayFX;
|
|
level.ballDroneSettings[ "ball_drone_backup" ].fxId_light1 = [];
|
|
level.ballDroneSettings[ "ball_drone_backup" ].fxId_light1[ "enemy" ] = LoadFX( "vfx/gameplay/mp/killstreaks/vfx_light_detonator_blink" );
|
|
level.ballDroneSettings[ "ball_drone_backup" ].fxId_light1[ "friendly" ] = LoadFX( "fx/misc/light_mine_blink_friendly" );
|
|
|
|
//ballDrone_setAirNodeMesh();
|
|
|
|
level.ballDrones = [];
|
|
|
|
/#
|
|
SetDevDvarIfUninitialized( "scr_balldrone_timeout", 60.0 );
|
|
SetDevDvarIfUninitialized( "scr_balldrone_debug_position", 0 );
|
|
SetDevDvarIfUninitialized( "scr_balldrone_debug_position_forward", 50.0 );
|
|
SetDevDvarIfUninitialized( "scr_balldrone_debug_position_height", 35.0 );
|
|
SetDevDvarIfUninitialized( "scr_balldrone_debug_path", 0 );
|
|
#/
|
|
}
|
|
|
|
tryUseBallDrone( lifeId, streakName ) // self == player
|
|
{
|
|
return useBallDrone( streakName );
|
|
}
|
|
|
|
useBallDrone( ballDroneType )
|
|
{
|
|
numIncomingVehicles = 1;
|
|
if( self isUsingRemote() )
|
|
{
|
|
return false;
|
|
}
|
|
else if( exceededMaxBallDrones() )
|
|
{
|
|
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
|
return false;
|
|
}
|
|
else if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
|
|
{
|
|
self IPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
|
return false;
|
|
}
|
|
else if( IsDefined( self.ballDrone ) )
|
|
{
|
|
self IPrintLnBold( &"KILLSTREAKS_COMPANION_ALREADY_EXISTS" );
|
|
return false;
|
|
}
|
|
else if ( IsDefined ( self.drones_disabled ))
|
|
{
|
|
self IPrintLnBold( &"KILLSTREAKS_UNAVAILABLE" );
|
|
return false;
|
|
}
|
|
|
|
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
|
incrementFauxVehicleCount();
|
|
|
|
ballDrone = createBallDrone( ballDroneType );
|
|
if( !IsDefined( ballDrone ) )
|
|
{
|
|
if(is_aliens())
|
|
self.drone_failed = true;
|
|
else
|
|
self IPrintLnBold( &"KILLSTREAKS_UNAVAILABLE" );
|
|
|
|
// decrement the faux vehicle count since this failed to spawn
|
|
decrementFauxVehicleCount();
|
|
|
|
return false;
|
|
}
|
|
|
|
self.ballDrone = ballDrone;
|
|
self thread startBallDrone( ballDrone );
|
|
|
|
//level thread teamPlayerCardSplash( level.ballDroneSettings[ ballDroneType ].teamSplash, self, self.team );
|
|
|
|
if ( ballDroneType == "ball_drone_backup" && maps\mp\agents\_agent_utility::getNumOwnedActiveAgentsByType( self, "dog" ) > 0 )
|
|
{
|
|
self maps\mp\gametypes\_missions::processChallenge( "ch_twiceasdeadly" );
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
createBallDrone( ballDroneType ) // self == player
|
|
{
|
|
// Node way
|
|
//closestStartNode = ballDrone_getClosestNode( self.origin );
|
|
//if( IsDefined( closestStartNode.angles ) )
|
|
// startAng = closestStartNode.angles;
|
|
//else
|
|
// startAng = ( 0, 0, 0);
|
|
|
|
//closestNode = ballDrone_getClosestNode( self.origin );
|
|
|
|
//forward = AnglesToForward( self.angles );
|
|
//targetPos = closestNode.origin;
|
|
|
|
//startPos = closestStartNode.origin;
|
|
|
|
// new way
|
|
startAng = self.angles;
|
|
forward = AnglesToForward( self.angles );
|
|
startPos = self.origin + ( forward * 100 ) + Z_OFFSET;
|
|
playerStartPos = self.origin + Z_OFFSET;
|
|
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;
|
|
|
|
right = AnglesToRight( self.angles );
|
|
targetPos = self.origin + ( right * 20 ) + Z_OFFSET;
|
|
trace = BulletTrace( startPos, targetPos, false );
|
|
// make sure we aren't sending it into geo
|
|
attempts = 3;
|
|
while( trace[ "surfacetype" ] != "none" && attempts > 0 )
|
|
{
|
|
targetPos = startPos + ( VectorNormalize( startPos - trace[ "position" ] ) * 5 );
|
|
trace = BulletTrace( startPos, targetPos, false );
|
|
attempts--;
|
|
wait( 0.05 );
|
|
}
|
|
if( attempts <= 0 )
|
|
return;
|
|
|
|
drone = SpawnHelicopter( self, startPos, startAng, level.ballDroneSettings[ ballDroneType ].vehicleInfo, level.ballDroneSettings[ ballDroneType ].modelBase );
|
|
if( !IsDefined( drone ) )
|
|
return;
|
|
|
|
drone EnableAimAssist();
|
|
|
|
drone MakeVehicleNotCollideWithPlayers( true );
|
|
|
|
drone addToBallDroneList();
|
|
drone thread removeFromBallDroneListOnDeath();
|
|
|
|
drone.health = level.ballDroneSettings[ ballDroneType ].health;
|
|
drone.maxHealth = level.ballDroneSettings[ ballDroneType ].maxHealth;
|
|
drone.damageTaken = 0; // how much damage has it taken
|
|
|
|
drone.speed = 140;
|
|
drone.followSpeed = 140;
|
|
drone.owner = self;
|
|
drone.team = self.team;
|
|
drone Vehicle_SetSpeed( drone.speed, 16, 16 );
|
|
drone SetYawSpeed( 120, 90 );
|
|
drone SetNearGoalNotifyDist( 16 );
|
|
drone.ballDroneType = ballDroneType;
|
|
drone SetHoverParams( 30, 10, 5 );
|
|
drone SetOtherEnt(self);
|
|
|
|
// make expendable if it is non-lethal drone
|
|
drone make_entity_sentient_mp( self.team, balldroneType != "ball_drone_backup" );
|
|
if ( IsSentient( drone ) )
|
|
{
|
|
drone SetThreatBiasGroup( "DogsDontAttack" );
|
|
}
|
|
if ( !is_aliens() )
|
|
{
|
|
if( level.teamBased )
|
|
drone maps\mp\_entityheadicons::setTeamHeadIcon( drone.team, ( 0, 0, 25 ) );
|
|
else
|
|
drone maps\mp\_entityheadicons::setPlayerHeadIcon( drone.owner, ( 0, 0, 25 ) );
|
|
}
|
|
// for special settings on different types of drones
|
|
maxPitch = 45;
|
|
maxRoll = 45;
|
|
switch( ballDroneType )
|
|
{
|
|
case "ball_drone_radar":
|
|
maxPitch = 90;
|
|
maxRoll = 90;
|
|
|
|
radar = Spawn( "script_model", self.origin );
|
|
radar.team = self.team;
|
|
radar MakePortableRadar( self );
|
|
drone.radar = radar;
|
|
drone thread radarMover();
|
|
drone.ammo = 99999; // trophy "ammo" for how many things it can shot down before dying
|
|
drone.cameraOffset = distance( drone.origin, drone GetTagOrigin( "camera_jnt" ) ) ;
|
|
drone thread maps\mp\gametypes\_trophy_system::trophyActive( self );
|
|
drone thread ballDrone_handleDamage();
|
|
break;
|
|
|
|
case "ball_drone_backup":
|
|
case "alien_ball_drone":
|
|
case "alien_ball_drone_1":
|
|
case "alien_ball_drone_2":
|
|
case "alien_ball_drone_3":
|
|
case "alien_ball_drone_4":
|
|
turret = SpawnTurret( "misc_turret", drone GetTagOrigin( level.ballDroneSettings[ ballDroneType ].weaponTag ), level.ballDroneSettings[ ballDroneType ].weaponInfo );
|
|
turret LinkTo( drone, level.ballDroneSettings[ ballDroneType ].weaponTag );
|
|
turret SetModel( level.ballDroneSettings[ ballDroneType ].weaponModel );
|
|
turret.angles = drone.angles;
|
|
turret.owner = drone.owner;
|
|
turret.team = self.team;
|
|
turret MakeTurretInoperable();
|
|
turret MakeUnusable();
|
|
turret.vehicle = drone;
|
|
|
|
turret.health = level.ballDroneSettings[ ballDroneType ].health;
|
|
turret.maxHealth = level.ballDroneSettings[ ballDroneType ].maxHealth;
|
|
turret.damageTaken = 0; // how much damage has it taken
|
|
|
|
// when the turret is idle it needs to look at something behind the player
|
|
idleTargetPos = self.origin + ( forward * -100 ) + ( 0, 0, 40 );
|
|
turret.idleTarget = Spawn( "script_origin", idleTargetPos );
|
|
turret.idleTarget.targetname = "test";
|
|
self thread idleTargetMover( turret.idleTarget );
|
|
|
|
if( level.teamBased )
|
|
turret SetTurretTeam( self.team );
|
|
turret SetMode( level.ballDroneSettings[ ballDroneType ].sentryMode );
|
|
turret SetSentryOwner( self );
|
|
turret SetLeftArc( 180 );
|
|
turret SetRightArc( 180 );
|
|
turret SetBottomArc( 50 );
|
|
turret thread ballDrone_attackTargets();
|
|
turret SetTurretMinimapVisible( true, "buddy_turret" );
|
|
|
|
killCamOrigin = ( drone.origin + ( ( AnglesToForward( drone.angles ) * -10 ) + ( AnglesToRight( drone.angles ) * -10 ) ) ) + ( 0, 0, 10 );
|
|
turret.killCamEnt = Spawn( "script_model", killCamOrigin );
|
|
turret.killCamEnt SetScriptMoverKillCam( "explosive" );
|
|
turret.killCamEnt LinkTo( drone );
|
|
//turret.killCamEnt LinkTo( drone, "tag_origin" );
|
|
|
|
drone.turret = turret;
|
|
turret.parent = drone;
|
|
|
|
drone thread ballDrone_backup_handleDamage();
|
|
drone.turret thread ballDrone_backup_turret_handleDamage();
|
|
|
|
// this is for using the vehicle's turret
|
|
//drone SetVehWeapon( level.ballDroneSettings[ ballDroneType ].weaponInfo );
|
|
//drone thread ballDrone_targeting();
|
|
//drone thread ballDrone_attackTargets();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
drone SetMaxPitchRoll( maxPitch, maxRoll );
|
|
|
|
drone.targetPos = targetPos;
|
|
//drone.currentNode = closestNode;
|
|
|
|
drone.attract_strength = 10000;
|
|
drone.attract_range = 150;
|
|
if(!(is_aliens() && isdefined(level.script) && level.script == "mp_alien_last"))
|
|
drone.attractor = Missile_CreateAttractorEnt( drone, drone.attract_strength, drone.attract_range );
|
|
|
|
drone.hasDodged = false;
|
|
drone.stunned = false;
|
|
drone.inactive = false;
|
|
|
|
drone thread watchEMPDamage();
|
|
drone thread ballDrone_watchDeath();
|
|
drone thread ballDrone_watchTimeout();
|
|
drone thread ballDrone_watchOwnerLoss();
|
|
drone thread ballDrone_watchOwnerDeath();
|
|
drone thread ballDrone_watchRoundEnd();
|
|
drone thread ballDrone_enemy_lightFX();
|
|
drone thread ballDrone_friendly_lightFX();
|
|
|
|
// Handle moving platform.
|
|
data = SpawnStruct();
|
|
data.validateAccurateTouching = true;
|
|
data.deathOverrideCallback = ::balldrone_moving_platform_death;
|
|
drone thread maps\mp\_movers::handle_moving_platforms( data );
|
|
|
|
drone.owner maps\mp\_matchdata::logKillstreakEvent( level.ballDroneSettings[ drone.ballDroneType ].streakName, drone.targetPos );
|
|
|
|
return drone;
|
|
}
|
|
|
|
balldrone_moving_platform_death( data )
|
|
{
|
|
if ( !IsDefined( data.lastTouchedPlatform.destroyDroneOnCollision ) || data.lastTouchedPlatform.destroyDroneOnCollision )
|
|
{
|
|
self notify( "death" );
|
|
}
|
|
}
|
|
|
|
idleTargetMover( ent ) // self == player
|
|
{
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
ent endon( "death" );
|
|
|
|
// keep the idleTarget entity behind the player so the turret is always default looking back there
|
|
forward = AnglesToForward( self.angles );
|
|
while( true )
|
|
{
|
|
if( isReallyAlive( self ) && !self isUsingRemote() && AnglesToForward( self.angles ) != forward )
|
|
{
|
|
forward = AnglesToForward( self.angles );
|
|
pos = self.origin + ( forward * -100 ) + ( 0, 0, 40 );
|
|
ent MoveTo( pos, 0.5 );
|
|
}
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
ballDrone_enemy_lightFX() // self == drone
|
|
{
|
|
// non-looping fx
|
|
|
|
self endon( "death" );
|
|
settings = level.ballDroneSettings[ self.ballDroneType ];
|
|
|
|
while ( true )
|
|
{
|
|
foreach( player in level.players )
|
|
{
|
|
if( IsDefined( player ) )
|
|
{
|
|
if( level.teamBased )
|
|
{
|
|
if( player.team != self.team )
|
|
self [[ settings.playFXCallback ]]( "enemy", player );
|
|
}
|
|
else
|
|
{
|
|
if( player != self.owner )
|
|
self [[ settings.playFXCallback ]]( "enemy", player );
|
|
}
|
|
}
|
|
}
|
|
|
|
wait( 1.0 );
|
|
}
|
|
}
|
|
|
|
ballDrone_friendly_lightFX() // self == drone
|
|
{
|
|
// looping fx
|
|
|
|
self endon( "death" );
|
|
settings = level.ballDroneSettings[ self.ballDroneType ];
|
|
|
|
foreach( player in level.players )
|
|
{
|
|
if( IsDefined( player ) )
|
|
{
|
|
if( level.teamBased )
|
|
{
|
|
if( player.team == self.team )
|
|
self [[ settings.playFXCallback ]]( "friendly", player );
|
|
}
|
|
else
|
|
{
|
|
if( player == self.owner )
|
|
self [[ settings.playFXCallback ]]( "friendly", player );
|
|
}
|
|
}
|
|
}
|
|
|
|
self thread watchConnectedPlayFX();
|
|
self thread watchJoinedTeamPlayFX();
|
|
}
|
|
|
|
backupBuddyPlayFX( fof, player ) // self == drone
|
|
{
|
|
settings = level.ballDroneSettings[ self.ballDroneType ];
|
|
|
|
PlayFXOnTagForClients( settings.fxId_light1[ fof ], self.turret, "tag_fx", player );
|
|
PlayFXOnTagForClients( settings.fxId_light1[ fof ], self, "tag_fx", player );
|
|
}
|
|
|
|
radarBuddyPlayFx( fof, player ) // self == drone
|
|
{
|
|
settings = level.ballDroneSettings[ self.ballDroneType ];
|
|
|
|
PlayFXOnTagForClients( settings.fxId_light1[ fof ], self, "tag_fx", player );
|
|
PlayFXOnTagForClients( settings.fxId_light2[ fof ], self, "tag_fx1", player );
|
|
PlayFXOnTagForClients( settings.fxId_light3[ fof ], self, "tag_fx2", player );
|
|
PlayFXOnTagForClients( settings.fxId_light4[ fof ], self, "tag_fx3", player );
|
|
}
|
|
|
|
watchConnectedPlayFX() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
|
|
// play fx for late comers
|
|
while( true )
|
|
{
|
|
level waittill( "connected", player );
|
|
player waittill( "spawned_player" );
|
|
|
|
settings = level.ballDroneSettings[ self.ballDroneType ];
|
|
|
|
if( IsDefined( player ) )
|
|
{
|
|
if( level.teamBased )
|
|
{
|
|
if( player.team == self.team )
|
|
self [[ settings.playFXCallback ]]( "friendly", player );
|
|
else
|
|
self [[ settings.playFXCallback ]]( "enemy", player );
|
|
}
|
|
else
|
|
{
|
|
if( player == self.owner )
|
|
self [[ settings.playFXCallback ]]( "friendly", player );
|
|
else
|
|
self [[ settings.playFXCallback ]]( "enemy", player );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
watchJoinedTeamPlayFX() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
|
|
// play fx for team changers
|
|
while( true )
|
|
{
|
|
level waittill( "joined_team", player );
|
|
player waittill( "spawned_player" );
|
|
|
|
settings = level.ballDroneSettings[ self.ballDroneType ];
|
|
|
|
if( IsDefined( player ) )
|
|
{
|
|
if( level.teamBased )
|
|
{
|
|
if( player.team == self.team )
|
|
self [[ settings.playFXCallback ]]( "friendly", player );
|
|
else
|
|
self [[ settings.playFXCallback ]]( "enemy", player );
|
|
}
|
|
else
|
|
{
|
|
if( player == self.owner )
|
|
self [[ settings.playFXCallback ]]( "friendly", player );
|
|
else
|
|
self [[ settings.playFXCallback ]]( "enemy", player );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
startBallDrone( ballDrone ) // self == player
|
|
{
|
|
level endon( "game_ended" );
|
|
ballDrone endon( "death" );
|
|
|
|
switch( ballDrone.ballDroneType )
|
|
{
|
|
case "ball_drone_backup":
|
|
case "alien_ball_drone":
|
|
case "alien_ball_drone_1":
|
|
case "alien_ball_drone_2":
|
|
case "alien_ball_drone_3":
|
|
case "alien_ball_drone_4":
|
|
// watch the player's back
|
|
if( IsDefined( ballDrone.turret ) && IsDefined( ballDrone.turret.idleTarget ) )
|
|
ballDrone SetLookAtEnt( ballDrone.turret.idleTarget );
|
|
else
|
|
ballDrone SetLookAtEnt( self );
|
|
break;
|
|
|
|
default:
|
|
// look at the player
|
|
ballDrone SetLookAtEnt( self );
|
|
break;
|
|
}
|
|
|
|
// go to pos
|
|
targetOffset = (0, 0, BALL_DRONE_STAND_UP_OFFSET);
|
|
ballDrone SetDroneGoalPos( self, targetOffset );
|
|
ballDrone waittill( "near_goal" );
|
|
ballDrone Vehicle_SetSpeed( ballDrone.speed, 10, 10 );
|
|
ballDrone waittill( "goal" );
|
|
|
|
// begin following player
|
|
ballDrone thread ballDrone_followPlayer();
|
|
}
|
|
|
|
ballDrone_followPlayer() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
if( !IsDefined( self.owner ) )
|
|
{
|
|
self thread ballDrone_leave();
|
|
return;
|
|
}
|
|
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
self Vehicle_SetSpeed( self.followSpeed, 10, 10 );
|
|
previousOrigin = ( 0, 0, 0 );
|
|
destRadiusSq = 64 * 64;
|
|
|
|
self thread low_entries_watcher();
|
|
|
|
while( true )
|
|
{
|
|
if( IsDefined( self.owner ) && IsAlive( self.owner ) )
|
|
{
|
|
// check to see if the player has moved
|
|
// make sure the turret isn't currently trying to shoot anyone
|
|
// check if player is still within a radius
|
|
if( self.owner.origin != previousOrigin &&
|
|
DistanceSquared( self.owner.origin, previousOrigin ) > destRadiusSq )
|
|
{
|
|
if( self.ballDroneType == "ball_drone_backup" || self.ballDroneType == "alien_ball_drone" || self.ballDroneType == "alien_ball_drone_1" || self.ballDroneType == "alien_ball_drone_2" || self.ballDroneType == "alien_ball_drone_3" || self.ballDroneType == "alien_ball_drone_4" )
|
|
{
|
|
if( !IsDefined( self.turret GetTurretTarget( false ) ) )
|
|
{
|
|
previousOrigin = self.owner.origin;
|
|
ballDrone_moveToPlayer();
|
|
continue;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
previousOrigin = self.owner.origin;
|
|
ballDrone_moveToPlayer();
|
|
continue;
|
|
}
|
|
}
|
|
}
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
ballDrone_moveToPlayer() // 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( "ballDrone_moveToPlayer" );
|
|
self endon( "ballDrone_moveToPlayer" );
|
|
|
|
|
|
// collect the ideal offsets from the player
|
|
backOffset = BALL_DRONE_BACK_OFFSET;
|
|
|
|
sideOffset = BALL_DRONE_SIDE_OFFSET;
|
|
|
|
heightOffset = BALL_DRONE_STAND_UP_OFFSET;
|
|
switch( self.owner 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;
|
|
}
|
|
|
|
// If ball drone is touching a low_entry volume, we adjust the height by a custom offset to allow for low clearance situations
|
|
if ( IsDefined( self.low_entry ) )
|
|
heightOffset = heightOffset * self.low_entry;
|
|
|
|
targetOffset = (sideOffset, backOffset, heightOffset);
|
|
|
|
/#
|
|
if( GetDvarInt( "scr_balldrone_debug_position" ) )
|
|
{
|
|
targetOffset = (0, -1*GetDvarFloat( "scr_balldrone_debug_position_forward" ), GetDvarFloat( "scr_balldrone_debug_position_height" ) );
|
|
}
|
|
#/
|
|
|
|
// 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( self.owner, targetOffset );
|
|
self.inTransit = true;
|
|
self thread ballDrone_watchForGoal();
|
|
}
|
|
|
|
/#
|
|
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 );
|
|
}
|
|
}
|
|
#/
|
|
|
|
ballDrone_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( "ballDrone_watchForGoal" );
|
|
self endon( "ballDrone_watchForGoal" );
|
|
|
|
result = self waittill_any_return( "goal", "near_goal", "hit_goal" );
|
|
self.inTransit = false;
|
|
self.inactive = false;
|
|
self notify( "hit_goal" );
|
|
}
|
|
|
|
radarMover() // self == drone
|
|
{
|
|
level endon("game_ended");
|
|
self endon( "death" );
|
|
|
|
while( true )
|
|
{
|
|
if( IsDefined( self.stunned ) && self.stunned )
|
|
{
|
|
wait( 0.5 );
|
|
continue;
|
|
}
|
|
if( IsDefined( self.inactive ) && self.inactive )
|
|
{
|
|
wait( 0.5 );
|
|
continue;
|
|
}
|
|
|
|
if( IsDefined( self.radar ) )
|
|
self.radar MoveTo( self.origin, 0.5 );
|
|
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
low_entries_watcher()
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "gone" );
|
|
self endon( "death" );
|
|
|
|
// Users can add as many trigger volumes as they need for tight spaces. KVPs needed are:
|
|
// targetname: low_entry
|
|
// script_parameters: X (where X is a number, 0-1, that represents a fraction of the default height that you need the drone to lower to while inside the volume
|
|
//
|
|
// If no script_parameters are defined, it will default to half the default height
|
|
low_entries = GetEntArray( "low_entry", "targetname" );
|
|
|
|
while( low_entries.size > 0 ) // bails on the thread if there are no low_entry volumes present
|
|
{
|
|
foreach( trigger in low_entries )
|
|
{
|
|
while( self IsTouching( trigger ) || self.owner IsTouching( trigger ) )
|
|
{
|
|
if ( IsDefined( trigger.script_parameters ) )
|
|
self.low_entry = float( trigger.script_parameters );
|
|
else
|
|
self.low_entry = 0.5;
|
|
|
|
wait 0.1;
|
|
}
|
|
|
|
self.low_entry = undefined;
|
|
}
|
|
|
|
wait 0.1;
|
|
}
|
|
}
|
|
|
|
|
|
/* ============================
|
|
State Trackers
|
|
============================ */
|
|
|
|
ballDrone_watchDeath() // self == drone
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "gone" );
|
|
|
|
self waittill( "death" );
|
|
|
|
self thread ballDroneDestroyed();
|
|
}
|
|
|
|
|
|
ballDrone_watchTimeout() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
self endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
self endon( "owner_gone" );
|
|
|
|
config = level.ballDroneSettings[ self.ballDroneType ];
|
|
timeout = config.timeOut;
|
|
if ( is_aliens() && isDefined( level.ball_drone_alien_timeout_func ) && isDefined( self.owner ) )
|
|
{
|
|
timeout = self [[level.ball_drone_alien_timeout_func]]( timeout, self.owner );
|
|
}
|
|
if ( !is_aliens() )
|
|
{
|
|
/#
|
|
timeout = GetDvarFloat( "scr_balldrone_timeout" );
|
|
#/
|
|
}
|
|
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeout );
|
|
if( IsDefined( self.owner ) && !is_aliens() )
|
|
self.owner leaderDialogOnPlayer( config.voTimedOut );
|
|
|
|
self thread ballDrone_leave();
|
|
}
|
|
|
|
|
|
ballDrone_watchOwnerLoss() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
self.owner waittill( "killstreak_disowned" );
|
|
|
|
self notify( "owner_gone" );
|
|
// leave
|
|
self thread ballDrone_leave();
|
|
}
|
|
|
|
ballDrone_watchOwnerDeath() // self == drone
|
|
{
|
|
level endon ( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "leaving" );
|
|
|
|
while( true )
|
|
{
|
|
self.owner waittill( "death" );
|
|
|
|
if( getGametypeNumLives() && self.owner.pers[ "deaths" ] == getGametypeNumLives() )
|
|
self thread ballDrone_leave();
|
|
// else
|
|
// self.inactive = true;
|
|
}
|
|
}
|
|
|
|
ballDrone_watchRoundEnd() // self == drone
|
|
{
|
|
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 ballDrone_leave();
|
|
}
|
|
|
|
ballDrone_leave() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
self notify( "leaving" );
|
|
|
|
ballDroneExplode();
|
|
}
|
|
|
|
/* ============================
|
|
End State Trackers
|
|
============================ */
|
|
|
|
/* ============================
|
|
Damage and Death Monitors
|
|
============================ */
|
|
|
|
ballDrone_handleDamage() // self == drone
|
|
{
|
|
self maps\mp\gametypes\_damage::monitorDamage(
|
|
self.maxHealth,
|
|
"ball_drone",
|
|
::handleDeathDamage,
|
|
::modifyDamage,
|
|
true // isKillstreak
|
|
);
|
|
}
|
|
|
|
ballDrone_backup_handleDamage() // self == drone
|
|
{
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
self SetCanDamage( true );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
|
|
|
self maps\mp\gametypes\_damage::monitorDamageOneShot(
|
|
damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon,
|
|
"ball_drone",
|
|
::handleDeathDamage,
|
|
::modifyDamage,
|
|
true // isKillstreak
|
|
);
|
|
}
|
|
}
|
|
|
|
ballDrone_backup_turret_handleDamage() // self == turret attached to drone
|
|
{
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
self MakeTurretSolid();
|
|
self SetCanDamage( true );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
|
|
|
// if this is explosive damage then don't do it on the turret because the tank will do it, unless this is an airstrike or stealth bomb
|
|
if( IsDefined( self.parent ) )
|
|
{
|
|
self.parent maps\mp\gametypes\_damage::monitorDamageOneShot(
|
|
damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon,
|
|
"ball_drone",
|
|
::handleDeathDamage,
|
|
::modifyDamage,
|
|
true // isKillstreak
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
modifyDamage( attacker, weapon, type, damage )
|
|
{
|
|
modifiedDamage = damage;
|
|
|
|
// for stuns
|
|
// self thread ballDrone_stunned();
|
|
|
|
// modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
|
// modifiedDamage = self maps\mp\gametypes\_damage::handleEmpDamage( weapon, type, modifiedDamage );
|
|
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
|
modifiedDamage = self maps\mp\gametypes\_damage::handleGrenadeDamage( weapon, type, modifiedDamage );
|
|
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
|
|
|
return modifiedDamage;
|
|
}
|
|
|
|
handleDeathDamage( attacker, weapon, type, damage ) // self == drone
|
|
{
|
|
config = level.ballDroneSettings[ self.ballDroneType ];
|
|
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, config.xpPopup, config.voDestroyed );
|
|
|
|
if ( self.ballDroneType == "ball_drone_backup" )
|
|
{
|
|
attacker maps\mp\gametypes\_missions::processChallenge( "ch_vulturekiller" );
|
|
}
|
|
/*
|
|
else if ( self.ballDroneType == "ball_drone_radar" )
|
|
{
|
|
attacker maps\mp\gametypes\_missions::processChallenge( "???" );
|
|
}
|
|
*/
|
|
}
|
|
|
|
watchEMPDamage()
|
|
{
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
while( true )
|
|
{
|
|
// this handles any flash or concussion damage
|
|
self waittill( "emp_damage", attacker, duration );
|
|
|
|
self ballDrone_stunned( duration );
|
|
}
|
|
}
|
|
|
|
ballDrone_stunned( duration ) // self == drone
|
|
{
|
|
self notify( "ballDrone_stunned" );
|
|
self endon( "ballDrone_stunned" );
|
|
|
|
self endon( "death" );
|
|
self.owner endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
|
|
self.stunned = true;
|
|
|
|
if( IsDefined( level.ballDroneSettings[ self.ballDroneType ].fxId_sparks ) )
|
|
{
|
|
PlayFXOnTag( level.ballDroneSettings[ self.ballDroneType ].fxId_sparks, self, "tag_origin" );
|
|
}
|
|
|
|
// for the portable radar we need to destroy it and recreate it
|
|
if( self.ballDroneType == "ball_drone_radar" )
|
|
{
|
|
if( IsDefined( self.radar ) )
|
|
self.radar delete();
|
|
}
|
|
|
|
if ( IsDefined( self.turret ) )
|
|
{
|
|
self.turret notify( "turretstatechange" );
|
|
}
|
|
|
|
wait( duration );
|
|
|
|
self.stunned = false;
|
|
|
|
if( self.ballDroneType == "ball_drone_radar" )
|
|
{
|
|
radar = Spawn( "script_model", self.origin );
|
|
radar.team = self.team;
|
|
radar MakePortableRadar( self.owner );
|
|
self.radar = radar;
|
|
}
|
|
|
|
if ( IsDefined( self.turret ) )
|
|
{
|
|
self.turret notify( "turretstatechange" );
|
|
}
|
|
}
|
|
|
|
ballDroneDestroyed() // self == drone
|
|
{
|
|
if( !IsDefined( self ) )
|
|
return;
|
|
|
|
// TODO: could put some drama here as it crashes
|
|
|
|
ballDroneExplode();
|
|
}
|
|
|
|
ballDroneExplode() // self == drone
|
|
{
|
|
if( IsDefined( level.ballDroneSettings[ self.ballDroneType ].fxId_explode ) )
|
|
{
|
|
PlayFX( level.ballDroneSettings[ self.ballDroneType ].fxId_explode, self.origin );
|
|
}
|
|
|
|
if( IsDefined( level.ballDroneSettings[ self.ballDroneType ].sound_explode ) )
|
|
{
|
|
self PlaySound( level.ballDroneSettings[ self.ballDroneType ].sound_explode );
|
|
}
|
|
|
|
self notify( "explode" );
|
|
|
|
self removeBallDrone();
|
|
}
|
|
|
|
removeBallDrone() // 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.radar ) )
|
|
self.radar delete();
|
|
|
|
if( IsDefined( self.turret ) )
|
|
{
|
|
self.turret SetTurretMinimapVisible( false );
|
|
|
|
if( IsDefined( self.turret.idleTarget ) )
|
|
self.turret.idleTarget delete();
|
|
|
|
if( IsDefined( self.turret.killCamEnt ) )
|
|
self.turret.killCamEnt delete();
|
|
|
|
self.turret delete();
|
|
}
|
|
|
|
if( IsDefined( self.owner ) && IsDefined( self.owner.ballDrone ) )
|
|
self.owner.ballDrone = undefined;
|
|
|
|
self delete();
|
|
}
|
|
|
|
/* ============================
|
|
End Damage and Death Monitors
|
|
============================ */
|
|
|
|
/* ============================
|
|
List and Count Management
|
|
============================ */
|
|
|
|
addToBallDroneList()
|
|
{
|
|
level.ballDrones[ self GetEntityNumber() ] = self;
|
|
}
|
|
|
|
removeFromBallDroneListOnDeath()
|
|
{
|
|
entNum = self GetEntityNumber();
|
|
|
|
self waittill ( "death" );
|
|
|
|
level.ballDrones[ entNum ] = undefined;
|
|
}
|
|
|
|
exceededMaxBallDrones()
|
|
{
|
|
if( level.ballDrones.size >= maxVehiclesAllowed() )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
/* ============================
|
|
End List and Count Management
|
|
============================ */
|
|
|
|
/* ============================
|
|
Turret Logic Functions
|
|
============================ */
|
|
|
|
ballDrone_attackTargets() // self == turret
|
|
{
|
|
self.vehicle endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "turretstatechange" );
|
|
|
|
if( self IsFiringTurret() &&
|
|
( IsDefined( self.vehicle.stunned ) && !self.vehicle.stunned ) &&
|
|
( IsDefined( self.vehicle.inactive ) && !self.vehicle.inactive ) )
|
|
{
|
|
self LaserOn();
|
|
self doLockOn( level.ballDroneSettings[ self.vehicle.ballDroneType ].lockonTime );
|
|
self thread ballDrone_burstFireStart();
|
|
}
|
|
else
|
|
{
|
|
self LaserOff();
|
|
self thread ballDrone_burstFireStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
ballDrone_burstFireStart() // self == turret
|
|
{
|
|
self.vehicle endon( "death" );
|
|
self endon( "stop_shooting" );
|
|
level endon( "game_ended" );
|
|
|
|
vehicle = self.vehicle;
|
|
|
|
fireTime = WeaponFireTime( level.ballDroneSettings[ vehicle.ballDroneType ].weaponInfo );
|
|
minShots = level.ballDroneSettings[ vehicle.ballDroneType ].burstMin;
|
|
maxShots = level.ballDroneSettings[ vehicle.ballDroneType ].burstMax;
|
|
minPause = level.ballDroneSettings[ vehicle.ballDroneType ].pauseMin;
|
|
maxPause = level.ballDroneSettings[ vehicle.ballDroneType ].pauseMax;
|
|
|
|
if ( is_aliens() && level.ballDroneSettings[ vehicle.ballDroneType ].weaponInfo == "alien_ball_drone_gun4_mp" )
|
|
self childthread fire_rocket();
|
|
|
|
while( true )
|
|
{
|
|
numShots = RandomIntRange( minShots, maxShots + 1 );
|
|
for( i = 0; i < numShots; i++ )
|
|
{
|
|
// don't shoot when inactive
|
|
if( IsDefined( vehicle.inactive ) && vehicle.inactive )
|
|
break;
|
|
|
|
targetEnt = self GetTurretTarget( false );
|
|
if( IsDefined( targetEnt ) && canBeTargeted( targetEnt ) )
|
|
{
|
|
vehicle SetLookAtEnt( targetEnt );
|
|
//self PlaySound( level.ballDroneSettings[ vehicle.ballDroneType ].sound_weapon );
|
|
self ShootTurret();
|
|
//MagicBullet( level.ballDroneSettings[ vehicle.ballDroneType ].projectileInfo, self GetTagOrigin( "tag_flash" ), targetEnt.origin, self.owner );
|
|
}
|
|
|
|
wait( fireTime );
|
|
}
|
|
|
|
wait( RandomFloatRange( minPause, maxPause ) );
|
|
}
|
|
}
|
|
|
|
fire_rocket( )
|
|
{
|
|
while ( true )
|
|
{
|
|
targetEnt = self GetTurretTarget( false );
|
|
if( IsDefined( targetEnt ) && canBeTargeted( targetEnt ) )
|
|
{
|
|
MagicBullet( "alienvulture_mp", self GetTagOrigin( "tag_flash" ), targetEnt.origin, self.owner );
|
|
}
|
|
waittime = WeaponFireTime( "alienvulture_mp" );
|
|
|
|
if ( isDefined( level.ball_drone_faster_rocket_func ) && isDefined( self.owner ) )
|
|
{
|
|
waittime = self [[level.ball_drone_faster_rocket_func]]( waittime, self.owner );
|
|
}
|
|
|
|
wait WeaponFireTime( "alienvulture_mp" );
|
|
}
|
|
}
|
|
|
|
doLockOn( time ) // self == turret
|
|
{
|
|
// lock-on time
|
|
while( time > 0 )
|
|
{
|
|
self PlaySound( level.ballDroneSettings[ self.vehicle.ballDroneType ].sound_targeting );
|
|
|
|
wait( 0.5 );
|
|
time -= 0.5;
|
|
}
|
|
|
|
// locked on
|
|
self PlaySound( level.ballDroneSettings[ self.vehicle.ballDroneType ].sound_lockon );
|
|
}
|
|
|
|
ballDrone_burstFireStop() // self == turret
|
|
{
|
|
self notify( "stop_shooting" );
|
|
if( IsDefined( self.idleTarget ) )
|
|
self.vehicle SetLookAtEnt( self.idleTarget );
|
|
}
|
|
|
|
canBeTargeted( ent ) // self == turret
|
|
{
|
|
canTarget = true;
|
|
|
|
if( IsPlayer( ent ) )
|
|
{
|
|
if( !isReallyAlive( ent ) || ent.sessionstate != "playing" )
|
|
return false;
|
|
}
|
|
|
|
if( level.teamBased && IsDefined( ent.team ) && ent.team == self.team )
|
|
return false;
|
|
|
|
if( IsDefined( ent.team ) && ent.team == "spectator" )
|
|
return false;
|
|
|
|
if( IsPlayer( ent ) && ent == self.owner )
|
|
return false;
|
|
|
|
if( IsPlayer( ent ) && IsDefined( ent.spawntime ) && ( GetTime() - ent.spawntime ) / 1000 <= 5 )
|
|
return false;
|
|
|
|
if( IsPlayer( ent ) && ent _hasPerk( "specialty_blindeye" ) )
|
|
return false;
|
|
|
|
if( DistanceSquared( ent.origin, self.origin ) > level.ballDroneSettings[ self.vehicle.ballDroneType ].visual_range_sq )
|
|
return false;
|
|
|
|
turret_point = self GetTagOrigin( "tag_flash" );
|
|
|
|
return canTarget;
|
|
} |