627 lines
14 KiB
Plaintext
627 lines
14 KiB
Plaintext
#include common_scripts\utility;
|
|
#include maps\_utility;
|
|
|
|
init()
|
|
{
|
|
level._effect[ "c4_light_blink" ] = loadfx( "misc/light_c4_blink" );
|
|
level._effect[ "claymore_laser" ] = loadfx( "misc/claymore_laser" );
|
|
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
level.players[ i ] thread watchGrenadeUsage();
|
|
}
|
|
}
|
|
|
|
watchGrenadeUsage()
|
|
{
|
|
level.c4explodethisframe = false;
|
|
self endon( "death" );
|
|
self.c4array = [];
|
|
self.throwingGrenade = false;
|
|
|
|
thread watchC4();
|
|
thread watchC4Detonation();
|
|
thread watchC4AltDetonation();
|
|
thread watchClaymores();
|
|
thread begin_semtex_grenade_tracking();
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill( "grenade_pullback", weaponName );
|
|
self.throwingGrenade = true;
|
|
|
|
if ( weaponName == "c4" )
|
|
self beginC4Tracking();
|
|
else if ( weaponName == "smoke_grenade_american" )
|
|
self beginsmokegrenadetracking();
|
|
//else if ( weaponName == "semtex_grenade" )
|
|
// self beginsemtexgrenadetracking();
|
|
else
|
|
self beginGrenadeTracking();
|
|
}
|
|
}
|
|
|
|
beginsmokegrenadetracking()
|
|
{
|
|
self waittill( "grenade_fire", grenade, weaponName );
|
|
if ( !isdefined( level.smokegrenades ) )
|
|
level.smokegrenades = 0;
|
|
if ( level.smokegrenades > 2 && getdvar( "player_sustainAmmo" ) != "0" )
|
|
grenade delete();
|
|
else
|
|
grenade thread smoke_grenade_death();
|
|
}
|
|
|
|
begin_semtex_grenade_tracking()
|
|
{
|
|
while( 1 )
|
|
{
|
|
self waittill( "grenade_fire", grenade, weaponName );
|
|
if ( weaponName == "semtex_grenade" )
|
|
{
|
|
thread track_semtex_grenade( grenade );
|
|
grenade thread semtex_sticky_handle( self );
|
|
}
|
|
}
|
|
}
|
|
|
|
track_semtex_grenade( grenade )
|
|
{
|
|
self.throwingGrenade = false;
|
|
|
|
if( !isdefined( level.thrown_semtex_grenades ) )
|
|
level.thrown_semtex_grenades = 1;
|
|
else
|
|
level.thrown_semtex_grenades++;
|
|
|
|
grenade waittill ( "death" );
|
|
|
|
waittillframeend;
|
|
level.thrown_semtex_grenades--;
|
|
}
|
|
|
|
semtex_sticky_handle( attacker )
|
|
{
|
|
self waittill ("missile_stuck", entity );
|
|
|
|
if( !isdefined( entity ) )
|
|
return;
|
|
|
|
// just handling vehicles for now.
|
|
if( entity.code_classname != "script_vehicle" )
|
|
return;
|
|
|
|
entity.has_semtex_on_it = true;
|
|
|
|
self waittill ( "explode" );
|
|
|
|
if( !isdefined( entity ) || !isalive( entity ) )
|
|
return; // possible it could be dead at this point
|
|
|
|
if(
|
|
entity maps\_vehicle::is_godmode()
|
|
|| entity maps\_vehicle::attacker_isonmyteam( attacker )
|
|
)
|
|
{
|
|
entity.has_semtex_on_it = undefined;
|
|
return;
|
|
}
|
|
|
|
entity kill( entity.origin, attacker );
|
|
|
|
}
|
|
|
|
smoke_grenade_death()
|
|
{
|
|
level.smokegrenades++ ;
|
|
wait 50;
|
|
level.smokegrenades -- ;
|
|
}
|
|
|
|
beginGrenadeTracking()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "grenade_fire", grenade, weaponName );
|
|
if ( weaponName == "fraggrenade" )
|
|
grenade thread grenade_earthQuake();
|
|
|
|
self.throwingGrenade = false;
|
|
}
|
|
|
|
|
|
beginC4Tracking()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill_any( "grenade_fire", "weapon_change" );
|
|
self.throwingGrenade = false;
|
|
}
|
|
|
|
|
|
watchC4()
|
|
{
|
|
//maxc4 = 2;
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "grenade_fire", c4, weapname );
|
|
if ( weapname == "c4" )
|
|
{
|
|
if ( !self.c4array.size )
|
|
self thread watchC4AltDetonate();
|
|
|
|
/*if ( self.c4array.size >= maxc4 )
|
|
{
|
|
newarray = [];
|
|
for ( i = 0; i < self.c4array.size; i++ )
|
|
{
|
|
if ( isdefined(self.c4array[i]) )
|
|
newarray[newarray.size] = self.c4array[i];
|
|
}
|
|
self.c4array = newarray;
|
|
for ( i = 0; i < self.c4array.size - maxc4 + 1; i++ )
|
|
{
|
|
self.c4array[i] delete();
|
|
}
|
|
newarray = [];
|
|
for ( i = 0; i < maxc4 - 1; i++ )
|
|
{
|
|
newarray[i] = self.c4array[self.c4array.size - maxc4 + 1 + i];
|
|
}
|
|
self.c4array = newarray;
|
|
}*/
|
|
|
|
self.c4array[ self.c4array.size ] = c4;
|
|
if ( self.c4array.size > 15 && getdvar( "player_sustainAmmo" ) != "0" )
|
|
self.c4array[ 0 ] delete();
|
|
c4.owner = self;
|
|
// c4 thread maps\mp\gametypes\_shellshock::c4_earthQuake();
|
|
c4 thread c4Damage();
|
|
self thread c4death( c4 );
|
|
c4 thread playC4Effects();
|
|
}
|
|
}
|
|
}
|
|
|
|
c4death( c4 )
|
|
{
|
|
// this allows me to delete the first one thrown and reconstruct the array for cheats that enable all the ammo. - Nate
|
|
c4 waittill( "death" );
|
|
self.c4array = array_remove_nokeys( self.c4array, c4 );
|
|
}
|
|
|
|
watchClaymores()
|
|
{
|
|
self endon( "spawned_player" );
|
|
self endon( "disconnect" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "grenade_fire", claymore, weapname );
|
|
if ( weapname == "claymore" || weapname == "claymore_mp" )
|
|
{
|
|
claymore.owner = self;
|
|
claymore thread c4Damage();
|
|
claymore thread claymoreDetonation();
|
|
claymore thread playClaymoreEffects();
|
|
claymore thread claymoreMakeSentient( self.team );
|
|
}
|
|
}
|
|
}
|
|
|
|
claymoreMakeSentient( team )
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait 1; // let claymore planting animation finish, and settle
|
|
|
|
self MakeEntitySentient( team, true );
|
|
self.attackerAccuracy = 2;
|
|
self.maxVisibleDist = 750;
|
|
self.threatBias = -1000;
|
|
}
|
|
|
|
claymoreDetonation()
|
|
{
|
|
self endon( "death" );
|
|
|
|
// wait until we settle
|
|
self waittill( "missile_stuck" );
|
|
|
|
detonateRadius = 192;// matches MP
|
|
|
|
damagearea = spawn( "trigger_radius", self.origin + ( 0, 0, 0 - detonateRadius ), 9, detonateRadius, detonateRadius * 2 );
|
|
|
|
self thread deleteOnDeath( damagearea );
|
|
|
|
if ( !isdefined( level.claymores ) )
|
|
level.claymores = [];
|
|
level.claymores = array_add( level.claymores, self );
|
|
|
|
// limit the number of active claymores
|
|
if ( !is_specialop() && level.claymores.size > 15 )
|
|
{
|
|
level.claymores[ 0 ] delete();
|
|
}
|
|
|
|
while ( 1 )
|
|
{
|
|
damagearea waittill( "trigger", ent );
|
|
|
|
if ( isdefined( self.owner ) && ent == self.owner )
|
|
continue;
|
|
|
|
if ( isplayer( ent ) )
|
|
continue;// no enemy claymores in SP.
|
|
|
|
if ( ent damageConeTrace( self.origin, self ) > 0 )
|
|
{
|
|
self playsound( "claymore_activated_SP" );
|
|
wait 0.4;
|
|
if ( isdefined( self.owner ) )
|
|
self detonate( self.owner );
|
|
else
|
|
self detonate( undefined );
|
|
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
deleteOnDeath( ent )
|
|
{
|
|
self waittill( "death" );
|
|
// stupid getarraykeys in array_remove reversing the order - nate
|
|
level.claymores = array_remove_nokeys( level.claymores, self );
|
|
wait .05;
|
|
if ( isdefined( ent ) )
|
|
ent delete();
|
|
}
|
|
|
|
watchC4Detonation()
|
|
{
|
|
self endon( "death" );
|
|
while ( 1 )
|
|
{
|
|
self waittill( "detonate" );
|
|
weap = self getCurrentWeapon();
|
|
if ( weap == "c4" )
|
|
{
|
|
for ( i = 0; i < self.c4array.size; i++ )
|
|
{
|
|
if ( isdefined( self.c4array[ i ] ) )
|
|
self.c4array[ i ] thread waitAndDetonate( 0.1 );
|
|
}
|
|
self.c4array = [];
|
|
}
|
|
}
|
|
}
|
|
|
|
watchC4AltDetonation()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "alt_detonate" );
|
|
weap = self getCurrentWeapon();
|
|
if ( weap != "c4" )
|
|
{
|
|
newarray = [];
|
|
for ( i = 0; i < self.c4array.size; i++ )
|
|
{
|
|
c4 = self.c4array[ i ];
|
|
if ( isdefined( self.c4array[ i ] ) )
|
|
c4 thread waitAndDetonate( 0.1 );
|
|
}
|
|
self.c4array = newarray;
|
|
self notify( "detonated" );
|
|
}
|
|
}
|
|
}
|
|
|
|
waitAndDetonate( delay )
|
|
{
|
|
self endon( "death" );
|
|
wait delay;
|
|
|
|
self detonate();
|
|
}
|
|
|
|
|
|
c4Damage()
|
|
{
|
|
// self endon( "death" );
|
|
|
|
self.health = 100;
|
|
self setcandamage( true );
|
|
self.maxhealth = 100000;
|
|
self.health = self.maxhealth;
|
|
|
|
attacker = undefined;
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "damage", amount, attacker );
|
|
|
|
// don't allow people to destroy C4 on their team if FF is off
|
|
// if ( !friendlyFireCheck(self.owner, attacker) )
|
|
// continue;
|
|
|
|
break;
|
|
}
|
|
|
|
self playsound( "claymore_activated_SP" );
|
|
|
|
if ( level.c4explodethisframe )
|
|
wait .1 + randomfloat( .4 );
|
|
else
|
|
wait .05;
|
|
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
|
|
level.c4explodethisframe = true;
|
|
|
|
thread resetC4ExplodeThisFrame();
|
|
|
|
if ( isplayer( attacker ) )
|
|
self detonate( attacker );
|
|
else
|
|
self detonate();
|
|
// won't get here; got death notify.
|
|
}
|
|
|
|
resetC4ExplodeThisFrame()
|
|
{
|
|
wait .05;
|
|
level.c4explodethisframe = false;
|
|
}
|
|
|
|
saydamaged( orig, amount )
|
|
{
|
|
for ( i = 0; i < 60; i++ )
|
|
{
|
|
print3d( orig, "damaged! " + amount );
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
|
|
playC4Effects()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "missile_stuck" );
|
|
|
|
PlayFXOnTag( getfx( "c4_light_blink" ), self, "tag_fx" );
|
|
}
|
|
|
|
playClaymoreEffects()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "missile_stuck" );
|
|
|
|
PlayFXOnTag( getfx( "claymore_laser" ), self, "tag_fx" );
|
|
}
|
|
|
|
clearFXOnDeath( fx )
|
|
{
|
|
self waittill( "death" );
|
|
fx delete();
|
|
}
|
|
|
|
|
|
|
|
// these functions are used with scripted weapons (like c4, claymores, artillery)
|
|
// returns an array of objects representing damageable entities (including players) within a given sphere.
|
|
// each object has the property damageCenter, which represents its center (the location from which it can be damaged).
|
|
// each object also has the property entity, which contains the entity that it represents.
|
|
// to damage it, call damageEnt() on it.
|
|
getDamageableEnts( pos, radius, doLOS, startRadius )
|
|
{
|
|
ents = [];
|
|
|
|
if ( !isdefined( doLOS ) )
|
|
doLOS = false;
|
|
|
|
if ( !isdefined( startRadius ) )
|
|
startRadius = 0;
|
|
|
|
// players
|
|
for ( i = 0; i < level.players.size; i++ )
|
|
{
|
|
if ( !isalive( level.players[ i ] ) || level.players[ i ].sessionstate != "playing" )
|
|
continue;
|
|
|
|
playerpos = level.players[ i ].origin + ( 0, 0, 32 );
|
|
dist = distance( pos, playerpos );
|
|
if ( dist < radius && ( !doLOS || weaponDamageTracePassed( pos, playerpos, startRadius, undefined ) ) )
|
|
{
|
|
newent = spawnstruct();
|
|
newent.isPlayer = true;
|
|
newent.isADestructable = false;
|
|
newent.entity = level.players[ i ];
|
|
newent.damageCenter = playerpos;
|
|
ents[ ents.size ] = newent;
|
|
}
|
|
}
|
|
|
|
// grenades
|
|
grenades = getentarray( "grenade", "classname" );
|
|
for ( i = 0; i < grenades.size; i++ )
|
|
{
|
|
entpos = grenades[ i ].origin;
|
|
dist = distance( pos, entpos );
|
|
if ( dist < radius && ( !doLOS || weaponDamageTracePassed( pos, entpos, startRadius, grenades[ i ] ) ) )
|
|
{
|
|
newent = spawnstruct();
|
|
newent.isPlayer = false;
|
|
newent.isADestructable = false;
|
|
newent.entity = grenades[ i ];
|
|
newent.damageCenter = entpos;
|
|
ents[ ents.size ] = newent;
|
|
}
|
|
}
|
|
|
|
destructables = getentarray( "destructable", "targetname" );
|
|
for ( i = 0; i < destructables.size; i++ )
|
|
{
|
|
entpos = destructables[ i ].origin;
|
|
dist = distance( pos, entpos );
|
|
if ( dist < radius && ( !doLOS || weaponDamageTracePassed( pos, entpos, startRadius, destructables[ i ] ) ) )
|
|
{
|
|
newent = spawnstruct();
|
|
newent.isPlayer = false;
|
|
newent.isADestructable = true;
|
|
newent.entity = destructables[ i ];
|
|
newent.damageCenter = entpos;
|
|
ents[ ents.size ] = newent;
|
|
}
|
|
}
|
|
|
|
return ents;
|
|
}
|
|
|
|
weaponDamageTracePassed( from, to, startRadius, ignore )
|
|
{
|
|
midpos = undefined;
|
|
|
|
diff = to - from;
|
|
if ( lengthsquared( diff ) < startRadius * startRadius )
|
|
midpos = to;
|
|
dir = vectornormalize( diff );
|
|
midpos = from + ( dir[ 0 ] * startRadius, dir[ 1 ] * startRadius, dir[ 2 ] * startRadius );
|
|
|
|
trace = bullettrace( midpos, to, false, ignore );
|
|
|
|
if ( getdvarint( "scr_damage_debug" ) != 0 )
|
|
{
|
|
if ( trace[ "fraction" ] == 1 )
|
|
{
|
|
thread debugline( midpos, to, ( 1, 1, 1 ) );
|
|
}
|
|
else
|
|
{
|
|
thread debugline( midpos, trace[ "position" ], ( 1, .9, .8 ) );
|
|
thread debugline( trace[ "position" ], to, ( 1, .4, .3 ) );
|
|
}
|
|
}
|
|
|
|
return( trace[ "fraction" ] == 1 );
|
|
}
|
|
|
|
// eInflictor = the entity that causes the damage (e.g. a claymore)
|
|
// eAttacker = the player that is attacking
|
|
// iDamage = the amount of damage to do
|
|
// sMeansOfDeath = string specifying the method of death (e.g. "MOD_PROJECTILE_SPLASH")
|
|
// sWeapon = string specifying the weapon used (e.g. "claymore_mp")
|
|
// damagepos = the position damage is coming from
|
|
// damagedir = the direction damage is moving in
|
|
damageEnt( eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, damagepos, damagedir )
|
|
{
|
|
if ( self.isPlayer )
|
|
{
|
|
self.damageOrigin = damagepos;
|
|
self.entity thread [[ level.callbackPlayerDamage ]](
|
|
eInflictor,// eInflictor The entity that causes the damage.( e.g. a turret )
|
|
eAttacker,// eAttacker The entity that is attacking.
|
|
iDamage,// iDamage Integer specifying the amount of damage done
|
|
0,// iDFlags Integer specifying flags that are to be applied to the damage
|
|
sMeansOfDeath,// sMeansOfDeath Integer specifying the method of death
|
|
sWeapon,// sWeapon The weapon number of the weapon used to inflict the damage
|
|
damagepos,// vPoint The point the damage is from?
|
|
damagedir,// vDir The direction of the damage
|
|
"none",// sHitLoc The location of the hit
|
|
0// psOffsetTime The time offset for the damage
|
|
);
|
|
}
|
|
else
|
|
{
|
|
// destructable walls and such can only be damaged in certain ways.
|
|
if ( self.isADestructable && ( sWeapon == "artillery_mp" || sWeapon == "claymore_mp" ) )
|
|
return;
|
|
|
|
self.entity notify( "damage", iDamage, eAttacker );
|
|
}
|
|
}
|
|
|
|
debugline( a, b, color )
|
|
{
|
|
for ( i = 0; i < 30 * 20; i++ )
|
|
{
|
|
line( a, b, color );
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
|
|
onWeaponDamage( eInflictor, sWeapon, meansOfDeath, damage )
|
|
{
|
|
self endon( "death" );
|
|
|
|
switch( sWeapon )
|
|
{
|
|
case "concussion_grenade_mp":
|
|
// should match weapon settings in gdt
|
|
radius = 512;
|
|
scale = 1 - ( distance( self.origin, eInflictor.origin ) / radius );
|
|
|
|
time = 1 + ( 4 * scale );
|
|
|
|
wait( 0.05 );
|
|
self shellShock( "concussion_grenade_mp", time );
|
|
break;
|
|
default:
|
|
// shellshock will only be done if meansofdeath is an appropriate type and if there is enough damage.
|
|
// maps\mp\gametypes\_shellshock::shellshockOnDamage( meansOfDeath, damage );
|
|
break;
|
|
}
|
|
|
|
}
|
|
|
|
watchC4AltDetonate()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
self endon( "detonated" );
|
|
level endon( "game_ended" );
|
|
|
|
buttonTime = 0;
|
|
for ( ;; )
|
|
{
|
|
if ( self UseButtonPressed() )
|
|
{
|
|
buttonTime = 0;
|
|
while ( self UseButtonPressed() )
|
|
{
|
|
buttonTime += 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
|
|
println( "pressTime1: " + buttonTime );
|
|
if ( buttonTime >= 0.5 )
|
|
continue;
|
|
|
|
buttonTime = 0;
|
|
while ( !self UseButtonPressed() && buttonTime < 0.5 )
|
|
{
|
|
buttonTime += 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
|
|
println( "delayTime: " + buttonTime );
|
|
if ( buttonTime >= 0.5 )
|
|
continue;
|
|
|
|
if ( !self.c4Array.size )
|
|
return;
|
|
|
|
self notify( "alt_detonate" );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
} |