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

835 lines
23 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
EMP_GRENADE_TIME = 3.5;
init()
{
level.killStreakFuncs[ "littlebird_support" ] = ::tryUseLBSupport;
level.heliGuardSettings = [];
level.heliGuardSettings[ "littlebird_support" ] = spawnStruct();
level.heliGuardSettings[ "littlebird_support" ].timeOut = 60.0;
level.heliGuardSettings[ "littlebird_support" ].health = 999999; // keep it from dying anywhere in code
level.heliGuardSettings[ "littlebird_support" ].maxHealth = 2000; // this is what we check against for death
level.heliGuardSettings[ "littlebird_support" ].streakName = "littlebird_support";
level.heliGuardSettings[ "littlebird_support" ].vehicleInfo = "attack_littlebird_mp";
level.heliGuardSettings[ "littlebird_support" ].weaponInfo = "littlebird_guard_minigun_mp";
level.heliGuardSettings[ "littlebird_support" ].weaponModelLeft = "vehicle_little_bird_minigun_left";
level.heliGuardSettings[ "littlebird_support" ].weaponModelRight = "vehicle_little_bird_minigun_right";
level.heliGuardSettings[ "littlebird_support" ].weaponTagLeft = "tag_flash";
level.heliGuardSettings[ "littlebird_support" ].weaponTagRight = "tag_flash_2";
level.heliGuardSettings[ "littlebird_support" ].sentryMode = "auto_nonai";
level.heliGuardSettings[ "littlebird_support" ].modelBase = level.littlebird_model;
level.heliGuardSettings[ "littlebird_support" ].teamSplash = "used_littlebird_support";
lbSupport_setAirStartNodes();
lbSupport_setAirNodeMesh();
/#
SetDevDvarIfUninitialized( "scr_lbguard_timeout", 60.0 );
#/
}
tryUseLBSupport( lifeId, streakName ) // self == player
{
heliGuardType = "littlebird_support";
numIncomingVehicles = 1;
if( IsDefined( level.littlebirdGuard ) || maps\mp\killstreaks\_helicopter::exceededMaxLittlebirds( heliGuardType ) )
{
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
return false;
}
else if( !level.air_node_mesh.size )
{
self IPrintLnBold( &"KILLSTREAKS_UNAVAILABLE_IN_LEVEL" );
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();
littleBird = createLBGuard( heliGuardType );
if ( !IsDefined( littleBird ) )
{
// decrement the faux vehicle count since this failed to spawn
decrementFauxVehicleCount();
return false;
}
self thread startLBSupport( littleBird );
level thread teamPlayerCardSplash( level.heliGuardSettings[ heliGuardType ].teamSplash, self, self.team );
return true;
}
createLBGuard( heliGuardType )
{
closestStartNode = lbSupport_getClosestStartNode( self.origin );
if( IsDefined( closestStartNode.angles ) )
startAng = closestStartNode.angles;
else
startAng = ( 0, 0, 0);
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( self.origin );
closestNode = lbSupport_getClosestNode( self.origin );
forward = AnglesToForward( self.angles );
targetPos = ( closestNode.origin*(1,1,0) ) + ( (0,0,1)*flyHeight ) + ( forward * -100 );
startPos = closestStartNode.origin;
lb = spawnHelicopter( self, startPos, startAng, level.heliGuardSettings[ heliGuardType ].vehicleInfo, level.heliGuardSettings[ heliGuardType ].modelBase );
if ( !IsDefined( lb ) )
return;
lb maps\mp\killstreaks\_helicopter::addToLittleBirdList();
lb thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath();
lb.health = level.heliGuardSettings[ heliGuardType ].health;
lb.maxHealth = level.heliGuardSettings[ heliGuardType ].maxHealth;
lb.damageTaken = 0; // how much damage has it taken
lb.speed = 100;
lb.followSpeed = 40;
lb.owner = self;
lb SetOtherEnt(self);
lb.team = self.team;
lb SetMaxPitchRoll( 45, 45 );
lb Vehicle_SetSpeed( lb.speed, 100, 40 );
lb SetYawSpeed( 120, 60 );
lb setneargoalnotifydist( 512 );
lb.killCount = 0;
lb.heliType = "littlebird";
lb.heliGuardType = "littlebird_support";
lb.targettingRadius = 2000; // matches the maxRange on the turret gdt setting
//lb ThermalDrawEnable();
lb make_entity_sentient_mp( lb.team );
lb.targetPos = targetPos;
lb.currentNode = closestNode;
mgTurret = SpawnTurret( "misc_turret", lb.origin, level.heliGuardSettings[ heliGuardType ].weaponInfo );
mgTurret LinkTo( lb, level.heliGuardSettings[ heliGuardType ].weaponTagLeft, (0,0,0), (0,0,0) );
mgTurret SetModel( level.heliGuardSettings[ heliGuardType ].weaponModelLeft );
mgTurret.angles = lb.angles;
mgTurret.owner = lb.owner;
mgTurret.team = self.team;
mgTurret makeTurretInoperable();
mgTurret.vehicle = lb;
//mgTurret ThermalDrawEnable();
lb.mgTurretLeft = mgTurret;
lb.mgTurretLeft SetDefaultDropPitch( 0 );
killCamOrigin = ( lb.origin + ( ( AnglesToForward( lb.angles ) * -100 ) + ( AnglesToRight( lb.angles ) * -100 ) ) ) + ( 0, 0, 50 );
mgTurret.killCamEnt = Spawn( "script_model", killCamOrigin );
mgTurret.killCamEnt SetScriptMoverKillCam( "explosive" );
mgTurret.killCamEnt LinkTo( lb, "tag_origin" );
mgTurret = SpawnTurret( "misc_turret", lb.origin, level.heliGuardSettings[ heliGuardType ].weaponInfo );
mgTurret LinkTo( lb, level.heliGuardSettings[ heliGuardType ].weaponTagRight, (0,0,0), (0,0,0) );
mgTurret SetModel( level.heliGuardSettings[ heliGuardType ].weaponModelRight );
mgTurret.angles = lb.angles;
mgTurret.owner = lb.owner;
mgTurret.team = self.team;
mgTurret makeTurretInoperable();
mgTurret.vehicle = lb;
lb.mgTurretRight = mgTurret;
lb.mgTurretRight SetDefaultDropPitch( 0 );
killCamOrigin = ( lb.origin + ( ( AnglesToForward( lb.angles ) * -100 ) + ( AnglesToRight( lb.angles ) * 100 ) ) ) + ( 0, 0, 50 );
mgTurret.killCamEnt = Spawn( "script_model", killCamOrigin );
mgTurret.killCamEnt SetScriptMoverKillCam( "explosive" );
mgTurret.killCamEnt LinkTo( lb, "tag_origin" );
if ( level.teamBased )
{
lb.mgTurretLeft setTurretTeam( self.team );
lb.mgTurretRight setTurretTeam( self.team );
}
lb.mgTurretLeft SetMode( level.heliGuardSettings[ heliGuardType ].sentryMode );
lb.mgTurretRight SetMode( level.heliGuardSettings[ heliGuardType ].sentryMode );
lb.mgTurretLeft SetSentryOwner( self );
lb.mgTurretRight SetSentryOwner( self );
lb.mgTurretLeft thread lbSupport_attackTargets();
lb.mgTurretRight thread lbSupport_attackTargets();
lb.attract_strength = 10000;
lb.attract_range = 150;
lb.attractor = Missile_CreateAttractorEnt( lb, lb.attract_strength, lb.attract_range );
lb.hasDodged = false;
lb.empGrenaded = false;
lb thread lbSupport_handleDamage();
lb thread lbSupport_watchDeath();
lb thread lbSupport_watchTimeout();
lb thread lbSupport_watchOwnerLoss();
lb thread lbSupport_watchOwnerDamage();
lb thread lbSupport_watchRoundEnd();
lb thread lbSupport_lightFX();
level.littlebirdGuard = lb;
lb.owner maps\mp\_matchdata::logKillstreakEvent( level.heliGuardSettings[ lb.heliGuardType ].streakName, lb.targetPos );
return lb;
}
lbSupport_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" );
}
startLBSupport( littleBird ) // self == player
{
level endon( "game_ended" );
littleBird endon( "death" );
// look at the player
littleBird SetLookAtEnt( self );
// go to pos
littleBird setVehGoalPos( littleBird.targetPos );
littleBird waittill( "near_goal" );
littleBird Vehicle_SetSpeed( littleBird.speed, 60, 30 );
littleBird waittill ( "goal" );
// drop to target
littleBird setVehGoalPos( littleBird.currentNode.origin, 1 );
littleBird waittill ( "goal" );
// begin following player
littleBird thread lbSupport_followPlayer();
// dodge the first sam or lock-on attack
littleBird thread maps\mp\killstreaks\_flares::flares_handleIncomingSAM( ::lbSupport_watchSAMProximity );
littleBird thread maps\mp\killstreaks\_flares::flares_handleIncomingStinger( ::lbSupport_watchStingerProximity );
}
lbSupport_followPlayer() // self == lb
{
level endon( "game_ended" );
self endon( "death" );
self endon( "leaving" );
if( !IsDefined( self.owner ) )
{
self thread lbSupport_leave();
return;
}
self.owner endon( "disconnect" );
self endon( "owner_gone" );
self Vehicle_SetSpeed( self.followSpeed, 20, 20 );
while( true )
{
if( IsDefined( self.owner ) && IsAlive( self.owner ) )
{
currentNode = lbSupport_getClosestLinkedNode( self.owner.origin );
if( IsDefined( currentNode ) && currentNode != self.currentNode )
{
self.currentNode = currentNode;
// don't thread because we want to waittill goal before we pick the next node
lbSupport_moveToPlayer();
continue;
}
}
wait( 1 );
}
}
lbSupport_moveToPlayer() // self == lb
{
level endon( "game_ended" );
self endon( "death" );
self endon( "leaving" );
self.owner endon( "death" );
self.owner endon( "disconnect" );
self endon( "owner_gone" );
self notify( "lbSupport_moveToPlayer" );
self endon( "lbSupport_moveToPlayer" );
self.inTransit = true;
self setVehGoalPos( self.currentNode.origin + ( 0, 0, 100 ), 1 );
self waittill ( "goal" );
self.inTransit = false;
self notify( "hit_goal" );
}
//
// state trackers
//
lbSupport_watchDeath()
{
level endon( "game_ended" );
self endon( "gone" );
self waittill( "death" );
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
}
lbSupport_watchTimeout()
{
level endon ( "game_ended" );
self endon( "death" );
self.owner endon( "disconnect" );
self endon( "owner_gone" );
timeout = level.heliGuardSettings[ self.heliGuardType ].timeOut;
/#
timeout = GetDvarFloat( "scr_lbguard_timeout" );
#/
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeout );
self thread lbSupport_leave();
}
lbSupport_watchOwnerLoss()
{
level endon ( "game_ended" );
self endon( "death" );
self endon( "leaving" );
self.owner waittill( "killstreak_disowned" );
self notify( "owner_gone" );
// leave
self thread lbSupport_leave();
}
lbSupport_watchOwnerDamage() // self == lb
{
level endon ( "game_ended" );
self endon( "death" );
self endon( "leaving" );
self.owner endon( "disconnect" );
self endon( "owner_gone" );
while( true )
{
// if someone is attacking the owner, attack them
self.owner waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
if( isPlayer( attacker ) )
{
if( attacker != self.owner &&
Distance2D( attacker.origin, self.origin ) <= self.targettingRadius &&
!attacker _hasPerk( "specialty_blindeye" ) &&
!( level.hardcoreMode && level.teamBased && attacker.team == self.team ) )
{
self SetLookAtEnt( attacker );
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft SetTargetEntity( attacker );
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight SetTargetEntity( attacker );
}
}
}
}
lbSupport_watchRoundEnd()
{
self endon( "death" );
self endon( "leaving" );
self.owner endon( "disconnect" );
self endon( "owner_gone" );
level waittill_any( "round_end_finished", "game_ended" );
// leave
self thread lbSupport_leave();
}
lbSupport_leave()
{
self endon( "death" );
self notify( "leaving" );
level.littlebirdGuard = undefined;
self ClearLookAtEnt();
// rise
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( self.origin );
targetPos = self.origin + (0,0,flyHeight);
self Vehicle_SetSpeed( self.speed, 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();
}
//
// Damage, death, and destruction
//
lbSupport_handleDamage() // self == lb
{
self endon( "death" );
level endon( "game_ended" );
self SetCanDamage( true );
while( true )
{
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
// don't allow people to destroy things on their team if FF is off
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
continue;
if ( !IsDefined( self ) )
return;
if ( IsDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
self.wasDamagedFromBulletPenetration = true;
self.wasDamaged = true;
modifiedDamage = damage;
if( IsPlayer( attacker ) )
{
// attack the attacker
if( attacker != self.owner &&
Distance2D( attacker.origin, self.origin ) <= self.targettingRadius &&
!attacker _hasPerk( "specialty_blindeye" ) &&
!( level.hardcoreMode && level.teamBased && attacker.team == self.team ) )
{
self SetLookAtEnt( attacker );
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft SetTargetEntity( attacker );
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight SetTargetEntity( attacker );
}
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
if( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
{
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
modifiedDamage += damage * level.armorPiercingMod;
}
}
// in case we are shooting from a remote position, like being in the osprey gunner shooting this
if( IsDefined( attacker.owner ) && IsPlayer( attacker.owner ) )
{
attacker.owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
}
if( IsDefined( weapon ) )
{
switch( weapon )
{
case "ac130_105mm_mp":
case "ac130_40mm_mp":
case "stinger_mp":
case "javelin_mp":
case "remote_mortar_missile_mp":
case "remotemissile_projectile_mp":
self.largeProjectileDamage = true;
modifiedDamage = self.maxHealth + 1;
break;
case "sam_projectile_mp":
self.largeProjectileDamage = true;
modifiedDamage = self.maxHealth * 0.25; // takes about 1 burst of sam rockets
break;
case "emp_grenade_mp":
// NOTE: don't destroy helicopters with an emp grenade, it didn't look good in practice
modifiedDamage = 0;
self thread lbSupport_EMPGrenaded();
break;
case "osprey_player_minigun_mp":
self.largeProjectileDamage = false;
modifiedDamage *= 2; // since it's a larger caliber, make it hurt
break;
}
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
}
self.damageTaken += modifiedDamage;
if ( self.damageTaken >= self.maxHealth )
{
if ( isPlayer( attacker ) && ( !IsDefined( self.owner ) || attacker != self.owner ) )
{
attacker notify( "destroyed_helicopter" );
attacker notify( "destroyed_killstreak", weapon );
thread teamPlayerCardSplash( "callout_destroyed_little_bird", attacker );
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300, weapon, meansOfDeath );
attacker thread maps\mp\gametypes\_rank::xpEventPopup( "destroyed_little_bird" );
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
}
if( IsDefined( self.owner ) )
self.owner thread leaderDialogOnPlayer( "lbguard_destroyed" );
self notify ( "death" );
return;
}
}
}
lbSupport_EMPGrenaded() // self == vehicle
{
self notify( "lbSupport_EMPGrenaded" );
self endon( "lbSupport_EMPGrenaded" );
self endon( "death" );
self.owner endon( "disconnect" );
level endon( "game_ended" );
self.empGrenaded = true;
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight notify( "stop_shooting" );
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft notify( "stop_shooting" );
// TU0: using the ims fx and not creating its own because we've already locked down ConfigStrings and it will shift them if we load fx in this file
if( IsDefined( level._effect[ "ims_sensor_explode" ] ) )
{
if( IsDefined( self.mgTurretRight ) )
PlayFXOnTag( getfx( "ims_sensor_explode" ), self.mgTurretRight, "tag_aim" );
if( IsDefined( self.mgTurretLeft ) )
PlayFXOnTag( getfx( "ims_sensor_explode" ), self.mgTurretLeft, "tag_aim" );
}
wait( EMP_GRENADE_TIME );
self.empGrenaded = false;
if( IsDefined( self.mgTurretRight ) )
self.mgTurretRight notify( "turretstatechange" );
if( IsDefined( self.mgTurretLeft ) )
self.mgTurretLeft notify( "turretstatechange" );
}
// this is different from the flares monitoring that the other helicopters do
// we want this to dodge the missiles if at all possible, making for a cool evasive manuever
lbSupport_watchSAMProximity( player, missileTeam, missileTarget, missileGroup ) // self == level
{
level endon ( "game_ended" );
missileTarget endon( "death" );
for( i = 0; i < missileGroup.size; i++ )
{
if( IsDefined( missileGroup[ i ] ) && !missileTarget.hasDodged )
{
missileTarget.hasDodged = true;
newTarget = spawn( "script_origin", missileTarget.origin );
newTarget.angles = missileTarget.angles;
newTarget MoveGravity( AnglesToRight( missileGroup[ i ].angles ) * -1000, 0.05 );
newTarget thread maps\mp\killstreaks\_flares::flares_deleteAfterTime( 5.0 );
for( j = 0; j < missileGroup.size; j++ )
{
if( IsDefined( missileGroup[ j ] ) )
{
missileGroup[ j ] Missile_SetTargetEnt( newTarget );
}
}
// dodge the incoming missiles
dodgePoint = missileTarget.origin + ( AnglesToRight( missileGroup[ i ].angles ) * 200 );
missileTarget Vehicle_SetSpeed( missileTarget.speed, 100, 40 );
missileTarget SetVehGoalPos( dodgePoint, true );
wait( 2.0 );
missileTarget Vehicle_SetSpeed( missileTarget.followSpeed, 20, 20 );
break;
}
}
}
// this is different from the flares monitoring that the other helicopters do
// we want this to dodge the missiles if at all possible, making for a cool evasive manuever
lbSupport_watchStingerProximity( player, missileTeam, missileTarget ) // self == missile
{
level endon ( "game_ended" );
missileTarget endon( "death" );
if( IsDefined( self ) && !missileTarget.hasDodged )
{
missileTarget.hasDodged = true;
newTarget = spawn( "script_origin", missileTarget.origin );
newTarget.angles = missileTarget.angles;
newTarget MoveGravity( AnglesToRight( self.angles ) * -1000, 0.05 );
newTarget thread maps\mp\killstreaks\_flares::flares_deleteAfterTime( 5.0 );
self Missile_SetTargetEnt( newTarget );
// dodge the incoming missiles
dodgePoint = missileTarget.origin + ( AnglesToRight( self.angles ) * 200 );
missileTarget Vehicle_SetSpeed( missileTarget.speed, 100, 40 );
missileTarget SetVehGoalPos( dodgePoint, true );
wait( 2.0 );
missileTarget Vehicle_SetSpeed( missileTarget.followSpeed, 20, 20 );
}
}
//
// node funcs
//
lbSupport_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;
}
lbSupport_getClosestNode( pos )
{
// gets the closest node to the position passed in, regardless of link
closestNode = undefined;
closestDistance = 999999;
foreach( loc in level.air_node_mesh )
{
nodeDistance = distance( loc.origin, pos );
if ( nodeDistance < closestDistance )
{
closestNode = loc;
closestDistance = nodeDistance;
}
}
return closestNode;
}
lbSupport_getClosestLinkedNode( pos ) // self == lb
{
// gets the linked node that is closest to the current position and moving towards the position passed in
closestNode = undefined;
totalDistance = Distance2D( self.currentNode.origin, pos );
closestDistance = totalDistance;
// loop through each neighbor and find the closest to the final goal
foreach( loc in self.currentNode.neighbors )
{
nodeDistance = Distance2D( loc.origin, pos );
if ( nodeDistance < totalDistance && nodeDistance < closestDistance )
{
closestNode = loc;
closestDistance = nodeDistance;
}
}
return closestNode;
}
lbSupport_arrayContains( array, compare )
{
if ( array.size <= 0 )
return false;
foreach ( member in array )
{
if ( member == compare )
return true;
}
return false;
}
lbSupport_getLinkedStructs()
{
array = [];
if ( IsDefined( self.script_linkTo ) )
{
linknames = get_links();
for ( i = 0; i < linknames.size; i++ )
{
ent = getstruct( linknames[ i ], "script_linkname" );
if ( IsDefined( ent ) )
{
array[ array.size ] = ent;
}
}
}
return array;
}
lbSupport_setAirStartNodes()
{
level.air_start_nodes = getstructarray( "chopper_boss_path_start", "targetname" );
foreach( loc in level.air_start_nodes )
{
// Grab array of path loc refs that this loc links to
loc.neighbors = loc lbSupport_getLinkedStructs();
}
}
lbSupport_setAirNodeMesh()
{
level.air_node_mesh = getstructarray( "so_chopper_boss_path_struct", "script_noteworthy" );
foreach( loc in level.air_node_mesh )
{
// Grab array of path loc refs that this loc links to
loc.neighbors = loc lbSupport_getLinkedStructs();
// Step through each loc in the map and if it
// links to this loc, add it
foreach( other_loc in level.air_node_mesh )
{
if ( loc == other_loc )
continue;
if ( !lbSupport_arrayContains( loc.neighbors, other_loc ) && lbSupport_arrayContains( other_loc lbSupport_getLinkedStructs(), loc ) )
loc.neighbors[ loc.neighbors.size ] = other_loc;
}
}
}
/* ============================
Turret Logic Functions
============================ */
lbSupport_attackTargets() // self == turret
{
self.vehicle endon( "death" );
level endon( "game_ended" );
for ( ;; )
{
self waittill( "turretstatechange" );
if ( self IsFiringTurret() && !self.vehicle.empGrenaded )
self thread lbSupport_burstFireStart();
else
self thread lbSupport_burstFireStop();
}
}
lbSupport_burstFireStart() // self == turret
{
self.vehicle endon( "death" );
self.vehicle endon( "leaving" );
self endon( "stop_shooting" );
level endon( "game_ended" );
fireTime = 0.1;
minShots = 40;
maxShots = 80;
minPause = 1.0;
maxPause = 2.0;
for ( ;; )
{
numShots = RandomIntRange( minShots, maxShots + 1 );
for ( i = 0; i < numShots; i++ )
{
targetEnt = self GetTurretTarget( false );
if( IsDefined( targetEnt ) &&
( !IsDefined( targetEnt.spawntime ) || ( gettime() - targetEnt.spawntime )/1000 > 5 ) &&
( IsDefined( targetEnt.team ) && targetEnt.team != "spectator" ) &&
isReallyAlive( targetEnt ) )
{
self.vehicle SetLookAtEnt( targetEnt );
self ShootTurret();
}
wait ( fireTime );
}
wait ( RandomFloatRange( minPause, maxPause ) );
}
}
lbSupport_burstFireStop() // self == turret
{
self notify( "stop_shooting" );
if( IsDefined( self.vehicle.owner ) )
self.vehicle SetLookAtEnt( self.vehicle.owner );
}
/* ============================
END Turret Logic Functions
============================ */