611 lines
21 KiB
Plaintext
611 lines
21 KiB
Plaintext
#using scripts\shared\challenges_shared;
|
|
#using scripts\shared\clientfield_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\util_shared;
|
|
|
|
|
|
|
|
|
|
|
|
#using scripts\mp\gametypes\_battlechatter;
|
|
#using scripts\mp\gametypes\_globallogic_player;
|
|
|
|
#using scripts\mp\_challenges;
|
|
|
|
|
|
|
|
#namespace destructible;
|
|
|
|
function autoexec __init__sytem__() { system::register("destructible",&__init__,undefined,undefined); }
|
|
|
|
#using_animtree ( "mp_vehicles" );
|
|
|
|
function __init__()
|
|
{
|
|
clientfield::register( "scriptmover", "start_destructible_explosion", 1, 10, "int" );
|
|
|
|
level.destructible_callbacks = [];
|
|
destructibles = GetEntArray( "destructible", "targetname" );
|
|
|
|
// since this is globally run, only continue if we have a destructible in the level
|
|
if( destructibles.size <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// array::thread_all( destructibles,&destructible_think );
|
|
|
|
for ( i = 0; i < destructibles.size; i++ )
|
|
{
|
|
if ( GetSubStr( destructibles[i].destructibledef, 0, 4 ) == "veh_" )
|
|
{
|
|
destructibles[i] thread car_death_think();
|
|
destructibles[i] thread car_grenade_stuck_think();
|
|
}
|
|
else if ( destructibles[i].destructibledef == "fxdest_upl_metal_tank_01" )
|
|
{
|
|
destructibles[i] thread tank_grenade_stuck_think();
|
|
}
|
|
}
|
|
|
|
init_explosions();
|
|
}
|
|
|
|
function init_explosions()
|
|
{
|
|
level.explosion_manager = SpawnStruct();
|
|
level.explosion_manager.count = 0;
|
|
level.explosion_manager.a_explosions = [];
|
|
|
|
i = 0;
|
|
while( i < 32 )
|
|
{
|
|
sExplosion = Spawn( "script_model", (0,0,0) );
|
|
if ( !isdefined( level.explosion_manager.a_explosions ) ) level.explosion_manager.a_explosions = []; else if ( !IsArray( level.explosion_manager.a_explosions ) ) level.explosion_manager.a_explosions = array( level.explosion_manager.a_explosions ); level.explosion_manager.a_explosions[level.explosion_manager.a_explosions.size]=sExplosion;;
|
|
i++;
|
|
}
|
|
}
|
|
|
|
function get_unused_explosion()
|
|
{
|
|
foreach( explosion in level.explosion_manager.a_explosions )
|
|
{
|
|
if( !( isdefined( explosion.in_use ) && explosion.in_use ) )
|
|
{
|
|
return explosion;
|
|
}
|
|
}
|
|
return level.explosion_manager.a_explosions[0];
|
|
}
|
|
|
|
//assumes explosion radius will never be greater than 2 ^ ( DESTRUCTIBLE_CLIENTFIELD_NUM_BITS )
|
|
function physics_explosion_and_rumble( origin, radius, physics_explosion )
|
|
{
|
|
sExplosion = get_unused_explosion();
|
|
sExplosion.in_use = true;
|
|
sExplosion.origin = origin;
|
|
|
|
assert( radius <= pow( 2, 10 ) - 1 );
|
|
|
|
if( ( isdefined( physics_explosion ) && physics_explosion ) )
|
|
{
|
|
radius += ( 1 << ( 10 - 1 ) );
|
|
}
|
|
|
|
{wait(.05);};
|
|
|
|
sExplosion clientfield::set( "start_destructible_explosion", radius );
|
|
|
|
sExplosion.in_use = false;
|
|
}
|
|
|
|
function event_callback( destructible_event, attacker, weapon ) // self == the destructible object (like the car or barrel)
|
|
{
|
|
explosion_radius = 0;
|
|
if(IsSubStr(destructible_event, "explode") && destructible_event != "explode")
|
|
{
|
|
tokens = StrTok(destructible_event, "_");
|
|
explosion_radius = tokens[1];
|
|
|
|
if(explosion_radius == "sm")
|
|
{
|
|
explosion_radius = 150;
|
|
}
|
|
else if(explosion_radius == "lg")
|
|
{
|
|
explosion_radius = 450;
|
|
}
|
|
else
|
|
{
|
|
explosion_radius = Int(explosion_radius);
|
|
}
|
|
|
|
destructible_event = "explode_complex"; // use a different explosion function
|
|
}
|
|
|
|
if( IsSubStr( destructible_event, "explosive" ) )
|
|
{
|
|
tokens = StrTok(destructible_event, "_");
|
|
explosion_radius_type = tokens[3];
|
|
|
|
if(explosion_radius_type == "small")
|
|
{
|
|
explosion_radius = 150;
|
|
}
|
|
else if(explosion_radius_type == "large")
|
|
{
|
|
explosion_radius = 450;
|
|
}
|
|
else
|
|
{
|
|
explosion_radius = 300;
|
|
}
|
|
}
|
|
|
|
if ( IsSubStr( destructible_event, "simple_timed_explosion" ) )
|
|
{
|
|
self thread simple_timed_explosion( destructible_event, attacker );
|
|
return;
|
|
}
|
|
|
|
switch ( destructible_event )
|
|
{
|
|
case "destructible_car_explosion":
|
|
self car_explosion( attacker );
|
|
if ( isdefined( weapon ) )
|
|
{
|
|
self.destroyingWeapon = weapon;
|
|
}
|
|
break;
|
|
|
|
case "destructible_car_fire":
|
|
level thread battlechatter::on_player_near_explodable( self, "car" );
|
|
self thread car_fire_think(attacker);
|
|
if ( isdefined( weapon ) )
|
|
{
|
|
self.destroyingWeapon = weapon;
|
|
}
|
|
break;
|
|
|
|
case "explode":
|
|
self thread simple_explosion( attacker );
|
|
break;
|
|
|
|
case "explode_complex":
|
|
self thread complex_explosion( attacker, explosion_radius );
|
|
break;
|
|
|
|
case "destructible_explosive_incendiary_small":
|
|
case "destructible_explosive_incendiary_large":
|
|
self explosive_incendiary_explosion( attacker, explosion_radius, false );
|
|
if ( isdefined( weapon ) )
|
|
{
|
|
self.destroyingWeapon = weapon;
|
|
}
|
|
break;
|
|
|
|
case "destructible_explosive_electrical_small":
|
|
case "destructible_explosive_electrical_large":
|
|
self explosive_electrical_explosion( attacker, explosion_radius, false );
|
|
if ( isdefined( weapon ) )
|
|
{
|
|
self.destroyingWeapon = weapon;
|
|
}
|
|
break;
|
|
|
|
case "destructible_explosive_concussive_small":
|
|
case "destructible_explosive_concussive_large":
|
|
self explosive_concussive_explosion( attacker, explosion_radius, false );
|
|
if ( isdefined( weapon ) )
|
|
{
|
|
self.destroyingWeapon = weapon;
|
|
}
|
|
break;
|
|
|
|
default:
|
|
//iprintln( "_destructible.gsc: unknown destructible event: '" + destructible_event + "'" );
|
|
break;
|
|
}
|
|
|
|
if ( isdefined( level.destructible_callbacks[ destructible_event ] ) )
|
|
{
|
|
self thread [[level.destructible_callbacks[ destructible_event ]]]( destructible_event, attacker, weapon );
|
|
}
|
|
}
|
|
|
|
function simple_explosion( attacker )
|
|
{
|
|
if ( ( isdefined( self.exploded ) && self.exploded ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.exploded = true;
|
|
|
|
offset = (0, 0, 5);
|
|
self RadiusDamage( self.origin + offset, 256, 300, 75, attacker, "MOD_EXPLOSIVE", GetWeapon( "explodable_barrel" ) );
|
|
physics_explosion_and_rumble( self.origin, 255, true );
|
|
|
|
if ( isdefined( attacker ) )
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + offset, attacker );
|
|
}
|
|
else
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + offset );
|
|
}
|
|
}
|
|
|
|
function simple_timed_explosion( destructible_event, attacker )
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait_times = [];
|
|
|
|
str = GetSubStr( destructible_event, 23 /* strlen( simple_timed_explosion ) */ );
|
|
tokens = StrTok( str, "_" );
|
|
|
|
for ( i = 0; i < tokens.size; i++ )
|
|
{
|
|
wait_times[ wait_times.size ] = Int( tokens[i] );
|
|
}
|
|
|
|
if ( wait_times.size <= 0 )
|
|
{
|
|
wait_times[ 0 ] = 5;
|
|
wait_times[ 1 ] = 10;
|
|
}
|
|
|
|
wait( RandomIntRange( wait_times[0], wait_times[1] ) );
|
|
simple_explosion( attacker );
|
|
}
|
|
|
|
function complex_explosion( attacker, max_radius )
|
|
{
|
|
offset = (0, 0, 5);
|
|
|
|
if( isdefined( attacker ) )
|
|
{
|
|
self RadiusDamage( self.origin + offset, max_radius, 300, 100, attacker );
|
|
}
|
|
else
|
|
{
|
|
self RadiusDamage( self.origin + offset, max_radius, 300, 100 );
|
|
}
|
|
|
|
physics_explosion_and_rumble( self.origin, max_radius, true );
|
|
if( isdefined( attacker ) )
|
|
{
|
|
self DoDamage( 20000, self.origin + offset, attacker );
|
|
}
|
|
else
|
|
{
|
|
self DoDamage( 20000, self.origin + offset );
|
|
}
|
|
}
|
|
|
|
|
|
function car_explosion( attacker, physics_explosion )
|
|
{
|
|
if ( isdefined( self.car_dead ) && self.car_dead )
|
|
{
|
|
// prevents recursive entry caused by DoDamage call below
|
|
return;
|
|
}
|
|
|
|
if ( !isdefined( physics_explosion ) )
|
|
{
|
|
physics_explosion = true;
|
|
}
|
|
|
|
self notify( "car_dead" );
|
|
self.car_dead = true;
|
|
|
|
if ( isdefined( attacker ))
|
|
{
|
|
self RadiusDamage( self.origin, 256, 300, 75, attacker, "MOD_EXPLOSIVE", GetWeapon( "destructible_car" ) );
|
|
}
|
|
else
|
|
{
|
|
self RadiusDamage( self.origin, 256, 300, 75 );
|
|
}
|
|
|
|
physics_explosion_and_rumble( self.origin, 255, physics_explosion );
|
|
|
|
if ( isdefined ( attacker ) )
|
|
attacker thread challenges::destroyed_car();
|
|
|
|
level.globalCarsDestroyed++;
|
|
if ( isdefined ( attacker ) )
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + (0, 0, 1), attacker );
|
|
}
|
|
else
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + (0, 0, 1));
|
|
}
|
|
|
|
self MarkDestructibleDestroyed();
|
|
}
|
|
|
|
function tank_grenade_stuck_think()
|
|
{
|
|
self endon( "destructible_base_piece_death" );
|
|
self endon( "death" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill( "grenade_stuck", missile );
|
|
|
|
if ( !IsDefined( missile ) || !IsDefined( missile.model ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( missile.model == "t5_weapon_crossbow_bolt" || missile.model == "t6_wpn_grenade_semtex_projectile" || missile.model == "wpn_t7_c4_world" )
|
|
{
|
|
self thread tank_grenade_stuck_explode( missile );
|
|
}
|
|
}
|
|
}
|
|
|
|
function tank_grenade_stuck_explode( missile )
|
|
{
|
|
self endon( "destructible_base_piece_death" );
|
|
self endon( "death" );
|
|
|
|
owner = GetMissileOwner( missile );
|
|
|
|
if ( IsDefined( owner ) && missile.model == "wpn_t7_c4_world" )
|
|
{
|
|
owner endon( "disconnect" );
|
|
owner endon( "weapon_object_destroyed" );
|
|
missile endon( "picked_up" );
|
|
|
|
missile thread tank_hacked_c4( self );
|
|
}
|
|
|
|
missile waittill( "explode" );
|
|
|
|
if ( IsDefined( owner ) )
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + (0, 0, 1), owner );
|
|
}
|
|
else
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + (0, 0, 1) );
|
|
}
|
|
}
|
|
|
|
function tank_hacked_c4( tank )
|
|
{
|
|
tank endon( "destructible_base_piece_death" );
|
|
tank endon( "death" );
|
|
self endon( "death" );
|
|
|
|
self waittill( "hacked" );
|
|
|
|
self notify( "picked_up" );
|
|
tank thread tank_grenade_stuck_explode( self );
|
|
}
|
|
|
|
function car_death_think()
|
|
{
|
|
self endon( "car_dead" );
|
|
self.car_dead = false;
|
|
|
|
self thread car_death_notify();
|
|
|
|
self waittill( "destructible_base_piece_death", attacker );
|
|
|
|
if ( isdefined( self ) )
|
|
{
|
|
self thread car_explosion( attacker, false );
|
|
}
|
|
}
|
|
|
|
function car_grenade_stuck_think()
|
|
{
|
|
self endon( "destructible_base_piece_death" );
|
|
self endon( "car_dead" );
|
|
self endon( "death" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill( "grenade_stuck", missile );
|
|
|
|
if ( !isdefined( missile ) || !isdefined( missile.model ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( missile.model == "t5_weapon_crossbow_bolt" || missile.model == "t6_wpn_grenade_semtex_projectile" || missile.model == "wpn_t7_c4_world" )
|
|
{
|
|
self thread car_grenade_stuck_explode( missile );
|
|
}
|
|
}
|
|
}
|
|
|
|
function car_grenade_stuck_explode( missile )
|
|
{
|
|
self endon( "destructible_base_piece_death" );
|
|
self endon( "car_dead" );
|
|
self endon( "death" );
|
|
|
|
owner = GetMissileOwner( missile );
|
|
|
|
if ( isdefined( owner ) && missile.model == "wpn_t7_c4_world" )
|
|
{
|
|
owner endon( "disconnect" );
|
|
owner endon( "weapon_object_destroyed" );
|
|
missile endon( "picked_up" );
|
|
|
|
missile thread car_hacked_c4( self );
|
|
}
|
|
|
|
missile waittill( "explode" );
|
|
|
|
if ( isdefined( owner ) )
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + (0, 0, 1), owner );
|
|
}
|
|
else
|
|
{
|
|
self DoDamage( self.health + 10000, self.origin + (0, 0, 1) );
|
|
}
|
|
}
|
|
|
|
function car_hacked_c4( car )
|
|
{
|
|
car endon( "destructible_base_piece_death" );
|
|
car endon( "car_dead" );
|
|
car endon( "death" );
|
|
self endon( "death" );
|
|
|
|
self waittill( "hacked" );
|
|
|
|
self notify( "picked_up" );
|
|
car thread car_grenade_stuck_explode( self );
|
|
}
|
|
|
|
function car_death_notify()
|
|
{
|
|
self endon( "car_dead" );
|
|
|
|
self waittill( "death", attacker );
|
|
self notify( "destructible_base_piece_death", attacker );
|
|
}
|
|
|
|
function car_fire_think(attacker)
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait( RandomIntRange( 7, 10 ) );
|
|
|
|
self thread car_explosion( attacker );
|
|
}
|
|
|
|
function CodeCallback_DestructibleEvent( event, param1, param2, param3, param4 )
|
|
{
|
|
if( event == "broken" )
|
|
{
|
|
notify_type = param1;
|
|
attacker = param2;
|
|
piece = param3;
|
|
weapon = param4;
|
|
|
|
event_callback( notify_type, attacker, weapon );
|
|
|
|
self notify( event, notify_type, attacker );
|
|
}
|
|
else if( event == "breakafter" )
|
|
{
|
|
piece = param1;
|
|
time = param2;
|
|
damage = param3;
|
|
self thread breakAfter( time, damage, piece );
|
|
}
|
|
}
|
|
|
|
function breakAfter( time, damage, piece )
|
|
{
|
|
self notify( "breakafter" );
|
|
self endon( "breakafter" );
|
|
|
|
wait time;
|
|
|
|
// this does not work in mp. DoDamage does not take a piece for mp.
|
|
self dodamage( damage, self.origin, undefined, /*piece*/undefined );
|
|
}
|
|
|
|
function explosive_incendiary_explosion( attacker, explosion_radius, physics_explosion )
|
|
{
|
|
if ( !IsVehicle( self ) )
|
|
{
|
|
offset = (0, 0, 5);
|
|
if ( isdefined( attacker ))
|
|
{
|
|
self RadiusDamage( self.origin + offset, explosion_radius, 256, 75, attacker, "MOD_BURNED", GetWeapon( "incendiary_fire" ) );
|
|
}
|
|
else
|
|
{
|
|
self RadiusDamage( self.origin + offset, explosion_radius, 256, 75 );
|
|
}
|
|
physics_explosion_and_rumble( self.origin, 255, physics_explosion );
|
|
|
|
}
|
|
|
|
|
|
// delete the clip that is attached
|
|
if( isdefined( self.target ) )
|
|
{
|
|
dest_clip = GetEnt( self.target, "targetname" );
|
|
if( isDefined( dest_clip ) )
|
|
{
|
|
dest_clip delete();
|
|
}
|
|
}
|
|
|
|
self MarkDestructibleDestroyed();
|
|
}
|
|
|
|
function explosive_electrical_explosion( attacker, explosion_radius, physics_explosion )
|
|
{
|
|
if ( !IsVehicle( self ) )
|
|
{
|
|
offset = (0, 0, 5);
|
|
if ( isdefined( attacker ))
|
|
{
|
|
self RadiusDamage( self.origin + offset, explosion_radius, 256, 75, attacker, "MOD_ELECTROCUTED" );
|
|
}
|
|
else
|
|
{
|
|
self RadiusDamage( self.origin + offset, explosion_radius, 256, 75 );
|
|
}
|
|
physics_explosion_and_rumble( self.origin, 255, physics_explosion );
|
|
}
|
|
|
|
|
|
// delete the clip that is attached
|
|
if( isdefined( self.target ) )
|
|
{
|
|
dest_clip = GetEnt( self.target, "targetname" );
|
|
if( isDefined( dest_clip ) )
|
|
{
|
|
dest_clip delete();
|
|
}
|
|
}
|
|
|
|
self MarkDestructibleDestroyed();
|
|
}
|
|
|
|
function explosive_concussive_explosion( attacker, explosion_radius, physics_explosion )
|
|
{
|
|
if ( !IsVehicle( self ) )
|
|
{
|
|
offset = (0, 0, 5);
|
|
if ( isdefined( attacker ))
|
|
{
|
|
self RadiusDamage( self.origin + offset, explosion_radius, 256, 75, attacker, "MOD_GRENADE" );
|
|
}
|
|
else
|
|
{
|
|
self RadiusDamage( self.origin + offset, explosion_radius, 256, 75 );
|
|
}
|
|
physics_explosion_and_rumble( self.origin, 255, physics_explosion );
|
|
}
|
|
|
|
|
|
// delete the clip that is attached
|
|
if( isdefined( self.target ) )
|
|
{
|
|
dest_clip = GetEnt( self.target, "targetname" );
|
|
if( isDefined( dest_clip ) )
|
|
{
|
|
dest_clip delete();
|
|
}
|
|
}
|
|
|
|
self MarkDestructibleDestroyed();
|
|
}
|