3456 lines
94 KiB
Plaintext
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" );
|
|
}
|
|
}
|