iw6-scripts-dev/maps/mp/killstreaks/_helicopter_pilot.gsc
2024-12-11 11:28:08 +01:00

634 lines
16 KiB
Plaintext

#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include common_scripts\utility;
init()
{
level.killstreakFuncs[ "heli_pilot" ] = ::tryUseHeliPilot;
level.heli_pilot = [];
level.heliPilotSettings = [];
level.heliPilotSettings[ "heli_pilot" ] = SpawnStruct();
level.heliPilotSettings[ "heli_pilot" ].timeOut = 60.0;
level.heliPilotSettings[ "heli_pilot" ].maxHealth = 2000; // this is what we check against for death
level.heliPilotSettings[ "heli_pilot" ].streakName = "heli_pilot";
level.heliPilotSettings[ "heli_pilot" ].vehicleInfo = "heli_pilot_mp";
level.heliPilotSettings[ "heli_pilot" ].modelBase = level.littlebird_model;
level.heliPilotSettings[ "heli_pilot" ].teamSplash = "used_heli_pilot";
heliPilot_setAirStartNodes();
// throw the mesh way up into the air, the gdt entry for the vehicle must match
level.heli_pilot_mesh = GetEnt( "heli_pilot_mesh", "targetname" );
if( !IsDefined( level.heli_pilot_mesh ) )
PrintLn( "heli_pilot_mesh doesn't exist in this level: " + level.script );
else
level.heli_pilot_mesh.origin += getHeliPilotMeshOffset();
config = SpawnStruct();
config.xpPopup = "destroyed_helo_pilot";
// !!! NEED VO
config.voDestroyed = undefined;
config.callout = "callout_destroyed_helo_pilot";
config.samDamageScale = 0.09;
config.engineVFXtag = "tag_engine_right";
// xpval = 200
level.heliConfigs[ "heli_pilot" ] = config;
/#
SetDevDvarIfUninitialized( "scr_helipilot_timeout", 60.0 );
#/
}
tryUseHeliPilot( lifeId, streakName )
{
heliPilotType = "heli_pilot";
numIncomingVehicles = 1;
if ( IsDefined( self.underWater ) && self.underWater )
{
return false;
}
else if( exceededMaxHeliPilots( self.team ) )
{
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
return false;
}
else if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
{
self IPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
return false;
}
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
incrementFauxVehicleCount();
heli = createHeliPilot( heliPilotType );
if( !IsDefined( heli ) )
{
// decrement the faux vehicle count since this failed to spawn
decrementFauxVehicleCount();
return false;
}
level.heli_pilot[ self.team ] = heli;
result = self startHeliPilot( heli );
if( !IsDefined( result ) )
result = false;
return result;
}
exceededMaxHeliPilots( team )
{
if ( level.gameType == "dm" )
{
if ( IsDefined( level.heli_pilot[ team ] ) || IsDefined( level.heli_pilot[ level.otherTeam[ team ] ] ) )
return true;
else
return false;
}
else
{
if ( IsDefined( level.heli_pilot[ team ] ) )
return true;
else
return false;
}
}
//============================================
// watchHostMigrationFinishedInit
//============================================
watchHostMigrationFinishedInit( player )
{
player endon( "killstreak_disowned" );
player endon( "disconnect" );
level endon( "game_ended" );
self endon ( "death" );
for (;;)
{
level waittill( "host_migration_end" );
player SetClientOmnvar( "ui_heli_pilot", 1 );
}
}
createHeliPilot( heliPilotType )
{
closestStartNode = heliPilot_getClosestStartNode( self.origin );
closestNode = heliPilot_getLinkedStruct( closestStartNode );
startAng = VectorToAngles( closestNode.origin - closestStartNode.origin );
forward = AnglesToForward( self.angles );
targetPos = closestNode.origin + ( forward * -100 );
startPos = closestStartNode.origin;
heli = SpawnHelicopter( self, startPos, startAng, level.heliPilotSettings[ heliPilotType ].vehicleInfo, level.heliPilotSettings[ heliPilotType ].modelBase );
if( !IsDefined( heli ) )
return;
// radius and offset should match vehHelicopterBoundsRadius (GDT) and bg_vehicle_sphere_bounds_offset_z.
heli MakeVehicleSolidCapsule( 18, -9, 18 );
heli maps\mp\killstreaks\_helicopter::addToLittleBirdList();
heli thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath();
heli.maxHealth = level.heliPilotSettings[ heliPilotType ].maxHealth;
heli.speed = 40;
heli.owner = self;
heli SetOtherEnt(self);
heli.team = self.team;
heli.heliType = "littlebird";
heli.heliPilotType = "heli_pilot";
heli SetMaxPitchRoll( 45, 45 );
heli Vehicle_SetSpeed( heli.speed, 40, 40 );
heli SetYawSpeed( 120, 60 );
heli SetNearGoalNotifyDist( 32 );
heli SetHoverParams( 100, 100, 100 );
heli make_entity_sentient_mp( heli.team );
heli.targetPos = targetPos;
heli.currentNode = closestNode;
heli.attract_strength = 10000;
heli.attract_range = 150;
heli.attractor = Missile_CreateAttractorEnt( heli, heli.attract_strength, heli.attract_range );
// heli thread heliPilot_handleDamage(); // since the model is what players will be shooting at, it should handle the damage
heli thread maps\mp\killstreaks\_helicopter::heli_damage_monitor( "heli_pilot" );
heli thread heliPilot_lightFX();
heli thread heliPilot_watchTimeout();
heli thread heliPilot_watchOwnerLoss();
heli thread heliPilot_watchRoundEnd();
heli thread heliPilot_watchObjectiveCam();
heli thread heliPilot_watchDeath();
heli thread watchHostMigrationFinishedInit( self );
heli.owner maps\mp\_matchdata::logKillstreakEvent( level.heliPilotSettings[ heli.heliPilotType ].streakName, heli.targetPos );
return heli;
}
heliPilot_lightFX()
{
PlayFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_nose" );
wait ( 0.05 );
PlayFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
wait ( 0.05 );
PlayFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail1" );
wait ( 0.05 );
PlayFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail2" );
}
startHeliPilot( heli ) // self == player
{
level endon( "game_ended" );
heli endon( "death" );
self setUsingRemote( heli.heliPilotType );
if( GetDvarInt( "camera_thirdPerson" ) )
self setThirdPersonDOF( false );
self.restoreAngles = self.angles;
heli thread maps\mp\killstreaks\_flares::ks_setup_manual_flares( 2, "+smoke", "ui_heli_pilot_flare_ammo", "ui_heli_pilot_warn" );
self thread watchIntroCleared( heli );
self freezeControlsWrapper( true );
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( heli.heliPilotType );
if( result != "success" )
{
if( IsDefined( self.disabledWeapon ) && self.disabledWeapon )
self _enableWeapon();
heli notify( "death" );
return false;
}
self freezeControlsWrapper( false );
/*
// need to link the player here but not give control yet
self CameraLinkTo( heli, "tag_player" );
// go to pos
heli SetVehGoalPos( heli.targetPos );
heli waittill( "near_goal" );
heli Vehicle_SetSpeed( heli.speed, 60, 30 );
heli waittill( "goal" );
*/
// make sure we go to the mesh as we enter the map
traceOffset = getHeliPilotTraceOffset();
traceStart = ( heli.currentNode.origin ) + ( getHeliPilotMeshOffset() + traceOffset );
traceEnd = ( heli.currentNode.origin ) + ( getHeliPilotMeshOffset() - traceOffset );
traceResult = BulletTrace( traceStart, traceEnd, false, undefined, false, false, true );
if( !IsDefined( traceResult["entity"] ) )
{
/#
// draw where it thinks this is breaking down
self thread drawSphere( traceResult[ "position" ] - getHeliPilotMeshOffset(), 32, 10000, ( 1, 0, 0 ) );
self thread drawSphere( heli.currentNode.origin, 16, 10000, ( 0, 1, 0 ) );
self thread drawLine( traceStart - getHeliPilotMeshOffset(), traceEnd - getHeliPilotMeshOffset(), 10000, ( 0, 0, 1 ) );
#/
AssertMsg( "The trace didn't hit the heli_pilot_mesh. Please grab an MP scripter." );
}
targetOrigin = ( traceResult[ "position" ] - getHeliPilotMeshOffset() ) + ( 0, 0, 250 ); // offset to make sure we're on top of the mesh
targetNode = Spawn( "script_origin", targetOrigin );
// link the heli into the mesh and give them control
self RemoteControlVehicle( heli );
heli thread heliGoToStartPosition( targetNode );
heli thread heliPilot_watchADS();
level thread teamPlayerCardSplash( level.heliPilotSettings[ heli.heliPilotType ].teamSplash, self );
heli.killCamEnt = Spawn( "script_origin", self GetViewOrigin() );
return true;
}
heliGoToStartPosition( targetNode ) // self == heli
{
self endon( "death" );
level endon( "game_ended" );
self RemoteControlVehicleTarget( targetNode );
self waittill( "goal_reached" );
self RemoteControlVehicleTargetOff();
targetNode delete();
}
watchIntroCleared( heli ) // self == player
{
self endon( "disconnect" );
self endon( "joined_team" );
self endon( "joined_spectators" );
level endon( "game_ended" );
heli endon( "death" );
self waittill( "intro_cleared" );
self SetClientOmnvar( "ui_heli_pilot", 1 );
// highlight the owner
id = outlineEnableForPlayer( self, "cyan", self, false, "killstreak" );
self removeOutline( id, heli );
// highlight enemies
foreach( player in level.participants )
{
if( !isReallyAlive( player ) || player.sessionstate != "playing" )
continue;
if( self isEnemy( player ) )
{
if( !player _hasPerk( "specialty_noplayertarget" ) )
{
id = outlineEnableForPlayer( player, "orange", self, false, "killstreak" );
player removeOutline( id, heli );
}
else
{
player thread watchForPerkRemoval( heli );
}
}
}
// watch for enemies spawning while the pilot is up
heli thread watchPlayersSpawning();
// do this here to make sure we are in the killstreak before letting them leave, it was causing a bug where the player could get stuck on a black screen
self thread watchEarlyExit( heli );
}
watchForPerkRemoval( heli ) // self == enemy player
{
self notify( "watchForPerkRemoval" );
self endon( "watchForPerkRemoval" );
self endon( "death" );
// since we give blindeye and noplayetarget each time a player spawns, we need to wait for it to be removed to turn the outline on
self waittill( "removed_specialty_noplayertarget" );
id = outlineEnableForPlayer( self, "orange", heli.owner, false, "killstreak" );
self removeOutline( id, heli );
}
watchPlayersSpawning() // self == heli
{
self endon( "leaving" );
self endon( "death" );
while( true )
{
level waittill( "player_spawned", player );
if( player.sessionstate == "playing" && self.owner isEnemy( player ) )
player thread watchForPerkRemoval( self );
}
}
removeOutline( id, heli ) // self == player
{
self thread heliRemoveOutline( id, heli );
self thread playerRemoveOutline( id, heli );
}
heliRemoveOutline( id, heli ) // self == player
{
self notify( "heliRemoveOutline" );
self endon( "heliRemoveOutline" );
self endon( "outline_removed" );
self endon( "disconnect" );
level endon( "game_ended" );
wait_array = [ "leaving", "death" ];
heli waittill_any_in_array_return_no_endon_death( wait_array );
if( IsDefined( self ) )
{
outlineDisable( id, self );
self notify( "outline_removed" );
}
}
playerRemoveOutline( id, heli ) // self == player
{
self notify( "playerRemoveOutline" );
self endon( "playerRemoveOutline" );
self endon( "outline_removed" );
self endon( "disconnect" );
level endon( "game_ended" );
wait_array = [ "death" ];
self waittill_any_in_array_return_no_endon_death( wait_array );
outlineDisable( id, self );
self notify( "outline_removed" );
}
//
// state trackers
//
heliPilot_watchDeath()
{
level endon( "game_ended" );
self endon( "gone" );
self waittill( "death" );
if( IsDefined( self.owner ) )
self.owner heliPilot_EndRide( self );
if( IsDefined( self.killCamEnt ) )
self.killCamEnt delete();
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
}
heliPilot_watchObjectiveCam()
{
level endon( "game_ended" );
self endon( "gone" );
self.owner endon( "disconnect" );
self.owner endon( "joined_team" );
self.owner endon( "joined_spectators" );
level waittill( "objective_cam" );
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
if( IsDefined( self.owner ) )
self.owner heliPilot_EndRide( self );
}
heliPilot_watchTimeout()
{
level endon ( "game_ended" );
self endon( "death" );
self.owner endon( "disconnect" );
self.owner endon( "joined_team" );
self.owner endon( "joined_spectators" );
timeout = level.heliPilotSettings[ self.heliPilotType ].timeOut;
/#
timeout = GetDvarFloat( "scr_helipilot_timeout" );
#/
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeout );
self thread heliPilot_leave();
}
heliPilot_watchOwnerLoss()
{
level endon ( "game_ended" );
self endon( "death" );
self endon( "leaving" );
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
// leave
self thread heliPilot_leave();
}
heliPilot_watchRoundEnd()
{
self endon( "death" );
self endon( "leaving" );
self.owner endon( "disconnect" );
self.owner endon( "joined_team" );
self.owner endon( "joined_spectators" );
level waittill_any( "round_end_finished", "game_ended" );
// leave
self thread heliPilot_leave();
}
heliPilot_leave()
{
self endon( "death" );
self notify( "leaving" );
if( IsDefined( self.owner ) )
self.owner heliPilot_EndRide( self );
// rise
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( self.origin );
targetPos = self.origin + ( 0, 0, flyHeight );
self Vehicle_SetSpeed( 140, 60 );
self SetMaxPitchRoll( 45, 180 );
self SetVehGoalPos( targetPos );
self waittill( "goal" );
// leave
targetPos = targetPos + AnglesToForward( self.angles ) * 15000;
// make sure it doesn't fly away backwards
endEnt = Spawn( "script_origin", targetPos );
if( IsDefined( endEnt ) )
{
self SetLookAtEnt( endEnt );
endEnt thread wait_and_delete( 3.0 );
}
self SetVehGoalPos( targetPos );
self waittill( "goal" );
// remove
self notify( "gone" );
self maps\mp\killstreaks\_helicopter::removeLittlebird();
}
wait_and_delete( waitTime )
{
self endon( "death" );
level endon( "game_ended" );
wait( waitTime );
self delete();
}
heliPilot_EndRide( heli )
{
if( IsDefined( heli ) )
{
self SetClientOmnvar( "ui_heli_pilot", 0 );
heli notify( "end_remote" );
if( self isUsingRemote() )
self clearUsingRemote();
if( GetDvarInt( "camera_thirdPerson" ) )
self setThirdPersonDOF( true );
self RemoteControlVehicleOff( heli );
self SetPlayerAngles( self.restoreAngles );
self thread heliPilot_FreezeBuffer();
}
}
heliPilot_FreezeBuffer()
{
self endon( "disconnect" );
self endon( "death" );
level endon( "game_ended" );
self freezeControlsWrapper( true );
wait( 0.5 );
self freezeControlsWrapper( false );
}
heliPilot_watchADS() // self == heli
{
self endon( "leaving" );
self endon( "death" );
level endon( "game_ended" );
already_set = false;
while( true )
{
if( IsDefined( self.owner ) )
{
if( self.owner AdsButtonPressed() )
{
if( !already_set )
{
self.owner SetClientOmnvar( "ui_heli_pilot", 2 );
already_set = true;
}
}
else
{
if( already_set )
{
self.owner SetClientOmnvar( "ui_heli_pilot", 1 );
already_set = false;
}
}
}
wait( 0.1 );
}
}
//
// node funcs
//
heliPilot_setAirStartNodes()
{
level.air_start_nodes = getstructarray( "chopper_boss_path_start", "targetname" );
}
heliPilot_getLinkedStruct( struct )
{
if( IsDefined( struct.script_linkTo ) )
{
linknames = struct get_links();
for( i = 0; i < linknames.size; i++ )
{
ent = getstruct( linknames[ i ], "script_linkname" );
if( IsDefined( ent ) )
{
return ent;
}
}
}
return undefined;
}
heliPilot_getClosestStartNode( pos )
{
// gets the start node that is closest to the position passed in
closestNode = undefined;
closestDistance = 999999;
foreach( loc in level.air_start_nodes )
{
nodeDistance = Distance( loc.origin, pos );
if ( nodeDistance < closestDistance )
{
closestNode = loc;
closestDistance = nodeDistance;
}
}
return closestNode;
}
watchEarlyExit( heli ) // self == player
{
level endon( "game_ended" );
heli endon( "death" );
self endon ("leaving");
heli thread maps\mp\killstreaks\_killstreaks::allowRideKillstreakPlayerExit();
heli waittill("killstreakExit");
heli thread heliPilot_leave();
}