4035 lines
107 KiB
Plaintext
4035 lines
107 KiB
Plaintext
#using scripts\codescripts\struct;
|
|
|
|
#using scripts\shared\array_shared;
|
|
#using scripts\shared\math_shared;
|
|
#using scripts\shared\callbacks_shared;
|
|
#using scripts\shared\clientfield_shared;
|
|
#using scripts\shared\exploder_shared;
|
|
#using scripts\shared\flag_shared;
|
|
#using scripts\shared\spawner_shared;
|
|
#using scripts\shared\system_shared;
|
|
#using scripts\shared\trigger_shared;
|
|
#using scripts\shared\turret_shared;
|
|
#using scripts\shared\vehicles\_auto_turret;
|
|
#using scripts\shared\util_shared;
|
|
#using scripts\shared\vehicleriders_shared;
|
|
#using scripts\shared\vehicle_death_shared;
|
|
|
|
|
|
|
|
|
|
// match MAX_LIGHTFX_GROUPS in vehiclecustomsettings.awi
|
|
// match MAX_AMBIENT_ANIM_GROUPS in vehiclecustomsettings.awi
|
|
|
|
|
|
|
|
#precache( "material", "black" );
|
|
#precache( "eventstring", "hud_vehicle_turret_fire" );
|
|
|
|
#namespace vehicle;
|
|
|
|
function autoexec __init__sytem__() { system::register("vehicle_shared",&__init__,&__main__,undefined); }
|
|
|
|
/*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
|
VEHICLE script
|
|
|
|
This handles playing the various effects and animations on a vehicle.
|
|
function It handles initializing a vehicle( giving it life, turrets, machine guns, treads and things )
|
|
|
|
It also handles spawning of vehicles in a very ugly way for now, we're getting code to make it pretty
|
|
|
|
Most things you see in the vehicle menu in Radiant are handled here. There's all sorts of properties
|
|
that you can set on a trigger to access some of this functionality. A trigger can spawn a vehicle,
|
|
toggle different behaviors,
|
|
|
|
|
|
HIGH LEVEL FUNCTIONS
|
|
|
|
// init( vehicle )
|
|
this give the vehicle life, treads, turrets, machine guns, all that good stuff
|
|
|
|
// animmode::main()
|
|
this is setup, sets up spawners, trigger associations etc is ran on first frame by _load
|
|
|
|
// trigger_process( trigger, vehicles )
|
|
since triggers are multifunction I made them all happen in the same thread so that
|
|
the sequencing would be easy to handle
|
|
|
|
// paths()
|
|
This makes the nodes get notified trigger when they are hit by a vehicle, we hope
|
|
to move this functionality to CODE side because we have to use a lot of wrappers for
|
|
attaching a vehicle to a path
|
|
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
*/
|
|
|
|
|
|
#using_animtree( "generic" );
|
|
|
|
function __init__()
|
|
{
|
|
clientfield::register( "vehicle", "toggle_lockon", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_sounds", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "use_engine_damage_sounds", 1, 2, "int" );
|
|
clientfield::register( "vehicle", "toggle_treadfx", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_exhaustfx", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_lights", 1, 2, "int" );
|
|
clientfield::register( "vehicle", "toggle_lights_group1", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_lights_group2", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_lights_group3", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_lights_group4", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_ambient_anim_group1", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_ambient_anim_group2", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_ambient_anim_group3", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_emp_fx", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "toggle_burn_fx", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "deathfx", 1, 2, "int" );
|
|
clientfield::register( "vehicle", "alert_level", 1, 2, "int" );
|
|
clientfield::register( "vehicle", "set_lighting_ent", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "use_lighting_ent", 1, 1, "int" );
|
|
clientfield::register( "vehicle", "damage_level", 1, 3, "int" );
|
|
clientfield::register( "vehicle", "spawn_death_dynents", 1, 2, "int" );
|
|
clientfield::register( "vehicle", "spawn_gib_dynents", 1, 1, "int" );
|
|
|
|
clientfield::register( "helicopter", "toggle_lockon", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_sounds", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "use_engine_damage_sounds", 1, 2, "int" );
|
|
clientfield::register( "helicopter", "toggle_treadfx", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_exhaustfx", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_lights", 1, 2, "int" );
|
|
clientfield::register( "helicopter", "toggle_lights_group1", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_lights_group2", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_lights_group3", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_lights_group4", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_ambient_anim_group1", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_ambient_anim_group2", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_ambient_anim_group3", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_emp_fx", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "toggle_burn_fx", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "deathfx", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "alert_level", 1, 2, "int" );
|
|
clientfield::register( "helicopter", "set_lighting_ent", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "use_lighting_ent", 1, 1, "int" );
|
|
clientfield::register( "helicopter", "damage_level", 1, 3, "int" );
|
|
clientfield::register( "helicopter", "spawn_death_dynents", 1, 2, "int" );
|
|
clientfield::register( "helicopter", "spawn_gib_dynents", 1, 1, "int" );
|
|
|
|
clientfield::register( "plane", "toggle_treadfx", 1, 1, "int" );
|
|
|
|
clientfield::register( "toplayer", "toggle_dnidamagefx", 1, 1, "int" );
|
|
clientfield::register( "toplayer", "toggle_flir_postfx", 1, 2, "int" );
|
|
|
|
clientfield::register( "toplayer", "static_postfx", 1, 1, "int" );
|
|
|
|
if(isdefined(level.bypassVehicleScripts))
|
|
{
|
|
return;
|
|
}
|
|
|
|
level.heli_default_decel = 10;
|
|
|
|
// put all the vehicles with targetnames into an array so we can spawn vehicles from
|
|
// a string instead of their vehicle group #
|
|
setup_targetname_spawners();
|
|
|
|
// vehicle related dvar initializing goes here
|
|
setup_dvars();
|
|
|
|
// initialize all the level wide vehicle system variables
|
|
setup_level_vars();
|
|
|
|
// pre - associate vehicle triggers and vehicle nodes with stuff.
|
|
setup_triggers();
|
|
|
|
setup_nodes();
|
|
|
|
// send the setup triggers to be processed
|
|
level array::thread_all_ents( level.vehicle_processtriggers, &trigger_process );
|
|
|
|
// CHECKME
|
|
level.vehicle_processtriggers = undefined;
|
|
|
|
// SCRIPTER_MOD: dguzzo: 3-9-09 : this looks to be used only for arcade mode. going to leave in the one cod:waw reference as an example.
|
|
// CODER_MOD: Tommy K
|
|
level.vehicle_enemy_tanks = [];
|
|
level.vehicle_enemy_tanks[ "vehicle_ger_tracked_king_tiger" ] = true;
|
|
|
|
level thread _watch_for_hijacked_vehicles();
|
|
|
|
}
|
|
|
|
function __main__()
|
|
{
|
|
a_all_spawners = GetVehicleSpawnerArray();
|
|
setup_spawners( a_all_spawners );
|
|
|
|
/#
|
|
level thread vehicle_spawner_tool();
|
|
level thread spline_debug();
|
|
#/
|
|
}
|
|
|
|
/* DEAD CODE REMOVAL
|
|
function trigger_getlinkmap( trigger )
|
|
{
|
|
linkMap = [];
|
|
if( isdefined( trigger.script_linkTo ) )
|
|
{
|
|
links = strtok( trigger.script_linkTo, " " );
|
|
for( i = 0; i < links.size; i++ )
|
|
{
|
|
linkMap[ links[ i ] ] = true;
|
|
}
|
|
links = undefined;
|
|
}
|
|
return linkMap;
|
|
}
|
|
*/
|
|
|
|
// setup_script_gatetrigger( trigger, linkMap )
|
|
function setup_script_gatetrigger( trigger )
|
|
{
|
|
gates = [];
|
|
if( isdefined( trigger.script_gatetrigger ) )
|
|
{
|
|
return level.vehicle_gatetrigger[ trigger.script_gatetrigger ];
|
|
}
|
|
return gates;
|
|
}
|
|
|
|
/* DEAD CODE REMOVAL
|
|
// setup_script_vehiclespawngroup( trigger, vehicles, linkMap )
|
|
function setup_script_vehiclespawngroup( trigger, vehicles )
|
|
{
|
|
script_vehiclespawngroup = false;
|
|
if( isdefined( trigger.script_vehiclespawngroup ) )
|
|
{
|
|
script_vehiclespawngroup = true;
|
|
}
|
|
return script_vehiclespawngroup;
|
|
}
|
|
*/
|
|
|
|
function trigger_process( trigger )
|
|
{
|
|
// these triggers only trigger once where vehicle paths trigger everytime a vehicle crosses them
|
|
if( isdefined( trigger.classname ) && ( trigger.classname == "trigger_multiple" || trigger.classname == "trigger_radius" || trigger.classname == "trigger_lookat" || trigger.classname == "trigger_box" ))
|
|
{
|
|
bTriggeronce = true;
|
|
}
|
|
else
|
|
{
|
|
bTriggeronce = false;
|
|
}
|
|
|
|
// override to make a trigger loop
|
|
if( isdefined( trigger.script_noteworthy ) && trigger.script_noteworthy == "trigger_multiple" )
|
|
{
|
|
bTriggeronce = false;
|
|
}
|
|
|
|
trigger.processed_trigger = undefined; // clear out this flag that was used to get the trigger to this point.
|
|
|
|
gates = setup_script_gatetrigger( trigger );
|
|
|
|
// origin paths and script struct paths get this value
|
|
script_vehicledetour = isdefined( trigger.script_vehicledetour ) && ( is_node_script_origin( trigger ) || is_node_script_struct( trigger ) ) ;
|
|
|
|
// ground paths get this value
|
|
detoured = isdefined( trigger.detoured ) && !( is_node_script_origin( trigger ) || is_node_script_struct( trigger ) );
|
|
gotrigger = true;
|
|
|
|
while( gotrigger )
|
|
{
|
|
trigger trigger::wait_till();
|
|
other = trigger.who;
|
|
|
|
// bbarnes - starting to trim out things we probably don't need.
|
|
// if ( isdefined( trigger.script_vehicletriggergroup ) )
|
|
// {
|
|
// if( !isdefined( other.script_vehicletriggergroup ) )
|
|
// {
|
|
// continue;
|
|
// }
|
|
//
|
|
// if( isdefined(other) && other.script_vehicletriggergroup != trigger.script_vehicletriggergroup )
|
|
// {
|
|
// continue;
|
|
// }
|
|
// }
|
|
|
|
if( isdefined( trigger.enabled ) && !trigger.enabled )
|
|
{
|
|
trigger waittill( "enable" );
|
|
}
|
|
|
|
if ( isdefined( trigger.script_flag_set ) )
|
|
{
|
|
if ( isdefined(other) && isdefined( other.vehicle_flags ) )
|
|
{
|
|
other.vehicle_flags[ trigger.script_flag_set ] = true;
|
|
}
|
|
|
|
if ( isdefined(other) )
|
|
{
|
|
other notify( "vehicle_flag_arrived", trigger.script_flag_set );
|
|
}
|
|
|
|
level flag::set( trigger.script_flag_set );
|
|
}
|
|
|
|
if ( isdefined( trigger.script_flag_clear ) )
|
|
{
|
|
if ( isdefined(other) && isdefined( other.vehicle_flags ) )
|
|
{
|
|
other.vehicle_flags[ trigger.script_flag_clear ] = false;
|
|
}
|
|
|
|
level flag::clear( trigger.script_flag_clear );
|
|
}
|
|
|
|
if( isdefined(other) && script_vehicledetour )
|
|
{
|
|
other thread path_detour_script_origin( trigger );
|
|
}
|
|
else if ( detoured && isdefined( other ) )
|
|
{
|
|
other thread path_detour( trigger );
|
|
}
|
|
|
|
trigger util::script_delay();
|
|
|
|
if( bTriggeronce )
|
|
{
|
|
gotrigger = false;
|
|
}
|
|
|
|
if ( isdefined( trigger.script_vehicleGroupDelete ) )
|
|
{
|
|
if( !isdefined( level.vehicle_DeleteGroup[ trigger.script_vehicleGroupDelete ] ) )
|
|
{
|
|
/#println( "failed to find deleteable vehicle with script_vehicleGroupDelete group number: ", trigger.script_vehicleGroupDelete );#/
|
|
level.vehicle_DeleteGroup[ trigger.script_vehicleGroupDelete ] = [];
|
|
}
|
|
array::delete_all( level.vehicle_DeleteGroup[ trigger.script_vehicleGroupDelete ] );
|
|
}
|
|
|
|
if( isdefined( trigger.script_vehiclespawngroup ) )
|
|
{
|
|
level notify( "spawnvehiclegroup" + trigger.script_vehiclespawngroup );
|
|
level waittill( "vehiclegroup spawned" + trigger.script_vehiclespawngroup );
|
|
}
|
|
|
|
if ( gates.size > 0 && bTriggeronce )
|
|
{
|
|
level array::thread_all_ents( gates,&path_gate_open );
|
|
}
|
|
|
|
if ( isdefined( trigger ) && isdefined( trigger.script_VehicleStartMove ) )
|
|
{
|
|
if ( !isdefined( level.vehicle_StartMoveGroup[ trigger.script_VehicleStartMove ] ) )
|
|
{
|
|
/#println( "^3Vehicle start trigger is: ", trigger.script_VehicleStartMove );#/
|
|
return;
|
|
}
|
|
|
|
foreach ( vehicle in ArrayCopy( level.vehicle_StartMoveGroup[ trigger.script_VehicleStartMove ] ) )
|
|
{
|
|
if ( isdefined( vehicle ) )
|
|
{
|
|
vehicle thread go_path();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function path_detour_get_detourpath( detournode )
|
|
{
|
|
detourpath = undefined;
|
|
for( j = 0; j < level.vehicle_detourpaths[ detournode.script_vehicledetour ].size; j++ )
|
|
{
|
|
if( level.vehicle_detourpaths[ detournode.script_vehicledetour ][ j ] != detournode )
|
|
{
|
|
if( !islastnode( level.vehicle_detourpaths[ detournode.script_vehicledetour ][ j ] ) )
|
|
{
|
|
detourpath = level.vehicle_detourpaths[ detournode.script_vehicledetour ][ j ];
|
|
}
|
|
}
|
|
}
|
|
|
|
return detourpath;
|
|
}
|
|
|
|
function path_detour_script_origin( detournode )
|
|
{
|
|
detourpath = path_detour_get_detourpath( detournode );
|
|
if( isdefined( detourpath ) )
|
|
{
|
|
self thread paths( detourpath );
|
|
}
|
|
}
|
|
|
|
function crash_detour_check( detourpath )
|
|
{
|
|
// long somewhat complex set of conditions on which a vehicle will detour through a crashpath.
|
|
return
|
|
(
|
|
isdefined( detourpath.script_crashtype )
|
|
&&
|
|
(
|
|
isdefined( self.deaddriver )
|
|
|| self.health <= 0
|
|
|| detourpath.script_crashtype == "forced"
|
|
)
|
|
&&
|
|
(
|
|
!isdefined( detourpath.derailed )
|
|
|| ( isdefined( detourpath.script_crashtype ) && detourpath.script_crashtype == "plane" )
|
|
)
|
|
);
|
|
}
|
|
|
|
function crash_derailed_check( detourpath )
|
|
{
|
|
return isdefined( detourpath.derailed ) && detourpath.derailed;
|
|
}
|
|
|
|
function path_detour( node )
|
|
{
|
|
detournode = getvehiclenode( node.target, "targetname" );
|
|
detourpath = path_detour_get_detourpath( detournode );
|
|
|
|
// be more aggressive with this maybe?
|
|
if( !isdefined( detourpath ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( node.detoured && !isdefined( detourpath.script_vehicledetourgroup ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( crash_detour_check( detourpath ) )
|
|
{
|
|
self notify( "crashpath", detourpath );
|
|
detourpath.derailed = 1;
|
|
self notify( "newpath" );
|
|
self setSwitchNode( node, detourpath );
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
if( crash_derailed_check( detourpath ) )
|
|
{
|
|
return; // .derailed crashpaths fail crash check. this keeps other vehicles from following.
|
|
}
|
|
|
|
// detour paths specific to grouped vehicles. So they can share a lane and detour when they need to be exciting.
|
|
if( isdefined( detourpath.script_vehicledetourgroup ) )
|
|
{
|
|
if( !isdefined( self.script_vehicledetourgroup ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( detourpath.script_vehicledetourgroup != self.script_vehicledetourgroup )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
//PARAMETER CLEANUP
|
|
function levelstuff( vehicle/*, trigger*/ )
|
|
{
|
|
// associate with links
|
|
if( isdefined( vehicle.script_linkname ) )
|
|
{
|
|
level.vehicle_link = array_2d_add( level.vehicle_link, vehicle.script_linkname, vehicle );
|
|
}
|
|
|
|
if( isdefined( vehicle.script_VehicleSpawngroup ) )
|
|
{
|
|
level.vehicle_SpawnGroup = array_2d_add( level.vehicle_SpawnGroup, vehicle.script_VehicleSpawngroup, vehicle );
|
|
}
|
|
|
|
if( isdefined( vehicle.script_VehicleStartMove ) )
|
|
{
|
|
level.vehicle_StartMoveGroup = array_2d_add( level.vehicle_StartMoveGroup, vehicle.script_VehicleStartMove, vehicle );
|
|
}
|
|
|
|
if( isdefined( vehicle.script_vehicleGroupDelete ) )
|
|
{
|
|
level.vehicle_DeleteGroup = array_2d_add( level.vehicle_DeleteGroup, vehicle.script_vehicleGroupDelete, vehicle );
|
|
}
|
|
}
|
|
|
|
function _spawn_array( spawners )
|
|
{
|
|
ai = _remove_non_riders_from_array( spawner::simple_spawn( spawners ) );
|
|
return ai;
|
|
}
|
|
|
|
function _remove_non_riders_from_array( ai )
|
|
{
|
|
living_ai = [];
|
|
for( i = 0; i < ai.size; i++ )
|
|
{
|
|
if ( !ai_should_be_added( ai[ i ] ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
living_ai[ living_ai.size ] = ai[ i ];
|
|
}
|
|
return living_ai;
|
|
}
|
|
|
|
function ai_should_be_added( ai )
|
|
{
|
|
if( isalive( ai ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if ( !isdefined( ai ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !isdefined( ai.classname ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return ai.classname == "script_model";
|
|
}
|
|
|
|
function sort_by_startingpos( guysarray )
|
|
{
|
|
firstarray = [];
|
|
secondarray = [];
|
|
|
|
for ( i = 0 ; i < guysarray.size ; i++ )
|
|
{
|
|
if ( isdefined( guysarray[ i ].script_startingposition ) )
|
|
{
|
|
firstarray[ firstarray.size ] = guysarray[ i ];
|
|
}
|
|
else
|
|
{
|
|
secondarray[ secondarray.size ] = guysarray[ i ];
|
|
}
|
|
}
|
|
|
|
return ArrayCombine( firstarray, secondarray, true, false );
|
|
}
|
|
|
|
function rider_walk_setup( vehicle )
|
|
{
|
|
if ( !isdefined( self.script_vehiclewalk ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( isdefined( self.script_followmode ) )
|
|
{
|
|
self.FollowMode = self.script_followmode;
|
|
}
|
|
else
|
|
{
|
|
self.FollowMode = "cover nodes";
|
|
}
|
|
|
|
// check if the AI should go to a node after walking with the vehicle
|
|
if ( !isdefined( self.target ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
node = getnode( self.target, "targetname" );
|
|
if( isdefined( node ) )
|
|
{
|
|
self.NodeAftervehicleWalk = node;
|
|
}
|
|
}
|
|
|
|
function setup_groundnode_detour( node )
|
|
{
|
|
realdetournode = getvehiclenode( node.targetname, "target" );
|
|
if( !isdefined( realdetournode ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
realdetournode.detoured = 0;
|
|
add_proccess_trigger( realdetournode );
|
|
}
|
|
|
|
function add_proccess_trigger( trigger )
|
|
{
|
|
// TODO: next game. stop trying to make everything a trigger. remove trigger process. I'd do it this game but there is too much complexity in Detour nodes.
|
|
// .processedtrigger is a flag that I set to keep a trigger from getting added twice.
|
|
if( isdefined( trigger.processed_trigger ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( !isdefined( level.vehicle_processtriggers ) ) level.vehicle_processtriggers = []; else if ( !IsArray( level.vehicle_processtriggers ) ) level.vehicle_processtriggers = array( level.vehicle_processtriggers ); level.vehicle_processtriggers[level.vehicle_processtriggers.size]=trigger;;
|
|
trigger.processed_trigger = true;
|
|
}
|
|
|
|
function islastnode( node )
|
|
{
|
|
if( !isdefined( node.target ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( !isdefined( getvehiclenode( node.target, "targetname" ) ) && !isdefined( get_vehiclenode_any_dynamic( node.target ) ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
function paths( node )
|
|
{
|
|
self endon( "death" );
|
|
|
|
assert( isdefined( node ) || isdefined( self.attachedpath ), "vehicle_path() called without a path" );
|
|
self notify( "newpath" );
|
|
|
|
// dynamicpaths unique. node isn't defined by info vehicle node calls to this function
|
|
if( isdefined( node ) )
|
|
{
|
|
self.attachedpath = node;
|
|
}
|
|
|
|
pathstart = self.attachedpath;
|
|
self.currentNode = self.attachedpath;
|
|
|
|
if ( !isdefined( pathstart ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
/#
|
|
self thread debug_vehicle_paths();
|
|
#/
|
|
|
|
self endon( "newpath" );
|
|
|
|
currentPoint = pathstart;
|
|
|
|
while ( isdefined( currentPoint ) )
|
|
{
|
|
self waittill( "reached_node", currentPoint );
|
|
|
|
currentPoint enable_turrets( self );
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.currentNode = currentPoint;
|
|
self.nextNode = ( isdefined( currentPoint.target ) ? GetVehicleNode( currentPoint.target, "targetname" ) : undefined );
|
|
|
|
if ( isdefined( currentPoint.gateopen ) && !currentPoint.gateopen )
|
|
{
|
|
// threaded because vehicle may setspeed( 0, 15 ) and run into the next node
|
|
self thread path_gate_wait_till_open( currentPoint );
|
|
}
|
|
|
|
currentPoint notify( "trigger", self );
|
|
|
|
// SRS 05/03/07: added for _planeweapons to drop bombs
|
|
// amount, delay, delay trace
|
|
if( isdefined( currentPoint.script_dropbombs ) && currentPoint.script_dropbombs > 0 )
|
|
{
|
|
amount = currentPoint.script_dropbombs;
|
|
delay = 0;
|
|
delaytrace = 0;
|
|
|
|
if( isdefined( currentPoint.script_dropbombs_delay ) && currentPoint.script_dropbombs_delay > 0 )
|
|
{
|
|
delay = currentPoint.script_dropbombs_delay;
|
|
}
|
|
|
|
if( isdefined( currentPoint.script_dropbombs_delaytrace ) && currentPoint.script_dropbombs_delaytrace > 0 )
|
|
{
|
|
delaytrace = currentPoint.script_dropbombs_delaytrace;
|
|
}
|
|
|
|
self notify( "drop_bombs", amount, delay, delaytrace );
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_noteworthy ) )
|
|
{
|
|
self notify( currentPoint.script_noteworthy );
|
|
self notify( "noteworthy", currentPoint.script_noteworthy );
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_notify) )
|
|
{
|
|
self notify( currentPoint.script_notify );
|
|
level notify( currentPoint.script_notify );
|
|
}
|
|
|
|
waittillframeend; // this lets other scripts interupt
|
|
|
|
if ( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( ( isdefined( currentPoint.script_delete ) && currentPoint.script_delete ) )
|
|
{
|
|
if ( isdefined( self.riders ) && self.riders.size > 0 )
|
|
{
|
|
array::delete_all( self.riders );
|
|
}
|
|
|
|
self.delete_on_death = true; self notify( "death" ); if( !IsAlive( self ) ) self Delete();;
|
|
return;
|
|
}
|
|
|
|
if( isdefined( currentPoint.script_sound ) )
|
|
{
|
|
self playsound( currentPoint.script_sound );
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_noteworthy ) )
|
|
{
|
|
if ( currentPoint.script_noteworthy == "godon" )
|
|
{
|
|
self god_on();
|
|
}
|
|
else if ( currentPoint.script_noteworthy == "godoff" )
|
|
{
|
|
self god_off();
|
|
}
|
|
else if ( currentPoint.script_noteworthy == "drivepath" )
|
|
{
|
|
self DrivePath(); // this will auto tilt and stuff for helicopters
|
|
}
|
|
else if ( currentPoint.script_noteworthy == "lockpath" )
|
|
{
|
|
self StartPath(); // this will stop the auto tilting and lock the heli back on the spline
|
|
}
|
|
else if ( currentPoint.script_noteworthy == "brake" )
|
|
{
|
|
if ( self.isphysicsvehicle )
|
|
{
|
|
self SetBrake( true );
|
|
}
|
|
|
|
self SetSpeed( 0, 60, 60 );
|
|
}
|
|
else if ( currentPoint.script_noteworthy == "resumespeed" )
|
|
{
|
|
accel = 30;
|
|
if ( isdefined( currentPoint.script_float ) )
|
|
{
|
|
accel = currentPoint.script_float;
|
|
}
|
|
self ResumeSpeed( accel );
|
|
}
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_crashtypeoverride ) )
|
|
{
|
|
self.script_crashtypeoverride = currentPoint.script_crashtypeoverride;
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_badplace ) )
|
|
{
|
|
self.script_badplace = currentPoint.script_badplace;
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_team ) )
|
|
{
|
|
self.team = currentPoint.script_team;
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_turningdir ) )
|
|
{
|
|
self notify( "turning", currentPoint.script_turningdir );
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_deathroll ) )
|
|
{
|
|
if ( currentPoint.script_deathroll == 0 )
|
|
{
|
|
self thread vehicle_death::deathrolloff();
|
|
}
|
|
else
|
|
{
|
|
self thread vehicle_death::deathrollon();
|
|
}
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_exploder ) )
|
|
{
|
|
exploder::exploder( currentPoint.script_exploder );
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_flag_set ) )
|
|
{
|
|
if ( isdefined( self.vehicle_flags ) )
|
|
{
|
|
self.vehicle_flags[ currentPoint.script_flag_set ] = true;
|
|
}
|
|
|
|
self notify( "vehicle_flag_arrived", currentPoint.script_flag_set );
|
|
level flag::set( currentPoint.script_flag_set );
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_flag_clear ) )
|
|
{
|
|
if ( isdefined( self.vehicle_flags ) )
|
|
{
|
|
self.vehicle_flags[ currentPoint.script_flag_clear ] = false;
|
|
}
|
|
|
|
level flag::clear( currentPoint.script_flag_clear );
|
|
}
|
|
|
|
if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) && isdefined( self.drivepath ) && self.drivepath == 1 )
|
|
{
|
|
if ( isdefined( self.nextNode ) && self.nextNode is_unload_node() )
|
|
{
|
|
unload_node_helicopter( undefined );
|
|
self.attachedpath = self.nextNode;
|
|
self DrivePath( self.attachedpath );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( currentPoint is_unload_node() )
|
|
{
|
|
unload_node( currentPoint );
|
|
}
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_wait ) )
|
|
{
|
|
pause_path();
|
|
currentPoint util::script_wait();
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_waittill ) )
|
|
{
|
|
pause_path();
|
|
util::waittill_any_ents( self, currentPoint.script_waittill, level, currentPoint.script_waittill );
|
|
}
|
|
|
|
if( isdefined( currentPoint.script_flag_wait ) )
|
|
{
|
|
if ( !isdefined( self.vehicle_flags ) )
|
|
{
|
|
self.vehicle_flags = [];
|
|
}
|
|
|
|
self.vehicle_flags[ currentPoint.script_flag_wait ] = true;
|
|
self notify( "vehicle_flag_arrived", currentPoint.script_flag_wait );
|
|
self flag::set( "waiting_for_flag" );
|
|
|
|
// helicopters stop on their own because they know to stop at destination for script_flag_wait
|
|
// may have to provide a smoother way to stop and go tho, this is rather arbitrary, for tanks
|
|
// in this case
|
|
|
|
if ( !level flag::get( currentPoint.script_flag_wait ) )
|
|
{
|
|
pause_path();
|
|
level flag::wait_till( currentPoint.script_flag_wait );
|
|
}
|
|
|
|
self flag::clear( "waiting_for_flag" );
|
|
}
|
|
|
|
if ( isdefined( self.set_lookat_point ) )
|
|
{
|
|
self.set_lookat_point = undefined;
|
|
self clearLookAtEnt();
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_lights_on ) )
|
|
{
|
|
if ( currentPoint.script_lights_on )
|
|
{
|
|
self lights_on();
|
|
}
|
|
else
|
|
{
|
|
self lights_off();
|
|
}
|
|
}
|
|
|
|
if ( isdefined( currentPoint.script_stopnode ) )
|
|
{
|
|
self set_goal_pos( currentPoint.origin, true );
|
|
}
|
|
|
|
if ( isdefined( self.switchNode ) )
|
|
{
|
|
if ( currentPoint == self.switchNode )
|
|
{
|
|
self.switchNode = undefined;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if ( !isdefined( currentPoint.target ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
resume_path();
|
|
}
|
|
|
|
self notify( "reached_dynamic_path_end" );
|
|
|
|
if ( isdefined( self.script_delete ) )
|
|
{
|
|
self Delete();
|
|
}
|
|
}
|
|
|
|
function pause_path()
|
|
{
|
|
if ( !( isdefined( self.vehicle_paused ) && self.vehicle_paused ) )
|
|
{
|
|
if ( self.isphysicsvehicle )
|
|
{
|
|
self SetBrake( true );
|
|
}
|
|
|
|
if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
|
|
{
|
|
if ( ( isdefined( self.drivepath ) && self.drivepath ) )
|
|
{
|
|
// hover
|
|
self SetVehGoalPos( self.origin, true );
|
|
}
|
|
else
|
|
{
|
|
self SetSpeed( 0, 100, 100 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self SetSpeed( 0, 35, 35 );
|
|
}
|
|
|
|
self.vehicle_paused = true;
|
|
}
|
|
}
|
|
|
|
function resume_path()
|
|
{
|
|
if ( ( isdefined( self.vehicle_paused ) && self.vehicle_paused ) )
|
|
{
|
|
if ( self.isphysicsvehicle )
|
|
{
|
|
self SetBrake( false );
|
|
}
|
|
|
|
if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
|
|
{
|
|
if ( ( isdefined( self.drivepath ) && self.drivepath ) )
|
|
{
|
|
// stop hovering and get back on path
|
|
self DrivePath( self.currentNode );
|
|
}
|
|
|
|
self ResumeSpeed( 100 );
|
|
}
|
|
else
|
|
{
|
|
self ResumeSpeed( 35 );
|
|
}
|
|
|
|
self.vehicle_paused = undefined;
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: get_on_path( <path_start>, [str_key] )"
|
|
"Module: Vehicle"
|
|
"CallOn: Vehicle"
|
|
"Summary: Will attach the vehicle to a path"
|
|
"MandatoryArg: <path_start> : the actual start node or string name of the node"
|
|
"OptionalArg: [str_key] if passing in a string, the key of the node to look for."
|
|
"Example: v_jeep vehicle::get_on_path( "escape_jeep_start" );"
|
|
@/
|
|
function get_on_path( path_start, str_key = "targetname" ) // self == vehicle
|
|
{
|
|
if( IsString( path_start ) )
|
|
{
|
|
path_start = GetVehicleNode( path_start, str_key );
|
|
}
|
|
|
|
if( !isdefined( path_start ) )
|
|
{
|
|
if( isdefined( self.targetname ) )
|
|
{
|
|
AssertMsg( "Start Node not defined for vehicle: " + self.targetname );
|
|
}
|
|
else
|
|
{
|
|
AssertMsg( "Start Node not defined for vehicle: " + self.targetname );
|
|
}
|
|
}
|
|
|
|
//[ceng 4/26/2010] If a vehicle previously used _utility::go_path() then .hasstarted
|
|
//would be set but never cleared, preventing the vehicle from using _utility::go_path()
|
|
//again. This allows the vehicle to follow more paths beyond their first.
|
|
if( isdefined( self.hasstarted ) )
|
|
{
|
|
self.hasstarted = undefined;
|
|
}
|
|
|
|
self.attachedpath = path_start;
|
|
|
|
if ( !( isdefined( self.drivepath ) && self.drivepath ) )
|
|
{
|
|
//self.origin = path_start.origin;
|
|
self AttachPath( path_start );
|
|
}
|
|
|
|
if( self.disconnectPathOnStop === true && !IsSentient(self) )
|
|
{
|
|
self vehicle::disconnect_paths( self.disconnectPathDetail );
|
|
}
|
|
|
|
if (( isdefined( self.isphysicsvehicle ) && self.isphysicsvehicle ))
|
|
{
|
|
self SetBrake(true);
|
|
}
|
|
|
|
self thread paths();
|
|
}
|
|
|
|
function get_off_path()
|
|
{
|
|
self CancelAIMove();
|
|
self ClearVehGoalPos();
|
|
}
|
|
|
|
|
|
/@
|
|
"Name: create_from_spawngroup_and_go_path( <spawnGroup> )"
|
|
"Summary: spawns and returns and array of the vehicles in the specified spawngroup starting them on their paths"
|
|
"Module: Vehicle"
|
|
"CallOn: An entity"
|
|
"MandatoryArg: <spawnGroup> : the script_vehiclespawngroup asigned to the vehicles in radiant"
|
|
"Example: vehicle::create_from_spawngroup_and_go_path( spawnGroup )"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
|
|
function create_from_spawngroup_and_go_path( spawnGroup )
|
|
{
|
|
vehicleArray = _scripted_spawn( spawnGroup );
|
|
for( i = 0; i < vehicleArray.size; i++ )
|
|
{
|
|
if (isdefined(vehicleArray[ i ]))
|
|
{
|
|
vehicleArray[ i ] thread vehicle::go_path();
|
|
}
|
|
}
|
|
return vehicleArray;
|
|
}
|
|
|
|
/@
|
|
"Name: get_on_and_go_path( <path_start> )"
|
|
"Module: Vehicle"
|
|
"CallOn: a vehicle"
|
|
"MandatoryArg: <path_start> : the node, script_origin, or struct to start from"
|
|
"Summary: Attach and start the vehicle on its path."
|
|
"Example: vehicle thread vehicle::get_on_and_go_path(path_start)"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function get_on_and_go_path(path_start) // self == vehicle
|
|
{
|
|
// get_on_path will attach us to the path and allow us to get script_noteworthy notifies from nodes
|
|
self vehicle::get_on_path(path_start);
|
|
|
|
// go_path starts us on the path
|
|
self vehicle::go_path();
|
|
}
|
|
|
|
// AE 5-15-09: cleaned up by taking out the vehicle parameter and just having the vehicle as self
|
|
function go_path() // self == vehicle
|
|
{
|
|
self endon( "death" );
|
|
self endon( "stop path" );
|
|
|
|
if( self.isphysicsvehicle )
|
|
{
|
|
self SetBrake(false);
|
|
}
|
|
|
|
if( isdefined( self.script_vehiclestartmove ) )
|
|
{
|
|
ArrayRemoveValue( level.vehicle_StartMoveGroup[ self.script_vehiclestartmove ], self );
|
|
}
|
|
|
|
if( isdefined( self.hasstarted ) )
|
|
{
|
|
/#println( "vehicle already moving when triggered with a startmove" );#/
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
self.hasstarted = true;
|
|
}
|
|
|
|
self util::script_delay();
|
|
|
|
self notify( "start_vehiclepath" );
|
|
|
|
if ( ( isdefined( self.drivepath ) && self.drivepath ) )
|
|
{
|
|
self DrivePath( self.attachedpath );
|
|
}
|
|
else
|
|
{
|
|
self StartPath();
|
|
}
|
|
|
|
// start waiting for the end of the path
|
|
wait .05;
|
|
self vehicle::connect_paths();
|
|
|
|
self waittill( "reached_end_node" );
|
|
|
|
if ( self.disconnectPathOnStop === true && !IsSentient( self ) )
|
|
{
|
|
self vehicle::disconnect_paths( self.disconnectPathDetail );
|
|
}
|
|
|
|
if( isdefined( self.currentnode ) && isdefined( self.currentnode.script_noteworthy ) && self.currentnode.script_noteworthy == "deleteme" )
|
|
{
|
|
return;
|
|
}
|
|
}
|
|
|
|
function path_gate_open( node )
|
|
{
|
|
node.gateopen = true;
|
|
node notify( "gate opened" );
|
|
}
|
|
|
|
function path_gate_wait_till_open( pathspot )
|
|
{
|
|
self endon( "death" );
|
|
self.waitingforgate = true;
|
|
self set_speed( 0, 15, "path gate closed" );
|
|
pathspot waittill( "gate opened" );
|
|
self.waitingforgate = false;
|
|
|
|
if( self.health > 0 )
|
|
{
|
|
script_resume_speed( "gate opened", level.vehicle_ResumeSpeed );
|
|
}
|
|
}
|
|
|
|
function _spawn_group( spawngroup )
|
|
{
|
|
while( 1 )
|
|
{
|
|
// waittill we get a notify for our group
|
|
level waittill( "spawnvehiclegroup" + spawngroup );
|
|
|
|
spawned_vehicles = [];
|
|
|
|
// for all spawners in the group
|
|
for( i = 0; i < level.vehicle_spawners[ spawngroup ].size; i++ )
|
|
{
|
|
// spawn the vehicle
|
|
spawned_vehicles[ spawned_vehicles.size ] = _vehicle_spawn( level.vehicle_spawners[ spawngroup ][ i ] );
|
|
}
|
|
|
|
// notify we spawned and pass back spawned vehicles
|
|
level notify( "vehiclegroup spawned" + spawngroup, spawned_vehicles );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: _scripted_spawn( <group> )"
|
|
"Summary: spawns and returns a vehiclegroup, you will need to tell it to vehicle::go_path() when you want it to go"
|
|
"Module: Vehicle"
|
|
"CallOn: An entity"
|
|
"MandatoryArg: <group> : "
|
|
"Example: bmps = _scripted_spawn( 32 );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
|
|
function _scripted_spawn( group )
|
|
{
|
|
thread _scripted_spawn_go( group );
|
|
level waittill( "vehiclegroup spawned" + group, vehicles );
|
|
return vehicles;
|
|
}
|
|
|
|
function _scripted_spawn_go( group )
|
|
{
|
|
waittillframeend;
|
|
level notify( "spawnvehiclegroup" + group );
|
|
}
|
|
|
|
function set_variables( vehicle )
|
|
{
|
|
if ( isdefined( vehicle.script_deathflag ) )
|
|
{
|
|
if ( !level flag::exists( vehicle.script_deathflag ) )
|
|
{
|
|
level flag::init( vehicle.script_deathflag );
|
|
}
|
|
}
|
|
}
|
|
|
|
//TODO T7 - set up a think function and send out a notify once we get a callback so vehicle spawning is similar to AI spawning
|
|
function _vehicle_spawn( vspawner, from )
|
|
{
|
|
if( !isdefined( vspawner ) || !vspawner.count )
|
|
{
|
|
return;
|
|
}
|
|
|
|
str_targetname = undefined;
|
|
if( isdefined( vspawner.targetname ) )
|
|
{
|
|
str_targetname = vspawner.targetname + "_vh";
|
|
}
|
|
|
|
spawner::global_spawn_throttle( 1 );
|
|
|
|
if( !isdefined( vspawner ) || !vspawner.count )
|
|
{
|
|
return;
|
|
}
|
|
|
|
vehicle = vspawner SpawnFromSpawner( str_targetname, true );
|
|
|
|
//failed to spawn a vehicle.
|
|
if ( !isdefined( vehicle ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
//TODO T7 - bring over _destructible to CP
|
|
/*if( isdefined( vehicle.destructibledef ) )
|
|
{
|
|
vehicle thread destructible::destructible_think();
|
|
}*/
|
|
|
|
if ( isdefined( vspawner.script_team ) )
|
|
{
|
|
vehicle SetTeam( vspawner.script_team );
|
|
}
|
|
|
|
if ( isdefined( vehicle.lockheliheight ) )
|
|
{
|
|
vehicle SetHeliHeightLock( vehicle.lockheliheight );
|
|
}
|
|
|
|
if( isdefined( vehicle.targetname ) )
|
|
{
|
|
level notify( "new_vehicle_spawned" + vehicle.targetname, vehicle );
|
|
}
|
|
|
|
if( isdefined( vehicle.script_noteworthy ) )
|
|
{
|
|
level notify( "new_vehicle_spawned" + vehicle.script_noteworthy, vehicle );
|
|
}
|
|
|
|
if( isdefined(vehicle.script_animname))
|
|
{
|
|
vehicle.animname = vehicle.script_animname;
|
|
}
|
|
|
|
if ( isdefined( vehicle.script_animscripted ) )
|
|
{
|
|
vehicle.supportsAnimScripted = vehicle.script_animscripted;
|
|
}
|
|
|
|
return vehicle;
|
|
}
|
|
|
|
function init( vehicle )
|
|
{
|
|
callback::callback( #"on_vehicle_spawned" );
|
|
|
|
vehicle UseAnimTree( #animtree );
|
|
|
|
if ( isdefined( vehicle.e_dyn_path ) )
|
|
{
|
|
vehicle.e_dyn_path LinkTo( vehicle );
|
|
}
|
|
|
|
vehicle flag::init("waiting_for_flag");
|
|
vehicle.takedamage = !( isdefined( vehicle.script_godmode ) && vehicle.script_godmode );
|
|
|
|
vehicle.zerospeed = true;
|
|
|
|
if( !isdefined( vehicle.modeldummyon ) )
|
|
{
|
|
vehicle.modeldummyon = false;
|
|
}
|
|
|
|
if ( ( isdefined( vehicle.isphysicsvehicle ) && vehicle.isphysicsvehicle ) )
|
|
{
|
|
if ( ( isdefined( vehicle.script_brake ) && vehicle.script_brake ) )
|
|
{
|
|
vehicle SetBrake( true );
|
|
}
|
|
}
|
|
|
|
type = vehicle.vehicletype;
|
|
|
|
// give the vehicle health
|
|
vehicle _vehicle_life();
|
|
|
|
vehicle thread maingun_fx();
|
|
|
|
// getoutrig means fastrope.
|
|
vehicle.getoutrig = [];
|
|
if( isdefined( level.vehicle_attachedmodels ) && isdefined( level.vehicle_attachedmodels[ type ] ) )
|
|
{
|
|
rigs = level.vehicle_attachedmodels[ type ];
|
|
strings = getarraykeys( rigs );
|
|
for( i = 0; i < strings.size; i++ )
|
|
{
|
|
vehicle.getoutrig[ strings[ i ] ] = undefined;
|
|
vehicle.getoutriganimating[ strings[ i ] ] = false;
|
|
}
|
|
}
|
|
|
|
// make ai run way from vehicle
|
|
if( isdefined( self.script_badplace ) )
|
|
{
|
|
vehicle thread _vehicle_bad_place();
|
|
}
|
|
|
|
if ( isdefined( vehicle.scriptbundlesettings ) )
|
|
{
|
|
settings = struct::get_script_bundle( "vehiclecustomsettings", vehicle.scriptbundlesettings );
|
|
|
|
if ( isdefined( settings ) && isdefined( settings.lightgroups_numGroups ) )
|
|
{
|
|
if ( settings.lightgroups_numGroups >= 1 && settings.lightgroups_1_always_on === true )
|
|
{
|
|
vehicle toggle_lights_group( 1, true );
|
|
}
|
|
if ( settings.lightgroups_numGroups >= 2 && settings.lightgroups_2_always_on === true )
|
|
{
|
|
vehicle toggle_lights_group( 2, true );
|
|
}
|
|
if ( settings.lightgroups_numGroups >= 3 && settings.lightgroups_3_always_on === true )
|
|
{
|
|
vehicle toggle_lights_group( 3, true );
|
|
}
|
|
if ( settings.lightgroups_numGroups >= 4 && settings.lightgroups_4_always_on === true )
|
|
{
|
|
vehicle toggle_lights_group( 4, true );
|
|
}
|
|
}
|
|
}
|
|
|
|
// regenerate friendly fire damage
|
|
if( !vehicle is_cheap() )
|
|
{
|
|
vehicle friendly_fire_shield();
|
|
}
|
|
|
|
// handles guys riding and doing stuff on vehicles
|
|
// vehicle thread vehicle_aianim::handle_attached_guys();
|
|
|
|
// make vehicle shake physics objects.
|
|
if( isdefined( vehicle.script_physicsjolt ) && vehicle.script_physicsjolt )
|
|
{
|
|
//T7 - Request from code to get this ability back if needed
|
|
}
|
|
|
|
// associate vehicle with living level variables.
|
|
levelstuff( vehicle );
|
|
|
|
if ( ( isdefined(vehicle.vehicleclass) && ( vehicle.vehicleclass == "artillery" ) ) )
|
|
{
|
|
vehicle.disconnectPathOnStop = undefined;
|
|
self vehicle::disconnect_paths( 0 );
|
|
}
|
|
else
|
|
{
|
|
vehicle.disconnectPathOnStop = self.script_disconnectpaths;
|
|
}
|
|
|
|
vehicle.disconnectPathDetail = self.script_disconnectpath_detail;
|
|
if ( !isdefined( vehicle.disconnectPathDetail ) )
|
|
{
|
|
vehicle.disconnectPathDetail = 0;
|
|
}
|
|
|
|
// every vehicle that stops will disconnect its paths
|
|
if( !vehicle is_cheap() && !( isdefined(vehicle.vehicleclass) && (vehicle.vehicleclass == "plane") ) && !( isdefined(vehicle.vehicleclass) && ( vehicle.vehicleclass == "artillery" ) ) )
|
|
{
|
|
vehicle thread _disconnect_paths_when_stopped();
|
|
}
|
|
|
|
if ( !isdefined( vehicle.script_nonmovingvehicle ) )
|
|
{
|
|
if ( isdefined( vehicle.target ) )
|
|
{
|
|
path_start = GetVehicleNode( vehicle.target, "targetname" );
|
|
if ( !isdefined( path_start ) )
|
|
{
|
|
path_start = GetEnt(vehicle.target, "targetname" );
|
|
if ( !isdefined( path_start ) )
|
|
{
|
|
path_start = struct::get( vehicle.target, "targetname" );
|
|
}
|
|
}
|
|
}
|
|
|
|
if ( isdefined( path_start ) && vehicle.vehicletype != "inc_base_jump_spotlight" )
|
|
{
|
|
vehicle thread get_on_path( path_start );
|
|
}
|
|
}
|
|
|
|
if ( isdefined( vehicle.script_vehicleattackgroup ) )
|
|
{
|
|
vehicle thread attack_group_think();
|
|
}
|
|
|
|
/#
|
|
if( ( isdefined( vehicle.script_recordent ) && vehicle.script_recordent ) )
|
|
{
|
|
RecordEnt( vehicle );
|
|
}
|
|
#/
|
|
|
|
// helicopters do dust kickup fx
|
|
if( vehicle has_helicopter_dust_kickup() )
|
|
{
|
|
if(!level.clientscripts)
|
|
{
|
|
vehicle thread aircraft_dust_kickup();
|
|
}
|
|
}
|
|
|
|
/#
|
|
// MikeD (8/1/2007): Debug info for vehicles
|
|
vehicle thread debug_vehicle();
|
|
#/
|
|
|
|
// spawn the vehicle and it's associated ai
|
|
//vehicle spawn_ai_group();
|
|
vehicle thread vehicle_death::main();
|
|
|
|
// Set myself as a target if specificed
|
|
if ( isdefined( vehicle.script_targetset ) && vehicle.script_targetset == 1 )
|
|
{
|
|
offset = ( 0, 0, 0 );
|
|
if ( isdefined( vehicle.script_targetoffset ) )
|
|
{
|
|
offset = vehicle.script_targetoffset;
|
|
}
|
|
|
|
Target_Set( vehicle, offset );
|
|
}
|
|
|
|
if ( ( isdefined( vehicle.script_vehicleavoidance ) && vehicle.script_vehicleavoidance ) )
|
|
{
|
|
vehicle SetVehicleAvoidance( true );
|
|
}
|
|
|
|
vehicle enable_turrets();
|
|
|
|
if( isdefined( level.vehicleSpawnCallbackThread ) )
|
|
{
|
|
level thread [[ level.vehicleSpawnCallbackThread ]]( vehicle );
|
|
}
|
|
}
|
|
|
|
function detach_getoutrigs()
|
|
{
|
|
if ( !isdefined( self.getoutrig ) )
|
|
return;
|
|
if ( ! self.getoutrig.size )
|
|
return;
|
|
keys = GetArrayKeys( self.getoutrig );
|
|
for ( i = 0; i < keys.size; i++ )
|
|
{
|
|
self.getoutrig[ keys[ i ] ] Unlink();
|
|
}
|
|
}
|
|
|
|
function enable_turrets( veh )
|
|
{
|
|
if ( !isdefined( veh ) )
|
|
{
|
|
veh = self;
|
|
}
|
|
|
|
if ( ( isdefined( self.script_enable_turret0 ) && self.script_enable_turret0 ) )
|
|
{
|
|
veh turret::enable( 0 );
|
|
}
|
|
|
|
if ( ( isdefined( self.script_enable_turret1 ) && self.script_enable_turret1 ) )
|
|
{
|
|
veh turret::enable( 1 );
|
|
}
|
|
|
|
if ( ( isdefined( self.script_enable_turret2 ) && self.script_enable_turret2 ) )
|
|
{
|
|
veh turret::enable( 2 );
|
|
}
|
|
|
|
if ( ( isdefined( self.script_enable_turret3 ) && self.script_enable_turret3 ) )
|
|
{
|
|
veh turret::enable( 3 );
|
|
}
|
|
|
|
if ( ( isdefined( self.script_enable_turret4 ) && self.script_enable_turret4 ) )
|
|
{
|
|
veh turret::enable( 4 );
|
|
}
|
|
|
|
if ( isdefined( self.script_enable_turret0 ) && !self.script_enable_turret0 )
|
|
{
|
|
veh turret::disable( 0 );
|
|
}
|
|
|
|
if ( isdefined( self.script_enable_turret1 ) && !self.script_enable_turret1 )
|
|
{
|
|
veh turret::disable( 1 );
|
|
}
|
|
|
|
if ( isdefined( self.script_enable_turret2 ) && !self.script_enable_turret2 )
|
|
{
|
|
veh turret::disable( 2 );
|
|
}
|
|
|
|
if ( isdefined( self.script_enable_turret3 ) && !self.script_enable_turret3 )
|
|
{
|
|
veh turret::disable( 3 );
|
|
}
|
|
|
|
if ( isdefined( self.script_enable_turret4 ) && !self.script_enable_turret4 )
|
|
{
|
|
veh turret::disable( 4 );
|
|
}
|
|
}
|
|
|
|
// self == vehicle
|
|
function enable_auto_disconnect_path()
|
|
{
|
|
self notify( "kill_disconnect_paths_forever" );
|
|
self.disconnectPathOnStop = false;
|
|
self thread _disconnect_paths_when_stopped();
|
|
}
|
|
|
|
function _disconnect_paths_when_stopped()
|
|
{
|
|
if( IsPathfinder( self) )
|
|
{
|
|
self.disconnectPathOnStop = false;// lets other parts of the script know not to disconnect script
|
|
return;
|
|
}
|
|
|
|
if ( isdefined( self.script_disconnectpaths ) && !self.script_disconnectpaths )
|
|
{
|
|
self.disconnectPathOnStop = false;// lets other parts of the script know not to disconnect script
|
|
return;
|
|
}
|
|
|
|
self endon( "death" );
|
|
self endon( "kill_disconnect_paths_forever" );
|
|
|
|
wait 1;
|
|
threshold = 3;
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
if ( LengthSquared( self.velocity ) < ( (threshold) * (threshold) ) )
|
|
{
|
|
if ( self.disconnectPathOnStop === true )
|
|
{
|
|
self vehicle::disconnect_paths( self.disconnectPathDetail );
|
|
self notify( "speed_zero_path_disconnect" );
|
|
}
|
|
|
|
while( LengthSquared( self.velocity ) < ( (threshold) * (threshold) ) )
|
|
{
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
self vehicle::connect_paths();
|
|
|
|
while( LengthSquared( self.velocity ) >= ( (threshold) * (threshold) ) )
|
|
{
|
|
wait 0.05;
|
|
}
|
|
}
|
|
}
|
|
|
|
//function disconnect_paths_while_moving( interval )
|
|
//{
|
|
// if( IsSentient( self) )
|
|
// {
|
|
// return;
|
|
// }
|
|
//
|
|
// if ( isdefined( self.script_disconnectpaths ) && !self.script_disconnectpaths )
|
|
// {
|
|
// self.disconnectPathOnStop = true;// lets other parts of the script know not to disconnect script
|
|
// return;
|
|
// }
|
|
//
|
|
// self endon( "death" );
|
|
// self endon( "kill_disconnect_paths_forever" );
|
|
//
|
|
// while ( isdefined( self ) )
|
|
// {
|
|
// if ( Length( self.velocity ) > 1 )
|
|
// {
|
|
// if ( !isdefined( self.disconnectPathOnStop ) )
|
|
// {
|
|
// //self vehicle::connect_paths();
|
|
// self vehicle::disconnect_paths();
|
|
// }
|
|
//
|
|
// self notify( "moving_path_disconnect" );
|
|
// }
|
|
//
|
|
// wait interval;
|
|
// }
|
|
//}
|
|
|
|
function set_speed( speed, rate, msg )
|
|
{
|
|
if( self getspeedmph() == 0 && speed == 0 )
|
|
{
|
|
return; // potential for disaster? keeps messages from overriding previous messages
|
|
}
|
|
|
|
/#
|
|
self thread debug_set_speed( speed, rate, msg );
|
|
#/
|
|
self setspeed( speed, rate );
|
|
}
|
|
|
|
function debug_set_speed( speed, rate, msg )
|
|
{
|
|
/#
|
|
self notify( "new debug_vehiclesetspeed" );
|
|
self endon( "new debug_vehiclesetspeed" );
|
|
self endon( "resuming speed" );
|
|
self endon( "death" );
|
|
while( 1 )
|
|
{
|
|
while( GetDvarString( "debug_vehiclesetspeed" ) != "off" )
|
|
{
|
|
print3d( self.origin + ( 0, 0, 192 ), "vehicle setspeed: " + msg, ( 1, 1, 1 ), 1, 3 );
|
|
wait .05;
|
|
}
|
|
wait .5;
|
|
}
|
|
#/
|
|
}
|
|
|
|
function script_resume_speed( msg, rate )
|
|
{
|
|
self endon( "death" );
|
|
fSetspeed = 0;
|
|
type = "resumespeed";
|
|
if( !isdefined( self.resumemsgs ) )
|
|
{
|
|
self.resumemsgs = [];
|
|
}
|
|
if( isdefined( self.waitingforgate ) && self.waitingforgate )
|
|
{
|
|
return; // ignore resumespeeds on waiting for gate.
|
|
}
|
|
|
|
if( isdefined( self.attacking ) && self.attacking )
|
|
{
|
|
fSetspeed = self.attackspeed;
|
|
type = "setspeed";
|
|
}
|
|
|
|
self.zerospeed = false;
|
|
if( fSetspeed == 0 )
|
|
{
|
|
self.zerospeed = true;
|
|
}
|
|
if( type == "resumespeed" )
|
|
{
|
|
self resumespeed( rate );
|
|
}
|
|
else if( type == "setspeed" )
|
|
{
|
|
self set_speed( fSetspeed, 15, "resume setspeed from attack" );
|
|
}
|
|
self notify( "resuming speed" );
|
|
/# self thread debug_resume( msg + " :" + type ); #/
|
|
|
|
}
|
|
|
|
/#
|
|
function debug_resume( msg )
|
|
{
|
|
if( GetDvarString( "debug_vehicleresume" ) == "off" )
|
|
{
|
|
return;
|
|
}
|
|
self endon( "death" );
|
|
number = self.resumemsgs.size;
|
|
self.resumemsgs[ number ] = msg;
|
|
const timer = 3;
|
|
self thread print_resume_speed( gettime() + ( timer * 1000 ) );
|
|
|
|
wait timer;
|
|
newarray = [];
|
|
for( i = 0; i < self.resumemsgs.size; i++ )
|
|
{
|
|
if( i != number )
|
|
{
|
|
newarray[ newarray.size ] = self.resumemsgs[ i ];
|
|
}
|
|
}
|
|
self.resumemsgs = newarray;
|
|
}
|
|
#/
|
|
|
|
function print_resume_speed( timer )
|
|
{
|
|
self notify( "newresumespeedmsag" );
|
|
self endon( "newresumespeedmsag" );
|
|
self endon( "death" );
|
|
while( gettime() < timer && isdefined( self.resumemsgs ) )
|
|
{
|
|
if( self.resumemsgs.size > 6 )
|
|
{
|
|
start = self.resumemsgs.size - 5;
|
|
}
|
|
else
|
|
{
|
|
start = 0;
|
|
}
|
|
for( i = start; i < self.resumemsgs.size; i++ ) // only display last 5 messages
|
|
{
|
|
position = i * 32;
|
|
/#print3d( self.origin + ( 0, 0, position ), "resuming speed: " + self.resumemsgs[ i ], ( 0, 1, 0 ), 1, 3 );#/
|
|
}
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
function god_on()
|
|
{
|
|
self.takedamage = false;
|
|
}
|
|
|
|
function god_off()
|
|
{
|
|
self.takedamage = true;
|
|
}
|
|
|
|
function get_normal_anim_time( animation )
|
|
{
|
|
animtime = self getanimtime( animation );
|
|
animlength = getanimlength( animation );
|
|
if( animtime == 0 )
|
|
{
|
|
return 0;
|
|
}
|
|
return self getanimtime( animation ) / getanimlength( animation );
|
|
}
|
|
|
|
function setup_dynamic_detour( pathnode , get_func )
|
|
{
|
|
prevnode = [[ get_func ]]( pathnode.targetname );
|
|
assert( isdefined( prevnode ), "detour can't be on start node" );
|
|
prevnode.detoured = 0;
|
|
}
|
|
|
|
/*
|
|
function setup_origins()
|
|
{
|
|
triggers = [];
|
|
origins = getentarray( "script_origin", "classname" );
|
|
for( i = 0; i < origins.size; i++ )
|
|
{
|
|
if( isdefined( origins[ i ].script_vehicledetour ) )
|
|
{
|
|
|
|
level.vehicle_detourpaths = array_2d_add( level.vehicle_detourpaths, origins[ i ].script_vehicledetour, origins[ i ] );
|
|
if( level.vehicle_detourpaths[ origins[ i ].script_vehicledetour ].size > 2 )
|
|
println( "more than two script_vehicledetour grouped in group number: ", origins[ i ].script_vehicledetour );
|
|
|
|
prevnode = getent( origins[ i ].targetname, "target" );
|
|
assert( isdefined( prevnode ), "detour can't be on start node" );
|
|
triggers[ triggers.size ] = prevnode;
|
|
prevnode.detoured = 0;
|
|
prevnode = undefined;
|
|
}
|
|
}
|
|
return triggers;
|
|
}
|
|
*/
|
|
|
|
function array_2d_add( array, firstelem, newelem )
|
|
{
|
|
if( !isdefined( array[ firstelem ] ) )
|
|
{
|
|
array[ firstelem ] = [];
|
|
}
|
|
array[ firstelem ][ array[ firstelem ].size ] = newelem;
|
|
return array;
|
|
}
|
|
|
|
function is_node_script_origin( pathnode )
|
|
{
|
|
return isdefined( pathnode.classname ) && pathnode.classname == "script_origin";
|
|
}
|
|
|
|
// this determines if the node will be sent through trigger_process. The uber trigger function that may get phased out.
|
|
function node_trigger_process()
|
|
{
|
|
processtrigger = false;
|
|
|
|
// special treatment for start nodes
|
|
if ( (isdefined(self.spawnflags)&&((self.spawnflags & 1) == 1)) )
|
|
{
|
|
if( isdefined( self.script_crashtype ) )
|
|
{
|
|
level.vehicle_crashpaths[ level.vehicle_crashpaths.size ] = self;
|
|
}
|
|
|
|
level.vehicle_startnodes[ level.vehicle_startnodes.size ] = self;
|
|
}
|
|
|
|
if( isdefined( self.script_vehicledetour ) && isdefined( self.targetname ) )
|
|
{
|
|
get_func = undefined;
|
|
// get_func is differnt for struct types and script_origin types of paths
|
|
if( isdefined( get_from_entity( self.targetname ) ) )
|
|
{
|
|
get_func =&get_from_entity_target;
|
|
}
|
|
if( isdefined( get_from_spawnstruct( self.targetname ) ) )
|
|
{
|
|
get_func =&get_from_spawnstruct_target;
|
|
}
|
|
|
|
if( isdefined( get_func ) )
|
|
{
|
|
setup_dynamic_detour( self, get_func );
|
|
processtrigger = true; // the node with the script_vehicledetour waits for the trigger here unlike ground nodes which need to know 1 node in advanced that there's a detour, tricky tricky.
|
|
}
|
|
else
|
|
{
|
|
setup_groundnode_detour( self ); // other trickery. the node is set to process in there.
|
|
}
|
|
|
|
level.vehicle_detourpaths = array_2d_add( level.vehicle_detourpaths, self.script_vehicledetour, self );
|
|
/#
|
|
if( level.vehicle_detourpaths[ self.script_vehicledetour ].size > 2 )
|
|
{
|
|
println( "more than two script_vehicledetour grouped in group number: ", self.script_vehicledetour );
|
|
}
|
|
#/
|
|
}
|
|
|
|
// if a gate isn't open then the vehicle will stop there and wait for it to become open.
|
|
if( isdefined( self.script_gatetrigger ) )
|
|
{
|
|
level.vehicle_gatetrigger = array_2d_add( level.vehicle_gatetrigger, self.script_gatetrigger, self );
|
|
self.gateopen = false;
|
|
}
|
|
|
|
// init the flags!
|
|
if ( isdefined( self.script_flag_set ) )
|
|
{
|
|
if ( !isdefined(level.flag) || !isdefined( level.flag[ self.script_flag_set ] ) )
|
|
{
|
|
level flag::init( self.script_flag_set );
|
|
}
|
|
}
|
|
|
|
// init the flags!
|
|
if ( isdefined( self.script_flag_clear ) )
|
|
{
|
|
if ( !level flag::exists( self.script_flag_clear ) )
|
|
{
|
|
level flag::init( self.script_flag_clear );
|
|
}
|
|
}
|
|
|
|
if( isdefined( self.script_flag_wait ) )
|
|
{
|
|
if ( !level flag::exists( self.script_flag_wait ) )
|
|
{
|
|
level flag::init( self.script_flag_wait );
|
|
}
|
|
}
|
|
|
|
// various nodes that will be sent through trigger_process
|
|
if (isdefined( self.script_VehicleSpawngroup )
|
|
|| isdefined( self.script_VehicleStartMove )
|
|
|| isdefined( self.script_gatetrigger )
|
|
|| isdefined( self.script_Vehiclegroupdelete ))
|
|
{
|
|
processtrigger = true;
|
|
}
|
|
|
|
if( processtrigger )
|
|
{
|
|
add_proccess_trigger( self );
|
|
}
|
|
}
|
|
|
|
function setup_triggers()
|
|
{
|
|
// TODO: move this to _load under the triggers section. larger task than this simple cleanup.
|
|
|
|
// the processtriggers array is all the triggers and vehicle node triggers to be put through
|
|
// the trigger_process function. This is so that I only do a waittill trigger once
|
|
// in script to assure better sequencing on a multi - function trigger.
|
|
|
|
// some of the vehiclenodes don't need to waittill trigger on anything and are here only
|
|
// for being linked with other trigger
|
|
|
|
level.vehicle_processtriggers = [];
|
|
|
|
triggers = [];
|
|
triggers = ArrayCombine( getallvehiclenodes(), getentarray( "script_origin", "classname" ), true, false );
|
|
triggers = ArrayCombine( triggers, level.struct, true, false );
|
|
triggers = ArrayCombine( triggers, trigger::get_all(), true, false );
|
|
array::thread_all( triggers, &node_trigger_process );
|
|
|
|
}
|
|
|
|
function setup_nodes()
|
|
{
|
|
a_nodes = GetAllVehicleNodes();
|
|
foreach ( node in a_nodes )
|
|
{
|
|
if ( isdefined( node.script_flag_set ) )
|
|
{
|
|
if ( !level flag::exists( node.script_flag_set ) )
|
|
{
|
|
level flag::init( node.script_flag_set );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
function is_node_script_struct( node )
|
|
{
|
|
if ( !isdefined( node.targetname ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return isdefined( struct::get( node.targetname, "targetname" ) );
|
|
}
|
|
|
|
function setup_spawners( a_veh_spawners )
|
|
{
|
|
spawnvehicles = [];
|
|
groups = [];
|
|
|
|
foreach ( spawner in a_veh_spawners )
|
|
{
|
|
if ( isdefined( spawner.script_vehiclespawngroup ) )
|
|
{
|
|
if ( !isdefined( spawnvehicles[ spawner.script_vehiclespawngroup ] ) ) spawnvehicles[ spawner.script_vehiclespawngroup ] = []; else if ( !IsArray( spawnvehicles[ spawner.script_vehiclespawngroup ] ) ) spawnvehicles[ spawner.script_vehiclespawngroup ] = array( spawnvehicles[ spawner.script_vehiclespawngroup ] ); spawnvehicles[ spawner.script_vehiclespawngroup ][spawnvehicles[ spawner.script_vehiclespawngroup ].size]=spawner;;
|
|
|
|
addgroup[ 0 ] = spawner.script_vehiclespawngroup;
|
|
groups = ArrayCombine( groups, addgroup, false, false );
|
|
}
|
|
}
|
|
|
|
waittillframeend; //T7 - wait here and let all the vehicle inits run
|
|
|
|
foreach ( spawngroup in groups )
|
|
{
|
|
a_veh_spawners = spawnvehicles[ spawngroup ];
|
|
|
|
level.vehicle_spawners[ spawngroup ] = [];
|
|
|
|
foreach ( sp in a_veh_spawners )
|
|
{
|
|
if( sp.count < 1 )
|
|
{
|
|
sp.count = 1;
|
|
}
|
|
|
|
// sp vehicle_dynamic_cover();
|
|
set_variables( sp );
|
|
|
|
if ( !isdefined( level.vehicle_spawners[ spawngroup ] ) ) level.vehicle_spawners[ spawngroup ] = []; else if ( !IsArray( level.vehicle_spawners[ spawngroup ] ) ) level.vehicle_spawners[ spawngroup ] = array( level.vehicle_spawners[ spawngroup ] ); level.vehicle_spawners[ spawngroup ][level.vehicle_spawners[ spawngroup ].size]=sp;;
|
|
}
|
|
|
|
level thread _spawn_group( spawngroup );
|
|
}
|
|
}
|
|
|
|
function _vehicle_life()
|
|
{
|
|
if (isdefined(self.destructibledef))
|
|
{
|
|
self.health = 99999;
|
|
}
|
|
else
|
|
{
|
|
type = self.vehicletype;
|
|
|
|
if( isdefined( self.script_startinghealth ) )
|
|
{
|
|
self.health = self.script_startinghealth;
|
|
}
|
|
else
|
|
{
|
|
if( self.healthdefault == -1 )
|
|
{
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
self.health = self.healthdefault;
|
|
//println("set health: " + self.health);
|
|
}
|
|
}
|
|
|
|
// if( isdefined( level.destructible_model[ self.model ] ) )
|
|
// {
|
|
// self.health = 2000;
|
|
// self.destructible_type = level.destructible_model[ self.model ];
|
|
// self destructible::setup_destructibles( true );
|
|
// }
|
|
}
|
|
}
|
|
|
|
function _vehicle_load_assets()
|
|
{
|
|
|
|
}
|
|
|
|
|
|
function is_cheap()
|
|
{
|
|
if( !isdefined( self.script_cheap ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( !self.script_cheap )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
|
|
function has_helicopter_dust_kickup()
|
|
{
|
|
if( !( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( is_cheap() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function play_looped_fx_on_tag( effect, durration, tag )
|
|
{
|
|
eModel = get_dummy();
|
|
effectorigin = sys::spawn( "script_origin", eModel.origin );
|
|
|
|
self endon( "fire_extinguish" );
|
|
thread _play_looped_fx_on_tag_origin_update( tag, effectorigin );
|
|
while( 1 )
|
|
{
|
|
playfx( effect, effectorigin.origin, effectorigin.upvec );
|
|
wait durration;
|
|
}
|
|
}
|
|
|
|
function _play_looped_fx_on_tag_origin_update( tag, effectorigin )
|
|
{
|
|
effectorigin.angles = self gettagangles( tag );
|
|
effectorigin.origin = self gettagorigin( tag );
|
|
effectorigin.forwardvec = anglestoforward( effectorigin.angles );
|
|
effectorigin.upvec = anglestoup( effectorigin.angles );
|
|
while( isdefined( self ) && self.classname == "script_vehicle" && self getspeedmph() > 0 )
|
|
{
|
|
eModel = get_dummy();
|
|
effectorigin.angles = eModel gettagangles( tag );
|
|
effectorigin.origin = eModel gettagorigin( tag );
|
|
effectorigin.forwardvec = anglestoforward( effectorigin.angles );
|
|
effectorigin.upvec = anglestoup( effectorigin.angles );
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
function setup_dvars()
|
|
{
|
|
/#
|
|
if( GetDvarString( "debug_vehicleresume" ) == "" )
|
|
{
|
|
SetDvar( "debug_vehicleresume", "off" );
|
|
}
|
|
if( GetDvarString( "debug_vehiclesetspeed" ) == "" )
|
|
{
|
|
SetDvar( "debug_vehiclesetspeed", "off" );
|
|
}
|
|
#/
|
|
}
|
|
|
|
function setup_level_vars()
|
|
{
|
|
level.vehicle_ResumeSpeed = 5;
|
|
level.vehicle_DeleteGroup = [];
|
|
level.vehicle_SpawnGroup = [];
|
|
level.vehicle_StartMoveGroup = [];
|
|
level.vehicle_DeathSwitch = [];
|
|
level.vehicle_gatetrigger = [];
|
|
level.vehicle_crashpaths = [];
|
|
level.vehicle_link = [];
|
|
level.vehicle_detourpaths = [];
|
|
// level.vehicle_linkedpaths = [];
|
|
level.vehicle_startnodes = [];
|
|
level.vehicle_spawners = [];
|
|
level.a_vehicle_types = [];
|
|
level.a_vehicle_targetnames = [];
|
|
|
|
// AE 3-5-09: added this vehicle_walkercount so we can have ai walk with vehicles
|
|
level.vehicle_walkercount = [];
|
|
|
|
level.helicopter_crash_locations = getentarray( "helicopter_crash_location", "targetname" );
|
|
|
|
level.playervehicle = sys::Spawn( "script_origin", ( 0, 0, 0 ) ); // no isdefined for level.playervehicle
|
|
level.playervehiclenone = level.playervehicle; // no isdefined for level.playervehicle
|
|
|
|
if( !isdefined( level.vehicle_death_thread ) )
|
|
{
|
|
level.vehicle_death_thread = [];
|
|
}
|
|
if( !isdefined( level.vehicle_DriveIdle ) )
|
|
{
|
|
level.vehicle_DriveIdle = [];
|
|
}
|
|
if( !isdefined( level.vehicle_DriveIdle_r ) )
|
|
{
|
|
level.vehicle_DriveIdle_r = [];
|
|
}
|
|
if( !isdefined( level.attack_origin_condition_threadd ) )
|
|
{
|
|
level.attack_origin_condition_threadd = [];
|
|
}
|
|
if( !isdefined( level.vehiclefireanim ) )
|
|
{
|
|
level.vehiclefireanim = [];
|
|
}
|
|
if( !isdefined( level.vehiclefireanim_settle ) )
|
|
{
|
|
level.vehiclefireanim_settle = [];
|
|
}
|
|
if( !isdefined( level.vehicle_hasname ) )
|
|
{
|
|
level.vehicle_hasname = [];
|
|
}
|
|
if( !isdefined( level.vehicle_turret_requiresrider ) )
|
|
{
|
|
level.vehicle_turret_requiresrider = [];
|
|
}
|
|
if( !isdefined( level.vehicle_isStationary ) )
|
|
{
|
|
level.vehicle_isStationary = [];
|
|
}
|
|
if( !isdefined( level.vehicle_compassicon ) )
|
|
{
|
|
level.vehicle_compassicon = [];
|
|
}
|
|
if( !isdefined( level.vehicle_unloadgroups ) )
|
|
{
|
|
level.vehicle_unloadgroups = [];
|
|
}
|
|
// if( !isdefined( level.vehicle_aianims ) )
|
|
// {
|
|
// level.vehicle_aianims = [];
|
|
// }
|
|
if( !isdefined( level.vehicle_unloadwhenattacked ) )
|
|
{
|
|
level.vehicle_unloadwhenattacked = [];
|
|
}
|
|
if( !isdefined( level.vehicle_deckdust ) )
|
|
{
|
|
level.vehicle_deckdust = [];
|
|
}
|
|
if( !isdefined( level.vehicle_types ) )
|
|
{
|
|
level.vehicle_types = [];
|
|
}
|
|
if( !isdefined( level.vehicle_compass_types ) )
|
|
{
|
|
level.vehicle_compass_types = [];
|
|
}
|
|
if( !isdefined( level.vehicle_bulletshield ) )
|
|
{
|
|
level.vehicle_bulletshield = [];
|
|
}
|
|
if( !isdefined( level.vehicle_death_badplace ) )
|
|
{
|
|
level.vehicle_death_badplace = [];
|
|
}
|
|
|
|
// vehicle_aianim::setup_aianimthreads();
|
|
|
|
}
|
|
|
|
|
|
function attacker_is_on_my_team( attacker )
|
|
{
|
|
if( ( isdefined( attacker ) ) && isdefined( attacker.team ) && ( isdefined( self.team ) ) && ( attacker.team == self.team ) )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function attacker_troop_is_on_my_team( attacker )
|
|
{
|
|
if ( isdefined( self.team ) && self.team == "allies" && isdefined( attacker ) && isdefined(level.player) && attacker == level.player )
|
|
{
|
|
return true; // player is always on the allied team.. hahah! future CoD games that let the player be the enemy be damned!
|
|
}
|
|
else if( isai( attacker ) && attacker.team == self.team )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function bullet_shielded( type )
|
|
{
|
|
if ( !isdefined( self.script_bulletshield ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
type = tolower( type );
|
|
|
|
if ( ! isdefined( type ) || ! issubstr( type, "bullet" ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( self.script_bulletshield )
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
function friendly_fire_shield()
|
|
{
|
|
self.friendlyfire_shield = true;
|
|
|
|
if ( isdefined( level.vehicle_bulletshield[ self.vehicletype ] ) && !isdefined( self.script_bulletshield ) )
|
|
{
|
|
self.script_bulletshield = level.vehicle_bulletshield[ self.vehicletype ];
|
|
}
|
|
}
|
|
|
|
function friendly_fire_shield_callback( attacker, amount, type )
|
|
{
|
|
if( !isdefined(self.friendlyfire_shield) || !self.friendlyfire_shield )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if(
|
|
( ! isdefined( attacker ) && self.team != "neutral" ) ||
|
|
attacker_is_on_my_team( attacker ) ||
|
|
attacker_troop_is_on_my_team( attacker ) ||
|
|
is_destructible() || // destructible pieces take the damage
|
|
bullet_shielded( type )
|
|
)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//function vehicle_dynamic_cover()
|
|
//{
|
|
// if ( isdefined( self.targetname ) )
|
|
// {
|
|
// ent = GetEnt( self.targetname, "target" );
|
|
// if ( isdefined( ent ) )
|
|
// {
|
|
// if ( isdefined( ent.script_noteworthy ) && ( ent.script_noteworthy == "dynamic_cover" ) )
|
|
// {
|
|
// e_dyn_path = ent;
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
// if ( isdefined( e_dyn_path ) )
|
|
// {
|
|
// self.e_dyn_path = e_dyn_path;
|
|
// }
|
|
// else
|
|
// {
|
|
// e_dyn_path = self;
|
|
// }
|
|
//}
|
|
|
|
function _vehicle_bad_place()
|
|
{
|
|
self endon( "kill_badplace_forever" );
|
|
self endon( "death" );
|
|
self endon( "delete" );
|
|
|
|
if( isdefined( level.custombadplacethread ) )
|
|
{
|
|
self thread [[ level.custombadplacethread ]]();
|
|
return;
|
|
}
|
|
|
|
hasturret = isdefined( self.turretweapon ) && self.turretweapon != level.weaponNone;
|
|
const bp_duration = .5;
|
|
const bp_height = 300;
|
|
const bp_angle_left = 17;
|
|
const bp_angle_right = 17;
|
|
|
|
while ( true )
|
|
{
|
|
if( !self.script_badplace )
|
|
{
|
|
// badplace_delete( "tankbadplace" );
|
|
while ( !self.script_badplace )
|
|
{
|
|
wait .5;
|
|
}
|
|
}
|
|
|
|
speed = self GetSpeedMPH();
|
|
if ( speed <= 0 )
|
|
{
|
|
wait bp_duration;
|
|
continue;
|
|
}
|
|
|
|
if ( speed < 5 )
|
|
{
|
|
bp_radius = 200;
|
|
}
|
|
else if ( ( speed > 5 ) && ( speed < 8 ) )
|
|
{
|
|
bp_radius = 350;
|
|
}
|
|
else
|
|
{
|
|
bp_radius = 500;
|
|
}
|
|
|
|
if ( isdefined( self.BadPlaceModifier ) )
|
|
{
|
|
bp_radius = ( bp_radius * self.BadPlaceModifier );
|
|
}
|
|
|
|
v_turret_angles = self GetTagAngles( "tag_turret" );
|
|
|
|
if ( hasturret && isdefined( v_turret_angles ) )
|
|
{
|
|
bp_direction = AnglesToForward( v_turret_angles );
|
|
}
|
|
else
|
|
{
|
|
bp_direction = AnglesToForward( self.angles );
|
|
}
|
|
|
|
//badplace_arc( "", bp_duration, self.origin, bp_radius * 1.9, bp_height, bp_direction, bp_angle_left, bp_angle_right, "allies", "axis" );
|
|
//badplace_cylinder( "", bp_duration, self.origin, 200, "allies", "axis" );
|
|
//badplace_cylinder( "", bp_duration, self.colidecircle[ 1 ].origin, 200, bp_height, "allies", "axis" );
|
|
wait bp_duration + .05;
|
|
}
|
|
}
|
|
|
|
//function handle_unload_event()
|
|
//{
|
|
// self notify( "vehicle_handleunloadevent" );
|
|
// self endon( "vehicle_handleunloadevent" );
|
|
// self endon( "death" );
|
|
//
|
|
// type = self.vehicletype;
|
|
// while( 1 )
|
|
// {
|
|
// self waittill( "unload", who );
|
|
//
|
|
// // setting an unload group unloaded guys resets to "default"
|
|
// // SCRIPTER_MOD: JesseS (5/14/2007) - took this out "who" for now, seems to be breaking unloadgroups.
|
|
// //if( isdefined( who ) )
|
|
// // self.unload_group = who;
|
|
// // makes ai unload
|
|
// self notify( "groupedanimevent", "unload" );
|
|
// }
|
|
//}
|
|
|
|
function get_vehiclenode_any_dynamic( target )
|
|
{
|
|
// the should return undefined
|
|
path_start = GetVehicleNode( target, "targetname" );
|
|
|
|
if ( !isdefined( path_start ) )
|
|
{
|
|
path_start = GetEnt( target, "targetname" );
|
|
}
|
|
else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) )
|
|
{
|
|
/#
|
|
PrintLn( "helicopter node targetname: " + path_start.targetname );
|
|
PrintLn( "vehicletype: " + self.vehicletype );
|
|
#/
|
|
AssertMsg( "helicopter on vehicle path( see console for info )" );
|
|
}
|
|
|
|
if ( !isdefined( path_start ) )
|
|
{
|
|
path_start = struct::get( target, "targetname" );
|
|
}
|
|
|
|
return path_start;
|
|
}
|
|
|
|
//TODO T7 - ask code how this function differs from vehicle::resume_path()
|
|
function resume_path_vehicle()
|
|
{
|
|
if ( isdefined( self.currentnode.target ) )
|
|
{
|
|
node = get_vehiclenode_any_dynamic( self.currentnode.target );
|
|
}
|
|
|
|
if ( isdefined( node ) )
|
|
{
|
|
self ResumeSpeed( 35 );
|
|
paths( node );
|
|
}
|
|
}
|
|
|
|
function land()
|
|
{
|
|
self setNearGoalNotifyDist( 2 );
|
|
self sethoverparams( 0, 0, 10 );
|
|
self cleargoalyaw();
|
|
self settargetyaw( ( 0, self.angles[1], 0 )[ 1 ] );
|
|
self set_goal_pos( (bullettrace(self.origin,(self.origin + ( 0, 0, -100000 ) ), 0, self )[ "position" ]), 1 );
|
|
self waittill( "goal" );
|
|
}
|
|
|
|
function set_goal_pos( origin, bStop )
|
|
{
|
|
if( self.health <= 0 )
|
|
{
|
|
return;
|
|
}
|
|
if( isdefined( self.originheightoffset ) )
|
|
{
|
|
origin += ( 0, 0, self.originheightoffset ); // TODO - FIXME: this is temporarily set in the vehicles init_local function working on getting it this requirement removed
|
|
}
|
|
self setvehgoalpos( origin, bStop );
|
|
}
|
|
|
|
function liftoff( height )
|
|
{
|
|
if( !isdefined( height ) )
|
|
{
|
|
height = 512;
|
|
}
|
|
dest = self.origin + ( 0, 0, height );
|
|
self setNearGoalNotifyDist( 10 );
|
|
self set_goal_pos( dest, 1 );
|
|
self waittill( "goal" );
|
|
}
|
|
|
|
function wait_till_stable()
|
|
{
|
|
// wait for it to level out before unloading
|
|
const offset = 12;
|
|
const stabletime = 400;
|
|
timer = gettime() + stabletime;
|
|
while( isdefined( self ) )
|
|
{
|
|
if( self.angles[ 0 ] > offset || self.angles [ 0 ] < ( - 1 * offset ) )
|
|
{
|
|
timer = gettime() + stabletime;
|
|
}
|
|
if( self.angles[ 2 ] > offset || self.angles [ 2 ] < ( - 1 * offset ) )
|
|
{
|
|
timer = gettime() + stabletime;
|
|
}
|
|
if( gettime() > timer )
|
|
{
|
|
break;
|
|
}
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
function unload_node( node )
|
|
{
|
|
// needed by RTS mode to halt choppers quicker.
|
|
// Result is ugly though and should be done properly by someone that knows vehicles at some point.
|
|
if( isdefined( self.custom_unload_function ) )
|
|
{
|
|
[[ self.custom_unload_function ]]();
|
|
return;
|
|
}
|
|
|
|
pause_path();
|
|
|
|
// if ( self.riders.size > 0 )
|
|
// {
|
|
// pathnode = GetNode( node.targetname, "target" );
|
|
// if ( isdefined( pathnode ) )
|
|
// {
|
|
// foreach ( ai_rider in self.riders )
|
|
// {
|
|
// if ( IsAI( ai_rider ) )
|
|
// {
|
|
// ai_rider thread spawner::go_to_node( pathnode );
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
|
|
if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "plane") ) )
|
|
{
|
|
wait_till_stable();
|
|
}
|
|
else if ( ( isdefined(self.vehicleclass) && (self.vehicleclass == "helicopter" ) ) )
|
|
{
|
|
self SetHoverParams( 0, 0, 10 );
|
|
wait_till_stable();
|
|
}
|
|
|
|
if ( node is_unload_node() )
|
|
{
|
|
unload( node.script_unload );
|
|
}
|
|
|
|
// if ( vehicle_aianim::riders_unloadable( node.script_unload ) || IS_TRUE( self.custom_unload ) )
|
|
// {
|
|
// self waittill( "unloaded" );
|
|
// }
|
|
}
|
|
|
|
function is_unload_node()
|
|
{
|
|
return ( isdefined( self.script_unload ) && ( self.script_unload != "none" ) );
|
|
}
|
|
|
|
function unload_node_helicopter( node )
|
|
{
|
|
// needed by RTS mode to halt choppers quicker.
|
|
// Result is ugly though and should be done properly by someone that knows vehicles at some point.
|
|
if( isdefined( self.custom_unload_function ) )
|
|
{
|
|
self thread [[ self.custom_unload_function ]]();
|
|
}
|
|
|
|
self SetHoverParams( 0, 0, 10 );
|
|
|
|
goal = self.nextNode.origin;
|
|
|
|
// find out how far off the ground the drop node is
|
|
start = self.nextNode.origin;
|
|
end = start - ( 0, 0, 10000 );
|
|
|
|
// trace the ground
|
|
trace = BulletTrace( start, end, false, undefined, true );
|
|
if ( trace["fraction"] <= 1 )
|
|
{
|
|
goal = ( trace["position"][0], trace["position"][1], trace["position"][2] + self.fastropeoffset );
|
|
}
|
|
|
|
// For now always do the ri tag
|
|
drop_offset_tag = "tag_fastrope_ri";
|
|
|
|
// unless there's a custom one set on the helicopter
|
|
if( isdefined( self.drop_offset_tag ) )
|
|
drop_offset_tag = self.drop_offset_tag;
|
|
|
|
// Get offset from drop tag to origin
|
|
drop_offset = self GetTagOrigin( "tag_origin" ) - self GetTagOrigin( drop_offset_tag );
|
|
|
|
// offset goal
|
|
goal += ( drop_offset[0], drop_offset[1], 0 );
|
|
|
|
self SetVehGoalPos( goal, 1 );
|
|
self waittill( "goal" );
|
|
self notify( "unload", self.nextNode.script_unload );
|
|
|
|
self waittill( "unloaded" );
|
|
}
|
|
|
|
function detach_path()
|
|
{
|
|
self.attachedpath = undefined;
|
|
self notify( "newpath" );
|
|
|
|
self setGoalyaw( ( 0, self.angles[1], 0 )[ 1 ] );
|
|
self setvehgoalpos( self.origin + ( 0, 0, 4 ), 1 );
|
|
}
|
|
|
|
function setup_targetname_spawners()
|
|
{
|
|
level.vehicle_targetname_array = [];
|
|
|
|
vehicles = getentarray( "script_vehicle", "classname" );
|
|
|
|
n_highest_group = 0;
|
|
// get the highest script_vehicleSpawnGroup in use
|
|
foreach ( vh in vehicles )
|
|
{
|
|
if ( isdefined( vh.script_vehicleSpawnGroup ) )
|
|
{
|
|
n_spawn_group = Int( vh.script_vehicleSpawnGroup );
|
|
|
|
if ( n_spawn_group > n_highest_group )
|
|
{
|
|
n_highest_group = n_spawn_group;
|
|
}
|
|
}
|
|
}
|
|
|
|
for( i = 0; i < vehicles.size; i++ )
|
|
{
|
|
vehicle = vehicles[ i ];
|
|
|
|
if ( isdefined( vehicle.targetname ) && IsVehicleSpawner( vehicle ) )
|
|
{
|
|
if( !isdefined( vehicle.script_vehicleSpawnGroup ) )
|
|
{
|
|
// vehicle spawners that have no script_vehiclespawngroup get assigned one, if they have a targetname
|
|
n_highest_group++;
|
|
vehicle.script_vehicleSpawnGroup = n_highest_group;
|
|
}
|
|
|
|
if( !isdefined( level.vehicle_targetname_array[ vehicle.targetname ] ) )
|
|
{
|
|
level.vehicle_targetname_array[ vehicle.targetname ] = [];
|
|
}
|
|
|
|
level.vehicle_targetname_array[ vehicle.targetname ][ vehicle.script_vehicleSpawnGroup ] = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
function simple_spawn( name, b_supress_assert=false )
|
|
{
|
|
// spawns an array of vehicles that all have the specified targetname in the editor,
|
|
// but are deleted at runtime
|
|
|
|
Assert( b_supress_assert || isdefined( level.vehicle_targetname_array[ name ] ), "No vehicle spawners had targetname " + name );
|
|
|
|
vehicles = [];
|
|
if ( isdefined( level.vehicle_targetname_array[ name ] ) )
|
|
{
|
|
array = level.vehicle_targetname_array[ name ];
|
|
|
|
if ( array.size > 0 )
|
|
{
|
|
keys = GetArrayKeys( array );
|
|
|
|
foreach ( key in keys )
|
|
{
|
|
vehicle_array = _scripted_spawn( key );
|
|
vehicles = ArrayCombine( vehicles, vehicle_array, true, false );
|
|
}
|
|
}
|
|
}
|
|
|
|
return vehicles;
|
|
}
|
|
|
|
function simple_spawn_single( name, b_supress_assert=false )
|
|
{
|
|
vehicle_array = simple_spawn( name, b_supress_assert );
|
|
Assert( b_supress_assert || vehicle_array.size == 1, "Tried to spawn a vehicle from targetname " + name + " but it returned " + vehicle_array.size + " vehicles, instead of 1" );
|
|
|
|
if ( vehicle_array.size > 0 )
|
|
{
|
|
return vehicle_array[ 0 ];
|
|
}
|
|
}
|
|
|
|
function simple_spawn_single_and_drive( name )
|
|
{
|
|
// spawns 1 vehicle and makes sure it gets 1
|
|
vehicleArray = simple_spawn( name );
|
|
assert( vehicleArray.size == 1, "Tried to spawn a vehicle from targetname " + name + " but it returned " + vehicleArray.size + " vehicles, instead of 1" );
|
|
|
|
vehicleArray[ 0 ] thread go_path();
|
|
return vehicleArray[ 0 ];
|
|
}
|
|
|
|
function simple_spawn_and_drive( name )
|
|
{
|
|
// spawns 1 vehicle and makes sure it gets 1
|
|
vehicleArray = simple_spawn( name );
|
|
for( i = 0; i < vehicleArray.size; i++ )
|
|
{
|
|
vehicleArray[ i ] thread go_path();
|
|
}
|
|
|
|
return vehicleArray;
|
|
}
|
|
|
|
//Wrapper for SpawnVehicle, use this to throttle vehicle spawning
|
|
function spawn( modelname, targetname, vehicletype, origin, angles, destructibledef )
|
|
{
|
|
assert(isdefined(targetname));
|
|
assert(isdefined(vehicletype));
|
|
assert(isdefined(origin));
|
|
assert(isdefined(angles));
|
|
|
|
return SpawnVehicle( vehicletype, origin, angles, targetname, destructibledef );
|
|
}
|
|
|
|
function aircraft_dust_kickup( model )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "death_finished" );
|
|
self endon( "stop_kicking_up_dust" );
|
|
|
|
assert( isdefined( self.vehicletype ) );
|
|
|
|
const maxHeight = 1200;
|
|
const minHeight = 350;
|
|
|
|
const slowestRepeatWait = 0.15;
|
|
const fastestRepeatWait = 0.05;
|
|
|
|
const numFramesPerTrace = 3;
|
|
doTraceThisFrame = numFramesPerTrace;
|
|
|
|
const defaultRepeatRate = 1.0;
|
|
repeatRate = defaultRepeatRate;
|
|
|
|
trace = undefined;
|
|
d = undefined;
|
|
|
|
trace_ent = self;
|
|
if ( isdefined( model ) )
|
|
{
|
|
trace_ent = model;
|
|
}
|
|
|
|
while( isdefined( self ) )
|
|
{
|
|
if( repeatRate <= 0 )
|
|
{
|
|
repeatRate = defaultRepeatRate;
|
|
}
|
|
wait repeatRate;
|
|
|
|
if( !isdefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
doTraceThisFrame -- ;
|
|
|
|
|
|
if( doTraceThisFrame <= 0 )
|
|
{
|
|
doTraceThisFrame = numFramesPerTrace;
|
|
|
|
trace = bullettrace( trace_ent.origin, trace_ent.origin - ( 0, 0, 100000 ), false, trace_ent );
|
|
/*
|
|
trace[ "entity" ]
|
|
trace[ "fraction" ]
|
|
trace[ "normal" ]
|
|
trace[ "position" ]
|
|
trace[ "surfacetype" ]
|
|
*/
|
|
|
|
d = distance( trace_ent.origin, trace[ "position" ] );
|
|
|
|
repeatRate = ( ( d - minHeight ) / ( maxHeight - minHeight ) ) * ( slowestRepeatWait - fastestRepeatWait ) + fastestRepeatWait;
|
|
}
|
|
|
|
if( !isdefined( trace ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
assert( isdefined( d ) );
|
|
|
|
if( d > maxHeight )
|
|
{
|
|
repeatRate = defaultRepeatRate;
|
|
continue;
|
|
}
|
|
|
|
if( isdefined( trace[ "entity" ] ) )
|
|
{
|
|
repeatRate = defaultRepeatRate;
|
|
continue;
|
|
}
|
|
|
|
if( !isdefined( trace[ "position" ] ) )
|
|
{
|
|
repeatRate = defaultRepeatRate;
|
|
continue;
|
|
}
|
|
|
|
if( !isdefined( trace[ "surfacetype" ] ) )
|
|
{
|
|
trace[ "surfacetype" ] = "dirt";
|
|
}
|
|
assert( isdefined( level._vehicle_effect[ self.vehicletype ] ), self.vehicletype + " vehicle script hasn't run _tradfx properly" );
|
|
assert( isdefined( level._vehicle_effect[ self.vehicletype ][ trace[ "surfacetype" ] ] ), "UNKNOWN SURFACE TYPE: " + trace[ "surfacetype" ] );
|
|
|
|
|
|
if( level._vehicle_effect[ self.vehicletype ][ trace[ "surfacetype" ] ] != -1 )
|
|
{
|
|
playfx( level._vehicle_effect[ self.vehicletype ][ trace[ "surfacetype" ] ], trace[ "position" ] );
|
|
}
|
|
}
|
|
}
|
|
|
|
function impact_fx( fxname, surfaceTypes )
|
|
{
|
|
if ( isdefined( fxname ) )
|
|
{
|
|
body = self GetTagOrigin( "tag_body" );
|
|
if ( !isdefined( body ) )
|
|
{
|
|
body = self.origin + (0,0,10);
|
|
}
|
|
|
|
trace = BulletTrace( body, body - (0,0,2 * self.radius), false, self );
|
|
if( trace["fraction"] < 1.0 && !isdefined( trace[ "entity" ] ) && ( !isdefined(surfaceTypes) || array::contains( surfaceTypes, trace["surfacetype"] ) ) )
|
|
{
|
|
pos = 0.5 * ( self.origin + trace["position"] );
|
|
up = 0.5 * ( trace["normal"] + AnglesToUp( self.angles ) );
|
|
forward = AnglesToForward( self.angles );
|
|
PlayFx( fxname, pos, up, forward ); // reverse up and forward because impact FX are X-axis up
|
|
}
|
|
}
|
|
}
|
|
|
|
function maingun_fx()
|
|
{
|
|
if( !isdefined( level.vehicle_deckdust[ self.model ] ) )
|
|
{
|
|
return;
|
|
}
|
|
self endon( "death" );
|
|
while( true )
|
|
{
|
|
self waittill( "weapon_fired" ); // waits for Code notify when fireWeapon() is called.
|
|
playfxontag( level.vehicle_deckdust[ self.model ], self, "tag_engine_exhaust" );
|
|
barrel_origin = self gettagorigin( "tag_flash" );
|
|
ground = physicstrace( barrel_origin, barrel_origin + ( 0, 0, -128 ) );
|
|
physicsExplosionSphere( ground, 192, 100, 1 );
|
|
}
|
|
}
|
|
|
|
function lights_on( team )
|
|
{
|
|
//lights:
|
|
//0 - turn on normal
|
|
//1 - turn off
|
|
//2 - override to allied color
|
|
//3 - override to axis color
|
|
|
|
if( IsDefined( team ) )
|
|
{
|
|
if( team == "allies" )
|
|
{
|
|
self clientfield::set( "toggle_lights", 2 );
|
|
}
|
|
else if( team == "axis" )
|
|
{
|
|
self clientfield::set( "toggle_lights", 3 );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
self clientfield::set( "toggle_lights", 0 );
|
|
}
|
|
}
|
|
|
|
function lights_off()
|
|
{
|
|
self clientfield::set( "toggle_lights", 1 );
|
|
}
|
|
|
|
function toggle_lights_group( groupID, on )
|
|
{
|
|
bit = 1;
|
|
if ( !on )
|
|
{
|
|
bit = 0;
|
|
}
|
|
|
|
self clientfield::set( "toggle_lights_group" + groupID, bit );
|
|
}
|
|
|
|
function toggle_ambient_anim_group( groupID, on )
|
|
{
|
|
bit = 1;
|
|
if ( !on )
|
|
{
|
|
bit = 0;
|
|
}
|
|
|
|
self clientfield::set( "toggle_ambient_anim_group" + groupID, bit );
|
|
}
|
|
|
|
function do_death_fx()
|
|
{
|
|
deathfxtype = ( ( self.died_by_emp === true ) ? 2 : 1 );
|
|
|
|
self clientfield::set( "deathfx", deathfxtype );
|
|
self stopsounds();
|
|
}
|
|
|
|
function toggle_emp_fx( on )
|
|
{
|
|
self clientfield::set( "toggle_emp_fx", on );
|
|
}
|
|
|
|
function toggle_burn_fx( on )
|
|
{
|
|
self clientfield::set( "toggle_burn_fx", on );
|
|
}
|
|
|
|
// special_status: 1 - normal death; 2 - EMP death; 3 - burn death; 0 - no fx
|
|
function do_death_dynents( special_status = 1 )
|
|
{
|
|
assert( special_status >= 0 && special_status <= 3 );
|
|
self clientfield::set( "spawn_death_dynents", special_status );
|
|
}
|
|
|
|
function do_gib_dynents()
|
|
{
|
|
self clientfield::set( "spawn_gib_dynents", 1 );
|
|
|
|
numDynents = 2;
|
|
for( i = 0; i < numDynents; i++ )
|
|
{
|
|
hidetag = GetStructField( self.settings, "servo_gib_tag" + i );
|
|
if ( isdefined( hidetag ) )
|
|
{
|
|
self HidePart( hidetag, "", true );
|
|
}
|
|
}
|
|
}
|
|
|
|
function set_alert_fx_level( alert_level ) // 0 is off, 1 unaware, 2 alert, 3 combat
|
|
{
|
|
self clientfield::set( "alert_level", alert_level );
|
|
}
|
|
|
|
function should_update_damage_fx_level( currentHealth, damage, maxHealth )
|
|
{
|
|
settings = struct::get_script_bundle( "vehiclecustomsettings", self.scriptbundlesettings );
|
|
if ( !isdefined( settings ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
currentRatio = math::clamp( float( currentHealth ) / float( maxHealth ), 0.0, 1.0 );
|
|
afterDamageRatio = math::clamp( float( currentHealth - damage ) / float( maxHealth ), 0.0, 1.0 );
|
|
currentLevel = undefined;
|
|
afterDamageLevel = undefined;
|
|
switch( (isdefined(settings.damagestate_numStates)?settings.damagestate_numStates:0) )
|
|
{
|
|
case 6:
|
|
if ( settings.damagestate_lv6_ratio >= afterDamageRatio )
|
|
{
|
|
afterDamageLevel = 6;
|
|
currentLevel = 6;
|
|
if ( settings.damagestate_lv6_ratio < currentRatio )
|
|
{
|
|
currentLevel = 5;
|
|
}
|
|
break;
|
|
} // fall through
|
|
case 5:
|
|
if ( settings.damagestate_lv5_ratio >= afterDamageRatio )
|
|
{
|
|
afterDamageLevel = 5;
|
|
currentLevel = 5;
|
|
if ( settings.damagestate_lv5_ratio < currentRatio )
|
|
{
|
|
currentLevel = 4;
|
|
}
|
|
break;
|
|
} // fall through
|
|
case 4:
|
|
if ( settings.damagestate_lv4_ratio >= afterDamageRatio )
|
|
{
|
|
afterDamageLevel = 4;
|
|
currentLevel = 4;
|
|
if ( settings.damagestate_lv4_ratio < currentRatio )
|
|
{
|
|
currentLevel = 3;
|
|
}
|
|
break;
|
|
} // fall through
|
|
case 3:
|
|
if ( settings.damagestate_lv3_ratio >= afterDamageRatio )
|
|
{
|
|
afterDamageLevel = 3;
|
|
currentLevel = 3;
|
|
if ( settings.damagestate_lv3_ratio < currentRatio )
|
|
{
|
|
currentLevel = 2;
|
|
}
|
|
break;
|
|
} // fall through
|
|
case 2:
|
|
if ( settings.damagestate_lv2_ratio >= afterDamageRatio )
|
|
{
|
|
afterDamageLevel = 2;
|
|
currentLevel = 2;
|
|
if ( settings.damagestate_lv2_ratio < currentRatio )
|
|
{
|
|
currentLevel = 1;
|
|
}
|
|
break;
|
|
} // fall through
|
|
case 1:
|
|
if ( settings.damagestate_lv1_ratio >= afterDamageRatio )
|
|
{
|
|
afterDamageLevel = 1;
|
|
currentLevel = 1;
|
|
if ( settings.damagestate_lv1_ratio < currentRatio )
|
|
{
|
|
currentLevel = 0;
|
|
}
|
|
break;
|
|
} // fall through
|
|
default:
|
|
// do nothing
|
|
}
|
|
|
|
if ( !isdefined( currentLevel ) || !isdefined( afterDamageLevel ) )
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
if ( currentLevel != afterDamageLevel )
|
|
{
|
|
return afterDamageLevel;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
// self == vehicle
|
|
// returns if damage level changed after damage
|
|
function update_damage_fx_level( currentHealth, damage, maxHealth )
|
|
{
|
|
newDamageLevel = should_update_damage_fx_level( currentHealth, damage, maxHealth );
|
|
if ( newDamageLevel > 0 )
|
|
{
|
|
self set_damage_fx_level( newDamageLevel );
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
// self == vehicle
|
|
// 0 is off, higher level means higher damage
|
|
function set_damage_fx_level( damage_level )
|
|
{
|
|
self clientfield::set( "damage_level", damage_level );
|
|
}
|
|
|
|
/@
|
|
"Name: build_drive( <forward> , <reverse> , <normalspeed> , <rate> )"
|
|
"Summary: called in individual vehicle file - assigns animations to be used on vehicles"
|
|
"Module: Vehicle"
|
|
"CallOn: A vehicle"
|
|
"MandatoryArg: <forward> : forward animation"
|
|
"OptionalArg: <reverse> : reverse animation"
|
|
"OptionalArg: <normalspeed> : speed at which animation will be played at 1x defaults to 10mph"
|
|
"OptionalArg: <rate> : scales speed of animation( please only use this for testing )"
|
|
"Example: vehicle build_drive( %abrams_movement, %abrams_movement_backwards, 10 );"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
|
|
function build_drive( forward, reverse, normalspeed, rate )
|
|
{
|
|
if( !isdefined( normalspeed ) )
|
|
{
|
|
normalspeed = 10;
|
|
}
|
|
level.vehicle_DriveIdle[ self.model ] = forward;
|
|
|
|
if( isdefined( reverse ) )
|
|
{
|
|
level.vehicle_DriveIdle_r[ self.model ] = reverse;
|
|
}
|
|
level.vehicle_DriveIdle_normal_speed[ self.model ] = normalspeed;
|
|
if( isdefined( rate ) )
|
|
{
|
|
level.vehicle_DriveIdle_animrate[ self.model ] = rate;
|
|
}
|
|
}
|
|
|
|
///@
|
|
//"Name: build_ai_anims( <aithread> , <vehiclethread> )"
|
|
//"Summary: called in individual vehicle file - set threads for ai animation and vehicle animation assignments"
|
|
//"Module: Vehicle"
|
|
//"CallOn: A vehicle"
|
|
//"MandatoryArg: <aithread> : ai thread"
|
|
//"OptionalArg: <vehiclethread> : vehicle thread"
|
|
//"Example: vehicle build_ai_anims(&setanims,&set_vehicle_anims );"
|
|
//"SPMP: singleplayer"
|
|
//@/
|
|
//
|
|
//function build_ai_anims( aithread, vehiclethread )
|
|
//{
|
|
// level.vehicle_aianims[ self.vehicletype ] = [[ aithread ]]();
|
|
// if( isdefined( vehiclethread ) )
|
|
// {
|
|
// level.vehicle_aianims[ self.vehicletype ] = [[ vehiclethread ]]( level.vehicle_aianims[ self.vehicletype ] );
|
|
// }
|
|
//}
|
|
|
|
|
|
///@
|
|
//"Name: build_attach_models( <modelsthread> )"
|
|
//"Summary: called in individual vehicle file - thread for building attached models( ropes ) with animation"
|
|
//"Module: Vehicle"
|
|
//"CallOn: "
|
|
//"MandatoryArg: <modelsthread> : thread"
|
|
//"Example: build_attach_models(&set_attached_models );"
|
|
//"SPMP: singleplayer"
|
|
//@/
|
|
//
|
|
//function build_attach_models( modelsthread )
|
|
//{
|
|
// level.vehicle_attachedmodels[ self.vehicletype ] = [[ modelsthread ]]();;
|
|
//}
|
|
|
|
///@
|
|
//"Name: build_unload_groups( <unloadgroupsthread> )"
|
|
//"Summary: called in individual vehicle file - thread for building unload groups"
|
|
//"Module: Vehicle"
|
|
//"CallOn: A vehicle"
|
|
//"MandatoryArg: <modelsthread> : thread"
|
|
//"Example: vehicle build_unload_groups(&Unload_Groups );"
|
|
//"SPMP: singleplayer"
|
|
//@/
|
|
//
|
|
//function build_unload_groups( unloadgroupsthread )
|
|
//{
|
|
// level.vehicle_unloadgroups[ self.vehicletype ] = [[ unloadgroupsthread ]]();
|
|
//}
|
|
|
|
function get_from_spawnstruct( target )
|
|
{
|
|
return struct::get( target, "targetname" );
|
|
}
|
|
|
|
function get_from_entity( target )
|
|
{
|
|
return getent( target, "targetname" );
|
|
}
|
|
|
|
function get_from_spawnstruct_target( target )
|
|
{
|
|
return struct::get( target, "target" );
|
|
}
|
|
|
|
function get_from_entity_target( target )
|
|
{
|
|
return getent( target, "target" );
|
|
}
|
|
|
|
function is_destructible()
|
|
{
|
|
return isdefined( self.destructible_type );
|
|
}
|
|
|
|
// SCRIPTER_MOD: JesseS (5/12/200) - readded script_vehicleattackgroup scripting
|
|
// This allows vehicles to attack other vehicles automatically. Just set the script_vehicleattackgroup of the attacker
|
|
// to the script_vehiclespawngroup you want to attack.
|
|
function attack_group_think()
|
|
{
|
|
self endon ("death");
|
|
self endon ("switch group");
|
|
self endon ("killed all targets");
|
|
|
|
if (isdefined (self.script_vehicleattackgroupwait))
|
|
{
|
|
wait (self.script_vehicleattackgroupwait);
|
|
}
|
|
|
|
for(;;)
|
|
{
|
|
//get all the vehicles
|
|
group = getentarray("script_vehicle", "classname");
|
|
|
|
// get our target array
|
|
valid_targets = [];
|
|
for (i = 0; i< group.size; i++)
|
|
{
|
|
// dpg: not all script vehicles have a script_vehiclespawngroup
|
|
if( !isdefined( group[i].script_vehiclespawngroup ) )
|
|
{
|
|
continue;
|
|
}
|
|
// get all vehicles with the same spawngroup as the agroup we want to attack
|
|
if (group[i].script_vehiclespawngroup == self.script_vehicleattackgroup)
|
|
{
|
|
// Make sure we only attack different teams vehicles
|
|
if (group[i].team != self.team)
|
|
{
|
|
if ( !isdefined( valid_targets ) ) valid_targets = []; else if ( !IsArray( valid_targets ) ) valid_targets = array( valid_targets ); valid_targets[valid_targets.size]=group[i];;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Try again every .5 seconds if there are no valid targets.
|
|
if (valid_targets.size == 0)
|
|
{
|
|
wait (0.5);
|
|
continue;
|
|
}
|
|
|
|
// Main loop which makes the vehicle fire on the nearest group it's been set to attack
|
|
for (;;)
|
|
{
|
|
current_target = undefined;
|
|
if (valid_targets.size != 0)
|
|
{
|
|
current_target = self get_nearest_target(valid_targets);
|
|
}
|
|
else
|
|
{
|
|
// We killed them all, end this thread
|
|
self notify ("killed all targets");
|
|
}
|
|
|
|
// if it's a death vehicle, remove it from the list of valid targets
|
|
if (current_target.health <= 0)
|
|
{
|
|
ArrayRemoveValue(valid_targets, current_target);
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
// set the target ent for the vehicle. offset a bit so it doesnt shoot into the ground
|
|
self setturrettargetent( current_target, (0,0,50) );
|
|
|
|
// DPG 10/22/07 - in case we want specific wait values on the vehicle
|
|
if( isdefined( self.fire_delay_min ) && isdefined( self.fire_delay_max ) )
|
|
{
|
|
// in case max is less than min
|
|
if( self.fire_delay_max < self.fire_delay_min )
|
|
{
|
|
self.fire_delay_max = self.fire_delay_min;
|
|
}
|
|
|
|
wait ( randomintrange(self.fire_delay_min, self.fire_delay_max) );
|
|
}
|
|
|
|
else
|
|
{
|
|
wait (randomintrange(4, 6));
|
|
}
|
|
self fireweapon();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
// if this is called again, just add new group to queue
|
|
// queue should be sorted by distance
|
|
// delete from queue once vehicle is dead, check to see if vehicles health > 0
|
|
}
|
|
|
|
// Gets the nearest ent to self
|
|
function get_nearest_target(valid_targets)
|
|
{
|
|
nearest_distsq = 99999999;
|
|
nearest = undefined;
|
|
|
|
for (i = 0; i < valid_targets.size; i++)
|
|
{
|
|
if( !isdefined( valid_targets[i] ) )
|
|
{
|
|
continue;
|
|
}
|
|
current_distsq = distancesquared( self.origin, valid_targets[i].origin );
|
|
if (current_distsq < nearest_distsq)
|
|
{
|
|
nearest_distsq = current_distsq;
|
|
nearest = valid_targets[i];
|
|
}
|
|
}
|
|
return nearest;
|
|
}
|
|
|
|
//---------------//
|
|
// Debug section //
|
|
//---------------//
|
|
/#
|
|
function debug_vehicle()
|
|
{
|
|
self endon( "death" );
|
|
|
|
if( GetDvarString( "debug_vehicle_health" ) == "" )
|
|
{
|
|
SetDvar( "debug_vehicle_health", "0" );
|
|
}
|
|
|
|
while( 1 )
|
|
{
|
|
if( GetDvarInt( "debug_vehicle_health" ) > 0 )
|
|
{
|
|
print3d( self.origin, "Health: " + self.health, ( 1, 1, 1 ), 1, 3 );
|
|
}
|
|
|
|
{wait(.05);};
|
|
}
|
|
}
|
|
|
|
function debug_vehicle_paths()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "newpath" );
|
|
self endon( "reached_dynamic_path_end" );
|
|
|
|
nextNode = self.currentNode;
|
|
|
|
while( 1 )
|
|
{
|
|
if( GetDvarInt( "debug_vehicle_paths" ) > 0 )
|
|
{
|
|
RecordLine( self.origin, self.currentNode.origin, (1,0,0), "Script", self );
|
|
RecordLine( self.origin, nextNode.origin, (0,1,0), "Script", self );
|
|
RecordLine( self.currentNode.origin, nextNode.origin, (1,1,1), "Script", self );
|
|
}
|
|
|
|
{wait(.05);};
|
|
|
|
// update
|
|
if( isdefined(self.nextNode) && self.nextNode != nextNode )
|
|
{
|
|
nextNode = self.nextNode;
|
|
}
|
|
}
|
|
}
|
|
#/
|
|
|
|
function get_dummy()
|
|
{
|
|
if ( ( isdefined( self.modeldummyon ) && self.modeldummyon ) )
|
|
{
|
|
eModel = self.modeldummy;
|
|
}
|
|
else
|
|
{
|
|
eModel = self;
|
|
}
|
|
return eModel;
|
|
}
|
|
|
|
function add_main_callback( vehicleType, main )
|
|
{
|
|
if ( !isdefined( level.vehicle_main_callback ) )
|
|
{
|
|
level.vehicle_main_callback = [];
|
|
}
|
|
|
|
/#
|
|
if ( isdefined( level.vehicle_main_callback[ vehicleType ] ) )
|
|
{
|
|
PrintLn( "WARNING! Main callback function for vehicle " + vehicleType + " already exists. Proceeding with override" );
|
|
}
|
|
#/
|
|
|
|
level.vehicle_main_callback[ vehicleType ] = main;
|
|
}
|
|
|
|
function 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.team;
|
|
}
|
|
}
|
|
|
|
return self.team;
|
|
}
|
|
|
|
/@
|
|
"Name: toggle_exhaust_fx( <on> )"
|
|
"Module: Vehicle"
|
|
"CallOn: a vehicle"
|
|
"Summary: Will toggle the vehicles exhaust fx on (1) and off (0)"
|
|
"MandatoryArg: <on> : 1 - exhaustfx on, 0 - exhaustfx off"
|
|
"Example: level.tank vehicle::toggle_exhaust_fx(0);"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function toggle_exhaust_fx( on )
|
|
{
|
|
if(!on)
|
|
{
|
|
self clientfield::set( "toggle_exhaustfx", 1 );
|
|
}
|
|
else
|
|
{
|
|
self clientfield::set( "toggle_exhaustfx", 0 );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: toggle_tread_fx( <on> )"
|
|
"Module: Vehicle"
|
|
"CallOn: a vehicle"
|
|
"Summary: Will toggle the vehicles tread fx on (1) and off (0)"
|
|
"MandatoryArg: <on> : 1 - treadfx on, 0 - treadfx off"
|
|
"Example: level.tank toggle_tread_fx(1);"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function toggle_tread_fx( on )
|
|
{
|
|
if(on)
|
|
{
|
|
self clientfield::set( "toggle_treadfx", 1 );
|
|
}
|
|
else
|
|
{
|
|
self clientfield::set( "toggle_treadfx", 0 );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: toggle_sounds( <on> )"
|
|
"Module: Vehicle"
|
|
"CallOn: Vehicle"
|
|
"Summary: Will toggle the vehicle's sounds between on (1) and off (0)"
|
|
"MandatoryArg: <on> : 1 - sounds on, 0 - sounds off"
|
|
"Example: car toggle_sounds(0);"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function toggle_sounds( on )
|
|
{
|
|
// this flag number should *NOT* be changed. if it needs to be changed, code must be updated as well. See EF2_DISABLE_VEHICLE_SOUNDS in bg_public.h
|
|
if(!on)
|
|
{
|
|
self clientfield::set( "toggle_sounds", 1 );
|
|
}
|
|
else
|
|
{
|
|
self clientfield::set( "toggle_sounds", 0 );
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: is_corpse( <veh> )"
|
|
"Summary: Checks to see if a vehicle is a corpse."
|
|
"CallOn: AI"
|
|
"MandatoryArg: <veh> The vehicle you're checking to see is a corpse."
|
|
"Example: if ( !is_corpse( veh_truck ) )"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function is_corpse( veh )
|
|
{
|
|
if ( isdefined( veh ) )
|
|
{
|
|
if ( ( isdefined( veh.isacorpse ) && veh.isacorpse ) )
|
|
{
|
|
return true;
|
|
}
|
|
else if ( isdefined( veh.classname ) && ( veh.classname == "script_vehicle_corpse" ) )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
///@
|
|
//"Name: enter( <vehicle> )"
|
|
//"Summary: This puts the guy into the vehicle and tells him to idle."
|
|
//"Module: AI"
|
|
//"CallOn: an actor"
|
|
//"MandatoryArg: <vehicle>: the vehicle to get in"
|
|
//"Example: my_ai thread vehicle::enter(my_vehicle);"
|
|
//"SPMP: singleplayer"
|
|
//@/
|
|
//function enter( vehicle, tag ) // self == ai
|
|
//{
|
|
// self vehicle_aianim::vehicle_enter( vehicle, tag );
|
|
//}
|
|
|
|
/@
|
|
"Name: is_on( <vehicle> )"
|
|
"Summary: Returns true if a player is on the vehicle."
|
|
"Module: Vehicle"
|
|
"CallOn: a player"
|
|
"MandatoryArg: <vehicle>: the vehicle to check against"
|
|
"Example: if( player is_on( vehicle ) )"
|
|
"SPMP: singleplayer"
|
|
@/
|
|
function is_on(vehicle) // self == player
|
|
{
|
|
|
|
if(!isdefined(self.viewlockedentity))
|
|
{
|
|
return false;
|
|
}
|
|
else if(self.viewlockedentity == vehicle)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if(!isdefined(self.groundentity))
|
|
{
|
|
return false;
|
|
}
|
|
else if(self.groundentity == vehicle)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
/@
|
|
"Name: add_spawn_function( <targetname>, <func> , <param1> , <param2> , <param3>, <param4> )"
|
|
"Summary: Anything that spawns from this spawner will run this function. Anything."
|
|
"MandatoryArg: <targetname> : The targetname of the vehicle."
|
|
"MandatoryArg: <func1> : The function to run."
|
|
"OptionalArg: <param1> : An optional parameter."
|
|
"OptionalArg: <param2> : An optional parameter."
|
|
"OptionalArg: <param3> : An optional parameter."
|
|
"OptionalArg: <param4> : An optional parameter."
|
|
"Example: vehicle::add_spawn_function( "amazing_vehicle",&do_the_amazing_thing, some_amazing_parameter );"
|
|
"Note: This function must be called before load::main"
|
|
@/
|
|
function add_spawn_function( veh_targetname, spawn_func, param1, param2, param3, param4 )
|
|
{
|
|
func = [];
|
|
func[ "function" ] =spawn_func;
|
|
func[ "param1" ] = param1;
|
|
func[ "param2" ] = param2;
|
|
func[ "param3" ] = param3;
|
|
func[ "param4" ] = param4;
|
|
|
|
if(!isdefined(level.a_vehicle_targetnames))level.a_vehicle_targetnames=[];
|
|
|
|
if ( !isdefined( level.a_vehicle_targetnames[ veh_targetname ] ) ) level.a_vehicle_targetnames[ veh_targetname ] = []; else if ( !IsArray( level.a_vehicle_targetnames[ veh_targetname ] ) ) level.a_vehicle_targetnames[ veh_targetname ] = array( level.a_vehicle_targetnames[ veh_targetname ] ); level.a_vehicle_targetnames[ veh_targetname ][level.a_vehicle_targetnames[ veh_targetname ].size]=func;;
|
|
}
|
|
|
|
/@
|
|
"Name: add_spawn_function_by_type( <vehicle_type>, <func> , <param1> , <param2> , <param3>, <param4> )"
|
|
"Summary: Anything that spawns from this spawner will run this function. Anything."
|
|
"MandatoryArg: <vehicle_type> : The .vehicletype of the vehicle."
|
|
"MandatoryArg: <func1> : The function to run."
|
|
"OptionalArg: <param1> : An optional parameter."
|
|
"OptionalArg: <param2> : An optional parameter."
|
|
"OptionalArg: <param3> : An optional parameter."
|
|
"OptionalArg: <param4> : An optional parameter."
|
|
"Example: vehicle::add_spawn_function_by_type( "tank_t72",&do_the_amazing_thing, some_amazing_parameter );"
|
|
"Note: This function must be called before load::main"
|
|
@/
|
|
function add_spawn_function_by_type( veh_type, spawn_func, param1, param2, param3, param4 )
|
|
{
|
|
func = [];
|
|
func[ "function" ] =spawn_func;
|
|
func[ "param1" ] = param1;
|
|
func[ "param2" ] = param2;
|
|
func[ "param3" ] = param3;
|
|
func[ "param4" ] = param4;
|
|
|
|
if(!isdefined(level.a_vehicle_types))level.a_vehicle_types=[];
|
|
|
|
if ( !isdefined( level.a_vehicle_types[ veh_type ] ) ) level.a_vehicle_types[ veh_type ] = []; else if ( !IsArray( level.a_vehicle_types[ veh_type ] ) ) level.a_vehicle_types[ veh_type ] = array( level.a_vehicle_types[ veh_type ] ); level.a_vehicle_types[ veh_type ][level.a_vehicle_types[ veh_type ].size]=func;;
|
|
}
|
|
|
|
/@
|
|
"Name: add_hijack_function( <targetname>, <func> , <param1> , <param2> , <param3>, <param4> )"
|
|
"Summary: Runs when a vehicle gets hijacked/hacked with 'security breach'."
|
|
"MandatoryArg: <targetname> : The targetname of the vehicle."
|
|
"MandatoryArg: <func1> : The function to run."
|
|
"OptionalArg: <param1> : An optional parameter."
|
|
"OptionalArg: <param2> : An optional parameter."
|
|
"OptionalArg: <param3> : An optional parameter."
|
|
"OptionalArg: <param4> : An optional parameter."
|
|
"Example: vehicle::add_hijack_function( "amazing_vehicle", &do_the_amazing_thing, some_amazing_parameter );"
|
|
"Note: This function must be called before load::main"
|
|
@/
|
|
function add_hijack_function( veh_targetname, spawn_func, param1, param2, param3, param4 )
|
|
{
|
|
func = [];
|
|
func[ "function" ] =spawn_func;
|
|
func[ "param1" ] = param1;
|
|
func[ "param2" ] = param2;
|
|
func[ "param3" ] = param3;
|
|
func[ "param4" ] = param4;
|
|
|
|
if(!isdefined(level.a_vehicle_hijack_targetnames))level.a_vehicle_hijack_targetnames=[];
|
|
|
|
if ( !isdefined( level.a_vehicle_hijack_targetnames[ veh_targetname ] ) ) level.a_vehicle_hijack_targetnames[ veh_targetname ] = []; else if ( !IsArray( level.a_vehicle_hijack_targetnames[ veh_targetname ] ) ) level.a_vehicle_hijack_targetnames[ veh_targetname ] = array( level.a_vehicle_hijack_targetnames[ veh_targetname ] ); level.a_vehicle_hijack_targetnames[ veh_targetname ][level.a_vehicle_hijack_targetnames[ veh_targetname ].size]=func;;
|
|
}
|
|
|
|
function private _watch_for_hijacked_vehicles()
|
|
{
|
|
while ( true )
|
|
{
|
|
level waittill( "ClonedEntity", clone );
|
|
|
|
str_targetname = clone.targetname;
|
|
if (isdefined( str_targetname ) && StrEndsWith( str_targetname, "_ai" ) )
|
|
{
|
|
str_targetname = GetSubStr( str_targetname, 0, str_targetname.size - 3 );
|
|
}
|
|
|
|
waittillframeend;
|
|
|
|
if ( isdefined( str_targetname ) && isdefined( level.a_vehicle_hijack_targetnames ) && isdefined( level.a_vehicle_hijack_targetnames[ str_targetname ] ) )
|
|
{
|
|
foreach ( func in level.a_vehicle_hijack_targetnames[ str_targetname ] )
|
|
{
|
|
util::single_thread( clone, func[ "function" ], func[ "param1" ], func[ "param2" ], func[ "param3" ], func[ "param4" ] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/@
|
|
"Name: disconnect_paths( <detail_level> )"
|
|
"Summary: Disconnects paths on a vehicle and disables its obstacle."
|
|
"OptionalArg: <detail_level> : The type of bounding volume to create."
|
|
"Example: ent vehicle::disconnect_paths( detail_level );"
|
|
@/
|
|
function disconnect_paths( detail_level = 2, move_allowed = true ) // self == vehicle
|
|
{
|
|
self DisconnectPaths( detail_level, move_allowed );
|
|
self EnableObstacle( false );
|
|
}
|
|
|
|
/@
|
|
"Name: connect_paths()"
|
|
"Summary: Connects paths on a vehicle and enables its obstacle."
|
|
"Example: ent vehicle::connect_paths();"
|
|
@/
|
|
function connect_paths() // self == vehicle
|
|
{
|
|
self ConnectPaths();
|
|
self EnableObstacle( true );
|
|
}
|
|
|
|
function init_target_group() // self == vehicle
|
|
{
|
|
self.target_group = [];
|
|
}
|
|
|
|
function add_to_target_group( target_ent )
|
|
{
|
|
assert( isdefined( self.target_group ), "Call init_target_group() first." );
|
|
|
|
if ( !isdefined( self.target_group ) ) self.target_group = []; else if ( !IsArray( self.target_group ) ) self.target_group = array( self.target_group ); self.target_group[self.target_group.size]=target_ent;;
|
|
}
|
|
|
|
function remove_from_target_group( target_ent )
|
|
{
|
|
assert( isdefined( self.target_group ), "Call init_target_group() first." );
|
|
|
|
ArrayRemoveValue( self.target_group, target_ent );
|
|
}
|
|
|
|
function monitor_missiles_locked_on_to_me( player, wait_time = 0.1 ) // self == monitored_entity which holds the target group
|
|
{
|
|
monitored_entity = self;
|
|
|
|
monitored_entity endon( "death" );
|
|
|
|
assert( isdefined( monitored_entity.target_group ), "Call init_lock_on_group() first." );
|
|
|
|
player endon( "stop_monitor_missile_locked_on_to_me" );
|
|
player endon( "disconnect" );
|
|
player endon( "joined_team" );
|
|
|
|
// player endon( "death" ); // do not end on death as player can still drive while dead
|
|
|
|
while ( 1 )
|
|
{
|
|
closest_attacker = player get_closest_attacker_with_missile_locked_on_to_me( monitored_entity );
|
|
player SetVehicleLockedOnByEnt( closest_attacker );
|
|
|
|
wait wait_time;
|
|
}
|
|
}
|
|
|
|
function stop_monitor_missiles_locked_on_to_me() // self == player
|
|
{
|
|
self notify( "stop_monitor_missile_locked_on_to_me" );
|
|
}
|
|
|
|
function get_closest_attacker_with_missile_locked_on_to_me( monitored_entity ) // self == entity that holds target group
|
|
{
|
|
// gets the missile attacker that has the best dot relative to the player's camera view
|
|
|
|
assert( isdefined( monitored_entity.target_group ), "Call init_lock_on_group() first." );
|
|
|
|
player = self;
|
|
closest_attacker = undefined;
|
|
closest_attacker_dot = -999;
|
|
|
|
view_origin = player GetPlayerCameraPos();
|
|
view_forward = AnglesToForward( player GetPlayerAngles() );
|
|
|
|
// setup locked on flags for the group
|
|
remaining_locked_on_flags = 0;
|
|
foreach( target_ent in monitored_entity.target_group )
|
|
{
|
|
if ( isdefined( target_ent ) && isdefined( target_ent.locked_on ) )
|
|
{
|
|
remaining_locked_on_flags |= target_ent.locked_on;
|
|
}
|
|
}
|
|
|
|
// find the closest attacker
|
|
for ( i = 0; remaining_locked_on_flags && i < level.players.size; i++ )
|
|
{
|
|
attacker = level.players[ i ];
|
|
if ( isdefined( attacker ) )
|
|
{
|
|
client_flag = ( 1 << attacker getEntityNumber() );
|
|
if ( client_flag & remaining_locked_on_flags )
|
|
{
|
|
to_attacker = VectorNormalize( attacker.origin - view_origin );
|
|
attacker_dot = VectorDot( view_forward, to_attacker );
|
|
|
|
if ( attacker_dot > closest_attacker_dot )
|
|
{
|
|
closest_attacker = attacker;
|
|
closest_attacker_dot = attacker_dot;
|
|
}
|
|
|
|
remaining_locked_on_flags &= ~client_flag;
|
|
}
|
|
}
|
|
}
|
|
|
|
return closest_attacker;
|
|
}
|
|
|
|
function set_vehicle_drivable_time_starting_now( duration_ms ) // self == player
|
|
{
|
|
end_time_ms = GetTime() + duration_ms;
|
|
|
|
set_vehicle_drivable_time( duration_ms, end_time_ms );
|
|
|
|
return end_time_ms;
|
|
}
|
|
|
|
function set_vehicle_drivable_time( duration_ms, end_time_ms ) // self == player
|
|
{
|
|
self SetVehicleDrivableDuration( duration_ms );
|
|
self SetVehicleDrivableEndTime( end_time_ms );
|
|
}
|
|
|
|
function update_damage_as_occupant( damage_taken, max_health ) // self == player
|
|
{
|
|
damage_taken_normalized = math::clamp( damage_taken / max_health, 0.0, 1.0 );
|
|
self SetVehicleDamageMeter( damage_taken_normalized );
|
|
|
|
// /# IPrintLn( "Damage Taken: " + damage_taken_normalized ); #/
|
|
}
|
|
|
|
function stop_monitor_damage_as_occupant( ) // self == player
|
|
{
|
|
self notify( "stop_monitor_damage_as_occupant" );
|
|
}
|
|
|
|
function monitor_damage_as_occupant( player ) // self == vehicle
|
|
{
|
|
player endon( "disconnect" ); // note: no need to end on player death because player can still control
|
|
player notify( "stop_monitor_damage_as_occupant" ); // just make sure we never have two of these running
|
|
player endon( "stop_monitor_damage_as_occupant" );
|
|
self endon( "death" );
|
|
|
|
if( !IsDefined( self.maxhealth ) )
|
|
{
|
|
self.maxhealth = self.healthdefault; // healthdefault if from the GDT
|
|
}
|
|
|
|
wait 0.1;
|
|
player update_damage_as_occupant( self.maxhealth - self.health, self.maxhealth );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "damage" );
|
|
waittillframeend;
|
|
|
|
player update_damage_as_occupant( self.maxhealth - self.health, self.maxhealth );
|
|
}
|
|
}
|
|
|
|
// this was copied over from cp/_vehicle.gsc so we could nuke that file
|
|
function kill_vehicle( attacker )
|
|
{
|
|
damageOrigin = self.origin + (0,0,1);
|
|
self finishVehicleRadiusDamage(attacker, attacker, 32000, 32000, 10, 0, "MOD_EXPLOSIVE", level.weaponNone, damageOrigin, 400, -1, (0,0,1), 0);
|
|
}
|
|
|
|
function 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;
|
|
}
|
|
|
|
/#
|
|
function vehicle_spawner_tool()
|
|
{
|
|
allvehicles = GetEntArray( "script_vehicle", "classname" );
|
|
|
|
vehicletypes = [];
|
|
|
|
foreach( veh in allvehicles )
|
|
{
|
|
vehicletypes[ veh.vehicletype ] = veh.model;
|
|
}
|
|
|
|
if( IsAssetLoaded( "vehicle", "civ_pickup_mini" ) )
|
|
{
|
|
veh = SpawnVehicle( "civ_pickup_mini", (0,0,10000), (0,0,0), "debug_spawn_vehicle" );
|
|
vehicletypes[ veh.vehicletype ] = veh.model;
|
|
veh Delete();
|
|
}
|
|
|
|
if( IsAssetLoaded( "vehicle", "atv" ) )
|
|
{
|
|
veh = SpawnVehicle( "atv", (0,0,10000), (0,0,0), "debug_spawn_vehicle" );
|
|
vehicletypes[ veh.vehicletype ] = veh.model;
|
|
veh Delete();
|
|
}
|
|
|
|
if( IsAssetLoaded( "vehicle", "prowler_quad" ) )
|
|
{
|
|
veh = SpawnVehicle( "prowler_quad", (0,0,10000), (0,0,0), "debug_spawn_vehicle" );
|
|
vehicletypes[ veh.vehicletype ] = veh.model;
|
|
veh Delete();
|
|
}
|
|
|
|
if( IsAssetLoaded( "vehicle", "rc_car_racer" ) )
|
|
{
|
|
veh = SpawnVehicle( "rc_car_racer", (0,0,10000), (0,0,0), "debug_spawn_vehicle" );
|
|
vehicletypes[ veh.vehicletype ] = veh.model;
|
|
veh Delete();
|
|
}
|
|
|
|
if( IsAssetLoaded( "vehicle", "jeep_fav_player" ) )
|
|
{
|
|
veh = SpawnVehicle( "jeep_fav_player", (0,0,10000), (0,0,0), "debug_spawn_vehicle" );
|
|
vehicletypes[ veh.vehicletype ] = veh.model;
|
|
veh Delete();
|
|
}
|
|
|
|
types = getArrayKeys( vehicletypes );
|
|
|
|
if( types.size == 0 )
|
|
return;
|
|
|
|
type_index = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
if( GetDvarInt( "debug_vehicle_spawn" ) > 0 )
|
|
{
|
|
player = GetPlayers()[0];
|
|
|
|
dynamic_spawn_hud = NewClientHudElem( player );
|
|
dynamic_spawn_hud.alignX = "left";
|
|
dynamic_spawn_hud.x = 20;
|
|
dynamic_spawn_hud.y = 395;
|
|
dynamic_spawn_hud.fontscale = 2;
|
|
|
|
dynamic_spawn_dummy_model = sys::Spawn( "script_model", (0,0,0) );
|
|
|
|
const waittime = 0.3;
|
|
|
|
while( GetDvarInt( "debug_vehicle_spawn" ) > 0 )
|
|
{
|
|
origin = player.origin + AnglesToForward( player getPlayerAngles() ) * 270.0;
|
|
origin += (0,0,40);
|
|
|
|
if( player UseButtonPressed() )
|
|
{
|
|
dynamic_spawn_dummy_model Hide();
|
|
vehicle = SpawnVehicle( types[type_index], origin, player.angles, "debug_spawn_vehicle" );
|
|
vehicle MakeVehicleUsable();
|
|
|
|
if( GetDvarInt( "debug_vehicle_spawn" ) == 1 )
|
|
{
|
|
SetDvar( "debug_vehicle_spawn", "0" );
|
|
continue;
|
|
}
|
|
wait waittime;
|
|
}
|
|
if( player buttonpressed("DPAD_RIGHT") )
|
|
{
|
|
dynamic_spawn_dummy_model Hide();
|
|
type_index++;
|
|
if( type_index >= types.size )
|
|
type_index = 0;
|
|
wait waittime;
|
|
}
|
|
if( player buttonpressed("DPAD_LEFT") )
|
|
{
|
|
dynamic_spawn_dummy_model Hide();
|
|
type_index--;
|
|
if( type_index < 0 )
|
|
type_index = types.size - 1;
|
|
wait waittime;
|
|
}
|
|
type = types[type_index];
|
|
dynamic_spawn_hud settext("Press X to spawn vehicle " + type );
|
|
|
|
dynamic_spawn_dummy_model SetModel( vehicletypes[type] );
|
|
dynamic_spawn_dummy_model Show();
|
|
dynamic_spawn_dummy_model NotSolid();
|
|
dynamic_spawn_dummy_model.origin = origin;
|
|
dynamic_spawn_dummy_model.angles = player.angles;
|
|
{wait(.05);};
|
|
}
|
|
|
|
dynamic_spawn_hud destroy();
|
|
dynamic_spawn_dummy_model delete();
|
|
}
|
|
|
|
wait 2;
|
|
}
|
|
}
|
|
|
|
function spline_debug()
|
|
{
|
|
level flag::init( "debug_vehicle_splines" );
|
|
|
|
level thread _spline_debug();
|
|
|
|
while ( true )
|
|
{
|
|
level flag::set_val( "debug_vehicle_splines", GetDvarInt( "g_vehicleDrawSplines" ) );
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
function _spline_debug()
|
|
{
|
|
while ( true )
|
|
{
|
|
level flag::wait_till( "debug_vehicle_splines" );
|
|
|
|
foreach ( nd in GetAllVehicleNodes() )
|
|
{
|
|
nd show_node_debug_info();
|
|
}
|
|
|
|
wait .05;
|
|
}
|
|
}
|
|
|
|
function show_node_debug_info()
|
|
{
|
|
self.n_debug_display_count = 0;
|
|
|
|
if ( is_unload_node() )
|
|
{
|
|
print_debug_info( "unload: \"" + self.script_unload + "\"" );
|
|
}
|
|
|
|
if ( isdefined( self.script_notify ) )
|
|
{
|
|
print_debug_info( "notify: \"" + self.script_notify + "\"" );
|
|
}
|
|
|
|
if ( ( isdefined( self.script_delete ) && self.script_delete ) )
|
|
{
|
|
print_debug_info( "delete" );
|
|
}
|
|
}
|
|
|
|
function print_debug_info( str_info )
|
|
{
|
|
self.n_debug_display_count++;
|
|
Print3D( self.origin - ( 0, 0, self.n_debug_display_count * 20 ), str_info, ( 0, 0, 1 ), 1, 1 );
|
|
}
|
|
|
|
#/
|