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

1796 lines
45 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;
CONST_JUGG_TYPE = "juggernaut_predator";
CONST_AIRDROP_TYPE = "airdrop_" + CONST_JUGG_TYPE;
CONST_JUGG_CRATE_WEIGHT = 85;
CONST_JUGG_CRATE_STRING = &"MP_JUGG_PREDATOR_PICKUP";
CONST_CANNON_WEAPON = "iw6_predatorcannon_mp";
CONST_PREDATOR_COMPUTER = "iw6_predatorwristcpu_mp";
CONST_PREDATOR_SELF_DESTRUCT = "iw6_predatorsuicide_mp";
CONST_CLOAK_ON_DELAY = 500; // in ms. How long after turning on before it can turn off
CONST_CLOAK_OFF_DELAY = 1000; // in ms. How long after turning off before you can turn it on
// in ms
CONST_CLOAK_PENALTY_SPRINT = 1250;
CONST_CLOAK_PENALTY_CANNON = 3000;
CONST_CLOAK_PENALTY_MELEE = 1000;
CONST_CLOAK_PENALTY_FORCED_OFF = 1000;
CONST_DEBUG_DVAR = "scr_pred_nocloak";
CONST_DEBUG_LASTSTAND_DVAR = "scr_pred_laststand";
// cloaking is based on movement
CONST_MOVEMENT_CHECK_FREQ = 0.1;
CONST_MOVEMENT_THRESHOLD = 135 * 135; // about the speed of crouch walk
CANNON_LOCK_TIME = 0.375; // in secs
CANNON_RELOAD_TIME = 8; // in secs
CANNON_FOV = 65;
CANNON_TARGET_RADIUS_READY = 80;
CANNON_TARGET_RADIUS_LOCKED = 150;
CANNON_TARGET_OFFSET = (0, 0, 32);
CANNON_TARGET_BONE = "j_spineupper";
CANNON_MAX_RANGE_SQ = 720 * 720;
CANNON_STATE_RELOADING = -1;
CANNON_STATE_READY = 0;
CANNON_STATE_LOCKING = 1;
CANNON_STATE_LOCKED = 2;
CONST_VISIONSET_TRANSITION_TIME = 0.25;
CONST_OUTLINE_COLOR_ENEMY = "red"; // Predator thermal effect requires "red" for enemies.
CONST_OUTLINE_COLOR_FRIENDLY = "cyan"; // Predator thermal effect requires "cyan" for friendlies.
// in seconds
CONST_VOICE_CLICK_MIN = 10;
CONST_VOICE_CLICK_MAX = 15;
CONST_TENSION_MUSIC_FREQ = 50;
// effects state timers
CONST_EYE_FLASH_MIN_TIME = 500; // in ms !!!! INCREASE THIS LATER
CONST_PREDATOR_SUICIDE_RADIUS = 800;
CONST_LAST_STAND_TIME = 8; // in s. Must be >= 2
juggPredatorInit()
{
maps\mp\killstreaks\_juggernaut::initLevelCustomJuggernaut( ::juggPredatorCreate, ::setJuggPredatorClass, ::setJuggPredatorModel, "callout_killed_juggernaut_predator" );
level.mapCustomCrateFunc = ::customCrateFunc;
level.mapCustomKillstreakFunc = ::customKillstreakFunc;
level.mapCustomBotKillstreakFunc = ::customBotKillstreakFunc;
level._effect[ "predator_uncloak_sparks" ] = LoadFX( "vfx/_requests/pred/vfx_fade_distortion" );
level._effect[ "predator_uncloak_chest" ] = LoadFX( "vfx/_requests/pred/vfx_fade_distortion_chest" );
level._effect[ "predator_uncloak_limbs" ] = LoadFX( "vfx/_requests/pred/vfx_fade_distortion_long" );
level._effect[ "predator_eyes" ] = LoadFX( "vfx/gameplay/mp/events/vfx_mp_battery3_glowing_eyes" );
level._effect[ "predator_kill" ] = LoadFX( "vfx/gameplay/mp/events/vfx_mp_battery3_pred_kill" );
level._effect[ "predator_self_destruct" ] = LoadFX( "vfx/_requests/pred/vfx_pred_self_dest" );
level.previousLastStandCallback = level.callbackPlayerLastStand;
level.callbackPlayerLastStand = ::Callback_PlayerLastStandPredator;
/#
SetDvarIfUninitialized( CONST_DEBUG_DVAR, 0 );
SetDvarIfUninitialized( CONST_DEBUG_LASTSTAND_DVAR, 0 );
#/
// SOUND: put level entity here
}
juggPredatorCreate( juggType )
{
AssertEx( juggType == CONST_JUGG_TYPE );
self.isJuggernautLevelCustom = true;
self.juggMoveSpeedScaler = 1.05; // for unset perk juiced
self maps\mp\gametypes\_class::giveLoadout( self.pers["team"], juggType, false );
// self givePerk( "specialty_coldblooded", false );
self givePerk( "specialty_spygame", false );
// self givePerk( "specialty_marathon", false );
self givePerk( "specialty_longersprint", false );
self givePerk( "specialty_falldamage", false );
self givePerk( "specialty_pistoldeath", false );
self givePerk( "specialty_selectivehearing", false );
self givePerk( "specialty_quieter", false );
self.moveSpeedScaler = 1.05; // this needs to happen last because some perks change speed
self.healthRegenDisabled = true; // to disable health overlay sounds
self.breathingStopTime = 0;
//play the whip spawn sound
//DR: now playing for everyone
self PlaySound("scn_predator_spawn_whip");
//needs to wait a half second so the music covers it up, so there's not a sharp cutoff
self thread juggTurnOnPredatorAudioZoneUponCreate();
self SetClientOmnvar( "ui_predator_hud", 1 );
self thread watchJuggHostMigrationFinishedInit();
self SetSurfaceType( "fruit" ); // !!! Hack-ish. see fx/maps/mp_battery3/iw_impacts.csv
self thread juggPredatorOnDeath();
self thread juggPredatorOnDisconnect();
self thread juggPredatorOnGameEnded();
self thread predatorCannonUpdate();
self thread predatorOnEnemyKilled();
self thread watchEMPDamage(); // grenades
self thread watchEMPEvent(); // jammer
self thread predatorVocalClicks();
self thread predatorPainCry();
self thread teamPlayerCardSplash( "used_juggernaut_predator", self );
if ( !IsAI(self) )
{
// self NotifyOnPlayerCommand( "predator_cloak_toggle", "+smoke" );
self NotifyOnPlayerCommand( "player_melee", "+melee_zoom" );
}
// timestamps for checking how frequently we can do things
self initTimeStamp( "victoryCry" );
self initTimeStamp( "painCry" );
//self maps\mp\killstreaks\_killstreaks::disableKillstreakActionSlots();
self.canUseKillstreakCallback = ::juggPredatorCanUseOtherKillstreaks;
self.killstreakErrorMsg = ::juggPredatorKillsteakErrorMsg;
self thread predatorBeginMusic();
maps\mp\gametypes\_battlechatter_mp::disableBattleChatter( self );
self PlaySound( "scn_predator_first_raise_shing_npc" );
level notify( "update_bombsquad" ); // since predator doesn't have sitrep, update in case the player previously did
/#
self thread debugDvarWatcher();
#/
level.predatorUser = self;
return false;
}
juggTurnOnPredatorAudioZoneUponCreate()
{
self endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
//wait( 0.5 );
self SetClientOmnvar( "enableCustomAudioZone", true );
}
juggPredatorOnGameEnded()
{
self endon( "jugg_removed" );
self endon( "disconnect" );
level waittill( "game_ended" );
self juggPredatorCleanup();
}
juggPredatorOnDeath()
{
self endon( "disconnect" );
level endon( "game_ended" );
self waittill_any( "death", "joined_team", "joined_spectators", "lost_juggernaut" );
self resetCannonLock();
self predatorUnsetCloaked( false );
self juggPredatorCleanup();
}
juggPredatorOnDisconnect()
{
self endon( "death" );
self waittill( "disconnect" );
thread predatorEndMusic();
}
juggPredatorCleanup()
{
self predatorVisionDisable();
self SetClientOmnvar( "enableCustomAudioZone", false );
self SetClientOmnvar( "ui_predator_hud", 0 );
self SetClientOmnvar( "ui_predator_hud_scanline", false );
self SetSurfaceType( "flesh" );
if ( !IsAI(self) )
{
// self NotifyOnPlayerCommand( "", "+smoke" );
self NotifyOnPlayerCommand( "", "+speed_throw" );
if( !level.console )
{
self NotifyOnPlayerCommand( "", "+toggleads_throw" );
}
self NotifyOnPlayerCommand( "", "+melee" );
}
self resetTimeStamps();
self.cloakTimeStamp = undefined;
self.predatorVisionEnabled = undefined;
self.canUseKillstreakCallback = undefined;
self.killstreakErrorMsg = undefined;
self.predMoveSlowTimeStamp = undefined;
self.predFastSlowTimeStamp = undefined;
self.healthRegenDisabled = undefined;
self.breathingStopTime = undefined;
thread predatorEndMusic();
maps\mp\gametypes\_battlechatter_mp::enableBattleChatter( self );
level notify( "update_bombsquad" ); // since predator doesn't have sitrep, update in case the player previously did
self notify( "jugg_removed" );
}
setJuggPredatorClass( class )
{
// rely on "none" defaults
loadout = [];
loadout[ "loadoutPrimary" ] = "iw6_predatorcannon";
loadout[ "loadoutPrimaryBuff" ] = "specialty_null";
loadout[ "loadoutSecondaryBuff" ] = "specialty_null";
loadout[ "loadoutEquipment" ] = "specialty_null";
return loadout;
}
setJuggPredatorModel()
{
if( IsDefined( self.headModel ) )
{
self Detach( self.headModel, "" );
self.headModel = undefined;
}
self SetClothType( "nylon" );
self.predatorVisionEnabled = false;
self.isCloaked = true;
self predatorUnsetCloaked( false, true );
self thread setInitialCloak();
}
setInitialCloak()
{
self endon ( "death" );
self endon ( "disconnect" );
level endon( "game_ended" );
wait ( 3 );
self thread predatorVisionToggle();
if ( !self isEMPed() )
{
self predatorSetCloaked( false );
}
self thread predatorCloakMovementMonitor();
// self thread predatorCloakWaitForInput();
}
watchJuggHostMigrationFinishedInit()
{
level endon( "game_ended" );
while ( true )
{
level waittill( "host_migration_end" );
if ( IsDefined( self ) && IsDefined( self.isJuggernautLevelCustom ) && self.isJuggernautLevelCustom )
{
self SetClientOmnvar( "enableCustomAudioZone", true );
self SetClientOmnvar( "ui_predator_hud", 1 );
}
}
}
juggPredatorCanUseOtherKillstreaks( streakName )
{
return false;
}
juggPredatorKillsteakErrorMsg()
{
self IPrintLnBold( &"MP_JUGG_PREDATOR_NO_KILLSTREAKS" );
}
// -------------------------------------------------------------------
// Killstreak set up
// -------------------------------------------------------------------
tryUseCustomJuggernaut( lifeId, streakName )
{
maps\mp\killstreaks\_juggernaut::giveJuggernaut( streakName );
game[ "player_holding_level_killstrek" ] = false;
return true;
}
enable_level_killstreak()
{
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", CONST_JUGG_TYPE, CONST_JUGG_CRATE_WEIGHT);
}
disable_level_killstreak()
{
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", CONST_JUGG_TYPE, 0);
}
customCrateFunc()
{
if(!IsDefined(game["player_holding_level_killstrek"]))
game["player_holding_level_killstrek"] = false;
if(!allowLevelKillstreaks() || game["player_holding_level_killstrek"])
return;
//"Press and hold [{+activate}] for Swamp Slasher."
maps\mp\killstreaks\_airdrop::addCrateType(
"airdrop_assault",
CONST_JUGG_TYPE,
CONST_JUGG_CRATE_WEIGHT,
maps\mp\killstreaks\_airdrop::juggernautCrateThink,
maps\mp\killstreaks\_airdrop::get_friendly_juggernaut_crate_model(),
maps\mp\killstreaks\_airdrop::get_enemy_juggernaut_crate_model(),
CONST_JUGG_CRATE_STRING
);
level thread watch_for_jugg_crate();
}
watch_for_jugg_crate()
{
while( true )
{
level waittill( "createAirDropCrate", dropCrate );
if( IsDefined( dropCrate ) && IsDefined( dropCrate.crateType ) && dropCrate.crateType == CONST_JUGG_TYPE )
{
disable_level_killstreak();
captured = wait_for_capture( dropCrate );
if(!captured)
{
enable_level_killstreak();
}
else
{
//Once its picked up it needs to remain off.
game[ "player_holding_level_killstrek" ] = true;
break;
}
}
}
}
//death and capture are sent on the same frame but death is processed first :(
wait_for_capture( dropCrate )
{
result = watch_for_air_drop_death( dropCrate );
return !IsDefined( result ); //If !isdefined the captured notify was also sent.
}
watch_for_air_drop_death(dropCrate)
{
dropCrate endon( "captured" );
dropCrate waittill( "death" );
waittillframeend;
return true;
}
customKillstreakFunc()
{
AddDebugCommand( "devgui_cmd \"MP/Killstreak/Level Event:5/Care Package/Predator\" \"set scr_devgivecarepackage " + CONST_JUGG_TYPE + "; set scr_devgivecarepackagetype airdrop_assault\"\n");
AddDebugCommand( "devgui_cmd \"MP/Killstreak/Level Event:5/Predator\" \"set scr_givekillstreak " + CONST_JUGG_TYPE + "\"\n" );
/#
AddDebugCommand( "bind p \"set scr_givekillstreak " + CONST_JUGG_TYPE + "\"\n" );
AddDebugCommand( "bind semicolon \"set " + CONST_DEBUG_LASTSTAND_DVAR + " 2\"\n" );
#/
level.killStreakFuncs[ CONST_JUGG_TYPE ] = ::tryUseCustomJuggernaut;
}
customBotKillstreakFunc()
{
AddDebugCommand( "devgui_cmd \"MP/Bots(Killstreak)/Level Events:5/Predator\" \"set scr_testclients_givekillstreak " + CONST_JUGG_TYPE + "\"\n" );
maps\mp\bots\_bots_ks::bot_register_killstreak_func( CONST_JUGG_TYPE, maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
}
// -------------------------------------------------------------------
// CLOAK
// -------------------------------------------------------------------
predatorSetCloaked( useWrist )
{
if ( !self.isCloaked )
{
self.isCloaked = true;
self.cloakTimeStamp = GetTime() + CONST_CLOAK_ON_DELAY;
if ( useWrist )
{
// play sound
//DR: playing both 1st person and 3rd person sounds
self PlayLocalSound("scn_predator_plr_cloak_on");
self Playsound("scn_predator_npc_cloak_on");
// self predatorUseWristComputer();
}
self thread predatorPlayUnloakVfx();
self SetViewModel( "viewhands_mp_predator_proto_cloaked" ); // switch the viewmodel before the delay, so that we get better feedback
// if ( IsAlive( self ) ) // don't wait if the player is already dead (from switching teams, or kill command)
// {
// wait (0.25);
// }
self SetModel( "fullbody_mp_predator_a_cloaked" );
self givePerk( "specialty_blindeye", false );
self givePerk( "specialty_noscopeoutline", false);
self thread predatorEyeFlashUpdate();
}
}
predatorUnsetCloaked( useWrist, isFirstTime )
{
if ( self.isCloaked )
{
self.isCloaked = false;
self.cloakTimeStamp = GetTime() + CONST_CLOAK_OFF_DELAY;
if ( useWrist )
{
// play sound
//DR: playing both 1st person and 3rd person sounds
self PlayLocalSound("scn_predator_plr_cloak_off");
self Playsound("scn_predator_npc_cloak_off");
// self predatorUseWristComputer();
}
else if ( IsDefined( self.inWater ) && self.inWater )
{
// SOUND: play water decloak sound
//DR: playing both 1st person and 3rd person sounds
self PlayLocalSound("scn_predator_plr_cloak_off_waterfall");
self Playsound("scn_predator_npc_cloak_off_waterfall");
}
// start recharge timer
// play fx
self thread predatorPlayUnloakVfx();
self SetViewModel( "viewhands_mp_predator_proto" ); // switch the viewmodel before the delay, so that we get better feedback
if ( IsAlive( self ) && !IsDefined( isFirstTime ) ) // don't wait if the player is already dead (from switching teams, or kill command)
{
wait (0.25);
}
self SetModel( "fullbody_mp_predator_a" );
self _unsetPerk( "specialty_blindeye" );
self _unsetPerk( "specialty_noscopeoutline" );
}
}
predatorPlayUnloakVfx()
{
self endon( "death" );
PlayFXOnTag( getfx( "predator_uncloak_chest" ), self, "j_spineupper" );
PlayFXOnTag( getfx( "predator_uncloak_chest" ), self, "j_spinelower" );
wait( 0.05 );
PlayFXOnTag( getfx( "predator_uncloak_sparks" ), self, "j_head" );
PlayFXOnTag( getfx( "predator_uncloak_limbs" ), self, "j_knee_ri" );
PlayFXOnTag( getfx( "predator_uncloak_limbs" ), self, "j_knee_le" );
wait( 0.05 );
PlayFXOnTag( getfx( "predator_uncloak_limbs" ), self, "j_elbow_ri" );
PlayFXOnTag( getfx( "predator_uncloak_limbs" ), self, "j_elbow_le" );
wait( 0.05 );
PlayFXOnTag( getfx( "predator_uncloak_sparks" ), self, "j_wrist_le" );
PlayFXOnTag( getfx( "predator_uncloak_sparks" ), self, "j_wrist_ri" );
wait( 0.05 );
PlayFXOnTag( getfx( "predator_uncloak_limbs" ), self, "j_ankle_le" );
PlayFXOnTag( getfx( "predator_uncloak_limbs" ), self, "j_ankle_ri" );
/*
PlayFXOnTag( getfx( "predator_uncloak_sparks" ), self, "j_spinelower" );
// jointList = [ "j_head", "j_neck", "j_shoulder_le", "j_shoulder_ri", "j_elbow_le", "j_elbow_ri", "j_wrist_le", "j_wrist_ri", "j_knee_le", "j_knee_ri", "j_hip_le", "j_hip_ri" ];
jointList = [ "j_head",
// "j_mid_le_1", "j_mid_ri_1",
"j_wrist_le", "j_wrist_ri",
"j_shoulder_le", "j_knee_ri", "j_neck",
"j_shoulder_ri", "j_hip_le", "j_elbow_le",
"j_elbow_ri", "j_hip_ri", "j_knee_le" ];
for (i = 0; i < jointList.size; i++)
{
joint = jointList[i];
PlayFXOnTag( getfx( "predator_uncloak_sparks" ), self, joint );
if ( i % 2 == 1 )
wait( 0.05 );
}
*/
}
predatorUseWristComputer()
{
self _giveWeapon( CONST_PREDATOR_COMPUTER );
self SwitchToWeapon( CONST_PREDATOR_COMPUTER );
wait ( 1 );
self SwitchToWeapon( CONST_CANNON_WEAPON );
self TakeWeapon( CONST_PREDATOR_COMPUTER );
}
predatorCloakMovementMonitor()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
self.predMoveFastTimeStamp = 0;
self.predMoveSlowTimeStamp = 0;
self.predCloakDeniedUntilTime = 0;
self childthread predatorCloakWaitForForceEnd();
while ( true )
{
/#
if ( GetDvarInt( CONST_DEBUG_DVAR, 0 ) != 0 )
{
if ( self.isCloaked )
{
self thread predatorUnsetCloaked( true );
}
wait ( 1 );
continue;
}
#/
if ( self.isCloaked )
{
result = self waittill_any_return( "sprint_begin", "predator_force_uncloak" );
if ( result == "sprint_begin" )
{
self thread predatorUnsetCloaked( true );
}
}
else
{
if ( ( IsDefined( self.inWater ) && self.inWater )
|| self isEMPed()
)
{
self.predCloakDeniedUntilTime = GetTime() + CONST_CLOAK_PENALTY_FORCED_OFF;
}
else if ( self IsOnGround()
&& !self IsMantling()
)
{
speedSq = LengthSquared( self GetVelocity() );
curTime = GetTime();
if ( speedSq < CONST_MOVEMENT_THRESHOLD && !self IsSprinting() )
{
if ( curTime >= self.predCloakDeniedUntilTime )
{
self predatorSetCloaked( true );
continue; // skip over the wait, because the player may start sprinting immediately.
}
}
else
{
self.predCloakDeniedUntilTime = curTime + CONST_CLOAK_PENALTY_SPRINT;
}
}
}
wait (CONST_MOVEMENT_CHECK_FREQ);
}
}
predatorCloakWaitForInput()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
self childthread predatorCloakWaitForForceEnd();
while ( true )
{
self waittill( "predator_cloak_toggle" );
if ( GetTime() < self.cloakTimeStamp ) // don't let players mash the cloak button
continue;
// ignore cloak inputs while mantling, meleeing, and emp'd
if ( self IsMantling()
|| self IsMeleeing()
|| self isEMPed()
)
{
wait (0.05);
continue;
}
// if we're still recharging
// or emp'd
/// play the animation, but also play the sound?
if ( self.isCloaked )
{
self thread predatorUnsetCloaked( true );
}
else
{
// can't cloak in water
if ( IsDefined( self.inWater ) && self.inWater )
{
// play a warning sound
//self PlayLocalSound( "scn_predator_plr_cloak_not_ready" );
// play electrical effect
self thread predatorPlayUnloakVfx();
wait (0.05);
continue;
}
else
{
self predatorSetCloaked( true );
}
}
}
}
predatorCloakWaitForForceEnd()
{
// childthread
while ( true )
{
self waittill( "predator_force_uncloak" );
// don't do the model swamp while playing an animation
while ( self IsMantling()
// || self IsMeleeing()
)
{
wait 0.05;
}
if ( self.isCloaked )
{
self predatorUnsetCloaked( false );
}
}
}
predatorCloakForceEnd( penaltyTime )
{
self.predCloakDeniedUntilTime = GetTime() + penaltyTime;
self notify( "predator_force_uncloak" );
}
// -------------------------------------------------------------------
// Shoulder Cannon
// -------------------------------------------------------------------
predatorCannonUpdate()
{
self endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
self endon( "predator_lastStand" );
self.cannonState = CANNON_STATE_READY;
while ( true )
{
if ( self.cannonState == CANNON_STATE_READY )
{
newTarget = self findTargetInReticle( CANNON_FOV, CANNON_TARGET_RADIUS_READY );
if ( IsDefined( newTarget ) )
{
self startCannonLock( newTarget );
}
else
{
wait ( 0.5 );
}
}
else if ( self.cannonState == CANNON_STATE_LOCKING )
{
target = self.cannonTarget;
if ( IsDefined( target ) // connected
&& isReallyAlive( target ) // alive
&& self WorldPointInReticle_Circle( target GetEye(), CANNON_FOV, CANNON_TARGET_RADIUS_LOCKED ) // be a little more generous by giving you some grace time?
&& !(self isEMPed() )
)
{
if ( GetTime() >= self.cannonLockReadyTime )
{
self finalizeCannonLock( self.cannonTarget );
self childthread waittillCannonFire( self.cannonTarget );
}
}
else
{
self resetCannonLock( true );
}
wait( 0.05 );
}
else if ( self.cannonState == CANNON_STATE_LOCKED )
{
if ( ( !IsDefined( self.cannonTarget ) || !isReallyAlive( self.cannonTarget ) ) // victim is disconnected or dead
|| Distance2DSquared( self.origin, self.cannonTarget.origin ) > CANNON_MAX_RANGE_SQ // victim is too far away
|| self isEMPed()
)
{
self resetCannonLock( true );
}
wait( 0.05 );
}
else if ( self.cannonState == CANNON_STATE_RELOADING )
{
self reloadCannon();
}
}
}
findTargetInReticle( cameraFOV, reticleRadius )
{
targetsInReticle = self findAllTargetsInReticle( cameraFOV, reticleRadius );
return targetsInReticle[0];
}
findAllTargetsInReticle( cameraFOV, reticleRadius )
{
// collect all visible enemies near the center of the screen
targetsInReticle = [];
if ( !(self isEMPed()) )
{
// foreach ( participant in level.participants )
foreach ( participant in level.characters )
{
if ( IsDefined( participant )
&& isReallyAlive( participant )
&& self isEnemy( participant )
&& !participant _hasPerk( "specialty_blindeye" )
&& Distance2DSquared( self.origin, participant.origin ) <= CANNON_MAX_RANGE_SQ
)
{
targetPoint = participant GetEye();
if ( self WorldPointInReticle_Circle( targetPoint, cameraFOV, reticleRadius )
&& SightTracePassed( self GetEye(), targetPoint, false, undefined, undefined ) )
{
targetsInReticle[ targetsInReticle.size ] = participant;
}
}
}
if ( targetsInReticle.size > 1 )
{
return SortByDistance( targetsInReticle, self.origin );
}
}
return targetsInReticle;
}
startCannonLock( target )
{
Assert( IsDefined( target ) && isReallyAlive( target ) );
self PlayLocalSound( "scn_predator_weap_plr_lock_01" );
self.cannonTarget = target;
self.cannonLockReadyTime = GetTime() + CANNON_LOCK_TIME * 1000;
self.cannonState = CANNON_STATE_LOCKING;
self WeaponLockStart( target );
// !!! NEED TO HANDLE CASE WHERE TARGET IS KILLED OR DISCONNECTS
self SetClientOmnvar( "ui_predator_target_ent", target GetEntityNumber() );
self SetClientOmnvar( "ui_predator_hud_reticle", 1 );
self LaserOn();
// self enableWeaponLaser();
self thread playCannonLockSound();
}
playCannonLockSound()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predatorLockEnded" );
wait ( CANNON_LOCK_TIME * 0.5 );
//self PlayLocalSound( "scn_predator_weap_plr_lock_02" );
}
finalizeCannonLock( target )
{
self.cannonState = CANNON_STATE_LOCKED;
// offset = self GetTagOrigin( CANNON_TARGET_BONE ) - self.origin;
offset = (0, 0, 0);
self WeaponLockFinalize( target, offset, false );
// SOUND: start lock loop
self StopLocalSound("scn_predator_weap_plr_lock_01");
self PlayLocalSound( "scn_predator_weap_plr_lock_03" );
self SetClientOmnvar( "ui_predator_hud_reticle", 2 );
}
detachReticleVfxFromTarget( target, lockFailed )
{
self SetClientOmnvar( "ui_predator_hud_reticle", 0 );
//stop the reticle lock sound
self StopLocalSound("scn_predator_weap_plr_lock_03");
if ( IsDefined( lockFailed ) && lockFailed )
{
// SOUND: stop lock sound
self StopLocalSound("scn_predator_weap_plr_lock_01");
self PlayLocalSound( "scn_predator_weap_plr_lose_lock" );
}
}
waittillCannonFire( target )
{
self endon( "predatorLockEnded" );
self waittill( "missile_fire", projectile, weapname );
AssertEx( weapname == CONST_CANNON_WEAPON, "Instead of predator cannon, somehow fired unknown weapon: " + weapName );
// projectile Missile_SetFlightmodeDirect();
// offset = self GetTagOrigin( CANNON_TARGET_BONE ) - self.origin;
// projectile Missile_SetTargetEnt( target, CANNON_TARGET_OFFSET );
self predatorCloakForceEnd( CONST_CLOAK_PENALTY_CANNON );
// !!! UNSET OUTLINE / RETICLE WHEN MISSILE DETONATES
self childthread clearLockOnProjectileImpact( projectile );
self SetClientOmnvar( "ui_predator_hud_reticle", 0 );
self.cannonState = CANNON_STATE_RELOADING;
}
reloadCannon()
{
wait( CANNON_RELOAD_TIME - 0.85 );
// SOUND: reload sound
self PlayLocalSound("scn_predator_weap_plr_reload_done");
wait ( 0.85 );
self SetWeaponAmmoClip( CONST_CANNON_WEAPON, 1 );
self resetCannonLock();
}
resetCannonLock( loseLock )
{
self detachReticleVfxFromTarget( self.cannonTarget, loseLock );
self.cannonTarget = undefined;
self.cannonLockReadyTime = undefined;
self.cannonState = CANNON_STATE_READY;
self WeaponLockFree();
self WeaponLockTargetTooClose( false );
self WeaponLockNoClearance( false );
// self disableWeaponLaser();
self LaserOff();
self notify( "predatorLockEnded" );
self SetClientOmnvar( "ui_predator_target_ent", -1 );
}
isLocking()
{
return ( self PlayerAds() > 0.95 );
}
clearLockOnProjectileImpact( projectile ) // self == predator
{
self endon( "predatorLockEnded" );
projectile waittill( "death" );
self detachReticleVfxFromTarget( self.cannonTarget );
self notify( "predatorLockEnded" );
}
// -------------------------------------------------------------------
// VISION SET
// -------------------------------------------------------------------
predatorVisionToggle()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
if ( !IsAI(self) )
{
self NotifyOnPlayerCommand( "predator_vision", "+speed_throw" );
if( !level.console )
{
self NotifyOnPlayerCommand( "predator_vision", "+toggleads_throw" );
}
}
if ( !self isEMPed() )
{
self predatorVisionEnable();
}
while (true)
{
self waittill( "predator_vision" );
if ( !self IsThrowingGrenade()
&& !(self isEMPed())
)
{
if ( self.predatorVisionEnabled )
{
self predatorVisionDisable();
}
else
{
self predatorVisionEnable();
}
wait( 0.5 );
}
}
}
predatorVisionEnable()
{
if ( !self.predatorVisionEnabled )
{
self.predatorVisionEnabled = true;
// SOUND: Vison switch
self playlocalsound("scn_predator_plr_switch_vision_to_predator");
self SetClientOmnvar( "ui_predator_hud_scanline", true );
self predatorOutlinesOn();
}
}
predatorVisionDisable()
{
if ( self.predatorVisionEnabled )
{
self.predatorVisionEnabled = false;
// SOUND: Vison switch
self playlocalsound("scn_predator_plr_switch_vision_to_normal");
// self VisionSetThermalForPlayer( "ac130_enhanced_mp" );
// self ThermalVisionOff();
self VisionSetNakedForPlayer( "", 0.1 );
self SetClientOmnvar( "ui_predator_vision", false );
self predatorOutlinesOff();
self SetClientOmnvar( "ui_predator_hud_scanline", true );
}
}
predatorOutlinesOn() // self == player
{
self SetClientOmnvar( "ui_predator_vision", true );
foreach ( character in level.characters )
{
if( isReallyAlive( character )
&& character != self
&& ( IsAgent( character ) || character.sessionstate == "playing" )
) // !!! could get rid of the self check if we only highlight enemies
{
outlineColor = CONST_OUTLINE_COLOR_FRIENDLY; // Predator thermal effect requires "cyan" for friendlies.
if ( self isEnemy( character ) )
{
outlineColor = CONST_OUTLINE_COLOR_ENEMY; // Predator thermal effect requires "red" for enemies.
}
if( !character _hasPerk( "specialty_incog" ) )
{
self thread outlinePredatorTarget( character, outlineColor );
}
}
}
self thread watchPlayersSpawning();
self thread watchAgentsSpawning();
self thread watchOwnerDisconnect();
}
watchForPerkRemoval( targetPlayer )
{
self endon( "death" );
self endon( "disconnect" );
self endon( "endPredatorVision" );
level endon( "game_ended" );
targetPlayer endon ( "death" );
// We need to wait for the player to completely finish spawning before we try to apply the outline
// This is to solve the issue for players connecting/spawning in for the very first time
// To guarantee this, we are going to wait until the player's starting perks are unset before applying it
targetPlayer waittill ( "starting_perks_unset" );
if ( self isEnemy ( targetPlayer ) )
self thread outlinePredatorTarget( targetPlayer, CONST_OUTLINE_COLOR_ENEMY );
else
self thread outlinePredatorTarget( targetPlayer, CONST_OUTLINE_COLOR_FRIENDLY );
}
outlinePredatorTarget( target, outlineColor ) // self == predator
{
self endon( "death" );
self endon( "disconnect" );
self endon( "endPredatorVision" );
level endon( "game_ended" );
id = outlineEnableForPlayer( target, outlineColor, self, true, "killstreak_personal" );
targetId = target GetEntityNumber();
// the targets are stored on the level because the predator might disconnect, and we still need to clean up the references
level.predatorTargetsEnts[ targetId ] = target;
level.predatorTargetsOutlines[ targetId ] = id;
target waittill_any( "death", "disconnect" );
if ( IsDefined( target ) )
{
outlineDisable( level.predatorTargetsOutlines[ targetId ], target );
}
level.predatorTargetsEnts[ targetId ] = undefined;
level.predatorTargetsOutlines[ targetId ] = undefined;
}
watchPlayersSpawning() // self == predator
{
self endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
self endon( "endPredatorVision" );
while( true )
{
level waittill( "player_spawned", player );
if( player.sessionstate == "playing" && !player _hasPerk( "specialty_incog" ) )
self thread watchForPerkRemoval( player );
}
}
watchAgentsSpawning()
{
self endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
self endon( "endPredatorVision" );
while( true )
{
level waittill( "spawned_agent", agent );
if ( self isEnemy( agent ) )
{
self thread outlinePredatorTarget( agent, CONST_OUTLINE_COLOR_ENEMY );
}
else
{
self thread outlinePredatorTarget( agent, CONST_OUTLINE_COLOR_FRIENDLY );
}
}
}
watchOwnerDisconnect() // self == predator
{
self endon( "death" );
level endon( "game_ended" );
self waittill( "disconnect" );
predatorOutlinesOff();
}
predatorOutlinesOff()
{
self notify( "endPredatorVision" );
if ( IsDefined( level.predatorTargetsEnts ) )
{
foreach ( entNum, ent in level.predatorTargetsEnts )
{
if ( IsAlive( ent ) )
{
outlineDisable( level.predatorTargetsOutlines[ entNum ], ent );
}
}
level.predatorTargetsEnts = undefined;
level.predatorTargetsOutlines = undefined;
}
if ( IsDefined( self ) )
{
self SetClientOmnvar( "ui_predator_vision", false );
}
}
// -------------------------------------------------------------------
// Last stand / death sequence
// -------------------------------------------------------------------
Callback_PlayerLastStandPredator( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration )
{
if ( IsDefined( self.isJuggernautLevelCustom ) && self.isJuggernautLevelCustom )
{
self resetCannonLock();
self predatorUnsetCloaked( false );
self predatorVisionDisable();
// Last Stand Setup
lastStandParams = spawnStruct();
lastStandParams.eInflictor = eInflictor;
lastStandParams.attacker = attacker;
lastStandParams.iDamage = iDamage;
lastStandParams.attackerPosition = attacker.origin;
if ( attacker == self )
lastStandParams.sMeansOfDeath = "MOD_SUICIDE";
else
lastStandParams.sMeansOfDeath = sMeansOfDeath;
lastStandParams.sWeapon = sWeapon;
if ( IsDefined( attacker ) && IsPlayer( attacker ) && attacker getCurrentPrimaryWeapon() != "none" )
lastStandParams.sPrimaryWeapon = attacker getCurrentPrimaryWeapon();
else
lastStandParams.sPrimaryWeapon = undefined;
lastStandParams.vDir = vDir;
lastStandParams.sHitLoc = sHitLoc;
lastStandParams.lastStandStartTime = GetTime() + CONST_LAST_STAND_TIME * 1000;
mayDoLastStand = mayDoLastStand( sWeapon, lastStandParams.sMeansOfDeath, sHitLoc );
if ( IsDefined( self.endGame ) )
mayDoLastStand = false;
if ( !mayDoLastStand )
{
lastStandParams.lastStandStartTime = GetTime();
self.lastStandParams = lastStandParams;
self.useLastStandParams = true;
self _suicide();
return;
}
self.inLastStand = true;
self.health = 9999;
// SOUND: Play suicide timer beeps
// May need to move the laugh sound to its own function and delay
// set hud to last stand (hide control tips; show death tip)
// play laugh vo
foreach ( player in level.players )
{
if ( IsDefined( player ) && !IsAI( player ) )
{
player PlayLocalSound( "scn_predator_plr_vocal_laugh" );
}
}
// self PlaySound( "scn_predator_plr_vocal_laugh" );
if ( IsDefined( level.ac130player ) && IsDefined( attacker ) && level.ac130player == attacker )
level notify( "ai_crawling", self );
self.previousPrimary = self.lastdroppableweapon;
self.lastStandParams = lastStandParams;
self TakeAllWeapons();
self GiveWeapon( CONST_PREDATOR_SELF_DESTRUCT, 0, false );
self SwitchToWeapon( CONST_PREDATOR_SELF_DESTRUCT );
self _disableUsability();
self.inC4Death = true;
self thread activatePredatorCountdown();
self thread clearPredatorBombTimer();
self thread lastStandTimer( CONST_LAST_STAND_TIME );
self notify( "predator_lastStand" );
// change the help text
}
else if ( IsDefined( level.previousLastStandCallback ) )
{
[[ level.previousLastStandCallback ]]( eInflictor, attacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, psOffsetTime, deathAnimDuration );
}
}
mayDoLastStand( sWeapon, sMeansOfDeath, sHitLoc )
{
if ( sMeansOfDeath == "MOD_TRIGGER_HURT" )
return false;
if ( sMeansOfDeath == "MOD_SUICIDE" )
return false;
/*
if ( sMeansOfDeath != "MOD_PISTOL_BULLET" && sMeansOfDeath != "MOD_RIFLE_BULLET" && sMeansOfDeath != "MOD_FALLING" && sMeansOfDeath != "MOD_EXPLOSIVE_BULLET" )
return false;
if ( isHeadShot( sWeapon, sHitLoc, sMeansOfDeath ) )
return false;
*/
return true;
}
activatePredatorCountdown()
{
self endon( "disconnect" );
self endon( "joined_team" );
self endon( "predator_detonate" );
level endon( "game_ended" );
// TODO: Need the actual thing to wait for when the dial in animation is complete
timeUntilDialEnd = 3;
countdownTime = CONST_LAST_STAND_TIME - timeUntilDialEnd;
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeUntilDialEnd );
SetOmnvar( "ui_bomb_timer", 4 );
childthread update_pred_ui_timer( countdownTime );
}
update_pred_ui_timer( countdownTime )
{
predatorExplosionEndTime = ( countdownTime * 1000) + gettime();
SetOmnvar( "ui_nuke_end_milliseconds", predatorExplosionEndTime );
level waittill( "host_migration_begin" );
timePassed = maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
if ( timePassed > 0 )
{
SetOmnvar( "ui_nuke_end_milliseconds", predatorExplosionEndTime + timePassed );
}
}
clearPredatorBombTimer()
{
level endon( "game_ended" );
self waittill_any ( "predator_detonate", "joined_team", "disconnect" );
SetOmnvar( "ui_bomb_timer", 0 );
}
lastStandTimer( delay )
{
self endon( "disconnect" );
self endon( "joined_team" );
level endon( "game_ended" );
self waittill_any_timeout( delay, "death", "detonate" );
self predatorSuicideNuke();
}
detonateOnUse()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "joined_team" );
level endon( "game_ended" );
self waittill( "detonate" );
self predatorSuicideNuke();
}
detonateOnDeath()
{
self endon( "detonate" );
self endon( "disconnect" );
self endon( "joined_team" );
level endon( "game_ended" );
self waittill( "death" );
self predatorSuicideNuke();
}
predatorSuicideNuke()
{
self.useLastStandParams = true;
self PlaySound( "predator_explosion" );
//DR: making this so it doesn't play twice - once for real and once in the killcam
foreach ( player in level.players )
{
if ( IsDefined( player ) && !IsAI( player ) )
{
player PlayLocalSound( "predator_explosion_boom_local" );
}
}
self VisionSetNakedForPlayer( "coup_sunblind", 0.1 );
wait( 0.2 );
PlayFX( getfx( "predator_self_destruct" ), self.origin, AnglesToUp( self.angles), AnglesToForward( self.angles ) );
RadiusDamage( self.origin, CONST_PREDATOR_SUICIDE_RADIUS, 100, 100, self );
Earthquake( 0.1, 3.0, self.origin, CONST_PREDATOR_SUICIDE_RADIUS );
playPredatorExpVisionSequence();
level notify( "jugg_predator_killed", self );
self notify ( "predator_detonate" );
if ( IsAlive( self ) )
self _suicide();
}
// -------------------------------------------------------------------
// Polish Effects
// -------------------------------------------------------------------
predatorEyeFlashUpdate()
{
self endon( "death" );
self endon( "disconnect" );
level endon( "game_ended" );
while ( true )
{
self waittill( "player_melee" );
if ( self.isCloaked )
{
self predatorCloakForceEnd( CONST_CLOAK_PENALTY_MELEE );
}
else
{
break;
}
}
}
predatorOnEnemyKilled() // self == player
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while( true )
{
self waittill( "killed_enemy", victim, sWeapon, sMeansOfDeath );
if ( sWeapon == CONST_CANNON_WEAPON )
{
if ( IsDefined( sMeansofDeath ) && sMeansofDeath == "MOD_MELEE" )
{
self predatorMeleeKillEffect( victim );
}
// play disintegration effect?
}
}
}
predatorMeleeKillEffect( victim ) // self == player
{
pos = victim.origin + (0, 0, 50);
self playlocalsound("scn_predator_melee_swing_plr");
attackDir = self.origin - victim.origin;
hitPos = victim GetTagOrigin( "j_neck");
PlayFx( level._effect[ "predator_kill" ], hitPos, VectorNormalize( attackDir ), AnglesToUp( victim GetTagAngles( "j_neck" ) ) );
if ( self checkTimeStamp( "victoryCry" ) && self checkTimeStamp( "painCry" ) )
{
self setTimeStamp( "victoryCry", 10000 );
self predatorPlayVo( "scn_predator_plr_vocal_victory_cry", "scn_predator_npc_vocal_victory_cry" );
}
}
watchEMPDamage()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while( true )
{
// this handles any flash or concussion damage
self waittill( "emp_damage", attacker, duration );
// !!! HACK: have to wait a little bit because the empGrenaded flag doesn't get set for a frame or two
waitframe();
if ( self isEMPed() )
{
self predatorVisionDisable();
self predatorCloakForceEnd( CONST_CLOAK_PENALTY_FORCED_OFF );
}
}
}
watchEMPEvent()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while( true )
{
level waittill( "emp_update" );
if ( self isEMPed() )
{
self predatorVisionDisable();
self predatorCloakForceEnd( CONST_CLOAK_PENALTY_FORCED_OFF );
}
}
}
predatorVocalClicks()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while( true )
{
delay = RandomFloatRange( CONST_VOICE_CLICK_MIN, CONST_VOICE_CLICK_MAX );
wait( delay );
if ( self checkTimeStamp( "victoryCry" ) && self checkTimeStamp( "painCry" ) )
{
self predatorPlayVo( "scn_predator_plr_vocal_clicks", "scn_predator_npc_vocal_clicks" );
}
}
}
predatorPainCry()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while ( true )
{
self waittill( "damage", amount, attacker, direction_vec, point, type );
if( IsDefined( attacker )
&& attacker != self
&& amount > 10
&& self checkTimeStamp( "painCry", 1000 )
)
{
self predatorPlayVo( "scn_predator_plr_vocal_pain", "scn_predator_npc_vocal_pain" );
}
// SOUND: If damage large enough, but not playing pain sound, force uncloak? (don't want to pile up too many sounds at once)
}
}
predatorPlayVo( playerSound, npcSound )
{
self PlayLocalSound( playerSound );
self PlaySoundOnMovingEnt( npcSound );
}
predatorBeginMusic()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
// this won't stop currently playing music
maps\mp\gametypes\_music_and_dialog::disableMusic();
level.predatorMusicEnt = Spawn( "script_origin", (0,0,0) );
level.predatorMusicEnt PlaySound( "mus_predator_music_spawn" );
self childthread predatorTensionMusic();
}
predatorTensionMusic()
{
tensionMusic = [ "mus_predator_music_tension_01", "mus_predator_music_tension_02" ];
curIndex = RandomInt( tensionMusic.size );
while ( true )
{
wait( CONST_TENSION_MUSIC_FREQ );
level.predatorMusicEnt PlaySound( tensionMusic[curIndex] );
// play each tension music in order
curIndex++;
if ( curIndex >= tensionMusic.size )
curIndex = 0;
}
}
predatorEndMusic()
{
level.predatorMusicEnt StopSounds();
thread maps\mp\gametypes\_music_and_dialog::enableMusic();
waitframe();
level.predatorMusicEnt Delete();
level.predatorMusicEnt = undefined;
}
/*
predatorHeartBeat()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while ( true )
{
heartbeatTargets = [];
foreach ( participant in level.participants )
{
if ( IsDefined( participant )
&& isReallyAlive( participant )
&& self isEnemy( participant )
&& Distance2DSquared( self.origin, participant.origin ) <= CANNON_MAX_RANGE_SQ
)
{
heartbeatTargets[ heartbeatTargets.size ] = participant;
}
}
if ( heartbeatTargets.size > 1 )
{
heartbeatTargets = SortByDistance( heartbeatTargets, self.origin );
}
wait ( 0.5 );
}
}
*/
// -------------------------------------------------------------------
// Predator Explosion
// -------------------------------------------------------------------
playPredatorExpVisionSequence()
{
level.predatorExpTimer = 0;
level thread delaythread_predatorExp( level.predatorExpTimer, ::predatorExpSlowMo);
level thread delaythread_predatorExp( level.predatorExpTimer, ::predatorExpVision );
level thread delaythread_predatorExp( level.predatorExpTimer + 0.5, ::predatorExpDeath );
}
delaythread_predatorExp( delay, func )
{
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( delay );
thread [[ func ]]();
}
predatorExpVision()
{
if ( is_gen4() )
{
VisionSetPostApply( "mpnuke", 0.2 );
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.2 ) ;
VisionSetPostApply("nuke_global_flash", 0.5 );
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.3 ) ;
VisionSetPostApply("", 1 );
}
else
{
VisionSetPostApply( "mpnuke", 0.7 );
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.5 );
VisionSetPostApply("nuke_global_flash", 0.5 );
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.5 ) ;
VisionSetPostApply("", 0.5 );
}
}
predatorExpSlowMo()
{
SetSlowMotion( 1.0, 0.25, 0.5 );
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 1 );
SetSlowMotion( 0.25, 1, 2.0 );
}
predatorExpDeath()
{
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
if ( IsDefined( level.predatorUser ) )
{
foreach ( character in level.characters )
{
if ( predatorExpCanKill( character ) )
{
if ( IsPlayer( character ) )
{
if ( isReallyAlive( character ) )
{
character thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper( level.predatorUser, level.predatorUser, 999999, 0, "MOD_EXPLOSIVE", "iw6_predatorsuicide_mp", character.origin, ( 0, 0, 1 ), "none", 0, 0 );
}
}
else
{
character maps\mp\agents\_agents::agent_damage_finished( level.predatorUser, level.predatorUser, 999999, 0, "MOD_EXPLOSIVE", "iw6_predatorsuicide_mp", character.origin, ( 0, 0, 1 ), "none", 0 );
}
}
}
}
// Take out everything else
victimTeam = getOtherTeam( level.predatorUser.team );
level maps\mp\killstreaks\_jammer::destroyGroundObjects( level.predatorUser, victimTeam );
level maps\mp\killstreaks\_air_superiority::destroyActiveVehicles( level.predatorUser, victimTeam );
}
predatorExpCanKill( player )
{
if( level.teambased )
{
if( player.team == level.predatorUser.team )
return false;
}
else
{
isKillstreakPlayer = ( player == level.predatorUser );
ownerIsPlayer = IsDefined( player.owner ) && ( player.owner == level.predatorUser );
if( isKillstreakPlayer || ownerIsPlayer )
return false;
}
return true;
}
// -------------------------------------------------------------------
// Helpers
// -------------------------------------------------------------------
initTimeStamp( key )
{
self._eventTimeStamps[ key ] = 0;
}
checkTimeStamp( key, minDelay )
{
Assert( IsDefined( self._eventTimeStamps[ key ] ) );
return ( GetTime() >= self._eventTimeStamps[ key ] );
}
setTimeStamp( key, minDelay )
{
self._eventTimeStamps[ key ] = GetTime() + minDelay;
}
resetTimeStamps()
{
self._eventTimeStamps = undefined;
}
/#
debugDvarWatcher()
{
self endon( "death" );
self endon( "disconnect" );
self endon( "predator_lastStand" );
level endon( "game_ended" );
while ( true )
{
value = GetDvarInt( CONST_DEBUG_LASTSTAND_DVAR, 0 );
if ( value > 0 )
{
self childthread debugLastStand( value );
SetDvar( CONST_DEBUG_LASTSTAND_DVAR, 0 );
}
wait (0.5);
}
}
debugLastStand( waitTime )
{
while ( true )
{
wait waitTime;
hitDir = (1,0,0);
hitOrigin = self.origin;
damage = 50 + RandomInt(50);
IPrintLn( "taking " + damage + " damage; use demigod to see anims");
attacker = self;
foreach ( player in level.players )
{
if ( IsDefined( player ) && isReallyAlive( player ) && self isEnemy( player ) )
{
attacker = player;
break;
}
}
self FinishPlayerDamage( attacker, attacker, damage, 0, "MOD_EXPLOSIVE", "iw6_honeybadger_mp", hitOrigin, hitDir, "none", 0, 0 );
}
}
#/