#using scripts\codescripts\struct; #using scripts\shared\audio_shared; #using scripts\shared\callbacks_shared; #using scripts\shared\challenges_shared; #using scripts\shared\clientfield_shared; #using scripts\shared\killstreaks_shared; #using scripts\shared\scoreevents_shared; #using scripts\shared\util_shared; #using scripts\shared\_oob; #using scripts\shared\popups_shared; #using scripts\shared\vehicle_shared; #using scripts\shared\weapons\_heatseekingmissile; #using scripts\shared\weapons\_weaponobjects; #using scripts\shared\weapons\_hacker_tool; #using scripts\shared\visionset_mgr_shared; #using scripts\mp\_util; #using scripts\mp\gametypes\_globallogic; #using scripts\mp\gametypes\_globallogic_audio; #using scripts\mp\gametypes\_spawning; #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\teams\_teams; #using scripts\mp\killstreaks\_remote_weapons; #precache( "string", "KILLSTREAK_EARNED_HELICOPTER_GUNNER" ); #precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_NOT_AVAILABLE" ); #precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_INBOUND" ); #precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_HACKED" ); #precache( "string", "KILLSTREAK_DESTROYED_HELICOPTER_GUNNER" ); #precache( "string", "KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" ); #precache( "eventstring", "mpl_killstreak_osprey_strt" ); #namespace helicopter_gunner; function init() { killstreaks::register( "helicopter_gunner", "helicopter_player_gunner", "killstreak_helicopter_player_gunner", "helicopter_used", &ActivateMainGunner, true ); killstreaks::register_strings( "helicopter_gunner", &"KILLSTREAK_EARNED_HELICOPTER_GUNNER", &"KILLSTREAK_HELICOPTER_GUNNER_NOT_AVAILABLE", &"KILLSTREAK_HELICOPTER_GUNNER_INBOUND", undefined, &"KILLSTREAK_HELICOPTER_GUNNER_HACKED" ); killstreaks::register_dialog( "helicopter_gunner", "mpl_killstreak_osprey_strt", "helicopterGunnerDialogBundle", "helicopterGunnerPilotDialogBundle", "friendlyHelicopterGunner", "enemyHelicopterGunner", "enemyHelicopterGunnerMultiple", "friendlyHelicopterGunnerHacked", "enemyHelecopterGunnerHacked", "requestHelicopterGunner", "threatHelicopterGunner" ); killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_rockets" ); killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_primary" ); killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_secondary" ); killstreaks::register_alt_weapon( "helicopter_gunner", "helicopter_gunner_turret_tertiary" ); killstreaks::set_team_kill_penalty_scale( "helicopter_gunner", level.teamKillReducedPenalty ); killstreaks::devgui_scorestreak_command( "helicopter_gunner", "Debug Paths", "toggle scr_devHeliPathsDebugDraw 1 0"); killstreaks::register( "helicopter_gunner_assistant", "helicopter_gunner_assistant", "killstreak_" + "helicopter_gunner_assistant", "helicopter_used", &ActivateSupportGunner, true, undefined, false, false ); killstreaks::register_strings( "helicopter_gunner_assistant", &"KILLSTREAK_EARNED_HELICOPTER_GUNNER", &"KILLSTREAK_HELICOPTER_GUNNER_NOT_AVAILABLE", &"KILLSTREAK_HELICOPTER_GUNNER_INBOUND", undefined, &"KILLSTREAK_HELICOPTER_GUNNER_HACKED" ); killstreaks::register_dialog( "helicopter_gunner_assistant", "mpl_killstreak_osprey_strt", "helicopterGunnerDialogBundle", "helicopterGunnerPilotDialogBundle", "friendlyHelicopterGunner", "enemyHelicopterGunner", "enemyHelicopterGunnerMultiple", "friendlyHelicopterGunnerHacked", "enemyHelecopterGunnerHacked", "requestHelicopterGunner", "threatHelicopterGunner" ); killstreaks::set_team_kill_penalty_scale( "helicopter_gunner_assistant", level.teamKillReducedPenalty ); // TODO: Move to killstreak data level.killstreaks["helicopter_gunner"].threatOnKill = true; callback::on_connect( &OnPlayerConnect ); callback::on_spawned( &UpdatePlayerState ); callback::on_joined_team( &UpdatePlayerState ); callback::on_joined_spectate( &UpdatePlayerState ); callback::on_disconnect( &UpdatePlayerState ); callback::on_player_killed( &UpdatePlayerState ); clientfield::register( "vehicle", "vtol_turret_destroyed_0", 1, 1, "int" ); clientfield::register( "vehicle", "vtol_turret_destroyed_1", 1, 1, "int" ); clientfield::register( "vehicle", "mothership", 1, 1, "int" ); clientfield::register( "toplayer", "vtol_update_client", 1, 1, "counter" ); clientfield::register( "toplayer", "fog_bank_2", 1, 1, "int" ); visionset_mgr::register_info( "visionset", "mothership_visionset", 1, 70, 16, true, &visionset_mgr::ramp_in_out_thread_per_player_death_shutdown, false ); level thread WaitForGameEndThread(); level.vtol = undefined; } function OnPlayerConnect() { if( !isdefined( self.entNum ) ) { self.entNum = self getEntityNumber(); } } function UpdatePlayerState() { player = self; UpdateAllKillstreakInventory(); } function UpdateAllKillstreakInventory() { foreach( player in level.players ) { if( isdefined( player.sessionstate ) && player.sessionstate == "playing" ) UpdateKillstreakInventory( player ); } } function UpdateKillstreakInventory( player ) { if( !isdefined( player ) ) return; heli_team = undefined; if( isdefined( level.vtol ) && isdefined( level.vtol.owner ) && !level.vtol.shuttingDown && ( level.vtol.totalRocketHits < ( 6 ) ) ) heli_team = level.vtol.owner.team; if( isdefined( heli_team ) && ( player.team == heli_team ) ) { if( ( GetFirstAvailableSeat( player ) != -1 ) && !isdefined( level.vtol.usage[player.entNum] ) ) { if( !player killstreaks::has_killstreak( "inventory_helicopter_gunner_assistant" ) ) player killstreaks::give( "inventory_helicopter_gunner_assistant", undefined, undefined, true, true ); return; } } if( player killstreaks::has_killstreak( "inventory_helicopter_gunner_assistant" ) ) player killstreaks::take( "inventory_helicopter_gunner_assistant" ); } function ActivateMainGunner( killstreakType ) { player = self; while( isdefined( level.vtol ) && level.vtol.shuttingdown ) { if( !player killstreakrules::isKillstreakAllowed( "helicopter_gunner", player.team ) ) return false; } player util::freeze_player_controls( true ); result = player SpawnHeliGunner(); player util::freeze_player_controls( false ); if( level.gameEnded ) return true; if( !isdefined( result ) ) return false; return result; } function ActivateSupportGunner( killstreakType ) { player = self; if( isdefined( level.vtol ) && level.vtol.shuttingdown ) return false; if( isdefined( level.vtol.usage[player.entNum] ) ) return false; player util::freeze_player_controls( true ); result = player EnterHelicopter( false ); player util::freeze_player_controls( false ); return result; } function GetFirstAvailableSeat( player ) { if( isdefined( level.vtol ) && ( !level.vtol.shuttingDown ) && ( level.vtol.team == player.team ) && ( level.vtol.owner != player ) ) { for( i = 0; i < ( 2 ); i++ ) { if( !isdefined( level.vtol.assistants[i].occupant ) && !level.vtol.assistants[i].destroyed ) { return i; } } } return -1; } function InitHelicopterSeat( index, destroyTag ) { level.vtol.assistants[index] = SpawnStruct(); assistant = level.vtol.assistants[index]; assistant.occupant = undefined; assistant.destroyed = false; assistant.rocketHits = 0; assistant.targetTag = destroyTag; assistant.targetEnt = spawn( "script_model", ( 0, 0, 0 ) ); assistant.targetEnt.useVTOLTime = true; assistant.targetEnt SetModel( "p7_dogtags_enemy" ); // hack to send ent to clients for targeting assistant.targetEnt LinkTo( level.vtol, assistant.targetTag, ( 0, 0, 0 ), ( 0, 0, 0 ) ); assistant.targetEnt.team = level.vtol.team; Target_Set( assistant.targetEnt, ( 0, 0, 0 ) ); Target_SetAllowHighSteering( assistant.targetEnt, true ); assistant.targetEnt.parent = level.vtol; level.vtol vehicle::add_to_target_group( assistant.targetEnt ); } function HackedPreFunction( hacker ) { heliGunner = self; heliGunner.owner unlink(); level.vtol clientfield::set( "vehicletransition", 0 ); visionset_mgr::deactivate( "visionset", "mothership_visionset", heliGunner.owner ); heliGunner.owner SetModelLodBias( 0 ); heliGunner.owner clientfield::set_to_player( "fog_bank_2", 0 ); heliGunner.owner clientfield::set_to_player( "toggle_flir_postfx", 0 ); heliGunner.owner notify( "gunner_left" ); heliGunner.owner killstreaks::clear_using_remote(); heliGunner.owner killstreaks::unhide_compass(); heliGunner.owner vehicle::stop_monitor_missiles_locked_on_to_me(); heliGunner.owner vehicle::stop_monitor_damage_as_occupant(); foreach( assistant in heliGunner.assistants ) { if( isdefined( assistant.occupant ) ) assistant.occupant iPrintLnBold( &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" ); LeaveHelicopter( assistant.occupant, false ); } heliGunner MakeVehicleUnusable(); } function HackedPostFunction( hacker ) { heliGunner = self; heliGunner clientfield::set( "enemyvehicle", 2 ); heliGunner MakeVehicleUsable(); heliGunner UseVehicle( hacker, 0 ); level.vtol clientfield::set( "vehicletransition", 1 ); heliGunner thread vehicle::monitor_missiles_locked_on_to_me( hacker ); heliGunner thread vehicle::monitor_damage_as_occupant( hacker ); hacker thread WatchVisionSwitchThread(); hacker killstreaks::hide_compass(); heliGunner thread WatchPlayerExitRequestThread( hacker ); visionset_mgr::activate( "visionset", "mothership_visionset", hacker, 1, heliGunner killstreak_hacking::get_hacked_timeout_duration_ms(), 1 ); hacker SetModelLodBias( (isdefined(level.mothership_lod_bias)?level.mothership_lod_bias:8) ); heliGunner.owner GiveDedicatedShadow( level.vtol ); heliGunner.owner clientfield::set_to_player( "fog_bank_2", 1 ); hacker thread WatchPlayerTeamChangeThread( heliGunner ); hacker killstreaks::set_killstreak_delay_killcam( "helicopter_gunner" ); if ( heliGunner.killstreak_timer_started ) { heliGunner.killstreak_duration = heliGunner killstreak_hacking::get_hacked_timeout_duration_ms(); heliGunner.killstreak_end_time = hacker killstreak_hacking::set_vehicle_drivable_time_starting_now( heliGunner ); heliGunner.killstreakEndTime = int( heliGunner.killstreak_end_time ); } else { heliGunner.killstreak_timer_start_using_hacked_time = true; } } function SpawnHeliGunner() { player = self; player endon( "disconnect" ); level endon( "game_ended" ); if( !isdefined( level.heli_paths ) || !level.heli_paths.size ) return false; if( !isdefined( level.Heli_primary_path ) || !level.heli_primary_path.size ) return false; if( ( isdefined( player.isPlanting ) && player.isPlanting ) || ( isdefined( player.isDefusing ) && player.isDefusing ) || player util::isUsingRemote() || player IsWallRunning() || player oob::IsOutOfBounds() ) return false; killstreak_id = player killstreakrules::killstreakStart( "helicopter_gunner", player.team, undefined, true ); if( killstreak_id == (-1) ) return false; startNode = level.heli_primary_path[0]; level.vtol = SpawnVehicle( "veh_bo3_mil_gunship_mp", startnode.origin, startnode.angles, "dynamic_spawn_ai" ); level.vtol killstreaks::configure_team( "helicopter_gunner", killstreak_id, player, "helicopter" ); level.vtol killstreak_hacking::enable_hacking( "helicopter_gunner", &HackedPreFunction, &HackedPostFunction ); level.vtol.killstreak_id = killstreak_id; level.vtol.destroyFunc = &DeleteHelicopterCallback; level.vtol.hardpointType = "helicopter_gunner"; level.vtol clientfield::set( "enemyvehicle", 1 ); level.vtol clientfield::set( "vtol_turret_destroyed_0", 0 ); level.vtol clientfield::set( "vtol_turret_destroyed_1", 0 ); level.vtol clientfield::set( "mothership", 1 ); level.vtol vehicle::init_target_group(); level.vtol.killstreak_timer_started = false; level.vtol.allowdeath = false; level.vtol.playerMovedRecently = false; level.vtol.soundmod = "default_loud"; level.vtol hacker_tool::registerwithhackertool(( 50 ), ( 10000 ) ); level.vtol.assistants = []; level.vtol.usage = []; InitHelicopterSeat( 0, "tag_gunner_barrel1" ); InitHelicopterSeat( 1, "tag_gunner_barrel2"); level.destructible_callbacks["turret_destroyed"] = &VTOLDestructibleCallback; level.destructible_callbacks["turret1_destroyed"] = &VTOLDestructibleCallback; level.destructible_callbacks["turret2_destroyed"] = &VTOLDestructibleCallback; level.vtol.shuttingDown = false; level.vtol thread PlayLockOnSoundsThread( player, level.vtol ); level.vtol thread helicopter::wait_for_killed(); level.vtol thread wait_for_bda_dialog(); level.vtol.maxhealth = ( 15000 ); tableHealth = killstreak_bundles::get_max_health( "helicopter_gunner" ); if ( isdefined( tableHealth ) ) { level.vtol.maxhealth = tableHealth; } level.vtol.original_health = level.vtol.maxhealth; level.vtol.health = level.vtol.maxhealth; level.vtol SetCanDamage( true ); level.vtol thread heatseekingmissile::MissileTarget_ProximityDetonateIncomingMissile( "death" ); level.vtol thread WatchMissilesThread(); attack_nodes = GetEntArray( "heli_attack_area", "targetname" ); if( attack_nodes.size ) { level.vtol thread HelicopterThinkThread( startNode, attack_nodes ); player thread WatchLocationChangeThread( attack_nodes ); } else { level.vtol thread helicopter::heli_fly( startNode, 0.0, "helicopter_gunner" ); } level.vtol.totalRocketHits = 0; level.vtol.turretRocketHits = 0; level.vtol.targetEnt = undefined; level.vtol.overrideVehicleDamage = &HelicopterGunnerDamageOverride; level.vtol.hackedHealthUpdateCallback = &HelicopterGunner_hacked_health_callback; level.vtol.DetonateViaEMP = &helicopteDetonateViaEMP; player thread killstreaks::play_killstreak_start_dialog( "helicopter_gunner", player.team, killstreak_id ); level.vtol killstreaks::play_pilot_dialog_on_owner( "arrive", "helicopter_gunner", killstreak_id ); player AddWeaponStat( GetWeapon( "helicopter_player_gunner" ), "used", 1 ); level.vtol thread WaitForVTOLShutdownThread(); result = player EnterHelicopter( true ); return result; } function HelicopterGunner_hacked_health_callback() { helicopter = self; if ( helicopter.shuttingDown == true ) { return; } // Not sure what design wants here. // for( seatIndex = 0; seatIndex < HELICOPTER_GUNNER_ASSISTANT_SEAT_COUNT; seatIndex++ ) // { // assistant = helicopter.assistants[seatIndex]; // if( !assistant.destroyed ) // { // damage = 1000; // helicopter.noDamageFeedback = 1; // helicopter DoDamage( damage, assistant.targetEnt.origin, undefined, undefined, undefined, "MOD_UNKNOWN", 0, undefined, seatIndex + 8 ); // helicopter.noDamageFeedback = 0; // // SupportTurretDestroyed( helicopter, seatIndex ); // } // } // helicopter AllowMainTurretLockon(); hackedHealth = killstreak_bundles::get_hacked_health( "helicopter_gunner" ); assert( isdefined( hackedHealth ) ); if ( helicopter.health > hackedhealth ) { helicopter.health = hackedhealth; } } function WaitForGameEndThread() { level waittill( "game_ended" ); if( isdefined( level.vtol ) && isdefined( level.vtol.owner )) LeaveHelicopter( level.vtol.owner, true ); } function WaitForVTOLShutdownThread() { helicopter = self; helicopter waittill( "vtol_shutdown", attacker ); if( isdefined( attacker ) ) { LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_DESTROYED_HELICOPTER_GUNNER", attacker.entnum ); } if( isdefined( helicopter.targetEnt ) ) { Target_Remove( helicopter.targetEnt ); helicopter.targetEnt Delete(); helicopter.targetEnt = undefined; } for( seatIndex = 0; seatIndex < ( 2 ); seatIndex++ ) { assistant = level.vtol.assistants[seatIndex]; if( isdefined( assistant.targetEnt ) ) { Target_Remove( assistant.targetEnt ); assistant.targetEnt Delete(); assistant.targetEnt = undefined; } } killstreakrules::killstreakStop( "helicopter_gunner", helicopter.originalTeam, helicopter.killstreak_id ); LeaveHelicopter( level.vtol.owner, true ); level.vtol = undefined; helicopter delete(); } function DeleteHelicopterCallback() { helicopter = self; helicopter notify( "vtol_shutdown", undefined ); } function OnTimeoutCallback() { for( i = 0; i < ( 2 ); i++ ) { if( isdefined(level.vtol.assistants[i].occupant ) ) { level.vtol.assistants[i].occupant killstreaks::play_pilot_dialog( "timeout", "helicopter_gunner", undefined, level.vtol.killstreak_id ); } } LeaveHelicopter( level.vtol.owner, true ); } function WatchPlayerTeamChangeThread( helicopter ) { helicopter notify( "mothership_team_change" ); helicopter endon ( "mothership_team_change" ); assert( IsPlayer( self ) ); player = self; player endon( "gunner_left" ); player util::waittill_any( "joined_team", "disconnect", "joined_spectators" ); ownerLeft = helicopter.ownerEntNum == player.entNum; player thread LeaveHelicopter( player, ownerLeft ); // need to thread to prevent endon( "gunner_left" ) to terminate the LeaveHelicopter if( ownerLeft ) helicopter notify( "vtol_shutdown", undefined ); } function WatchPlayerExitRequestThread( player ) { player notify( "WatchPlayerExitRequestThread_singleton" ); player endon ( "WatchPlayerExitRequestThread_singleton" ); assert( IsPlayer( player ) ); mothership = self; level endon( "game_ended" ); player endon( "disconnect" ); player endon( "gunner_left" ); owner = mothership.ownerEntNum == player.entNum; while( true ) { timeUsed = 0; while( player UseButtonPressed() ) { timeUsed += 0.05; if( timeUsed > 0.25 ) { mothership killstreaks::play_pilot_dialog_on_owner( "remoteOperatorRemoved", "helicopter_gunner", level.vtol.killstreak_id ); player thread LeaveHelicopter( player, owner ); // need to thread this so that endon( "gunner_left" ) does not self termniate in LeaveHelicopter() return; } {wait(.05);}; } {wait(.05);}; } } function EnterHelicopter( isOwner ) { assert( IsPlayer( self ) ); player = self; seatIndex = -1; if( !isOwner ) { seatIndex = GetFirstAvailableSeat( player ); if( seatIndex == -1 ) { return false; } level.vtol.assistants[ seatIndex ].occupant = player; } level.vtol.occupied = true; // needed for killcam to function properly player util::setUsingRemote( "helicopter_gunner" ); player.ignoreEMPJammed = true; result = player killstreaks::init_ride_killstreak( "helicopter_gunner" ); player.ignoreEMPJammed = false; if( result != "success" ) { if( result != "disconnect" ) { player killstreaks::clear_using_remote(); } if( !isOwner ) level.vtol.assistants[ seatIndex ].occupant = undefined; if( isOwner ) { level.vtol.failed2enter = true; level.vtol notify( "vtol_shutdown" ); } return false; } if( isOwner ) { level.vtol UseVehicle( player, 0 ); level.vtol clientfield::set( "vehicletransition", 1 ); } else { if( level.vtol.shuttingdown ) { player killstreaks::clear_using_remote(); return false; } level.vtol UseVehicle( player, seatIndex + ( 1 ) ); level.vtol clientfield::set( "vehicletransition", 1 ); level.vtol killstreaks::play_pilot_dialog_on_owner( "remoteOperatorAdd", "helicopter_gunner", level.vtol.killstreak_id ); } killcament = spawn( "script_model", ( 0, 0, 0 ) ); killcament SetModel( "tag_origin" ); killcament.angles = ( 0, 0, 0 ); killcament SetWeapon( GetWeapon( "helicopter_gunner_turret_primary" ) ); killcament linkto( level.vtol, "tag_barrel", ( 370, 0, 25 ), ( 0, 0, 0 ) ); level.vtol.killcament = killcament; level.vtol.usage[player.entNum] = 1; level.vtol thread audio::sndUpdateVehicleContext(true); level.vtol thread vehicle::monitor_missiles_locked_on_to_me( player ); level.vtol thread vehicle::monitor_damage_as_occupant( player ); if ( level.vtol.killstreak_timer_started ) { player vehicle::set_vehicle_drivable_time( level.vtol.killstreak_duration, level.vtol.killstreak_end_time ); } else { player vehicle::set_vehicle_drivable_time( 9009009, GetTime() + 9009009 ); } update_client_for_player( player ); UpdateAllKillstreakInventory(); player thread WatchVisionSwitchThread(); level.vtol thread WatchPlayerExitRequestThread( player ); player thread WatchPlayerTeamChangeThread( level.vtol ); visionset_mgr::activate( "visionset", "mothership_visionset", player, 1, ( 60000 ), 1 ); player SetModelLodBias( (isdefined(level.mothership_lod_bias)?level.mothership_lod_bias:8) ); player GiveDedicatedShadow( level.vtol ); player clientfield::set_to_player( "fog_bank_2", 1 ); if ( true ) { player thread HideCompassAfterWait( 0.1 ); // need to do this due to the way this scorestreak starts up } return true; } function HideCompassAfterWait( waittime ) { self endon( "death" ); self endon( "disconnect" ); wait waittime; self killstreaks::hide_compass(); } function MainTurretDestroyed( helicopter, eAttacker, weapon ) { helicopter.owner iPrintLnBold( &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" ); if ( isdefined(helicopter.targetEnt)) { Target_Remove( helicopter.targetEnt ); helicopter.targetEnt Delete(); helicopter.targetEnt = undefined; } helicopter.shuttingDown = true; UpdateAllKillstreakInventory(); eAttacker = self [[ level.figure_out_attacker ]]( eAttacker ); if( !isdefined( helicopter.destroyScoreEventGiven ) && isdefined( eAttacker ) && ( !isdefined( helicopter.owner ) || helicopter.owner util::IsEnemyPlayer( eAttacker ) ) ) { LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED", eAttacker.entnum ); challenges::destroyedAircraft( eAttacker, weapon, true ); eAttacker challenges::addFlySwatterStat( weapon, helicopter ); scoreevents::processScoreEvent( "destroyed_vtol_mothership", eAttacker, helicopter.owner, weapon ); helicopter killstreaks::play_destroyed_dialog_on_owner( "helicopter_gunner", helicopter.killstreak_id ); helicopter.destroyScoreEventGiven = 1; } helicopter thread PerformLeaveHelicopterFromDamage(); } function wait_for_bda_dialog( killstreakId ) { self endon( "vtol_shutdown" ); while(true) { self waittill( "bda_dialog", dialogKey ); for( i = 0; i < ( 2 ); i++ ) { if( isdefined( level.vtol.assistants[i].occupant ) ) { level.vtol.assistants[i].occupant killstreaks::play_pilot_dialog( dialogKey, "helicopter_gunner", killstreakId, self.pilotIndex ); } } } } function SupportTurretDestroyed( helicopter, seatIndex ) { assistant = helicopter.assistants[seatIndex]; if( !assistant.destroyed ) { Target_Remove( assistant.targetEnt ); level.vtol vehicle::remove_from_target_group(); assistant.targetEnt Delete(); assistant.targetEnt = undefined; assistant.destroyed = true; if ( isdefined( helicopter.owner ) && isdefined( helicopter.hardpointType ) ) { helicopter killstreaks::play_pilot_dialog_on_owner( "weaponDestroyed", helicopter.hardpointType, helicopter.killstreak_id ); } if( isdefined( assistant.occupant ) ) { assistant.occupant globallogic_audio::flush_killstreak_dialog_on_player( helicopter.killstreak_id ); assistant.occupant killstreaks::play_pilot_dialog( "weaponDestroyed", helicopter.hardpointType, undefined, helicopter.pilotIndex ); wait 2.0; LeaveHelicopter( assistant.occupant, false ); } } // update destroyed states if ( seatIndex == 0 ) { level.vtol clientfield::set( "vtol_turret_destroyed_0", 1 ); level.vtol update_client_for_driver_and_occupants(); } else if ( seatIndex == 1 ) { level.vtol clientfield::set( "vtol_turret_destroyed_1", 1 ); level.vtol update_client_for_driver_and_occupants(); } } function update_client_for_driver_and_occupants() // self == vtol { vtol = self; update_client_for_player( vtol.owner ); foreach( assistant in vtol.assistants ) { update_client_for_player( assistant.occupant ); } } function update_client_for_player( player ) { if ( isdefined( player ) ) { player clientfield::increment_to_player( "vtol_update_client", 1 ); } } function VTOLDestructibleCallback( brokenNotify, eAttacker, weapon ) { helicopter = self; helicopter endon ( "delete" ); helicopter endon ( "vtol_shutdown" ); notifies = []; notifies[0] = "turret1_destroyed"; notifies[1] = "turret2_destroyed"; for( seatIndex = 0; seatIndex < ( 2 ); seatIndex++ ) { if( brokenNotify == notifies[seatIndex] ) { SupportTurretDestroyed( helicopter, seatIndex ); break; } } if( brokenNotify == "turret_destroyed" ) { MainTurretDestroyed( helicopter, eAttacker, weapon ); return; } helicopter AllowMainTurretLockon(); } function AllowMainTurretLockon() { helicopter = self; // allow lockon on the main turrets if( helicopter.assistants[0].destroyed && helicopter.assistants[1].destroyed ) { if( !isdefined( helicopter.targetEnt ) ) { helicopter.targetEnt = spawn( "script_model", ( 0, 0, 0 ) ); helicopter.targetEnt SetModel( "p7_dogtags_enemy" ); // hack to send ent to clients for targeting helicopter.targetEnt LinkTo( level.vtol, "tag_barrel", ( 0, 0, 0 ), ( 0, 0, 0 ) ); helicopter.targetEnt.parent = level.vtol; helicopter.targetEnt.team = level.vtol.team; Target_Set( helicopter.targetEnt, ( 0, 0, 0 ) ); helicopter.targetEnt.useVTOLTime = true; Target_SetAllowHighSteering( helicopter.targetEnt, true ); level.vtol vehicle::add_to_target_group( helicopter.targetEnt ); } } } function LeaveHelicopter( player, ownerLeft ) { if( !isdefined( level.vtol ) || level.vtol.completely_shutdown === true ) return; if( isdefined( player ) ) { player vehicle::stop_monitor_missiles_locked_on_to_me(); player vehicle::stop_monitor_damage_as_occupant(); } if( isdefined( player ) && isdefined( level.vtol ) && isdefined( level.vtol.owner ) ) { if( isdefined( player.usingvehicle ) && player.usingvehicle ) { player unlink(); level.vtol clientfield::set( "vehicletransition", 0 ); if( ownerLeft ) player killstreaks::take( "helicopter_gunner" ); else player killstreaks::take( "inventory_helicopter_gunner_assistant" ); } } if( ownerLeft ) { level.vtol.shuttingDown = true; foreach( assistant in level.vtol.assistants ) { if( isdefined( assistant.occupant ) ) { assistant.occupant iPrintLnBold( &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED" ); LeaveHelicopter( assistant.occupant, false ); } } level.vtol.occupied = false; level.vtol.hardpointType = "helicopter_gunner"; level.vtol thread helicopter::heli_leave(); level.vtol thread audio::sndUpdateVehicleContext(false); } else { if( isdefined( player ) ) { player globallogic_audio::flush_killstreak_dialog_on_player( level.vtol.killstreak_id ); foreach( assistant in level.vtol.assistants ) { if( isdefined( assistant.occupant ) && assistant.occupant == player ) { assistant.occupant = undefined; break; } } } } if( isdefined( player ) ) { player clientfield::set_to_player( "toggle_flir_postfx", 0 ); visionset_mgr::deactivate( "visionset", "mothership_visionset", player ); player SetModelLodBias( 0 ); player GiveDedicatedShadow( player ); player clientfield::set_to_player( "fog_bank_2", 0 ); player killstreaks::unhide_compass(); player notify( "gunner_left" ); player killstreaks::clear_using_remote(); if( level.gameEnded ) player util::freeze_player_controls( true ); } UpdateAllKillstreakInventory(); if ( ownerLeft ) level.vtol.completely_shutdown = true; } function vtol_shake() { if( isdefined( level.vtol ) && isdefined( level.vtol.owner ) ) { org = level.vtol GetTagOrigin( "tag_barrel" ); magnitude = 0.3; duration = 2; radius = 500; v_pos = self.origin; Earthquake( magnitude, duration, org, 500 ); } } function HelicopterGunnerDamageOverride( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, vPoint, vDir, sHitLoc, vDamageOrigin, psOffsetTime, damageFromUnderneath, modelIndex, partName, vSurfaceNormal ) { helicopter = self; if( sMeansOfDeath == "MOD_TRIGGER_HURT" ) return 0; if( helicopter.shuttingDown ) return 0; iDamage = self killstreaks::OnDamagePerWeapon( "helicopter_gunner", eAttacker, iDamage, iDFlags, sMeansOfDeath, weapon, level.vtol.maxhealth, undefined, level.vtol.maxhealth*0.4, undefined, 0, undefined, true, 1.0 ); if( iDamage == 0 ) return 0; // handle rocket damage handleAsRocketDamage = ( ( sMeansOfDeath == "MOD_PROJECTILE" ) || ( sMeansOfDeath == "MOD_EXPLOSIVE" ) ); if ( weapon.statIndex == level.weaponShotgunEnergy.statIndex || weapon.statIndex == level.weaponPistolEnergy.statIndex || weapon.statIndex == level.weaponSmgNailGun.statIndex ) handleAsRocketDamage = false; if( handleAsRocketDamage ) { updateInventory = 1; missileTarget = eInflictor missile_gettarget(); vtol_shake(); rocketHit = 1.0; if ( weapon.statIndex == level.weaponLauncherMulti.statIndex ) rocketHit = 0.5; helicopter.totalRocketHits += rocketHit; if( isdefined( missileTarget ) ) { // handle rocket damage to the support turrets for( seatIndex = 0; seatIndex < ( 2 ); seatIndex++ ) { assistant = helicopter.assistants[seatIndex]; if( !assistant.destroyed && ( assistant.targetEnt == missileTarget ) ) { assistant.rocketHits += rocketHit; if ( assistant.rocketHits >= 2 ) { helicopter DoDamage( iDamage, assistant.targetEnt.origin, eAttacker, eInflictor, sHitLoc, "MOD_UNKNOWN", 0, weapon, seatIndex + 8 ); iDamage = 0; SupportTurretDestroyed( helicopter, seatIndex ); } } } // handle rocket damage to the main turrets if( isdefined( helicopter.targetEnt ) && ( helicopter.targetEnt == missileTarget ) ) { helicopter.turretRocketHits += rocketHit; // main turret need 2 rockets if( helicopter.turretRocketHits >= 2 ) { Target_Remove( helicopter.targetEnt ); helicopter.targetEnt Delete(); helicopter.targetEnt = undefined; } } } // allow lockon on the main turret if( helicopter.assistants[0].destroyed && helicopter.assistants[1].destroyed && ( !isdefined( helicopter.targetEnt ) ) ) { helicopter.targetEnt = spawn( "script_model", ( 0, 0, 0 ) ); helicopter.targetEnt SetModel( "p7_dogtags_enemy" ); // hack to send ent to clients for targeting helicopter.targetEnt LinkTo( level.vtol, "tag_barrel", ( 0, 0, 0 ), ( 0, 0, 0 ) ); helicopter.targetEnt.parent = level.vtol; helicopter.targetEnt.team = level.vtol.team; Target_Set( helicopter.targetEnt, ( 0, 0, 0 ) ); helicopter.targetEnt.useVTOLTime = true; Target_SetAllowHighSteering( helicopter.targetEnt, true ); } if( helicopter.totalRocketHits >= ( 6 ) ) { MainTurretDestroyed( helicopter, eAttacker, weapon ); updateInventory = 0; } if ( updateInventory ) UpdateAllKillstreakInventory(); } if( iDamage >= level.vtol.health && !helicopter.shuttingDown ) { helicopter.shuttingDown = true; UpdateAllKillstreakInventory(); if ( !isdefined( helicopter.destroyScoreEventGiven ) && isdefined( eAttacker ) && ( !isdefined( helicopter.owner ) || helicopter.owner util::IsEnemyPlayer( eAttacker ) ) ) { eAttacker = self [[ level.figure_out_attacker ]]( eAttacker ); LUINotifyEvent( &"player_callout", 2, &"KILLSTREAK_HELICOPTER_GUNNER_DAMAGED", eAttacker.entnum ); scoreevents::processScoreEvent( "destroyed_vtol_mothership", eAttacker, helicopter.owner, weapon ); helicopter killstreaks::play_destroyed_dialog_on_owner( "helicopter_gunner", helicopter.killstreak_id ); helicopter.destroyScoreEventGiven = 1; } helicopter thread PerformLeaveHelicopterFromDamage(); } if( helicopter.shuttingDown ) { if( iDamage >= helicopter.health ) iDamage = helicopter.health - 1; // keep it alive. We want it to go away not explode } ///#iprintln( partName + " health:" + helicopter.health + " damage:" + iDamage );#/ return iDamage; } function PerformLeaveHelicopterFromDamage() { helicopter = self; helicopter endon( "death" ); if ( self.leave_by_damage_initiated === true ) return; self.leave_by_damage_initiated = true; helicopter thread remote_weapons::do_static_fx(); failsafe_timeout = 5.0; helicopter util::waittill_any_timeout( failsafe_timeout, "static_fx_done" ); LeaveHelicopter( helicopter.owner, true ); } function helicopteDetonateViaEMP( attacker, weapon ) { MainTurretDestroyed( level.vtol, attacker, weapon ); } function MissileCleanupThread( missile ) { targetEnt = self; targetEnt endon( "delete" ); targetEnt endon( "death" ); missile util::waittill_any( "death", "delete" ); targetEnt Delete(); } function WatchMissilesThread() { helicopter = self; player = helicopter.owner; player endon( "disconnect" ); player endon( "gunner_left" ); heliMissile = GetWeapon( "helicopter_gunner_turret_rockets" ); while( true ) { player waittill( "missile_fire", missile ); trace_origin = level.vtol GetTagOrigin( "tag_flash" ); trace_direction = level.vtol GetTagAngles( "tag_barrel" ); trace_direction = AnglesToForward( trace_direction ) * 8000; trace = BulletTrace( trace_origin, trace_origin + trace_direction, false, level.vtol ); end_origin = trace["position"]; missiles = getentarray( "rocket", "classname" ); /# //Box( end_origin, (-4, -4, 0 ), ( 4, 4, 1000 ), 0, ( 0, 0.7, 0 ), 0.6, false, 9999999 ); #/ foreach( missile in missiles ) { if( missile.item == heliMissile ) { targetEnt = Spawn( "script_model", end_origin ); missile Missile_SetTarget( targetEnt ); targetEnt thread MissileCleanupThread( missile ); } } // setup the "reload" time for the player's vehicle HUD weapon_wait_duration_ms = Int( heliMissile.fireTime * 1000 ); player SetVehicleWeaponWaitDuration( weapon_wait_duration_ms ); player SetVehicleWeaponWaitEndTime( GetTime() + weapon_wait_duration_ms ); } } function WatchVisionSwitchThread() { assert( IsPlayer( self ) ); player = self; player endon( "disconnect" ); player endon( "gunner_left" ); inverted = false; player clientfield::set_to_player( "toggle_flir_postfx", 2 ); while( true ) { if( player JumpButtonPressed() ) { if( inverted ) { player clientfield::set_to_player( "toggle_flir_postfx", 2 ); player PlaySoundToPlayer( "mpl_cgunner_flir_off", player ); } else { player clientfield::set_to_player( "toggle_flir_postfx", 1 ); player PlaySoundToPlayer( "mpl_cgunner_flir_on", player ); } inverted = !inverted; while( player JumpButtonPressed() ) {wait(.05);}; } {wait(.05);}; } } function PlayLockOnSoundsThread( player, heli ) { player endon( "disconnect" ); player endon( "gunner_left" ); heli endon( "death" ); heli endon ( "crashing" ); heli endon ( "leaving" ); heli.lockSounds = spawn( "script_model", heli.origin ); wait ( 0.1 ); heli.lockSounds LinkTo( heli, "tag_player" ); while( true ) { heli waittill( "locking on" ); while( true ) { if( EnemyIsLocking( heli ) ) { heli.lockSounds PlaySoundToPlayer( "uin_alert_lockon", player ); wait ( 0.125 ); } if( EnemyLockedOn( heli ) ) { heli.lockSounds PlaySoundToPlayer( "uin_alert_lockon", player ); wait ( 0.125 ); } if( !EnemyIsLocking( heli ) && !EnemyLockedOn( heli ) ) { heli.lockSounds StopSounds(); break; } } } } function EnemyIsLocking( heli ) { return ( isdefined( heli.locking_on ) && heli.locking_on ); } function EnemyLockedOn( heli ) { return ( isdefined( heli.locked_on ) && heli.locked_on ); } function HelicopterThinkThread( startNode, destNodes ) { self notify( "flying"); self endon( "flying" ); self endon ( "death" ); self endon ( "crashing" ); self endon ( "leaving" ); nextnode = getent( startNode.target, "targetname" ); assert( isdefined( nextnode ), "Next node in path is undefined, but has targetname" ); self SetSpeed( 150, 80 ); self setvehgoalpos( nextnode.origin + ( 0, 0, ( 2000 ) ), 1 ); self waittill( "near_goal" ); firstpass = true; //while( true ) { if( !self.playerMovedRecently ) { node = self UpdateAreaNodes( destNodes, false ); level.vtol.currentNode = node; targetNode = getEnt( node.target, "targetname" ); TravelToNode( targetNode ); if( isdefined( targetNode.script_airspeed ) && isdefined( targetNode.script_accel ) ) { heli_speed = targetNode.script_airspeed; heli_accel = targetNode.script_accel; } else { heli_speed = 150+randomInt(20); heli_accel = 40+randomInt(10); } self SetSpeed( heli_speed, heli_accel ); self setvehgoalpos( targetNode.origin + ( 0, 0, ( 2000 ) ), 1 ); self setgoalyaw( targetNode.angles[ 1 ] + ( 0 ) ); } if( ( 0 ) != 0 ) { self waittill( "near_goal" ); waitTime = ( 0 ); } else if( !isdefined( targetNode.script_delay ) ) { self waittill( "near_goal" ); waitTime = 10 + randomInt( 5 ); } else { self waittillmatch( "goal" ); waitTime = targetNode.script_delay; } if( firstpass ) { self.killstreak_duration = ( ( self.killstreak_timer_start_using_hacked_time === true ) ? self killstreak_hacking::get_hacked_timeout_duration_ms() : ( 60000 ) ); self.killstreak_end_time = GetTime() + self.killstreak_duration; self.killstreakEndTime = int( self.killstreak_end_time ); self thread killstreaks::WaitForTimeout( "helicopter_gunner", self.killstreak_duration, &OnTimeoutCallback, "delete", "death" ); self.killstreak_timer_started = true; self UpdateDrivableTimeForAllOccupants( self.killstreak_duration, self.killstreak_end_time ); firstpass = false; } wait( waitTime ); } } function UpdateDrivableTimeForAllOccupants( duration_ms, end_time_ms ) // self == vtol { if ( isdefined( self.owner ) ) { self.owner vehicle::set_vehicle_drivable_time( duration_ms, end_time_ms ); } for( i = 0; i < ( 2 ); i++ ) { if( isdefined( self.assistants[i].occupant ) && !self.assistants[i].destroyed ) { self.assistants[i].occupant vehicle::set_vehicle_drivable_time( duration_ms, end_time_ms ); } } } function WatchLocationChangeThread( destNodes ) { player = self; player endon( "disconnect" ); player endon( "gunner_left" ); helicopter = level.vtol; helicopter endon ( "delete" ); helicopter endon ( "vtol_shutdown" ); player.moves = 0; helicopter waittill ( "near_goal" ); helicopter waittill ( "goal" ); while( true ) { if( self SecondaryOffhandButtonPressed() ) { player.moves++; player thread SetPlayerMovedRecentlyThread(); node = self UpdateAreaNodes( destNodes, true ); helicopter.currentNode = node; targetNode = getEnt( node.target, "targetname" ); player playlocalsound ( "mpl_cgunner_nav" ); helicopter TravelToNode( targetNode ); if( isdefined( targetNode.script_airspeed ) && isdefined( targetNode.script_accel ) ) { heli_speed = targetNode.script_airspeed; heli_accel = targetNode.script_accel; } else { heli_speed = 80+randomInt(20); heli_accel = 40+randomInt(10); } helicopter SetSpeed( heli_speed, heli_accel ); helicopter setvehgoalpos( targetNode.origin + ( 0, 0, ( 2000 ) ), 1 ); helicopter setgoalyaw( targetNode.angles[ 1 ] + ( 0 ) ); helicopter waittill( "goal" ); // wait for the button to release: while ( self SecondaryOffhandButtonPressed() ) { {wait(.05);}; } } {wait(.05);}; } } function SetPlayerMovedRecentlyThread() { player = self; player endon( "disconnect" ); player endon( "gunner_left" ); helicopter = level.vtol; helicopter endon ( "delete" ); helicopter endon ( "vtol_shutdown" ); myMove = self.moves; level.vtol.playerMovedRecently = true; wait ( 100 ); //only remove the flag if I am still the most recent move if( myMove == self.moves && isdefined( level.vtol ) ) { level.vtol.playerMovedRecently = false; } } function UpdateAreaNodes( areaNodes, forceMove ) { validEnemies = []; foreach( node in areaNodes ) { node.validPlayers = []; node.nodeScore = 0; } foreach( player in level.players ) { if( !isAlive( player ) ) { continue; } if( player.team == self.team ) { continue; } foreach( node in areaNodes ) { if( distanceSquared( player.origin, node.origin ) > 1048576 ) { continue; } node.validPlayers[node.validPlayers.size] = player; } } bestNode = undefined; foreach ( node in areaNodes ) { if( isdefined( level.vtol.currentNode ) && ( node == level.vtol.currentNode ) ) { continue; } heliNode = getEnt( node.target, "targetname" ); foreach( player in node.validPlayers ) { node.nodeScore += 1; if( bulletTracePassed( player.origin + (0,0,32), heliNode.origin, false, player ) ) { node.nodeScore += 3; } } if( forceMove && ( distance( level.vtol.origin, heliNode.origin ) < 200 ) ) { node.nodeScore = -1; } if( !isdefined( bestNode ) || ( node.nodeScore > bestNode.nodeScore ) ) { bestNode = node; } } return bestNode; } function TravelToNode( goalNode ) { originOffets = GetOriginOffsets( goalNode ); if( originOffets["start"] != self.origin ) { if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) ) { heli_speed = goalNode.script_airspeed; heli_accel = goalNode.script_accel; } else { heli_speed = 30 + randomInt(20); heli_accel = 15 + randomInt(15); } self SetSpeed( heli_speed, heli_accel ); self setvehgoalpos( originOffets["start"] + (0,0,30), 0 ); self setgoalyaw( goalNode.angles[ 1 ] + ( 0 ) ); self waittill ( "goal" ); } if( originOffets["end"] != goalNode.origin ) { if( isdefined( goalNode.script_airspeed ) && isdefined( goalNode.script_accel ) ) { heli_speed = goalNode.script_airspeed; heli_accel = goalNode.script_accel; } else { heli_speed = 30+randomInt(20); heli_accel = 15+randomInt(15); } self SetSpeed( heli_speed, heli_accel ); self setvehgoalpos( originOffets["end"] + (0,0,30), 0 ); self setgoalyaw( goalNode.angles[ 1 ] + ( 0 ) ); self waittill ( "goal" ); } } function GetOriginOffsets( goalNode ) { startOrigin = self.origin; endOrigin = goalNode.origin; numTraces = 0; maxTraces = 40; traceOffset = (0,0,-196); traceOrigin = BulletTrace( startOrigin+traceOffset, endOrigin+traceOffset, false, self ); while( DistanceSquared( traceOrigin[ "position" ], endOrigin+traceOffset ) > 10 && numTraces < maxTraces ) { /#println( "trace failed: " + DistanceSquared( traceOrigin[ "position" ], endOrigin+traceOffset ) );#/ if( startOrigin[2] < endOrigin[2] ) { startOrigin += (0,0,128); } else if( startOrigin[2] > endOrigin[2] ) { endOrigin += (0,0,128); } else { startOrigin += (0,0,128); endOrigin += (0,0,128); } numTraces++; traceOrigin = BulletTrace( startOrigin+traceOffset, endOrigin+traceOffset, false, self ); } offsets = []; offsets["start"] = startOrigin; offsets["end"] = endOrigin; return offsets; }