#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 ); } } #/