cod5-sdk/raw/maps/mp/_vehicles.gsc
2008-11-20 00:00:00 +00:00

2035 lines
54 KiB
Text

#include common_scripts\utility;
#include maps\mp\_utility;
#include maps\mp\gametypes\_hud_util;
#using_animtree( "vehicles" );
init()
{
setdvar( "scr_veh_cleanupdebugprint", "0" );
// We can control whether vehicle occupants can take damage (such as from bullets)
setdvar( "scr_veh_driversarehidden", "1" );
setdvar( "scr_veh_driversareinvulnerable", "1" );
// "cleanuptime" must pass before veh is considered for cleanup (either by abandonment or drift)
// these are given in seconds
setdvar( "scr_veh_alive_cleanuptimemin", "119" );
setdvar( "scr_veh_alive_cleanuptimemax", "120" );
setdvar( "scr_veh_dead_cleanuptimemin", "20" );
setdvar( "scr_veh_dead_cleanuptimemax", "30" );
// These cleanuptime factors only modify cleanup due to abandonment or drift,
// i.e. the cleanup of an alive vehicle. Once the vehicle dies and becomes a
// husk, only the cleanuptimemin and cleanuptimemax values apply.
setdvar( "scr_veh_cleanuptime_dmgfactor_min", "0.33" );
setdvar( "scr_veh_cleanuptime_dmgfactor_max", "1.0" );
setdvar( "scr_veh_cleanuptime_dmgfactor_deadtread", "0.25" ); // decrease the damage factor ( which scales wait time ) by this much for each tread that has been completely destroyed
setdvar( "scr_veh_cleanuptime_dmgfraction_curve_begin", "0.0" ); // this is the amount of damage at which the damage factor affecting wait time will be at max
setdvar( "scr_veh_cleanuptime_dmgfraction_curve_end", "1.0" ); // this is the amount of damage at which the damage factor affecting wait time will be at min
setdvar( "scr_veh_cleanupabandoned", "1" ); // Decide whether to cleanup abandoned vehicles
setdvar( "scr_veh_cleanupdrifted", "1" ); // Decide whether to cleanup drifted vehicles
setdvar( "scr_veh_cleanupmaxspeedmph", "1" ); // If it's going slower than this, then the vehicle can be cleaned up
setdvar( "scr_veh_cleanupmindistancefeet", "75" ); // If it's at least this far from its original position, then the vehicle can be cleaned up
setdvar( "scr_veh_waittillstoppedandmindist_maxtime", "10" );
setdvar( "scr_veh_waittillstoppedandmindist_maxtimeenabledistfeet", "5" );
// Respawn time is the wait between cleaning up an old vehicle
// and spawning a new replacement vehicle.
// these are given in seconds
setdvar( "scr_veh_respawnafterhuskcleanup", "1" ); // if true, vehicle respawn kicks off after husk cleanup. otherwise, kicks off after live vehicle is killed
setdvar( "scr_veh_respawntimemin", "50" );
setdvar( "scr_veh_respawntimemax", "90" );
setdvar( "scr_veh_respawnwait_maxiterations", "30" );
setdvar( "scr_veh_respawnwait_iterationwaitseconds", "1" );
// Explodes when killed or when dvar scr_veh_explode_on_cleanup is not zero
setdvar( "scr_veh_explosion_spawnfx", "1" ); // Whether or not to create an explosion FX when the vehicle is destroyed
setdvar( "scr_veh_explosion_doradiusdamage", "1" ); // whether or not to hurt nearby entities when the vehicle is destroyed
setdvar( "scr_veh_explosion_radius", "256" );
setdvar( "scr_veh_explosion_mindamage", "20" );
setdvar( "scr_veh_explosion_maxdamage", "200" );
setdvar( "scr_veh_ondeath_createhusk", "1" ); // Whether or not to init a burnt-out husk for players to use as cover when the vehicle is destroyed
setdvar( "scr_veh_ondeath_usevehicleashusk", "1" ); // if true, husks will be the vehicle entities themselves. otherwise, husk will be a Spawn("script_model")
setdvar( "scr_veh_explosion_husk_forcepointvariance", "30" ); // controls the "spin" when the vehicle husk is launched
setdvar( "scr_veh_explosion_husk_horzvelocityvariance", "25" );
setdvar( "scr_veh_explosion_husk_vertvelocitymin", "100" );
setdvar( "scr_veh_explosion_husk_vertvelocitymax", "200" );
setdvar( "scr_veh_explode_on_cleanup", "1" ); ///< if not "exploding", then "disappearing". this only affect auto-cleanup case, not when vehicle is destroyed. destroyed vehicle always explodes
setdvar( "scr_veh_disappear_maxwaittime", "60" ); ///< # seconds at which test for "player too close" and "player can see" will timeout and the husk will be cleaned up anyway
setdvar( "scr_veh_disappear_maxpreventdistancefeet", "30" ); ///< If vehicle closer to the player than this, then it won't be cleaned up
setdvar( "scr_veh_disappear_maxpreventvisibilityfeet", "150" ); ///< Only apply visibility check if vehicle is closer to the player than this
setdvar( "scr_veh_health_tank", "1350" ); // default health
level.vehicle_drivers_are_invulnerable = getdvarint( "scr_veh_driversareinvulnerable" );
level.vehicleHealths[ "panzer4_mp" ] = 2600;
level.vehicleHealths[ "t34_mp" ] = 2600;
setdvar( "scr_veh_health_jeep", "700" );
if ( init_vehicle_entities() )
{
level.vehicle_explosion_effect= loadFX("explosions/large_vehicle_explosion"); // kaboom!
initialize_vehicle_damage_effects_for_level();
maps\mp\_panzeriv::main( "vehicle_ger_tracked_panzer4_mp" );
maps\mp\_sherman::main( "vehicle_usa_tracked_sherman_m4a3_mp" );
maps\mp\_t34::main( "vehicle/exhaust/fx_exhaust_t34" );
maps\mp\_type97::main( "vehicle/exhaust/fx_exhaust_t97" );
{
level.veh_husk_models = [];
if ( IsDefined( level.use_new_veh_husks ) )
{
level.veh_husk_models[ "t34_mp" ] = "veh_t34_destroyed_mp";
//level.veh_husk_models[ "panzer4_mp" ] = "veh_panzer4_destroyed_mp";
}
keys = GetArrayKeys( level.veh_husk_models );
for ( i = 0; i < keys.size; i++ )
{
precacheModel( level.veh_husk_models[ keys[ i ] ] );
}
}
precacheRumble( "tank_damage_light_mp" );
precacheRumble( "tank_damage_heavy_mp" );
level._effect["tanksquish"] = loadfx("maps/see2/fx_body_blood_splat");
}
return;
}
initialize_vehicle_damage_effects_for_level()
{
//###stefan $TODO it would be great if these came from a GDT somewhere instead of being hard-coded in scripts
/*
Vehicle damage effects are stored in level.vehicles_damage_states[]
where the array key is the GDT vehicle-type name for the particular vehicle.
For example, "sherman_mp" --> level.vehicles_damage_states["sherman_mp"]
The default MP vehicle is "defaultvehicle_mp". This is used for vehicles who do not have a custom entry in the vehicles_damage_states[] array.
Here we setup the vehicle damage effects response behaviors for all vehicles, using a data structure layout as follows:
struct s_vehicle_damage_state
{
float health_percentage; // apply effect(s) from effect_array[] when vehicle health is <= this amount
struct s_effect_array
{
id damage_effect; // this is the damage effect to apply
string sound_effect; // this is the sound effect to play
string vehicle_tag; // this is the tag on the vehicle where the effect should be applied
} effect_array[];
};
So, when a vehicle's health percentage becomes <= s_vehicle_damage_effects.health_percentage,
all the effects listed in s_vehicle_damage_effects.effect_array[] are applied as defined
*/
// damage indices
k_mild_damage_index= 0;
k_moderate_damage_index= 1;
k_severe_damage_index= 2;
k_total_damage_index= 3;
// health_percentage constants
k_mild_damage_health_percentage= 0.85;
k_moderate_damage_health_percentage= 0.55;
k_severe_damage_health_percentage= 0.35;
k_total_damage_health_percentage= 0;
level.k_mild_damage_health_percentage = k_mild_damage_health_percentage;
level.k_moderate_damage_health_percentage = k_moderate_damage_health_percentage;
level.k_severe_damage_health_percentage = k_severe_damage_health_percentage;
level.k_total_damage_health_percentage = k_total_damage_health_percentage;
level.vehicles_damage_states= [];
level.vehicles_husk_effects = [];
level.vehicles_damage_treadfx = [];
// setup the default vehicle
vehicle_name= get_default_vehicle_name();
{
level.vehicles_damage_states[vehicle_name]= [];
level.vehicles_damage_treadfx[vehicle_name] = [];
// mild damage
{
level.vehicles_damage_states[vehicle_name][k_mild_damage_index]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_mild_damage_index].health_percentage= k_mild_damage_health_percentage;
level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array= [];
// effect '0' - placed @ "tag_origin"
level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0].damage_effect= loadFX("vehicle/vfire/fx_tank_sherman_smldr"); // smoldering (smoke puffs)
level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0].sound_effect= undefined;
level.vehicles_damage_states[vehicle_name][k_mild_damage_index].effect_array[0].vehicle_tag= "tag_origin";
}
// moderate damage
{
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].health_percentage= k_moderate_damage_health_percentage;
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array= [];
// effect '0' - placed @ "tag_origin"
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0].damage_effect= loadFX("vehicle/vfire/fx_vfire_med_12"); // flames & more smoke
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0].sound_effect= undefined;
level.vehicles_damage_states[vehicle_name][k_moderate_damage_index].effect_array[0].vehicle_tag= "tag_origin";
}
// severe damage
{
level.vehicles_damage_states[vehicle_name][k_severe_damage_index]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_severe_damage_index].health_percentage= k_severe_damage_health_percentage;
level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array= [];
// effect '0' - placed @ "tag_origin"
level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0].damage_effect= loadFX("vehicle/vfire/fx_vfire_sherman"); // pillar of smoke
level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0].sound_effect= undefined;
level.vehicles_damage_states[vehicle_name][k_severe_damage_index].effect_array[0].vehicle_tag= "tag_origin";
}
// total damage
{
level.vehicles_damage_states[vehicle_name][k_total_damage_index]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_total_damage_index].health_percentage= k_total_damage_health_percentage;
level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array= [];
// effect '0' - placed @ "tag_origin"
level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0]= SpawnStruct();
level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0].damage_effect= loadFX("explosions/large_vehicle_explosion");
level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0].sound_effect= "vehicle_explo"; // kaboom!
level.vehicles_damage_states[vehicle_name][k_total_damage_index].effect_array[0].vehicle_tag= "tag_origin";
}
{
default_husk_effects = SpawnStruct();
default_husk_effects.damage_effect = undefined;//loadFX("vehicle/vfire/fx_vfire_med_12"); // flames & more smoke
default_husk_effects.sound_effect = undefined;
default_husk_effects.vehicle_tag = "tag_origin";
level.vehicles_husk_effects[ vehicle_name ] = default_husk_effects;
}
}
maps\mp\_t34::build_damage_states();
maps\mp\_panzeriv::build_damage_states();
return;
}
//string
get_vehicle_name(
vehicle)
{
name= "";
if (IsDefined(vehicle))
{
if (IsDefined(vehicle.vehicletype))
{
name= vehicle.vehicletype;
}
}
return name;
}
//string
get_default_vehicle_name()
{
return "defaultvehicle_mp";
}
//string
get_vehicle_name_key_for_damage_states(
vehicle)
{
vehicle_name= get_vehicle_name(vehicle);
if (!IsDefined(level.vehicles_damage_states[vehicle_name]))
{
vehicle_name= get_default_vehicle_name();
}
return vehicle_name;
}
//int
get_vehicle_damage_state_index_from_health_percentage(
vehicle)
{
damage_state_index= -1;
vehicle_name= get_vehicle_name_key_for_damage_states();
for (test_index= 0; test_index<level.vehicles_damage_states[vehicle_name].size; test_index++)
{
if (vehicle.current_health_percentage<=level.vehicles_damage_states[vehicle_name][test_index].health_percentage)
{
damage_state_index= test_index;
}
else
{
break;
}
}
return damage_state_index;
}
// called each time a vehicle takes damage
update_damage_effects(
vehicle,
attacker)
{
if (vehicle.initial_state.health>0)
{
previous_damage_state_index= get_vehicle_damage_state_index_from_health_percentage(vehicle);
vehicle.current_health_percentage= vehicle.health/vehicle.initial_state.health;
current_damage_state_index= get_vehicle_damage_state_index_from_health_percentage(vehicle);
// if we have reached a new damage state, play associated effects
if (previous_damage_state_index!=current_damage_state_index)
{
vehicle notify ( "damage_state_changed" );
if (previous_damage_state_index<0)
{
start_damage_state_index= 0;
}
else
{
start_damage_state_index= previous_damage_state_index+1;
}
play_damage_state_effects(vehicle, start_damage_state_index, current_damage_state_index);
if ( vehicle.health <= 0 )
{
vehicle kill_vehicle(attacker);
}
}
}
return;
}
play_damage_state_effects(
vehicle,
start_damage_state_index,
end_damage_state_index)
{
vehicle_name= get_vehicle_name_key_for_damage_states( vehicle );
// play effects for all damage states from start_damage_state_index --> end_damage_state_index
for (damage_state_index= start_damage_state_index; damage_state_index<=end_damage_state_index; damage_state_index++)
{
for (effect_index= 0;
effect_index<level.vehicles_damage_states[vehicle_name][damage_state_index].effect_array.size;
effect_index++)
{
effects = level.vehicles_damage_states[ vehicle_name ][ damage_state_index ].effect_array[ effect_index ];
vehicle thread play_vehicle_effects( effects );
}
}
return;
}
play_vehicle_effects( effects, isDamagedTread )
{
self endon( "delete" );
self endon( "removed" );
if ( !isdefined( isDamagedTread ) || isDamagedTread == 0 )
{
self endon( "damage_state_changed" );
}
// if there is an associated sound effect, play it
if ( IsDefined( effects.sound_effect ) )
{
self PlaySound( effects.sound_effect );
}
waitTime = 0;
if ( isdefined ( effects.damage_effect_loop_time ) )
{
waitTime = effects.damage_effect_loop_time;
}
while ( waitTime > 0 )
{
// if the specified effect was loaded, play it on the associated vehicle tag
if ( IsDefined( effects.damage_effect ) )
{
PlayFxOnTag( effects.damage_effect, self, effects.vehicle_tag );
}
wait( waitTime );
}
}
init_vehicle_entities()
{
vehicles = getentarray( "script_vehicle", "classname" );
array_thread( vehicles, ::init_original_vehicle );
if ( isdefined( vehicles ) )
{
return vehicles.size;
}
return 0;
}
precache_vehicles()
{
// Last time I tested, this is actually not called from anywhere
}
register_vehicle()
{
// Register vehicle husk
if ( !IsDefined( level.vehicles_list ) )
{
level.vehicles_list = [];
}
level.vehicles_list[ level.vehicles_list.size ] = self;
}
// Before spawning a new vehicle, we need to bookkeep our list
// of instantiated vehicles. If spawning the new vehicle would cause us to go
// over the max vehicle limit, then we need to force-delete
// the oldest (dead) ones to make room.
manage_vehicles()
{
if ( !IsDefined( level.vehicles_list ) )
{
return true;
}
else
{
MAX_VEHICLES = GetMaxVehicles();
{
// Consolidate array - Husks might have been cleaned up in the interim
newArray = [];
for ( i = 0; i < level.vehicles_list.size; i++ )
{
if ( IsDefined( level.vehicles_list[ i ] ) )
{
newArray[ newArray.size ] = level.vehicles_list[ i ];
}
}
level.vehicles_list = newArray;
}
// make sure there's room for one more
vehiclesToDelete = ( level.vehicles_list.size + 1 ) - MAX_VEHICLES;
if ( vehiclesToDelete > 0 )
{
newArray = [];
for ( i = 0; i < level.vehicles_list.size; i++ )
{
vehicle = level.vehicles_list[ i ];
if ( vehiclesToDelete > 0 )
{
// ".permanentlyRemoved" vehicles will never be really deleted
if ( IsDefined( vehicle.is_husk ) && !IsDefined( vehicle.permanentlyRemoved ) )
{
deleted = vehicle husk_do_cleanup();
if ( deleted )
{
vehiclesToDelete--;
continue;
}
}
}
newArray[ newArray.size ] = vehicle;
}
level.vehicles_list = newArray;
}
return level.vehicles_list.size < MAX_VEHICLES;
}
}
/*
=============
///ScriptDocBegin
"Name: init_vehicle( )"
"Summary: Initializes a vehicle entity when the game starts"
"Module: Vehicle"
"Example: vehicle init_vehicle();"
"SPMP: multiplayer"
///ScriptDocEnd
=============
*/
init_vehicle()
{
self register_vehicle();
// setting the tank health here so it is universal
// we should do the same for the other vehicles
if ( isdefined( level.vehicleHealths ) && isdefined( level.vehicleHealths[ self.vehicletype ] ) )
{
self.maxhealth = level.vehicleHealths[ self.vehicletype ];
}
else
{
self.maxhealth = getdvarint("scr_veh_health_tank");
/#
println( "No health specified for vehicle type "+self.vehicletype+"! Using default..." );
#/
}
self.health = self.maxhealth;
self vehicle_record_initial_values();
self init_vehicle_threads();
self maps\mp\gametypes\_spawning::create_vehicle_influencers();
self thread monitorTankDeath();
}
initialize_vehicle_damage_state_data()
{
if (self.initial_state.health>0)
{
self.current_health_percentage= self.health/self.initial_state.health;
self.previous_health_percentage= self.health/self.initial_state.health;
}
else
{
self.current_health_percentage= 1;
self.previous_health_percentage= 1;
}
return;
}
init_original_vehicle()
{
// this is a temporary hack trying to resolve the "!cent->pose.fx.effect"
// crash bug. Basically I think the bug is caused by deleteing the original
// tanks that were in the bsp
self.original_vehicle = true;
self init_vehicle();
}
monitorTankDeath()
{
self endon( "disconnect" );
for ( ;; )
{
self waittill ("death", attacker, damageFromUnderneath, weaponName );
if ( isdefined ( attacker ) && isdefined ( weaponName ) )
{
if ( attacker.health < 100 && isDefined ( attacker.lastTankThatAttacked ) )
{
if (self == attacker.lastTankThatAttacked )
attacker maps\mp\gametypes\_missions::doMissionCallback( "youtalkintome", attacker );
}
if ( isdefined ( damageFromUnderneath ) && isdefined( attacker.pers ) && vehicle_get_occupant_team() != attacker.pers["team"] || game["dialog"]["gametype"] == "freeforall" )
{
if ( damageFromUnderneath && weaponName == "satchel_charge_mp")
attacker maps\mp\gametypes\_missions::doMissionCallback( "trapper", attacker );
}
}
}
}
/*###stefan $NOTE OLD debug vehicle health bar system; disabled per David Vonderhaar - do not turn this on!
// debug code below is for a simple vehicle health bar
vehicle_wait_player_enter_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
while( 1 )
{
self waittill( "enter_vehicle", player );
player thread player_wait_exit_vehicle_t();
player player_update_vehicle_hud( true, self );
}
}
player_wait_exit_vehicle_t()
{
// Don't endon "death". Player will receive
// "exit_vehicle" message when killed in a vehicle
self endon( "disconnect" );
self waittill( "exit_vehicle", vehicle );
self player_update_vehicle_hud( false, vehicle );
}
vehicle_wait_damage_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
while( 1 )
{
self waittill ( "damage", damage, attacker, direction_vec, point, type );
occupants = self GetVehOccupants();
if( isDefined( occupants ) )
{
for( i = 0; i < occupants.size; i++ )
{
occupants[i] player_update_vehicle_hud( true, self );
}
}
}
}
player_update_vehicle_hud( show, vehicle )
{
if( show )
{
if ( !isDefined( self.vehicleHud ) )
{
self.vehicleHud = createBar( (1, 1, 1), 64, 16 );
self.vehicleHud setPoint( "CENTER", "BOTTOM", 0, -40 );
self.vehicleHud.alpha = 0.75;
}
self.vehicleHud updateBar( vehicle.health / vehicle.initial_state.health );
}
else
{
if ( isDefined( self.vehicleHud ) )
{
self.vehicleHud destroyElem();
}
}
if ( getDvar( "scr_vehicle_healthnumbers" )!= "" )
{
if ( getDvarInt( "scr_vehicle_healthnumbers" )!= 0 )
{
if( show )
{
if ( !isDefined( self.vehicleHudHealthNumbers ) )
{
self.vehicleHudHealthNumbers = createFontString( "default", 2.0 );
self.vehicleHudHealthNumbers setParent( self.vehicleHud );
self.vehicleHudHealthNumbers setPoint( "LEFT", "RIGHT", 8, 0 );
self.vehicleHudHealthNumbers.alpha = 0.75;
self.vehicleHudHealthNumbers.hideWhenInMenu = false;
self.vehicleHudHealthNumbers.archived = false;
}
self.vehicleHudHealthNumbers setValue( vehicle.health );
}
else
{
if ( isDefined( self.vehicleHudHealthNumbers ) )
{
self.vehicleHudHealthNumbers destroyElem();
}
}
}
}
}
*/
init_vehicle_threads()
{
self thread vehicle_fireweapon_t();
self thread vehicle_abandoned_by_drift_t();
self thread vehicle_abandoned_by_occupants_t();
self thread vehicle_damage_t();
self thread vehicle_ghost_entering_occupants_t();
self thread vehicle_recycle_spawner_t();
self thread vehicle_disconnect_paths();
/*###stefan $NOTE removed 07/29/2008
// enable debug vehicle health bar
self thread vehicle_wait_player_enter_t();
self thread vehicle_wait_damage_t();
*/
self thread vehicle_wait_tread_damage();
self thread vehicle_overturn_suicide();
/#
self thread cleanup_debug_print_t();
self thread cleanup_debug_print_clearmsg_t();
#/
}
/*
=============
///ScriptDocBegin
"Name: build_template( <type> , <model> , <typeoverride> )"
"Summary: called in individual vehicle file - mandatory to call this in all vehicle files at the top!"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <type> : vehicle type to set"
"MandatoryArg: <model> : model to set( this is usually generated by the level script )"
"OptionalArg: <typeoverride> : this overrides the type, used for copying a vehicle script"
"Example: build_template( "bmp", model, type );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_template( type, model, typeoverride )
{
if( isdefined( typeoverride ) )
type = typeoverride;
//precachevehicle( type );
if( !isdefined( level.vehicle_death_fx ) )
level.vehicle_death_fx = [];
if( !isdefined( level.vehicle_death_fx[ type ] ) )
level.vehicle_death_fx[ type ] = []; // can have overrides
level.vehicle_compassicon[ type ] = false;
level.vehicle_team[ type ] = "axis";
level.vehicle_life[ type ] = 999;
level.vehicle_hasMainTurret[ model ] = false;
level.vehicle_mainTurrets[ model ] = [];
level.vtmodel = model;
level.vttype = type;
}
/*
=============
///ScriptDocBegin
"Name: build_rumble( <rumble> , <scale> , <duration> , <radius> , <basetime> , <randomaditionaltime> )"
"Summary: called in individual vehicle file - define amount of radius damage to be set on each vehicle"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <rumble> : rumble asset"
"MandatoryArg: <scale> : scale"
"MandatoryArg: <duration> : duration"
"MandatoryArg: <radius> : radius"
"MandatoryArg: <basetime> : time to wait between rumbles"
"MandatoryArg: <randomaditionaltime> : random amount of time to add to basetime"
"Example: build_rumble( "tank_rumble", 0.15, 4.5, 600, 1, 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_rumble( rumble, scale, duration, radius, basetime, randomaditionaltime )
{
if( !isdefined( level.vehicle_rumble ) )
level.vehicle_rumble = [];
struct = build_quake( scale, duration, radius, basetime, randomaditionaltime );
assert( isdefined( rumble ) );
precacherumble( rumble );
struct.rumble = rumble;
level.vehicle_rumble[ level.vttype ] = struct;
precacherumble( "tank_damaged_rumble_mp" );
}
build_quake( scale, duration, radius, basetime, randomaditionaltime )
{
struct = spawnstruct();
struct.scale = scale;
struct.duration = duration;
struct.radius = radius;
if( isdefined( basetime ) )
struct.basetime = basetime;
if( isdefined( randomaditionaltime ) )
struct.randomaditionaltime = randomaditionaltime;
return struct;
}
/*
=============
///ScriptDocBegin
"Name: build_exhaust( <exhaust_effect_str> )"
"Summary: called in individual vehicle file - assign an exhaust effect to this vehicle!"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <exhaust_effect_str> : exhaust effect in string format"
"Example: build_exhaust( "distortion / abrams_exhaust" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_exhaust( effect )
{
level.vehicle_exhaust[ level.vtmodel ] = loadfx( effect );
}
/*
=============
///ScriptDocBegin
"Name: build_treadfx()"
"Summary: called in individual vehicle file - enables treadfx"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"Example: build_treadfx();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_treadfx( type )
{
if( ! isdefined( type ) )
type = level.vttype;
maps\mp\_treadfx::main( type );
}
cleanup_debug_print_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
/#
while ( 1 )
{
if ( IsDefined( self.debug_message )
&& getdvarint( "scr_veh_cleanupdebugprint" ) != 0 )
{
Print3d( self.origin + ( 0, 0, 150 ), self.debug_message, ( 0, 1, 0 ), 1, 1, 1 );
}
wait 0.01;
}
#/
}
cleanup_debug_print_clearmsg_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
/#
while ( 1 )
{
self waittill( "enter_vehicle" );
self.debug_message = undefined;
}
#/
}
cleanup_debug_print( message )
{
/#
self.debug_message = message;
#/
}
// =====================================================================================
// Abandonment Code
vehicle_abandoned_by_drift_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
self wait_then_cleanup_vehicle( "Drift Test", "scr_veh_cleanupdrifted" );
}
vehicle_abandoned_by_occupants_timeout_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
self wait_then_cleanup_vehicle( "Abandon Test", "scr_veh_cleanupabandoned" );
}
wait_then_cleanup_vehicle( test_name, cleanup_dvar_name )
{
self endon( "enter_vehicle" );
self wait_until_severely_damaged();
self do_alive_cleanup_wait( test_name );
self wait_for_vehicle_to_stop_outside_min_radius(); // unoccupied vehicle can be being pushed!
self cleanup( test_name, cleanup_dvar_name, ::vehicle_recycle );
}
wait_until_severely_damaged()
{
while ( 1 )
{
health_percentage = self.health / self.initial_state.health;
if ( IsDefined( level.k_severe_damage_health_percentage ) ) // initially called before this is set up.
{
self cleanup_debug_print( "Damage Test: Still healthy - (" + health_percentage + " >= " + level.k_severe_damage_health_percentage + ") and working treads");
}
else
{
self cleanup_debug_print( "Damage Test: Still healthy and working treads");
}
self waittill( "damage" );
health_percentage = self.health / self.initial_state.health;
if ( ( health_percentage < level.k_severe_damage_health_percentage )
|| ( ( self GetTreadHealth( 0 ) ) <= 0.0 )
|| ( ( self GetTreadHealth( 1 ) ) <= 0.0 ) )
break;
}
}
get_random_cleanup_wait_time( state )
{
varnamePrefix = "scr_veh_" + state + "_cleanuptime";
minTime = getdvarfloat( varnamePrefix + "min" );
maxTime = getdvarfloat( varnamePrefix + "max" );
if ( maxTime > minTime )
{
return RandomFloatRange( minTime, maxTime );
}
else
{
return maxTime;
}
}
do_alive_cleanup_wait( test_name )
{
initialRandomWaitSeconds = get_random_cleanup_wait_time( "alive" );
secondsWaited = 0.0;
seconds_per_iteration = 1.0;
while ( true )
{
curve_begin = GetDvarFloat( "scr_veh_cleanuptime_dmgfraction_curve_begin" );
curve_end = GetDvarFloat( "scr_veh_cleanuptime_dmgfraction_curve_end" );
factor_min = GetDvarFloat( "scr_veh_cleanuptime_dmgfactor_min" );
factor_max = GetDvarFloat( "scr_veh_cleanuptime_dmgfactor_max" );
treadDeadDamageFactor = GetDvarFloat( "scr_veh_cleanuptime_dmgfactor_deadtread" );
damageFraction = 0.0;
if ( self is_vehicle() )
{
damageFraction = ( self.initial_state.health - self.health ) / self.initial_state.health;
}
else // is husk
{
damageFraction = 1.0;
}
damageFactor = 0.0;
if ( damageFraction <= curve_begin )
{
damageFactor = factor_max;
}
else if ( damageFraction >= curve_end )
{
damageFactor = factor_min;
}
else
{
dydx = ( factor_min - factor_max ) / ( curve_end - curve_begin );
damageFactor = factor_max + ( damageFraction - curve_begin ) * dydx;
}
{
// For each tread that has been completely destroyed, decrease the
// total amount of time to wait before cleaning up.
for ( i = 0; i < 2; i++ )
{
if ( ( self GetTreadHealth( i ) ) <= 0 )
{
damageFactor -= treadDeadDamageFactor;
}
}
}
totalSecsToWait = initialRandomWaitSeconds * damageFactor;
if ( secondsWaited >= totalSecsToWait )
{
break;
}
self cleanup_debug_print( test_name + ": Waiting " + ( totalSecsToWait - secondsWaited ) + "s" );
wait seconds_per_iteration;
secondsWaited = secondsWaited + seconds_per_iteration;
}
}
do_dead_cleanup_wait( test_name )
{
total_secs_to_wait = get_random_cleanup_wait_time( "dead" );
seconds_waited = 0.0;
seconds_per_iteration = 1.0;
while ( seconds_waited < total_secs_to_wait )
{
self cleanup_debug_print( test_name + ": Waiting " + ( total_secs_to_wait - seconds_waited ) + "s" );
wait seconds_per_iteration;
seconds_waited = seconds_waited + seconds_per_iteration;
}
}
cleanup( test_name, cleanup_dvar_name, cleanup_func )
{
keep_waiting = true;
while ( keep_waiting )
{
cleanupEnabled = !IsDefined( cleanup_dvar_name )
|| getdvarint( cleanup_dvar_name ) != 0;
if ( cleanupEnabled != 0 )
{
self [[cleanup_func]]();
break;
}
keep_waiting = false;
/#
// Only in debug mode, we will keep looping in case someone turns the
// cleanup dvar back on.
self cleanup_debug_print( "Cleanup disabled for " + test_name + " ( dvar = " + cleanup_dvar_name + " )" );
wait 5.0;
keep_waiting = true;
#/
}
}
vehicle_wait_tread_damage()
{
self endon( "death" );
self endon( "delete" );
vehicle_name= get_vehicle_name(self);
while ( 1 )
{
self waittill ( "broken", brokenNotify );
if ( brokenNotify == "left_tread_destroyed" )
{
if ( isdefined( level.vehicles_damage_treadfx[vehicle_name] ) && isdefined( level.vehicles_damage_treadfx[vehicle_name][0] ) )
{
self thread play_vehicle_effects( level.vehicles_damage_treadfx[vehicle_name][0], true );
}
}
else if ( brokenNotify == "right_tread_destroyed" )
{
if ( isdefined( level.vehicles_damage_treadfx[vehicle_name] ) && isdefined( level.vehicles_damage_treadfx[vehicle_name][1] ) )
{
self thread play_vehicle_effects( level.vehicles_damage_treadfx[vehicle_name][1], true );
}
}
}
}
wait_for_vehicle_to_stop_outside_min_radius()
{
maxWaitTime = getdvarfloat( "scr_veh_waittillstoppedandmindist_maxtime" );
iterationWaitSeconds = 1.0;
maxWaitTimeEnableDistInches = 12 * getdvarfloat( "scr_veh_waittillstoppedandmindist_maxtimeenabledistfeet" );
initialOrigin = self.initial_state.origin;
for ( totalSecondsWaited = 0.0; totalSecondsWaited < maxWaitTime; totalSecondsWaited += iterationWaitSeconds )
{
// We don't want to disappear it if someone is
// currently pushing it with another vehicle
speedMPH = self GetSpeedMPH();
cutoffMPH = getdvarfloat( "scr_veh_cleanupmaxspeedmph" );
if ( speedMPH > cutoffMPH )
{
cleanup_debug_print( "(" + ( maxWaitTime - totalSecondsWaited ) + "s) Speed: " + speedMPH + ">" + cutoffMPH );
}
else
{
break;
/*
initialOriginOnTerrain = ( initialOrigin[0], initialOrigin[1], 0 );
originOnTerrain = ( self.origin[0], self.origin[1], 0 );
moveDistInches = Distance( initialOriginOnTerrain, originOnTerrain );
cutoffInches = 12 * getdvarint( "scr_veh_cleanupmindistancefeet" );
if ( moveDistInches < cutoffInches )
{
cleanup_debug_print( "(" + ( maxWaitTime - totalSecondsWaited ) + "s) Dist: " + moveDistInches + "<" + cutoffInches );
if ( moveDistInches < maxWaitTimeEnableDistInches )
{
totalSecondsWaited = 0.0;
}
}
else
{
break;
}
*/
}
wait iterationWaitSeconds;
}
}
vehicle_abandoned_by_occupants_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
while ( 1 )
{
self waittill( "exit_vehicle" );
occupants = self GetVehOccupants();
if ( occupants.size == 0 )
{
self play_start_stop_sound( "tank_shutdown_sfx" );
self thread vehicle_abandoned_by_occupants_timeout_t();
}
}
}
play_start_stop_sound( sound_alias, modulation )
{
if ( IsDefined( self.start_stop_sfxid ) )
{
//stopSound( self.start_stop_sfxid );
}
self.start_stop_sfxid = self playSound( sound_alias );
}
// this function should be replaced by code in cg_player.cpp cg_player()
// we should just not be rendering the player when in a non-visible seat
vehicle_ghost_entering_occupants_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
//if ( self vehicle_is_tank() )
{
while ( 1 )
{
self waittill( "enter_vehicle", player, seat );
isDriver = seat == 0;
if ( getdvarint( "scr_veh_driversarehidden" ) != 0
&& isDriver )
{
player Ghost();
}
{
occupants = self GetVehOccupants();
if ( occupants.size == 1 )
{
self play_start_stop_sound( "tank_startup_sfx" );
}
}
player thread player_change_seat_handler_t( self );
player thread player_leave_vehicle_cleanup_t( self );
}
}
}
player_is_occupant_invulnerable( sMeansOfDeath )
{
invulnerable = ( level.vehicle_drivers_are_invulnerable && ( self player_is_driver() ) );
return invulnerable;
}
player_is_driver()
{
if ( !isalive(self) )
return false;
vehicle = self GetVehicleOccupied();
if ( IsDefined( vehicle ) )
{
seat = vehicle GetOccupantSeat( self );
if ( isdefined(seat) && seat == 0 )
return true;
}
return false;
}
// this function should be replaced by code in cg_player.cpp cg_player()
// we should just not be rendering the player when in a non-visible seat
player_change_seat_handler_t( vehicle )
{
self endon( "disconnect" );
self endon( "exit_vehicle" );
while ( 1 )
{
self waittill( "change_seat", vehicle, oldSeat, newSeat );
isDriver = newSeat == 0;
if ( isDriver )
{
if ( getdvarint( "scr_veh_driversarehidden" ) != 0 )
{
self Ghost();
}
}
else
{
self Show();
}
}
}
// this function should be replaced by code in cg_player.cpp cg_player()
// we should just not be rendering the player when in a non-visible seat
player_leave_vehicle_cleanup_t( vehicle )
{
self endon( "disconnect" );
self waittill( "exit_vehicle" );
currentWeapon = self getCurrentWeapon();
if( self.lastWeapon != currentWeapon && self.lastWeapon != "none" )
self switchToWeapon( self.lastWeapon );
self Show();
}
vehicle_is_tank()
{
return self.vehicletype == "sherman_mp"
|| self.vehicletype == "panzer4_mp"
|| self.vehicletype == "type97_mp"
|| self.vehicletype == "t34_mp";
}
// =====================================================================================
vehicle_record_initial_values()
{
if ( !IsDefined( self.initial_state ) )
{
self.initial_state= SpawnStruct();
}
if ( IsDefined( self.origin ) )
{
self.initial_state.origin= self.origin;
}
if ( IsDefined( self.angles ) )
{
self.initial_state.angles= self.angles;
}
if ( IsDefined( self.health ) )
{
self.initial_state.health= self.health;
}
self maps\mp\_destructible::destructible_record_initial_values();
self initialize_vehicle_damage_state_data();
return;
}
vehicle_fireweapon_t()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
for( ;; )
{
self waittill( "turret_fire", player );
if ( isdefined(player) && isalive(player) && player isinvehicle() )
self fireweapon();
}
}
vehicle_should_explode_on_cleanup()
{
return getdvarint( "scr_veh_explode_on_cleanup" ) != 0;
}
vehicle_recycle()
{
self wait_for_unnoticeable_cleanup_opportunity();
self.recycling = true;
self suicide();
}
wait_for_vehicle_overturn()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
worldup = anglestoup((0,90,0));
overturned = 0;
while (!overturned)
{
if ( IsDefined( self.angles ) )
{
up = AnglesToUp( self.angles );
dot = vectordot(up, worldup);
if (dot <= 0.0)
overturned = 1;
}
if (!overturned)
wait (1.0);
}
}
vehicle_overturn_suicide()
{
self endon( "transmute" );
self endon( "death" );
self endon( "delete" );
self wait_for_vehicle_overturn();
seconds = RandomFloatRange( 5, 7 );
wait seconds;
damageOrigin = self.origin + (0,0,25);
self finishVehicleRadiusDamage(self, self, 32000, 32000, 32000, 0, "MOD_EXPLOSIVE", "defaultweapon_mp", damageOrigin, 400, -1, (0,0,1), 0);
}
suicide()
{
self kill_vehicle( self );
}
kill_vehicle( attacker )
{
damageOrigin = self.origin + (0,0,1);
self finishVehicleRadiusDamage(attacker, attacker, 32000, 32000, 10, 0, "MOD_EXPLOSIVE", "defaultweapon_mp", damageOrigin, 400, -1, (0,0,1), 0);
}
value_with_default( preferred_value, default_value )
{
if ( IsDefined( preferred_value ) )
{
return preferred_value;
}
return default_value;
}
vehicle_transmute( attacker )
{
deathOrigin = self.origin;
deathAngles = self.angles;
modelname = self VehGetModel();
vehicle_name = get_vehicle_name_key_for_damage_states( self );
respawn_parameters = SpawnStruct();
respawn_parameters.origin = self.initial_state.origin;
respawn_parameters.angles = self.initial_state.angles;
respawn_parameters.health = self.initial_state.health;
respawn_parameters.modelname = modelname;
respawn_parameters.targetname = value_with_default( self.targetname, "" );
respawn_parameters.vehicletype = value_with_default( self.vehicletype, "" );
respawn_parameters.destructibledef = self.destructibledef; // Vehicle may or may not be a destructible vehicle
vehicleWasDestroyed = !IsDefined( self.recycling );
if ( vehicleWasDestroyed
|| vehicle_should_explode_on_cleanup() )
{
_spawn_explosion( deathOrigin );
if ( vehicleWasDestroyed
&& getdvarint( "scr_veh_explosion_doradiusdamage" ) != 0 )
{
// Vehicle is exploding, so damage nearby entities
// Damage first, so doesn't affect other entities spawned
// by this function
explosionRadius = getdvarint( "scr_veh_explosion_radius" );
explosionMinDamage = getdvarint( "scr_veh_explosion_mindamage" );
explosionMaxDamage = getdvarint( "scr_veh_explosion_maxdamage" );
self kill_vehicle(attacker);
self RadiusDamage( deathOrigin, explosionRadius, explosionMaxDamage, explosionMinDamage, attacker, "MOD_EXPLOSIVE", self.vehicletype+"_explosion_mp" );////////////////XXXXXXXXXXXXXXXXX COLLATERAL DAMAGE
}
}
self notify( "transmute" );
respawn_vehicle_now = true;
if ( vehicleWasDestroyed
&& getdvarint( "scr_veh_ondeath_createhusk" ) != 0 )
{
// Spawn burned out husk for players to use as cover
if ( getdvarint( "scr_veh_ondeath_usevehicleashusk" ) != 0 )
{
husk = self;
self.is_husk = true;
}
else
{
husk = _spawn_husk( deathOrigin, deathAngles, modelname );
}
husk _init_husk( vehicle_name, respawn_parameters );
if ( getdvarint( "scr_veh_respawnafterhuskcleanup" ) != 0 )
{
respawn_vehicle_now = false;
}
}
if ( !IsDefined( self.is_husk ) )
{
self remove_vehicle_from_world();
}
if ( respawn_vehicle_now )
{
respawn_vehicle( respawn_parameters );
}
}
respawn_vehicle( respawn_parameters )
{
{
minTime = getdvarint( "scr_veh_respawntimemin" );
maxTime = getdvarint( "scr_veh_respawntimemax" );
seconds = RandomFloatRange( minTime, maxTime );
wait seconds;
}
wait_until_vehicle_position_wont_telefrag( respawn_parameters.origin );
if ( !manage_vehicles() ) // make sure we don't hit max vehicle limit
{
/#
iprintln("Vehicle can't respawn because MAX_VEHICLES has been reached and none of the vehicles could be cleaned up.");
#/
}
else
{
if ( IsDefined( respawn_parameters.destructibledef ) ) // passing undefined argument doesn't make the server happy
{
vehicle = SpawnVehicle(
respawn_parameters.modelname,
respawn_parameters.targetname,
respawn_parameters.vehicletype,
respawn_parameters.origin,
respawn_parameters.angles,
respawn_parameters.destructibledef );
}
else
{
vehicle = SpawnVehicle(
respawn_parameters.modelname,
respawn_parameters.targetname,
respawn_parameters.vehicletype,
respawn_parameters.origin,
respawn_parameters.angles );
}
vehicle.vehicletype = respawn_parameters.vehicletype;
vehicle.destructibledef = respawn_parameters.destructibledef;
vehicle.health = respawn_parameters.health;
vehicle init_vehicle();
vehicle vehicle_telefrag_griefers_at_position( respawn_parameters.origin );
}
}
remove_vehicle_from_world()
{
// this is a temporary hack trying to resolve the "!cent->pose.fx.effect"
// crash bug. Basically I think the bug is caused by deleteing the original
// tanks that were in the bsp
self notify ( "removed" );
if ( IsDefined( self.original_vehicle ) )
{
if ( !IsDefined( self.permanentlyRemoved ) )
{
self.permanentlyRemoved = true; // Mark that it has been permanently removed from the world
self thread hide_vehicle(); // threaded because it calls a wait()
}
return false;
}
else
{
self _delete_entity();
return true;
}
}
_delete_entity()
{
/#
//iprintln("$e" + ( self GetEntNum() ) + " is deleting");
#/
self Delete();
}
hide_vehicle()
{
under_the_world = ( self.origin[0], self.origin[1], self.origin[2] - 10000 );
self.origin = under_the_world;
wait 0.1;
self Hide();
self notify( "hidden_permanently" );
}
wait_for_unnoticeable_cleanup_opportunity()
{
maxPreventDistanceFeet = getdvarint( "scr_veh_disappear_maxpreventdistancefeet" );
maxPreventVisibilityFeet = getdvarint( "scr_veh_disappear_maxpreventvisibilityfeet" );
maxPreventDistanceInchesSq = 144 * maxPreventDistanceFeet * maxPreventDistanceFeet;
maxPreventVisibilityInchesSq = 144 * maxPreventVisibilityFeet * maxPreventVisibilityFeet;
maxSecondsToWait = getdvarfloat( "scr_veh_disappear_maxwaittime" );
iterationWaitSeconds = 1.0;
for ( secondsWaited = 0.0; secondsWaited < maxSecondsToWait; secondsWaited += iterationWaitSeconds )
{
players_s = get_all_alive_players_s();
okToCleanup = true;
for ( j = 0; j < players_s.a.size && okToCleanup; j++ )
{
player = players_s.a[ j ];
distInchesSq = DistanceSquared( self.origin, player.origin );
if ( distInchesSq < maxPreventDistanceInchesSq )
{
self cleanup_debug_print( "(" + ( maxSecondsToWait - secondsWaited ) + "s) Player too close: " + distInchesSq + "<" + maxPreventDistanceInchesSq );
okToCleanup = false;
}
else if ( distInchesSq < maxPreventVisibilityInchesSq )
{
vehicleVisibilityFromPlayer = self SightConeTrace( player.origin, player, AnglesToForward( player.angles ) );
if ( vehicleVisibilityFromPlayer > 0 )
{
self cleanup_debug_print( "(" + ( maxSecondsToWait - secondsWaited ) + "s) Player can see" );
okToCleanup = false;
}
}
}
if ( okToCleanup )
{
return;
}
wait iterationWaitSeconds;
}
}
wait_until_vehicle_position_wont_telefrag( position )
{
maxIterations = getdvarint( "scr_veh_respawnwait_maxiterations" );
iterationWaitSeconds = getdvarint( "scr_veh_respawnwait_iterationwaitseconds" );
for ( i = 0; i < maxIterations; i++ )
{
if ( !vehicle_position_will_telefrag( position ) )
{
return;
}
wait iterationWaitSeconds;
}
}
vehicle_position_will_telefrag( position )
{
players_s = get_all_alive_players_s();
for ( i = 0; i < players_s.a.size; i++ )
{
if ( players_s.a[ i ] player_vehicle_position_will_telefrag( position ) )
{
return true;
}
}
return false;
}
vehicle_telefrag_griefers_at_position( position )
{
attacker = self;
inflictor = self;
doDamageToHead = 0;
players_s = get_all_alive_players_s();
for ( i = 0; i < players_s.a.size; i++ )
{
player = players_s.a[ i ];
if ( player player_vehicle_position_will_telefrag( position ) )
{
player DoDamage( 20000, player.origin + ( 0, 0, 1 ), attacker, inflictor, doDamageToHead );
}
}
}
player_vehicle_position_will_telefrag( position )
{
distanceInches = 20 * 12; ///< 20 ft., in inches
minDistInchesSq = distanceInches * distanceInches;
distInchesSq = DistanceSquared( self.origin, position );
return distInchesSq < minDistInchesSq;
}
vehicle_recycle_spawner_t()
{
self endon( "delete" );
self waittill( "death", attacker ); // "vehicle Delete()" sends death message too!!!
if ( IsDefined( self ) )
{
self vehicle_transmute( attacker );
}
}
vehicle_play_explosion_sound()
{
self playSound( "car_explo_large" );
}
vehicle_damage_t()
{
self endon( "delete" );
self endon( "removed" );
for( ;; )
{
self waittill ( "damage", damage, attacker );
players = get_players();
for ( i = 0 ; i < players.size ; i++ )
{
if ( !isalive(players[i]) )
continue;
vehicle = players[i] GetVehicleOccupied();
if ( isdefined( vehicle) && self == vehicle && players[i] player_is_driver() )
{
if (damage>0)
{
// ^^^ earthquake() will generate an SRE if scale <= 0
earthquake( damage/400, 1.0, players[i].origin, 512, players[i] );
}
if ( damage > 100.0 )
{
/#
println( "Playing heavy rumble." );
#/
players[i] PlayRumbleOnEntity( "tank_damage_heavy_mp" );
}
else if ( damage > 10.0 )
{
/#
println( "Playing light rumble." );
#/
players[i] PlayRumbleOnEntity( "tank_damage_light_mp" );
}
}
}
update_damage_effects(self, attacker);
if ( self.health <= 0 )
{
return;
}
}
}
// =====================================================================================
// Burnt-Out Husk Code
_spawn_husk( origin, angles, modelname )
{
husk = Spawn( "script_model", origin );
husk.angles = angles;
husk SetModel( modelname );
husk.health = 1;
husk SetCanDamage( false ); ///< Does this really work? It doesn't for players, but might for other entities
return husk;
}
is_vehicle()
{
// Could check classname=="script_vehicle", but this is a little more general purpose, I think
return IsDefined( self.vehicletype );
}
swap_to_husk_model()
{
if ( IsDefined( self.vehicletype ) )
{
husk_model = level.veh_husk_models[ self.vehicletype ];
if ( IsDefined( husk_model ) )
{
self SetModel( husk_model );
}
}
}
_init_husk( vehicle_name, respawn_parameters )
{
self swap_to_husk_model();
effects = level.vehicles_husk_effects[ vehicle_name ];
self play_vehicle_effects( effects );
self.respawn_parameters = respawn_parameters;
forcePointVariance = getdvarint( "scr_veh_explosion_husk_forcepointvariance" );
horzVelocityVariance = getdvarint( "scr_veh_explosion_husk_horzvelocityvariance" );
vertVelocityMin = getdvarint( "scr_veh_explosion_husk_vertvelocitymin" );
vertVelocityMax = getdvarint( "scr_veh_explosion_husk_vertvelocitymax" );
forcePointX = RandomFloatRange( 0-forcePointVariance, forcePointVariance );
forcePointY = RandomFloatRange( 0-forcePointVariance, forcePointVariance );
forcePoint = ( forcePointX, forcePointY, 0 );
forcePoint += self.origin;
initialVelocityX = RandomFloatRange( 0-horzVelocityVariance, horzVelocityVariance );
initialVelocityY = RandomFloatRange( 0-horzVelocityVariance, horzVelocityVariance );
initialVelocityZ = RandomFloatRange( vertVelocityMin, vertVelocityMax );
initialVelocity = ( initialVelocityX, initialVelocityY, initialVelocityZ );
if ( self is_vehicle() )
{
self LaunchVehicle( initialVelocity, forcePoint );
}
else
{
self PhysicsLaunch( forcePoint, initialVelocity );
}
self thread husk_cleanup_t();
/#
self thread cleanup_debug_print_t();
#/
}
husk_cleanup_t()
{
self endon( "death" ); // ent Delete() actually sends the "death" message!!!
self endon( "delete" );
self endon( "hidden_permanently" );
respawn_parameters = self.respawn_parameters;
self do_dead_cleanup_wait( "Husk Cleanup Test" );
self wait_for_unnoticeable_cleanup_opportunity();
self thread final_husk_cleanup_t( respawn_parameters ); // break off new thread to avoid end-ons
}
final_husk_cleanup_t( respawn_parameters )
{
self husk_do_cleanup(); // causes endons, which is why we broke this off into a new thread
if ( getdvarint( "scr_veh_respawnafterhuskcleanup" ) != 0 )
{
respawn_vehicle( respawn_parameters );
}
}
// Returns true only if the entity is actually deleted, rather than just hidden.
husk_do_cleanup()
{
// Don't ever let vehicles just blink out. Spawn a VFX
// explosion that doesn't injur any surrounding entities,
// just to mask the blink out, in case players are
// looking in the direction of this vehicle husk.
self _spawn_explosion( self.origin );
if ( self is_vehicle() )
{
return self remove_vehicle_from_world();
}
else
{
self _delete_entity();
return true;
}
}
// =====================================================================================
// =====================================================================================
// Explosion Code
_spawn_explosion( origin )
{
if ( getdvarint( "scr_veh_explosion_spawnfx" ) == 0 )
{
return;
}
if ( IsDefined( level.vehicle_explosion_effect ) )
{
forward = ( 0, 0, 1 );
rot = randomfloat( 360 );
up = ( cos( rot ), sin( rot ), 0 );
PlayFX( level.vehicle_explosion_effect, origin, forward, up );
}
thread _play_sound_in_space( "vehicle_explo", origin );
}
// NOTE: This function was copied from sab.gsc. Should be centralized somewhere...
_play_sound_in_space( soundEffectName, origin )
{
org = Spawn( "script_origin", origin );
org.origin = origin;
org playSound( soundEffectName );
wait 10; // MP doesn't have "sounddone" notifies =(
org delete();
}
// =====================================================================================
vehicle_get_occupant_team()
{
occupants = self GetVehOccupants();
if ( occupants.size != 0 )
{
// first occupant defines the vehicle team
occupant = occupants[0];
if ( isplayer(occupant) )
{
return occupant.pers["team"];
}
}
return "free";
}
vehicleDeathWaiter()
{
self notify ("vehicleDeathWaiter");
self endon ( "vehicleDeathWaiter" );
self endon ( "disconnect" );
while ( true )
{
self waittill( "vehicle_death", vehicle_died );
if( vehicle_died )
{
self.diedOnVehicle = true;
}
else
{
// if the vehicle did not blow up then they died on the turret
self.diedOnTurret = true;
}
}
}
turretDeathWaiter()
{
// this has been depricated and merged with vehicleDeathWaiter
}
vehicle_kill_disconnect_paths_forever()
{
self notify( "kill_disconnect_paths_forever" );
}
vehicle_disconnect_paths()
{
self endon( "death" );
self endon( "kill_disconnect_paths_forever" );
if ( isdefined( self.script_disconnectpaths ) && !self.script_disconnectpaths )
{
self.dontDisconnectPaths = true;// lets other parts of the script know not to disconnect script
return;
}
wait( randomfloat( 1 ) );
while( isdefined( self ) )
{
if( self getspeed() < 1 )
{
if ( !isdefined( self.dontDisconnectPaths ) )
self disconnectpaths();
self notify( "speed_zero_path_disconnect" );
while( self getspeed() < 1 )
wait .05;
}
self connectpaths();
wait 1;
}
}