boiii-scripts/mp/killstreaks/_helicopter_gunner.gsc
2023-04-13 17:30:38 +02:00

1498 lines
52 KiB
Plaintext

#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;
}