2025-05-21 16:23:17 +02:00

3456 lines
94 KiB
Plaintext

#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;
#include maps\mp\killstreaks\_orbital_util;
CONST_JUGG_EXO_SPEED_SCALE = 1; // animations are already 65% of normal player speed
CONST_JUGG_EXO_SPEED_SCALE_MANIAC = 1.15;
CONST_JUGG_EXO_TURRET_TOP_ARC = 55;
CONST_JUGG_EXO_TURRET_BOTTOM_ARC = 30;
CONST_JUGG_EXO_TURRET_HORZ_ARC = 180;
CONST_JUGG_EXO_ROCKET_SWARM_RELOAD_TIME = 10;
CONST_JUGG_EXO_ROCKET_RELOAD_TIME = 10;
CONST_JUGG_EXO_HEALTH = 125;
CONST_JUGG_EXO_HEALTH_HORDE = 300;
CONST_JUGG_EXO_LETHAL = "playermech_rocket_mp";
CONST_JUGG_EXO_TACTICAL = "playermech_rocket_swarm_mp";
CONST_JUGG_EXO_TACTICAL_MANIAC = "playermech_rocket_swarm_maniac_mp";
CONST_JUGG_EXO_TIMEOUT_SEC = 120;
CONST_JUGG_EXO_USABILITY_RADIUS_SQ = 6000;
HEAVY_EXO_PING_RANGE = 700;
HEAVY_EXO_PING_RANGE_SQ = 490000;
HEAVY_EXO_PING_DURATION = 1.5;
HEAVY_EXO_PING_THREAT_DURATION = 1.75;
MIN_TIME_BETWEEN_PINGS = 5;
PING_DURATION = 10;
init()
{
level.juggSettings = [];
level.juggSettings[ "juggernaut_exosuit" ] = spawnStruct();
level.juggSettings[ "juggernaut_exosuit" ].splashUsedName = "used_juggernaut";
level.juggSettings[ "juggernaut_exosuit" ].splashAttachmentName = "callout_destroyed_heavyexoattachment";
level.juggSettings[ "juggernaut_exosuit" ].splashWeakenedName = "callout_weakened_heavyexoattachment";
level._effect[ "green_light_mp" ] = LoadFX( "vfx/lights/aircraft_light_wingtip_green" );
level._effect[ "juggernaut_sparks" ] = LoadFX( "vfx/explosion/bouncing_betty_explosion" );
level._effect[ "jugg_droppod_open" ] = LoadFX( "vfx/explosion/goliath_pod_opening");
level._effect[ "jugg_droppod_marker" ] = LoadFX( "vfx/unique/vfx_marker_killstreak_guide_goliath" );
level._effect[ "exo_ping_inactive" ] = LoadFX( "vfx/unique/exo_ping_inactive" );
level._effect[ "exo_ping_active" ] = LoadFX( "vfx/unique/exo_ping_active" );
level._effect[ "goliath_death_fire" ] = LoadFX( "vfx/fire/goliath_death_fire" );
level._effect[ "goliath_self_destruct" ] = LoadFX( "vfx/explosion/goliath_self_destruct" );
level._effect[ "lethal_rocket_wv" ] = LoadFX( "vfx/muzzleflash/playermech_lethal_flash_wv" );
level._effect[ "swarm_rocket_wv" ] = LoadFX( "vfx/muzzleflash/playermech_tactical_wv_run" );
level.killstreakWieldWeapons["juggernaut_sentry_mg_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["iw5_juggernautrockets_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["iw5_exoxmgjugg_mp_akimbo"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["iw5_juggtitan45_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["iw5_exominigun_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["iw5_mechpunch_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["playermech_rocket_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["killstreak_goliathsd_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["orbital_carepackage_droppod_mp"] = "juggernaut_exosuit";
level.killstreakWieldWeapons["heavy_exo_trophy_mp"] = "juggernaut_exosuit";
level.killstreakFuncs["heavy_exosuit"] = ::tryUseHeavyExosuit;
/#
SetDvarIfUninitialized( "scr_goliath_god", "0" );
#/
// Coop Buddy Assistance VO
game["dialog"][ "assist_mp_goliath" ] = "ks_goliath_joinreq";
game["dialog"][ "copilot_mp_goliath" ] = "copilot_mp_goliath";
// Offline VO
game["dialog"][ "sntryoff_mp_exoai" ] = "sntryoff_mp_exoai";
game["dialog"][ "mancoff_mp_exoai" ] = "mancoff_mp_exoai";
game["dialog"][ "longoff_mp_exoai" ] = "longoff_mp_exoai";
game["dialog"][ "rcnoff_mp_exoai" ] = "rcnoff_mp_exoai";
game["dialog"][ "rcktoff_mp_exoai" ] = "rcktoff_mp_exoai";
game["dialog"][ "trphyoff_mp_exoai" ] = "trphyoff_mp_exoai";
game["dialog"][ "weakdmg_mp_exoai" ] = "weakdmg_mp_exoai";
level thread onPlayerConnect();
}
tryUseHeavyExosuit( lifeId, modules )
{
if ( isdefined ( level.isHorde ) && level.isHorde )
{
if ( isdefined ( self.hordeGoliathPodInField ) || isdefined ( self.hordeGoliathController ) || isdefined ( self.hordeClassGoliathController ) )
{
self IPrintLnBold( &"KILLSTREAKS_HEAVY_EXO_IN_USE" );
return false;
}
}
result = self playerLaunchDropPod( modules );
return result;
}
resetWeapon()
{
killstreakWeapon = getKillstreakWeapon( "heavy_exosuit" );
self SwitchToWeapon( self getLastWeapon() );
self maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( killstreakWeapon );
}
canSetupStance()
{
if ( self GetStance() == "prone" || self GetStance() == "crouch" )
self SetStance( "stand" );
self freezeControlsWrapper( true );
waitTimeEnd = GetTime() + 1500;
while ( GetTime() < waitTimeEnd && self GetStance() != "stand" )
waitframe();
self freezeControlsWrapper( false );
return self GetStance() == "stand";
}
giveJuggernaut( juggType, modules ) // self == player or bot
{
self endon( "death" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
// added this wait here because i think the disabling the weapons and re-enabling while getting the crate,
// needs a little time or else we sometimes won't have a weapon in front of us after we get juggernaut
// wait(0.05);
// remove light armor if equipped
if ( self maps\mp\perks\_perkfunctions::hasLightArmor() )
maps\mp\perks\_perkfunctions::unsetLightArmor();
// remove explosive bullets if equipped
if ( self _hasPerk( "specialty_explosivebullets" ) )
self _unsetPerk( "specialty_explosivebullets" );
self.maxHealth = CONST_JUGG_EXO_HEALTH;
if ( isdefined ( level.isHorde ) && level.isHorde )
self.maxHealth = CONST_JUGG_EXO_HEALTH_HORDE + ( 25 * self.hordeArmor );
self.health = self.maxHealth;
self.attackerList = [];
switch( juggType )
{
case "juggernaut_exosuit":
default:
speedScale = CONST_JUGG_EXO_SPEED_SCALE;
juggClass = "juggernaut_exosuit";
if ( !IsDefined( modules ) || array_contains( modules, "heavy_exosuit_maniac" ) )
{
speedScale = CONST_JUGG_EXO_SPEED_SCALE_MANIAC;
juggClass = "juggernaut_exosuit_maniac";
}
self.juggMoveSpeedScaler = speedScale;
self removeWeapons();
hasHardline = IsDefined( self.perks["specialty_hardline"] );
self maps\mp\gametypes\_class::giveAndApplyLoadout( self.pers["team"], juggClass, false, false );
self maps\mp\gametypes\_playerlogic::streamClassWeapons( false, false, juggClass );
self.isJuggernaut = true;
self.moveSpeedScaler = speedScale;
self givePerk( "specialty_radarjuggernaut", false );
if ( hasHardline )
self givePerk( "specialty_hardline", false );
self thread playerSetupJuggernautExo( modules, juggType );
self.saved_lastWeapon = self getWeaponsListPrimaries()[0];
break;
}
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
self disableWeaponPickup();
// self thread juggernautSounds();
if ( !IsDefined( modules ) || array_contains( modules, "heavy_exosuit_maniac" ) ) // is a maniac
self PlaySound( "goliath_suit_up_mp" );
else
self PlaySound( "goliath_suit_up_mp" );
self thread teamPlayerCardSplash( level.juggSettings[ juggType ].splashUsedName, self );
// - giveLoadout() nukes action slot 4 (killstreak weapon)
// - it's usually restored after activating a killstreak but
// equipping juggernaut out of a box isn't part of killstreak activation flow
// - restore action slot 4 by re-updating killstreaks
// self thread maps\mp\killstreaks\_killstreaks::updateKillstreaks( true );
self thread juggRemover();
level notify( "juggernaut_equipped", self );
self maps\mp\_matchdata::logKillstreakEvent( "juggernaut", self.origin );
}
juggernautSounds()
{
level endon ( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
self endon( "jugg_removed" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
for ( ;; )
{
wait ( 3.0 );
self playSound( "juggernaut_breathing_sound" );
}
}
radarMover( portableRadar )
{
level endon("game_ended");
self endon( "disconnect" );
self endon( "jugg_removed" );
self endon( "jugdar_removed" );
for( ;; )
{
portableRadar MoveTo( self.origin, .05 );
wait (0.05);
}
}
juggRemover()
{
level endon("game_ended");
self endon( "disconnect" );
self endon( "jugg_removed" );
self thread juggRemoveOnGameEnded();
self waittill_any( "death", "joined_team", "joined_spectators", "lost_juggernaut" );
self enableWeaponPickup();
self.isJuggernaut = false;
if ( isDefined( self.juggernautOverlay ) )
self.juggernautOverlay destroy();
self unsetPerk( "specialty_radarjuggernaut", true );
if ( isDefined( self.personalRadar ) )
{
self notify( "jugdar_removed" );
level maps\mp\gametypes\_portable_radar::deletePortableRadar( self.personalRadar );
self.personalRadar = undefined;
}
self notify( "jugg_removed" );
}
juggRemoveOnGameEnded()
{
self endon( "disconnect" );
self endon( "jugg_removed" );
level waittill( "game_ended" );
if( IsDefined( self.juggernautOverlay ) )
self.juggernautOverlay destroy();
}
removeWeapons()
{
self.primaryToRestore = self getLastWeapon();
// since we're taking your weapons we need to keep track of the ammo for when we restore them
foreach( weapon in self.weaponlist )
{
weaponTokens = getWeaponNameTokens( weapon );
if( weaponTokens[0] == "alt" )
{
// we don't need to take the alt weapons but we do need to store the ammo for when we restore them
self.restoreWeaponClipAmmo[ weapon ] = self GetWeaponAmmoClip( weapon );
self.restoreWeaponStockAmmo[ weapon ] = self GetWeaponAmmoStock( weapon );
continue;
}
self.restoreWeaponClipAmmo[ weapon ] = self GetWeaponAmmoClip( weapon );
self.restoreWeaponStockAmmo[ weapon ] = self GetWeaponAmmoStock( weapon );
}
// now take the weapons
self.weaponsToRestore = [];
foreach( weapon in self.weaponlist )
{
weaponTokens = getWeaponNameTokens( weapon );
if( weaponTokens[0] == "alt" )
continue;
if ( isKillstreakWeapon( weapon ) )
continue;
// keep a list of the weapons we take because self.weaponlist isn't reliable
self.weaponsToRestore[ self.weaponsToRestore.size ] = weapon;
self TakeWeapon( weapon );
}
// self DisableOffhandWeapons();
}
//restoreWeapons()
//{
// if( !IsDefined( self.restoreWeaponClipAmmo ) ||
// !IsDefined( self.restoreWeaponStockAmmo ) ||
// !IsDefined( self.weaponsToRestore ) )
// return;
//
// altWeapons = [];
// foreach( weapon in self.weaponsToRestore )
// {
// weaponTokens = getWeaponNameTokens( weapon );
// if( weaponTokens[0] == "alt" )
// {
// // we don't need to give the alt weapons but we do need to restore the ammo
// altWeapons[ altWeapons.size ] = weapon;
// continue;
// }
//
// self _giveWeapon( weapon );
// if( IsDefined( self.restoreWeaponClipAmmo[ weapon ] ) )
// self SetWeaponAmmoClip( weapon, self.restoreWeaponClipAmmo[ weapon ] );
// if( IsDefined( self.restoreWeaponStockAmmo[ weapon ] ) )
// self SetWeaponAmmoStock( weapon, self.restoreWeaponStockAmmo[ weapon ] );
// }
//
// foreach( altWeapon in altWeapons )
// {
// if( IsDefined( self.restoreWeaponClipAmmo[ altWeapon ] ) )
// self SetWeaponAmmoClip( altWeapon, self.restoreWeaponClipAmmo[ altWeapon ] );
// if( IsDefined( self.restoreWeaponStockAmmo[ altWeapon ] ) )
// self SetWeaponAmmoStock( altWeapon, self.restoreWeaponStockAmmo[ altWeapon ] );
// }
//
// self.restoreWeaponClipAmmo = undefined;
// self.restoreWeaponStockAmmo = undefined;
// self EnableOffhandWeapons();
//}
playerSetupJuggernautExo( modules, juggType ) // self == player
{
data = SpawnStruct();
self.heavyExoData = data;
data.streakPlayer = self;
data.hasCoopSentry = true;
data.modules = modules;
data.juggType = juggType;
/#
data.hasCoopSentry = GetDvarInt( "scr_heavy_exo_turret", 1 ) == 1;
#/
if ( IsDefined( modules ) )
{
data.hasRadar = array_contains( modules, "heavy_exosuit_radar" );
data.hasManiac = array_contains( modules, "heavy_exosuit_maniac" );
data.hasLongPunch = array_contains( modules, "heavy_exosuit_punch" );
data.hasTrophy = array_contains( modules, "heavy_exosuit_trophy" );
data.hasRockets = array_contains( modules, "heavy_exosuit_rockets" );
data.hasExtraAmmo = array_contains( modules, "heavy_exosuit_ammo" );
}
else // from devgui
{
data.hasRadar = true;
data.hasManiac = true;
data.hasLongPunch = false;
data.hasTrophy = true;
data.hasRockets = true;
data.hasExtraAmmo = true;
}
modulesOn = 0;
// Must match powers of 2 defined in exoSuitAttachments.csv
if ( data.hasRockets ) modulesOn += 1;
if ( data.hasLongPunch ) modulesOn += 2;
if ( data.hasRadar ) modulesOn += 4;
if ( data.hasTrophy ) modulesOn += 8;
if ( data.hasManiac ) modulesOn += 16;
if ( data.hasCoopSentry ) modulesOn += 32;
self SetClientOmnvar( "ui_exo_suit_modules_on", modulesOn );
self playerAllowPowerSlide( false, "heavyexo" );
if ( !data.hasManiac )
{
self playerAllowDodge( false, "heavyexo" );
self playerAllowBoostJump( false, "heavyexo" );
self playerAllowHighJump( false, "heavyexo" );
self playerAllowHighJumpDrop( false, "heavyexo" );
}
self _disableUsability();
self AllowJump( false );
self AllowCrouch( false );
self AllowLadder( false );
self AllowMantle( false );
self.inLivePlayerKillstreak = true;
self.mechHealth = CONST_JUGG_EXO_HEALTH;
if ( isdefined ( level.isHorde ) && level.isHorde )
self.mechHealth = self.maxhealth;
self SetDemiGod( true );
self SetClientOmnvar( "ui_exo_suit_health", 1 );
self playerSetJuggExoModel( data );
self thread playerShowJuggernautHud( data );
self thread playerCleanupOnDeath( data );
self thread playerCleanupOnOther();
self thread playerRocketsAndSwarmWatcher();
self thread playermech_invalid_weapon_watcher();
self thread playerHandleBootupSequence();
self thread play_goliath_death_fx();
self thread playermech_watch_emp_grenade();
if ( isdefined ( level.isHorde ) && level.isHorde )
self thread playerMechTimeout();
if ( data.hasCoopSentry )
{
// TODO: Disabled co-op turret for Gamescom, need to come up with something for ship.
// turret = setupCoopTurret( data, self );
// if ( level.teamBased )
// level thread handleCoopJoining( data, self );
}
if ( data.hasRadar )
level thread setupRadar( self, data );
if ( data.hasManiac )
{
level thread setupManiac( self );
set_mech_chaingun_state( "offline" );
}
else
{
self thread playerHandleBarrel();
set_mech_chaingun_state( "ready" );
}
if ( data.hasLongPunch )
{
level thread setupLongPunch( self, data );
set_mech_rocket_state( "ready" );
self thread playermech_monitor_rocket_recharge();
}
else
{
set_mech_rocket_state( "offline" );
if ( !data.hasManiac )
self DisableOffhandWeapons();
}
if ( data.hasTrophy )
level thread setupTrophy( self, data );
if ( data.hasRockets )
{
level thread setupRocketSwarm( self, data );
set_mech_swarm_state( "ready" );
self thread playermech_monitor_swarm_recharge();
}
else
{
self DisableOffhandSecondaryWeapons();
set_mech_swarm_state( "offline" );
}
// if ( data.hasExtraAmmo && !data.hasManiac )
// {
// weapon = "iw5_exominigun_mp";
// maxAmmo = WeaponMaxAmmo( weapon );
// self SetWeaponAmmoStock( weapon, maxAmmo );
// }
level thread delaySetWeapon( self );
//Allow the cancelling of the goliath early (for instance, destroying it during the manticore event or jumping off of a cliff)
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "horde_cancel_goliath" );
// Wait until the player is finished switching weapons to allow self destruct. Without this wait, players can self destruct without dying.
wait( 5 );
if ( IsDefined(self) )
self thread self_destruct_goliath();
}
playerHandleBootupSequence()
{
self.goliathBootupSequence = true;
wait 4.16; // this is the length of the pullout animation
self.goliathBootupSequence = undefined;
}
juggernautModifyDamage( victim, eAttacker, iDamage, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, eInflictor )
{
if ( !victim isJuggernaut() )
return iDamage;
finalDamage = iDamage;
if ( IsDefined( sMeansOfDeath ) && sMeansOfDeath == "MOD_FALLING" )
finalDamage = 0;
if ( IsDefined( sWeapon ) && sWeapon == "boost_slam_mp" )
finalDamage = 20;
if ( IsDefined( eAttacker ) && IsDefined( victim ) && eAttacker == victim && IsDefined( sWeapon ) && ( sWeapon == "iw5_juggernautrockets_mp" || sWeapon == "playermech_rocket_mp" ) )
finalDamage = 0;
if ( IsDefined( victim.goliathBootupSequence ) && victim.goliathBootupSequence )
{
if( IsDefined( level.isHorde ) && level.isHorde && sMeansOfDeath == "MOD_TRIGGER_HURT" && victim touchingBadTrigger() )
{
//If we've hit a death trigger (cliff jump), then make sure we deal enough damage to kill the player
finalDamage = 10000;
}
else
{
finalDamage = 0;
}
}
if ( IsDefined( eAttacker ) && !maps\mp\gametypes\_weapons::friendlyFireCheck( victim, eAttacker ) )
finalDamage = 0;
/#
if ( GetDvar( "scr_goliath_god", "0" ) != "0" )
finalDamage = 0;
#/
if ( finalDamage > 0 )
{
// HACK: have to handle friendly fire here because cac_modified_damage() is only called in Callback_PlayerDamage_internal() when it's not friendly fire.
if ( attackerIsHittingTeam( victim, eAttacker ) )
{
if ( IsDefined( level.juggernautMod ) )
{
finalDamage *= level.juggernautMod;
}
else
{
finalDamage *= 0.08;
}
}
if ( IsDefined( sHitLoc ) && sHitLoc == "head" )
{
finalDamage *= 4.0;
}
if ( IsDefined( sWeapon ) && sWeapon == "killstreak_goliathsd_mp" && IsDefined( eAttacker ) && IsDefined( victim ) && eAttacker == victim )
{
finalDamage = victim.mechHealth + 1;
}
if ( IsDefined( sWeapon ) && sWeapon == "nuke_mp" && IsDefined( eAttacker ) && IsDefined( victim ) && eAttacker != victim )
{
finalDamage = victim.mechHealth + 1;
}
victim.mechHealth -= finalDamage;
if ( isdefined ( level.isHorde ) && level.isHorde )
victim SetClientOmnvar( "ui_exo_suit_health", victim.mechHealth / victim.maxhealth );
else
victim SetClientOmnvar( "ui_exo_suit_health", victim.mechHealth / CONST_JUGG_EXO_HEALTH );
if ( IsDefined( eAttacker ) && IsPlayer( eAttacker ) )
{
if ( IsDefined( sHitLoc ) && sHitLoc == "head" )
{
eAttacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "headshot" ); // TODO: need a new hitmarker for a juggernaut headshot.
}
else
{
eAttacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "hitjuggernaut" );
}
if( victim maps\mp\gametypes\_damage::isNewAttacker(eAttacker) )
{
victim.attackerList[victim.attackerList.size] = eAttacker;
}
}
if ( victim.mechHealth < 0 )
{
if ( isdefined ( level.isHorde ) && level.isHorde )
{
self maps\mp\_snd_common_mp::snd_message( "goliath_self_destruct" );
PlayFX( getfx( "goliath_self_destruct" ), self.origin, AnglesToUp(self.angles) );
self thread [[level.hordeHandleJuggDeath]]();
}
else
victim thread playerKillHeavyExo( vPoint, eAttacker, sMeansOfDeath, sWeapon, eInflictor );
}
}
return int( finalDamage );
}
playerKillHeavyExo( point, attacker, meansOfDeath, weapon, eInflictor )
{
self notify( "killHeavyExo" );
self _enableUsability();
self AllowJump( true );
self AllowCrouch( true );
self AllowLadder( true );
self AllowMantle( true );
self SetDemiGod( false );
self.isJuggernaut = false;
damage = 1001;
if ( !IsDefined( point ) )
point = self.origin;
damaged = false;
if ( IsDefined( weapon ) && IsDefined( attacker ) && IsDefined( meansOfDeath ) && IsDefined( eInflictor ) )
damaged = self DoDamage( damage, point, attacker, eInflictor, meansOfDeath, weapon );
else if ( IsDefined( weapon ) && IsDefined( attacker ) && IsDefined( meansOfDeath ) )
damaged = self DoDamage( damage, point, attacker, undefined, meansOfDeath, weapon );
else if ( IsDefined( attacker ) && IsDefined( meansOfDeath ) )
damaged = self DoDamage( damage, point, attacker, undefined, meansOfDeath );
else if ( IsDefined( attacker ) )
damaged = self DoDamage( damage, point, attacker, undefined );
else
damaged = self DoDamage( damage, point );
Assert( damaged == true );
}
delaySetWeapon( player )
{
player endon( "death" );
player endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
killstreakWeapon = getKillstreakWeapon( "heavy_exosuit" );
player maps\mp\killstreaks\_killstreaks::takeKillstreakWeaponIfNoDupe( killstreakWeapon );
player GiveWeapon( "iw5_exominigun_mp" );
player SwitchToWeapon( "iw5_exominigun_mp" );
player notify( "waitTakeKillstreakWeapon" );
waitframe();
player SetPlayerMech( 1 );
player DisableWeaponSwitch();
}
playerCleanupOnDeath( data ) // self == player
{
self endon( "disconnect" );
self waittill( "death", attacker, meansOfDeath, weapon );
if( IsDefined(attacker) && IsPlayer(attacker) && (attacker != self) )
{
attacker incPlayerStat( "goliath_destroyed", 1 );
level thread maps\mp\gametypes\_rank::awardGameEvent( "goliath_destroyed", attacker, weapon, self, meansOfDeath );
}
if ( !isdefined ( level.isHorde ) )
self maps\mp\_events::checkVandalismMedal( attacker );
self.inLivePlayerKillstreak = undefined;
self.mechHealth = undefined;
self playerReset( data );
}
playerCleanupOnOther() // self = player
{
self endon( "death" );
self endon( "disconnect" );
self endon( "joined_team" );
self endon( "faux_spawn" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
level waittill_any( "game_ended" );
self playerResetOmnvars();
}
playerReset( data )
{
self notify( "lost_juggernaut" );
self notify( "exit_mech" );
self playerResetOmnvars();
self playerAllowDodge( true, "heavyexo" );
self playerAllowPowerSlide( true, "heavyexo" );
self playerAllowBoostJump( true, "heavyexo" );
self playerAllowHighJump( true, "heavyexo" );
self EnableOffhandSecondaryWeapons();
self EnableOffhandWeapons();
self EnableWeaponSwitch();
self SetPlayerMech( 0 );
self.restoreWeaponClipAmmo = undefined;
self.restoreWeaponStockAmmo = undefined;
self.juggernautWeak = undefined;
self.heavyExoData = undefined;
if ( IsDefined( self.juggernautAttachments ) )
self.juggernautAttachments = undefined;
foreach ( element in data.hud )
{
if ( IsDefined( element ) )
{
element.textOffline = undefined;
element.type = undefined;
element Destroy();
}
}
}
playerResetOmnvars()
{
self SetClientOmnvar( "ui_exo_suit_enabled", false );
self SetClientOmnvar( "ui_exo_suit_modules_on", 0 );
self SetClientOmnvar( "ui_exo_suit_health", 0 );
self SetClientOmnvar( "ui_exo_suit_recon_cd", 0 );
self SetClientOmnvar( "ui_exo_suit_punch_cd", 0 );
self SetClientOmnvar( "ui_exo_suit_rockets_cd", 0 );
self SetClientOmnvar( "ui_playermech_swarmrecharge", 0 );
self SetClientOmnvar( "ui_playermech_rocketrecharge", 0 );
}
playerSetJuggExoModel( data ) // self == player
{
self DetachAll();
self SetModel( "npc_exo_armor_mp_base" );
self Attach( "head_hero_cormack_sentinel_halo" );
self SetViewModel( "vm_view_arms_mech_mp" );
// self SetClothType("cloth");
if ( ( isdefined ( data ) && !data.hasManiac ) || isdefined ( level.isHorde ) )
self Attach( "npc_exo_armor_minigun_handle", "TAG_HANDLE" );
// Because bots and agents don't handle death anims right now, we'll just hide their bodies on death and play an explosion effect.
if ( IsAI( self ) )
self.hideOnDeath = true;
self notify( "goliath_equipped" );
}
playerHandleBarrel()
{
self endon( "death" );
self endon( "disconnect" );
if ( IsDefined ( level.isHorde ) && level.isHorde )
self endon ( "becameSpectator" );
self thread playerCleanupBarrel();
self NotifyOnPlayerCommand( "goliathAttack", "+attack" );
self NotifyOnPlayerCommand( "goliathAttackDone", "-attack" );
self.barrelLinker = Spawn( "script_model", self GetTagOrigin( "tag_barrel" ) );
self.barrelLinker SetModel( "generic_prop_raven" );
self.barrelLinker LinkToSynchronizedParent( self, "tag_barrel", ( 12.7, 0, -2.9 ), ( 90, 0, 0 ) );
self.barrel = Spawn( "script_model", self.barrelLinker GetTagOrigin( "j_prop_1" ) );
self.barrel SetModel( "npc_exo_armor_minigun_barrel" );
self.barrel LinkToSynchronizedParent( self.barrelLinker, "j_prop_1", ( 0, 0, 0 ), ( -90, 0, 0 ) );
if ( isdefined ( level.isHorde ) && level.isHorde && isPlayer ( self ) )
self.barrel HudOutlineEnable ( 5, true );
self.barrelLinker ScriptModelPlayAnimDeltaMotion( "mp_generic_prop_spin_02" );
self.barrelLinker ScriptModelPauseAnim( true );
while ( true )
{
self waittill( "goliathAttack" );
self.barrelLinker ScriptModelPauseAnim( false );
self waittill( "goliathAttackDone" );
self.barrelLinker ScriptModelPauseAnim( true );
}
}
playerCleanupBarrel()
{
if ( isdefined ( level.isHorde ) && level.isHorde )
self waittill_any( "death", "disconnect", "becameSpectator" );
else
self waittill_any( "death", "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self.barrel HudOutlineDisable();
self.barrel Delete();
self.barrelLinker Delete();
}
playerRocketsAndSwarmWatcher()
{
self endon( "death" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
while ( true )
{
self waittill( "grenade_pullback", weaponName );
if( weaponName == CONST_JUGG_EXO_LETHAL )
{
// Rocket
self notify( "mech_rocket_pullback" );
self waittill( "grenade_fire", missileEnt, weaponName );
self notify( "mech_rocket_fire", missileEnt );
}
else if ( weaponName == CONST_JUGG_EXO_TACTICAL || weaponName == CONST_JUGG_EXO_TACTICAL_MANIAC )
{
// Swarm Missiles
self notify( "mech_swarm_pullback" );
self waittill( "grenade_fire", missileEnt, weaponName );
self notify( "mech_swarm_fire", missileEnt.origin );
missileEnt delete();
}
waitframe();
}
}
// -------------------------------------------------------------------------------
// Attachments/Modules
setupCoopTurret( data, player )
{
startOrigin = player GetTagOrigin( "tag_turret" );
turret = spawnAttachment( "juggernaut_sentry_mg_mp", "npc_heavy_exo_armor_turret_base", startOrigin, 200, player, &"KILLSTREAKS_HEAVY_EXO_SENTRY_LOST" );
turret SetMode( "sentry_offline" );
turret SetSentryOwner( player );
turret SetLeftArc( CONST_JUGG_EXO_TURRET_HORZ_ARC );
turret SetRightArc( CONST_JUGG_EXO_TURRET_HORZ_ARC );
turret SetTopArc( CONST_JUGG_EXO_TURRET_TOP_ARC );
turret SetBottomArc( CONST_JUGG_EXO_TURRET_BOTTOM_ARC );
turret SetDefaultDropPitch( 0.0 );
turret SetTurretModeChangeWait( true );
turret MakeUnusable();
turret MakeTurretSolid();
turret.rocketTurret = false;
turret.energyTurret = false;
turret.turretType = "mg_turret";
turret.isSentry = false;
turret.stunned = false;
turret.nextTracer = 5;
turret.heatLevel = 0;
turret.baseOwner = player;
if ( level.teamBased )
turret setTurretTeam( player.team );
// turret thread maps\mp\killstreaks\_remoteturret::sentry_heatMonitor( "juggernaut_sentry_mg_mp", 4, 0.07 );
// turret thread maps\mp\killstreaks\_remoteturret::sentry_attackTargets( ::turretShoot );
turret make_entity_sentient_mp( player.team );
turret maps\mp\killstreaks\_autosentry::addToTurretList( turret GetEntityNumber() );
turret thread maps\mp\killstreaks\_remoteturret::turret_watchDisabled();
turret LinkTo( player, "tag_turret", ( 0, 0, 0 ), ( 0, 0, 0 ) );
turret.effect = spawnAttachmentEffect( startOrigin, player );
turret.effect LinkTo( turret, "tag_player", ( 29, -7, -6 ), ( 0, 0, 0 ) );
turret.effect Hide();
data.coopTurret = turret;
thread stopTurret( data, turret, player );
thread handleCoopShooting( data, turret, player );
thread handleTurretOnPlayerDone( data, turret, player );
return turret;
}
stopTurret( data, turret, player )
{
turret waittill( "death" );
if ( IsDefined( turret ) )
{
turret.isSentry = false; // so autosentry does not start up again
player notify( "turretDead" );
removeCoopTurretBuddy( data );
stopFXOnAttachment( turret, getfx( "green_light_mp" ), true );
turret PlaySound( "sentry_explode" );
turret thread maps\mp\killstreaks\_remoteturret::sentry_stopAttackingTargets();
turret maps\mp\killstreaks\_autosentry::removeFromTurretList( turret GetEntityNumber() );
turret SetMode( "sentry_offline" );
turret.damagecallback = undefined;
turret SetCanDamage( false );
turret SetDamageCallbackOn( false );
turret FreeEntitySentient();
turret setDefaultDropPitch( 35 );
turret SetSentryOwner( undefined );
level thread doTurretDeathEffects( turret );
}
}
handleCoopShooting( data, turret, player )
{
turret endon( "death" );
fireTime = weaponFireTime( "juggernaut_sentry_mg_mp" );
while ( true )
{
if ( !IsDefined( turret.remoteControlled ) || !turret.remoteControlled )
{
waitframe();
continue;
}
if ( turret.owner AttackButtonPressed() && !turret IsTurretOverheated() )
{
turret turretShootBlank( turret.baseOwner );
wait fireTime;
continue;
}
waitframe();
}
}
turretShoot() // self == turret
{
self ShootTurret();
self turretShootBlank( self.baseOwner );
}
turretShootBlank( showTo ) // self == turret
{
aimOrigin = self GetTagOrigin( "tag_flash" );
aimDir = AnglesToForward( self GetTagAngles( "tag_flash" ) );
aimEnd = aimOrigin + ( aimDir * 1000 );
drawTracer = false;
self.nextTracer--;
if ( self.nextTracer <= 0 )
{
drawTracer = true;
self.nextTracer = 5;
}
ShootBlank( aimOrigin, aimEnd, "juggernaut_sentry_mg_mp", drawTracer, showTo );
}
doTurretDeathEffects( turret )
{
turret playSound( "sentry_explode" );
PlayFXOnTag( getFx( "sentry_explode_mp" ), turret, "tag_aim" );
wait ( 1.5 );
if ( !IsDefined( turret ) )
return;
turret PlaySound( "sentry_explode_smoke" );
for ( i = 0; i < 10; i++ )
{
PlayFXOnTag( getFx( "sentry_smoke_mp" ), turret, "tag_aim" );
wait ( 0.4 );
if ( !IsDefined( turret ) )
return;
}
}
handleTurretOnPlayerDone( data, turret, player )
{
thread attachmentDeath( data, turret, player );
waittillAttachmentDone( player );
stopFXOnAttachment( turret, getfx( "green_light_mp" ) );
turret maps\mp\killstreaks\_autosentry::removeFromTurretList( turret GetEntityNumber() );
turret.isSentry = false; // so auto sentry does not start up again
player notify( "turretDead" );
removeCoopTurretBuddy( data );
turret Delete();
}
setupRadar( player, data )
{
radarOrigin = player GetTagOrigin( "tag_recon_back" );
radar = spawnAttachment( "radar", "npc_heavy_exo_armor_recon_back_base", radarOrigin, undefined, player );
radar LinkTo( player, "tag_recon_back", ( 0, 0, 0 ), ( 0, 0, 0 ) );
player thread playerHandleRadarPing( data, radar );
// thread attachmentDeath( data, radar, player );
waittillAttachmentDone( player );
// attachmentExplode( radar, player, "radar" );
waitframe();
radar Delete();
}
playerHandleRadarPing( data, radar ) // self == player
{
radar endon( "death" );
self endon( "death" );
self endon( "disconnect" );
self endon( "faux_spawn" );
self endon( "joined_team" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon ( "becameSpectator" );
if ( !IsBot( self ) )
self NotifyOnPlayerCommand( "juggernautPing", "weapnext" );
//turn on disabled state vfx
PlayFXOnTag( getfx( "exo_ping_inactive" ), self, "J_SpineUpper" );
while ( true )
{
self waittill( "juggernautPing" );
activate_exo_ping();
self SetClientOmnvar( "ui_exo_suit_recon_cd", 1 );
wait PING_DURATION;
deactivate_exo_ping();
waitAttachmentCooldown( MIN_TIME_BETWEEN_PINGS, "ui_exo_suit_recon_cd" );
}
}
activate_exo_ping() // self = player
{
self thread stop_exo_ping();
self SetPerk( "specialty_exo_ping", true, false );
// temp activation sound
self PlayLocalSound( "mp_exo_cloak_activate" );
self.highlight_effect = maps\mp\_threatdetection::detection_highlight_hud_effect_on( self, -1 );
KillFXOnTag( getfx( "exo_ping_inactive" ), self, "J_SpineUpper" );
PlayFXOnTag( getfx( "exo_ping_active" ), self, "J_SpineUpper" );
//ping overlay played in code to account for killcam
}
deactivate_exo_ping() // self = player
{
self UnSetPerk( "specialty_exo_ping", true );
// temp deactivation sound
self PlayLocalSound( "mp_exo_cloak_deactivate" );
if ( IsDefined( self.highlight_effect ) )
maps\mp\_threatdetection::detection_highlight_hud_effect_off( self.highlight_effect );
KillFXOnTag( getfx( "exo_ping_active" ), self, "J_SpineUpper" );
PlayFXOnTag( getfx( "exo_ping_inactive" ), self, "J_SpineUpper" );
}
stop_exo_ping()
{
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self waittill_any( "death", "faux_spawn", "joined_team", "becameSpectator" );
else
self waittill_any( "death", "faux_spawn", "joined_team" );
self UnSetPerk( "specialty_exo_ping", true );
if ( IsDefined( self.highlight_effect ) )
maps\mp\_threatdetection::detection_highlight_hud_effect_off( self.highlight_effect );
KillFXOnTag( getfx( "exo_ping_active" ), self, "J_SpineUpper" );
}
setupManiac( player )
{
legsOrigin = player GetTagOrigin( "tag_maniac_l" );
speedAttachment = spawnAttachment( "speedAttachment", "npc_heavy_exo_armor_maniac_l_base", legsOrigin, undefined, player );
speedAttachment LinkTo( player, "tag_maniac_l", ( 0, 0, 0 ), ( 0, 0, 0 ) );
legsOrigin = player GetTagOrigin( "tag_maniac_r" );
speedAttachmentR = spawnAttachment( "speedAttachment", "npc_heavy_exo_armor_maniac_r_base", legsOrigin, undefined, player );
speedAttachmentR LinkTo( player, "tag_maniac_r", ( 0, 0, 0 ), ( 0, 0, 0 ) );
backOrigin = player GetTagOrigin( "tag_jetpack" );
speedAttachmentB = spawnAttachment( "speedAttachment", "npc_heavy_exo_armor_jetpack_base", backOrigin, undefined, player );
speedAttachmentB LinkTo( player, "tag_jetpack", ( 0, 0, 0 ), ( 0, 0, 0 ) );
waittillAttachmentDone( player );
attachmentExplode( speedAttachment, player, "maniac", speedAttachmentR );
attachmentExplode( speedAttachmentB, player, "maniac" );
waitframe();
speedAttachment Delete();
speedAttachmentR Delete();
speedAttachmentB Delete();
}
setupLongPunch( player, data )
{
player SetLethalWeapon( CONST_JUGG_EXO_LETHAL );
player GiveWeapon( CONST_JUGG_EXO_LETHAL );
tag = "tag_origin"; // "tag_hand_armor_l";
player thread playerWatchNoobTubeUse( data );
waittillAttachmentDone( player );
}
playerWatchNoobTubeUse( data ) // self == player
{
self endon( "death" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
while ( true )
{
self waittill( "mech_rocket_fire", missileEnt );
//play lethal rocket muzzleflash
PlayFXOnTag( getfx("lethal_rocket_wv"), self, "TAG_WEAPON_RIGHT" );
thread reloadRocket( self, data );
}
}
reloadRocket( player, data )
{
player endon( "death" );
player endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
// thread playRocketReloadSound( player );
waitAttachmentCooldown( CONST_JUGG_EXO_ROCKET_RELOAD_TIME, "ui_exo_suit_punch_cd" );
}
playRocketReloadSound( player )
{
self PlayLocalSound( "orbitalsupport_reload_40mm" );
}
waitAttachmentCooldown( duration, omnvar )
{
waited = 0;
while( true )
{
wait 0.05;
waited += 0.05;
coolDown = 1 - ( waited / duration );
coolDown = clamp( coolDown, 0, 1 );
self SetClientOmnvar( omnvar, coolDown );
if ( coolDown <= 0 )
{
break;
}
}
}
setupTrophy( player, data )
{
chestOrigin = player GetTagOrigin( "j_spine4" );
trophy1 = spawnAttachment( "trophy", "npc_heavy_exo_armor_trophy_l_base", chestOrigin, undefined, player );
trophy1.stunned = false;
trophy1.ammo = 1;
trophy1 LinkTo( player, "tag_trophy_l", ( 0, 0, 0 ), ( 0, 0, 0 ) );
trophy1.weaponName = "heavy_exo_trophy_mp";
trophy1 thread maps\mp\gametypes\_equipment::trophyActive( player, undefined, true, trophy1.weaponName );
trophy1 thread maps\mp\gametypes\_equipment::trophyAddlaser( 12, ( 90, 90, 270 ) ); // , ( 150, 90, 270 ), true ); // 90 -90 270
trophy1 thread maps\mp\gametypes\_equipment::trophySetMinDot( -0.087, ( 90, 90, 270 ) ); // cos 95
level.trophies[level.trophies.size] = trophy1;
trophy2 = spawnAttachment( "trophy", "npc_heavy_exo_armor_trophy_r_base", chestOrigin, undefined, player );
trophy2.stunned = false;
trophy2.ammo = 1;
trophy2 LinkTo( player, "tag_trophy_r", ( 0, 0, 0 ), ( 0, 0, 0 ) );
trophy2.weaponName = "heavy_exo_trophy_mp";
trophy2 thread maps\mp\gametypes\_equipment::trophyActive( player, undefined, true, trophy2.weaponName );
trophy2 thread maps\mp\gametypes\_equipment::trophyAddlaser( 6, ( 260, 90, 270 ) ); // , ( 180, 90, 270 ), false ); // ( 260, -90, 270 )
trophy2 thread maps\mp\gametypes\_equipment::trophySetMinDot( -0.087, ( 260, 90, 270 ) ); // cos 95
level.trophies[level.trophies.size] = trophy2;
trophy1.otherTrophy = trophy2;
trophy2.otherTrophy = trophy1;
waittillAttachmentDone( player );
trophy1 notify( "trophyDisabled" );
trophy2 notify( "trophyDisabled" );
// attachmentExplode( trophy1, player, "trophy", trophy2 );
waitframe();
if ( IsDefined( trophy1.laserEnt ) )
trophy1.laserEnt Delete();
if ( IsDefined( trophy2.laserEnt ) )
trophy2.laserEnt Delete();
trophy1 Delete();
trophy2 Delete();
}
trophyStunBegin()
{
if ( self.stunned )
return;
self.stunned = true;
self.otherTrophy.stunned = true;
stunEnt = Spawn( "script_model", self.origin );
stunEnt SetModel( "tag_origin" );
PlayFXOnTag( getfx( "mine_stunned" ), stunEnt, "tag_origin" );
self thread trophyMoveStunEnt( stunEnt );
self waittill_notify_or_timeout( "death", 3 );
self notify( "stunEnd" );
StopFXOnTag( getfx( "mine_stunned" ), stunEnt, "tag_origin" );
waitframe();
stunEnt Delete();
if ( IsDefined( self ) )
{
self.stunned = false;
self.otherTrophy.stunned = false;
}
}
trophyMoveStunEnt( stunEnt )
{
self endon( "death" );
self endon( "stunEnd" );
while ( true )
{
stunEnt.origin = self.origin;
waitframe();
}
}
setupRocketSwarm( player, data )
{
swarmWeapon = CONST_JUGG_EXO_TACTICAL;
if ( data.hasManiac )
swarmWeapon = CONST_JUGG_EXO_TACTICAL_MANIAC;
player SetTacticalWeapon( swarmWeapon );
player GiveWeapon( swarmWeapon );
tag = "tag_origin"; // "tag_missile_pack";
startOrigin = player GetTagOrigin( tag );
rocketAttachment = spawnAttachment( "rocketAttachment", "npc_heavy_exo_armor_missile_pack_base", startOrigin, undefined, player );
rocketAttachment.lockedTarget = false;
rocketAttachment.reloading = false;
rocketAttachment.rockets = [];
rocketAttachment.icons = [];
rocketAttachment LinkTo( player, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
rocketAttachment Hide(); // temp
player.rocketAttachment = rocketAttachment;
thread scanForRocketEnemies( rocketAttachment, player );
player thread playerWatchRocketUse( rocketAttachment, data );
waittillAttachmentDone( player, rocketAttachment );
// attachmentExplode( rocketAttachment, player, "rockets" );
waitframe();
rocketAttachment Delete();
player.rocketAttachment = undefined;
}
scanForRocketEnemies( rocketAttachment, streakPlayer )
{
streakPlayer endon( "death" );
streakPlayer endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
streakplayer endon( "becameSpectator" );
while ( true )
{
waitframe();
if ( rocketAttachment.reloading || rocketAttachment.rockets.size > 0 || rocketAttachment.lockedTarget )
continue;
bestEnemy = getBestEnemy( streakPlayer, 4 );
// if ( IsDefined( enemy ) )
// Sphere( enemy GetEye(), 10, ( 0, 1, 0 ) );
if ( IsDefined( bestEnemy ) )
{
if ( !IsDefined( rocketAttachment.enemyTarget ) || rocketAttachment.enemyTarget != bestEnemy )
thread markPlayerAsRocketTarget( rocketAttachment, streakPlayer, bestEnemy );
}
else if ( IsDefined( rocketAttachment.enemyTarget ) )
{
rocketAttachment notify( "unmark" );
rocketAttachment.enemyTarget = undefined;
}
}
}
playerIsRocketSwarmReloading()
{
return ( IsDefined( self.rocketAttachment ) && IsDefined( self.rocketAttachment.reloading ) && self.rocketAttachment.reloading );
}
playerIsRocketSwarmTargetLocked()
{
return ( IsDefined( self.rocketAttachment ) && IsDefined( self.rocketAttachment.enemyTarget ) );
}
getBestEnemy( player, numSightTracesTotal )
{
cos32 = 0.8433914458; // view fov
playerDir = AnglesToForward( player GetPlayerAngles() );
playerEye = player GetEye();
bestEnemy = undefined;
bestEnemies = [];
foreach ( guy in level.participants )
{
if ( guy.team == player.team )
continue;
if ( !isReallyAlive( guy ) )
continue;
enemyEye = guy GetEye();
dirToEnemy = VectorNormalize( enemyEye - playerEye );
dot = VectorDot( playerDir, dirToEnemy );
if ( dot > cos32 )
{
bestEnemies[bestEnemies.size] = guy;
guy.dot = dot;
guy.checked = false;
}
}
if ( bestEnemies.size == 0 )
return;
numSightTraces = 0;
while ( numSightTraces < numSightTracesTotal && numSightTraces < bestEnemies.size )
{
nextEnemy = getHighestDot( bestEnemies );
nextEnemy.checked = true;
start = playerEye;
end = nextEnemy GetEye();
passed = SightTracePassed( start, end, true, player, nextEnemy );
if ( passed )
{
bestEnemy = nextEnemy;
// Line( start + ( 0, 0, -5 ), end, ( 0, 1, 0 ) );
break;
}
// else
// {
// Line( start + ( 0, 0, -5 ), end, ( 1, 0, 0 ) );
// }
numSightTraces++;
}
foreach ( guy in level.participants )
{
guy.dot = undefined;
guy.checked = undefined;
}
return bestEnemy;
}
getHighestDot( enemies )
{
if ( enemies.size == 0 )
return;
bestEnemy = undefined;
bestDot = 0;
foreach ( enemy in enemies )
{
if ( !enemy.checked && enemy.dot > bestDot )
{
bestEnemy = enemy;
bestDot = enemy.dot;
}
}
return bestEnemy;
}
playerWatchRocketUse( rocketAttachment, data ) // self == player
{
rocketAttachment endon( "death" );
while ( true )
{
self waittill( "mech_swarm_fire", origin );
if ( rocketAttachment.reloading || rocketAttachment.lockedTarget )
{
waitframe();
continue;
}
// if ( IsDefined( rocketAttachment.enemyTarget ) )
{
thread handleLockedTarget( rocketAttachment, data );
thread reloadRocketSwarm( rocketAttachment, self, data );
thread fireRocketSwarm( rocketAttachment, self, origin );
}
}
}
handleLockedTarget( rocketAttachment, data )
{
rocketAttachment endon( "death" );
rocketAttachment.lockedTarget = true;
rocketAttachment notify( "lockedTarget" );
waittillRocketsExploded( rocketAttachment );
if ( IsDefined( rocketAttachment ) )
{
rocketAttachment.lockedTarget = false;
rocketAttachment.enemyTarget = undefined;
}
}
fireRocketSwarm( rocketAttachment, streakPlayer, origin )
{
playerDir = AnglesToForward( streakPlayer GetPlayerAngles() );
playerRight = AnglesToRight( streakPlayer GetPlayerAngles() );
offsets = [ ( 0, 0, 50 ), ( 0, 0, 20 ), ( 10, 0, 0 ), ( 0, 10, 0 ) ];
//3P muzzleflash fx
PlayFXOnTag( getfx("swarm_rocket_wv"), streakPlayer, "TAG_ROCKET4" );
for ( i = 0; i < 4; i++ )
{
//push it out from the player and to the left
initOrigin = origin + ( playerDir * 20 ) + ( playerRight * -30 );
fireDirection = playerDir + random_vector( 0.2 );
rocket = MagicBullet( "iw5_juggernautrockets_mp", initOrigin, initOrigin + fireDirection, streakPlayer );
rocketAttachment.rockets = array_add( rocketAttachment.rockets, rocket );
rocket thread rocketTargetEnt( rocketAttachment, rocketAttachment.enemyTarget, offsets[i] );
rocket thread rocketDestroyAfterTime( 7 );
}
}
rocketTargetEnt( rocketAttachment, target, offset ) // self == rocket
{
rocketAttachment endon( "death" );
if ( IsDefined( target ) )
self Missile_SetTargetEnt( target, offset );
self waittill( "death" );
rocketAttachment.rockets = array_remove( rocketAttachment.rockets, self );
}
rocketDestroyAfterTime( time )
{
self endon( "death" );
wait time;
self Delete();
}
reloadRocketSwarm( rocketAttachment, player, data )
{
rocketAttachment endon( "death" );
rocketAttachment.reloading = true;
// thread playRocketSwarmReloadSound( rocketAttachment, player, CONST_JUGG_EXO_ROCKET_SWARM_RELOAD_TIME );
waitAttachmentCooldown( CONST_JUGG_EXO_ROCKET_SWARM_RELOAD_TIME, "ui_exo_suit_rockets_cd" );
rocketAttachment.reloading = false;
}
playRocketSwarmReloadSound( rocketAttachment, player, reloadTime )
{
rocketAttachment endon( "death" );
missileCount = 3;
self PlayLocalSound( "warbird_missile_reload_bed" );
wait 0.5;
for ( i = 0; i < missileCount; i++ )
{
self PlayLocalSound( "warbird_missile_reload" );
wait( reloadTime / missileCount );
}
}
markPlayerAsRocketTarget( rocketAttachment, streakPlayer, markedPlayer )
{
markedPlayer endon( "disconnect" );
rocketAttachment notify( "mark" );
rocketAttachment endon( "mark" );
rocketAttachment endon( "unmark" );
offset = ( 0, 0, 60 );
entNum = markedPlayer GetEntityNumber();
rocketAttachment.enemyTarget = markedPlayer;
if ( isdefined ( level.isHorde ) && level.isHorde )
{
markedPlayer HudOutlineEnableForClient( streakPlayer, 1, 0 );
streakPlayer.markedForMech [ streakPlayer.markedForMech.size ] = markedPlayer;
}
else
markedPlayer HudOutlineEnableForClient( streakPlayer, 4, 0 );
thread cleanupRocketTargetIcon( rocketAttachment, markedPlayer, streakPlayer );
rocketAttachment waittill( "lockedTarget" );
markedPlayer HudOutlineEnableForClient( streakPlayer, 0, 0 );
waittillRocketsExploded( rocketAttachment );
if ( isdefined ( level.isHorde ) && level.isHorde )
{
if ( level.currentAliveEnemyCount < 3 )
{
if ( ( level.objDefend && distancesquared ( streakPlayer.origin, level.currentDefendLoc.origin ) > 640000 ) )
markedPlayer HudOutlineEnableForClient ( streakPlayer, level.enemyOutlineColor, false );
streakPlayer.markedForMech = array_remove ( streakPlayer.markedForMech, markedPlayer );
}
else
{
markedPlayer HudOutlineDisableForClient( streakPlayer );
streakPlayer.markedForMech = array_remove ( streakPlayer.markedForMech, markedPlayer );
}
}
else
markedPlayer HudOutlineDisableForClient( streakPlayer );
}
cleanupRocketTargetIcon( rocketAttachment, markedPlayer, streakPlayer )
{
markedPlayer endon( "disconnect" );
waittillUnmarkPlayerAsRocketTarget( rocketAttachment );
if ( isdefined ( level.isHorde ) && level.isHorde && isdefined ( streakPlayer ) )
{
if ( level.currentAliveEnemyCount < 3 )
{
if ( ( level.objDefend && distancesquared ( streakPlayer.origin, level.currentDefendLoc.origin ) > 640000 ) )
markedPlayer HudOutlineEnableForClient ( streakPlayer, level.enemyOutlineColor, false );
streakPlayer.markedForMech = array_remove ( streakPlayer.markedForMech, markedPlayer );
}
else
{
markedPlayer HudOutlineDisableForClient( streakPlayer );
streakPlayer.markedForMech = array_remove ( streakPlayer.markedForMech, markedPlayer );
}
}
else if ( IsDefined( streakPlayer ) )
markedPlayer HudOutlineDisableForClient( streakPlayer );
}
waittillUnmarkPlayerAsRocketTarget( rocketAttachment )
{
rocketAttachment.enemyTarget endon( "death" );
rocketAttachment waittill_any( "death", "mark", "unmark" );
}
waittillRocketsExploded( rocketAttachment )
{
wait 0.1;
while ( IsDefined( rocketAttachment ) && rocketAttachment.rockets.size > 0 )
waitframe();
}
// -----------------------------------------------------------------------------------------------------
// Utilities
waittillAttachmentDone( player, attachment1, attachment2 )
{
player endon( "disconnect" );
player endon( "death" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
if ( IsDefined( attachment1 ) )
attachment1 endon( "death" );
if ( IsDefined( attachment2 ) )
attachment2 endon( "death" );
player waittill( "forever" );
}
delayPlayFX( ent, fx )
{
ent endon( "death" );
waitframe();
waitframe();
PlayFXOnTag( fx, ent, "tag_origin" );
}
stopFXOnAttachment( attachment, fx, doSparksFx )
{
if ( IsDefined( attachment.effect ) )
{
StopFXOnTag( fx, attachment.effect, "tag_origin" );
if ( IsDefined( doSparksFx ) && doSparksFx )
PlayFX( getfx( "juggernaut_sparks" ), attachment.effect.origin );
attachment.effect Delete();
}
}
attachmentDeath( data, attachment, player, attachment2 )
{
player endon( "death" );
player endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
if ( IsDefined( attachment2 ) )
attachment2 endon( "death" );
attachment waittill( "death", attacker, meansOfDeath, weapon );
if ( IsDefined( attacker ) && IsPlayer( attacker ) )
{
splash = level.juggSettings[ data.juggType ].splashAttachmentName;
if ( IsSubStr( attachment.attachmentType, "weakSpot" ) )
splash = level.juggSettings[ data.juggType ].splashWeakenedName;
teamPlayerCardSplash( splash, attacker );
}
}
attachmentExplode( attachment1, player, type, attachment2 )
{
if ( IsDefined( player ) )
{
if ( IsAlive( player ) )
{
player thread playerPlayAttachmentDialog( attachment1.attachmentType );
}
if ( IsDefined( attachment1 ) )
PlayFX( getfx( "juggernaut_sparks" ), attachment1.origin );
if ( IsDefined( attachment2 ) )
PlayFX( getfx( "juggernaut_sparks" ), attachment2.origin );
player PlaySound( "sentry_explode" );
}
}
HideFromPlayer( playerToHideFrom ) // self == entity
{
self Hide();
foreach ( player in level.players )
{
if ( player != playerToHideFrom )
self ShowToPlayer( player );
}
}
HideFromPlayers( playersToHideFrom ) // self == entity
{
self Hide();
foreach ( player in level.players )
{
if ( !array_contains( playersToHideFrom, player ) )
self ShowToPlayer( player );
}
}
spawnAttachment( type, modelName, startOrigin, health, player, eventString )
{
attachment = undefined;
if ( IsSubStr( type, "sentry" ) )
attachment = SpawnTurret( "misc_turret", startOrigin, type );
else
attachment = Spawn( "script_model", startOrigin );
attachment SetModel( modelName );
attachment.attachmentType = type;
if ( IsDefined( health ) )
{
attachment.health = health;
attachment.maxhealth = attachment.health;
attachment.damagecallback = ::handleAttachmentDamage;
if ( IsDefined( eventString ) )
attachment thread handleAttachmentDeath( type, player, eventString );
attachment SetDamageCallbackOn( true );
//attachment SetCanDamage( true );
}
attachment HideFromPlayer( player );
attachment.owner = player;
if ( level.teamBased )
attachment.team = player.team;
// for debug
/#
if ( !IsDefined( player.juggernautAttachments ) )
player.juggernautAttachments = [];
else
player.juggernautAttachments = array_removeUndefined( player.juggernautAttachments );
player.juggernautAttachments[ player.juggernautAttachments.size ] = attachment;
#/
return attachment;
}
spawnAttachmentEffect( startOrigin, player, isWeakSpot )
{
if ( !IsDefined( isWeakSpot ) )
isWeakSpot = false;
effect = Spawn( "script_model", startOrigin );
effect SetModel( "tag_origin" );
effect HideFromPlayer( player );
thread delayPlayFX( effect, getfx( "green_light_mp" ) );
return effect;
}
handleAttachmentDeath( type, player, eventString )
{
if ( type == "weakSpotHead" )
return;
level endon( "game_ended" );
self waittill( "death", attacker, meansOfDeath, weapon );
if ( !IsDefined( attacker ) || !IsPlayer( attacker ) || ( IsDefined( player ) && attacker == player ) )
return;
level thread maps\mp\gametypes\_rank::awardGameEvent( "heavy_exo_attachment", attacker, undefined, undefined, undefined, eventString );
}
handleAttachmentDamage( inflictor, attacker, damage, iDFlags, meansOfDeath, weapon, point, direction_vec, hitLoc, timeOffset, modelIndex, partName )
{
if ( !IsDefined( self.lastTimeDamaged ) )
self.lastTimeDamaged = 0;
finalDamage = damage;
// don't allow people to destroy equipment on their team if FF is off
if ( IsDefined( attacker ) && !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) || attacker == self.owner || self.lastTimeDamaged == GetTime() )
{
finalDamage = 0;
}
else
{
if ( IsDefined( weapon ) && weapon == "boost_slam_mp" && damage > 10 )
finalDamage = 10;
if ( isMeleeMOD(meansOfDeath) )
finalDamage += self.maxHealth;
if ( IsDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
self.wasDamagedFromBulletPenetration = true;
self.wasDamaged = true;
self.damageFade = 0.0;
if( IsPlayer( attacker ) )
{
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
{
finalDamage = finalDamage * level.armorPiercingMod;
}
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "juggernautAttachment" );
attacker notify( "hitHeavyExoAttachment" );
self.lastAttacker = attacker;
}
if( IsDefined( weapon ) )
{
shortWeapon = maps\mp\_utility::strip_suffix( weapon, "_lefthand" );
switch( shortWeapon )
{
case "ac130_105mm_mp":
case "ac130_40mm_mp":
case "stinger_mp":
case "remotemissile_projectile_mp":
self.largeProjectileDamage = true;
finalDamage = self.maxHealth + 1;
break;
case "artillery_mp":
case "stealth_bomb_mp":
self.largeProjectileDamage = false;
finalDamage += ( damage * 4 );
break;
case "bomb_site_mp":
case "emp_grenade_mp":
case "emp_grenade_var_mp":
case "emp_grenade_killstreak_mp":
self.largeProjectileDamage = false;
finalDamage = self.maxHealth + 1;
break;
}
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
}
}
self.lastTimeDamaged = GetTime();
self FinishEntityDamage( inflictor, attacker, finalDamage, iDFlags, meansOfDeath, weapon, point, direction_vec, hitLoc, timeOffset, modelIndex, partName );
}
random_vector( num )
{
return( RandomFloat( num ) - num * 0.5, RandomFloat( num ) - num *0.5, RandomFloat( num ) - num * 0.5 );
}
// -----------------------------------------------------------------------------------------------------
// Coop Logic
handleCoopJoining( data, player )
{
while ( true )
{
id = maps\mp\killstreaks\_coop_util::promptForStreakSupport( player.team, &"MP_JOIN_HEAVY_EXO", "heavy_exosuit_coop_offensive", "assist_mp_goliath", "copilot_mp_goliath", player );
level thread watchForJoin( id, player, data );
result = waittillPromptComplete( player, "buddyJoinedStreak" ); // , "turretBeingHacked", "turretStunned" );
maps\mp\killstreaks\_coop_util::stopPromptForStreakSupport( id );
if ( !IsDefined( result ) )
return;
result = waittillPromptComplete( player, "buddyLeftCoopTurret" ); // , "turretBeingHacked", "turretStunned" );
if ( !IsDefined( result ) )
return;
}
}
waittillPromptComplete( player, text, text2, text3 )
{
player endon( "death" );
player endon( "disconnect" );
player endon( "turretDead" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
return player waittill_any_return_no_endon_death( text, text2, text3 );
}
waittillTurretStunComplete( data, player )
{
player endon( "death" );
player endon( "disconnect" );
player endon( "turretDead" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
while ( true )
{
waitframe();
if ( data.coopTurret.stunned || data.coopTurret.directHacked )
continue;
return true;
}
}
//playerNotifyOnTurretStun( data )
//{
// self endon( "death" );
// self endon( "disconnect" );
// self endon( "turretDead" );
// data.coopTurret endon( "death" );
//
// while ( true )
// {
// result = data.coopTurret waittill_any_return_no_endon_death( "stunned", "beingHacked" );
//
// if ( result == "stunned" )
// self notify( "turretStunned" );
// else // if ( result == "beingHacked" )
// self notify( "turretBeingHacked" );
//
// }
//}
watchForJoin( id, player, data )
{
player endon( "disconnect" );
player endon( "death" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
buddy = maps\mp\killstreaks\_coop_util::waittillBuddyJoinedStreak( id );
player notify( "buddyJoinedStreak" );
buddy thread playerRemoteCoopTurret( data );
}
playerRemoteCoopTurret( data )
{
self endon( "disconnect" );
data.coopTurret endon( "death" );
data.streakPlayer endon( "death" );
data.streakPlayer endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
{
self endon( "becameSpectator" );
data.streakPlayer endon( "becameSpectator" );
}
data.coopTurret SetSentryOwner( undefined );
data.coopTurret SetSentryOwner( self );
data.coopTurret.owner = self;
data.coopTurret.effect HideFromPlayers( [ self, data.streakPlayer ] );
self.using_remote_turret = true;
data.coopTurret maps\mp\killstreaks\_remoteturret::startUsingRemoteTurret( CONST_JUGG_EXO_TURRET_HORZ_ARC, CONST_JUGG_EXO_TURRET_HORZ_ARC, CONST_JUGG_EXO_TURRET_TOP_ARC, CONST_JUGG_EXO_TURRET_BOTTOM_ARC, true );
self thread removeCoopTurretBuddyOnDisconnect( data );
data.coopTurret maps\mp\killstreaks\_remoteturret::waittillRemoteTurretLeaveReturn();
removeCoopTurretBuddy( data );
}
removeCoopTurretBuddyOnDisconnect( data )
{
data.coopTurret endon( "removeCoopTurretBuddy" );
self waittill( "disconnect" );
thread removeCoopTurretBuddy( data );
}
removeCoopTurretBuddy( data )
{
Assert( IsDefined( data.coopTurret ) );
if ( !IsDefined( data.coopTurret.remoteControlled ) )
return;
data.coopTurret notify( "removeCoopTurretBuddy" );
data.coopTurret.remoteControlled = undefined;
buddy = data.coopTurret.owner;
if ( IsDefined( buddy ) )
{
buddy.using_remote_turret = undefined;
data.coopTurret maps\mp\killstreaks\_remoteTurret::stopUsingRemoteTurret( false );
}
else if ( IsAlive( data.coopTurret ) )
{
// data.coopTurret thread maps\mp\killstreaks\_remoteturret::sentry_attackTargets( ::turretShoot );
}
buddy EnableWeaponSwitch();
if ( IsDefined( data.streakPlayer ) && isReallyAlive( data.streakPlayer ) )
{
if ( IsDefined( data.coopTurret.effect ) )
data.coopTurret.effect Hide();
data.coopTurret SetSentryOwner( undefined );
data.coopTurret SetSentryOwner( data.streakPlayer );
data.coopTurret.owner = data.streakPlayer;
data.streakPlayer notify( "buddyLeftCoopTurret" );
}
}
playerShowJuggernautHud( data ) // self == player
{
data.hud = [];
self thread playerWatchEMP( data );
createJuggernautOverlay( data );
}
createJuggernautOverlay( data ) // self == player
{
// if ( getDvarInt( "camera_thirdPerson" ) )
// return;
self SetClientOmnvar( "ui_exo_suit_enabled", true );
self thread playermech_state_manager();
}
playerWatchEMP( data )
{
self endon( "death" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
while ( true )
{
self waittill_any( "emp_grenaded", "applyEMPkillstreak", "directHackStarted" );
foreach ( element in data.hud )
element.alpha = 0;
while ( true )
{
self waittill_any( "empGrenadeTimedOut", "removeEMPkillstreak", "directHackTimedOut" );
waitframe();
if ( playerShouldShowHUD() )
break;
}
foreach ( element in data.hud )
{
if ( element.type != "rocketReload" )
{
element FadeOverTime( 0.5 );
element.alpha = 1;
}
}
}
}
playerShouldShowHUD()
{
return ( ( !IsDefined( self.empGrenaded ) || !self.empGrenaded ) || ( !IsDefined( self.empOn ) || !self.empOn ) );
}
playerPlayAttachmentDialog( attachmentType )
{
dialogRef = undefined;
switch ( attachmentType )
{
case "juggernaut_sentry_mp_mp":
dialogRef = "sntryoff_mp_exoai";
break;
case "speedAttachment":
dialogRef = "mancoff_mp_exoai";
break;
case "punchAttachment":
dialogRef = "longoff_mp_exoai";
break;
case "radar":
dialogRef = "rcnoff_mp_exoai";
break;
case "rocketAttachment":
dialogRef = "rcktoff_mp_exoai";
break;
case "trophy":
dialogRef = "trphyoff_mp_exoai";
break;
default:
dialogRef = "weakdmg_mp_exoai";
break;
}
self leaderDialogOnPlayer( dialogRef );
}
playerLaunchDropPod( modules ) // self = player or bot
{
outsideNode = self playerGetOutsideNode();
if ( !IsDefined( outsideNode ) )
{
self thread playerPlayInvalidPositionEffect( getfx( "ocp_ground_marker_bad" ) );
self SetClientOmnvar( "ui_invalid_goliath", 1 );
return false;
}
self thread fireDropPod( outsideNode, modules );
return true;
}
dropPodMoveNearbyAllies( player ) // self = drop pod
{
if ( !IsDefined( self ) || !IsDefined( player ) )
return;
// Don't let unresolved_collision_nearest_node() below calculate the nodes
// A min radius is necessary because the pod uses patches and an invalid node could be selected
self.unresolved_collision_nodes = GetNodesInRadius( self.origin, 300, 80, 200 );
foreach ( character in level.characters )
{
if ( !IsAlive( character ) )
continue;
if ( IsAlliedSentient( character, player ) )
{
// Any allies that are touching the pod should be teleported out of its way
// IsTouching() doesn't work all the time because the pod uses patches
// if ( character IsTouching( self ) )
if ( DistanceSquared( self.origin, character.origin ) < CONST_JUGG_EXO_USABILITY_RADIUS_SQ )
{
self maps\mp\_movers::unresolved_collision_nearest_node( character, true );
}
}
}
}
fireDropPod( node, modules ) // self = player or bot
{
startPos = self playerGetOrbitalStartPos( node );
targetPos = node.origin;
podrocket = MagicBullet( "orbital_carepackage_droppod_mp", startPos, targetPos, self );
podrocket.team = self.team;
podrocket.killCamEnt = Spawn( "script_model", (0,0,0) );
podrocket.killCamEnt LinkToSynchronizedParent( podrocket, "tag_origin", (0,0,200), (0,10,10) );
podrocket.killCamEnt.targetname = "killCamEnt_goliath_droppod";
podrocket.killCamEnt SetScriptMoverKillCam( "missile" );
podrocket thread maps\mp\_load::deleteDestructibleKillCamEnt();
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
Objective_Add( curObjID, "invisible", ( 0, 0, 0 ) );
Objective_Position( curObjID, targetPos );
Objective_State( curObjID, "active" );
shaderName = "compass_waypoint_farp";
Objective_Icon( curObjID, shaderName );
marker = Spawn( "script_model", targetPos + ( 0, 0, 5 ) );
marker.angles = ( -90, 0, 0 );
marker SetModel( "tag_origin" );
marker Hide();
marker ShowToPlayer( self );
PlayFXOnTag( getfx( "jugg_droppod_marker" ), marker, "tag_origin" );
maps\mp\killstreaks\_orbital_util::addDropMarker( marker );
//Track if this is a goliath called as a built-in heavy scoretreak in co-op so we can start the cool-down on pod-timeout
//and also start cooldown on the owner when another player picks up the built-in scorestreak goliath
hordeClassGoliath = false;
if ( isdefined ( level.isHorde ) && level.isHorde )
{
if ( self.killstreakIndexWeapon == 1 )
{
self notify ( "used_horde_goliath" );
hordeClassGoliath = true;
self.hordeClassGoliathPodInField = true;
}
self.hordeGoliathPodInField = true;
}
podrocket waittill( "death" );
// pod may not have landed on a valid spawn location, so adjust target
targetPos = GetGroundPosition( podrocket.origin + ( 0, 0, 8 ), 20 );
thread destroy_nearby_turrets( targetPos );
marker Hide();
Earthquake( 0.4, 1, targetPos, 800 );
PlayRumbleOnPosition( "artillery_rumble", targetPos );
StopFXOnTag( getfx( "jugg_droppod_marker" ), marker, "tag_origin" );
useEnt = Spawn( "script_model", targetPos );
useEnt.angles = ( 0, 0, 0 );
useEnt createCollision( targetPos );
useEnt.targetname = "care_package";
useEnt.droppingToGround = false;
useEnt.curObjID = curObjID;
goliathPodModel = Spawn( "script_model", targetPos );
goliathPodModel.angles = ( 90, 0, 0 );
goliathPodModel.targetname = "goliath_pod_model";
goliathPodModel SetModel( "vehicle_drop_pod" );
goliathPodModel thread handle_goliath_drop_pod_removal( useEnt );
if ( IsDefined( self ) )
useEnt.owner = self;
useEnt.crateType = "juggernaut";
useEnt.dropType = "juggernaut";
useEnt thread control_goliath_usability();
useEnt SetHintString( &"KILLSTREAKS_HEAVY_EXO_PICKUP" );
useEnt thread maps\mp\killstreaks\_airdrop::crateOtherCaptureThink(); // these threads might never end.
useEnt thread maps\mp\killstreaks\_airdrop::crateOwnerCaptureThink();
useEnt thread useGoliathUpdater();
// Handle kill triggers?
// Moving platforms.
data = SpawnStruct();
//data.emptyMech = emptyMech;
data.useEnt = useEnt;
data.playDeathFx = true;
data.deathOverrideCallback = ::movingPlatformDeathFunc;
data.touchingPlatformValid = ::movingPlatformTouchValid;
useEnt thread maps\mp\_movers::handle_moving_platforms( data );
useEnt thread handle_goliath_drop_pod_timeout( hordeClassGoliath );
useEnt dropPodMoveNearbyAllies( self );
if ( isdefined ( level.isHorde ) && level.isHorde )
{
if ( level.zombiesStarted || level.teamEMPed["allies"] )
useEnt deleteGoliathPod();
else
useent thread delete_goliath_drop_pod_for_event();
}
activator = useEnt playerWaittillGoliathActivated();
if ( isdefined ( level.isHorde ) && level.isHorde )
{
//this is for tracking death of a goliath called as a built-in class goliath for the heavy in
//co-op to start the cool-down regardless of who picked up the goliath. No matter who takes
//control, we wait for this goliath to die to start the cool-down
if ( isdefined ( activator ) && activator != self )
{
if ( hordeClassGoliath )
{
activator.hordeClassGoliathOwner = self;
self.hordeClassGoliathController = activator;
}
else
{
activator.hordeGoliathOwner = self;
self.hordeGoliathController = activator;
}
activator [[level.lastStandSaveLoadoutInfo]]( true, true, true );
}
else
{
//save our loadout info so we get the right stuff back when we come back from spectator
self [[level.lastStandSaveLoadoutInfo]]( true, true, true );
}
self.hordeClassGoliathPodInField = undefined;
self.hordeGoliathPodInField = undefined;
}
if ( IsDefined( activator ) && IsAlive( activator ) )
{
activator.enteringGoliath = true;
activator TakeAllWeapons();
activator GiveWeapon( "iw5_combatknifegoliath_mp", 0, false, 0, true );
activator SwitchToWeapon( "iw5_combatknifegoliath_mp" );
// unlink from pod before freezing controls
activator Unlink();
activator freezeControlsWrapper( true );
goliath_to_player_vector = targetPos - activator.origin;
goliath_to_player_angles = VectorToAngles( goliath_to_player_vector );
goliath_angles = ( 0, goliath_to_player_angles[1], 0 );
drop_pod_vfx_forward_vector = RotateVector( goliath_to_player_vector, ( 45, 0, 0 ) );
emptyMech = Spawn( "script_model", targetPos );
emptyMech.angles = goliath_angles;
emptyMech SetModel( "npc_exo_armor_ingress" );
emptyMech ScriptModelPlayAnimDeltaMotion( "mp_goliath_spawn" );
activator maps\mp\_snd_common_mp::snd_message( "goliath_pod_burst" );
// remove collision so player can go inside goliath
if ( IsDefined( useEnt ) )
useEnt deleteGoliathPod( false );
PlayFX( level._effect[ "jugg_droppod_open" ], targetPos, drop_pod_vfx_forward_vector );
// Wait for the "mp_goliath_spawn" animation on the "npc_exo_armor_ingress" to finish.
wait( 0.1 ); //was 0.6
// Lock the player's controls and wait until they animate into the Goliath.
activator is_entering_goliath( emptyMech, targetPos );
if ( IsDefined( activator ) && IsAlive( activator ) )
{
activator SetOrigin( targetPos, true );
activator SetPlayerAngles( emptyMech.angles );
activator EnableWeapons();
activator giveJuggernaut( "juggernaut_exosuit", modules );
// we now have player goliath model, remove empty ones
emptyMech Delete();
// ideally, this anim should be as long as vm "weapon raise" anim
activator PlayGoliathToIdleAnim();
// wait for vm "weapon raise" anim and 3rd-person "to idle" to finish
wait( 1 );
activator.enteringGoliath = undefined;
activator freezeControlsWrapper( false );
if ( isdefined ( level.isHorde ) && level.isHorde )
activator HudOutlineEnable ( 5, true );
}
else
{
emptyMech Delete();
}
}
marker Delete();
}
destroy_nearby_turrets( org )
{
dist_sq = 64 * 64;
foreach( player in level.players )
{
if( IsDefined( player.turret ) && DistanceSquared( player.turret.origin, org ) <= dist_sq )
{
player.turret notify( "death" );
}
}
}
is_goliath_drop_pod( crate )
{
return IsDefined(crate.crateType) && (crate.crateType == "juggernaut") && IsDefined(crate.dropType) && crate.dropType == "juggernaut";
}
movingPlatformDeathFunc( data )
{
if ( IsDefined( data.emptyMech ) )
data.emptyMech Delete();
if ( IsDefined( data.useEnt ) )
data.useEnt Delete();
}
movingPlatformTouchValid( platform )
{
return ( self goliathAndCarepackageValid( platform ) && self goliathAndGoliathValid( platform ) && goliathAndPlatformValid( platform ) );
}
goliathAndCarepackageValid( platform )
{
return ( !IsDefined( self.crateType )|| !IsDefined( platform.targetname ) || self.crateType != "juggernaut" || platform.targetname != "care_package" );
}
goliathAndGoliathValid( platform )
{
return ( !IsDefined( self.crateType )|| !IsDefined( platform.crateType ) || self.crateType != "juggernaut" || platform.crateType != "juggernaut" );
}
goliathAndPlatformValid( platform )
{
return ( !IsDefined( self.crateType ) || !IsDefined( platform.carepackageTouchValid ) || self.crateType != "juggernaut" || !platform.carepackageTouchValid );
}
control_goliath_usability() // self = useEnt
{
self endon( "captured" );
self endon( "death" );
level endon( "game_ended" );
self MakeUsable();
foreach ( player in level.players )
{
// Disable usability for all players.
self DisablePlayerUse( player );
}
while ( true )
{
foreach ( player in level.players )
{
enable_goliath_use = false;
if ( player IsOnGround() && !player IsOnLadder() && !player IsJumping() && !player IsMantling() && isReallyAlive( player ) && player GetStance() == "stand" )
{
if ( DistanceSquared( self.origin, player.origin ) < CONST_JUGG_EXO_USABILITY_RADIUS_SQ )
{
if ( player WorldPointInReticle_Rect( self.origin + (0,0,50), 65, 400, 600 ) )
{
//if ( VectorDot( AnglesToForward( self.angles ), AnglesToForward( player.angles ) ) > 0.7 )
//
enable_goliath_use = true;
//}
}
}
}
if ( enable_goliath_use == true )
{
self EnablePlayerUse( player );
}
else
{
self DisablePlayerUse( player );
}
}
wait( 0.2 );
}
}
is_entering_goliath( goliathModel, targetPos ) // self = player
{
goliathForward = AnglesToForward( goliathModel.angles );
targetPos = targetPos - ( goliathForward * 37 );
self SetOrigin( targetPos, false );
self SetPlayerAngles( goliathModel.angles );
// allow a few frames to lerp to position
wait( 0.05 );
goliathModel ScriptModelPlayAnimDeltaMotion( "mp_goliath_enter" );
self PlayGoliathEntryAnim();
//self waittill( "goliath_entry_complete" );
wait( 2.3 );
}
createCollision( targetPos ) // self = useEnt
{
collision = GetEnt( "goliath_collision", "targetname" );
if ( IsDefined( collision ) )
{
self CloneBrushmodelToScriptmodel( collision );
}
}
playerWaittillGoliathActivated()
{
self endon( "death" );
self waittill( "captured", player );
player SetStance( "stand" );
// Setting demigod here because it would be shitty for the player to die while getting into the Goliath.
player SetDemiGod( true );
if ( isDefined( self.owner ) && player != self.owner )
{
if ( !level.teamBased || player.team != self.owner.team )
{
player thread maps\mp\_events::hijackerEvent( self.owner );
}
else
{
if ( !isdefined ( level.isHorde ) )
self.owner thread maps\mp\_events::sharedEvent();
}
}
return player;
}
useGoliathUpdater() // self == useEnt
{
self endon( "death" );
level endon( "game_ended" );
foreach ( player in level.players )
{
if ( player isJuggernaut() )
{
self DisablePlayerUse( player );
self thread usePostJuggernautUpdater( player );
}
}
while ( true )
{
level waittill ( "juggernaut_equipped", player );
self disablePlayerUse( player );
self thread usePostJuggernautUpdater( player );
}
}
usePostJuggernautUpdater( player ) // self == useEnt
{
self endon( "death" );
level endon( "game_ended" );
player endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
player waittill( "death" );
self enablePlayerUse( player );
}
// ------------------------------------------------------------------
// Debug Functions
adjustLink( item, tagName, player, startOrigin, startAngles )
{
item endon( "death" );
if ( !IsDefined( startOrigin ) )
startOrigin = ( 0, 0, 0 );
if ( !IsDefined( startAngles ) )
startAngles = ( 0, 0, 0 );
thread drawSpine( player, item );
SetDvar( "scr_adjust_angles", ""+startAngles );
SetDvar( "scr_adjust_origin", ""+startOrigin );
currentAngles = (0, 0, 0);
currentOrigin = (0, 0, 0);
while ( true )
{
waitframe();
nextAngles = GetDvarVector( "scr_adjust_angles" );
nextOrigin = GetDvarVector( "scr_adjust_origin" );
if ( nextAngles == currentAngles && nextOrigin == currentOrigin )
continue;
currentAngles = nextAngles;
currentOrigin = nextOrigin;
item Unlink();
item LinkTo( player, tagName, currentOrigin, currentAngles );
}
}
drawSpine( player, item )
{
player endon( "disconnect" );
player endon( "death" );
item endon( "death" );
if ( isdefined ( level.isHorde ) && level.isHorde )
player endon( "becameSpectator" );
while ( true )
{
// origin = player GetTagOrigin( "j_spine4" );
// angles = player GetTagAngles( "j_spine4" );
// Box(origin, 0, ( 0, 1, 0 ) );
origin = item.origin;
angles = item.angles;
debug_axis( origin, angles );
waitframe();
}
}
debug_axis( origin, angles )
{
size = 20;
forward = AnglesToForward( angles ) * size;
right = AnglesToRight( angles ) * size;
up = AnglesToUp( angles ) * size;
Line( origin, origin + forward, ( 1, 0, 0 ), 1.0, 0, 1 );
Line( origin, origin + up , ( 0, 1, 0 ), 1.0, 0, 1 );
Line( origin, origin + right, ( 0, 0, 1 ), 1.0, 0, 1 );
}
// ---------------------------------------------------------------------------------------
// Copied from _playermech_code.gsc
MECH_CHAINGUN_STATE_NONE = 0;
MECH_CHAINGUN_STATE_READY = 1;
MECH_CHAINGUN_STATE_FIRING = 2;
MECH_CHAINGUN_STATE_OVERHEAT = 3;
MECH_CHAINGUN_STATE_OFFLINE = 4;
MECH_SWARM_STATE_NONE = 0;
MECH_SWARM_STATE_READY = 1;
MECH_SWARM_STATE_TARGETING = 2;
MECH_SWARM_STATE_RELOADING = 3;
MECH_SWARM_STATE_OFFLINE = 4;
MECH_ROCKET_STATE_NONE = 0;
MECH_ROCKET_STATE_READY = 1;
MECH_ROCKET_STATE_RELOADING = 2;
MECH_ROCKET_STATE_OFFLINE = 3;
//*******************************************************************
// *
// UI / FX *
// *
//*******************************************************************
playermech_ui_state_reset()
{
if ( !IsDefined( self.mechUIState ) )
{
self.mechUIState = SpawnStruct();
self.mechUIState.chaingun = SpawnStruct();
self.mechUIState.swarm = SpawnStruct();
self.mechUIState.rocket = SpawnStruct();
self.mechUIState.threat_list = SpawnStruct();
self.mechUIState.state = "none";
self.mechUIState.chaingun.state = "none";
self.mechUIState.chaingun.last_state = "none";
self.mechUIState.swarm.state = "none";
self.mechUIState.swarm.last_state = "none";
self.mechUIState.rocket.state = "none";
self.mechUIState.rocket.last_state = "none";
}
self set_mech_state();
// Threats
self.mechUIState.threat_list.threats = [];
self.mechUIState.threat_list.compass_offsets = [];
// Chaingun
self.mechUIState.chaingun.heatlevel = 0;
self.mechUIState.chaingun.overheated = false;
// Swarm
// self.mechUIState.swarm.target_list = [];
self.mechUIState.swarm.threat_scan = false;
self.mechUIState.swarm.recharge = 100;
// Rocket
self.mechUIState.rocket.fire = false;
self.mechUIState.rocket.recharge = 100;
}
//playermech_ui_state_enter( state )
//{
// switch( state )
// {
// case "base_noweap_bootup":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGame( "playermech_bootup" );
// break;
// }
// case "base_swarmonly_nolegs":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGameLoop( "playermech_base_swarmonly_nolegs" );
// break;
// }
// case "base_swarmonly_exit":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGame( "playermech_base_swarmonly_exit" );
// break;
// }
// case "base_swarmonly":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGameLoop( "playermech_base_swarmonly" );
// break;
// }
// case "base_transition":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGame( "playermech_base_transition" );
// break;
// }
// case "base":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// //CinematicInGameLoop( "playermech_base" );
// break;
// }
// case "dmg1_transition":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGame( "playermech_dmg1_transition" );
// break;
// }
// case "dmg1":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGameLoop( "playermech_dmg1" );
// break;
// }
// case "dmg2_transition":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGame( "playermech_dmg2_transition" );
// break;
// }
// case "dmg2":
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "0" );
// CinematicInGameLoop( "playermech_dmg2" );
// break;
// }
// }
//}
//
//playermech_ui_state_leave( state )
//{
// switch( state )
// {
// case "base_noweap_bootup":
// break;
// default:
// {
// // Player enters mech
// setsaveddvar( "cg_cinematicfullscreen", "1" );
// stopcinematicingame();
// break;
// }
// }
//}
playermech_state_manager()
{
self endon( "disconnect" );
self endon( "exit_mech" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
// To be kept up to date for UI and FX.
playermech_ui_state_reset();
// Initialize states
self set_mech_state();
self set_mech_chaingun_state();
self set_mech_rocket_state();
self set_mech_swarm_state();
waitframe();
while ( 1 )
{
// Main mech state pump (Binks, main mech stuff)
// state_main_pump();
// Weapon state pumps
state_chaingun_pump();
state_rocket_pump();
state_swarm_pump();
// // Update current threat list
// self.mechUIState.threat_list.threats = remove_dead_from_array( self.mechData.threat_list );
// self playermech_ui_update_threat_compass_values( self.mechUIState.threat_list );
// Send data to HUD.
self playermech_ui_update_lui( self.mechUIState );
wait 0.05;
}
}
set_mech_state( state )
{
Assert( IsPlayer( self ) );
if( !IsDefined( state ) )
state = "none";
if( !IsDefined( self.mechUIState ) )
return;
if( self.mechUIState.state == state )
return;
// playermech_ui_state_leave( self.mechUIState.state );
self.mechUIState.state = state;
// playermech_ui_state_enter( state );
// iprintln( "Main state: " + state + ", Weap:" + self GetCurrentPrimaryWeapon() );
}
get_mech_state()
{
Assert( IsPlayer( self ) );
if( !IsDefined( self.mechUIState ) )
return;
return self.mechUIState.state;
}
get_is_in_mech()
{
modelName = self getattachmodelname( 0 );
if( IsDefined( modelName ) && modelName == "head_hero_cormack_sentinel_halo" )
return true;
return false;
}
get_front_sorted_threat_list( threats, forward )
{
sorted = [];
// Sort threats.
foreach( threat in threats )
{
// Check if in front of player.
if ( VectorDot( threat.origin - self.origin, forward ) < 0 )
continue;
sorted[sorted.size] = threat;
}
sorted = SortByDistance( sorted, self.origin );
return sorted;
}
playermech_ui_weapon_feedback( buttonPressed, omnvar )
{
self endon( "disconnect" );
self endon( "exit_mech" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
self SetClientOmnvar( omnvar, false );
while ( 1 )
{
while ( !self call [[buttonPressed]]() )
{
wait( 0.05 );
}
self SetClientOmnvar( omnvar, true );
while ( self call [[buttonPressed]]() )
{
wait( 0.05 );
}
self SetClientOmnvar( omnvar, false );
wait( 0.05 );
}
}
//playermech_ui_update_threat_compass_values( threatList )
//{
// threatList.compass_offsets = [];
//
// // Test
// //threatList.threats = [];
// //threatList.threats = SortByDistance( GetAIArray( "axis" ), self.origin );
//
// forward = AnglesToForward( self GetPlayerAngles() );
// if ( forward[2] > 0.99 )
// return;
// forward = VectorNormalize( forward * (1,1,0) );
//
// sorted = get_front_sorted_threat_list( threatList.threats, forward );
//
// // Fill threat data.
// compassFudge = GetDvarFloat( "mechCompassScaleFudge" );
// foreach( threat in sorted )
// {
// threatDir = threat.origin - self.origin;
// threatDir = VectorNormalize( threatDir * (1,1,0) );
// if ( threatDir[2] > 0.99 )
// {
// sorted.compass_offsets[threatList.compass_offsets.size] = 0;
// continue;
// }
//
// frac = acos( clamp( VectorDot( threatDir, forward ), -1, 1 ) ) / 180.0;
// cross = VectorCross( threatDir, forward );
// if ( cross[2] <= 0 )
// frac *= -1.0;
// frac *= compassFudge;
// threatList.compass_offsets[threatList.compass_offsets.size] = frac;
// //iprintlnbold( frac );
// }
//}
//
playermech_ui_update_lui( uiState )
{
isThreat = self playerIsRocketSwarmTargetLocked();
numSwarmTargets = 0;
if ( isThreat )
numSwarmTargets = 1;
if ( self.heavyExoData.hasRockets )
self SetClientOmnvar( "ui_playermech_swarmrecharge", uiState.swarm.recharge );
if ( self.heavyExoData.hasLongPunch )
self SetClientOmnvar( "ui_playermech_rocketrecharge", uiState.rocket.recharge );
}
//*******************************************************************
// *
// Invalid weapon events *
// *
//*******************************************************************
playermech_invalid_gun_callback()
{
if ( self.mechUIState.chaingun.overheated )
{
// Play mech gun invalid ui and sound.
return true;
}
return false;
}
playermech_invalid_rocket_callback()
{
if ( self.mechUIState.rocket.recharge < 100 )
{
// Play mech rocket invalid ui and sound.
return true;
}
return false;
}
playermech_invalid_swarm_callback()
{
if ( self.mechUIState.swarm.recharge < 100 )
{
// Play mech swarm invalid ui and sound.
return true;
}
return false;
}
playermech_invalid_weapon_instance( buttonFunc, callback )
{
self endon( "disconnect" );
self endon( "exit_mech" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
buttonDown = false;
while ( 1 )
{
wait 0.05;
if ( self call [[buttonFunc]]() )
{
if ( !buttonDown )
{
if ( [[callback]]() )
{
buttonDown = true;
self PlayLocalSound( "wpn_mech_offline" );
wait 1.5;
}
}
}
else
{
buttonDown = false;
}
}
}
playermech_invalid_weapon_watcher()
{
self thread playermech_invalid_weapon_instance( ::AttackButtonPressed, ::playermech_invalid_gun_callback );
self thread playermech_invalid_weapon_instance( ::FragButtonPressed, ::playermech_invalid_rocket_callback );
self thread playermech_invalid_weapon_instance( ::SecondaryOffhandButtonPressed, ::playermech_invalid_swarm_callback );
}
//*******************************************************************
// *
// Bink States *
// *
//*******************************************************************
// Main mech state
state_main_pump()
{
switch( self get_mech_state() )
{
case "base_noweap_bootup":
case "base_swarmonly_nolegs":
case "base_swarmonly_exit":
case "base_noweap":
case "base_swarmonly":
case "base_transition":
case "base":
case "dmg1_transition":
case "dmg1":
case "dmg2_transition":
case "dmg2":
break;
case "none":
{
playermech_ui_state_reset();
break;
}
default:
{
assertmsg( "Mech state invalid: " + self get_mech_state() );
}
}
}
//*******************************************************************
// *
// Weapon States *
// *
//*******************************************************************
state_chaingun_pump()
{
// // Let's avoid running this function if we are in the same state
// if( same_mech_chaingun_last_state() )
// return;
current_state = self get_mech_chaingun_state();
// Overheating chaingun checks
weapon = self GetCurrentWeapon();
self.mechUIState.chaingun.heatlevel = self GetWeaponHeatLevel( weapon );
self.mechUIState.chaingun.overheated = self IsWeaponOverheated( weapon );
// Overheat
if( current_state == "ready" )
{
if( self.mechUIState.chaingun.overheated )
set_mech_chaingun_state( "overheat" );
else if( self AttackButtonPressed() )
set_mech_chaingun_state( "firing" );
}
else if( current_state == "firing" )
{
if( self.mechUIState.chaingun.overheated )
set_mech_chaingun_state( "overheat" );
else if( !self AttackButtonPressed() )
set_mech_chaingun_state( "ready" );
}
else if( current_state == "overheat" && !self.mechUIState.chaingun.overheated )
{
set_mech_chaingun_state( "ready" );
}
}
state_rocket_pump()
{
// // Let's avoid running this function if we are in the same state
// if( same_mech_rocket_last_state() )
// return;
current_state = self get_mech_rocket_state();
// Reloading
if( current_state != "offline" && self playermech_invalid_rocket_callback() )
set_mech_rocket_state( "reload" );
else if( current_state == "reload" && !self playermech_invalid_rocket_callback() )
set_mech_rocket_state( "ready" );
}
state_swarm_pump()
{
// // Let's avoid running this function if we are in the same state
// if( same_mech_swarm_last_state() )
// return;
current_state = self get_mech_swarm_state();
// // Swarm target list update
// self.mechUIState.swarm.target_list = remove_dead_from_array( self.mechData.swarm_target_list );
// Targetting / Reloading
if( !self playerIsRocketSwarmTargetLocked() && !self playerIsRocketSwarmReloading() )
set_mech_swarm_state( "target" );
else if( current_state == "target" && self playermech_invalid_swarm_callback() )
set_mech_swarm_state( "reload" );
else if( current_state == "reload" && !self playermech_invalid_swarm_callback() )
set_mech_swarm_state( "ready" );
}
set_mech_chaingun_state( state )
{
Assert( IsPlayer( self ) );
if( !IsDefined( state ) )
state = "none";
if( !IsDefined( self.mechUIState.chaingun.state ) )
self.mechUIState.chaingun.state = "none";
if( self.mechUIState.chaingun.state == state )
return;
self.mechUIState.chaingun.state = state;
self notify( "chaingun_state_" + state );
//iprintln( "Chaingun state: " + state );
}
get_mech_chaingun_state()
{
Assert( IsPlayer( self ) );
if( !IsDefined( self.mechUIState ) )
return;
return self.mechUIState.chaingun.state;
}
same_mech_chaingun_last_state()
{
if( IsDefined( self.mechUIState.chaingun.last_state ) && self.mechUIState.chaingun.state == self.mechUIState.chaingun.last_state )
return true;
self.mechUIState.chaingun.last_state = self.mechUIState.chaingun.state;
return false;
}
set_mech_rocket_state( state )
{
Assert( IsPlayer( self ) );
if( !IsDefined( state ) )
state = "none";
if( !IsDefined( self.mechUIState.rocket.state ) )
self.mechUIState.rocket.state = "none";
if( self.mechUIState.rocket.state == state )
return;
self.mechUIState.rocket.state = state;
//iprintln( "Rocket state: " + state );
}
get_mech_rocket_state()
{
Assert( IsPlayer( self ) );
if( !IsDefined( self.mechUIState ) )
return;
return self.mechUIState.rocket.state;
}
same_mech_rocket_last_state()
{
if( IsDefined( self.mechUIState.rocket.last_state ) && self.mechUIState.rocket.state == self.mechUIState.rocket.last_state )
return true;
self.mechUIState.rocket.last_state = self.mechUIState.rocket.state;
return false;
}
set_mech_swarm_state( state )
{
Assert( IsPlayer( self ) );
if( !IsDefined( state ) )
state = "none";
if( !IsDefined( self.mechUIState.swarm.state ) )
self.mechUIState.swarm.state = "none";
if( self.mechUIState.swarm.state == state )
return;
self.mechUIState.swarm.state = state;
//iprintln( "Swarm state: " + state );
}
get_mech_swarm_state()
{
Assert( IsPlayer( self ) );
if( !IsDefined( self.mechUIState ) )
return;
return self.mechUIState.swarm.state;
}
same_mech_swarm_last_state()
{
if( IsDefined( self.mechUIState.swarm.last_state ) && self.mechUIState.swarm.state == self.mechUIState.swarm.last_state )
return true;
self.mechUIState.swarm.last_state = self.mechUIState.swarm.state;
return false;
}
playermech_monitor_update_recharge( weapon, time )
{
weapon.recharge = 0;
iteration = 100.0 / (time / 0.05);
while ( weapon.recharge < 100 )
{
weapon.recharge += iteration;
wait 0.05;
}
weapon.recharge = 100;
while ( IsDefined( self.underwaterMotionType ) ) // delay re-enabling weapons until the Goliath is out of water.
{
wait( 0.05 );
}
}
playermech_monitor_rocket_recharge()
{
self endon( "disconnect" );
self endon( "exit_mech" );
self notify( "stop_rocket_recharge" );
self endon( "stop_rocket_recharge" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
while ( 1 )
{
self waittill( "mech_rocket_fire" );
self DisableOffhandWeapons();
self playermech_monitor_update_recharge( self.mechuistate.rocket, CONST_JUGG_EXO_ROCKET_RELOAD_TIME );
self EnableOffhandWeapons();
wait 0.05;
}
}
playermech_monitor_swarm_recharge()
{
self endon( "disconnect" );
self endon( "exit_mech" );
self notify( "stop_swarm_recharge" );
self endon( "stop_swarm_recharge" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
while ( 1 )
{
self waittill( "mech_swarm_fire" );
self DisableOffhandSecondaryWeapons();
self playermech_monitor_update_recharge( self.mechuistate.swarm, CONST_JUGG_EXO_ROCKET_SWARM_RELOAD_TIME );
self EnableOffhandSecondaryWeapons();
wait 0.05;
}
}
play_goliath_death_fx() // self = player
{
level endon( "game_ended" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
self waittill_any( "death", "joined_team", "faux_spawn" );
if ( IsAI( self ) )
{
self maps\mp\_snd_common_mp::snd_message( "goliath_self_destruct" );
PlayFX( getfx( "goliath_self_destruct" ), self.origin, AnglesToUp(self.angles) );
if ( IsAgent( self ) && IsDefined( self.hideOnDeath ) && self.hideOnDeath == true )
{
// Need to hide the Agent's body here. For Bots and Players, their bodies are hidden in _damage::PlayerKilled_internal().
agent_corpse = self GetCorpseEntity();
if ( IsDefined( agent_corpse ) )
agent_corpse Hide();
}
}
else if ( !isDefined( self.juggernautSuicide ) && !IsDefined( level.isHorde ) )
{
playfxontag( getfx( "goliath_death_fire" ), self.body, "J_NECK" );
self maps\mp\_snd_common_mp::snd_message( "goliath_death_explosion" );
}
self.juggernautSuicide = undefined;
}
self_destruct_goliath() // self = player
{
level endon( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
self endon( "joined_team" );
self endon( "faux_spawn" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "horde_cancel_goliath" );
button_hold_time = 0;
while ( true )
{
if ( self UseButtonPressed() )
{
button_hold_time += 0.05;
if ( button_hold_time > 1.0 )
{
self maps\mp\_snd_common_mp::snd_message( "goliath_self_destruct" );
PlayFX( getfx( "goliath_self_destruct" ), self.origin, AnglesToUp(self.angles) );
wait 0.05;
self.hideOnDeath = true;
self.juggernautSuicide = true;
RadiusDamage( self.origin + ( 0, 0, 50 ), 400, 200, 20, self, "MOD_EXPLOSIVE", "killstreak_goliathsd_mp" );
if ( isdefined ( level.isHorde ) && level.isHorde )
{
self thread [[level.hordeHandleJuggDeath]]();
}
}
}
else
{
button_hold_time = 0;
}
wait( 0.05 );
}
}
playerMechTimeout()
{
level endon ( "game_ended" );
self endon ( "disconnect" );
self endon ( "lost_juggernaut" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon ( "horde_cancel_goliath" );
while ( true )
{
wait 1;
self.mechHealth -= int ( self.maxhealth / 100 );
if ( self.mechHealth < 0 )
{
self maps\mp\_snd_common_mp::snd_message( "goliath_self_destruct" );
PlayFX( getfx( "goliath_self_destruct" ), self.origin, AnglesToUp(self.angles) );
self thread [[level.hordeHandleJuggDeath]]();
}
self SetClientOmnvar( "ui_exo_suit_health", self.mechHealth / self.maxhealth );
}
}
onPlayerConnect()
{
for(;;)
{
level waittill("connected", player);
player thread onPlayerSpawned();
}
}
onPlayerSpawned()
{
self endon("disconnect");
for(;;)
{
self waittill( "spawned_player" );
self.hideOnDeath = false;
}
}
deleteGoliathPod( playDestroyVFX, playDestroySound ) // self = goliath pod useEnt. This function is similar to _airdrop::deleteCrate().
{
if ( !IsDefined( playDestroyVFX ) )
playDestroyVFX = true;
if ( !IsDefined( playDestroySound ) )
playDestroySound = false;
if ( isDefined( self.curObjID ) )
_objective_delete( self.curObjID );
if ( isDefined( self.dropType ) )
{
if ( playDestroyVFX )
{
PlayFX( getfx( "ocp_death" ), self.origin );
}
if ( playDestroySound )
{
playSoundAtPos( self.origin, "orbital_pkg_self_destruct" );
}
}
self delete();
}
handle_goliath_drop_pod_timeout( hordeClassGoliath ) // self = useEnt
{
level endon( "game_ended" );
self endon( "death" );
wait( CONST_JUGG_EXO_TIMEOUT_SEC );
if ( isdefined ( level.isHorde ) && level.isHorde && hordeClassGoliath )
{
self.owner.hordeClassGoliathPodInField = undefined;
self.owner.hordeGoliathPodInField = undefined;
self.owner notify ( "startJuggCooldown" );
}
self deleteGoliathPod();
}
delete_goliath_drop_pod_for_event()
{
level endon( "game_ended" );
self endon( "death" );
level waittill_any ( "zombies_start", "EMP_JamTeamallies" );
self deleteGoliathPod();
}
handle_goliath_drop_pod_removal( useEnt ) // self = goliath drop pod model
{
level endon( "game_ended" );
self endon( "death" );
useEnt waittill( "death" );
self Delete();
}
playermech_watch_emp_grenade()
{
level endon( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
while( true )
{
self waittill( "emp_grenaded", attacker );
if( IsDefined( attacker ) && IsPlayer( attacker ) )
{
attacker thread ch_emp_goliath_think();
}
}
}
ch_emp_goliath_think()
{
//self is the guy that emp'd the goliath
level endon( "game_ended" );
self endon( "death" );
self endon( "disconnect" );
if ( isdefined ( level.isHorde ) && level.isHorde )
self endon( "becameSpectator" );
time_to_wait = 5.0;
wait( time_to_wait );
if( isReallyAlive( self ) )
{
self maps\mp\gametypes\_missions::processChallenge( "ch_precision_closecall" );
}
}