1864 lines
48 KiB
Plaintext
1864 lines
48 KiB
Plaintext
#using scripts\codescripts\struct;
|
|
|
|
#using scripts\shared\flag_shared;
|
|
#using scripts\shared\math_shared;
|
|
#using scripts\shared\sound_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\vehicle_shared;
|
|
#using scripts\shared\vehicle_ai_shared;
|
|
|
|
|
|
|
|
|
|
|
|
#namespace vehicle_death;
|
|
|
|
function autoexec __init__sytem__() { system::register("vehicle_death",&__init__,undefined,undefined); }
|
|
|
|
// _vehicle_death.gsc - all things related to vehicles dying
|
|
|
|
// Utility rain functions:
|
|
|
|
|
|
#using_animtree( "generic" );
|
|
|
|
function __init__()
|
|
{
|
|
SetDvar( "debug_crash_type", -1 );
|
|
|
|
//TODO T7 - function port
|
|
/*if( IsAssetLoaded( "fx", "_debug/fx_debug_deleted_fx" ) )
|
|
{
|
|
//level.heli_hit_fx =
|
|
level.heli_crash_smoke_trail_fx = "_debug/fx_debug_deleted_fx";
|
|
}*/
|
|
}
|
|
|
|
function main()
|
|
{
|
|
self endon( "nodeath_thread" );
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
// waittill death twice. in some cases the vehicle dies and does a bunch of stuff. then it gets deleted. which it then needs to do more stuff
|
|
self waittill( "death", attacker, damageFromUnderneath, weapon, point, dir );
|
|
|
|
if( isdefined( self.death_enter_cb ) )
|
|
[[self.death_enter_cb]]();
|
|
|
|
if ( isdefined( self.script_deathflag ) )
|
|
{
|
|
level flag::set( self.script_deathflag );
|
|
}
|
|
|
|
if ( !isdefined( self.delete_on_death ) )
|
|
{
|
|
self thread play_death_audio();
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self death_cleanup_level_variables();
|
|
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
|
|
if ( vehicle::is_corpse( self ) )
|
|
{
|
|
// Cleanup Riders
|
|
if ( !( isdefined( self.dont_kill_riders ) && self.dont_kill_riders ) )
|
|
{
|
|
self death_cleanup_riders();
|
|
}
|
|
|
|
// kills some destructible fxs
|
|
self notify( "delete_destructible" );
|
|
|
|
// Done here
|
|
return;
|
|
}
|
|
|
|
self vehicle::lights_off();
|
|
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
|
|
// Run vehicle death thread
|
|
if ( isdefined( level.vehicle_death_thread[ self.vehicletype ] ) )
|
|
{
|
|
thread [[ level.vehicle_death_thread[ self.vehicletype ] ]]();
|
|
}
|
|
|
|
if ( !isdefined( self.delete_on_death ) )
|
|
{
|
|
// Do radius damage
|
|
thread death_radius_damage();
|
|
}
|
|
|
|
is_aircraft = ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) || ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) );
|
|
|
|
if ( !isdefined( self.destructibledef ) )
|
|
{
|
|
if ( !is_aircraft && !( self.vehicleType == "horse" || self.vehicleType == "horse_player" || self.vehicleType == "horse_player_low" || self.vehicleType == "horse_low" || self.vehicleType == "horse_axis" ) && isdefined( self.deathmodel ) && self.deathmodel != "" )
|
|
{
|
|
self thread set_death_model( self.deathmodel, self.modelswapdelay );
|
|
}
|
|
|
|
// Do death_fx if it has not been mantled
|
|
if ( !isdefined( self.delete_on_death ) && ( !isdefined( self.mantled ) || !self.mantled ) && !isdefined( self.nodeathfx ) )
|
|
{
|
|
thread death_fx();
|
|
}
|
|
|
|
if ( isdefined( self.delete_on_death ) )
|
|
{
|
|
{wait(.05);};
|
|
|
|
if ( self.disconnectPathOnStop === true )
|
|
{
|
|
self vehicle::disconnect_paths();
|
|
}
|
|
|
|
if ( !( isdefined( self.no_free_on_death ) && self.no_free_on_death ) )
|
|
{
|
|
self freevehicle();
|
|
self.isacorpse = true;
|
|
|
|
{wait(.05);};
|
|
|
|
if ( isdefined( self ) )
|
|
{
|
|
self notify( "death_finished" );
|
|
self delete();
|
|
}
|
|
}
|
|
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// // makes riders blow up
|
|
// if ( isdefined( self.riders ) && self.riders.size > 0 )
|
|
// {
|
|
// vehicle_aianim::blowup_riders();
|
|
// }
|
|
|
|
// Place a bad place cylinder if specified
|
|
thread death_make_badplace( self.vehicletype );
|
|
|
|
// Send vehicle type specific notify
|
|
if ( isdefined( level.vehicle_deathnotify ) && isdefined( level.vehicle_deathnotify[ self.vehicletype ] ) )
|
|
{
|
|
level notify( level.vehicle_deathnotify[ self.vehicletype ], attacker );
|
|
}
|
|
|
|
if ( Target_IsTarget( self ) )
|
|
{
|
|
Target_Remove( self );
|
|
}
|
|
|
|
// all the vehicles get the same jolt..
|
|
if ( self.classname == "script_vehicle" )
|
|
{
|
|
self thread death_jolt( self.vehicletype );
|
|
}
|
|
|
|
if ( do_scripted_crash() )
|
|
{
|
|
// Check for scripted crash
|
|
self thread death_update_crash( point, dir );
|
|
}
|
|
|
|
// Clear turret target
|
|
if ( isdefined( self.turretweapon ) && self.turretweapon != level.weaponNone)
|
|
{
|
|
self clearTurretTarget();
|
|
}
|
|
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
|
|
// Wait here until we're finished with a crash or rolling death
|
|
self waittill_crash_done_or_stopped();
|
|
|
|
if ( isdefined( self ) )
|
|
{
|
|
while ( isdefined( self ) && isdefined( self.dontfreeme ) )
|
|
{
|
|
wait( .05 );
|
|
}
|
|
|
|
// send notifies
|
|
self notify( "stop_looping_death_fx" );
|
|
self notify( "death_finished" );
|
|
|
|
wait .05;
|
|
|
|
if ( isdefined( self ) )
|
|
{
|
|
if ( vehicle::is_corpse( self ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// AE 2-18-09: if the player is using it, then spit them out or kill them before freeing the vehicle
|
|
occupants = self GetVehOccupants();
|
|
if ( isdefined( occupants ) && occupants.size )
|
|
{
|
|
for ( i = 0; i < occupants.size; i++ )
|
|
{
|
|
self usevehicle( occupants[i], 0 );
|
|
}
|
|
}
|
|
|
|
if ( !( isdefined( self.no_free_on_death ) && self.no_free_on_death ) )
|
|
{
|
|
self freevehicle();
|
|
self.isacorpse = true;
|
|
}
|
|
|
|
if ( self.modeldummyon )
|
|
{
|
|
self hide();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function do_scripted_crash()
|
|
{
|
|
return !isdefined( self.do_scripted_crash ) || ( isdefined( self.do_scripted_crash ) && self.do_scripted_crash );
|
|
}
|
|
|
|
function play_death_audio()
|
|
{
|
|
if ( isdefined ( self ) && ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
|
|
{
|
|
if ( !isdefined ( self.death_counter ) )
|
|
{
|
|
self.death_counter = 0;
|
|
}
|
|
if ( self.death_counter == 0 )
|
|
{
|
|
self.death_counter++;
|
|
self playsound ( "exp_veh_helicopter_hit" );
|
|
}
|
|
}
|
|
}
|
|
|
|
function play_spinning_plane_sound()
|
|
{
|
|
self playloopsound( "veh_drone_spin", .05 );
|
|
level util::waittill_any( "crash_move_done", "death" );
|
|
self stoploopsound( .02 );
|
|
}
|
|
|
|
function set_death_model( sModel, fDelay )
|
|
{
|
|
if( !isdefined( sModel ) )
|
|
return;
|
|
|
|
|
|
if ( isdefined( fDelay ) && ( fDelay > 0 ) )
|
|
{
|
|
wait fDelay;
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( isdefined( self.deathmodel_attached ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
eModel = vehicle::get_dummy();
|
|
if ( !isdefined( eModel ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !isdefined( eModel.death_anim ) && ( isdefined( eModel.animtree ) ) )
|
|
{
|
|
eModel ClearAnim( %root, 0 );
|
|
}
|
|
|
|
// SetModel can remove the destructible so only do if if we are really trying to swap to a specified death model
|
|
if( sModel != self.vehmodel )
|
|
{
|
|
eModel SetModel( sModel );
|
|
eModel SetEnemyModel( sModel );
|
|
}
|
|
}
|
|
|
|
|
|
function aircraft_crash( point, dir )
|
|
{
|
|
self.crashing = true;
|
|
|
|
if ( isdefined( self.unloading ) )
|
|
{
|
|
while ( isdefined( self.unloading ) )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self thread aircraft_crash_move( point, dir );
|
|
self thread play_spinning_plane_sound();
|
|
}
|
|
|
|
function helicopter_crash( point, dir )
|
|
{
|
|
self.crashing = true;
|
|
self thread play_crashing_loop();
|
|
|
|
if ( isdefined( self.unloading ) )
|
|
{
|
|
while ( isdefined( self.unloading ) )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//self thread helicopter_crash_move( point, dir );
|
|
self thread helicopter_crash_movement( point, dir );
|
|
|
|
}
|
|
|
|
function helicopter_crash_movement( point, dir )
|
|
{
|
|
self endon( "crash_done" );
|
|
|
|
self CancelAIMove();
|
|
self ClearVehGoalPos();
|
|
|
|
if ( isdefined( level.heli_crash_smoke_trail_fx ) )
|
|
{
|
|
if ( IsSubStr( self.vehicletype, "v78" ) )
|
|
{
|
|
playfxontag( level.heli_crash_smoke_trail_fx, self, "tag_origin" );
|
|
}
|
|
else if ( self.vehicletype == "drone_firescout_axis" || self.vehicletype == "drone_firescout_isi" )
|
|
{
|
|
playfxontag( level.heli_crash_smoke_trail_fx, self, "tag_main_rotor" );
|
|
}
|
|
else
|
|
{
|
|
playfxontag( level.heli_crash_smoke_trail_fx, self, "tag_engine_left" );
|
|
}
|
|
}
|
|
|
|
crash_zones = struct::get_array( "heli_crash_zone", "targetname" );
|
|
if ( crash_zones.size > 0 )
|
|
{
|
|
best_dist = 99999;
|
|
best_idx = -1;
|
|
|
|
if ( isdefined( self.a_crash_zones ) )
|
|
{
|
|
crash_zones = self.a_crash_zones;
|
|
}
|
|
|
|
for ( i = 0; i < crash_zones.size; i++ )
|
|
{
|
|
vec_to_crash_zone = crash_zones[i].origin - self.origin;
|
|
vec_to_crash_zone = ( vec_to_crash_zone[0], vec_to_crash_zone[1], 0 );
|
|
dist = Length( vec_to_crash_zone );
|
|
vec_to_crash_zone /= dist;
|
|
|
|
veloctiy_scale = -VectorDot( self.velocity, vec_to_crash_zone );
|
|
dist += 500 * veloctiy_scale;
|
|
|
|
if ( dist < best_dist )
|
|
{
|
|
best_dist = dist;
|
|
best_idx = i;
|
|
}
|
|
}
|
|
|
|
if ( best_idx != -1 )
|
|
{
|
|
self.crash_zone = crash_zones[best_idx];
|
|
self thread helicopter_crash_zone_accel( dir );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( isdefined( dir ) )
|
|
dir = VectorNormalize( dir );
|
|
else
|
|
dir = ( 1,0,0 );
|
|
side_dir = VectorCross( dir, ( 0,0,1 ) );
|
|
side_dir_mag = RandomFloatRange( -500, 500 );
|
|
side_dir_mag += math::sign( side_dir_mag ) * 60;
|
|
side_dir *= side_dir_mag;
|
|
side_dir += ( 0,0,150 );
|
|
|
|
self SetPhysAcceleration( ( RandomIntRange( -500, 500 ), RandomIntRange( -500, 500 ), -1000 ) );
|
|
self SetVehVelocity( self.velocity + side_dir );
|
|
self thread helicopter_crash_accel();
|
|
if( isdefined( point ) )
|
|
self thread helicopter_crash_rotation( point, dir );
|
|
else
|
|
self thread helicopter_crash_rotation( self.origin, dir );
|
|
}
|
|
|
|
//self thread helicopter_collision();
|
|
self thread crash_collision_test();
|
|
|
|
wait 15;
|
|
|
|
// failsafe notify
|
|
if ( IsDefined( self ) )
|
|
{
|
|
self notify( "crash_done" );
|
|
}
|
|
}
|
|
|
|
function helicopter_crash_accel()
|
|
{
|
|
self endon( "crash_done" );
|
|
self endon( "crash_move_done" );
|
|
self endon( "death" );
|
|
|
|
if ( !isdefined( self.crash_accel ) )
|
|
{
|
|
self.crash_accel = RandomFloatRange( 50, 80 );
|
|
}
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
self SetVehVelocity( self.velocity + AnglesToUp( self.angles ) * self.crash_accel );
|
|
wait 0.1;
|
|
}
|
|
}
|
|
|
|
function helicopter_crash_rotation( point, dir )
|
|
{
|
|
self endon( "crash_done" );
|
|
self endon( "crash_move_done" );
|
|
self endon( "death" );
|
|
|
|
start_angles = self.angles;
|
|
start_angles = (start_angles[0] + 10, start_angles[1], start_angles[2]); //RandomIntRange( -5, 5 ) );
|
|
start_angles = (start_angles[0], start_angles[1], start_angles[2] + 10); //RandomIntRange( -5, 5 ) );
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel = ( 0, ang_vel[1] * RandomFloatRange( 2, 3 ), 0 );
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
point_2d = ( point[0], point[1], self.origin[2] );
|
|
|
|
//DebugStar( self.origin, 1000, ( 1, 1, 1 ) );
|
|
//DebugStar( point_2d, 1000, ( 1, 0, 0 ) );
|
|
|
|
torque = ( 0, RandomIntRange( 90, 180 ), 0 );
|
|
if ( self GetAngularVelocity()[1] < 0 )
|
|
{
|
|
torque *= -1;
|
|
}
|
|
|
|
if ( Distance( self.origin, point_2d ) > 5 )
|
|
{
|
|
local_hit_point = point_2d - self.origin;
|
|
|
|
dir_2d = ( dir[0], dir[1], 0 );
|
|
if ( Length( dir_2d ) > 0.01 )
|
|
{
|
|
dir_2d = VectorNormalize( dir_2D );
|
|
torque = VectorCross( VectorNormalize( local_hit_point ), dir );
|
|
torque = ( 0, 0, torque[2] );
|
|
torque = VectorNormalize( torque );
|
|
torque = ( 0, torque[2] * 180, 0 );
|
|
}
|
|
}
|
|
|
|
while ( 1 )
|
|
{
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel += torque * 0.05;
|
|
|
|
const max_angluar_vel = 360;
|
|
if ( ang_vel[1] < max_angluar_vel * -1 )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
|
|
}
|
|
else if ( ang_vel[1] > max_angluar_vel )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
|
|
}
|
|
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function helicopter_crash_zone_accel( dir )
|
|
{
|
|
self endon( "crash_done" );
|
|
self endon( "crash_move_done" );
|
|
|
|
torque = ( 0, RandomIntRange( 90, 150 ), 0 );
|
|
ang_vel = self GetAngularVelocity();
|
|
//ang_vel = ( 0, ang_vel[1], 0 ); // get rid of the pitch and roll
|
|
//self SetAngularVelocity( ang_vel );
|
|
torque *= math::sign( ang_vel[1] );
|
|
|
|
/#
|
|
if ( isdefined( self.crash_zone.height ) )
|
|
{
|
|
self.crash_zone.height = 0;
|
|
}
|
|
#/
|
|
|
|
// if you don't have any roll give it a little
|
|
if ( Abs( self.angles[2] ) < 3.0 )
|
|
{
|
|
self.angles = ( self.angles[0], self.angles[1], RandomIntRange( 3, 6 ) * math::sign( self.angles[2] ) );
|
|
}
|
|
|
|
is_vtol = IsSubStr( self.vehicletype, "v78" );
|
|
|
|
if ( is_vtol )
|
|
{
|
|
torque *= 0.3;
|
|
}
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
assert( isdefined( self.crash_zone ) );
|
|
|
|
dist = Distance2D( self.origin, self.crash_zone.origin );
|
|
if ( dist < self.crash_zone.radius )
|
|
{
|
|
//if ( isdefined( self.crash_zone.angles ) )
|
|
// self.crash_vel = Length( self.velocity ) * AnglesToForward( self.crash_zone.angles );
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -400 ) );
|
|
/#Circle( self.crash_zone.origin + ( 0, 0, self.crash_zone.height ), self.crash_zone.radius, ( 0, 1, 0 ), false, 2000 );#/
|
|
self.crash_accel = 0;
|
|
}
|
|
else
|
|
{
|
|
self SetPhysAcceleration( ( 0, 0, -50 ) );
|
|
/#Circle( self.crash_zone.origin + ( 0, 0, self.crash_zone.height ), self.crash_zone.radius, ( 1, 0, 0 ), false, 2 );#/
|
|
}
|
|
|
|
self.crash_vel = self.crash_zone.origin - self.origin;
|
|
self.crash_vel = ( self.crash_vel[0], self.crash_vel[1], 0 );
|
|
self.crash_vel = VectorNormalize( self.crash_vel );
|
|
self.crash_vel *= self GetMaxSpeed() * 0.5;
|
|
|
|
if ( is_vtol )
|
|
{
|
|
self.crash_vel *= 0.5;
|
|
}
|
|
|
|
crash_vel_forward = AnglesToUp( self.angles ) * self GetMaxSpeed() * 2;
|
|
crash_vel_forward = ( crash_vel_forward[0], crash_vel_forward[1], 0 );
|
|
self.crash_vel += crash_vel_forward;
|
|
|
|
vel_x = DiffTrack( self.crash_vel[0], self.velocity[0], 1, 0.1 );
|
|
vel_y = DiffTrack( self.crash_vel[1], self.velocity[1], 1, 0.1 );
|
|
vel_z = DiffTrack( self.crash_vel[2], self.velocity[2], 1, 0.1 );
|
|
|
|
self SetVehVelocity( ( vel_x, vel_y, vel_z ) );
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel = ( 0, ang_vel[1], 0 );
|
|
ang_vel += torque * 0.1;
|
|
|
|
max_angluar_vel = 200;
|
|
if ( is_vtol )
|
|
{
|
|
max_angluar_vel = 100;
|
|
}
|
|
|
|
if ( ang_vel[1] < max_angluar_vel * -1 )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
|
|
}
|
|
else if ( ang_vel[1] > max_angluar_vel )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
|
|
}
|
|
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
wait( 0.1 );
|
|
}
|
|
}
|
|
|
|
function helicopter_collision()
|
|
{
|
|
self endon( "crash_done" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "veh_collision", velocity, normal );
|
|
|
|
ang_vel = self GetAngularVelocity() * 0.5;
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
// bounce off walls
|
|
if ( normal[2] < 0.7 )
|
|
{
|
|
self SetVehVelocity( self.velocity + normal * 70 );
|
|
}
|
|
else
|
|
{
|
|
//self.crash_accel *= 0.5;
|
|
//self SetVehVelocity( self.velocity * 0.8 );
|
|
//TODO T7 - function port
|
|
//CreateDynEntAndLaunch( self.deathmodel, self.origin, self.angles, self.origin, self.velocity * 0.03, self.deathfx );
|
|
|
|
self notify( "crash_done" );
|
|
}
|
|
}
|
|
}
|
|
|
|
function play_crashing_loop()
|
|
{
|
|
ent = Spawn ( "script_origin", self.origin );
|
|
ent linkto ( self );
|
|
ent playloopsound ( "exp_heli_crash_loop" );
|
|
self util::waittill_any ( "death", "snd_impact" );
|
|
ent delete();
|
|
|
|
}
|
|
function helicopter_explode( delete_me )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self vehicle::do_death_fx();
|
|
|
|
if ( isdefined( delete_me ) && delete_me == true )
|
|
{
|
|
self Delete();
|
|
}
|
|
|
|
self thread set_death_model( self.deathmodel, self.modelswapdelay );
|
|
}
|
|
|
|
function aircraft_crash_move( point, dir )
|
|
{
|
|
self endon( "crash_move_done" );
|
|
self endon( "death" );
|
|
|
|
self thread crash_collision_test();
|
|
self ClearVehGoalPos();
|
|
self CancelAIMove();
|
|
self SetRotorSpeed( 0.2 );
|
|
|
|
// swap to deathmodel here
|
|
if ( isdefined( self ) && isdefined( self.vehicletype ) )
|
|
{
|
|
b_custom_deathmodel_setup = true;
|
|
switch ( self.vehicletype )
|
|
{
|
|
|
|
default:
|
|
b_custom_deathmodel_setup = false;
|
|
break;
|
|
}
|
|
|
|
if ( b_custom_deathmodel_setup )
|
|
{
|
|
self.deathmodel_attached = true; // this will not add another deathmodel
|
|
}
|
|
}
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel = ( 0, 0, 0 );
|
|
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
nodes = self GetVehicleAvoidanceNodes( 10000 );
|
|
closest_index = -1;
|
|
best_dist = 999999;
|
|
if ( nodes.size > 0 )
|
|
{
|
|
for ( i = 0; i < nodes.size; i++ )
|
|
{
|
|
dir = VectorNormalize( nodes[i] - self.origin );
|
|
forward = AnglesToForward( self.angles );
|
|
dot = VectorDot( dir, forward );
|
|
if ( dot < 0.0 )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
dist = Distance2D( self.origin, nodes[i] );
|
|
if ( dist < best_dist )
|
|
{
|
|
best_dist = dist;
|
|
closest_index = i;
|
|
}
|
|
}
|
|
|
|
if ( closest_index >= 0 )
|
|
{
|
|
o = nodes[closest_index];
|
|
o = ( o[0], o[1], self.origin[2] );
|
|
|
|
//Line( self.origin, o, ( 1, 1, 1 ), false, 10000 );
|
|
//Circle( o, 2000, ( 1, 1, 0 ), true, 10000 );
|
|
|
|
dir = VectorNormalize( o - self.origin );
|
|
|
|
self SetVehVelocity( self.velocity + dir * 2000 );
|
|
}
|
|
else
|
|
{
|
|
self SetVehVelocity( self.velocity + AnglesToRight( self.angles ) * RandomIntRange( -1000, 1000 ) + ( 0, 0, RandomIntRange( 0, 1500 ) ) );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self SetVehVelocity( self.velocity + AnglesToRight( self.angles ) * RandomIntRange( -1000, 1000 ) + ( 0, 0, RandomIntRange( 0, 1500 ) ) );
|
|
}
|
|
|
|
//self SetVehVelocity( self.velocity + AnglesToRight( self.angles ) * RandomIntRange( -1000, 1000 ) + ( 0, 0, RandomIntRange( 0, 1500 ) ) );
|
|
self thread delay_set_gravity( RandomFloatRange( 1.5, 3 ) );
|
|
|
|
torque = ( 0, RandomIntRange( -90, 90 ), RandomIntRange( 90, 720 ) );
|
|
if ( RandomInt( 100 ) < 50 )
|
|
{
|
|
torque = ( torque[0], torque[1], -torque[2] );
|
|
}
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel += torque * 0.05;
|
|
|
|
const max_angluar_vel = 500;
|
|
if ( ang_vel[2] < max_angluar_vel * -1 )
|
|
{
|
|
ang_vel = ( ang_vel[0], ang_vel[1], max_angluar_vel * -1 );
|
|
}
|
|
else if ( ang_vel[2] > max_angluar_vel )
|
|
{
|
|
ang_vel = ( ang_vel[0], ang_vel[1], max_angluar_vel );
|
|
}
|
|
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function delay_set_gravity( delay )
|
|
{
|
|
self endon( "crash_move_done" );
|
|
self endon( "death" );
|
|
|
|
wait( delay );
|
|
|
|
self SetPhysAcceleration( ( RandomIntRange( -1600, 1600 ), RandomIntRange( -1600, 1600 ), -1600 ) );
|
|
}
|
|
|
|
function helicopter_crash_move( point, dir )
|
|
{
|
|
self endon( "crash_move_done" );
|
|
self endon( "death" );
|
|
|
|
self thread crash_collision_test();
|
|
self CancelAIMove();
|
|
self ClearVehGoalPos();
|
|
self SetTurningAbility( 0 );
|
|
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -800 ) );
|
|
|
|
vel = self.velocity;
|
|
dir = VectorNormalize( dir );
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel = ( 0, ang_vel[1] * RandomFloatRange( 1, 3 ), 0 );
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
// if ( Length( vel ) < 10 )
|
|
// {
|
|
// random_yaw = RandomIntRange( -180, 180 );
|
|
// launch_dir = AnglesToForward( ( 0, random_yaw, 0 ) );
|
|
// vel = launch_dir * RandomIntRange( 500, 1000 );
|
|
// }
|
|
// else
|
|
// {
|
|
// vel += dir * 1000.0;
|
|
// }
|
|
//
|
|
// self SetVehVelocity( vel );
|
|
|
|
point_2d = ( point[0], point[1], self.origin[2] );
|
|
|
|
//DebugStar( self.origin, 1000, ( 1, 1, 1 ) );
|
|
//DebugStar( point_2d, 1000, ( 1, 0, 0 ) );
|
|
|
|
torque = ( 0, 720, 0 );
|
|
if ( Distance( self.origin, point_2d ) > 5 )
|
|
{
|
|
local_hit_point = point_2d - self.origin;
|
|
|
|
dir_2d = ( dir[0], dir[1], 0 );
|
|
if ( Length( dir_2d ) > 0.01 )
|
|
{
|
|
dir_2d = VectorNormalize( dir_2D );
|
|
torque = VectorCross( VectorNormalize( local_hit_point ), dir );
|
|
torque = ( 0, 0, torque[2] );
|
|
torque = VectorNormalize( torque );
|
|
torque = ( 0, torque[2] * 180, 0 );
|
|
}
|
|
}
|
|
|
|
while ( 1 )
|
|
{
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel += torque * 0.05;
|
|
|
|
const max_angluar_vel = 360;
|
|
if ( ang_vel[1] < max_angluar_vel * -1 )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
|
|
}
|
|
else if ( ang_vel[1] > max_angluar_vel )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
|
|
}
|
|
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
|
|
function boat_crash( point, dir )
|
|
{
|
|
self.crashing = true;
|
|
// self thread play_crashing_loop();
|
|
|
|
if ( isdefined( self.unloading ) )
|
|
{
|
|
while ( isdefined( self.unloading ) )
|
|
{
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self thread boat_crash_movement( point, dir );
|
|
}
|
|
|
|
function boat_crash_movement( point, dir )
|
|
{
|
|
self endon( "crash_move_done" );
|
|
self endon( "death" );
|
|
|
|
// self thread crash_collision_test();
|
|
self CancelAIMove();
|
|
self ClearVehGoalPos();
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -50 ) );
|
|
|
|
vel = self.velocity;
|
|
dir = VectorNormalize( dir );
|
|
/// self SetVehVelocity( ( 0, 0, 0 ) );
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel = ( 0, 0, 0 );
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
torque = ( RandomIntRange( -5, -3 ), 0, ( RandomIntRange( 0, 100 ) < 50 ? -5 : 5 ) );
|
|
|
|
self thread boat_crash_monitor( point, dir, 4 );
|
|
|
|
while ( 1 )
|
|
{
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel += torque * 0.05;
|
|
|
|
const max_angluar_vel = 360;
|
|
if ( ang_vel[1] < max_angluar_vel * -1 )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel * -1, ang_vel[2] );
|
|
}
|
|
else if ( ang_vel[1] > max_angluar_vel )
|
|
{
|
|
ang_vel = ( ang_vel[0], max_angluar_vel, ang_vel[2] );
|
|
}
|
|
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
velocity = self.velocity;
|
|
velocity = (velocity[0] * 0.975, velocity[1], velocity[2]);
|
|
velocity = (velocity[0], velocity[1] * 0.975, velocity[2]);
|
|
self SetVehVelocity( velocity );
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function boat_crash_monitor( point, dir, crash_time )
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait( crash_time );
|
|
|
|
self notify( "crash_move_done" );
|
|
self crash_stop();
|
|
self notify( "crash_done" );
|
|
}
|
|
|
|
function crash_stop()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self SetPhysAcceleration( ( 0, 0, 0 ) );
|
|
self SetRotorSpeed( 0 );
|
|
|
|
speed = self GetSpeedMPH();
|
|
while ( speed > 2 )
|
|
{
|
|
velocity = self.velocity;
|
|
velocity *= 0.9;
|
|
self SetVehVelocity( velocity );
|
|
|
|
angular_velocity = self GetAngularVelocity();
|
|
angular_velocity *= 0.9;
|
|
self SetAngularVelocity( angular_velocity );
|
|
|
|
speed = self GetSpeedMPH();
|
|
|
|
{wait(.05);};
|
|
}
|
|
|
|
self SetVehVelocity( ( 0, 0, 0 ) );
|
|
self SetAngularVelocity( ( 0, 0, 0 ) );
|
|
|
|
self vehicle::toggle_tread_fx( false );
|
|
self vehicle::toggle_exhaust_fx( false );
|
|
self vehicle::toggle_sounds( false );
|
|
}
|
|
|
|
function crash_collision_test()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "veh_collision", velocity, normal );
|
|
|
|
self helicopter_explode();
|
|
self notify( "crash_move_done" );
|
|
|
|
if ( normal[2] > 0.7 )
|
|
{
|
|
forward = AnglesToForward( self.angles );
|
|
right = VectorCross( normal, forward );
|
|
desired_forward = VectorCross( right, normal );
|
|
self SetPhysAngles( VectorToAngles( desired_forward ) );
|
|
|
|
self crash_stop();
|
|
self notify( "crash_done" );
|
|
}
|
|
else
|
|
{
|
|
{wait(.05);};
|
|
self Delete();
|
|
}
|
|
}
|
|
|
|
function crash_path_check( node )
|
|
{
|
|
// find a crashnode on the current path
|
|
// this only works on ground info_vehicle_node vheicles. not dynamic helicopter script_origin paths. they have their own dynamic crashing.
|
|
targ = node;
|
|
search_depth = 5;
|
|
|
|
while ( isdefined( targ ) && search_depth >= 0 )
|
|
{
|
|
if ( ( isdefined( targ.detoured ) ) && ( targ.detoured == 0 ) )
|
|
{
|
|
detourpath = vehicle::path_detour_get_detourpath( getvehiclenode( targ.target, "targetname" ) );
|
|
if ( isdefined( detourpath ) && isdefined( detourpath.script_crashtype ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
if ( isdefined( targ.target ) )
|
|
{
|
|
// Edited for the case of nodes targetting eachother for looping paths 1/30/08 TFlame
|
|
targ1 = getvehiclenode( targ.target, "targetname" );
|
|
if ( isdefined( targ1 ) && isdefined( targ1.target ) && isdefined( targ.targetname ) && targ1.target == targ.targetname )
|
|
{
|
|
return false;
|
|
}
|
|
else if ( isdefined( targ1 ) && targ1 == node ) // circular case -AP 1/15/09
|
|
{
|
|
return false;
|
|
}
|
|
else
|
|
{
|
|
targ = targ1;
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
targ = undefined;
|
|
}
|
|
|
|
search_depth--;
|
|
}
|
|
return false;
|
|
|
|
}
|
|
|
|
function death_firesound( sound )
|
|
{
|
|
self thread sound::loop_on_tag( sound, undefined, false );
|
|
self util::waittill_any( "fire_extinguish", "stop_crash_loop_sound" );
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
self notify( "stop sound" + sound );
|
|
}
|
|
|
|
function death_fx()
|
|
{
|
|
// going to use vehicletypes for identifying a vehicles association with effects.
|
|
// will add new vehicle types if vehicle is different enough that it needs to use
|
|
// different effect. also handles the sound
|
|
if ( self vehicle::is_destructible() )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self util::explode_notify_wrapper();
|
|
|
|
if(isdefined(self.do_death_fx))
|
|
{
|
|
self [[self.do_death_fx]]();
|
|
}
|
|
else
|
|
{
|
|
self vehicle::do_death_fx();
|
|
}
|
|
}
|
|
|
|
|
|
function death_make_badplace( type )
|
|
{
|
|
if ( !isdefined( level.vehicle_death_badplace[ type ] ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
struct = level.vehicle_death_badplace[ type ];
|
|
if ( isdefined( struct.delay ) )
|
|
{
|
|
wait struct.delay;
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
badplace_box( "vehicle_kill_badplace", struct.duration, self.origin, struct.radius, "all" );
|
|
}
|
|
|
|
function death_jolt( type )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( ( isdefined( self.ignore_death_jolt ) && self.ignore_death_jolt ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self JoltBody( ( self.origin + ( 23, 33, 64 ) ), 3 );
|
|
|
|
if ( isdefined( self.death_anim ) )
|
|
{
|
|
self AnimScripted( "death_anim", self.origin, self.angles, self.death_anim, "normal", %root, 1, 0 );
|
|
self waittillmatch( "death_anim", "end" );
|
|
}
|
|
else // if ( !isdefined( self.destructibledef ) )
|
|
{
|
|
if ( self.isphysicsvehicle )
|
|
{
|
|
num_launch_multiplier = 1;
|
|
|
|
if ( isdefined( self.physicslaunchdeathscale ) )
|
|
{
|
|
num_launch_multiplier = self.physicslaunchdeathscale;
|
|
}
|
|
|
|
self LaunchVehicle( (0, 0, 180) * num_launch_multiplier, (RandomFloatRange(5, 10), RandomFloatRange(-5, 5), 0), true, false, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
function deathrollon()
|
|
{
|
|
if ( self.health > 0 )
|
|
{
|
|
self.rollingdeath = 1;
|
|
}
|
|
}
|
|
|
|
function deathrolloff()
|
|
{
|
|
self.rollingdeath = undefined;
|
|
self notify( "deathrolloff" );
|
|
}
|
|
|
|
function loop_fx_on_vehicle_tag( effect, loopTime, tag )
|
|
{
|
|
assert( isdefined( effect ) );
|
|
assert( isdefined( tag ) );
|
|
assert( isdefined( loopTime ) );
|
|
|
|
self endon( "stop_looping_death_fx" );
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
playfxontag( effect, deathfx_ent(), tag );
|
|
wait loopTime;
|
|
}
|
|
}
|
|
|
|
function deathfx_ent()
|
|
{
|
|
if ( !isdefined( self.deathfx_ent ) )
|
|
{
|
|
ent = Spawn( "script_model", ( 0, 0, 0 ) );
|
|
emodel = vehicle::get_dummy();
|
|
ent setmodel( self.model );
|
|
ent.origin = emodel.origin;
|
|
ent.angles = emodel.angles;
|
|
ent notsolid();
|
|
ent hide();
|
|
ent linkto( emodel );
|
|
self.deathfx_ent = ent;
|
|
}
|
|
else
|
|
{
|
|
self.deathfx_ent setmodel( self.model );
|
|
}
|
|
return self.deathfx_ent;
|
|
}
|
|
|
|
function death_cleanup_level_variables()
|
|
{
|
|
script_linkname = self.script_linkname;
|
|
targetname = self.targetname;
|
|
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
|
|
if ( isdefined( script_linkname ) )
|
|
{
|
|
ArrayRemoveValue( level.vehicle_link[ script_linkname ], self );
|
|
}
|
|
|
|
if ( isdefined( self.script_VehicleSpawngroup ) )
|
|
{
|
|
if ( isdefined( level.vehicle_SpawnGroup[ self.script_VehicleSpawngroup ] ) )
|
|
{
|
|
ArrayRemoveValue( level.vehicle_SpawnGroup[ self.script_VehicleSpawngroup ], self );
|
|
ArrayRemoveValue( level.vehicle_SpawnGroup[ self.script_VehicleSpawngroup ], undefined );
|
|
}
|
|
}
|
|
|
|
if ( isdefined( self.script_VehicleStartMove ) )
|
|
{
|
|
ArrayRemoveValue( level.vehicle_StartMoveGroup[ self.script_VehicleStartMove ], self );
|
|
}
|
|
|
|
if ( isdefined( self.script_vehicleGroupDelete ) )
|
|
{
|
|
ArrayRemoveValue( level.vehicle_DeleteGroup[ self.script_vehicleGroupDelete ], self );
|
|
}
|
|
}
|
|
|
|
function death_cleanup_riders()
|
|
{
|
|
// if vehicle is gone then delete the ai here.
|
|
if ( isdefined( self.riders ) )
|
|
{
|
|
for ( j = 0; j < self.riders.size; j++ )
|
|
{
|
|
if ( isdefined( self.riders[ j ] ) )
|
|
{
|
|
self.riders[ j ] delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( vehicle::is_corpse( self ) )
|
|
{
|
|
self.riders = [];
|
|
}
|
|
}
|
|
|
|
function death_radius_damage( meansOfDamage = "MOD_EXPLOSIVE" )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( !isdefined( self ) || self.abandoned === true || self.damage_on_death === false || self.radiusdamageradius <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
position = self.origin + ( 0,0,15 );
|
|
radius = self.radiusdamageradius;
|
|
damageMax = self.radiusdamagemax;
|
|
damageMin = self.radiusdamagemin;
|
|
attacker = self;
|
|
|
|
{wait(.05);};
|
|
|
|
if ( isdefined( self ) )
|
|
{
|
|
self RadiusDamage( position, radius, damageMax, damageMin, attacker, meansOfDamage );
|
|
}
|
|
}
|
|
|
|
|
|
function death_update_crash( point, dir )
|
|
{
|
|
if ( !isdefined( self.destructibledef ) )
|
|
{
|
|
if ( isdefined( self.script_crashtypeoverride ) )
|
|
{
|
|
crashtype = self.script_crashtypeoverride;
|
|
}
|
|
else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) )
|
|
{
|
|
crashtype = "aircraft";
|
|
}
|
|
else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
|
|
{
|
|
crashtype = "helicopter";
|
|
}
|
|
else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "boat" ) ) )
|
|
{
|
|
crashtype = "boat";
|
|
}
|
|
else if ( isdefined( self.currentnode ) && crash_path_check( self.currentnode ) )
|
|
{
|
|
crashtype = "none";
|
|
}
|
|
else
|
|
{
|
|
crashtype = "tank"; // tanks used to be the only vehicle that would stop. legacy nonsense from CoD1
|
|
}
|
|
|
|
if ( crashtype == "aircraft" )
|
|
{
|
|
self thread aircraft_crash( point, dir );
|
|
}
|
|
else if ( crashtype == "helicopter" )
|
|
{
|
|
if ( isdefined( self.script_nocorpse ) )
|
|
{
|
|
// GLocke - Does not drop a physics script model on death
|
|
self thread helicopter_explode();
|
|
}
|
|
else
|
|
{
|
|
self thread helicopter_crash( point, dir );
|
|
}
|
|
}
|
|
else if ( crashtype == "boat" )
|
|
{
|
|
self thread boat_crash( point, dir );
|
|
}
|
|
else if ( crashtype == "tank" )
|
|
{
|
|
if ( !isdefined( self.rollingdeath ) )
|
|
{
|
|
self vehicle::set_speed( 0, 25, "Dead" );
|
|
}
|
|
else
|
|
{
|
|
// dpg 3/12/08 removed this. if you need it for something, let someone know
|
|
//self vehicle::set_speed( 8, 25, "Dead rolling out of path intersection" );
|
|
self waittill( "deathrolloff" );
|
|
self vehicle::set_speed( 0, 25, "Dead, finished path intersection" );
|
|
}
|
|
|
|
wait .4;
|
|
|
|
if ( isdefined( self ) && !vehicle::is_corpse( self ) )
|
|
{
|
|
self vehicle::set_speed( 0, 10000, "deadstop" );
|
|
|
|
self notify( "deadstop" );
|
|
if ( self.disconnectPathOnStop === true )
|
|
{
|
|
self vehicle::disconnect_paths();
|
|
}
|
|
|
|
if ( ( isdefined( self.tankgetout ) ) && ( self.tankgetout > 0 ) )
|
|
{
|
|
// tankgetout will never get notified if there are no guys getting out
|
|
self waittill( "animsdone" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function waittill_crash_done_or_stopped()
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( isdefined( self ) && ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) || ( isdefined(self.vehicleclass) && (self.vehicleclass == "boat" ) ) ) )
|
|
{
|
|
if ( ( isdefined( self.crashing ) ) && ( self.crashing == true ) )
|
|
{
|
|
self waittill( "crash_done" );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
wait 0.2; // don't check the velocity right away because we might have been launched
|
|
|
|
if ( self.isphysicsvehicle )
|
|
{
|
|
self ClearVehGoalPos();
|
|
self CancelAIMove();
|
|
//GLocke 2/16/10 - just wait for physics vehicles to get close to 0
|
|
stable_count = 0;
|
|
while( stable_count < 3 )
|
|
{
|
|
if ( isdefined( self.velocity ) && LengthSquared( self.velocity ) > 1.0 )
|
|
{
|
|
stable_count = 0;
|
|
}
|
|
else
|
|
{
|
|
stable_count++;
|
|
}
|
|
wait( 0.3 );
|
|
}
|
|
self vehicle::disconnect_paths();
|
|
}
|
|
else
|
|
{
|
|
while ( isdefined( self ) && self GetSpeedMPH() > 0 )
|
|
{
|
|
wait( 0.3 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
function vehicle_damage_filter_damage_watcher( driver, heavy_damage_threshold )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "exit_vehicle" );
|
|
self endon( "end_damage_filter" );
|
|
|
|
if ( !isdefined( heavy_damage_threshold ) )
|
|
{
|
|
heavy_damage_threshold = 100;
|
|
}
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction, point, type, tagName, modelName, partname, weapon );
|
|
|
|
//IPrintLn( "damage: " + damage );
|
|
//self ClearDamageIndicator();
|
|
Earthquake( 0.25, 0.15, self.origin, 512, self );
|
|
driver PlayRumbleOnEntity( "damage_light" );
|
|
|
|
time = GetTime();
|
|
if ( ( time - level.n_last_damage_time ) > 500 )
|
|
{
|
|
level.n_hud_damage = true;
|
|
|
|
if ( damage > heavy_damage_threshold )
|
|
{
|
|
//rpc( "scripts/_vehicle", "damage_filter_heavy" );
|
|
driver playsound ( "veh_damage_filter_heavy" );
|
|
}
|
|
else
|
|
{
|
|
//rpc( "scripts/_vehicle", "damage_filter_light" );
|
|
driver playsound ( "veh_damage_filter_light" );
|
|
}
|
|
|
|
level.n_last_damage_time = GetTime();
|
|
}
|
|
}
|
|
}
|
|
|
|
function vehicle_damage_filter_exit_watcher( driver )
|
|
{
|
|
self util::waittill_any( "exit_vehicle", "death", "end_damage_filter" );
|
|
|
|
//rpc( "scripts/_vehicle", "damage_filter_disable" );
|
|
//rpc( "scripts/_vehicle", "damage_filter_off" );
|
|
|
|
//self.damage_filter_init = undefined;
|
|
|
|
//if ( isdefined( driver.save_visionset ) )
|
|
//{
|
|
// //TODO T7 - convert to use manager
|
|
// driver VisionSetNaked( driver.save_visionset, 0 );
|
|
// #endif
|
|
//}
|
|
}
|
|
|
|
function vehicle_damage_filter( vision_set, heavy_damage_threshold, filterid = 0, b_use_player_damage = false )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "exit_vehicle" );
|
|
self endon( "end_damage_filter" );
|
|
|
|
driver = self GetSeatOccupant( 0 );
|
|
|
|
if ( !isdefined( self.damage_filter_init ) )
|
|
{
|
|
//rpc( "scripts/_vehicle", "init_damage_filter", filterid );
|
|
self.damage_filter_init = true;
|
|
}
|
|
else
|
|
{
|
|
//rpc( "scripts/_vehicle", "damage_filter_enable", 0, filterid );
|
|
}
|
|
|
|
if ( isdefined( vision_set ) )
|
|
{
|
|
//TODO T7 - convert to use manager
|
|
/*level.player.save_visionset = level.player GetVisionSetNaked();
|
|
level.player VisionSetNaked( vision_set, 0.5 );*/
|
|
}
|
|
|
|
level.n_hud_damage = false;
|
|
level.n_last_damage_time = GetTime();
|
|
|
|
damagee = ( ( isdefined( b_use_player_damage ) && b_use_player_damage ) ? driver : self );
|
|
|
|
damagee thread vehicle_damage_filter_damage_watcher( driver, heavy_damage_threshold );
|
|
damagee thread vehicle_damage_filter_exit_watcher( driver );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( ( isdefined( level.n_hud_damage ) && level.n_hud_damage ) )
|
|
{
|
|
time = GetTime();
|
|
if ( ( time - level.n_last_damage_time ) > 500 )
|
|
{
|
|
//rpc( "scripts/_vehicle", "damage_filter_off" );
|
|
level.n_hud_damage = false;
|
|
}
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
// self == vehicle
|
|
function flipping_shooting_death( attacker, hitDir )
|
|
{
|
|
// do we need this?
|
|
if( isdefined( self.delete_on_death ) )
|
|
{
|
|
if ( isdefined( self ) )
|
|
{
|
|
self delete();
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self endon( "death" ); //quit thread if deleted
|
|
|
|
self vehicle_death::death_cleanup_level_variables();
|
|
|
|
self DisableAimAssist();
|
|
|
|
self vehicle_death::death_fx();
|
|
self thread vehicle_death::death_radius_damage();
|
|
self thread vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
|
|
|
|
self vehicle::toggle_tread_fx( false );
|
|
self vehicle::toggle_exhaust_fx( false );
|
|
self vehicle::toggle_sounds( false );
|
|
self vehicle::lights_off();
|
|
self thread flipping_shooting_crash_movement( attacker, hitDir );
|
|
|
|
self waittill( "crash_done" );
|
|
|
|
while( ( isdefined( self.controlled ) && self.controlled ) )
|
|
{wait(.05);};
|
|
|
|
// A dynEnt will be spawned in the collision thread when it hits the ground and "crash_done" notify will be sent
|
|
self delete();
|
|
}
|
|
|
|
function plane_crash()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -1000 ) );
|
|
self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify
|
|
|
|
forward = AnglesToForward( self.angles );
|
|
forward_mag = RandomFloatRange( 0, 300 );
|
|
forward_mag += math::sign( forward_mag ) * 400;
|
|
forward *= forward_mag;
|
|
|
|
new_vel = forward + (self.velocity * 0.2);
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
|
|
yaw_vel = RandomFloatRange( 0, 130 ) * math::sign( ang_vel[1] );
|
|
yaw_vel += math::sign( yaw_vel ) * 20;
|
|
|
|
ang_vel = ( RandomFloatRange( -1, 1 ), yaw_vel, 0 );
|
|
|
|
// setup the roll to look correct
|
|
roll_amount = ( abs( ang_vel[1] ) / 150.0 ) * 30.0;
|
|
if( ang_vel[1] > 0 )
|
|
{
|
|
roll_amount = -roll_amount;
|
|
}
|
|
|
|
self.angles = ( self.angles[0], self.angles[1], roll_amount );
|
|
ang_vel = ( ang_vel[0], ang_vel[1], roll_amount * 0.9 );
|
|
|
|
// set how much of the forward velocity to rotate when the vehicle rotates, more like a plane
|
|
self.velocity_rotation_frac = 1.0;//RandomFloatRange( 0.95, 0.99 );
|
|
|
|
self.crash_accel = RandomFloatRange( 65, 90 );
|
|
|
|
set_movement_and_accel( new_vel, ang_vel );
|
|
}
|
|
|
|
function barrel_rolling_crash()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -1000 ) );
|
|
self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify
|
|
|
|
forward = AnglesToForward( self.angles );
|
|
forward_mag = RandomFloatRange( 0, 250 );
|
|
forward_mag += math::sign( forward_mag ) * 300;
|
|
forward *= forward_mag;
|
|
|
|
new_vel = forward + (0,0,70);
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
yaw_vel = RandomFloatRange( 0, 60 ) * math::sign( ang_vel[1] );
|
|
yaw_vel += math::sign( yaw_vel ) * 30;
|
|
|
|
roll_vel = RandomFloatRange( -200, 200 );
|
|
roll_vel += math::sign( roll_vel ) * 300;
|
|
ang_vel = ( RandomFloatRange( -5, 5 ), yaw_vel, roll_vel );
|
|
|
|
// set how much of the forward velocity to rotate when the vehicle rotates, more like a plane
|
|
self.velocity_rotation_frac = 1.0;//RandomFloatRange( 0.95, 0.99 );
|
|
|
|
self.crash_accel = RandomFloatRange( 145, 210 );
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -250 ) );
|
|
|
|
set_movement_and_accel( new_vel, ang_vel );
|
|
}
|
|
|
|
function random_crash( hitdir )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -1000 ) );
|
|
self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify
|
|
|
|
if( !isdefined( hitdir ) )
|
|
{
|
|
hitdir = (1,0,0);
|
|
}
|
|
hitdir = VectorNormalize( hitdir );
|
|
|
|
side_dir = VectorCross( hitdir, (0,0,1) );
|
|
side_dir_mag = RandomFloatRange( -280, 280 );
|
|
side_dir_mag += math::sign( side_dir_mag ) * 150;
|
|
side_dir *= side_dir_mag;
|
|
|
|
forward = AnglesToForward( self.angles );
|
|
forward_mag = RandomFloatRange( 0, 300 );
|
|
forward_mag += math::sign( forward_mag ) * 30;
|
|
forward *= forward_mag;
|
|
|
|
new_vel = (self.velocity * 1.2) + forward + side_dir + (0,0,50);
|
|
|
|
ang_vel = self GetAngularVelocity();
|
|
ang_vel = ( ang_vel[0] * 0.3, ang_vel[1], ang_vel[2] * 1.2 );
|
|
|
|
yaw_vel = RandomFloatRange( 0, 130 ) * math::sign( ang_vel[1] );
|
|
yaw_vel += math::sign( yaw_vel ) * 50;
|
|
|
|
ang_vel += ( RandomFloatRange( -5, 5 ), yaw_vel, RandomFloatRange( -18, 18 ) );
|
|
|
|
// set how much of the forward velocity to rotate when the vehicle rotates, more like a plane
|
|
self.velocity_rotation_frac = RandomFloatRange( 0.3, 0.99 );
|
|
|
|
self.crash_accel = RandomFloatRange( 65, 90 );
|
|
|
|
set_movement_and_accel( new_vel, ang_vel );
|
|
}
|
|
|
|
function set_movement_and_accel( new_vel, ang_vel )
|
|
{
|
|
self death_fx();
|
|
self thread death_radius_damage();
|
|
|
|
self SetVehVelocity( new_vel );
|
|
self SetAngularVelocity( ang_vel );
|
|
|
|
if( !isdefined( self.off ) )
|
|
{
|
|
self thread flipping_shooting_crash_accel();
|
|
}
|
|
self thread vehicle_ai::nudge_collision();
|
|
|
|
//drone death sounds JM - play 1 shot hit, turn off main loop, thread dmg loop
|
|
self playsound("veh_wasp_dmg_hit");
|
|
self vehicle::toggle_sounds( 0 );
|
|
|
|
if( !isdefined( self.off ) )
|
|
{
|
|
self thread flipping_shooting_dmg_snd();
|
|
}
|
|
|
|
wait 0.1;
|
|
|
|
if( RandomInt( 100 ) < 40 && !isdefined( self.off ) && self.variant !== "rocket" )
|
|
{
|
|
self thread vehicle_ai::fire_for_time( RandomFloatRange( 0.7, 2.0 ) );
|
|
}
|
|
|
|
result = self util::waittill_any_timeout( 15, "crash_done" );
|
|
|
|
if ( result === "crash_done" )
|
|
{
|
|
self vehicle::do_death_dynents();
|
|
self vehicle_death::set_death_model( self.deathmodel, self.modelswapdelay );
|
|
}
|
|
else
|
|
{
|
|
// failsafe notify
|
|
self notify( "crash_done" );
|
|
}
|
|
}
|
|
|
|
function flipping_shooting_crash_movement( attacker, hitdir )
|
|
{
|
|
self endon( "crash_done" );
|
|
self endon( "death" );
|
|
|
|
self CancelAIMove();
|
|
self ClearVehGoalPos();
|
|
self ClearLookAtEnt();
|
|
|
|
self SetPhysAcceleration( ( 0, 0, -1000 ) );
|
|
self.vehcheckforpredictedcrash = true; // code field to get veh_predictedcollision notify
|
|
|
|
if( !isdefined( hitdir ) )
|
|
{
|
|
hitdir = (1,0,0);
|
|
}
|
|
|
|
hitdir = VectorNormalize( hitdir );
|
|
|
|
new_vel = self.velocity;
|
|
|
|
self.crash_style = GetDvarInt( "debug_crash_type" );
|
|
if( self.crash_style == -1 )
|
|
{
|
|
self.crash_style = RandomInt( 3 );
|
|
}
|
|
|
|
switch( self.crash_style )
|
|
{
|
|
case 0: barrel_rolling_crash(); break;
|
|
case 1: plane_crash(); break;
|
|
default: random_crash( hitdir );
|
|
}
|
|
}
|
|
|
|
function flipping_shooting_dmg_snd()
|
|
{
|
|
dmg_ent = Spawn("script_origin", self.origin);
|
|
dmg_ent linkto (self);
|
|
dmg_ent PlayLoopSound ("veh_wasp_dmg_loop");
|
|
self util::waittill_any("crash_done", "death");
|
|
dmg_ent stoploopsound(1);
|
|
wait (2);
|
|
dmg_ent delete();
|
|
}
|
|
|
|
function flipping_shooting_crash_accel()
|
|
{
|
|
self endon( "crash_done" );
|
|
self endon( "death" );
|
|
|
|
count = 0;
|
|
|
|
prev_forward = AnglesToForward( self.angles );
|
|
prev_forward_vel = VectorDot( self.velocity, prev_forward ) * self.velocity_rotation_frac;
|
|
if( prev_forward_vel < 0 )
|
|
{
|
|
prev_forward_vel = 0;
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
self SetVehVelocity( self.velocity + AnglesToUp( self.angles ) * self.crash_accel );
|
|
self.crash_accel *= 0.98;
|
|
|
|
// Rotate part of the velocity
|
|
new_velocity = self.velocity;
|
|
new_velocity -= prev_forward * prev_forward_vel;
|
|
|
|
forward = AnglesToForward( self.angles );
|
|
new_velocity += forward * prev_forward_vel;// * 0.98;
|
|
|
|
prev_forward = forward;
|
|
prev_forward_vel = VectorDot( new_velocity, prev_forward ) * self.velocity_rotation_frac;
|
|
if( prev_forward_vel < 10 )
|
|
{
|
|
new_velocity += forward * 40;
|
|
prev_forward_vel = 0;
|
|
}
|
|
|
|
self SetVehVelocity( new_velocity );
|
|
|
|
wait 0.1;
|
|
|
|
count++;
|
|
if ( count % 8 == 0 && RandomInt( 100 ) > 40 )
|
|
{
|
|
if ( self.velocity[2] > 130.0 )
|
|
{
|
|
self.crash_accel *= 0.75;
|
|
}
|
|
else if ( self.velocity[2] < 40.0 && count < 60 )
|
|
{
|
|
if ( Abs( self.angles[0] ) > 35 || Abs( self.angles[2] ) > 35 ) // tilted
|
|
{
|
|
self.crash_accel = RandomFloatRange( 100, 150 );
|
|
}
|
|
else
|
|
{
|
|
self.crash_accel = RandomFloatRange( 45, 70 );
|
|
}
|
|
}
|
|
|
|
//ang_vel = self GetAngularVelocity();
|
|
//ang_vel += ( RandomFloatRange( -10, 10 ), RandomFloatRange( -35, 35 ), RandomFloatRange( -1, 1 ) );
|
|
//self SetAngularVelocity( ang_vel );
|
|
}
|
|
}
|
|
}
|
|
|
|
function death_fire_loop_audio()
|
|
{
|
|
sound_ent = Spawn( "script_origin", self.origin );
|
|
sound_ent PlayLoopSound( "veh_qrdrone_death_fire_loop" , .1 );
|
|
wait 11;
|
|
sound_ent StopLoopSound( 1 );
|
|
sound_ent delete();
|
|
}
|
|
|
|
function FreeWhenSafe( time = 4 )
|
|
{
|
|
self thread DelayedRemove_thread( time, false );
|
|
}
|
|
|
|
function DeleteWhenSafe( time = 4 )
|
|
{
|
|
self thread DelayedRemove_thread( time, true );
|
|
}
|
|
|
|
function DelayedRemove_thread( time, shouldDelete )
|
|
{
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self endon ( "death" );
|
|
self endon ( "free_vehicle" );
|
|
|
|
if ( shouldDelete === true )
|
|
{
|
|
self SetVehVelocity( ( 0, 0, 0 ) );
|
|
self Ghost();
|
|
self NotSolid();
|
|
}
|
|
|
|
util::waitForTimeAndNetworkFrame( time );
|
|
|
|
if ( shouldDelete === true )
|
|
{
|
|
self Delete();
|
|
}
|
|
else
|
|
{
|
|
self FreeVehicle();
|
|
}
|
|
}
|
|
|
|
function CleanUp()
|
|
{
|
|
if ( isdefined( self.cleanup_after_time ) )
|
|
{
|
|
wait self.cleanup_after_time;
|
|
if ( isdefined( self ) )
|
|
{
|
|
self Delete();
|
|
}
|
|
}
|
|
} |