#include maps\mp\_utility; #include maps\mp\gametypes\_hud_util; #include common_scripts\utility; UAV_REMOTE_FLY_TIME = 60; UAV_REMOTE_AIM_ASSIST_RANGE = 200; UAV_REMOTE_MAX_PAST_RANGE = 200; UAV_REMOTE_MIN_HELI_PROXIMITY = 150; UAV_REMOTE_MAX_HELI_PROXIMITY = 300; UAV_REMOTE_PAST_RANGE_COUNTDOWN = 6; UAV_REMOTE_HELI_RANGE_COUNTDOWN = 3; UAV_REMOTE_COLLISION_RADIUS = 18; UAV_REMOTE_Z_OFFSET = -9; init() { level.RemoteUAV_fx["hit"] = loadfx("fx/impacts/large_metal_painted_hit"); level.RemoteUAV_fx["smoke"] = loadfx( "fx/smoke/remote_heli_damage_smoke_runner" ); level.RemoteUAV_fx["explode"] = loadfx( "fx/explosions/bouncing_betty_explosion" ); level.RemoteUAV_fx["missile_explode"] = loadfx( "fx/explosions/stinger_explosion" ); level.RemoteUAV_dialog["launch"][0] = "ac130_plt_yeahcleared"; level.RemoteUAV_dialog["launch"][1] = "ac130_plt_rollinin"; level.RemoteUAV_dialog["launch"][2] = "ac130_plt_scanrange"; level.RemoteUAV_dialog["out_of_range"][0] = "ac130_plt_cleanup"; level.RemoteUAV_dialog["out_of_range"][1] = "ac130_plt_targetreset"; level.RemoteUAV_dialog["track"][0] = "ac130_fco_moreenemy"; level.RemoteUAV_dialog["track"][1] = "ac130_fco_getthatguy"; level.RemoteUAV_dialog["track"][2] = "ac130_fco_guymovin"; level.RemoteUAV_dialog["track"][3] = "ac130_fco_getperson"; level.RemoteUAV_dialog["track"][4] = "ac130_fco_guyrunnin"; level.RemoteUAV_dialog["track"][5] = "ac130_fco_gotarunner"; level.RemoteUAV_dialog["track"][6] = "ac130_fco_backonthose"; level.RemoteUAV_dialog["track"][7] = "ac130_fco_gonnagethim"; level.RemoteUAV_dialog["track"][8] = "ac130_fco_personnelthere"; level.RemoteUAV_dialog["track"][9] = "ac130_fco_rightthere"; level.RemoteUAV_dialog["track"][10] = "ac130_fco_tracking"; level.RemoteUAV_dialog["tag"][0] = "ac130_fco_nice"; level.RemoteUAV_dialog["tag"][1] = "ac130_fco_yougothim"; level.RemoteUAV_dialog["tag"][2] = "ac130_fco_yougothim2"; level.RemoteUAV_dialog["tag"][3] = "ac130_fco_okyougothim"; level.RemoteUAV_dialog["assist"][0] = "ac130_fco_goodkill"; level.RemoteUAV_dialog["assist"][1] = "ac130_fco_thatsahit"; level.RemoteUAV_dialog["assist"][2] = "ac130_fco_directhit"; level.RemoteUAV_dialog["assist"][3] = "ac130_fco_rightontarget"; level.RemoteUAV_lastDialogTime = 0; level.RemoteUAV_noDeployZones = GetEntArray( "no_vehicles", "targetname" ); level.killstreakFuncs["remote_uav"] = ::useRemoteUAV; level.remote_uav = []; /# SetDevDvarIfUninitialized( "scr_remoteUAVFlyTime", 60 ); #/ } useRemoteUAV( lifeId, streakName ) { return tryUseRemoteUAV( lifeId, "remote_uav" ); } exceededMaxRemoteUAVs( team ) { if ( level.gameType == "dm" ) { if ( isDefined( level.remote_uav[team] ) || isDefined( level.remote_uav[level.otherTeam[team]] ) ) return true; else return false; } else { if ( isDefined( level.remote_uav[team] ) ) return true; else return false; } } tryUseRemoteUAV( lifeId, streakName ) { self _disableUsability(); if ( self isUsingRemote() || self isUsingTurret() || isDefined( level.nukeIncoming ) ) { self _enableUsability(); return false; } numIncomingVehicles = 1; if ( exceededMaxRemoteUAVs( self.team ) || level.littleBirds.size >= 4 ) { self iPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" ); self _enableUsability(); return false; } else if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() ) { self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" ); self _enableUsability(); return false; } // ui vars self setPlayerData( "reconDroneState", "staticAlpha", 0 ); self setPlayerData( "reconDroneState", "incomingMissile", false ); // increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn incrementFauxVehicleCount(); result = self giveCarryRemoteUAV( lifeId, streakName ); if ( result ) { self maps\mp\_matchdata::logKillstreakEvent( streakName, self.origin ); self thread teamPlayerCardSplash( "used_remote_uav", self ); } else { // decrement the faux vehicle count since this failed to spawn decrementFauxVehicleCount(); } self.isCarrying = false; return ( result ); } giveCarryRemoteUAV( lifeId, streakName ) { // create carry object carryRemoteUAV = createCarryRemoteUAV( streakName, self ); // get rid of clicker and give hand model self takeWeapon( "killstreak_uav_mp" ); self _giveWeapon( "killstreak_remote_uav_mp" ); self SwitchToWeaponImmediate( "killstreak_remote_uav_mp" ); // give carry object and wait for placement (blocking loop) self setCarryingRemoteUAV( carryRemoteUAV ); // we're back, what happened? if ( isAlive( self ) && isDefined( carryRemoteUAV ) ) { // if it placed, start the killstreak at that location origin = carryRemoteUAV.origin; angles = self.angles; carryRemoteUAV.soundEnt delete(); carryRemoteUAV delete(); result = self startRemoteUAV( lifeId, streakName, origin, angles ); } else { // cancelled placement or died result = false; if ( isAlive( self ) ) { // get rid of hand model self takeWeapon( "killstreak_remote_uav_mp" ); // give back the clicker to be able to active killstreak again self _giveWeapon( "killstreak_uav_mp" ); } } return result; } // Carry Remote UAV createCarryRemoteUAV( streakName, owner ) { pos = owner.origin + ( anglesToForward( owner.angles ) * 4 ) + ( anglesToUp( owner.angles ) * 50 ); carryRemoteUAV = spawnTurret( "misc_turret", pos, "sentry_minigun_mp" ); carryRemoteUAV.origin = pos; carryRemoteUAV.angles = owner.angles; carryRemoteUAV.sentryType = "sentry_minigun"; carryRemoteUAV.canBePlaced = true; carryRemoteUAV setTurretModeChangeWait( true ); carryRemoteUAV setMode( "sentry_offline" ); carryRemoteUAV makeUnusable(); carryRemoteUAV makeTurretInoperable(); carryRemoteUAV.owner = owner; carryRemoteUAV SetSentryOwner( carryRemoteUAV.owner ); carryRemoteUAV.scale = 3; carryRemoteUAV.inHeliProximity = false; carryRemoteUAV thread carryRemoteUAV_handleExistence(); carryRemoteUAV.rangeTrigger = GetEnt( "remote_uav_range", "targetname" ); if ( !isDefined( carryRemoteUAV.rangeTrigger ) ) { heightEnt = GetEnt( "airstrikeheight", "targetname" ); carryRemoteUAV.maxHeight = heightEnt.origin[2]; carryRemoteUAV.maxDistance = 3600; } // apparently can't call playLoopSound on a turret? carryRemoteUAV.soundEnt = spawn( "script_origin", carryRemoteUAV.origin ); carryRemoteUAV.soundEnt.angles = carryRemoteUAV.angles; carryRemoteUAV.soundEnt.origin = carryRemoteUAV.origin; carryRemoteUAV.soundEnt linkTo( carryRemoteUAV ); carryRemoteUAV.soundEnt playLoopSound( "recondrone_idle_high" ); return carryRemoteUAV; } setCarryingRemoteUAV( carryRemoteUAV ) { carryRemoteUAV thread carryRemoteUAV_setCarried( self ); self notifyOnPlayerCommand( "place_carryRemoteUAV", "+attack" ); self notifyOnPlayerCommand( "place_carryRemoteUAV", "+attack_akimbo_accessible" ); // support accessibility control scheme self notifyOnPlayerCommand( "cancel_carryRemoteUAV", "+actionslot 4" ); if( !level.console ) { self notifyOnPlayerCommand( "cancel_carryRemoteUAV", "+actionslot 5" ); self notifyOnPlayerCommand( "cancel_carryRemoteUAV", "+actionslot 6" ); self notifyOnPlayerCommand( "cancel_carryRemoteUAV", "+actionslot 7" ); } for ( ;; ) { result = local_waittill_any_return( "place_carryRemoteUAV", "cancel_carryRemoteUAV", "weapon_switch_started", "force_cancel_placement", "death", "disconnect" ); self forceUseHintOff(); if ( result != "place_carryRemoteUAV" ) { self carryRemoteUAV_delete( carryRemoteUAV ); break; } if ( !carryRemoteUAV.canBePlaced ) { if ( self.team != "spectator" ) self ForceUseHintOn( &"KILLSTREAKS_REMOTE_UAV_CANNOT_PLACE" ); continue; } if( isDefined( level.nukeIncoming ) || self isEMPed() || exceededMaxRemoteUAVs( self.team ) || currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount >= maxVehiclesAllowed() ) { if ( isDefined( level.nukeIncoming ) || self isEMPed() ) { self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE_FOR_N_WHEN_EMP", level.empTimeRemaining ); } else { self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" ); } self carryRemoteUAV_delete( carryRemoteUAV ); break; } self.isCarrying = false; carryRemoteUAV.carriedBy = undefined; carryRemoteUAV playSound( "sentry_gun_plant" ); carryRemoteUAV notify ( "placed" ); break; } } local_waittill_any_return( string1, string2, string3, string4, string5, string6 ) { if ( ( !isdefined( string1 ) || string1 != "death" ) && ( !isdefined( string2 ) || string2 != "death" ) && ( !isdefined( string3 ) || string3 != "death" ) && ( !isdefined( string4 ) || string4 != "death" ) && ( !isdefined( string5 ) || string5 != "death" ) ) self endon( "death" ); ent = spawnstruct(); if ( isdefined( string1 ) ) self thread waittill_string( string1, ent ); if ( isdefined( string2 ) ) self thread waittill_string( string2, ent ); if ( isdefined( string3 ) ) self thread waittill_string( string3, ent ); if ( isdefined( string4 ) ) self thread waittill_string( string4, ent ); if ( isdefined( string5 ) ) self thread waittill_string( string5, ent ); if ( isdefined( string6 ) ) self thread waittill_string( string6, ent ); ent waittill( "returned", msg ); ent notify( "die" ); return msg; } carryRemoteUAV_setCarried( carrier ) { self setCanDamage( false ); self setSentryCarrier( carrier ); self setContents( 0 ); self.carriedBy = carrier; carrier.isCarrying = true; carrier thread updateCarryRemoteUAVPlacement( self ); self notify ( "carried" ); } carryRemoteUAV_delete( carryRemoteUAV ) { self.isCarrying = false; if ( isDefined( carryRemoteUAV ) ) { if ( isDefined( carryRemoteUAV.soundEnt ) ) { carryRemoteUAV.soundEnt delete(); } carryRemoteUAV delete(); } } isInRemoteNoDeploy() { if ( isDefined( level.RemoteUAV_noDeployZones ) && level.RemoteUAV_noDeployZones.size ) { foreach( zone in level.RemoteUAV_noDeployZones ) { if ( self isTouching( zone ) ) return true; } } return false; } updateCarryRemoteUAVPlacement( carryRemoteUAV ) { self endon ( "death" ); self endon ( "disconnect" ); level endon ( "game_ended" ); carryRemoteUAV endon ( "placed" ); carryRemoteUAV endon ( "death" ); carryRemoteUAV.canBePlaced = true; lastCanPlaceCarryRemoteUAV = -1; // force initial update self _enableUsability(); for( ;; ) { heightOffset = UAV_REMOTE_COLLISION_RADIUS; switch( self getStance() ) { case "stand": heightOffset = 40; break; case "crouch": heightOffset = 25; break; case "prone": heightOffset = 10; break; } placement = self CanPlayerPlaceTank( 22, 22, 50, heightOffset, 0, 0 ); carryRemoteUAV.origin = placement[ "origin" ] + ( anglesToUp(self.angles) * ( UAV_REMOTE_COLLISION_RADIUS - UAV_REMOTE_Z_OFFSET ) ); carryRemoteUAV.angles = placement[ "angles" ]; carryRemoteUAV.canBePlaced = self isOnGround() && placement[ "result" ] && carryRemoteUAV remoteUAV_in_range() && !carryRemoteUAV isInRemoteNoDeploy(); if ( carryRemoteUAV.canBePlaced != lastCanPlaceCarryRemoteUAV ) { if ( carryRemoteUAV.canBePlaced ) { if ( self.team != "spectator" ) self ForceUseHintOn( &"KILLSTREAKS_REMOTE_UAV_PLACE" ); // if they're holding it in launch position just launch now if ( self attackButtonPressed() ) self notify( "place_carryRemoteUAV" ); } else { if ( self.team != "spectator" ) self ForceUseHintOn( &"KILLSTREAKS_REMOTE_UAV_CANNOT_PLACE" ); } } lastCanPlaceCarryRemoteUAV = carryRemoteUAV.canBePlaced; wait ( 0.05 ); } } carryRemoteUAV_handleExistence() { level endon ( "game_ended" ); self.owner endon ( "place_carryRemoteUAV" ); self.owner endon ( "cancel_carryRemoteUAV" ); self.owner waittill_any( "death", "disconnect", "joined_team", "joined_spectators" ); if ( isDefined( self ) ) { if ( isDefined( self.soundEnt ) ) self.soundEnt delete(); self delete(); } } // Remote UAV removeRemoteWeapon() { level endon( "game_ended" ); self endon ( "disconnect" ); wait(0.7); } startRemoteUAV( lifeId, streakName, origin, angles ) { self lockPlayerForRemoteUAVLaunch(); self setUsingRemote( streakName ); self _giveWeapon("uav_remote_mp"); self SwitchToWeaponImmediate("uav_remote_mp"); self VisionSetNakedForPlayer( "black_bw", 0.0 ); result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( "remote_uav" ); if ( result != "success" ) { if ( result != "disconnect" ) { self notify( "remoteuav_unlock" ); self takeWeapon("uav_remote_mp"); self clearUsingRemote(); } return false; } if( exceededMaxRemoteUAVs( self.team ) || currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount >= maxVehiclesAllowed() ) { self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" ); self notify( "remoteuav_unlock" ); self takeWeapon("uav_remote_mp"); self clearUsingRemote(); return false; } self notify( "remoteuav_unlock" ); remoteUAV = createRemoteUAV( lifeId, self, streakName, origin, angles ); if ( isDefined( remoteUAV ) ) { self thread remoteUAV_Ride( lifeId, remoteUAV, streakName ); return true; } else { self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" ); self takeWeapon("uav_remote_mp"); self clearUsingRemote(); return false; } } lockPlayerForRemoteUAVLaunch() { // lock lockSpot = spawn( "script_origin", self.origin ); lockSpot hide(); self playerLinkTo( lockSpot ); // wait for unlock self thread clearPlayerLockFromRemoteUAVLaunch( lockSpot ); } clearPlayerLockFromRemoteUAVLaunch( lockSpot ) { level endon( "game_ended" ); msg = self waittill_any_return( "disconnect", "death", "remoteuav_unlock" ); // do unlock stuff if ( msg != "disconnect" ) self unlink(); lockSpot delete(); } createRemoteUAV( lifeId, owner, streakName, origin, angles ) { if ( level.console ) { remoteUAV = spawnHelicopter( owner, origin, angles, "remote_uav_mp", "vehicle_remote_uav" ); } else { remoteUAV = spawnHelicopter( owner, origin, angles, "remote_uav_mp_pc", "vehicle_remote_uav" ); } if ( !isDefined( remoteUAV ) ) return undefined; remoteUAV maps\mp\killstreaks\_helicopter::addToLittleBirdList(); remoteUAV thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath(); //radius and offset should match vehHelicopterBoundsRadius (GDT) and bg_vehicle_sphere_bounds_offset_z. remoteUAV MakeVehicleSolidCapsule( UAV_REMOTE_COLLISION_RADIUS, UAV_REMOTE_Z_OFFSET, UAV_REMOTE_COLLISION_RADIUS ); remoteUAV.lifeId = lifeId; remoteUAV.team = owner.team; remoteUAV.pers["team"] = owner.team; remoteUAV.owner = owner; remoteUAV SetOtherEnt(owner); remoteUAV make_entity_sentient_mp( owner.team ); remoteUAV.maxHealth = 250; // this is the health we'll check //remoteUAV ThermalDrawEnable(); // scrambler remoteUAV.scrambler = spawn( "script_model", origin ); remoteUAV.scrambler linkTo( remoteUAV, "tag_origin", (0,0,-160), (0,0,0) ); remoteUAV.scrambler makeScrambler( owner ); remoteUAV.smoking = false; remoteUAV.inHeliProximity = false; remoteUAV.heliType = "remote_uav"; remoteUAV.markedPlayers = []; remoteUAV thread remoteUAV_light_fx(); remoteUAV thread remoteUAV_explode_on_disconnect(); remoteUAV thread remoteUAV_explode_on_changeTeams(); remoteUAV thread remoteUAV_explode_on_death(); remoteUAV thread remoteUAV_clear_marked_on_gameEnded(); remoteUAV thread remoteUAV_leave_on_timeout(); remoteUAV thread remoteUAV_watch_distance(); remoteUAV thread remoteUAV_watchHeliProximity(); remoteUAV thread remoteUAV_handleDamage(); remoteUAV.numFlares = 2; remoteUAV.hasIncoming = false; remoteUAV.incomingMissiles = []; remoteUAV thread remoteUAV_clearIncomingWarning(); remoteUAV thread remoteUAV_handleIncomingStinger(); remoteUAV thread remoteUAV_handleIncomingSAM(); level.remote_uav[remoteUAV.team] = remoteUAV; return remoteUAV; } remoteUAV_ride( lifeId, remoteUAV, streakName ) { remoteUAV.playerLinked = true; self.restoreAngles = self.angles; if ( getDvarInt( "camera_thirdPerson" ) ) self setThirdPersonDOF( false ); self CameraLinkTo( remoteUAV, "tag_origin" ); self RemoteControlVehicle( remoteUAV ); self thread remoteUAV_playerExit( remoteUAV ); self thread remoteUAV_Track( remoteUAV ); self thread remoteUAV_Fire( remoteUAV ); //self thread remoteUAV_operationRumble( remoteUAV ); self.remote_uav_rideLifeId = lifeId; self.remoteUAV = remoteUAV; self thread remoteUAV_delayLaunchDialog( remoteUAV ); self VisionSetNakedForPlayer( "black_bw", 0.0 ); self restoreBaseVisionSet( 1 ); } remoteUAV_delayLaunchDialog( remoteUAV ) { level endon( "game_ended" ); self endon ( "disconnect" ); remoteUAV endon ( "death" ); remoteUAV endon ( "end_remote" ); remoteUAV endon ( "end_launch_dialog" ); wait( 3 ); self remoteUAV_dialog( "launch" ); } remoteUAV_endride( remoteUAV ) { if ( isDefined( remoteUAV ) ) { remoteUAV.playerLinked = false; remoteUAV notify( "end_remote" ); self clearUsingRemote(); if ( getDvarInt( "camera_thirdPerson" ) ) self setThirdPersonDOF( true ); self CameraUnlink( remoteUAV ); self RemoteControlVehicleOff( remoteUAV ); self ThermalVisionOff(); self setPlayerAngles( self.restoreAngles ); lastWeapon = self getLastWeapon(); if( !self hasWeapon( lastWeapon ) ) { lastWeapon = self maps\mp\killstreaks\_killstreaks::getFirstPrimaryWeapon(); } self switchToWeapon( lastWeapon ); self TakeWeapon( "uav_remote_mp" ); self thread remoteUAV_freezeBuffer(); } self.remoteUAV = undefined; } remoteUAV_freezeBuffer() { self endon( "disconnect" ); self endon( "death" ); level endon( "game_ended" ); self freezeControlsWrapper( true ); wait( 0.5 ); self freezeControlsWrapper( false ); } remoteUAV_playerExit( remoteUAV ) { level endon( "game_ended" ); self endon ( "disconnect" ); remoteUAV endon ( "death" ); remoteUAV endon ( "end_remote" ); // delay exit for transition into remote wait( 2 ); while( true ) { timeUsed = 0; while( self UseButtonPressed() ) { timeUsed += 0.05; if( timeUsed > 0.75 ) { remoteUAV thread remoteUAV_leave(); return; } wait( 0.05 ); } wait( 0.05 ); } } remoteUAV_Track( remoteUAV ) { level endon ( "game_ended" ); self endon ( "disconnect" ); remoteUAV endon ( "death" ); remoteUAV endon ( "end_remote" ); remoteUAV.lastTrackingDialogTime = 0; self.lockedTarget = undefined; self weaponLockFree(); // finish transitioning into remote wait( 1 ); // now loop and track while( true ) { // aim assist 'lock on' target for radius check pos = remoteUAV getTagOrigin( "tag_turret" ); forward = anglesToForward( self getPlayerAngles() ); endpos = pos + forward * 1024; trace = bulletTrace( pos, endpos, true, remoteUAV ); if ( isDefined( trace["position"] ) ) targetPos = trace["position"]; else { targetPos = endpos; trace["endpos"] = endpos; } remoteUAV.trace = trace; // track all targetable entities, update head icons, check lock-on lockedPlayer = self remoteUAV_trackEntities( remoteUAV, level.players, targetPos ); lockedTurret = self remoteUAV_trackEntities( remoteUAV, level.turrets, targetPos ); lockedUAV = undefined; if( level.multiTeamBased ) { //accumulate a list of all enemy uav models entityList = []; foreach ( teamName in level.teamNameList ) { if ( teamName != self.team ) { foreach( model in level.uavmodels[teamName] ) { entityList[entityList.size] = model; } } } lockedUAV = self remoteUAV_trackEntities( remoteUAV, entityList, targetPos ); } else if ( level.teamBased ) lockedUAV = self remoteUAV_trackEntities( remoteUAV, level.uavmodels[level.otherTeam[self.team]], targetPos ); else lockedUAV = self remoteUAV_trackEntities( remoteUAV, level.uavmodels, targetPos ); lockedTarget = undefined; if ( isDefined( lockedPlayer ) ) lockedTarget = lockedPlayer; else if ( isDefined( lockedTurret ) ) lockedTarget = lockedTurret; else if ( isDefined( lockedUAV ) ) lockedTarget = lockedUAV; if ( isDefined( lockedTarget ) ) { if ( !isDefined( self.lockedTarget ) || ( isDefined( self.lockedTarget ) && self.lockedTarget != lockedTarget ) ) { self weaponLockFinalize( lockedTarget ); self.lockedTarget = lockedTarget; // do vo for players if ( isDefined( lockedPlayer ) ) { // cancels the launch dialog delay if we get a target before then remoteUAV notify( "end_launch_dialog" ); self remoteUAV_dialog( "track" ); } } } else { self weaponLockFree(); self.lockedTarget = undefined; } wait( 0.05 ); } } remoteUAV_trackEntities( remoteUAV, entities, targetPos ) { level endon( "game_ended" ); lockedTarget = undefined; foreach ( entity in entities ) { if ( level.teamBased && ( !isDefined( entity.team ) || entity.team == self.team ) ) continue; if ( isPlayer( entity ) ) { if ( !isReallyAlive( entity ) ) continue; if ( entity == self ) continue; id = entity.guid; } else id = entity.birthtime; // offset if ( isDefined( entity.sentryType ) || isDefined( entity.turretType ) ) { offset = (0,0,32); unmarkedShader = "hud_fofbox_hostile_vehicle"; } else if ( isDefined( entity.uavType ) ) { offset = (0,0,-52); unmarkedShader = "hud_fofbox_hostile_vehicle"; } else { offset = (0,0,26); unmarkedShader = "veh_hud_target_unmarked"; } // already marked if ( isDefined( entity.UAVRemoteMarkedBy ) ) { // marked, but no headicon yet if ( !isDefined( remoteUAV.markedPlayers[id] ) ) { remoteUAV.markedPlayers[id] = []; remoteUAV.markedPlayers[id]["player"] = entity; remoteUAV.markedPlayers[id]["icon"] = entity maps\mp\_entityheadIcons::setHeadIcon( self, "veh_hud_target_marked", offset, 10, 10, false, 0.05, false, false, false, false ); remoteUAV.markedPlayers[id]["icon"].shader = "veh_hud_target_marked"; if ( !isDefined( entity.sentryType ) || !isDefined( entity.turretType ) ) remoteUAV.markedPlayers[id]["icon"] SetTargetEnt( entity ); } // headicon hasn't been switched to marked yet else if ( isDefined( remoteUAV.markedPlayers[id] ) && isDefined( remoteUAV.markedPlayers[id]["icon"] ) && isDefined( remoteUAV.markedPlayers[id]["icon"].shader ) && remoteUAV.markedPlayers[id]["icon"].shader != "veh_hud_target_marked" ) { remoteUAV.markedPlayers[id]["icon"].shader = "veh_hud_target_marked"; remoteUAV.markedPlayers[id]["icon"] setShader( "veh_hud_target_marked", 10, 10 ); remoteUAV.markedPlayers[id]["icon"] setWaypoint( false, false, false, false ); } } // not marked yet else { // exceptions if ( isPlayer( entity ) ) { spawnProtected = ( isDefined( entity.spawntime ) && ( getTime() - entity.spawntime )/1000 <= 5 ); hudTargetProtected = entity _hasPerk( "specialty_blindeye" ); carried = false; leaving = false; } else { spawnProtected = false; hudTargetProtected = false; carried = isDefined( entity.carriedBy ); leaving = ( isDefined( entity.isLeaving ) && entity.isLeaving == true ); } // no headicon yet if ( !isDefined( remoteUAV.markedPlayers[id] ) && !spawnProtected && !hudTargetProtected && !carried && !leaving ) { remoteUAV.markedPlayers[id] = []; remoteUAV.markedPlayers[id]["player"] = entity; remoteUAV.markedPlayers[id]["icon"] = entity maps\mp\_entityheadIcons::setHeadIcon( self, unmarkedShader, offset, 10, 10, false, 0.05, false, false, false, false ); remoteUAV.markedPlayers[id]["icon"].shader = unmarkedShader; if ( !isDefined( entity.sentryType ) || !isDefined( entity.turretType ) ) remoteUAV.markedPlayers[id]["icon"] SetTargetEnt( entity ); } // lock on? (don't allow aim assist for spawn campers) if ( ( !isDefined( lockedTarget ) || lockedTarget != entity ) && ( isDefined( remoteUAV.trace["entity"] ) && remoteUAV.trace["entity"] == entity && !carried && !leaving ) || ( distance( entity.origin, targetPos ) < UAV_REMOTE_AIM_ASSIST_RANGE * remoteUAV.trace[ "fraction" ] && !spawnProtected && !carried && !leaving ) || ( !leaving && remoteUAV_canTargetUAV( remoteUAV, entity ) ) ) { // final check, make sure there is line of sight trace = bulletTrace( remoteUAV.origin, entity.origin + (0,0,32), true, remoteUAV ); if ( ( isDefined( trace["entity"] ) && trace["entity"] == entity ) || trace["fraction"] == 1 ) { self playLocalSound( "recondrone_lockon" ); lockedTarget = entity; } } } } return lockedTarget; } remoteUAV_canTargetUAV( remoteUAV, uav ) { // lenient targeting for other uavs, just point in the correct direction and ignore range to keep players flying low if ( isDefined( uav.uavType ) ) { forward = anglesToForward( self getPlayerAngles() ); toUAV = vectorNormalize( uav.origin - remoteUAV getTagOrigin( "tag_turret" ) ); dot = vectorDot( forward, toUAV ); if ( dot > 0.985 ) return true; } return false; } remoteUAV_Fire( remoteUAV ) { self endon ( "disconnect" ); remoteUAV endon ( "death" ); level endon ( "game_ended" ); remoteUAV endon ( "end_remote" ); // transition into remote wait( 1 ); self notifyOnPlayerCommand( "remoteUAV_tag", "+attack" ); self notifyOnPlayerCommand( "remoteUAV_tag", "+attack_akimbo_accessible" ); // support accessibility control scheme while ( true ) { self waittill( "remoteUAV_tag" ); if ( isDefined( self.lockedTarget ) ) { self playLocalSound( "recondrone_tag" ); // hit FX self maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" ); // mark target self thread remoteUAV_markPlayer( self.lockedTarget ); // feedback self thread remoteUAV_Rumble( remoteUAV, 3 ); wait( 0.25 ); } else { wait( 0.05 ); } } } remoteUAV_Rumble( remoteUAV, amount ) { self endon ( "disconnect" ); remoteUAV endon ( "death" ); level endon ( "game_ended" ); remoteUAV endon ( "end_remote" ); remoteUAV notify( "end_rumble" ); remoteUAV endon ( "end_rumble" ); for( i=0; i 0 && curDist < 4000 ) { newTarget = missileTarget deployFlares(); self Missile_SetTargetEnt( newTarget ); return; } // still on target? else { curVecToTarget = vectorNormalize( missileTarget.origin - self.origin ); if ( vectorDot( curVecToTarget, lastVecToTarget ) < 0 ) { self playSound( "exp_stinger_armor_destroy" ); playFX( level.RemoteUAV_fx["missile_explode"], self.origin ); if ( isDefined( self.owner ) ) RadiusDamage( self.origin, 400, 1000, 1000, self.owner, "MOD_EXPLOSIVE", "stinger_mp" ); else RadiusDamage( self.origin, 400, 1000, 1000, undefined, "MOD_EXPLOSIVE", "stinger_mp" ); self hide(); wait ( 0.05 ); self delete(); } else lastVecToTarget = curVecToTarget; } wait ( 0.05 ); } } watchSAMProximity( missileTarget, missileGroup ) { level endon ( "game_ended" ); missileTarget endon( "death" ); foreach ( missile in missileGroup ) { if ( isDefined( missile ) ) { missile Missile_SetTargetEnt( missileTarget ); missile.lastVecToTarget = vectorNormalize( missileTarget.origin - missile.origin ); } } while( missileGroup.size && isDefined( missileTarget ) ) { center = missileTarget GetPointInBounds( 0, 0, 0 ); foreach ( missile in missileGroup ) { if ( isDefined( missile ) ) { if ( isDefined( self.markForDetete ) ) { self delete(); continue; } // flares? if ( missileTarget.numFlares > 0 ) { distToTarget = distance( missile.origin, center ); if ( distToTarget < 4000 ) { newTarget = missileTarget deployFlares(); foreach ( missileToRedirect in missileGroup ) if ( IsDefined( missileToRedirect ) ) missileToRedirect Missile_SetTargetEnt( newTarget ); return; } } // still on target? else { curVecToTarget = vectorNormalize( missileTarget.origin - missile.origin ); if ( vectorDot( curVecToTarget, missile.lastVecToTarget ) < 0 ) { missile playSound( "exp_stinger_armor_destroy" ); playFX( level.RemoteUAV_fx["missile_explode"], missile.origin ); if ( isDefined( missile.owner ) ) RadiusDamage( missile.origin, 400, 1000, 1000, missile.owner, "MOD_EXPLOSIVE", "stinger_mp" ); else RadiusDamage( missile.origin, 400, 1000, 1000, undefined, "MOD_EXPLOSIVE", "stinger_mp" ); missile hide(); missile.markForDetete = true; } else missile.lastVecToTarget = curVecToTarget; } } } missileGroup = array_removeUndefined( missileGroup ); wait ( 0.05 ); } } deployFlares() { // decrement self.numFlares--; // player feedback self.owner thread remoteUAV_Rumble( self, 6 ); self playSound( "WEAP_SHOTGUNATTACH_FIRE_NPC" ); // fx self thread playFlareFx(); // flare spawnPos = self.origin + (0,0,-100); flareObject = spawn( "script_origin", spawnPos ); flareObject.angles = self.angles; flareObject moveGravity( (0,0,-1), 5.0 ); flareObject thread deleteAfterTime( 5.0 ); return flareObject; } playFlareFx() { for ( i = 0; i < 5; i++ ) { if ( !isDefined( self ) ) return; PlayFXOnTag( level._effect[ "vehicle_flares" ], self, "TAG_FLARE" ); wait ( 0.15 ); } } deleteAfterTime( delay ) { wait ( delay ); self delete(); } remoteUAV_clearIncomingWarning() { level endon ( "game_ended" ); self endon ( "death" ); self endon ( "end_remote" ); while( true ) { numIncoming = 0; for ( i=0; i 0 ); } remoteUAV_watchHeliProximity() { level endon( "game_ended" ); self endon( "death" ); self endon( "end_remote" ); while( true ) { inHeliProximity = false; foreach( heli in level.helis ) { if ( distance( heli.origin, self.origin ) < UAV_REMOTE_MAX_HELI_PROXIMITY ) { inHeliProximity = true; self.heliInProximity = heli; } } foreach( littlebird in level.littleBirds ) { if ( littlebird != self && ( !isDefined(littlebird.heliType) || littlebird.heliType != "remote_uav" ) && distance( littlebird.origin, self.origin ) < UAV_REMOTE_MAX_HELI_PROXIMITY ) { inHeliProximity = true; self.heliInProximity = littlebird; } } if ( !self.inHeliProximity && inHeliProximity ) self.inHeliProximity = true; else if ( self.inHeliProximity && !inHeliProximity ) { self.inHeliProximity = false; self.heliInProximity = undefined; } wait( 0.05 ); } } remoteUAV_handleDamage() { self endon( "end_remote" ); self maps\mp\gametypes\_damage::monitorDamage( self.maxHealth, "remote_uav", ::handleDeathDamage, ::modifyDamage, true // isKillstreak ); } modifyDamage( attacker, weapon, type, damage ) { modifiedDamage = damage; // 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::handleAPDamage( weapon, type, modifiedDamage, attacker ); // this is super hacky. Should be in an "onDamaged" function PlayFXOnTagForClients( level.RemoteUAV_fx["hit"], self, "tag_origin", self.owner ); self PlaySound( "recondrone_damaged" ); if( self.smoking == false && self.damageTaken >= self.maxhealth/2 ) { self.smoking = true; PlayFxOnTag( level.RemoteUAV_fx["smoke"], self, "tag_origin" ); } return modifiedDamage; } handleDeathDamage( attacker, weapon, type, damage ) // self == trophy { // !!! need VO self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, "destroyed_remote_uav", undefined, "callout_destroyed_remote_uav" ); }