s1-scripts-dev/raw/maps/mp/killstreaks/_aerial_utility.gsc
2025-05-21 16:23:17 +02:00

1567 lines
40 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
EMP_GRENADE_TIME = 3.5;
CONST_default_height_z = 2032;
CONST_streak_static_disabled = 0;
CONST_streak_static_enabled = 1;
CONST_streak_static_damage = 2;
CONST_streak_static_range1 = 3;
CONST_streak_static_range2 = 4;
CONST_streak_static_range3 = 5;
CONST_streak_static_range4 = 6;
CONST_streak_static_fullnotext = 7;
CONST_VEHICLE_SPAWNFLAG = 16;
//===========================================
// init
//===========================================
init()
{
if(GetDvarInt("virtuallobbyactive", 0))
return;
/#
if ( GetDvar( "scr_scorestreak_skip_aerial", "0" ) != "0" )
return;
#/
level.helis = [];
level.littleBirds = [];
level.heli_leave_nodes = getEntOrStructArray( "heli_leave", "targetname" );
level.heli_crash_nodes = getEntOrStructArray( "heli_crash_start", "targetname" );
assertEx( level.heli_leave_nodes.size, "No \"heli_leave\" nodes found in map!" );
assertEx( level.heli_crash_nodes.size, "No \"heli_crash_start\" nodes found in map!" );
level.chopper_fx["explode"]["death"] = [];
level.chopper_fx["explode"]["air_death"] = [];
level.chopper_fx["damage"]["light_smoke"] = loadfx ("vfx/trail/smoke_trail_white_heli_emitter");
level.chopper_fx["damage"]["heavy_smoke"] = loadfx ("vfx/trail/smoke_trail_black_heli_emitter");
level.chopper_fx["damage"]["on_fire"] = loadfx ("vfx/fire/helicopter_damaged_fire_m");
level.chopper_fx["explode"]["large"] = loadfx ("fx/explosions/helicopter_explosion_secondary_small");
// level.chopper_fx["light"]["left"] = loadfx( "vfx/lights/aircraft_light_wingtip_green" );
// level.chopper_fx["light"]["right"] = loadfx( "vfx/lights/aircraft_light_wingtip_red" );
// level.chopper_fx["light"]["belly"] = loadfx( "vfx/lights/aircraft_light_red_blink" );
// level.chopper_fx["light"]["tail"] = loadfx( "vfx/lights/aircraft_light_white_blink" );
level.chopper_fx["rocketlaunch"]["warbird"] = LoadFX( "vfx/muzzleflash/rocket_launch_air_to_ground" );
level.heli_sound["allies"]["hit"] = "warbird_death_explo";
level.heli_sound["axis"]["hit"] = "warbird_death_explo";
// level.heli_sound["allies"]["hitsecondary"] = "cobra_helicopter_secondary_exp";
// level.heli_sound["axis"]["hitsecondary"] = "cobra_helicopter_secondary_exp";
level.heli_sound["allies"]["spinloop"] = "warbird_death_spin_loop";
level.heli_sound["axis"]["spinloop"] = "warbird_death_spin_loop";
// level.heli_sound["allies"]["spinstart"] = "cobra_helicopter_dying_layer";
// level.heli_sound["axis"]["spinstart"] = "cobra_helicopter_dying_layer";
level.heli_sound["allies"]["crash"] = "warbird_air_death";
level.heli_sound["axis"]["crash"] = "warbird_air_death";
level._effect[ "flare" ] = loadfx( "vfx/lensflare/flares_warbird" );
level.heli_attract_strength = 1000;
level.heli_attract_range = 4096;
level.heli_maxhealth = 2000;
level.heli_targeting_delay = 0.5;
}
//===========================================
// makeHeliType
//===========================================
makeHeliType( heliType, deathFx, lightFXFunc )
{
level.chopper_fx["explode"]["death"][ heliType ] = loadFx( deathFX );
level.lightFxFunc[ heliType ] = lightFXFunc;
}
//===========================================
// addAirExplosion
//===========================================
addAirExplosion( heliType, explodeFx )
{
level.chopper_fx["explode"]["air_death"][ heliType ] = loadFx( explodeFx );
}
//===========================================
// addToHeliList
//===========================================
addToHeliList()
{
level.helis[self getEntityNumber()] = self;
}
//===========================================
// removeFromHeliList
//===========================================
removeFromHeliList( entityNumber )
{
level.helis[entityNumber] = undefined;
}
//===========================================
// addToLittleBirdList
//===========================================
addToLittleBirdList( lbType )
{
level.littleBirds[self GetEntityNumber()] = self;
}
//===========================================
// removeFromLittleBirdListOnDeath
//===========================================
removeFromLittleBirdListOnDeath( lbType )
{
entNum = self GetEntityNumber();
self waittill ( "death" );
level.littleBirds[entNum] = undefined;
}
//===========================================
// exceededMaxLittlebirds
//===========================================
exceededMaxLittlebirds( streakName )
{
if( level.littleBirds.size >= 4 )
{
return true;
}
else
{
return false;
}
}
//===========================================
// heli_leave_on_disconnect
//===========================================
heli_leave_on_disconnect( owner )
{
self endon ( "death" );
self endon ( "helicopter_done" );
owner waittill( "disconnect" );
self thread heli_leave();
}
//===========================================
// heli_leave_on_changeTeams
//===========================================
heli_leave_on_changeTeams( owner )
{
self endon ( "death" );
self endon ( "helicopter_done" );
owner waittill_any( "joined_team", "joined_spectators" );
self thread heli_leave();
}
//===========================================
// heli_ModifyDamage
//===========================================
heli_ModifyDamage( attacker, weapon, type, damage )
{
modifiedDamage = self maps\mp\gametypes\_damage::modifyDamage( attacker, weapon, type, damage );
if( modifiedDamage > 0 )
self heli_staticDamage( weapon, type, modifiedDamage );
// self thread heli_addRecentDamage( modifiedDamage );
return modifiedDamage;
}
//===========================================
// heli_addRecentDamage
//===========================================
heli_addRecentDamage( damage )
{
self endon( "death" );
self.recentDamageAmount += damage;
wait ( 4.0 );
self.recentDamageAmount -= damage;
}
//===========================================
// heli_leave_on_timeout
//===========================================
heli_leave_on_timeout( timeOut )
{
self endon ( "death" );
self endon ( "helicopter_done" );
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeOut );
self thread heli_leave();
}
//===========================================
// heli_leave_on_gameended
//===========================================
heli_leave_on_gameended( owner )
{
self endon ( "death" );
self endon ( "helicopter_done" );
level waittill ( "game_ended" );
self thread heli_leave();
}
//===========================================
// heli_leave_on_timeout
//===========================================
heli_leave( leavePos )
{
self notify( "leaving" );
self.isLeaving = true;
self ClearLookAtEnt();
leaveNode = undefined;
if( !isDefined( leavePos ) )
{
leaveNode = heli_pick_fly_node( level.heli_leave_nodes );
leavePos = leaveNode.origin;
}
// make sure it doesn't fly away backwards
endEnt = Spawn( "script_origin", leavePos );
if( IsDefined( endEnt ) )
{
self SetLookAtEnt( endEnt );
endEnt thread wait_and_delete( 3.0 );
}
self heli_reset();
self Vehicle_SetSpeed( 100, 45 );
if ( IsDefined( leaveNode ) )
{
if ( IsDefined( leaveNode.target ) )
{
self heli_fly_simple_path( leaveNode );
}
else
{
self _setVehGoalPos( leaveNode.origin, false );
self waittillmatch( "goal" );
}
}
else
{
self _setVehGoalPos( leavePos, false );
self waittillmatch( "goal" );
}
self notify( "death" );
// give "death" notify time to process
wait ( 0.05 );
if( IsDefined( self.killCamEnt ) )
self.killCamEnt delete();
// decrement the faux vehicle count right before it is deleted this way we know for sure it is gone
decrementFauxVehicleCount();
self delete();
}
//===========================================
// heli_pick_fly_node
//===========================================
heli_pick_fly_node( nodes )
{
// nodes = array_randomize( nodes );
start = self.origin;
nextNode = undefined;
/#
if ( GetDvar( "scr_heli_pick_fly_node_debug", "0" ) != "0" )
{
foreach ( node in nodes )
Sphere( node.origin, 100, ( 0, 1, 0 ), false, 300 );
}
#/
for ( i = 0; i < nodes.size; i++ )
{
end = nodes[i].origin;
if ( flyNodeOrgTracePassed( start, end, self ) )
{
dir = end - start;
dist = Distance( start, end );
dirRight = RotateVector( dir, ( 0, 90, 0 ) );
startWing = start + ( dirRight * 100 );
endWing = startWing + ( dir * dist );
if ( flyNodeOrgTracePassed( startWing, endWing, self ) )
{
dirLeft = RotateVector( dir , ( 0, -90, 0 ) );
startWing = start + ( dirLeft * 100 );
endWing = startWing + ( dir * dist );
if ( flyNodeOrgTracePassed( startWing, endWing, self ) )
{
return nodes[i];
}
}
}
}
return nodes[ RandomInt( nodes.size ) ];
}
flyNodeOrgTracePassed( start, end, ignoreEnt )
{
trace = BulletTrace( start, end, false, ignoreEnt, false, false, true, false, false );
passed = ( trace["fraction"] >= 1 );
/#
if ( GetDvar( "scr_heli_pick_fly_node_debug", "0" ) != "0" )
{
if ( passed )
Line( start, end, ( 0, 1, 0 ), 1, false, 300 );
else
Line( start, end, ( 1, 0, 0 ), 1, false, 300 );
}
return passed;
#/
}
//===========================================
// wait_and_delete
//===========================================
wait_and_delete( waitTime )
{
self endon( "death" );
level endon( "game_ended" );
wait( waitTime );
self delete();
}
//===========================================
// deleteAfterTime
//===========================================
deleteAfterTime( delay )
{
wait ( delay );
self delete();
}
//===========================================
// heli_reset
//===========================================
heli_reset()
{
// resets helicopter's motion values
self clearTargetYaw();
self clearGoalYaw();
self Vehicle_SetSpeed( 60, 25 );
self setyawspeed( 100, 45, 45 );
self setmaxpitchroll( 30, 30 );
self setneargoalnotifydist( 100 );
self setturningability(1.0);
}
//===========================================
// _setVehGoalPos
//===========================================
_setVehGoalPos( goalPosition, shouldStop )
{
if( !isDefined( shouldStop ) )
shouldStop = false;
self SetVehGoalPos( goalPosition, shouldStop );
}
//===========================================
// heli_flares_monitor
//===========================================
heli_flares_monitor( extraFlares )
{
switch( self.heliType )
{
default:
self.numFlares = 1;
break;
}
if( IsDefined( extraFlares ) )
self.numFlares += extraFlares;
self thread handleIncomingStinger();
}
//===========================================
// handleIncomingStinger
//===========================================
handleIncomingStinger( functionOverride )
{
level endon ( "game_ended" );
self endon ( "death" );
self endon ( "crashing" );
self endon ( "leaving" );
self endon ( "helicopter_done" );
for( ;; )
{
level waittill ( "stinger_fired", player, missiles );
if( !maps\mp\_stingerm7::anyStingerMissileLockedOn( missiles, self ) )
continue;
if( !IsDefined( missiles ) )
continue;
if( IsDefined( functionOverride ) )
{
level thread [[ functionOverride ]]( missiles, player, player.team );
}
else
{
level thread watchMissileProximity( missiles, player, player.team );
}
}
}
//===========================================
// watchMissileProximity
//===========================================
watchMissileProximity( missiles, player, missileTeam ) // self == level
{
foreach( missile in missiles )
missile thread missileWatchProximity( player, missileTeam, missile.lockedStingerTarget );
}
//===========================================
// missileWatchProximity
//===========================================
missileWatchProximity( player, missileTeam, missileTarget )
{
self endon( "death" );
missileTarget endon( "death" );
flaresTime = 5.0;
flaresDistance = 4000;
while( true )
{
if( !isDefined( missileTarget ) )
break;
center = missileTarget GetPointInBounds( 0, 0, 0 );
curDist = distance( self.origin, center );
if( IsDefined( missileTarget.player ) )
missileTarget.player thread doProximityAlarm( self, missileTarget );
if( curDist < flaresDistance )
{
if( missileTarget.numFlares > 0 || IsDefined( missileTarget.flaresTarget ) )
{
if ( IsDefined( missileTarget.owner ) && IsWarbird( missileTarget ) )
{
if ( missileTarget.numFlares == 2 )
missileTarget.owner SetClientOmnvar( "ui_warbird_flares", 1 );
else if ( missileTarget.numFlares == 1 )
missileTarget.owner SetClientOmnvar( "ui_warbird_flares", 2 );
missileTarget.owner PlayLocalSound( "paladin_deploy_flares" );
}
newTarget = missileTarget deployFlares( flaresTime );
PlayFXOnTag( getfx( "flare" ), newTarget, "tag_origin" );
if( !IsDefined( missileTarget.flaresTarget ) )
{
missileTarget.numFlares--;
level thread handleFlaresTimer( missileTarget, newTarget, flaresTime );
}
self Missile_SetTargetEnt( newTarget );
return;
}
}
wait ( 0.05 );
}
}
//===========================================
// deployFlares
//===========================================
deployFlares( time ) // self == heli/plane
{
org = (self GetTagOrigin("tag_origin")) + ( 0, 0, -50 );
flareObject = Spawn( "script_model", org );
flareObject SetModel( "tag_origin" );
flareObject.angles = self.angles;
if ( !IsDefined( self.flaresDeployedYaw ) )
self.flaresDeployedYaw = RandomFloatRange( -180, 180 );
else
self.flaresDeployedYaw = self.flaresDeployedYaw + 90;
vel = AnglesToForward( ( self.angles[0], self.flaresDeployedYaw, self.angles[2] ) );
vel = vehicleModifyFlareVector( vel );
flareObject MoveGravity( vel, time );
flareObject thread deleteAfterTime( time );
return flareObject;
}
vehicleModifyFlareVector( velocityVec ) // self == heli/plane
{
if ( self.vehicleType == "warbird" )
{
return ( VectorNormalize( velocityVec + ( 0, 0, -0.2 ) ) * 300 );
}
else if ( self.vehicleType == "paladin" )
{
return ( VectorNormalize( velocityVec + ( 0, 0, -0.5 ) ) * 2000 );
}
else // strafing run
{
return ( VectorNormalize( velocityVec + ( 0, 0, -0.4 ) ) * 1000 );
}
}
//===========================================
// handleFlaresTimer
//===========================================
handleFlaresTimer( missileTarget, flaresTarget, flaresTime )
{
missileTarget endon( "death" );
missileTarget.flaresTarget = flaresTarget;
wait flaresTime;
missileTarget.flaresTarget = undefined;
if ( IsDefined( missileTarget.owner ) && IsWarbird( missileTarget ) )
missileTarget.owner SetClientOmnvar( "ui_warbird_flares", 0 );
}
//===========================================
// HasTag
//===========================================
HasTag( model, tag )
{
partCount = GetNumParts( model );
for ( i = 0; i < partCount; i++ )
{
if( toLower( GetPartName( model, i)) == toLower( tag ))
return true;
}
return false;
}
//===========================================
// IsWarbird
//===========================================
IsWarbird( heli )
{
return IsDefined( heli.heli_type ) && ( heli.heli_type == "warbird" );
}
//===========================================
// doProximityAlarm
//===========================================
doProximityAlarm( missile, heli )
{
self endon( "disconnect" );
if ( shouldStopProximityAlarm( missile, heli ) || IsDefined( heli.incomingMissileSound ) )
return;
if ( IsWarbird( heli ) )
self SetClientOmnvar( "ui_warbird_flares", 3 );
self PlayLocalSound( "mp_aerial_enemy_locked" );
heli.incomingMissileSound = true;
while( true )
{
if( shouldStopProximityAlarm( missile, heli ) )
{
self StopLocalSound( "mp_aerial_enemy_locked" );
heli.incomingMissileSound = undefined;
return;
}
waitframe();
}
}
//===========================================
// playerFakeShootPaintMissile
//===========================================
playerFakeShootPaintMissile( soundEnt ) // self == player
{
dir = VectorNormalize( AnglesToForward( self GetPlayerAngles() ) );
right = VectorNormalize( AnglesToRight( self GetPlayerAngles() ) );
start = self GetEye() + ( dir * 100 );
end = start + ( dir * 20000 );
trace = BulletTrace( start, end, false );
if ( trace[ "fraction" ] == 1 )
return;
Earthquake( 0.1, 1, self GetEye(), 500 );
start = self GetEye() + right * -1 * 50;
end = trace["position"];
rocket = MagicBullet( "paint_missile_killstreak_mp", start, end, self );
rocket.owner = self;
rocket thread watchPaintGrenade();
self thread playerFireSounds( soundEnt, "paladin_threat_bomb_shot_2d", "paladin_threat_bomb_shot_3d" );
}
playerFakeShootPaintGrenadeAtTarget( soundEnt, startPos, targetPos, stunPlayers, vehicle ) // self == player
{
GRENADE_SPEED = 5000;
Earthquake( 0.2, 1, self GetViewOrigin(), 300 );
grenadeForward = VectorNormalize( targetPos - startPos );
grenadeVelocity = grenadeForward * GRENADE_SPEED;
grenade = MagicGrenadeManual( "paint_grenade_killstreak_mp", startPos, grenadeVelocity, 2, self );
grenade.owner = self;
grenade thread watchPaintGrenade( stunPlayers, vehicle );
self thread playerFireSounds( soundEnt, "recon_drn_launcher_shot_plr", "recon_drn_launcher_shot_npc" );
self playRumbleOnEntity( "damage_heavy" );
}
playerFakeShootEmpGrenadeAtTarget( soundEnt, startPos, targetPos )
{
GRENADE_SPEED = 5000;
Earthquake( 0.2, 1, self GetViewOrigin(), 300 );
grenadeForward = VectorNormalize( targetPos - startPos );
grenadeVelocity = grenadeForward * GRENADE_SPEED;
grenade = MagicGrenadeManual( "emp_grenade_killstreak_mp", startPos, grenadeVelocity, 2, self );
grenade.owner = self;
self thread playerFireSounds( soundEnt, "recon_drn_launcher_shot_plr", "recon_drn_launcher_shot_npc" );
self playRumbleOnEntity( "damage_heavy" );
}
playerFireSounds( soundEnt, sound2d, sound3d )
{
if ( IsDefined( sound3d ) )
soundEnt PlaySoundOnMovingEnt( sound3d );
/#
if ( GetDvar( "scr_paladin_stay_on_ground", "0" ) != "0" )
return;
#/
if ( IsDefined( sound2d ) )
self PlayLocalSound( sound2d );
}
watchPaintGrenade( stunPlayers, vehicle )
{
if ( !IsDefined( stunPlayers ) )
stunPlayers = false;
owner = self.owner;
owner endon( "disconnect" );
owner endon( "death" );
self waittill( "explode", position );
if ( owner isEMPed() && IsDefined( level.empEquipmentDisabled ) && level.empEquipmentDisabled )
return;
detectionGrenadeThink( position, owner, stunPlayers, vehicle );
}
detectionGrenadeThink( position, owner, stunPlayers, vehicle )
{
if ( !IsDefined( stunPlayers ) )
stunPlayers = false;
assert( isDefined( owner ) );
foreach ( guy in level.players )
{
if ( !IsDefined( guy ) || !isReallyAlive( guy ) || !IsAlliedSentient( owner, guy ) )
{
continue;
}
thread maps\mp\_threatdetection::detection_grenade_hud_effect( guy, position, 1.0, 400 );
thread maps\mp\_threatdetection::detection_highlight_hud_effect( guy, 5 );
}
teamPlayers = getPlayersOnTeam( owner.team );
foreach( guy in level.players )
{
if ( !IsDefined( guy ) || !isReallyAlive( guy ) || IsAlliedSentient( owner, guy ) || guy _hasPerk( "specialty_coldblooded" ) )
{
continue;
}
if( Distance( guy.origin, position ) < 400 )
{
guy maps\mp\_threatdetection::addThreatEvent( teamPlayers, 5, "PAINT_GRENADE", true, false );
owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "paint" );
guy thread detectionGrenadeWatch( owner, 5 );
guy notify( "paint_marked_target", owner );
if ( stunPlayers )
maps\mp\gametypes\_weapons::flashbangPlayer( guy, position, owner );
if( isdefined( vehicle ) && vehicle.VehName == "recon_uav" )
owner maps\mp\gametypes\_missions::processChallenge( "ch_streak_recon");
}
}
}
detectionGrenadeWatch( owner, time ) // self == victim
{
level endon( "game_ended" );
self notify( "detectionGrenadeWatch" );
self endon( "detectionGrenadeWatch" );
// Award XP on tag, unless already tagged by this player
if ( !IsDefined( self.tagMarkedBy ) || self.tagMarkedBy != owner )
{
owner thread maps\mp\_events::killStreakTagEvent();
owner playRumbleOnEntity( "damage_heavy" );
}
// Mark the target
self DesignateFoFTarget( true );
self.tagMarkedBy = owner;
self waittill_any_timeout( time, "death", "disconnect" );
// Unmark the target
if ( IsDefined( self ) )
{
self DesignateFoFTarget( false );
self.tagMarkedBy = undefined;
}
}
//===========================================
// getPlayersOnTeam
//===========================================
getPlayersOnTeam( team ) // optional: self == player
{
teammates = [];
foreach( player in level.players )
{
if( player.hasSpawned && isAlive( player ) && team == player.team && ( !IsPlayer( self ) || player != self ) )
{
teammates[ teammates.size ] = player;
}
}
return teammates;
}
//===========================================
// shouldStopProximityAlarm
//===========================================
shouldStopProximityAlarm( missile, heli )
{
return ( !IsDefined( heli ) || !IsDefined( heli.player ) || !IsDefined( missile ) || IsDefined( heli.flaresTarget ) || !isReallyAlive( self ) || IsDefined( heli.crashed ) || IsDefined( heli.isCrashing ) );
}
//===========================================
// heli_staticDamage
//===========================================
heli_staticDamage( weapon, damageType, modifiedDamage )
{
if( modifiedDamage > 0 && IsDefined( self.owner ) )
{
self.owner thread maps\mp\killstreaks\_aerial_utility::playerShowStreakStaticForDamage();
}
if( modifiedDamage > 0 && IsDefined( self.warbirdBuddyTurret ) && IsDefined( self.warbirdBuddyTurret.owner ) )
{
self.warbirdBuddyTurret.owner thread maps\mp\killstreaks\_aerial_utility::playerShowStreakStaticForDamage();
}
}
//===========================================
// heli_monitorEMP
//===========================================
heli_monitorEMP()
{
level endon( "game_ended" );
self endon( "death" );
self endon( "crashing" );
self endon( "leaving" );
while( true )
{
self waittill( "emp_damage" );
self thread heli_EMPGrenaded();
}
}
//===========================================
// heli_EMPGrenaded
//===========================================
heli_EMPGrenaded()
{
self notify( "heli_EMPGrenaded" );
self endon( "heli_EMPGrenaded" );
self endon( "death" );
self endon( "leaving" );
self endon( "crashing" );
self.owner endon( "disconnect" );
level endon( "game_ended" );
self.empGrenaded = true;
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft notify( "stop_shooting" );
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight notify( "stop_shooting" );
wait( EMP_GRENADE_TIME );
self.empGrenaded = false;
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft notify( "turretstatechange" );
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight notify( "turretstatechange" );
}
//===========================================
// heli_existance
//===========================================
heli_existance()
{
entityNumber = self getEntityNumber();
self waittill_any( "death", "crashing", "leaving" );
self removeFromHeliList( entityNumber );
self notify( "helicopter_done" );
}
//===========================================
// heli_crash
//===========================================
heli_crash()
{
self notify( "crashing" );
self ClearLookAtEnt();
self.isCrashing = true;
crashNode = heli_pick_fly_node( level.heli_crash_nodes );
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft notify( "stop_shooting" );
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight notify( "stop_shooting" );
self thread heli_spin( 180 );
self thread heli_secondary_explosions();
self Vehicle_SetSpeed( 100, 45 );
if ( IsDefined( crashNode.target ) )
{
self heli_fly_simple_path( crashNode );
}
else
{
self _setVehGoalPos( crashNode.origin, false );
self waittillmatch( "goal" );
}
self thread heli_explode();
}
//===========================================
// heli_secondary_explosions
//===========================================
heli_secondary_explosions()
{
teamname = self.team;
playFxOnTag( level.chopper_fx["explode"]["large"], self, "tag_engine_left" );
if ( IsDefined( level.heli_sound[teamname]["hitsecondary"] ) )
self playSound ( level.heli_sound[teamname]["hitsecondary"] );
wait ( 3.0 );
if ( !isDefined( self ) )
return;
playFxOnTag( level.chopper_fx["explode"]["large"], self, "tag_engine_left" );
if ( IsDefined( level.heli_sound[teamname]["hitsecondary"] ) )
self playSound ( level.heli_sound[teamname]["hitsecondary"] );
}
//===========================================
// heli_spin
//===========================================
heli_spin( speed )
{
self endon( "death" );
teamname = self.team;
// play hit sound immediately so players know they got it
self playSound ( level.heli_sound[teamname]["hit"] );
// play heli crashing spinning sound
self thread spinSoundShortly();
// spins until death
self setyawspeed( speed, speed, speed );
while ( isdefined( self ) )
{
self settargetyaw( self.angles[1]+(speed*0.9) );
wait ( 1 );
}
}
//===========================================
// spinSoundShortly
//===========================================
spinSoundShortly()
{
self endon("death");
wait .25;
teamname = self.team;
self stopLoopSound();
wait .05;
self playLoopSound( level.heli_sound[teamname]["spinloop"] );
wait .05;
if ( IsDefined( level.heli_sound[teamname]["spinstart"] ) )
self playLoopSound( level.heli_sound[teamname]["spinstart"] );
}
//===========================================
// heli_explode
//===========================================
heli_explode( altStyle )
{
self notify( "death" );
if( isDefined( altStyle ) && isDefined( level.chopper_fx["explode"]["air_death"][self.heli_type] ) )
{
deathAngles = self getTagAngles( "tag_deathfx" );
playFx( level.chopper_fx["explode"]["air_death"][self.heli_type], self getTagOrigin( "tag_deathfx" ), anglesToForward( deathAngles ), anglesToUp( deathAngles ) );
}
else
{
org = self.origin;
forward = ( self.origin + ( 0, 0, 1 ) ) - self.origin;
playFx( level.chopper_fx["explode"]["death"][self.heli_type], org, forward );
}
// play heli explosion sound
teamname = self.team;
self playSound( level.heli_sound[teamname]["crash"] );
// give "death" notify time to process
wait ( 0.05 );
if( IsDefined( self.killCamEnt ) )
self.killCamEnt delete();
// decrement the faux vehicle count right before it is deleted this way we know for sure it is gone
decrementFauxVehicleCount();
self delete();
}
//===========================================
// heli_fly_simple_path
//===========================================
heli_fly_simple_path( startNode )
{
self endon ( "death" );
self endon ( "leaving" );
// only one thread instance allowed
self notify( "flying");
self endon( "flying" );
heli_reset();
currentNode = startNode;
while ( isDefined( currentNode.target ) )
{
nextNode = getEntOrStruct( currentNode.target, "targetname" );
assertEx( isDefined( nextNode ), "Next node in path is undefined, but has targetname. Bad Node Position: " + currentNode.origin );
if( isDefined( currentNode.script_airspeed ) && isDefined( currentNode.script_accel ) )
{
heli_speed = currentNode.script_airspeed;
heli_accel = currentNode.script_accel;
}
else
{
heli_speed = 30 + randomInt(20);
heli_accel = 15 + randomInt(15);
}
if( isDefined( self.isAttacking ) && self.isAttacking )
{
wait ( 0.05 );
continue;
}
if( isDefined( self.isPerformingManeuver ) && self.isPerformingManeuver )
{
wait ( 0.05 );
continue;
}
self Vehicle_SetSpeed( heli_speed, heli_accel );
// end of the path
if ( !isDefined( nextNode.target ) )
{
self _setVehGoalPos( nextNode.origin+(self.zOffset), false );
self waittill( "near_goal" );
}
else
{
self _setVehGoalPos( nextNode.origin+(self.zOffset), false );
self waittill( "near_goal" );
self setGoalYaw( nextNode.angles[ 1 ] );
self waittillmatch( "goal" );
}
currentNode = nextNode;
}
printLn( currentNode.origin );
printLn( self.origin );
}
//===========================================
// handle_player_starting_aerial_view
//===========================================
handle_player_starting_aerial_view()
{
self notify( "player_start_aerial_view" );
}
//===========================================
// handle_player_ending_aerial_view
//===========================================
handle_player_ending_aerial_view()
{
self notify( "player_stop_aerial_view" );
}
//===========================================
// getHeliAnchor
//===========================================
getHeliAnchor()
{
if ( IsDefined( level.heliAnchor ) )
return level.heliAnchor;
warbirdAnchor = getEntOrStruct( "warbird_anchor", "targetname" );
if ( !IsDefined( warbirdAnchor ) )
{
PrintLn( "WARNING: need a struct or entity with targetname warbird_anchor in the center of the map." );
warbirdAnchor = SpawnStruct();
warbirdAnchor.origin = ( 0, 0, CONST_default_height_z );
warbirdAnchor.targetname = "warbird_anchor";
}
if ( !IsDefined( warbirdAnchor.script_noteworthy ) )
warbirdAnchor.script_noteworthy = 3500;
level.heliAnchor = warbirdAnchor;
return level.heliAnchor;
}
//===========================================
// playerHandleBoundaryStatic
//===========================================
playerHandleBoundaryStatic( vehicle, endonString1, endonString2 )
{
if ( IsDefined( endonString1 ) )
self endon( endonString1 );
if ( IsDefined( endonString2 ) )
self endon( endonString2 );
outOfBoundsTriggers = GetEntArray( "remote_heli_range", "targetname" );
if ( !IsDefined( vehicle.vehicleType ) || outOfBoundsTriggers.size == 0 )
{
self playerHandleBoundaryStaticRadius( vehicle, endonString1, endonString2 );
return;
}
while ( true )
{
leaving = vehicle vehicleTouchingAnyTrigger( outOfBoundsTriggers );
if ( leaving )
{
self thread playerStartOutOfBoundsStatic( vehicle, endonString1, endonString2 );
while ( true )
{
waitframe();
if ( !IsDefined( vehicle.alwaysStaticOut ) || !vehicle.alwaysStaticOut )
{
back = !vehicle vehicleTouchingAnyTrigger( outOfBoundsTriggers );
if ( back )
{
vehicle notify( "staticDone" );
self thread playerStaticToNormal( vehicle, endonString1, endonString2 );
break;
}
}
}
}
waitframe();
}
}
vehicleTouchingAnyTrigger( triggers ) // self == vehicle
{
foreach ( trigger in triggers )
{
if ( self IsTouching( trigger ) )
return true;
}
return false;
}
playerStaticToNormal( vehicle, endonString1, endonString2 )
{
if ( IsDefined( endonString1 ) )
self endon( endonString1 );
if ( IsDefined( endonString2 ) )
self endon( endonString2 );
vehicle endon( "staticStarting" );
vehicle.staticLevel--;
while ( vehicle.staticLevel > 0 )
{
self playerShowStreakStaticForRange( vehicle.staticLevel );
if ( IsDefined( vehicle.buddy ) )
vehicle.buddy playerShowStreakStaticForRange( vehicle.staticLevel );
wait 0.5;
vehicle.staticLevel--;
}
self playerShowStreakStaticForRange( 0 );
if ( IsDefined( vehicle.buddy ) )
vehicle.buddy playerShowStreakStaticForRange( 0 );
}
playerStartOutOfBoundsStatic( vehicle, endonString1, endonString2 )
{
if ( IsDefined( endonString1 ) )
self endon( endonString1 );
if ( IsDefined( endonString2 ) )
self endon( endonString2 );
vehicle notify( "staticStarting" );
vehicle endon( "staticDone" );
if ( !IsDefined( vehicle.staticLevel ) || vehicle.staticLevel == 0 )
vehicle.staticLevel = 1;
while ( vehicle.staticLevel < 4 )
{
self playerShowStreakStaticForRange( vehicle.staticLevel );
if ( IsDefined( vehicle.buddy ) )
vehicle.buddy playerShowStreakStaticForRange( vehicle.staticLevel );
if ( IsDefined( vehicle.PlayerAttachPoint ) )
vehicle.PlayerAttachPoint PlaySound( "mp_warbird_outofbounds_warning" );
if ( IsDefined( vehicle.staticLevelWaitTime ) )
wait vehicle.staticLevelWaitTime;
else
wait 2;
vehicle.staticLevel++;
}
vehicle notify( "outOfBounds" );
}
playerHandleBoundaryStaticRadius( vehicle, endonString1, endonString2 )
{
if ( IsDefined( endonString1 ) )
self endon( endonString1 );
if ( IsDefined( endonString2 ) )
self endon( endonString2 );
WarbirdAnchor = getHeliAnchor();
levelBoundaryRadius = Int( WarbirdAnchor.script_noteworthy );
while ( true )
{
DistanceFromCenter = Distance( WarbirdAnchor.origin, vehicle.origin );
if ( DistanceFromCenter < levelBoundaryRadius )
{
self playerShowStreakStaticForRange( 0 );
}
else if ( DistanceFromCenter > levelBoundaryRadius && DistanceFromCenter < levelBoundaryRadius + 500 )
{
self playerShowStreakStaticForRange( 1 );
if ( IsDefined( vehicle.PlayerAttachPoint ) )
vehicle.PlayerAttachPoint PlaySound( "mp_warbird_outofbounds_warning" );
}
else if ( DistanceFromCenter > levelBoundaryRadius + 500 && DistanceFromCenter < levelBoundaryRadius + 1000 )
{
self playerShowStreakStaticForRange( 2 );
if ( IsDefined( vehicle.PlayerAttachPoint ) )
vehicle.PlayerAttachPoint PlaySound( "mp_warbird_outofbounds_warning" );
}
else if ( DistanceFromCenter > levelBoundaryRadius + 1000 && DistanceFromCenter < levelBoundaryRadius + 1500 )
{
self playerShowStreakStaticForRange( 3 );
if ( IsDefined( vehicle.PlayerAttachPoint ) )
vehicle.PlayerAttachPoint PlaySound( "mp_warbird_outofbounds_warning" );
}
else
{
self playerShowStreakStaticForRange( 4 );
vehicle notify( "outOfBounds" );
}
wait 0.5;
}
}
playerEnableStreakStatic()
{
self notify( "playerUpdateStreakStatic" );
self SetClientOmnvar( "ui_streak_overlay_state", CONST_streak_static_enabled );
}
playerDisableStreakStatic()
{
self notify( "playerUpdateStreakStatic" );
self SetClientOmnvar( "ui_streak_overlay_state", CONST_streak_static_disabled );
}
playerShowFullStatic()
{
self notify( "playerUpdateStreakStatic" );
self SetClientOmnvar( "ui_streak_overlay_state", CONST_streak_static_fullnotext );
}
playerShowStreakStaticForDamage()
{
self endon( "disconnect" );
// Only allow damage if in the base 'enabled' state
if ( self GetClientOmnvar( "ui_streak_overlay_state" ) != CONST_streak_static_enabled )
return;
self notify( "playerUpdateStreakStatic" );
self endon( "playerUpdateStreakStatic" );
self SetClientOmnvar( "ui_streak_overlay_state", CONST_streak_static_damage );
wait( 1 );
self SetClientOmnvar( "ui_streak_overlay_state", CONST_streak_static_enabled );
}
playerShowStreakStaticForRange( rangeIndex )
{
assert( rangeIndex >= 0 && rangeIndex <= 4 );
targetIndex = CONST_streak_static_enabled;
switch( rangeIndex )
{
case 0: targetIndex = CONST_streak_static_enabled; break;
case 1: targetIndex = CONST_streak_static_range1; break;
case 2: targetIndex = CONST_streak_static_range2; break;
case 3: targetIndex = CONST_streak_static_range3; break;
case 4: targetIndex = CONST_streak_static_range4; break;
default:
AssertMsg( "Unhandled range for playerShowStreakStaticForRange" );
}
self notify( "playerUpdateStreakStatic" );
self SetClientOmnvar( "ui_streak_overlay_state", targetIndex );
}
getEntOrStruct( name, type )
{
ent = GetEnt( name, type );
if ( IsDefined( ent ) )
return ent;
return getstruct( name, type );
}
getEntOrStructArray( name, type )
{
structArray = getstructarray( name, type );
entArray = GetEntArray( name, type );
if ( entArray.size > 0 )
structArray = array_combine( structArray, entArray );
return structArray;
}
//===========================================
// playerHandleKillVehicle
//===========================================
playerHandleKillVehicle( vehicle, endonString1, endonString2 )
{
if ( IsDefined( endonString1 ) )
self endon( endonString1 );
if ( IsDefined( endonString2 ) )
self endon( endonString2 );
if( !isDefined( level.vehicle_kill_triggers ) )
return;
while ( true )
{
inDeathTrigger = vehicle vehicleTouchingAnyTrigger( level.vehicle_kill_triggers );
if ( inDeathTrigger )
{
vehicle notify( "death" );
}
waitframe();
}
}
setup_kill_drone_trig( name, type )
{
if ( isdefined( name ) && isdefined( type ) )
{
ents = getentarray( name, type );
array_thread( ents, ::setup_kill_drone_trig_proc );
}
else if( self isVehicleKillTrigger() )
self setup_kill_drone_trig_proc();
}
setup_kill_drone_trig_proc()
{
if( self isVehicleKillTrigger() )
{
if( !isDefined( level.vehicle_kill_triggers ) )
level.vehicle_kill_triggers = [];
level.vehicle_kill_triggers[ level.vehicle_kill_triggers.size ] = self;
}
}
isVehicleKillTrigger()
{
if( isDefined( self.classname ) && IsSubStr( self.classname, "trigger_multiple" ) && isDefined( self.spawnflags ) && (self.spawnflags & CONST_VEHICLE_SPAWNFLAG ) )
{
return true;
}
return false;
}
vehicleIsCloaked() // self == vehicle
{
return ( IsDefined( self.cloakstate ) && self.cloakstate < 1 );
}
thermalVision( endonString, adsAperature, adsFocalDistance, normalAperature, normalFocalDistance, focusSpeed, aperatureSpeed )
{
self endon( endonString );
inverted = false;
disableOrbitalThermal( self );
self VisionSetThermalForPlayer( "default", 0.25 );
self SetClientOmnvar( "ui_killstreak_optic", false );
if ( IsBot( self ) )
return;
self notifyOnPlayerCommand( "switch thermal", "+actionslot 1" );
self thread playerCleanupThermalVisionCommands( endonString );
while ( true )
{
self waittill ( "switch thermal" );
if ( !inverted )
{
enableOrbitalThermal( self, endonString, adsAperature, adsFocalDistance, normalAperature, normalFocalDistance, focusSpeed, aperatureSpeed );
self SetClientOmnvar( "ui_killstreak_optic", true );
self PlayLocalSound( "paladin_toggle_flir_plr" );
}
else
{
disableOrbitalThermal( self );
self SetClientOmnvar( "ui_killstreak_optic", false );
self PlayLocalSound( "paladin_toggle_flir_plr" );
}
inverted = !inverted;
}
}
playerCleanupThermalVisionCommands( endonString )
{
self endon( "disconnect" );
self waittill( endonString );
self NotifyOnPlayerCommandRemove( "switch thermal", "+actionslot 1" );
}
disableOrbitalThermal( ent )
{
ent ThermalVisionOff();
ent notify ( "thermal_vision_off" );
ent DisablePhysicalDepthOfFieldScripting();
ent.orbitalThermalMode = false;
}
enableOrbitalThermal( ent, endonString, adsAperature, adsFocalDistance, normalAperature, normalFocalDistance, focusSpeed, aperatureSpeed )
{
ent endon( "disconnect" );
ent endon( "death" );
ent endon( "faux_spawn" );
ent endon( endonString );
// Should be initialized in opticsthermal_think() in _opticsthermal.gsc
if ( !IsDefined( ent.opticsThermalEnabled ) )
ent.opticsThermalEnabled = false;
if ( !IsDefined( ent.orbitalThermalMode ) )
ent.orbitalThermalMode = false;
ent.orbitalThermalMode = true;
while ( ent.opticsThermalEnabled )
wait 0.05;
ent ThermalVisionOn();
ent EnablePhysicalDepthOfFieldScripting(3);
ent thread setThermalDOF( endonString, adsAperature, adsFocalDistance, normalAperature, normalFocalDistance, focusSpeed, aperatureSpeed );
}
setThermalDOF( endonString, adsAperature, adsFocalDistance, normalAperature, normalFocalDistance, focusSpeed, aperatureSpeed )
{
self endon ( endonString );
self endon( "disconnect" );
self endon( "thermal_vision_off" );
while( 1 )
{
adsAmt = self playerAds();
aperature = float_lerp( normalAperature, adsAperature, adsAmt );
focalDistance = float_lerp( normalFocalDistance, adsFocalDistance, adsAmt );
self SetPhysicalDepthOfField( aperature, focalDistance, focusSpeed, aperatureSpeed );
wait 0.1;
}
}
float_lerp( start, end, percent )
{
return (start + percent*(end - start));
}
patchHeliLoopNode( nodeOrigin, newNodeOrigin )
{
checkedNodes = [];
nextNode = getEntOrStruct( "heli_loop_start", "targetname" );
while ( true )
{
if ( array_contains( checkedNodes, nextNode ) )
break;
if ( nextNode.origin == nodeOrigin )
{
nextNode.origin = newNodeOrigin;
return;
}
checkedNodes[checkedNodes.size] = nextNode;
nextNode = getEntOrStruct( nextNode.target, "targetname" );
}
AssertMsg( "Could not patch node with origin " + nodeOrigin + " because it can't be found." );
}