2024-12-11 11:28:08 +01:00

572 lines
15 KiB
Plaintext

#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#include common_scripts\utility;
//============================================
// constants
//============================================
CONST_UPLINK_WEAPON = "killstreak_uplink_mp";
CONST_UPLINK_TIME = 30;
CONST_UPLINK_MIN = 0;
CONST_EYES_ON = 1;
CONST_UPLINK_FULL_RADAR = 2;
CONST_UPLINK_FAST_PING = 3;
CONST_DIRECTIONAL = 4;
CONST_UPLINK_MAX = 4;
CONST_FAST_SWEEP = "fast_radar";
CONST_NORMAL_SWEEP = "normal_radar";
CONST_HEAD_ICON_OFFSET = 42;
CONST_EMP_VFX = "emp_stun";
CONST_EMP_VFX_TAG = "tag_origin";
//============================================
// init
//============================================
init()
{
level.uplinks = [];
level.killstreakFuncs["uplink"] = ::tryUseUpLink;
level.killstreakFuncs["uplink_support"] = ::tryUseUpLink;
level.comExpFuncs = [];
level.comExpFuncs[ "giveComExpBenefits" ] = ::giveComExpBenefits;
level.comExpFuncs[ "removeComExpBenefits" ] = ::removeComExpBenefits;
level.comExpFuncs[ "getRadarStrengthForTeam" ] = ::getRadarStrengthForTeam;
level.comExpFuncs[ "getRadarStrengthForPlayer" ] = ::getRadarStrengthForPlayer;
unblockTeamRadar( "axis" );
unblockTeamRadar( "allies" );
level thread upLinkTracker();
level thread uplinkUpdateEyesOn();
config = spawnStruct();
config.streakName = "uplink";
config.weaponInfo = "ims_projectile_mp";
config.modelBase = "mp_satcom";
// config.modelDestroyed = "placeable_barrier_destroyed";
config.modelPlacement = "mp_satcom_obj";
config.modelPlacementFailed = "mp_satcom_obj_red";
config.modelBombSquad = "mp_satcom_bombsquad";
config.hintString = &"KILLSTREAKS_HINTS_UPLINK_PICKUP";
config.placeString = &"KILLSTREAKS_HINTS_UPLINK_PLACE";
config.cannotPlaceString = &"KILLSTREAKS_HINTS_UPLINK_CANNOT_PLACE";
config.headIconHeight = CONST_HEAD_ICON_OFFSET;
config.splashName = "used_uplink";
config.lifeSpan = CONST_UPLINK_TIME;
// config.goneVO = "satcom_gone";
config.maxHealth = 500;
config.allowMeleeDamage = true;
config.allowEmpDamage = true;
config.damageFeedback = "trophy";
config.xpPopup = "destroyed_uplink";
config.destroyedVO = "satcom_destroyed";
//config.onCreateDelegate = ::createObject;
config.placementHeightTolerance = 30.0;
config.placementRadius = 16.0;
config.placementOffsetZ = 16;
config.onPlacedDelegate = ::onPlaced;
config.onCarriedDelegate = ::onCarried;
config.placedSfx = "mp_killstreak_satcom_deploy";
config.activeSfx = "mp_killstreak_satcom_loop";
config.onMovingPlatformCollision = ::uplink_override_moving_platform_death;
// config.onDamagedDelegate = ::onDamaged;
config.onDeathDelegate = ::onDeath;
config.onDestroyedDelegate = ::onDestroyed; // when killed by a player
config.deathVfx = loadfx( "vfx/gameplay/mp/killstreaks/vfx_ballistic_vest_death" );
// config.onDeactivateDelegate = ::onDeactivated;
// config.onActivateDelegate = ::onActivated;
level.placeableConfigs[ "uplink" ] = config;
level.placeableConfigs[ "uplink_support" ] = config;
}
//============================================
// upLinkTracker
//============================================
upLinkTracker()
{
level endon ( "game_ended" );
while( true )
{
level waittill( "update_uplink" );
level childthread updateAllUplinkThreads();
}
}
// 2013-08-27 wallace: I wonder if it would be ok to just update the uplink strength for a particular player/team on notify
// since the uplinks know who they belong to
updateAllUplinkThreads()
{
self notify("updateAllUplinkThreads");
self endon("updateAllUplinkThreads");
level childthread comExpNotifyWatcher();
if( level.teamBased )
{
level childthread updateTeamUpLink( "axis" );
level childthread updateTeamUpLink( "allies" );
}
else
{
level childthread updatePlayerUpLink();
}
// Update the the UpLink for Com Specialist players
level childthread updateComExpUpLink();
}
comExpNotifyWatcher()
{
// Guarantee that the previous update functions have finished before we proceed into updating com exp
teamsFinished = [];
if ( !level.teamBased )
level waittill ( "radar_status_change_players" );
else
{
while ( teamsFinished.size < 2 )
{
level waittill ( "radar_status_change", team );
teamsFinished[ teamsFinished.size ] = team;
}
}
level notify ( "start_com_exp" );
}
//============================================
// updateTeamUpLink
//============================================
updateTeamUpLink( team )
{
currentStrengthForTeam = getRadarStrengthForTeam( team );
shouldBeEyesOn = ( currentStrengthForTeam == CONST_EYES_ON );
shouldBeFullRadar = ( currentStrengthForTeam >= CONST_UPLINK_FULL_RADAR );
shouldBeFastSweep = ( currentStrengthForTeam >= CONST_UPLINK_FAST_PING );
shouldBeDirectional = ( currentStrengthForTeam >= CONST_DIRECTIONAL );
if( shouldBeFullRadar )
{
unblockTeamRadar( team );
}
if( shouldBeFastSweep )
{
level.radarMode[team] = CONST_FAST_SWEEP;
}
else
{
level.radarMode[team] = CONST_NORMAL_SWEEP;
}
foreach( player in level.participants )
{
if ( !IsDefined(player) )
continue;
if( player.team != team )
continue;
player.shouldBeEyesOn = shouldBeEyesOn;
player SetEyesOnUplinkEnabled( shouldBeEyesOn );
player.radarMode = level.radarMode[player.team];
player.radarShowEnemyDirection = shouldBeDirectional;
player updateSatcomActiveOmnvar( team );
wait(0.05); // Setting all these player variables can get expensive, so spread it out over multiple frames
}
setTeamRadar( team, shouldBeFullRadar );
level notify( "radar_status_change", team );
}
//============================================
// updatePlayerUpLink
//============================================
updatePlayerUpLink()
{
foreach ( player in level.participants )
{
if ( !IsDefined(player) )
continue;
currentStrengthForSelf = getRadarStrengthForPlayer( player );
setPlayerRadarEffect( player, currentStrengthForSelf );
player updateSatcomActiveOmnvar();
wait(0.05); // Setting all these player variables can get expensive, so spread it out over multiple frames
}
level notify( "radar_status_change_players" );
}
//============================================
// updateComExpUpLink
//============================================
updateComExpUpLink()
{
level waittill ( "start_com_exp" );
foreach ( player in level.participants )
{
if ( !IsDefined(player) )
continue;
player giveComExpBenefits();
wait(0.05); // Setting all these player variables can get expensive, so spread it out over multiple frames
}
}
// give/remove ComExpBenefits
// encapsulate the innerworkings of uplink system for the perk's set/unset functions
giveComExpBenefits() // self == player
{
if( ( self _hasPerk( "specialty_comexp" ) ) )
{
radarStrength = getRadarStrengthForComExp( self );
setPlayerRadarEffect( self, radarStrength );
self updateSatcomActiveOmnvar();
}
}
// since multiple functions are trying to set the ui_satcom_active omnvar, I've made it one function that will handle the wiretap perk functionality
// this will make sure we always get it right and get the correct amount of radar
updateSatcomActiveOmnvar( team ) // self == player
{
radarStrength = 0;
if( IsDefined( team ) )
radarStrength = getRadarStrengthForTeam( team );
else
radarStrength = getRadarStrengthForPlayer( self );
if( self _hasPerk( "specialty_comexp" ) )
radarStrength = getRadarStrengthForComExp( self );
if( radarStrength > CONST_UPLINK_MIN )
self SetClientOmnvar( "ui_satcom_active", true );
else
self SetClientOmnvar( "ui_satcom_active", false );
}
removeComExpBenefits() // self == player
{
// mainly for clearing out radarMode, the other stuff will get set by the appropriate update function
self.shouldBeEyesOn = false;
self SetEyesOnUplinkEnabled( false );
self.radarShowEnemyDirection = false;
self.radarMode = CONST_NORMAL_SWEEP;
self.hasRadar = false;
self.isRadarBlocked = false;
}
setPlayerRadarEffect( player, radarStrength )
{
shouldBeEyesOn = ( radarStrength == CONST_EYES_ON );
shouldBeFullRadar = ( radarStrength >= CONST_UPLINK_FULL_RADAR );
shouldBeFastSweep = ( radarStrength >= CONST_UPLINK_FAST_PING );
shouldBeDirectional = ( radarStrength >= CONST_DIRECTIONAL );
player.shouldBeEyesOn = shouldBeEyesOn;
player SetEyesOnUplinkEnabled( shouldBeEyesOn );
player.radarShowEnemyDirection = shouldBeDirectional;
player.radarMode = CONST_NORMAL_SWEEP;
player.hasRadar = shouldBeFullRadar;
player.isRadarBlocked = false;
if( shouldBeFastSweep )
{
player.radarMode = CONST_FAST_SWEEP;
}
}
//============================================
// tryUseUpLink
//============================================
tryUseUpLink( lifeId, streakName )
{
result = self maps\mp\killstreaks\_placeable::givePlaceable( streakName );
if( result )
{
// we want both to log to the same event?
self maps\mp\_matchdata::logKillstreakEvent( "uplink", self.origin );
}
// we're done carrying for sure and sometimes this might not get reset
// this fixes a bug where you could be carrying and have it in a place where it won't plant, get killed, now you can't scroll through killstreaks
self.isCarrying = undefined;
return result;
}
onCarried( streakName ) // self == obj
{
entNum = self GetEntityNumber();
if ( IsDefined( level.uplinks[ entNum ] ) )
{
self stopUplink();
}
}
onPlaced( streakName ) // self == obj
{
config = level.placeableConfigs[ streakName ];
self.owner notify( "uplink_deployed" );
self SetModel( config.modelBase );
self.immediateDeath = false;
self SetOtherEnt(self.owner);
self make_entity_sentient_mp( self.owner.team, true );
self.config = config;
self startUplink( true );
self thread watchEMPDamage();
// 2013-07-25 wallace: not sure if we need this still
/*
wait( 0.75 );
if( IsDefined(self) && (self touchingBadTrigger()) )
self notify( "death" );
*/
}
// Operations that need to be done when the uplink is first spawned
// AND when it resumes from
startUplink( playOpenAnim )
{
addUplinkToLevelList( self );
self thread playUplinkAnimations( playOpenAnim );
// play operation sound
self PlayLoopSound( self.config.activeSfx );
}
stopUplink()
{
self maps\mp\gametypes\_weapons::stopBlinkingLight();
self ScriptModelClearAnim();
if ( IsDefined( self.bombSquadModel ) )
{
self.bombSquadModel ScriptModelClearAnim();
}
removeUplinkFromLevelList( self );
self StopLoopSound();
}
#using_animtree( "animated_props" );
//============================================
// playUplinkAnimations
//============================================
playUplinkAnimations( playOpenAnim ) // self = uplink
{
self endon( "emp_damage" );
self endon( "death" );
self endon( "carried" );
if ( playOpenAnim )
{
waitTime = GetNotetrackTimes( %Satcom_killStreak, "stop anim" );
animLength = GetAnimLength( %Satcom_killStreak );
self ScriptModelPlayAnim( "Satcom_killStreak" );
if ( IsDefined( self.bombSquadModel ) )
{
self.bombSquadModel ScriptModelPlayAnim( "Satcom_killStreak" );
}
wait( waitTime[0] * animLength );
}
self ScriptModelPlayAnim( "Satcom_killStreak_idle" );
if ( IsDefined( self.bombSquadModel ) )
{
self.bombSquadModel ScriptModelPlayAnim( "Satcom_killStreak_idle" );
}
// play vfx
self thread maps\mp\gametypes\_weapons::doBlinkingLight( "tag_fx" );
}
onDestroyed( streakName, attacker, owner, sMeansOfDeath )
{
attacker notify( "destroyed_equipment" );
}
onDeath( streakName, attacker, owner, sMeansOfDeath )
{
self maps\mp\gametypes\_weapons::stopBlinkingLight();
self maps\mp\gametypes\_weapons::equipmentDeathVfx();
removeUplinkFromLevelList( self );
self ScriptModelClearAnim();
if(!self.immediateDeath)
{
wait( 3.0 );
}
self maps\mp\gametypes\_weapons::equipmentDeleteVfx();
}
//============================================
// addUplinkToLevelList
//============================================
addUplinkToLevelList( obj )
{
entNum = obj GetEntityNumber();
level.uplinks[entNum] = obj;
level notify( "update_uplink" );
}
//============================================
// removeUplinkFromLevelList
//============================================
removeUplinkFromLevelList( obj )
{
entNum = obj GetEntityNumber();
level.uplinks[ entNum ] = undefined;
level notify( "update_uplink" );
}
//============================================
// getRadarStrengthForTeam
//============================================
getRadarStrengthForTeam( team )
{
currentRadarStrength = 0;
foreach( satellite in level.uplinks )
{
if( IsDefined(satellite) && (satellite.team == team) )
currentRadarStrength++;
}
// give eyes-on when heli sniper is in the air
if ( currentRadarStrength == 0
&& IsDefined( level.heliSniperEyesOn )
&& level.heliSniperEyesOn.team == team
)
{
currentRadarStrength++;
}
return clamp( currentRadarStrength, CONST_UPLINK_MIN, CONST_UPLINK_MAX );
}
//============================================
// getRadarStrengthForPlayer
//============================================
getRadarStrengthForPlayer( player )
{
currentRadarStrength = 0;
foreach( satellite in level.uplinks )
{
if( IsDefined( satellite ) )
{
// not sure if it's safe to remove an item while we're iterating through it
if ( IsDefined( satellite.owner ) )
{
if ( satellite.owner.guid == player.guid)
currentRadarStrength++;
}
else
{
// if the owner has disconnected, stop tracking this satellite
entNum = satellite GetEntityNumber();
level.uplinks[ entNum ] = undefined;
}
}
}
// in FFA, eyes-on is useless, so automatically bump the benefit to tier 2
if ( !level.teamBased && currentRadarStrength > 0 )
currentRadarStrength++;
return clamp( currentRadarStrength, CONST_UPLINK_MIN, CONST_UPLINK_MAX );
}
//============================================
// getRadarStrengthForComExp
//============================================
getRadarStrengthForComExp( player )
{
currentRadarStrength = 0;
// Add to the radar strength regardless of which team places down the SAT COM
foreach( satellite in level.uplinks )
{
if( IsDefined(satellite) )
currentRadarStrength++;
}
// in FFA, eyes-on is useless, so automatically bump the benefit to tier 2
if ( !level.teamBased && currentRadarStrength > 0 )
currentRadarStrength++;
return clamp( currentRadarStrength, CONST_UPLINK_MIN, CONST_UPLINK_MAX );
}
uplink_override_moving_platform_death( data )
{
self.immediateDeath = true;
self notify( "death" );
}
watchEMPDamage()
{
// self endon( "carried" );
self endon( "death" );
level endon( "game_ended" );
while( true )
{
// this handles any flash or concussion damage
self waittill( "emp_damage", attacker, duration );
self maps\mp\gametypes\_weapons::equipmentEmpStunVfx();
self stopUplink();
wait( duration );
self startUplink( false );
}
}
uplinkUpdateEyesOn()
{
level endon( "game_ended" );
while (true)
{
level waittill( "player_spawned", player );
eyesOn = ( IsDefined( player.shouldBeEyesOn ) && player.shouldBeEyesOn );
player SetEyesOnUplinkEnabled( eyesOn );
}
}