init
This commit is contained in:
1005
maps/mp/killstreaks/_a10.gsc
Normal file
1005
maps/mp/killstreaks/_a10.gsc
Normal file
File diff suppressed because it is too large
Load Diff
200
maps/mp/killstreaks/_aalauncher.gsc
Normal file
200
maps/mp/killstreaks/_aalauncher.gsc
Normal file
@ -0,0 +1,200 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
//============================================
|
||||
// constants
|
||||
//============================================
|
||||
CONST_AA_LAUNCHER_WEAPON = "iw6_maaws_mp";
|
||||
CONST_AA_LAUNCHER_WEAPON_CHILD = "iw6_maawschild_mp";
|
||||
CONST_AA_LAUNCHER_WEAPON_HOMING = "iw6_maawshoming_mp";
|
||||
CONST_AA_LAUNCHER_WEAPON_AMMO = 2;
|
||||
|
||||
//============================================
|
||||
// init
|
||||
//============================================
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs["aa_launcher"] = ::tryUseAALauncher;
|
||||
maps\mp\_laserGuidedLauncher::LGM_init( "vfx/gameplay/mp/killstreaks/vfx_maaws_split", "vfx/gameplay/mp/killstreaks/vfx_maaws_homing" );
|
||||
}
|
||||
|
||||
//============================================
|
||||
// Getters and Setters
|
||||
//============================================
|
||||
getAALauncherName()
|
||||
{
|
||||
return CONST_AA_LAUNCHER_WEAPON;
|
||||
}
|
||||
|
||||
getAALauncherChildName()
|
||||
{
|
||||
return CONST_AA_LAUNCHER_WEAPON_CHILD;
|
||||
}
|
||||
|
||||
getAALauncherHomingName()
|
||||
{
|
||||
return CONST_AA_LAUNCHER_WEAPON_HOMING;
|
||||
}
|
||||
|
||||
getAALauncherAmmo( player )
|
||||
{
|
||||
AssertEx( IsDefined( player.pers[ "aaLauncherAmmo" ] ), "getAALauncherAmmo() called on player with no \"aaLauncherAmmo\" array key." );
|
||||
|
||||
ksUniqueID = getAALauncherUniqueIndex( player );
|
||||
ammo = 0;
|
||||
if ( IsDefined( player.pers[ "aaLauncherAmmo" ][ ksUniqueID ] ) )
|
||||
{
|
||||
ammo = player.pers[ "aaLauncherAmmo" ][ ksUniqueID ];
|
||||
}
|
||||
return ammo;
|
||||
}
|
||||
|
||||
clearAALauncherAmmo( player )
|
||||
{
|
||||
ksUniqueID = getAALauncherUniqueIndex( player );
|
||||
player.pers[ "aaLauncherAmmo" ][ ksUniqueID ] = undefined;
|
||||
}
|
||||
|
||||
setAALauncherAmmo( player, ammo, setAmmo )
|
||||
{
|
||||
AssertEx( IsDefined( player.pers[ "aaLauncherAmmo" ] ), "setAALauncherAmmo() called on player with no \"aaLauncherAmmo\" array key." );
|
||||
|
||||
// The player could also have two of the AA Launcher
|
||||
// so ammo needs to be stored on a per killstreak instance
|
||||
ksUniqueID = getAALauncherUniqueIndex( player );
|
||||
player.pers[ "aaLauncherAmmo" ][ ksUniqueID ] = ammo;
|
||||
|
||||
if ( !IsDefined( setAmmo ) || setAmmo )
|
||||
{
|
||||
if ( player HasWeapon( getAALauncherName() ) )
|
||||
{
|
||||
player SetWeaponAmmoClip( getAALauncherName(), ammo );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
getAALauncherUniqueIndex( player )
|
||||
{
|
||||
AssertEx( IsDefined( player.killstreakIndexWeapon ), "getAALauncherAmmo() called on player with no killstreakIndexWeapon field" );
|
||||
|
||||
return player.pers["killstreaks"][ player.killstreakIndexWeapon ].kID;
|
||||
}
|
||||
|
||||
//============================================
|
||||
// tryUseAALauncher
|
||||
//============================================
|
||||
tryUseAALauncher( lifeId, streakName )
|
||||
{
|
||||
return useAALauncher( self, lifeId );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// useAALauncher - Returns true if the killstreak was used up
|
||||
//============================================
|
||||
useAALauncher( player, lifeId )
|
||||
{
|
||||
// Ammo is persistent to handle round switching. If this is
|
||||
// the first use init the self.per[ "aaLauncherAmmo" ] array
|
||||
if ( !IsDefined( self.pers[ "aaLauncherAmmo" ] ) )
|
||||
{
|
||||
self.pers[ "aaLauncherAmmo" ] = [];
|
||||
}
|
||||
|
||||
// The launcher persistent ammo count will only be empty in the
|
||||
// use function when the persistent ammo has yet to be set
|
||||
// so set it to the starting ammo
|
||||
if ( getAALauncherAmmo( player ) == 0 )
|
||||
{
|
||||
setAALauncherAmmo( self, CONST_AA_LAUNCHER_WEAPON_AMMO, false );
|
||||
}
|
||||
|
||||
level thread monitorWeaponSwitch( player );
|
||||
level thread monitorLauncherAmmo( player );
|
||||
|
||||
self thread maps\mp\_laserGuidedLauncher::LGM_firing_monitorMissileFire( getAALauncherName(), getAALauncherChildName(), getAALauncherHomingName() );
|
||||
|
||||
result = false;
|
||||
msg = player waittill_any_return( "aa_launcher_switch", "aa_launcher_empty", "death", "disconnect" );
|
||||
|
||||
if ( msg == "aa_launcher_empty" )
|
||||
{
|
||||
// The player is potentially still guiding the missiles at this point
|
||||
// so wait until the player switches out or the missiles are destroyed
|
||||
player waittill_any( "weapon_change", "LGM_player_allMissilesDestroyed", "death", "disconnect" );
|
||||
result = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// In case death and aa_launcher_empty came on the same frame verify ammo
|
||||
if ( player HasWeapon( getAALauncherName() ) && player GetAmmoCount( getAALauncherName() ) == 0 )
|
||||
{
|
||||
clearAALauncherAmmo( player );
|
||||
}
|
||||
|
||||
if ( getAALauncherAmmo( player ) == 0 )
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
player notify( "aa_launcher_end" );
|
||||
|
||||
self maps\mp\_laserGuidedLauncher::LGM_firing_endMissileFire();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorWeaponSwitch
|
||||
//============================================
|
||||
monitorWeaponSwitch( player )
|
||||
{
|
||||
player endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
player endon( "aa_launcher_empty" );
|
||||
player endon( "aa_launcher_end" );
|
||||
|
||||
currentWeapon = player GetCurrentWeapon();
|
||||
|
||||
while( currentWeapon == getAALauncherName() )
|
||||
{
|
||||
player waittill( "weapon_change", currentWeapon );
|
||||
}
|
||||
|
||||
player notify( "aa_launcher_switch" );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorLauncherAmmo
|
||||
//============================================
|
||||
monitorLauncherAmmo( player )
|
||||
{
|
||||
player endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
player endon( "aa_launcher_switch" );
|
||||
player endon( "aa_launcher_end" );
|
||||
|
||||
setAALauncherAmmo( player, getAALauncherAmmo( player ), true );
|
||||
|
||||
while( true )
|
||||
{
|
||||
player waittill( "weapon_fired", weaponName );
|
||||
|
||||
if ( weaponName != getAALauncherName() )
|
||||
continue;
|
||||
|
||||
ammo = player GetAmmoCount( getAALauncherName() );
|
||||
setAALauncherAmmo( player, ammo, false );
|
||||
|
||||
if( getAALauncherAmmo( player ) == 0 )
|
||||
{
|
||||
clearAALauncherAmmo( player );
|
||||
player notify( "aa_launcher_empty" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
244
maps/mp/killstreaks/_aamissile.gsc
Normal file
244
maps/mp/killstreaks/_aamissile.gsc
Normal file
@ -0,0 +1,244 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*******************************************************************
|
||||
// _aamissile.gsc
|
||||
//
|
||||
// Holds all the aamissile specific functions
|
||||
//
|
||||
// Jordan Hirsh Jan. 18th 2011
|
||||
********************************************************************/
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
precacheItem( "aamissile_projectile_mp" );
|
||||
precacheShader( "ac130_overlay_grain" );
|
||||
|
||||
level.AAMissileLaunchVert = 14000;
|
||||
level.AAMissileLaunchHorz = 30000;
|
||||
level.AAMissileLaunchTargetDist = 1500;
|
||||
|
||||
level.rockets = [];
|
||||
level.killstreakFuncs["aamissile"] = ::tryUseAAMissile;
|
||||
|
||||
}
|
||||
|
||||
|
||||
tryUseAAMissile( lifeId, streakName )
|
||||
{
|
||||
self setUsingRemote( "aamissile" );
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak();
|
||||
if ( result != "success" )
|
||||
{
|
||||
if ( result != "disconnect" )
|
||||
self clearUsingRemote();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
level thread aa_missile_fire( lifeId, self );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
getTargets()
|
||||
{
|
||||
lbTargets = [];
|
||||
heliTargets = [];
|
||||
|
||||
if ( isDefined( level.littleBirds ) && level.littleBirds.size )
|
||||
{
|
||||
foreach ( lb in level.littleBirds )
|
||||
{
|
||||
if ( lb.team != self.team )
|
||||
lbTargets[lbTargets.size] = lb;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDefined(level.helis) && level.helis.size )
|
||||
{
|
||||
foreach ( heli in level.helis )
|
||||
{
|
||||
if ( heli.team != self.team )
|
||||
heliTargets[heliTargets.size] = heli;
|
||||
}
|
||||
}
|
||||
//if( level.ac130InUse && isDefined( level.ac130.owner ) && level.ac130.owner.team != self.team )
|
||||
// return level.ac130.planemodel;
|
||||
if( isDefined(heliTargets) && heliTargets.size )
|
||||
return heliTargets[0];
|
||||
else if ( isDefined(lbTargets) && lbTargets.size )
|
||||
return lbTargets[0];
|
||||
}
|
||||
|
||||
aa_missile_fire( lifeId, player )
|
||||
{
|
||||
aaMissileSpawn = undefined;
|
||||
|
||||
upVector = (0, 0, level.AAMissileLaunchVert );
|
||||
backDist = level.AAMissileLaunchHorz;
|
||||
targetDist = level.AAMmissileLaunchTargetDist;
|
||||
|
||||
bestTarget = player getTargets();
|
||||
|
||||
if( !isDefined(bestTarget) )
|
||||
targetPos = (0,0,0);
|
||||
else
|
||||
{
|
||||
targetPos = bestTarget.origin;
|
||||
upVector = (0,0,1) * (targetPos) + (0,0,1000);
|
||||
}
|
||||
|
||||
forward = AnglesToForward( player.angles );
|
||||
startpos = player.origin + upVector + forward * backDist * -1;
|
||||
|
||||
rocket = MagicBullet( "aamissile_projectile_mp", startpos, targetPos, player );
|
||||
|
||||
//spawn f16 model and make it do somthing cool
|
||||
|
||||
if ( !IsDefined( rocket ) )
|
||||
{
|
||||
player clearUsingRemote();
|
||||
return;
|
||||
}
|
||||
|
||||
rocket.lifeId = lifeId;
|
||||
rocket.type = "remote";
|
||||
MissileEyes( player, rocket );
|
||||
}
|
||||
|
||||
MissileEyes( player, rocket )
|
||||
{
|
||||
player endon ( "joined_team" );
|
||||
player endon ( "joined_spectators" );
|
||||
|
||||
rocket thread Rocket_CleanupOnDeath();
|
||||
player thread Player_CleanupOnGameEnded( rocket );
|
||||
player thread Player_CleanupOnTeamChange( rocket );
|
||||
|
||||
player VisionSetMissilecamForPlayer( "black_bw", 0 );
|
||||
|
||||
player endon ( "disconnect" );
|
||||
|
||||
if ( isDefined( rocket ) )
|
||||
{
|
||||
player VisionSetMissilecamForPlayer( game["thermal_vision"], 1.0 );
|
||||
player thread delayedFOFOverlay();
|
||||
player CameraLinkTo( rocket, "tag_origin" );
|
||||
player ControlsLinkTo( rocket );
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
player setThirdPersonDOF( false );
|
||||
|
||||
rocket waittill( "death" );
|
||||
|
||||
// is defined check required because remote missile doesnt handle lifetime explosion gracefully
|
||||
// instantly deletes its self after an explode and death notify
|
||||
if ( isDefined(rocket) )
|
||||
player maps\mp\_matchdata::logKillstreakEvent( "predator_missile", rocket.origin );
|
||||
|
||||
player ControlsUnlink();
|
||||
player freezeControlsWrapper( true );
|
||||
|
||||
// If a player gets the final kill with a hellfire, level.gameEnded will already be true at this point
|
||||
if ( !level.gameEnded || isDefined( player.finalKill ) )
|
||||
player thread staticEffect( 0.5 );
|
||||
|
||||
wait ( 0.5 );
|
||||
|
||||
player ThermalVisionFOFOverlayOff();
|
||||
|
||||
player CameraUnlink();
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
player setThirdPersonDOF( true );
|
||||
|
||||
}
|
||||
|
||||
player clearUsingRemote();
|
||||
}
|
||||
|
||||
|
||||
delayedFOFOverlay()
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
wait ( 0.15 );
|
||||
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
}
|
||||
|
||||
staticEffect( duration )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
|
||||
staticBG = newClientHudElem( self );
|
||||
staticBG.horzAlign = "fullscreen";
|
||||
staticBG.vertAlign = "fullscreen";
|
||||
staticBG setShader( "white", 640, 480 );
|
||||
staticBG.archive = true;
|
||||
staticBG.sort = 10;
|
||||
|
||||
static = newClientHudElem( self );
|
||||
static.horzAlign = "fullscreen";
|
||||
static.vertAlign = "fullscreen";
|
||||
static setShader( "ac130_overlay_grain", 640, 480 );
|
||||
static.archive = true;
|
||||
static.sort = 20;
|
||||
|
||||
wait ( duration );
|
||||
|
||||
static destroy();
|
||||
staticBG destroy();
|
||||
}
|
||||
|
||||
|
||||
Player_CleanupOnTeamChange( rocket )
|
||||
{
|
||||
rocket endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
|
||||
self waittill_any( "joined_team" , "joined_spectators" );
|
||||
|
||||
if ( self.team != "spectator" )
|
||||
{
|
||||
self ThermalVisionFOFOverlayOff();
|
||||
self ControlsUnlink();
|
||||
self CameraUnlink();
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
self setThirdPersonDOF( true );
|
||||
}
|
||||
self clearUsingRemote();
|
||||
|
||||
level.remoteMissileInProgress = undefined;
|
||||
}
|
||||
|
||||
|
||||
Rocket_CleanupOnDeath()
|
||||
{
|
||||
entityNumber = self getEntityNumber();
|
||||
level.rockets[ entityNumber ] = self;
|
||||
self waittill( "death" );
|
||||
|
||||
level.rockets[ entityNumber ] = undefined;
|
||||
}
|
||||
|
||||
|
||||
Player_CleanupOnGameEnded( rocket )
|
||||
{
|
||||
rocket endon ( "death" );
|
||||
self endon ( "death" );
|
||||
|
||||
level waittill ( "game_ended" );
|
||||
|
||||
self ThermalVisionFOFOverlayOff();
|
||||
self ControlsUnlink();
|
||||
self CameraUnlink();
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
self setThirdPersonDOF( true );
|
||||
}
|
327
maps/mp/killstreaks/_aastrike.gsc
Normal file
327
maps/mp/killstreaks/_aastrike.gsc
Normal file
@ -0,0 +1,327 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*******************************************************************
|
||||
// _aastrike.gsc
|
||||
//
|
||||
// Holds all the aastrike specific functions
|
||||
//
|
||||
// Jordan Hirsh Jan. 25th 2011
|
||||
********************************************************************/
|
||||
|
||||
|
||||
init()
|
||||
{
|
||||
precacheItem( "aamissile_projectile_mp" );
|
||||
precacheModel( "vehicle_av8b_harrier_jet_mp" );
|
||||
|
||||
|
||||
level.teamAirDenied["axis"] = false;
|
||||
level.teamAirDenied["allies"] = false;
|
||||
|
||||
level.rockets = [];
|
||||
level.killstreakFuncs["aastrike"] = ::tryUseAAStrike;
|
||||
}
|
||||
|
||||
|
||||
tryUseAAStrike( lifeId, streakName )
|
||||
{
|
||||
/#
|
||||
heightEnt = GetEnt( "airstrikeheight", "targetname" );
|
||||
assertEx( isDefined( heightEnt ), "NO HEIGHT ENT IN LEVEL: Don't know what this means, ask Ned or Jordan" );
|
||||
|
||||
if ( !isDefined(heightEnt) )
|
||||
return false;
|
||||
#/
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "aastrike", self.origin );
|
||||
|
||||
self thread finishAAStrike( lifeId );
|
||||
self thread teamPlayerCardSplash( "used_aastrike", self, self.team );
|
||||
return true;
|
||||
}
|
||||
|
||||
cycleTargets()
|
||||
{
|
||||
self endon("stopFindingTargets");
|
||||
self endon("disconnect");
|
||||
self endon ( "owner_gone" );
|
||||
self endon ( "game_ended" );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
wait(.05);
|
||||
self findTargets();
|
||||
wait( randomIntRange(4,5) );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
findTargets()
|
||||
{
|
||||
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "owner_gone" );
|
||||
self endon ( "game_ended" );
|
||||
|
||||
lbTargets = [];
|
||||
heliTargets = [];
|
||||
uavTargets = [];
|
||||
|
||||
if ( isDefined( level.littleBirds ) && level.littleBirds.size )
|
||||
{
|
||||
foreach ( lb in level.littleBirds )
|
||||
{
|
||||
if ( isDefined( lb.team ) && lb.team != self.team )
|
||||
lbTargets[lbTargets.size] = lb;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isDefined(level.helis) && level.helis.size )
|
||||
{
|
||||
foreach ( heli in level.helis )
|
||||
{
|
||||
if ( heli.team != self.team )
|
||||
heliTargets[heliTargets.size] = heli;
|
||||
}
|
||||
}
|
||||
|
||||
otherTeam = getOtherTeam( self.team );
|
||||
|
||||
if ( isDefined( level.activeUAVs[otherTeam] ) )
|
||||
{
|
||||
foreach ( uav in level.uavmodels[otherTeam] )
|
||||
{
|
||||
uavTargets[uavTargets.size] = uav;
|
||||
}
|
||||
}
|
||||
|
||||
targetCount = 0;
|
||||
foreach( lb in lbTargets )
|
||||
{
|
||||
wait( 3 );
|
||||
|
||||
if ( targetCount % 2 )
|
||||
self thread fireAtTarget( lb, self.team, true );
|
||||
else
|
||||
self thread fireAtTarget( lb, self.team, false );
|
||||
|
||||
targetCount++;
|
||||
}
|
||||
|
||||
foreach( heli in heliTargets )
|
||||
{
|
||||
wait( 3 );
|
||||
self thread fireAtTarget( heli, self.team, true );
|
||||
}
|
||||
|
||||
foreach( uav in uavTargets )
|
||||
{
|
||||
wait( 0.5 );
|
||||
self thread fireAtTarget( uav, self.team, false );
|
||||
}
|
||||
|
||||
/*
|
||||
if( level.ac130InUse && isDefined( level.ac130.owner ) && level.ac130.owner.team != self.team )
|
||||
{
|
||||
ac130Target = level.ac130.planemodel;
|
||||
wait( 6 );
|
||||
self thread fireAtTarget( ac130Target, self.team, true );
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
earlyAbortWatcher()
|
||||
{
|
||||
self endon( "stopFindingTargets" );
|
||||
|
||||
team = self.team;
|
||||
|
||||
if ( bot_is_fireteam_mode() )
|
||||
{
|
||||
self waittill( "killstreak_disowned" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self waittill_either( "killstreak_disowned", "game_ended" );
|
||||
}
|
||||
|
||||
self notify( "owner_gone" );
|
||||
|
||||
level.teamAirDenied[getOtherTeam(team)] = false;
|
||||
level.airDeniedPlayer = undefined;
|
||||
}
|
||||
|
||||
finishAAStrike( lifeId )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "owner_gone" );
|
||||
self endon ( "game_ended" );
|
||||
|
||||
//message air up there
|
||||
level.teamAirDenied[getOtherTeam(self.team)] = true;
|
||||
level.airDeniedPlayer = self;
|
||||
self thread earlyAbortWatcher();
|
||||
|
||||
//splash incoming in 10
|
||||
//wait ( 1 );
|
||||
//splash incoming in 9
|
||||
//wait(3)
|
||||
//splash incoming in 6
|
||||
//wait(4)
|
||||
//splash incoming in 2
|
||||
//wait( 1 )
|
||||
//splash incoming in 1
|
||||
|
||||
self thread cycleTargets();
|
||||
|
||||
for( i = 0; i < 4; i++ )
|
||||
{
|
||||
wait ( 6 );
|
||||
if ( i == 1 || i == 3 )
|
||||
self thread doFlyBy( true );
|
||||
else
|
||||
self thread doFlyBy( false );
|
||||
}
|
||||
|
||||
wait(3);
|
||||
|
||||
self notify("stopFindingTargets");
|
||||
level.teamAirDenied[getOtherTeam(self.team)] = false;
|
||||
level.airDeniedPlayer = undefined;
|
||||
}
|
||||
|
||||
fireAtTarget( curTarget, team, showIcon )
|
||||
{
|
||||
if ( !isDefined(curTarget) )
|
||||
return;
|
||||
|
||||
upVector = (0, 0, 14000 );
|
||||
miniUpVector = (0, 0, 1500);
|
||||
backDist = 15000;
|
||||
forwardDist = 20000;
|
||||
|
||||
targetPos = curTarget.origin;
|
||||
upVector = (0,0,1) * (targetPos) + (0,0,1000);
|
||||
|
||||
curTargetYaw = curTarget.angles * (0,1,0);
|
||||
|
||||
forward = AnglesToForward( curTargetYaw );
|
||||
startpos = curTarget.origin + miniUpVector + forward * backDist * -1;
|
||||
endPos = curTarget.origin + miniUpVector + forward * forwardDist;
|
||||
|
||||
rocket1 = MagicBullet( "aamissile_projectile_mp", startpos + (0,0,-75), curTarget.origin, self );
|
||||
rocket1 Missile_SetTargetEnt( curTarget );
|
||||
rocket1 Missile_SetFlightmodeDirect();
|
||||
|
||||
rocket2 = MagicBullet( "aamissile_projectile_mp", startpos + (RandomInt(500), RandomInt(500), -75), curTarget.origin, self );
|
||||
rocket2 Missile_SetTargetEnt( curTarget );
|
||||
rocket2 Missile_SetFlightmodeDirect();
|
||||
|
||||
if ( showIcon )
|
||||
plane = spawnplane( self, "script_model", startpos, "compass_objpoint_airstrike_friendly", "compass_objpoint_airstrike_friendly" );
|
||||
else
|
||||
plane = spawnplane( self, "script_model", startpos );
|
||||
|
||||
if ( self.team == "allies" )
|
||||
plane setModel( "vehicle_av8b_harrier_jet_mp" );
|
||||
else
|
||||
plane setModel( "vehicle_av8b_harrier_jet_opfor_mp" );
|
||||
|
||||
length = distance(startPos, endPos);
|
||||
|
||||
plane.angles = vectorToAngles( endPos-startPos );
|
||||
|
||||
plane thread AASoundManager(length);
|
||||
plane thread playPlaneFx();
|
||||
|
||||
length = distance(startPos, endPos);
|
||||
plane moveTo( endPos * 2, length/2000, 0, 0 );
|
||||
|
||||
wait( length/3000 );
|
||||
plane delete();
|
||||
}
|
||||
|
||||
|
||||
AASoundManager( length )
|
||||
{
|
||||
self playloopsound( "veh_aastrike_flyover_loop" );
|
||||
|
||||
wait( (length/2) / 2000 );
|
||||
self stopLoopSound();
|
||||
self playloopsound( "veh_aastrike_flyover_outgoing_loop" );
|
||||
}
|
||||
|
||||
|
||||
doFlyBy( showIcon )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
|
||||
randSpawn = randomInt( level.spawnPoints.size - 1 );
|
||||
targetPos = level.spawnPoints[randSpawn].origin * (1,1,0);
|
||||
|
||||
backDist = 20000;
|
||||
forwardDist = 20000;
|
||||
heightEnt = GetEnt( "airstrikeheight", "targetname" );
|
||||
|
||||
upVector = (0, 0, heightEnt.origin[2] + randomIntRange(-100, 600) );
|
||||
|
||||
forward = AnglesToForward( (0,randomInt(45),0) );
|
||||
|
||||
startpos = targetPos + upVector + forward * backDist * -1;
|
||||
endPos = targetPos + upVector + forward * forwardDist;
|
||||
|
||||
plane2StartPos = startpos + ( randomIntRange(400,500), randomIntRange(400,500), randomIntRange(200,300) );
|
||||
plane2EndPos = endPos + ( randomIntRange(400,500), randomIntRange(400,500), randomIntRange(200,300) );
|
||||
|
||||
if ( showIcon )
|
||||
plane = spawnplane( self, "script_model", startpos, "hud_minimap_harrier_green", "hud_minimap_harrier_red" );
|
||||
else
|
||||
plane = spawnplane( self, "script_model", startpos );
|
||||
|
||||
plane2 = spawnplane( self, "script_model", plane2StartPos );
|
||||
|
||||
if ( self.team == "allies" )
|
||||
{
|
||||
plane setModel( "vehicle_av8b_harrier_jet_mp" );
|
||||
plane2 setModel( "vehicle_av8b_harrier_jet_mp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
plane setModel( "vehicle_av8b_harrier_jet_opfor_mp" );
|
||||
plane2 setModel( "vehicle_av8b_harrier_jet_opfor_mp" );
|
||||
}
|
||||
|
||||
plane.angles = vectorToAngles( endPos-startPos );
|
||||
plane playloopsound( "veh_aastrike_flyover_loop" );
|
||||
plane thread playPlaneFx();
|
||||
|
||||
plane2.angles = vectorToAngles( endPos-plane2StartPos );
|
||||
plane2 thread playPlaneFx();
|
||||
|
||||
length = distance(startPos, endPos);
|
||||
plane moveTo( endPos * 2, length/1800, 0, 0 );
|
||||
wait( randomFloatRange( .25, .5 ) );
|
||||
plane2 moveTo( plane2EndPos * 2, length/1800, 0, 0 );
|
||||
|
||||
wait( length/1600 );
|
||||
plane delete();
|
||||
plane2 delete();
|
||||
|
||||
}
|
||||
|
||||
playPlaneFx()
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
wait( 0.5);
|
||||
playfxontag( level.fx_airstrike_afterburner, self, "tag_engine_right" );
|
||||
wait( 0.5);
|
||||
playfxontag( level.fx_airstrike_afterburner, self, "tag_engine_left" );
|
||||
wait( 0.5);
|
||||
playfxontag( level.fx_airstrike_contrail, self, "tag_right_wingtip" );
|
||||
wait( 0.5);
|
||||
playfxontag( level.fx_airstrike_contrail, self, "tag_left_wingtip" );
|
||||
}
|
||||
|
2438
maps/mp/killstreaks/_ac130.gsc
Normal file
2438
maps/mp/killstreaks/_ac130.gsc
Normal file
File diff suppressed because it is too large
Load Diff
214
maps/mp/killstreaks/_agent_killstreak.gsc
Normal file
214
maps/mp/killstreaks/_agent_killstreak.gsc
Normal file
@ -0,0 +1,214 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
#include maps\mp\bots\_bots_util;
|
||||
#include maps\mp\bots\_bots_strategy;
|
||||
|
||||
//===========================================
|
||||
// constants
|
||||
//===========================================
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_GAME = 5;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER = 2;
|
||||
|
||||
|
||||
//===========================================
|
||||
// init
|
||||
//===========================================
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs["agent"] = ::tryUseSquadmate;
|
||||
level.killStreakFuncs["recon_agent"] = ::tryUseReconSquadmate;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// setup_callbacks
|
||||
//===========================================
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["squadmate"] = level.agent_funcs["player"];
|
||||
|
||||
level.agent_funcs["squadmate"]["think"] = ::squadmate_agent_think;
|
||||
level.agent_funcs["squadmate"]["on_killed"] = ::on_agent_squadmate_killed;
|
||||
level.agent_funcs["squadmate"]["on_damaged"] = maps\mp\agents\_agents::on_agent_player_damaged;
|
||||
level.agent_funcs["squadmate"]["gametype_update"]= ::no_gametype_update;
|
||||
}
|
||||
|
||||
no_gametype_update()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
//===========================================
|
||||
// tryUseSquadmate
|
||||
//===========================================
|
||||
tryUseSquadmate( lifeId, streakName )
|
||||
{
|
||||
return useSquadmate( "agent" );
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// tryUseReconSquadmate
|
||||
//===========================================
|
||||
tryUseReconSquadmate( lifeId, streakName )
|
||||
{
|
||||
return useSquadmate( "reconAgent" );
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// useSquadmate
|
||||
//===========================================
|
||||
useSquadmate( killStreakType )
|
||||
{
|
||||
// limit the number of active "squadmate" agents allowed per game
|
||||
if( getNumActiveAgents( "squadmate" ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_GAME )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// limit the number of active agents allowed per player
|
||||
if( getNumOwnedActiveAgents( self ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to spawn the agent on a path node near the player
|
||||
nearestPathNode = self getValidSpawnPathNodeNearPlayer( false, true );
|
||||
|
||||
if( !IsDefined(nearestPathNode) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the player is still alive before the agent trys to spawn on the player
|
||||
if( !isReallyAlive(self) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
spawnOrigin = nearestPathNode.origin;
|
||||
spawnAngles = VectorToAngles( self.origin - nearestPathNode.origin );
|
||||
|
||||
agent = maps\mp\agents\_agents::add_humanoid_agent( "squadmate", self.team, undefined, spawnOrigin, spawnAngles, self, false, false, "veteran" );
|
||||
if( !IsDefined( agent ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
agent.killStreakType = killStreakType;
|
||||
|
||||
if ( agent.killStreakType == "reconAgent" )
|
||||
{
|
||||
// 2013-06-26 wallace
|
||||
// At the time of this comment, giveLoadout runs and finishes execution immediately
|
||||
// We run sendAgentWeaponNotify and finishReconAgentLoadout since they block until giveLoadout sends its notify
|
||||
agent thread sendAgentWeaponNotify( "iw6_riotshield_mp" );
|
||||
agent thread finishReconAgentLoadout();
|
||||
agent thread maps\mp\gametypes\_class::giveLoadout( self.pers["team"], "reconAgent", false );
|
||||
agent maps\mp\agents\_agent_common::set_agent_health( 250 );
|
||||
agent maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
}
|
||||
else
|
||||
{
|
||||
agent maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
}
|
||||
|
||||
agent _setNameplateMaterial( "player_name_bg_green_agent", "player_name_bg_red_agent" );
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( agent.killStreakType, self.origin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
finishReconAgentLoadout()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "giveLoadout" );
|
||||
|
||||
self maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
self givePerk( "specialty_quickswap", false );
|
||||
self givePerk( "specialty_regenfaster", false );
|
||||
|
||||
// 2014-04-24 JC: Reduced accuracy on Squad Mate Support Streak
|
||||
self BotSetDifficultySetting( "minInaccuracy", 1.5 * self BotGetDifficultySetting( "minInaccuracy" ) );
|
||||
self BotSetDifficultySetting( "maxInaccuracy", 1.5 * self BotGetDifficultySetting( "maxInaccuracy" ) );
|
||||
// 2014-04-24 JC: Reduced fire rate on Squad Mate Support Streak
|
||||
// min: from 200 to 300
|
||||
// max: from 400 to 500
|
||||
self BotSetDifficultySetting( "minFireTime", 1.5 * self BotGetDifficultySetting( "minFireTime" ) );
|
||||
self BotSetDifficultySetting( "maxFireTime", 1.25 * self BotGetDifficultySetting( "maxFireTime" ) );
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// sendAgentWeaponNotify
|
||||
//===========================================
|
||||
sendAgentWeaponNotify( weaponName )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "giveLoadout" );
|
||||
|
||||
if( !IsDefined(weaponName) )
|
||||
weaponName = "iw6_riotshield_mp";
|
||||
|
||||
self notify( "weapon_change", weaponName );
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// squadmate_agent_think
|
||||
//=======================================================
|
||||
squadmate_agent_think()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "owner_disconnect" );
|
||||
|
||||
level endon( "game_ended" );
|
||||
|
||||
while(1)
|
||||
{
|
||||
// Squad mate agent prefers to have shield out when not in combat and guarding player
|
||||
self BotSetFlag( "prefer_shield_out", true );
|
||||
|
||||
handled_by_gametype = self [[ self agentFunc("gametype_update") ]]();
|
||||
if ( !handled_by_gametype )
|
||||
{
|
||||
if ( !self bot_is_guarding_player( self.owner ) )
|
||||
self bot_guard_player( self.owner, 350 );
|
||||
}
|
||||
|
||||
wait(0.05);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_squadmate_killed
|
||||
//=======================================================
|
||||
on_agent_squadmate_killed(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration)
|
||||
{
|
||||
self maps\mp\agents\_agents::on_humanoid_agent_killed_common(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration, false);
|
||||
|
||||
// award XP for killing agents
|
||||
if( IsPlayer( eAttacker ) && IsDefined(self.owner) && eAttacker != self.owner )
|
||||
{
|
||||
self.owner leaderDialogOnPlayer( "squad_killed" );
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_squad_mate" );
|
||||
}
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
}
|
298
maps/mp/killstreaks/_air_superiority.gsc
Normal file
298
maps/mp/killstreaks/_air_superiority.gsc
Normal file
@ -0,0 +1,298 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// replacement for EMP
|
||||
// only affects aircraft
|
||||
// remarkably similar to aastrike, should probably get rid of that...
|
||||
KS_NAME = "air_superiority";
|
||||
kProjectileName = "aamissile_projectile_mp";
|
||||
|
||||
init()
|
||||
{
|
||||
config = SpawnStruct();
|
||||
config.modelNames = [];
|
||||
config.modelNames[ "allies" ] = "vehicle_a10_warthog_iw6_mp";
|
||||
config.modelNames[ "axis" ] = "vehicle_a10_warthog_iw6_mp";
|
||||
config.inboundSfx = "veh_mig29_dist_loop";
|
||||
//config.inboundSfx = "veh_aastrike_flyover_loop";
|
||||
//config.outboundSfx = "veh_aastrike_flyover_outgoing_loop";
|
||||
config.compassIconFriendly = "compass_objpoint_airstrike_friendly";
|
||||
config.compassIconEnemy = "compass_objpoint_airstrike_busy";
|
||||
// sonic boom?
|
||||
config.speed = 4000;
|
||||
config.halfDistance = 20000;
|
||||
config.distFromPlayer = 4000;
|
||||
config.heightRange = 250;
|
||||
//config.attackTime = 2.0;
|
||||
config.numMissileVolleys = 3;
|
||||
config.outboundFlightAnim = "airstrike_mp_roll";
|
||||
config.sonicBoomSfx = "veh_mig29_sonic_boom";
|
||||
config.onAttackDelegate = ::attackEnemyAircraft;
|
||||
config.onFlybyCompleteDelegate = ::cleanupFlyby;
|
||||
config.xpPopup = "destroyed_air_superiority";
|
||||
config.callout = "callout_destroyed_air_superiority";
|
||||
config.voDestroyed = undefined;
|
||||
config.killCamOffset = (-800, 0, 200);
|
||||
|
||||
level.planeConfigs[ KS_NAME ] = config;
|
||||
|
||||
level.killstreakFuncs[KS_NAME] = ::onUse;
|
||||
|
||||
level.teamAirDenied["axis"] = false;
|
||||
level.teamAirDenied["allies"] = false;
|
||||
}
|
||||
|
||||
onUse( lifeId, streakName )
|
||||
{
|
||||
assert( isDefined( self ) );
|
||||
|
||||
// check for active air_superiority strikes
|
||||
otherTeam = getOtherTeam( self.team );
|
||||
if ( (level.teamBased && level.teamAirDenied[ otherTeam] )
|
||||
|| (!level.teamBased && IsDefined( level.airDeniedPlayer ) && level.airDeniedPlayer == self )
|
||||
)
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// scramble the fighters
|
||||
self thread doStrike( lifeId, KS_NAME );
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "air_superiority", self.origin );
|
||||
self thread teamPlayerCardSplash( "used_air_superiority", self );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
doStrike( lifeId, streakName )
|
||||
{
|
||||
config = level.planeConfigs[ streakName ];
|
||||
|
||||
flightPlan = self maps\mp\killstreaks\_plane::getPlaneFlightPlan( config.distFromPlayer );
|
||||
|
||||
// play inbound vo
|
||||
|
||||
wait( 1 );
|
||||
|
||||
targetTeam = getOtherTeam(self.team);
|
||||
|
||||
level.teamAirDenied[targetTeam] = true;
|
||||
level.airDeniedPlayer = self;
|
||||
|
||||
doOneFlyby( streakName, lifeId, flightPlan.targetPos, flightPlan.flightDir, flightPlan.height);
|
||||
|
||||
self waittill( "aa_flyby_complete" );
|
||||
|
||||
// coming back around vo
|
||||
wait( 2 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
// don't do second flyby if owner has disconnected
|
||||
if ( IsDefined( self ) )
|
||||
{
|
||||
doOneFlyby( streakName, lifeId, flightPlan.targetPos, -1 * flightPlan.flightDir, flightPlan.height );
|
||||
|
||||
self waittill( "aa_flyby_complete" );
|
||||
}
|
||||
|
||||
level.teamAirDenied[targetTeam] = false;
|
||||
level.airDeniedPlayer = undefined;
|
||||
|
||||
// play outbound vo
|
||||
// should check if there are still enemy aircraft in the air and play appropriate vo
|
||||
}
|
||||
|
||||
doOneFlyby( streakName, lifeId, targetPos, dir, flyHeight )
|
||||
{
|
||||
config = level.planeConfigs[ streakName ];
|
||||
|
||||
// absolute height should be derived from the heightEnt
|
||||
flightPath = maps\mp\killstreaks\_plane::getFlightPath( targetPos, dir, config.halfDistance, true, flyHeight, config.speed, -0.5 * config.halfDistance, streakName );
|
||||
|
||||
// may want to break this up into spawn, move, cleanup components
|
||||
// so that we can reuse the plane
|
||||
level thread maps\mp\killstreaks\_plane::doFlyby( lifeId, self, lifeId,
|
||||
flightPath["startPoint"] + (0, 0, randomInt(config.heightRange) ),
|
||||
flightPath["endPoint"] + (0, 0, randomInt(config.heightRange) ),
|
||||
flightPath["attackTime"],
|
||||
flightPath["flyTime"],
|
||||
dir,
|
||||
streakName );
|
||||
}
|
||||
|
||||
|
||||
attackEnemyAircraft( pathEnd, flyTime, beginAttackTime, owner, streakName ) // self == plane
|
||||
{
|
||||
self endon( "death" );
|
||||
self.owner endon( "killstreak_disowned" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
wait (beginAttackTime);
|
||||
|
||||
targets = findAllTargets( self.owner, self.team );
|
||||
config = level.planeConfigs[ streakName ];
|
||||
numVolleys = config.numMissileVolleys;
|
||||
targetIndex = targets.size - 1;
|
||||
|
||||
while (targetIndex >= 0
|
||||
&& numVolleys > 0
|
||||
)
|
||||
{
|
||||
target = targets[ targetIndex ];
|
||||
if ( IsDefined( target ) && IsAlive( target ) )
|
||||
{
|
||||
self fireAtTarget( target );
|
||||
numVolleys--;
|
||||
wait ( 1 );
|
||||
}
|
||||
targetIndex--;
|
||||
}
|
||||
}
|
||||
|
||||
cleanupFlyby( owner, plane, streakName )
|
||||
{
|
||||
owner notify( "aa_flyby_complete" );
|
||||
}
|
||||
|
||||
// curTargetsStruct is a struct that holds the array of targets
|
||||
// !!! use this hack since arrays are passed by value, while structs are passed by reference
|
||||
findTargetsOfType( attacker, victimTeam, checkFunc, candidateList, curTargetsStruct )
|
||||
{
|
||||
if ( IsDefined( candidateList ) )
|
||||
{
|
||||
foreach ( target in candidateList )
|
||||
{
|
||||
if ( [[ checkFunc ]]( attacker, victimTeam, target ) )
|
||||
{
|
||||
curTargetsStruct.targets[ curTargetsStruct.targets.size ] = target;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return curTargetsStruct;
|
||||
}
|
||||
|
||||
// unlike the aa strike, we only search for targets once
|
||||
// because we block new air strikes from behing launched
|
||||
// also: probably could flip the order that targets are acquired if we want
|
||||
// the jets to go after low-cost killstreaks first
|
||||
findAllTargets( attacker, attackerTeam )
|
||||
{
|
||||
wrapper = SpawnStruct();
|
||||
wrapper.targets = [];
|
||||
|
||||
// ok, I'm sorry for function pointers, but it makes me sad to do unnecessary if checks all the time
|
||||
// isEnemyFunc will test if the target belongs to an enemy
|
||||
isEnemyFunc = undefined;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
isEnemyFunc = ::isValidTeamTarget;
|
||||
}
|
||||
else
|
||||
{
|
||||
isEnemyFunc = ::isValidFFATarget;
|
||||
}
|
||||
victimTeam = undefined;
|
||||
if ( IsDefined( attackerTeam ) )
|
||||
{
|
||||
victimTeam = getOtherTeam( attackerTeam );
|
||||
}
|
||||
|
||||
// 2013-09-02 wallace: Since arrays are passed by value (or so JoeC tells me)
|
||||
// we will wrap up the targets array in a struct that is past by reference
|
||||
// this means that each call to findTargetsOfType is adding targets to the SAME array, not a copy
|
||||
// Destroy player controlled and higher level-KS's last, so put them in the front of the arraw
|
||||
findTargetsOfType( attacker, victimTeam, isEnemyFunc, level.heli_pilot, wrapper );
|
||||
if ( IsDefined( level.lbSniper ) )
|
||||
{
|
||||
if ( [[ isEnemyFunc ]]( attacker, victimTeam, level.lbSniper ) )
|
||||
{
|
||||
wrapper.targets[ wrapper.targets.size ] = level.lbSniper;
|
||||
}
|
||||
}
|
||||
|
||||
findTargetsOfType( attacker, victimTeam, isEnemyFunc, level.planes, wrapper );
|
||||
// 2013-09-03 wallace: ugh, this is stupid. Vanguard puts itself in both remote_uav and littlebird arrays. So, don't use remote_uav as possible targets
|
||||
// findTargetsOfType( attacker, victimTeam, checkFunc, level.remote_uav, wrapper );
|
||||
findTargetsOfType( attacker, victimTeam, isEnemyFunc, level.littleBirds, wrapper );
|
||||
findTargetsOfType( attacker, victimTeam, isEnemyFunc, level.helis, wrapper );
|
||||
|
||||
return wrapper.targets;
|
||||
}
|
||||
|
||||
fireAtTarget( curTarget ) // self == plane
|
||||
{
|
||||
if ( !isDefined(curTarget) )
|
||||
return;
|
||||
|
||||
// do this check in case the plane's owner disconnects mid flight
|
||||
// we still want this pass to finish
|
||||
owner = undefined;
|
||||
if ( IsDefined( self.owner ) )
|
||||
owner = self.owner;
|
||||
|
||||
forwardVec = 384 * AnglesToForward( self.angles );
|
||||
|
||||
startpoint = self GetTagOrigin( "tag_missile_1" ) + forwardVec;
|
||||
rocket1 = MagicBullet( kProjectileName, startPoint, startPoint + forwardVec, owner );
|
||||
rocket1.vehicle_fired_from = self;
|
||||
|
||||
startpoint = self GetTagOrigin( "tag_missile_2" ) + forwardVec;
|
||||
rocket2 = MagicBullet( kProjectileName, startPoint, startPoint + forwardVec, owner );
|
||||
rocket2.vehicle_fired_from = self;
|
||||
|
||||
missiles = [ rocket1, rocket2 ];
|
||||
curTarget notify( "targeted_by_incoming_missile", missiles );
|
||||
|
||||
self thread startMissileGuidance( curTarget, 0.25, missiles );
|
||||
}
|
||||
|
||||
startMissileGuidance( curTarget, igniteTime, missileArray )
|
||||
{
|
||||
wait( igniteTime );
|
||||
|
||||
if ( IsDefined( curTarget ) )
|
||||
{
|
||||
targetPoint = undefined;
|
||||
//AH: HACK: The harrier doesn't have the tag_missile_target, but it does have a tag_body.
|
||||
// The code works fine without this check, but GetTagOrigin throws an SRE if the tag does not exist.
|
||||
if ( curTarget.model != "vehicle_av8b_harrier_jet_mp" )
|
||||
targetPoint = curTarget GetTagOrigin( "tag_missile_target" );
|
||||
if ( !IsDefined( targetPoint ) )
|
||||
{
|
||||
targetPoint = curTarget GetTagOrigin( "tag_body" );
|
||||
}
|
||||
targetOffset = targetPoint - curTarget.origin;
|
||||
|
||||
foreach ( missile in missileArray )
|
||||
{
|
||||
if ( IsValidMissile( missile ) )
|
||||
{
|
||||
missile Missile_SetTargetEnt( curTarget, targetOffset );
|
||||
missile Missile_SetFlightmodeDirect();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveVehicles( attacker, victimTeam )
|
||||
{
|
||||
// thread all of the things that need to get destroyed, this way we can put frame waits in between each destruction so we don't hit the server with a lot at one time
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", level.helis );
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", level.littleBirds );
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", level.heli_pilot );
|
||||
if ( IsDefined( level.lbSniper ) )
|
||||
{
|
||||
// kind of hack, but destroyTargets does a lot of needed setup
|
||||
tempArray = [];
|
||||
tempArray[0] = level.lbSniper;
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", tempArray );
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", level.remote_uav );
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, victimTeam, "aamissile_projectile_mp", level.planes );
|
||||
}
|
2504
maps/mp/killstreaks/_airdrop.gsc
Normal file
2504
maps/mp/killstreaks/_airdrop.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1368
maps/mp/killstreaks/_airstrike.gsc
Normal file
1368
maps/mp/killstreaks/_airstrike.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2021
maps/mp/killstreaks/_autosentry.gsc
Normal file
2021
maps/mp/killstreaks/_autosentry.gsc
Normal file
File diff suppressed because it is too large
Load Diff
134
maps/mp/killstreaks/_autoshotgun.gsc
Normal file
134
maps/mp/killstreaks/_autoshotgun.gsc
Normal file
@ -0,0 +1,134 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs["auto_shotgun"] = ::tryUseAutoShotgun;
|
||||
level.killstreakSetupFuncs["auto_shotgun"] = ::shotgunSetup;
|
||||
|
||||
level.killStreakFuncs["thumper"] = ::tryUseThumper;
|
||||
level.killstreakSetupFuncs["thumper"] = ::thumperSetup;
|
||||
|
||||
thread onPlayerConnect();
|
||||
}
|
||||
|
||||
|
||||
shotgunSetup()
|
||||
{
|
||||
self giveMaxAmmo( "aa12_mp" );
|
||||
|
||||
self thread saveWeaponAmmoOnDeath( "aa12_mp" );
|
||||
}
|
||||
|
||||
|
||||
tryUseAutoShotgun( lifeId )
|
||||
{
|
||||
self thread removeWeaponOnOutOfAmmo( "aa12_mp" );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
thumperSetup()
|
||||
{
|
||||
self giveMaxAmmo( "m79_mp" );
|
||||
|
||||
self thread saveWeaponAmmoOnDeath( "m79_mp" );
|
||||
}
|
||||
|
||||
|
||||
tryUseThumper()
|
||||
{
|
||||
self thread removeWeaponOnOutOfAmmo( "m79_mp" );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if ( !isDefined( self.pers["ksWeapon_clip_ammo"] ) || !isDefined( self.pers["ksWeapon_name"] ) )
|
||||
continue;
|
||||
|
||||
weaponName = self.pers["ksWeapon_name"];
|
||||
|
||||
if ( isDefined( self.pers["killstreak"] ) && getKillstreakWeapon( self.pers["killstreak"] ) != weaponName )
|
||||
{
|
||||
self.pers["ksWeapon_name"] = undefined;
|
||||
self.pers["ksWeapon_clip_ammo"] = undefined;
|
||||
self.pers["ksWeapon_stock_ammo"] = undefined;
|
||||
continue;
|
||||
}
|
||||
|
||||
self maps\mp\killstreaks\_killstreaks::giveKillstreakWeapon( weaponName );
|
||||
|
||||
self setWeaponAmmoStock( weaponName, self.pers["ksWeapon_stock_ammo"] );
|
||||
self setWeaponAmmoClip( weaponName, self.pers["ksWeapon_clip_ammo"] );
|
||||
|
||||
self thread removeWeaponOnOutOfAmmo( weaponName );
|
||||
self thread saveWeaponAmmoOnDeath( weaponName );
|
||||
}
|
||||
}
|
||||
|
||||
saveWeaponAmmoOnDeath( weaponName )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "got_killstreak" );
|
||||
|
||||
self notify( "saveWeaponAmmoOnDeath" );
|
||||
self endon( "saveWeaponAmmoOnDeath" );
|
||||
|
||||
self.pers["ksWeapon_name"] = undefined;
|
||||
self.pers["ksWeapon_clip_ammo"] = undefined;
|
||||
self.pers["ksWeapon_stock_ammo"] = undefined;
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
if ( !self hasWeapon( weaponName ) )
|
||||
return;
|
||||
|
||||
self.pers["ksWeapon_name"] = weaponName;
|
||||
self.pers["ksWeapon_clip_ammo"] = self getWeaponAmmoClip( weaponName );
|
||||
self.pers["ksWeapon_stock_ammo"] = self getWeaponAmmoStock( weaponName );
|
||||
}
|
||||
|
||||
|
||||
removeWeaponOnOutOfAmmo( weaponName )
|
||||
{
|
||||
self endon ( "disconnect" );
|
||||
self endon ( "death" );
|
||||
|
||||
self notify( weaponName + "_ammo_monitor" );
|
||||
self endon( weaponName + "_ammo_monitor" );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
self waittill ( "end_firing" );
|
||||
|
||||
if ( self getCurrentWeapon() != weaponName )
|
||||
continue;
|
||||
|
||||
total_ammo = self getWeaponAmmoClip( weaponName ) + self getWeaponAmmoStock( weaponName );
|
||||
if ( total_ammo )
|
||||
continue;
|
||||
|
||||
self takeWeapon( weaponName );
|
||||
return;
|
||||
}
|
||||
}
|
1261
maps/mp/killstreaks/_ball_drone.gsc
Normal file
1261
maps/mp/killstreaks/_ball_drone.gsc
Normal file
File diff suppressed because it is too large
Load Diff
846
maps/mp/killstreaks/_deployablebox.gsc
Normal file
846
maps/mp/killstreaks/_deployablebox.gsc
Normal file
@ -0,0 +1,846 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
/*
|
||||
Deployable box killstreaks: the player will be able to place a box in the world and teammates can grab items from it
|
||||
this will be used on multiple killstreaks where you can place a box in the world with something in it
|
||||
*/
|
||||
|
||||
BOX_TIMEOUT_UPDATE_INTERVAL = 1.0;
|
||||
DEFAULT_USE_TIME = 3000;
|
||||
BOX_DEFAULT_HEALTH = 999999; // so that boxes aren't killed in code
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !IsDefined( level.boxSettings ) )
|
||||
{
|
||||
level.boxSettings = [];
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// MARKER FUNCTIONS
|
||||
// 2012-06-21 wallace
|
||||
// Stole an updated version from _uplink.gsc. Should probably unify all these funcs eventually
|
||||
//////////////////////////////////////////////////
|
||||
beginDeployableViaMarker( lifeId, boxType )
|
||||
{
|
||||
self thread watchDeployableMarkerCancel( boxType );
|
||||
self thread watchDeployableMarkerPlacement( boxType, lifeId );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
result = self waittill_any_return( "deployable_canceled", "deployable_deployed", "death", "disconnect" );
|
||||
|
||||
return ( result == "deployable_deployed" );
|
||||
}
|
||||
}
|
||||
|
||||
tryUseDeployable( lifeId, boxType ) // self == player
|
||||
{
|
||||
self thread watchDeployableMarkerCancel( boxType );
|
||||
self thread watchDeployableMarkerPlacement( boxType, lifeId );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
result = self waittill_any_return( "deployable_canceled", "deployable_deployed", "death", "disconnect" );
|
||||
|
||||
return ( result == "deployable_deployed" );
|
||||
}
|
||||
}
|
||||
|
||||
watchDeployableMarkerCancel( boxType )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "deployable_deployed" );
|
||||
|
||||
boxConfig = level.boxSettings[ boxType ];
|
||||
currentWeapon = self getCurrentWeapon();
|
||||
|
||||
while( currentWeapon == boxConfig.weaponInfo )
|
||||
{
|
||||
self waittill( "weapon_change", currentWeapon );
|
||||
}
|
||||
|
||||
self notify( "deployable_canceled" );
|
||||
}
|
||||
|
||||
watchDeployableMarkerPlacement( boxType, lifeId )
|
||||
{
|
||||
self endon( "spawned_player" ); // you shouldn't do endon( "death" ) here because this thread needs to run
|
||||
self endon( "disconnect" );
|
||||
self endon( "deployable_canceled" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "grenade_fire", marker, weaponName );
|
||||
|
||||
if( isReallyAlive(self) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
marker Delete();
|
||||
}
|
||||
}
|
||||
|
||||
marker MakeCollideWithItemClip( true );
|
||||
|
||||
self notify( "deployable_deployed" );
|
||||
|
||||
marker.owner = self;
|
||||
marker.weaponName = weaponName;
|
||||
self.marker = marker;
|
||||
|
||||
marker PlaySoundToPlayer( level.boxSettings[ boxType ].deployedSfx, self );
|
||||
|
||||
marker thread markerActivate( lifeId, boxType, ::box_setActive );
|
||||
}
|
||||
|
||||
override_box_moving_platform_death( data )
|
||||
{
|
||||
self notify( "death" ); // we're doing this here instead of letting the mover code just delete us so that we can run our necessary clean-up functionality (like removal of the objective marker from the minimap)
|
||||
}
|
||||
|
||||
markerActivate( lifeId, boxType, usedCallback ) // self == marker
|
||||
{
|
||||
self notify( "markerActivate" );
|
||||
self endon( "markerActivate" );
|
||||
//self waittill( "explode", position );
|
||||
self waittill( "missile_stuck" );
|
||||
owner = self.owner;
|
||||
position = self.origin;
|
||||
|
||||
if ( !isDefined( owner ) )
|
||||
return;
|
||||
|
||||
box = createBoxForPlayer( boxType, position, owner );
|
||||
|
||||
// For moving platforms.
|
||||
data = SpawnStruct();
|
||||
data.linkParent = self GetLinkedParent();
|
||||
|
||||
//fixes wall hack exploit with linked items
|
||||
if ( isDefined( data.linkParent ) && isDefined( data.linkParent.model ) && DeployableExclusion( data.linkParent.model ) )
|
||||
{
|
||||
box.origin = data.linkParent.origin;
|
||||
|
||||
grandParent = data.linkParent GetLinkedParent();
|
||||
|
||||
if ( isDefined( grandParent ) )
|
||||
data.linkParent = grandParent;
|
||||
else
|
||||
data.linkParent = undefined;
|
||||
}
|
||||
|
||||
data.deathOverrideCallback = ::override_box_moving_platform_death;
|
||||
box thread maps\mp\_movers::handle_moving_platforms( data );
|
||||
|
||||
box.moving_platform = data.linkParent;
|
||||
|
||||
box SetOtherEnt(owner);
|
||||
|
||||
// ES - 2/24/14 - This waitframe is causing an issue where, when deployed on a moving platform, the "death" notification is sent instantly, but is never caught.
|
||||
wait 0.05;
|
||||
|
||||
//self playSound( "sentry_gun_beep" );
|
||||
box thread [[ usedCallback ]]();
|
||||
|
||||
self delete();
|
||||
|
||||
if( IsDefined(box) && (box touchingBadTrigger()) )
|
||||
{
|
||||
box notify( "death" );
|
||||
}
|
||||
}
|
||||
|
||||
DeployableExclusion( parentModel )
|
||||
{
|
||||
if ( parentModel == "mp_satcom" )
|
||||
return true;
|
||||
else if ( IsSubStr( parentModel, "paris_catacombs_iron" ) )
|
||||
return true;
|
||||
else if ( IsSubStr( parentModel, "mp_warhawk_iron_gate" ) )
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
isHoldingDeployableBox()
|
||||
{
|
||||
curWeap = self GetCurrentWeapon();
|
||||
if ( IsDefined( curWeap ) )
|
||||
{
|
||||
foreach( deplBoxWeap in level.boxSettings )
|
||||
{
|
||||
if ( curWeap == deplBoxWeap.weaponInfo )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
///////////////////////////////////////////////////
|
||||
// END MARKER FUNCTIONS
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////
|
||||
// BOX HANDLER FUNCTIONS
|
||||
//////////////////////////////////////////////////
|
||||
|
||||
createBoxForPlayer( boxType, position, owner )
|
||||
{
|
||||
assertEx( isDefined( owner ), "createBoxForPlayer() called without owner specified" );
|
||||
|
||||
boxConfig = level.boxSettings[ boxType ];
|
||||
|
||||
box = Spawn( "script_model", position - (0,0,1) );
|
||||
box setModel( boxConfig.modelBase );
|
||||
box.health = BOX_DEFAULT_HEALTH;
|
||||
box.maxHealth = boxConfig.maxHealth;
|
||||
box.angles = owner.angles;
|
||||
box.boxType = boxType;
|
||||
box.owner = owner;
|
||||
box.team = owner.team;
|
||||
box.id = boxConfig.id;
|
||||
|
||||
if ( IsDefined( boxConfig.dpadName ) )
|
||||
{
|
||||
box.dpadName = boxConfig.dpadName;
|
||||
}
|
||||
if ( IsDefined( boxConfig.maxUses ) )
|
||||
{
|
||||
box.usesRemaining = boxConfig.maxUses;
|
||||
}
|
||||
|
||||
box box_setInactive();
|
||||
box thread box_handleOwnerDisconnect();
|
||||
box addBoxToLevelArray();
|
||||
|
||||
return box;
|
||||
}
|
||||
|
||||
box_setActive( skipOwnerUse ) // self == box
|
||||
{
|
||||
self setCursorHint( "HINT_NOICON" );
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
self setHintString( boxConfig.hintString );
|
||||
|
||||
self.inUse = false;
|
||||
|
||||
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
Objective_Add( curObjID, "invisible", (0,0,0) );
|
||||
|
||||
if ( !IsDefined( self GetLinkedParent() ) )
|
||||
Objective_Position( curObjID, self.origin );
|
||||
else
|
||||
Objective_OnEntity( curObjID, self );
|
||||
|
||||
Objective_State( curObjID, "active" );
|
||||
Objective_Icon( curObjID, boxConfig.shaderName );
|
||||
self.objIdFriendly = curObjID;
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
Objective_Team( curObjID, self.team );
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( self.team == player.team
|
||||
&& (!IsDefined(boxConfig.canUseCallback) || player [[ boxConfig.canUseCallback ]](self) )
|
||||
)
|
||||
{
|
||||
self box_SetIcon( player, boxConfig.streakName, boxConfig.headIconOffset );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Objective_Player( curObjID, self.owner GetEntityNumber() );
|
||||
|
||||
if( !IsDefined(boxConfig.canUseCallback) || self.owner [[ boxConfig.canUseCallback ]](self) )
|
||||
{
|
||||
self box_SetIcon( self.owner, boxConfig.streakName, boxConfig.headIconOffset );
|
||||
}
|
||||
}
|
||||
|
||||
self MakeUsable();
|
||||
self.isUsable = true;
|
||||
self SetCanDamage( true );
|
||||
self thread box_handleDamage();
|
||||
self thread box_handleDeath();
|
||||
self thread box_timeOut();
|
||||
self thread disableWhenJuggernaut();
|
||||
self make_entity_sentient_mp( self.team, true );
|
||||
if ( IsSentient( self ) )
|
||||
{
|
||||
self SetThreatBiasGroup( "DogsDontAttack" );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner ) )
|
||||
self.owner notify( "new_deployable_box", self );
|
||||
|
||||
if (level.teamBased)
|
||||
{
|
||||
foreach ( player in level.participants )
|
||||
{
|
||||
_box_setActiveHelper( player, self.team == player.team, boxConfig.canUseCallback );
|
||||
|
||||
// handle team switches for human players
|
||||
if ( !IsAI( player ) )
|
||||
{
|
||||
self thread box_playerJoinedTeam( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach ( player in level.participants )
|
||||
{
|
||||
_box_setActiveHelper( player, IsDefined( self.owner ) && self.owner == player, boxConfig.canUseCallback );
|
||||
}
|
||||
}
|
||||
|
||||
level thread teamPlayerCardSplash( boxConfig.splashName, self.owner, self.team );
|
||||
|
||||
self thread box_playerConnected();
|
||||
self thread box_agentConnected();
|
||||
|
||||
if ( IsDefined( boxConfig.onDeployCallback ) )
|
||||
{
|
||||
self [[ boxConfig.onDeployCallback ]]( boxConfig );
|
||||
}
|
||||
|
||||
self thread createBombSquadModel( self.boxType );
|
||||
}
|
||||
|
||||
_box_setActiveHelper( player, bActivate, canUseFunc )
|
||||
{
|
||||
if ( bActivate )
|
||||
{
|
||||
if ( !IsDefined( canUseFunc ) || player [[ canUseFunc ]](self) )
|
||||
{
|
||||
self box_enablePlayerUse( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
self box_disablePlayerUse( player );
|
||||
// if this player is already a juggernaut then when they die, let them use the box
|
||||
self thread doubleDip( player );
|
||||
}
|
||||
self thread boxThink( player );
|
||||
}
|
||||
else
|
||||
{
|
||||
self box_disablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
|
||||
box_playerConnected() // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
// when new players connect they need a boxthink thread run on them
|
||||
while( true )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
self childthread box_waittill_player_spawn_and_add_box( player );
|
||||
}
|
||||
}
|
||||
|
||||
box_agentConnected() // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
// when new agents connect they need a boxthink thread run on them
|
||||
while( true )
|
||||
{
|
||||
level waittill( "spawned_agent_player", agent );
|
||||
self box_addBoxForPlayer( agent );
|
||||
}
|
||||
}
|
||||
|
||||
box_waittill_player_spawn_and_add_box( player ) // self == box
|
||||
{
|
||||
player waittill( "spawned_player" );
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self box_addBoxForPlayer( player );
|
||||
// handle team switches for late joins
|
||||
self thread box_playerJoinedTeam( player );
|
||||
}
|
||||
}
|
||||
|
||||
box_playerJoinedTeam( player ) // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
|
||||
// when new players connect they need a boxthink thread run on them
|
||||
while( true )
|
||||
{
|
||||
player waittill( "joined_team" );
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self box_addBoxForPlayer( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
box_addBoxForPlayer( player ) // self == box
|
||||
{
|
||||
if ( self.team == player.team )
|
||||
{
|
||||
self box_enablePlayerUse( player );
|
||||
self thread boxThink( player );
|
||||
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
||||
}
|
||||
else
|
||||
{
|
||||
self box_disablePlayerUse( player );
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
||||
}
|
||||
}
|
||||
|
||||
box_SetIcon( player, streakName, vOffset )
|
||||
{
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, getKillstreakOverheadIcon( streakName ), (0, 0, vOffset), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
|
||||
}
|
||||
|
||||
box_enablePlayerUse( player ) // self == box
|
||||
{
|
||||
if ( IsPlayer(player) )
|
||||
self EnablePlayerUse( player );
|
||||
|
||||
self.disabled_use_for[player GetEntityNumber()] = false;
|
||||
}
|
||||
|
||||
box_disablePlayerUse( player ) // self == box
|
||||
{
|
||||
if ( IsPlayer(player) )
|
||||
self DisablePlayerUse( player );
|
||||
|
||||
self.disabled_use_for[player GetEntityNumber()] = true;
|
||||
}
|
||||
|
||||
box_setInactive()
|
||||
{
|
||||
self makeUnusable();
|
||||
self.isUsable = false;
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( "none", "", (0,0,0) );
|
||||
if ( isDefined( self.objIdFriendly ) )
|
||||
_objective_delete( self.objIdFriendly );
|
||||
}
|
||||
|
||||
box_handleDamage() // self == box
|
||||
{
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
|
||||
self maps\mp\gametypes\_damage::monitorDamage(
|
||||
boxConfig.maxHealth,
|
||||
boxConfig.damageFeedback,
|
||||
::box_handleDeathDamage,
|
||||
::box_ModifyDamage,
|
||||
true // isKillstreak
|
||||
);
|
||||
}
|
||||
|
||||
box_ModifyDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
modifiedDamage = damage;
|
||||
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
if ( boxConfig.allowMeleeDamage )
|
||||
{
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
||||
}
|
||||
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleGrenadeDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
||||
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
box_handleDeathDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
notifyAttacker = self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, boxconfig.xpPopup, boxConfig.voDestroyed );
|
||||
if ( notifyAttacker )
|
||||
{
|
||||
attacker notify( "destroyed_equipment" );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
box_handleDeath()
|
||||
{
|
||||
self waittill ( "death" );
|
||||
|
||||
// this handles cases of deletion
|
||||
if ( !isDefined( self ) )
|
||||
return;
|
||||
|
||||
self box_setInactive();
|
||||
self removeBoxFromLevelArray();
|
||||
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
PlayFX( boxConfig.deathVfx, self.origin );
|
||||
self PlaySound( "mp_killstreak_disappear" );
|
||||
// 2013-03-08 wsh: whould probably validate all the used fields...
|
||||
if ( IsDefined( boxConfig.deathDamageMax ) )
|
||||
{
|
||||
owner = undefined;
|
||||
if ( IsDefined(self.owner) )
|
||||
owner = self.owner;
|
||||
|
||||
// somewhat hacky:
|
||||
// shift the origin of the damage because it'll collide with the box otherwise
|
||||
// we could also apply the damage after we delete the item?
|
||||
RadiusDamage( self.origin + (0, 0, boxConfig.headIconOffset),
|
||||
boxConfig.deathDamageRadius,
|
||||
boxConfig.deathDamageMax,
|
||||
boxConfig.deathDamageMin,
|
||||
owner,
|
||||
"MOD_EXPLOSIVE",
|
||||
boxConfig.deathWeaponInfo
|
||||
);
|
||||
}
|
||||
|
||||
self notify( "deleting" );
|
||||
|
||||
self delete();
|
||||
}
|
||||
|
||||
box_handleOwnerDisconnect() // self == box
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self notify ( "box_handleOwner" );
|
||||
self endon ( "box_handleOwner" );
|
||||
|
||||
self.owner waittill( "killstreak_disowned" );
|
||||
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
boxThink( player )
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
self thread boxCaptureThink( player );
|
||||
|
||||
if ( !IsDefined(player.boxes) )
|
||||
{
|
||||
player.boxes = [];
|
||||
}
|
||||
player.boxes[player.boxes.size] = self;
|
||||
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill ( "captured", capturer );
|
||||
|
||||
if (capturer == player)
|
||||
{
|
||||
player PlayLocalSound( boxConfig.onUseSfx );
|
||||
|
||||
if ( IsDefined( boxConfig.onuseCallback ) )
|
||||
{
|
||||
player [[ boxConfig.onUseCallback ]]( self );
|
||||
}
|
||||
|
||||
// if this is not the owner then give the owner some xp
|
||||
if( IsDefined( self.owner ) && player != self.owner )
|
||||
{
|
||||
self.owner thread maps\mp\gametypes\_rank::xpEventPopup( boxConfig.event );
|
||||
self.owner thread maps\mp\gametypes\_rank::giveRankXP( "support", boxConfig.useXP );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.usesRemaining ) )
|
||||
{
|
||||
self.usesRemaining--;
|
||||
if ( self.usesRemaining == 0)
|
||||
{
|
||||
self box_leave();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( boxConfig.canUseOtherBoxes ) && boxConfig.canUseOtherBoxes )
|
||||
{
|
||||
// don't let the player use any other boxes
|
||||
foreach( box in level.deployable_box[ boxConfig.streakName ] )
|
||||
{
|
||||
box maps\mp\killstreaks\_deployablebox::box_disablePlayerUse( self );
|
||||
box maps\mp\_entityheadIcons::setHeadIcon( self, "", (0,0,0) );
|
||||
box thread maps\mp\killstreaks\_deployablebox::doubleDip( self );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
||||
self box_disablePlayerUse( player );
|
||||
self thread doubleDip( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
doubleDip( player ) // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
|
||||
// once they die, let them take from the box again
|
||||
player waittill( "death" );
|
||||
|
||||
if( level.teamBased )
|
||||
{
|
||||
if( self.team == player.team )
|
||||
{
|
||||
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
||||
self box_enablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( self.owner ) && self.owner == player )
|
||||
{
|
||||
self box_SetIcon( player, level.boxSettings[ self.boxType ].streakName, level.boxSettings[ self.boxType ].headIconOffset );
|
||||
self box_enablePlayerUse( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
boxCaptureThink( player ) // self == box
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( isDefined( self ) )
|
||||
{
|
||||
self waittill( "trigger", tiggerer );
|
||||
if (
|
||||
IsDefined( level.boxSettings[ self.boxType ].noUseKillstreak )
|
||||
&& level.boxSettings[ self.boxType ].noUseKillstreak
|
||||
&& isKillstreakWeapon( player GetCurrentWeapon() )
|
||||
)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( tiggerer == player && self useHoldThink( player, level.boxSettings[ self.boxType ].useTime ) )
|
||||
{
|
||||
self notify( "captured", player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isFriendlyToBox( box )
|
||||
{
|
||||
return ( level.teamBased
|
||||
&& self.team == box.team );
|
||||
}
|
||||
|
||||
box_timeOut() // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
boxConfig = level.boxSettings[ self.boxType ];
|
||||
lifeSpan = boxConfig.lifeSpan;
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( lifeSpan );
|
||||
|
||||
if ( IsDefined( boxConfig.voGone ) )
|
||||
{
|
||||
self.owner thread leaderDialogOnPlayer( boxConfig.voGone );
|
||||
}
|
||||
|
||||
self box_leave();
|
||||
}
|
||||
|
||||
box_leave()
|
||||
{
|
||||
// TODO: get sound for this
|
||||
//if ( isDefined( self.owner ) )
|
||||
// self.owner thread leaderDialogOnPlayer( "sentry_gone" );
|
||||
wait( 0.05 );
|
||||
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
deleteOnOwnerDeath( owner ) // self == box.friendlyModel or box.enemyModel, owner == box
|
||||
{
|
||||
wait ( 0.25 );
|
||||
self linkTo( owner, "tag_origin", (0,0,0), (0,0,0) );
|
||||
|
||||
owner waittill ( "death" );
|
||||
|
||||
box_leave();
|
||||
}
|
||||
|
||||
box_ModelTeamUpdater( showForTeam ) // self == box model (enemy or friendly)
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
self hide();
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == showForTeam )
|
||||
self showToPlayer( player );
|
||||
}
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "joined_team" );
|
||||
|
||||
self hide();
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == showForTeam )
|
||||
self showToPlayer( player );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
useHoldThink( player, useTime )
|
||||
{
|
||||
self maps\mp\_movers::script_mover_link_to_use_object( player );
|
||||
|
||||
player _disableWeapon();
|
||||
|
||||
player.boxParams = SpawnStruct();
|
||||
player.boxParams.curProgress = 0;
|
||||
player.boxParams.inUse = true;
|
||||
player.boxParams.useRate = 0;
|
||||
player.boxParams.id = self.id;
|
||||
|
||||
if ( isDefined( useTime ) )
|
||||
{
|
||||
player.boxParams.useTime = useTime;
|
||||
}
|
||||
else
|
||||
{
|
||||
player.boxParams.useTime = DEFAULT_USE_TIME;
|
||||
}
|
||||
|
||||
result = useHoldThinkLoop( player );
|
||||
Assert( IsDefined( result ) );
|
||||
|
||||
if ( isAlive( player ) )
|
||||
{
|
||||
player _enableWeapon();
|
||||
maps\mp\_movers::script_mover_unlink_from_use_object( player );
|
||||
}
|
||||
|
||||
if ( !isDefined( self ) )
|
||||
return false;
|
||||
|
||||
player.boxParams.inUse = false;
|
||||
player.boxParams.curProgress = 0;
|
||||
|
||||
return ( result );
|
||||
}
|
||||
|
||||
useHoldThinkLoop( player )
|
||||
{
|
||||
config = player.boxParams;
|
||||
while( player isPlayerUsingBox( config ) )
|
||||
{
|
||||
if ( !player maps\mp\_movers::script_mover_use_can_link( self ) )
|
||||
{
|
||||
player maps\mp\gametypes\_gameobjects::updateUIProgress( config, false );
|
||||
return false;
|
||||
}
|
||||
|
||||
config.curProgress += (50 * config.useRate);
|
||||
|
||||
if ( isDefined( player.objectiveScaler ) )
|
||||
config.useRate = 1 * player.objectiveScaler;
|
||||
else
|
||||
config.useRate = 1;
|
||||
|
||||
player maps\mp\gametypes\_gameobjects::updateUIProgress( config, true );
|
||||
|
||||
if ( config.curProgress >= config.useTime )
|
||||
{
|
||||
player maps\mp\gametypes\_gameobjects::updateUIProgress( config, false );
|
||||
return ( isReallyAlive( player ) );
|
||||
}
|
||||
|
||||
wait 0.05;
|
||||
}
|
||||
|
||||
player maps\mp\gametypes\_gameobjects::updateUIProgress( config, false );
|
||||
return false;
|
||||
}
|
||||
|
||||
disableWhenJuggernaut() // self == box
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "juggernaut_equipped", player );
|
||||
self maps\mp\_entityheadIcons::setHeadIcon( player, "", (0,0,0) );
|
||||
self box_disablePlayerUse( player );
|
||||
self thread doubleDip( player );
|
||||
}
|
||||
}
|
||||
|
||||
addBoxToLevelArray() // self == box
|
||||
{
|
||||
// put the newly created box in the level array for the box type
|
||||
level.deployable_box[ self.boxType ][ self GetEntityNumber() ] = self;
|
||||
}
|
||||
|
||||
removeBoxFromLevelArray() // self == box
|
||||
{
|
||||
level.deployable_box[ self.boxType ][ self GetEntityNumber() ] = undefined;
|
||||
}
|
||||
|
||||
createBombSquadModel( streakName ) // self == box
|
||||
{
|
||||
config = level.boxSettings[ streakName ];
|
||||
|
||||
if ( IsDefined( config.modelBombSquad ) )
|
||||
{
|
||||
bombSquadModel = Spawn( "script_model", self.origin );
|
||||
bombSquadModel.angles = self.angles;
|
||||
bombSquadModel Hide();
|
||||
|
||||
bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( self.owner );
|
||||
bombSquadModel SetModel( config.modelBombSquad );
|
||||
bombSquadModel LinkTo( self );
|
||||
bombSquadModel SetContents( 0 );
|
||||
self.bombSquadModel = bombSquadModel;
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
// Could have been deleted when the player was carrying the ims and died
|
||||
if ( IsDefined( bombSquadModel ) )
|
||||
{
|
||||
bombSquadModel delete();
|
||||
self.bombSquadModel = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isPlayerUsingBox( box ) // self == player
|
||||
{
|
||||
return ( !level.gameEnded
|
||||
&& IsDefined( box )
|
||||
&& isReallyAlive( self ) && self UseButtonPressed()
|
||||
&& !( self IsOnLadder() ) // fix for 154828
|
||||
&& !( self MeleeButtonPressed() ) // from gameobjects.gsc
|
||||
&& !( IsDefined( self.throwingGrenade ) ) // from gameobjects.gsc
|
||||
&& box.curProgress < box.useTime
|
||||
&& ( !IsDefined( self.teleporting ) || !self.teleporting )
|
||||
);
|
||||
}
|
176
maps/mp/killstreaks/_deployablebox_ammo.gsc
Normal file
176
maps/mp/killstreaks/_deployablebox_ammo.gsc
Normal file
@ -0,0 +1,176 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
BOX_TYPE = "deployable_ammo";
|
||||
|
||||
// we depend on deployablebox being init'd first
|
||||
init ()
|
||||
{
|
||||
boxConfig = SpawnStruct();
|
||||
boxConfig.weaponInfo = "deployable_vest_marker_mp";
|
||||
boxConfig.modelBase = "mil_ammo_case_1_open";
|
||||
boxConfig.hintString = &"KILLSTREAKS_HINTS_DEPLOYABLE_AMMO_USE"; //
|
||||
boxConfig.capturingString = &"KILLSTREAKS_DEPLOYABLE_AMMO_TAKING"; //
|
||||
boxConfig.event = "deployable_ammo_taken"; //
|
||||
boxConfig.streakName = BOX_TYPE; //
|
||||
boxConfig.splashName = "used_deployable_ammo"; //
|
||||
boxConfig.shaderName = "compass_objpoint_deploy_ammo_friendly";
|
||||
boxConfig.headIconOffset = 25;
|
||||
boxConfig.lifeSpan = 90.0;
|
||||
boxConfig.useXP = 50;
|
||||
boxConfig.xpPopup = "destroyed_vest";
|
||||
boxConfig.voDestroyed = "ballistic_vest_destroyed";
|
||||
boxConfig.deployedSfx = "mp_vest_deployed_ui";
|
||||
boxConfig.onUseSfx = "ammo_crate_use";
|
||||
boxConfig.onUseCallback = ::onUseDeployable;
|
||||
boxConfig.canUseCallback = ::canUseDeployable;
|
||||
boxConfig.useTime = 500;
|
||||
boxConfig.maxHealth = 150;
|
||||
boxConfig.damageFeedback = "deployable_bag";
|
||||
boxConfig.deathWeaponInfo = "deployable_ammo_mp";
|
||||
boxConfig.deathVfx = loadfx( "fx/explosions/clusterbomb_exp_direct_runner" );
|
||||
boxConfig.deathDamageRadius = 256; // these params taken from frag_grenade
|
||||
boxConfig.deathDamageMax = 130;
|
||||
boxconfig.deathDamageMin = 50;
|
||||
boxConfig.allowMeleeDamage = true;
|
||||
boxConfig.allowGrenadeDamage = true; // this doesn't seem dependable, like with c-4. Why isn't this in an object description?
|
||||
boxConfig.maxUses = 4;
|
||||
|
||||
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
||||
|
||||
level.killStreakFuncs[ BOX_TYPE ] = ::tryUseDeployableAmmo;
|
||||
|
||||
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
||||
}
|
||||
|
||||
tryUseDeployableAmmo( lifeId, streakName ) // self == player
|
||||
{
|
||||
result = self maps\mp\killstreaks\_deployablebox::beginDeployableViaMarker( lifeId, BOX_TYPE );
|
||||
|
||||
if( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !is_aliens() )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( BOX_TYPE, self.origin );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
onUseDeployable( boxEnt ) // self == player
|
||||
{
|
||||
if( is_aliens() )
|
||||
{
|
||||
self addAlienWeaponAmmo( boxEnt );
|
||||
}
|
||||
else
|
||||
{
|
||||
self addAllWeaponAmmo();
|
||||
}
|
||||
}
|
||||
addAllWeaponAmmo()
|
||||
{
|
||||
weaponList = self GetWeaponsListAll();
|
||||
|
||||
if ( IsDefined( weaponList ) )
|
||||
{
|
||||
foreach ( weaponName in weaponList )
|
||||
{
|
||||
// allow bullet weapons to get extra clips
|
||||
if ( maps\mp\gametypes\_weapons::isBulletWeapon( weaponName ) )
|
||||
{
|
||||
self addOneWeaponAmmo( weaponName, 2 );
|
||||
}
|
||||
// limit the ammo of launchers so they aren't abused
|
||||
else if ( WeaponClass( weaponName ) == "rocketlauncher" )
|
||||
{
|
||||
self addOneWeaponAmmo( weaponName, 1 );
|
||||
// self GiveStartAmmo( weaponName );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addOneWeaponAmmo( weaponName, numClips ) // self == plyaer
|
||||
{
|
||||
// want players to be able to go above starting ammo
|
||||
// but don't want it to be too crazy
|
||||
clipSize = WeaponClipSize( weaponName );
|
||||
curStock = self GetWeaponAmmoStock( weaponName );
|
||||
self SetWeaponAmmoStock( weaponName, curStock + numClips * clipSize );
|
||||
|
||||
// self GiveStartAmmo( weaponName );
|
||||
}
|
||||
|
||||
addRatioMaxStockToAllWeapons( ratio_of_max )
|
||||
{
|
||||
primary_weapons = self GetWeaponsListPrimaries();
|
||||
foreach ( weapon in primary_weapons )
|
||||
{
|
||||
// only allow bullet weapons to refill
|
||||
if ( maps\mp\gametypes\_weapons::isBulletWeapon( weapon ) )
|
||||
{
|
||||
if ( weapon != "iw6_alienminigun_mp" )
|
||||
{
|
||||
cur_stock = self GetWeaponAmmoStock( weapon );
|
||||
max_stock = WeaponMaxAmmo( weapon );
|
||||
new_stock = cur_stock + max_stock * ratio_of_max;
|
||||
self SetWeaponAmmoStock( weapon, int( min( new_stock, max_stock ) ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
addFullClipToAllWeapons()
|
||||
{
|
||||
primary_weapons = self GetWeaponsListPrimaries();
|
||||
foreach ( weapon in primary_weapons )
|
||||
{
|
||||
clip_size = WeaponClipSize( weapon );
|
||||
self SetWeaponAmmoClip( weapon, clip_size );
|
||||
}
|
||||
}
|
||||
|
||||
addAlienWeaponAmmo( boxEnt )
|
||||
{
|
||||
primary_weapons = self GetWeaponsListPrimaries();
|
||||
|
||||
assert( isDefined( boxEnt.upgrade_rank ) );
|
||||
|
||||
switch (boxEnt.upgrade_rank)
|
||||
{
|
||||
case 0:
|
||||
self addRatioMaxStockToAllWeapons( .4 );
|
||||
break;
|
||||
case 1:
|
||||
self addRatioMaxStockToAllWeapons( .7 );
|
||||
break;
|
||||
case 2:
|
||||
self addRatioMaxStockToAllWeapons( 1.0 );
|
||||
break;
|
||||
case 3:
|
||||
self addRatioMaxStockToAllWeapons( 1.0 );
|
||||
self addFullClipToAllWeapons();
|
||||
break;
|
||||
case 4:
|
||||
self addRatioMaxStockToAllWeapons( 1.0 );
|
||||
self addFullClipToAllWeapons();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
canUseDeployable(boxEnt) // self == player
|
||||
{
|
||||
if( is_aliens() && isDefined( boxEnt ) && boxEnt.owner == self && !isdefined( boxEnt.air_dropped ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !is_aliens() )
|
||||
return ( !self isJuggernaut() );
|
||||
else
|
||||
return true;
|
||||
}
|
96
maps/mp/killstreaks/_deployablebox_grenades.gsc
Normal file
96
maps/mp/killstreaks/_deployablebox_grenades.gsc
Normal file
@ -0,0 +1,96 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
BOX_TYPE = "deployable_grenades";
|
||||
|
||||
// we depend on deployablebox being init'd first
|
||||
init ()
|
||||
{
|
||||
boxConfig = SpawnStruct();
|
||||
boxConfig.weaponInfo = "deployable_vest_marker_mp";
|
||||
boxConfig.modelBase = "afr_mortar_ammo_01";
|
||||
boxConfig.hintString = &"KILLSTREAKS_HINTS_DEPLOYABLE_GRENADES_PICKUP"; //
|
||||
boxConfig.capturingString = &"KILLSTREAKS_DEPLOYABLE_GRENADES_TAKING"; //
|
||||
boxConfig.event = "deployable_grenades_taken"; //
|
||||
boxConfig.streakName = BOX_TYPE; //
|
||||
boxConfig.splashName = "used_deployable_grenades"; //
|
||||
boxConfig.shaderName = "compass_objpoint_deploy_grenades_friendly";
|
||||
boxConfig.headIconOffset = 25;
|
||||
boxConfig.lifeSpan = 90.0;
|
||||
boxConfig.useXP = 50;
|
||||
boxConfig.xpPopup = "destroyed_vest";
|
||||
boxConfig.voDestroyed = "ballistic_vest_destroyed";
|
||||
boxConfig.deployedSfx = "mp_vest_deployed_ui";
|
||||
boxConfig.onUseSfx = "ammo_crate_use";
|
||||
boxConfig.onUseCallback = ::onUseDeployable;
|
||||
boxConfig.canUseCallback = ::canUseDeployable;
|
||||
boxConfig.useTime = 500;
|
||||
boxConfig.maxHealth = 150;
|
||||
boxConfig.damageFeedback = "deployable_bag";
|
||||
boxConfig.deathWeaponInfo = "deployable_grenades_mp";
|
||||
boxConfig.deathVfx = loadfx( "fx/explosions/grenadeexp_default" );
|
||||
boxConfig.deathDamageRadius = 256; // these params taken from frag_grenade
|
||||
boxConfig.deathDamageMax = 150;
|
||||
boxconfig.deathDamageMin = 50;
|
||||
boxConfig.allowMeleeDamage = true;
|
||||
boxConfig.allowGrenadeDamage = true; // this doesn't seem dependable, like with c-4. Why isn't this in an object description?
|
||||
boxConfig.maxUses = 3;
|
||||
|
||||
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
||||
|
||||
level.killStreakFuncs[ BOX_TYPE ] = ::tryUseDeployableGrenades;
|
||||
|
||||
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
||||
}
|
||||
|
||||
tryUseDeployableGrenades( lifeId, streakName ) // self == player
|
||||
{
|
||||
result = self maps\mp\killstreaks\_deployablebox::beginDeployableViaMarker( lifeId, BOX_TYPE );
|
||||
|
||||
if( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( BOX_TYPE, self.origin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
onUseDeployable( boxEnt ) // self == player
|
||||
{
|
||||
self refillExplosiveWeapons();
|
||||
}
|
||||
|
||||
refillExplosiveWeapons()
|
||||
{
|
||||
weaponList = self GetWeaponsListAll();
|
||||
|
||||
if ( IsDefined( weaponList ) )
|
||||
{
|
||||
foreach ( weaponName in weaponList )
|
||||
{
|
||||
if ( maps\mp\gametypes\_weapons::isGrenade( weaponName )
|
||||
|| maps\mp\gametypes\_weapons::isOffhandWeapon( weaponName )
|
||||
// && !maps\mp\_utility::isKillstreakWeapon( weaponName )
|
||||
)
|
||||
{
|
||||
// for some reason, tactical grenades are getting 2
|
||||
self GiveStartAmmo( weaponName );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( self _hasPerk( "specialty_tacticalinsertion" ) && self getAmmoCount( "flare_mp" ) < 1 )
|
||||
self givePerkOffhand( "specialty_tacticalinsertion", false );
|
||||
}
|
||||
|
||||
canUseDeployable(boxEnt) // self == player
|
||||
{
|
||||
if( is_aliens() && isDefined( boxEnt ) && boxEnt.owner == self && !isdefined( boxEnt.air_dropped ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return ( !self isJuggernaut() );
|
||||
}
|
252
maps/mp/killstreaks/_deployablebox_gun.gsc
Normal file
252
maps/mp/killstreaks/_deployablebox_gun.gsc
Normal file
@ -0,0 +1,252 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
BOX_TYPE = "deployable_ammo";
|
||||
|
||||
// we depend on deployablebox being init'd first
|
||||
init ()
|
||||
{
|
||||
boxConfig = SpawnStruct();
|
||||
boxConfig.id = "deployable_weapon_crate";
|
||||
boxConfig.weaponInfo = "deployable_weapon_crate_marker_mp";
|
||||
boxConfig.modelBase = "mp_weapon_crate";
|
||||
boxConfig.modelBombSquad = "mp_weapon_crate_bombsquad";
|
||||
boxConfig.hintString = &"KILLSTREAKS_HINTS_DEPLOYABLE_AMMO_USE"; //
|
||||
boxConfig.capturingString = &"KILLSTREAKS_DEPLOYABLE_AMMO_TAKING"; //
|
||||
boxConfig.event = "deployable_ammo_taken"; //
|
||||
boxConfig.streakName = BOX_TYPE; //
|
||||
boxConfig.splashName = "used_deployable_ammo"; //
|
||||
boxConfig.shaderName = "compass_objpoint_deploy_ammo_friendly";
|
||||
boxConfig.headIconOffset = 20;
|
||||
boxConfig.lifeSpan = 90.0;
|
||||
boxConfig.voGone = "ammocrate_gone";
|
||||
boxConfig.useXP = 50;
|
||||
boxConfig.xpPopup = "destroyed_ammo";
|
||||
boxConfig.voDestroyed = "ammocrate_destroyed";
|
||||
boxConfig.deployedSfx = "mp_vest_deployed_ui";
|
||||
boxConfig.onUseSfx = "ammo_crate_use";
|
||||
boxConfig.onUseCallback = ::onUseDeployable;
|
||||
boxConfig.canUseCallback = ::canUseDeployable;
|
||||
boxConfig.noUseKillstreak = true;
|
||||
boxConfig.useTime = 1000;
|
||||
boxConfig.maxHealth = 150;
|
||||
boxConfig.damageFeedback = "deployable_bag";
|
||||
boxConfig.deathVfx = loadfx( "vfx/gameplay/mp/killstreaks/vfx_ballistic_vest_death" );
|
||||
boxConfig.allowMeleeDamage = true;
|
||||
boxConfig.allowGrenadeDamage = false; // this doesn't seem dependable, like with c-4. Why isn't this in an object description?
|
||||
boxConfig.maxUses = 4;
|
||||
|
||||
boxConfig.minigunChance = 20; // 1 in minigunChance
|
||||
// artificially increase the odds for development
|
||||
/#
|
||||
boxConfig.miniGunChance = 10;
|
||||
#/
|
||||
|
||||
boxConfig.minigunWeapon = "iw6_minigun_mp";
|
||||
boxConfig.ammoRestockCheckFreq = 0.5; // how often we check for new players
|
||||
boxConfig.ammoRestockTime = 10.0; // how often players can get restocked
|
||||
boxConfig.triggerRadius = 200;
|
||||
boxConfig.triggerHeight = 64;
|
||||
boxConfig.onDeployCallback = ::onBoxDeployed;
|
||||
boxConfig.canUseOtherBoxes = false;
|
||||
|
||||
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
||||
|
||||
level.killStreakFuncs[ BOX_TYPE ] = ::tryUseDeployable;
|
||||
|
||||
level.deployableGunBox_BonusInXUses = RandomIntRange(1, boxConfig.minigunChance + 1);
|
||||
|
||||
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
||||
|
||||
maps\mp\gametypes\sotf::defineChestWeapons();
|
||||
}
|
||||
|
||||
tryUseDeployable( lifeId, streakName ) // self == player
|
||||
{
|
||||
result = self maps\mp\killstreaks\_deployablebox::beginDeployableViaMarker( lifeId, BOX_TYPE );
|
||||
|
||||
if( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !is_aliens() )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( BOX_TYPE, self.origin );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
onUseDeployable( boxEnt ) // self == player
|
||||
{
|
||||
level.deployableGunBox_BonusInXUses--;
|
||||
|
||||
if ( level.deployableGunBox_BonusInXUses == 0 )
|
||||
{
|
||||
boxConfig = level.boxSettings[ boxEnt.boxType ];
|
||||
|
||||
// For when we want to replace the minigun with something else for a specific level (e.g. Venom-X in dome_ns )
|
||||
if ( IsDefined( level.deployableBoxGiveWeaponFunc ) )
|
||||
[[ level.deployableBoxGiveWeaponFunc ]]( true );
|
||||
else
|
||||
giveGun( self, boxConfig.minigunWeapon );
|
||||
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_guninabox" );
|
||||
|
||||
// reset gun chance, but make it very unlikely to get a second one
|
||||
level.deployableGunBox_BonusInXUses = RandomIntRange(boxConfig.minigunChance, boxConfig.minigunChance + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
giveRandomGun( self );
|
||||
}
|
||||
}
|
||||
|
||||
onBoxDeployed( config ) // self == box
|
||||
{
|
||||
self thread restockAmmoAura( config );
|
||||
}
|
||||
|
||||
giveRandomGun( player )
|
||||
{
|
||||
// this is adapted from SOTF / Hunted
|
||||
|
||||
// give the player a new gun, but make sure he's not carrying a variant of it in either slot
|
||||
baseWeapons = [];
|
||||
foreach ( gun in player GetWeaponsListPrimaries() )
|
||||
{
|
||||
baseWeapons[ baseWeapons.size ] = GetWeaponBaseName( gun );
|
||||
}
|
||||
|
||||
newWeapon = undefined;
|
||||
while ( true )
|
||||
{
|
||||
// getRandomWeapon returns an array
|
||||
newWeapon = maps\mp\gametypes\sotf::getRandomWeapon( level.weaponArray );
|
||||
newBaseWeapon = newWeapon["name"];
|
||||
|
||||
// make sure he's not carrying this weapon already
|
||||
if ( !array_contains( baseWeapons, newBaseWeapon ) )
|
||||
break;
|
||||
}
|
||||
|
||||
// get randomAttachments returns a full weapon string
|
||||
newWeapon = maps\mp\gametypes\sotf::getRandomAttachments( newWeapon );
|
||||
|
||||
giveGun( player, newWeapon );
|
||||
|
||||
}
|
||||
|
||||
giveGun( player, newWeapon )
|
||||
{
|
||||
weaponList = player GetWeaponsListPrimaries();
|
||||
|
||||
primaryCount = 0;
|
||||
foreach ( weap in weaponList )
|
||||
{
|
||||
if ( !maps\mp\gametypes\_weapons::isAltModeWeapon( weap ) )
|
||||
{
|
||||
primaryCount++;
|
||||
}
|
||||
}
|
||||
|
||||
if ( primaryCount > 1 )
|
||||
{
|
||||
playerPrimary = player.lastDroppableWeapon;
|
||||
if ( IsDefined( playerPrimary ) && playerPrimary != "none" )
|
||||
{
|
||||
player DropItem( playerPrimary );
|
||||
}
|
||||
}
|
||||
|
||||
player _giveWeapon( newWeapon );
|
||||
player SwitchToWeapon( newWeapon );
|
||||
player GiveStartAmmo( newWeapon );
|
||||
}
|
||||
|
||||
restockAmmoAura( config ) // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_eneded" );
|
||||
|
||||
trigger = Spawn( "trigger_radius", self.origin, 0, config.triggerRadius, config.triggerHeight );
|
||||
trigger.owner = self;
|
||||
self thread maps\mp\gametypes\_weapons::deleteOnDeath( trigger );
|
||||
|
||||
if ( IsDefined( self.moving_platform ) )
|
||||
{
|
||||
trigger EnableLinkTo();
|
||||
trigger LinkTo( self.moving_platform );
|
||||
}
|
||||
|
||||
rangeSq = config.triggerRadius * config.triggerRadius;
|
||||
player = undefined;
|
||||
while ( true )
|
||||
{
|
||||
// trigger waittill( "trigger", player );
|
||||
touchingPlayers = trigger GetIsTouchingEntities( level.players );
|
||||
|
||||
|
||||
foreach ( player in touchingPlayers )
|
||||
{
|
||||
if ( IsDefined( player )
|
||||
&& !(self.owner IsEnemy( player ))
|
||||
&& self shouldAddAmmo( player ) )
|
||||
{
|
||||
// self thread addAmmoOverTime( player, rangeSq, config.ammoRestockTime );
|
||||
self addAmmo( player, config.ammoRestockTime );
|
||||
}
|
||||
}
|
||||
|
||||
wait ( config.ammoRestockCheckFreq );
|
||||
}
|
||||
}
|
||||
|
||||
shouldAddAmmo( player ) // self == box
|
||||
{
|
||||
return ( !IsDefined( player.deployableGunNextAmmoTime )
|
||||
|| GetTime() >= player.deployableGunNextAmmoTime );
|
||||
}
|
||||
|
||||
addAmmo( player, freq ) // self == box
|
||||
{
|
||||
player.deployableGunNextAmmoTime = GetTime() + (freq * 1000);
|
||||
maps\mp\gametypes\_weapons::scavengerGiveAmmo( player );
|
||||
|
||||
player maps\mp\gametypes\_damagefeedback::hudIconType( "boxofguns" );
|
||||
}
|
||||
|
||||
addAmmoOverTime( player, rangeSq, freq ) // self == box
|
||||
{
|
||||
self endon( "death" );
|
||||
player endon( "death" );
|
||||
player endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self addAmmo( player );
|
||||
|
||||
wait ( freq );
|
||||
|
||||
if ( DistanceSquared( player.origin, self.origin ) > rangeSq )
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
canUseDeployable(boxEnt) // self == player
|
||||
{
|
||||
if( is_aliens() && isDefined( boxEnt ) && boxEnt.owner == self && !isdefined( boxEnt.air_dropped ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if( !is_aliens() )
|
||||
return ( !self isJuggernaut() );
|
||||
else
|
||||
return true;
|
||||
}
|
83
maps/mp/killstreaks/_deployablebox_juicebox.gsc
Normal file
83
maps/mp/killstreaks/_deployablebox_juicebox.gsc
Normal file
@ -0,0 +1,83 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
BOX_TYPE = "deployable_juicebox";
|
||||
|
||||
// we depend on deployablebox being init'd first
|
||||
init ()
|
||||
{
|
||||
boxConfig = SpawnStruct();
|
||||
boxConfig.weaponInfo = "deployable_vest_marker_mp";
|
||||
boxConfig.modelBase = "afr_mortar_ammo_01";
|
||||
boxConfig.hintString = &"KILLSTREAKS_HINTS_DEPLOYABLE_JUICEBOX_PICKUP"; //
|
||||
boxConfig.capturingString = &"KILLSTREAKS_DEPLOYABLE_JUICEBOX_TAKING"; //
|
||||
boxConfig.event = "deployable_juicebox_taken"; //
|
||||
boxConfig.streakName = BOX_TYPE; //
|
||||
boxConfig.splashName = "used_deployable_juicebox"; //
|
||||
boxConfig.shaderName = "compass_objpoint_deploy_juiced_friendly";
|
||||
boxConfig.headIconOffset = 25;
|
||||
boxConfig.lifeSpan = 90.0;
|
||||
boxConfig.useXP = 50;
|
||||
boxConfig.xpPopup = "destroyed_vest";
|
||||
boxConfig.voDestroyed = "ballistic_vest_destroyed";
|
||||
boxConfig.deployedSfx = "mp_vest_deployed_ui";
|
||||
boxConfig.onUseSfx = "ammo_crate_use";
|
||||
boxConfig.onUseCallback = ::onUseDeployable;
|
||||
boxConfig.canUseCallback = ::canUseDeployable;
|
||||
boxConfig.useTime = 500;
|
||||
boxConfig.maxHealth = 300;
|
||||
boxConfig.damageFeedback = "deployable_bag";
|
||||
boxConfig.deathWeaponInfo = "deployable_ammo_mp";
|
||||
boxConfig.deathVfx = loadfx( "vfx/gameplay/mp/killstreaks/vfx_ballistic_vest_death" );
|
||||
boxConfig.allowMeleeDamage = true;
|
||||
boxConfig.allowGrenadeDamage = false; // this doesn't seem dependable, like with c-4. Why isn't this in an object description?
|
||||
boxConfig.maxUses = 4;
|
||||
|
||||
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
||||
|
||||
level.killStreakFuncs[ BOX_TYPE ] = ::tryUseDeployableJuiced;
|
||||
|
||||
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
||||
}
|
||||
|
||||
tryUseDeployableJuiced( lifeId, streakName ) // self == player
|
||||
{
|
||||
result = self maps\mp\killstreaks\_deployablebox::beginDeployableViaMarker( lifeId, BOX_TYPE );
|
||||
|
||||
if( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !is_aliens() )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( BOX_TYPE, self.origin );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
onUseDeployable( boxEnt ) // self == player
|
||||
{
|
||||
if ( is_aliens() )
|
||||
{
|
||||
assert( isDefined( boxEnt.upgrade_rank) , "No rank specified for deployable juicebox" );
|
||||
self thread maps\mp\perks\_perkfunctions::setJuiced( level.deployablebox_juicebox_rank[boxEnt.upgrade_rank] );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread maps\mp\perks\_perkfunctions::setJuiced( 15 );
|
||||
}
|
||||
}
|
||||
|
||||
canUseDeployable(boxEnt) // self == player
|
||||
{
|
||||
if( is_aliens() && isDefined( boxEnt ) && boxEnt.owner == self && !isdefined( boxEnt.air_dropped ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return ( !(self isJuggernaut()) && !(self maps\mp\perks\_perkfunctions::hasJuiced()) );
|
||||
}
|
||||
|
||||
|
112
maps/mp/killstreaks/_deployablebox_vest.gsc
Normal file
112
maps/mp/killstreaks/_deployablebox_vest.gsc
Normal file
@ -0,0 +1,112 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
BOX_TYPE = "deployable_vest";
|
||||
|
||||
// we depend on deployablebox being init'd first
|
||||
init ()
|
||||
{
|
||||
boxConfig = SpawnStruct();
|
||||
boxConfig.id = "deployable_vest";
|
||||
boxConfig.weaponInfo = "deployable_vest_marker_mp";
|
||||
boxConfig.modelBase = "prop_ballistic_vest_iw6";
|
||||
boxConfig.modelBombSquad = "prop_ballistic_vest_iw6_bombsquad";
|
||||
boxConfig.hintString = &"KILLSTREAKS_HINTS_LIGHT_ARMOR_PICKUP";
|
||||
boxConfig.capturingString = &"KILLSTREAKS_BOX_GETTING_VEST";
|
||||
boxConfig.event = "deployable_vest_taken";
|
||||
boxConfig.streakName = BOX_TYPE;
|
||||
boxConfig.splashName = "used_deployable_vest";
|
||||
boxConfig.shaderName = "compass_objpoint_deploy_friendly";
|
||||
boxConfig.headIconOffset = 20;
|
||||
boxConfig.lifeSpan = 90.0;
|
||||
boxConfig.useXP = 50;
|
||||
boxConfig.xpPopup = "destroyed_vest";
|
||||
boxConfig.voDestroyed = "ballistic_vest_destroyed";
|
||||
boxConfig.deployedSfx = "mp_vest_deployed_ui";
|
||||
boxConfig.onUseSfx = "ammo_crate_use";
|
||||
boxConfig.onUseCallback = ::onUseDeployable;
|
||||
boxConfig.canUseCallback = ::canUseDeployable;
|
||||
boxConfig.useTime = 1000;
|
||||
boxConfig.maxHealth = 300;
|
||||
boxConfig.damageFeedback = "deployable_bag";
|
||||
boxConfig.deathVfx = loadfx( "vfx/gameplay/mp/killstreaks/vfx_ballistic_vest_death" );
|
||||
boxConfig.allowMeleeDamage = true;
|
||||
boxConfig.allowGrenadeDamage = false; // this doesn't seem dependable, like with c-4. Why isn't this in an object description?
|
||||
boxConfig.maxUses = 4;
|
||||
boxConfig.canUseOtherBoxes = false;
|
||||
|
||||
level.boxSettings[ BOX_TYPE ] = boxConfig;
|
||||
|
||||
level.killStreakFuncs[ BOX_TYPE ] = ::tryUseDeployableVest;
|
||||
|
||||
level.deployable_box[ BOX_TYPE ] = []; // storing each created box in their own array
|
||||
}
|
||||
|
||||
tryUseDeployableVest( lifeId, streakName ) // self == player
|
||||
{
|
||||
result = self maps\mp\killstreaks\_deployablebox::beginDeployableViaMarker( lifeId, BOX_TYPE );
|
||||
|
||||
if( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( !is_aliens() )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( BOX_TYPE, self.origin );
|
||||
}
|
||||
|
||||
// we used to give the player a vest/health after we deployed the box
|
||||
// instead, we automatically call onUseCallback once in _deployabox.gsc
|
||||
// (because we don't want the player to be able to manually use vest again until he dies
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
canUseDeployable(boxEnt) // self == player
|
||||
{
|
||||
if(!is_aliens() )
|
||||
{
|
||||
return ( !(self maps\mp\perks\_perkfunctions::hasLightArmor()) && !self isJuggernaut() );
|
||||
}
|
||||
if( isDefined( boxEnt ) && boxEnt.owner == self && !isdefined( boxEnt.air_dropped ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return !self isJuggernaut();
|
||||
|
||||
}
|
||||
|
||||
onUseDeployable( boxEnt ) // self == player
|
||||
{
|
||||
if ( is_aliens() )
|
||||
{
|
||||
existing_armor = 0;
|
||||
if( isDefined( self.lightArmorHP ))
|
||||
{
|
||||
existing_armor = self.lightArmorHP;
|
||||
}
|
||||
|
||||
assertex ( isDefined( boxEnt.upgrade_rank ), "No upgrade rank defined for deployable armor" );
|
||||
|
||||
armor_to_give = get_adjusted_armor(existing_armor,boxEnt.upgrade_rank );
|
||||
self maps\mp\perks\_perkfunctions::setLightArmor( armor_to_give );
|
||||
|
||||
self notify( "enable_armor" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\perks\_perkfunctions::setLightArmor();
|
||||
}
|
||||
}
|
||||
|
||||
get_adjusted_armor( existing_armor,rank)
|
||||
{
|
||||
if( existing_armor + level.deployablebox_vest_rank[rank] > level.deployablebox_vest_max)
|
||||
{
|
||||
return level.deployablebox_vest_max;
|
||||
}
|
||||
|
||||
return existing_armor + level.deployablebox_vest_rank[rank];
|
||||
}
|
132
maps/mp/killstreaks/_designator_grenade.gsc
Normal file
132
maps/mp/killstreaks/_designator_grenade.gsc
Normal file
@ -0,0 +1,132 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// 2013-03-12 wsh
|
||||
// shared utility for grenade-style designators
|
||||
// init in your own ks script
|
||||
// callbackFunc( killstreakName, designatorGrenadeEnt );
|
||||
// We assume designator weapon can only be used once
|
||||
designator_Start( killstreakName, designatorName, onTargetAcquiredCallback )
|
||||
{
|
||||
AssertEx( IsDefined( onTargetAcquiredCallback ), "designator onTargetAcquiredCallback must be specified for " + designatorName );
|
||||
|
||||
self endon ( "death" );
|
||||
self.marker = undefined;
|
||||
|
||||
if ( self GetCurrentWeapon() == designatorName )
|
||||
{
|
||||
self thread designator_DisableUsabilityDuringGrenadePullback( designatorName );
|
||||
self thread designator_WaitForGrenadeFire( killstreakName, designatorName, onTargetAcquiredCallback );
|
||||
|
||||
self designator_WaitForWeaponChange( designatorName );
|
||||
|
||||
return ( !(self GetAmmoCount( designatorName ) && self HasWeapon( designatorName ) ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
designator_DisableUsabilityDuringGrenadePullback( designatorName ) // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
usedWeaponName = "";
|
||||
while ( usedWeaponName != designatorName )
|
||||
{
|
||||
self waittill( "grenade_pullback", usedWeaponName );
|
||||
}
|
||||
|
||||
self _disableUsability();
|
||||
self designator_EnableUsabilityWhenDesignatorFinishes();
|
||||
}
|
||||
|
||||
designator_EnableUsabilityWhenDesignatorFinishes() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
// self notify( "beginMarkerTracking" );
|
||||
// self endon( "beginMarkerTracking" );
|
||||
|
||||
self waittill_any( "grenade_fire", "weapon_change" );
|
||||
self _enableUsability();
|
||||
}
|
||||
|
||||
designator_WaitForGrenadeFire( killstreakName, designatorName, callbackFunc ) // self == player
|
||||
{
|
||||
self endon( "designator_finished" );
|
||||
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
designatorGrenade = undefined;
|
||||
usedWeaponName = "";
|
||||
while ( usedWeaponName != designatorName )
|
||||
{
|
||||
self waittill( "grenade_fire", designatorGrenade, usedWeaponName );
|
||||
}
|
||||
|
||||
if ( IsAlive( self ) )
|
||||
{
|
||||
designatorGrenade.owner = self;
|
||||
designatorGrenade.weaponName = designatorName;
|
||||
|
||||
self.marker = designatorGrenade;
|
||||
|
||||
// maybe this should be in the callback?
|
||||
// marker PlaySoundToPlayer( "", self );
|
||||
|
||||
self thread designator_OnTargetAcquired( killstreakName, designatorGrenade, callbackFunc );
|
||||
}
|
||||
else
|
||||
{
|
||||
designatorGrenade Delete();
|
||||
}
|
||||
|
||||
self notify( "designator_finished" );
|
||||
}
|
||||
|
||||
designator_WaitForWeaponChange( designatorName ) // self == player
|
||||
{
|
||||
self endon( "spawned_player" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
currentWeapon = self getCurrentWeapon();
|
||||
while ( currentWeapon == designatorName )
|
||||
{
|
||||
self waittill( "weapon_change", currentWeapon );
|
||||
}
|
||||
|
||||
// this could have ended because we switched weapons normally
|
||||
// or because we used the killstreak
|
||||
if ( self GetAmmoCount( designatorName ) == 0 )
|
||||
{
|
||||
self designator_RemoveDesignatorAndRestorePreviousWeapon( designatorName );
|
||||
}
|
||||
|
||||
self notify( "designator_finished" );
|
||||
}
|
||||
|
||||
designator_RemoveDesignatorAndRestorePreviousWeapon( designatorName )
|
||||
{
|
||||
if ( self HasWeapon( designatorName ) )
|
||||
{
|
||||
self TakeWeapon( designatorName );
|
||||
// self SwitchToWeapon( self getLastWeapon() );
|
||||
}
|
||||
}
|
||||
|
||||
designator_OnTargetAcquired( killstreakName, designatorGrenade, onTargetAcquiredCallback ) // self == player
|
||||
{
|
||||
designatorGrenade waittill( "missile_stuck", stuckTo );
|
||||
|
||||
if ( IsDefined( designatorGrenade.owner ) )
|
||||
{
|
||||
self thread [[ onTargetAcquiredCallback ]]( killstreakName, designatorGrenade );
|
||||
}
|
||||
|
||||
if ( IsDefined( designatorGrenade ) )
|
||||
{
|
||||
designatorGrenade Delete();
|
||||
}
|
||||
}
|
345
maps/mp/killstreaks/_dog_killstreak.gsc
Normal file
345
maps/mp/killstreaks/_dog_killstreak.gsc
Normal file
@ -0,0 +1,345 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
|
||||
//===========================================
|
||||
// constants
|
||||
//===========================================
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME = 5;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_PLAYER = 1;
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER = 2;
|
||||
|
||||
//===========================================
|
||||
// init
|
||||
//===========================================
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs["guard_dog"] = ::tryUseDog;
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_devWolf", 0 ); // 0 == dog, 1 == wolf
|
||||
SetDevDvarIfUninitialized( "scr_devWolfType", 0 ); // 0 == wolfA, 1 == wolfB
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// setup_callbacks
|
||||
//===========================================
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["dog"] = level.agent_funcs["player"];
|
||||
|
||||
level.agent_funcs["dog"]["spawn"] = ::spawn_dog;
|
||||
level.agent_funcs["dog"]["on_killed"] = ::on_agent_dog_killed;
|
||||
level.agent_funcs["dog"]["on_damaged"] = maps\mp\agents\_agents::on_agent_generic_damaged;
|
||||
level.agent_funcs["dog"]["on_damaged_finished"] = ::on_damaged_finished;
|
||||
level.agent_funcs["dog"]["think"] = maps\mp\agents\dog\_dog_think::main;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// tryUseDog
|
||||
//===========================================
|
||||
tryUseDog( lifeId, streakName )
|
||||
{
|
||||
return useDog();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// useDog
|
||||
//===========================================
|
||||
useDog()
|
||||
{
|
||||
// limit the number of active "dog" agents allowed per player
|
||||
if( isDefined(self.hasDog) && self.hasDog )
|
||||
{
|
||||
dog_type = self GetCommonPlayerDataReservedInt( "mp_dog_type" );
|
||||
if( dog_type == 1 )
|
||||
self iPrintLnBold( &"KILLSTREAKS_ALREADY_HAVE_WOLF" );
|
||||
else
|
||||
self iPrintLnBold( &"KILLSTREAKS_ALREADY_HAVE_DOG" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// limit the number of active "dog" agents allowed per game
|
||||
if( getNumActiveAgents( "dog" ) >= CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_DOGS" );
|
||||
return false;
|
||||
}
|
||||
// limit the number of active agents allowed per player
|
||||
if( getNumOwnedActiveAgents( self ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we should probably do a queue system for these, so the player can call it but it'll go into a queue for when an agent dies to open up a spot
|
||||
// limit the number of active agents allowed per player
|
||||
maxagents = GetMaxAgents();
|
||||
if( getNumActiveAgents() >= maxagents )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the player is still alive before the agent trys to spawn on the player
|
||||
if( !isReallyAlive( self ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// try to spawn the agent on a path node near the player
|
||||
nearestPathNode = self getValidSpawnPathNodeNearPlayer( true );
|
||||
if( !IsDefined(nearestPathNode) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// find an available agent
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "dog" , self.team );
|
||||
if( !IsDefined( agent ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self.hasDog = true;
|
||||
|
||||
// set the agent to the player's team
|
||||
agent set_agent_team( self.team, self );
|
||||
|
||||
spawnOrigin = nearestPathNode.origin;
|
||||
spawnAngles = VectorToAngles( self.origin - nearestPathNode.origin );
|
||||
|
||||
agent thread [[ agent agentFunc("spawn") ]]( spawnOrigin, spawnAngles, self );
|
||||
|
||||
agent _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
|
||||
if ( IsDefined( self.ballDrone ) && self.ballDrone.ballDroneType == "ball_drone_backup" )
|
||||
{
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_twiceasdeadly" );
|
||||
}
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "guard_dog", self.origin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_dog_killed
|
||||
//=======================================================
|
||||
on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
|
||||
//agent dogs in safeguard do not have an owner.
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner.hasDog = false;
|
||||
|
||||
eAttacker.lastKillDogTime = GetTime();
|
||||
|
||||
if ( IsDefined( self.animCBs.OnExit[ self.aiState ] ) )
|
||||
self [[ self.animCBs.OnExit[ self.aiState ] ]] ();
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && IsDefined(self.owner) && (eAttacker != self.owner) )
|
||||
{
|
||||
self.owner leaderDialogOnPlayer( "dog_killed" );
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_guard_dog" );
|
||||
|
||||
if ( IsPlayer( eAttacker ) )
|
||||
{
|
||||
eAttacker maps\mp\gametypes\_missions::processChallenge( "ch_notsobestfriend" );
|
||||
|
||||
// assume jumping?
|
||||
if ( !self IsOnGround() )
|
||||
{
|
||||
eAttacker maps\mp\gametypes\_missions::processChallenge( "ch_hoopla" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self SetAnimState( "death" );
|
||||
animEntry = self GetAnimEntry();
|
||||
animLength = GetAnimLength( animEntry );
|
||||
|
||||
deathAnimDuration = int( animLength * 1000 ); // duration in milliseconds
|
||||
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
|
||||
self PlaySound( ter_op( self.bIsWolf, "anml_wolf_shot_death", "anml_dog_shot_death" ) );
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
|
||||
self notify( "killanimscript" );
|
||||
}
|
||||
|
||||
|
||||
on_damaged_finished( eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset )
|
||||
{
|
||||
if( !IsDefined( self.playing_pain_sound ) )
|
||||
self thread play_pain_sound( 2.5 );
|
||||
|
||||
// damage needs to be modified depending on the weapon and hit location because the head is really easy to hit when the dog is running at you
|
||||
// assuming 1.4 modifier for head shots
|
||||
damageModified = iDamage;
|
||||
if( IsDefined( sHitLoc ) && sHitLoc == "head" && level.gametype != "horde" )
|
||||
{
|
||||
damageModified = int( damageModified * 0.6 );
|
||||
if ( iDamage > 0 && damageModified <= 0 )
|
||||
damageModified = 1;
|
||||
}
|
||||
|
||||
if ( self.health - damageModified > 0 )
|
||||
{
|
||||
self maps\mp\agents\dog\_dog_think::OnDamage( eInflictor, eAttacker, damageModified, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
// attack the player that has damaged them
|
||||
if( IsPlayer( eAttacker ) )
|
||||
{
|
||||
// is the dog already attacking?
|
||||
if ( IsDefined( self.attackState ) && self.attackState != "attacking" )
|
||||
{
|
||||
// is the attacker within the dog damaged range?
|
||||
if( DistanceSquared( self.origin, eAttacker.origin ) <= self.dogDamagedRadiusSq )
|
||||
{
|
||||
self.favoriteEnemy = eAttacker;
|
||||
self.forceAttack = true;
|
||||
self thread maps\mp\agents\dog\_dog_think::watchFavoriteEnemyDeath();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
maps\mp\agents\_agents::agent_damage_finished( eInflictor, eAttacker, damageModified, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset );
|
||||
}
|
||||
|
||||
play_pain_sound( delay ) // self == dog
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
self PlaySound( ter_op( self.bIsWolf, "anml_wolf_shot_pain", "anml_dog_shot_pain" ) );
|
||||
self.playing_pain_sound = true;
|
||||
wait( delay );
|
||||
self.playing_pain_sound = undefined;
|
||||
}
|
||||
|
||||
spawn_dog( optional_spawnOrigin, optional_spawnAngles, optional_owner ) // self == agent
|
||||
{
|
||||
dog_type = 0; // 0 == dog, 1 == wolf
|
||||
wolf_type = 0; // 0 == wolfB, 1 == wolfC
|
||||
if( IsDefined( optional_owner ) )
|
||||
{
|
||||
if( IsDefined( optional_owner.squad_bot_dog_type ) )
|
||||
{
|
||||
dog_type = optional_owner.squad_bot_dog_type;
|
||||
}
|
||||
else
|
||||
{
|
||||
dog_type = optional_owner GetCommonPlayerDataReservedInt( "mp_dog_type" );
|
||||
}
|
||||
}
|
||||
|
||||
/#
|
||||
if( GetDvarInt( "scr_devWolf" ) != 0 )
|
||||
{
|
||||
dog_type = GetDvarInt( "scr_devWolf" );
|
||||
wolf_type = GetDvarInt( "scr_devWolfType" );
|
||||
}
|
||||
#/
|
||||
dog_model = "mp_fullbody_dog_a";
|
||||
if( dog_type == 1 )
|
||||
{
|
||||
if ( wolf_type == 0 )
|
||||
dog_model = "mp_fullbody_wolf_b";
|
||||
else
|
||||
dog_model = "mp_fullbody_wolf_c";
|
||||
}
|
||||
|
||||
if( IsHairRunning() )
|
||||
dog_model = dog_model + "_fur";
|
||||
|
||||
self SetModel( dog_model );
|
||||
|
||||
self.species = "dog";
|
||||
|
||||
self.OnEnterAnimState = maps\mp\agents\dog\_dog_think::OnEnterAnimState;
|
||||
|
||||
// allow killstreaks to pass in specific spawn locations
|
||||
if( IsDefined(optional_spawnOrigin) && IsDefined(optional_spawnAngles) )
|
||||
{
|
||||
spawnOrigin = optional_spawnOrigin;
|
||||
spawnAngles = optional_spawnAngles;
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
}
|
||||
self activateAgent();
|
||||
self.spawnTime = GetTime();
|
||||
self.lastSpawnTime = GetTime();
|
||||
|
||||
self.bIsWolf = (dog_type == 1);
|
||||
|
||||
self maps\mp\agents\dog\_dog_think::init();
|
||||
|
||||
if ( dog_type == 1 )
|
||||
animclass = "wolf_animclass";
|
||||
else
|
||||
animclass = "dog_animclass";
|
||||
|
||||
// called from code when an agent is done initializing after AddAgent is called
|
||||
// this should set up any state specific to this agent and game
|
||||
self SpawnAgent( spawnOrigin, spawnAngles, animclass, 15, 40, optional_owner );
|
||||
level notify( "spawned_agent", self );
|
||||
|
||||
self maps\mp\agents\_agent_common::set_agent_health( 250 );
|
||||
|
||||
// must set the team after SpawnAgent to fix a bug with weapon crosshairs and nametags
|
||||
if( IsDefined(optional_owner) )
|
||||
{
|
||||
self set_agent_team( optional_owner.team, optional_owner );
|
||||
}
|
||||
|
||||
self SetThreatBiasGroup( "Dogs" );
|
||||
|
||||
self TakeAllWeapons();
|
||||
|
||||
// hide the dog, let the whistle happen, then show the dog
|
||||
if( IsDefined(self.owner) )
|
||||
{
|
||||
self Hide();
|
||||
wait( 1.0 ); // not sure what to endon for this
|
||||
|
||||
// The dog could have died during the 1 second wait (for example if he spawned in a kill trigger), so if that happened,
|
||||
// don't start thinking since it will cause SREs due to him missing a self.agent_type
|
||||
if ( !IsAlive(self) )
|
||||
return;
|
||||
|
||||
self Show();
|
||||
}
|
||||
|
||||
self thread [[ self agentFunc("think") ]]();
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
if ( IsHairRunning() )
|
||||
{
|
||||
if ( dog_type == 1 )
|
||||
furFX = level.wolfFurFX[ wolf_type ];
|
||||
else
|
||||
furFX = level.furFX;
|
||||
|
||||
assert( IsDefined( furFX ) );
|
||||
PlayFXOnTag( furFX, self, "tag_origin" );
|
||||
}
|
||||
}
|
603
maps/mp/killstreaks/_dronehive.gsc
Normal file
603
maps/mp/killstreaks/_dronehive.gsc
Normal file
@ -0,0 +1,603 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
|
||||
//============================================
|
||||
// constants
|
||||
//============================================
|
||||
CONST_DRONE_HIVE_DEBUG = false;
|
||||
CONST_MISSILE_COUNT = 2;
|
||||
CONST_WEAPON_MAIN = "drone_hive_projectile_mp";
|
||||
CONST_WEAPON_CHILD = "switch_blade_child_mp";
|
||||
|
||||
|
||||
//============================================
|
||||
// init
|
||||
//============================================
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs["drone_hive"] = ::tryUseDroneHive;
|
||||
|
||||
level.droneMissileSpawnArray = GetEntArray( "remoteMissileSpawn", "targetname" );
|
||||
|
||||
foreach( missileSpawn in level.droneMissileSpawnArray )
|
||||
{
|
||||
missileSpawn.targetEnt = GetEnt( missileSpawn.target, "targetname" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// tryUseDroneHive
|
||||
//============================================
|
||||
tryUseDroneHive( lifeId, streakName )
|
||||
{
|
||||
/#
|
||||
if( (!IsDefined(level.droneMissileSpawnArray) || !level.droneMissileSpawnArray.size) )
|
||||
{
|
||||
AssertMsg( "map needs remoteMissileSpawn entities" );
|
||||
}
|
||||
#/
|
||||
|
||||
return useDroneHive( self, lifeId );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// useDroneHive
|
||||
//============================================
|
||||
useDroneHive( player, lifeId )
|
||||
{
|
||||
if ( IsDefined( self.underWater ) && self.underWater )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
player setUsingRemote( "remotemissile" );
|
||||
player freezeControlsWrapper( true );
|
||||
player _disableWeaponSwitch();
|
||||
// JC-10/01/13-Because the init ride of the killstreak has waits
|
||||
// the killstreak disowned notify could get missed causing the
|
||||
// usingRemote variable to never get cleared on the player. Start
|
||||
// these clean up threads off before to prevent this.
|
||||
level thread monitorDisownKillstreaks( player );
|
||||
level thread monitorGameEnd( player );
|
||||
level thread monitorObjectiveCamera(player);
|
||||
|
||||
result = player maps\mp\killstreaks\_killstreaks::initRideKillstreak( "drone_hive" );
|
||||
|
||||
if ( result == "success" )
|
||||
{
|
||||
player freezeControlsWrapper( false );
|
||||
level thread runDroneHive( player, lifeId);
|
||||
}
|
||||
else
|
||||
{
|
||||
player notify( "end_kill_streak" ); // stop the monitor threads
|
||||
player clearUsingRemote();
|
||||
player _enableWeaponSwitch();
|
||||
}
|
||||
|
||||
return result == "success";
|
||||
}
|
||||
|
||||
//============================================
|
||||
// watchHostMigrationStartedInit
|
||||
//============================================
|
||||
watchHostMigrationStartedInit( player )
|
||||
{
|
||||
player endon( "killstreak_disowned" );
|
||||
player endon ( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
level waittill( "host_migration_begin" );
|
||||
|
||||
if ( isDefined( self ) )
|
||||
{
|
||||
player VisionSetMissilecamForPlayer( game["thermal_vision"], 0 );
|
||||
player set_visionset_for_watching_players( "default", 0, undefined, true );
|
||||
player ThermalVisionFOFOverlayOn();
|
||||
}
|
||||
else
|
||||
{
|
||||
player SetClientOmnvar( "ui_predator_missile", 2 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//============================================
|
||||
// watchHostMigrationFinishedInit
|
||||
//============================================
|
||||
watchHostMigrationFinishedInit( player )
|
||||
{
|
||||
player endon( "killstreak_disowned" );
|
||||
player endon ( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
level waittill( "host_migration_end" );
|
||||
|
||||
|
||||
if ( isDefined( self ) )
|
||||
{
|
||||
player SetClientOmnvar( "ui_predator_missile", 1 );
|
||||
player SetClientOmnvar( "ui_predator_missiles_left", self.missilesLeft );
|
||||
}
|
||||
else
|
||||
{
|
||||
player SetClientOmnvar( "ui_predator_missile", 2 );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//============================================
|
||||
// useDroneHive
|
||||
//============================================
|
||||
runDroneHive( player, lifeId )
|
||||
{
|
||||
player endon( "killstreak_disowned" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
player notifyOnPlayerCommand( "missileTargetSet", "+attack" );
|
||||
player notifyOnPlayerCommand( "missileTargetSet", "+attack_akimbo_accessible" );
|
||||
|
||||
remoteMissileSpawn = getBestMissileSpawnPoint( player, level.droneMissileSpawnArray );
|
||||
|
||||
startPos = remoteMissileSpawn.origin;
|
||||
targetPos = remoteMissileSpawn.targetEnt.origin;
|
||||
vector = VectorNormalize( startPos - targetPos );
|
||||
startPos = ( vector * 14000 ) + targetPos;
|
||||
|
||||
/#
|
||||
if( CONST_DRONE_HIVE_DEBUG )
|
||||
{
|
||||
level thread drawLine( startPos, targetPos, 15, (1,0,0) );
|
||||
}
|
||||
#/
|
||||
|
||||
rocket = MagicBullet( CONST_WEAPON_MAIN, startpos, targetPos, player );
|
||||
rocket SetCanDamage( true );
|
||||
rocket DisableMissileBoosting();
|
||||
rocket SetMissileMinimapVisible( true );
|
||||
|
||||
rocket.team = player.team;
|
||||
rocket.lifeId = lifeId;
|
||||
rocket.type = "remote";
|
||||
rocket.owner = player;
|
||||
rocket.entityNumber = rocket GetEntityNumber();
|
||||
|
||||
level.rockets[ rocket.entityNumber ] = rocket;
|
||||
level.remoteMissileInProgress = true;
|
||||
|
||||
level thread monitorDeath( rocket, true );
|
||||
level thread monitorBoost( rocket );
|
||||
|
||||
// this is used for challenge tracking
|
||||
if ( IsDefined( player.killsThisLifePerWeapon ) )
|
||||
{
|
||||
player.killsThisLifePerWeapon[ CONST_WEAPON_MAIN ] = 0;
|
||||
player.killsThisLifePerWeapon[ CONST_WEAPON_CHILD ] = 0;
|
||||
}
|
||||
|
||||
missileEyes( player, rocket );
|
||||
|
||||
player SetClientOmnvar( "ui_predator_missile", 1 );
|
||||
|
||||
rocket thread watchHostMigrationStartedInit( player );
|
||||
rocket thread watchHostMigrationFinishedInit( player );
|
||||
|
||||
missileCount = 0;
|
||||
rocket.missilesLeft = CONST_MISSILE_COUNT;
|
||||
|
||||
player setClientOmnVar( "ui_predator_missiles_left", CONST_MISSILE_COUNT );
|
||||
|
||||
while( true )
|
||||
{
|
||||
result = rocket waittill_any_return( "death", "missileTargetSet" );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if( result == "death" )
|
||||
break;
|
||||
|
||||
//rocket trigger could be hit pre migration then rocket dies during migration
|
||||
//causing this case
|
||||
if ( !isDefined( rocket ) )
|
||||
break;
|
||||
|
||||
if( missileCount < CONST_MISSILE_COUNT )
|
||||
{
|
||||
level thread spawnSwitchBlade( rocket, missileCount );
|
||||
missileCount++;
|
||||
|
||||
rocket.missilesLeft = CONST_MISSILE_COUNT - missileCount;
|
||||
player setClientOmnVar( "ui_predator_missiles_left", rocket.missilesLeft );
|
||||
|
||||
if( missileCount == CONST_MISSILE_COUNT )
|
||||
rocket EnableMissileBoosting();
|
||||
}
|
||||
}
|
||||
|
||||
thread returnPlayer( player );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorLockedTarget NOT IN USE WorldPointInReticle_Circle doesnt function here
|
||||
//============================================
|
||||
monitorLockedTarget()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
enemyTargets = [];
|
||||
sortedTargets = [];
|
||||
|
||||
for(;;)
|
||||
{
|
||||
targetsInsideReticle = [];
|
||||
enemyTargets = getEnemyTargets();
|
||||
|
||||
foreach( targ in enemyTargets )
|
||||
{
|
||||
targInReticle = self.owner WorldPointInReticle_Circle( targ.origin, 65, 90 );
|
||||
|
||||
if ( targInReticle )
|
||||
{
|
||||
self.owner thread drawLine( self.origin, targ.origin, 10, (0,0,1) );
|
||||
targetsInsideReticle[targetsInsideReticle.size] = targ;
|
||||
}
|
||||
}
|
||||
|
||||
if( targetsInsideReticle.size )
|
||||
{
|
||||
sortedTargets = SortByDistance( targetsInsideReticle, self.origin );
|
||||
self.lastTargetLocked = sortedTargets[0];
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.25 );
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
}
|
||||
}
|
||||
|
||||
//============================================
|
||||
// getEnemyTargets
|
||||
//============================================
|
||||
getEnemyTargets( owner )
|
||||
{
|
||||
enemyTargets = [];
|
||||
|
||||
foreach ( player in level.participants )
|
||||
{
|
||||
if ( owner isEnemy( player )
|
||||
&& !(player _hasPerk( "specialty_blindeye" ))
|
||||
)
|
||||
{
|
||||
enemyTargets[ enemyTargets.size ] = player;
|
||||
}
|
||||
}
|
||||
|
||||
enemyVehicleTargets = maps\mp\gametypes\_weapons::lockOnLaunchers_getTargetArray();
|
||||
|
||||
if ( enemyTargets.size && enemyVehicleTargets.size )
|
||||
{
|
||||
finalTargets = array_combine( enemyTargets, enemyVehicleTargets );
|
||||
return finalTargets;
|
||||
}
|
||||
else if ( enemyTargets.size )
|
||||
{
|
||||
return enemyTargets;
|
||||
}
|
||||
else
|
||||
{
|
||||
return enemyVehicleTargets;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// spawnSwitchBlade
|
||||
//============================================
|
||||
spawnSwitchBlade( rocket, spawnOnLeft )
|
||||
{
|
||||
rocket.owner playLocalSound( "ammo_crate_use" );
|
||||
|
||||
playerViewAngles = rocket GetTagAngles( "tag_camera" );
|
||||
forwardDir = AnglesToForward( playerViewAngles );
|
||||
rightDir = AnglesToRight( playerViewAngles );
|
||||
spawnOffset = (35,35,35);
|
||||
targetOffset = (15000,15000,15000);
|
||||
|
||||
if(spawnOnLeft)
|
||||
spawnOffset = spawnOffset * -1;
|
||||
|
||||
result = BulletTrace( rocket.origin, rocket.origin + (forwardDir * targetOffset), false, rocket );
|
||||
|
||||
targetOffset = targetOffset * result["fraction"];
|
||||
startPosition = rocket.origin + (rightDir * spawnOffset );
|
||||
targetLocation = rocket.origin + (forwardDir * targetOffset );
|
||||
|
||||
targets = rocket.owner getEnemyTargets( rocket.owner );
|
||||
missile = MagicBullet( CONST_WEAPON_CHILD, startPosition, targetLocation, rocket.owner );
|
||||
|
||||
foreach ( targ in targets )
|
||||
{
|
||||
if ( Distance2dsquared( targ.origin, targetLocation ) < (512*512) )
|
||||
{
|
||||
missile Missile_SetTargetEnt( targ );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//missile.targEffect = SpawnFx( level.lasedStrikeGlow, targetLocation );
|
||||
//self thread DrawLine(startPosition, targetLocation, 20, (1,0,0) );
|
||||
|
||||
missile SetCanDamage( true );
|
||||
missile SetMissileMinimapVisible( true );
|
||||
|
||||
missile.team = rocket.team;
|
||||
missile.lifeId = rocket.lifeId;
|
||||
missile.type = rocket.type;
|
||||
missile.owner = rocket.owner;
|
||||
missile.entityNumber = missile GetEntityNumber();
|
||||
|
||||
level.rockets[ missile.entityNumber ] = missile;
|
||||
level thread monitorDeath( missile, false );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// loop effect for targ
|
||||
//============================================
|
||||
loopTriggeredEffect( effect, missile )
|
||||
{
|
||||
missile endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
TriggerFX( effect );
|
||||
wait ( 0.25 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// getNextMissileSpawnIndex
|
||||
//============================================
|
||||
getNextMissileSpawnIndex( oldIndex )
|
||||
{
|
||||
index = oldIndex + 1;
|
||||
|
||||
if( index == level.droneMissileSpawnArray.size )
|
||||
{
|
||||
index = 0;
|
||||
}
|
||||
|
||||
return index;
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorBoost
|
||||
//============================================
|
||||
monitorBoost( rocket )
|
||||
{
|
||||
rocket endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
rocket.owner waittill( "missileTargetSet" );
|
||||
rocket notify( "missileTargetSet" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// getBestMissileSpawnPoint
|
||||
//============================================
|
||||
getBestMissileSpawnPoint( owner, remoteMissileSpawnPoints )
|
||||
{
|
||||
validEnemies = [];
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if( !isReallyAlive( player ) )
|
||||
continue;
|
||||
|
||||
if( player.team == owner.team )
|
||||
continue;
|
||||
|
||||
if( player.team == "spectator" )
|
||||
continue;
|
||||
|
||||
validEnemies[validEnemies.size] = player;
|
||||
}
|
||||
|
||||
if( !validEnemies.size )
|
||||
{
|
||||
return remoteMissileSpawnPoints[ RandomInt(remoteMissileSpawnPoints.size)];
|
||||
}
|
||||
|
||||
remoteMissileSpawnPointsRandomized = array_randomize(remoteMissileSpawnPoints);
|
||||
bestMissileSpawn = remoteMissileSpawnPointsRandomized[0];
|
||||
|
||||
// select a missile spawn that can see the most enemies
|
||||
foreach( missileSpawn in remoteMissileSpawnPointsRandomized )
|
||||
{
|
||||
missileSpawn.sightedEnemies = 0;
|
||||
|
||||
for ( i = 0; i < validEnemies.size; i++ )
|
||||
{
|
||||
enemy = validEnemies[i];
|
||||
if( !isReallyAlive( enemy ) )
|
||||
{
|
||||
validEnemies[i] = validEnemies[validEnemies.size-1];
|
||||
validEnemies[validEnemies.size-1] = undefined;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
|
||||
//IW6 JH made this way cheaper if it sees an enemy it returns it.
|
||||
if( BulletTracePassed( enemy.origin + (0,0,32), missileSpawn.origin, false, enemy ) )
|
||||
{
|
||||
missileSpawn.sightedEnemies += 1;
|
||||
return missileSpawn;
|
||||
}
|
||||
|
||||
wait(0.05); // This loop is O(n^2), and it does a trace each time, so make sure to only do one per frame
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
}
|
||||
|
||||
if ( missileSpawn.sightedEnemies == validEnemies.size )
|
||||
{
|
||||
// Optimization - if this missileSpawn can see all enemies, then we're not going to find a better one, so just return it immediately
|
||||
return missileSpawn;
|
||||
}
|
||||
|
||||
if( missileSpawn.sightedEnemies > bestMissileSpawn.sightedEnemies )
|
||||
{
|
||||
bestMissileSpawn = missileSpawn;
|
||||
}
|
||||
}
|
||||
|
||||
return bestMissileSpawn;
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// missileEyes
|
||||
//============================================
|
||||
missileEyes( player, rocket )
|
||||
{
|
||||
delayTime = 1.0;
|
||||
|
||||
player freezeControlsWrapper( true );
|
||||
player CameraLinkTo( rocket, "tag_origin" );
|
||||
player ControlsLinkTo( rocket );
|
||||
player VisionSetMissilecamForPlayer( "default", delayTime );
|
||||
player thread set_visionset_for_watching_players( "default", delayTime, undefined, true );
|
||||
|
||||
player VisionSetMissilecamForPlayer( game["thermal_vision"], 1.0 );
|
||||
player thread delayedFOFOverlay();
|
||||
|
||||
level thread unfreezeControls( player, delayTime );
|
||||
}
|
||||
|
||||
|
||||
delayedFOFOverlay()
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.25 );
|
||||
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
}
|
||||
|
||||
//============================================
|
||||
// unfreezeControls
|
||||
//============================================
|
||||
unfreezeControls( player, delayTime, i )
|
||||
{
|
||||
player endon( "disconnect" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( delayTime - 0.35 );
|
||||
|
||||
player freezeControlsWrapper( false );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorDisownKillstreaks
|
||||
//============================================
|
||||
monitorDisownKillstreaks( player )
|
||||
{
|
||||
player endon( "disconnect" );
|
||||
player endon( "end_kill_streak" );
|
||||
|
||||
player waittill( "killstreak_disowned" );
|
||||
|
||||
level thread returnPlayer( player );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorGameEnd
|
||||
//============================================
|
||||
monitorGameEnd( player )
|
||||
{
|
||||
player endon( "disconnect" );
|
||||
player endon( "end_kill_streak" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
level thread returnPlayer( player );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorObjectiveCamera
|
||||
//============================================
|
||||
monitorObjectiveCamera( player )
|
||||
{
|
||||
player endon( "end_kill_streak" );
|
||||
player endon( "disconnect" );
|
||||
|
||||
level waittill( "objective_cam" );
|
||||
|
||||
level thread returnPlayer( player, true );
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// monitorDeath
|
||||
//============================================
|
||||
monitorDeath( killStreakEnt, mainMissile )
|
||||
{
|
||||
killStreakEnt waittill( "death" );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if ( isDefined( killStreakEnt.targEffect ) )
|
||||
killStreakEnt.targEffect Delete();
|
||||
|
||||
if ( isDefined ( killStreakEnt.entityNumber ) )
|
||||
level.rockets[ killStreakEnt.entityNumber ] = undefined;
|
||||
|
||||
if ( mainMissile )
|
||||
level.remoteMissileInProgress = undefined;
|
||||
}
|
||||
|
||||
|
||||
//============================================
|
||||
// returnPlayer
|
||||
//============================================
|
||||
returnPlayer( player, instant )
|
||||
{
|
||||
if( !IsDefined(player) )
|
||||
return;
|
||||
|
||||
player SetClientOmnvar( "ui_predator_missile", 2 );
|
||||
player notify( "end_kill_streak" );
|
||||
|
||||
player freezeControlsWrapper( true );
|
||||
player ThermalVisionFOFOverlayOff();
|
||||
player ControlsUnlink();
|
||||
|
||||
if ( !isDefined( instant ) )
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 0.95 );
|
||||
|
||||
player CameraUnlink();
|
||||
player SetClientOmnvar( "ui_predator_missile", 0 );
|
||||
player clearUsingRemote();
|
||||
player _enableWeaponSwitch();
|
||||
}
|
564
maps/mp/killstreaks/_emp.gsc
Normal file
564
maps/mp/killstreaks/_emp.gsc
Normal file
@ -0,0 +1,564 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\killstreaks\_emp_common;
|
||||
|
||||
init()
|
||||
{
|
||||
// 2013-06-30 wallace: make sure emp code doesn't run. jammer reuses some of the sstuff, and we don't want the two stomping on each other
|
||||
/*
|
||||
level._effect[ "emp_flash" ] = loadfx( "fx/explosions/emp_flash_mp" );
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
level.teamEMPed[level.teamNameList[i]] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level.teamEMPed["allies"] = false;
|
||||
level.teamEMPed["axis"] = false;
|
||||
}
|
||||
|
||||
level.empPlayer = undefined;
|
||||
level.empTimeout = 15.0;
|
||||
level.empTimeRemaining = int( level.empTimeout );
|
||||
|
||||
if ( level.teamBased )
|
||||
level thread EMP_TeamTracker();
|
||||
else
|
||||
level thread EMP_PlayerTracker();
|
||||
|
||||
level.killstreakFuncs["emp"] = ::EMP_Use;
|
||||
|
||||
level thread onPlayerConnect();
|
||||
*/
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_emp_timeout", 15.0 );
|
||||
SetDevDvarIfUninitialized( "scr_emp_damage_debug", 0 );
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if ( (level.teamBased && level.teamEMPed[self.team]) || (!level.teamBased && isDefined( level.empPlayer ) && level.empPlayer != self) )
|
||||
self setEMPJammed( true );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EMP_Use( lifeId, streakName )
|
||||
{
|
||||
assert( isDefined( self ) );
|
||||
|
||||
myTeam = self.pers["team"];
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
self thread EMP_JamTeams( myTeam );
|
||||
}
|
||||
else if ( level.teamBased )
|
||||
{
|
||||
otherTeam = level.otherTeam[myTeam];
|
||||
self thread EMP_JamTeam( otherTeam );
|
||||
}
|
||||
else
|
||||
self thread EMP_JamPlayers( self );
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "emp", self.origin );
|
||||
self notify( "used_emp" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
EMP_JamTeams( ownerTeam )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
assert( ownerTeam == "allies" || ownerTeam == "axis" || IsSubStr( ownerTeam, "team_" ));
|
||||
|
||||
thread teamPlayerCardSplash( "used_emp", self );
|
||||
|
||||
level notify ( "EMP_JamTeam" + ownerTeam );
|
||||
level endon ( "EMP_JamTeam" + ownerTeam );
|
||||
|
||||
//turn off jammer abilities on emp'd players
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player playLocalSound( "emp_activate" );
|
||||
|
||||
if ( player.team == ownerTeam )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player ClearScrambler();
|
||||
}
|
||||
|
||||
VisionSetNaked( "coup_sunblind", 0.1 );
|
||||
thread empEffects();
|
||||
|
||||
wait ( 0.1 );
|
||||
|
||||
// resetting the vision set to the same thing won't normally have an effect.
|
||||
// however, if the client receives the previous visionset change in the same packet as this one,
|
||||
// this will force them to lerp from the bright one to the normal one.
|
||||
VisionSetNaked( "coup_sunblind", 0 );
|
||||
VisionSetNaked( "", 3.0 ); // go to default visionset
|
||||
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
if( ownerTeam != level.teamNameList[i] )
|
||||
{
|
||||
level.teamEMPed[level.teamNameList[i]] = true;
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
if( ownerTeam != level.teamNameList[i] )
|
||||
{
|
||||
level destroyActiveVehicles( self, level.teamNameList[i] );
|
||||
}
|
||||
}
|
||||
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_emp_timeout" );
|
||||
#/
|
||||
level thread keepEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.empTimeout );
|
||||
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
if( ownerTeam != level.teamNameList[i] )
|
||||
{
|
||||
level.teamEMPed[level.teamNameList[i]] = false;
|
||||
}
|
||||
}
|
||||
|
||||
//turn jammer abilities back on
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == ownerTeam )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player MakeScrambler();
|
||||
}
|
||||
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
//jams all players on the team passed in the argument teamName
|
||||
EMP_JamTeam( teamName )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
assert( teamName == "allies" || teamName == "axis" );
|
||||
|
||||
thread teamPlayerCardSplash( "used_emp", self );
|
||||
|
||||
level notify ( "EMP_JamTeam" + teamName );
|
||||
level endon ( "EMP_JamTeam" + teamName );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player playLocalSound( "emp_activate" );
|
||||
|
||||
if ( player.team != teamName )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player ClearScrambler();
|
||||
|
||||
player VisionSetNakedForPlayer( "coup_sunblind", 0.1 );
|
||||
}
|
||||
|
||||
thread empEffects();
|
||||
|
||||
wait ( 0.1 );
|
||||
|
||||
// resetting the vision set to the same thing won't normally have an effect.
|
||||
// however, if the client receives the previous visionset change in the same packet as this one,
|
||||
// this will force them to lerp from the bright one to the normal one.
|
||||
VisionSetNaked( "coup_sunblind", 0 );
|
||||
VisionSetNaked( "", 3.0 ); // go to default visionset
|
||||
|
||||
level.teamEMPed[teamName] = true;
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level destroyActiveVehicles( self, teamName );
|
||||
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_emp_timeout" );
|
||||
#/
|
||||
level thread keepEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.empTimeout );
|
||||
|
||||
level.teamEMPed[teamName] = false;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team != teamName )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player MakeScrambler();
|
||||
}
|
||||
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
|
||||
EMP_JamPlayers( owner )
|
||||
{
|
||||
level notify ( "EMP_JamPlayers" );
|
||||
level endon ( "EMP_JamPlayers" );
|
||||
|
||||
assert( isDefined( owner ) );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player playLocalSound( "emp_activate" );
|
||||
|
||||
if ( player == owner )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player ClearScrambler();
|
||||
}
|
||||
|
||||
VisionSetNaked( "coup_sunblind", 0.1 );
|
||||
thread empEffects();
|
||||
|
||||
wait ( 0.1 );
|
||||
|
||||
// resetting the vision set to the same thing won't normally have an effect.
|
||||
// however, if the client receives the previous visionset change in the same packet as this one,
|
||||
// this will force them to lerp from the bright one to the normal one.
|
||||
VisionSetNaked( "coup_sunblind", 0 );
|
||||
VisionSetNaked( "", 3.0 ); // go to default visionset
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level.empPlayer = owner;
|
||||
level.empPlayer thread empPlayerFFADisconnect();
|
||||
level destroyActiveVehicles( owner );
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_emp_timeout" );
|
||||
#/
|
||||
level thread keepEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.empTimeout );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player == owner )
|
||||
continue;
|
||||
|
||||
if ( player _hasPerk( "specialty_localjammer" ) )
|
||||
player MakeScrambler();
|
||||
}
|
||||
|
||||
level.empPlayer = undefined;
|
||||
level notify ( "emp_update" );
|
||||
level notify ( "emp_ended" );
|
||||
}
|
||||
|
||||
keepEMPTimeRemaining()
|
||||
{
|
||||
level notify( "keepEMPTimeRemaining" );
|
||||
level endon( "keepEMPTimeRemaining" );
|
||||
|
||||
level endon( "emp_ended" );
|
||||
|
||||
// we need to know how much time is left for the unavailable string
|
||||
level.empTimeRemaining = int( level.empTimeout );
|
||||
while( level.empTimeRemaining )
|
||||
{
|
||||
wait( 1.0 );
|
||||
level.empTimeRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
empPlayerFFADisconnect()
|
||||
{
|
||||
level endon ( "EMP_JamPlayers" );
|
||||
level endon ( "emp_ended" );
|
||||
|
||||
self waittill( "disconnect" );
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
empEffects()
|
||||
{
|
||||
foreach( player in level.players )
|
||||
{
|
||||
playerForward = anglestoforward( player.angles );
|
||||
playerForward = ( playerForward[0], playerForward[1], 0 );
|
||||
playerForward = VectorNormalize( playerForward );
|
||||
|
||||
empDistance = 20000;
|
||||
|
||||
empEnt = Spawn( "script_model", player.origin + ( 0, 0, 8000 ) + ( playerForward * empDistance ) );
|
||||
empEnt setModel( "tag_origin" );
|
||||
empEnt.angles = empEnt.angles + ( 270, 0, 0 );
|
||||
empEnt thread empEffect( player );
|
||||
}
|
||||
}
|
||||
|
||||
empEffect( player )
|
||||
{
|
||||
player endon( "disconnect" );
|
||||
|
||||
wait( 0.5 );
|
||||
PlayFXOnTagForClients( level._effect[ "emp_flash" ], self, "tag_origin", player );
|
||||
}
|
||||
|
||||
EMP_TeamTracker()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill_either ( "joined_team", "emp_update" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == "spectator" )
|
||||
continue;
|
||||
|
||||
// if this emp is over, let's do an extra check to make sure we shouldn't be nuke emped, isEmped() does the extra nuke emp check
|
||||
if( !level.teamEMPed[ player.team ] && !player isEMPed() )
|
||||
player enableJammedEffect( false );
|
||||
else
|
||||
player enableJammedEffect( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
EMP_PlayerTracker()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill_either ( "joined_team", "emp_update" );
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == "spectator" )
|
||||
continue;
|
||||
|
||||
if ( isDefined( level.empPlayer ) && level.empPlayer != player )
|
||||
{
|
||||
player enableJammedEffect( true );
|
||||
}
|
||||
else
|
||||
{
|
||||
// if this emp is over, let's do an extra check to make sure we shouldn't be nuke emped, isEmped() does the extra nuke emp check
|
||||
if( !player isEMPed() )
|
||||
player enableJammedEffect( false );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveVehicles( attacker, teamEMPed )
|
||||
{
|
||||
// thread all of the things that need to get destroyed, this way we can put frame waits in between each destruction so we don't hit the server with a lot at one time
|
||||
thread destroyActiveHelis( attacker, teamEMPed );
|
||||
thread destroyActiveLittleBirds( attacker, teamEMPed );
|
||||
thread destroyActiveTurrets( attacker, teamEMPed );
|
||||
thread destroyActiveRockets( attacker, teamEMPed );
|
||||
thread destroyActiveUAVs( attacker, teamEMPed );
|
||||
thread destroyActiveIMSs( attacker, teamEMPed );
|
||||
thread destroyActiveUGVs( attacker, teamEMPed );
|
||||
thread destroyActiveAC130( attacker, teamEMPed );
|
||||
thread destroyActiveBallDrones( attacker, teamEMPed );
|
||||
thread destroyTargets( attacker, teamEMPed, level.remote_uav );
|
||||
thread destroyTargets( attacker, teamEMPed, level.uplinks );
|
||||
}
|
||||
|
||||
destroyTargets( attacker, teamEMPed, targetList )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
|
||||
damage = 5000;
|
||||
direction_vec = ( 0, 0, 0 );
|
||||
point = ( 0, 0, 0 );
|
||||
modelName = "";
|
||||
tagName = "";
|
||||
partName = "";
|
||||
iDFlags = undefined;
|
||||
|
||||
foreach ( target in targetList )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( target.team ) && target.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( target.owner ) && target.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
target notify( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveHelis( attacker, teamEMPed )
|
||||
{
|
||||
destroyTargets( attacker, teamEMPed, level.helis );
|
||||
}
|
||||
|
||||
destroyActiveLittleBirds( attacker, teamEMPed )
|
||||
{
|
||||
destroyTargets( attacker, teamEMPed, level.littleBirds );
|
||||
}
|
||||
|
||||
destroyActiveTurrets( attacker, teamEMPed )
|
||||
{
|
||||
destroyTargets( attacker, teamEMPed, level.turrets );
|
||||
}
|
||||
|
||||
destroyActiveRockets( attacker, teamEMPed )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
|
||||
damage = 5000;
|
||||
direction_vec = ( 0, 0, 0 );
|
||||
point = ( 0, 0, 0 );
|
||||
modelName = "";
|
||||
tagName = "";
|
||||
partName = "";
|
||||
iDFlags = undefined;
|
||||
|
||||
foreach ( rocket in level.rockets )
|
||||
{
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if( IsDefined( rocket.team ) && rocket.team != teamEMPed )
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( rocket.owner ) && rocket.owner == attacker )
|
||||
continue;
|
||||
}
|
||||
|
||||
// this is different from destroy target
|
||||
PlayFX( level.remotemissile_fx[ "explode" ], rocket.origin );
|
||||
rocket delete();
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveUAVs( attacker, teamEMPed )
|
||||
{
|
||||
uavArray = level.uavModels;
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
uavArray = level.uavModels[ teamEMPed ];
|
||||
|
||||
destroyTargets( attacker, teamEMPed, uavArray );
|
||||
}
|
||||
|
||||
destroyActiveIMSs( attacker, teamEMPed )
|
||||
{
|
||||
destroyTargets( attacker, teamEMPed, level.ims );
|
||||
}
|
||||
|
||||
destroyActiveUGVs( attacker, teamEMPed )
|
||||
{
|
||||
destroyTargets( attacker, teamEMPed, level.ugvs );
|
||||
}
|
||||
|
||||
destroyActiveAC130( attacker, teamEMPed )
|
||||
{
|
||||
meansOfDeath = "MOD_EXPLOSIVE";
|
||||
weapon = "killstreak_emp_mp";
|
||||
|
||||
damage = 5000;
|
||||
direction_vec = ( 0, 0, 0 );
|
||||
point = ( 0, 0, 0 );
|
||||
modelName = "";
|
||||
tagName = "";
|
||||
partName = "";
|
||||
iDFlags = undefined;
|
||||
|
||||
if ( level.teamBased && IsDefined( teamEMPed ) )
|
||||
{
|
||||
if ( IsDefined( level.ac130player ) && IsDefined( level.ac130player.team ) && level.ac130player.team == teamEMPed )
|
||||
level.ac130.planeModel notify( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( level.ac130player ) )
|
||||
{
|
||||
if( !IsDefined( level.ac130.owner ) || ( IsDefined( level.ac130.owner ) && level.ac130.owner != attacker ) )
|
||||
level.ac130.planeModel notify( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
destroyActiveBallDrones( attacker, teamEMPed )
|
||||
{
|
||||
destroyTargets( attacker, teamEMPed, level.ballDrones );
|
||||
}
|
||||
|
||||
enableJammedEffect( flag )
|
||||
{
|
||||
self setEMPJammed( flag );
|
||||
shakeValue = 0;
|
||||
if ( flag )
|
||||
{
|
||||
shakeValue = 1;
|
||||
}
|
||||
|
||||
self thread startEmpJamSequence();
|
||||
}
|
||||
|
||||
/#
|
||||
drawEMPDamageOrigin( pos, ang, radius )
|
||||
{
|
||||
while( GetDvarInt( "scr_emp_damage_debug" ) )
|
||||
{
|
||||
Line( pos, pos + ( AnglesToForward( ang ) * radius ), ( 1, 0, 0 ) );
|
||||
Line( pos, pos + ( AnglesToRight( ang ) * radius ), ( 0, 1, 0 ) );
|
||||
Line( pos, pos + ( AnglesToUp( ang ) * radius ), ( 0, 0, 1 ) );
|
||||
|
||||
Line( pos, pos - ( AnglesToForward( ang ) * radius ), ( 1, 0, 0 ) );
|
||||
Line( pos, pos - ( AnglesToRight( ang ) * radius ), ( 0, 1, 0 ) );
|
||||
Line( pos, pos - ( AnglesToUp( ang ) * radius ), ( 0, 0, 1 ) );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
#/
|
227
maps/mp/killstreaks/_emp_common.gsc
Normal file
227
maps/mp/killstreaks/_emp_common.gsc
Normal file
@ -0,0 +1,227 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
kEMP_VISION_SET = "coup_sunblind";
|
||||
kEMP_BLIND_ON_DURATION = 0.05;
|
||||
kEMP_BLIND_OFF_DURATION = 0.5;
|
||||
|
||||
shouldPlayerBeAffectedByEMP()
|
||||
{
|
||||
// isEMPed will handle the team and FFA cases
|
||||
return !(self _hasPerk( "specialty_empimmune" )) && (self isEMPed());
|
||||
}
|
||||
|
||||
applyGlobalEMPEffects()
|
||||
{
|
||||
VisionSetNaked( "coup_sunblind", kEMP_BLIND_ON_DURATION );
|
||||
wait ( kEMP_BLIND_ON_DURATION );
|
||||
|
||||
// resetting the vision set to the same thing won't normally have an effect.
|
||||
// however, if the client receives the previous visionset change in the same packet as this one,
|
||||
// this will force them to lerp from the bright one to the normal one.
|
||||
VisionSetNaked( kEMP_VISION_SET, 0 );
|
||||
VisionSetNaked( "", kEMP_BLIND_OFF_DURATION ); // go to default visionset
|
||||
}
|
||||
|
||||
// these are the effects played when the EMP/jammer is triggered
|
||||
applyPerPlayerEMPEffects_OnDetonate()
|
||||
{
|
||||
self playLocalSound( "emp_activate" );
|
||||
}
|
||||
|
||||
// these only affect enemy players
|
||||
// should be applied to all players who join late
|
||||
applyPerPlayerEMPEffects()
|
||||
{
|
||||
self setEMPJammed( true );
|
||||
|
||||
if ( self _hasPerk( "specialty_localjammer" ) )
|
||||
{
|
||||
self ClearScrambler();
|
||||
}
|
||||
|
||||
// self SetClientDvar( "ui_hud_shake_looping", 3 );
|
||||
self thread startEmpJamSequence();
|
||||
}
|
||||
|
||||
removePerPlayerEMPEffects()
|
||||
{
|
||||
self setEMPJammed( false );
|
||||
|
||||
if ( self _hasPerk( "specialty_localjammer" ) )
|
||||
{
|
||||
self MakeScrambler();
|
||||
}
|
||||
|
||||
// self SetClientDvar( "ui_hud_shake_looping", 0 );
|
||||
self thread stopEmpJamSequence();
|
||||
}
|
||||
|
||||
kArtifactReps = 3;
|
||||
kArtifactWaitTimeMin = 0.375;
|
||||
kArtifactWaitTimeMax = 0.5;
|
||||
kNoSignalWaitTimeMin = 0.25;
|
||||
kNoSignalWaitTimeMax = 1.25;
|
||||
startEmpJamSequence() // self == player
|
||||
{
|
||||
level endon("game_ended");
|
||||
self endon("emp_stop_effect");
|
||||
self endon("disconnect");
|
||||
|
||||
self.bIsPlayingJamEffects = true;
|
||||
|
||||
self thread doEmpArtifactLoop();
|
||||
|
||||
wait (1.0);
|
||||
|
||||
self SetClientOmnvar( "ui_hud_static", 2 );
|
||||
|
||||
wait (0.5);
|
||||
|
||||
self notify ("emp_stop_artifact");
|
||||
self SetClientOmnvar( "ui_hud_emp_artifact", 0 );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self SetClientOmnvar( "ui_hud_static", 3 );
|
||||
|
||||
waitTime = RandomFloatRange(kNoSignalWaitTimeMin, kNoSignalWaitTimeMax);
|
||||
wait( waitTime );
|
||||
|
||||
self SetClientOmnvar( "ui_hud_static", 2 );
|
||||
|
||||
wait (0.5);
|
||||
}
|
||||
}
|
||||
|
||||
stopEmpJamSequence()
|
||||
{
|
||||
level endon("game_ended");
|
||||
self notify("emp_stop_effect");
|
||||
self endon("disconnect");
|
||||
|
||||
if ( IsDefined( self.bIsPlayingJamEffects ) )
|
||||
{
|
||||
self.bIsPlayingJamEffects = undefined;
|
||||
|
||||
// make sure we're not in a field
|
||||
self SetClientOmnvar( "ui_hud_static", 0 );
|
||||
|
||||
for (i = 0; i < kArtifactReps; i++)
|
||||
{
|
||||
self SetClientOmnvar( "ui_hud_emp_artifact", 1 );
|
||||
|
||||
wait (0.5);
|
||||
}
|
||||
|
||||
self SetClientOmnvar( "ui_hud_emp_artifact", 0 );
|
||||
|
||||
self.player_static_value = 0;
|
||||
}
|
||||
}
|
||||
|
||||
stopEmpJamSequenceImmediate()
|
||||
{
|
||||
self notify("emp_stop_effect");
|
||||
|
||||
if ( IsDefined( self.bIsPlayingJamEffects )
|
||||
|| IsDefined( self.player_static_value)
|
||||
)
|
||||
{
|
||||
self.bIsPlayingJamEffects = undefined;
|
||||
self.player_static_value = 0;
|
||||
|
||||
// make sure we're not in a field
|
||||
self SetClientOmnvar( "ui_hud_static", 0 );
|
||||
self SetClientOmnvar( "ui_hud_emp_artifact", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
doEmpArtifactLoop()
|
||||
{
|
||||
self notify ( "emp_stop_artifact" );
|
||||
level endon("game_ended");
|
||||
self endon("emp_stop_effect");
|
||||
self endon("emp_stop_artifact");
|
||||
self endon("disconnect");
|
||||
self endon("joined_spectators");
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self SetClientOmnvar( "ui_hud_emp_artifact", 1 );
|
||||
waitTime = RandomFloatRange( kArtifactWaitTimeMin, kArtifactWaitTimeMax );
|
||||
wait ( waitTime );
|
||||
}
|
||||
}
|
||||
|
||||
doEmpStaticLoop( strengthVal )
|
||||
{
|
||||
self notify ( "emp_stop_static" );
|
||||
|
||||
level endon("game_ended");
|
||||
self endon("emp_stop_effect");
|
||||
self endon("emp_stop_static");
|
||||
self endon("disconnect");
|
||||
self endon("joined_spectators");
|
||||
|
||||
kStaticWaitTimeMin = 1.0;
|
||||
kStaticWaitTimeMax = 2.0;
|
||||
if ( strengthVal == 2 )
|
||||
{
|
||||
kStaticWaitTimeMin = 0.5;
|
||||
kStaticWaitTimeMax = 0.75;
|
||||
}
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self SetClientOmnvar( "ui_hud_static", 2 );
|
||||
waitTime = RandomFloatRange( kStaticWaitTimeMin, kStaticWaitTimeMax );
|
||||
wait ( waitTime );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------
|
||||
// static field, for mp_snow
|
||||
// or any other condition that triggers the effect based on proximity
|
||||
// !!! We are relying on _jammer.gsc::onPlayerSpawned to clean up static field effects on spawn
|
||||
// (in addition to other emp effects).
|
||||
// ------------------------------------
|
||||
staticFieldInit() // self == player
|
||||
{
|
||||
self.player_static_value = 0;
|
||||
}
|
||||
|
||||
staticFieldSetStrength( strengthVal ) // self == player
|
||||
{
|
||||
if ( self.player_static_value != strengthVal
|
||||
&& IsAlive( self )
|
||||
&& !(self isEMPed())
|
||||
)
|
||||
{
|
||||
self.player_static_value = strengthVal;
|
||||
|
||||
switch ( strengthVal )
|
||||
{
|
||||
case 0:
|
||||
stopEmpJamSequence();
|
||||
break;
|
||||
case 1:
|
||||
self.bIsPlayingJamEffects = true;
|
||||
self notify ( "emp_stop_static" );
|
||||
self thread doEmpArtifactLoop();
|
||||
self thread doEmpStaticLoop(1);
|
||||
break;
|
||||
case 2:
|
||||
self.bIsPlayingJamEffects = true;
|
||||
self notify ( "emp_stop_static" );
|
||||
self notify ( "emp_stop_artifact" );
|
||||
self thread doEmpStaticLoop(2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
staticFieldGetStrength() // self == player
|
||||
{
|
||||
return self.player_static_value;
|
||||
}
|
1220
maps/mp/killstreaks/_escortairdrop.gsc
Normal file
1220
maps/mp/killstreaks/_escortairdrop.gsc
Normal file
File diff suppressed because it is too large
Load Diff
404
maps/mp/killstreaks/_flares.gsc
Normal file
404
maps/mp/killstreaks/_flares.gsc
Normal file
@ -0,0 +1,404 @@
|
||||
#include common_scripts\utility;
|
||||
|
||||
FLARES_POP_SOUND_NPC = "veh_helo_flares_npc";
|
||||
FLARES_POP_SOUND_PLR = "veh_helo_flares_plr";
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Flare Init and Deployment
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
flares_monitor( flareCount ) // self == vehicle
|
||||
{
|
||||
self.flaresReserveCount = flareCount;
|
||||
self.flaresLive = [];
|
||||
|
||||
self thread ks_laserGuidedMissile_handleIncoming();
|
||||
|
||||
// We no longer have fire and forget launchers.
|
||||
// self thread flares_handleIncomingSAM();
|
||||
// self thread flares_handleIncomingStinger();
|
||||
}
|
||||
|
||||
flares_playFx() // self == vehicle
|
||||
{
|
||||
for ( i = 0; i < 10; i++ )
|
||||
{
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
PlayFXOnTag( level._effect[ "vehicle_flares" ], self, "TAG_FLARE" );
|
||||
|
||||
wait ( 0.15 );
|
||||
}
|
||||
}
|
||||
|
||||
flares_deploy() // self == missile target
|
||||
{
|
||||
flare = spawn( "script_origin", self.origin + ( 0, 0, -256 ) );
|
||||
flare.angles = self.angles;
|
||||
|
||||
flare MoveGravity( (0, 0, -1), 5.0 );
|
||||
|
||||
self.flaresLive[ self.flaresLive.size ] = flare;
|
||||
|
||||
flare thread flares_deleteAfterTime( 5.0, 2.0, self );
|
||||
|
||||
playSoundAtPos( flare.origin, FLARES_POP_SOUND_NPC );
|
||||
|
||||
return flare;
|
||||
}
|
||||
|
||||
flares_deleteAfterTime( delayDelete, delayStopTracking, vehicle )
|
||||
{
|
||||
AssertEx( !IsDefined( delayStopTracking ) || delayStopTracking < delayDelete, "flares_deleteAfterTime() delayDelete should never be greater than delayStopTracking." );
|
||||
|
||||
if ( IsDefined( delayStopTracking ) && IsDefined( vehicle ) )
|
||||
{
|
||||
delayDelete -= delayStopTracking;
|
||||
wait( delayStopTracking );
|
||||
|
||||
if( IsDefined( vehicle ) )
|
||||
vehicle.flaresLive = array_remove( vehicle.flaresLive, self );
|
||||
}
|
||||
|
||||
wait( delayDelete );
|
||||
|
||||
self Delete();
|
||||
}
|
||||
|
||||
flares_getNumLeft( vehicle )
|
||||
{
|
||||
return vehicle.flaresReserveCount;
|
||||
}
|
||||
|
||||
flares_areAvailable( vehicle )
|
||||
{
|
||||
flares_cleanFlaresLiveArray( vehicle );
|
||||
return vehicle.flaresReserveCount > 0 || vehicle.flaresLive.size > 0;
|
||||
}
|
||||
|
||||
flares_getFlareReserve( vehicle )
|
||||
{
|
||||
AssertEx( vehicle.flaresReserveCount > 0, "flares_getFlareReserve() called on vehicle without any flares in reserve." );
|
||||
|
||||
vehicle.flaresReserveCount--;
|
||||
|
||||
vehicle thread flares_playFx();
|
||||
flare = vehicle flares_deploy();
|
||||
|
||||
return flare;
|
||||
}
|
||||
|
||||
flares_cleanFlaresLiveArray( vehicle )
|
||||
{
|
||||
vehicle.flaresLive = array_removeUndefined( vehicle.flaresLive );
|
||||
}
|
||||
|
||||
flares_getFlareLive( vehicle )
|
||||
{
|
||||
flares_cleanFlaresLiveArray( vehicle );
|
||||
|
||||
flare = undefined;
|
||||
if ( vehicle.flaresLive.size > 0 )
|
||||
{
|
||||
flare = vehicle.flaresLive[ vehicle.flaresLive.size - 1 ];
|
||||
}
|
||||
return flare;
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Missile Incoming Logic
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
ks_laserGuidedMissile_handleIncoming() //self == vehicle
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "crashing" );
|
||||
self endon( "leaving" );
|
||||
self endon( "helicopter_done" );
|
||||
|
||||
while ( flares_areAvailable( self ) )
|
||||
{
|
||||
level waittill( "laserGuidedMissiles_incoming", player, missiles, target );
|
||||
|
||||
if ( !IsDefined( target ) || target != self )
|
||||
continue;
|
||||
|
||||
foreach ( missile in missiles )
|
||||
{
|
||||
if ( IsValidMissile( missile ) )
|
||||
{
|
||||
level thread ks_laserGuidedMissile_monitorProximity( missile, player, player.team, target );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ks_laserGuidedMissile_monitorProximity( missile, player, team, target )
|
||||
{
|
||||
target endon( "death" );
|
||||
missile endon( "death" );
|
||||
missile endon( "missile_targetChanged" );
|
||||
|
||||
while ( flares_areAvailable( target ) )
|
||||
{
|
||||
if ( !IsDefined( target ) || !IsValidMissile( missile ) )
|
||||
break;
|
||||
|
||||
center = target GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
if ( DistanceSquared( missile.origin, center ) < 4000000 ) // 2000 * 2000
|
||||
{
|
||||
flare = flares_getFlareLive( target );
|
||||
if ( !IsDefined( flare ) )
|
||||
{
|
||||
flare = flares_getFlareReserve( target );
|
||||
}
|
||||
|
||||
missile Missile_SetTargetEnt( flare );
|
||||
missile notify( "missile_pairedWithFlare" );
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Old Missile Incoming Logic
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
flares_handleIncomingSAM( functionOverride )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "crashing" );
|
||||
self endon ( "leaving" );
|
||||
self endon ( "helicopter_done" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "sam_fired", player, missileGroup, lockTarget );
|
||||
|
||||
if ( !IsDefined( lockTarget ) || ( lockTarget != self ) )
|
||||
continue;
|
||||
|
||||
if( IsDefined( functionOverride ) )
|
||||
level thread [[ functionOverride ]]( player, player.team, lockTarget, missileGroup );
|
||||
else
|
||||
level thread flares_watchSAMProximity( player, player.team, lockTarget, missileGroup );
|
||||
}
|
||||
}
|
||||
|
||||
flares_watchSAMProximity( player, missileTeam, missileTarget, missileGroup ) // self == level
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
missileTarget endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
center = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
curDist = [];
|
||||
for( i = 0; i < missileGroup.size; i++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ i ] ) )
|
||||
curDist[ i ] = distance( missileGroup[ i ].origin, center );
|
||||
}
|
||||
|
||||
for( i = 0; i < curDist.size; i++ )
|
||||
{
|
||||
if( IsDefined( curDist[ i ] ) )
|
||||
{
|
||||
if ( curDist[ i ] < 4000 && missileTarget.flaresReserveCount > 0 )
|
||||
{
|
||||
missileTarget.flaresReserveCount--;
|
||||
|
||||
missileTarget thread flares_playFx();
|
||||
newTarget = missileTarget flares_deploy();
|
||||
for( j = 0; j < missileGroup.size; j++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ j ] ) )
|
||||
{
|
||||
missileGroup[ j ] Missile_SetTargetEnt( newTarget );
|
||||
missileGroup[ j ] notify( "missile_pairedWithFlare" );
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
flares_handleIncomingStinger( functionOverride )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "crashing" );
|
||||
self endon ( "leaving" );
|
||||
self endon ( "helicopter_done" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill ( "stinger_fired", player, missile, lockTarget );
|
||||
|
||||
if ( !IsDefined( lockTarget ) || (lockTarget != self) )
|
||||
continue;
|
||||
|
||||
if( IsDefined( functionOverride ) )
|
||||
missile thread [[ functionOverride ]]( player, player.team, lockTarget );
|
||||
else
|
||||
missile thread flares_watchStingerProximity( player, player.team, lockTarget );
|
||||
}
|
||||
}
|
||||
|
||||
flares_watchStingerProximity( player, missileTeam, missileTarget ) // self == missile
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
if ( !isDefined( missileTarget ) )
|
||||
break;
|
||||
|
||||
center = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
curDist = distance( self.origin, center );
|
||||
|
||||
if ( curDist < 4000 && missileTarget.flaresReserveCount > 0 )
|
||||
{
|
||||
missileTarget.flaresReserveCount--;
|
||||
|
||||
missileTarget thread flares_playFx();
|
||||
newTarget = missileTarget flares_deploy();
|
||||
self Missile_SetTargetEnt( newTarget );
|
||||
self notify( "missile_pairedWithFlare" );
|
||||
return;
|
||||
}
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
// Manual Flares and Missile Incoming Logic
|
||||
// -.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-.-. //
|
||||
|
||||
ks_setup_manual_flares( num_flares, button_action, flares_omnvar_name, incoming_omnvar_name ) // self == vehicle
|
||||
{
|
||||
self.flaresReserveCount = num_flares;
|
||||
self.flaresLive = [];
|
||||
|
||||
if( IsDefined( flares_omnvar_name ) )
|
||||
self.owner SetClientOmnvar( flares_omnvar_name, num_flares );
|
||||
|
||||
self thread ks_manualFlares_watchUse( button_action, flares_omnvar_name );
|
||||
self thread ks_manualFlares_handleIncoming( incoming_omnvar_name );
|
||||
}
|
||||
|
||||
ks_manualFlares_watchUse( button_action, omnvar_name ) // self == vehicle
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "crashing" );
|
||||
self endon( "leaving" );
|
||||
self endon( "helicopter_done" );
|
||||
|
||||
if ( !IsAI(self.owner) ) // Bots handle this internally
|
||||
self.owner NotifyOnPlayerCommand( "manual_flare_popped", button_action );
|
||||
|
||||
while( flares_getNumLeft( self ) )
|
||||
{
|
||||
self.owner waittill( "manual_flare_popped" );
|
||||
|
||||
flare = flares_getFlareReserve( self );
|
||||
if( IsDefined( flare ) && IsDefined( self.owner ) && !IsAI( self.owner ) )
|
||||
{
|
||||
self.owner PlayLocalSound( FLARES_POP_SOUND_PLR );
|
||||
if( IsDefined( omnvar_name ) )
|
||||
self.owner SetClientOmnvar( omnvar_name, self flares_getNumLeft( self ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ks_manualFlares_handleIncoming( omnvar_name ) // self == vehicle
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "crashing" );
|
||||
self endon( "leaving" );
|
||||
self endon( "helicopter_done" );
|
||||
|
||||
while( flares_areAvailable( self ) )
|
||||
{
|
||||
self waittill( "targeted_by_incoming_missile", missiles );
|
||||
|
||||
if( !IsDefined( missiles ) )
|
||||
continue;
|
||||
|
||||
self.owner PlayLocalSound( "missile_incoming" );
|
||||
self.owner thread ks_watch_death_stop_sound( self, "missile_incoming" );
|
||||
|
||||
if( IsDefined( omnvar_name ) )
|
||||
{
|
||||
// 1 for center, 2 right, 3 left
|
||||
// just check the first missile because the others won't matter at this point
|
||||
vec_to_target = VectorNormalize( missiles[ 0 ].origin - self.origin );
|
||||
vec_to_right = VectorNormalize( AnglesToRight( self.angles ) );
|
||||
vec_dot = VectorDot( vec_to_target, vec_to_right );
|
||||
dir_index = 1;
|
||||
if( vec_dot > 0 )
|
||||
dir_index = 2;
|
||||
else if( vec_dot < 0 )
|
||||
dir_index = 3;
|
||||
self.owner SetClientOmnvar( omnvar_name, dir_index );
|
||||
}
|
||||
|
||||
foreach( missile in missiles )
|
||||
{
|
||||
if( IsValidMissile( missile ) )
|
||||
{
|
||||
self thread ks_manualFlares_monitorProximity( missile );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ks_manualFlares_monitorProximity( missile ) // self == vehicle
|
||||
{
|
||||
self endon( "death" );
|
||||
missile endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
if ( !IsDefined( self ) || !IsValidMissile( missile ) )
|
||||
break;
|
||||
|
||||
center = self GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
if( DistanceSquared( missile.origin, center ) < 4000000 ) // 2000 * 2000
|
||||
{
|
||||
flare = flares_getFlareLive( self );
|
||||
if( IsDefined( flare ) )
|
||||
{
|
||||
missile Missile_SetTargetEnt( flare );
|
||||
missile notify( "missile_pairedWithFlare" );
|
||||
self.owner StopLocalSound( "missile_incoming" );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
waitframe();
|
||||
}
|
||||
}
|
||||
|
||||
ks_watch_death_stop_sound( vehicle, sound ) // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
vehicle waittill( "death" );
|
||||
self StopLocalSound( sound );
|
||||
}
|
264
maps/mp/killstreaks/_gas_airstrike.gsc
Normal file
264
maps/mp/killstreaks/_gas_airstrike.gsc
Normal file
@ -0,0 +1,264 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// replacement for EMP
|
||||
// only affects aircraft
|
||||
// remarkably similar to aastrike, should probably get rid of that...
|
||||
KS_NAME = "gas_airstrike";
|
||||
|
||||
init()
|
||||
{
|
||||
precacheLocationSelector( "map_artillery_selector" );
|
||||
|
||||
config = SpawnStruct();
|
||||
config.modelNames = [];
|
||||
config.modelNames[ "allies" ] = "vehicle_mig29_desert";
|
||||
config.modelNames[ "axis" ] = "vehicle_mig29_desert";
|
||||
config.inboundSfx = "veh_mig29_dist_loop";
|
||||
//config.inboundSfx = "veh_aastrike_flyover_loop";
|
||||
//config.outboundSfx = "veh_aastrike_flyover_outgoing_loop";
|
||||
config.compassIconFriendly = "compass_objpoint_airstrike_friendly";
|
||||
config.compassIconEnemy = "compass_objpoint_airstrike_busy";
|
||||
// sonic boom?
|
||||
config.speed = 5000;
|
||||
config.halfDistance = 15000;
|
||||
config.heightRange = 500;
|
||||
//config.attackTime = 2.0;
|
||||
config.outboundFlightAnim = "airstrike_mp_roll";
|
||||
config.onAttackDelegate = ::dropBombs;
|
||||
config.onFlybyCompleteDelegate = ::cleanupFlyby;
|
||||
config.chooseDirection = true;
|
||||
config.selectLocationVO = "KS_hqr_airstrike";
|
||||
config.inboundVO = "KS_ast_inbound";
|
||||
config.bombModel = "projectile_cbu97_clusterbomb";
|
||||
config.numBombs = 3;
|
||||
// should be 2x the effect radius to have no gaps/overlap
|
||||
config.distanceBetweenBombs = 350;
|
||||
config.effectRadius = 200;
|
||||
config.effectHeight = 120;
|
||||
// this should be something else
|
||||
// remember to precache this
|
||||
//config.effectVFX = LoadFX( "fx/smoke/poisonous_gas_linger_medium_thick_killer_pulse");
|
||||
config.effectVFX = LoadFX( "fx/smoke/poisonous_gas_linger_medium_thick_killer_instant");
|
||||
config.effectMinDelay = 0.25;
|
||||
config.effectMaxDelay = 0.5;
|
||||
config.effectLifeSpan = 13;
|
||||
config.effectCheckFrequency = 1.0;
|
||||
config.effectDamage = 10;
|
||||
config.obitWeapon = "gas_strike_mp";
|
||||
config.killCamOffset = (0, 0, 60);
|
||||
|
||||
level.planeConfigs[ KS_NAME ] = config;
|
||||
|
||||
level.killstreakFuncs[KS_NAME] = ::onUse;
|
||||
}
|
||||
|
||||
onUse( lifeId, streakName )
|
||||
{
|
||||
assert( isDefined( self ) );
|
||||
|
||||
// check for active air_superiority strikes
|
||||
otherTeam = getOtherTeam( self.team );
|
||||
|
||||
if ( IsDefined( level.numGasStrikeActive ) )
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
result = maps\mp\killstreaks\_plane::selectAirstrikeLocation( lifeId, KS_NAME, ::doStrike );
|
||||
|
||||
return ( IsDefined( result ) && result );
|
||||
}
|
||||
}
|
||||
|
||||
doStrike( lifeId, location, directionYaw, streakName )
|
||||
{
|
||||
level.numGasStrikeActive = 0;
|
||||
|
||||
wait ( 1 );
|
||||
|
||||
planeFlyHeight = maps\mp\killstreaks\_plane::getPlaneFlyHeight();
|
||||
|
||||
dirVector = AnglesToForward( (0, directionYaw, 0) );
|
||||
|
||||
doOneFlyby( streakName, lifeId, location, dirVector, planeFlyHeight );
|
||||
|
||||
self waittill( "gas_airstrike_flyby_complete" );
|
||||
|
||||
// play outbound vo
|
||||
}
|
||||
|
||||
doOneFlyby( streakName, lifeId, targetPos, dir, flyHeight )
|
||||
{
|
||||
config = level.planeConfigs[ streakName ];
|
||||
|
||||
// absolute height should be derived from the heightEnt
|
||||
flightPath = maps\mp\killstreaks\_plane::getFlightPath( targetPos, dir, config.halfDistance, true, flyHeight, config.speed, 0, streakName );
|
||||
|
||||
// Box( targetPos, dir[1], (0, 0, 1), false, 200);
|
||||
|
||||
// may want to break this up into spawn, move, cleanup components
|
||||
// so that we can reuse the plane
|
||||
level thread maps\mp\killstreaks\_plane::doFlyby( lifeId, self, lifeId,
|
||||
flightPath["startPoint"] + (0, 0, randomInt(config.heightRange) ),
|
||||
flightPath["endPoint"] + (0, 0, randomInt(config.heightRange) ),
|
||||
flightPath["attackTime"],
|
||||
flightPath["flyTime"],
|
||||
dir,
|
||||
streakName );
|
||||
}
|
||||
|
||||
cleanupFlyby( owner, plane, streakName )
|
||||
{
|
||||
owner notify( "gas_airstrike_flyby_complete" );
|
||||
}
|
||||
|
||||
|
||||
dropBombs( pathEnd, flyTime, beginAttackTime, owner, streakName ) // self == plane
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
wait (beginAttackTime);
|
||||
|
||||
config = level.planeConfigs[ streakName ];
|
||||
|
||||
numBombsLeft = config.numBombs;
|
||||
timeBetweenBombs = config.distanceBetweenBombs / config.speed;
|
||||
|
||||
while (numBombsLeft > 0)
|
||||
{
|
||||
self thread dropOneBomb( owner, streakName );
|
||||
|
||||
numBombsLeft--;
|
||||
wait ( timeBetweenBombs );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dropOneBomb( owner, streakName ) // self == plane
|
||||
{
|
||||
level.numGasStrikeActive++;
|
||||
|
||||
plane = self;
|
||||
|
||||
config = level.planeConfigs[ streakName ];
|
||||
|
||||
planeDir = AnglesToForward( plane.angles );
|
||||
|
||||
bomb = spawnBomb( config.bombModel, plane.origin, plane.angles );
|
||||
bomb MoveGravity( ( planeDir * ( config.speed / 1.5 ) ), 3.0 );
|
||||
|
||||
// bomb.lifeId = requiredDeathCount;
|
||||
|
||||
newBomb = Spawn( "script_model", bomb.origin );
|
||||
newBomb SetModel( "tag_origin" );
|
||||
newBomb.origin = bomb.origin;
|
||||
newBomb.angles = bomb.angles;
|
||||
|
||||
bomb SetModel( "tag_origin" );
|
||||
wait (0.10); // wait two server frames before playing fx
|
||||
|
||||
bombOrigin = newBomb.origin;
|
||||
bombAngles = newBomb.angles;
|
||||
if ( level.splitscreen )
|
||||
{
|
||||
playfxontag( level.airstrikessfx, newBomb, "tag_origin" );
|
||||
}
|
||||
else
|
||||
{
|
||||
playfxontag( level.airstrikefx, newBomb, "tag_origin" );
|
||||
}
|
||||
|
||||
wait ( 1.0 );
|
||||
|
||||
trace = BulletTrace(newBomb.origin, newBomb.origin + (0,0,-1000000), false, undefined);
|
||||
impactPosition = trace["position"];
|
||||
|
||||
// Line( newBomb.origin, impactPosition, (1, 0, 0), 1, true, 20 * config.effectLifeSpan);
|
||||
|
||||
// artillery damage center?
|
||||
|
||||
// set up kill cam?
|
||||
|
||||
// need to wait the right amount of time before
|
||||
|
||||
bomb onBombImpact( owner, impactPosition, streakName );
|
||||
|
||||
newBomb delete();
|
||||
bomb delete();
|
||||
|
||||
level.numGasStrikeActive--;
|
||||
if (level.numGasStrikeActive == 0)
|
||||
{
|
||||
level.numGasStrikeActive = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
spawnBomb( modelName, origin, angles )
|
||||
{
|
||||
bomb = Spawn( "script_model", origin );
|
||||
bomb.angles = angles;
|
||||
bomb SetModel( modelname );
|
||||
|
||||
return bomb;
|
||||
}
|
||||
|
||||
onBombImpact( owner, position, streakName ) // self == bomb?
|
||||
{
|
||||
config = level.planeConfigs[ streakName ];
|
||||
|
||||
// position = self.origin;
|
||||
|
||||
effectArea = Spawn( "trigger_radius", position, 0, config.effectRadius, config.effectHeight );
|
||||
effectArea.owner = owner;
|
||||
|
||||
effectRadius = config.effectRadius;
|
||||
vfx = SpawnFx( config.effectVFX, position );
|
||||
TriggerFX( vfx );
|
||||
|
||||
wait ( RandomFloatRange( config.effectMinDelay, config.effectMaxDelay ) );
|
||||
|
||||
timeRemaining = config.effectLifeSpan;
|
||||
|
||||
// self.primaryWeapon = config.obitWeapon;
|
||||
|
||||
killCamEnt = Spawn( "script_model", position + config.killCamOffset );
|
||||
killCamEnt LinkTo( effectArea );
|
||||
self.killCamEnt = killCamEnt;
|
||||
//self.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
|
||||
while ( timeRemaining > 0.0 )
|
||||
{
|
||||
foreach ( character in level.characters )
|
||||
{
|
||||
character applyGasEffect( owner, position, effectArea, self, config.effectDamage );
|
||||
}
|
||||
|
||||
wait ( config.effectCheckFrequency );
|
||||
timeRemaining -= config.effectCheckFrequency;
|
||||
}
|
||||
|
||||
self.killCamEnt Delete();
|
||||
|
||||
effectArea Delete();
|
||||
vfx Delete();
|
||||
}
|
||||
|
||||
applyGasEffect( attacker, position, trigger, inflictor, damage ) // self == target
|
||||
{
|
||||
if( (attacker isEnemy( self )) && IsAlive( self ) && self IsTouching( trigger ) )
|
||||
{
|
||||
// rumble
|
||||
inflictor RadiusDamage( self.origin, 1, damage, damage, attacker, "MOD_RIFLE_BULLET", "gas_strike_mp");
|
||||
// self DoDamage( damage, position, attacker, inflictor, "MOD_UNKNOWN" );
|
||||
|
||||
if ( !(self isUsingRemote()) )
|
||||
{
|
||||
duration = maps\mp\perks\_perkfunctions::applyStunResistence( 2.0 );
|
||||
self ShellShock( "default", duration );
|
||||
}
|
||||
}
|
||||
}
|
1222
maps/mp/killstreaks/_harrier.gsc
Normal file
1222
maps/mp/killstreaks/_harrier.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2352
maps/mp/killstreaks/_helicopter.gsc
Normal file
2352
maps/mp/killstreaks/_helicopter.gsc
Normal file
File diff suppressed because it is too large
Load Diff
481
maps/mp/killstreaks/_helicopter_flock.gsc
Normal file
481
maps/mp/killstreaks/_helicopter_flock.gsc
Normal file
@ -0,0 +1,481 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
precacheVehicle( "attack_littlebird_mp" );
|
||||
|
||||
//precacheString( &"HELICOPTER_FLOCK_INBOUND" );
|
||||
|
||||
precacheModel( "vehicle_apache_mp" );
|
||||
precacheModel( "vehicle_apache_mg" );
|
||||
precacheTurret( "apache_minigun_mp" );
|
||||
precacheVehicle( "apache_strafe_mp" );
|
||||
|
||||
level.killStreakFuncs["littlebird_flock"] = ::tryUseLbFlock;
|
||||
|
||||
level.heli_flock = [];
|
||||
}
|
||||
|
||||
|
||||
tryUseLbFlock( lifeId, streakName )
|
||||
{
|
||||
numIncomingVehicles = 5;
|
||||
if( heliFlockActive() || currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
||||
incrementFauxVehicleCount();
|
||||
incrementFauxVehicleCount();
|
||||
incrementFauxVehicleCount();
|
||||
incrementFauxVehicleCount();
|
||||
incrementFauxVehicleCount();
|
||||
|
||||
result = self selectLbStrikeLocation( lifeId, "littlebird_flock" );
|
||||
if ( ( !isDefined( result ) || !result ) )
|
||||
{
|
||||
// decrement the faux vehicle count since this failed to spawn
|
||||
decrementFauxVehicleCount();
|
||||
decrementFauxVehicleCount();
|
||||
decrementFauxVehicleCount();
|
||||
decrementFauxVehicleCount();
|
||||
decrementFauxVehicleCount();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
level thread teamPlayerCardSplash( "used_littlebird_flock", self, self.team );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
heliFlockActive()
|
||||
{
|
||||
result = false;
|
||||
for ( i=0; i<level.heli_flock.size; i++ )
|
||||
{
|
||||
if ( isDefined( level.heli_flock[i] ) )
|
||||
{
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
selectLbStrikeLocation( lifeId, streakName )
|
||||
{
|
||||
self PlayLocalSound( game[ "voice" ][ self.team ] + "KS_lbd_inposition" );
|
||||
self _beginLocationSelection( streakName, "map_artillery_selector", true, 500 );
|
||||
self endon( "stop_location_selection" );
|
||||
|
||||
// wait for the selection
|
||||
self waittill( "confirm_location", location, locationYaw);
|
||||
|
||||
if( heliFlockActive() || currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount >= maxVehiclesAllowed() )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
||||
self notify( "cancel_location" );
|
||||
return false;
|
||||
}
|
||||
|
||||
level.heli_flock = [];
|
||||
level.heli_flock_victims = [];
|
||||
|
||||
self thread littlebirdMadeSelectionVO();
|
||||
self thread finishLbStrikeUsage( lifeId, location, ::callStrike, locationYaw );
|
||||
|
||||
self setblurforplayer( 0, 0.3 );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
littlebirdMadeSelectionVO()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
self PlayLocalSound( game[ "voice" ][ self.team ] + "KS_hqr_littlebird" );
|
||||
wait( 3.0 );
|
||||
self PlayLocalSound( game[ "voice" ][ self.team ] + "KS_lbd_inbound" );
|
||||
}
|
||||
|
||||
|
||||
finishLbStrikeUsage( lifeId, location, usedCallback, locationYaw )
|
||||
{
|
||||
self notify( "used" );
|
||||
|
||||
wait ( 0.05 );
|
||||
self thread stopLocationSelection( false );
|
||||
|
||||
if ( isDefined( self ) )
|
||||
self thread [[usedCallback]]( lifeId, location, locationYaw );
|
||||
}
|
||||
|
||||
|
||||
callStrike( lifeId, location, locationYaw )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon("disconnect");
|
||||
|
||||
self thread handleOwnerLeft();
|
||||
|
||||
// get flight paths all at once
|
||||
flightPath1 = getFlightPath( location, locationYaw, 0 );
|
||||
flightPath2 = getFlightPath( location, locationYaw, -520 );
|
||||
flightPath3 = getFlightPath( location, locationYaw, 520 );
|
||||
flightPath4 = getFlightPath( location, locationYaw, -1040 );
|
||||
flightPath5 = getFlightPath( location, locationYaw, 1040 );
|
||||
|
||||
// leader
|
||||
level thread doLbStrike( lifeId, self, flightPath1, 0 );
|
||||
|
||||
wait( 0.3 );
|
||||
|
||||
// left wingman
|
||||
level thread doLbStrike( lifeId, self, flightPath2, 1 );
|
||||
|
||||
// right wingman
|
||||
level thread doLbStrike( lifeId, self, flightPath3, 2 );
|
||||
|
||||
wait( 0.3 );
|
||||
|
||||
// left wingman
|
||||
level thread doLbStrike( lifeId, self, flightPath4, 3 );
|
||||
|
||||
// right wingman
|
||||
level thread doLbStrike( lifeId, self, flightPath5, 4 );
|
||||
|
||||
// log it
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "littlebird_flock", location );
|
||||
}
|
||||
|
||||
|
||||
getFlightPath( location, locationYaw, rightOffset )
|
||||
{
|
||||
location = location * (1,1,0);
|
||||
initialDirection = ( 0, locationYaw, 0 );
|
||||
planeHalfDistance = 12000;
|
||||
flightPath = [];
|
||||
|
||||
if ( isDefined( rightOffset ) && rightOffset != 0 )
|
||||
location = location + ( AnglesToRight( initialDirection ) * rightOffset ) + ( 0, 0, RandomInt( 300 ) );
|
||||
|
||||
// start point
|
||||
startPoint = ( location + ( AnglesToForward( initialDirection ) * ( -1 * planeHalfDistance ) ) );
|
||||
|
||||
// end point
|
||||
endPoint = ( location + ( AnglesToForward( initialDirection ) * planeHalfDistance ) );
|
||||
|
||||
// get height
|
||||
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( location ) + 256;
|
||||
flightPath["start"] = startPoint + ( 0, 0, flyHeight );
|
||||
flightPath["end"] = endPoint + ( 0, 0, flyHeight );
|
||||
|
||||
return flightPath;
|
||||
}
|
||||
|
||||
|
||||
doLbStrike( lifeId, owner, flightPath, flockIndex )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( !isDefined( owner ) )
|
||||
return;
|
||||
|
||||
forward = vectorToAngles( flightPath["end"] - flightPath["start"] );
|
||||
lb = spawnAttackLittleBird( owner, flightPath["start"], forward, flockIndex );
|
||||
lb.lifeId = lifeId;
|
||||
lb.alreadyDead = false;
|
||||
|
||||
lb thread watchTimeOut();
|
||||
lb thread watchDeath();
|
||||
lb thread flock_handleDamage();
|
||||
lb thread startLbFiring1();
|
||||
lb thread monitorKills();
|
||||
|
||||
lb endon( "death" );
|
||||
|
||||
// initial target run
|
||||
lb SetMaxPitchRoll( 120, 60 );
|
||||
lb Vehicle_SetSpeed( 48, 48 );
|
||||
lb setVehGoalPos( flightPath["end"], 0 );
|
||||
lb waittill( "goal" );
|
||||
|
||||
// turn
|
||||
lb SetMaxPitchRoll( 30, 40 );
|
||||
lb Vehicle_SetSpeed( 32, 32 );
|
||||
lb setVehGoalPos( flightPath["start"], 0 );
|
||||
wait( 2 );
|
||||
|
||||
// return
|
||||
lb SetMaxPitchRoll( 100, 60 );
|
||||
lb Vehicle_SetSpeed( 64, 64 );
|
||||
lb waittill ( "goal" );
|
||||
lb notify( "gone" );
|
||||
|
||||
// remove
|
||||
lb maps\mp\killstreaks\_helicopter::removeLittlebird();
|
||||
}
|
||||
|
||||
|
||||
spawnAttackLittleBird( owner, origin, forward, flockIndex )
|
||||
{
|
||||
lb = spawnHelicopter( owner, origin, forward, "apache_strafe_mp" , "vehicle_apache_mp" );
|
||||
|
||||
if ( !isDefined( lb ) )
|
||||
return;
|
||||
|
||||
lb maps\mp\killstreaks\_helicopter::addToLittleBirdList();
|
||||
lb thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath();
|
||||
|
||||
lb.health = 999999; // keep it from dying anywhere in code
|
||||
lb.maxHealth = 2000; // this is the health we'll check
|
||||
lb.damageTaken = 0; // how much damage has it taken
|
||||
lb setCanDamage( true );
|
||||
lb.owner = owner;
|
||||
lb.team = owner.team;
|
||||
lb.killCount = 0;
|
||||
lb.streakName = "littlebird_flock";
|
||||
lb.heliType = "littlebird";
|
||||
//lb ThermalDrawEnable();
|
||||
|
||||
lb.specialDamageCallback = ::Callback_VehicleDamage;
|
||||
|
||||
mgTurret1 = spawnTurret( "misc_turret", lb.origin, "apache_minigun_mp" );
|
||||
mgTurret1 linkTo( lb, "tag_turret", (0,0,0), (0,0,0) );
|
||||
mgTurret1 setModel( "vehicle_apache_mg" );
|
||||
mgTurret1.angles = lb.angles;
|
||||
mgTurret1.owner = lb.owner;
|
||||
mgTurret1.team = mgTurret1.owner.team;
|
||||
mgTurret1 makeTurretInoperable();
|
||||
mgTurret1.vehicle = lb;
|
||||
//mgTurret1 ThermalDrawEnable();
|
||||
|
||||
killCamOrigin = ( lb.origin + ( ( AnglesToForward( lb.angles ) * -200 ) + ( AnglesToRight( lb.angles ) * -200 ) ) ) + ( 0, 0, 50 );
|
||||
mgTurret1.killCamEnt = Spawn( "script_model", killCamOrigin );
|
||||
mgTurret1.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
mgTurret1.killCamEnt LinkTo( lb, "tag_origin" );
|
||||
|
||||
lb.mgTurret1 = mgTurret1;
|
||||
lb.mgTurret1 SetDefaultDropPitch( 0 );
|
||||
|
||||
lb.mgTurret1 setMode( "auto_nonai" );
|
||||
|
||||
lb.mgTurret1 SetSentryOwner( lb.owner );
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
lb.mgTurret1 setTurretTeam( lb.owner.team );
|
||||
}
|
||||
|
||||
level.heli_flock[flockIndex] = lb;
|
||||
|
||||
return lb;
|
||||
}
|
||||
|
||||
|
||||
watchTimeOut()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "gone" );
|
||||
self endon( "death" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 60.0 );
|
||||
|
||||
self notify( "death" );
|
||||
}
|
||||
|
||||
monitorKills()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "gone" );
|
||||
self endon( "death" );
|
||||
self endon( "stopFiring" );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "killedPlayer", player );
|
||||
self.killCount++;
|
||||
level.heli_flock_victims[level.heli_flock_victims.size] = player;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
startLbFiring1( )
|
||||
{
|
||||
self endon( "gone" );
|
||||
self endon( "death" );
|
||||
self endon( "stopFiring" );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self.mgTurret1 waittill( "turret_on_target" );
|
||||
|
||||
fireOnTarget = true;
|
||||
targetPlayer = self.mgTurret1 GetTurretTarget( false );
|
||||
foreach ( victim in level.heli_flock_victims )
|
||||
{
|
||||
if ( targetPlayer == victim )
|
||||
{
|
||||
self.mgTurret1 ClearTargetEntity();
|
||||
fireOnTarget = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( fireOnTarget )
|
||||
self.mgTurret1 ShootTurret();
|
||||
}
|
||||
}
|
||||
|
||||
handleOwnerLeft() // self == owner
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "flock_done" );
|
||||
self thread notifyOnFlockDone();
|
||||
|
||||
self waittill( "killstreak_disowned" );
|
||||
|
||||
for ( i=0; i<level.heli_flock.size; i++ )
|
||||
{
|
||||
if ( isDefined( level.heli_flock[i] ) )
|
||||
level.heli_flock[i] notify( "stopFiring" );
|
||||
}
|
||||
|
||||
for ( i=0; i<level.heli_flock.size; i++ )
|
||||
{
|
||||
if ( isDefined( level.heli_flock[i] ) )
|
||||
{
|
||||
level.heli_flock[i] notify( "death" );
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
notifyOnFlockDone() // self == owner
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
if ( !bot_is_fireteam_mode() )
|
||||
{
|
||||
self endon( "joined_team" );
|
||||
self endon( "joined_spectators" );
|
||||
}
|
||||
|
||||
while( heliFlockActive() )
|
||||
wait( 0.5 );
|
||||
|
||||
self notify( "flock_done" );
|
||||
}
|
||||
|
||||
flock_handleDamage() // self == heli
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
if( IsDefined( self.specialDamageCallback ) )
|
||||
self [[self.specialDamageCallback]]( undefined, attacker, damage, iDFlags, meansOfDeath, weapon, point, direction_vec, undefined, undefined, modelName, partName );
|
||||
}
|
||||
}
|
||||
|
||||
Callback_VehicleDamage( inflictor, attacker, damage, iDFlags, meansOfDeath, weapon, point, dir, hitLoc, timeOffset, modelIndex, partName )
|
||||
{
|
||||
if( isDefined( self.alreadyDead ) && self.alreadyDead )
|
||||
return;
|
||||
|
||||
if( !isDefined( attacker ) || attacker == self )
|
||||
return;
|
||||
|
||||
// don't allow people to destroy things on their team if FF is off
|
||||
if( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
return;
|
||||
|
||||
if( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
self.wasDamaged = true;
|
||||
|
||||
modifiedDamage = damage;
|
||||
|
||||
if ( isPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
|
||||
|
||||
if( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
|
||||
{
|
||||
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
|
||||
modifiedDamage += damage * level.armorPiercingMod;
|
||||
}
|
||||
}
|
||||
|
||||
// in case we are shooting from a remote position, like being in the osprey gunner shooting this
|
||||
if( IsDefined( attacker.owner ) && IsPlayer( attacker.owner ) )
|
||||
{
|
||||
attacker.owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
|
||||
}
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
switch( weapon )
|
||||
{
|
||||
case "ac130_105mm_mp":
|
||||
case "ac130_40mm_mp":
|
||||
case "stinger_mp":
|
||||
case "javelin_mp":
|
||||
case "remote_mortar_missile_mp":
|
||||
case "remotemissile_projectile_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
modifiedDamage = self.maxHealth + 1;
|
||||
break;
|
||||
|
||||
case "sam_projectile_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
modifiedDamage = self.maxHealth * 0.25; // takes about 1 burst of sam rockets
|
||||
break;
|
||||
|
||||
case "emp_grenade_mp":
|
||||
self.largeProjectileDamage = false;
|
||||
modifiedDamage = self.maxHealth + 1;
|
||||
break;
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
}
|
||||
|
||||
self.damageTaken += modifiedDamage;
|
||||
|
||||
if( self.damageTaken >= self.maxHealth )
|
||||
{
|
||||
if ( isPlayer( attacker ) && ( !isDefined( self.owner ) || attacker != self.owner ) )
|
||||
{
|
||||
self.alreadyDead = true;
|
||||
attacker notify( "destroyed_helicopter" );
|
||||
attacker notify( "destroyed_killstreak", weapon );
|
||||
thread teamPlayerCardSplash( "callout_destroyed_helicopter", attacker );
|
||||
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300, weapon, meansOfDeath );
|
||||
attacker thread maps\mp\gametypes\_rank::xpEventPopup( "destroyed_helicopter" );
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
||||
}
|
||||
|
||||
self notify ( "death" );
|
||||
}
|
||||
}
|
||||
|
||||
watchDeath()
|
||||
{
|
||||
self endon( "gone" );
|
||||
self waittill( "death" );
|
||||
|
||||
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
|
||||
}
|
834
maps/mp/killstreaks/_helicopter_guard.gsc
Normal file
834
maps/mp/killstreaks/_helicopter_guard.gsc
Normal file
@ -0,0 +1,834 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
EMP_GRENADE_TIME = 3.5;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs[ "littlebird_support" ] = ::tryUseLBSupport;
|
||||
|
||||
level.heliGuardSettings = [];
|
||||
|
||||
level.heliGuardSettings[ "littlebird_support" ] = spawnStruct();
|
||||
level.heliGuardSettings[ "littlebird_support" ].timeOut = 60.0;
|
||||
level.heliGuardSettings[ "littlebird_support" ].health = 999999; // keep it from dying anywhere in code
|
||||
level.heliGuardSettings[ "littlebird_support" ].maxHealth = 2000; // this is what we check against for death
|
||||
level.heliGuardSettings[ "littlebird_support" ].streakName = "littlebird_support";
|
||||
level.heliGuardSettings[ "littlebird_support" ].vehicleInfo = "attack_littlebird_mp";
|
||||
level.heliGuardSettings[ "littlebird_support" ].weaponInfo = "littlebird_guard_minigun_mp";
|
||||
level.heliGuardSettings[ "littlebird_support" ].weaponModelLeft = "vehicle_little_bird_minigun_left";
|
||||
level.heliGuardSettings[ "littlebird_support" ].weaponModelRight = "vehicle_little_bird_minigun_right";
|
||||
level.heliGuardSettings[ "littlebird_support" ].weaponTagLeft = "tag_flash";
|
||||
level.heliGuardSettings[ "littlebird_support" ].weaponTagRight = "tag_flash_2";
|
||||
level.heliGuardSettings[ "littlebird_support" ].sentryMode = "auto_nonai";
|
||||
level.heliGuardSettings[ "littlebird_support" ].modelBase = level.littlebird_model;
|
||||
level.heliGuardSettings[ "littlebird_support" ].teamSplash = "used_littlebird_support";
|
||||
|
||||
lbSupport_setAirStartNodes();
|
||||
lbSupport_setAirNodeMesh();
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_lbguard_timeout", 60.0 );
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
tryUseLBSupport( lifeId, streakName ) // self == player
|
||||
{
|
||||
heliGuardType = "littlebird_support";
|
||||
|
||||
numIncomingVehicles = 1;
|
||||
|
||||
if( IsDefined( level.littlebirdGuard ) || maps\mp\killstreaks\_helicopter::exceededMaxLittlebirds( heliGuardType ) )
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
else if( !level.air_node_mesh.size )
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_UNAVAILABLE_IN_LEVEL" );
|
||||
return false;
|
||||
}
|
||||
else if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
||||
incrementFauxVehicleCount();
|
||||
|
||||
littleBird = createLBGuard( heliGuardType );
|
||||
if ( !IsDefined( littleBird ) )
|
||||
{
|
||||
// decrement the faux vehicle count since this failed to spawn
|
||||
decrementFauxVehicleCount();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self thread startLBSupport( littleBird );
|
||||
|
||||
level thread teamPlayerCardSplash( level.heliGuardSettings[ heliGuardType ].teamSplash, self, self.team );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
createLBGuard( heliGuardType )
|
||||
{
|
||||
closestStartNode = lbSupport_getClosestStartNode( self.origin );
|
||||
if( IsDefined( closestStartNode.angles ) )
|
||||
startAng = closestStartNode.angles;
|
||||
else
|
||||
startAng = ( 0, 0, 0);
|
||||
|
||||
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( self.origin );
|
||||
|
||||
closestNode = lbSupport_getClosestNode( self.origin );
|
||||
|
||||
forward = AnglesToForward( self.angles );
|
||||
targetPos = ( closestNode.origin*(1,1,0) ) + ( (0,0,1)*flyHeight ) + ( forward * -100 );
|
||||
|
||||
startPos = closestStartNode.origin;
|
||||
|
||||
lb = spawnHelicopter( self, startPos, startAng, level.heliGuardSettings[ heliGuardType ].vehicleInfo, level.heliGuardSettings[ heliGuardType ].modelBase );
|
||||
if ( !IsDefined( lb ) )
|
||||
return;
|
||||
|
||||
lb maps\mp\killstreaks\_helicopter::addToLittleBirdList();
|
||||
lb thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath();
|
||||
|
||||
lb.health = level.heliGuardSettings[ heliGuardType ].health;
|
||||
lb.maxHealth = level.heliGuardSettings[ heliGuardType ].maxHealth;
|
||||
lb.damageTaken = 0; // how much damage has it taken
|
||||
|
||||
lb.speed = 100;
|
||||
lb.followSpeed = 40;
|
||||
lb.owner = self;
|
||||
lb SetOtherEnt(self);
|
||||
lb.team = self.team;
|
||||
lb SetMaxPitchRoll( 45, 45 );
|
||||
lb Vehicle_SetSpeed( lb.speed, 100, 40 );
|
||||
lb SetYawSpeed( 120, 60 );
|
||||
lb setneargoalnotifydist( 512 );
|
||||
lb.killCount = 0;
|
||||
lb.heliType = "littlebird";
|
||||
lb.heliGuardType = "littlebird_support";
|
||||
lb.targettingRadius = 2000; // matches the maxRange on the turret gdt setting
|
||||
//lb ThermalDrawEnable();
|
||||
lb make_entity_sentient_mp( lb.team );
|
||||
|
||||
lb.targetPos = targetPos;
|
||||
lb.currentNode = closestNode;
|
||||
|
||||
mgTurret = SpawnTurret( "misc_turret", lb.origin, level.heliGuardSettings[ heliGuardType ].weaponInfo );
|
||||
mgTurret LinkTo( lb, level.heliGuardSettings[ heliGuardType ].weaponTagLeft, (0,0,0), (0,0,0) );
|
||||
mgTurret SetModel( level.heliGuardSettings[ heliGuardType ].weaponModelLeft );
|
||||
mgTurret.angles = lb.angles;
|
||||
mgTurret.owner = lb.owner;
|
||||
mgTurret.team = self.team;
|
||||
mgTurret makeTurretInoperable();
|
||||
mgTurret.vehicle = lb;
|
||||
//mgTurret ThermalDrawEnable();
|
||||
|
||||
lb.mgTurretLeft = mgTurret;
|
||||
lb.mgTurretLeft SetDefaultDropPitch( 0 );
|
||||
|
||||
killCamOrigin = ( lb.origin + ( ( AnglesToForward( lb.angles ) * -100 ) + ( AnglesToRight( lb.angles ) * -100 ) ) ) + ( 0, 0, 50 );
|
||||
mgTurret.killCamEnt = Spawn( "script_model", killCamOrigin );
|
||||
mgTurret.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
mgTurret.killCamEnt LinkTo( lb, "tag_origin" );
|
||||
|
||||
mgTurret = SpawnTurret( "misc_turret", lb.origin, level.heliGuardSettings[ heliGuardType ].weaponInfo );
|
||||
mgTurret LinkTo( lb, level.heliGuardSettings[ heliGuardType ].weaponTagRight, (0,0,0), (0,0,0) );
|
||||
mgTurret SetModel( level.heliGuardSettings[ heliGuardType ].weaponModelRight );
|
||||
mgTurret.angles = lb.angles;
|
||||
mgTurret.owner = lb.owner;
|
||||
mgTurret.team = self.team;
|
||||
mgTurret makeTurretInoperable();
|
||||
mgTurret.vehicle = lb;
|
||||
lb.mgTurretRight = mgTurret;
|
||||
lb.mgTurretRight SetDefaultDropPitch( 0 );
|
||||
|
||||
killCamOrigin = ( lb.origin + ( ( AnglesToForward( lb.angles ) * -100 ) + ( AnglesToRight( lb.angles ) * 100 ) ) ) + ( 0, 0, 50 );
|
||||
mgTurret.killCamEnt = Spawn( "script_model", killCamOrigin );
|
||||
mgTurret.killCamEnt SetScriptMoverKillCam( "explosive" );
|
||||
mgTurret.killCamEnt LinkTo( lb, "tag_origin" );
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
lb.mgTurretLeft setTurretTeam( self.team );
|
||||
lb.mgTurretRight setTurretTeam( self.team );
|
||||
}
|
||||
|
||||
lb.mgTurretLeft SetMode( level.heliGuardSettings[ heliGuardType ].sentryMode );
|
||||
lb.mgTurretRight SetMode( level.heliGuardSettings[ heliGuardType ].sentryMode );
|
||||
|
||||
lb.mgTurretLeft SetSentryOwner( self );
|
||||
lb.mgTurretRight SetSentryOwner( self );
|
||||
|
||||
lb.mgTurretLeft thread lbSupport_attackTargets();
|
||||
lb.mgTurretRight thread lbSupport_attackTargets();
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
lb.attract_strength = 10000;
|
||||
lb.attract_range = 150;
|
||||
lb.attractor = Missile_CreateAttractorEnt( lb, lb.attract_strength, lb.attract_range );
|
||||
|
||||
lb.hasDodged = false;
|
||||
lb.empGrenaded = false;
|
||||
|
||||
lb thread lbSupport_handleDamage();
|
||||
lb thread lbSupport_watchDeath();
|
||||
lb thread lbSupport_watchTimeout();
|
||||
lb thread lbSupport_watchOwnerLoss();
|
||||
lb thread lbSupport_watchOwnerDamage();
|
||||
lb thread lbSupport_watchRoundEnd();
|
||||
lb thread lbSupport_lightFX();
|
||||
|
||||
level.littlebirdGuard = lb;
|
||||
|
||||
lb.owner maps\mp\_matchdata::logKillstreakEvent( level.heliGuardSettings[ lb.heliGuardType ].streakName, lb.targetPos );
|
||||
|
||||
return lb;
|
||||
}
|
||||
|
||||
lbSupport_lightFX()
|
||||
{
|
||||
PlayFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_nose" );
|
||||
wait ( 0.05 );
|
||||
PlayFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
|
||||
wait ( 0.05 );
|
||||
PlayFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail1" );
|
||||
wait ( 0.05 );
|
||||
PlayFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail2" );
|
||||
}
|
||||
|
||||
startLBSupport( littleBird ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
littleBird endon( "death" );
|
||||
|
||||
// look at the player
|
||||
littleBird SetLookAtEnt( self );
|
||||
|
||||
// go to pos
|
||||
littleBird setVehGoalPos( littleBird.targetPos );
|
||||
littleBird waittill( "near_goal" );
|
||||
littleBird Vehicle_SetSpeed( littleBird.speed, 60, 30 );
|
||||
littleBird waittill ( "goal" );
|
||||
|
||||
// drop to target
|
||||
littleBird setVehGoalPos( littleBird.currentNode.origin, 1 );
|
||||
littleBird waittill ( "goal" );
|
||||
|
||||
// begin following player
|
||||
littleBird thread lbSupport_followPlayer();
|
||||
|
||||
// dodge the first sam or lock-on attack
|
||||
littleBird thread maps\mp\killstreaks\_flares::flares_handleIncomingSAM( ::lbSupport_watchSAMProximity );
|
||||
littleBird thread maps\mp\killstreaks\_flares::flares_handleIncomingStinger( ::lbSupport_watchStingerProximity );
|
||||
}
|
||||
|
||||
|
||||
lbSupport_followPlayer() // self == lb
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
if( !IsDefined( self.owner ) )
|
||||
{
|
||||
self thread lbSupport_leave();
|
||||
return;
|
||||
}
|
||||
|
||||
self.owner endon( "disconnect" );
|
||||
self endon( "owner_gone" );
|
||||
|
||||
self Vehicle_SetSpeed( self.followSpeed, 20, 20 );
|
||||
while( true )
|
||||
{
|
||||
if( IsDefined( self.owner ) && IsAlive( self.owner ) )
|
||||
{
|
||||
currentNode = lbSupport_getClosestLinkedNode( self.owner.origin );
|
||||
if( IsDefined( currentNode ) && currentNode != self.currentNode )
|
||||
{
|
||||
self.currentNode = currentNode;
|
||||
// don't thread because we want to waittill goal before we pick the next node
|
||||
lbSupport_moveToPlayer();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
wait( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
lbSupport_moveToPlayer() // self == lb
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
self.owner endon( "death" );
|
||||
self.owner endon( "disconnect" );
|
||||
self endon( "owner_gone" );
|
||||
|
||||
self notify( "lbSupport_moveToPlayer" );
|
||||
self endon( "lbSupport_moveToPlayer" );
|
||||
|
||||
self.inTransit = true;
|
||||
self setVehGoalPos( self.currentNode.origin + ( 0, 0, 100 ), 1 );
|
||||
self waittill ( "goal" );
|
||||
self.inTransit = false;
|
||||
self notify( "hit_goal" );
|
||||
}
|
||||
|
||||
|
||||
//
|
||||
// state trackers
|
||||
//
|
||||
|
||||
|
||||
lbSupport_watchDeath()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "gone" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
|
||||
}
|
||||
|
||||
|
||||
lbSupport_watchTimeout()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self.owner endon( "disconnect" );
|
||||
self endon( "owner_gone" );
|
||||
|
||||
timeout = level.heliGuardSettings[ self.heliGuardType ].timeOut;
|
||||
/#
|
||||
timeout = GetDvarFloat( "scr_lbguard_timeout" );
|
||||
#/
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeout );
|
||||
|
||||
self thread lbSupport_leave();
|
||||
}
|
||||
|
||||
|
||||
lbSupport_watchOwnerLoss()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
self.owner waittill( "killstreak_disowned" );
|
||||
|
||||
self notify( "owner_gone" );
|
||||
|
||||
// leave
|
||||
self thread lbSupport_leave();
|
||||
}
|
||||
|
||||
lbSupport_watchOwnerDamage() // self == lb
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
self.owner endon( "disconnect" );
|
||||
self endon( "owner_gone" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
// if someone is attacking the owner, attack them
|
||||
self.owner waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
if( isPlayer( attacker ) )
|
||||
{
|
||||
if( attacker != self.owner &&
|
||||
Distance2D( attacker.origin, self.origin ) <= self.targettingRadius &&
|
||||
!attacker _hasPerk( "specialty_blindeye" ) &&
|
||||
!( level.hardcoreMode && level.teamBased && attacker.team == self.team ) )
|
||||
{
|
||||
self SetLookAtEnt( attacker );
|
||||
if( IsDefined( self.mgTurretLeft ) )
|
||||
self.mgTurretLeft SetTargetEntity( attacker );
|
||||
if( IsDefined( self.mgTurretRight ) )
|
||||
self.mgTurretRight SetTargetEntity( attacker );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbSupport_watchRoundEnd()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
self.owner endon( "disconnect" );
|
||||
self endon( "owner_gone" );
|
||||
|
||||
level waittill_any( "round_end_finished", "game_ended" );
|
||||
|
||||
// leave
|
||||
self thread lbSupport_leave();
|
||||
}
|
||||
|
||||
lbSupport_leave()
|
||||
{
|
||||
self endon( "death" );
|
||||
self notify( "leaving" );
|
||||
level.littlebirdGuard = undefined;
|
||||
|
||||
self ClearLookAtEnt();
|
||||
|
||||
// rise
|
||||
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( self.origin );
|
||||
targetPos = self.origin + (0,0,flyHeight);
|
||||
self Vehicle_SetSpeed( self.speed, 60 );
|
||||
self SetMaxPitchRoll( 45, 180 );
|
||||
self setVehGoalPos( targetPos );
|
||||
self waittill ( "goal" );
|
||||
|
||||
// leave
|
||||
targetPos = targetPos + AnglesToForward( self.angles ) * 15000;
|
||||
// make sure it doesn't fly away backwards
|
||||
endEnt = Spawn( "script_origin", targetPos );
|
||||
if( IsDefined( endEnt ) )
|
||||
{
|
||||
self SetLookAtEnt( endEnt );
|
||||
endEnt thread wait_and_delete( 3.0 );
|
||||
}
|
||||
self setVehGoalPos( targetPos );
|
||||
self waittill ( "goal" );
|
||||
|
||||
// remove
|
||||
self notify( "gone" );
|
||||
self maps\mp\killstreaks\_helicopter::removeLittlebird();
|
||||
}
|
||||
|
||||
wait_and_delete( waitTime )
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
wait( waitTime );
|
||||
self delete();
|
||||
}
|
||||
|
||||
//
|
||||
// Damage, death, and destruction
|
||||
//
|
||||
|
||||
lbSupport_handleDamage() // self == lb
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self SetCanDamage( true );
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
// don't allow people to destroy things on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
if ( IsDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
self.wasDamaged = true;
|
||||
|
||||
modifiedDamage = damage;
|
||||
|
||||
if( IsPlayer( attacker ) )
|
||||
{
|
||||
// attack the attacker
|
||||
if( attacker != self.owner &&
|
||||
Distance2D( attacker.origin, self.origin ) <= self.targettingRadius &&
|
||||
!attacker _hasPerk( "specialty_blindeye" ) &&
|
||||
!( level.hardcoreMode && level.teamBased && attacker.team == self.team ) )
|
||||
{
|
||||
self SetLookAtEnt( attacker );
|
||||
if( IsDefined( self.mgTurretLeft ) )
|
||||
self.mgTurretLeft SetTargetEntity( attacker );
|
||||
if( IsDefined( self.mgTurretRight ) )
|
||||
self.mgTurretRight SetTargetEntity( attacker );
|
||||
}
|
||||
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
|
||||
|
||||
if( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
|
||||
{
|
||||
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
|
||||
modifiedDamage += damage * level.armorPiercingMod;
|
||||
}
|
||||
}
|
||||
|
||||
// in case we are shooting from a remote position, like being in the osprey gunner shooting this
|
||||
if( IsDefined( attacker.owner ) && IsPlayer( attacker.owner ) )
|
||||
{
|
||||
attacker.owner maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "helicopter" );
|
||||
}
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
switch( weapon )
|
||||
{
|
||||
case "ac130_105mm_mp":
|
||||
case "ac130_40mm_mp":
|
||||
case "stinger_mp":
|
||||
case "javelin_mp":
|
||||
case "remote_mortar_missile_mp":
|
||||
case "remotemissile_projectile_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
modifiedDamage = self.maxHealth + 1;
|
||||
break;
|
||||
|
||||
case "sam_projectile_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
modifiedDamage = self.maxHealth * 0.25; // takes about 1 burst of sam rockets
|
||||
break;
|
||||
|
||||
case "emp_grenade_mp":
|
||||
// NOTE: don't destroy helicopters with an emp grenade, it didn't look good in practice
|
||||
modifiedDamage = 0;
|
||||
self thread lbSupport_EMPGrenaded();
|
||||
break;
|
||||
|
||||
case "osprey_player_minigun_mp":
|
||||
self.largeProjectileDamage = false;
|
||||
modifiedDamage *= 2; // since it's a larger caliber, make it hurt
|
||||
break;
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
}
|
||||
|
||||
self.damageTaken += modifiedDamage;
|
||||
|
||||
if ( self.damageTaken >= self.maxHealth )
|
||||
{
|
||||
if ( isPlayer( attacker ) && ( !IsDefined( self.owner ) || attacker != self.owner ) )
|
||||
{
|
||||
attacker notify( "destroyed_helicopter" );
|
||||
attacker notify( "destroyed_killstreak", weapon );
|
||||
thread teamPlayerCardSplash( "callout_destroyed_little_bird", attacker );
|
||||
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300, weapon, meansOfDeath );
|
||||
attacker thread maps\mp\gametypes\_rank::xpEventPopup( "destroyed_little_bird" );
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
||||
}
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner thread leaderDialogOnPlayer( "lbguard_destroyed" );
|
||||
|
||||
self notify ( "death" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lbSupport_EMPGrenaded() // self == vehicle
|
||||
{
|
||||
self notify( "lbSupport_EMPGrenaded" );
|
||||
self endon( "lbSupport_EMPGrenaded" );
|
||||
|
||||
self endon( "death" );
|
||||
self.owner endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self.empGrenaded = true;
|
||||
if( IsDefined( self.mgTurretRight ) )
|
||||
self.mgTurretRight notify( "stop_shooting" );
|
||||
if( IsDefined( self.mgTurretLeft ) )
|
||||
self.mgTurretLeft notify( "stop_shooting" );
|
||||
// TU0: using the ims fx and not creating its own because we've already locked down ConfigStrings and it will shift them if we load fx in this file
|
||||
if( IsDefined( level._effect[ "ims_sensor_explode" ] ) )
|
||||
{
|
||||
if( IsDefined( self.mgTurretRight ) )
|
||||
PlayFXOnTag( getfx( "ims_sensor_explode" ), self.mgTurretRight, "tag_aim" );
|
||||
if( IsDefined( self.mgTurretLeft ) )
|
||||
PlayFXOnTag( getfx( "ims_sensor_explode" ), self.mgTurretLeft, "tag_aim" );
|
||||
}
|
||||
|
||||
wait( EMP_GRENADE_TIME );
|
||||
|
||||
self.empGrenaded = false;
|
||||
if( IsDefined( self.mgTurretRight ) )
|
||||
self.mgTurretRight notify( "turretstatechange" );
|
||||
if( IsDefined( self.mgTurretLeft ) )
|
||||
self.mgTurretLeft notify( "turretstatechange" );
|
||||
}
|
||||
|
||||
// this is different from the flares monitoring that the other helicopters do
|
||||
// we want this to dodge the missiles if at all possible, making for a cool evasive manuever
|
||||
lbSupport_watchSAMProximity( player, missileTeam, missileTarget, missileGroup ) // self == level
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
missileTarget endon( "death" );
|
||||
|
||||
for( i = 0; i < missileGroup.size; i++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ i ] ) && !missileTarget.hasDodged )
|
||||
{
|
||||
missileTarget.hasDodged = true;
|
||||
|
||||
newTarget = spawn( "script_origin", missileTarget.origin );
|
||||
newTarget.angles = missileTarget.angles;
|
||||
newTarget MoveGravity( AnglesToRight( missileGroup[ i ].angles ) * -1000, 0.05 );
|
||||
newTarget thread maps\mp\killstreaks\_flares::flares_deleteAfterTime( 5.0 );
|
||||
|
||||
for( j = 0; j < missileGroup.size; j++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ j ] ) )
|
||||
{
|
||||
missileGroup[ j ] Missile_SetTargetEnt( newTarget );
|
||||
}
|
||||
}
|
||||
|
||||
// dodge the incoming missiles
|
||||
dodgePoint = missileTarget.origin + ( AnglesToRight( missileGroup[ i ].angles ) * 200 );
|
||||
missileTarget Vehicle_SetSpeed( missileTarget.speed, 100, 40 );
|
||||
missileTarget SetVehGoalPos( dodgePoint, true );
|
||||
|
||||
wait( 2.0 );
|
||||
missileTarget Vehicle_SetSpeed( missileTarget.followSpeed, 20, 20 );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// this is different from the flares monitoring that the other helicopters do
|
||||
// we want this to dodge the missiles if at all possible, making for a cool evasive manuever
|
||||
lbSupport_watchStingerProximity( player, missileTeam, missileTarget ) // self == missile
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
missileTarget endon( "death" );
|
||||
|
||||
if( IsDefined( self ) && !missileTarget.hasDodged )
|
||||
{
|
||||
missileTarget.hasDodged = true;
|
||||
|
||||
newTarget = spawn( "script_origin", missileTarget.origin );
|
||||
newTarget.angles = missileTarget.angles;
|
||||
newTarget MoveGravity( AnglesToRight( self.angles ) * -1000, 0.05 );
|
||||
newTarget thread maps\mp\killstreaks\_flares::flares_deleteAfterTime( 5.0 );
|
||||
|
||||
self Missile_SetTargetEnt( newTarget );
|
||||
|
||||
// dodge the incoming missiles
|
||||
dodgePoint = missileTarget.origin + ( AnglesToRight( self.angles ) * 200 );
|
||||
missileTarget Vehicle_SetSpeed( missileTarget.speed, 100, 40 );
|
||||
missileTarget SetVehGoalPos( dodgePoint, true );
|
||||
|
||||
wait( 2.0 );
|
||||
missileTarget Vehicle_SetSpeed( missileTarget.followSpeed, 20, 20 );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// node funcs
|
||||
//
|
||||
|
||||
lbSupport_getClosestStartNode( pos )
|
||||
{
|
||||
// gets the start node that is closest to the position passed in
|
||||
closestNode = undefined;
|
||||
closestDistance = 999999;
|
||||
|
||||
foreach( loc in level.air_start_nodes )
|
||||
{
|
||||
nodeDistance = distance( loc.origin, pos );
|
||||
if ( nodeDistance < closestDistance )
|
||||
{
|
||||
closestNode = loc;
|
||||
closestDistance = nodeDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return closestNode;
|
||||
}
|
||||
|
||||
lbSupport_getClosestNode( pos )
|
||||
{
|
||||
// gets the closest node to the position passed in, regardless of link
|
||||
closestNode = undefined;
|
||||
closestDistance = 999999;
|
||||
|
||||
foreach( loc in level.air_node_mesh )
|
||||
{
|
||||
nodeDistance = distance( loc.origin, pos );
|
||||
if ( nodeDistance < closestDistance )
|
||||
{
|
||||
closestNode = loc;
|
||||
closestDistance = nodeDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return closestNode;
|
||||
}
|
||||
|
||||
lbSupport_getClosestLinkedNode( pos ) // self == lb
|
||||
{
|
||||
// gets the linked node that is closest to the current position and moving towards the position passed in
|
||||
closestNode = undefined;
|
||||
totalDistance = Distance2D( self.currentNode.origin, pos );
|
||||
closestDistance = totalDistance;
|
||||
|
||||
// loop through each neighbor and find the closest to the final goal
|
||||
foreach( loc in self.currentNode.neighbors )
|
||||
{
|
||||
nodeDistance = Distance2D( loc.origin, pos );
|
||||
if ( nodeDistance < totalDistance && nodeDistance < closestDistance )
|
||||
{
|
||||
closestNode = loc;
|
||||
closestDistance = nodeDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return closestNode;
|
||||
}
|
||||
|
||||
lbSupport_arrayContains( array, compare )
|
||||
{
|
||||
if ( array.size <= 0 )
|
||||
return false;
|
||||
|
||||
foreach ( member in array )
|
||||
{
|
||||
if ( member == compare )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
lbSupport_getLinkedStructs()
|
||||
{
|
||||
array = [];
|
||||
|
||||
if ( IsDefined( self.script_linkTo ) )
|
||||
{
|
||||
linknames = get_links();
|
||||
for ( i = 0; i < linknames.size; i++ )
|
||||
{
|
||||
ent = getstruct( linknames[ i ], "script_linkname" );
|
||||
if ( IsDefined( ent ) )
|
||||
{
|
||||
array[ array.size ] = ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array;
|
||||
}
|
||||
|
||||
lbSupport_setAirStartNodes()
|
||||
{
|
||||
level.air_start_nodes = getstructarray( "chopper_boss_path_start", "targetname" );
|
||||
|
||||
foreach( loc in level.air_start_nodes )
|
||||
{
|
||||
// Grab array of path loc refs that this loc links to
|
||||
loc.neighbors = loc lbSupport_getLinkedStructs();
|
||||
}
|
||||
}
|
||||
|
||||
lbSupport_setAirNodeMesh()
|
||||
{
|
||||
level.air_node_mesh = getstructarray( "so_chopper_boss_path_struct", "script_noteworthy" );
|
||||
|
||||
foreach( loc in level.air_node_mesh )
|
||||
{
|
||||
// Grab array of path loc refs that this loc links to
|
||||
loc.neighbors = loc lbSupport_getLinkedStructs();
|
||||
|
||||
// Step through each loc in the map and if it
|
||||
// links to this loc, add it
|
||||
foreach( other_loc in level.air_node_mesh )
|
||||
{
|
||||
if ( loc == other_loc )
|
||||
continue;
|
||||
|
||||
if ( !lbSupport_arrayContains( loc.neighbors, other_loc ) && lbSupport_arrayContains( other_loc lbSupport_getLinkedStructs(), loc ) )
|
||||
loc.neighbors[ loc.neighbors.size ] = other_loc;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================
|
||||
Turret Logic Functions
|
||||
============================ */
|
||||
|
||||
lbSupport_attackTargets() // self == turret
|
||||
{
|
||||
self.vehicle endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill( "turretstatechange" );
|
||||
|
||||
if ( self IsFiringTurret() && !self.vehicle.empGrenaded )
|
||||
self thread lbSupport_burstFireStart();
|
||||
else
|
||||
self thread lbSupport_burstFireStop();
|
||||
}
|
||||
}
|
||||
|
||||
lbSupport_burstFireStart() // self == turret
|
||||
{
|
||||
self.vehicle endon( "death" );
|
||||
self.vehicle endon( "leaving" );
|
||||
self endon( "stop_shooting" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
fireTime = 0.1;
|
||||
minShots = 40;
|
||||
maxShots = 80;
|
||||
minPause = 1.0;
|
||||
maxPause = 2.0;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
numShots = RandomIntRange( minShots, maxShots + 1 );
|
||||
|
||||
for ( i = 0; i < numShots; i++ )
|
||||
{
|
||||
targetEnt = self GetTurretTarget( false );
|
||||
if( IsDefined( targetEnt ) &&
|
||||
( !IsDefined( targetEnt.spawntime ) || ( gettime() - targetEnt.spawntime )/1000 > 5 ) &&
|
||||
( IsDefined( targetEnt.team ) && targetEnt.team != "spectator" ) &&
|
||||
isReallyAlive( targetEnt ) )
|
||||
{
|
||||
self.vehicle SetLookAtEnt( targetEnt );
|
||||
self ShootTurret();
|
||||
}
|
||||
|
||||
wait ( fireTime );
|
||||
}
|
||||
|
||||
wait ( RandomFloatRange( minPause, maxPause ) );
|
||||
}
|
||||
}
|
||||
|
||||
lbSupport_burstFireStop() // self == turret
|
||||
{
|
||||
self notify( "stop_shooting" );
|
||||
if( IsDefined( self.vehicle.owner ) )
|
||||
self.vehicle SetLookAtEnt( self.vehicle.owner );
|
||||
}
|
||||
|
||||
/* ============================
|
||||
END Turret Logic Functions
|
||||
============================ */
|
634
maps/mp/killstreaks/_helicopter_pilot.gsc
Normal file
634
maps/mp/killstreaks/_helicopter_pilot.gsc
Normal file
@ -0,0 +1,634 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs[ "heli_pilot" ] = ::tryUseHeliPilot;
|
||||
|
||||
level.heli_pilot = [];
|
||||
|
||||
level.heliPilotSettings = [];
|
||||
|
||||
level.heliPilotSettings[ "heli_pilot" ] = SpawnStruct();
|
||||
level.heliPilotSettings[ "heli_pilot" ].timeOut = 60.0;
|
||||
level.heliPilotSettings[ "heli_pilot" ].maxHealth = 2000; // this is what we check against for death
|
||||
level.heliPilotSettings[ "heli_pilot" ].streakName = "heli_pilot";
|
||||
level.heliPilotSettings[ "heli_pilot" ].vehicleInfo = "heli_pilot_mp";
|
||||
level.heliPilotSettings[ "heli_pilot" ].modelBase = level.littlebird_model;
|
||||
level.heliPilotSettings[ "heli_pilot" ].teamSplash = "used_heli_pilot";
|
||||
|
||||
heliPilot_setAirStartNodes();
|
||||
|
||||
// throw the mesh way up into the air, the gdt entry for the vehicle must match
|
||||
level.heli_pilot_mesh = GetEnt( "heli_pilot_mesh", "targetname" );
|
||||
if( !IsDefined( level.heli_pilot_mesh ) )
|
||||
PrintLn( "heli_pilot_mesh doesn't exist in this level: " + level.script );
|
||||
else
|
||||
level.heli_pilot_mesh.origin += getHeliPilotMeshOffset();
|
||||
|
||||
config = SpawnStruct();
|
||||
config.xpPopup = "destroyed_helo_pilot";
|
||||
// !!! NEED VO
|
||||
config.voDestroyed = undefined;
|
||||
config.callout = "callout_destroyed_helo_pilot";
|
||||
config.samDamageScale = 0.09;
|
||||
config.engineVFXtag = "tag_engine_right";
|
||||
// xpval = 200
|
||||
level.heliConfigs[ "heli_pilot" ] = config;
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_helipilot_timeout", 60.0 );
|
||||
#/
|
||||
}
|
||||
|
||||
tryUseHeliPilot( lifeId, streakName )
|
||||
{
|
||||
heliPilotType = "heli_pilot";
|
||||
|
||||
numIncomingVehicles = 1;
|
||||
|
||||
if ( IsDefined( self.underWater ) && self.underWater )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if( exceededMaxHeliPilots( self.team ) )
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
else if( currentActiveVehicleCount() >= maxVehiclesAllowed() || level.fauxVehicleCount + numIncomingVehicles >= maxVehiclesAllowed() )
|
||||
{
|
||||
self IPrintLnBold( &"KILLSTREAKS_TOO_MANY_VEHICLES" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// increment the faux vehicle count before we spawn the vehicle so no other vehicles try to spawn
|
||||
incrementFauxVehicleCount();
|
||||
|
||||
heli = createHeliPilot( heliPilotType );
|
||||
|
||||
if( !IsDefined( heli ) )
|
||||
{
|
||||
// decrement the faux vehicle count since this failed to spawn
|
||||
decrementFauxVehicleCount();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
level.heli_pilot[ self.team ] = heli;
|
||||
|
||||
result = self startHeliPilot( heli );
|
||||
|
||||
if( !IsDefined( result ) )
|
||||
result = false;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
exceededMaxHeliPilots( team )
|
||||
{
|
||||
if ( level.gameType == "dm" )
|
||||
{
|
||||
if ( IsDefined( level.heli_pilot[ team ] ) || IsDefined( level.heli_pilot[ level.otherTeam[ team ] ] ) )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( IsDefined( level.heli_pilot[ team ] ) )
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
//============================================
|
||||
// watchHostMigrationFinishedInit
|
||||
//============================================
|
||||
watchHostMigrationFinishedInit( player )
|
||||
{
|
||||
player endon( "killstreak_disowned" );
|
||||
player endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
self endon ( "death" );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
level waittill( "host_migration_end" );
|
||||
|
||||
player SetClientOmnvar( "ui_heli_pilot", 1 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
createHeliPilot( heliPilotType )
|
||||
{
|
||||
closestStartNode = heliPilot_getClosestStartNode( self.origin );
|
||||
closestNode = heliPilot_getLinkedStruct( closestStartNode );
|
||||
startAng = VectorToAngles( closestNode.origin - closestStartNode.origin );
|
||||
|
||||
forward = AnglesToForward( self.angles );
|
||||
targetPos = closestNode.origin + ( forward * -100 );
|
||||
|
||||
startPos = closestStartNode.origin;
|
||||
|
||||
heli = SpawnHelicopter( self, startPos, startAng, level.heliPilotSettings[ heliPilotType ].vehicleInfo, level.heliPilotSettings[ heliPilotType ].modelBase );
|
||||
if( !IsDefined( heli ) )
|
||||
return;
|
||||
|
||||
// radius and offset should match vehHelicopterBoundsRadius (GDT) and bg_vehicle_sphere_bounds_offset_z.
|
||||
heli MakeVehicleSolidCapsule( 18, -9, 18 );
|
||||
|
||||
heli maps\mp\killstreaks\_helicopter::addToLittleBirdList();
|
||||
heli thread maps\mp\killstreaks\_helicopter::removeFromLittleBirdListOnDeath();
|
||||
|
||||
heli.maxHealth = level.heliPilotSettings[ heliPilotType ].maxHealth;
|
||||
|
||||
heli.speed = 40;
|
||||
heli.owner = self;
|
||||
heli SetOtherEnt(self);
|
||||
heli.team = self.team;
|
||||
heli.heliType = "littlebird";
|
||||
heli.heliPilotType = "heli_pilot";
|
||||
heli SetMaxPitchRoll( 45, 45 );
|
||||
heli Vehicle_SetSpeed( heli.speed, 40, 40 );
|
||||
heli SetYawSpeed( 120, 60 );
|
||||
heli SetNearGoalNotifyDist( 32 );
|
||||
heli SetHoverParams( 100, 100, 100 );
|
||||
heli make_entity_sentient_mp( heli.team );
|
||||
|
||||
heli.targetPos = targetPos;
|
||||
heli.currentNode = closestNode;
|
||||
|
||||
heli.attract_strength = 10000;
|
||||
heli.attract_range = 150;
|
||||
heli.attractor = Missile_CreateAttractorEnt( heli, heli.attract_strength, heli.attract_range );
|
||||
|
||||
// heli thread heliPilot_handleDamage(); // since the model is what players will be shooting at, it should handle the damage
|
||||
heli thread maps\mp\killstreaks\_helicopter::heli_damage_monitor( "heli_pilot" );
|
||||
heli thread heliPilot_lightFX();
|
||||
heli thread heliPilot_watchTimeout();
|
||||
heli thread heliPilot_watchOwnerLoss();
|
||||
heli thread heliPilot_watchRoundEnd();
|
||||
heli thread heliPilot_watchObjectiveCam();
|
||||
heli thread heliPilot_watchDeath();
|
||||
heli thread watchHostMigrationFinishedInit( self );
|
||||
|
||||
heli.owner maps\mp\_matchdata::logKillstreakEvent( level.heliPilotSettings[ heli.heliPilotType ].streakName, heli.targetPos );
|
||||
|
||||
return heli;
|
||||
}
|
||||
|
||||
heliPilot_lightFX()
|
||||
{
|
||||
PlayFXOnTag( level.chopper_fx["light"]["left"], self, "tag_light_nose" );
|
||||
wait ( 0.05 );
|
||||
PlayFXOnTag( level.chopper_fx["light"]["belly"], self, "tag_light_belly" );
|
||||
wait ( 0.05 );
|
||||
PlayFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail1" );
|
||||
wait ( 0.05 );
|
||||
PlayFXOnTag( level.chopper_fx["light"]["tail"], self, "tag_light_tail2" );
|
||||
}
|
||||
|
||||
startHeliPilot( heli ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
heli endon( "death" );
|
||||
|
||||
self setUsingRemote( heli.heliPilotType );
|
||||
|
||||
if( GetDvarInt( "camera_thirdPerson" ) )
|
||||
self setThirdPersonDOF( false );
|
||||
|
||||
self.restoreAngles = self.angles;
|
||||
|
||||
heli thread maps\mp\killstreaks\_flares::ks_setup_manual_flares( 2, "+smoke", "ui_heli_pilot_flare_ammo", "ui_heli_pilot_warn" );
|
||||
|
||||
self thread watchIntroCleared( heli );
|
||||
|
||||
self freezeControlsWrapper( true );
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( heli.heliPilotType );
|
||||
if( result != "success" )
|
||||
{
|
||||
if( IsDefined( self.disabledWeapon ) && self.disabledWeapon )
|
||||
self _enableWeapon();
|
||||
heli notify( "death" );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self freezeControlsWrapper( false );
|
||||
/*
|
||||
// need to link the player here but not give control yet
|
||||
self CameraLinkTo( heli, "tag_player" );
|
||||
|
||||
// go to pos
|
||||
heli SetVehGoalPos( heli.targetPos );
|
||||
heli waittill( "near_goal" );
|
||||
heli Vehicle_SetSpeed( heli.speed, 60, 30 );
|
||||
heli waittill( "goal" );
|
||||
*/
|
||||
|
||||
|
||||
// make sure we go to the mesh as we enter the map
|
||||
traceOffset = getHeliPilotTraceOffset();
|
||||
traceStart = ( heli.currentNode.origin ) + ( getHeliPilotMeshOffset() + traceOffset );
|
||||
traceEnd = ( heli.currentNode.origin ) + ( getHeliPilotMeshOffset() - traceOffset );
|
||||
traceResult = BulletTrace( traceStart, traceEnd, false, undefined, false, false, true );
|
||||
if( !IsDefined( traceResult["entity"] ) )
|
||||
{
|
||||
/#
|
||||
// draw where it thinks this is breaking down
|
||||
self thread drawSphere( traceResult[ "position" ] - getHeliPilotMeshOffset(), 32, 10000, ( 1, 0, 0 ) );
|
||||
self thread drawSphere( heli.currentNode.origin, 16, 10000, ( 0, 1, 0 ) );
|
||||
self thread drawLine( traceStart - getHeliPilotMeshOffset(), traceEnd - getHeliPilotMeshOffset(), 10000, ( 0, 0, 1 ) );
|
||||
#/
|
||||
AssertMsg( "The trace didn't hit the heli_pilot_mesh. Please grab an MP scripter." );
|
||||
}
|
||||
|
||||
targetOrigin = ( traceResult[ "position" ] - getHeliPilotMeshOffset() ) + ( 0, 0, 250 ); // offset to make sure we're on top of the mesh
|
||||
targetNode = Spawn( "script_origin", targetOrigin );
|
||||
|
||||
// link the heli into the mesh and give them control
|
||||
self RemoteControlVehicle( heli );
|
||||
|
||||
heli thread heliGoToStartPosition( targetNode );
|
||||
heli thread heliPilot_watchADS();
|
||||
|
||||
level thread teamPlayerCardSplash( level.heliPilotSettings[ heli.heliPilotType ].teamSplash, self );
|
||||
|
||||
heli.killCamEnt = Spawn( "script_origin", self GetViewOrigin() );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
heliGoToStartPosition( targetNode ) // self == heli
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self RemoteControlVehicleTarget( targetNode );
|
||||
self waittill( "goal_reached" );
|
||||
self RemoteControlVehicleTargetOff();
|
||||
|
||||
targetNode delete();
|
||||
}
|
||||
|
||||
watchIntroCleared( heli ) // self == player
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "joined_team" );
|
||||
self endon( "joined_spectators" );
|
||||
level endon( "game_ended" );
|
||||
heli endon( "death" );
|
||||
|
||||
self waittill( "intro_cleared" );
|
||||
self SetClientOmnvar( "ui_heli_pilot", 1 );
|
||||
|
||||
// highlight the owner
|
||||
id = outlineEnableForPlayer( self, "cyan", self, false, "killstreak" );
|
||||
self removeOutline( id, heli );
|
||||
|
||||
// highlight enemies
|
||||
foreach( player in level.participants )
|
||||
{
|
||||
if( !isReallyAlive( player ) || player.sessionstate != "playing" )
|
||||
continue;
|
||||
|
||||
if( self isEnemy( player ) )
|
||||
{
|
||||
if( !player _hasPerk( "specialty_noplayertarget" ) )
|
||||
{
|
||||
id = outlineEnableForPlayer( player, "orange", self, false, "killstreak" );
|
||||
player removeOutline( id, heli );
|
||||
}
|
||||
else
|
||||
{
|
||||
player thread watchForPerkRemoval( heli );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// watch for enemies spawning while the pilot is up
|
||||
heli thread watchPlayersSpawning();
|
||||
|
||||
// do this here to make sure we are in the killstreak before letting them leave, it was causing a bug where the player could get stuck on a black screen
|
||||
self thread watchEarlyExit( heli );
|
||||
}
|
||||
|
||||
watchForPerkRemoval( heli ) // self == enemy player
|
||||
{
|
||||
self notify( "watchForPerkRemoval" );
|
||||
self endon( "watchForPerkRemoval" );
|
||||
|
||||
self endon( "death" );
|
||||
|
||||
// since we give blindeye and noplayetarget each time a player spawns, we need to wait for it to be removed to turn the outline on
|
||||
self waittill( "removed_specialty_noplayertarget" );
|
||||
id = outlineEnableForPlayer( self, "orange", heli.owner, false, "killstreak" );
|
||||
self removeOutline( id, heli );
|
||||
}
|
||||
|
||||
watchPlayersSpawning() // self == heli
|
||||
{
|
||||
self endon( "leaving" );
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill( "player_spawned", player );
|
||||
if( player.sessionstate == "playing" && self.owner isEnemy( player ) )
|
||||
player thread watchForPerkRemoval( self );
|
||||
}
|
||||
}
|
||||
|
||||
removeOutline( id, heli ) // self == player
|
||||
{
|
||||
self thread heliRemoveOutline( id, heli );
|
||||
self thread playerRemoveOutline( id, heli );
|
||||
}
|
||||
|
||||
heliRemoveOutline( id, heli ) // self == player
|
||||
{
|
||||
self notify( "heliRemoveOutline" );
|
||||
self endon( "heliRemoveOutline" );
|
||||
|
||||
self endon( "outline_removed" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
wait_array = [ "leaving", "death" ];
|
||||
heli waittill_any_in_array_return_no_endon_death( wait_array );
|
||||
|
||||
if( IsDefined( self ) )
|
||||
{
|
||||
outlineDisable( id, self );
|
||||
self notify( "outline_removed" );
|
||||
}
|
||||
}
|
||||
|
||||
playerRemoveOutline( id, heli ) // self == player
|
||||
{
|
||||
self notify( "playerRemoveOutline" );
|
||||
self endon( "playerRemoveOutline" );
|
||||
|
||||
self endon( "outline_removed" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
wait_array = [ "death" ];
|
||||
self waittill_any_in_array_return_no_endon_death( wait_array );
|
||||
|
||||
outlineDisable( id, self );
|
||||
self notify( "outline_removed" );
|
||||
}
|
||||
|
||||
//
|
||||
// state trackers
|
||||
//
|
||||
|
||||
heliPilot_watchDeath()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "gone" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner heliPilot_EndRide( self );
|
||||
|
||||
if( IsDefined( self.killCamEnt ) )
|
||||
self.killCamEnt delete();
|
||||
|
||||
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
|
||||
}
|
||||
|
||||
|
||||
heliPilot_watchObjectiveCam()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "gone" );
|
||||
self.owner endon( "disconnect" );
|
||||
self.owner endon( "joined_team" );
|
||||
self.owner endon( "joined_spectators" );
|
||||
|
||||
level waittill( "objective_cam" );
|
||||
|
||||
self thread maps\mp\killstreaks\_helicopter::lbOnKilled();
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner heliPilot_EndRide( self );
|
||||
}
|
||||
|
||||
|
||||
heliPilot_watchTimeout()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self.owner endon( "disconnect" );
|
||||
self.owner endon( "joined_team" );
|
||||
self.owner endon( "joined_spectators" );
|
||||
|
||||
timeout = level.heliPilotSettings[ self.heliPilotType ].timeOut;
|
||||
/#
|
||||
timeout = GetDvarFloat( "scr_helipilot_timeout" );
|
||||
#/
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeout );
|
||||
|
||||
self thread heliPilot_leave();
|
||||
}
|
||||
|
||||
|
||||
heliPilot_watchOwnerLoss()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
self.owner waittill_any( "disconnect", "joined_team", "joined_spectators" );
|
||||
|
||||
// leave
|
||||
self thread heliPilot_leave();
|
||||
}
|
||||
|
||||
heliPilot_watchRoundEnd()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
self.owner endon( "disconnect" );
|
||||
self.owner endon( "joined_team" );
|
||||
self.owner endon( "joined_spectators" );
|
||||
|
||||
level waittill_any( "round_end_finished", "game_ended" );
|
||||
|
||||
// leave
|
||||
self thread heliPilot_leave();
|
||||
}
|
||||
|
||||
heliPilot_leave()
|
||||
{
|
||||
self endon( "death" );
|
||||
self notify( "leaving" );
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner heliPilot_EndRide( self );
|
||||
|
||||
// rise
|
||||
flyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( self.origin );
|
||||
targetPos = self.origin + ( 0, 0, flyHeight );
|
||||
self Vehicle_SetSpeed( 140, 60 );
|
||||
self SetMaxPitchRoll( 45, 180 );
|
||||
self SetVehGoalPos( targetPos );
|
||||
self waittill( "goal" );
|
||||
|
||||
// leave
|
||||
targetPos = targetPos + AnglesToForward( self.angles ) * 15000;
|
||||
// make sure it doesn't fly away backwards
|
||||
endEnt = Spawn( "script_origin", targetPos );
|
||||
if( IsDefined( endEnt ) )
|
||||
{
|
||||
self SetLookAtEnt( endEnt );
|
||||
endEnt thread wait_and_delete( 3.0 );
|
||||
}
|
||||
self SetVehGoalPos( targetPos );
|
||||
self waittill( "goal" );
|
||||
|
||||
// remove
|
||||
self notify( "gone" );
|
||||
self maps\mp\killstreaks\_helicopter::removeLittlebird();
|
||||
}
|
||||
|
||||
wait_and_delete( waitTime )
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
wait( waitTime );
|
||||
self delete();
|
||||
}
|
||||
|
||||
heliPilot_EndRide( heli )
|
||||
{
|
||||
if( IsDefined( heli ) )
|
||||
{
|
||||
self SetClientOmnvar( "ui_heli_pilot", 0 );
|
||||
|
||||
heli notify( "end_remote" );
|
||||
|
||||
if( self isUsingRemote() )
|
||||
self clearUsingRemote();
|
||||
|
||||
if( GetDvarInt( "camera_thirdPerson" ) )
|
||||
self setThirdPersonDOF( true );
|
||||
|
||||
self RemoteControlVehicleOff( heli );
|
||||
|
||||
self SetPlayerAngles( self.restoreAngles );
|
||||
|
||||
self thread heliPilot_FreezeBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
heliPilot_FreezeBuffer()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
self freezeControlsWrapper( true );
|
||||
wait( 0.5 );
|
||||
self freezeControlsWrapper( false );
|
||||
}
|
||||
|
||||
heliPilot_watchADS() // self == heli
|
||||
{
|
||||
self endon( "leaving" );
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
already_set = false;
|
||||
while( true )
|
||||
{
|
||||
if( IsDefined( self.owner ) )
|
||||
{
|
||||
if( self.owner AdsButtonPressed() )
|
||||
{
|
||||
if( !already_set )
|
||||
{
|
||||
self.owner SetClientOmnvar( "ui_heli_pilot", 2 );
|
||||
already_set = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if( already_set )
|
||||
{
|
||||
self.owner SetClientOmnvar( "ui_heli_pilot", 1 );
|
||||
already_set = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait( 0.1 );
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// node funcs
|
||||
//
|
||||
|
||||
heliPilot_setAirStartNodes()
|
||||
{
|
||||
level.air_start_nodes = getstructarray( "chopper_boss_path_start", "targetname" );
|
||||
}
|
||||
|
||||
heliPilot_getLinkedStruct( struct )
|
||||
{
|
||||
if( IsDefined( struct.script_linkTo ) )
|
||||
{
|
||||
linknames = struct get_links();
|
||||
for( i = 0; i < linknames.size; i++ )
|
||||
{
|
||||
ent = getstruct( linknames[ i ], "script_linkname" );
|
||||
if( IsDefined( ent ) )
|
||||
{
|
||||
return ent;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
heliPilot_getClosestStartNode( pos )
|
||||
{
|
||||
// gets the start node that is closest to the position passed in
|
||||
closestNode = undefined;
|
||||
closestDistance = 999999;
|
||||
|
||||
foreach( loc in level.air_start_nodes )
|
||||
{
|
||||
nodeDistance = Distance( loc.origin, pos );
|
||||
if ( nodeDistance < closestDistance )
|
||||
{
|
||||
closestNode = loc;
|
||||
closestDistance = nodeDistance;
|
||||
}
|
||||
}
|
||||
|
||||
return closestNode;
|
||||
}
|
||||
|
||||
|
||||
watchEarlyExit( heli ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
heli endon( "death" );
|
||||
self endon ("leaving");
|
||||
|
||||
heli thread maps\mp\killstreaks\_killstreaks::allowRideKillstreakPlayerExit();
|
||||
|
||||
heli waittill("killstreakExit");
|
||||
|
||||
heli thread heliPilot_leave();
|
||||
}
|
1088
maps/mp/killstreaks/_helisniper.gsc
Normal file
1088
maps/mp/killstreaks/_helisniper.gsc
Normal file
File diff suppressed because it is too large
Load Diff
155
maps/mp/killstreaks/_highvaluetarget.gsc
Normal file
155
maps/mp/killstreaks/_highvaluetarget.gsc
Normal file
@ -0,0 +1,155 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_hostmigration;
|
||||
|
||||
//============================================
|
||||
// constants
|
||||
//============================================
|
||||
CONST_XP_INCREASE = 0.5;
|
||||
CONST_MIN_MULTIPLIER = 1;
|
||||
CONST_MAX_MULTIPLIER = 4; // shouldn't hit this, but just to be safe.
|
||||
|
||||
CONST_MAX_ACTIVE_HVT_PER_GAME = 4;
|
||||
CONST_MAX_ACTIVE_HVT_PER_PLAYER = 2;
|
||||
|
||||
CONST_HVT_TIMEOUT = 10;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs["high_value_target"] = ::tryUseHighValueTarget;
|
||||
|
||||
level.hvts_active[ "axis" ] = 0;
|
||||
level.hvts_active[ "allies" ] = 0;
|
||||
|
||||
game["dialog"]["hvt_gone"] = "hvt_gone";
|
||||
}
|
||||
|
||||
tryUseHighValueTarget( lifeId, streakName )
|
||||
{
|
||||
return useHighValueTarget( self, lifeId );
|
||||
}
|
||||
|
||||
reached_max_xp_multiplier() // self == player
|
||||
{
|
||||
if( level.teamBased )
|
||||
return ( level.hvts_active[ self.team ] >= CONST_MAX_ACTIVE_HVT_PER_GAME );
|
||||
else if( IsDefined( self.hvts_active ) )
|
||||
return ( self.hvts_active >= CONST_MAX_ACTIVE_HVT_PER_PLAYER );
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
useHighValueTarget( player, lifeId )
|
||||
{
|
||||
if( !isReallyAlive( player ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if( player.team == "spectator" )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// limit the number of active hvts allowed per game
|
||||
if( reached_max_xp_multiplier()
|
||||
// limit the number of active hvts allowed per player
|
||||
|| ( IsDefined( player.hvts_active ) && player.hvts_active >= CONST_MAX_ACTIVE_HVT_PER_PLAYER )
|
||||
)
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_HVT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
player thread setHighValueTarget();
|
||||
|
||||
level thread teamPlayerCardSplash( "used_hvt", player, player.team );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
setHighValueTarget() // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
team = self.team;
|
||||
|
||||
self increaseXPBoost();
|
||||
self thread watchHVTOwner( team );
|
||||
|
||||
// wait for timeout
|
||||
waitLongDurationWithHostMigrationPause( CONST_HVT_TIMEOUT );
|
||||
|
||||
if( level.teamBased )
|
||||
leaderDialog( "hvt_gone", team );
|
||||
else
|
||||
self leaderDialogOnPlayer( "hvt_gone" );
|
||||
|
||||
if( level.teamBased )
|
||||
level decreaseXPBoost( team );
|
||||
else
|
||||
self decreaseXPBoost();
|
||||
}
|
||||
|
||||
increaseXPBoost() // self == player
|
||||
{
|
||||
hvts_active = 0;
|
||||
if( level.teamBased )
|
||||
{
|
||||
level.hvts_active[ self.team ]++;
|
||||
hvts_active = level.hvts_active[ self.team ];
|
||||
array_index = self.team;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !IsDefined( self.hvts_active ) )
|
||||
self.hvts_active = 1;
|
||||
else
|
||||
self.hvts_active++;
|
||||
|
||||
hvts_active = self.hvts_active;
|
||||
array_index = self GetEntityNumber();
|
||||
}
|
||||
|
||||
bonus = CONST_MIN_MULTIPLIER + hvts_active * CONST_XP_INCREASE;
|
||||
level.teamXPScale[ array_index ] = clamp( bonus, CONST_MIN_MULTIPLIER, CONST_MAX_MULTIPLIER );
|
||||
}
|
||||
|
||||
decreaseXPBoost( team ) // self == level or player
|
||||
{
|
||||
hvts_active = 0;
|
||||
if( level.teamBased )
|
||||
{
|
||||
if( level.hvts_active[ team ] > 0 )
|
||||
level.hvts_active[ team ]--;
|
||||
hvts_active = level.hvts_active[ team ];
|
||||
array_index = team;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( self.hvts_active > 0 )
|
||||
self.hvts_active--;
|
||||
|
||||
hvts_active = self.hvts_active;
|
||||
array_index = self GetEntityNumber();
|
||||
}
|
||||
|
||||
bonus = CONST_MIN_MULTIPLIER + hvts_active * CONST_XP_INCREASE;
|
||||
level.teamXPScale[ array_index ] = clamp( bonus, CONST_MIN_MULTIPLIER, CONST_MAX_MULTIPLIER );
|
||||
}
|
||||
|
||||
watchHVTOwner( team ) // self == player
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
result = self waittill_any_return( "disconnect", "joined_team", "joined_spectators" );
|
||||
|
||||
if( level.teamBased )
|
||||
level decreaseXPBoost( team );
|
||||
else if( IsDefined( self ) && result != "disconnect" )
|
||||
self decreaseXPBoost();
|
||||
}
|
1342
maps/mp/killstreaks/_ims.gsc
Normal file
1342
maps/mp/killstreaks/_ims.gsc
Normal file
File diff suppressed because it is too large
Load Diff
252
maps/mp/killstreaks/_jammer.gsc
Normal file
252
maps/mp/killstreaks/_jammer.gsc
Normal file
@ -0,0 +1,252 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\killstreaks\_emp_common;
|
||||
|
||||
// replacement for EMP
|
||||
// only affects ground items
|
||||
// 2013-06-29 wallace: I am no longer supporting multi-team code. It's just ugly.
|
||||
init()
|
||||
{
|
||||
level.teamEMPed["allies"] = false;
|
||||
level.teamEMPed["axis"] = false;
|
||||
|
||||
level.empPlayer = undefined;
|
||||
level.empTimeout = 10.0;
|
||||
level.empTimeRemaining = int( level.empTimeout );
|
||||
|
||||
level.killstreakFuncs["jammer"] = ::EMP_Use;
|
||||
|
||||
level thread onPlayerConnect();
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_emp_timeout", 15.0 );
|
||||
SetDevDvarIfUninitialized( "scr_emp_damage_debug", 0 );
|
||||
#/
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerKilled();
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
if ( self shouldPlayerBeAffectedByEMP() )
|
||||
{
|
||||
self applyPerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerKilled()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "death" );
|
||||
|
||||
self stopEmpJamSequenceImmediate();
|
||||
}
|
||||
}
|
||||
|
||||
EMP_Use( lifeId, streakName )
|
||||
{
|
||||
assert( isDefined( self ) );
|
||||
|
||||
myTeam = self.pers["team"];
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
otherTeam = level.otherTeam[myTeam];
|
||||
self thread EMP_JamTeam( otherTeam );
|
||||
}
|
||||
else
|
||||
{
|
||||
self thread EMP_JamPlayers( self );
|
||||
}
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "jammer", self.origin );
|
||||
self notify( "used_emp" );
|
||||
level notify( "emp_used" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
INITIAL_DELAY = 0.5;
|
||||
//jams all players on the team passed in the argument teamName
|
||||
EMP_JamTeam( teamName )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
// time this with the button press of the remote
|
||||
wait (INITIAL_DELAY);
|
||||
|
||||
assert( teamName == "allies" || teamName == "axis" );
|
||||
|
||||
// TURN ON EMP EFFECTS
|
||||
thread teamPlayerCardSplash( "used_jammer", self );
|
||||
|
||||
level notify ( "EMP_JamTeam" + teamName );
|
||||
level endon ( "EMP_JamTeam" + teamName );
|
||||
|
||||
level.teamEMPed[teamName] = true;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player applyPerPlayerEMPEffects_OnDetonate();
|
||||
|
||||
if ( player shouldPlayerBeAffectedByEMP() )
|
||||
{
|
||||
player applyPerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level thread applyGlobalEMPEffects();
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level destroyGroundObjects( self, teamName );
|
||||
|
||||
// WAIT
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_emp_timeout" );
|
||||
#/
|
||||
level thread keepEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.empTimeout );
|
||||
|
||||
// TURN OF EMP EFFECTS
|
||||
|
||||
level.teamEMPed[teamName] = false;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == teamName
|
||||
&& !(player shouldPlayerBeAffectedByEMP() )
|
||||
)
|
||||
{
|
||||
player removePerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
|
||||
EMP_JamPlayers( owner )
|
||||
{
|
||||
level notify ( "EMP_JamPlayers" );
|
||||
level endon ( "EMP_JamPlayers" );
|
||||
|
||||
// time this with the button press of the remote
|
||||
wait (INITIAL_DELAY);
|
||||
|
||||
if ( !IsDefined( owner ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
level.empPlayer = owner;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
player applyPerPlayerEMPEffects_OnDetonate();
|
||||
|
||||
if ( player shouldPlayerBeAffectedByEMP() )
|
||||
{
|
||||
player applyPerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level thread applyGlobalEMPEffects();
|
||||
|
||||
level notify ( "emp_update" );
|
||||
|
||||
level.empPlayer thread empPlayerFFADisconnect();
|
||||
level destroyGroundObjects( owner );
|
||||
|
||||
// 2013-06-28 wallace: not sure why this sends an update
|
||||
// the team versions only send it on Start and Stop
|
||||
// level notify ( "emp_update" );
|
||||
|
||||
// WAIT
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_emp_timeout" );
|
||||
#/
|
||||
level thread keepEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.empTimeout );
|
||||
|
||||
// STOP
|
||||
level.empPlayer = undefined;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( (!IsDefined( owner ) || player != owner) // it is possible for the owner to disconnect during the jam sequence
|
||||
&& !(player shouldPlayerBeAffectedByEMP() )
|
||||
)
|
||||
{
|
||||
player removePerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "emp_update" );
|
||||
level notify ( "emp_ended" );
|
||||
}
|
||||
|
||||
keepEMPTimeRemaining()
|
||||
{
|
||||
level notify( "keepEMPTimeRemaining" );
|
||||
level endon( "keepEMPTimeRemaining" );
|
||||
|
||||
level endon( "emp_ended" );
|
||||
|
||||
// we need to know how much time is left for the unavailable string
|
||||
level.empTimeRemaining = int( level.empTimeout );
|
||||
while( level.empTimeRemaining )
|
||||
{
|
||||
wait( 1.0 );
|
||||
level.empTimeRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
empPlayerFFADisconnect()
|
||||
{
|
||||
level endon ( "EMP_JamPlayers" );
|
||||
level endon ( "emp_ended" );
|
||||
|
||||
self waittill( "disconnect" );
|
||||
level notify ( "emp_update" );
|
||||
}
|
||||
|
||||
destroyGroundObjects( attacker, teamEMPed )
|
||||
{
|
||||
// thread all of the things that need to get destroyed, this way we can put frame waits in between each destruction so we don't hit the server with a lot at one time
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, teamEMPed, "killstreak_emp_mp", level.turrets );
|
||||
|
||||
// thread destroyActiveRockets( attacker, teamEMPed );
|
||||
|
||||
// IMS
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, teamEMPed, "killstreak_emp_mp", level.placedIMS );
|
||||
|
||||
// ball drones
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, teamEMPed, "killstreak_emp_mp", level.ballDrones );
|
||||
|
||||
// vanguard
|
||||
// thread destroyTargets( attacker, teamEMPed, level.remote_uav );
|
||||
|
||||
// satcoms
|
||||
thread maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, teamEMPed, "killstreak_emp_mp", level.uplinks );
|
||||
|
||||
// this affects all placed equipment
|
||||
maps\mp\killstreaks\_killstreaks::destroyTargetArray( attacker, teamEMPed, "killstreak_emp_mp", level.mines );
|
||||
}
|
334
maps/mp/killstreaks/_juggernaut.gsc
Normal file
334
maps/mp/killstreaks/_juggernaut.gsc
Normal file
@ -0,0 +1,334 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
DECAP_SCALAR_X = 6000;
|
||||
DECAP_SCALAR_Y = 10000;
|
||||
DECAP_SCALAR_Z = 4000;
|
||||
|
||||
init()
|
||||
{
|
||||
level.juggSettings = [];
|
||||
|
||||
level.juggSettings[ "juggernaut" ] = SpawnStruct();
|
||||
level.juggSettings[ "juggernaut" ].splashUsedName = "used_juggernaut";
|
||||
|
||||
level.juggSettings[ "juggernaut_recon" ] = SpawnStruct();
|
||||
level.juggSettings[ "juggernaut_recon" ].splashUsedName = "used_juggernaut_recon";
|
||||
|
||||
level.juggSettings[ "juggernaut_maniac" ] = SpawnStruct();
|
||||
level.juggSettings[ "juggernaut_maniac" ].splashUsedName = "used_juggernaut_maniac";
|
||||
|
||||
level thread watchJuggHostMigrationFinishedInit();
|
||||
}
|
||||
|
||||
giveJuggernaut( juggType ) // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
|
||||
// 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 ( IsDefined( self.lightArmorHP ) )
|
||||
self maps\mp\perks\_perkfunctions::unsetLightArmor();
|
||||
|
||||
self maps\mp\gametypes\_weapons::disablePlantedEquipmentUse();
|
||||
|
||||
// remove explosive bullets if equipped
|
||||
if ( self _hasPerk( "specialty_explosivebullets" ) )
|
||||
self _unsetPerk( "specialty_explosivebullets" );
|
||||
|
||||
// give 100% health to fix some issues
|
||||
// first was if you are being damaged and pick up juggernaut then you could die very quickly as juggernaut, seems weird in practice
|
||||
self.health = self.maxHealth;
|
||||
|
||||
defaultSetup = true;
|
||||
|
||||
switch( juggType )
|
||||
{
|
||||
case "juggernaut":
|
||||
self.isJuggernaut = true;
|
||||
self.juggMoveSpeedScaler = .80; // for unset perk juiced
|
||||
self maps\mp\gametypes\_class::giveLoadout( self.pers["team"], juggType, false );
|
||||
self.moveSpeedScaler = .80;
|
||||
self givePerk( "specialty_scavenger", false );
|
||||
self givePerk( "specialty_quickdraw", false );
|
||||
self givePerk( "specialty_detectexplosive", false );
|
||||
self givePerk( "specialty_sharp_focus", false ); // juggernauts are in heavy armor and shouldn't be flinching like crazy
|
||||
self givePerk( "specialty_radarjuggernaut", false ); // this shows the jugg on the minimap
|
||||
break;
|
||||
case "juggernaut_recon":
|
||||
self.isJuggernautRecon = true;
|
||||
self.juggMoveSpeedScaler = .80; // for unset perk juiced
|
||||
self maps\mp\gametypes\_class::giveLoadout( self.pers["team"], juggType, false );
|
||||
self.moveSpeedScaler = .80;
|
||||
self givePerk( "specialty_scavenger", false );
|
||||
self givePerk( "specialty_coldblooded", false );
|
||||
self givePerk( "specialty_noscopeoutline", false);
|
||||
self givePerk( "specialty_detectexplosive", false );
|
||||
self givePerk( "specialty_sharp_focus", false ); // juggernauts are in heavy armor and shouldn't be flinching like crazy
|
||||
self givePerk( "specialty_radarjuggernaut", false ); // this shows the jugg on the minimap
|
||||
|
||||
if( !IsAgent(self) )
|
||||
{
|
||||
self makePortableRadar( self );
|
||||
|
||||
self maps\mp\gametypes\_missions::processChallenge( "ch_airdrop_juggernaut_recon" );
|
||||
}
|
||||
|
||||
break;
|
||||
case "juggernaut_maniac":
|
||||
self.isJuggernautManiac = true;
|
||||
self.juggMoveSpeedScaler = 1.15; // for unset perk juiced
|
||||
self maps\mp\gametypes\_class::giveLoadout( self.pers["team"], juggType, false );
|
||||
//self maps\mp\killstreaks\_killstreaks::giveAllPerks();
|
||||
//self _unsetPerk( "specialty_lightweight" );
|
||||
//self _unsetPerk( "specialty_combat_speed" );
|
||||
//self givePerk( "specialty_delaymine", false );
|
||||
//self givePerk( "specialty_regenfaster", false );
|
||||
//self givePerk( "specialty_extendedmelee", false );
|
||||
self givePerk( "specialty_blindeye", false );
|
||||
self givePerk( "specialty_coldblooded", false );
|
||||
self givePerk( "specialty_noscopeoutline", false);
|
||||
self givePerk( "specialty_detectexplosive", false );
|
||||
self givePerk( "specialty_marathon", false );
|
||||
self givePerk( "specialty_falldamage", false );
|
||||
//self givePerk( "specialty_explosivedamage", false );
|
||||
//self givePerk( "specialty_fastermelee", false );
|
||||
//self givePerk( "specialty_radarjuggernaut", false );
|
||||
self.moveSpeedScaler = 1.15; // this needs to happen last because some perks change speed
|
||||
break;
|
||||
default:
|
||||
// we rely on self.isJuggernautLevelCustom == true
|
||||
// this would be better if the juggType had some kind of standard prefix, like "juggernaut_custom_swamp_slasher" or "juggernaut_custom_predator"
|
||||
AssertEx( IsDefined( level.mapCustomJuggFunc ), "Juggernaut type " + juggType + " needs to have a level.mapCustomJuggFunc defined!" );
|
||||
defaultSetup = self [[ level.mapCustomJuggFunc ]]( juggType );
|
||||
break;
|
||||
}
|
||||
|
||||
// make sure to give players hardline if they previously had it equipped
|
||||
// we are doing this instead of _hasPerk, because at this point all of the loadout perks would have been cleared by giveLoadout
|
||||
if ( self perkCheck( "specialty_hardline" ) )
|
||||
self givePerk( "specialty_hardline", false );
|
||||
|
||||
self maps\mp\gametypes\_weapons::updateMoveSpeedScale();
|
||||
self disableWeaponPickup();
|
||||
|
||||
if( !IsAgent(self) )
|
||||
{
|
||||
// TODO: no overlay yet but if we want one we can remove these if checks
|
||||
if( defaultSetup )
|
||||
{
|
||||
self SetClientOmnvar( "ui_juggernaut", 1 );
|
||||
self thread teamPlayerCardSplash( level.juggSettings[ juggType ].splashUsedName, self );
|
||||
self thread juggernautSounds();
|
||||
self thread watchDisableJuggernaut();
|
||||
self thread watchEnableJuggernaut();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// if we are using the specialist strike package then we need to clear it, or else players will think they have the perks while jugg
|
||||
if( self.streakType == "specialist" )
|
||||
{
|
||||
self thread maps\mp\killstreaks\_killstreaks::clearKillstreaks();
|
||||
}
|
||||
// - 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
|
||||
else
|
||||
{
|
||||
self thread maps\mp\killstreaks\_killstreaks::updateKillstreaks( true );
|
||||
}
|
||||
|
||||
self thread juggRemover();
|
||||
|
||||
// - model change happens at the end of giveLoadout(), removing any attached models
|
||||
// - re-apply flag if we were carrying one
|
||||
if ( IsDefined( self.carryFlag ) )
|
||||
{
|
||||
wait( 0.05 );
|
||||
self attach( self.carryFlag, "J_spine4", true );
|
||||
}
|
||||
|
||||
level notify( "juggernaut_equipped", self );
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( juggType, self.origin );
|
||||
}
|
||||
|
||||
perkCheck( perkToCheck )
|
||||
{
|
||||
// self == player
|
||||
loadoutPerks = self.pers[ "loadoutPerks" ];
|
||||
|
||||
foreach ( perk in loadoutPerks )
|
||||
{
|
||||
if ( perk == perkToCheck )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
juggernautSounds()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "jugg_removed" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
wait ( 3.0 );
|
||||
playPlayerAndNpcSounds( self, "juggernaut_breathing_player", "juggernaut_breathing_sound" );
|
||||
}
|
||||
}
|
||||
|
||||
//============================================
|
||||
// watchHostMigrationFinishedInit
|
||||
//============================================
|
||||
watchJuggHostMigrationFinishedInit()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
for (;;)
|
||||
{
|
||||
level waittill( "host_migration_end" );
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
if ( isAI( player ) )
|
||||
continue;
|
||||
else if ( player isJuggernaut() && !( IsDefined( player.isJuggernautLevelCustom ) && player.isJuggernautLevelCustom ) )
|
||||
player SetClientOmnvar( "ui_juggernaut", 1 );
|
||||
else
|
||||
player SetClientOmnvar( "ui_juggernaut", 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
self.isJuggernautDef = false;
|
||||
self.isJuggernautGL = false;
|
||||
self.isJuggernautRecon = false;
|
||||
self.isJuggernautManiac = false;
|
||||
self.isJuggernautLevelCustom = false;
|
||||
if ( IsPlayer(self) )
|
||||
self SetClientOmnvar( "ui_juggernaut", 0 );
|
||||
|
||||
self unsetPerk( "specialty_radarjuggernaut", true );
|
||||
|
||||
self notify( "jugg_removed" );
|
||||
}
|
||||
|
||||
juggRemoveOnGameEnded()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self endon( "jugg_removed" );
|
||||
|
||||
level waittill( "game_ended" );
|
||||
|
||||
if( IsPlayer(self) )
|
||||
self SetClientOmnvar( "ui_juggernaut", 0 );
|
||||
}
|
||||
|
||||
setJugg()
|
||||
{
|
||||
if( IsDefined( self.headModel ) )
|
||||
{
|
||||
self Detach( self.headModel, "" );
|
||||
self.headModel = undefined;
|
||||
}
|
||||
self SetModel( "mp_fullbody_juggernaut_heavy_black" );
|
||||
self SetViewModel( "viewhands_juggernaut_ally" );
|
||||
self SetClothType( "vestheavy" );
|
||||
}
|
||||
|
||||
setJuggManiac()
|
||||
{
|
||||
if( IsDefined( self.headModel ) )
|
||||
{
|
||||
self Detach( self.headModel, "" );
|
||||
self.headModel = undefined;
|
||||
}
|
||||
self SetModel( "mp_body_juggernaut_light_black" );
|
||||
self SetViewModel( "viewhands_juggernaut_ally" );
|
||||
self Attach( "head_juggernaut_light_black", "", true );
|
||||
self.headModel = "head_juggernaut_light_black";
|
||||
self SetClothType( "nylon" );
|
||||
}
|
||||
|
||||
disableJuggernaut() // self == player
|
||||
{
|
||||
if( self isJuggernaut() )
|
||||
{
|
||||
self.juggernaut_disabled = true;
|
||||
self SetClientOmnvar( "ui_juggernaut", 0 );
|
||||
}
|
||||
}
|
||||
|
||||
enableJuggernaut() // self == player
|
||||
{
|
||||
if( self isJuggernaut() )
|
||||
{
|
||||
self.juggernaut_disabled = undefined;
|
||||
self SetClientOmnvar( "ui_juggernaut", 1 );
|
||||
}
|
||||
}
|
||||
|
||||
watchDisableJuggernaut() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "jugg_removed" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
if( !IsDefined( self.juggernaut_disabled ) && self isUsingRemote() )
|
||||
{
|
||||
self waittill( "black_out_done" );
|
||||
disableJuggernaut();
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
watchEnableJuggernaut() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "jugg_removed" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
if( IsDefined( self.juggernaut_disabled ) && !self isUsingRemote() )
|
||||
enableJuggernaut();
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
initLevelCustomJuggernaut( createFunc, loadoutFunc, modelFunc, useSplashStr )
|
||||
{
|
||||
level.mapCustomJuggFunc = createFunc;
|
||||
level.mapCustomJuggSetClass = loadoutFunc;
|
||||
level.mapCustomJuggKilledSplash = useSplashStr;
|
||||
|
||||
game[ "allies_model" ][ "JUGGERNAUT_CUSTOM" ] = modelFunc;
|
||||
game[ "axis_model" ][ "JUGGERNAUT_CUSTOM" ] = modelFunc;
|
||||
}
|
1795
maps/mp/killstreaks/_juggernaut_predator.gsc
Normal file
1795
maps/mp/killstreaks/_juggernaut_predator.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2974
maps/mp/killstreaks/_killstreaks.gsc
Normal file
2974
maps/mp/killstreaks/_killstreaks.gsc
Normal file
File diff suppressed because it is too large
Load Diff
100
maps/mp/killstreaks/_killstreaks_init.gsc
Normal file
100
maps/mp/killstreaks/_killstreaks_init.gsc
Normal file
@ -0,0 +1,100 @@
|
||||
#include maps\mp\killstreaks\_killstreaks;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
initKillstreakData();
|
||||
|
||||
level.killstreakFuncs = [];
|
||||
level.killstreakSetupFuncs = [];
|
||||
level.killstreakWeapons = [];
|
||||
|
||||
thread maps\mp\killstreaks\_uav::init();
|
||||
// thread maps\mp\killstreaks\_airstrike::init(); // not using airstrikes for iw6
|
||||
thread maps\mp\killstreaks\_plane::init(); // eventually, move this above airstrike and simplify the existing killstreaks
|
||||
thread maps\mp\killstreaks\_airdrop::init();
|
||||
thread maps\mp\killstreaks\_helicopter::init();
|
||||
|
||||
thread maps\mp\killstreaks\_nuke::init();
|
||||
thread maps\mp\killstreaks\_a10::init();
|
||||
thread maps\mp\killstreaks\_portableAOEgenerator::init();
|
||||
thread maps\mp\killstreaks\_ims::init();
|
||||
thread maps\mp\killstreaks\_perkstreaks::init();
|
||||
thread maps\mp\killstreaks\_juggernaut::init();
|
||||
thread maps\mp\killstreaks\_ball_drone::init();
|
||||
thread maps\mp\killstreaks\_autosentry::init();
|
||||
thread maps\mp\killstreaks\_remotemissile::init();
|
||||
thread maps\mp\killstreaks\_deployablebox::init();
|
||||
thread maps\mp\killstreaks\_deployablebox_vest::init();
|
||||
thread maps\mp\killstreaks\_deployablebox_gun::init(); // careful, _gun and _ammo use the same setup
|
||||
thread maps\mp\killstreaks\_heliSniper::init();
|
||||
thread maps\mp\killstreaks\_helicopter_pilot::init();
|
||||
thread maps\mp\killstreaks\_vanguard::init();
|
||||
thread maps\mp\killstreaks\_uplink::init();
|
||||
thread maps\mp\killstreaks\_droneHive::init();
|
||||
thread maps\mp\killstreaks\_jammer::init();
|
||||
thread maps\mp\killstreaks\_air_superiority::init();
|
||||
thread maps\mp\killstreaks\_odin::init();
|
||||
thread maps\mp\killstreaks\_highValueTarget::init();
|
||||
thread maps\mp\killstreaks\_AALauncher::init();
|
||||
|
||||
// all killstreak weapons that kill, this is used for weapon to killstreak association
|
||||
level.killstreakWeildWeapons = [];
|
||||
level.killstreakWeildWeapons["sentry_minigun_mp"] = "sentry"; // Sentry Gun
|
||||
level.killstreakWeildWeapons["hind_bomb_mp"] = "helicopter"; // Attack Helicopter, Missile
|
||||
level.killstreakWeildWeapons["hind_missile_mp"] = "helicopter"; // Attack Helicopter, Missile
|
||||
level.killstreakWeildWeapons["cobra_20mm_mp"] = "helicopter"; // Attack Helicopter
|
||||
level.killstreakWeildWeapons["nuke_mp"] = "nuke"; // Nuke
|
||||
level.killstreakWeildWeapons["manned_littlebird_sniper_mp"] = "heli_sniper"; // heli sniper
|
||||
level.killstreakWeildWeapons["iw6_minigunjugg_mp"] = "airdrop_juggernaut"; // juggernaut assault primary
|
||||
level.killstreakWeildWeapons["iw6_p226jugg_mp"] = "airdrop_juggernaut"; // juggernaut assault secondary
|
||||
level.killstreakWeildWeapons["mortar_shelljugg_mp"] = "airdrop_juggernaut"; // juggernaut assault equipment
|
||||
level.killstreakWeildWeapons["iw6_riotshieldjugg_mp"] = "airdrop_juggernaut_recon"; // juggernaut support primary
|
||||
level.killstreakWeildWeapons["iw6_magnumjugg_mp"] = "airdrop_juggernaut_recon"; // juggernaut support secondary
|
||||
level.killstreakWeildWeapons["smoke_grenadejugg_mp"] = "airdrop_juggernaut_recon"; // juggernaut support offhand
|
||||
level.killstreakWeildWeapons["iw6_knifeonlyjugg_mp"] = "airdrop_juggernaut_maniac"; // juggernaut maniac primary
|
||||
level.killstreakWeildWeapons["throwingknifejugg_mp"] = "airdrop_juggernaut_maniac"; // juggernaut maniac equipment
|
||||
level.killstreakWeildWeapons["deployable_vest_marker_mp"] = "deployable_vest"; // deployable vest
|
||||
level.killstreakWeildWeapons["deployable_weapon_crate_marker_mp"] = "deployable_ammo"; // deployable vest
|
||||
level.killstreakWeildWeapons["heli_pilot_turret_mp"] = "heli_pilot"; // heli pilot
|
||||
level.killstreakWeildWeapons["guard_dog_mp"] = "guard_dog"; // guard dog
|
||||
level.killstreakWeildWeapons["ims_projectile_mp"] = "ims"; // ims
|
||||
level.killstreakWeildWeapons["ball_drone_gun_mp"] = "ball_drone_backup"; // backup buddy
|
||||
level.killstreakWeildWeapons["drone_hive_projectile_mp"] = "drone_hive"; // drone hive
|
||||
level.killstreakWeildWeapons["switch_blade_child_mp"] = "drone_hive"; // drone hive
|
||||
level.killstreakWeildWeapons["iw6_maaws_mp"] = "aa_launcher"; // aa launcher
|
||||
level.killstreakWeildWeapons["iw6_maawschild_mp"] = "aa_launcher"; // aa launcher
|
||||
level.killstreakWeildWeapons["iw6_maawshoming_mp"] = "aa_launcher"; // aa launcher
|
||||
level.killstreakWeildWeapons["killstreak_uplink_mp"] = "uplink"; // uplink
|
||||
level.killstreakWeildWeapons["odin_projectile_large_rod_mp"] = "odin_assault"; // odin assault
|
||||
level.killstreakWeildWeapons["odin_projectile_small_rod_mp"] = "odin_assault"; // odin assault
|
||||
level.killstreakWeildWeapons["iw6_gm6helisnipe_mp"] = "heli_sniper"; // heli sniper (Extra entry for stat tracking)
|
||||
level.killstreakWeildWeapons["iw6_gm6helisnipe_mp_gm6scope"] = "heli_sniper"; // heli sniper
|
||||
level.killstreakWeildWeapons["aamissile_projectile_mp"] = "air_superiority"; // air superiority
|
||||
level.killstreakWeildWeapons["airdrop_marker_mp"] = "airdrop_assault"; // airdrop marker
|
||||
level.killstreakWeildWeapons["remote_tank_projectile_mp"] = "vanguard"; // vanguard
|
||||
level.killstreakWeildWeapons["killstreak_vanguard_mp"] = "vanguard"; // vanguard
|
||||
level.killstreakWeildWeapons["agent_mp"] = "agent"; // squad member, even jugg from odin and loki
|
||||
level.killstreakWeildWeapons["agent_support_mp"] = "recon_agent"; // support squad member (recon agent)
|
||||
level.killstreakWeildWeapons[ "iw6_axe_mp" ] = "juggernaut_swamp_slasher"; // level killstreak for mp_swamp
|
||||
level.killstreakWeildWeapons[ "venomxgun_mp" ] = "venom_x_gun"; // Extinction gun for mp_dome_ns. Not technically a killstreak
|
||||
level.killstreakWeildWeapons[ "venomxproj_mp" ] = "venom_x_projectile"; // Projectile for Extinction gun
|
||||
level.killstreakWeildWeapons[ "iw6_predatorcannon_mp" ] = "juggernaut_predator"; // level killstreak for mp_battery3
|
||||
level.killstreakWeildWeapons[ "iw6_predatorsuicide_mp" ] = "juggernaut_predator"; // level killstreak for mp_battery3
|
||||
level.killstreakWeildWeapons[ "volcano_rock_mp" ] = "volcano"; // level killstreak for mp_battery3
|
||||
level.killstreakWeildWeapons["ac130_105mm_mp"] = "ac130"; // AC130 level killstreak for mp_favela_iw6
|
||||
level.killstreakWeildWeapons["ac130_40mm_mp"] = "ac130"; // AC130 level killstreak for mp_favela_iw6
|
||||
level.killstreakWeildWeapons["ac130_25mm_mp"] = "ac130"; // AC130 level killstreak for mp_favela_iw6
|
||||
level.killstreakWeildWeapons[ "iw6_mariachimagnum_mp_akimbo" ] = "juggernaut_death_mariachi"; // level killstreak for mp_zulu
|
||||
level.killstreakWeildWeapons[ "harrier_20mm_mp" ] = "harrier_airstrike"; // level killstreak for mp_conflict
|
||||
level.killstreakWeildWeapons[ "artillery_mp" ] = "harrier_airstrike"; // level killstreak for mp_conflict
|
||||
|
||||
if(IsDefined(level.mapCustomKillstreakFunc))
|
||||
[[ level.mapCustomKillstreakFunc ]]();
|
||||
|
||||
level.killstreakRoundDelay = getIntProperty( "scr_game_killstreakdelay", 8 );
|
||||
|
||||
level thread onPlayerConnect();
|
||||
}
|
890
maps/mp/killstreaks/_lasedstrike.gsc
Normal file
890
maps/mp/killstreaks/_lasedstrike.gsc
Normal file
@ -0,0 +1,890 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
//PrecacheMiniMapIcon( "compass_objpoint_reaper_friendly" );
|
||||
//PrecacheMiniMapIcon( "compass_objpoint_reaper_enemy" );
|
||||
|
||||
level.killStreakFuncs["lasedStrike"] = ::tryUseLasedStrike;
|
||||
level.numberOfSoflamAmmo = 2;
|
||||
|
||||
level.lasedStrikeGlow = loadfx("fx/misc/laser_glow");
|
||||
level.lasedStrikeExplode = LoadFX( "fx/explosions/uav_advanced_death" );
|
||||
|
||||
//level.lasedStrikeActive = false;
|
||||
//level.lasedStrikeDrone = undefined;
|
||||
|
||||
remoteMissileSpawnArray = getEntArray( "remoteMissileSpawn" , "targetname" );
|
||||
foreach ( startPoint in remoteMissileSpawnArray )
|
||||
{
|
||||
if ( isDefined( startPoint.target ) )
|
||||
startPoint.targetEnt = getEnt( startPoint.target, "targetname" );
|
||||
}
|
||||
level.lasedStrikeEnts = remoteMissileSpawnArray;
|
||||
|
||||
thread onPlayerConnect();
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
player.soflamAmmoUsed = 0;
|
||||
player.hasSoflam = false;
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
for(;;)
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
//if( isDefined( level.soflamCrate ) )
|
||||
// level.soflamCrate maps\mp\killstreaks\_airdrop::setUsableByTeam( self.team );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tryUseLasedStrike( lifeId, streakName )
|
||||
{
|
||||
return useLasedStrike();
|
||||
}
|
||||
|
||||
|
||||
useLasedStrike()
|
||||
{
|
||||
//self maps\mp\killstreaks\_killstreaks::giveKillstreakWeapon( "iw5_soflam_mp" );
|
||||
|
||||
//level thread teamPlayerCardSplash( "teamstreak_earned_lasedStrike", self, self.team );
|
||||
used = self watchSoflamUsage();
|
||||
|
||||
if ( isDefined( used ) && used )
|
||||
{
|
||||
self.hasSoflam = false;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
giveMarker()
|
||||
{
|
||||
//wait( .25 );
|
||||
self maps\mp\killstreaks\_killstreaks::giveKillstreakWeapon( "iw5_soflam_mp" );
|
||||
|
||||
self.hasSoflam = true;
|
||||
self thread watchSoflamUsage();
|
||||
}
|
||||
|
||||
|
||||
watchSoflamUsage()
|
||||
{
|
||||
self notify( "watchSoflamUsage" );
|
||||
self endon( "watchSoflamUsage" );
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "death" );
|
||||
|
||||
while( self isChangingWeapon() )
|
||||
wait ( 0.05 );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
|
||||
if ( self AttackButtonPressed() && self GetCurrentWeapon() == "iw5_soflam_mp" && self AdsButtonPressed() )
|
||||
{
|
||||
//self WeaponLockNoClearance( false );
|
||||
self WeaponLockTargetTooClose( false );
|
||||
self WeaponLockFree();
|
||||
|
||||
targetInfo = getTargetPoint();
|
||||
|
||||
if( !isDefined( targetInfo ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
if( !isDefined( targetInfo[0] ) )
|
||||
{
|
||||
wait 0.05;
|
||||
continue;
|
||||
}
|
||||
|
||||
targPoint = targetInfo[0];
|
||||
|
||||
used = self attackLasedTarget( targPoint );
|
||||
|
||||
if ( used )
|
||||
self.soflamAmmoUsed++;
|
||||
|
||||
if ( self.soflamAmmoUsed >= level.numberOfSoflamAmmo )
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( self isChangingWeapon() )
|
||||
return false;
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
playLockSound()
|
||||
{
|
||||
if ( isDefined( self.playingLockSound ) && self.playingLockSound )
|
||||
return;
|
||||
|
||||
self PlayLocalSound( "javelin_clu_lock" );
|
||||
self.playingLockSound = true;
|
||||
|
||||
wait( .75 );
|
||||
|
||||
self StopLocalSound( "javelin_clu_lock" );
|
||||
self.playingLockSound = false;
|
||||
}
|
||||
|
||||
playLockErrorSound()
|
||||
{
|
||||
if ( isDefined( self.playingLockSound ) && self.playingLockSound )
|
||||
return;
|
||||
|
||||
self PlayLocalSound( "javelin_clu_aquiring_lock" );
|
||||
self.playingLockSound = true;
|
||||
|
||||
wait( .75 );
|
||||
|
||||
self StopLocalSound( "javelin_clu_aquiring_lock" );
|
||||
self.playingLockSound = false;
|
||||
}
|
||||
|
||||
|
||||
attackLasedTarget( targPoint )
|
||||
{
|
||||
finalTargetEnt = undefined;
|
||||
midTargetEnt = undefined;
|
||||
|
||||
upQuantity = 6000;
|
||||
upVector = (0, 0, upQuantity );
|
||||
backDist = 3000;
|
||||
forward = AnglesToForward( self.angles );
|
||||
ownerOrigin = self.origin;
|
||||
startpos = ownerOrigin + upVector + forward * backDist * -1;
|
||||
|
||||
foundAngle = false;
|
||||
|
||||
//straight down
|
||||
|
||||
skyTrace = bulletTrace( targPoint + (0,0,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (0,0,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (0,0,upQuantity), 25, (1,0,0) );
|
||||
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (300,0,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (300,0,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (300,0,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (0,300,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (0,300,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (0,300,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (0,-300,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (0,-300,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (0,-300,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (300,300,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (300,300,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (300,300,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (-300,0,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (-300,0,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (-300,0,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (-300,-300,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (-300,-300,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (-300,-300,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
skyTrace = bulletTrace( targPoint + (300,-300,upQuantity), targPoint , false );
|
||||
if( skyTrace["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
startPos = targPoint + (300,-300,upQuantity);
|
||||
}
|
||||
//self thread drawLine( targPoint, targPoint + (300,-300,upQuantity), 25, (1,0,0) );
|
||||
}
|
||||
|
||||
//backwards vector from player over target area
|
||||
if( !foundAngle )
|
||||
{
|
||||
for ( i = 0; i < 5; i++ )
|
||||
{
|
||||
upQuantity = upQuantity / 2;
|
||||
upVector = (0, 0, upQuantity );
|
||||
startpos = self.origin + upVector + forward * backDist * -1;
|
||||
|
||||
targetSkyCheck = BulletTrace( targPoint, startpos, false );
|
||||
if ( targetSkyCheck["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
break;
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
//check increased angle to try to get it over large objects
|
||||
if( !foundAngle )
|
||||
{
|
||||
for ( i = 0; i < 5; i++ )
|
||||
{
|
||||
upQuantity = upQuantity * 2.5;
|
||||
|
||||
upVector = (0, 0, upQuantity );
|
||||
startpos = self.origin + upVector + forward * backDist * -1;
|
||||
|
||||
targetSkyCheck = BulletTrace( targPoint, startpos, false );
|
||||
if ( targetSkyCheck["fraction"] > .99 )
|
||||
{
|
||||
foundAngle = true;
|
||||
break;
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
if( !foundAngle )
|
||||
{
|
||||
self thread cantHitTarget();
|
||||
return false;
|
||||
}
|
||||
|
||||
//finalTargetEnt = Spawn( "script_origin", targPoint );
|
||||
finalTargetEnt = SpawnFx( level.lasedStrikeGlow, targPoint );
|
||||
|
||||
self thread playLockSound();
|
||||
//self WeaponLockFinalize( finalTargetEnt, (0,0,0), false );
|
||||
self WeaponLockFinalize( targPoint, (0,0,0), false );
|
||||
|
||||
missile = MagicBullet("lasedStrike_missile_mp", startPos, targPoint, self);
|
||||
missile Missile_SetTargetEnt( finalTargetEnt );
|
||||
|
||||
self thread loopTriggeredeffect( finalTargetEnt, missile );
|
||||
|
||||
missile waittill( "death" );
|
||||
|
||||
if( isDefined( finalTargetEnt ) )
|
||||
{
|
||||
finalTargetEnt delete();
|
||||
}
|
||||
|
||||
self WeaponLockFree();
|
||||
return true;
|
||||
}
|
||||
|
||||
loopTriggeredEffect( effect, missile )
|
||||
{
|
||||
missile endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
TriggerFX( effect );
|
||||
wait ( 0.05 );
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
lasedMissileDistance( remote )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
remote endon( "death" );
|
||||
remote endon( "remote_done" );
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
targetDist = distance( self.origin, remote.targetent.origin );
|
||||
remote.owner SetClientDvar( "ui_reaper_targetDistance", int( targetDist / 12 ) );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
cantHitTarget()
|
||||
{
|
||||
self thread playLockErrorSound();
|
||||
//self WeaponLockNoClearance( true );
|
||||
self WeaponLockTargetTooClose( true );
|
||||
}
|
||||
|
||||
|
||||
//does one check per frame
|
||||
checkBestTargetVector( remote, targPoint )
|
||||
{
|
||||
|
||||
foreach( ent in level.lasedStrikeEnts )
|
||||
{
|
||||
check = BulletTrace( ent.origin, targPoint, false, remote );
|
||||
if ( check["fraction"] >= .98 )
|
||||
{
|
||||
return ent;
|
||||
}
|
||||
|
||||
wait (0.05 );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
getTargetPoint()
|
||||
{
|
||||
origin = self GetEye();
|
||||
angles = self GetPlayerAngles();
|
||||
forward = AnglesToForward( angles );
|
||||
endpoint = origin + forward * 15000;
|
||||
|
||||
res = BulletTrace( origin, endpoint, false, undefined );
|
||||
|
||||
if ( res["surfacetype"] == "none" )
|
||||
return undefined;
|
||||
if ( res["surfacetype"] == "default" )
|
||||
return undefined;
|
||||
|
||||
ent = res["entity"];
|
||||
if ( IsDefined( ent ) )
|
||||
{
|
||||
if ( ent == level.ac130.planeModel )
|
||||
return undefined;
|
||||
}
|
||||
|
||||
results = [];
|
||||
results[0] = res["position"];
|
||||
results[1] = res["normal"];
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
|
||||
//Not In Use
|
||||
spawnRemote( owner )
|
||||
{
|
||||
remote = spawnPlane( owner, "script_model", level.UAVRig getTagOrigin( "tag_origin" ), "compass_objpoint_reaper_friendly", "compass_objpoint_reaper_enemy" );
|
||||
if ( !isDefined( remote ) )
|
||||
return undefined;
|
||||
|
||||
remote setModel( "vehicle_predator_b" );
|
||||
remote.team = owner.team;
|
||||
remote.owner = owner;
|
||||
remote.numFlares = 2;
|
||||
|
||||
remote setCanDamage( true );
|
||||
remote thread damageTracker();
|
||||
|
||||
remote.heliType = "remote_mortar";
|
||||
|
||||
// for target lists (javelin, stinger, sam, emp, etc)
|
||||
remote.uavType = "remote_mortar";
|
||||
remote maps\mp\killstreaks\_uav::addUAVModel();
|
||||
|
||||
// same height and radius as the AC130 with random angle and counter rotation
|
||||
zOffset = 6300;
|
||||
angle = randomInt( 360 );
|
||||
radiusOffset = 6100;
|
||||
xOffset = cos( angle ) * radiusOffset;
|
||||
yOffset = sin( angle ) * radiusOffset;
|
||||
angleVector = vectorNormalize( (xOffset,yOffset,zOffset) );
|
||||
angleVector = ( angleVector * 6100 );
|
||||
remote linkTo( level.UAVRig, "tag_origin", angleVector, (0,angle-90,10) );
|
||||
|
||||
remote thread handleDeath( owner );
|
||||
|
||||
remote thread handleOwnerChangeTeam( owner );
|
||||
remote thread handleOwnerDisconnect( owner );
|
||||
remote thread handleTimeOut();
|
||||
|
||||
remote thread handleIncomingStinger();
|
||||
remote thread handleIncomingSAM();
|
||||
|
||||
return remote;
|
||||
}
|
||||
|
||||
|
||||
handleDeath( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
owner endon( "disconnect" );
|
||||
self endon( "remote_removed" );
|
||||
self endon( "remote_done" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
level thread removeRemote( self, true );
|
||||
}
|
||||
|
||||
|
||||
handleOwnerChangeTeam( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "remote_done" );
|
||||
self endon( "death" );
|
||||
owner endon( "disconnect" );
|
||||
owner endon( "removed_reaper_ammo" );
|
||||
|
||||
owner waittill_any( "joined_team", "joined_spectators" );
|
||||
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
handleOwnerDisconnect( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "remote_done" );
|
||||
self endon( "death" );
|
||||
owner endon( "removed_reaper_ammo" );
|
||||
|
||||
owner waittill( "disconnect" );
|
||||
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
shotCounter()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "remote_done" );
|
||||
|
||||
numShotsFired = 0;
|
||||
|
||||
for( ;; )
|
||||
{
|
||||
self waittill( "lasedTargetShotFired" );
|
||||
|
||||
numShotsFired++;
|
||||
|
||||
if ( numShotsFired >= 5 )
|
||||
break;
|
||||
}
|
||||
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
handleTimeOut()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "remote_done" );
|
||||
|
||||
wait 120;
|
||||
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
removeRemote( remote, clearLevelRef )
|
||||
{
|
||||
self notify( "remote_removed" );
|
||||
|
||||
if ( isDefined( remote.targetEnt ) )
|
||||
remote.targetEnt delete();
|
||||
|
||||
level.lasedStrikeActive = false;
|
||||
level.lasedStrikeCrateActive = false;
|
||||
|
||||
if( IsDefined( remote ) )
|
||||
{
|
||||
remote delete();
|
||||
remote maps\mp\killstreaks\_uav::removeUAVModel();
|
||||
}
|
||||
|
||||
if ( !IsDefined( clearLevelRef ) || clearLevelRef == true )
|
||||
level.remote_mortar = undefined;
|
||||
}
|
||||
|
||||
|
||||
remoteLeave()
|
||||
{
|
||||
// setting the level variable here because there is a bug if this gets shot down on the way out then this doesn't get cleared because of the endon("death")
|
||||
// now it'll definitely get cleared as soon as it tries to leave
|
||||
level.remote_mortar = undefined;
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
self notify( "remote_done" );
|
||||
|
||||
destPoint = self.origin + ( AnglesToForward( self.angles ) * 20000 );
|
||||
self moveTo( destPoint, 30 );
|
||||
PlayFXOnTag( level._effect[ "ac130_engineeffect" ] , self, "tag_origin" );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 3 );
|
||||
|
||||
self moveTo( destPoint, 4, 4, 0.0 );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 4 );
|
||||
|
||||
level thread removeRemote( self, false );
|
||||
}
|
||||
|
||||
|
||||
remoteExplode()
|
||||
{
|
||||
self notify( "death" );
|
||||
self Hide();
|
||||
forward = ( AnglesToRight( self.angles ) * 200 );
|
||||
playFx ( level.lasedStrikeExplode, self.origin, forward );
|
||||
|
||||
level.lasedStrikeActive = false;
|
||||
level.lasedStrikeCrateActive = false;
|
||||
}
|
||||
|
||||
|
||||
// Entities spawned from SpawnPlane do not respond to pre-damage callbacks
|
||||
// so we have to wait until we get the post-damage event.
|
||||
//
|
||||
// Because the damage has already happened by the time we find out about it,
|
||||
// we need to use an artificially high health value, restore it on erroneous damage
|
||||
// events and track a virtual damage taken against a virtual max health.
|
||||
damageTracker()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self.owner endon( "disconnect" );
|
||||
|
||||
self.health = 999999; // keep it from dying anywhere in code
|
||||
self.maxHealth = 1500; // this is the health we'll check
|
||||
self.damageTaken = 0; // how much damage has it taken
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
// don't allow people to destroy things on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
if ( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
self.wasDamaged = true;
|
||||
|
||||
modifiedDamage = damage;
|
||||
|
||||
if( IsPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" );
|
||||
|
||||
if( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
|
||||
{
|
||||
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
|
||||
modifiedDamage += damage * level.armorPiercingMod;
|
||||
}
|
||||
}
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
switch( weapon )
|
||||
{
|
||||
case "stinger_mp":
|
||||
case "javelin_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
modifiedDamage = self.maxhealth + 1;
|
||||
break;
|
||||
|
||||
case "sam_projectile_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
break;
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
}
|
||||
|
||||
self.damageTaken += modifiedDamage;
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner playLocalSound( "reaper_damaged" );
|
||||
|
||||
if ( self.damageTaken >= self.maxHealth )
|
||||
{
|
||||
if ( isPlayer( attacker ) && ( !isDefined( self.owner ) || attacker != self.owner ) )
|
||||
{
|
||||
attacker notify( "destroyed_killstreak", weapon );
|
||||
thread teamPlayerCardSplash( "callout_destroyed_remote_mortar", attacker );
|
||||
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 50, weapon, meansOfDeath );
|
||||
attacker thread maps\mp\gametypes\_rank::xpEventPopup( "destroyed_remote_mortar" );
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
||||
|
||||
}
|
||||
|
||||
if ( isDefined( self.owner ) )
|
||||
self.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
self thread remoteExplode();
|
||||
// adding this to make sure it gets undefined, there was a weird bug where it could get killed as soon as it was leaving
|
||||
// then the threads got killed because of the endon's and this never got reset
|
||||
level.remote_mortar = undefined;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleIncomingStinger() // self == remote mortar
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "remote_done" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill ( "stinger_fired", player, missile, lockTarget );
|
||||
|
||||
if ( !IsDefined( lockTarget ) || (lockTarget != self) )
|
||||
continue;
|
||||
|
||||
missile thread stingerProximityDetonate( lockTarget, player );
|
||||
}
|
||||
}
|
||||
|
||||
stingerProximityDetonate( missileTarget, player ) // self == missile
|
||||
{
|
||||
self endon ( "death" );
|
||||
missileTarget endon( "death" );
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner PlayLocalSound( "missile_incoming" );
|
||||
|
||||
self Missile_SetTargetEnt( missileTarget );
|
||||
|
||||
minDist = Distance( self.origin, missileTarget GetPointInBounds( 0, 0, 0 ) );
|
||||
lastCenter = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
while( true )
|
||||
{
|
||||
// already destroyed
|
||||
if( !IsDefined( missileTarget ) )
|
||||
center = lastCenter;
|
||||
else
|
||||
center = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
lastCenter = center;
|
||||
|
||||
curDist = Distance( self.origin, center );
|
||||
|
||||
if( curDist < 3000 && missileTarget.numFlares > 0 )
|
||||
{
|
||||
missileTarget.numFlares--;
|
||||
|
||||
missileTarget thread maps\mp\killstreaks\_flares::flares_playFx();
|
||||
newTarget = missileTarget maps\mp\killstreaks\_flares::flares_deploy();
|
||||
|
||||
self Missile_SetTargetEnt( newTarget );
|
||||
missileTarget = newTarget;
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( curDist < minDist )
|
||||
minDist = curDist;
|
||||
|
||||
if( curDist > minDist )
|
||||
{
|
||||
if( curDist > 1536 )
|
||||
return;
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
{
|
||||
missileTarget.owner stopLocalSound( "missile_incoming" );
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
if( missileTarget.team != player.team )
|
||||
RadiusDamage( self.origin, 1000, 1000, 1000, player, "MOD_EXPLOSIVE", "stinger_mp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
RadiusDamage( self.origin, 1000, 1000, 1000, player, "MOD_EXPLOSIVE", "stinger_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
self Hide();
|
||||
|
||||
wait( 0.05 );
|
||||
self delete();
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
handleIncomingSAM() // self == remote mortar
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "remote_done" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill ( "sam_fired", player, missileGroup, lockTarget );
|
||||
|
||||
if ( !IsDefined( lockTarget ) || (lockTarget != self) )
|
||||
continue;
|
||||
|
||||
level thread samProximityDetonate( lockTarget, player, missileGroup );
|
||||
}
|
||||
}
|
||||
|
||||
samProximityDetonate( missileTarget, player, missileGroup )
|
||||
{
|
||||
missileTarget endon( "death" );
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner PlayLocalSound( "missile_incoming" );
|
||||
|
||||
sam_projectile_damage = 150; // this should match the gdt entry
|
||||
sam_projectile_damage_radius = 1000;
|
||||
|
||||
minDist = [];
|
||||
for( i = 0; i < missileGroup.size; i++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ i ] ) )
|
||||
minDist[ i ] = Distance( missileGroup[ i ].origin, missileTarget GetPointInBounds( 0, 0, 0 ) );
|
||||
else
|
||||
minDist[ i ] = undefined;
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
center = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
curDist = [];
|
||||
for( i = 0; i < missileGroup.size; i++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ i ] ) )
|
||||
curDist[ i ] = Distance( missileGroup[ i ].origin, center );
|
||||
}
|
||||
|
||||
for( i = 0; i < curDist.size; i++ )
|
||||
{
|
||||
if( IsDefined( curDist[ i ] ) )
|
||||
{
|
||||
// if one of the missiles in the group get close, set off flares and redirect them all
|
||||
if( curDist[ i ] < 3000 && missileTarget.numFlares > 0 )
|
||||
{
|
||||
missileTarget.numFlares--;
|
||||
|
||||
missileTarget thread maps\mp\killstreaks\_flares::flares_playFx();
|
||||
newTarget = missileTarget maps\mp\killstreaks\_flares::flares_deploy();
|
||||
|
||||
for( j = 0; j < missileGroup.size; j++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ j ] ) )
|
||||
{
|
||||
missileGroup[ j ] Missile_SetTargetEnt( newTarget );
|
||||
}
|
||||
}
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( curDist[ i ] < minDist[ i ] )
|
||||
minDist[ i ] = curDist[ i ];
|
||||
|
||||
if( curDist[ i ] > minDist[ i ] )
|
||||
{
|
||||
if( curDist[ i ] > 1536 )
|
||||
continue;
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
{
|
||||
missileTarget.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
if( missileTarget.team != player.team )
|
||||
RadiusDamage( missileGroup[ i ].origin, sam_projectile_damage_radius, sam_projectile_damage, sam_projectile_damage, player, "MOD_EXPLOSIVE", "sam_projectile_mp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
RadiusDamage( missileGroup[ i ].origin, sam_projectile_damage_radius, sam_projectile_damage, sam_projectile_damage, player, "MOD_EXPLOSIVE", "sam_projectile_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
missileGroup[ i ] Hide();
|
||||
|
||||
wait ( 0.05 );
|
||||
missileGroup[ i ] delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
715
maps/mp/killstreaks/_mobilemortar.gsc
Normal file
715
maps/mp/killstreaks/_mobilemortar.gsc
Normal file
@ -0,0 +1,715 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
|
||||
init()
|
||||
{
|
||||
level.tankFire = loadfx( "fx/props/barrelexp" );
|
||||
//level.tankDamage = loadfx( "fx/fire/tank_fire_turret_abrams" );
|
||||
level.tankExplode = loadfx( "fx/explosions/large_vehicle_explosion" );
|
||||
level.tankFlash = loadfx( "fx/muzzleflashes/ac130_105mm" );
|
||||
level.tankDust1 = loadfx( "fx/smoke/ground_smoke_launch_a" );
|
||||
level.tankDust2 = loadfx( "fx/smoke/ground_smoke_launch_a" );
|
||||
|
||||
level.ground_support_locs = [];
|
||||
|
||||
//
|
||||
// ALPHA
|
||||
level.ground_support_locs["mp_alpha"][0]["origin"] = ( -2748.91, 2921.33, 125.394 );
|
||||
level.ground_support_locs["mp_alpha"][0]["angles"] = ( 0, 16, 0 );
|
||||
|
||||
level.ground_support_locs["mp_alpha"][1]["origin"] = ( 707.795, -499.191, -7.875 );
|
||||
level.ground_support_locs["mp_alpha"][1]["angles"] = ( 0, 90, 0 );
|
||||
|
||||
level.ground_support_locs["mp_alpha"][2]["origin"] = ( 81.8557, 2343.87, -7.875 );
|
||||
level.ground_support_locs["mp_alpha"][2]["angles"] = ( 0, -90.1, 0 );
|
||||
|
||||
//
|
||||
// BRAVO
|
||||
level.ground_support_locs["mp_bravo"][0]["origin"] = ( -761.529, 1221.7, 1212.13 );
|
||||
level.ground_support_locs["mp_bravo"][0]["angles"] = ( 0, -92.373, 0 );
|
||||
|
||||
level.ground_support_locs["mp_bravo"][1]["origin"] = ( 1474.99, 971.865, 1140.13 );
|
||||
level.ground_support_locs["mp_bravo"][1]["angles"] = ( 0, -178.401, 0 );
|
||||
|
||||
level.ground_support_locs["mp_bravo"][2]["origin"] = ( -1366.57, -370.995, 975.807 );
|
||||
level.ground_support_locs["mp_bravo"][2]["angles"] = ( 0, 0.807495, 0 );
|
||||
|
||||
//
|
||||
// DOME
|
||||
level.ground_support_locs["mp_dome"][0]["origin"] = ( 960.279, -482.564, -388.872 );
|
||||
level.ground_support_locs["mp_dome"][0]["angles"] = ( 0, 100.536, 0 );
|
||||
|
||||
level.ground_support_locs["mp_dome"][1]["origin"] = ( -921.941, 166.449, -418.131 );
|
||||
level.ground_support_locs["mp_dome"][1]["angles"] = ( 0, 25.4524, 0 );
|
||||
|
||||
level.ground_support_locs["mp_dome"][2]["origin"] = ( 43.3564, 2102.85, -290.875 );
|
||||
level.ground_support_locs["mp_dome"][2]["angles"] = ( 0, -95.0347, 0 );
|
||||
|
||||
//
|
||||
// PLAZA 2
|
||||
level.ground_support_locs["mp_plaza2"][0]["origin"] = ( -1579.34, -2349.41, 556.125 );
|
||||
level.ground_support_locs["mp_plaza2"][0]["angles"] = ( 0, 5.32288, 0 );
|
||||
|
||||
level.ground_support_locs["mp_plaza2"][1]["origin"] = ( -135.286, 1622.04, 607.13 );
|
||||
level.ground_support_locs["mp_plaza2"][1]["angles"] = ( 0, 173.639, 0 );
|
||||
|
||||
level.ground_support_locs["mp_plaza2"][2]["origin"] = ( -1544.55, 1966.84, 632.024 );
|
||||
level.ground_support_locs["mp_plaza2"][2]["angles"] = ( 0, 0.796509, 0 );
|
||||
|
||||
//
|
||||
// RADAR
|
||||
level.ground_support_locs["mp_radar"][0]["origin"] = ( 896.685, 2692.74, 1208.13 );
|
||||
level.ground_support_locs["mp_radar"][0]["angles"] = ( 0, -178.313, 0 );
|
||||
|
||||
level.ground_support_locs["mp_radar"][1]["origin"] = ( -2455.87, 1564.41, 1308.9 );
|
||||
level.ground_support_locs["mp_radar"][1]["angles"] = ( 0, 1.93471, 0 );
|
||||
|
||||
level.ground_support_locs["mp_radar"][2]["origin"] = ( 1609, -1098.99, 1162.13 );
|
||||
level.ground_support_locs["mp_radar"][2]["angles"] = ( 0, 170.421, 0 );
|
||||
|
||||
//
|
||||
// INTERCHANGE
|
||||
level.ground_support_locs["mp_interchange"][0]["origin"] = ( -2067.08, 1218.17, -82.0487 );
|
||||
level.ground_support_locs["mp_interchange"][0]["angles"] = ( 0, -26.2946, 0 );
|
||||
|
||||
level.ground_support_locs["mp_interchange"][1]["origin"] = ( -1198.2, -1782.62, 103.665 );
|
||||
level.ground_support_locs["mp_interchange"][1]["angles"] = ( 0, 23.3544, 0 );
|
||||
|
||||
level.ground_support_locs["mp_interchange"][2]["origin"] = ( 2391.95, 899.034, 87.7839 );
|
||||
level.ground_support_locs["mp_interchange"][2]["angles"] = ( 0, -136.134, 0 );
|
||||
|
||||
//
|
||||
// LAMBETH
|
||||
level.ground_support_locs["mp_lambeth"][0]["origin"] = ( 1641.37, -1318.01, -260.173 );
|
||||
level.ground_support_locs["mp_lambeth"][0]["angles"] = ( 0, 133.329, 0 );
|
||||
|
||||
level.ground_support_locs["mp_lambeth"][1]["origin"] = ( -1346.56, -880.226, -191.875 );
|
||||
level.ground_support_locs["mp_lambeth"][1]["angles"] = ( 0, 0.432892, 0 );
|
||||
|
||||
level.ground_support_locs["mp_lambeth"][2]["origin"] = ( 1403.95, 3083.4, -287.354 );
|
||||
level.ground_support_locs["mp_lambeth"][2]["angles"] = ( 0, -106.321, 0 );
|
||||
|
||||
//
|
||||
// PARIS
|
||||
level.ground_support_locs["mp_paris"][0]["origin"] = ( -2427.42, 619.217, 188.826 );
|
||||
level.ground_support_locs["mp_paris"][0]["angles"] = ( 0, -2.90588, 0 );
|
||||
|
||||
level.ground_support_locs["mp_paris"][1]["origin"] = ( 2066.95, 796.542, -88.322 );
|
||||
level.ground_support_locs["mp_paris"][1]["angles"] = ( 0, 177.292, 0 );
|
||||
|
||||
level.ground_support_locs["mp_paris"][2]["origin"] = ( 506.406, -2165.36, -64.1201 );
|
||||
level.ground_support_locs["mp_paris"][2]["angles"] = ( 0, 89.5715, 0 );
|
||||
|
||||
//
|
||||
// HARDHAT
|
||||
level.ground_support_locs["mp_hardhat"][0]["origin"] = ( 2033.65, -1428.62, 299.86 );
|
||||
level.ground_support_locs["mp_hardhat"][0]["angles"] = ( 0, 177.979, 0 );
|
||||
|
||||
level.ground_support_locs["mp_hardhat"][1]["origin"] = ( -1044.73, 82.9179, 181.022 );
|
||||
level.ground_support_locs["mp_hardhat"][1]["angles"] = ( 0, -2.68066, 0 );
|
||||
|
||||
level.ground_support_locs["mp_hardhat"][2]["origin"] = ( 1248.95, 1322.56, 304.125 );
|
||||
level.ground_support_locs["mp_hardhat"][2]["angles"] = ( 0, -93.4772, 0 );
|
||||
|
||||
//
|
||||
// CARBON
|
||||
level.ground_support_locs["mp_carbon"][0]["origin"] = ( -47.1408, -2841.26, 3940.01 );
|
||||
level.ground_support_locs["mp_carbon"][0]["angles"] = ( 0, -101.667, 0 );
|
||||
|
||||
level.ground_support_locs["mp_carbon"][1]["origin"] = ( -1686.2, -4727.09, 3756.16 );
|
||||
level.ground_support_locs["mp_carbon"][1]["angles"] = ( 0, 87.6436, 0 );
|
||||
|
||||
level.ground_support_locs["mp_carbon"][2]["origin"] = ( -3761.18, -3716.69, 3568.91 );
|
||||
level.ground_support_locs["mp_carbon"][2]["angles"] = ( 0, -4.20761, 0 );
|
||||
|
||||
//
|
||||
// SEATOWN
|
||||
level.ground_support_locs["mp_seatown"][0]["origin"] = ( 1339.87, 763.592, 175.114 );
|
||||
level.ground_support_locs["mp_seatown"][0]["angles"] = ( 0, 178.551, 0 );
|
||||
|
||||
level.ground_support_locs["mp_seatown"][1]["origin"] = ( 1317.92, -725.589, 232.125 );
|
||||
level.ground_support_locs["mp_seatown"][1]["angles"] = ( 0, 177.738, 0 );
|
||||
|
||||
level.ground_support_locs["mp_seatown"][2]["origin"] = ( -961.699, -1581.56, 144.125 );
|
||||
level.ground_support_locs["mp_seatown"][2]["angles"] = ( 0, 90.0176, 0 );
|
||||
|
||||
//
|
||||
// BOOTLEG
|
||||
level.ground_support_locs["mp_bootleg"][0]["origin"] = ( -988.964, 1833.74, -99.9509 );
|
||||
level.ground_support_locs["mp_bootleg"][0]["angles"] = ( 0, -78.8909, 0 );
|
||||
|
||||
level.ground_support_locs["mp_bootleg"][1]["origin"] = ( 1105.84, -1116.13, -72.3048 );
|
||||
level.ground_support_locs["mp_bootleg"][1]["angles"] = ( 0, 176.558, 0 );
|
||||
|
||||
level.ground_support_locs["mp_bootleg"][2]["origin"] = ( -2027.31, 84.2235, -51.875 );
|
||||
level.ground_support_locs["mp_bootleg"][2]["angles"] = ( 0, -5.12868, 0 );
|
||||
|
||||
//
|
||||
// METEORA
|
||||
level.ground_support_locs["mp_meteora"][0]["origin"] = ( -590.972, 1667.65, -99.6187 );
|
||||
level.ground_support_locs["mp_meteora"][0]["angles"] = ( 0, -89.7745, 0 );
|
||||
|
||||
level.ground_support_locs["mp_meteora"][1]["origin"] = ( -1371.02, -1095.66, 4.125 );
|
||||
level.ground_support_locs["mp_meteora"][1]["angles"] = ( 0, 179.879, 0 );
|
||||
|
||||
level.ground_support_locs["mp_meteora"][2]["origin"] = ( 938.851, -1376.99, -60.0877 );
|
||||
level.ground_support_locs["mp_meteora"][2]["angles"] = ( 0, 110.545, 0 );
|
||||
|
||||
level.killStreakFuncs["mobile_mortar"] = ::tryUseMobileMortar;
|
||||
}
|
||||
|
||||
|
||||
tryUseMobileMortar( lifeId, streakName )
|
||||
{
|
||||
// context fail cases
|
||||
if ( !isDefined( level.ground_support_locs[level.script] ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE_IN_LEVEL" );
|
||||
return false;
|
||||
}
|
||||
if ( isDefined( self.lastStand ) && !self _hasPerk( "specialty_finalstand" ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE_IN_LASTSTAND" );
|
||||
return false;
|
||||
}
|
||||
else if ( isDefined( level.mobileMortar ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_GROUND_APPROACHES_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
else if ( self isUsingRemote() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// select location
|
||||
locIndex = self selectEntranceLocation();
|
||||
if ( !isDefined( locIndex ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
self thread stopLocationSelection( false );
|
||||
|
||||
// create it
|
||||
mobileMortar = createMobileMortar( self, locIndex );
|
||||
if ( !isDefined( mobileMortar ) )
|
||||
return false;
|
||||
|
||||
// use it
|
||||
mobileMortar thread moveToPosition( "entrance" );
|
||||
return true;
|
||||
}
|
||||
|
||||
selectEntranceLocation()
|
||||
{
|
||||
locIndex = undefined;
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self thread showIcons();
|
||||
self _beginLocationSelection( "mobile_mortar", "map_artillery_selector", false, 500 );
|
||||
self endon( "stop_location_selection" );
|
||||
|
||||
self waittill( "confirm_location", location );
|
||||
for ( i=0; i<3; i++ )
|
||||
{
|
||||
targetLoc = level.ground_support_locs[level.script][i]["origin"] * (1,1,0);
|
||||
distSquared = distanceSquared( location, targetLoc );
|
||||
if ( distSquared < 60000 )
|
||||
{
|
||||
locIndex = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( isDefined( locIndex ) )
|
||||
{
|
||||
for ( i=0; i<3; i++ )
|
||||
{
|
||||
if ( i == locIndex )
|
||||
Objective_Icon( self.locationObjectives[i], "compass_objpoint_mortar_target" );
|
||||
else
|
||||
Objective_state( self.locationObjectives[i], "invisible" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for ( i=0; i<3; i++ )
|
||||
{
|
||||
Objective_Icon( self.locationObjectives[i], "compass_objpoint_tank_enemy" );
|
||||
}
|
||||
}
|
||||
wait ( 0.5 );
|
||||
self notify( "picked_location" );
|
||||
wait( 0.05 );
|
||||
if ( isDefined( locIndex ) )
|
||||
break;
|
||||
}
|
||||
|
||||
return locIndex;
|
||||
}
|
||||
|
||||
showIcons()
|
||||
{
|
||||
msg = self maps\mp\gametypes\_hud_util::createFontString( "bigfixed", 0.5 );
|
||||
msg maps\mp\gametypes\_hud_util::setPoint( "CENTER", "CENTER", 0 , -150 );
|
||||
msg setText( &"KILLSTREAKS_SELECT_MOBILE_MORTAR_LOCATION" );
|
||||
|
||||
self.locationObjectives = [];
|
||||
|
||||
for ( i=0; i<3; i++ )
|
||||
{
|
||||
self.locationObjectives[i] = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( self.locationObjectives[i], "invisible", (0,0,0) );
|
||||
objective_position( self.locationObjectives[i], level.ground_support_locs[level.script][i]["origin"] );
|
||||
objective_state( self.locationObjectives[i], "active" );
|
||||
objective_team( self.locationObjectives[i], self.team );
|
||||
objective_icon( self.locationObjectives[i], "compass_objpoint_tank_friendly" );
|
||||
}
|
||||
|
||||
self waittill_any( "cancel_location", "picked_location", "stop_location_selection" );
|
||||
|
||||
msg destroyElem();
|
||||
for ( i=0; i<3; i++ )
|
||||
{
|
||||
_objective_delete( self.locationObjectives[i] );
|
||||
}
|
||||
}
|
||||
|
||||
createMobileMortar( owner, locIndex )
|
||||
{
|
||||
// position
|
||||
skyHeight = self maps\mp\killstreaks\_airdrop::getFlyHeightOffset( level.ground_support_locs[level.script][locIndex]["origin"] );
|
||||
primaryTrace = bulletTrace( level.ground_support_locs[level.script][locIndex]["origin"]+(0,0,skyHeight), level.ground_support_locs[level.script][locIndex]["origin"]-(0,0,skyHeight), false );
|
||||
spawnPos = primaryTrace["position"] + anglesToForward(level.ground_support_locs[level.script][locIndex]["angles"])*-1000;
|
||||
|
||||
// create it
|
||||
mobileMortar = Spawn( "script_model", spawnPos );
|
||||
if ( !isDefined( mobileMortar ) )
|
||||
return undefined;
|
||||
|
||||
// set state vars
|
||||
mobileMortar.angles = level.ground_support_locs[level.script][locIndex]["angles"];
|
||||
mobileMortar SetModel( "vehicle_bradley" );
|
||||
mobileMortar setCanDamage( true );
|
||||
mobileMortar.maxhealth = level.heli_maxhealth*2;
|
||||
mobileMortar.health = mobileMortar.maxhealth;
|
||||
mobileMortar.owner = owner;
|
||||
mobileMortar.playersAttacked = [];
|
||||
mobileMortar.lastTarget = mobileMortar.origin;
|
||||
if ( level.teamBased )
|
||||
mobileMortar.team = owner.team;
|
||||
|
||||
// save some info about the level spawn bounds for fallback random targeting
|
||||
mobileMortar.lowX = level.spawnpoints[0].origin[0];
|
||||
mobileMortar.highX = level.spawnpoints[0].origin[0];
|
||||
mobileMortar.lowY = level.spawnpoints[0].origin[1];
|
||||
mobileMortar.highY = level.spawnpoints[0].origin[1];
|
||||
increment = 200;
|
||||
if ( level.spawnpoints.size > 1 )
|
||||
{
|
||||
for( i=1; i<level.spawnpoints.size; i++ )
|
||||
{
|
||||
if ( level.spawnpoints[i].origin[0] < mobileMortar.lowX )
|
||||
mobileMortar.lowX = level.spawnpoints[i].origin[0];
|
||||
else if ( level.spawnpoints[i].origin[0] > mobileMortar.highX )
|
||||
mobileMortar.highX = level.spawnpoints[i].origin[0];
|
||||
|
||||
if ( level.spawnpoints[i].origin[1] < mobileMortar.lowY )
|
||||
mobileMortar.lowY = level.spawnpoints[i].origin[1];
|
||||
else if ( level.spawnpoints[i].origin[1] > mobileMortar.highY )
|
||||
mobileMortar.highY = level.spawnpoints[i].origin[1];
|
||||
}
|
||||
}
|
||||
else
|
||||
increment = -2000;
|
||||
mobileMortar.lowX += increment;
|
||||
mobileMortar.highX -= increment;
|
||||
mobileMortar.lowY += increment;
|
||||
mobileMortar.highY -= increment;
|
||||
|
||||
// minimap
|
||||
if ( level.teamBased )
|
||||
{
|
||||
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( curObjID, "invisible", (0,0,0) );
|
||||
objective_position( curObjID, primaryTrace["position"] );
|
||||
objective_state( curObjID, "active" );
|
||||
objective_team( curObjID, mobileMortar.team );
|
||||
objective_icon( curObjID, "compass_objpoint_tank_friendly" );
|
||||
mobileMortar.objIdFriendly = curObjID;
|
||||
|
||||
curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( curObjID, "invisible", (0,0,0) );
|
||||
objective_position( curObjID, primaryTrace["position"] );
|
||||
objective_state( curObjID, "active" );
|
||||
objective_team( curObjID, level.otherTeam[mobileMortar.team] );
|
||||
objective_icon( curObjID, "compass_objpoint_tank_enemy" );
|
||||
mobileMortar.objIdEnemy = curObjID;
|
||||
}
|
||||
|
||||
// launch watch threads
|
||||
mobileMortar thread watchTimeOut();
|
||||
mobileMortar thread maps\mp\killstreaks\_helicopter::heli_damage_monitor();
|
||||
//mobileMortar thread watchDamage();
|
||||
mobileMortar thread watchDeath();
|
||||
mobileMortar thread watchProximity();
|
||||
|
||||
level.mobileMortar = mobileMortar;
|
||||
|
||||
return mobileMortar;
|
||||
}
|
||||
|
||||
|
||||
moveToPosition( position /*lifeId, airShip, pathStart, pathGoal, pathEnd, flyHeight*/ )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
if ( position == "entrance" )
|
||||
pos = self.origin + anglesToForward(self.angles)*1000;
|
||||
else
|
||||
{
|
||||
self notify( "leaving" );
|
||||
pos = self.origin + anglesToForward(self.angles)*-1000;
|
||||
}
|
||||
|
||||
movetime = 3.0;
|
||||
self MoveTo( pos, movetime, movetime * 0.6, movetime * 0.4 );
|
||||
wait( movetime );
|
||||
|
||||
if ( position == "entrance" )
|
||||
self thread mortarAttack();
|
||||
else
|
||||
{
|
||||
StopFXOnTag( level.tankDust1, self.fxEnt, "tag_origin" );
|
||||
StopFXOnTag( level.tankDust2, self.fxEnt, "tag_origin" );
|
||||
//StopFXOnTag( level.tankDamage, self, "tag_origin" );
|
||||
self.fxEnt delete();
|
||||
|
||||
_objective_delete( self.objIdFriendly );
|
||||
_objective_delete( self.objIdEnemy );
|
||||
level.mobileMortar = undefined;
|
||||
self delete();
|
||||
}
|
||||
}
|
||||
|
||||
findTarget()
|
||||
{
|
||||
bestTarget = undefined;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player == self.owner )
|
||||
continue;
|
||||
if ( player _hasPerk( "specialty_blindeye" ) )
|
||||
continue;
|
||||
if ( level.teamBased && player.team == self.owner.team )
|
||||
continue;
|
||||
if ( distanceSquared( self.origin, player.origin ) < 1000000 )
|
||||
continue;
|
||||
for ( i=0; i<self.playersAttacked.size; i++ )
|
||||
{
|
||||
if ( player == self.playersAttacked[i] )
|
||||
continue;
|
||||
}
|
||||
if ( distanceSquared( player.origin, self.lastTarget ) < 500000 )
|
||||
continue;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
friendlyClose = false;
|
||||
for ( i=0; i<level.players.size; i++ )
|
||||
{
|
||||
if ( level.players[i].team != player.team && distanceSquared( player.origin, level.players[i].origin ) < 250000 )
|
||||
{
|
||||
friendlyClose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ( friendlyClose == true )
|
||||
continue;
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
trace = bulletTrace( player.origin+(0,0,player maps\mp\killstreaks\_airdrop::getFlyHeightOffset( player.origin )), player.origin+(0,0,100), false );
|
||||
if ( trace["surfacetype"] != "none" )
|
||||
continue;
|
||||
|
||||
bestTarget = player.origin;
|
||||
self.lastTarget = player.origin;
|
||||
self.playersAttacked[self.playersAttacked.size] = player;
|
||||
break;
|
||||
}
|
||||
return bestTarget;
|
||||
}
|
||||
|
||||
findRandomTarget()
|
||||
{
|
||||
randomTarget = undefined;
|
||||
for( i=0; i<20; i++ )
|
||||
{
|
||||
testTarget = ( RandomFloatRange( self.lowX, self.highX ), RandomFloatRange( self.lowY, self.highY ), 0 );
|
||||
if ( distanceSquared( self.origin*(1,1,0), testTarget ) < 1000000 )
|
||||
continue;
|
||||
if ( distanceSquared( self.owner.origin*(1,1,0), testTarget ) < 250000 )
|
||||
continue;
|
||||
if ( distanceSquared( self.origin*(1,1,0), self.lastTarget ) < 500000 )
|
||||
continue;
|
||||
|
||||
friendlyClose = false;
|
||||
if ( level.teamBased )
|
||||
{
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == self.owner.team && distanceSquared( player.origin*(1,1,0), testTarget ) < 250000 )
|
||||
{
|
||||
friendlyClose = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( friendlyClose == false )
|
||||
{
|
||||
randomTarget = testTarget;
|
||||
self.lastTarget = testTarget;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// we tried
|
||||
if ( !isDefined( randomTarget ) )
|
||||
randomTarget = ( RandomFloatRange( self.lowX, self.highX ), RandomFloatRange( self.lowY, self.highY ), 0 );
|
||||
|
||||
return randomTarget;
|
||||
}
|
||||
|
||||
mortarAttack()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
// testing fx stuff
|
||||
dustCount = 0;
|
||||
self.fxEnt = Spawn( "script_model", self.origin );
|
||||
self.fxEnt setModel( "tag_origin" );
|
||||
self.fxEnt.angles = self.angles;
|
||||
self.fxEnt AddPitch( -90 );
|
||||
|
||||
for(;;)
|
||||
{
|
||||
// target
|
||||
flatPos = self findTarget();
|
||||
if ( !isDefined( flatPos ) )
|
||||
flatPos = self findRandomTarget();
|
||||
flyHeight = self.origin[2] + 3500;
|
||||
primaryTrace = bulletTrace( flatPos+(0,0,flyHeight), flatPos-(0,0,flyHeight), false );
|
||||
assert( primaryTrace["surfacetype"] != "none" );
|
||||
startPos = flatPos + (0,0,flyHeight);
|
||||
targetPos = flatPos + (0,0,primaryTrace["position"][2]);
|
||||
|
||||
// fx
|
||||
self playSound( "bmp_fire" );
|
||||
playFX( level.tankFlash, self.origin+anglesToForward(self.angles)*50 );
|
||||
if ( dustCount < 3 )
|
||||
{
|
||||
PlayFXOnTag( level.tankDust1, self.fxEnt, "tag_origin" );
|
||||
PlayFXOnTag( level.tankDust2, self.fxEnt, "tag_origin" );
|
||||
dustCount++;
|
||||
}
|
||||
self thread mortarRecoil();
|
||||
|
||||
// fire
|
||||
level thread fireMortar( self, startPos, targetPos );
|
||||
|
||||
// wait
|
||||
self waittill( "mortar_fire_done" );
|
||||
wait( 2.5 );
|
||||
}
|
||||
}
|
||||
|
||||
fireMortar( mobileMortar, startPos, targetPos )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
owner = mobileMortar.owner;
|
||||
|
||||
projectile = magicBullet( "javelin_mp", mobileMortar.origin+(0,0,150), startPos, owner );
|
||||
|
||||
curObjID1 = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( curObjID1, "invisible", (0,0,0) );
|
||||
objective_position( curObjID1, targetPos );
|
||||
objective_state( curObjID1, "active" );
|
||||
objective_team( curObjID1, mobileMortar.team );
|
||||
objective_icon( curObjID1, "compass_objpoint_mortar_target" );
|
||||
projectile.objIdFriendly = curObjID1;
|
||||
|
||||
curObjID2 = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
objective_add( curObjID2, "invisible", (0,0,0) );
|
||||
objective_position( curObjID2, targetPos );
|
||||
objective_state( curObjID2, "active" );
|
||||
objective_team( curObjID2, level.otherTeam[mobileMortar.team] );
|
||||
objective_icon( curObjID2, "compass_objpoint_mortar_target" );
|
||||
projectile.objIdEnemy = curObjID2;
|
||||
|
||||
flyCount = 0;
|
||||
for( ;; )
|
||||
{
|
||||
if ( !isDefined( projectile ) || flyCount > 115 || distanceSquared( projectile.origin, startPos ) < 500 )
|
||||
break;
|
||||
else
|
||||
wait( 0.05 );
|
||||
flyCount++;
|
||||
}
|
||||
|
||||
if ( isDefined( projectile ) )
|
||||
projectile delete();
|
||||
|
||||
if ( isDefined( owner ) )
|
||||
projectile2 = magicBullet( "javelin_mp", startPos+(0,0,200), targetPos, owner );
|
||||
else
|
||||
projectile2 = magicBullet( "javelin_mp", startPos+(0,0,200), targetPos );
|
||||
projectile2.objIdFriendly = curObjID1;
|
||||
projectile2.objIdEnemy = curObjID2;
|
||||
projectile2 thread watchProjectileOnMiniMap( mobileMortar );
|
||||
}
|
||||
|
||||
watchProjectileOnMiniMap( mobileMortar )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
_objective_delete( self.objIdFriendly );
|
||||
_objective_delete( self.objIdEnemy );
|
||||
|
||||
if ( isDefined( mobileMortar ) )
|
||||
mobileMortar notify( "mortar_fire_done" );
|
||||
}
|
||||
|
||||
|
||||
mortarRecoil()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
backPos = self.origin + anglesToForward(self.angles)*-20;
|
||||
restorePos = self.origin;
|
||||
restoreAngles = self.angles;
|
||||
|
||||
self MoveTo( backPos, 0.10 );
|
||||
self RotatePitch( -3, 0.10 );
|
||||
wait( 0.10 );
|
||||
|
||||
self MoveTo( restorePos, 0.15 );
|
||||
self RotateTo( restoreAngles, 0.15 );
|
||||
wait( 0.15 );
|
||||
}
|
||||
|
||||
watchTimeOut()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 90.0 );
|
||||
|
||||
self setCanDamage( false );
|
||||
self thread moveToPosition( "exit" );
|
||||
}
|
||||
|
||||
watchProximity()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
self endon( "leaving" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
RadiusDamage( self.origin, 200, 20, 20 );
|
||||
wait( 1 );
|
||||
}
|
||||
}
|
||||
|
||||
watchDeath()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "leaving" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
playFX( level.tankFire, self.origin);
|
||||
playFX( level.tankExplode, self.origin);
|
||||
destroyedTank = Spawn( "script_model", self.origin );
|
||||
destroyedTank setModel( "vehicle_bradley_destroyed" );
|
||||
destroyedTank.angles = self.angles;
|
||||
|
||||
StopFXOnTag( level.tankDust1, self.fxEnt, "tag_origin" );
|
||||
StopFXOnTag( level.tankDust2, self.fxEnt, "tag_origin" );
|
||||
//StopFXOnTag( level.tankDamage, self, "tag_origin" );
|
||||
_objective_delete( self.objIdFriendly );
|
||||
_objective_delete( self.objIdEnemy );
|
||||
self.fxEnt delete();
|
||||
self delete();
|
||||
|
||||
wait( 3.5 );
|
||||
|
||||
destroyedTank delete();
|
||||
level.mobileMortar = undefined;
|
||||
}
|
||||
|
||||
watchDamage()
|
||||
{
|
||||
/*
|
||||
if ( !isDefined( attacker ) || attacker == self )
|
||||
return;
|
||||
|
||||
if ( !maps\mp\gameTypes\_weapons::attackerCanDamageItem( attacker, self.owner ) )
|
||||
return;
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
self.damageTaken+=damage;
|
||||
if ( self.damageTaken >= self.maxhealth && ( (level.teamBased && self.team != attacker.team) || !level.teamBased) )
|
||||
{
|
||||
validAttacker = undefined;
|
||||
if ( isDefined( attacker.owner ) && (!isDefined(self.owner) || attacker.owner != self.owner) )
|
||||
validAttacker = attacker.owner;
|
||||
else if ( !isDefined(self.owner) || attacker != self.owner )
|
||||
validAttacker = attacker;
|
||||
|
||||
// sanity checks
|
||||
if ( !isDefined(attacker.owner) && attacker.classname == "script_vehicle" )
|
||||
validAttacker = undefined;
|
||||
if ( isDefined( attacker.class ) && attacker.class == "worldspawn" )
|
||||
validAttacker = undefined;
|
||||
|
||||
if ( isDefined( validAttacker ) )
|
||||
{
|
||||
validAttacker notify( "destroyed_helicopter" );
|
||||
validAttacker notify( "destroyed_killstreak", weapon );
|
||||
thread teamPlayerCardSplash( "callout_destroyed_helicopter", validAttacker );
|
||||
validAttacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 300 );
|
||||
validAttacker thread maps\mp\gametypes\_rank::xpEventPopup( &"SPLASHES_DESTROYED_HELICOPTER" );
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, validAttacker, damage, meansOfDeath );
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill( "damage", amount, attacker, dir, point, type );
|
||||
//if ( attacker == self )
|
||||
//self.health += amount;
|
||||
/*if ( self.health < 500 && !isDefined( self.playingDamageFX ) )
|
||||
{
|
||||
self.playingDamageFX = true;
|
||||
PlayFXOnTag( level.tankDamage, self, "tag_origin" );
|
||||
}*/
|
||||
if ( self.health < 0 )
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
518
maps/mp/killstreaks/_mortarstrike.gsc
Normal file
518
maps/mp/killstreaks/_mortarstrike.gsc
Normal file
@ -0,0 +1,518 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
createMortar( config )
|
||||
{
|
||||
// necessary fields, see rumble for examples?
|
||||
// config.crateWeight = 85;
|
||||
// config.crateHint = &"KILLSTREAKS_HINTS_WARHAWK_MORTARS";
|
||||
// config.debugName = "Warhawk mortars";
|
||||
// config.id = "warhawk_mortars";
|
||||
// config.weaponName = "warhawk_mortar_mp";
|
||||
// config.splashName = "used_warhawk_mortars";
|
||||
// config.sourceStructs = "rumble_mortar_source";
|
||||
// config.sourceEnts = use ents for moving objects, structs for static ones. Only one is needed!
|
||||
// config.targetStructs = "rumble_mortar_target";
|
||||
//
|
||||
// // launch delay
|
||||
// config.launchDelay = 3;
|
||||
//
|
||||
// config.projectilePerLoop = 12;
|
||||
// // air time
|
||||
//
|
||||
// // projectile model
|
||||
// config.Model = "projectile_rpg7";
|
||||
//
|
||||
// // warning sfx, played in environment
|
||||
// config.warningSfxEntName = "mortar_siren";
|
||||
// config.warningSfx = "mortar_siren";
|
||||
// config.warningSfxDuration = 10;
|
||||
//
|
||||
// // launch parameters
|
||||
// // min delay, max delay, max air time
|
||||
// // launch sfx
|
||||
// config.launchSfx = "mortar_launch";
|
||||
// config.launchDelayMin = 0.5;
|
||||
// config.launchDelayMax = 0.6;
|
||||
// config.launchAirTimeMin = 10;
|
||||
// config.launchAirTimeMax = 12;
|
||||
// config.strikeDuration = 25;
|
||||
//
|
||||
// // incoming sfx
|
||||
// config.incomingSfx = "mortar_incoming";
|
||||
// config.trailVfx = "random_mortars_trail"; // the projectilModel needs a tag_fx on it
|
||||
//
|
||||
// // impact
|
||||
// config.impactVfx = "mortar_impact_00";
|
||||
|
||||
level.mortarConfig = config;
|
||||
|
||||
config thread update_mortars(); //setup and update.
|
||||
level.air_raid_active = false;
|
||||
|
||||
level.mapCustomCrateFunc = ::mortarCustomCrateFunc;
|
||||
level.mapCustomKillstreakFunc = ::mortarCustomKillstreakFunc;
|
||||
level.mapCustomBotKillstreakFunc = ::mortarCustomBotKillstreakFunc;
|
||||
|
||||
}
|
||||
|
||||
|
||||
RUMBLE_MORTARS_WEIGHT = 85;
|
||||
mortarCustomCrateFunc()
|
||||
{
|
||||
if(!IsDefined(game["player_holding_level_killstrek"]))
|
||||
game["player_holding_level_killstrek"] = false;
|
||||
|
||||
if(!allowLevelKillstreaks() || game["player_holding_level_killstrek"])
|
||||
return;
|
||||
|
||||
maps\mp\killstreaks\_airdrop::addCrateType( "airdrop_assault",
|
||||
level.mortarConfig.id,
|
||||
level.mortarConfig.crateWeight,
|
||||
maps\mp\killstreaks\_airdrop::killstreakCrateThink,
|
||||
maps\mp\killstreaks\_airdrop::get_friendly_crate_model(),
|
||||
maps\mp\killstreaks\_airdrop::get_enemy_crate_model(),
|
||||
level.mortarConfig.crateHint
|
||||
);
|
||||
level thread watch_for_mortars_crate();
|
||||
}
|
||||
|
||||
watch_for_mortars_crate()
|
||||
{
|
||||
while(1)
|
||||
{
|
||||
level waittill("createAirDropCrate", dropCrate);
|
||||
|
||||
if( IsDefined(dropCrate) && IsDefined(dropCrate.crateType) && dropCrate.crateType == level.mortarConfig.id )
|
||||
{
|
||||
maps\mp\killstreaks\_airdrop::changeCrateWeight("airdrop_assault", level.mortarConfig.id, 0);
|
||||
captured = wait_for_capture(dropCrate);
|
||||
|
||||
if(!captured)
|
||||
{
|
||||
//reEnable warhawk mortars care packages if it expires with out anyone picking it up
|
||||
maps\mp\killstreaks\_airdrop::changeCrateWeight( "airdrop_assault", level.mortarConfig.id, level.mortarConfig.crateWeight );
|
||||
}
|
||||
else
|
||||
{
|
||||
//Once its picked up it needs to remain off.
|
||||
game["player_holding_level_killstrek"] = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//death and capture are sent on the same frame but death is processed first :(
|
||||
wait_for_capture(dropCrate)
|
||||
{
|
||||
result = watch_for_air_drop_death(dropCrate);
|
||||
return !IsDefined(result); //If !isdefined the captured notify was also sent.
|
||||
}
|
||||
|
||||
watch_for_air_drop_death(dropCrate)
|
||||
{
|
||||
dropCrate endon("captured");
|
||||
|
||||
dropCrate waittill("death");
|
||||
waittillframeend;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mortarCustomKillstreakFunc()
|
||||
{
|
||||
AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/Care Package/" + level.mortarConfig.debugName + "\" \"set scr_devgivecarepackage " + level.mortarConfig.id + "; set scr_devgivecarepackagetype airdrop_assault\"\n");
|
||||
AddDebugCommand("devgui_cmd \"MP/Killstreak/Level Event:5/" + level.mortarConfig.debugName + "\" \"set scr_givekillstreak " + level.mortarConfig.id + "\"\n");
|
||||
|
||||
level.killStreakFuncs[ level.mortarConfig.id ] = ::tryUseMortars;
|
||||
|
||||
level.killstreakWeildWeapons[ level.mortarConfig.weaponName ] = level.mortarConfig.id;
|
||||
}
|
||||
|
||||
mortarCustomBotKillstreakFunc()
|
||||
{
|
||||
AddDebugCommand("devgui_cmd \"MP/Bots(Killstreak)/Level Events:5/" + level.mortarConfig.debugName + "\" \"set scr_testclients_givekillstreak " + level.mortarConfig.id + "\"\n");
|
||||
maps\mp\bots\_bots_ks::bot_register_killstreak_func( level.mortarConfig.id, maps\mp\bots\_bots_ks::bot_killstreak_simple_use );
|
||||
}
|
||||
|
||||
tryUseMortars(lifeId, streakName)
|
||||
{
|
||||
if(level.air_raid_active)
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
|
||||
game["player_holding_level_killstrek"] = false;
|
||||
level notify( "mortar_killstreak_used", self);
|
||||
|
||||
if ( IsDefined( level.mortarConfig.splashName ) )
|
||||
{
|
||||
self thread teamPlayerCardSplash( level.mortarConfig.splashName, self );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
mortars_activate_at_end_of_match()
|
||||
{
|
||||
level endon( "mortar_killstreak_used" );
|
||||
level waittill ( "spawning_intermission" );
|
||||
level.ending_flourish = true;
|
||||
|
||||
// this should come from the config somewhere
|
||||
mortar_fire(0.1, 0.3, 2.5, 2.5, 6, level.players[0]);
|
||||
}
|
||||
|
||||
|
||||
update_mortars() // self == mortar system
|
||||
{
|
||||
level endon("stop_dynamic_events");
|
||||
|
||||
waitframe(); //allow load main to finish
|
||||
|
||||
//Build list of structs
|
||||
if ( IsDefined( self.sourceStructs ) )
|
||||
{
|
||||
self.mortar_sources = getstructarray( self.sourceStructs, "targetname" );
|
||||
}
|
||||
else if ( IsDefined( self.sourceEnts ) )
|
||||
{
|
||||
self.mortar_sources = GetEntArray( self.sourceEnts, "targetname" );
|
||||
}
|
||||
foreach(source in self.mortar_sources)
|
||||
{
|
||||
if(!IsDefined(source.radius))
|
||||
source.radius = 300;
|
||||
}
|
||||
|
||||
self.mortar_targets = getstructarray( self.targetStructs, "targetname" );
|
||||
foreach(mortar_target in self.mortar_targets)
|
||||
{
|
||||
if(!IsDefined(mortar_target.radius))
|
||||
mortar_target.radius = 100;
|
||||
}
|
||||
|
||||
while(1)
|
||||
{
|
||||
level.air_raid_active = false;
|
||||
level.air_raid_team_called = "none";
|
||||
self thread mortars_activate_at_end_of_match();
|
||||
|
||||
level waittill("mortar_killstreak_used", player);
|
||||
|
||||
level.air_raid_active = true;
|
||||
level.air_raid_team_called = player.team;
|
||||
self thread warning_audio();
|
||||
wait ( self.launchDelay ); //Delay between siren start and mortar fire
|
||||
|
||||
self mortar_fire( self.launchDelayMin, self.launchDelayMax,
|
||||
self.launchAirTimeMin, self.launchAirTimeMax,
|
||||
self.strikeDuration,
|
||||
player
|
||||
);
|
||||
|
||||
level notify( "mortar_killstreak_end" );
|
||||
}
|
||||
}
|
||||
|
||||
warning_audio() // self == mortar system
|
||||
{
|
||||
if ( !IsDefined( self.warningSfxEntName ) || !IsDefined( self.warningSfx ) )
|
||||
return;
|
||||
|
||||
if(!IsDefined( self.warning_sfx_ent ))
|
||||
{
|
||||
self.warning_sfx_ent = GetEnt(self.warningSfxEntName, "targetname");
|
||||
}
|
||||
|
||||
if( IsDefined(self.warning_sfx_ent) )
|
||||
{
|
||||
self.warning_sfx_ent PlaySound( self.warningSfx );
|
||||
|
||||
wait ( self.warningSfxDuration );
|
||||
|
||||
self.warning_sfx_ent StopSounds();
|
||||
}
|
||||
}
|
||||
|
||||
mortar_fire(delay_min, delay_max, airtime_min, airtime_max, mortar_time_sec, owner) // self == mortar system
|
||||
{
|
||||
motar_strike_end_time = GetTime() + mortar_time_sec*1000;
|
||||
|
||||
//pick team appropriate for owner team
|
||||
source_structs = self random_mortars_get_source_structs(level.air_raid_team_called);
|
||||
if (source_structs.size <= 0)
|
||||
{
|
||||
PrintLn( "Mortars: Didn't find any sources: targetname = " + self.sourceStructs );
|
||||
return;
|
||||
}
|
||||
|
||||
level notify( "mortar_killstreak_start" );
|
||||
|
||||
air_raid_num = 0;
|
||||
while( motar_strike_end_time>GetTime() )
|
||||
{
|
||||
mortars_per_loop = self.projectilePerLoop;
|
||||
mortars_launched = 0;
|
||||
foreach(player in level.players)
|
||||
{
|
||||
if(!isReallyAlive(player))
|
||||
continue;
|
||||
|
||||
if(level.teamBased)
|
||||
{
|
||||
if(player.team == level.air_raid_team_called)
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
if( IsDefined( owner ) && player == owner)
|
||||
continue;
|
||||
}
|
||||
|
||||
if( player.spawnTime + 8000 > GetTime() )
|
||||
continue;
|
||||
|
||||
vel = player GetVelocity();
|
||||
|
||||
mortar_air_time = airtime_min;
|
||||
if ( airtime_max > airtime_min )
|
||||
mortar_air_time = RandomFloatRange( airtime_min, airtime_max );
|
||||
|
||||
mortar_target_pos = player.origin + (vel*mortar_air_time);
|
||||
|
||||
nodes_near = GetNodesInRadiusSorted(mortar_target_pos,100,0,60);
|
||||
foreach(node in nodes_near)
|
||||
{
|
||||
if(NodeExposedToSky(node))
|
||||
{
|
||||
source_struct = random(source_structs);
|
||||
|
||||
if( self random_mortars_fire( source_struct.origin, node.origin, mortar_air_time, owner, true, true, source_struct ) )
|
||||
{
|
||||
wait RandomFloatRange(delay_min, delay_max);
|
||||
mortars_launched++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//If there weren't enough player targets, drop the rest randomly:
|
||||
if (self.mortar_targets.size > 0)
|
||||
{
|
||||
source_structs = array_randomize(source_structs);
|
||||
while(mortars_launched<mortars_per_loop)
|
||||
{
|
||||
source_struct = source_structs[air_raid_num];
|
||||
air_raid_num++;
|
||||
if(air_raid_num >= source_structs.size)
|
||||
air_raid_num = 0;//loop
|
||||
|
||||
target_struct = random(self.mortar_targets);
|
||||
|
||||
mortar_air_time = airtime_min;
|
||||
if ( airtime_max > airtime_min )
|
||||
mortar_air_time = RandomFloatRange( airtime_min, airtime_max );
|
||||
|
||||
start = random_point_in_circle(source_struct.origin, source_struct.radius);
|
||||
end = random_point_in_circle(target_struct.origin, target_struct.radius);
|
||||
self thread random_mortars_fire( start, end, mortar_air_time, owner, false, true, source_struct );
|
||||
wait RandomFloatRange(delay_min, delay_max);
|
||||
mortars_launched++;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
break; // no targets!
|
||||
}
|
||||
|
||||
if ( IsDefined( self.delayBetweenVolleys ) )
|
||||
{
|
||||
level notify( "mortar_volleyFinished" );
|
||||
|
||||
wait ( self.delayBetweenVolleys );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
random_point_in_circle(origin, radius)
|
||||
{
|
||||
if(radius>0)
|
||||
{
|
||||
rand_dir = AnglesToForward((0,RandomFloatRange(0,360),0));
|
||||
rand_radius = RandomFloatRange(0, radius);
|
||||
origin = origin + (rand_dir*rand_radius);
|
||||
}
|
||||
|
||||
return origin;
|
||||
}
|
||||
|
||||
random_mortars_fire( start_org, end_org, air_time, owner, trace_test, play_fx, sourceObj ) // self == mortar_system
|
||||
{
|
||||
if ( IsDefined( self.launchSfx ) )
|
||||
{
|
||||
PlaySoundAtPos( start_org, self.launchSfx );
|
||||
}
|
||||
else if ( IsDefined( self.launchSfxArray ) && self.launchSfxArray.size > 0 && IsDefined( self.launchSfxStartId ) )
|
||||
{
|
||||
if ( self.launchSfxStartId >= self.launchSfxArray.size )
|
||||
{
|
||||
self.launchSfxStartId = 0;
|
||||
}
|
||||
|
||||
PlaySoundAtPos( start_org, self.launchSfxArray[ self.launchSfxStartId ] );
|
||||
self.launchSfxStartId++;
|
||||
}
|
||||
|
||||
gravity = (0,0,-800);
|
||||
|
||||
launch_dir = TrajectoryCalculateInitialVelocity(start_org, end_org, gravity, air_time);
|
||||
|
||||
// ugh, would be better to use the start point's angles
|
||||
if ( IsDefined( self.launchVfx ) )
|
||||
{
|
||||
// check model?
|
||||
if ( IsDefined( sourceObj ) && sourceObj.classname != "struct" )
|
||||
{
|
||||
PlayFXOnTag( getfx(self.launchVfx), sourceObj, "tag_origin" );
|
||||
}
|
||||
else
|
||||
{
|
||||
PlayFX( getfx(self.launchVfx), start_org, launch_dir );
|
||||
}
|
||||
}
|
||||
|
||||
if(IsDefined(trace_test) && trace_test)
|
||||
{
|
||||
delta_height = TrajectoryComputeDeltaHeightAtTime(launch_dir[2], -1*gravity[2], air_time/2);
|
||||
trace_point = ((end_org - start_org)/2) + start_org + (0,0,delta_height);
|
||||
|
||||
//self thread drawLine(trace_point, end_org, 60*10, (0,1,0));
|
||||
if(BulletTracePassed(trace_point, end_org, false, undefined))
|
||||
{
|
||||
thread random_mortars_fire_run( start_org, end_org, air_time, owner, launch_dir, play_fx );
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
random_mortars_fire_run( start_org, end_org, air_time, owner, launch_dir, play_fx );
|
||||
}
|
||||
|
||||
random_mortars_fire_run( start_org, end_org, air_time, owner, launch_dir, play_fx ) // self == mortar system
|
||||
{
|
||||
dirt_effect_radius = 350;
|
||||
|
||||
mortar_model = createProjectileEntity(start_org, self.model);
|
||||
mortar_model.in_use = true;
|
||||
|
||||
waitframe();//Model may have just spawned
|
||||
if ( IsDefined( self.trailVfx ) )
|
||||
{
|
||||
PlayFXOnTag( getfx( self.trailVfx ), mortar_model, "tag_fx");
|
||||
}
|
||||
|
||||
mortar_model.angles = VectorToAngles(launch_dir) * (-1,1,1);
|
||||
|
||||
if ( IsDefined( self.incomingSfx ) )
|
||||
{
|
||||
thread playSoundAtPosInTime( self.incomingSfx, end_org, air_time - 2.0 );
|
||||
}
|
||||
|
||||
mortar_model MoveGravity(launch_dir, air_time - 0.05); // dial back by a frame so the mortar/killcam doesn't go below ground.
|
||||
|
||||
if ( IsDefined( self.rotateProjectiles ) && self.rotateProjectiles )
|
||||
{
|
||||
mortar_model RotateVelocity( randomvectorrange( self.minRotatation, self.maxRotation ), air_time, 2, 0 );
|
||||
}
|
||||
|
||||
mortar_model waittill("movedone");
|
||||
|
||||
if(level.createFX_enabled && !IsDefined(level.players))
|
||||
level.players = [];
|
||||
|
||||
if( IsDefined(owner) )
|
||||
{
|
||||
mortar_model RadiusDamage(end_org, 250, 750, 500, owner, "MOD_EXPLOSIVE", self.weaponName );
|
||||
}
|
||||
else
|
||||
{
|
||||
mortar_model RadiusDamage(end_org, 140, 5, 5, undefined, "MOD_EXPLOSIVE", self.weaponName );
|
||||
}
|
||||
PlayRumbleOnPosition("artillery_rumble", end_org);
|
||||
|
||||
//Dirt effect on Players:
|
||||
dirt_effect_radiusSq = dirt_effect_radius * dirt_effect_radius;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player isUsingRemote() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( DistanceSquared( end_org, player.origin ) > dirt_effect_radiusSq )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( player DamageConeTrace( end_org ) )
|
||||
{
|
||||
player thread maps\mp\gametypes\_shellshock::dirtEffect( end_org );
|
||||
}
|
||||
}
|
||||
|
||||
if( play_fx )
|
||||
{
|
||||
if ( IsDefined( self.impactVfx ) )
|
||||
{
|
||||
PlayFX( getfx( self.impactVfx ), end_org);
|
||||
}
|
||||
|
||||
if ( IsDefined( self.impactSfx ) )
|
||||
{
|
||||
PlaySoundAtPos( end_org, self.impactSfx ) ;
|
||||
}
|
||||
}
|
||||
|
||||
mortar_model Delete();
|
||||
}
|
||||
|
||||
playSoundAtPosInTime( sound, pos, delay )
|
||||
{
|
||||
wait ( delay );
|
||||
|
||||
playSoundAtPos( pos, sound );
|
||||
}
|
||||
|
||||
createProjectileEntity( origin, modelName )
|
||||
{
|
||||
mortar_model = Spawn("script_model", origin);
|
||||
mortar_model SetModel( modelName );
|
||||
return mortar_model;
|
||||
}
|
||||
|
||||
random_mortars_get_source_structs( owner_team ) // self == mortar system
|
||||
{
|
||||
source_structs = [];
|
||||
|
||||
if( level.teamBased )
|
||||
{
|
||||
foreach( struct in self.mortar_sources )
|
||||
{
|
||||
if (IsDefined(struct.script_team) && struct.script_team == owner_team )
|
||||
source_structs[source_structs.size] = struct;
|
||||
}
|
||||
}
|
||||
|
||||
//No approprate team source, just use any:
|
||||
if (source_structs.size == 0)
|
||||
source_structs = self.mortar_sources;
|
||||
|
||||
return source_structs;
|
||||
}
|
107
maps/mp/killstreaks/_mrsiartillery.gsc
Normal file
107
maps/mp/killstreaks/_mrsiartillery.gsc
Normal file
@ -0,0 +1,107 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
KILLSTREAK_NAME = "mrsiartillery";
|
||||
|
||||
init()
|
||||
{
|
||||
level.killstreakFuncs[ KILLSTREAK_NAME ] = ::tryUseStrike;
|
||||
|
||||
configData = SpawnStruct();
|
||||
configData.weaponName = "airdrop_marker_mp";
|
||||
configData.projectileName = "mrsiartillery_projectile_mp";
|
||||
// configData.explodeVfx = LoadFX( "fx/explosions/aerial_explosion" );
|
||||
// configData.model = "projectile_icbm_missile";
|
||||
// configData.model = "mil_ammo_case_1_open";
|
||||
//configData.impactVfx = LoadFX( "fx/explosions/wall_explosion_pm_a" );
|
||||
configData.numStrikes = 6;
|
||||
configData.initialDelay = 1.0;
|
||||
configData.minFireDelay = 0.375;
|
||||
configdata.maxFireDelay = 0.5;
|
||||
configData.strikeRadius = 150;
|
||||
|
||||
if ( !IsDefined( level.killstreakConfigData ) )
|
||||
{
|
||||
level.killstreakConfigData = [];
|
||||
}
|
||||
level.killstreakConfigData[ KILLSTREAK_NAME ] = configData;
|
||||
}
|
||||
|
||||
tryUseStrike( lifeId, streakName )
|
||||
{
|
||||
configData = level.killStreakConfigData[ KILLSTREAK_NAME ];
|
||||
result = self maps\mp\killstreaks\_designator_grenade::designator_Start( KILLSTREAK_NAME, configData.weaponName, ::onTargetAcquired );
|
||||
|
||||
if ( ( !IsDefined( result ) || !result ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
// self maps\mp\_matchdata::logKillstreakEvent( BOX_TYPE, self.origin );
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
onTargetAcquired( killstreakName, designatorEnt ) // self == player
|
||||
{
|
||||
configData = level.killStreakConfigData[ killstreakName ];
|
||||
|
||||
owner = designatorEnt.owner;
|
||||
endPos = designatorEnt.origin;
|
||||
|
||||
designatorEnt Detonate();
|
||||
|
||||
doStrike( owner, killstreakName, owner.origin, endPos );
|
||||
}
|
||||
|
||||
doStrike( owner, killstreakName, startPosition, endPosition )
|
||||
{
|
||||
configData = level.killStreakConfigData[ killstreakName ];
|
||||
|
||||
// play some firing sounds
|
||||
|
||||
dir = endPosition - startPosition;
|
||||
xyDir = ( dir[0], dir[1], 0);
|
||||
dir = VectorNormalize( dir );
|
||||
|
||||
strikeTarget = endPosition;
|
||||
// shift up slightly to not intersect with ground
|
||||
strikeOrigin = maps\mp\killstreaks\_killstreaks::findUnobstructedFiringPoint( owner, endPosition + (0, 0, 10), 10000 );
|
||||
|
||||
if ( IsDefined( strikeOrigin ) )
|
||||
{
|
||||
IPrintLn( "Firing Motar!" );
|
||||
|
||||
// play sounds
|
||||
|
||||
wait( configData.initialDelay );
|
||||
|
||||
// always hit the target directly once
|
||||
wait( RandomFloatRange( configData.minFireDelay, configData.maxFireDelay ) );
|
||||
projectile = MagicBullet( configData.projectileName, strikeOrigin, strikeTarget, owner );
|
||||
|
||||
// then scatter around
|
||||
for ( i = 1; i < configData.numStrikes; i++ )
|
||||
{
|
||||
wait( RandomFloatRange( configData.minFireDelay, configData.maxFireDelay ) );
|
||||
|
||||
// pick target points
|
||||
randomTarget = pickRandomTargetPoint( strikeTarget, configData.strikeRadius );
|
||||
|
||||
projectile = MagicBullet( configData.projectileName, strikeOrigin, randomTarget, owner );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
IPrintLn( "Mortar LOS blocked!" );
|
||||
}
|
||||
}
|
||||
|
||||
pickRandomTargetPoint( targetPoint, strikeRadius )
|
||||
{
|
||||
x = RandomFloatRange( -1 * strikeRadius, strikeRadius );
|
||||
y = RandomFloatRange( -1 * strikeRadius, strikeRadius );
|
||||
return targetPoint + (x, y, 0);
|
||||
}
|
675
maps/mp/killstreaks/_nuke.gsc
Normal file
675
maps/mp/killstreaks/_nuke.gsc
Normal file
@ -0,0 +1,675 @@
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\killstreaks\_emp_common;
|
||||
|
||||
// the nuke ended the game in MW2, for MW3 it will be an MOAB, not end the game but kill the other team and emp them for 60 seconds, it will also change the visionset for the level
|
||||
|
||||
init()
|
||||
{
|
||||
level.nukeVisionSet = "aftermath_post";
|
||||
|
||||
level._effect[ "nuke_player" ] = loadfx( "fx/explosions/player_death_nuke" );
|
||||
level._effect[ "nuke_flash" ] = loadfx( "fx/explosions/player_death_nuke_flash" );
|
||||
level._effect[ "nuke_aftermath" ] = loadfx( "fx/dust/nuke_aftermath_mp" );
|
||||
|
||||
//game["strings"]["nuclear_strike"] = &"KILLSTREAKS_TACTICAL_NUKE";
|
||||
|
||||
level.killstreakFuncs["nuke"] = ::tryUseNuke;
|
||||
|
||||
SetDvarIfUninitialized( "scr_nukeTimer", 10 );
|
||||
SetDvarIfUninitialized( "scr_nukeCancelMode", 0 );
|
||||
|
||||
level.nukeTimer = getDvarInt( "scr_nukeTimer" );
|
||||
level.cancelMode = getDvarInt( "scr_nukeCancelMode" );
|
||||
|
||||
|
||||
if( level.multiTeamBased )
|
||||
{
|
||||
for( i = 0; i < level.teamNameList.size; i++ )
|
||||
{
|
||||
level.teamNukeEMPed[level.teamNameList[i]] = false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
level.teamNukeEMPed["allies"] = false;
|
||||
level.teamNukeEMPed["axis"] = false;
|
||||
}
|
||||
|
||||
level.nukeEmpTimeout = 60.0;
|
||||
level.nukeEmpTimeRemaining = int( level.nukeEmpTimeout );
|
||||
level.nukeInfo = spawnStruct();
|
||||
level.nukeInfo.xpScalar = 2;
|
||||
level.nukeDetonated = undefined;
|
||||
|
||||
level thread onPlayerConnect();
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_nuke_empTimeout", 60.0 );
|
||||
SetDevDvarIfUninitialized( "scr_nukeDistance", 5000 );
|
||||
SetDevDvarIfUninitialized( "scr_nukeEndsGame", true );
|
||||
SetDevDvarIfUninitialized( "scr_nukeDebugPosition", false );
|
||||
#/
|
||||
}
|
||||
|
||||
tryUseNuke( lifeId, streakName, allowCancel )
|
||||
{
|
||||
if( isDefined( level.nukeIncoming ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_NUKE_ALREADY_INBOUND" );
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( self isUsingRemote() && ( !isDefined( level.gtnw ) || !level.gtnw ) )
|
||||
return false;
|
||||
|
||||
if ( !isDefined( allowCancel ) )
|
||||
allowCancel = true;
|
||||
|
||||
self thread doNuke( allowCancel );
|
||||
self notify( "used_nuke" );
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "nuke", self.origin );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
delaythread_nuke( delay, func )
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( delay );
|
||||
|
||||
thread [[ func ]]();
|
||||
}
|
||||
|
||||
doNuke( allowCancel )
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level.nukeInfo.player = self;
|
||||
level.nukeInfo.team = self.pers["team"];
|
||||
|
||||
level.nukeIncoming = true;
|
||||
|
||||
//maps\mp\gametypes\_gamelogic::pauseTimer();
|
||||
//level.timeLimitOverride = true;
|
||||
//setGameEndTime( int( gettime() + (level.nukeTimer * 1000) ) );
|
||||
|
||||
// capture the previous bomb timer value so we can go back to it after the nuke goes off
|
||||
level.prevUIBombTimer = int( GetOmnvar( "ui_bomb_timer" ) );
|
||||
SetOmnvar( "ui_bomb_timer", 4 ); // Nuke sets '4' to avoid briefcase icon showing
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
thread teamPlayerCardSplash( "used_nuke", self, self.team );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( !level.hardcoreMode )
|
||||
self IPrintLnBold( &"KILLSTREAKS_FRIENDLY_TACTICAL_NUKE" );
|
||||
}
|
||||
|
||||
if(!IsDefined(level.doNuke_fx) || ![[level.doNuke_fx]]() )
|
||||
{
|
||||
if( !IsDefined( level.nuke_soundObject ) )
|
||||
{
|
||||
level.nuke_soundObject = Spawn( "script_origin", (0,0,1) );
|
||||
level.nuke_soundObject hide();
|
||||
}
|
||||
|
||||
level thread delaythread_nuke( (level.nukeTimer - 3.3), ::nukeSoundIncoming );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeSoundExplosion );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeSlowMo );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeEffects );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 0.25), ::nukeVision );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 1.5), ::nukeDeath );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 1.5), ::nukeEarthquake );
|
||||
level thread nukeAftermathEffect();
|
||||
|
||||
if ( level.cancelMode && allowCancel )
|
||||
level thread cancelNukeOnDeath( self );
|
||||
}
|
||||
|
||||
level thread update_ui_timers();
|
||||
|
||||
// need objects to play sound off of, i'm keeping them on level so we don't spawn them more than once if multiple nukes get called in a match
|
||||
if( !IsDefined( level.nuke_clockObject ) )
|
||||
{
|
||||
level.nuke_clockObject = Spawn( "script_origin", (0,0,0) );
|
||||
level.nuke_clockObject hide();
|
||||
}
|
||||
|
||||
nukeTimer = level.nukeTimer;
|
||||
while( nukeTimer > 0 )
|
||||
{
|
||||
// TODO: get a new sound for this so we don't remind people of the old nuke
|
||||
level.nuke_clockObject playSound( "ui_mp_kem_timer" );
|
||||
wait( 1.0 );
|
||||
nukeTimer--;
|
||||
}
|
||||
}
|
||||
|
||||
// used in aliens mode
|
||||
doNukeSimple()
|
||||
{
|
||||
level.nukeInfo.player = self;
|
||||
level.nukeInfo.team = self.pers["team"];
|
||||
level.nukeIncoming = true;
|
||||
|
||||
level thread delaythread_nuke( (level.nukeTimer - 3.3), ::nukeSoundIncoming );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeSoundExplosion );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeSlowMo );
|
||||
level thread delaythread_nuke( level.nukeTimer, ::nukeEffects );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 0.25), ::nukeVision );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 1.5), ::nukeDeathSimple );
|
||||
level thread delaythread_nuke( (level.nukeTimer + 1.5), ::nukeEarthquake );
|
||||
|
||||
// need objects to play sound off of, i'm keeping them on level so we don't spawn them more than once if multiple nukes get called in a match
|
||||
if( !IsDefined( level.nuke_soundObject ) )
|
||||
{
|
||||
level.nuke_soundObject = Spawn( "script_origin", (0,0,1) );
|
||||
level.nuke_soundObject hide();
|
||||
}
|
||||
}
|
||||
|
||||
nukeDeathSimple()
|
||||
{
|
||||
level notify( "nuke_death" );
|
||||
}
|
||||
|
||||
cancelNukeOnDeath( player )
|
||||
{
|
||||
player waittill_any( "death", "disconnect" );
|
||||
|
||||
if ( isDefined( player ) && level.cancelMode == 2 )
|
||||
player thread maps\mp\killstreaks\_emp::EMP_Use( 0, 0 );
|
||||
|
||||
|
||||
//maps\mp\gametypes\_gamelogic::resumeTimer();
|
||||
//level.timeLimitOverride = false;
|
||||
|
||||
nukeClearTimer();
|
||||
level.nukeIncoming = undefined;
|
||||
|
||||
level notify ( "nuke_cancelled" );
|
||||
}
|
||||
|
||||
nukeSoundIncoming()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
if( IsDefined( level.nuke_soundObject ) )
|
||||
level.nuke_soundObject PlaySound( "nuke_incoming" );
|
||||
}
|
||||
|
||||
nukeSoundExplosion()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
if( IsDefined( level.nuke_soundObject ) )
|
||||
{
|
||||
level.nuke_soundObject PlaySound( "nuke_explosion" );
|
||||
level.nuke_soundObject PlaySound( "nuke_wave" );
|
||||
}
|
||||
}
|
||||
|
||||
nukeClearTimer()
|
||||
{
|
||||
uiBombTimer = 0;
|
||||
if( IsDefined( level.prevUIBombTimer ) )
|
||||
uiBombTimer = level.prevUIBombTimer;
|
||||
SetOmnvar( "ui_bomb_timer", uiBombTimer );
|
||||
}
|
||||
|
||||
nukeEffects()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
nukeClearTimer();
|
||||
//setGameEndTime( 0 );
|
||||
|
||||
level.nukeDetonated = true;
|
||||
|
||||
foreach( player in level.players )
|
||||
{
|
||||
level thread nukeEffect( player );
|
||||
//player.nuked = true;
|
||||
}
|
||||
}
|
||||
|
||||
nukeEffect( player )
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
player endon( "disconnect" );
|
||||
|
||||
waitframe();
|
||||
|
||||
nukeLoc = undefined;
|
||||
dirToNuke = undefined;
|
||||
// nuke location override: from aliens
|
||||
if ( !IsDefined( level.nukeLoc ) )
|
||||
{
|
||||
yawAngle = (0, player.angles[1], 0);
|
||||
dirToNuke = AnglesToForward( yawAngle );
|
||||
|
||||
nukeDistance = 5000;
|
||||
/# nukeDistance = getDvarInt( "scr_nukeDistance" ); #/
|
||||
|
||||
nukeLoc = player.origin + ( dirToNuke * nukeDistance );
|
||||
}
|
||||
else
|
||||
{
|
||||
nukeLoc = level.nukeLoc;
|
||||
}
|
||||
|
||||
effectFwd = undefined;
|
||||
effectUp = (0, 0, 1);
|
||||
if ( !IsDefined( level.nukeAngles ) )
|
||||
{
|
||||
effectFwd = AnglesToRight( (0, (player.angles[1] + 180), 0) );
|
||||
}
|
||||
else
|
||||
{
|
||||
effectFwd = AnglesToForward( level.nukeAngles );
|
||||
effectUp = AnglesToUp( level.nukeAngles );
|
||||
}
|
||||
|
||||
/#
|
||||
if ( getDvarInt( "scr_nukeDebugPosition" ) )
|
||||
{
|
||||
thread draw_line_for_time( nukeLoc, nukeLoc + 500 * effectUp, 1, 0, 0, 20 );
|
||||
thread draw_line_for_time( nukeLoc, nukeLoc + 500 * effectFwd, 0, 1, 0, 20 );
|
||||
}
|
||||
#/
|
||||
|
||||
// 2013-08-29 wallace: in FXed, forward direction is mushroom cloud rising in the air, left direction is shooting towards the player
|
||||
// have to do some
|
||||
fxObj = SpawnFXForClient( level._effect[ "nuke_flash" ], nukeLoc, player, effectUp, effectFwd );
|
||||
TriggerFX( fxObj );
|
||||
|
||||
// 30s is a little arbitrary
|
||||
player thread cleanupNukeEffect( fxObj, 30 );
|
||||
}
|
||||
|
||||
cleanupNukeEffect( fxObj, lifetime )
|
||||
{
|
||||
self waitForTimeOrNotify( lifetime, "disconnect" );
|
||||
|
||||
fxObj Delete();
|
||||
}
|
||||
|
||||
nukeAftermathEffect()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level waittill ( "spawning_intermission" );
|
||||
|
||||
afermathEnt = getEntArray( "mp_global_intermission", "classname" );
|
||||
afermathEnt = afermathEnt[0];
|
||||
up = anglestoup( afermathEnt.angles );
|
||||
right = anglestoright( afermathEnt.angles );
|
||||
|
||||
PlayFX( level._effect[ "nuke_aftermath" ], afermathEnt.origin, up, right );
|
||||
}
|
||||
|
||||
nukeSlowMo()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
//SetSlowMotion( <startTimescale>, <endTimescale>, <deltaTime> )
|
||||
SetSlowMotion( 1.0, 0.25, 0.5 );
|
||||
level waittill( "nuke_death" );
|
||||
SetSlowMotion( 0.25, 1, 2.0 );
|
||||
}
|
||||
|
||||
nukeVision()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level.nukeVisionInProgress = true;
|
||||
|
||||
VisionSetPostApply( "mpnuke", 3 );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 2 );
|
||||
|
||||
VisionSetPostApply("nuke_global_flash", .1);
|
||||
SetExpFog(0, 956, 0.72, 0.61, 0.39, 0.968, 0.85, 1, 0.298, 0.273, 0.266, .25, (0, 0, -1), 84, 118, 2.75, .984, 124, 100);
|
||||
SetDvar( "r_materialBloomHQScriptMasterEnable", 0 );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( .5 );
|
||||
|
||||
level notify ( "nuke_aftermath_post_started" );
|
||||
|
||||
VisionSetPostApply("aftermath_post", .5);
|
||||
|
||||
level waittill( "nuke_death" );
|
||||
|
||||
level thread updateNukeVisionOnHostMigration();
|
||||
|
||||
level setNukeAftermathVision( 5 );
|
||||
}
|
||||
|
||||
nukeDeath()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
level notify( "nuke_death" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
foreach( character in level.characters )
|
||||
{
|
||||
if( nukeCanKill(character) )
|
||||
{
|
||||
if( isPlayer(character) )
|
||||
{
|
||||
character.nuked = true;
|
||||
if ( isReallyAlive( character ) )
|
||||
{
|
||||
character thread maps\mp\gametypes\_damage::finishPlayerDamageWrapper( level.nukeInfo.player, level.nukeInfo.player, 999999, 0, "MOD_EXPLOSIVE", "nuke_mp", character.origin, (0,0,1), "none", 0, 0 );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
character maps\mp\agents\_agents::agent_damage_finished( level.nukeInfo.player, level.nukeInfo.player, 999999, 0, "MOD_EXPLOSIVE", "nuke_mp", character.origin, (0,0,1), "none", 0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// emp jam them after death, if we do before then the timing is off
|
||||
level thread nuke_EMPJam();
|
||||
|
||||
// since the nuke death happened, the nuke is no longer incoming
|
||||
level.nukeIncoming = undefined;
|
||||
}
|
||||
|
||||
nukeCanKill(character)
|
||||
{
|
||||
if(!IsDefined(level.nukeInfo))
|
||||
return false;
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
if( IsDefined( level.nukeInfo.team ) && character.team == level.nukeInfo.team )
|
||||
return false;
|
||||
}
|
||||
// ffa, don't kill the player who called it
|
||||
else
|
||||
{
|
||||
isKillstreakPlayer = IsDefined( level.nukeInfo.player ) && (character == level.nukeInfo.player);
|
||||
ownerIsPlayer = IsDefined( level.nukeInfo.player ) && IsDefined(character.owner) && (character.owner == level.nukeInfo.player);
|
||||
|
||||
if( isKillstreakPlayer || ownerIsPlayer )
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
nukeEarthquake()
|
||||
{
|
||||
level endon ( "nuke_cancelled" );
|
||||
|
||||
level waittill( "nuke_death" );
|
||||
|
||||
// TODO: need to get a different position to call this on
|
||||
//earthquake( 0.6, 10, nukepos, 100000 );
|
||||
|
||||
//foreach( player in level.players )
|
||||
//player PlayRumbleOnEntity( "damage_heavy" );
|
||||
}
|
||||
|
||||
|
||||
//waitForNukeCancel()
|
||||
//{
|
||||
// self waittill( "cancel_location" );
|
||||
// self setblurforplayer( 0, 0.3 );
|
||||
//}
|
||||
//
|
||||
//endSelectionOn( waitfor )
|
||||
//{
|
||||
// self endon( "stop_location_selection" );
|
||||
// self waittill( waitfor );
|
||||
// self thread stopNukeLocationSelection( (waitfor == "disconnect") );
|
||||
//}
|
||||
//
|
||||
//endSelectionOnGameEnd()
|
||||
//{
|
||||
// self endon( "stop_location_selection" );
|
||||
// level waittill( "game_ended" );
|
||||
// self thread stopNukeLocationSelection( false );
|
||||
//}
|
||||
//
|
||||
//stopNukeLocationSelection( disconnected )
|
||||
//{
|
||||
// if ( !disconnected )
|
||||
// {
|
||||
// self setblurforplayer( 0, 0.3 );
|
||||
// self endLocationSelection();
|
||||
// self.selectingLocation = undefined;
|
||||
// }
|
||||
// self notify( "stop_location_selection" );
|
||||
//}
|
||||
|
||||
nuke_EMPJam()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
if ( level.teamBased )
|
||||
{
|
||||
level nukeEmpJamTeam( getOtherTeam( level.nukeInfo.team ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
level.teamNukeEMPed[ level.nukeInfo.team ] = true;
|
||||
level.teamNukeEMPed[ getOtherTeam( level.nukeInfo.team ) ] = true;
|
||||
nukeEmpJamPlayers( level.nukeInfo.player );
|
||||
}
|
||||
}
|
||||
|
||||
nukeEmpJamTeam( teamName )
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
level notify( "nuke_EMPJam" );
|
||||
level endon( "nuke_EMPJam" );
|
||||
|
||||
assert( teamName == "allies" || teamName == "axis" );
|
||||
|
||||
level.teamNukeEMPed[teamName] = true;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player shouldPlayerBeAffectedByEMP() )
|
||||
{
|
||||
player applyPerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "nuke_emp_update" );
|
||||
|
||||
level maps\mp\killstreaks\_jammer::destroyGroundObjects( level.nukeInfo.player, teamName );
|
||||
level maps\mp\killstreaks\_air_superiority::destroyActiveVehicles( level.nukeInfo.player, teamName );
|
||||
|
||||
// WAIT
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_nuke_empTimeout" );
|
||||
#/
|
||||
level thread keepNukeEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.nukeEmpTimeout );
|
||||
|
||||
// TURN OF EMP EFFECTS
|
||||
level.teamNukeEMPed[teamName] = false;
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player.team == teamName
|
||||
&& !(player shouldPlayerBeAffectedByEMP() )
|
||||
)
|
||||
{
|
||||
player removePerPlayerEMPEffects();
|
||||
player.nuked = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "nuke_emp_update" );
|
||||
level notify ( "nuke_emp_ended" );
|
||||
}
|
||||
|
||||
|
||||
nukeEmpJamPlayers( owner )
|
||||
{
|
||||
level notify ( "nuke_EMPJam" );
|
||||
level endon ( "nuke_EMPJam" );
|
||||
|
||||
if ( !IsDefined( owner ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( player shouldPlayerBeAffectedByEMP() )
|
||||
{
|
||||
player applyPerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "nuke_emp_update" );
|
||||
|
||||
// level.nukeInfo.player thread empPlayerFFADisconnect();
|
||||
level maps\mp\killstreaks\_jammer::destroyGroundObjects( level.nukeInfo.player );
|
||||
level maps\mp\killstreaks\_air_superiority::destroyActiveVehicles( level.nukeInfo.player );
|
||||
|
||||
|
||||
// 2013-06-28 wallace: not sure why this sends an update
|
||||
// the team versions only send it on Start and Stop
|
||||
// level notify ( "emp_update" );
|
||||
|
||||
// WAIT
|
||||
/#
|
||||
level.empTimeout = GetDvarFloat( "scr_emp_timeout" );
|
||||
#/
|
||||
level thread keepNukeEMPTimeRemaining();
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( level.nukeEmpTimeout );
|
||||
|
||||
|
||||
// STOP
|
||||
level.nukeInfo.player = undefined;
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( (!IsDefined( owner ) || player != owner) // it is possible for the owner to disconnect during the jam sequence
|
||||
&& !(player shouldPlayerBeAffectedByEMP() )
|
||||
)
|
||||
{
|
||||
player removePerPlayerEMPEffects();
|
||||
}
|
||||
}
|
||||
|
||||
level notify ( "nuke_emp_update" );
|
||||
level notify ( "nuke_emp_ended" );
|
||||
}
|
||||
|
||||
keepNukeEMPTimeRemaining()
|
||||
{
|
||||
level notify( "keepNukeEMPTimeRemaining" );
|
||||
level endon( "keepNukeEMPTimeRemaining" );
|
||||
|
||||
level endon( "nuke_emp_ended" );
|
||||
|
||||
// we need to know how much time is left for the unavailable string
|
||||
level.nukeEmpTimeRemaining = int( level.nukeEmpTimeout );
|
||||
while( level.nukeEmpTimeRemaining )
|
||||
{
|
||||
wait( 1.0 );
|
||||
level.nukeEmpTimeRemaining--;
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerConnect()
|
||||
{
|
||||
for(;;)
|
||||
{
|
||||
level waittill("connected", player);
|
||||
player thread onPlayerSpawned();
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerSpawned()
|
||||
{
|
||||
self endon("disconnect");
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
// emp effects are already handled in _jammer.gsc; no need to repeat
|
||||
/*
|
||||
if ( self shouldPlayerBeAffectedByEMP() )
|
||||
{
|
||||
self applyPerPlayerEMPEffects();
|
||||
}
|
||||
else
|
||||
{
|
||||
self stopEmpJamSequenceImmediate();
|
||||
}
|
||||
*/
|
||||
|
||||
// make sure the vision set stays on between deaths
|
||||
if( IsDefined( level.nukeDetonated ) )
|
||||
{
|
||||
self thread setVisionForPlayer();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setVisionForPlayer()
|
||||
{
|
||||
wait( 0.1 );
|
||||
self VisionSetPostApplyForPlayer( level.nukeVisionSet, 0 );
|
||||
}
|
||||
|
||||
update_ui_timers()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
level endon ( "disconnect" );
|
||||
level endon ( "nuke_cancelled" );
|
||||
level endon ( "nuke_death" );
|
||||
|
||||
nukeEndMilliseconds = (level.nukeTimer * 1000) + gettime();
|
||||
SetOmnvar( "ui_nuke_end_milliseconds", nukeEndMilliseconds );
|
||||
|
||||
level waittill( "host_migration_begin" );
|
||||
|
||||
timePassed = maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if ( timePassed > 0 )
|
||||
{
|
||||
SetOmnvar( "ui_nuke_end_milliseconds", nukeEndMilliseconds + timePassed );
|
||||
}
|
||||
}
|
||||
|
||||
updateNukeVisionOnHostMigration()
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "host_migration_end" );
|
||||
|
||||
level setNukeAftermathVision(0);
|
||||
}
|
||||
}
|
||||
|
||||
setNukeAftermathVision( transitionTime )
|
||||
{
|
||||
// we should probably move these callbacks into level specific fog files for aftermath
|
||||
if( IsDefined(level.nukeDeathVisionFunc) )
|
||||
{
|
||||
level thread [[ level.nukeDeathVisionFunc ]]();
|
||||
}
|
||||
else
|
||||
{
|
||||
VisionSetPostApply( level.nukeVisionSet, transitionTime );
|
||||
}
|
||||
}
|
1376
maps/mp/killstreaks/_odin.gsc
Normal file
1376
maps/mp/killstreaks/_odin.gsc
Normal file
File diff suppressed because it is too large
Load Diff
155
maps/mp/killstreaks/_perkstreaks.gsc
Normal file
155
maps/mp/killstreaks/_perkstreaks.gsc
Normal file
@ -0,0 +1,155 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*
|
||||
Perks as killstreaks:
|
||||
The player will earn the killstreak and automatically get the perk.
|
||||
This has been repurposed to be used for very simple killstreaks that are perk-like.
|
||||
*/
|
||||
|
||||
KILLSTREAK_GIMME_SLOT = 0;
|
||||
KILLSTREAK_SLOT_1 = 1;
|
||||
KILLSTREAK_SLOT_2 = 2;
|
||||
KILLSTREAK_SLOT_3 = 3;
|
||||
KILLSTREAK_ALL_PERKS_SLOT = 4;
|
||||
KILLSTREAK_STACKING_START_SLOT = 5;
|
||||
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs[ "specialty_fastsprintrecovery_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_fastreload_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_lightweight_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_marathon_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_stalker_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_reducedsway_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_quickswap_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_pitcher_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_bulletaccuracy_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_quickdraw_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_sprintreload_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_silentkill_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_blindeye_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_gpsjammer_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_quieter_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_incog_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_paint_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_scavenger_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_detectexplosive_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_selectivehearing_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_comexp_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_falldamage_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_regenfaster_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_sharp_focus_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_stun_resistance_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "_specialty_blastshield_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_gunsmith_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_extraammo_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_extra_equipment_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_extra_deadly_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_extra_attachment_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_explosivedamage_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_gambler_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_hardline_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_twoprimaries_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_boom_ks" ] = ::tryUsePerkStreak;
|
||||
level.killStreakFuncs[ "specialty_deadeye_ks" ] = ::tryUsePerkStreak;
|
||||
|
||||
level.killStreakFuncs[ "all_perks_bonus" ] = ::tryUseAllPerks;
|
||||
|
||||
level.killStreakFuncs[ "speed_boost" ] = ::tryUseSpeedBoost;
|
||||
level.killStreakFuncs[ "refill_grenades" ] = ::tryUseRefillGrenades;
|
||||
level.killStreakFuncs[ "refill_ammo" ] = ::tryUseRefillAmmo;
|
||||
level.killStreakFuncs[ "regen_faster" ] = ::tryUseRegenFaster;
|
||||
}
|
||||
|
||||
tryUseSpeedBoost( lifeId, streakName )
|
||||
{
|
||||
self doKillstreakFunctions( "specialty_juiced", "speed_boost" );
|
||||
return true;
|
||||
}
|
||||
|
||||
tryUseRefillGrenades( lifeId, streakName )
|
||||
{
|
||||
self doKillstreakFunctions( "specialty_refill_grenades", "refill_grenades" );
|
||||
return true;
|
||||
}
|
||||
|
||||
tryUseRefillAmmo( lifeId, streakName )
|
||||
{
|
||||
self doKillstreakFunctions( "specialty_refill_ammo", "refill_ammo" );
|
||||
return true;
|
||||
}
|
||||
|
||||
tryUseRegenFaster( lifeId, streakName )
|
||||
{
|
||||
self doKillstreakFunctions( "specialty_regenfaster", "regen_faster" );
|
||||
return true;
|
||||
}
|
||||
|
||||
tryUseAllPerks( lifeId, streakName )
|
||||
{
|
||||
// left blank on purpose
|
||||
return true;
|
||||
}
|
||||
|
||||
tryUsePerkStreak( lifeId, streakName )
|
||||
{
|
||||
AssertEx( IsDefined( streakName ), "tryUsePerkStreak needs a streakName instead of undefined" );
|
||||
perk = strip_suffix( streakName, "_ks" );
|
||||
self doPerkFunctions( perk );
|
||||
return true;
|
||||
}
|
||||
|
||||
doPerkFunctions( perkName )
|
||||
{
|
||||
self givePerk( perkName, false );
|
||||
self thread watchDeath( perkName );
|
||||
self thread checkForPerkUpgrade( perkName );
|
||||
|
||||
if( perkName == "specialty_hardline" )
|
||||
self maps\mp\killstreaks\_killstreaks::setStreakCountToNext();
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( perkName + "_ks", self.origin );
|
||||
}
|
||||
|
||||
doKillstreakFunctions( perkName, killstreakEvent )
|
||||
{
|
||||
self givePerk( perkName, false );
|
||||
|
||||
if( IsDefined( killstreakEvent ) )
|
||||
self maps\mp\_matchdata::logKillstreakEvent( killstreakEvent, self.origin );
|
||||
}
|
||||
|
||||
watchDeath( perkName )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
self waittill( "death" );
|
||||
self _unsetPerk( perkName );
|
||||
//self _unsetExtraPerks( perkName );
|
||||
}
|
||||
|
||||
checkForPerkUpgrade( perkName )
|
||||
{
|
||||
// check for pro version
|
||||
perk_upgrade = self maps\mp\gametypes\_class::getPerkUpgrade( perkName );
|
||||
if( perk_upgrade != "specialty_null" )
|
||||
{
|
||||
self givePerk( perk_upgrade, false );
|
||||
self thread watchDeath( perk_upgrade );
|
||||
}
|
||||
}
|
||||
|
||||
isPerkStreakOn( streakName ) // self == player
|
||||
{
|
||||
// return whether the perk is available right now or not
|
||||
for( i = KILLSTREAK_SLOT_1; i < KILLSTREAK_SLOT_3 + 1; i++ )
|
||||
{
|
||||
if( IsDefined( self.pers[ "killstreaks" ][ i ].streakName ) && self.pers[ "killstreaks" ][ i ].streakName == streakName )
|
||||
{
|
||||
if( self.pers[ "killstreaks" ][ i ].available )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
705
maps/mp/killstreaks/_placeable.gsc
Normal file
705
maps/mp/killstreaks/_placeable.gsc
Normal file
@ -0,0 +1,705 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !IsDefined( level.placeableConfigs ) )
|
||||
{
|
||||
level.placeableConfigs = [];
|
||||
}
|
||||
}
|
||||
|
||||
givePlaceable( streakName ) // self == player
|
||||
{
|
||||
placeable = self createPlaceable( streakName );
|
||||
|
||||
// returning from this streak activation seems to strip this?
|
||||
// manually removing and restoring
|
||||
self removePerks();
|
||||
|
||||
self.carriedItem = placeable;
|
||||
|
||||
result = self onBeginCarrying( streakName, placeable, true );
|
||||
|
||||
self.carriedItem = undefined;
|
||||
|
||||
self restorePerks();
|
||||
|
||||
// if the placeable exist, then it was placed
|
||||
return ( IsDefined( placeable ) );
|
||||
}
|
||||
|
||||
createPlaceable( streakName )
|
||||
{
|
||||
if( IsDefined( self.isCarrying ) && self.isCarrying )
|
||||
return;
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
obj = Spawn( "script_model", self.origin );
|
||||
obj SetModel( config.modelBase );
|
||||
obj.angles = self.angles;
|
||||
obj.owner = self;
|
||||
obj.team = self.team;
|
||||
obj.config = config;
|
||||
obj.firstPlacement = true;
|
||||
|
||||
/*
|
||||
obj = SpawnTurret( "misc_turret", self.origin + ( 0, 0, 25 ), "sentry_minigun_mp" );
|
||||
|
||||
obj.angles = self.angles;
|
||||
obj.owner = self;
|
||||
|
||||
obj SetModel( config.modelBase );
|
||||
|
||||
obj MakeTurretInoperable();
|
||||
obj SetTurretModeChangeWait( true );
|
||||
obj SetMode( "sentry_offline" );
|
||||
obj MakeUnusable();
|
||||
obj SetSentryOwner( self );
|
||||
*/
|
||||
|
||||
// inits happen here
|
||||
if ( IsDefined( config.onCreateDelegate ) )
|
||||
{
|
||||
obj [[ config.onCreateDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
obj deactivate( streakName );
|
||||
|
||||
obj thread timeOut( streakName );
|
||||
obj thread handleUse( streakName );
|
||||
|
||||
obj thread onKillstreakDisowned( streakName );
|
||||
obj thread onGameEnded( streakName );
|
||||
|
||||
obj thread createBombSquadModel( streakName );
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
|
||||
handleUse( streakName ) // self == placeable
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill ( "trigger", player );
|
||||
|
||||
assert( player == self.owner );
|
||||
assert( !IsDefined( self.carriedBy ) );
|
||||
|
||||
if ( !isReallyAlive( player ) )
|
||||
continue;
|
||||
|
||||
if ( IsDefined( self GetLinkedParent() ) )
|
||||
{
|
||||
self Unlink();
|
||||
}
|
||||
|
||||
// why does the IMS create a second one?
|
||||
|
||||
player onBeginCarrying( streakName, self, false );
|
||||
}
|
||||
}
|
||||
|
||||
// setCarrying
|
||||
onBeginCarrying( streakName, placeable, allowCancel ) // self == player
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
|
||||
assert( isReallyAlive( self ) );
|
||||
|
||||
placeable thread onCarried( streakName, self );
|
||||
|
||||
self _disableWeapon();
|
||||
|
||||
if ( !IsAI(self) ) // Bots handle these internally
|
||||
{
|
||||
self notifyOnPlayerCommand( "placePlaceable", "+attack" );
|
||||
self notifyOnPlayerCommand( "placePlaceable", "+attack_akimbo_accessible" ); // support accessibility control scheme
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 4" );
|
||||
if( !level.console )
|
||||
{
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 5" );
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 6" );
|
||||
self notifyOnPlayerCommand( "cancelPlaceable", "+actionslot 7" );
|
||||
}
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
result = waittill_any_return( "placePlaceable", "cancelPlaceable", "force_cancel_placement" );
|
||||
|
||||
// object was deleted
|
||||
if ( !IsDefined( placeable ) )
|
||||
{
|
||||
self _enableWeapon();
|
||||
return true;
|
||||
}
|
||||
// !!! 2013-08-08 wallace: this force cancel problem is really ugly
|
||||
// it's also being used to indicate player wading in water, which we should use the "bad placement" model instead of canceling outright. But it's too late to fix now.
|
||||
else if ( (result == "cancelPlaceable" && allowCancel)
|
||||
|| result == "force_cancel_placement" )
|
||||
{
|
||||
placeable onCancel( streakName, result == "force_cancel_placement" && !IsDefined( placeable.firstPlacement ) );
|
||||
return false;
|
||||
}
|
||||
else if ( placeable.canBePlaced )
|
||||
{
|
||||
placeable thread onPlaced( streakName );
|
||||
self _enableWeapon();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onCancel( streakName, playDestroyVfx ) // self == placeable
|
||||
{
|
||||
if( IsDefined( self.carriedBy ) )
|
||||
{
|
||||
owner = self.carriedBy;
|
||||
owner ForceUseHintOff();
|
||||
owner.isCarrying = undefined;
|
||||
|
||||
owner.carriedItem = undefined;
|
||||
|
||||
owner _enableWeapon();
|
||||
}
|
||||
|
||||
if( IsDefined( self.bombSquadModel ) )
|
||||
{
|
||||
self.bombSquadModel Delete();
|
||||
}
|
||||
|
||||
if ( IsDefined( self.carriedObj ) )
|
||||
{
|
||||
self.carriedObj Delete();
|
||||
}
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
if ( IsDefined( config.onCancelDelegate ) )
|
||||
{
|
||||
self [[ config.onCancelDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
if ( IsDefined( playDestroyVfx ) && playDestroyVfx )
|
||||
{
|
||||
self maps\mp\gametypes\_weapons::equipmentDeleteVfx();
|
||||
}
|
||||
|
||||
self Delete();
|
||||
}
|
||||
|
||||
onPlaced( streakName ) // self == placeable
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
self.origin = self.placementOrigin;
|
||||
self.angles = self.carriedObj.angles;
|
||||
|
||||
self PlaySound( config.placedSfx );
|
||||
|
||||
self showPlacedModel( streakName );
|
||||
|
||||
if ( IsDefined( config.onPlacedDelegate ) )
|
||||
{
|
||||
self [[ config.onPlacedDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
self setCursorHint( "HINT_NOICON" );
|
||||
self setHintString( config.hintString );
|
||||
|
||||
owner = self.owner;
|
||||
owner ForceUseHintOff();
|
||||
owner.isCarrying = undefined;
|
||||
self.carriedBy = undefined;
|
||||
self.isPlaced = true;
|
||||
self.firstPlacement = undefined;
|
||||
|
||||
if ( IsDefined( config.headIconHeight ) )
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( self.team, (0,0,config.headIconHeight) );
|
||||
}
|
||||
else
|
||||
{
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( owner, (0,0,config.headIconHeight) );
|
||||
}
|
||||
}
|
||||
|
||||
self thread handleDamage( streakName );
|
||||
self thread handleDeath( streakName );
|
||||
|
||||
self MakeUsable();
|
||||
|
||||
self make_entity_sentient_mp( self.owner.team );
|
||||
if ( IsSentient( self ) )
|
||||
{
|
||||
self SetThreatBiasGroup( "DogsDontAttack" );
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if( player == owner )
|
||||
self EnablePlayerUse( player );
|
||||
else
|
||||
self DisablePlayerUse( player );
|
||||
}
|
||||
|
||||
if( IsDefined( self.shouldSplash ) )
|
||||
{
|
||||
level thread teamPlayerCardSplash( config.splashName, owner );
|
||||
self.shouldSplash = false;
|
||||
}
|
||||
|
||||
// Moving platforms.
|
||||
data = SpawnStruct();
|
||||
data.linkParent = self.moving_platform;
|
||||
data.playDeathFx = true;
|
||||
data.endonString = "carried";
|
||||
if ( IsDefined( config.onMovingPlatformCollision ) )
|
||||
{
|
||||
data.deathOverrideCallback = config.onMovingPlatformCollision;
|
||||
}
|
||||
self thread maps\mp\_movers::handle_moving_platforms( data );
|
||||
|
||||
self thread watchPlayerConnected();
|
||||
|
||||
self notify ( "placed" );
|
||||
|
||||
self.carriedObj Delete();
|
||||
self.carriedObj = undefined;
|
||||
}
|
||||
|
||||
onCarried( streakName, carrier ) // self == placeable
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
assert( isPlayer( carrier ) );
|
||||
assertEx( carrier == self.owner, "_placeable::onCarried: specified carrier does not own this ims" );
|
||||
|
||||
self.carriedObj = carrier createCarriedObject( streakName );
|
||||
|
||||
// self SetModel( config.modelPlacement );
|
||||
|
||||
self.isPlaced = undefined;
|
||||
self.carriedBy = carrier;
|
||||
carrier.isCarrying = true;
|
||||
|
||||
self deactivate( streakName );
|
||||
|
||||
self hidePlacedModel( streakName );
|
||||
|
||||
if ( IsDefined( config.onCarriedDelegate ) )
|
||||
{
|
||||
self [[ config.onCarriedDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
self thread updatePlacement( streakName, carrier );
|
||||
|
||||
self thread onCarrierDeath( streakName, carrier );
|
||||
|
||||
self notify ( "carried" );
|
||||
}
|
||||
|
||||
updatePlacement( streakName, carrier ) // self == placeable
|
||||
{
|
||||
carrier endon ( "death" );
|
||||
carrier endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self endon ( "placed" );
|
||||
self endon ( "death" );
|
||||
|
||||
self.canBePlaced = true;
|
||||
prevCanBePlaced = -1; // force initial update
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
// allow the visuals be raised up slightly (e.g. iw6 Sat Com, so that player can see it)
|
||||
placementOffset = (0 ,0, 0);
|
||||
if ( IsDefined( config.placementOffsetZ ) )
|
||||
{
|
||||
placementOffset = (0 ,0 ,config.placementOffsetZ);
|
||||
}
|
||||
|
||||
carriedObj = self.carriedObj;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
placement = carrier CanPlayerPlaceSentry( true, config.placementRadius );
|
||||
|
||||
// NOTE TO SELF: Talk to Simon C about how to get vertical offset / additional rotation working with client prediction
|
||||
self.placementOrigin = placement[ "origin" ];
|
||||
carriedObj.origin = self.placementOrigin + placementOffset;
|
||||
carriedObj.angles = placement[ "angles" ];
|
||||
|
||||
self.canBePlaced = carrier IsOnGround()
|
||||
&& placement[ "result" ]
|
||||
&& ( abs(self.placementOrigin[2] - carrier.origin[2]) < config.placementHeightTolerance );
|
||||
|
||||
if ( isdefined( placement[ "entity" ] ) )
|
||||
{
|
||||
self.moving_platform = placement[ "entity" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
self.moving_platform = undefined;
|
||||
}
|
||||
|
||||
if ( self.canBePlaced != prevCanBePlaced )
|
||||
{
|
||||
if ( self.canBePlaced )
|
||||
{
|
||||
carriedObj SetModel( config.modelPlacement );
|
||||
carrier ForceUseHintOn( config.placeString );
|
||||
}
|
||||
else
|
||||
{
|
||||
carriedObj SetModel( config.modelPlacementFailed );
|
||||
carrier ForceUseHintOn( config.cannotPlaceString );
|
||||
}
|
||||
}
|
||||
|
||||
prevCanBePlaced = self.canBePlaced;
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
deactivate( streakName ) // self == placeable
|
||||
{
|
||||
self MakeUnusable();
|
||||
|
||||
self hideHeadIcons();
|
||||
|
||||
self FreeEntitySentient();
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
if ( IsDefined( config.onDeactiveDelegate ) )
|
||||
{
|
||||
self [[ config.onDeactiveDelegate ]]( streakName );
|
||||
}
|
||||
}
|
||||
|
||||
hideHeadIcons()
|
||||
{
|
||||
if ( level.teamBased )
|
||||
{
|
||||
self maps\mp\_entityheadicons::setTeamHeadIcon( "none", ( 0, 0, 0 ) );
|
||||
}
|
||||
else if ( IsDefined( self.owner ) )
|
||||
{
|
||||
self maps\mp\_entityheadicons::setPlayerHeadIcon( undefined, ( 0, 0, 0 ) );
|
||||
}
|
||||
}
|
||||
|
||||
// important callbacks:
|
||||
// onDamagedDelegate - filter out or amplify damage based on specifics
|
||||
// onDestroyedDelegate - any extra handling when the object is killed
|
||||
handleDamage( streakName ) // self == placeable
|
||||
{
|
||||
self endon( "carried" );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
self maps\mp\gametypes\_damage::monitorDamage(
|
||||
config.maxHealth,
|
||||
config.damageFeedback,
|
||||
::handleDeathDamage,
|
||||
::modifyDamage,
|
||||
true // isKillstreak
|
||||
);
|
||||
}
|
||||
|
||||
modifyDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
modifiedDamage = damage;
|
||||
|
||||
config = self.config;
|
||||
if ( IsDefined( config.allowMeleeDamage ) && config.allowMeleeDamage )
|
||||
{
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
||||
}
|
||||
|
||||
if ( IsDefined( config.allowEmpDamage ) && config.allowEmpDamage )
|
||||
{
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleEmpDamage( weapon, type, modifiedDamage );
|
||||
}
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleGrenadeDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
||||
|
||||
if ( IsDefined( config.modifyDamage ) )
|
||||
{
|
||||
modifiedDamage = self [[ config.modifyDamage ]]( weapon, type, modifiedDamage );
|
||||
}
|
||||
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
handleDeathDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
config = self.config;
|
||||
|
||||
notifyAttacker = self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, config.xpPopup, config.destroyedVO );
|
||||
if ( notifyAttacker
|
||||
&& IsDefined( config.onDestroyedDelegate )
|
||||
)
|
||||
{
|
||||
self [[ config.onDestroyedDelegate ]]( self.streakName, attacker, self.owner, type );
|
||||
}
|
||||
}
|
||||
|
||||
handleDeath( streakName )
|
||||
{
|
||||
self endon( "carried" );
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
// this handles cases of deletion
|
||||
if ( IsDefined( self ) )
|
||||
{
|
||||
// play sound
|
||||
|
||||
self deactivate( streakName );
|
||||
|
||||
// set destroyed model
|
||||
if ( IsDefined( config.modelDestroyed ) )
|
||||
{
|
||||
self SetModel( config.modelDestroyed );
|
||||
}
|
||||
|
||||
// or do it in the callbacks?
|
||||
|
||||
if ( IsDefined( config.onDeathDelegate ) )
|
||||
{
|
||||
self [[ config.onDeathDelegate ]]( streakName );
|
||||
}
|
||||
|
||||
self Delete();
|
||||
}
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
onCarrierDeath( streakName, carrier ) // self == placeable
|
||||
{
|
||||
self endon ( "placed" );
|
||||
self endon ( "death" );
|
||||
carrier endon( "disconnect" );
|
||||
|
||||
carrier waittill ( "death" );
|
||||
|
||||
if ( self.canBePlaced )
|
||||
{
|
||||
self thread onPlaced( streakName );
|
||||
}
|
||||
else
|
||||
{
|
||||
self onCancel( streakName );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
onKillstreakDisowned( streakName ) // self == placeable
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
self.owner waittill ( "killstreak_disowned" );
|
||||
|
||||
self cleanup( streakName );
|
||||
}
|
||||
|
||||
onGameEnded( streakName ) // self == placeable
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
level waittill ( "game_ended" );
|
||||
|
||||
self cleanup( streakName );
|
||||
}
|
||||
|
||||
cleanup( streakName ) // self == placeable
|
||||
{
|
||||
if ( IsDefined( self.isPlaced ) )
|
||||
{
|
||||
self notify( "death" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self onCancel( streakName );
|
||||
}
|
||||
}
|
||||
|
||||
watchPlayerConnected() // self == ims
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
// when new players connect they need to not be able to use the planted ims
|
||||
level waittill( "connected", player );
|
||||
self thread onPlayerConnected( player );
|
||||
}
|
||||
}
|
||||
|
||||
onPlayerConnected( owner ) // self == placeable
|
||||
{
|
||||
self endon( "death" );
|
||||
owner endon( "disconnect" );
|
||||
|
||||
owner waittill( "spawned_player" );
|
||||
|
||||
// this can't possibly be the owner because the ims is destroyed if the owner leaves the game, so disable use for this player
|
||||
self DisablePlayerUse( owner );
|
||||
}
|
||||
|
||||
timeOut( streakName )
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
lifeSpan = config.lifeSpan;
|
||||
|
||||
while ( lifeSpan > 0.0 )
|
||||
{
|
||||
wait ( 1.0 );
|
||||
maps\mp\gametypes\_hostmigration::waitTillHostMigrationDone();
|
||||
|
||||
if ( !IsDefined( self.carriedBy ) )
|
||||
{
|
||||
lifeSpan -= 1.0;
|
||||
}
|
||||
}
|
||||
|
||||
if ( IsDefined( self.owner ) && IsDefined( config.goneVO ) )
|
||||
{
|
||||
self.owner thread leaderDialogOnPlayer( config.goneVO );
|
||||
}
|
||||
|
||||
self notify ( "death" );
|
||||
}
|
||||
|
||||
//--------------------------------------------------------------------
|
||||
removeWeapons()
|
||||
{
|
||||
if ( self HasWeapon( "iw6_riotshield_mp" ) )
|
||||
{
|
||||
self.restoreWeapon = "iw6_riotshield_mp";
|
||||
self takeWeapon( "iw6_riotshield_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
removePerks()
|
||||
{
|
||||
if ( self _hasPerk( "specialty_explosivebullets" ) )
|
||||
{
|
||||
self.restorePerk = "specialty_explosivebullets";
|
||||
self _unsetPerk( "specialty_explosivebullets" );
|
||||
}
|
||||
}
|
||||
|
||||
restoreWeapons()
|
||||
{
|
||||
if ( IsDefined( self.restoreWeapon ) )
|
||||
{
|
||||
self _giveWeapon( self.restoreWeapon );
|
||||
self.restoreWeapon = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
restorePerks()
|
||||
{
|
||||
if ( IsDefined( self.restorePerk ) )
|
||||
{
|
||||
self givePerk( self.restorePerk, false );
|
||||
self.restorePerk = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
createBombSquadModel( streakName ) // self == box
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
|
||||
if ( IsDefined( config.modelBombSquad ) )
|
||||
{
|
||||
bombSquadModel = Spawn( "script_model", self.origin );
|
||||
bombSquadModel.angles = self.angles;
|
||||
bombSquadModel Hide();
|
||||
|
||||
bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( self.owner );
|
||||
bombSquadModel SetModel( config.modelBombSquad );
|
||||
bombSquadModel LinkTo( self );
|
||||
bombSquadModel SetContents( 0 );
|
||||
self.bombSquadModel = bombSquadModel;
|
||||
|
||||
self waittill ( "death" );
|
||||
|
||||
// Could have been deleted when the player was carrying the ims and died
|
||||
if ( IsDefined( bombSquadModel ) )
|
||||
{
|
||||
bombSquadModel delete();
|
||||
self.bombSquadModel = undefined;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
showPlacedModel( streakname )
|
||||
{
|
||||
self Show();
|
||||
|
||||
if ( IsDefined( self.bombSquadModel ) )
|
||||
{
|
||||
self.bombSquadModel Show();
|
||||
level notify( "update_bombsquad" );
|
||||
}
|
||||
}
|
||||
|
||||
hidePlacedModel( streakName )
|
||||
{
|
||||
self Hide();
|
||||
|
||||
if ( IsDefined( self.bombSquadModel ) )
|
||||
{
|
||||
self.bombSquadModel Hide();
|
||||
}
|
||||
}
|
||||
|
||||
createCarriedObject( streakName )
|
||||
{
|
||||
assertEx( IsDefined( self ), "createIMSForPlayer() called without owner specified" );
|
||||
|
||||
// need to make sure we aren't already carrying, this fixes a bug where you could start to pull a new one out as you picked the old one up
|
||||
// this resulted in you being able to plant one and then pull your gun out while having another one attached to you like you're carrying it
|
||||
if( IsDefined( self.isCarrying ) && self.isCarrying )
|
||||
return;
|
||||
|
||||
carriedObj = SpawnTurret( "misc_turret", self.origin + ( 0, 0, 25 ), "sentry_minigun_mp" );
|
||||
|
||||
carriedObj.angles = self.angles;
|
||||
carriedObj.owner = self;
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
carriedObj SetModel( config.modelBase );
|
||||
|
||||
carriedObj MakeTurretInoperable();
|
||||
carriedObj SetTurretModeChangeWait( true );
|
||||
carriedObj SetMode( "sentry_offline" );
|
||||
carriedObj MakeUnusable();
|
||||
carriedObj SetSentryOwner( self );
|
||||
carriedObj SetSentryCarrier( self );
|
||||
|
||||
carriedObj SetCanDamage( false );
|
||||
carriedObj SetContents( 0 );
|
||||
|
||||
return carriedObj;
|
||||
}
|
158
maps/mp/killstreaks/_placeable_barrier.gsc
Normal file
158
maps/mp/killstreaks/_placeable_barrier.gsc
Normal file
@ -0,0 +1,158 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
KS_NAME = "placeable_barrier";
|
||||
init()
|
||||
{
|
||||
//PreCacheModel( "portable_barrier" );
|
||||
//PreCacheModel( "portable_barrier_obj" );
|
||||
//PreCacheModel( "portable_barrier_obj_red" );
|
||||
|
||||
config = spawnStruct();
|
||||
config.streakName = KS_NAME;
|
||||
config.weaponInfo = "ims_projectile_mp";
|
||||
// config.modelBase = "ims_scorpion_body";
|
||||
config.modelBase = "placeable_barrier";
|
||||
config.modelDestroyed = "placeable_barrier_destroyed";
|
||||
config.modelPlacement = "placeable_barrier_obj";
|
||||
config.modelPlacementFailed = "placeable_barrier_obj_red";
|
||||
// config.modelDestroyed = "ims_scorpion_body";
|
||||
config.hintString = &"KILLSTREAKS_HINTS_PLACEABLE_COVER_PICKUP";
|
||||
config.placeString = &"KILLSTREAKS_HINTS_PLACEABLE_COVER_PLACE";
|
||||
config.cannotPlaceString = &"KILLSTREAKS_HINTS_PLACEABLE_COVER_CANNOT_PLACE";
|
||||
config.headIconHeight = 75;
|
||||
config.splashName = "used_placeable_barrier";
|
||||
config.lifeSpan = 60.0;
|
||||
// config.goneVO = "ims_gone";
|
||||
config.maxHealth = 500;
|
||||
config.allowMeleeDamage = false;
|
||||
config.damageFeedback = "ims";
|
||||
config.xpPopup = "destroyed_ims";
|
||||
config.destroyedVO = "ims_destroyed";
|
||||
//config.onCreateDelegate = ::createObject;
|
||||
config.onPlacedDelegate = ::onPlaced;
|
||||
config.onCarriedDelegate = ::onCarried;
|
||||
config.placedSfx = "ims_plant";
|
||||
config.onDamagedDelegate = ::onDamaged;
|
||||
//config.onDestroyedDelegate = ::onDestroyed;
|
||||
config.onDeathDelegate = ::onDeath;
|
||||
config.deathVfx = loadfx( "vfx/gameplay/mp/killstreaks/vfx_ballistic_vest_death" );
|
||||
// config.onDeactivateDelegate = ::onDeactivated;
|
||||
// config.onActivateDelegate = ::onActivated;
|
||||
config.colRadius = 72;
|
||||
config.colHeight = 36;
|
||||
|
||||
level.placeableConfigs[ KS_NAME ] = config;
|
||||
|
||||
setupBrushModel();
|
||||
|
||||
level.killStreakFuncs[ KS_NAME ] = ::tryUsePlaceable;
|
||||
}
|
||||
|
||||
tryUsePlaceable( lifeId, streakName ) // self == player
|
||||
{
|
||||
result = self maps\mp\killstreaks\_placeable::givePlaceable( KS_NAME );
|
||||
|
||||
if( result )
|
||||
{
|
||||
self maps\mp\_matchdata::logKillstreakEvent( KS_NAME, 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;
|
||||
}
|
||||
|
||||
createObject( streakName ) // self == barrier
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
onPlaced( streakName ) // self == barrier
|
||||
{
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
self setModel( config.modelBase );
|
||||
|
||||
// turn on collisions
|
||||
// self SetContents( 1 );
|
||||
|
||||
collision = spawn_tag_origin();
|
||||
collision Show();
|
||||
collision.origin = self.origin;
|
||||
|
||||
//AssertMsg( IsDefined( level.barrierCollision ), "missing barrier_collision brush model for placeable_barrier" );
|
||||
if ( !IsDefined( level.barrierCollision ) )
|
||||
{
|
||||
setupBrushModel();
|
||||
}
|
||||
collision CloneBrushmodelToScriptmodel( level.barrierCollision );
|
||||
|
||||
otherTeam = getOtherTeam( self.owner.team );
|
||||
BadPlace_Cylinder( streakName + (self GetEntityNumber()), -1, self.origin, config.colRadius, config.colHeight, otherTeam );
|
||||
|
||||
self.collision = collision;
|
||||
}
|
||||
|
||||
onCarried( streakName ) // self == barrier
|
||||
{
|
||||
self disableCollision( streakName );
|
||||
}
|
||||
|
||||
onDamaged( streakname, attacker, owner, damage )
|
||||
{
|
||||
return damage;
|
||||
}
|
||||
|
||||
onDestroyed( streakName, attacker, owner, sMeansOfDeath ) // self == barrier / victim
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
onDeath( streakName )
|
||||
{
|
||||
self disableCollision( streakName );
|
||||
|
||||
config = level.placeableConfigs[ streakName ];
|
||||
if ( IsDefined( config.deathSfx ) )
|
||||
{
|
||||
self PlaySound( config.deathSfx );
|
||||
}
|
||||
|
||||
PlayFX( config.deathVfx, self.origin );
|
||||
|
||||
wait( 0.5 );
|
||||
}
|
||||
|
||||
disableCollision( streakName )
|
||||
{
|
||||
// turn off the path blocker
|
||||
if ( IsDefined( self.collision ) )
|
||||
{
|
||||
BadPlace_Delete( streakName + (self GetEntityNumber()) );
|
||||
|
||||
// self.collision ConnectPaths();
|
||||
self.collision Delete();
|
||||
|
||||
self.collision = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
setupBrushModel()
|
||||
{
|
||||
scriptModel = GetEnt( "barrier_collision", "targetname" );
|
||||
|
||||
if ( IsDefined ( scriptmodel ) )
|
||||
{
|
||||
level.barrierCollision = GetEnt( scriptModel.target, "targetname" );
|
||||
scriptModel Delete();
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.barrierCollision ) )
|
||||
{
|
||||
Print( "!!! level does not contain a barrier_collision script model, please add one!" );
|
||||
level.barrierCollision = level.airDropCrateCollision;
|
||||
}
|
||||
}
|
444
maps/mp/killstreaks/_plane.gsc
Normal file
444
maps/mp/killstreaks/_plane.gsc
Normal file
@ -0,0 +1,444 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
// _plane.gsc
|
||||
// a modular component intended to control a plane flying overhead
|
||||
// adapted from _airstrike.gsc
|
||||
kPlaneHealth = 800;
|
||||
|
||||
init()
|
||||
{
|
||||
if ( !IsDefined( level.planes ) )
|
||||
{
|
||||
level.planes = [];
|
||||
}
|
||||
|
||||
if ( !IsDefined( level.planeConfigs ) )
|
||||
{
|
||||
level.planeConfigs = [];
|
||||
}
|
||||
|
||||
level.fighter_deathfx = LoadFX( "vfx/gameplay/explosions/vehicle/hind_mp/vfx_x_mphnd_primary" );
|
||||
level.fx_airstrike_afterburner = loadfx ("vfx/gameplay/mp/killstreaks/vfx_air_superiority_afterburner");
|
||||
level.fx_airstrike_contrail = loadfx ("vfx/gameplay/mp/killstreaks/vfx_aircraft_contrail");
|
||||
level.fx_airstrike_wingtip_light_green = LoadFX ( "vfx/gameplay/mp/killstreaks/vfx_acraft_light_wingtip_green" );
|
||||
level.fx_airstrike_wingtip_light_red = LoadFX ( "vfx/gameplay/mp/killstreaks/vfx_acraft_light_wingtip_red" );
|
||||
|
||||
/*
|
||||
* streakName
|
||||
* modelNames[] // alllied, axis
|
||||
* halfDistance
|
||||
* speed
|
||||
* initialHeight
|
||||
* flightSound
|
||||
* flyTime
|
||||
* attackTime
|
||||
* inboundFlightAnim?
|
||||
* outboundFlightAnim = "airstrike_mp"
|
||||
* onAttackDelegate
|
||||
* killcam stuff?
|
||||
* planeFX?
|
||||
*/
|
||||
}
|
||||
|
||||
getFlightPath( coord, directionVector, planeHalfDistance, absoluteHeight, planeFlyHeight, planeFlySpeed, attackDistance, streakName )
|
||||
{
|
||||
// stealth_airstrike moves this a lot more
|
||||
startPoint = coord + ( directionVector * ( -1 * planeHalfDistance ) );
|
||||
endPoint = coord + ( directionVector * planeHalfDistance );
|
||||
|
||||
if ( absoluteHeight ) // used in the new height system
|
||||
{
|
||||
startPoint *= (1, 1, 0);
|
||||
endPoint *= (1, 1, 0);
|
||||
}
|
||||
|
||||
startPoint += ( 0, 0, planeFlyHeight );
|
||||
endPoint += ( 0, 0, planeFlyHeight );
|
||||
|
||||
// Make the plane fly by
|
||||
d = length( startPoint - endPoint );
|
||||
flyTime = ( d / planeFlySpeed );
|
||||
|
||||
// bomb explodes planeBombExplodeDistance after the plane passes the center
|
||||
d = abs( 0.5 * d + attackDistance );
|
||||
attackTime = ( d / planeFlySpeed );
|
||||
|
||||
assert( flyTime > attackTime );
|
||||
|
||||
flightPath["startPoint"] = startPoint;
|
||||
flightPath["endPoint"] = endPoint;
|
||||
flightPath["attackTime"] = attackTime;
|
||||
flightPath["flyTime"] = flyTime;
|
||||
|
||||
return flightPath;
|
||||
}
|
||||
|
||||
//doPlaneStrike( lifeId, owner, requiredDeathCount, bombsite, startPoint, endPoint, bombTime, flyTime, direction, streakName )
|
||||
doFlyby( lifeId, owner, requiredDeathCount, startPoint, endPoint, attackTime, flyTime, directionVector, streakName )
|
||||
{
|
||||
plane = planeSpawn( lifeId, owner, startPoint, directionVector, streakName );
|
||||
|
||||
plane endon( "death" );
|
||||
|
||||
// plane spawning randomness = up to 125 units, biased towards 0
|
||||
// radius of bomb damage is 512
|
||||
endPathRandomness = 150;
|
||||
pathEnd = endPoint + ( (RandomFloat(2) - 1) * endPathRandomness, (RandomFloat(2) - 1) * endPathRandomness, 0 );
|
||||
|
||||
plane planeMove( pathEnd, flyTime, attackTime, streakName );
|
||||
|
||||
plane planecleanup();
|
||||
}
|
||||
|
||||
planeSpawn( lifeId, owner, startPoint, directionVector, streakName )
|
||||
{
|
||||
if ( !isDefined( owner ) )
|
||||
return;
|
||||
|
||||
startPathRandomness = 100;
|
||||
pathStart = startPoint + ( (RandomFloat(2) - 1) * startPathRandomness, (RandomFloat(2) - 1) * startPathRandomness, 0 );
|
||||
|
||||
//self thread DrawLine(pathStart, (AnglesToForward( direction ) * 200000), 120, (1,0,1) );
|
||||
|
||||
configData = level.planeConfigs[ streakName ];
|
||||
|
||||
plane = undefined;
|
||||
|
||||
plane = Spawn( "script_model", pathStart );
|
||||
plane.team = owner.team;
|
||||
plane.origin = pathStart;
|
||||
plane.angles = VectorToAngles( directionVector );
|
||||
plane.lifeId = lifeId;
|
||||
plane.streakName = streakName;
|
||||
plane.owner = owner;
|
||||
|
||||
plane SetModel( configData.modelNames[ owner.team ] );
|
||||
|
||||
if ( IsDefined( configData.compassIconFriendly ) )
|
||||
{
|
||||
plane setObjectiveIcons(configData.compassIconFriendly, configData.compassIconEnemy );
|
||||
}
|
||||
|
||||
plane thread handleDamage();
|
||||
plane thread handleDeath();
|
||||
// handle the nuke instead?
|
||||
// plane thread handleEMP( owner );
|
||||
|
||||
startTrackingPlane( plane );
|
||||
|
||||
// stealth bomber doesn't have effects
|
||||
if ( !IsDefined( configData.noLightFx ) )
|
||||
{
|
||||
plane thread playPlaneFx();
|
||||
}
|
||||
plane PlayLoopSound( configData.inboundSfx );
|
||||
|
||||
plane createKillCam( streakName );
|
||||
|
||||
return plane;
|
||||
}
|
||||
|
||||
planeMove( destination, flyTime, attackTime, streakName ) // self == plane
|
||||
{
|
||||
configData = level.planeConfigs[ streakName ];
|
||||
|
||||
// begin flight
|
||||
self MoveTo( destination, flyTime, 0, 0 );
|
||||
|
||||
// begin attack
|
||||
//thread callStrike_planeSound( plane, bombsite );
|
||||
// hmm, don't like the timing of these flybys
|
||||
if ( IsDefined( configData.onAttackDelegate ) )
|
||||
{
|
||||
self thread [[ configData.onAttackDelegate ]]( destination, flyTime, attackTime, self.owner, streakName );
|
||||
}
|
||||
|
||||
if ( IsDefined( configData.sonicBoomSfx ) )
|
||||
{
|
||||
self thread playSonicBoom( configData.sonicBoomSfx, 0.5 * flyTime );
|
||||
}
|
||||
|
||||
// fly away
|
||||
wait( 0.65 * flyTime );
|
||||
|
||||
if ( IsDefined( configData.outboundSfx ) )
|
||||
{
|
||||
self StopLoopSound();
|
||||
self PlayLoopSound( configData.outboundSfx );
|
||||
}
|
||||
|
||||
if ( IsDefined( configData.outboundFlightAnim ) )
|
||||
{
|
||||
self ScriptModelPlayAnimDeltaMotion( configData.outboundFlightAnim );
|
||||
}
|
||||
|
||||
wait ( 0.35 * flyTime );
|
||||
}
|
||||
|
||||
planeCleanup() // self == plane
|
||||
{
|
||||
configData = level.planeConfigs[ self.streakName ];
|
||||
|
||||
if ( IsDefined( configData.onFlybyCompleteDelegate ) )
|
||||
{
|
||||
thread [[ configData.onFlybyCompleteDelegate ]]( self.owner, self, self.streakName );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.friendlyTeamId ) )
|
||||
{
|
||||
_objective_delete( self.friendlyTeamId );
|
||||
_objective_delete( self.enemyTeamID );
|
||||
}
|
||||
|
||||
if ( IsDefined( self.killCamEnt ) )
|
||||
{
|
||||
self.killCamEnt Delete();
|
||||
}
|
||||
|
||||
stopTrackingPlane( self );
|
||||
|
||||
self notify( "delete" );
|
||||
self delete();
|
||||
}
|
||||
|
||||
handleEMP( owner ) // self == plane
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( owner isEMPed() )
|
||||
{
|
||||
self notify( "death" );
|
||||
return;
|
||||
}
|
||||
|
||||
level waittill ( "emp_update" );
|
||||
}
|
||||
}
|
||||
|
||||
handleDeath() // self == plane
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "delete" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
forward = AnglesToForward( self.angles ) * 200;
|
||||
PlayFX( level.fighter_deathfx, self.origin, forward );
|
||||
|
||||
self thread planeCleanup();
|
||||
}
|
||||
|
||||
handleDamage()
|
||||
{
|
||||
self endon( "end_remote" );
|
||||
|
||||
self maps\mp\gametypes\_damage::monitorDamage(
|
||||
kPlaneHealth, // should be defined elsewhere
|
||||
"helicopter", // should there be a death one?
|
||||
::handleDeathDamage,
|
||||
::modifyDamage,
|
||||
true // isKillstreak
|
||||
);
|
||||
}
|
||||
|
||||
modifyDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
modifiedDamage = damage;
|
||||
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
||||
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
handleDeathDamage( attacker, weapon, type, damage ) // self == plane
|
||||
{
|
||||
config = level.planeConfigs[ self.streakName ];
|
||||
// !!! need VO
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( attacker, weapon, type, damage, config.xpPopup, config.destroyedVO, config.callout );
|
||||
|
||||
maps\mp\gametypes\_missions::checkAAChallenges( attacker, self, weapon );
|
||||
}
|
||||
|
||||
playPlaneFX()
|
||||
{
|
||||
self endon ( "death" );
|
||||
|
||||
wait( 0.5);
|
||||
PlayFXOnTag( level.fx_airstrike_afterburner, self, "tag_engine_right" );
|
||||
wait( 0.5);
|
||||
PlayFXOnTag( level.fx_airstrike_afterburner, self, "tag_engine_left" );
|
||||
wait( 0.5);
|
||||
PlayFXOnTag( level.fx_airstrike_contrail, self, "tag_right_wingtip" );
|
||||
wait( 0.5);
|
||||
PlayFXOnTag( level.fx_airstrike_contrail, self, "tag_left_wingtip" );
|
||||
wait( 0.5);
|
||||
PlayFXOnTag( level.fx_airstrike_wingtip_light_red, self, "tag_right_wingtip" );
|
||||
wait( 0.5);
|
||||
PlayFXOnTag( level.fx_airstrike_wingtip_light_green, self, "tag_left_wingtip" );
|
||||
}
|
||||
|
||||
getPlaneFlyHeight()
|
||||
{
|
||||
heightEnt = GetEnt( "airstrikeheight", "targetname" );
|
||||
if ( IsDefined( heightEnt ) )
|
||||
{
|
||||
return heightEnt.origin[2];
|
||||
}
|
||||
else
|
||||
{
|
||||
println( "NO DEFINED AIRSTRIKE HEIGHT SCRIPT_ORIGIN IN LEVEL" );
|
||||
planeFlyHeight = 950;
|
||||
if ( isdefined( level.airstrikeHeightScale ) )
|
||||
planeFlyHeight *= level.airstrikeHeightScale;
|
||||
|
||||
return planeFlyHeight;
|
||||
}
|
||||
}
|
||||
|
||||
// getPlaneFlightPlan
|
||||
// Summary: On certain levels, we use airstrikeheight's position and orientation to indicate a safe flight path. The entity must have script_noteworthy="fixedposition" and have angles set.
|
||||
// If not specified, we create one that across the player's current field of view.
|
||||
getPlaneFlightPlan( distFromPlayer ) // self == player
|
||||
{
|
||||
result = SpawnStruct();
|
||||
result.height = getPlaneFlyHeight();
|
||||
|
||||
heightEnt = GetEnt( "airstrikeheight", "targetname" );
|
||||
if ( IsDefined( heightEnt )
|
||||
&& IsDefined( heightEnt.script_noteworthy )
|
||||
&& heightEnt.script_noteworthy == "fixedposition"
|
||||
)
|
||||
{
|
||||
result.targetPos = heightEnt.origin;
|
||||
result.flightDir = AnglesToForward( heightEnt.angles );
|
||||
if ( RandomInt(2) == 0 )
|
||||
result.flightDir *= -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
forwardVec = AnglesToForward( self.angles );
|
||||
rightVec = AnglesToRight( self.angles );
|
||||
result.targetPos = self.origin + distFromPlayer * forwardVec;
|
||||
result.flightDir = -1 * rightVec;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
getExplodeDistance( height )
|
||||
{
|
||||
standardHeight = 850;
|
||||
standardDistance = 1500;
|
||||
distanceFrac = standardHeight/height;
|
||||
|
||||
newDistance = distanceFrac * standardDistance;
|
||||
|
||||
return newDistance;
|
||||
}
|
||||
|
||||
startTrackingPlane( obj )
|
||||
{
|
||||
entNum = obj GetEntityNumber();
|
||||
level.planes[ entNum ] = obj;
|
||||
}
|
||||
|
||||
stopTrackingPlane( obj )
|
||||
{
|
||||
entNum = obj GetEntityNumber();
|
||||
level.planes[ entNum ] = undefined;
|
||||
}
|
||||
|
||||
selectAirstrikeLocation( lifeId, streakname, doStrikeFn )
|
||||
{
|
||||
targetSize = level.mapSize / 6.46875; // 138 in 720
|
||||
if ( level.splitscreen )
|
||||
targetSize *= 1.5;
|
||||
|
||||
config = level.planeConfigs[ streakname ];
|
||||
if ( IsDefined( config.selectLocationVO ) )
|
||||
{
|
||||
self PlayLocalSound( game[ "voice" ][ self.team ] + config.selectLocationVO );
|
||||
}
|
||||
|
||||
self _beginLocationSelection( streakname, "map_artillery_selector", config.chooseDirection, targetSize );
|
||||
|
||||
self endon( "stop_location_selection" );
|
||||
|
||||
// wait for the selection. randomize the yaw if we're not doing a precision airstrike.
|
||||
self waittill( "confirm_location", location, directionYaw );
|
||||
|
||||
if ( !config.chooseDirection )
|
||||
{
|
||||
directionYaw = randomint(360);
|
||||
}
|
||||
|
||||
self setblurforplayer( 0, 0.3 );
|
||||
|
||||
if ( IsDefined( config.inboundVO ) )
|
||||
{
|
||||
self PlayLocalSound( game[ "voice" ][ self.team ] + config.inboundVO );
|
||||
}
|
||||
|
||||
// turn off logging for DLC killstreaks (no shipped ks uses this)
|
||||
// self maps\mp\_matchdata::logKillstreakEvent( streakName, location );
|
||||
|
||||
self thread [[ doStrikeFn ]]( lifeId, location, directionYaw, streakName );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
setObjectiveIcons( friendlyIcon, enemyIcon ) // self == plane
|
||||
{
|
||||
friendlyTeamId = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
Objective_Add( friendlyTeamId, "active", (0,0,0), friendlyIcon );
|
||||
Objective_OnEntityWithRotation( friendlyTeamId, self );
|
||||
self.friendlyTeamId = friendlyTeamId;
|
||||
|
||||
enemyTeamID = maps\mp\gametypes\_gameobjects::getNextObjID();
|
||||
Objective_Add( enemyTeamID, "active", (0,0,0), enemyIcon );
|
||||
Objective_OnEntityWithRotation( enemyTeamID, self );
|
||||
self.enemyTeamID = enemyTeamID;
|
||||
|
||||
if (level.teamBased)
|
||||
{
|
||||
Objective_Team( friendlyTeamId, self.team );
|
||||
Objective_Team( enemyTeamID, getOtherTeam(self.team) );
|
||||
}
|
||||
else
|
||||
{
|
||||
ownerEntityNum = self.owner GetEntityNumber();
|
||||
Objective_PlayerTeam( friendlyTeamId, ownerEntityNum );
|
||||
Objective_PlayerEnemyTeam( enemyTeamID, ownerEntityNum );
|
||||
}
|
||||
}
|
||||
|
||||
playSonicBoom( soundName, delay )
|
||||
{
|
||||
self endon ("death");
|
||||
|
||||
wait ( delay );
|
||||
|
||||
self PlaySoundOnMovingEnt( soundName );
|
||||
}
|
||||
|
||||
createKillCam( streakName ) // self == plane
|
||||
{
|
||||
configData = level.planeConfigs[ streakName ];
|
||||
|
||||
if ( IsDefined( configData.killCamOffset ) )
|
||||
{
|
||||
planedir = AnglesToForward( self.angles );
|
||||
|
||||
killCamEnt = Spawn( "script_model", self.origin + (0,0,100) - planedir * 200 );
|
||||
killCamEnt.startTime = GetTime();
|
||||
killCamEnt SetScriptMoverKillCam( "airstrike" );
|
||||
killCamEnt LinkTo( self, "tag_origin", configData.killCamOffset, ( 0,0,0 ) );
|
||||
|
||||
self.killCamEnt = killCamEnt;
|
||||
}
|
||||
}
|
429
maps/mp/killstreaks/_portableaoegenerator.gsc
Normal file
429
maps/mp/killstreaks/_portableaoegenerator.gsc
Normal file
@ -0,0 +1,429 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/* Portable AOE generator
|
||||
* 2013-03-25 wsh
|
||||
* Class of equipment that can be placed and moved in the world
|
||||
* that affects all targeted players in an area around the deployed equipment
|
||||
* examples: scrambler, portable radar
|
||||
* Adapted from old scrambler code.
|
||||
*/
|
||||
init()
|
||||
{
|
||||
if ( !IsDefined( level.portableAOEgeneratorSettings ) )
|
||||
{
|
||||
level.portableAOEgeneratorSettings = [];
|
||||
level.generators = [];
|
||||
}
|
||||
|
||||
// in your specific implementation, need to set these config vars
|
||||
/*
|
||||
config = SpawnStruct();
|
||||
config.generatorType = GENERATOR_TYPE;
|
||||
config.weaponName = "scrambler_mp";
|
||||
config.targetType = "enemy"; // valid types { "friendly", "enemy", "all" }
|
||||
// callbacks are in the format: generator callbackFunc( player, generatorType )
|
||||
config.onDeployCallback = ::onCreateScrambler;
|
||||
config.onDestroyCallback = ::onDestroyScrambler;
|
||||
// callbacks are in the format: player callbackFunc()
|
||||
config.onEnterCallback = ::onEnterScrambler;
|
||||
config.onExitCallback = ::onExitScrambler;
|
||||
// config.timeLimit = undefined;
|
||||
config.health = 100;
|
||||
config.placementZTolerance = 40;
|
||||
config.placedModel = "weapon_jammer";
|
||||
config.bombSquadModel = "weapon_jammer_bombsquad";
|
||||
config.damageFeedback = "scrambler";
|
||||
config.useHintString = &"MP_PATCH_PICKUP_SCRAMBLER";
|
||||
config.headIconHeight = 20;
|
||||
config.useSound = "scavenger_pack_pickup";
|
||||
config.aoeRadius = 512;
|
||||
*/
|
||||
}
|
||||
|
||||
// make sure that killstreaks don't get ammo refills
|
||||
setWeapon( generatorType ) // self == player
|
||||
{
|
||||
config = level.portableAOEgeneratorSettings[ generatorType ];
|
||||
self SetOffhandSecondaryClass( "flash" );
|
||||
self _giveWeapon( config.weaponName, 0 );
|
||||
self giveStartAmmo( config.weaponName );
|
||||
|
||||
if ( !IsDefined( self.deployedGenerators ) )
|
||||
{
|
||||
self.deployedGenerators = [];
|
||||
}
|
||||
|
||||
self thread monitorGeneratorUse( generatorType );
|
||||
}
|
||||
|
||||
unsetWeapon( generatorType ) // self == player
|
||||
{
|
||||
self notify( "end_monitorUse_" + generatorType );
|
||||
}
|
||||
|
||||
deleteGenerator( generator, generatorType ) // self == player
|
||||
{
|
||||
if ( !IsDefined( generator ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( IsDefined( player )
|
||||
&& IsDefined( player.inGeneratorAOE )
|
||||
)
|
||||
{
|
||||
// need to check if this array is defined
|
||||
// or create it
|
||||
// !!!
|
||||
player.inGeneratorAOE[ generatorType ] = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
// remove all references to this generator
|
||||
self registerGenerator( generator, generatorType, undefined );
|
||||
|
||||
generator notify( "death" );
|
||||
generator Delete();
|
||||
}
|
||||
|
||||
// bRegister = true to register
|
||||
// = undefined to unregister
|
||||
registerGenerator( generator, generatorType, bRegister ) // self == player
|
||||
{
|
||||
if ( IsDefined( bRegister ) && bRegister )
|
||||
{
|
||||
self.deployedGenerators[ generatorType ] = generator;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.deployedGenerators[ generatorType ] = undefined;
|
||||
bRegister = undefined; // just to make sure people aren't passing in false
|
||||
}
|
||||
|
||||
allGeneratorsOfThisType = level.generators[ generatorType ];
|
||||
if ( !IsDefined( allGeneratorsOfThisType ) )
|
||||
{
|
||||
level.generators[ generatorType ] = [];
|
||||
allGeneratorsOfThisType = level.generators[ generatorType ];
|
||||
}
|
||||
id = getID( generator );
|
||||
allGeneratorsOfThisType[ id ] = bRegister;
|
||||
}
|
||||
|
||||
monitorGeneratorUse( generatorType ) // self == player
|
||||
{
|
||||
self notify( "end_monitorUse_" + generatorType );
|
||||
self endon( "end_monitorUse_" + generatorType );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
config = level.portableAOEgeneratorSettings[ generatorType ];
|
||||
while ( true )
|
||||
{
|
||||
self waittill( "grenade_fire", grenade, weapName );
|
||||
|
||||
// grenade is the entity spawned by the G_FireGrenade() since we want this to be
|
||||
// script controlled, we won't actually use this entity
|
||||
if ( weapName == config.weaponName || weapName == generatorType )
|
||||
{
|
||||
if ( !IsAlive( self ) )
|
||||
{
|
||||
grenade delete();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( checkGeneratorPlacement( grenade, config.placementZTolerance ) )
|
||||
{
|
||||
previousGenerator = self.deployedGenerators[ generatorType ];
|
||||
if ( IsDefined( previousGenerator ) )
|
||||
{
|
||||
deleteGenerator( previousGenerator, generatorType );
|
||||
}
|
||||
|
||||
generator = self spawnNewGenerator( generatorType, grenade.origin );
|
||||
|
||||
// For moving platforms.
|
||||
parent = grenade GetLinkedParent();
|
||||
if ( IsDefined( parent ) )
|
||||
{
|
||||
generator LinkTo( parent );
|
||||
}
|
||||
|
||||
if( IsDefined( grenade ) )
|
||||
{
|
||||
grenade Delete();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self SetWeaponAmmoStock( config.weaponName, self GetWeaponAmmoStock( "trophy_mp" ) + 1 );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkGeneratorPlacement( grenade, maxZDistance ) // self == player
|
||||
{
|
||||
// need to see if this is being placed far away from the player and not let it do that
|
||||
// this will fix a legacy bug where you can stand on a ledge and plant a claymore down on the ground far below you
|
||||
grenade Hide();
|
||||
grenade waittill( "missile_stuck", stuckTo );
|
||||
|
||||
if( maxZDistance * maxZDistance < DistanceSquared( grenade.origin, self.origin ) )
|
||||
{
|
||||
secTrace = bulletTrace( self.origin, self.origin - (0, 0, maxZDistance), false, self );
|
||||
|
||||
if( secTrace["fraction"] == 1 )
|
||||
{
|
||||
// there's nothing under us so don't place the grenade up in the air
|
||||
grenade delete();
|
||||
return false;
|
||||
}
|
||||
|
||||
// move the grenade to a reasonable place
|
||||
grenade.origin = secTrace["position"];
|
||||
}
|
||||
|
||||
grenade Show();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
spawnNewGenerator( generatorType, origin ) // self == player
|
||||
{
|
||||
config = level.portableAOEgeneratorSettings[ generatorType ];
|
||||
|
||||
generator = spawn( "script_model", origin );
|
||||
generator.health = config.health;
|
||||
generator.team = self.team;
|
||||
generator.owner = self;
|
||||
|
||||
generator SetCanDamage( true );
|
||||
|
||||
generator SetModel( config.placedModel );
|
||||
|
||||
// setup icons for item so friendlies see it
|
||||
if ( level.teamBased )
|
||||
generator maps\mp\_entityheadIcons::setTeamHeadIcon( self.team , (0,0,config.headIconHeight) );
|
||||
else
|
||||
generator maps\mp\_entityheadicons::setPlayerHeadIcon( self, (0,0,config.headIconHeight) );
|
||||
|
||||
generator thread watchOwner( self, generatorType );
|
||||
generator thread watchDamage( self, generatorType );
|
||||
generator thread watchUse( self, generatorType );
|
||||
// 2013-03-25 wsh:
|
||||
// not sure why we need to set notUsableForJoiningPlayers
|
||||
generator thread notUsableForJoiningPlayers( self );
|
||||
|
||||
if ( IsDefined( config.onDeployCallback ) )
|
||||
{
|
||||
generator [[ config.onDeployCallback ]]( self, generatorType );
|
||||
}
|
||||
|
||||
generator thread maps\mp\gametypes\_weapons::createBombSquadModel( config.bombSquadModel, "tag_origin", self );
|
||||
|
||||
self registerGenerator( generator, generatorType, true );
|
||||
|
||||
// need to clear the changing weapon because it'll get stuck on c4_mp and player will stop spawning because we get locked in isChangingWeapon() loop when a killstreak is earned
|
||||
self.changingWeapon = undefined;
|
||||
|
||||
wait(0.05); // allows for the plant sound to play
|
||||
|
||||
if( IsDefined(generator) && (generator touchingBadTrigger()) )
|
||||
{
|
||||
generator notify( "death" );
|
||||
}
|
||||
|
||||
return generator;
|
||||
}
|
||||
|
||||
watchOwner( owner, generatorType ) // self == generator
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
if ( bot_is_fireteam_mode() )
|
||||
{
|
||||
owner waittill( "killstreak_disowned" );
|
||||
}
|
||||
else
|
||||
{
|
||||
owner waittill_either( "killstreak_disowned", "death" );
|
||||
}
|
||||
|
||||
owner thread deleteGenerator( self, generatorType );
|
||||
}
|
||||
|
||||
watchDamage( owner, generatorType ) // self == generator
|
||||
{
|
||||
self.generatorType = generatorType;
|
||||
config = level.portableAOEgeneratorSettings[ generatorType ];
|
||||
|
||||
self maps\mp\gametypes\_damage::monitorDamage(
|
||||
config.health,
|
||||
config.damageFeedback,
|
||||
::handleDeathDamage,
|
||||
::modifyDamage,
|
||||
false // isKillstreak
|
||||
);
|
||||
}
|
||||
|
||||
// we should just use the default one
|
||||
modifyDamage( attacker, weapon, type, damage )
|
||||
{
|
||||
modifiedDamage = damage;
|
||||
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMeleeDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleEmpDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleMissileDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleGrenadeDamage( weapon, type, modifiedDamage );
|
||||
modifiedDamage = self maps\mp\gametypes\_damage::handleAPDamage( weapon, type, modifiedDamage, attacker );
|
||||
|
||||
return modifiedDamage;
|
||||
}
|
||||
|
||||
handleDeathDamage( attacker, weapon, type )
|
||||
{
|
||||
owner = self.owner;
|
||||
config = level.portableAOEgeneratorSettings[ self.generatorType ];
|
||||
|
||||
if ( isDefined( owner ) && attacker != owner )
|
||||
{
|
||||
attacker notify( "destroyed_equipment" ); // count towards SitRep Pro challenge
|
||||
}
|
||||
|
||||
if ( IsDefined( config.onDestroyCallback ) )
|
||||
{
|
||||
owner [[ config.onDestroyCallback ]]( self, self.generatorType );
|
||||
}
|
||||
owner thread deleteGenerator( self, self.generatorType );
|
||||
}
|
||||
|
||||
watchUse( owner, generatorType ) // self == generator
|
||||
{
|
||||
self endon ( "death" );
|
||||
level endon ( "game_ended" );
|
||||
owner endon ( "disconnect" );
|
||||
|
||||
config = level.portableAOEgeneratorSettings[ generatorType ];
|
||||
self setCursorHint( "HINT_NOICON" );
|
||||
self setHintString( config.useHintString );
|
||||
self setSelfUsable( owner );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
self waittill ( "trigger", player );
|
||||
|
||||
player playLocalSound( config.useSound );
|
||||
|
||||
// give item to user (only if they haven't restocked from scavenger pickup since dropping)
|
||||
if ( player getAmmoCount( config.weaponName ) == 0 && !player isJuggernaut() )
|
||||
{
|
||||
player setWeapon( generatorType );
|
||||
}
|
||||
|
||||
player thread deleteGenerator( self, generatorType );
|
||||
}
|
||||
}
|
||||
|
||||
// called from _perks.gsc::spawnPlayer
|
||||
// because we want to accumlate the effects of all generators
|
||||
// before we determine whether or not to enable the effect
|
||||
// DOES NOT COUNT THE # OF EFFECTS ACTIVE, though it's easily changed
|
||||
generatorAOETracker() // self == player
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
self endon( "faux_spawn" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
// wait a random amount so that all players aren't checking at the same time
|
||||
delay = RandomFloat( 0.5 );
|
||||
wait( delay );
|
||||
|
||||
self.inGeneratorAOE = [];
|
||||
|
||||
while ( true )
|
||||
{
|
||||
wait ( 0.05 );
|
||||
|
||||
if ( level.generators.size > 0 || self.inGeneratorAOE.size > 0 )
|
||||
{
|
||||
foreach ( config in level.portableAOEgeneratorSettings )
|
||||
{
|
||||
self checkAllGeneratorsOfThisType( config.generatorType );
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
checkAllGeneratorsOfThisType( generatorType ) // self == player
|
||||
{
|
||||
generators = level.generators[ generatorType ];
|
||||
if ( IsDefined( generators ) )
|
||||
{
|
||||
config = level.portableAOEgeneratorSettings[ generatorType ];
|
||||
maxDistSq = config.aoeRadius * config.aoeRadius;
|
||||
result = undefined;
|
||||
foreach ( generator in generators )
|
||||
{
|
||||
// should I be cleaning the array?
|
||||
if ( IsDefined( generator ) && isReallyAlive( generator ) )
|
||||
{
|
||||
if ( ( level.teamBased && matchesTargetTeam( generator.team, self.team, config.targetType ) )
|
||||
|| ( !level.teamBased && matchesOwner( generator.owner, self, config.targetType ) )
|
||||
)
|
||||
{
|
||||
// stop as soon as we find a valid candidate
|
||||
distSq = DistanceSquared( generator.origin, self.origin );
|
||||
if ( distSq < maxDistSq )
|
||||
{
|
||||
result = generator;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
isInThisGenerator = IsDefined(result);
|
||||
wasInGeneratorOfThisType = IsDefined( self.inGeneratorAOE[ generatorType ] );
|
||||
if ( isInThisGenerator && !wasInGeneratorOfThisType )
|
||||
{
|
||||
self [[ config.onEnterCallback ]]();
|
||||
}
|
||||
else if ( !isInThisGenerator && wasInGeneratorOfThisType )
|
||||
{
|
||||
self [[ config.onExitCallback ]]();
|
||||
}
|
||||
|
||||
self.inGeneratorAOE[ generatorType ] = result;
|
||||
}
|
||||
}
|
||||
|
||||
matchesTargetTeam( myTeam, theirTeam, teamType )
|
||||
{
|
||||
return (
|
||||
( teamType == "all" )
|
||||
|| ( teamType == "friendly" && myTeam == theirTeam )
|
||||
|| ( teamType == "enemy" && myTeam != theirTeam )
|
||||
);
|
||||
}
|
||||
|
||||
matchesOwner( myOwner, player, teamType )
|
||||
{
|
||||
return (
|
||||
( teamType == "all" )
|
||||
|| ( teamType == "friendly" && myOwner == player )
|
||||
|| ( teamType == "enemy" && myOwner != player )
|
||||
);
|
||||
}
|
||||
|
||||
// since I can't use the generator itself as an index into the array
|
||||
// create some kind of unique ID to use as an index
|
||||
getID( generator )
|
||||
{
|
||||
return generator.owner.guid + generator.birthtime;
|
||||
}
|
323
maps/mp/killstreaks/_remotemissile.gsc
Normal file
323
maps/mp/killstreaks/_remotemissile.gsc
Normal file
@ -0,0 +1,323 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.missileRemoteLaunchVert = 14000;
|
||||
level.missileRemoteLaunchHorz = 7000;
|
||||
level.missileRemoteLaunchTargetDist = 1500;
|
||||
|
||||
level.rockets = [];
|
||||
|
||||
level.killstreakFuncs[ "predator_missile" ] = ::tryUsePredatorMissile;
|
||||
|
||||
level.remotemissile_fx[ "explode" ] = LoadFX( "fx/explosions/aerial_explosion" );
|
||||
}
|
||||
|
||||
|
||||
tryUsePredatorMissile( lifeId, streakName )
|
||||
{
|
||||
self setUsingRemote( "remotemissile" );
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak();
|
||||
if ( result != "success" )
|
||||
{
|
||||
if ( result != "disconnect" )
|
||||
self clearUsingRemote();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
self SetClientOmnvar( "ui_predator_missile", 1 );
|
||||
level thread _fire( lifeId, self );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
getBestSpawnPoint( remoteMissileSpawnPoints )
|
||||
{
|
||||
validEnemies = [];
|
||||
|
||||
foreach ( spawnPoint in remoteMissileSpawnPoints )
|
||||
{
|
||||
spawnPoint.validPlayers = [];
|
||||
spawnPoint.spawnScore = 0;
|
||||
}
|
||||
|
||||
foreach ( player in level.players )
|
||||
{
|
||||
if ( !isReallyAlive( player ) )
|
||||
continue;
|
||||
|
||||
if ( player.team == self.team )
|
||||
continue;
|
||||
|
||||
if ( player.team == "spectator" )
|
||||
continue;
|
||||
|
||||
bestDistance = 999999999;
|
||||
bestSpawnPoint = undefined;
|
||||
|
||||
foreach ( spawnPoint in remoteMissileSpawnPoints )
|
||||
{
|
||||
//could add a filtering component here but i dont know what it would be.
|
||||
spawnPoint.validPlayers[spawnPoint.validPlayers.size] = player;
|
||||
|
||||
potentialBestDistance = Distance2D( spawnPoint.targetent.origin, player.origin );
|
||||
|
||||
if ( potentialBestDistance <= bestDistance )
|
||||
{
|
||||
bestDistance = potentialBestDistance;
|
||||
bestSpawnpoint = spawnPoint;
|
||||
}
|
||||
}
|
||||
|
||||
assertEx( IsDefined( bestSpawnPoint ), "Closest remote-missile spawnpoint undefined for player: " + player.name );
|
||||
bestSpawnPoint.spawnScore += 2;
|
||||
}
|
||||
|
||||
bestSpawn = remoteMissileSpawnPoints[0];
|
||||
foreach ( spawnPoint in remoteMissileSpawnPoints )
|
||||
{
|
||||
foreach ( player in spawnPoint.validPlayers )
|
||||
{
|
||||
spawnPoint.spawnScore += 1;
|
||||
|
||||
if ( BulletTracePassed( player.origin + (0,0,32), spawnPoint.origin, false, player ) )
|
||||
spawnPoint.spawnScore += 3;
|
||||
|
||||
if ( spawnPoint.spawnScore > bestSpawn.spawnScore )
|
||||
{
|
||||
bestSpawn = spawnPoint;
|
||||
}
|
||||
else if ( spawnPoint.spawnScore == bestSpawn.spawnScore ) // equal spawn weights so we toss a coin.
|
||||
{
|
||||
if ( coinToss() )
|
||||
bestSpawn = spawnPoint;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ( bestSpawn );
|
||||
}
|
||||
|
||||
_fire( lifeId, player )
|
||||
{
|
||||
remoteMissileSpawnArray = GetEntArray( "remoteMissileSpawn" , "targetname" );
|
||||
|
||||
foreach ( spawn in remoteMissileSpawnArray )
|
||||
{
|
||||
if ( IsDefined( spawn.target ) )
|
||||
spawn.targetEnt = GetEnt( spawn.target, "targetname" );
|
||||
}
|
||||
|
||||
if ( remoteMissileSpawnArray.size > 0 )
|
||||
remoteMissileSpawn = player getBestSpawnPoint( remoteMissileSpawnArray );
|
||||
else
|
||||
remoteMissileSpawn = undefined;
|
||||
|
||||
if ( IsDefined( remoteMissileSpawn ) )
|
||||
{
|
||||
startPos = remoteMissileSpawn.origin;
|
||||
targetPos = remoteMissileSpawn.targetEnt.origin;
|
||||
|
||||
//thread drawLine( startPos, targetPos, 30, (0,1,0) );
|
||||
|
||||
vector = VectorNormalize( startPos - targetPos );
|
||||
startPos = ( vector * 14000 ) + targetPos;
|
||||
|
||||
//thread drawLine( startPos, targetPos, 15, (1,0,0) );
|
||||
|
||||
rocket = MagicBullet( "remotemissile_projectile_mp", startpos, targetPos, player );
|
||||
}
|
||||
else
|
||||
{
|
||||
upVector = (0, 0, level.missileRemoteLaunchVert );
|
||||
backDist = level.missileRemoteLaunchHorz;
|
||||
targetDist = level.missileRemoteLaunchTargetDist;
|
||||
|
||||
forward = AnglesToForward( player.angles );
|
||||
startpos = player.origin + upVector + forward * backDist * -1;
|
||||
targetPos = player.origin + forward * targetDist;
|
||||
|
||||
rocket = MagicBullet( "remotemissile_projectile_mp", startpos, targetPos, player );
|
||||
}
|
||||
|
||||
if ( !IsDefined( rocket ) )
|
||||
{
|
||||
player clearUsingRemote();
|
||||
return;
|
||||
}
|
||||
|
||||
rocket.team = player.team;
|
||||
rocket thread handleDamage();
|
||||
|
||||
rocket.lifeId = lifeId;
|
||||
rocket.type = "remote";
|
||||
level.remoteMissileInProgress = true;
|
||||
missileEyes( player, rocket );
|
||||
}
|
||||
|
||||
/#
|
||||
_fire_noplayer( lifeId, player )
|
||||
{
|
||||
upVector = (0, 0, level.missileRemoteLaunchVert );
|
||||
backDist = level.missileRemoteLaunchHorz;
|
||||
targetDist = level.missileRemoteLaunchTargetDist;
|
||||
|
||||
forward = AnglesToForward( player.angles );
|
||||
startpos = player.origin + upVector + forward * backDist * -1;
|
||||
targetPos = player.origin + forward * targetDist;
|
||||
|
||||
rocket = MagicBullet( "remotemissile_projectile_mp", startpos, targetPos, player );
|
||||
|
||||
if ( !IsDefined( rocket ) )
|
||||
return;
|
||||
|
||||
rocket thread handleDamage();
|
||||
|
||||
rocket.lifeId = lifeId;
|
||||
rocket.type = "remote";
|
||||
|
||||
player CameraLinkTo( rocket, "tag_origin" );
|
||||
player ControlsLinkTo( rocket );
|
||||
|
||||
rocket thread rocket_CleanupOnDeath();
|
||||
|
||||
wait ( 2.0 );
|
||||
|
||||
player ControlsUnlink();
|
||||
player CameraUnlink();
|
||||
}
|
||||
#/
|
||||
|
||||
handleDamage()
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "deleted" );
|
||||
|
||||
self SetCanDamage( true );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
self waittill( "damage" );
|
||||
|
||||
println ( "projectile damaged!" );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
missileEyes( player, rocket )
|
||||
{
|
||||
//level endon ( "game_ended" );
|
||||
player endon ( "joined_team" );
|
||||
player endon ( "joined_spectators" );
|
||||
|
||||
rocket thread rocket_CleanupOnDeath();
|
||||
player thread player_CleanupOnGameEnded( rocket );
|
||||
player thread player_CleanupOnTeamChange( rocket );
|
||||
|
||||
player VisionSetMissilecamForPlayer( "black_bw", 0 );
|
||||
|
||||
player endon ( "disconnect" );
|
||||
|
||||
if ( IsDefined( rocket ) )
|
||||
{
|
||||
player VisionSetMissilecamForPlayer( game["thermal_vision"], 1.0 );
|
||||
player ThermalVisionOn();
|
||||
player thread delayedFOFOverlay();
|
||||
player CameraLinkTo( rocket, "tag_origin" );
|
||||
player ControlsLinkTo( rocket );
|
||||
|
||||
if ( GetDvarInt( "camera_thirdPerson" ) )
|
||||
player SetThirdPersonDOF( false );
|
||||
|
||||
rocket waittill( "death" );
|
||||
player ThermalVisionOff();
|
||||
// is defined check required because remote missile doesnt handle lifetime explosion gracefully
|
||||
// instantly deletes its self after an explode and death notify
|
||||
if ( IsDefined(rocket) )
|
||||
player maps\mp\_matchdata::logKillstreakEvent( "predator_missile", rocket.origin );
|
||||
|
||||
player ControlsUnlink();
|
||||
player freezeControlsWrapper( true );
|
||||
|
||||
// If a player gets the final kill with a hellfire, level.gameEnded will already be true at this point
|
||||
if ( !level.gameEnded || IsDefined( player.finalKill ) )
|
||||
player SetClientOmnvar( "ui_predator_missile", 2 );
|
||||
|
||||
wait ( 0.5 );
|
||||
|
||||
player ThermalVisionFOFOverlayOff();
|
||||
|
||||
player CameraUnlink();
|
||||
|
||||
if ( GetDvarInt( "camera_thirdPerson" ) )
|
||||
player SetThirdPersonDOF( true );
|
||||
|
||||
}
|
||||
|
||||
player SetClientOmnvar( "ui_predator_missile", 0 );
|
||||
player clearUsingRemote();
|
||||
}
|
||||
|
||||
|
||||
delayedFOFOverlay()
|
||||
{
|
||||
self endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
level endon ( "game_ended" );
|
||||
|
||||
wait ( 0.15 );
|
||||
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
}
|
||||
|
||||
player_CleanupOnTeamChange( rocket )
|
||||
{
|
||||
rocket endon ( "death" );
|
||||
self endon ( "disconnect" );
|
||||
|
||||
self waittill_any( "joined_team" , "joined_spectators" );
|
||||
|
||||
if ( self.team != "spectator" )
|
||||
{
|
||||
self ThermalVisionFOFOverlayOff();
|
||||
self ControlsUnlink();
|
||||
self CameraUnlink();
|
||||
|
||||
if ( GetDvarInt( "camera_thirdPerson" ) )
|
||||
self SetThirdPersonDOF( true );
|
||||
}
|
||||
self clearUsingRemote();
|
||||
|
||||
level.remoteMissileInProgress = undefined;
|
||||
}
|
||||
|
||||
|
||||
rocket_CleanupOnDeath()
|
||||
{
|
||||
entityNumber = self GetEntityNumber();
|
||||
level.rockets[ entityNumber ] = self;
|
||||
self waittill( "death" );
|
||||
|
||||
level.rockets[ entityNumber ] = undefined;
|
||||
|
||||
level.remoteMissileInProgress = undefined;
|
||||
}
|
||||
|
||||
|
||||
player_CleanupOnGameEnded( rocket )
|
||||
{
|
||||
rocket endon ( "death" );
|
||||
self endon ( "death" );
|
||||
|
||||
level waittill ( "game_ended" );
|
||||
|
||||
self ThermalVisionFOFOverlayOff();
|
||||
self ControlsUnlink();
|
||||
self CameraUnlink();
|
||||
|
||||
if ( GetDvarInt( "camera_thirdPerson" ) )
|
||||
self SetThirdPersonDOF( true );
|
||||
}
|
798
maps/mp/killstreaks/_remotemortar.gsc
Normal file
798
maps/mp/killstreaks/_remotemortar.gsc
Normal file
@ -0,0 +1,798 @@
|
||||
#include maps\mp\_utility;
|
||||
#include maps\mp\gametypes\_hud_util;
|
||||
#include common_scripts\utility;
|
||||
|
||||
init()
|
||||
{
|
||||
level.remote_mortar_fx[ "laserTarget" ] = LoadFX( "fx/misc/laser_glow" );
|
||||
level.remote_mortar_fx[ "missileExplode" ] = LoadFX( "fx/explosions/bouncing_betty_explosion" );
|
||||
level.remote_mortar_fx[ "deathExplode" ] = LoadFX( "fx/explosions/uav_advanced_death" );
|
||||
|
||||
level.killstreakFuncs["remote_mortar"] = ::tryUseRemoteMortar;
|
||||
|
||||
level.remote_mortar = undefined;
|
||||
|
||||
/#
|
||||
SetDevDvarIfUninitialized( "scr_remote_mortar_timeout", 40.0 );
|
||||
#/
|
||||
}
|
||||
|
||||
|
||||
tryUseRemoteMortar( lifeId, streakName )
|
||||
{
|
||||
if ( isDefined( level.remote_mortar ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
return false;
|
||||
}
|
||||
|
||||
self setUsingRemote( "remote_mortar" );
|
||||
result = self maps\mp\killstreaks\_killstreaks::initRideKillstreak( "remote_mortar" );
|
||||
if ( result != "success" )
|
||||
{
|
||||
if ( result != "disconnect" )
|
||||
self clearUsingRemote();
|
||||
|
||||
return false;
|
||||
}
|
||||
// check again, could have filled up while initRideKillstreak was running
|
||||
else if ( isDefined( level.remote_mortar ) )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AIR_SPACE_TOO_CROWDED" );
|
||||
self clearUsingRemote();
|
||||
return false;
|
||||
}
|
||||
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "remote_mortar", self.origin );
|
||||
|
||||
return startRemoteMortar( lifeId );
|
||||
}
|
||||
|
||||
|
||||
startRemoteMortar( lifeId )
|
||||
{
|
||||
remote = spawnRemote( lifeId, self );
|
||||
if ( !isDefined( remote ) )
|
||||
return false;
|
||||
|
||||
level.remote_mortar = remote;
|
||||
|
||||
self remoteRide( remote );
|
||||
|
||||
self thread teamPlayerCardSplash( "used_remote_mortar", self );
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
spawnRemote( lifeId, owner )
|
||||
{
|
||||
remote = spawnPlane( owner, "script_model", level.UAVRig getTagOrigin( "tag_origin" ), "compass_objpoint_reaper_friendly", "compass_objpoint_reaper_enemy" );
|
||||
if ( !isDefined( remote ) )
|
||||
return undefined;
|
||||
|
||||
remote setModel( "vehicle_predator_b" );
|
||||
remote.lifeId = lifeId;
|
||||
remote.team = owner.team;
|
||||
remote.owner = owner;
|
||||
remote.numFlares = 1;
|
||||
//remote ThermalDrawEnable();
|
||||
|
||||
remote setCanDamage( true );
|
||||
remote thread damageTracker();
|
||||
|
||||
remote.heliType = "remote_mortar";
|
||||
|
||||
// for target lists (javelin, stinger, sam, emp, etc)
|
||||
remote.uavType = "remote_mortar";
|
||||
remote maps\mp\killstreaks\_uav::addUAVModel();
|
||||
|
||||
// same height and radius as the AC130 with random angle and counter rotation
|
||||
zOffset = 6300;
|
||||
angle = randomInt( 360 );
|
||||
radiusOffset = 6100;
|
||||
xOffset = cos( angle ) * radiusOffset;
|
||||
yOffset = sin( angle ) * radiusOffset;
|
||||
angleVector = vectorNormalize( (xOffset,yOffset,zOffset) );
|
||||
angleVector = ( angleVector * 6100 );
|
||||
remote linkTo( level.UAVRig, "tag_origin", angleVector, (0,angle-90,10) );
|
||||
|
||||
// hud stuff
|
||||
owner SetClientDvar( "ui_reaper_targetDistance", -1 );
|
||||
owner SetClientDvar( "ui_reaper_ammoCount", 14 );
|
||||
|
||||
remote thread handleDeath( owner );
|
||||
remote thread handleTimeout( owner );
|
||||
remote thread handleOwnerChangeTeam( owner );
|
||||
remote thread handleOwnerDisconnect( owner );
|
||||
remote thread handleIncomingStinger();
|
||||
remote thread handleIncomingSAM();
|
||||
|
||||
return remote;
|
||||
}
|
||||
|
||||
|
||||
lookCenter( remote )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
remote endon( "death" );
|
||||
|
||||
wait( 0.05 );
|
||||
|
||||
// changed it from self GetEye() to remote GetTagOrigin( "tag_player" ) becauser there was a rare bug where you could be looking the opposite direction
|
||||
// we thought it might be that the player didn't get linked before this thread was run so the vector was backwards
|
||||
lookVec = vectorToAngles( level.UAVRig.origin - remote GetTagOrigin( "tag_player" ) );
|
||||
|
||||
self setPlayerAngles( lookVec );
|
||||
}
|
||||
|
||||
|
||||
remoteRide( remote )
|
||||
{
|
||||
self _giveWeapon("mortar_remote_mp");
|
||||
self SwitchToWeapon("mortar_remote_mp");
|
||||
|
||||
// with the way we do visionsets we need to wait for the clearRideIntro() is done before we set thermal
|
||||
self thread waitSetThermal( 1.0, remote );
|
||||
self thread reInitializeThermal( remote );
|
||||
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
self setThirdPersonDOF( false );
|
||||
|
||||
self PlayerLinkWeaponviewToDelta( remote, "tag_player", 1.0, 40, 40, 25, 40 );
|
||||
self thread lookCenter( remote );
|
||||
|
||||
self _disableWeaponSwitch();
|
||||
|
||||
self thread remoteTargeting( remote );
|
||||
self thread remoteFiring( remote );
|
||||
self thread remoteZoom( remote );
|
||||
}
|
||||
|
||||
waitSetThermal( delay, remote )
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
remote endon( "death" );
|
||||
|
||||
wait( delay );
|
||||
|
||||
self VisionSetThermalForPlayer( level.ac130.enhanced_vision, 0 );
|
||||
self.lastVisionSetThermal = level.ac130.enhanced_vision;
|
||||
//self ThermalVisionOn( true );
|
||||
self ThermalVisionFOFOverlayOn();
|
||||
}
|
||||
|
||||
remoteTargeting( remote )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
remote endon( "remote_done" );
|
||||
remote endon( "death" );
|
||||
|
||||
remote.targetEnt = SpawnFx( level.remote_mortar_fx["laserTarget"], (0,0,0) );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
origin = self GetEye();
|
||||
forward = AnglesToForward( self GetPlayerAngles() );
|
||||
endpoint = origin + forward * 15000;
|
||||
traceData = BulletTrace( origin, endpoint, false, remote.targetEnt );
|
||||
if ( isDefined( traceData["position"] ) )
|
||||
{
|
||||
remote.targetEnt.origin = traceData["position"];
|
||||
triggerFX( remote.targetEnt );
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remoteFiring( remote )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
remote endon( "remote_done" );
|
||||
remote endon( "death" );
|
||||
|
||||
curTime = getTime();
|
||||
lastFireTime = curTime-2200;
|
||||
ammo = 14;
|
||||
self.firingReaper = false;
|
||||
|
||||
while( true )
|
||||
{
|
||||
curTime = getTime();
|
||||
if ( self attackButtonPressed() && ( curTime - lastFireTime > 3000 ) )
|
||||
{
|
||||
ammo--;
|
||||
self SetClientDvar( "ui_reaper_ammoCount", ammo );
|
||||
lastFireTime = curTime;
|
||||
self.firingReaper = true;
|
||||
|
||||
self playLocalSound( "reaper_fire" );
|
||||
self PlayRumbleOnEntity( "damage_heavy" );
|
||||
|
||||
origin = self GetEye();
|
||||
forward = AnglesToForward( self GetPlayerAngles() );
|
||||
right = AnglesToRight( self GetPlayerAngles() );
|
||||
offset = origin + ( forward * 100 ) + ( right * -100 );
|
||||
missile = MagicBullet( "remote_mortar_missile_mp", offset, remote.targetEnt.origin, self );
|
||||
missile.type = "remote_mortar";
|
||||
Earthquake( 0.3, 0.5, origin, 256 );
|
||||
|
||||
//// attach a kill cam ent so we can customize the view
|
||||
//killCamEnt = Spawn( "script_model", missile.origin + ( forward * -75 ) + ( right * 25 ) );
|
||||
//killCamEnt LinkTo( missile );
|
||||
//missile.killCamEnt = killCamEnt;
|
||||
|
||||
missile Missile_SetTargetEnt( remote.targetEnt );
|
||||
missile Missile_SetFlightmodeDirect();
|
||||
|
||||
missile thread remoteMissileDistance( remote );
|
||||
missile thread remoteMissileLife( remote );
|
||||
|
||||
missile waittill( "death" );
|
||||
|
||||
//if( IsDefined( killCamEnt ) )
|
||||
// killCamEnt delete();
|
||||
|
||||
self SetClientDvar( "ui_reaper_targetDistance", -1 );
|
||||
self.firingReaper = false;
|
||||
|
||||
if ( ammo == 0 )
|
||||
break;
|
||||
}
|
||||
else
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
self notify( "removed_reaper_ammo" );
|
||||
self remoteEndRide( remote );
|
||||
remote thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
handleToggleZoom( remote )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
remote endon( "remote_done" );
|
||||
remote endon( "death" );
|
||||
|
||||
self notifyOnPlayerCommand( "remote_mortar_toggleZoom1", "+ads_akimbo_accessible" );
|
||||
if ( !level.console )
|
||||
{
|
||||
self notifyOnPlayerCommand( "remote_mortar_toggleZoom1", "+toggleads_throw" );
|
||||
}
|
||||
|
||||
while ( true )
|
||||
{
|
||||
result = waittill_any_return( "remote_mortar_toggleZoom1" );
|
||||
|
||||
if ( !isDefined( self.remote_mortar_toggleZoom ) )
|
||||
self.remote_mortar_toggleZoom = 0;
|
||||
|
||||
self.remote_mortar_toggleZoom = 1-self.remote_mortar_toggleZoom;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remoteZoom( remote )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "disconnect" );
|
||||
remote endon( "remote_done" );
|
||||
remote endon( "death" );
|
||||
|
||||
self.remote_mortar_toggleZoom = undefined;
|
||||
self thread handleToggleZoom( remote );
|
||||
|
||||
remote.zoomed = false;
|
||||
usingToggle = false;
|
||||
|
||||
// first find out how zoom was initiated
|
||||
while ( true )
|
||||
{
|
||||
if ( self adsButtonPressed() )
|
||||
{
|
||||
wait( 0.05 );
|
||||
if ( isDefined( self.remote_mortar_toggleZoom ) )
|
||||
usingToggle = true;
|
||||
break;
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( ( !usingToggle && self adsButtonPressed() ) || ( usingToggle && self.remote_mortar_toggleZoom ) )
|
||||
{
|
||||
if ( remote.zoomed == false )
|
||||
{
|
||||
self _giveWeapon("mortar_remote_zoom_mp");
|
||||
self SwitchToWeapon("mortar_remote_zoom_mp");
|
||||
remote.zoomed = true;
|
||||
}
|
||||
}
|
||||
else if ( ( !usingToggle && !self adsButtonPressed() ) || ( usingToggle && !self.remote_mortar_toggleZoom ) )
|
||||
{
|
||||
if ( remote.zoomed == true )
|
||||
{
|
||||
self _giveWeapon("mortar_remote_mp");
|
||||
self SwitchToWeapon("mortar_remote_mp");
|
||||
remote.zoomed = false;
|
||||
}
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remoteMissileDistance( remote )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
remote endon( "death" );
|
||||
remote endon( "remote_done" );
|
||||
self endon( "death" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
targetDist = distance( self.origin, remote.targetent.origin );
|
||||
remote.owner SetClientDvar( "ui_reaper_targetDistance", int( targetDist / 12 ) );
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
remoteMissileLife( remote )
|
||||
{
|
||||
self endon( "death" );
|
||||
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 6 );
|
||||
|
||||
playFX( level.remote_mortar_fx["missileExplode"], self.origin );
|
||||
self delete();
|
||||
}
|
||||
|
||||
|
||||
remoteEndRide( remote )
|
||||
{
|
||||
if ( !self isUsingRemote() )
|
||||
return;
|
||||
|
||||
if ( isDefined( remote ) )
|
||||
{
|
||||
remote notify( "helicopter_done" );
|
||||
//if ( isDefined( remote.laserEnt ) )
|
||||
//remote.laserEnt LaserOff();
|
||||
}
|
||||
|
||||
self ThermalVisionOff();
|
||||
self ThermalVisionFOFOverlayOff();
|
||||
self VisionSetThermalForPlayer( game["thermal_vision"], 0 );
|
||||
self restoreBaseVisionSet( 0 );
|
||||
|
||||
self unlink();
|
||||
self clearUsingRemote();
|
||||
if ( getDvarInt( "camera_thirdPerson" ) )
|
||||
self setThirdPersonDOF( true );
|
||||
|
||||
self switchToWeapon( self getLastWeapon() );
|
||||
// take the killstreak weapons
|
||||
killstreakWeapon = getKillstreakWeapon( "remote_mortar" );
|
||||
self TakeWeapon( killstreakWeapon );
|
||||
self TakeWeapon( "mortar_remote_zoom_mp" );
|
||||
self TakeWeapon( "mortar_remote_mp" );
|
||||
|
||||
self _enableWeaponSwitch();
|
||||
}
|
||||
|
||||
|
||||
handleTimeout( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
owner endon( "disconnect" );
|
||||
owner endon( "removed_reaper_ammo" );
|
||||
self endon( "death" );
|
||||
|
||||
lifeSpan = 40.0;
|
||||
/#
|
||||
lifeSpan = GetDvarInt( "scr_remote_mortar_timeout", lifeSpan );
|
||||
#/
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( lifeSpan );
|
||||
|
||||
while( owner.firingReaper )
|
||||
wait( 0.05 );
|
||||
|
||||
if ( isDefined( owner ) )
|
||||
owner remoteEndRide( self );
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
handleDeath( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
owner endon( "disconnect" );
|
||||
self endon( "remote_removed" );
|
||||
self endon( "remote_done" );
|
||||
|
||||
self waittill( "death" );
|
||||
|
||||
if ( isDefined( owner ) )
|
||||
owner remoteEndRide( self );
|
||||
level thread removeRemote( self, true );
|
||||
}
|
||||
|
||||
|
||||
handleOwnerChangeTeam( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "remote_done" );
|
||||
self endon( "death" );
|
||||
owner endon( "disconnect" );
|
||||
owner endon( "removed_reaper_ammo" );
|
||||
|
||||
owner waittill_any( "joined_team", "joined_spectators" );
|
||||
|
||||
if ( isDefined( owner ) )
|
||||
owner remoteEndRide( self );
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
handleOwnerDisconnect( owner )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self endon( "remote_done" );
|
||||
self endon( "death" );
|
||||
owner endon( "removed_reaper_ammo" );
|
||||
|
||||
owner waittill( "disconnect" );
|
||||
|
||||
self thread remoteLeave();
|
||||
}
|
||||
|
||||
|
||||
removeRemote( remote, clearLevelRef )
|
||||
{
|
||||
self notify( "remote_removed" );
|
||||
|
||||
if ( isDefined( remote.targetEnt ) )
|
||||
remote.targetEnt delete();
|
||||
|
||||
if( IsDefined( remote ) )
|
||||
{
|
||||
remote delete();
|
||||
remote maps\mp\killstreaks\_uav::removeUAVModel();
|
||||
}
|
||||
|
||||
if ( !IsDefined( clearLevelRef ) || clearLevelRef == true )
|
||||
level.remote_mortar = undefined;
|
||||
}
|
||||
|
||||
|
||||
remoteLeave()
|
||||
{
|
||||
// setting the level variable here because there is a bug if this gets shot down on the way out then this doesn't get cleared because of the endon("death")
|
||||
// now it'll definitely get cleared as soon as it tries to leave
|
||||
level.remote_mortar = undefined;
|
||||
|
||||
level endon( "game_ended" );
|
||||
self endon( "death" );
|
||||
|
||||
self notify( "remote_done" );
|
||||
|
||||
self unlink();
|
||||
destPoint = self.origin + ( AnglesToForward( self.angles ) * 20000 );
|
||||
self moveTo( destPoint, 30 );
|
||||
PlayFXOnTag( level._effect[ "ac130_engineeffect" ] , self, "tag_origin" );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 3 );
|
||||
|
||||
self moveTo( destPoint, 4, 4, 0.0 );
|
||||
maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( 4 );
|
||||
|
||||
level thread removeRemote( self, false );
|
||||
}
|
||||
|
||||
|
||||
remoteExplode()
|
||||
{
|
||||
self notify( "death" );
|
||||
self Hide();
|
||||
forward = ( AnglesToRight( self.angles ) * 200 );
|
||||
PlayFX( level.remote_mortar_fx[ "deathExplode" ], self.origin, forward );
|
||||
}
|
||||
|
||||
|
||||
// Entities spawned from SpawnPlane do not respond to pre-damage callbacks
|
||||
// so we have to wait until we get the post-damage event.
|
||||
//
|
||||
// Because the damage has already happened by the time we find out about it,
|
||||
// we need to use an artificially high health value, restore it on erroneous damage
|
||||
// events and track a virtual damage taken against a virtual max health.
|
||||
damageTracker()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
self.owner endon( "disconnect" );
|
||||
|
||||
self.health = 999999; // keep it from dying anywhere in code
|
||||
self.maxHealth = 1500; // this is the health we'll check
|
||||
self.damageTaken = 0; // how much damage has it taken
|
||||
|
||||
while( true )
|
||||
{
|
||||
self waittill( "damage", damage, attacker, direction_vec, point, meansOfDeath, modelName, tagName, partName, iDFlags, weapon );
|
||||
|
||||
// don't allow people to destroy things on their team if FF is off
|
||||
if ( !maps\mp\gametypes\_weapons::friendlyFireCheck( self.owner, attacker ) )
|
||||
continue;
|
||||
|
||||
if ( !IsDefined( self ) )
|
||||
return;
|
||||
|
||||
if ( isDefined( iDFlags ) && ( iDFlags & level.iDFLAGS_PENETRATION ) )
|
||||
self.wasDamagedFromBulletPenetration = true;
|
||||
|
||||
self.wasDamaged = true;
|
||||
|
||||
modifiedDamage = damage;
|
||||
|
||||
if( IsPlayer( attacker ) )
|
||||
{
|
||||
attacker maps\mp\gametypes\_damagefeedback::updateDamageFeedback( "" );
|
||||
|
||||
if( meansOfDeath == "MOD_RIFLE_BULLET" || meansOfDeath == "MOD_PISTOL_BULLET" )
|
||||
{
|
||||
if ( attacker _hasPerk( "specialty_armorpiercing" ) )
|
||||
modifiedDamage += damage * level.armorPiercingMod;
|
||||
}
|
||||
}
|
||||
|
||||
if( IsDefined( weapon ) )
|
||||
{
|
||||
switch( weapon )
|
||||
{
|
||||
case "stinger_mp":
|
||||
case "javelin_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
modifiedDamage = self.maxhealth + 1;
|
||||
break;
|
||||
|
||||
case "sam_projectile_mp":
|
||||
self.largeProjectileDamage = true;
|
||||
break;
|
||||
}
|
||||
|
||||
maps\mp\killstreaks\_killstreaks::killstreakHit( attacker, weapon, self );
|
||||
}
|
||||
|
||||
self.damageTaken += modifiedDamage;
|
||||
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner playLocalSound( "reaper_damaged" );
|
||||
|
||||
if ( self.damageTaken >= self.maxHealth )
|
||||
{
|
||||
if ( isPlayer( attacker ) && ( !isDefined( self.owner ) || attacker != self.owner ) )
|
||||
{
|
||||
attacker notify( "destroyed_killstreak", weapon );
|
||||
thread teamPlayerCardSplash( "callout_destroyed_remote_mortar", attacker );
|
||||
attacker thread maps\mp\gametypes\_rank::giveRankXP( "kill", 50, weapon, meansOfDeath );
|
||||
attacker thread maps\mp\gametypes\_rank::xpEventPopup( "destroyed_remote_mortar" );
|
||||
thread maps\mp\gametypes\_missions::vehicleKilled( self.owner, self, undefined, attacker, damage, meansOfDeath, weapon );
|
||||
|
||||
}
|
||||
|
||||
if ( isDefined( self.owner ) )
|
||||
self.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
self thread remoteExplode();
|
||||
// adding this to make sure it gets undefined, there was a weird bug where it could get killed as soon as it was leaving
|
||||
// then the threads got killed because of the endon's and this never got reset
|
||||
level.remote_mortar = undefined;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleIncomingStinger() // self == remote mortar
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "remote_done" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill ( "stinger_fired", player, missile, lockTarget );
|
||||
|
||||
if ( !IsDefined( lockTarget ) || (lockTarget != self) )
|
||||
continue;
|
||||
|
||||
missile thread stingerProximityDetonate( lockTarget, player );
|
||||
}
|
||||
}
|
||||
|
||||
stingerProximityDetonate( missileTarget, player ) // self == missile
|
||||
{
|
||||
self endon ( "death" );
|
||||
missileTarget endon( "death" );
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner PlayLocalSound( "missile_incoming" );
|
||||
|
||||
self Missile_SetTargetEnt( missileTarget );
|
||||
|
||||
minDist = Distance( self.origin, missileTarget GetPointInBounds( 0, 0, 0 ) );
|
||||
lastCenter = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
while( true )
|
||||
{
|
||||
// already destroyed
|
||||
if( !IsDefined( missileTarget ) )
|
||||
center = lastCenter;
|
||||
else
|
||||
center = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
lastCenter = center;
|
||||
|
||||
curDist = Distance( self.origin, center );
|
||||
|
||||
if( curDist < 3000 && missileTarget.numFlares > 0 )
|
||||
{
|
||||
missileTarget.numFlares--;
|
||||
|
||||
missileTarget thread maps\mp\killstreaks\_flares::flares_playFx();
|
||||
newTarget = missileTarget maps\mp\killstreaks\_flares::flares_deploy();
|
||||
|
||||
self Missile_SetTargetEnt( newTarget );
|
||||
missileTarget = newTarget;
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( curDist < minDist )
|
||||
minDist = curDist;
|
||||
|
||||
if( curDist > minDist )
|
||||
{
|
||||
if( curDist > 1536 )
|
||||
return;
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
{
|
||||
missileTarget.owner stopLocalSound( "missile_incoming" );
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
if( missileTarget.team != player.team )
|
||||
RadiusDamage( self.origin, 1000, 1000, 1000, player, "MOD_EXPLOSIVE", "stinger_mp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
RadiusDamage( self.origin, 1000, 1000, 1000, player, "MOD_EXPLOSIVE", "stinger_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
self Hide();
|
||||
|
||||
wait( 0.05 );
|
||||
self delete();
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
handleIncomingSAM() // self == remote mortar
|
||||
{
|
||||
level endon ( "game_ended" );
|
||||
self endon ( "death" );
|
||||
self endon ( "remote_done" );
|
||||
|
||||
while( true )
|
||||
{
|
||||
level waittill ( "sam_fired", player, missileGroup, lockTarget );
|
||||
|
||||
if ( !IsDefined( lockTarget ) || (lockTarget != self) )
|
||||
continue;
|
||||
|
||||
level thread samProximityDetonate( lockTarget, player, missileGroup );
|
||||
}
|
||||
}
|
||||
|
||||
samProximityDetonate( missileTarget, player, missileGroup )
|
||||
{
|
||||
missileTarget endon( "death" );
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner PlayLocalSound( "missile_incoming" );
|
||||
|
||||
sam_projectile_damage = 150; // this should match the gdt entry
|
||||
sam_projectile_damage_radius = 1000;
|
||||
|
||||
minDist = [];
|
||||
for( i = 0; i < missileGroup.size; i++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ i ] ) )
|
||||
minDist[ i ] = Distance( missileGroup[ i ].origin, missileTarget GetPointInBounds( 0, 0, 0 ) );
|
||||
else
|
||||
minDist[ i ] = undefined;
|
||||
}
|
||||
|
||||
while( true )
|
||||
{
|
||||
center = missileTarget GetPointInBounds( 0, 0, 0 );
|
||||
|
||||
curDist = [];
|
||||
for( i = 0; i < missileGroup.size; i++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ i ] ) )
|
||||
curDist[ i ] = Distance( missileGroup[ i ].origin, center );
|
||||
}
|
||||
|
||||
for( i = 0; i < curDist.size; i++ )
|
||||
{
|
||||
if( IsDefined( curDist[ i ] ) )
|
||||
{
|
||||
// if one of the missiles in the group get close, set off flares and redirect them all
|
||||
if( curDist[ i ] < 3000 && missileTarget.numFlares > 0 )
|
||||
{
|
||||
missileTarget.numFlares--;
|
||||
|
||||
missileTarget thread maps\mp\killstreaks\_flares::flares_playFx();
|
||||
newTarget = missileTarget maps\mp\killstreaks\_flares::flares_deploy();
|
||||
|
||||
for( j = 0; j < missileGroup.size; j++ )
|
||||
{
|
||||
if( IsDefined( missileGroup[ j ] ) )
|
||||
{
|
||||
missileGroup[ j ] Missile_SetTargetEnt( newTarget );
|
||||
}
|
||||
}
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
missileTarget.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if( curDist[ i ] < minDist[ i ] )
|
||||
minDist[ i ] = curDist[ i ];
|
||||
|
||||
if( curDist[ i ] > minDist[ i ] )
|
||||
{
|
||||
if( curDist[ i ] > 1536 )
|
||||
continue;
|
||||
|
||||
if( IsDefined( missileTarget.owner ) )
|
||||
{
|
||||
missileTarget.owner StopLocalSound( "missile_incoming" );
|
||||
|
||||
if( level.teambased )
|
||||
{
|
||||
if( missileTarget.team != player.team )
|
||||
RadiusDamage( missileGroup[ i ].origin, sam_projectile_damage_radius, sam_projectile_damage, sam_projectile_damage, player, "MOD_EXPLOSIVE", "sam_projectile_mp" );
|
||||
}
|
||||
else
|
||||
{
|
||||
RadiusDamage( missileGroup[ i ].origin, sam_projectile_damage_radius, sam_projectile_damage, sam_projectile_damage, player, "MOD_EXPLOSIVE", "sam_projectile_mp" );
|
||||
}
|
||||
}
|
||||
|
||||
missileGroup[ i ] Hide();
|
||||
|
||||
wait ( 0.05 );
|
||||
missileGroup[ i ] delete();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait ( 0.05 );
|
||||
}
|
||||
}
|
1420
maps/mp/killstreaks/_remotetank.gsc
Normal file
1420
maps/mp/killstreaks/_remotetank.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1238
maps/mp/killstreaks/_remoteturret.gsc
Normal file
1238
maps/mp/killstreaks/_remoteturret.gsc
Normal file
File diff suppressed because it is too large
Load Diff
1728
maps/mp/killstreaks/_remoteuav.gsc
Normal file
1728
maps/mp/killstreaks/_remoteuav.gsc
Normal file
File diff suppressed because it is too large
Load Diff
2246
maps/mp/killstreaks/_tank.gsc
Normal file
2246
maps/mp/killstreaks/_tank.gsc
Normal file
File diff suppressed because it is too large
Load Diff
67
maps/mp/killstreaks/_teamammorefill.gsc
Normal file
67
maps/mp/killstreaks/_teamammorefill.gsc
Normal file
@ -0,0 +1,67 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
|
||||
/*
|
||||
Team Ammo Refill Killstreak: when the player uses this, everyone on their team gets an ammo refill
|
||||
*/
|
||||
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs[ "team_ammo_refill" ] = ::tryUseTeamAmmoRefill;
|
||||
}
|
||||
|
||||
tryUseTeamAmmoRefill( lifeId )
|
||||
{
|
||||
result = self giveTeamAmmoRefill();
|
||||
if ( result )
|
||||
self maps\mp\_matchdata::logKillstreakEvent( "team_ammo_refill", self.origin );
|
||||
|
||||
return ( result );
|
||||
}
|
||||
|
||||
giveTeamAmmoRefill()
|
||||
{
|
||||
if( level.teambased )
|
||||
{
|
||||
foreach( teammate in level.players )
|
||||
{
|
||||
if( teammate.team == self.team )
|
||||
{
|
||||
teammate refillAmmo( true );
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
self refillAmmo( true );
|
||||
}
|
||||
|
||||
level thread teamPlayerCardSplash( "used_team_ammo_refill", self );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
refillAmmo( refillEquipment )
|
||||
{
|
||||
weaponList = self GetWeaponsListAll();
|
||||
|
||||
if ( refillEquipment )
|
||||
{
|
||||
if ( self _hasPerk( "specialty_tacticalinsertion" ) && self getAmmoCount( "flare_mp" ) < 1 )
|
||||
self givePerkOffhand( "specialty_tacticalinsertion", false );
|
||||
}
|
||||
|
||||
foreach ( weaponName in weaponList )
|
||||
{
|
||||
if ( isSubStr( weaponName, "grenade" ) || ( GetSubStr( weaponName, 0, 2 ) == "gl" ) )
|
||||
{
|
||||
if ( !refillEquipment || self getAmmoCount( weaponName ) >= 1 )
|
||||
continue;
|
||||
}
|
||||
|
||||
self giveMaxAmmo( weaponName );
|
||||
}
|
||||
|
||||
self playLocalSound( "ammo_crate_use" );
|
||||
|
||||
}
|
1111
maps/mp/killstreaks/_uav.gsc
Normal file
1111
maps/mp/killstreaks/_uav.gsc
Normal file
File diff suppressed because it is too large
Load Diff
572
maps/mp/killstreaks/_uplink.gsc
Normal file
572
maps/mp/killstreaks/_uplink.gsc
Normal file
@ -0,0 +1,572 @@
|
||||
#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 );
|
||||
}
|
||||
}
|
1256
maps/mp/killstreaks/_vanguard.gsc
Normal file
1256
maps/mp/killstreaks/_vanguard.gsc
Normal file
File diff suppressed because it is too large
Load Diff
622
maps/mp/killstreaks/mp_wolfpack_killstreak.gsc
Normal file
622
maps/mp/killstreaks/mp_wolfpack_killstreak.gsc
Normal file
@ -0,0 +1,622 @@
|
||||
#include maps\mp\_utility;
|
||||
#include common_scripts\utility;
|
||||
#include maps\mp\agents\_scriptedAgents;
|
||||
#include maps\mp\agents\_agent_utility;
|
||||
#include maps\mp\gametypes\_damage;
|
||||
|
||||
//===========================================
|
||||
// constants
|
||||
//===========================================
|
||||
CONST_KILLSTREAK_NAME = "mine_level_killstreak";
|
||||
CONST_WOLF_HEALTH = 200; // default dogs are 250
|
||||
|
||||
//===========================================
|
||||
// init
|
||||
//===========================================
|
||||
init()
|
||||
{
|
||||
level.killStreakFuncs[CONST_KILLSTREAK_NAME] = ::tryUseWolfpack;
|
||||
|
||||
level.killstreakWeildWeapons[ "killstreak_wolfpack_mp" ] = CONST_KILLSTREAK_NAME;
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// setup_callbacks
|
||||
//===========================================
|
||||
setup_callbacks()
|
||||
{
|
||||
level.agent_funcs["wolf"] = level.agent_funcs["dog"];
|
||||
|
||||
level.agent_funcs["wolf"]["spawn"] = ::spawn_dog;
|
||||
level.agent_funcs["wolf"]["on_killed"] = ::on_agent_dog_killed;
|
||||
level.agent_funcs["wolf"]["on_damaged"] = maps\mp\agents\_agents::on_agent_generic_damaged;
|
||||
level.agent_funcs["wolf"]["on_damaged_finished"] = maps\mp\killstreaks\_dog_killstreak::on_damaged_finished;
|
||||
// !!! CHANGE THIS
|
||||
level.agent_funcs["wolf"]["think"] = ::think_init;
|
||||
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// tryUseDog
|
||||
//===========================================
|
||||
tryUseWolfpack( lifeId, streakName )
|
||||
{
|
||||
setup_callbacks();
|
||||
|
||||
return useDog();
|
||||
}
|
||||
|
||||
|
||||
//===========================================
|
||||
// useDog
|
||||
//===========================================
|
||||
CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER = 2;
|
||||
useDog()
|
||||
{
|
||||
/*
|
||||
// limit the number of active "dog" agents allowed per player
|
||||
if( isDefined(self.hasDog) && self.hasDog )
|
||||
{
|
||||
dog_type = self GetCommonPlayerDataReservedInt( "mp_dog_type" );
|
||||
if( dog_type == 1 )
|
||||
self iPrintLnBold( &"KILLSTREAKS_ALREADY_HAVE_WOLF" );
|
||||
else
|
||||
self iPrintLnBold( &"KILLSTREAKS_ALREADY_HAVE_DOG" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// limit the number of active "dog" agents allowed per game
|
||||
if( getNumActiveAgents( "dog" ) >= CONST_MAX_ACTIVE_KILLSTREAK_DOGS_PER_GAME )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_TOO_MANY_DOGS" );
|
||||
return false;
|
||||
}
|
||||
*/
|
||||
|
||||
// limit the number of active agents allowed per player
|
||||
if( getNumOwnedActiveAgents( self ) >= CONST_MAX_ACTIVE_KILLSTREAK_AGENTS_PER_PLAYER )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_AGENT_MAX" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: we should probably do a queue system for these, so the player can call it but it'll go into a queue for when an agent dies to open up a spot
|
||||
// limit the number of active agents allowed per player
|
||||
maxagents = GetMaxAgents();
|
||||
if( getNumActiveAgents() >= maxagents )
|
||||
{
|
||||
self iPrintLnBold( &"KILLSTREAKS_UNAVAILABLE" );
|
||||
return false;
|
||||
}
|
||||
|
||||
// make sure the player is still alive before the agent trys to spawn on the player
|
||||
if( !isReallyAlive( self ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
result = self spawnWolf( 1 );
|
||||
if ( result )
|
||||
{
|
||||
self PlaySound( "mp_mine_wolf_spawn" );
|
||||
|
||||
self thread spawnWolfPack();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
spawnWolf( id )
|
||||
{
|
||||
// try to spawn the agent on a path node near the player
|
||||
// nearestPathNode = self getValidSpawnPathNodeNearPlayer( true );
|
||||
// if( !IsDefined(nearestPathNode) )
|
||||
// {
|
||||
// return false;
|
||||
// }
|
||||
|
||||
// find an available agent
|
||||
agent = maps\mp\agents\_agent_common::connectNewAgent( "wolf" , self.team );
|
||||
if( !IsDefined( agent ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// set the agent to the player's team
|
||||
agent set_agent_team( self.team, self );
|
||||
|
||||
// !!! CHANGE THIS
|
||||
structName = "wolf_spawn_0" + id;
|
||||
wolfStruct = getstruct( structName, "targetname" );
|
||||
|
||||
spawnOrigin = wolfStruct.origin;
|
||||
spawnAngles = self.angles;
|
||||
agent.wolfId = id;
|
||||
|
||||
// pick a path
|
||||
agent.pathNodeArray = getstructarray( "wolf_path_0" + id, "script_noteworthy" );
|
||||
|
||||
agent thread [[ agent agentFunc("spawn") ]]( spawnOrigin, spawnAngles, self );
|
||||
|
||||
agent _setNameplateMaterial( "player_name_bg_green_dog", "player_name_bg_red_dog" );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
CONST_NUM_WOLVES = 3;
|
||||
CONST_NUM_TOTAL_WOLVES = 6;
|
||||
spawnWolfPack()
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
// spawn the initial set of solves
|
||||
for ( i = 2; i <= CONST_NUM_WOLVES; i++ )
|
||||
{
|
||||
wait( 0.75 );
|
||||
|
||||
spawnWolf( i );
|
||||
}
|
||||
|
||||
numWolvesLeft = CONST_NUM_TOTAL_WOLVES - CONST_NUM_WOLVES;
|
||||
|
||||
while ( true )
|
||||
{
|
||||
level waittill( "wolf_killed", id );
|
||||
|
||||
if ( numWolvesLeft > 0 )
|
||||
{
|
||||
wait ( 0.75 );
|
||||
spawnWolf( id );
|
||||
|
||||
numWolvesLeft--;
|
||||
}
|
||||
else
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//=======================================================
|
||||
// on_agent_dog_killed
|
||||
//=======================================================
|
||||
on_agent_dog_killed( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration )
|
||||
{
|
||||
self.isActive = false;
|
||||
self.hasDied = false;
|
||||
|
||||
//agent dogs in safeguard do not have an owner.
|
||||
if( IsDefined( self.owner ) )
|
||||
self.owner.hasDog = false;
|
||||
|
||||
eAttacker.lastKillDogTime = GetTime();
|
||||
|
||||
if ( IsDefined( self.animCBs.OnExit[ self.aiState ] ) )
|
||||
self [[ self.animCBs.OnExit[ self.aiState ] ]] ();
|
||||
|
||||
// award XP for killing agents
|
||||
if( isPlayer( eAttacker ) && IsDefined(self.owner) && (eAttacker != self.owner) )
|
||||
{
|
||||
self maps\mp\gametypes\_damage::onKillstreakKilled( eAttacker, sWeapon, sMeansOfDeath, iDamage, "destroyed_ks_wolf" );
|
||||
}
|
||||
|
||||
|
||||
self SetAnimState( "death" );
|
||||
animEntry = self GetAnimEntry();
|
||||
animLength = GetAnimLength( animEntry );
|
||||
|
||||
deathAnimDuration = int( animLength * 1000 ); // duration in milliseconds
|
||||
|
||||
self.body = self CloneAgent( deathAnimDuration );
|
||||
|
||||
self PlaySound( "anml_wolf_shot_death" );
|
||||
|
||||
level notify( "wolf_killed", self.wolfId );
|
||||
|
||||
self maps\mp\agents\_agent_utility::deactivateAgent();
|
||||
|
||||
self notify( "killanimscript" );
|
||||
|
||||
// notify death so that we can spawn a new agent
|
||||
}
|
||||
|
||||
spawn_dog( optional_spawnOrigin, optional_spawnAngles, optional_owner ) // self == agent
|
||||
{
|
||||
dog_type = 1; // 0 == dog, 1 == wolf
|
||||
|
||||
dog_model = "mp_fullbody_wolf_c";
|
||||
wolf_type = 1;
|
||||
if ( self.wolfId == 1 )
|
||||
{
|
||||
dog_model = "mp_fullbody_wolf_b";
|
||||
wolf_type = 0;
|
||||
}
|
||||
|
||||
if( IsHairRunning() )
|
||||
dog_model = dog_model + "_fur";
|
||||
|
||||
self SetModel( dog_model );
|
||||
|
||||
self.species = "dog";
|
||||
|
||||
self.OnEnterAnimState = maps\mp\agents\dog\_dog_think::OnEnterAnimState;
|
||||
|
||||
// allow killstreaks to pass in specific spawn locations
|
||||
if( IsDefined(optional_spawnOrigin) && IsDefined(optional_spawnAngles) )
|
||||
{
|
||||
spawnOrigin = optional_spawnOrigin;
|
||||
spawnAngles = optional_spawnAngles;
|
||||
}
|
||||
else
|
||||
{
|
||||
spawnPoint = self [[level.getSpawnPoint]]();
|
||||
spawnOrigin = spawnpoint.origin;
|
||||
spawnAngles = spawnpoint.angles;
|
||||
}
|
||||
self activateAgent();
|
||||
self.spawnTime = GetTime();
|
||||
self.lastSpawnTime = GetTime();
|
||||
|
||||
self.bIsWolf = true;
|
||||
animclass = "wolf_animclass";
|
||||
|
||||
// !!! CHANGE THIS
|
||||
self maps\mp\agents\dog\_dog_think::init();
|
||||
self.watchAttackStateFunc = ::watchAttackState; // overwrite the dog's version of this to play custom sounds
|
||||
|
||||
// called from code when an agent is done initializing after AddAgent is called
|
||||
// this should set up any state specific to this agent and game
|
||||
self SpawnAgent( spawnOrigin, spawnAngles, animclass, 15, 40, optional_owner );
|
||||
level notify( "spawned_agent", self );
|
||||
|
||||
self maps\mp\agents\_agent_common::set_agent_health( CONST_WOLF_HEALTH );
|
||||
|
||||
// must set the team after SpawnAgent to fix a bug with weapon crosshairs and nametags
|
||||
if( IsDefined(optional_owner) )
|
||||
{
|
||||
self set_agent_team( optional_owner.team, optional_owner );
|
||||
}
|
||||
|
||||
self SetThreatBiasGroup( "Dogs" );
|
||||
|
||||
self TakeAllWeapons();
|
||||
|
||||
// hide the dog, let the whistle happen, then show the dog
|
||||
if( IsDefined(self.owner) )
|
||||
{
|
||||
self Hide();
|
||||
wait( 1.0 ); // not sure what to endon for this
|
||||
|
||||
// The dog could have died during the 1 second wait (for example if he spawned in a kill trigger), so if that happened,
|
||||
// don't start thinking since it will cause SREs due to him missing a self.agent_type
|
||||
if ( !IsAlive(self) )
|
||||
return;
|
||||
|
||||
self Show();
|
||||
}
|
||||
|
||||
self thread [[ self agentFunc("think") ]]();
|
||||
|
||||
wait( 0.1 );
|
||||
|
||||
if ( IsHairRunning() )
|
||||
{
|
||||
furFX = level.wolfFurFX[ wolf_type ];
|
||||
|
||||
assert( IsDefined( furFX ) );
|
||||
PlayFXOnTag( furFX, self, "tag_origin" );
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------
|
||||
// Adapted from dog_think and mp_dome_ns_alien_think
|
||||
think_init()
|
||||
{
|
||||
self maps\mp\agents\dog\_dog_think::setupDogState();
|
||||
self.wolfGoalPos = get_closest( self.origin , self.pathNodeArray );
|
||||
|
||||
self thread think();
|
||||
self thread maps\mp\agents\dog\_dog_think::watchOwnerDeath();
|
||||
self thread maps\mp\agents\dog\_dog_think::watchOwnerTeamChange();
|
||||
self thread maps\mp\agents\dog\_dog_think::WaitForBadPath();
|
||||
self thread maps\mp\agents\dog\_dog_think::WaitForPathSet();
|
||||
|
||||
/#
|
||||
self thread maps\mp\agents\dog\_dog_think::debug_dog();
|
||||
#/
|
||||
}
|
||||
|
||||
think()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( IsDefined( self.owner ) )
|
||||
{
|
||||
self endon( "owner_disconnect" );
|
||||
self thread maps\mp\agents\dog\_dog_think::destroyOnOwnerDisconnect( self.owner );
|
||||
}
|
||||
|
||||
self thread [[self.watchAttackStateFunc]]();
|
||||
self thread maps\mp\agents\dog\_dog_think::MonitorFlash();
|
||||
|
||||
while ( true )
|
||||
{
|
||||
/#
|
||||
if ( self maps\mp\agents\dog\_dog_think::ProcessDebugMode() )
|
||||
continue;
|
||||
#/
|
||||
|
||||
if ( self.aiState != "melee" && !self.stateLocked && self maps\mp\agents\dog\_dog_think::readyToMeleeTarget() && !self maps\mp\agents\dog\_dog_think::DidPastMeleeFail() )
|
||||
self ScrAgentBeginMelee( self.curMeleeTarget );
|
||||
|
||||
switch ( self.aiState )
|
||||
{
|
||||
case "idle":
|
||||
self updateMove();
|
||||
break;
|
||||
case "move":
|
||||
self updateMove();
|
||||
break;
|
||||
case "melee":
|
||||
self maps\mp\agents\dog\_dog_think::updateMelee();
|
||||
break;
|
||||
}
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
updateMove()
|
||||
{
|
||||
self UpdateMoveState();
|
||||
}
|
||||
|
||||
UpdateMoveState()
|
||||
{
|
||||
//IPrintLnBold ("updating move state");
|
||||
if ( self.bLockGoalPos )
|
||||
return;
|
||||
|
||||
self.prevMoveState = self.moveState;
|
||||
|
||||
attackPoint = undefined;
|
||||
bRefreshGoal = false;
|
||||
bWantedPursuitButFollowInstead = false;
|
||||
|
||||
cBadPathTimeOut = 500;
|
||||
if ( self.bHasBadPath && GetTime() - self.lastBadPathTime < cBadPathTimeOut )
|
||||
{
|
||||
self.moveState = "follow";
|
||||
bRefreshGoal = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.moveState = self maps\mp\agents\dog\_dog_think::GetMoveState();
|
||||
}
|
||||
|
||||
if ( self.moveState == "pursuit" )
|
||||
{
|
||||
attackPoint = self maps\mp\agents\dog\_dog_think::GetAttackPoint( self.enemy );
|
||||
bLastBadMeleeTarget = false;
|
||||
if ( IsDefined( self.lastBadPathTime ) && ( GetTime() - self.lastBadPathTime < 3000 ) )
|
||||
{
|
||||
if ( Distance2DSquared( attackPoint, self.lastBadPathGoal ) < 16 )
|
||||
bLastBadMeleeTarget = true;
|
||||
else if ( IsDefined( self.lastBadPathMoveState ) && self.lastBadPathMoveState == "pursuit" && Distance2DSquared( self.lastBadPathUltimateGoal, self.enemy.origin ) < 16 )
|
||||
bLastBadMeleeTarget = true;
|
||||
}
|
||||
if ( !isReallyAlive( self.enemy )
|
||||
|| bLastBadMeleeTarget
|
||||
|| self maps\mp\agents\dog\_dog_think::wantToAttackTargetButCant( true )
|
||||
|| self maps\mp\agents\dog\_dog_think::DidPastPursuitFail( self.enemy )
|
||||
)
|
||||
{
|
||||
self.moveState = "follow";
|
||||
bWantedPursuitButFollowInstead = true;
|
||||
}
|
||||
}
|
||||
|
||||
self maps\mp\agents\dog\_dog_think::SetPastPursuitFailed( bWantedPursuitButFollowInstead );
|
||||
|
||||
if ( self.moveState == "follow" )
|
||||
{
|
||||
self.curMeleeTarget = undefined;
|
||||
self.moveMode = self maps\mp\agents\dog\_dog_think::GetFollowMoveMode( self.moveMode );
|
||||
self.bArrivalsEnabled = true;
|
||||
|
||||
myPos = self GetPathGoalPos();
|
||||
if ( !IsDefined( myPos ) )
|
||||
myPos = self.origin;
|
||||
|
||||
if ( GetTime() - self.timeOfLastDamage < 5000 )
|
||||
bRefreshGoal = true;
|
||||
|
||||
distFromGoalPos = Distance2DSquared( self.origin, self.wolfGoalPos.origin );
|
||||
|
||||
if ( ( distFromGoalPos < 800 ) )
|
||||
{
|
||||
self pickNewLocation();
|
||||
}
|
||||
self ScrAgentSetGoalPos( self.wolfGoalPos.origin );
|
||||
|
||||
if ( bRefreshGoal == true )
|
||||
{
|
||||
self ScrAgentSetGoalPos( self.origin );
|
||||
}
|
||||
|
||||
}
|
||||
else if ( self.moveState == "pursuit" )
|
||||
{
|
||||
self.curMeleeTarget = self.enemy;
|
||||
self.moveMode = "sprint";
|
||||
self.bArrivalsEnabled = false;
|
||||
|
||||
assert( IsDefined( attackPoint ) );
|
||||
self ScrAgentSetGoalPos( attackPoint );
|
||||
}
|
||||
}
|
||||
|
||||
pickNewLocation()
|
||||
{
|
||||
// The new goal is the one targeted by the current goal
|
||||
self.wolfGoalPos = GetStruct ( self.wolfGoalPos.target, "targetname" );
|
||||
}
|
||||
|
||||
get_closest( origin, points, maxDist )
|
||||
{
|
||||
Assert( points.size );
|
||||
|
||||
closestPoint = points[ 0 ];
|
||||
dist = Distance( origin, closestPoint.origin );
|
||||
|
||||
for ( index = 0; index < points.size; index++ )
|
||||
{
|
||||
testDist = Distance( origin, points[ index ].origin );
|
||||
if ( testDist >= dist )
|
||||
continue;
|
||||
|
||||
dist = testDist;
|
||||
closestPoint = points[ index ];
|
||||
}
|
||||
|
||||
if ( !isDefined( maxDist ) || dist <= maxDist )
|
||||
return closestPoint;
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// !!! UGH, this is awful
|
||||
// dupliated directly from dog_think because I need to replace the sounds played by the wolves
|
||||
// wish there was a better way
|
||||
watchAttackState() // self == dog
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( true )
|
||||
{
|
||||
if ( self.aiState == "melee" )
|
||||
{
|
||||
if ( self.attackState != "melee" )
|
||||
{
|
||||
self.attackState = "melee";
|
||||
self SetSoundState( undefined );
|
||||
}
|
||||
}
|
||||
else if ( self.moveState == "pursuit" ) //( self wantsToAttackTarget() )
|
||||
{
|
||||
if ( self.attackState != "attacking" )
|
||||
{
|
||||
self.attackState = "attacking";
|
||||
self SetSoundState( "bark", "attacking" );
|
||||
}
|
||||
}
|
||||
else //if( !self wantsToAttackTarget() )
|
||||
{
|
||||
if ( self.attackState != "warning" )
|
||||
{
|
||||
if ( self maps\mp\agents\dog\_dog_think::wantsToGrowlAtTarget() )
|
||||
{
|
||||
self.attackState = "warning";
|
||||
self SetSoundState( "growl", "warning" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self.attackState = self.aiState;
|
||||
self SetSoundState( "pant" );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if ( !self maps\mp\agents\dog\_dog_think::wantsToGrowlAtTarget() )
|
||||
{
|
||||
self.attackState = self.aiState;
|
||||
self SetSoundState( "pant" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
wait( 0.05 );
|
||||
}
|
||||
}
|
||||
|
||||
SetSoundState( state, attackState )
|
||||
{
|
||||
if ( !IsDefined( state ) )
|
||||
{
|
||||
self notify( "end_dog_sound" );
|
||||
self.soundState = undefined;
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !IsDefined( self.soundState ) || self.soundState != state )
|
||||
{
|
||||
self notify( "end_dog_sound" );
|
||||
self.soundState = state;
|
||||
|
||||
if ( state == "bark" )
|
||||
{
|
||||
self thread playBark( attackState );
|
||||
}
|
||||
else if ( state == "growl" )
|
||||
{
|
||||
self thread playGrowl( attackState );
|
||||
}
|
||||
else if ( state == "pant" )
|
||||
{
|
||||
self thread maps\mp\agents\dog\_dog_think::playPanting();
|
||||
}
|
||||
else
|
||||
{
|
||||
assertmsg( "unknown sound state " + state );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
playBark( state ) // self == dog
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "end_dog_sound" );
|
||||
|
||||
if( !isDefined( self.barking_sound ) )
|
||||
{
|
||||
self PlaySoundOnMovingEnt( "mine_wolf_bark" );
|
||||
self.barking_sound = true;
|
||||
self watchBarking();
|
||||
}
|
||||
}
|
||||
|
||||
watchBarking() // self == dog
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "end_dog_sound" );
|
||||
|
||||
wait( RandomIntRange( 4, 6 ) ); // allow wolves to bark more frequently than dogs
|
||||
self.barking_sound = undefined;
|
||||
}
|
||||
|
||||
playGrowl( state ) // self == dog
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
self endon( "end_dog_sound" );
|
||||
|
||||
if ( IsDefined( self.lastGrowlPlayedTime ) && GetTime() - self.lastGrowlPlayedTime < 3000 )
|
||||
wait( 3 );
|
||||
|
||||
// while the dog is in this state randomly play growl
|
||||
while ( true )
|
||||
{
|
||||
self.lastGrowlPlayedTime = GetTime();
|
||||
self PlaySoundOnMovingEnt( "mine_wolf_growl" );
|
||||
|
||||
wait( RandomIntRange( 3, 6 ) );
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user