1551 lines
38 KiB
Plaintext
1551 lines
38 KiB
Plaintext
#include maps\mp\_utility;
|
|
#include maps\mp\gametypes\_hud_util;
|
|
#include common_scripts\utility;
|
|
|
|
|
|
init()
|
|
{
|
|
if ( !IsDefined( level.sentryType ) )
|
|
level.sentryType = [];
|
|
|
|
level.sentryType[ "sentry_minigun" ] = "sentry";
|
|
level.sentryType[ "sam_turret" ] = "sam_turret";
|
|
|
|
level.killStreakFuncs[ level.sentryType[ "sentry_minigun" ] ] = ::tryUseAutoSentry;
|
|
level.killStreakFuncs[ level.sentryType[ "sam_turret" ] ] = ::tryUseSAM;
|
|
|
|
if ( !IsDefined( level.sentrySettings ) )
|
|
level.sentrySettings = [];
|
|
|
|
level.sentrySettings[ "sentry_minigun" ] = spawnStruct();
|
|
level.sentrySettings[ "sentry_minigun" ].health = 999999; // keep it from dying anywhere in code
|
|
level.sentrySettings[ "sentry_minigun" ].maxHealth = 1000; // this is the health we'll check
|
|
level.sentrySettings[ "sentry_minigun" ].burstMin = 20;
|
|
level.sentrySettings[ "sentry_minigun" ].burstMax = 120;
|
|
level.sentrySettings[ "sentry_minigun" ].pauseMin = 0.15;
|
|
level.sentrySettings[ "sentry_minigun" ].pauseMax = 0.35;
|
|
level.sentrySettings[ "sentry_minigun" ].sentryModeOn = "sentry";
|
|
level.sentrySettings[ "sentry_minigun" ].sentryModeOff = "sentry_offline";
|
|
level.sentrySettings[ "sentry_minigun" ].timeOut = 90.0;
|
|
level.sentrySettings[ "sentry_minigun" ].spinupTime = 0.05;
|
|
level.sentrySettings[ "sentry_minigun" ].overheatTime = 8.0;
|
|
level.sentrySettings[ "sentry_minigun" ].cooldownTime = 0.1;
|
|
level.sentrySettings[ "sentry_minigun" ].fxTime = 0.3;
|
|
level.sentrySettings[ "sentry_minigun" ].streakName = "sentry";
|
|
level.sentrySettings[ "sentry_minigun" ].weaponInfo = "sentry_minigun_mp";
|
|
level.sentrySettings[ "sentry_minigun" ].modelBase = "sentry_minigun_weak";
|
|
level.sentrySettings[ "sentry_minigun" ].modelPlacement = "sentry_minigun_weak_obj";
|
|
level.sentrySettings[ "sentry_minigun" ].modelPlacementFailed = "sentry_minigun_weak_obj_red";
|
|
level.sentrySettings[ "sentry_minigun" ].modelDestroyed = "sentry_minigun_weak_destroyed";
|
|
level.sentrySettings[ "sentry_minigun" ].hintString = &"SENTRY_PICKUP";
|
|
level.sentrySettings[ "sentry_minigun" ].headIcon = true;
|
|
level.sentrySettings[ "sentry_minigun" ].teamSplash = "used_sentry";
|
|
level.sentrySettings[ "sentry_minigun" ].shouldSplash = false;
|
|
level.sentrySettings[ "sentry_minigun" ].voDestroyed = "sentry_destroyed";
|
|
|
|
level.sentrySettings[ "sam_turret" ] = spawnStruct();
|
|
level.sentrySettings[ "sam_turret" ].health = 999999; // keep it from dying anywhere in code
|
|
level.sentrySettings[ "sam_turret" ].maxHealth = 1000; // this is the health we'll check
|
|
level.sentrySettings[ "sam_turret" ].burstMin = 20;
|
|
level.sentrySettings[ "sam_turret" ].burstMax = 120;
|
|
level.sentrySettings[ "sam_turret" ].pauseMin = 0.15;
|
|
level.sentrySettings[ "sam_turret" ].pauseMax = 0.35;
|
|
level.sentrySettings[ "sam_turret" ].sentryModeOn = "sentry_manual";
|
|
level.sentrySettings[ "sam_turret" ].sentryModeOff = "sentry_offline";
|
|
level.sentrySettings[ "sam_turret" ].timeOut = 90.0;
|
|
level.sentrySettings[ "sam_turret" ].spinupTime = 0.05;
|
|
level.sentrySettings[ "sam_turret" ].overheatTime = 8.0;
|
|
level.sentrySettings[ "sam_turret" ].cooldownTime = 0.1;
|
|
level.sentrySettings[ "sam_turret" ].fxTime = 0.3;
|
|
level.sentrySettings[ "sam_turret" ].streakName = "sam_turret";
|
|
level.sentrySettings[ "sam_turret" ].weaponInfo = "sam_mp";
|
|
level.sentrySettings[ "sam_turret" ].modelBase = "mp_sam_turret";
|
|
level.sentrySettings[ "sam_turret" ].modelPlacement = "mp_sam_turret_placement";
|
|
level.sentrySettings[ "sam_turret" ].modelPlacementFailed = "mp_sam_turret_placement_failed";
|
|
level.sentrySettings[ "sam_turret" ].modelDestroyed = "mp_sam_turret";
|
|
level.sentrySettings[ "sam_turret" ].hintString = &"SENTRY_PICKUP";
|
|
level.sentrySettings[ "sam_turret" ].headIcon = true;
|
|
level.sentrySettings[ "sam_turret" ].teamSplash = "used_sam_turret";
|
|
level.sentrySettings[ "sam_turret" ].shouldSplash = false;
|
|
level.sentrySettings[ "sam_turret" ].voDestroyed = "sam_destroyed";
|
|
|
|
level._effect[ "sentry_overheat_mp" ] = loadfx( "vfx/distortion/sentrygun_overheat" );
|
|
level._effect[ "sentry_explode_mp" ] = loadfx( "vfx/explosion/vehicle_pdrone_explosion" );
|
|
level._effect[ "sentry_smoke_mp" ] = loadfx( "vfx/smoke/vehicle_sentrygun_damaged_smoke" );
|
|
level._effect[ "sentry_stunned" ] = Loadfx( "vfx/sparks/direct_hack_stun" );
|
|
|
|
/#
|
|
SetDevDvarIfUninitialized( "scr_sam_timeout", 90.0 );
|
|
#/
|
|
}
|
|
|
|
/* ============================
|
|
Killstreak Functions
|
|
============================ */
|
|
|
|
tryUseAutoSentry( lifeId, modules )
|
|
{
|
|
result = self giveSentry( "sentry_minigun" );
|
|
if ( result )
|
|
{
|
|
self maps\mp\_matchdata::logKillstreakEvent( level.sentrySettings[ "sentry_minigun" ].streakName, self.origin );
|
|
}
|
|
|
|
return ( result );
|
|
}
|
|
|
|
|
|
tryUseSAM( lifeId, modules )
|
|
{
|
|
result = self giveSentry( "sam_turret" );
|
|
if ( result )
|
|
{
|
|
self maps\mp\_matchdata::logKillstreakEvent( level.sentrySettings[ "sam_turret" ].streakName, self.origin );
|
|
}
|
|
|
|
return ( result );
|
|
}
|
|
|
|
giveSentry( sentryType )
|
|
{
|
|
if ( ! self validateUseStreak() )
|
|
return false;
|
|
|
|
self.last_sentry = sentryType;
|
|
|
|
sentryGun = createSentryForPlayer( sentryType, self );
|
|
|
|
// returning from this streak activation seems to strip this?
|
|
// manually removing and restoring
|
|
self removePerks();
|
|
|
|
result = self setCarryingSentry( sentryGun, true );
|
|
|
|
self thread waitRestorePerks();
|
|
|
|
// we're done carrying for sure and sometimes this might not get reset
|
|
// this fixes a bug where you could be carrying and have it in a place where it won't plant, get killed, now you can't scroll through killstreaks
|
|
self.isCarrying = false;
|
|
|
|
// if we failed to place the sentry, it will have been deleted at this point
|
|
if ( IsDefined( sentryGun ) )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
|
|
/* ============================
|
|
Player Functions
|
|
============================ */
|
|
|
|
setCarryingSentry( sentryGun, allowCancel )
|
|
{
|
|
self endon ( "death" );
|
|
self endon ( "disconnect" );
|
|
|
|
assert( isReallyAlive( self ) );
|
|
|
|
sentryGun sentry_setCarried( self );
|
|
|
|
self _disableWeapon();
|
|
|
|
if ( !IsAI(self) ) // Bots handle these internally
|
|
{
|
|
self notifyOnPlayerCommand( "place_sentry", "+attack" );
|
|
self notifyOnPlayerCommand( "place_sentry", "+attack_akimbo_accessible" ); // support accessibility control scheme
|
|
self notifyOnPlayerCommand( "cancel_sentry", "+actionslot 4" );
|
|
if( !level.console )
|
|
{
|
|
self notifyOnPlayerCommand( "cancel_sentry", "+actionslot 5" );
|
|
self notifyOnPlayerCommand( "cancel_sentry", "+actionslot 6" );
|
|
self notifyOnPlayerCommand( "cancel_sentry", "+actionslot 7" );
|
|
self notifyOnPlayerCommand( "cancel_sentry", "+actionslot 8" );
|
|
}
|
|
}
|
|
|
|
for ( ;; )
|
|
{
|
|
result = waittill_any_return( "place_sentry", "cancel_sentry", "force_cancel_placement" );
|
|
|
|
if ( result == "cancel_sentry" || result == "force_cancel_placement" )
|
|
{
|
|
if ( !allowCancel && result == "cancel_sentry" )
|
|
continue;
|
|
|
|
// pc doesn't need to do this
|
|
// NOTE: this actually might not be needed anymore because we figured out code was taking the weapon because it didn't have any ammo and the weapon was set up as clip only
|
|
if( level.console )
|
|
{
|
|
// failsafe because something takes away the killstreak weapon on occasions where you have them stacked in the gimme slot
|
|
// for example, if you stack uav, sam turret, emp and then use the emp, then pull out the sam turret, the list item weapon gets taken away before you plant it
|
|
// so to "fix" this, if the user cancels then we give the weapon back to them only if the selected killstreak is the same and the item list is zero
|
|
// this is done for anything you can pull out and plant (ims, sentry, sam turret, remote turret, remote tank)
|
|
killstreakWeapon = getKillstreakWeapon( level.sentrySettings[ sentryGun.sentryType ].streakName );
|
|
if( IsDefined( self.killstreakIndexWeapon ) &&
|
|
killstreakWeapon == getKillstreakWeapon( self.pers["killstreaks"][self.killstreakIndexWeapon].streakName ) &&
|
|
!( self GetWeaponsListItems() ).size )
|
|
{
|
|
self _giveWeapon( killstreakWeapon, 0 );
|
|
self _setActionSlot( 4, "weapon", killstreakWeapon );
|
|
}
|
|
}
|
|
|
|
sentryGun sentry_setCancelled();
|
|
self _enableWeapon();
|
|
return false;
|
|
}
|
|
|
|
if ( !sentryGun.canBePlaced )
|
|
continue;
|
|
|
|
sentryGun sentry_setPlaced();
|
|
self _enableWeapon();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
removeWeapons()
|
|
{
|
|
if ( self HasWeapon( "riotshield_mp" ) )
|
|
{
|
|
self.restoreWeapon = "riotshield_mp";
|
|
self takeWeapon( "riotshield_mp" );
|
|
}
|
|
}
|
|
|
|
removePerks()
|
|
{
|
|
if ( self _hasPerk( "specialty_explosivebullets" ) )
|
|
{
|
|
self.restorePerk = "specialty_explosivebullets";
|
|
self _unsetPerk( "specialty_explosivebullets" );
|
|
}
|
|
}
|
|
|
|
restoreWeapons()
|
|
{
|
|
if ( IsDefined( self.restoreWeapon ) )
|
|
{
|
|
self _giveWeapon( self.restoreWeapon );
|
|
self.restoreWeapon = undefined;
|
|
}
|
|
}
|
|
|
|
restorePerks()
|
|
{
|
|
if ( IsDefined( self.restorePerk ) )
|
|
{
|
|
self givePerk( self.restorePerk, false );
|
|
self.restorePerk = undefined;
|
|
}
|
|
}
|
|
|
|
waitRestorePerks()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
wait( 0.05 );
|
|
self restorePerks();
|
|
}
|
|
|
|
/* ============================
|
|
Sentry Functions
|
|
============================ */
|
|
|
|
createSentryForPlayer( sentryType, owner )
|
|
{
|
|
assertEx( IsDefined( owner ), "createSentryForPlayer() called without owner specified" );
|
|
|
|
sentryGun = spawnTurret( "misc_turret", owner.origin, level.sentrySettings[ sentryType ].weaponInfo );
|
|
sentryGun.angles = owner.angles;
|
|
|
|
//sentryGun ThermalDrawEnable();
|
|
sentryGun sentry_initSentry( sentryType, owner );
|
|
|
|
return ( sentryGun );
|
|
}
|
|
|
|
|
|
sentry_initSentry( sentryType, owner ) // self == sentry, turret, sam
|
|
{
|
|
self.sentryType = sentryType;
|
|
self.canBePlaced = true;
|
|
|
|
self setModel( level.sentrySettings[ self.sentryType ].modelBase );
|
|
self.shouldSplash = true; // we only want to splash on the first placement
|
|
|
|
self setCanDamage( true );
|
|
switch( sentryType )
|
|
{
|
|
case "sam_turret":
|
|
self makeTurretInoperable();
|
|
self SetLeftArc( 180 );
|
|
self SetRightArc( 180 );
|
|
self SetTopArc( 80 );
|
|
self SetDefaultDropPitch( -89.0 ); // setting this mainly prevents Turret_RestoreDefaultDropPitch() from running
|
|
self.laser_on = false;
|
|
|
|
// needs a kill cam ent
|
|
killCamEnt = Spawn( "script_model", self GetTagOrigin( "tag_laser" ) );
|
|
killCamEnt LinkTo( self );
|
|
self.killCamEnt = killCamEnt;
|
|
self.killCamEnt SetScriptMoverKillCam( "explosive" );
|
|
break;
|
|
default:
|
|
self makeTurretInoperable();
|
|
self SetDefaultDropPitch( -89.0 ); // setting this mainly prevents Turret_RestoreDefaultDropPitch() from running
|
|
break;
|
|
}
|
|
|
|
self setTurretModeChangeWait( true );
|
|
// self setConvergenceTime( .25, "pitch" );
|
|
// self setConvergenceTime( .25, "yaw" );
|
|
self sentry_setInactive();
|
|
|
|
self sentry_setOwner( owner );
|
|
self thread sentry_handleDeath();
|
|
self thread sentry_timeOut();
|
|
|
|
switch( sentryType )
|
|
{
|
|
case "sam_turret":
|
|
self thread sentry_handleUse();
|
|
self thread sentry_beepSounds();
|
|
break;
|
|
default:
|
|
self thread sentry_handleUse();
|
|
self thread sentry_attackTargets();
|
|
self thread sentry_beepSounds();
|
|
break;
|
|
}
|
|
}
|
|
|
|
|
|
sentry_watchDisabled()
|
|
{
|
|
self endon( "carried" );
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
while( true )
|
|
{
|
|
// this handles any flash or concussion damage
|
|
self waittill( "emp_damage", attacker, duration );
|
|
|
|
PlayFXOnTag( getfx( "sentry_stunned" ), self, "tag_aim" );
|
|
|
|
self SetDefaultDropPitch( 40 );
|
|
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
|
|
wait( duration );
|
|
|
|
StopFXOnTag( getfx( "sentry_stunned" ), self, "tag_aim" );
|
|
|
|
self SetDefaultDropPitch( -89.0 );
|
|
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
|
}
|
|
}
|
|
|
|
sentry_directHacked()
|
|
{
|
|
self endon ( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
self.directHackDuration = 0.25;
|
|
|
|
if(isDefined( self.directHackEndTime ) && gettime() < self.directHackEndTime)
|
|
{
|
|
self.directHackEndTime = getTime() + (self.directHackDuration * 1000);
|
|
return;
|
|
}
|
|
|
|
PlayFXOnTag( getfx( "sentry_stunned" ), self, "tag_aim" );
|
|
self.directHackEndTime = getTime() + (self.directHackDuration * 1000);
|
|
self SetDefaultDropPitch( 40 );
|
|
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
|
|
while(1)
|
|
{
|
|
if(getTime() > self.directHackEndTime )
|
|
{
|
|
break;
|
|
}
|
|
wait(0.05);
|
|
}
|
|
|
|
self SetDefaultDropPitch( -89.0 );
|
|
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
|
|
|
StopFXOnTag( getfx( "sentry_stunned" ), self, "tag_aim" );
|
|
}
|
|
|
|
sentry_handleDeath()
|
|
{
|
|
self waittill ( "death" );
|
|
|
|
// this handles cases of deletion
|
|
if ( !IsDefined( self ) )
|
|
return;
|
|
|
|
self setModel( level.sentrySettings[ self.sentryType ].modelDestroyed );
|
|
|
|
self sentry_setInactive();
|
|
self SetDefaultDropPitch( 40 );
|
|
self SetSentryOwner( undefined );
|
|
self SetTurretMinimapVisible( false );
|
|
|
|
if( IsDefined( self.ownerTrigger ) )
|
|
self.ownerTrigger delete();
|
|
|
|
self playSound( "sentry_explode" );
|
|
|
|
if ( IsDefined( self.inUseBy ) )
|
|
{
|
|
playFxOnTag( getFx( "sentry_explode_mp" ), self, "tag_origin" );
|
|
playFxOnTag( getFx( "sentry_smoke_mp" ), self, "tag_aim" );
|
|
|
|
self.inUseBy.turret_overheat_bar destroyElem();
|
|
self.inUseBy restorePerks();
|
|
self.inUseBy restoreWeapons();
|
|
|
|
self notify( "deleting" );
|
|
wait ( 1.0 );
|
|
StopFXOnTag( getFx( "sentry_explode_mp" ), self, "tag_origin" );
|
|
StopFXOnTag( getFx( "sentry_smoke_mp" ), self, "tag_aim" );
|
|
}
|
|
else
|
|
{
|
|
playFxOnTag( getFx( "sentry_explode_mp" ), self, "tag_aim" );
|
|
wait ( 1.5 );
|
|
self playSound( "sentry_explode_smoke" );
|
|
for ( smokeTime = 8; smokeTime > 0; smokeTime -= 0.4 )
|
|
{
|
|
playFxOnTag( getFx( "sentry_smoke_mp" ), self, "tag_aim" );
|
|
wait ( 0.4 );
|
|
}
|
|
self notify( "deleting" );
|
|
}
|
|
|
|
if( IsDefined( self.killCamEnt ) )
|
|
self.killCamEnt delete();
|
|
|
|
self delete();
|
|
}
|
|
|
|
|
|
sentry_handleUse()
|
|
{
|
|
self endon ( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill ( "trigger", player );
|
|
|
|
assert( player == self.owner );
|
|
assert( !IsDefined( self.carriedBy ) );
|
|
|
|
if ( !isReallyAlive( player ) )
|
|
continue;
|
|
|
|
if( self.sentryType == "sam_turret" )
|
|
self setMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
|
|
player setCarryingSentry( self, false );
|
|
}
|
|
}
|
|
|
|
turret_handlePickup( turret ) // self == owner (player)
|
|
{
|
|
self endon( "disconnect" );
|
|
level endon( "game_ended" );
|
|
turret endon( "death" );
|
|
|
|
if( !IsDefined( turret.ownerTrigger ) )
|
|
return;
|
|
|
|
buttonTime = 0;
|
|
for ( ;; )
|
|
{
|
|
if( IsAlive( self ) &&
|
|
self IsTouching( turret.ownerTrigger ) &&
|
|
!IsDefined( turret.inUseBy ) &&
|
|
!IsDefined( turret.carriedBy ) &&
|
|
self IsOnGround() )
|
|
{
|
|
if ( self UseButtonPressed() )
|
|
{
|
|
if( IsDefined( self.using_remote_turret ) && self.using_remote_turret )
|
|
continue;
|
|
|
|
buttonTime = 0;
|
|
while ( self UseButtonPressed() )
|
|
{
|
|
buttonTime += 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
|
|
println( "pressTime1: " + buttonTime );
|
|
if ( buttonTime >= 0.5 )
|
|
continue;
|
|
|
|
buttonTime = 0;
|
|
while ( !self UseButtonPressed() && buttonTime < 0.5 )
|
|
{
|
|
buttonTime += 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
|
|
println( "delayTime: " + buttonTime );
|
|
if ( buttonTime >= 0.5 )
|
|
continue;
|
|
|
|
if ( !isReallyAlive( self ) )
|
|
continue;
|
|
|
|
if( IsDefined( self.using_remote_turret ) && self.using_remote_turret )
|
|
continue;
|
|
|
|
turret setMode( level.sentrySettings[ turret.sentryType ].sentryModeOff );
|
|
self thread setCarryingSentry( turret, false );
|
|
turret.ownerTrigger delete();
|
|
return;
|
|
}
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
turret_handleUse() // self == turret
|
|
{
|
|
self notify ( "turret_handluse" );
|
|
self endon ( "turret_handleuse" );
|
|
self endon ( "deleting" );
|
|
level endon ( "game_ended" );
|
|
|
|
self.forceDisable = false;
|
|
colorStable = (1, 0.9, 0.7);
|
|
colorUnstable = (1, 0.65, 0);
|
|
colorOverheated = (1, 0.25, 0);
|
|
|
|
for( ;; )
|
|
{
|
|
self waittill( "trigger", player );
|
|
|
|
// exceptions
|
|
if( IsDefined( self.carriedBy ) )
|
|
continue;
|
|
if( IsDefined( self.inUseBy ) )
|
|
continue;
|
|
if( !isReallyAlive( player ) )
|
|
continue;
|
|
player removePerks();
|
|
player removeWeapons();
|
|
|
|
// ownership
|
|
self.inUseBy = player;
|
|
self setMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
self sentry_setOwner( player );
|
|
self setMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
|
player thread turret_shotMonitor( self );
|
|
|
|
// overheat bar
|
|
player.turret_overheat_bar = player createBar( colorStable, 100, 6 );
|
|
player.turret_overheat_bar setPoint("CENTER", "BOTTOM", 0, -70 );
|
|
player.turret_overheat_bar.alpha = 0.65;
|
|
player.turret_overheat_bar.bar.alpha = 0.65;
|
|
|
|
//lastHeatLevel = self.heatLevel;
|
|
//firing = false;
|
|
|
|
playingHeatFX = false;
|
|
|
|
for( ;; )
|
|
{
|
|
// exceptions
|
|
if ( !isReallyAlive( player ) )
|
|
{
|
|
self.inUseBy = undefined;
|
|
player.turret_overheat_bar destroyElem();
|
|
break;
|
|
}
|
|
if ( !player IsUsingTurret() )
|
|
{
|
|
self notify( "player_dismount" );
|
|
self.inUseBy = undefined;
|
|
player.turret_overheat_bar destroyElem();
|
|
player restorePerks();
|
|
player restoreWeapons();
|
|
self setHintString( level.sentrySettings[ self.sentryType ].hintString );
|
|
self setMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
self sentry_setOwner( self.originalOwner );
|
|
self setMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
|
break;
|
|
}
|
|
|
|
if ( self.heatLevel >= level.sentrySettings[ self.sentryType ].overheatTime )
|
|
barFrac = 1;
|
|
else
|
|
barFrac = self.heatLevel / level.sentrySettings[ self.sentryType ].overheatTime;
|
|
player.turret_overheat_bar updateBar( barFrac );
|
|
|
|
if ( self.forceDisable || self.overheated )
|
|
{
|
|
self TurretFireDisable();
|
|
player.turret_overheat_bar.bar.color = colorOverheated;
|
|
playingHeatFX = false;
|
|
}
|
|
else
|
|
{
|
|
player.turret_overheat_bar.bar.color = colorStable;
|
|
self TurretFireEnable();
|
|
playingHeatFX = false;
|
|
self notify( "not_overheated" );
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
self SetDefaultDropPitch( 0.0 );
|
|
}
|
|
}
|
|
|
|
sentry_handleOwnerDisconnect()
|
|
{
|
|
self endon ( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
self notify ( "sentry_handleOwner" );
|
|
self endon ( "sentry_handleOwner" );
|
|
|
|
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
|
|
|
|
self notify( "death" );
|
|
}
|
|
|
|
|
|
/* ============================
|
|
Sentry Utility Functions
|
|
============================ */
|
|
|
|
sentry_setOwner( owner )
|
|
{
|
|
assertEx( IsDefined( owner ), "sentry_setOwner() called without owner specified" );
|
|
assertEx( isPlayer( owner ), "sentry_setOwner() called on non-player entity type: " + owner.classname );
|
|
|
|
self.owner = owner;
|
|
|
|
self SetSentryOwner( self.owner );
|
|
self SetTurretMinimapVisible( true, self.sentryType );
|
|
|
|
if ( level.teamBased )
|
|
{
|
|
self.team = self.owner.team;
|
|
self setTurretTeam( self.team );
|
|
}
|
|
|
|
self thread sentry_handleOwnerDisconnect();
|
|
}
|
|
|
|
|
|
sentry_setPlaced()
|
|
{
|
|
self setModel( level.sentrySettings[ self.sentryType ].modelBase );
|
|
|
|
// failsafe check, for some reason this could be manual and setSentryCarried doesn't like that
|
|
if( self GetMode() == "manual" )
|
|
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
|
|
self setSentryCarrier( undefined );
|
|
self setCanDamage( true );
|
|
|
|
self sentry_makeSolid();
|
|
|
|
self.carriedBy forceUseHintOff();
|
|
self.carriedBy = undefined;
|
|
|
|
if( IsDefined( self.owner ) )
|
|
self.owner.isCarrying = false;
|
|
|
|
self sentry_setActive();
|
|
|
|
self playSound( "sentry_gun_plant" );
|
|
|
|
self notify ( "placed" );
|
|
}
|
|
|
|
|
|
sentry_setCancelled()
|
|
{
|
|
self.carriedBy forceUseHintOff();
|
|
if( IsDefined( self.owner ) )
|
|
self.owner.isCarrying = false;
|
|
|
|
self delete();
|
|
}
|
|
|
|
|
|
sentry_setCarried( carrier )
|
|
{
|
|
assert( isPlayer( carrier ) );
|
|
if( IsDefined( self.originalOwner ) )
|
|
assertEx( carrier == self.originalOwner, "sentry_setCarried() specified carrier does not own this sentry" );
|
|
else
|
|
assertEx( carrier == self.owner, "sentry_setCarried() specified carrier does not own this sentry" );
|
|
|
|
self setModel( level.sentrySettings[ self.sentryType ].modelPlacement );
|
|
|
|
self setSentryCarrier( carrier );
|
|
self setCanDamage( false );
|
|
self sentry_makeNotSolid();
|
|
|
|
self.carriedBy = carrier;
|
|
carrier.isCarrying = true;
|
|
|
|
carrier thread updateSentryPlacement( self );
|
|
|
|
self thread sentry_onCarrierDeath( carrier );
|
|
self thread sentry_onCarrierDisconnect( carrier );
|
|
self thread sentry_onCarrierChangedTeam( carrier );
|
|
self thread sentry_onGameEnded();
|
|
|
|
// setting the drop pitch here again because they could pick it up while it was stunned
|
|
self SetDefaultDropPitch( -89.0 );
|
|
|
|
self sentry_setInactive();
|
|
|
|
self notify ( "carried" );
|
|
}
|
|
|
|
updateSentryPlacement( sentryGun )
|
|
{
|
|
self endon ( "death" );
|
|
self endon ( "disconnect" );
|
|
level endon ( "game_ended" );
|
|
|
|
sentryGun endon ( "placed" );
|
|
sentryGun endon ( "death" );
|
|
|
|
sentryGun.canBePlaced = true;
|
|
lastCanPlaceSentry = -1; // force initial update
|
|
|
|
for( ;; )
|
|
{
|
|
placement = self canPlayerPlaceSentry( true, 22 );
|
|
|
|
sentryGun.origin = placement[ "origin" ];
|
|
sentryGun.angles = placement[ "angles" ];
|
|
sentryGun.canBePlaced = self isOnGround() && placement[ "result" ] && ( abs(sentryGun.origin[2]-self.origin[2]) < 30 );
|
|
|
|
if ( sentryGun.canBePlaced != lastCanPlaceSentry )
|
|
{
|
|
if ( sentryGun.canBePlaced )
|
|
{
|
|
sentryGun setModel( level.sentrySettings[ sentryGun.sentryType ].modelPlacement );
|
|
self ForceUseHintOn( &"SENTRY_PLACE" );
|
|
}
|
|
else
|
|
{
|
|
sentryGun setModel( level.sentrySettings[ sentryGun.sentryType ].modelPlacementFailed );
|
|
self ForceUseHintOn( &"SENTRY_CANNOT_PLACE" );
|
|
}
|
|
}
|
|
|
|
lastCanPlaceSentry = sentryGun.canBePlaced;
|
|
wait ( 0.05 );
|
|
}
|
|
}
|
|
|
|
sentry_onCarrierDeath( carrier )
|
|
{
|
|
self endon ( "placed" );
|
|
self endon ( "death" );
|
|
|
|
carrier waittill ( "death" );
|
|
|
|
if ( self.canBePlaced )
|
|
self sentry_setPlaced();
|
|
else
|
|
self delete();
|
|
}
|
|
|
|
|
|
sentry_onCarrierDisconnect( carrier )
|
|
{
|
|
self endon ( "placed" );
|
|
self endon ( "death" );
|
|
|
|
carrier waittill ( "disconnect" );
|
|
|
|
self delete();
|
|
}
|
|
|
|
sentry_onCarrierChangedTeam( carrier ) // self == sentry
|
|
{
|
|
self endon ( "placed" );
|
|
self endon ( "death" );
|
|
|
|
carrier waittill_any( "joined_team", "joined_spectators" );
|
|
|
|
self delete();
|
|
}
|
|
|
|
sentry_onGameEnded( carrier )
|
|
{
|
|
self endon ( "placed" );
|
|
self endon ( "death" );
|
|
|
|
level waittill ( "game_ended" );
|
|
|
|
self delete();
|
|
}
|
|
|
|
|
|
sentry_setActive()
|
|
{
|
|
self SetMode( level.sentrySettings[ self.sentryType ].sentryModeOn );
|
|
self setCursorHint( "HINT_NOICON" );
|
|
self setHintString( level.sentrySettings[ self.sentryType ].hintString );
|
|
|
|
if( level.sentrySettings[ self.sentryType ].headIcon )
|
|
{
|
|
if ( level.teamBased )
|
|
self maps\mp\_entityheadicons::setTeamHeadIcon( self.team, (0,0,65) );
|
|
else
|
|
self maps\mp\_entityheadicons::setPlayerHeadIcon( self.owner, (0,0,65) );
|
|
}
|
|
|
|
self makeUsable();
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
entNum = self GetEntityNumber();
|
|
self addToTurretList( entNum );
|
|
|
|
if( player == self.owner )
|
|
self enablePlayerUse( player );
|
|
else
|
|
self disablePlayerUse( player );
|
|
}
|
|
|
|
if( self.shouldSplash )
|
|
{
|
|
level thread teamPlayerCardSplash( level.sentrySettings[ self.sentryType ].teamSplash, self.owner, self.owner.team );
|
|
self.shouldSplash = false;
|
|
}
|
|
|
|
if( self.sentryType == "sam_turret" )
|
|
{
|
|
self thread sam_attackTargets();
|
|
}
|
|
|
|
self thread sentry_watchDisabled();
|
|
}
|
|
|
|
|
|
sentry_setInactive()
|
|
{
|
|
self setMode( level.sentrySettings[ self.sentryType ].sentryModeOff );
|
|
self makeUnusable();
|
|
self FreeEntitySentient();
|
|
|
|
entNum = self GetEntityNumber();
|
|
|
|
self removeFromTurretList( entNum );
|
|
|
|
if ( level.teamBased )
|
|
self maps\mp\_entityheadicons::setTeamHeadIcon( "none", ( 0, 0, 0 ) );
|
|
else if ( IsDefined( self.owner ) )
|
|
self maps\mp\_entityheadicons::setPlayerHeadIcon( undefined, ( 0, 0, 0 ) );
|
|
}
|
|
|
|
|
|
sentry_makeSolid()
|
|
{
|
|
self makeTurretSolid();
|
|
}
|
|
|
|
|
|
sentry_makeNotSolid()
|
|
{
|
|
self setContents( 0 );
|
|
}
|
|
|
|
|
|
isFriendlyToSentry( sentryGun )
|
|
{
|
|
if ( level.teamBased && self.team == sentryGun.team )
|
|
return true;
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
addToTurretList( entNum )
|
|
{
|
|
level.turrets[entNum] = self;
|
|
}
|
|
|
|
|
|
removeFromTurretList( entNum )
|
|
{
|
|
level.turrets[entNum] = undefined;
|
|
}
|
|
|
|
/* ============================
|
|
Sentry Logic Functions
|
|
============================ */
|
|
|
|
sentry_attackTargets()
|
|
{
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
self.momentum = 0;
|
|
self.heatLevel = 0;
|
|
self.overheated = false;
|
|
|
|
self thread sentry_heatMonitor();
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill_either( "turretstatechange", "cooled" );
|
|
|
|
if ( self isFiringTurret() )
|
|
{
|
|
self thread sentry_burstFireStart();
|
|
}
|
|
else
|
|
{
|
|
self sentry_spinDown();
|
|
self thread sentry_burstFireStop();
|
|
}
|
|
}
|
|
}
|
|
|
|
sentry_timeOut()
|
|
{
|
|
self endon( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
lifeSpan = level.sentrySettings[ self.sentryType ].timeOut;
|
|
/#
|
|
if( self.sentryType == "sam_turret" )
|
|
lifeSpan = GetDvarInt( "scr_sam_timeout", 90 );
|
|
#/
|
|
|
|
while ( lifeSpan )
|
|
{
|
|
wait ( 1.0 );
|
|
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
|
|
|
if ( !IsDefined( self.carriedBy ) )
|
|
lifeSpan = max( 0, lifeSpan - 1.0 );
|
|
}
|
|
|
|
if ( IsDefined( self.owner ) )
|
|
{
|
|
if ( self.sentryType == "sam_turret" )
|
|
self.owner thread leaderDialogOnPlayer( "sam_gone" );
|
|
else
|
|
self.owner thread leaderDialogOnPlayer( "sentry_gone" );
|
|
}
|
|
self notify ( "death" );
|
|
}
|
|
|
|
sentry_targetLockSound()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
self playSound( "sentry_gun_beep" );
|
|
wait ( 0.1 );
|
|
self playSound( "sentry_gun_beep" );
|
|
wait ( 0.1 );
|
|
self playSound( "sentry_gun_beep" );
|
|
}
|
|
|
|
sentry_spinUp()
|
|
{
|
|
self thread sentry_targetLockSound();
|
|
|
|
while ( self.momentum < level.sentrySettings[ self.sentryType ].spinupTime )
|
|
{
|
|
self.momentum += 0.1;
|
|
|
|
wait ( 0.1 );
|
|
}
|
|
}
|
|
|
|
sentry_spinDown()
|
|
{
|
|
self.momentum = 0;
|
|
}
|
|
|
|
|
|
sentry_burstFireStart()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "stop_shooting" );
|
|
|
|
level endon( "game_ended" );
|
|
|
|
self sentry_spinUp();
|
|
|
|
fireTime = weaponFireTime( level.sentrySettings[ self.sentryType ].weaponInfo );
|
|
minShots = level.sentrySettings[ self.sentryType ].burstMin;
|
|
maxShots = level.sentrySettings[ self.sentryType ].burstMax;
|
|
minPause = level.sentrySettings[ self.sentryType ].pauseMin;
|
|
maxPause = level.sentrySettings[ self.sentryType ].pauseMax;
|
|
|
|
for ( ;; )
|
|
{
|
|
numShots = randomIntRange( minShots, maxShots + 1 );
|
|
|
|
for ( i = 0; i < numShots && !self.overheated; i++ )
|
|
{
|
|
self shootTurret();
|
|
self.heatLevel += fireTime;
|
|
wait ( fireTime );
|
|
}
|
|
|
|
wait ( randomFloatRange( minPause, maxPause ) );
|
|
}
|
|
}
|
|
|
|
|
|
sentry_burstFireStop()
|
|
{
|
|
self notify( "stop_shooting" );
|
|
}
|
|
|
|
|
|
turret_shotMonitor( turret )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
level endon ( "game_ended" );
|
|
turret endon( "death" );
|
|
turret endon( "player_dismount" );
|
|
|
|
fireTime = weaponFireTime( level.sentrySettings[ turret.sentryType ].weaponInfo );
|
|
for ( ;; )
|
|
{
|
|
turret waittill ( "turret_fire" );
|
|
turret.heatLevel += fireTime;
|
|
// need to reset the heat wait time so the overheat bar knows that we've fired again before cooldown
|
|
turret.cooldownWaitTime = fireTime;
|
|
}
|
|
}
|
|
|
|
// TODO: think about using the turret_heatMonitor and turret_coolMonitor instead of this because this has a small flaw where it waits twice and gets out of sync with the firing
|
|
sentry_heatMonitor()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
fireTime = weaponFireTime( level.sentrySettings[ self.sentryType ].weaponInfo );
|
|
|
|
lastHeatLevel = 0;
|
|
lastFxTime = 0;
|
|
|
|
overheatTime = level.sentrySettings[ self.sentryType ].overheatTime;
|
|
overheatCoolDown = level.sentrySettings[ self.sentryType ].cooldownTime;
|
|
|
|
for ( ;; )
|
|
{
|
|
if ( self.heatLevel != lastHeatLevel )
|
|
wait ( fireTime );
|
|
else
|
|
self.heatLevel = max( 0, self.heatLevel - 0.05 );
|
|
|
|
if ( self.heatLevel > overheatTime )
|
|
{
|
|
self.overheated = true;
|
|
self thread PlayHeatFX();
|
|
|
|
while ( self.heatLevel )
|
|
{
|
|
self.heatLevel = max( 0, self.heatLevel - overheatCoolDown );
|
|
wait ( 0.1 );
|
|
}
|
|
|
|
self.overheated = false;
|
|
self notify( "not_overheated" );
|
|
}
|
|
|
|
lastHeatLevel = self.heatLevel;
|
|
wait ( 0.05 );
|
|
}
|
|
}
|
|
|
|
turret_heatMonitor()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
overheatTime = level.sentrySettings[ self.sentryType ].overheatTime;
|
|
|
|
while( true )
|
|
{
|
|
if ( self.heatLevel > overheatTime )
|
|
{
|
|
self.overheated = true;
|
|
self thread PlayHeatFX();
|
|
|
|
while ( self.heatLevel )
|
|
{
|
|
wait ( 0.1 );
|
|
}
|
|
|
|
self.overheated = false;
|
|
self notify( "not_overheated" );
|
|
}
|
|
|
|
wait ( 0.05 );
|
|
}
|
|
}
|
|
|
|
turret_coolMonitor()
|
|
{
|
|
self endon ( "death" );
|
|
|
|
while( true )
|
|
{
|
|
if( self.heatLevel > 0 )
|
|
{
|
|
if( self.cooldownWaitTime <= 0 )
|
|
{
|
|
self.heatLevel = max( 0, self.heatLevel - 0.05 );
|
|
}
|
|
else
|
|
{
|
|
self.cooldownWaitTime = max( 0, self.cooldownWaitTime - 0.05 );
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
|
|
playHeatFX()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "not_overheated" );
|
|
level endon ( "game_ended" );
|
|
|
|
self notify( "playing_heat_fx" );
|
|
self endon( "playing_heat_fx" );
|
|
|
|
for( ;; )
|
|
{
|
|
playFxOnTag( getFx( "sentry_overheat_mp" ), self, "tag_flash" );
|
|
|
|
wait( level.sentrySettings[ self.sentryType ].fxTime );
|
|
}
|
|
}
|
|
|
|
playSmokeFX()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "not_overheated" );
|
|
level endon ( "game_ended" );
|
|
|
|
for( ;; )
|
|
{
|
|
playFxOnTag( getFx( "sentry_smoke_mp" ), self, "tag_aim" );
|
|
wait ( 0.4 );
|
|
}
|
|
}
|
|
|
|
sentry_beepSounds()
|
|
{
|
|
self endon( "death" );
|
|
level endon ( "game_ended" );
|
|
|
|
for ( ;; )
|
|
{
|
|
wait ( 3.0 );
|
|
|
|
if ( !IsDefined( self.carriedBy ) )
|
|
self playSound( "sentry_gun_beep" );
|
|
}
|
|
}
|
|
|
|
/* ============================
|
|
SAM Logic Functions
|
|
============================ */
|
|
|
|
sam_attackTargets() // self == sam
|
|
{
|
|
self endon( "carried" );
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
self.samTargetEnt = undefined;
|
|
self.samMissileGroups = [];
|
|
|
|
while( true )
|
|
{
|
|
self.samTargetEnt = sam_acquireTarget();
|
|
self sam_fireOnTarget();
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
sam_acquireTarget() // self == sam turret
|
|
{
|
|
eyeLine = self GetTagOrigin( "tag_laser" );
|
|
if( !IsDefined( self.samTargetEnt ) )
|
|
{
|
|
// scan the air for targets
|
|
if( level.teambased )
|
|
{
|
|
entityList = [];
|
|
if( level.multiTeamBased )
|
|
{
|
|
foreach ( teamName in level.teamNameList )
|
|
{
|
|
if ( teamName != self.team )
|
|
{
|
|
foreach( model in level.uavmodels[teamName] )
|
|
{
|
|
entityList[entityList.size] = model;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (IsDefined(self.team)) // Fix for edge case when player is switching teams and self.team is undefined.
|
|
{
|
|
entityList = level.UAVModels[level.otherTeam[ self.team ]];
|
|
}
|
|
|
|
}
|
|
|
|
// uavs and cuavs
|
|
foreach( uav in entityList )
|
|
{
|
|
if( ( IsDefined( uav.isLeaving ) && uav.isLeaving ) )
|
|
continue;
|
|
|
|
if( isDefined( uav.orbit ) && uav.orbit )
|
|
continue;
|
|
|
|
if( SightTracePassed( eyeLine, uav.origin, false, self ) )
|
|
{
|
|
return uav;
|
|
}
|
|
}
|
|
|
|
// littlebirds
|
|
foreach( lb in level.littleBirds )
|
|
{
|
|
if( IsDefined( lb.team ) && lb.team == self.team )
|
|
continue;
|
|
|
|
if( SightTracePassed( eyeLine, lb.origin, false, self ) )
|
|
{
|
|
return lb;
|
|
}
|
|
}
|
|
|
|
// helicopters
|
|
foreach( heli in level.helis )
|
|
{
|
|
if( IsDefined( heli.team ) && heli.team == self.team )
|
|
continue;
|
|
if( isDefined( heli.cloakstate ) && heli.cloakstate < 1 )
|
|
continue;
|
|
if( SightTracePassed( eyeLine, heli.origin, false, self, heli ) )//HACK: adding heli as ignore entity for the warbird
|
|
{
|
|
return heli;
|
|
}
|
|
}
|
|
|
|
// orbital support
|
|
if( level.orbitalsupportInUse && IsDefined( level.orbitalsupport_planemodel.owner ) && level.orbitalsupport_planemodel.owner.team != self.team )
|
|
{
|
|
if( SightTracePassed( eyeLine, level.orbitalsupport_planemodel.origin, false, self ) )
|
|
{
|
|
return level.orbitalsupport_planemodel;
|
|
}
|
|
}
|
|
|
|
// orbital Care package - Resupply Pod
|
|
if(IsDefined(level._orbital_care_pod))
|
|
{
|
|
foreach(pod in level._orbital_care_pod)
|
|
{
|
|
if(IsDefined(pod.podrocket) && pod.owner.team != self.team)
|
|
{
|
|
if( SightTracePassed( eyeLine, pod.podrocket.origin, false, self ) )
|
|
{
|
|
return pod.podrocket;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//bombing run
|
|
foreach( plane in level.planes )
|
|
{
|
|
if( IsDefined( plane.team ) && plane.team == self.team )
|
|
continue;
|
|
if( SightTracePassed( eyeLine, plane.origin, false, self ) )
|
|
{
|
|
return plane;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// uavs and cuavs
|
|
foreach( uav in level.UAVModels )
|
|
{
|
|
if( IsDefined( uav.isLeaving ) && uav.isLeaving )
|
|
continue;
|
|
|
|
if( IsDefined( uav.owner ) && IsDefined( self.owner ) && uav.owner == self.owner )
|
|
continue;
|
|
|
|
if( isDefined( uav.orbit ) && uav.orbit )
|
|
continue;
|
|
|
|
if( SightTracePassed( eyeLine, uav.origin, false, self ) )
|
|
{
|
|
return uav;
|
|
}
|
|
}
|
|
|
|
// littlebirds
|
|
foreach( lb in level.littleBirds )
|
|
{
|
|
if( IsDefined( lb.owner ) && IsDefined( self.owner ) && lb.owner == self.owner )
|
|
continue;
|
|
|
|
if( SightTracePassed( eyeLine, lb.origin, false, self ) )
|
|
{
|
|
return lb;
|
|
}
|
|
}
|
|
|
|
// helicopters
|
|
foreach( heli in level.helis )
|
|
{
|
|
if( IsDefined( heli.owner ) && IsDefined( self.owner ) && heli.owner == self.owner )
|
|
continue;
|
|
if( isDefined( heli.cloakstate ) && heli.cloakstate < 1 )
|
|
continue;
|
|
if( SightTracePassed( eyeLine, heli.origin, false, self, heli ) )//HACK: adding ignore entity for the wardbird
|
|
{
|
|
return heli;
|
|
}
|
|
}
|
|
|
|
|
|
// orbital support
|
|
if( level.orbitalsupportInUse && IsDefined( level.orbitalsupport_planemodel.owner ) && IsDefined( self.owner ) && level.orbitalsupport_planemodel.owner != self.owner )
|
|
{
|
|
if( SightTracePassed( eyeLine, level.orbitalsupport_planemodel.owner.origin, false, self ) )
|
|
{
|
|
return level.orbitalsupport_planemodel.owner;
|
|
}
|
|
}
|
|
|
|
// orbital Care package - Resupply Pod
|
|
if(IsDefined(level._orbital_care_pod))
|
|
{
|
|
foreach(pod in level._orbital_care_pod)
|
|
{
|
|
if(IsDefined(pod.podrocket) && pod.owner != self )
|
|
{
|
|
if( SightTracePassed( eyeLine, pod.podrocket.origin, false, self ) )
|
|
{
|
|
return pod.podrocket;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//bombing run
|
|
foreach( plane in level.planes )
|
|
{
|
|
if( IsDefined( plane.team ) && plane.owner == self.owner )
|
|
continue;
|
|
if( SightTracePassed( eyeLine, plane.origin, false, self ) )
|
|
{
|
|
return plane;
|
|
}
|
|
}
|
|
}
|
|
|
|
self ClearTargetEntity();
|
|
return undefined;
|
|
}
|
|
else
|
|
{
|
|
if( !SightTracePassed( eyeLine, self.samTargetEnt.origin, false, self, self.samTargetEnt ) ) //HACK: adding in self.samTargetEnt as ignore entity for the warbird
|
|
{
|
|
self ClearTargetEntity();
|
|
return undefined;
|
|
}
|
|
|
|
// since it is defined and still in sight return it
|
|
return self.samTargetEnt;
|
|
}
|
|
}
|
|
|
|
sam_fireOnTarget() // self == sam turret
|
|
{
|
|
// locked on to target, turn on laser and fire
|
|
if( IsDefined( self.samTargetEnt ) )
|
|
{
|
|
// because we hide the ac130 and don't really delete it
|
|
if( (self.samTargetEnt == level.ac130.planemodel && !IsDefined( level.ac130player )) || ( isDefined( level.orbitalsupport_planeModel ) && self.samTargetEnt == level.orbitalsupport_planeModel && !IsDefined( level.orbitalsupport_player )) )
|
|
{
|
|
self.samTargetEnt = undefined;
|
|
self ClearTargetEntity();
|
|
return;
|
|
}
|
|
|
|
self SetTargetEntity( self.samTargetEnt );
|
|
self waittill( "turret_on_target" );
|
|
if( !IsDefined( self.samTargetEnt ) )
|
|
return;
|
|
|
|
// turn on the laser, also watch for if the target is crashing or leaving
|
|
if( !self.laser_on )
|
|
{
|
|
self thread sam_watchLaser();
|
|
self thread sam_watchCrashing();
|
|
self thread sam_watchLeaving();
|
|
self thread sam_watchLineOfSight();
|
|
}
|
|
|
|
wait( 2.0 );
|
|
|
|
if( !IsDefined( self.samTargetEnt ) )
|
|
return;
|
|
|
|
// because we hide the ac130 and don't really delete it
|
|
if( self.samTargetEnt == level.ac130.planemodel && !IsDefined( level.ac130player ) )
|
|
{
|
|
self.samTargetEnt = undefined;
|
|
self ClearTargetEntity();
|
|
return;
|
|
}
|
|
|
|
rocketOffsets = [];
|
|
rocketOffsets[ 0 ] = self GetTagOrigin( "tag_le_missile1" );
|
|
rocketOffsets[ 1 ] = self GetTagOrigin( "tag_le_missile2" );
|
|
rocketOffsets[ 2 ] = self GetTagOrigin( "tag_ri_missile1" );
|
|
rocketOffsets[ 3 ] = self GetTagOrigin( "tag_ri_missile2" );
|
|
|
|
missileGroup = self.samMissileGroups.size;
|
|
for( i = 0; i < 4; i++ )
|
|
{
|
|
if( !IsDefined( self.samTargetEnt ) )
|
|
return;
|
|
|
|
if( IsDefined( self.carriedBy ) )
|
|
return;
|
|
|
|
// need to shoot the turret so it'll show up on the mini-map as it shoots, it will shoot blanks
|
|
self ShootTurret();
|
|
|
|
rocket = MagicBullet( "sam_projectile_mp", rocketOffsets[ i ], self.samTargetEnt.origin, self.owner );
|
|
rocket Missile_SetTargetEnt( self.samTargetEnt );
|
|
rocket Missile_SetFlightmodeDirect();
|
|
rocket.samTurret = self;
|
|
|
|
rocket.samMissileGroup = missileGroup;
|
|
self.samMissileGroups[ missileGroup ][ i ] = rocket;
|
|
|
|
level notify( "sam_missile_fired", self.owner, rocket, self.samTargetEnt );
|
|
|
|
// get out before the wait on the last shot, that way the notify goes right after the last shot
|
|
if( i == 3 )
|
|
break;
|
|
|
|
wait( 0.25 );
|
|
}
|
|
level notify( "sam_fired", self.owner, self.samMissileGroups[ missileGroup ], self.samTargetEnt );
|
|
|
|
wait( 3.0 );
|
|
}
|
|
}
|
|
|
|
sam_watchLineOfSight() // self == sam turret
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "death" );
|
|
self endon( "fakedeath" );
|
|
|
|
while( IsDefined( self.samTargetEnt ) && IsDefined( self GetTurretTarget( true ) ) && self GetTurretTarget( true ) == self.samTargetEnt )
|
|
{
|
|
eyeLine = self GetTagOrigin( "tag_laser" );
|
|
if( !SightTracePassed( eyeLine, self.samTargetEnt.origin, false, self, self.samTargetEnt ) )
|
|
{
|
|
self ClearTargetEntity();
|
|
self.samTargetEnt = undefined;
|
|
break;
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
sam_watchLaser() // self == sam turret
|
|
{
|
|
self endon( "death" );
|
|
|
|
self LaserOn();
|
|
self.laser_on = true;
|
|
self notify( "laser_on" );
|
|
|
|
while( IsDefined( self.samTargetEnt ) && IsDefined( self GetTurretTarget( true ) ) && self GetTurretTarget( true ) == self.samTargetEnt )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
self LaserOff();
|
|
self.laser_on = false;
|
|
self notify( "laser_off" );
|
|
}
|
|
|
|
sam_watchCrashing() // self == sam turret
|
|
{
|
|
self endon( "death" );
|
|
self endon( "fakedeath" );
|
|
self.samTargetEnt endon( "death" );
|
|
|
|
if( !IsDefined( self.samTargetEnt.heliType ) )
|
|
return;
|
|
|
|
self.samTargetEnt waittill( "crashing" );
|
|
self ClearTargetEntity();
|
|
self.samTargetEnt = undefined;
|
|
}
|
|
|
|
sam_watchLeaving() // self == sam turret
|
|
{
|
|
self endon( "death" );
|
|
self endon( "fakedeath" );
|
|
self.samTargetEnt endon( "death" );
|
|
|
|
if( !IsDefined( self.samTargetEnt.model ) )
|
|
return;
|
|
|
|
if( self.samTargetEnt.model == "vehicle_uav_static_mp" )
|
|
{
|
|
self.samTargetEnt waittill( "leaving" );
|
|
self ClearTargetEntity();
|
|
self.samTargetEnt = undefined;
|
|
}
|
|
}
|
|
/* ============================
|
|
END SAM Logic Functions
|
|
============================ */
|
|
|
|
|