#using scripts\codescripts\struct; #using scripts\mp\_util; #using scripts\mp\gametypes\_battlechatter; #using scripts\mp\gametypes\_globallogic_audio; #using scripts\mp\killstreaks\_airsupport; #using scripts\mp\killstreaks\_helicopter; #using scripts\mp\killstreaks\_killstreak_bundles; #using scripts\mp\killstreaks\_killstreak_detect; #using scripts\mp\killstreaks\_killstreak_hacking; #using scripts\mp\killstreaks\_killstreakrules; #using scripts\mp\killstreaks\_killstreaks; #using scripts\mp\killstreaks\_satellite; #using scripts\mp\teams\_teams; #using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\damagefeedback_shared; #using scripts\shared\hostmigration_shared; #using scripts\shared\killstreaks_shared; #using scripts\shared\math_shared; #using scripts\shared\scoreevents_shared; #using scripts\shared\system_shared; #using scripts\shared\tweakables_shared; #using scripts\shared\util_shared; #using scripts\shared\weapons\_heatseekingmissile; #using scripts\shared\weapons\_weaponobjects; #namespace counteruav; #precache( "string", "KILLSTREAK_COUNTERUAV_INBOUND" ); #precache( "string", "KILLSTREAK_COUNTERUAV_NOT_AVAILABLE" ); #precache( "string", "KILLSTREAK_EARNED_COUNTERUAV" ); #precache( "string", "KILLSTREAK_DESTROYED_COUNTERUAV" ); #precache( "string", "KILLSTREAK_COUNTERUAV_HACKED" ); #precache( "string", "mpl_killstreak_radar" ); function init() { level.activeCounterUAVs = []; level.counter_uav_positions = GenerateRandomPoints( ( 20 ) ); level.counter_uav_position_index = []; level.counter_uav_offsets = BuildOffsetList( ( 0, 0, 0 ), ( 3 ), ( 450 ), ( 450 ) ); if ( level.teamBased ) { foreach( team in level.teams ) { level.activeCounterUAVs[ team ] = 0; level.counter_uav_position_index[ team ] = 0; level thread MovementManagerThink( team ); } } else { level.activeCounterUAVs = []; } level.activePlayerCounterUAVs = []; level.counter_uav_entities = []; if( tweakables::getTweakableValue( "killstreak", "allowcounteruav" ) ) { killstreaks::register( "counteruav", "counteruav", "killstreak_counteruav", "counteruav_used", &ActivateCounterUAV ); killstreaks::register_strings( "counteruav", &"KILLSTREAK_EARNED_COUNTERUAV", &"KILLSTREAK_COUNTERUAV_NOT_AVAILABLE", &"KILLSTREAK_COUNTERUAV_INBOUND", undefined, &"KILLSTREAK_COUNTERUAV_HACKED" ); killstreaks::register_dialog( "counteruav", "mpl_killstreak_radar", "counterUavDialogBundle", "counterUavPilotDialogBundle", "friendlyCounterUav", "enemyCounterUav", "enemyCounterUavMultiple", "friendlyCounterUavHacked", "enemyCounterUavHacked", "requestCounterUav", "threatCounterUav" ); } clientfield::register( "toplayer", "counteruav", 1, 1, "int" ); level thread WatchCounterUAVs(); callback::on_connect( &OnPlayerConnect ); callback::on_spawned( &OnPlayerSpawned ); callback::on_joined_team( &OnPlayerJoinedTeam ); /# if ( GetDvarInt( "scr_cuav_offset_debug" ) ) level thread WaitAndDebugDrawOffsetList(); #/ } function OnPlayerConnect() { self.entNum = self getEntityNumber(); if( !level.teamBased ) { level.activeCounterUAVs[ self.entNum ] = 0; level.counter_uav_position_index[ self.entNum ] = 0; self thread MovementManagerThink( self.entnum ); } level.activePlayerCounterUAVs[ self.entNum ] = 0; } function OnPlayerSpawned() { if( self EnemyCounterUAVActive() ) { self clientfield::set_to_player( "counteruav", 1 ); } else { self clientfield::set_to_player( "counteruav", 0 ); } } function GenerateRandomPoints( count ) { points = []; for( i = 0; i < count; i++ ) { point = airsupport::GetRandomMapPoint( (isdefined(level.cuav_map_x_offset)?level.cuav_map_x_offset:0), (isdefined(level.cuav_map_y_offset)?level.cuav_map_y_offset:0), (isdefined(level.cuav_map_x_percentage)?level.cuav_map_x_percentage:( 0.50 )), (isdefined(level.cuav_map_y_percentage)?level.cuav_map_y_percentage:( 0.50 )) ); minFlyHeight = airsupport::getMinimumFlyHeight(); point = point + ( 0, 0, minFlyHeight + (isdefined(level.counter_uav_position_z_offset)?level.counter_uav_position_z_offset:( 1000 )) ); points[ i ] = point; } return points; } function MovementManagerThink( teamOrEntNum ) { while( true ) { level waittill( "counter_uav_updated" ); activeCount = 0; while( level.activeCounterUAVs[ teamOrEntNum ] > 0 ) { if( activeCount == 0 ) { activeCount = level.activeCounterUAVs[ teamOrEntNum ]; } currentIndex = level.counter_uav_position_index[ teamOrEntNum ]; newIndex = currentIndex; while( newIndex == currentIndex ) { newIndex = RandomIntRange( 0, ( 20 ) ); } destination = level.counter_uav_positions[ newIndex ]; level.counter_uav_position_index[ teamOrEntNum ] = newIndex; level notify( "counter_uav_move_" + teamOrEntNum ); wait( ( 5 ) + RandomIntRange( ( 5 ), ( 10 ) ) ); } } } function GetCurrentPosition( teamOrEntNum ) { basePosition = level.counter_uav_positions[ level.counter_uav_position_index[ teamOrEntNum ] ]; offset = level.counter_uav_offsets[ self.cuav_offset_index ]; return basePosition + offset; } function AssignFirstAvailableOffsetIndex() { self.cuav_offset_index = GetFirstAvailableOffsetIndex(); MaintainCouterUavEntities(); } function GetFirstAvailableOffsetIndex() { // init available offset array available_offsets = []; for( i = 0; i < level.counter_uav_offsets.size; i++ ) available_offsets[ i ] = true; // update available offsets array foreach( cuav in level.counter_uav_entities ) { if ( isdefined( cuav ) ) { available_offsets[ cuav.cuav_offset_index ] = false; } } // return first available for( i = 0; i < available_offsets.size; i++ ) { if ( available_offsets[ i ] ) return i; } /#util::warning("Max counter-uav available offset slots reached. Using slot 0 for now.");#/ return 0; } function MaintainCouterUavEntities() { for( i = level.counter_uav_entities.size; i >= 0; i-- ) { if ( !isdefined( level.counter_uav_entities[ i ] ) ) { ArrayRemoveIndex( level.counter_uav_entities, i ); } } } /# function WaitAndDebugDrawOffsetList() { level endon( "game_ended" ); wait 10; DebugDrawOffsetList(); } function DebugDrawOffsetList() { basePosition = level.counter_uav_positions[ 0 ]; foreach ( offset in level.counter_uav_offsets ) { util::debug_sphere( basePosition + offset, 24, ( 0.95, 0.05, 0.05 ), 0.75, 9999999 ); } } #/ function BuildOffsetList( startOffset, depth, offset_x, offset_y ) { offsets = []; for( col = 0; col < depth; col++ ) { itemCount = math::pow( 2, col ); startingIndex = ( itemCount - 1 ); for( i = 0; i < itemCount; i++ ) { x = offset_x * col; y = 0; if( itemCount > 1 ) { y = ( i * offset_y ); total_y = offset_y * startingIndex; y -= ( total_y / 2 ); } offsets[ startingIndex + i ] = startOffset + ( x, y, 0 ); } } return offsets; } function ActivateCounterUAV() { if( self killstreakrules::isKillstreakAllowed( "counteruav", self.team ) == false ) { return false; } killstreak_id = self killstreakrules::killstreakStart( "counteruav", self.team ); if( killstreak_id == -1 ) { return false; } counterUav = SpawnCounterUAV( self, killstreak_id ); if( !isdefined( counterUav ) ) { return false; } counterUAV SetScale( ( 1 ) ); counterUav clientfield::set( "enemyvehicle", 1 ); counterUav.killstreak_id = killstreak_id; counterUav thread killstreaks::WaitTillEMP( &DestroyCounterUavByEMP ); counterUav thread killstreaks::WaitForTimeout( "counteruav", ( 30000 ), &OnTimeout, "delete", "death", "crashing" ); counterUav thread killstreaks::WaitForTimecheck( ( ( 30000 ) / 2 ), &OnTimecheck, "delete", "death", "crashing" ); counterUav thread util::WaitTillEndOnThreaded( "death", &DestroyCounterUav, "delete", "leaving" ); counterUav SetCanDamage( true ); counterUav thread killstreaks::MonitorDamage( "counteruav", ( 700 ), &DestroyCounterUAV, ( ( 700 ) * 0.5 ), &OnLowHealth, 0, undefined, true ); counterUav PlayLoopSound( "veh_uav_engine_loop", 1 ); counterUav thread ListenForMove(); self killstreaks::play_killstreak_start_dialog( "counteruav", self.team, killstreak_id ); counterUav killstreaks::play_pilot_dialog_on_owner( "arrive", "counteruav", killstreak_id ); counterUav thread killstreaks::player_killstreak_threat_tracking( "counteruav" ); self AddWeaponStat( GetWeapon( "counteruav" ), "used", 1 ); return true; } function HackedPreFunction( hacker ) { cuav = self; cuav ResetActiveCounterUAV(); } function SpawnCounterUAV( owner, killstreak_id ) { minFlyHeight = airsupport::getMinimumFlyHeight(); //cuav = spawn( "script_model", airsupport::GetMapCenter() + ( 0, 0, ( minFlyHeight + COUNTER_UAV_POSITION_Z_OFFSET ) ) ); cuav = SpawnVehicle( "veh_counteruav_mp", airsupport::GetMapCenter() + ( 0, 0, ( minFlyHeight + (isdefined(level.counter_uav_position_z_offset)?level.counter_uav_position_z_offset:( 1000 )) ) ), ( 0, 0, 0 ), "counteruav" ); cuav AssignFirstAvailableOffsetIndex(); cuav killstreaks::configure_team( "counteruav", killstreak_id, owner, undefined, undefined, &ConfigureTeamPost ); cuav killstreak_hacking::enable_hacking( "counteruav", &HackedPreFunction, undefined ); cuav.targetname = "counteruav"; killstreak_detect::killstreakTargetSet( cuav ); cuav thread heatseekingmissile::MissileTarget_ProximityDetonateIncomingMissile( "crashing", undefined, true ); cuav.maxhealth = ( 700 ); cuav.health = 99999; cuav.rocketDamage = ( 700 ) + 1; cuav SetDrawInfrared( true ); if ( !isdefined( level.counter_uav_entities ) ) level.counter_uav_entities = []; else if ( !IsArray( level.counter_uav_entities ) ) level.counter_uav_entities = array( level.counter_uav_entities ); level.counter_uav_entities[level.counter_uav_entities.size]=cuav;; return cuav; } function ConfigureTeamPost( owner, isHacked ) { cuav = self; if ( isHacked == false ) { cuav teams::HideToSameTeam(); } else { cuav SetVisibleToAll(); } cuav thread teams::WaitUntilTeamChangeSingleton( owner, "CUAV_watch_team_change", &OnTeamChange, self.entNum, "death", "leaving", "crashing" ); cuav AddActiveCounterUAV(); } function ListenForMove() { self endon( "death" ); self endon( "leaving" ); while( true ) { self thread CounterUAVMove(); level util::waittill_any( "counter_uav_move_" + self.team, "counter_uav_move_" + self.ownerEntNum ); } } function CounterUAVMove() { self endon( "death" ); self endon( "leaving" ); level endon( "counter_uav_move_" + self.team ); destination = ( 0, 0, 0 ); if( level.teamBased ) { destination = self GetCurrentPosition( self.team ); } else { destination = self GetCurrentPosition( self.ownerEntNum ); } lookAngles = VectorToAngles( destination - self.origin ); rotationAccelerationDuration = ( 0.5 ) * ( 0.2 ); rotationDecelerationDuration = ( 0.5 ) *( 0.2 ); // as a vehicle, we cannot use RotateTo anymore; we'll figure this out soon // self RotateTo( lookAngles, COUNTER_UAV_ROTATION_DURATION, rotationAccelerationDuration, rotationDecelerationDuration ); // self waittill( "rotatedone" ); travelAccelerationDuration = ( 5 ) * ( 0.2 ); travelDecelerationDuration = ( 5 ) * ( 0.2 ); //self MoveTo( destination, COUNTER_UAV_SPEED, travelAccelerationDuration, travelDecelerationDuration ); self SetVehGoalPos( destination, true, false ); } function PlayFx( name ) { self endon( "death" ); wait ( 0.1 ); if ( isdefined( self ) ) { PlayFXOnTag( name, self, "tag_origin" ); } } function OnLowHealth( attacker, weapon ) { self.is_damaged = true; params = level.killstreakBundle["counteruav"]; if( isdefined( params.fxLowHealth ) ) PlayFXOnTag( params.fxLowHealth, self, "tag_origin" ); } function OnTeamChange( entNum, event ) { DestroyCounterUAV( undefined, undefined ); } function OnPlayerJoinedTeam() { HideAllCounterUAVsToSameTeam(); } function OnTimeout() { self.leaving = true; self killstreaks::play_pilot_dialog_on_owner( "timeout", "counteruav" ); self airsupport::Leave( ( 5 ) ); wait( ( 5 ) ); self RemoveActiveCounterUAV(); Target_Remove( self ); self delete(); } function OnTimecheck() { self killstreaks::play_pilot_dialog_on_owner( "timecheck", "counteruav", self.killstreak_id ); } function DestroyCounterUavByEMP( attacker, arg ) { DestroyCounterUav( attacker, GetWeapon( "emp" ) ); } function DestroyCounterUAV( attacker, weapon ) { if ( self.leaving !== true ) { self killstreaks::play_destroyed_dialog_on_owner( "counteruav", self.killstreak_id ); } attacker = self [[ level.figure_out_attacker ]]( attacker ); if( isdefined( attacker ) && ( !isdefined( self.owner ) || self.owner util::IsEnemyPlayer( attacker ) ) ) { challenges::destroyedAircraft( attacker, weapon, false ); scoreevents::processScoreEvent( "destroyed_counter_uav", attacker, self.owner, weapon ); LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_COUNTERUAV", attacker.entnum ); attacker challenges::addFlySwatterStat( weapon, self ); } self PlaySound( "evt_helicopter_midair_exp" ); self RemoveActiveCounterUAV(); if ( Target_IsTarget( self ) ) { Target_Remove( self ); } self thread DeleteCounterUAV(); } function DeleteCounterUAV() { self notify( "crashing" ); params = level.killstreakBundle["counteruav"]; if( isdefined( params.ksExplosionFX ) && isdefined( self ) ) self thread PlayFx( params.ksExplosionFX ); wait( 0.1 ); if ( isdefined( self ) ) { self setModel( "tag_origin" ); } wait( 0.2 ); if ( isdefined( self ) ) { self notify( "delete" ); self delete(); } } function EnemyCounterUAVActive() { if( level.teamBased ) { foreach( team in level.teams ) { if( team == self.team ) { continue; } if( TeamHasActiveCounterUAV( team ) ) { return true; } } } else { enemies = self teams::GetEnemyPlayers(); foreach( player in enemies ) { if( player HasActiveCounterUAV() ) { return true; } } } return false; } function HasActiveCounterUAV() { return ( level.activeCounterUAVs[ self.entNum ] > 0 ); } function TeamHasActiveCounterUAV( team ) { return ( level.activeCounterUAVs[ team ] > 0 ); } function HasIndexActiveCounterUAV( team_or_entnum ) { return ( level.activeCounterUAVs[ team_or_entnum ] > 0 ); } function AddActiveCounterUAV() { if ( level.teamBased ) { level.activeCounterUAVs[ self.team ]++; foreach( team in level.teams ) { if ( team == self.team ) { continue; } if( satellite::HasSatellite( team ) ) { self.owner challenges::blockedSatellite(); } } } else { level.activeCounterUAVs[ self.ownerEntnum ]++; keys = getarraykeys( level.activeCounterUAVs ); for ( i = 0; i < keys.size; i++ ) { if( keys[i] == self.ownerEntNum ) { continue; } if( satellite::HasSatellite( keys[i] ) ) { self.owner challenges::blockedSatellite(); break; } } } level.activePlayerCounterUAVs[ self.ownerEntNum ]++; level notify( "counter_uav_updated" ); } function RemoveActiveCounterUAV() { cuav = self; cuav ResetActiveCounterUAV(); cuav killstreakrules::killstreakStop( "counteruav", self.originalteam, self.killstreak_id ); } function ResetActiveCounterUAV() { if ( level.teamBased ) { level.activeCounterUAVs[ self.team ]--; assert( level.activeCounterUAVs[ self.team ] >= 0 ); if ( level.activeCounterUAVs[ self.team ] < 0 ) { level.activeCounterUAVs[ self.team ] = 0; } } else if ( isdefined( self.owner ) ) { assert( isdefined( self.ownerEntNum ) ); if ( !isdefined( self.ownerEntNum ) ) { self.ownerEntNum = self.owner getEntityNumber(); } level.activeCounterUAVs[self.ownerEntNum ]--; assert( level.activeCounterUAVs[ self.ownerEntNum ] >= 0 ); if ( level.activeCounterUAVs[ self.ownerEntNum ] < 0 ) { level.activeCounterUAVs[ self.ownerEntNum ] = 0; } } level.activePlayerCounterUAVs[ self.ownerEntNum ]--; level notify ( "counter_uav_updated" ); } function WatchCounterUAVs() { while( true ) { level waittill( "counter_uav_updated" ); foreach( player in level.players ) { if( player EnemyCounterUAVActive() ) { player clientfield::set_to_player( "counteruav", 1 ); } else { player clientfield::set_to_player( "counteruav", 0 ); } } } } function HideAllCounterUAVsToSameTeam() { foreach( counteruav in level.counter_uav_entities ) { if ( isdefined( counteruav ) ) { counteruav teams::HideToSameTeam(); } } }