#include maps\mp\_utility; #include maps\mp\gametypes\_hud_util; #include common_scripts\utility; // Streak Data CONST_UAV_BASE_TIME = 30; CONST_UAV_MOD_INCREASED_TIME = 15; CONST_THREAT_DRAW_TIME = 10; init() { assert( CONST_UAV_BASE_TIME > 7 ); level._effect[ "uav_explode" ] = LoadFX( "vfx/explosion/vehicle_uav_explosion" ); level._effect[ "uav_exit" ] = LoadFX( "vfx/trail/smoketrail_uav" ); level._effect[ "uav_trail" ] = LoadFX( "vfx/trail/smoketrail_uav" ); level.killStreakFuncs["uav"] = ::tryUseUAV; level.killStreakFuncs["uav_support"] = ::tryUseUAV; level.killStreakFuncs["counter_uav"] = ::tryUseUAV; minimapOrigins = getEntArray( "minimap_corner", "targetname" ); if ( miniMapOrigins.size ) uavOrigin = maps\mp\gametypes\_spawnlogic::findBoxCenter( miniMapOrigins[0].origin, miniMapOrigins[1].origin ); else uavOrigin = (0,0,0); level.UAVRig = spawn( "script_model", uavOrigin ); level.UAVRig setModel( "c130_zoomrig" ); level.UAVRig.angles = (0,115,0); level.UAVRig hide(); level.UAVRig.targetname = "uavrig_script_model"; // used for debug printing level.UAVRig thread rotateUAVRig(); if ( level.teamBased ) { level.radarMode["allies"] = "normal_radar"; level.radarMode["axis"] = "normal_radar"; level.activeUAVs["allies"] = 0; level.activeUAVs["axis"] = 0; level.activeCounterUAVs["allies"] = 0; level.activeCounterUAVs["axis"] = 0; level.uavModels["allies"] = []; level.uavModels["axis"] = []; } else { level.radarMode = []; level.activeUAVs = []; level.activeCounterUAVs = []; level.uavModels = []; } level thread onPlayerConnect(); level thread UAVTracker(); /# SetDvarIfUninitialized( "scr_uav_timeout", "0" ); SetDvarIfUninitialized( "scr_uav_norandom", "0" ); SetDvarIfUninitialized( "scr_uav_alwaysshow", "0" ); #/ } onPlayerConnect() { for(;;) { level waittill( "connected", player ); if ( !level.teamBased ) { level.activeUAVs[ player.guid ] = 0; level.activeUAVs[ player.guid + "_radarStrength" ] = 0; level.activeCounterUAVs[ player.guid ] = 0; level.radarMode[ player.guid ] = "normal_radar"; } player thread onPlayerSpawned(); } } onPlayerSpawned() { self endon("disconnect"); while ( true ) { self waittill( "spawned_player" ); level notify ( "uav_update" ); } } rotateUAVRig() { for (;;) { self rotateyaw( -360, 60 ); wait ( 60 ); } } /# debugLocation() { self endon( "death" ); while( true ) { Print3d( self.origin, "UAV", ( 1, 0, 0 ) ); Print3d( self.origin, "UAV origin: " + self.origin[0] + ", " + self.origin[1] + ", " + self.origin[2], ( 1, 0, 0 ) ); Print3d( level.UAVRig.origin, "UAV Rig", ( 1, 0, 0 ) ); Print3d( level.UAVRig.origin, "UAV Rig origin: " + level.UAVRig.origin[0] + ", " + level.UAVRig.origin[1] + ", " + level.UAVRig.origin[2], ( 1, 0, 0 ) ); Print3d( level.UAVRig.origin - ( 0, 0, 50), "Distance: " + Distance( level.UAVRig.origin, self.origin ), ( 1, 0, 0 ) ); Line( level.UAVRig.origin, self.origin, ( 0, 0, 1 ) ); anglesForward = AnglesToForward( level.players[0].angles ); scalar = (anglesForward[0] * 200, anglesForward[1] * 200, anglesForward[2] ); Print3d( level.players[0].origin + scalar, "Distance: " + Distance( level.players[0].origin, self.origin ), ( 1, 0, 0 ) ); wait( 0.05 ); } } debugTrace() { self endon( "death" ); while( true ) { result = BulletTrace( level.players[0].origin, self.origin, false, undefined ); if( IsDefined( result ) && IsDefined( result[ "surfacetype" ] ) ) { PrintLn( "UAV debugTrace: " + result[ "surfacetype" ] ); } wait( 1.0 ); } } #/ playTrailFX() { self endon( "death" ); level endon( "game_ended" ); PlayFXOnTag( level._effect["uav_trail"],self,"tag_origin"); } launchUAV( owner, team, uavType, modules ) { UAVModel = spawn( "script_model", level.UAVRig getTagOrigin( "tag_origin" ) ); UAVModel.modules = modules; /# if( GetDvarInt( "scr_debuguav", 0 ) ) { UAVModel thread debugLocation(); UAVModel thread debugTrace(); } #/ UAVModel.value = 1; if ( array_contains( UAVModel.modules, "uav_advanced_updates" ) ) // streak customization UAVModel.value = 2; if ( array_contains( UAVModel.modules, "uav_enemy_direction" ) ) // streak customization UAVModel.value = 3; if ( array_contains( UAVModel.modules, "uav_scrambler" ) ) // streak customization isScrambler = true; else isScrambler = false; UAVModel setModel( "uav_drone_static" ); UAVModel thread playTrailFX(); UAVModel thread maps\mp\gametypes\_damage::setEntityDamageCallback( 1000, undefined, ::uavOnDeath, undefined, true ); UAVModel.team = team; UAVModel.owner = owner; UAVModel.timeToAdd = 0; UAVModel.orbit = array_contains( UAVModel.modules, "uav_orbit" ); // streak customization UAVModel.paintOutline = array_contains( UAVModel.modules, "uav_paint_outline" ); // streak customization UAVModel.assistPoints = array_contains( UAVModel.modules, "uav_assist_points" ); // streak customization UAVModel make_entity_sentient_mp( team ); UAVModel thread handleIncomingStinger(); UAVModel.StreakCustomization = owner.StreakCustomization; UAVModel addUAVModel(); thread flyIn( UAVModel ); UAVModel thread updateUAVModelVisibility(); UAVModel thread maps\mp\killstreaks\_killstreaks::updateAerialKillStreakMarker(); UAVModel addActiveUAV(); if ( isScrambler ) UAVModel addActiveCounterUAV(); //this adds 5 seconds of time to all active UAV's of the same type. if ( isDefined( level.activeUAVs[team] ) ) { foreach ( uav in level.UAVModels[team] ) { if (uav == UAVModel) continue; if ( isScrambler ) uav.timeToAdd += 5; else if ( !isScrambler ) uav.timeToAdd += 5; } } waitframe(); level notify ( "uav_update" ); duration = CONST_UAV_BASE_TIME; if ( array_contains( UAVModel.modules, "uav_increased_time" ) ) // streak customization duration += CONST_UAV_MOD_INCREASED_TIME; /# if ( GetDvarInt( "scr_uav_timeout", 0 ) != 0 ) duration = GetDvarInt( "scr_uav_timeout" ); #/ UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", duration ); if ( UAVModel.damageTaken < UAVModel.maxHealth ) { UAVModel unlink(); destPoint = UAVModel.origin + ( AnglesToForward( UAVModel.angles ) * 20000 ); UAVModel moveTo( destPoint, 60 ); PlayFXOnTag( getfx( "uav_exit" ) , UAVModel, "tag_origin" ); UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 3 ); if ( UAVModel.damageTaken < UAVModel.maxHealth ) { UAVModel notify( "leaving" ); UAVModel.isLeaving = true; UAVModel moveTo( destPoint, 4, 4, 0.0 ); } UAVModel waittill_notify_or_timeout_hostmigration_pause( "death", 4 + UAVModel.timeToAdd ); } if ( isScrambler ) UAVModel removeActiveCounterUAV(); UAVModel removeActiveUAV(); UAVModel delete(); UAVModel removeUAVModel(); level notify ( "uav_update" ); } flyIn( UAVModel ) { UAVModel Hide(); zOffset = RandomIntRange( 3000, 5000 ); // we need to make sure the uav doesn't go higher than 8100 units because bullets die at 8192 if ( IsDefined( level.spawnpoints ) ) spawns = level.spawnPoints; else spawns = level.startSpawnPoints; lowestSpawn = spawns[ 0 ]; foreach ( Spawn in spawns ) { if ( Spawn.origin[ 2 ] < lowestSpawn.origin[ 2 ] ) lowestSpawn = Spawn; } lowestZ = lowestSpawn.origin [ 2 ]; UAVRigZ = level.UAVRig.origin[ 2 ]; if ( lowestZ < 0 ) { UAVRigZ += lowestZ * -1; lowestZ = 0; } diffZ = UAVRigZ - lowestZ; AssertEx( diffZ < 8100.0, "The lowest spawn and the UAV node are more than 8100 z units apart, please notify MP Design." ); if ( diffZ + zOffset > 8100.0 ) { zOffset -= ( ( diffZ + zOffset ) - 8100.0 ); } angle = RandomInt( 360 ); radiusOffset = RandomInt( 2000 ) + 5000; /# if ( GetDvarInt( "scr_uav_norandom", 0 ) != 0 ) { angle = 0; radiusOffset = 5000; } #/ xOffset = Cos( angle ) * radiusOffset; yOffset = Sin( angle ) * radiusOffset; angleVector = VectorNormalize( ( xOffset, yOffset, zOffset ) ); angleVector = ( angleVector * RandomIntRange( 6000, 7000 ) ); UAVModel LinkTo( level.UAVRig, "tag_origin", angleVector, ( 0, angle - 90, 135 ) ); waitframe(); destination = UAVModel.origin; UAVModel Unlink(); UAVModel.origin = destination + ( AnglesToForward( UAVModel.angles ) * -20000 ); UAVModel MoveTo( destination, 4, 0, 2 ); wait 4; if ( IsDefined( UAVModel ) ) UAVModel LinkTo( level.UAVRig, "tag_origin" ); } waittill_notify_or_timeout_hostmigration_pause( msg, timer ) { self endon( msg ); maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timer ); } updateUAVModelVisibility() { self endon ( "death" ); /# if ( GetDvarInt( "scr_uav_alwaysshow", 0 ) != 0 ) { waitframe(); self Show(); return; } #/ for ( ;; ) { level waittill_either ( "joined_team", "uav_update" ); self hide(); foreach ( player in level.players ) { if ( level.teamBased ) { if ( (player.team != self.team) && !self.orbit ) self showToPlayer( player ); } else { if ( (isDefined( self.owner ) && player == self.owner) || self.orbit ) continue; self showToPlayer( player ); } } } } uavOnDeath( attacker, weapon, meansOfDeath, damage ) { self Hide(); self notify( "death" ); forward = ( AnglesToRight( self.angles ) * 200 ); playFx ( getfx( "uav_explode" ), self.origin, forward ); playSoundAtPos( self.origin, "uav_air_death" ); self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, meansOfDeath, damage, "uav_destroyed", undefined, "callout_destroyed_uav", true ); } tryUseUAV( lifeId, modules ) { if(IsDefined(self.pers["killstreaks"][self.killstreakIndexWeapon].streakName)) { StreakName = self.pers["killstreaks"][self.killstreakIndexWeapon].streakName; } else { StreakName = "uav_support"; } if ( isdefined ( level.isHorde ) && level.isHorde && self.killstreakIndexWeapon == 1 ) self notify ( "used_horde_uav" ); return useUAV( streakname, modules ); } useUAV( uavType, modules ) { self maps\mp\_matchdata::logKillstreakEvent( uavType, self.origin ); team = self.pers["team"]; level thread launchUAV( self, team, uavType, modules ); return true; } UAVTracker() { level endon ( "game_ended" ); for ( ;; ) { level waittill ( "uav_update" ); if ( level.teamBased ) { updateTeamUAVStatus( "allies" ); updateTeamUAVStatus( "axis" ); } else { updatePlayersUAVStatus(); } } } _getRadarStrength( team, fastSweep, enemyDirection ) { activeUAVs = 0; activeCounterUAVs = 0; foreach ( uav in level.UAVModels[team] ) { activeUAVs += uav.value; } foreach ( uav in level.UAVModels[level.otherTeam[team] ] ) { if ( uav.uavType != "counter" ) continue; activeCounterUAVs += uav.value; } if( activeCounterUAVs > 0 ) radarStrength = -3; else radarStrength = activeUAVs; strengthMin = GetUAVStrengthMin(); strengthMax = GetUAVStrengthMax(); //clamp between min/max if( radarStrength <= strengthMin ) { radarStrength = strengthMin; } else if( radarStrength >= strengthMax ) { radarStrength = strengthMax; } return radarStrength; } _getTeamPaintOutline( team ) { paintOutlineUavs = 0; paintOutline = false; foreach ( uav in level.UAVModels[team] ) { if ( uav.paintOutline ) // streak customization paintOutlineUavs += uav.value; } if( paintOutlineUavs > 0 ) paintOutline = true; else paintOutline = false; return paintOutline; } _getPaintOutline( player ) { paintOutlineUavs = 0; paintOutline = false; foreach ( uav in level.UAVModels ) { if ( IsDefined( uav.owner ) && uav.owner == player && uav.paintOutline ) // streak customization paintOutlineUavs += uav.value; } if( paintOutlineUavs > 0 ) paintOutline = true; else paintOutline = false; return paintOutline; } updateTeamUAVStatus( team ) { radarStrength = _getRadarStrength( team ); hasPaintOutline = _getTeamPaintOutline( team ); updateTeamPaintOutline( team, hasPaintOutline ); setTeamRadarStrength( team, radarStrength ); if ( radarStrength >= GetUAVStrengthLevelNeutral() ) //note: exception for counter uavs { updateTeamRadarBlocked( team, false ); unblockTeamRadar( team ); } else { updateTeamRadarBlocked( team, true ); blockTeamRadar( team ); } if ( radarStrength <= GetUAVStrengthLevelNeutral() ) { setTeamRadarWrapper( team, 0 ); updateTeamUAVType( team ); return; } if ( radarStrength >= GetUAVStrengthLevelShowEnemyFastSweep() ) level.radarMode[team] = "fast_radar"; else level.radarMode[team] = "normal_radar"; updateTeamUAVType( team ); setTeamRadarWrapper( team, 1 ); } //for FFA updatePlayersUAVStatus() { strengthMin = GetUAVStrengthMin(); strengthMax = GetUAVStrengthMax(); strengthDirectional = GetUAVStrengthLevelShowEnemyDirectional(); foreach ( player in level.players ) { radarStrength = level.activeUAVs[ player.guid + "_radarStrength" ]; hasPaintOutline = _getPaintOutline( player ); updatePaintOutline( player, hasPaintOutline ); // if there are any counters up that aren't this player's then they are blocked foreach( enemyPlayer in level.players ) { if( enemyPlayer == player ) continue; activeCounterUAVs = level.activeCounterUAVs[ enemyPlayer.guid ]; if( activeCounterUAVs > 0 ) { radarStrength = -3; break; } } //clamp between min/max if( radarStrength <= strengthMin ) { radarStrength = strengthMin; } else if( radarStrength >= strengthMax ) { radarStrength = strengthMax; } player.radarstrength = radarStrength; if ( radarStrength >= GetUAVStrengthLevelNeutral() ) { updatePlayerRadarBlocked( player, false ); player.isRadarBlocked = false; } else { updatePlayerRadarBlocked( player, true ); player.isRadarBlocked = true; } if ( radarStrength <= GetUAVStrengthLevelNeutral() ) { player.hasRadar = false; player.radarShowEnemyDirection = false; continue; } if ( radarStrength >= GetUAVStrengthLevelShowEnemyFastSweep()) player.radarMode = "fast_radar"; else player.radarMode = "normal_radar"; //set directional status player.radarShowEnemyDirection = radarStrength >= strengthDirectional; player.hasRadar = true; } } updateTeamUAVType( team, ShowEnemyDirection ) { shouldBeDirectional = _getRadarStrength( team ) >= GetUAVStrengthLevelShowEnemyDirectional(); foreach ( player in level.players ) { if ( player.team == "spectator" ) continue; enemyTeam = maps\mp\gametypes\_gameobjects::getEnemyTeam( player.team ); player.radarMode = level.radarMode[player.team]; player.enemyRadarMode = level.radarMode[enemyTeam]; //use direction based on uav signal strength if( player.team == team ) { player.radarShowEnemyDirection = shouldBeDirectional; } } } updateTeamPaintOutline( team, on ) { level endon( "game_ended" ); foreach ( player in level.players ) { if ( IsDefined( player ) && player.team == team ) player playerSetupUAVPaintOutline( on ); } } updatePaintOutline( player, on ) { level endon( "game_ended" ); player playerSetupUAVPaintOutline( on ); } playerSetupUAVPaintOutline( on ) // self == player { if ( on ) { if ( !IsDefined( self.uav_paint_effect ) ) { self.uav_paint_effect = maps\mp\_threatdetection::detection_highlight_hud_effect_on( self, -1 ); } self SetPerk( "specialty_uav_paint", true, false ); } else { if ( IsDefined( self.uav_paint_effect ) ) { maps\mp\_threatdetection::detection_highlight_hud_effect_off( self.uav_paint_effect ); self.uav_paint_effect = undefined; } self UnSetPerk( "specialty_uav_paint", true ); } } updateTeamRadarBlocked( team, isBlocked ) { foreach ( player in level.players ) { if ( IsDefined( player ) && player.team == team ) updatePlayerRadarBlocked( player, isBlocked ); } } updatePlayerRadarBlocked( player, isBlocked ) { if ( !isBlocked || !player _hasPerk( "specialty_class_hardwired" ) ) player SetClientOmnvar( "ui_uav_scrambler_on", isBlocked ); } setTeamRadarWrapper( team, value ) { setTeamRadar( team, value ); level notify( "radar_status_change", team ); } handleIncomingStinger() { level endon ( "game_ended" ); self endon ( "death" ); for ( ;; ) { level waittill ( "stinger_fired", player, missiles ); if ( !maps\mp\_stingerm7::anyStingerMissileLockedOn( missiles, self ) ) continue; foreach ( missile in missiles ) { if ( !IsDefined( missile ) ) continue; missile thread stingerProximityDetonate( missile.lockedStingerTarget, player ); } } } stingerProximityDetonate( targetEnt, player ) { self endon ( "death" ); minDist = distance( self.origin, targetEnt GetPointInBounds( 0, 0, 0 ) ); lastCenter = targetEnt GetPointInBounds( 0, 0, 0 ); for ( ;; ) { // UAV already destroyed if ( !isDefined( targetEnt ) ) center = lastCenter; else center = targetEnt GetPointInBounds( 0, 0, 0 ); lastCenter = center; curDist = distance( self.origin, center ); if ( curDist < minDist ) minDist = curDist; if ( curDist > minDist ) { if ( curDist > 1536 ) return; RadiusDamage( self.origin, 1536, 600, 600, player, "MOD_EXPLOSIVE", "stinger_mp" ); playFx( level.stingerFXid, self.origin ); //self playSound( "remotemissile_explode" ); self hide(); self notify("deleted"); wait ( 0.05 ); self delete(); player notify( "killstreak_destroyed" ); } wait ( 0.05 ); } } addUAVModel() // self == uavmodel { if ( level.teamBased ) level.UAVModels[ self.team ][level.UAVModels[ self.team ].size] = self; else level.UAVModels[ self.owner.guid + "_" + getTime() ] = self; } removeUAVModel() // self == uavmodel { UAVModels = []; if ( level.teamBased ) { team = self.team; foreach ( uavModel in level.UAVModels[team] ) { if ( !isDefined( uavModel ) ) continue; UAVModels[UAVModels.size] = uavModel; } level.UAVModels[team] = UAVModels; } else { foreach ( uavModel in level.UAVModels ) { if ( !isDefined( uavModel ) ) continue; UAVModels[UAVModels.size] = uavModel; } level.UAVModels = UAVModels; } } addActiveUAV() // self == uav model { self.uavType = "standard"; if ( level.teamBased ) level.activeUAVs[self.team]++; else { level.activeUAVs[ self.owner.guid ]++; level.activeUAVs[ self.owner.guid + "_radarStrength" ] += self.value; } } addActiveCounterUAV() { self.uavType = "counter"; if ( level.teamBased ) level.activeCounterUAVs[self.team]++; else level.activeCounterUAVs[self.owner.guid]++; } removeActiveUAV() // self == uav model { if ( level.teamBased ) { level.activeUAVs[self.team]--; if ( !level.activeUAVs[self.team] ) { //printOnTeam( &"MP_WAR_RADAR_EXPIRED", self.team ); //printOnTeam( &"MP_WAR_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] ); } } else if ( isDefined( self.owner ) ) { level.activeUAVs[ self.owner.guid ]--; level.activeUAVs[ self.owner.guid + "_radarStrength" ] -= self.value; } } removeActiveCounterUAV() { if ( level.teamBased ) { level.activeCounterUAVs[self.team]--; if ( !level.activeCounterUAVs[self.team] ) { //printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED", self.team ); //printOnTeam( &"MP_WAR_COUNTER_RADAR_EXPIRED_ENEMY", level.otherTeam[self.team] ); } } else if ( isDefined( self.owner ) ) { level.activeCounterUAVs[self.owner.guid]--; } }