iw6-scripts-dev/maps/mp/killstreaks/_portableaoegenerator.gsc
2024-12-11 11:28:08 +01:00

429 lines
12 KiB
Plaintext

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