boiii-scripts/mp/_destructible.gsc
2023-04-13 17:30:38 +02:00

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();
}