IW4-Dump-Files/maps/_vehicle.gsc

7910 lines
213 KiB
Plaintext

/*
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
VEHICLE script
This handles playing the various effects and animations on a vehicle.
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
// vehicle_init( vehicle )
this give the vehicle life, treads, turrets, machine guns, all that good stuff
// 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
// vehicle_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
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
#include maps\_utility;
#include maps\_anim;
#include maps\_vehicle_aianim;
#include common_scripts\utility;
#using_animtree( "vehicles" );
CONST_MPHCONVERSION = 17.6;
CONST_bp_height = 300;
init_vehicles()
{
// For Engineering when they need test specific functionality of vehicles
if ( IsDefined( level.disableVehicleScripts ) && level.disableVehicleScripts )
return;
level.heli_default_decel = 10;// will likely go back to 10 in the near future
// thread dump_handle();// press the hotkey and xenon will export a map with all the current vehicle positions
init_helicopter_list();
init_airplane_list();
// vehicle related dvar initializing goes here
setup_dvars();
// initialize all the level wide vehicle system variables
setup_levelvars();
setup_vehicle_spawners();
array_thread( GetEntArray( "truckjunk", "targetname" ), ::truckjunk );
// pre - associate ai and spawners with their vehicles
setup_ai();
// pre - associate vehicle triggers and vehicle nodes with stuff.
setup_triggers();
// check precacheing of vehicle scripts.
allvehiclesprespawn = precache_scripts();
// setup spawners and non - spawning vehicles
setup_vehicles( allvehiclesprespawn );
// send the setup triggers to be processed
array_levelthread( level.vehicle_processtriggers, ::trigger_process, allvehiclesprespawn );
array_thread( getstructarray( "gag_stage_littlebird_unload", "script_noteworthy" ), ::setup_gag_stage_littlebird_unload );
array_thread( getstructarray( "gag_stage_littlebird_load", "script_noteworthy" ), ::setup_gag_stage_littlebird_load );
level.vehicle_processtriggers = undefined;
init_level_has_vehicles();
add_hint_string( "invulerable_frags", &"SCRIPT_INVULERABLE_FRAGS", undefined );
add_hint_string( "invulerable_bullets", &"SCRIPT_INVULERABLE_BULLETS", undefined );
}
init_helicopter_list()
{
level.helicopter_list = [];
level.helicopter_list[ "blackhawk" ] = true;
level.helicopter_list[ "blackhawk_minigun" ] = true;
level.helicopter_list[ "blackhawk_minigun_so" ] = true;
level.helicopter_list[ "apache" ] = true;
level.helicopter_list[ "seaknight" ] = true;
level.helicopter_list[ "seaknight_airlift" ] = true;
level.helicopter_list[ "hind" ] = true;
level.helicopter_list[ "mi17" ] = true;
level.helicopter_list[ "mi17_noai" ] = true;
level.helicopter_list[ "mi17_bulletdamage" ] = true;
level.helicopter_list[ "cobra" ] = true;
level.helicopter_list[ "cobra_player" ] = true;
level.helicopter_list[ "viper" ] = true;
level.helicopter_list[ "littlebird_player" ] = true;
level.helicopter_list[ "littlebird" ] = true;
level.helicopter_list[ "mi28" ] = true;
level.helicopter_list[ "pavelow" ] = true;
level.helicopter_list[ "pavelow_noai" ] = true;
level.helicopter_list[ "harrier" ] = true;
}
init_airplane_list()
{
level.airplane_list = [];
level.airplane_list[ "mig29" ] = true;
level.airplane_list[ "b2" ] = true;
}
init_level_has_vehicles()
{
level.levelHasVehicles = false;
// if there are any vehicles prespawned of any team in the level then there are vehicles
vehicles = GetEntArray( "script_vehicle", "code_classname" );
if ( vehicles.size > 0 )
level.levelHasVehicles = true;
}
trigger_getlinkmap( trigger )
{
linkMap = [];
if ( IsDefined( trigger.script_linkTo ) )
{
links = StrTok( trigger.script_linkTo, " " );
foreach ( link in links )
linkMap[ link ] = true;
links = undefined;
}
return linkMap;
}
// setup_script_gatetrigger( trigger, linkMap )
setup_script_gatetrigger( trigger )
{
gates = [];
if ( IsDefined( trigger.script_gatetrigger ) )
return level.vehicle_gatetrigger[ trigger.script_gatetrigger ];
return gates;
}
// setup_script_vehiclespawngroup( trigger, vehicles, linkMap )
setup_script_vehiclespawngroup( trigger, vehicles )
{
script_vehiclespawngroup = false;
if ( IsDefined( trigger.script_VehicleSpawngroup ) )
script_vehiclespawngroup = true;
return script_vehiclespawngroup;
}
setup_vehicle_spawners()
{
spawners = _getvehiclespawnerarray();
foreach ( spawner in spawners )
{
spawner thread vehicle_spawn_think();
}
}
vehicle_spawn_think()
{
if ( IsDefined( self.script_kill_vehicle_spawner ) )
{
group = self.script_kill_vehicle_spawner;
if ( !isdefined( level.vehicle_killspawn_groups[ group ] ) )
{
level.vehicle_killspawn_groups[ group ] = [];
}
level.vehicle_killspawn_groups[ group ][ level.vehicle_killspawn_groups[ group ].size ] = self;
}
if ( IsDefined( self.script_deathflag ) )
thread maps\_spawner::vehicle_spawner_deathflag();
self thread vehicle_linked_entities_think();
self.count = 1;
self.spawn_functions = [];
for ( ;; )
{
vehicle = undefined;
self waittill( "spawned", vehicle );
self.count--;
if ( !isdefined( vehicle ) )
{
PrintLn( "Vehicle spawned from spawner at " + self.origin + " but didnt exist!" );
continue;
}
vehicle.spawn_funcs = self.spawn_functions;
vehicle.spawner = self;
vehicle thread maps\_spawner::run_spawn_functions();
}
}
vehicle_linked_entities_think()
{
//hides linked entities until spwned, then shows and links them to the spawned vehicle, then deletes them when vehicle dies
if ( !isdefined( self.script_vehiclecargo ) )
return;
if ( !isdefined( self.script_linkTo ) )
return;
//this is just to get at least one of the ents it is linked to...code doesn't really support script_Linking to a prefab
aLinkedEnts = GetEntArray( self.script_linkTo, "script_linkname" );
if ( aLinkedEnts.size == 0 )
return;
//need cargo to have a unique targetname....we can't get script_linkTo arrays within a prefab,
//and we can't target a vehicle to the cargo since we need to target it to its nodes
targetname = aLinkedEnts[ 0 ].targetname;
aLinkedEnts = GetEntArray( targetname, "targetname" );
eOrg = undefined;
foreach ( ent in aLinkedEnts )
{
if ( ent.classname == "script_origin" )
eOrg = ent;
ent Hide();
}
AssertEx( IsDefined( eOrg ), "Vehicles that have script_linkTo pointing to entities must have one of those entities be a script_origin to be used as a link point of reference" );
foreach ( ent in aLinkedEnts )
{
if ( ent != eOrg )
ent LinkTo( eOrg );
}
self waittill( "spawned", vehicle );
foreach ( ent in aLinkedEnts )
{
ent Show();
if ( ent != eOrg )
ent LinkTo( vehicle );
}
vehicle waittill( "death" );
foreach ( ent in aLinkedEnts )
ent Delete();
}
is_trigger_once()
{
// these triggers only trigger once where vehicle paths trigger everytime a vehicle crosses them
if ( !isdefined( self.classname ) )
return false;
if ( self.classname == "trigger_multiple" )
return true;
if ( self.classname == "trigger_radius" )
return true;
if ( self.classname == "trigger_lookat" )
return true;
return self.classname == "trigger_disk";
}
trigger_process( trigger, vehicles )
{
bTriggerOnce = trigger is_trigger_once();
trigger.processed_trigger = undefined;// clear out this flag that was used to get the trigger to this point.
// override to make a trigger loop
if ( IsDefined( trigger.script_noteworthy ) && trigger.script_noteworthy == "trigger_multiple" )
bTriggeronce = false;
gates = setup_script_gatetrigger( trigger );
script_vehiclespawngroup = IsDefined( trigger.script_VehicleSpawngroup );
// 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;
vehicles = undefined;
while ( gotrigger )
{
trigger waittill( "trigger", other );
if ( IsDefined( trigger.script_vehicletriggergroup ) )
{
if ( !isdefined( other.script_vehicletriggergroup ) )
continue;
if ( 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.vehicle_flags ) )
other.vehicle_flags[ trigger.script_flag_set ] = true;
other notify( "vehicle_flag_arrived", trigger.script_flag_set );
flag_set( trigger.script_flag_set );
}
if ( IsDefined( trigger.script_flag_clear ) )
{
if ( IsDefined( other.vehicle_flags ) )
other.vehicle_flags[ trigger.script_flag_clear ] = false;
flag_clear( trigger.script_flag_clear );
}
if ( script_vehicledetour )
other thread path_detour_script_origin( trigger );
else if ( detoured && IsDefined( other ) )
other thread path_detour( trigger );
trigger 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_levelthread( level.vehicle_DeleteGroup[ trigger.script_vehicleGroupDelete ], ::deleteEnt );
}
if ( script_vehiclespawngroup )
{
scripted_spawn( trigger.script_VehicleSpawngroup );
}
if ( gates.size > 0 && bTriggeronce )
array_levelthread( gates, ::path_gate_open );
if ( IsDefined( trigger.script_VehicleStartMove ) )
{
if ( !isdefined( level.vehicle_StartMoveGroup[ trigger.script_VehicleStartMove ] ) )
{
PrintLn( "^3Vehicle start trigger is: ", trigger.script_VehicleStartMove );
return;
}
array_levelthread( level.vehicle_StartMoveGroup[ trigger.script_VehicleStartMove ], ::gopath );
}
}
}
path_detour_get_detourpath( detournode )
{
detourpath = undefined;
foreach ( vehicle_detourpath in level.vehicle_detourpaths[ detournode.script_vehicledetour ] )
{
if ( vehicle_detourpath != detournode )
if ( !islastnode( vehicle_detourpath ) )
detourpath = vehicle_detourpath;
}
return detourpath;
}
path_detour_script_origin( detournode )
{
detourpath = path_detour_get_detourpath( detournode );
if ( IsDefined( detourpath ) )
self thread vehicle_paths( detourpath );
}
crash_detour_check( detourpath )
{
Assert( IsDefined( detourpath.script_crashtype ) );
// long somewhat complex set of conditions on which a vehicle will detour through a crashpath.
return
(
(
IsDefined( self.deaddriver )
|| ( self.health < self.healthbuffer )
|| detourpath.script_crashtype == "forced"
)
&&
(
!isdefined( detourpath.derailed )
|| detourpath.script_crashtype == "plane"
)
);
}
crash_derailed_check( detourpath )
{
return IsDefined( detourpath.derailed ) && detourpath.derailed;
}
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 a detourpath have a crashtype it's a crashpath and should only be used by crashing vehicles.
if ( IsDefined( detourpath.script_crashtype ) )
{
if ( !crash_detour_check( detourpath ) )
return;
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 ;
}
self notify( "newpath" );
self _SetSwitchNode( detournode, detourpath );
thread detour_flag( detourpath );
if ( !islastnode( detournode ) && !( IsDefined( node.scriptdetour_persist ) && node.scriptdetour_persist ) )
node.detoured = 1;
self.attachedpath = detourpath;
thread vehicle_paths();
// handle transmission for physics vehicles.
if ( self Vehicle_IsPhysVeh() && IsDefined( detournode.script_transmission ) )
self thread reverse_node( detournode );
return;
}
}
reverse_node( detournode )
{
self endon( "death" );
detournode waittillmatch( "trigger", self );
self.veh_transmission = detournode.script_transmission;
if ( self.veh_transmission == "forward" )
self vehicle_wheels_forward();
else
self vehicle_wheels_backward();
}
_SetSwitchNode( detournode, detourpath )
{
AssertEx( !( detourpath.lookahead == 1 && detourpath.speed == 1 ), "Detourpath has lookahead and speed of 1, this is indicative that neither has been set." );
self SetSwitchNode( detournode, detourpath );
}
detour_flag( detourpath )
{
self endon( "death" );
self.detouringpath = detourpath;
detourpath waittillmatch( "trigger", self );
self.detouringpath = undefined;
}
vehicle_Levelstuff( vehicle, trigger )
{
// associate with links. false
if ( IsDefined( vehicle.script_linkName ) )
level.vehicle_link = array_2dadd( level.vehicle_link, vehicle.script_linkname, vehicle );
if ( IsDefined( vehicle.script_VehicleStartMove ) )
level.vehicle_StartMoveGroup = array_2dadd( level.vehicle_StartMoveGroup, vehicle.script_VehicleStartMove, vehicle );
if ( IsDefined( vehicle.script_vehicleGroupDelete ) )
level.vehicle_DeleteGroup = array_2dadd( level.vehicle_DeleteGroup, vehicle.script_vehicleGroupDelete, vehicle );
}
spawn_array( spawners )
{
ai = [];
stalinggradspawneverybody = ent_flag_exist( "no_riders_until_unload" ) ;
foreach ( spawner in spawners )
{
spawner.count = 1;
dronespawn = false;
if ( IsDefined( spawner.script_drone ) )
{
dronespawn = true;
spawned = dronespawn_bodyonly( spawner );
spawned maps\_drone::drone_give_soul();
Assert( IsDefined( spawned ) );
}
else
{
dontShareEnemyInfo = ( IsDefined( spawner.script_stealth ) && flag( "_stealth_enabled" ) && !flag( "_stealth_spotted" ) );
if ( IsDefined( spawner.script_forcespawn ) || stalinggradspawneverybody )
spawned = spawner StalingradSpawn( dontShareEnemyInfo );
else
spawned = spawner DoSpawn( dontShareEnemyInfo );
}
if ( !dronespawn && !isalive( spawned ) )
continue;
Assert( IsDefined( spawned ) );
ai[ ai.size ] = spawned;
}
ai = remove_non_riders_from_array( ai );
return ai;
}
remove_non_riders_from_array( aiarray )
{
living_ai = [];
foreach ( ai in aiarray )
{
if ( !ai_should_be_added( ai ) )
continue;
living_ai[ living_ai.size ] = ai;
}
return living_ai;
}
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";
}
/*
=============
///ScriptDocBegin
"Name: get_vehicle_ai_spawners( <get_vehicle_ai_spawners> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_vehicle_ai_spawners()
{
spawners = [];
if ( IsDefined( self.target ) )
{
targets = GetEntArray( self.target, "targetname" );
foreach ( target in targets )
{
if ( !IsSubStr( target.code_classname, "actor" ) )
continue;
if ( !( target.spawnflags & 1 ) )
continue;
if ( IsDefined( target.dont_auto_ride ) )
continue;
spawners[ spawners.size ] = target;
}
}
if ( !isdefined( self.script_vehicleride ) )
return spawners;
if ( IsDefined( level.vehicle_RideSpawners[ self.script_vehicleride ] ) )
spawners = array_combine( spawners, level.vehicle_RideSpawners[ self.script_vehicleride ] );
return spawners;
}
/*
=============
///ScriptDocBegin
"Name: get_vehicle_ai_riders()"
"Summary: returns ai's asigned to a vehicle through the radiant menu. Note that this doesn not return actual riders, for that just read vehicle.riders"
"Module: Entity"
"CallOn: An entity"
"Example: riders = self get_vehicle_ai_riders()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_vehicle_ai_riders()
{
if ( !isdefined( self.script_vehicleride ) )
return [];
if ( !isdefined( level.vehicle_RideAI[ self.script_vehicleride ] ) )
return [];
return level.vehicle_RideAI[ self.script_vehicleride ];
}
spawn_group()
{
if ( ent_flag_exist( "no_riders_until_unload" ) && !ent_flag( "no_riders_until_unload" ) )
{
return [];
}
spawners = get_vehicle_ai_spawners();
if ( !spawners.size )
return [];
startinvehicles = [];
ai = spawn_array( spawners );
ai = array_combine( ai, get_vehicle_ai_riders() );
ai = sort_by_startingpos( ai );
foreach ( guy in ai )
self thread maps\_vehicle_aianim::guy_enter( guy );
// disabling the array_levelthread because it threads them in reverse. I don't really want to be the one to mess with that right now.
// array_levelthread( ai, maps\_vehicle_aianim::guy_enter, self );
return ai;
}
sort_by_startingpos( guysarray )
{
firstarray = [];
secondarray = [];
foreach ( guy in guysarray )
{
if ( IsDefined( guy.script_startingposition ) )
firstarray[ firstarray.size ] = guy;
else
secondarray[ secondarray.size ] = guy;
}
return array_combine( firstarray, secondarray );
}
vehicle_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;
}
runtovehicle( guy )
{
guyarray = [];
//Todo: anim_reach this stuff
climbinnode = self.climbnode;
climbinanim = self.climbanim;
closenode = climbinnode[ 0 ];
currentdist = 5000;
thenode = undefined;
for ( i = 0; i < climbinnode.size; i++ )
{
climborg = self GetTagOrigin( climbinnode[ i ] );
climbang = self GetTagAngles( climbinnode[ i ] );
org = GetStartOrigin( climborg, climbang, climbinanim[ i ] );
distance = Distance( guy.origin, climborg );
if ( distance < currentdist )
{
currentdist = distance;
closenode = climbinnode[ i ];
thenode = i;
}
}
climbang = undefined;
climborg = undefined;
thread runtovehicle_setgoal( guy );
while ( !guy.vehicle_goal )
{
climborg = self GetTagOrigin( climbinnode[ thenode ] );
climbang = self GetTagAngles( climbinnode[ thenode ] );
org = GetStartOrigin( climborg, climbang, climbinanim[ thenode ] );
guy set_forcegoal();
guy SetGoalPos( org );
guy.goalradius = 64;
wait .25;
}
guy unset_forcegoal();
if ( self Vehicle_GetSpeed() < 1 )
{
guy LinkTo( self );
guy AnimScripted( "hopinend", climborg, climbang, climbinanim[ thenode ] );
guy waittillmatch( "hopinend", "end" );
self guy_enter_vehicle( guy );
}
}
runtovehicle_setgoal( guy )
{
guy.vehicle_goal = false;
self endon( "death" );
guy endon( "death" );
guy waittill( "goal" );
guy.vehicle_goal = true;
}
setup_groundnode_detour( node )
{
realdetournode = GetVehicleNode( node.targetname, "target" );
if ( !isdefined( realdetournode ) )
return;
realdetournode.detoured = 0;
AssertEx( !isdefined( realdetournode.script_vehicledetour ), "Detour nodes require one non-detour node before another detournode!" );
add_proccess_trigger( realdetournode );
}
turn_unloading_drones_to_ai()
{
unload_group = self get_unload_group();
foreach ( index, rider in self.riders )
{
if( !isalive( rider ) )
continue;
// does this guy unload?
if ( IsDefined( unload_group[ rider.vehicle_position ] ) )
self.riders[ index ] = self guy_becomes_real_ai( rider, rider.vehicle_position );
}
}
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;
level.vehicle_processtriggers[ level.vehicle_processtriggers.size ] = trigger;
trigger.processed_trigger = true;
}
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;
}
get_path_getfunc( pathpoint )
{
get_func = ::get_from_vehicle_node;
// get_func is differnt for struct types and script_origin types of paths
if ( isHelicopter() && IsDefined( pathpoint.target ) )
{
if ( IsDefined( get_from_entity( pathpoint.target ) ) )
get_func = ::get_from_entity;
if ( IsDefined( get_from_spawnstruct( pathpoint.target ) ) )
get_func = ::get_from_spawnstruct;
}
return get_func;
}
path_array_setup( pathpoint )
{
get_func = ::get_from_vehicle_node;
// get_func is differnt for struct types and script_origin types of paths
if ( isHelicopter() && IsDefined( pathpoint.target ) )
{
if ( IsDefined( get_from_entity( pathpoint.target ) ) )
get_func = ::get_from_entity;
if ( IsDefined( get_from_spawnstruct( pathpoint.target ) ) )
get_func = ::get_from_spawnstruct;
}
arraycount = 0;
pathpoints = [];
while ( IsDefined( pathpoint ) )
{
pathpoints[ arraycount ] = pathpoint;
arraycount++;
if ( IsDefined( pathpoint.target ) )
pathpoint = [[ get_func ]]( pathpoint.target );
else
break;
}
return pathpoints;
}
node_wait( nextpoint, lastpoint )
{
if ( self.attachedpath == nextpoint )
{
waittillframeend;
return;
}
nextpoint waittillmatch( "trigger", self );
// self SetWaitNode( nextpoint );
// self waittill( "reached_wait_node" );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_paths( <node> , <bhelicopterwaitforstart> )"
"Summary: Call this on a vehicle to send it on it's way down a chain of nodes,structs, or origins. "
"Module: Vehicle"
"CallOn: A vehicle"
"OptionalArg: <node>: start node of chain of nodes,structs, or origins. if unspecified script will search for targeted node."
"OptionalArg: <bhelicopterwaitforstart>: defaults to false. turning it on will make it wait for the gopath() command "
"Example: vehicle maps\_vehicle::vehicle_paths( struct );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_paths( node, bhelicopterwaitforstart )
{
AssertEx( IsDefined( node ) || IsDefined( self.attachedpath ), "vehicle_path() called without a path" );
self notify( "newpath" );
if ( !isdefined( bhelicopterwaitforstart ) )
bhelicopterwaitforstart = false;// helicopters emulate StartPath() function waiting for a special scripted notify before going
if ( ishelicopter() )
self endon( "death" );
// 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 endon( "newpath" );
// this converts chains of whatever types of vehicle nodes and turns them into this array,
// from this point most of the script won't care what kind of path it is.
// pathpoints = path_array_setup( pathpoint );
pathpoint = pathstart;
// dynamic paths / struct path unique
if ( bhelicopterwaitforstart )
self waittill( "start_dynamicpath" );
// default type is for vehicle nodes
wait_func = ::node_wait;
// wait_func is different for helicopters than vehicles on the ground
if ( isHelicopter() )
{
wait_func = ::heli_wait_node;
}
lastpoint = undefined;
nextpoint = pathstart;
get_func = get_path_getfunc( pathstart );
while ( IsDefined( nextpoint ) )
{
if ( isHelicopter() && IsDefined( nextpoint.script_linkTo ) )
set_lookat_from_dest( nextpoint );
[[ wait_func ]]( nextpoint, lastpoint );
if ( !isdefined( self ) )
return;
self.currentNode = nextpoint;
if ( IsDefined( nextpoint.gateopen ) && !nextpoint.gateopen )
self thread path_gate_wait_till_open( nextpoint );// threaded because vehicle may Vehicle_SetSpeed( 0, 15 ) and run into the next node
// pretend like helicopter nodes are triggers.
if ( isHelicopter() )
{
nextpoint notify( "trigger", self );
if ( IsDefined( nextpoint.script_helimove ) )
{
set_heli_move( nextpoint.script_helimove );
}
}
if ( IsDefined( nextpoint.script_noteworthy ) )
{
self notify( nextpoint.script_noteworthy );
self notify( "noteworthy", nextpoint.script_noteworthy );
}
waittillframeend;// this lets other scripts interupt
if ( !isdefined( self ) )
return;
if ( IsDefined( nextpoint.script_prefab_exploder ) )
{
nextpoint.script_exploder = nextpoint.script_prefab_exploder;
nextpoint.script_prefab_exploder = undefined;
}
if ( IsDefined( nextpoint.script_exploder ) )
{
delay = nextpoint.script_exploder_delay;
if ( IsDefined( delay ) )
{
level delayThread( delay, ::exploder, nextpoint.script_exploder );
}
else
{
level exploder( nextpoint.script_exploder );
}
}
if ( IsDefined( nextpoint.script_flag_set ) )
{
if ( IsDefined( self.vehicle_flags ) )
self.vehicle_flags[ nextpoint.script_flag_set ] = true;
self notify( "vehicle_flag_arrived", nextpoint.script_flag_set );
flag_set( nextpoint.script_flag_set );
}
if ( IsDefined( nextpoint.script_ent_flag_set ) )
{
self ent_flag_set( nextpoint.script_ent_flag_set );
}
if ( IsDefined( nextpoint.script_ent_flag_clear ) )
{
self ent_flag_clear( nextpoint.script_ent_flag_clear );
}
if ( IsDefined( nextpoint.script_flag_clear ) )
{
if ( IsDefined( self.vehicle_flags ) )
self.vehicle_flags[ nextpoint.script_flag_clear ] = false;
flag_clear( nextpoint.script_flag_clear );
}
if ( IsDefined( nextpoint.script_noteworthy ) )
{
if ( nextpoint.script_noteworthy == "kill" )
self force_kill();
if ( nextpoint.script_noteworthy == "godon" )
self godon();
if ( nextpoint.script_noteworthy == "godoff" )
self godoff();
if ( nextpoint.script_noteworthy == "deleteme" )
{
level thread deleteent( self );
return;// this could be disasterous
}
}
if ( IsDefined( nextpoint.script_crashtypeoverride ) )
self.script_crashtypeoverride = nextpoint.script_crashtypeoverride;
if ( IsDefined( nextpoint.script_badplace ) )
self.script_badplace = nextpoint.script_badplace;
if ( IsDefined( nextpoint.script_turretmg ) )
self.script_turretmg = nextpoint.script_turretmg;
if ( IsDefined( nextpoint.script_team ) )
self.script_team = nextpoint.script_team;
if ( IsDefined( nextpoint.script_turningdir ) )
self notify( "turning", nextpoint.script_turningdir );
if ( IsDefined( nextpoint.script_deathroll ) )
if ( nextpoint.script_deathroll == 0 )
self thread deathrolloff();
else
self thread deathrollon();
if ( IsDefined( nextpoint.script_vehicleaianim ) )
{
if ( IsDefined( nextpoint.script_parameters ) && nextpoint.script_parameters == "queue" )
self.queueanim = true;
// if ( IsDefined( nextpoint.script_startingposition ) )
// self.groupedanim_pos = nextpoint.script_startingposition;
// self vehicle_ai_event( nextpoint.script_vehicleaianim );
}
if ( IsDefined( nextpoint.script_wheeldirection ) )
self wheeldirectionchange( nextpoint.script_wheeldirection );
if ( vehicle_should_unload( wait_func, nextpoint ) )
self thread unload_node( nextpoint );
// physics vehicles have transmission "forward" or "reverse"
if ( self Vehicle_IsPhysVeh() )
{
if ( IsDefined( nextpoint.script_transmission ) )
{
self.veh_transmission = nextpoint.script_transmission;
if ( self.veh_transmission == "forward" )
self vehicle_wheels_forward();
else
self vehicle_wheels_backward();
}
if ( IsDefined( nextpoint.script_pathtype ) )
self.veh_pathtype = nextpoint.script_pathtype;
}
if ( IsDefined( nextpoint.script_delay ) )
{
if ( isHelicopter() )
{
// helicopters do the script_delay in heli_wait_node()
}
else
{
decel = 35;
if ( IsDefined( nextpoint.script_decel ) )
decel = nextpoint.script_decel;
self Vehicle_SetSpeed( 0, decel );
if ( IsDefined( nextpoint.target ) )
self thread overshoot_next_node( [[ get_func ]]( nextpoint.target ) );
nextpoint script_delay();
self notify( "delay_passed" );
self ResumeSpeed( 60 );
}
}
if ( IsDefined( nextpoint.script_flag_wait ) )
{
if ( !isdefined( self.vehicle_flags ) )
{
self.vehicle_flags = [];
}
self.vehicle_flags[ nextpoint.script_flag_wait ] = true;
self notify( "vehicle_flag_arrived", nextpoint.script_flag_wait );
// 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 ( !flag( nextpoint.script_flag_wait ) || IsDefined( nextpoint.script_delay_post ) )
{
if ( !isHelicopter() )
{
decel = 35;
if ( IsDefined( nextpoint.script_decel ) )
decel = nextpoint.script_decel;
self Vehicle_SetSpeed( 0, decel );
self thread overshoot_next_node( [[ get_func ]]( nextpoint.target ) );
}
}
// wait at the end point if it has flag wait
flag_wait( nextpoint.script_flag_wait );
// added script_delay_post to vehicle paths
if ( IsDefined( nextpoint.script_delay_post ) )
wait nextpoint.script_delay_post;
if ( !isHelicopter() )
{
accel = 10;
if ( IsDefined( nextpoint.script_accel ) )
accel = nextpoint.script_accel;
self ResumeSpeed( accel );
}
self notify( "delay_passed" );
}
if ( IsDefined( self.set_lookat_point ) )
{
self.set_lookat_point = undefined;
self ClearLookAtEnt();
}
if ( IsDefined( nextpoint.script_vehicle_lights_off ) )
self thread lights_off( nextpoint.script_vehicle_lights_off );
if ( IsDefined( nextpoint.script_vehicle_lights_on ) )
self thread lights_on( nextpoint.script_vehicle_lights_on );
if ( IsDefined( nextpoint.script_forcecolor ) )
self thread vehicle_script_forcecolor_riders( nextpoint.script_forcecolor );
lastpoint = nextpoint;
if ( !isdefined( nextpoint.target ) )
break;
nextpoint = [[ get_func ]]( nextpoint.target );
if( !isdefined( nextpoint ) )
{
nextpoint = lastpoint;
assertmsg( "can't find nextpoint for node at origin (node targets nothing or different type?): " + lastpoint.origin );
break;
}
}
if ( IsDefined( self.script_turretmg ) )
{
if ( self.script_turretmg == 1 )
{
self mgOn();
}
else
{
self mgOff();
}
}
if ( IsDefined( nextpoint.script_land ) )
self thread vehicle_landvehicle();
self notify( "reached_dynamic_path_end" );
if ( IsDefined( self.script_vehicle_selfremove ) )
self Delete();
}
vehicle_should_unload( wait_func, nextpoint )
{
// if ( IsDefined( nextpoint.script_unload ) || ( wait_func == ::node_wait && islastnode( nextpoint ) && !isdefined( self.dontunloadonend ) && !is_script_vehicle_selfremove() ) )
if ( IsDefined( nextpoint.script_unload ) )
return true;
if ( wait_func != ::node_wait )
return false;
if ( !islastnode( nextpoint ) )
return false;
if ( isdefined( self.dontunloadonend ) )
return false;
if ( self.vehicletype == "empty" )
return false;
return !is_script_vehicle_selfremove();
}
overshoot_next_node( vnode )
{
// asserts if the next node in a chain is reached while trying to come to a complete stop.
// This can happen if the deceleration is too low and/or the next node is too close the the delay node.
// If this happens the vehicle script will have missed the notify on the upcomming node and be stuck waiting for it.
/#
if ( !isdefined( vnode ) )
return;
self endon( "delay_passed" );
vnode waittillmatch( "trigger", self );
PrintLn( "^1**************************************************************************************" );
PrintLn( "^1****** WARNING!!! ********************************************************************" );
PrintLn( "^1**************************************************************************************" );
PrintLn( "^1A vehicle most likely overshoot a node at " + vnode.origin + " while trying to come to a stop." );
PrintLn( "^1This will stop any future nodes for that vehicle to be handled by the vehicle script." );
PrintLn( "^1**************************************************************************************" );
#/
}
is_script_vehicle_selfremove()
{
if ( !isdefined( self.script_vehicle_selfremove ) )
return false;
return self.script_vehicle_selfremove;
}
/*
=============
///ScriptDocBegin
"Name: set_heli_move( <heliMove> )"
"Summary: Makes a heli turn with specific presets"
"Module: Vehicle"
"CallOn: A helicopter"
"MandatoryArg: <heliMove>: The type of turning"
"Example: heli set_heli_move( "fast" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_heli_move( heliMove )
{
switch( heliMove )
{
case "instant":
self SetYawSpeed( 290, 245, 222.5, 0 );
break;
case "faster":
self SetMaxPitchRoll( 25, 50 );
self SetYawSpeed( 180, 90, 22.5, 0 );
break;
case "fast":
self SetYawSpeed( 90, 45, 22.5, 0 );
break;
case "slow":
self SetYawSpeed( 15, 5, 15, 0 );
break;
default:
self SetYawSpeed( 90, 45, 22.5, 0 );
break;
}
}
must_stop_at_next_point( nextpoint )
{
// gotta be able to slow down for unload nodes
if ( IsDefined( nextpoint.script_unload ) )
return true;
if ( IsDefined( nextpoint.script_delay ) )
return true;
// gotta stop if it depends on a flag
return IsDefined( nextpoint.script_flag_wait ) && !flag( nextpoint.script_flag_wait );
}
heli_wait_node( nextpoint, lastpoint )
{
self endon( "newpath" );
// this handles a single node on helicopter path. they are script_structs in radiant, or script_origins
if ( IsDefined( nextpoint.script_unload ) && IsDefined( self.fastropeoffset ) )
{
nextpoint.radius = 2;
neworg = groundpos( nextpoint.origin ) + ( 0, 0, self.fastropeoffset );
if ( neworg[ 2 ] > nextpoint.origin[ 2 ] - 2000 )
{
// dont descend if it's going to be a huge drop, the designer may intend for it to drop guys behind a wall
// where there is no geo for it to align with
nextpoint.origin = groundpos( nextpoint.origin ) + ( 0, 0, self.fastropeoffset );
}
self SetHoverParams( 0, 0, 0 );
}
if ( IsDefined( lastpoint ) )
{
if ( IsDefined( lastpoint.script_airresistance ) )
{
self SetAirResistance( lastpoint.script_airresistance );
}
if ( IsDefined( lastpoint.speed ) )
{
speed = lastpoint.speed;
accel = 25;
decel = undefined;
if ( IsDefined( lastpoint.script_decel ) )
{
decel = lastpoint.script_decel;
}
else
{
if ( must_stop_at_next_point( nextpoint ) )
{
// decel = speed;
}
}
if ( IsDefined( lastpoint.script_accel ) )
{
accel = lastpoint.script_accel;
}
else
{
max_accel = speed / 4;
if ( accel > max_accel )
{
accel = max_accel;
}
if ( accel < 15 )
accel = 15;
}
if ( IsDefined( decel ) )
{
self Vehicle_SetSpeed( speed, accel, decel );
}
else
{
self Vehicle_SetSpeed( speed, accel );
}
}
else
{
if ( must_stop_at_next_point( nextpoint ) )
{
// self Vehicle_SetSpeed( 60, 15, 60 );
}
}
}
self set_heli_goal( nextpoint );
if ( IsDefined( nextpoint.radius ) )
{
self SetNearGoalNotifyDist( nextpoint.radius );
AssertEx( nextpoint.radius > 0, "radius: " + nextpoint.radius );
self waittill_any( "near_goal", "goal" );
}
else
{
self waittill( "goal" );
}
/#
if ( IsDefined( nextpoint.script_flag_set ) )
self notify( "reached_current_node", nextpoint, nextpoint.script_flag_set );
else
self notify( "reached_current_node", nextpoint );
#/
if ( IsDefined( nextpoint.script_firelink ) )
{
thread heli_firelink( nextpoint );
// self SetVehWeapon( "hind_turret" );
}
if ( IsDefined( nextpoint.script_stopnode ) )
{
if ( nextpoint.script_stopnode )
self notify( "reached_stop_node" );
nextpoint script_delay();
}
nextpoint script_delay();
}
heli_firelink( nextpoint )
{
target = GetEnt( nextpoint.script_linkto, "script_linkname" );
if ( !isdefined( target ) )
{
target = getstruct( nextpoint.script_linkto, "script_linkname" );
AssertEx( IsDefined( target ), "No target for script_firelink" );
}
fire_burst = nextpoint.script_fireLink;
switch( fire_burst )
{
case "zippy_burst":
wait( 1 );
maps\_helicopter_globals::fire_missile( "hind_zippy", 1, target );
wait( 0.1 );
maps\_helicopter_globals::fire_missile( "hind_zippy", 1, target );
wait( 0.2 );
maps\_helicopter_globals::fire_missile( "hind_zippy", 1, target );
wait( 0.3 );
maps\_helicopter_globals::fire_missile( "hind_zippy", 1, target );
wait( 0.3 );
maps\_helicopter_globals::fire_missile( "hind_zippy", 1, target );
break;
default:
// if its unknown, do the default for this class of vehicle
if ( self.classname == "script_vehicle_littlebird_armed" )
{
maps\_attack_heli::heli_fire_missiles( target, 2, 0.25 );
}
else
{
maps\_helicopter_globals::fire_missile( "hind_zippy", 5, target, 0.3 );
}
break;
}
}
helipath( msg, maxspeed, accel )
{
// depreciated
// gets a path from the targetname that is passed
// sets the lookat for the vehicle to ents that are script_linkname'd to the path
self SetAirResistance( 30 );
self Vehicle_SetSpeed( maxspeed, accel, level.heli_default_decel );
vehicle_paths( getstruct( msg, "targetname" ) );
}
set_heli_goal( node )
{
self endon( "death" );
stop = false;
if ( !isdefined( stop ) )
stop = true;
if ( IsDefined( node.script_stopnode ) )// z: stop at nodes if there is a script_stopnode = 1 value
stop = node.script_stopnode;
if ( IsDefined( node.script_unload ) )
stop = true;
script_anglevehicle = IsDefined( node.script_anglevehicle ) && node.script_anglevehicle;
script_goalyaw = IsDefined( node.script_goalyaw ) && node.script_goalyaw;
if ( IsDefined( node.script_anglevehicle ) || IsDefined( node.script_goalyaw ) )
self forcetarget( node, script_goalyaw, script_anglevehicle );
else
self unforcetarget();
if ( IsDefined( node.script_flag_wait ) )
{
if ( !flag( node.script_flag_wait ) )
{
// if the flag gets set during flight, we should update the setvehgoalpos to not stop
stop = true;
}
}
if ( !isdefined( node.target ) )
{
// stop if this is the end of the path
stop = true;
}
else
if ( IsDefined( node.script_delay ) )
{
stop = true;
}
/#
index = -1;
foreach ( index, struct in level.struct )
{
if ( node == struct )
break;
}
#/
self setvehgoalpos_wrap( node.origin, stop );// Z: second param = false dont stop at each node.
}
forcetarget( node, script_goalyaw, script_anglevehicle )
{
AssertEx( IsDefined( node.angles ), "Node with targetname ", node.target, " has no .angles" );
// [ 14:45 ] [ jiesang - ?? ]: lookat entity > goalyaw > targetyaw
if ( script_goalyaw )
{
self ClearTargetYaw();
self SetGoalYaw( node.angles[ 1 ] );
}
else
{
self ClearGoalYaw();// clear this thing
self SetTargetYaw( node.angles[ 1 ] );
}
}
unforcetarget()
{
self ClearGoalYaw();// clear this thing
self ClearTargetYaw();// clear the stuff
}
deathrollon()
{
if ( self.health > 0 )
self.rollingdeath = 1;
}
deathrolloff()
{
self.rollingdeath = undefined;
self notify( "deathrolloff" );
}
/*
=============
///ScriptDocBegin
"Name: getonpath( <getonpath> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
getonpath( skip_attach )
{
path_start = undefined;
type = self.vehicletype;
if ( isdefined( self.dontgetonpath ) )
return;
if ( IsDefined( self.target ) )
{
path_start = GetVehicleNode( self.target, "targetname" );
/#
if ( ishelicopter() && IsDefined( path_start ) )
{
PrintLn( "helicopter node targetname: " + path_start.targetname );
PrintLn( "vehicletype: " + self.vehicletype );
AssertMsg( "helicopter on vehicle path( see console for info )" );
}
#/
if ( !isdefined( path_start ) )
{
// get path start from the array of targets that may include guys that ride in the vehicle
path_start_array = GetEntArray( self.target, "targetname" );
foreach ( path in path_start_array )
{
if ( path.code_classname == "script_origin" )
{
path_start = path;
break;
}
}
}
if ( !isdefined( path_start ) )
{
path_start = getstruct( self.target, "targetname" );
}
}
if ( !isdefined( path_start ) )
{
if ( ishelicopter() )
self Vehicle_SetSpeed( 60, 20, level.heli_default_decel );
return;
}
self.attachedpath = path_start;
if ( !isHelicopter() )
{
self.origin = path_start.origin;
if ( !isdefined( skip_attach ) )
self AttachPath( path_start );
}
else
{
if ( IsDefined( self.speed ) )
{
self Vehicle_SetSpeedImmediate( self.speed, 20 );
}
else
if ( IsDefined( path_start.speed ) )
{
accel = 20;
decel = level.heli_default_decel;
if ( IsDefined( path_start.script_accel ) )
accel = path_start.script_accel;
if ( IsDefined( path_start.script_decel ) )
accel = path_start.script_decel;
self Vehicle_SetSpeedImmediate( path_start.speed, accel, decel );
}
else
{
// default heli speed
self Vehicle_SetSpeed( 60, 20, level.heli_default_decel );
}
}
if ( !isdefined( self.dontDisconnectPaths ) )
self DisconnectPaths();
self thread vehicle_paths( undefined, isHelicopter() );
}
/*
=============
///ScriptDocBegin
"Name: create_vehicle_from_spawngroup_and_gopath( <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: maps\_vehicle::create_vehicle_from_spawngroup_and_gopath( spawnGroup )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
create_vehicle_from_spawngroup_and_gopath( spawnGroup )
{
vehicleArray = maps\_vehicle::scripted_spawn( spawnGroup );
foreach ( vehicle in vehicleArray )
level thread maps\_vehicle::gopath( vehicle );
return vehicleArray;
}
/*
=============
///ScriptDocBegin
"Name: gopath( <vehicle> )"
"Summary: Helis notify reached_dynamic_path_end on end"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
gopath( vehicle )
{
// helis notify reached_dynamic_path_end on end
if ( !isdefined( vehicle ) )
{
vehicle = self;
AssertEx( self.code_classname == "script_vehicle", "Tried to do goPath on a non-vehicle" );
}
if ( IsDefined( vehicle.script_VehicleStartMove ) )
level.vehicle_StartMoveGroup[ vehicle.script_VehicleStartMove ] = array_remove( level.vehicle_StartMoveGroup[ vehicle.script_VehicleStartMove ], vehicle );
vehicle endon( "death" );
if ( IsDefined( vehicle.hasstarted ) )
{
PrintLn( "vehicle already moving when triggered with a startmove" );
return;
}
else
vehicle.hasstarted = true;
// I wonder if anybody uses this still. I rember using it for cars sitting on the side of the road in CoD1. heh.
vehicle script_delay();
vehicle notify( "start_vehiclepath" );
if ( vehicle isHelicopter() )
vehicle notify( "start_dynamicpath" );
else
vehicle StartPath();
}
path_gate_open( node )
{
node.gateopen = true;
node notify( "gate opened" );
}
path_gate_wait_till_open( pathspot )
{
self endon( "death" );
self.waitingforgate = true;
self notify( "wait for gate" );
self vehicle_setspeed_wrapper( 0, 15, "path gate closed" );
pathspot waittill( "gate opened" );
self.waitingforgate = false;
if ( self.health > 0 )
script_resumespeed( "gate opened", level.vehicle_ResumeSpeed );
}
/*
=============
///ScriptDocBegin
"Name: scripted_spawn( <group> )"
"Summary: spawns and returns a vehiclegroup, you will need to tell it to maps\_vehicle::gopath() when you want it to go"
"Module: Vehicle"
"CallOn: An entity"
"MandatoryArg: <group> : "
"Example: bmps = maps\_vehicle::scripted_spawn( 32 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
scripted_spawn( group )
{
spawners = _getvehiclespawnerarray_by_spawngroup( group );
vehicles = [];
foreach ( spawner in spawners )
vehicles[ vehicles.size ] = vehicle_spawn( spawner );
return vehicles;
}
/*
=============
///ScriptDocBegin
"Name: vehicle_spawn( <spawner> )"
"Summary: spawnes a vehicle from the given vehicle spawner."
"Module: Vehicle"
LevelOn: A Level"
"MandatoryArg: <spawner>: "
"Example: level.reinforcement_heli = vehicle_spawn( spawner );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_spawn( vspawner )
{
Assert( isSpawner( vspawner ) );
AssertEx( !isdefined( vspawner.vehicle_spawned_thisframe ), "spawning two vehicles on one spawner on the same frame is not allowed" );
vehicle = vspawner Vehicle_DoSpawn();
Assert( IsDefined( vehicle ) );
vspawner.vehicle_spawned_thisframe = vehicle;
vspawner.last_spawned_vehicle = vehicle;
vspawner thread remove_vehicle_spawned_thisframe();
thread vehicle_init( vehicle );
// want to get this put in code and rearrange all this stuff so that people can use Vehicle_DoSpawn() directly and not have to initialize the vehicle scripts.
vspawner notify( "spawned", vehicle );
return vehicle;
}
// this is getting a little strange.. kind of a special case for a mo script that I really don't want to spend time unravelling scoutsniper
get_vehicle_spawned_from_spawner_with_targetname( targetname )
{
spawner = GetEnt( targetname, "targetname" );
Assert( IsDefined( spawner ) );
if ( IsDefined( spawner.last_spawned_vehicle ) )
return spawner.last_spawned_vehicle;
return undefined;
}
remove_vehicle_spawned_thisframe()
{
wait .05;
self.vehicle_spawned_thisframe = undefined;
}
waittill_vehiclespawn( targetname )
{
spawner = GetEnt( targetname, "targetname" );
Assert( isSpawner( spawner ) );
// for those vehicles spawned on the first frame. trying to avoid waittillframeend juggling though I don't think this is much better =/.
if ( IsDefined( spawner.vehicle_spawned_thisframe ) )
return spawner.vehicle_spawned_thisframe;
spawner waittill( "spawned", vehicle );
return vehicle;
}
waittill_vehiclespawn_noteworthy( noteworthy )
{
potential_spawners = GetEntArray( noteworthy, "script_noteworthy" );
spawner = undefined;
foreach ( test in potential_spawners )
{
if ( isSpawner( test ) )
{
spawner = test;
break;
}
}
Assert( IsDefined( spawner ) );
// for those vehicles spawned on the first frame. trying to avoid waittillframeend juggling though I don't think this is much better =/.
if ( IsDefined( spawner.vehicle_spawned_thisframe ) )
return spawner.vehicle_spawned_thisframe;
spawner = GetEnt( noteworthy, "script_noteworthy" );
spawner waittill( "spawned", vehicle );
return vehicle;
}
waittill_vehiclespawn_noteworthy_array( noteworthy )
{
struct = SpawnStruct();
struct.array_count = 0;
struct.vehicles = [];
array = [];
potentials_array = GetEntArray( noteworthy, "script_noteworthy" );
foreach ( test in potentials_array )
{
if ( isSpawner( test ) )
array[ array.size ] = test;
}
Assert( array.size );
array_levelthread( array, ::waittill_vehiclespawn_noteworthy_array_countdown, struct );
struct waittill( "all_vehicles_spawned" );
return struct.vehicles;
}
waittill_vehiclespawn_noteworthy_array_countdown( spawner, struct )
{
struct.array_count++;
// for those vehicles spawned on the first frame. trying to avoid waittillframeend juggling though I don't think this is much better =/.
if ( !isdefined( spawner.vehicle_spawned_thisframe ) )
spawner waittill( "spawned", vehicle );
else
vehicle = spawner.vehicle_spawned_thisframe;
Assert( IsDefined( vehicle ) );
struct.array_count--;
struct.vehicles[ struct.vehicles.size ] = vehicle;
if ( !struct.array_count )
struct notify( "all_vehicles_spawned" );
}
vehicle_init( vehicle )
{
Assert( vehicle.classname != "script_model" );
if ( vehicle.vehicletype == "empty" )
{
vehicle thread getonpath();
return;
}
if ( vehicle.vehicletype == "bog_mortar" )
return;
if ( ( IsDefined( vehicle.script_noteworthy ) ) && ( vehicle.script_noteworthy == "playervehicle" ) )
return;// TODO: I really don't think we should branch off the players vehicle so early. - nate
vehicle set_ai_number();// unique id for each vehicle or ai
// TODO: These shouldn't be asigned to everyvehicle
vehicle.zerospeed = true;
if ( !isdefined( vehicle.modeldummyon ) )
vehicle.modeldummyon = false;
type = vehicle.vehicletype;
// give the vehicle health
vehicle vehicle_life();
// set the script_team value used everywhere to determine which team the vehicle belongs to
vehicle vehicle_setteam();
// init pointer is specified in the precache script( IE maps\_tiger::main() )
// only special case gag works should exist in this thread,
if ( !isdefined( level.vehicleInitThread[ vehicle.vehicletype ][ vehicle.model ] ) )
{
PrintLn( "vehicle.vehicletype is: " + vehicle.vehicletype );
PrintLn( "vehicle.model is: " + vehicle.model );
}
vehicle thread [[ level.vehicleInitThread[ vehicle.vehicletype ][ vehicle.model ] ]]();
vehicle thread maingun_FX();
vehicle thread playTankExhaust();
if ( !isdefined( vehicle.script_avoidplayer ) )
vehicle.script_avoidplayer = false;
vehicle ent_flag_init( "unloaded" );
vehicle ent_flag_init( "loaded" );
vehicle.riders = [];
vehicle.unloadque = [];// for ai. wait till a vehicle is unloaded all the way
vehicle.unload_group = "default";
vehicle.fastroperig = [];
if ( IsDefined( level.vehicle_attachedmodels ) && IsDefined( level.vehicle_attachedmodels[ type ] ) )
{
rigs = level.vehicle_attachedmodels[ type ];
strings = GetArrayKeys( rigs );
foreach ( string in strings )
{
vehicle.fastroperig[ string ] = undefined;
vehicle.fastroperiganimating[ string ] = false;
}
}
// make ai run way from vehicle
vehicle thread vehicle_badplace();
// toggle vehicle lights on / off
if ( IsDefined( vehicle.script_vehicle_lights_on ) )
vehicle thread lights_on( vehicle.script_vehicle_lights_on );
if ( IsDefined( vehicle.script_godmode ) )
{
vehicle godon();
}
// regenerate friendly fire damage
if ( !vehicle isCheap() )
vehicle thread friendlyfire_shield();
// handles guys riding and doing stuff on vehicles
vehicle thread maps\_vehicle_aianim::handle_attached_guys();
if ( isdefined( vehicle.script_friendname ) )
vehicle setVehicleLookAtText( vehicle.script_friendname, &"" );
// special stuff for unloading
if ( !vehicle isCheap() )
vehicle thread vehicle_handleunloadevent();
if ( isdefined( vehicle.script_dontunloadonend ) )
vehicle.dontunloadonend = true;
// Make the main turret think
vehicle thread turret_attack_think();
// Shellshock player on main turret fire.
if ( !vehicle isCheap() )
vehicle thread vehicle_shoot_shock();// moved to indiviual tank scripts.
// make the vehicle rumble
vehicle thread vehicle_rumble();
// make vehicle shake physics objects.
if ( IsDefined( vehicle.script_physicsjolt ) && vehicle.script_physicsjolt )
vehicle thread physicsjolt_proximity();
// handle tread effects
vehicle thread vehicle_treads();
// handle the compassicon for friendly vehicles
vehicle thread vehicle_compasshandle();
vehicle thread idle_animations();
// make the wheels rotate
vehicle thread animate_drive_idle();
if ( IsDefined( vehicle.script_deathflag ) )
{
vehicle thread maps\_spawner::vehicle_deathflag();
}
// handle machine guns
if ( !vehicle isCheap() )
vehicle thread mginit();
if ( IsDefined( level.vehicleSpawnCallbackThread ) )
level thread [[ level.vehicleSpawnCallbackThread ]]( vehicle );
// this got kind of ugly and hackery but it's how I deal with player driveable vehicles in decoytown, elalamein, 88ridge and libya
// if ( IsDefined( vehicle.spawnflags ) && vehicle.spawnflags & 1 )
// {
// startinvehicle = ( IsDefined( vehicle.script_noteworthy ) && vehicle.script_noteworthy == "startinside" );// can't see making a whole new keys.txt entry for something that's only going to be used once in any given level.
// vehicle maps\_vehicledrive::setup_vehicle_other();
// vehicle thread maps\_vehicledrive::vehicle_wait( startinvehicle );
// vehicle_Levelstuff( vehicle );
// vehicle thread Kill();
// return;
// }
// associate vehicle with living level variables.
vehicle_Levelstuff( vehicle );
if ( IsDefined( vehicle.script_team ) )
vehicle SetVehicleTeam( vehicle.script_team );
// every vehicle that stops will disconnect its paths
if ( !vehicle isCheap() )
vehicle thread disconnect_paths_whenstopped();
// get on path and start the path handler thread
vehicle thread getonpath();
// helicopters do dust kickup fx
if ( vehicle hasHelicopterDustKickup() )
vehicle thread aircraft_dust_kickup();
// physics vehicles have pathtypes constrained or follow
if ( vehicle Vehicle_IsPhysVeh() )
{
if ( !isdefined( vehicle.script_pathtype ) )
{
//vehicle.veh_pathtype = "follow";
}
else
{
vehicle.veh_pathtype = vehicle.script_pathtype;
}
}
// spawn the vehicle and it's associated ai
vehicle spawn_group();
vehicle thread vehicle_kill();
vehicle apply_truckjunk();
}
kill_damage( type )
{
if ( !isdefined( level.vehicle_death_radiusdamage ) || !isdefined( level.vehicle_death_radiusdamage[ type ] ) )
return;
if ( IsDefined( self.deathdamage_max ) )
maxdamage = self.deathdamage_max;
else
maxdamage = level.vehicle_death_radiusdamage[ type ].maxdamage;
if ( IsDefined( self.deathdamage_min ) )
mindamage = self.deathdamage_min;
else
mindamage = level.vehicle_death_radiusdamage[ type ].mindamage;
if ( IsDefined( level.vehicle_death_radiusdamage[ type ].delay ) )
wait level.vehicle_death_radiusdamage[ type ].delay;
if ( !isdefined( self ) )
return;// deleted in this time.
if ( level.vehicle_death_radiusdamage[ type ].bKillplayer )
level.player EnableHealthShield( false );
self RadiusDamage( self.origin + level.vehicle_death_radiusdamage[ type ].offset, level.vehicle_death_radiusdamage[ type ].range, maxdamage, mindamage, self );
if ( level.vehicle_death_radiusdamage[ type ].bKillplayer )
level.player EnableHealthShield( true );
}
vehicle_kill()
{
self endon( "nodeath_thread" );
type = self.vehicletype;
model = self.model;
targetname = self.targetname;
attacker = undefined;
cause = undefined;
registered_kill = false;
while ( 1 )
{
// waittill death twice. in some cases the vehicle dies and does a bunch of stuff. then it gets deleted. which it then needs to do more stuff
if ( IsDefined( self ) )
self waittill( "death", attacker, cause );
if ( !registered_kill )
{
registered_kill = true;
if ( IsDefined( attacker ) && IsDefined( cause ) )
{
attacker maps\_player_stats::register_kill( self, cause );
if ( IsDefined( self.damage_type ) )
{
self.damage_type = undefined;
}
}
}
self notify( "clear_c4" );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// some tank and turret cleanup
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if ( IsDefined( self.rumbletrigger ) )
self.rumbletrigger Delete();
if ( IsDefined( self.mgturret ) )
{
array_levelthread( self.mgturret, ::turret_deleteme );
self.mgturret = undefined;
}
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if ( IsDefined( self.script_team ) )
level.vehicles[ self.script_team ] = array_remove( level.vehicles[ self.script_team ], self );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// previously unstuff
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if ( IsDefined( self.script_linkName ) )
level.vehicle_link[ self.script_linkName ] = array_remove( level.vehicle_link[ self.script_linkName ], self );
// dis - associate with targets
if ( IsDefined( self.script_VehicleStartMove ) )
level.vehicle_StartMoveGroup[ self.script_VehicleStartMove ] = array_remove( level.vehicle_StartMoveGroup[ self.script_VehicleStartMove ], self );
if ( IsDefined( self.script_vehicleGroupDelete ) )
level.vehicle_DeleteGroup[ self.script_vehicleGroupDelete ] = array_remove( level.vehicle_DeleteGroup[ self.script_vehicleGroupDelete ], self );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// if vehicle is gone then delete the ai here.
if ( !isdefined( self ) || is_corpse() )
{
if ( IsDefined( self.riders ) )
foreach ( rider in self.riders )
if ( IsDefined( rider ) )
rider Delete();
if ( is_corpse() )
{
self.riders = [];
continue;
}
self notify( "delete_destructible" );// kills some destructible fxs
return;
}
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
if ( IsDefined( level.vehicle_rumble[ type ] ) )
self StopRumble( level.vehicle_rumble[ type ].rumble );
if ( IsDefined( level.vehicle_death_thread[ type ] ) )
thread [[ level.vehicle_death_thread[ type ] ]]();
// kill riders riders blow up
self array_levelthread( self.riders, maps\_vehicle_aianim::guy_vehicle_death, attacker, type );
// does radius damage
thread kill_damage( type );
thread kill_badplace( type );
kill_lights( model );// delay hacked.. IsDefined() checks for deleted each time.
delete_corpses_around_vehicle();
if ( IsDefined( level.vehicle_deathmodel[ model ] ) )
self thread set_death_model( level.vehicle_deathmodel[ model ], level.vehicle_deathmodel_delay[ model ] );
rocketdeath = vehicle_should_do_rocket_death( model, attacker, cause );
vehOrigin = self.origin;
thread kill_fx( model, rocketdeath );
// all the vehicles get the same jolt..
if ( self.code_classname == "script_vehicle" )
self thread kill_jolt( type );
if ( IsDefined( self.delete_on_death ) )
{
wait 0.05;
if ( !isdefined( self.dontDisconnectPaths ) && !self Vehicle_IsPhysVeh() )
self DisconnectPaths();
self FreeVehicle();
wait 0.05;
self notify( "death_finished" );
self Delete();
continue;
}
if ( IsDefined( self.free_on_death ) )
{
self notify( "newpath" );
if ( !isdefined( self.dontDisconnectPaths ) )
self DisconnectPaths();
Vehicle_kill_badplace_forever();
self FreeVehicle();
return;
}
vehicle_do_crash( model, attacker, cause );
if ( !rocketdeath )
vehOrigin = self.origin;
if ( IsDefined( level.vehicle_death_earthquake[ type ] ) )
earthquake
(
level.vehicle_death_earthquake[ type ].scale,
level.vehicle_death_earthquake[ type ].duration,
vehOrigin,
level.vehicle_death_earthquake[ type ].radius
);
wait .5;
if ( is_corpse() )
continue;
if ( IsDefined( self ) )
{
while ( IsDefined( self.dontfreeme ) && IsDefined( self ) )
wait .05;
if ( !isdefined( self ) )
continue;
if ( self Vehicle_IsPhysVeh() )
{
// if it's a physics vehicle then don't free it, since that stops it from doing physics when dead.
// wait for 0 speed then disconnect paths and kill badplaces.
while ( self.veh_speed != 0 )
wait 1;
self DisconnectPaths();
self notify( "kill_badplace_forever" );
self kill(); // make sure it's dead.
// terminates the vehicle_paths() thread to stop it from starting the vehicle moving again.
self notify( "newpath" );
self Vehicle_TurnEngineOff();
return;
}
else
self FreeVehicle();
if ( self.modeldummyon )
self Hide();
}
if ( vehicle_is_crashing() )
{
self Delete();
continue;
}
}
}
vehicle_should_do_rocket_death( model, attacker, cause )
{
//ability to disable by setting this variable to false.
if ( IsDefined( self.enableRocketDeath ) && self.enableRocketDeath == false )
return false;
if ( !IsDefined( cause ) )
return false;
if ( !( ( cause == "MOD_PROJECTILE" ) || ( cause == "MOD_PROJECTILE_SPLASH" ) ) )
return false;
return vehicle_has_rocket_death( model );
}
vehicle_has_rocket_death( model )
{
return IsDefined( level.vehicle_death_fx[ "rocket_death" + self.vehicletype + model ] ) && IsDefined( self.enableRocketDeath ) && self.enableRocketDeath == true;
}
vehicle_is_crashing()
{
return( IsDefined( self.crashing ) ) && ( self.crashing == true );
}
vehicle_do_crash( model, attacker, cause )
{
// crazy crashpath stuff.
crashtype = "tank";
if ( self Vehicle_IsPhysVeh() )
crashtype = "physics";
else
if ( IsDefined( self.script_crashtypeoverride ) )
crashtype = self.script_crashtypeoverride;
else
if ( self isHelicopter() )
crashtype = "helicopter";
else
if ( IsDefined( self.currentnode ) && crash_path_check( self.currentnode ) )
crashtype = "none";
switch( crashtype )
{
case "helicopter":
self thread helicopter_crash( attacker, cause );
break;
case "tank":
if ( !isdefined( self.rollingdeath ) )
self vehicle_setspeed_wrapper( 0, 25, "Dead" );
else
{
self vehicle_setspeed_wrapper( 8, 25, "Dead rolling out of path intersection" );
self waittill( "deathrolloff" );
self vehicle_setspeed_wrapper( 0, 25, "Dead, finished path intersection" );
}
self notify( "deadstop" );
if ( !isdefined( self.dontDisconnectPaths ) )
self DisconnectPaths();
if ( ( IsDefined( self.tankgetout ) ) && ( self.tankgetout > 0 ) )
self waittill( "animsdone" );// tankgetout will never get notified if there are no guys getting out
break;
case "physics":
self VehPhys_Crash();
self notify( "deadstop" );
if ( !isdefined( self.dontDisconnectPaths ) )
self DisconnectPaths();
if ( ( IsDefined( self.tankgetout ) ) && ( self.tankgetout > 0 ) )
self waittill( "animsdone" );// tankgetout will never get notified if there are no guys getting out
break;
}
if ( IsDefined( level.vehicle_hasMainTurret[ model ] ) && level.vehicle_hasMainTurret[ model ] )
self ClearTurretTarget();
if ( self isHelicopter() )
{
if ( ( IsDefined( self.crashing ) ) && ( self.crashing == true ) )
self waittill( "crash_done" );
}
else
{
while ( !is_corpse() && IsDefined( self ) && self Vehicle_GetSpeed() > 0 )
wait .1;
}
self notify( "stop_looping_death_fx" );
self notify( "death_finished" );
}
is_corpse()
{
is_corpse = false;
if ( IsDefined( self ) && self.classname == "script_vehicle_corpse" )
is_corpse = true;
return is_corpse;
}
set_death_model( sModel, fDelay )
{
Assert( IsDefined( sModel ) );
if ( IsDefined( fDelay ) && ( fDelay > 0 ) )
wait fDelay;
if ( !isdefined( self ) )
return;
eModel = get_dummy();
if ( IsDefined( self.clear_anims_on_death ) )
eModel ClearAnim( %root, 0 );
if ( IsDefined( self ) )
eModel SetModel( sModel );
}
helicopter_crash( attacker, cause )
{
if ( IsDefined( attacker ) && IsPlayer( attacker ) )
self.achievement_attacker = attacker;
self.crashing = true;
if ( !isdefined( self ) )
return;
if ( IsDefined( attacker ) && ( IsPlayer( attacker ) ) )
{
thread arcadeMode_kill( self.origin, "explosive", 750 );
attacker thread giveXp( "kill", 1000 );
if ( GetDvar( "money_sharing" ) == "1" )
{
foreach ( player in level.players )
player thread giveMoney( "kill", 750, attacker );
}
else
attacker thread giveMoney( "kill", 750 );
}
self thread helicopter_crash_move( attacker, cause );
}
_hasweapon( weapon )
{
weapons = self GetWeaponsListAll();
for ( i = 0; i < weapons.size; i++ )
{
if ( IsSubStr( weapons[ i ], weapon ) )
return true;
}
return false;
}
get_unused_crash_locations()
{
unusedLocations = [];
for ( i = 0; i < level.helicopter_crash_locations.size; i++ )
{
if ( IsDefined( level.helicopter_crash_locations[ i ].claimed ) )
continue;
unusedLocations[ unusedLocations.size ] = level.helicopter_crash_locations[ i ];
}
return unusedLocations;
}
detach_getoutrigs()
{
if ( !isdefined( self.fastroperig ) )
return;
if ( ! self.fastroperig.size )
return;
keys = GetArrayKeys( self.fastroperig );
for ( i = 0; i < keys.size; i++ )
{
self.fastroperig[ keys[ i ] ] Unlink();
}
}
helicopter_crash_move( attacker, cause )
{
if ( IsDefined( self.perferred_crash_location ) )
crashLoc = self.perferred_crash_location;
else
{
// get the nearest unused crash location
AssertEx( level.helicopter_crash_locations.size > 0, "A helicopter tried to crash but you didn't have any script_origins with targetname helicopter_crash_location in the level" );
unusedLocations = get_unused_crash_locations();
AssertEx( unusedLocations.size > 0, "You dont have enough script_origins with targetname helicopter_crash_location in the level" );
crashLoc = getClosest( self.origin, unusedLocations );
}
Assert( IsDefined( crashLoc ) );
crashLoc.claimed = true;
self detach_getoutrigs();
// make the chopper spin around
self thread helicopter_crash_rotate();
self notify( "newpath" );
if ( IsDefined( crashLoc.script_parameters ) && crashLoc.script_parameters == "direct" )
{
Assert( IsDefined( crashLoc.radius ) );
crash_speed = 60;
self Vehicle_SetSpeed( crash_speed, 15, 10 );
self SetNearGoalNotifyDist( crashLoc.radius );
self SetVehGoalPos( crashLoc.origin, 0 );
self waittill_any( "goal", "near_goal" );
}
else
{
// move chopper closer to crash point
self Vehicle_SetSpeed( 40, 10, 10 );
self SetNearGoalNotifyDist( 300 );
self SetVehGoalPos( ( crashLoc.origin[ 0 ], crashLoc.origin[ 1 ], self.origin[ 2 ] ), 1 );
msg = "blank";
while ( msg != "death" )
{
msg = self waittill_any( "goal", "near_goal", "death" );
// waittill_any ends on "death"
if ( !isdefined( msg ) && !isdefined( self ) )
{
crashLoc.claimed = undefined;
self notify( "crash_done" );
return;
}
else
msg = "death";// Mackey sends a non dead helicopter through this function. it dies. but not deleted.
}
self SetVehGoalPos( crashLoc.origin, 0 );
self waittill( "goal" );
}
crashLoc.claimed = undefined;
self notify( "stop_crash_loop_sound" );
self notify( "crash_done" );
}
helicopter_crash_rotate()
{
self endon( "crash_done" );
self ClearLookAtEnt();
//self SetMaxPitchRoll( 150, 600 );
self SetYawSpeed( 400, 100, 100 );
for ( ;; )
{
if ( !isdefined( self ) )
return;
iRand = RandomIntRange( 90, 120 );
self SetTargetYaw( self.angles[ 1 ] + iRand );
wait 0.5;
}
}
crash_path_check( node )
{
// find a crashnode on the current path
// this only works on ground info_vehicle_node vheicles. not dynamic helicopter script_origin paths. they have their own dynamic crashing.
targ = node;
while ( IsDefined( targ ) )
{
if ( ( IsDefined( targ.detoured ) ) && ( targ.detoured == 0 ) )
{
detourpath = path_detour_get_detourpath( GetVehicleNode( targ.target, "targetname" ) );
if ( IsDefined( detourpath ) && IsDefined( detourpath.script_crashtype ) )
return true;
}
if ( IsDefined( targ.target ) )
targ = GetVehicleNode( targ.target, "targetname" );
else
targ = undefined;
}
return false;
}
death_firesound( sound )
{
self thread play_loop_sound_on_tag( sound, undefined, false );
self waittill_any( "fire_extinguish", "stop_crash_loop_sound" );
if ( !isdefined( self ) )
return;
self notify( "stop sound" + sound );
}
kill_fx( model, rocketdeath )
{
if ( self isDestructible() )
return;
level notify( "vehicle_explosion", self.origin );
self notify( "explode" );
type = self.vehicletype;
typemodel = type + model;
if ( rocketdeath )
typemodel = "rocket_death" + typemodel;
for ( i = 0; i < level.vehicle_death_fx[ typemodel ].size; i++ )
{
struct = level.vehicle_death_fx[ typemodel ][ i ];
thread kill_fx_thread( model, struct, type );
}
}
/*
=============
///ScriptDocBegin
"Name: vehicle_flag_arrived( <msg> )"
"Summary: Script waits until the vehicle hits the node that has script_flag_wait with this msg"
"Module: Vehicle"
"CallOn: A vehicle"
"MandatoryArg: <msg> : The flag"
"Example: heli vehicle_flag_arrived( "surrender_to_me" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_flag_arrived( msg )
{
if ( !isdefined( self.vehicle_flags ) )
{
self.vehicle_flags = [];
}
while ( !isdefined( self.vehicle_flags[ msg ] ) )
{
self waittill( "vehicle_flag_arrived", notifymsg );
if ( msg == notifymsg )
return;
}
}
kill_fx_thread( model, struct, type )
{
Assert( IsDefined( struct ) );
if ( IsDefined( struct.waitDelay ) )
{
if ( struct.waitDelay >= 0 )
wait struct.waitDelay;
else
self waittill( "death_finished" );
}
if ( !isdefined( self ) )
{
// self may have been removed during the wait
return;
}
if ( IsDefined( struct.notifyString ) )
self notify( struct.notifyString );
eModel = get_dummy();
if ( IsDefined( struct.selfDeleteDelay ) )
self delayCall( struct.selfDeleteDelay, ::Delete );
if ( IsDefined( struct.effect ) )
{
if ( ( struct.bEffectLooping ) && ( !isdefined( self.delete_on_death ) ) )
{
if ( IsDefined( struct.tag ) )
{
if ( ( IsDefined( struct.stayontag ) ) && ( struct.stayontag == true ) )
thread loop_fx_on_vehicle_tag( struct.effect, struct.delay, struct.tag );
else
thread playLoopedFxontag( struct.effect, struct.delay, struct.tag );
}
else
{
forward = ( eModel.origin + ( 0, 0, 100 ) ) - eModel.origin;
PlayFX( struct.effect, eModel.origin, forward );
}
}
else if ( IsDefined( struct.tag ) )
PlayFXOnTag( struct.effect, deathfx_ent(), struct.tag );
else
{
forward = ( eModel.origin + ( 0, 0, 100 ) ) - eModel.origin;
PlayFX( struct.effect, eModel.origin, forward );
}
}
if ( ( IsDefined( struct.sound ) ) && ( !isdefined( self.delete_on_death ) ) )
{
if ( struct.bSoundlooping )
thread death_firesound( struct.sound );
else
self play_sound_in_space( struct.sound );
}
}
loop_fx_on_vehicle_tag( effect, loopTime, tag )
{
Assert( IsDefined( effect ) );
Assert( IsDefined( tag ) );
Assert( IsDefined( loopTime ) );
self endon( "stop_looping_death_fx" );
while ( IsDefined( self ) )
{
PlayFXOnTag( effect, deathfx_ent(), tag );
wait loopTime;
}
}
/*
=============
///ScriptDocBegin
"Name: build_radiusdamage( <offset> , <range> , <maxdamage> , <mindamage> , <bKillplayer> , <delay> )"
"Summary: called in individual vehicle file - define amount of radius damage to be set on each vehicle"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <health> : health"
"MandatoryArg: <offset> : worldspace offset vector, usually goes up"
"MandatoryArg: <range> : randomly chooses between the minhealth, maxhealth"
"MandatoryArg: <maxdamage> : randomly chooses between the minhealth, maxhealth"
"MandatoryArg: <mindamage> : randomly chooses between the minhealth, maxhealth"
"MandatoryArg: <bKillplayer> : true / false: kills player"
"OptionalArg: <delay> : delay after "death" to do the damage."
"Example: build_radiusdamage( ( 0, 0, 53 ), 512, 300, 20, false );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_radiusdamage( offset, range, maxdamage, mindamage, bKillplayer, delay )
{
if ( !isdefined( level.vehicle_death_radiusdamage ) )
level.vehicle_death_radiusdamage = [];
if ( !isdefined( bKillplayer ) )
bKillplayer = false;
if ( !isdefined( offset ) )
offset = ( 0, 0, 0 );
struct = SpawnStruct();
struct.offset = offset;
struct.range = range;
struct.maxdamage = maxdamage;
struct.mindamage = mindamage;
struct.bKillplayer = bKillplayer;
struct.delay = delay;
level.vehicle_death_radiusdamage[ level.vttype ] = struct;
}
/*
=============
///ScriptDocBegin
"Name: build_rumble( <rumble> , <scale> , <duration> , <radius> , <basetime> , <randomaditionaltime> )"
"Summary: called in individual vehicle file - define amount of radius damage to be set on each vehicle"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <rumble> : rumble asset"
"MandatoryArg: <scale> : scale"
"MandatoryArg: <duration> : duration"
"MandatoryArg: <radius> : radius"
"MandatoryArg: <basetime> : time to wait between rumbles"
"MandatoryArg: <randomaditionaltime> : random amount of time to add to basetime"
"Example: build_rumble( "tank_rumble", 0.15, 4.5, 600, 1, 1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_rumble( rumble, scale, duration, radius, basetime, randomaditionaltime )
{
if ( !isdefined( level.vehicle_rumble ) )
level.vehicle_rumble = [];
struct = build_quake( scale, duration, radius, basetime, randomaditionaltime );
Assert( IsDefined( rumble ) );
PreCacheRumble( rumble );
struct.rumble = rumble;
level.vehicle_rumble[ level.vttype ] = struct;
}
/*
=============
///ScriptDocBegin
"Name: build_deathquake( <scale> , <duration> , <radius> )"
"Summary: called in individual vehicle file - define amount of radius damage to be set on each vehicle"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <scale> : scale"
"MandatoryArg: <duration> : duration"
"MandatoryArg: <radius> : radius"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_deathquake( scale, duration, radius )
{
if ( !isdefined( level.vehicle_death_earthquake ) )
level.vehicle_death_earthquake = [];
level.vehicle_death_earthquake[ level.vttype ] = build_quake( scale, duration, radius );
}
build_quake( scale, duration, radius, basetime, randomaditionaltime )
{
struct = SpawnStruct();
struct.scale = scale;
struct.duration = duration;
struct.radius = radius;
if ( IsDefined( basetime ) )
struct.basetime = basetime;
if ( IsDefined( randomaditionaltime ) )
struct.randomaditionaltime = randomaditionaltime;
return struct;
}
build_fx( effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, selfDeleteDelay )
{
if ( !isdefined( bSoundlooping ) )
bSoundlooping = false;
if ( !isdefined( bEffectLooping ) )
bEffectLooping = false;
if ( !isdefined( delay ) )
delay = 1;
struct = SpawnStruct();
struct.effect = _loadfx( effect );
struct.tag = tag;
struct.sound = sound;
struct.bSoundlooping = bSoundlooping;
struct.delay = delay;
struct.waitDelay = waitDelay;
struct.stayontag = stayontag;
struct.notifyString = notifyString;
struct.bEffectLooping = bEffectLooping;
struct.selfDeleteDelay = selfDeleteDelay;
return struct;
}
/*
=============
///ScriptDocBegin
"Name: build_deathfx_override( <type> , <model>, <effect> , <tag> , <sound> , <bEffectLooping> , <delay> , <bSoundlooping> , <waitDelay> , <stayontag> , <notifyString> , <delete_vehicle_delay> )"
"Summary: called in individual vehicle file - death effects on vehicles, usually multiple lines for multistaged / multitagged sequences"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <type> : vehicle type to override the effect of"
"MandatoryArg: <type> : vehicle model to override the effect of"
"MandatoryArg: <effect> : effect to play on death"
"OptionalArg: <tag> : tag to play the effect on"
"OptionalArg: <sound> : " sound to play with effect
"OptionalArg: <bEffectLooping> : play it old fashioned loop style"
"OptionalArg: <delay> : old fashioned loop time"
"OptionalArg: <bSoundlooping> : true / false: sound loops "
"OptionalArg: <waitDelay> : wait this long after death to start this effect sequence"
"OptionalArg: <stayontag> : playfxontag"
"OptionalArg: <notifyString> : notifies vehicle this when effect starts"
"OptionalArg: <delete_vehicle_delay> : delete the vehicle after this amount of time"
"Example: build_deathfx_override( "explosions/large_vehicle_explosion", undefined, "explo_metal_rand" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_deathfx_override( type, model, effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, delete_vehicle_delay )
{
level.vttype = type;
level.vtmodel = model;
level.vtoverride = true;
typemodel = type + model;
//for pre precache script calls.
if ( !isdefined( level.vehicle_death_fx ) )
level.vehicle_death_fx = [];
// overwrite the deathfx post precache.
if( ! is_overrode( typemodel ) )
level.vehicle_death_fx[ typemodel ] = [];
level.vehicle_death_fx_override[ typemodel ] = true;
if ( !isdefined( level.vehicle_death_fx[ typemodel ] ) )
level.vehicle_death_fx[ typemodel ] = [];
level.vehicle_death_fx[ typemodel ][ level.vehicle_death_fx [ typemodel ].size ] = build_fx( effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, delete_vehicle_delay );
level.vtoverride = undefined;
}
/*
=============
///ScriptDocBegin
"Name: build_deathfx( <effect> , <tag> , <sound> , <bEffectLooping> , <delay> , <bSoundlooping> , <waitDelay> , <stayontag> , <notifyString> , <delete_vehicle_delay> )"
"Summary: called in individual vehicle file - death effects on vehicles, usually multiple lines for multistaged / multitagged sequences"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <effect> : effect to play on death"
"OptionalArg: <tag> : tag to play the effect on"
"OptionalArg: <sound> : sound to play with effect"
"OptionalArg: <bEffectLooping> : play it old fashioned loop style. Set this to true or undefined"
"OptionalArg: <delay> : old fashioned loop time in seconds"
"OptionalArg: <bSoundlooping> : true / false: sound loops"
"OptionalArg: <waitDelay> : wait this long after death to start this effect sequence"
"OptionalArg: <stayontag> : playfxontag"
"OptionalArg: <notifyString> : notifies vehicle this when effect starts"
"OptionalArg: <delete_vehicle_delay> : delete the vehicle after this amount of time"
"Example: build_deathfx( "explosions/large_vehicle_explosion", undefined, "explo_metal_rand" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_deathfx( effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, delete_vehicle_delay )
{
AssertEx( IsDefined( effect ), "Failed to build death effect because there is no effect specified for the model used for that vehicle." );
typemodel = level.vttype + level.vtmodel;
// don't build the deathfx if it's already in place. for call before _load.gsc.
if ( is_overrode ( typemodel ) )
return;
if ( !isdefined( level.vehicle_death_fx[ typemodel ] ) )
level.vehicle_death_fx[ typemodel ] = [];
level.vehicle_death_fx[ typemodel ][ level.vehicle_death_fx [ typemodel ].size ] = build_fx( effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, delete_vehicle_delay );
}
is_overrode( typemodel )
{
if( !IsDefined( level.vehicle_death_fx_override ) )
return false;
if( !IsDefined( level.vehicle_death_fx_override[ typemodel ] ) )
return false;
if( IsDefined( level.vtoverride ) )
return true; // not overrode if overriding.
return level.vehicle_death_fx_override[ typemodel ];
}
/*
=============
///ScriptDocBegin
"Name: build_rocket_deathfx( <effect> , <tag> , <sound> , <bEffectLooping> , <delay> , <bSoundlooping> , <waitDelay> , <stayontag> , <notifyString> , <delete_vehicle_delay> )"
"Summary: Specify the alternate set of effects for a death on a vehicle caused by rockets"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <effect> : effect to play on death"
"OptionalArg: <tag> : tag to play the effect on"
"OptionalArg: <sound> : sound to play with effect"
"OptionalArg: <bEffectLooping> : play it old fashioned loop style. Set this to true or undefined"
"OptionalArg: <delay> : old fashioned loop time in seconds"
"OptionalArg: <bSoundlooping> : true / false: sound loops"
"OptionalArg: <waitDelay> : wait this long after death to start this effect sequence"
"OptionalArg: <stayontag> : playfxontag"
"OptionalArg: <notifyString> : notifies vehicle this when effect starts"
"OptionalArg: <delete_vehicle_delay> : delete the vehicle after this amount of time"
"Example: build_rocket_deathfx( "explosions/large_vehicle_explosion", undefined, "explo_metal_rand" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_rocket_deathfx( effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, delete_vehicle_delay )
{
vttype = level.vttype;
level.vttype = "rocket_death" + vttype;
build_deathfx( effect, tag, sound, bEffectLooping, delay, bSoundlooping, waitDelay, stayontag, notifyString, delete_vehicle_delay );
level.vttype = vttype;
}
precache_scripts()
{
// find all the vehicles in the level and initialize precaching( calling of vehicles main() mostly )
allvehiclesprespawn = [];
vehicles = GetEntArray( "script_vehicle", "code_classname" );
level.needsprecaching = [];
playerdrivablevehicles = [];
allvehiclesprespawn = [];
if ( !isdefined( level.vehicleInitThread ) )
level.vehicleInitThread = [];
for ( i = 0; i < vehicles.size; i++ )
{
vehicles[ i ].vehicletype = ToLower( vehicles[ i ].vehicletype );
if ( vehicles[ i ].vehicletype == "bog_mortar" || vehicles[ i ].vehicletype == "empty" )
continue;
if ( IsDefined( vehicles[ i ].spawnflags ) && vehicles[ i ].spawnflags & 1 )
playerdrivablevehicles[ playerdrivablevehicles.size ] = vehicles[ i ];
allvehiclesprespawn[ allvehiclesprespawn.size ] = vehicles[ i ];
if ( !isdefined( level.vehicleInitThread[ vehicles[ i ].vehicletype ] ) )
level.vehicleInitThread[ vehicles[ i ].vehicletype ] = [];
loadstring = "maps\\\_" + vehicles[ i ].vehicletype + "::main( \"" + vehicles[ i ].model + "\" );";
precachesetup( loadstring, vehicles[ i ] );
}
if ( level.needsprecaching.size > 0 )
{
PrintLn( "----------------------------------------------------------------------------------" );
PrintLn( "---missing vehicle script: run repackage zone and precache scripts from launcher--" );
PrintLn( "----------------------------------------------------------------------------------" );
for ( i = 0; i < level.needsprecaching.size; i++ )
PrintLn( level.needsprecaching[ i ] );
PrintLn( "----------------------------------------------------------------------------------" );
AssertEx( false, "missing vehicle scripts, see above console prints" );
level waittill( "never" );
}
// if ( playerdrivablevehicles.size > 0 )
// thread maps\_vehicledrive::main();// precache driveable vehicle huds and such.
return allvehiclesprespawn;
}
precachesetup( string, vehicle )
{
if ( IsDefined( level.vehicleInitThread[ vehicle.vehicletype ][ vehicle.model ] ) )
return;
matched = false;
for ( i = 0; i < level.needsprecaching.size; i++ )
if ( level.needsprecaching[ i ] == string )
matched = true;
if ( !matched )
level.needsprecaching[ level.needsprecaching.size ] = string;
}
vehicle_kill_disconnect_paths_forever()
{
self notify( "kill_disconnect_paths_forever" );
}
disconnect_paths_whenstopped()
{
self endon( "death" );
dont_disconnect_paths = false;
if ( IsDefined( self.script_disconnectpaths ) && !self.script_disconnectpaths )
dont_disconnect_paths = true;
//if ( IsSubStr( self.vehicletype, "snowmobile" ) )
// dont_disconnect_paths = true;
if ( dont_disconnect_paths )
{
self.dontDisconnectPaths = true;// lets other parts of the script know not to disconnect script
return;
}
wait( RandomFloat( 1 ) );
while ( IsDefined( self ) )
{
if ( self Vehicle_GetSpeed() < 1 )
{
if ( !isdefined( self.dontDisconnectPaths ) )
self DisconnectPaths();
self notify( "speed_zero_path_disconnect" );
while ( self Vehicle_GetSpeed() < 1 )
wait .05;
}
self ConnectPaths();
wait 1;
}
}
vehicle_setspeed_wrapper( speed, rate, msg )
{
if ( self Vehicle_GetSpeed() == 0 && speed == 0 )
return;// potential for disaster? keeps messages from overriding previous messages
/#
self thread debug_vehiclesetspeed( speed, rate, msg );
#/
self Vehicle_SetSpeed( speed, rate );
}
debug_vehiclesetspeed( speed, rate, msg )
{
/#
self notify( "new debug_vehiclesetspeed" );
self endon( "new debug_vehiclesetspeed" );
self endon( "resuming speed" );
self endon( "death" );
while ( 1 )
{
while ( GetDvar( "debug_vehiclesetspeed" ) != "off" )
{
Print3d( self.origin + ( 0, 0, 192 ), "vehicle setspeed: " + msg, ( 1, 1, 1 ), 1, 3 );
wait .05;
}
wait .5;
}
#/
}
script_resumespeed( 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 ) )
{
if ( 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 vehicle_setspeed_wrapper( fSetspeed, 15, "resume setspeed from attack" );
self notify( "resuming speed" );
/# self thread debug_vehicleresume( msg + " :" + type ); #/
}
/#
debug_vehicleresume( msg )
{
if ( GetDvar( "debug_vehicleresume" ) == "off" )
return;
self endon( "death" );
number = self.resumemsgs.size;
self.resumemsgs[ number ] = msg;
timer = 3;
self thread print_resumespeed( 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;
}
#/
print_resumespeed( 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;
}
}
force_kill()
{
if ( isDestructible() )
{
self common_scripts\_destructible::force_explosion();
}
else
{
self Kill();
}
}
/*
=============
///ScriptDocBegin
"Name: godon( <godon> )"
"Summary: Vehicle gets god mode"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
godon()
{
self.godmode = true;
}
/*
=============
///ScriptDocBegin
"Name: godoff( <godoff> )"
"Summary: Vehicle loses god mode"
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
godoff()
{
self.godmode = false;
}
setturretfireondrones( b )
{
if ( IsDefined( self.mgturret ) && self.mgturret.size )
for ( i = 0; i < self.mgturret.size; i++ )
self.mgturret[ i ].script_fireondrones = b;
}
getnormalanimtime( animation )
{
animtime = self GetAnimTime( animation );
animlength = GetAnimLength( animation );
if ( animtime == 0 )
return 0;
return self GetAnimTime( animation ) / GetAnimLength( animation );
}
rotor_anim()
{
length = GetAnimLength( self getanim( "rotors" ) );
for ( ;; )
{
self SetAnim( self getanim( "rotors" ), 1, 0, 1 );
wait( length );
}
}
suspend_drive_anims()
{
self notify( "suspend_drive_anims" );
model = self.model;
self ClearAnim( level.vehicle_DriveIdle[ model ], 0 );
self ClearAnim( level.vehicle_DriveIdle_r[ model ], 0 );
}
idle_animations()
{
self UseAnimTree( #animtree );
if( !isdefined( level.vehicle_IdleAnim[ self.model ] ) )
return;
foreach( animation in level.vehicle_IdleAnim[ self.model ] )
self setanim( animation );
}
animate_drive_idle()
{
self endon( "suspend_drive_anims" );
if ( !isdefined( self.wheeldir ) )
self.wheeldir = 1;
model = self.model;
newanimtime = undefined;
self UseAnimTree( #animtree );
if ( !isdefined( level.vehicle_DriveIdle[ model ] ) )
return;
if ( !isdefined( level.vehicle_DriveIdle_r[ model ] ) )
level.vehicle_DriveIdle_r[ model ] = level.vehicle_DriveIdle[ model ];// use forward animation if no backwards anim exists
self endon( "death" );
normalspeed = level.vehicle_DriveIdle_normal_speed[ model ];
thread animate_drive_idle_death();
animrate = 1.0;
if ( ( IsDefined( level.vehicle_DriveIdle_animrate ) ) && ( IsDefined( level.vehicle_DriveIdle_animrate[ model ] ) ) )
animrate = level.vehicle_DriveIdle_animrate[ model ];
lastdir = self.wheeldir;
animatemodel = self;
animation = level.vehicle_DriveIdle[ model ];
while ( 1 )
{
// animatemodel = get_dummy();
if ( !normalspeed )
{
if ( IsDefined( self.suspend_driveanims ) )
{
wait .05;
continue;
}
// vehicles like helicopters always play the same rate. will come up with better design if need arises.
animatemodel SetAnim( level.vehicle_DriveIdle[ model ], 1, .2, animrate );
thread animtimer( .5 );
self waittill( "animtimer" );
continue;
}
speed = self Vehicle_GetSpeed();
if ( lastdir != self.wheeldir )
{
dif = 0;
if ( self.wheeldir )
{
animation = level.vehicle_DriveIdle [ model ];
dif = 1 - animatemodel getnormalanimtime( level.vehicle_DriveIdle_r [ model ] );
animatemodel ClearAnim( level.vehicle_DriveIdle_r [ model ], 0 );
}
else
{
animation = level.vehicle_DriveIdle_r[ model ];// reverse direction
dif = 1 - animatemodel getnormalanimtime( level.vehicle_DriveIdle [ model ] );
animatemodel ClearAnim( level.vehicle_DriveIdle[ model ], 0 );
}
newanimtime = 0.01;
if ( newanimtime >= 1 || newanimtime == 0 )
newanimtime = 0.01;// think setting animtime to 0 or 1 messes things up
lastdir = self.wheeldir;
}
if ( speed == 0 )
animatemodel SetAnim( animation, 1, .05, 0 );
else
animatemodel SetAnim( animation, 1, .05, speed / normalspeed );
if ( IsDefined( newanimtime ) )
{
animatemodel SetAnimTime( animation, newanimtime );
newanimtime = undefined;
}
thread animtimer( .05 );
self waittill( "animtimer" );
}
}
animtimer( time )
{
self endon( "animtimer" );
wait time;
self notify( "animtimer" );
}
animate_drive_idle_death()
{
self endon( "suspend_drive_anims" );
model = self.model;
self UseAnimTree( #animtree );
self waittill( "death_finished" );
if ( IsDefined( self ) )
self ClearAnim( level.vehicle_DriveIdle[ model ], 0 );
}
setup_dynamic_detour( pathnode, get_func )
{
prevnode = [[ get_func ]]( pathnode.targetname );
AssertEx( IsDefined( prevnode ), "detour can't be on start node" );
prevnode.detoured = 0;
}
/*
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_2dadd( 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" );
AssertEx( IsDefined( prevnode ), "detour can't be on start node" );
triggers[ triggers.size ] = prevnode;
prevnode.detoured = 0;
prevnode = undefined;
}
}
return triggers;
}
*/
setup_ai()
{
ai = GetAIArray();
for ( i = 0; i < ai.size; i++ )
{
if ( IsDefined( ai[ i ].script_vehicleride ) )
level.vehicle_RideAI = array_2dadd( level.vehicle_RideAI, ai[ i ].script_vehicleride, ai[ i ] );
else
if ( IsDefined( ai[ i ].script_vehiclewalk ) )
level.vehicle_WalkAI = array_2dadd( level.vehicle_WalkAI, ai[ i ].script_vehiclewalk, ai[ i ] );
}
ai = GetSpawnerArray();
for ( i = 0; i < ai.size; i++ )
{
if ( IsDefined( ai[ i ].script_vehicleride ) )
level.vehicle_RideSpawners = array_2dadd( level.vehicle_RideSpawners, ai[ i ].script_vehicleride, ai[ i ] );
if ( IsDefined( ai[ i ].script_vehiclewalk ) )
level.vehicle_walkspawners = array_2dadd( level.vehicle_walkspawners, ai[ i ].script_vehiclewalk, ai[ i ] );
}
}
array_2dadd( array, firstelem, newelem )
{
if ( !isdefined( array[ firstelem ] ) )
array[ firstelem ] = [];
array[ firstelem ][ array[ firstelem ].size ] = newelem;
return array;
}
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.
node_trigger_process()
{
processtrigger = false;
// special treatment for start nodes
if ( IsDefined( self.spawnflags ) && ( self.spawnflags & 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_2dadd( 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_2dadd( level.vehicle_gatetrigger, self.script_gatetrigger, self );
self.gateopen = false;
}
// init the flags!
if ( IsDefined( self.script_flag_set ) )
{
if ( !isDefined( level.flag[ self.script_flag_set ] ) )
flag_init( self.script_flag_set );
}
// init the flags!
if ( IsDefined( self.script_flag_clear ) )
{
if ( !isDefined( level.flag[ self.script_flag_clear ] ) )
flag_init( self.script_flag_clear );
}
if ( IsDefined( self.script_flag_wait ) )
{
if ( !isDefined( level.flag[ self.script_flag_wait ] ) )
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 );
}
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 = array_combine( GetAllVehicleNodes(), GetEntArray( "script_origin", "code_classname" ) );
triggers = array_combine( triggers, level.struct );
triggers = array_combine( triggers, GetEntArray( "trigger_radius", "code_classname" ) );
triggers = array_combine( triggers, GetEntArray( "trigger_disk", "code_classname" ) );
triggers = array_combine( triggers, GetEntArray( "trigger_multiple", "code_classname" ) );
triggers = array_combine( triggers, GetEntArray( "trigger_lookat", "code_classname" ) );
array_thread( triggers, ::node_trigger_process );
}
is_node_script_struct( node )
{
if ( ! IsDefined( node.targetname ) )
return false;
return IsDefined( getstruct( node.targetname, "targetname" ) );
}
setup_vehicles( vehicles )
{
nonspawned = [];
level.failed_spawnvehicles = [];
foreach ( vehicle in vehicles )
{
//here's a hook for gags on vehicles.
vehicle setup_gags();
if ( vehicle check_spawn_group_isspawner() )
continue;
else
nonspawned[ nonspawned.size ] = vehicle;
}
//print list of spawngroups that fail due to lack of spawner spawnflag
check_failed_spawn_groups();
// init vehicles that aren't spawned
foreach ( live_vehicle in nonspawned )
thread vehicle_init( live_vehicle );
}
check_failed_spawn_groups()
{
if ( !level.failed_spawnvehicles.size )
{
level.failed_spawnvehicles = undefined;
return;
}
PrintLn( "Error: FAILED SPAWNGROUPS" );
foreach ( failed_spawner in level.failed_spawnvehicles )
{
PrintLn( "Error: spawner at: " + failed_spawner.origin );
}
AssertMsg( "Spawngrouped vehicle( s ) without spawnflag checked, see console" );
}
check_spawn_group_isspawner()
{
if ( IsDefined( self.script_VehicleSpawngroup ) && !isSpawner( self ) )
{
level.failed_spawnvehicles[ level.failed_spawnvehicles.size ] = self;
return true;
}
return isSpawner( self );
}
vehicle_life()
{
type = self.vehicletype;
if ( !isdefined( level.vehicle_life ) || !isdefined( level.vehicle_life[ self.vehicletype ] ) )
{
wait 2;
}
AssertEx( IsDefined( level.vehicle_life[ type ] ), "need to specify build_life() in vehicle script for vehicletype: " + type );
if ( IsDefined( self.script_startinghealth ) )
self.health = self.script_startinghealth;
else
{
if ( level.vehicle_life[ type ] == -1 )
return;
else if ( IsDefined( level.vehicle_life_range_low[ type ] ) && IsDefined( level.vehicle_life_range_high[ type ] ) )
self.health = ( RandomInt( level.vehicle_life_range_high[ type ] - level.vehicle_life_range_low[ type ] ) + level.vehicle_life_range_low[ type ] );
else
self.health = level.vehicle_life[ type ];
}
if ( IsDefined( level.destructible_model[ self.model ] ) )
{
self.health = 2000;
self.destructible_type = level.destructible_model[ self.model ];
self common_scripts\_destructible::setup_destructibles( true );
}
}
mginit()
{
typemodel = self.vehicletype + self.model;
if ( ( ( IsDefined( self.script_nomg ) ) && ( self.script_nomg > 0 ) ) )
return;
if ( !isdefined( level.vehicle_mgturret[ typemodel ] ) )
return;
mgangle = 0;
if ( IsDefined( self.script_mg_angle ) )
mgangle = self.script_mg_angle;
turret_templates = level.vehicle_mgturret[ typemodel ];
if ( !isdefined( turret_templates ) )
return;
one_turret = IsDefined( self.script_noteworthy ) && self.script_noteworthy == "onemg";
foreach ( index, turret_template in turret_templates )
{
turret = SpawnTurret( "misc_turret", ( 0, 0, 0 ), turret_template.info );
turret LinkTo( self, turret_template.tag, ( 0, 0, 0 ), ( 0, -1 * mgangle, 0 ) );
turret SetModel( turret_template.model );
turret.angles = self.angles;
turret.isvehicleattached = true;// lets mgturret know not to mess with this turret
turret.ownerVehicle = self;
Assert( IsDefined( self.script_team ) );
turret.script_team = self.script_team;// lets mgturret know not to mess with this turret
turret thread maps\_mgturret::burst_fire_unmanned();
turret MakeUnusable();
set_turret_team( turret );
level thread maps\_mgturret::mg42_setdifficulty( turret, getDifficulty() );
if ( IsDefined( self.script_fireondrones ) )
turret.script_fireondrones = self.script_fireondrones;
if ( IsDefined( turret_template.deletedelay ) )
turret.deletedelay = turret_template.deletedelay;
if ( IsDefined( turret_template.maxrange ) )
turret.maxrange = turret_template.maxrange;
//default drop pitch defaultdroppitch, defaultdropyaw
if ( IsDefined( turret_template.defaultdroppitch ) )
turret SetDefaultDropPitch( turret_template.defaultdroppitch );
self.mgturret[ index ] = turret;
if ( one_turret )
break;
}
foreach ( i, turret in self.mgturret )
{
defaultOnMode = level.vehicle_mgturret[ typemodel ][ i ].defaultONmode;
if ( IsDefined( defaultOnMode ) )
{
turret turret_set_default_on_mode( defaultOnMode );
}
}
if ( !isdefined( self.script_turretmg ) )
self.script_turretmg = true;;
if ( IsDefined( self.script_turretmg ) && self.script_turretmg == 0 )
self thread mgoff();
else
{
self.script_turretmg = 1;
self thread mgon();
}
self thread mgtoggle();
}
mgtoggle()
{
self endon( "death" );
if ( self.script_turretmg )
lasttoggle = 1;
else
lasttoggle = 0;
while ( 1 )
{
if ( lasttoggle != self.script_turretmg )
{
lasttoggle = self.script_turretmg;
if ( self.script_turretmg )
self thread mgon();
else
self thread mgoff();
}
wait .5;
}
}
/*
=============
///ScriptDocBegin
"Name: mgoff( <mgoff> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
mgoff()
{
self.script_turretmg = 0;
if ( ( self isHelicopter() ) && ( self hasHelicopterTurret() ) )
{
self thread chopper_Turret_Off();
return;
}
if ( !isdefined( self.mgturret ) )
return;
foreach ( i, turret in self.mgturret )
{
if ( IsDefined( turret.script_fireondrones ) )
turret.script_fireondrones = false;
turret SetMode( "manual" );
}
}
/*
=============
///ScriptDocBegin
"Name: mgon( <mgon> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
mgon()
{
self.script_turretmg = 1;// fix me.. defense for scripts using mgon();
if ( ( self isHelicopter() ) && ( self hasHelicopterTurret() ) )
{
self thread chopper_Turret_On();
return;
}
if ( !isdefined( self.mgturret ) )
return;
foreach ( turret in self.mgturret )
{
turret Show();// for hidden turrets on vehicles that shouldn't have turrets
if ( IsDefined( turret.script_fireondrones ) )
turret.script_fireondrones = true;
if ( IsDefined( turret.defaultONmode ) )
{
turret SetMode( turret.defaultONmode );
}
else
{
turret SetMode( "auto_nonai" );
}
set_turret_team( turret );
}
}
set_turret_team( turret )
{
switch( self.script_team )
{
case "allies":
case "friendly":
turret SetTurretTeam( "allies" );
break;
case "axis":
case "enemy":
turret SetTurretTeam( "axis" );
break;
case "team3":
turret SetTurretTeam( "team3" );
break;
default:
AssertMsg( "Unknown script_team: " + self.script_team );
break;
}
}
turret_set_default_on_mode( defaultOnMode )
{
self.defaultONmode = defaultOnMode;
}
isHelicopter()
{
return IsDefined( level.helicopter_list[ self.vehicletype ] );
}
isAirplane()
{
return IsDefined( level.airplane_list[ self.vehicletype ] );
}
isCheap()
{
if ( !isdefined( self.script_cheap ) )
return false;
if ( !self.script_cheap )
return false;
return true;
}
hasHelicopterDustKickup()
{
if ( !isHelicopter() && !isAirplane() )
return false;
if ( isCheap() )
return false;
return true;
}
hasHelicopterTurret()
{
if ( !isdefined( self.vehicletype ) )
return false;
if ( isCheap() )
return false;
if ( self.vehicletype == "cobra" )
return true;
if ( self.vehicletype == "cobra_player" )
return true;
if ( self.vehicletype == "viper" )
return true;
return false;
}
Chopper_Turret_On()
{
self endon( "death" );
self endon( "mg_off" );
cosine55 = Cos( 55 );
while ( self.health > 0 )
{
// target range, target fov, getAITargets, doTrace
eTarget = self maps\_helicopter_globals::getEnemyTarget( 16000, cosine55, true, true );
if ( IsDefined( eTarget ) )
self thread maps\_helicopter_globals::shootEnemyTarget_Bullets( eTarget );
wait 2;
}
}
chopper_Turret_Off()
{
self notify( "mg_off" );
}
playLoopedFxontag( effect, durration, tag )
{
eModel = get_dummy();
effectorigin = Spawn( "script_origin", eModel.origin );
self endon( "fire_extinguish" );
thread playLoopedFxontag_originupdate( tag, effectorigin );
while ( 1 )
{
PlayFX( effect, effectorigin.origin, effectorigin.upvec );
wait durration;
}
}
playLoopedFxontag_originupdate( 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.code_classname == "script_vehicle" && self Vehicle_GetSpeed() > 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;
}
}
/*
=============
///ScriptDocBegin
"Name: build_turret( <info> , <tag> , <model> , <maxrange> , <defaultONmode> , <deletedelay>, <defaultdroppitch>, <defaultdropyaw> )"
"Summary: Creates an mg turret on a vehicle"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: An entity"
"MandatoryArg: <info>: weapon info"
"MandatoryArg: <tag>: of vehicle tag to attach the turret to"
"MandatoryArg: <model>: model of turret"
"MandatoryArg: <maxrange>: maxrange "
"MandatoryArg: <defaultONmode>: ai on mode for turret(auto-nonai and stuff)"
"MandatoryArg: <deletedelay>: used for hacking death sequences"
"MandatoryArg: <defaultdroppitch>: set the defaultdroppitch"
"MandatoryArg: <defaultdropyaw>: set the defaultdropyaw"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_turret( info, tag, model, maxrange, defaultONmode, deletedelay, defaultdroppitch, defaultdropyaw )
{
if ( !isdefined( level.vehicle_mgturret ) )
level.vehicle_mgturret = [];
typemodel = level.vttype + level.vtmodel;
if ( !isdefined( level.vehicle_mgturret[ typemodel ] ) )
level.vehicle_mgturret[ typemodel ] = [];
PreCacheModel( model );
PreCacheTurret( info );
struct = SpawnStruct();
struct.info = info;
struct.tag = tag;
struct.model = model;
struct.maxrange = maxrange;
struct.defaultONmode = defaultONmode;
struct.deletedelay = deletedelay;
struct.defaultdroppitch = defaultdroppitch;
struct.defaultdropyaw = defaultdropyaw;
level.vehicle_mgturret[ typemodel ][ level.vehicle_mgturret[ typemodel ].size ] = struct;
}
setup_dvars()
{
/#
SetDvarIfUninitialized( "debug_tankcrush", "0" );
SetDvarIfUninitialized( "debug_vehicleresume", "off" );
SetDvarIfUninitialized( "debug_vehiclesetspeed", "off" );
#/
}
empty_var( var )
{
}
setup_levelvars()
{
level.vehicle_ResumeSpeed = 5;
level.vehicle_DeleteGroup = [];
level.vehicle_StartMoveGroup = [];
level.vehicle_RideAI = [];
level.vehicle_WalkAI = [];
level.vehicle_DeathSwitch = [];
level.vehicle_RideSpawners = [];
level.vehicle_walkspawners = [];
level.vehicle_gatetrigger = [];
level.vehicle_crashpaths = [];
level.vehicle_link = [];
level.vehicle_detourpaths = [];
// level.vehicle_linkedpaths = [];
level.vehicle_startnodes = [];
level.vehicle_killspawn_groups = [];
if ( !isdefined( level.drive_spline_path_fun ) )
level.drive_spline_path_fun = ::empty_var;
level.helicopter_crash_locations = GetEntArray( "helicopter_crash_location", "targetname" );
level.playervehicle = Spawn( "script_origin", ( 0, 0, 0 ) );// no isdefined for level.playervehicle
level.playervehiclenone = level.playervehicle;// no isdefined for level.playervehicle
// TODO in a thousand next games.. I don't like managing this variable. not so much that I don't like it, just that I haven't been = /
level.vehicles = []; // will contain all the vehicles that are spawned and alive
level.vehicles[ "allies" ] = [];
level.vehicles[ "axis" ] = [];
level.vehicles[ "neutral" ] = [];
level.vehicles[ "team3" ] = [];
if ( !isdefined( level.vehicle_team ) )
level.vehicle_team = [];
if ( !isdefined( level.vehicle_deathmodel ) )
level.vehicle_deathmodel = [];
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_rumble ) )
level.vehicle_rumble = [];
if ( !isdefined( level.vehicle_mgturret ) )
level.vehicle_mgturret = [];
if ( !isdefined( level.vehicle_isStationary ) )
level.vehicle_isStationary = [];
if ( !isdefined( level.vehicle_rumble ) )
level.vehicle_rumble = [];
if ( !isdefined( level.vehicle_death_earthquake ) )
level.vehicle_death_earthquake = [];
if ( !isdefined( level.vehicle_treads ) )
level.vehicle_treads = [];
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_exhaust ) )
level.vehicle_exhaust = [];
if ( !isdefined( level.vehicle_deckdust ) )
level.vehicle_deckdust = [];
if ( !isdefined( level.vehicle_shoot_shock ) )
level.vehicle_shoot_shock = [];
if ( !isdefined( level.vehicle_frontarmor ) )
level.vehicle_frontarmor = [];
if ( !isdefined( level.destructible_model ) )
level.destructible_model = [];
if ( !isdefined( level.vehicle_types ) )
level.vehicle_types = [];
if ( !isdefined( level.vehicle_compass_types ) )
level.vehicle_compass_types = [];
if ( !isdefined( level.vehicle_grenadeshield ) )
level.vehicle_grenadeshield = [];
if ( !isdefined( level.vehicle_bulletshield ) )
level.vehicle_bulletshield = [];
if ( !isdefined( level.vehicle_death_jolt ) )
level.vehicle_death_jolt = [];
if ( !isdefined( level.vehicle_death_badplace ) )
level.vehicle_death_badplace = [];
if ( !isdefined( level.vehicle_IdleAnim ) )
level.vehicle_IdleAnim = [];
maps\_vehicle_aianim::setup_aianimthreads();
}
attacker_isonmyteam( attacker )
{
if ( ( IsDefined( attacker ) ) && IsDefined( attacker.script_team ) && ( IsDefined( self.script_team ) ) && ( attacker.script_team == self.script_team ) )
return true;
else
return false;
}
is_invulnerable_from_ai( attacker )
{
//vehicles with script_AI_invulnerable = 1 cannot be damaged by attacking AI
if ( !isdefined( self.script_AI_invulnerable ) )
return false;
if ( ( IsDefined( attacker ) ) && ( IsAI( attacker ) ) && ( self.script_AI_invulnerable == 1 ) )
return true;
else
return false;
}
is_godmode()
{
if ( IsDefined( self.godmode ) && self.godmode )
return true;
else
return false;
}
attacker_troop_isonmyteam( attacker )
{
if ( IsDefined( self.script_team ) && self.script_team == "allies" && IsDefined( attacker ) && IsPlayer( attacker ) )
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.script_team )
return true;
else
return false;
}
has_frontarmor()
{
return( IsDefined( level.vehicle_frontarmor [ self.vehicletype ] ) );
}
grenadeshielded( type )
{
if ( !isdefined( self.script_grenadeshield ) )
return false;
type = ToLower( type );
if ( ! IsDefined( type ) || ! IsSubStr( type, "grenade" ) )
return false;
if ( self.script_grenadeshield )
return true;
else
return false;
}
bulletshielded( type )
{
if ( !isdefined( self.script_bulletshield ) )
return false;
type = ToLower( type );
// hack to make explosive bullets bypass the bulletshield. -R
if ( ! IsDefined( type ) || ! IsSubStr( type, "bullet" ) || IsSubStr( type, "explosive" ) )
return false;
if ( self.script_bulletshield )
return true;
else
return false;
}
friendlyfire_shield()
{
self endon( "death" );
self endon( "stop_friendlyfire_shield" );
if ( IsDefined( level.vehicle_bulletshield[ self.vehicletype ] ) && !isdefined( self.script_bulletshield ) )
self.script_bulletshield = level.vehicle_bulletshield[ self.vehicletype ];
if ( IsDefined( level.vehicle_grenadeshield[ self.vehicletype ] ) && !isdefined( self.script_grenadeshield ) )
self.script_grenadeshield = level.vehicle_bulletshield[ self.vehicletype ];
if ( IsDefined( self.script_mp_style_helicopter ) )
{
self.script_mp_style_helicopter = true;
self.bullet_armor = 5000;
self.health = 350;
}
else
self.script_mp_style_helicopter = false;
self.healthbuffer = 20000;
self.health += self.healthbuffer;
self.currenthealth = self.health;
attacker = undefined;
type = undefined;
while ( self.health > 0 )
{
self waittill( "damage", amount, attacker, direction_vec, point, type, modelName, tagName );
if ( IsDefined( attacker ) )
attacker maps\_player_stats::register_shot_hit();
if (
( ! IsDefined( attacker ) && self.script_team != "neutral" )
|| is_godmode()
|| attacker_isonmyteam( attacker )
|| attacker_troop_isonmyteam( attacker )
|| isDestructible()
|| is_invulnerable_from_ai( attacker )
|| bulletshielded( type )
|| grenadeshielded( type )
|| type == "MOD_MELEE"
)
self.health = self.currenthealth;// give back health for these things
else if ( self has_frontarmor() )// regen health for tanks with armor in the front
{
self regen_front_armor( attacker, amount );
self.currenthealth = self.health;
}
else if ( self hit_bullet_armor( type ) )
{
self.health = self.currenthealth;
self.bullet_armor -= amount;
}
else
self.currenthealth = self.health;
if ( common_scripts\_destructible::getDamageType( type ) == "splash" )
self.rocket_destroyed_for_achievement = true;// little bit of hackery, not perfect but contributes to achievement script for determining that this heli was destroyed by the players RPG.
else
self.rocket_destroyed_for_achievement = undefined;
if ( self.health < self.healthbuffer && !isdefined( self.vehicle_stays_alive ) )
break;
amount = undefined;
attacker = undefined;
direction_vec = undefined;
point = undefined;
modelName = undefined;
tagName = undefined;
type = undefined;
}
self notify( "death", attacker, type );
}
hit_bullet_armor( type )
{
if ( ! self.script_mp_style_helicopter )
return false;
if ( self.bullet_armor <= 0 )
return false;
if ( !( IsDefined( type ) ) )
return false;
if ( ! IsSubStr( type, "BULLET" ) )
return false;
else
return true;
}
regen_front_armor( attacker, amount )
{
forwardvec = AnglesToForward( self.angles );
othervec = VectorNormalize( attacker.origin - self.origin );
if ( VectorDot( forwardvec, othervec ) > .86 )
self.health += Int( amount * level.vehicle_frontarmor [ self.vehicletype ] );
}
vehicle_kill_rumble_forever()
{
self notify( "kill_rumble_forever" );
}
vehicle_rumble()
{
// makes vehicle rumble
self endon( "kill_rumble_forever" );
type = self.vehicletype;
if ( !isdefined( level.vehicle_rumble[ type ] ) )
return;
rumblestruct = level.vehicle_rumble[ type ];
height = rumblestruct.radius * 2;
zoffset = -1 * rumblestruct.radius;
areatrigger = Spawn( "trigger_radius", self.origin + ( 0, 0, zoffset ), 0, rumblestruct.radius, height );
areatrigger EnableLinkTo();
areatrigger LinkTo( self );
self.rumbletrigger = areatrigger;
self endon( "death" );
// ( rumble, scale, duration, radius, basetime, randomaditionaltime )
if ( !isdefined( self.rumbleon ) )
self.rumbleon = true;
if ( IsDefined( rumblestruct.scale ) )
self.rumble_scale = rumblestruct.scale;
else
self.rumble_scale = 0.15;
if ( IsDefined( rumblestruct.duration ) )
self.rumble_duration = rumblestruct.duration;
else
self.rumble_duration = 4.5;
if ( IsDefined( rumblestruct.radius ) )
{
self.rumble_radius = rumblestruct.radius;
}
else
{
self.rumble_radius = 600;
}
if ( IsDefined( rumblestruct.basetime ) )
{
self.rumble_basetime = rumblestruct.basetime;
}
else
{
self.rumble_basetime = 1;
}
if ( IsDefined( rumblestruct.randomaditionaltime ) )
{
self.rumble_randomaditionaltime = rumblestruct.randomaditionaltime;
}
else
{
self.rumble_randomaditionaltime = 1;
}
areatrigger.radius = self.rumble_radius;
while ( 1 )
{
areatrigger waittill( "trigger" );
if ( self Vehicle_GetSpeed() == 0 || !self.rumbleon )
{
wait .1;
continue;
}
self PlayRumbleLoopOnEntity( level.vehicle_rumble[ type ].rumble );
while ( level.player IsTouching( areatrigger ) && self.rumbleon && self Vehicle_GetSpeed() > 0 )
{
Earthquake( self.rumble_scale, self.rumble_duration, self.origin, self.rumble_radius );// scale duration source radius
wait( self.rumble_basetime + RandomFloat( self.rumble_randomaditionaltime ) );
}
self StopRumble( level.vehicle_rumble[ type ].rumble );
}
}
vehicle_kill_badplace_forever()
{
self notify( "kill_badplace_forever" );
}
vehicle_badplace()
{
if ( !isdefined( self.script_badplace ) )
return;
self endon( "kill_badplace_forever" );
if ( !self Vehicle_IsPhysVeh() )
self endon( "death" );
self endon( "delete" );
if ( IsDefined( level.custombadplacethread ) )
{
self thread [[ level.custombadplacethread ]]();
return;
}
hasturret = IsDefined( level.vehicle_hasMainTurret[ self.model ] ) && level.vehicle_hasMainTurret[ self.model ];
bp_duration = .5;
bp_angle_left = 17;
bp_angle_right = 17;
for ( ;; )
{
if ( !self.script_badplace )
{
// BadPlace_Delete( "tankbadplace" );
while ( !self.script_badplace )
wait .5;
}
speed = self Vehicle_GetSpeed();
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 );
// bp_direction = AnglesToForward( self.angles );
if ( hasturret )
bp_direction = AnglesToForward( self GetTagAngles( "tag_turret" ) );
else
bp_direction = AnglesToForward( self.angles );
// have to use unique names for each bad place. if not they will be shared for all vehicles and thats not good. - R
BadPlace_Arc( self.unique_id + "arc", bp_duration, self.origin, bp_radius * 1.9, CONST_bp_height, bp_direction, bp_angle_left, bp_angle_right, "axis", "team3", "allies" );
BadPlace_Cylinder( self.unique_id + "cyl", bp_duration, self.origin, 200, CONST_bp_height, "axis", "team3", "allies" );
wait bp_duration + .05;
}
}
no_treads()
{
if ( self isHelicopter() )
return true;
if ( self isAirplane() )
return true;
return false;
}
vehicle_treads()
{
if ( !isdefined( level.vehicle_treads [ self.vehicletype ] ) )
return;
if ( no_treads() )
return;
if ( IsDefined( level.tread_override_thread ) )
{
self thread [[ level.tread_override_thread ]]( "tag_origin", "back_left", ( 160, 0, 0 ) );
return;
}
// vehicles such as snowmobiles and motorcycles should only do one treadfx in the center of two tags
singleTreadVehicles[ 0 ] = "snowmobile";
singleTreadVehicles[ 1 ] = "snowmobile_friendly";
singleTreadVehicles[ 2 ] = "snowmobile_player";
singleTreadVehicles[ 3 ] = "motorcycle";
if ( is_in_array( singleTreadVehicles, self.vehicletype ) )
{
self thread tread( "tag_wheel_back_left", "back_left", undefined, "tag_wheel_back_right" );
}
else
{
self thread tread( "tag_wheel_back_left", "back_left" );
self thread tread( "tag_wheel_back_right", "back_right" );
}
}
vehicle_kill_treads_forever()
{
self notify( "kill_treads_forever" );
}
tread( tagname, side, relativeOffset, secondTag, fakespeed )
{
self endon( "death" );
treadfx = treadget( self, side );
self endon( "kill_treads_forever" );
for ( ;; )
{
speed = self Vehicle_GetSpeed();
if ( speed == 0 )
{
if( isdefined( fakespeed ) )
{
speed = fakespeed;
}
else
{
wait 0.1;
continue;
}
}
speed *= CONST_MPHCONVERSION;
waitTime = ( 1 / speed );
waitTime = ( waitTime * 35 );
if ( waitTime < 0.1 )
waitTime = 0.1;
else if ( waitTime > 0.3 )
waitTime = 0.3;
wait waitTime;
lastfx = treadfx;
treadfx = treadget( self, side );
if ( treadfx != -1 )
{
ang = self GetTagAngles( tagname );
forwardVec = AnglesToForward( ang );
effectOrigin = self GetTagOrigin( tagname );
// if two tags then use the center between the two
if ( IsDefined( secondTag ) )
{
secondTagOrigin = self GetTagOrigin( secondTag );
effectOrigin = ( effectOrigin + secondTagOrigin ) / 2;
}
forwardVec = vector_multiply( forwardVec, waitTime );
upVec = AnglesToUp( ang );
PlayFX( treadfx, effectOrigin, upVec, forwardVec );
}
}
}
treadget( vehicle, side )
{
surface = self GetWheelSurface( side );
if ( !isdefined( vehicle.vehicletype ) )
{
treadfx = -1;
return treadfx;
}
if ( !isdefined( level._vehicle_effect[ vehicle.vehicletype ] ) )
{
PrintLn( "no treads setup for vehicle type: ", vehicle.vehicletype );
wait 1;
return - 1;
}
treadfx = level._vehicle_effect[ vehicle.vehicletype ][ surface ];
if ( surface == "ice" )
self notify( "iminwater" );
if ( !isdefined( treadfx ) )
treadfx = -1;
return treadfx;
}
turret_attack_think()
{
// chad - disable this for now, will eventually handle shooting of missiles at targets
if ( self isHelicopter() )
return;
// Nathan - Turrets don't think anymore. Sorry, and your welcome.
thread turret_shoot();
}
isStationary()
{
type = self.vehicletype;
if ( IsDefined( level.vehicle_isStationary[ type ] ) && level.vehicle_isStationary[ type ] )
return true;
else
return false;
}
turret_shoot()
{
type = self.vehicletype;
self endon( "death" );
self endon( "stop_turret_shoot" );
index = 0;
turrets = [];
if ( level.vehicle_mainTurrets[ self.model ].size )
{
turrets = GetArrayKeys( level.vehicle_mainTurrets[ self.model ] );
}
while ( self.health > 0 )
{
self waittill( "turret_fire" );// next game remove this. just a simple fireturret command should do
self notify( "groupedanimevent", "turret_fire" );
if ( ! turrets.size )
self FireWeapon();
else
{
self FireWeapon( turrets[ index ] );
index++;
if ( index >= turrets.size )
index = 0;
}
}
}
vehicle_shoot_shock()
{
// if no shellshock is specified just get out of here.
if ( !isdefined( level.vehicle_shoot_shock[ self.model ] ) )
return;
if ( GetDvar( "disable_tank_shock_minspec" ) == "1" )
return;
self endon( "death" );
if ( !isdefined( level.vehicle_shoot_shock_overlay ) )
{
level.vehicle_shoot_shock_overlay = NewHudElem();
level.vehicle_shoot_shock_overlay.x = 0;
level.vehicle_shoot_shock_overlay.y = 0;
level.vehicle_shoot_shock_overlay SetShader( "black", 640, 480 );
level.vehicle_shoot_shock_overlay.alignX = "left";
level.vehicle_shoot_shock_overlay.alignY = "top";
level.vehicle_shoot_shock_overlay.horzAlign = "fullscreen";
level.vehicle_shoot_shock_overlay.vertAlign = "fullscreen";
level.vehicle_shoot_shock_overlay.alpha = 0;
}
while ( true )
{
self waittill( "weapon_fired" );// waits for Code notify when FireWeapon() is called.
if ( IsDefined( self.shock_distance ) )
shock_distance = self.shock_distance;
else
shock_distance = 400;
if ( IsDefined( self.black_distance ) )
black_distance = self.black_distance;
else
black_distance = 800;
player_distance = Distance( self.origin, level.player.origin );
if ( player_distance > black_distance )
continue;
// might add this at some point, but it's so subtle now that I don't think it matters.
// if ( SightTracePassed( level.player GetEye(), self.origin + ( 0, 0, 64 ), false, self ) )
level.vehicle_shoot_shock_overlay.alpha = .5;
level.vehicle_shoot_shock_overlay FadeOverTime( 0.2 );
level.vehicle_shoot_shock_overlay.alpha = 0;
if ( player_distance > shock_distance )
continue;
if ( IsDefined( level.player.flashendtime ) && ( ( level.player.flashendtime - GetTime() ) > 200 ) )
continue;
fraction = player_distance / shock_distance;
time = 4 - ( 3 * fraction );
level.player ShellShock( level.vehicle_shoot_shock[ self.model ], time );
}
}
vehicle_compasshandle()
{
type = self.vehicletype;
if ( !isdefined( level.vehicle_compassicon[ type ] ) )
return;
if ( !level.vehicle_compassicon[ type ] )
return;
self enable_vehicle_compass();
}
vehicle_setteam()
{
type = self.vehicletype;
if ( !isdefined( self.script_team ) && IsDefined( level.vehicle_team[ type ] ) )
self.script_team = level.vehicle_team[ type ];
if ( IsDefined( level.vehicle_hasname[ type ] ) )
self thread maps\_vehiclenames::get_name();
level.vehicles[ self.script_team ] = array_add( level.vehicles[ self.script_team ], self );
}
vehicle_handleunloadevent()
{
self endon( "death" );
type = self.vehicletype;
if ( !ent_flag_exist( "unloaded" ) )
{
ent_flag_init( "unloaded" );
}
}
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 ( ishelicopter() )
{
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 = getstruct( target, "targetname" );
}
return path_start;
}
vehicle_resumepathvehicle()
{
if ( !self ishelicopter() )
{
self ResumeSpeed( 35 );
return;
}
node = undefined;
if ( IsDefined( self.currentnode.target ) )
node = get_vehiclenode_any_dynamic( self.currentnode.target );
if ( !isdefined( node ) )
return;
vehicle_paths( node );
}
setvehgoalpos_wrap( 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 );
//Line( self.origin, origin, (0,1,1), 1, 1, 5000 );
}
vehicle_liftoffvehicle( height )
{
if ( !isdefined( height ) )
height = 512;
dest = self.origin + ( 0, 0, height );
self SetNearGoalNotifyDist( 10 );
self setvehgoalpos_wrap( dest, 1 );
self waittill( "goal" );
}
waittill_stable()
{
// wait for it to level out before unloading
offset = 12;
stabletime = 400;
timer = GetTime() + stabletime;
while ( IsDefined( self ) )
{
if ( abs( self.angles[ 0 ] ) > offset )
timer = GetTime() + stabletime;
if ( abs( self.angles[ 2 ] ) > offset )
timer = GetTime() + stabletime;
if ( GetTime() > timer )
break;
wait .05;
}
}
littlebird_landing()
{
self endon ( "death" );
self ent_flag_init( "prep_unload" );
self ent_flag_wait( "prep_unload" );
self turn_unloading_drones_to_ai();
landing_node = self get_landing_node();
landing_node littlebird_lands_and_unloads( self );
self vehicle_paths( landing_node );
}
get_landing_node()
{
node = self.currentnode;
for ( ;; )
{
nextnode = getent_or_struct( node.target, "targetname" );
AssertEx( IsDefined( nextnode ), "Was looking for landing node with script_unload but ran out of nodes to look through." );
if ( IsDefined( nextnode.script_unload ) )
return nextnode;
node = nextnode;
}
}
unload_node( node )
{
if ( IsDefined( self.ent_flag[ "prep_unload" ] ) && self ent_flag( "prep_unload" ) )
{
// this vehicle is already in the process of unloading
return;
}
if ( IsSubStr( self.classname, "snowmobile" ) )
{
while ( self.veh_speed > 15 )
{
wait( 0.05 );
}
}
if ( !isdefined( node.script_flag_wait ) && !isdefined( node.script_delay ) )
{
// going to stop anyway so no need to kill the path
self notify( "newpath" );
}
Assert( IsDefined( self ) );
// self vehicle_detachfrompath();
pathnode = GetNode( node.targetname, "target" );
if ( IsDefined( pathnode ) && self.riders.size )
{
foreach ( rider in self.riders )
{
if ( IsAI( rider ) )
rider thread maps\_spawner::go_to_node( pathnode );
}
}
if ( self ishelicopter() )
{
self SetHoverParams( 0, 0, 0 );
waittill_stable();
}
else
{
self Vehicle_SetSpeed( 0, 35 );
}
// self vehicle_to_dummy ();
if ( IsDefined( node.script_noteworthy ) )
if ( node.script_noteworthy == "wait_for_flag" )
flag_wait( node.script_flag );
self vehicle_unload( node.script_unload );
if ( maps\_vehicle_aianim::riders_unloadable( node.script_unload ) )
self waittill( "unloaded" );
// self dummy_to_vehicle();
// if we want the helis to hang around for bog_b we can do some script_magic here.
// wait 1;
if ( IsDefined( node.script_flag_wait ) || IsDefined( node.script_delay ) )
{
return;
}
if ( IsDefined( self ) )
thread vehicle_resumepathvehicle();
}
move_turrets_here( model )
{
typemodel = self.vehicletype + self.model;
if ( !isdefined( self.mgturret ) )
return;
if ( self.mgturret.size == 0 )
return;
AssertEx( IsDefined( level.vehicle_mgturret[ typemodel ] ), "no turrets specified for model" );
foreach ( i, turret in self.mgturret )
{
turret Unlink();
turret LinkTo( model, level.vehicle_mgturret[ typemodel ][ i ].tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
}
}
vehicle_pathdetach()
{
self.attachedpath = undefined;
self notify( "newpath" );
self SetGoalYaw( flat_angle( self.angles )[ 1 ] );
self SetVehGoalPos( self.origin + ( 0, 0, 4 ), 1 );
}
vehicle_to_dummy()
{
// create a dummy model that takes the place of a vehicle, the vehicle gets hidden
AssertEx( !isdefined( self.modeldummy ), "Vehicle_to_dummy was called on a vehicle that already had a dummy." );
self.modeldummy = Spawn( "script_model", self.origin );
self.modeldummy SetModel( self.model );
self.modeldummy.origin = self.origin;
self.modeldummy.angles = self.angles;
self.modeldummy UseAnimTree( #animtree );
self Hide();
self notify( "animtimer" );
// move rider characters to dummy model
self thread model_dummy_death();
move_riders_here( self.modelDummy );
move_turrets_here( self.modeldummy );
move_ghettotags_here( self.modeldummy );
// move_lights_here( self.modeldummy );
move_effects_ent_here( self.modeldummy );
copy_destructable_attachments( self.modeldummy );// destructables are all Attach()'d. Little bit different but not too tricky
// flag for various looping functions keeps them from doing isdefined a lot
self.modeldummyon = true;
// helicopters do dust kickup fx
if ( self hasHelicopterDustKickup() )
{
self notify( "stop_kicking_up_dust" );
self thread aircraft_dust_kickup( self.modeldummy );
}
return self.modeldummy;
}
move_effects_ent_here( model )
{
ent = deathfx_ent();
ent Unlink();
ent LinkTo( model );
}
model_dummy_death()
{
// delete model dummy when the vehicle is deleted.
modeldummy = self.modeldummy;
modeldummy endon( "death" );
while ( IsDefined( self ) )
{
self waittill( "death" );
waittillframeend;
}
modeldummy Delete();
}
// will get back to this if people are using vehicle to dummy..
//move_lights_here( model )
//{
// if ( !isdefined( self.lights ) )
// return;
//
// lights = level.vehicle_lights_group[ self.model ][ group ];
//
//
// foreach ( light in lights )
// {
//
// light Unlink();
// light LinkTo( model, light.lighttag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
// }
//}
move_ghettotags_here( model )
{
if ( !isdefined( self.ghettotags ) )
return;
foreach ( ghettotag in self.ghettotags )
{
ghettotag Unlink();
ghettotag LinkTo( model );
}
}
dummy_to_vehicle()
{
AssertEx( IsDefined( self.modeldummy ), "Tried to turn a vehicle from a dummy into a vehicle. Can only be called on vehicles that have been turned into dummies with vehicle_to_dummy." );
if ( self isHelicopter() )
self.modeldummy.origin = self GetTagOrigin( "tag_ground" );
else
{
self.modeldummy.origin = self.origin;
self.modeldummy.angles = self.angles;
}
self Show();
// move rider characters back to the vehicle
move_riders_here( self );
move_turrets_here( self );
// move_lights_here( self );
move_effects_ent_here( self );
// flag for various looping functions keeps them from doing isdefined a lot
self.modeldummyon = false;
self.modeldummy Delete();
self.modeldummy = undefined;
// helicopters do dust kickup fx
if ( self hasHelicopterDustKickup() )
{
self notify( "stop_kicking_up_dust" );
self thread aircraft_dust_kickup();
}
return self.modeldummy;
}
move_riders_here( base )
{
if ( !isdefined( self.riders ) )
return;
riders = self.riders;
// move rider characters to their new location
foreach ( guy in riders )
{
if ( !isdefined( guy ) )
continue;
guy Unlink();
animpos = maps\_vehicle_aianim::anim_pos( self, guy.vehicle_position );
guy LinkTo( base, animpos.sittag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
if ( IsAI( guy ) )
guy Teleport( base GetTagOrigin( animpos.sittag ) );
else
guy.origin = base GetTagOrigin( animpos.sittag );
}
}
spawn_vehicles_from_targetname_newstyle( name )
{
vehicles = [];
test = GetEntArray( name, "targetname" );
test_return = [];
//strip out non vehicles..
foreach ( v in test )
{
if ( !isdefined( v.code_classname ) || v.code_classname != "script_vehicle" )
continue;
if ( isSpawner( v ) )
vehicles[ vehicles.size ] = vehicle_spawn( v );
}
return vehicles;
}
/*
=============
///ScriptDocBegin
"Name: spawn_vehicles_from_targetname( <name> )"
"Summary: returns an array of vehicles from a spawner with that targetname value"
"Module: Vehicle"
"CallOn: Level"
"MandatoryArg: <name>: targetname of the spawners "
"Example: level.helicopters = maps\_vehicle::spawn_vehicles_from_targetname( "blackhawk" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_vehicles_from_targetname( name )
{
vehicles = [];
vehicles = spawn_vehicles_from_targetname_newstyle( name );
AssertEx( vehicles.size, "No vehicle spawners had targetname " + name );
return vehicles;
}
/*
=============
///ScriptDocBegin
"Name: spawn_vehicle_from_targetname( <name> )"
"Summary: returns a vehicle from a spawner with that targetname value."
"Module: Vehicle"
"CallOn: Level"
"MandatoryArg: <name>: targetname of the spawner "
"Example: level.helicopter = maps\_vehicle::spawn_vehicle_from_targetname( "blackhawk" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_vehicle_from_targetname( name )
{
// spawns 1 vehicle and makes sure it gets 1
vehicleArray = spawn_vehicles_from_targetname( name );
AssertEx( vehicleArray.size == 1, "Tried to spawn a vehicle from targetname " + name + " but it returned " + vehicleArray.size + " vehicles, instead of 1" );
return vehicleArray[ 0 ];
}
/*
=============
///ScriptDocBegin
"Name: spawn_vehicle_from_targetname_and_drive( <name> )"
"Summary: returns a vehicle from a spawner with that targetname value and starts it on its targeted path"
"Module: Vehicle"
"CallOn: Level"
"MandatoryArg: <name>: targetname of the spawner "
"Example: level.helicopter = maps\_vehicle::spawn_vehicle_from_targetname_and_drive( "blackhawk" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_vehicle_from_targetname_and_drive( name )
{
// spawns 1 vehicle and makes sure it gets 1
vehicleArray = spawn_vehicles_from_targetname( name );
AssertEx( vehicleArray.size == 1, "Tried to spawn a vehicle from targetname " + name + " but it returned " + vehicleArray.size + " vehicles, instead of 1" );
thread gopath( vehicleArray[ 0 ] );
return vehicleArray[ 0 ];
}
/*
=============
///ScriptDocBegin
"Name: spawn_vehicles_from_targetname_and_drive( <name> )"
"Summary: returns an array of vehicles from a spawner with that targetname value and starts them on their targeted path"
"Module: Vehicle"
"CallOn: Level"
"MandatoryArg: <name>: targetname of the spawners"
"Example: level.helicopters = maps\_vehicle::spawn_vehicles_from_targetname_and_drive( "blackhawk" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_vehicles_from_targetname_and_drive( name )
{
// spawns 1 vehicle and makes sure it gets 1
vehicleArray = spawn_vehicles_from_targetname( name );
for ( i = 0; i < vehicleArray.size; i++ )
foreach ( vehicle in vehicleArray )
thread gopath( vehicle );
return vehicleArray;
}
aircraft_dust_kickup( model )
{
self endon( "death_finished" );
self endon( "stop_kicking_up_dust" );
assert( IsDefined( self.vehicletype ) );
maxHeight = 1200;
if ( isdefined( level.treadfx_maxheight ) )
maxHeight = level.treadfx_maxheight;
minHeight = 350;
slowestRepeatWait = 0.15;
fastestRepeatWait = 0.05;
numFramesPerTrace = 3;
doTraceThisFrame = numFramesPerTrace;
defaultRepeatRate = 1.0;
if ( self isAirplane() )
defaultRepeatRate = 0.15;
repeatRate = defaultRepeatRate;
trace = undefined;
d = undefined;
trace_ent = self;
if ( IsDefined( model ) )
trace_ent = model;
while ( IsDefined( self ) )
{
if ( isdefined( level.skip_treadfx ) )
return true;
if ( repeatRate <= 0 )
repeatRate = defaultRepeatRate;
wait repeatRate;
if ( !isdefined( self ) )
return;
doTraceThisFrame--;
// prof_begin( "aircraft_dust_kickup" );
if ( doTraceThisFrame <= 0 )
{
doTraceThisFrame = numFramesPerTrace;
trace = BulletTrace( trace_ent.origin, trace_ent.origin - ( 0, 0, 100000 ), false, trace_ent );
/#
//if ( IsDefined( trace ) && IsDefined( trace[ "position" ] ) )
// thread draw_line_for_time( trace_ent.origin, trace[ "position" ], 1, 0, 0, 2.0 );
#/
/*
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";
//iprintln( "surface: " + trace[ "surfacetype" ] );
AssertEx( IsDefined( level._vehicle_effect[ self.vehicletype ] ), self.vehicletype + " vehicle script hasn't run _tradfx properly" );
AssertEx( IsDefined( level._vehicle_effect[ self.vehicletype ][ trace[ "surfacetype" ] ] ), "UNKNOWN SURFACE TYPE: " + trace[ "surfacetype" ] );
// prof_end( "aircraft_dust_kickup" );
if ( level._vehicle_effect[ self.vehicletype ][ trace[ "surfacetype" ] ] != -1 )
{
PlayFX( level._vehicle_effect[ self.vehicletype ][ trace[ "surfacetype" ] ], trace[ "position" ] );
//Print3d( trace[ "position" ], trace[ "surfacetype" ], (0,1,0), 1, 1.1, 150 );
}
else
{
//Print3d( trace[ "position" ], trace[ "surfacetype" ], (1,0,0), 1, 1.1, 150 );
}
}
}
tank_crush( crushedVehicle, endNode, tankAnim, truckAnim, animTree, soundAlias )
{
// Chad G's tank crushing vehicle script. Self corrects for node positioning errors.
Assert( IsDefined( crushedVehicle ) );
Assert( IsDefined( endNode ) );
Assert( IsDefined( tankAnim ) );
Assert( IsDefined( truckAnim ) );
Assert( IsDefined( animTree ) );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Create an animatable tank and move the real tank to the next path and store required info
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
animatedTank = vehicle_to_dummy();
self Vehicle_SetSpeed( 7, 5, 5 );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Total time for animation, and correction and uncorrection times
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
animLength = GetAnimLength( tankAnim );
move_to_time = ( animLength / 3 );
move_from_time = ( animLength / 3 );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Node information used for calculating both starting and ending points for the animation
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// get node vecs
node_origin = crushedVehicle.origin;
node_angles = crushedVehicle.angles;
node_forward = AnglesToForward( node_angles );
node_up = AnglesToUp( node_angles );
node_right = AnglesToRight( node_angles );
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Calculate Starting Point for the animation from crushedVehicle and create the dummy
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// get anim starting point origin and angle
anim_start_org = GetStartOrigin( node_origin, node_angles, tankAnim );
anim_start_ang = GetStartAngles( node_origin, node_angles, tankAnim );
// get anim starting point vecs
animStartingVec_Forward = AnglesToForward( anim_start_ang );
animStartingVec_Up = AnglesToUp( anim_start_ang );
animStartingVec_Right = AnglesToRight( anim_start_ang );
// get tank vecs
tank_Forward = AnglesToForward( animatedTank.angles );
tank_Up = AnglesToUp( animatedTank.angles );
tank_Right = AnglesToRight( animatedTank.angles );
// spawn dummy with appropriate offset
offset_Vec = ( node_origin - anim_start_org );
offset_Forward = VectorDot( offset_Vec, animStartingVec_Forward );
offset_Up = VectorDot( offset_Vec, animStartingVec_Up );
offset_Right = VectorDot( offset_Vec, animStartingVec_Right );
dummy = Spawn( "script_origin", animatedTank.origin );
dummy.origin += vector_multiply( tank_Forward, offset_Forward );
dummy.origin += vector_multiply( tank_Up, offset_Up );
dummy.origin += vector_multiply( tank_Right, offset_Right );
// set dummy angles to reflect the different in animation starting angles and the tanks actual angles
offset_Vec = AnglesToForward( node_angles );
offset_Forward = VectorDot( offset_Vec, animStartingVec_Forward );
offset_Up = VectorDot( offset_Vec, animStartingVec_Up );
offset_Right = VectorDot( offset_Vec, animStartingVec_Right );
dummyVec = vector_multiply( tank_Forward, offset_Forward );
dummyVec += vector_multiply( tank_Up, offset_Up );
dummyVec += vector_multiply( tank_Right, offset_Right );
dummy.angles = VectorToAngles( dummyVec );
// -- -- -- -- -- -- -- -- -- -- -
// Debug Lines
// -- -- -- -- -- -- -- -- -- -- -
/#
if ( GetDvar( "debug_tankcrush" ) == "1" )
{
// line to where tank1 is
thread draw_line_from_ent_for_time( level.player, animatedTank.origin, 1, 0, 0, animLength / 2 );
// line to where tank1 SHOULD be
thread draw_line_from_ent_for_time( level.player, anim_start_org, 0, 1, 0, animLength / 2 );
// line to the dummy
thread draw_line_from_ent_to_ent_for_time( level.player, dummy, 0, 0, 1, animLength / 2 );
}
#/
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Animate the animatable tank and self correct into the crushed vehicle
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
if ( IsDefined( soundAlias ) )
level thread play_sound_in_space( soundAlias, node_origin );
animatedTank LinkTo( dummy );
crushedVehicle UseAnimTree( animTree );
animatedTank UseAnimTree( animTree );
Assert( IsDefined( level._vehicle_effect[ "tankcrush" ][ "window_med" ] ) );
Assert( IsDefined( level._vehicle_effect[ "tankcrush" ][ "window_large" ] ) );
crushedVehicle thread tank_crush_fx_on_tag( "tag_window_left_glass_fx", level._vehicle_effect[ "tankcrush" ][ "window_med" ], "veh_glass_break_small", 0.2 );
crushedVehicle thread tank_crush_fx_on_tag( "tag_window_right_glass_fx", level._vehicle_effect[ "tankcrush" ][ "window_med" ], "veh_glass_break_small", 0.4 );
crushedVehicle thread tank_crush_fx_on_tag( "tag_windshield_back_glass_fx", level._vehicle_effect[ "tankcrush" ][ "window_large" ], "veh_glass_break_large", 0.7 );
crushedVehicle thread tank_crush_fx_on_tag( "tag_windshield_front_glass_fx", level._vehicle_effect[ "tankcrush" ][ "window_large" ], "veh_glass_break_large", 1.5 );
crushedVehicle AnimScripted( "tank_crush_anim", node_origin, node_angles, truckAnim );
animatedTank AnimScripted( "tank_crush_anim", dummy.origin, dummy.angles, tankAnim );
dummy MoveTo( node_origin, move_to_time, ( move_to_time / 2 ), ( move_to_time / 2 ) );
dummy RotateTo( node_angles, move_to_time, ( move_to_time / 2 ), ( move_to_time / 2 ) );
wait move_to_time;
animLength -= move_to_time;
animLength -= move_from_time;
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// Tank plays animation in the exact correct location for a while
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
wait animLength;
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// Calculate Ending Point for the animation from crushedVehicle
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// get anim ending point origin and angle
// anim_end_org = anim_start_org + GetMoveDelta( tankAnim, 0, 1 );
temp = Spawn( "script_model", ( anim_start_org ) );
temp.angles = anim_start_ang;
anim_end_org = temp LocalToWorldCoords( GetMoveDelta( tankAnim, 0, 1 ) );
anim_end_ang = anim_start_ang + ( 0, GetAngleDelta( tankAnim, 0, 1 ), 0 );
temp Delete();
// get anim ending point vecs
animEndingVec_Forward = AnglesToForward( anim_end_ang );
animEndingVec_Up = AnglesToUp( anim_end_ang );
animEndingVec_Right = AnglesToRight( anim_end_ang );
// get ending tank pos vecs
attachPos = self GetAttachPos( endNode );
tank_Forward = AnglesToForward( attachPos[ 1 ] );
tank_Up = AnglesToUp( attachPos[ 1 ] );
tank_Right = AnglesToRight( attachPos[ 1 ] );
// see what the dummy's final origin will be
offset_Vec = ( node_origin - anim_end_org );
offset_Forward = VectorDot( offset_Vec, animEndingVec_Forward );
offset_Up = VectorDot( offset_Vec, animEndingVec_Up );
offset_Right = VectorDot( offset_Vec, animEndingVec_Right );
dummy.final_origin = attachPos[ 0 ];
dummy.final_origin += vector_multiply( tank_Forward, offset_Forward );
dummy.final_origin += vector_multiply( tank_Up, offset_Up );
dummy.final_origin += vector_multiply( tank_Right, offset_Right );
// set dummy angles to reflect the different in animation starting angles and the tanks actual angles
offset_Vec = AnglesToForward( node_angles );
offset_Forward = VectorDot( offset_Vec, animEndingVec_Forward );
offset_Up = VectorDot( offset_Vec, animEndingVec_Up );
offset_Right = VectorDot( offset_Vec, animEndingVec_Right );
dummyVec = vector_multiply( tank_Forward, offset_Forward );
dummyVec += vector_multiply( tank_Up, offset_Up );
dummyVec += vector_multiply( tank_Right, offset_Right );
dummy.final_angles = VectorToAngles( dummyVec );
// -- -- -- -- -- -- -- -- -- -- -
// Debug Lines
// -- -- -- -- -- -- -- -- -- -- -
if ( GetDvar( "debug_tankcrush" ) == "1" )
{
// line to where tank2 is
thread draw_line_from_ent_for_time( level.player, self.origin, 1, 0, 0, animLength / 2 );
// line to where tank2 SHOULD be
thread draw_line_from_ent_for_time( level.player, anim_end_org, 0, 1, 0, animLength / 2 );
// line to the dummy
thread draw_line_from_ent_to_ent_for_time( level.player, dummy, 0, 0, 1, animLength / 2 );
}
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
// Tank uncorrects to the real location of the tank on the spline
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
dummy MoveTo( dummy.final_origin, move_from_time, ( move_from_time / 2 ), ( move_from_time / 2 ) );
dummy RotateTo( dummy.final_angles, move_from_time, ( move_from_time / 2 ), ( move_from_time / 2 ) );
wait move_from_time;
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
// Tank is done animating now, remove the animatable tank and show the real one( they should be perfectly aligned now )
// -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
self DontInterpolate();
self AttachPath( endNode );
dummy_to_vehicle();
}
tank_crush_fx_on_tag( tagName, fxName, soundAlias, startDelay )
{
if ( IsDefined( startDelay ) )
wait startDelay;
PlayFXOnTag( fxName, self, tagName );
if ( IsDefined( soundAlias ) )
self thread play_sound_on_tag( soundAlias, tagName );
}
loadplayer( position, animfudgetime )
{
/#
SetDvarIfUninitialized( "fastrope_arms", "0" );
#/
if ( !isdefined( animfudgetime ) )
animfudgetime = 0;
Assert( IsDefined( self.riders ) );
Assert( self.riders.size );
guy = undefined;
foreach ( rider in self.riders )
{
if ( rider.vehicle_position == position )
{
guy = rider;
guy.drone_delete_on_unload = true;
guy.playerpiggyback = true;
break;
}
}
AssertEx( !isai( guy ), "guy in position of player needs to have script_drone set, use script_startingposition ans script drone in your map" );
Assert( IsDefined( guy ) );
thread show_rigs( position );
animpos = maps\_vehicle_aianim::anim_pos( self, position );
// guy StopAnimScripted();
// guy StopUseAnimTree();
guy notify( "newanim" );
guy DetachAll();
// guy SetModel( "" );
guy SetModel( "fastrope_arms" );
guy UseAnimTree( animpos.player_animtree );
thread maps\_vehicle_aianim::guy_idle( guy, position );
// PlayerLinkToDelta( <linkto entity> , <tag> , <viewpercentag fraction> , <right arc> , <left arc> , <top arc> , <bottom arc> )
level.player PlayerLinkToDelta( guy, "tag_player", 1.0, 40, 18, 30, 30 );
// level.player SetPlayerAngles( guy GetTagAngles( "tag_player" ) );
// level.player AllowCrouch( false );
// level.player AllowProne( false );
// level.player AllowStand( true );
guy Hide();
animtime = GetAnimLength( animpos.getout );
animtime -= animfudgetime;
self waittill( "unloading" );
/#
if ( GetDvar( "fastrope_arms" ) != "0" )
guy Show();
#/
level.player DisableWeapons();
// guy waittill( "jumpedout" );
guy NotSolid();
wait animtime;
level.player Unlink();
level.player EnableWeapons();
// level.player AllowCrouch( true );
// level.player AllowProne( true );
}
show_rigs( position )
{
wait .01;
self thread maps\_vehicle_aianim::getout_rigspawn( self, position );// spawn the getoutrig for this position
if ( !self.riders.size )
return;
foreach ( rider in self.riders )
self thread maps\_vehicle_aianim::getout_rigspawn( self, rider.vehicle_position );
}
turret_deleteme( turret )
{
if ( IsDefined( self ) )
if ( IsDefined( turret.deletedelay ) )
wait turret.deletedelay;
if ( IsDefined( turret ) )
turret Delete();
}
/*
=============
///ScriptDocBegin
"Name: vehicle_wheels_forward()"
"Summary: change the direction of the wheel animation on a vehicle to forward."
"Module: Vehicle"
"CallOn: A Vehicle"
"Example: vehicle vehicle_wheels_forward()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_wheels_forward()
{
wheeldirectionchange( 1 );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_wheels_backward()"
"Summary: change the direction of the wheel animation on a vehicle to backward."
"Module: Vehicle"
"CallOn: A Vehicle"
"Example: vehicle vehicle_wheels_backward()"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_wheels_backward()
{
wheeldirectionchange( 0 );
}
wheeldirectionchange( direction )
{
if ( direction <= 0 )
self.wheeldir = 0;
else
self.wheeldir = 1;
}
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 );
}
}
playTankExhaust()
{
self endon( "death" );
if ( !isdefined( level.vehicle_exhaust[ self.model ] ) )
return;
exhaustDelay = 0.1;
while ( IsDefined( self ) )
{
if ( !isdefined( self ) )
return;
if ( !isalive( self ) )
return;
PlayFXOnTag( level.vehicle_exhaust[ self.model ], self, "tag_engine_exhaust" );
wait exhaustDelay;
}
}
/*
=============
///ScriptDocBegin
"Name: build_light( <model> , <name> , <tag> , <effect> , <group> , <delay> )"
"Summary: contstruct a light fx to play on a vehicle tag, see lights_on lights_off"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: An entity"
"MandatoryArg: <model> : Name of model that you are building the light for"
"MandatoryArg: <name> : Unique name used for grouping"
"MandatoryArg: <tag> : Tag to play the light effect on"
"MandatoryArg: <effect> : the effect"
"MandatoryArg: <group> : Group is used for lights_on lights_off"
"MandatoryArg: <delay> : Used to offset the timing of this light so they don't all start at the same time"
"Example: build_light( model, "taillight_R", "TAG_REAR_LIGHT_RIGHT", "misc/car_taillight_btr80", "running", 0.1 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_light( model, name, tag, effect, group, delay )
{
if ( !isdefined( level.vehicle_lights ) )
level.vehicle_lights = [];
if ( !isdefined( level.vehicle_lights_group_override ) )
level.vehicle_lights_group_override = [];
if ( IsDefined( level.vehicle_lights_group_override[ group ] ) && !level.vtoverride )
return;// this light group has been overwritten and shouldn't be set.
struct = SpawnStruct();
struct.name = name;
struct.tag = tag;
struct.delay = delay;
struct.effect = _loadfx( effect );
level.vehicle_lights[ model ][ name ] = struct;
group_light( model, name, "all" );
if ( IsDefined( group ) )
group_light( model, name, group );
}
/*
=============
///ScriptDocBegin
"Name: build_light_override( <type>, <model> , <name> , <tag> , <effect> , <group> , <delay> )"
"Summary: contstruct a light fx override to play on a vehicle tag, see lights_on lights_off."
"Module: vehicle_build( vehicle.gsc )"
"CallOn: An entity"
"MandatoryArg: <type> : vehicletype of model that you are building the light for"
"MandatoryArg: <model> : Name of model that you are building the light for"
"MandatoryArg: <name> : Unique name used for grouping"
"MandatoryArg: <tag> : Tag to play the light effect on"
"MandatoryArg: <effect> : the effect"
"MandatoryArg: <group> : Group is used for lights_on lights_off"
"MandatoryArg: <delay> : Used to offset the timing of this light so they don't all start at the same time"
"Example: build_light_override( "btr80", "vehicle_btr80", "spotlight", "TAG_FRONT_LIGHT_RIGHT", "misc/spotlight_btr80_daytime", "spotlight", 0.2 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_light_override( type, model, name, tag, effect, group, delay )
{
level.vttype = type;
level.vtmodel = type;
level.vtoverride = true;
build_light( model, name, tag, effect, group, delay );
level.vtoverride = false;
level.vehicle_lights_group_override[ group ] = true;
}
group_light( model, name, group )
{
if ( !isdefined( level.vehicle_lights_group ) )
level.vehicle_lights_group = [];
if ( !isdefined( level.vehicle_lights_group[ model ] ) )
level.vehicle_lights_group[ model ] = [];
if ( !isdefined( level.vehicle_lights_group[ model ][ group ] ) )
level.vehicle_lights_group[ model ][ group ] = [];
foreach( lightgroup_name in level.vehicle_lights_group[ model ][ group ] )
if( name == lightgroup_name )
return; // this group has already been defined. supporting overrides post precache script. this part doesn't need to be overwritten.
level.vehicle_lights_group[ model ][ group ][ level.vehicle_lights_group[ model ][ group ].size ] = name;
}
lights_on( group )
{
groups = StrTok( group, " " );
array_levelthread( groups, ::lights_on_internal );
}
lights_delayfxforframe()
{
level notify( "new_lights_delayfxforframe" );
level endon( "new_lights_delayfxforframe" );
if ( !isdefined( level.fxdelay ) )
level.fxdelay = 0;
level.fxdelay += RandomFloatRange( 0.2, 0.4 );
if ( level.fxdelay > 2 )
level.fxdelay = 0;
wait 0.05;
level.fxdelay = undefined;
}
lights_on_internal( group )
{
level.lastlighttime = GetTime();
if ( !isdefined( group ) )
group = "all";
if ( !isDefined( level.vehicle_lights_group[ self.model ] )
|| !isdefined( level.vehicle_lights_group[ self.model ][ group ] )
)
return;
thread lights_delayfxforframe();
if ( !isdefined( self.lights ) )
self.lights = [];
lights = level.vehicle_lights_group[ self.model ][ group ];
count = 0;
delayoffsetter = [];
for ( i = 0; i < lights.size; i++ )
{
if ( IsDefined( self.lights[ lights[ i ] ] ) )
continue;// light is already on
template = level.vehicle_lights[ self.model ][ lights[ i ] ];
if ( IsDefined( template.delay ) )
delay = template.delay;
else
delay = 0;
while ( IsDefined( delayoffsetter[ "" + delay ] ) )
delay += .05;// don't start these on the same frame.
delay += level.fxdelay;
delayoffsetter[ "" + delay ] = true;
//pass the endon death to noself_delaycall
self endon( "death" );
childthread noself_delayCall( delay, ::playfxontag, template.effect, self, template.tag );
self.lights[ lights[ i ] ] = true;
if ( !isdefined( self ) )
break;
}
level.fxdelay = false;
}
deathfx_ent()
{
if ( !isdefined( self.deathfx_ent ) )
{
ent = Spawn( "script_model", ( 0, 0, 0 ) );
emodel = get_dummy();
ent SetModel( self.model );
ent.origin = emodel.origin;
ent.angles = emodel.angles;
ent NotSolid();
ent Hide();
ent LinkTo( emodel );
self.deathfx_ent = ent;
}
else
self.deathfx_ent SetModel( self.model );
return self.deathfx_ent;
}
lights_off( group, model )
{
groups = StrTok( group, " ", model );
array_levelthread( groups, ::lights_off_internal, model );
}
lights_off_internal( group, model )
{
if ( !isdefined( model ) )
model = self.model;
if ( !isdefined( group ) )
group = "all";
if ( !isdefined( self.lights ) )
return;
if ( !isdefined( level.vehicle_lights_group[ model ][ group ] ) )
{
PrintLn( "vehicletype: " + self.vehicletype );
PrintLn( "light group: " + group );
AssertMsg( "lights not defined for this vehicle( see console" );
}
lights = level.vehicle_lights_group[ model ][ group ];
count = 0;
for ( i = 0;i < lights.size;i++ )
{
template = level.vehicle_lights[ model ][ lights[ i ] ];
StopFXOnTag( template.effect, self, template.tag );
count++;
if ( count > 2 )
{
count = 0;
wait .05;// hackin around lights limitations.. seee BUGZILLA 87770
}
//handle delete while shutting of lights.
if ( !isdefined( self ) )
return;
self.lights[ lights[ i ] ] = undefined;
}
}
/*
=============
///ScriptDocBegin
"Name: build_deathmodel( <model> , <deathmodel> )"
"Summary: called in individual vehicle file - assigns death model to vehicles with this model. "
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <model> : name of model to associate death model"
"OptionalArg: <deathmodel> : name of death model to be associated with model"
"OptionalArg: <swapDelay> : number of seconds to wait before setting the death model after the vehicle dies. Defaults to 0"
"Example: build_deathmodel( "bmp", "bmp_destroyed" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_deathmodel( model, deathmodel, swapDelay )
{
if ( model != level.vtmodel )
return;
if ( !isdefined( deathmodel ) )
deathmodel = model;
PreCacheModel( model );
PreCacheModel( deathmodel );
level.vehicle_deathmodel[ model ] = deathmodel;
if ( !isdefined( swapDelay ) )
swapDelay = 0;
level.vehicle_deathmodel_delay[ model ] = swapDelay;
}
/*
=============
///ScriptDocBegin
"Name: build_shoot_shock( <shock> )"
"Summary: called in individual vehicle file - assigns shock file to be played when main cannon on a tank fires "
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <shock> : the shock asset"
"Example: build_shoot_shock( "tankblast" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_shoot_shock( shock )
{
// shock script uses "black" hudelem or something. I don't know . Just had to move it out of _m1a1.gsc
PreCacheShader( "black" );
PreCacheShellShock( shock );
level.vehicle_shoot_shock[ level.vtmodel ] = shock;
}
/*
=============
///ScriptDocBegin
"Name: build_idle( animation )"
"Summary: called in individual vehicle file - assigns animations to be used on vehicles"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <animation> : animation"
"Example: build_idle( %abrams_idle );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_idle( animation )
{
if( !isdefined( level.vehicle_IdleAnim ) )
level.vehicle_IdleAnim = [];
if( !isdefined( level.vehicle_IdleAnim[ level.vtmodel ] ) )
level.vehicle_IdleAnim[ level.vtmodel ] = [];
level.vehicle_IdleAnim[ level.vtmodel ][level.vehicle_IdleAnim[ level.vtmodel ].size] = animation;
}
/*
=============
///ScriptDocBegin
"Name: build_drive( <forward> , <reverse> , <normalspeed> , <rate> )"
"Summary: called in individual vehicle file - assigns animations to be used on vehicles"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"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: build_drive( %abrams_movement, %abrams_movement_backwards, 10 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_drive( forward, reverse, normalspeed, rate )
{
if ( !isdefined( normalspeed ) )
normalspeed = 10;
level.vehicle_DriveIdle[ level.vtmodel ] = forward;
if ( IsDefined( reverse ) )
level.vehicle_DriveIdle_r[ level.vtmodel ] = reverse;
level.vehicle_DriveIdle_normal_speed[ level.vtmodel ] = normalspeed;
if ( IsDefined( rate ) )
level.vehicle_DriveIdle_animrate[ level.vtmodel ] = rate;
}
/*
=============
///ScriptDocBegin
"Name: build_template( <type> , <model> , <typeoverride> )"
"Summary: called in individual vehicle file - mandatory to call this in all vehicle files at the top!"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <type> : vehicle type to set"
"MandatoryArg: <model> : model to set( this is usually generated by the level script )"
"OptionalArg: <typeoverride> : this overrides the type, used for copying a vehicle script"
"Example: build_template( "bmp", model, type );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_template( type, model, typeoverride )
{
if ( IsDefined( typeoverride ) )
type = typeoverride;
typemodel = type + model;
PrecacheVehicle( type );
if ( !isdefined( level.vehicle_death_fx ) )
level.vehicle_death_fx = [];
if ( !isdefined( level.vehicle_death_fx[ typemodel ] ) )
level.vehicle_death_fx[ typemodel ] = [];// can have overrides
level.vehicle_compassicon[ type ] = false;
level.vehicle_team[ type ] = "axis";
level.vehicle_life[ type ] = 999;
level.vehicle_hasMainTurret[ model ] = false;
level.vehicle_mainTurrets[ model ] = [];
level.vtmodel = model;
level.vttype = type;
}
/*
=============
///ScriptDocBegin
"Name: build_exhaust( <exhaust_effect_str> )"
"Summary: called in individual vehicle file - assign an exhaust effect to this vehicle!"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <exhaust_effect_str> : exhaust effect in string format"
"Example: build_exhaust( "distortion/abrams_exhaust" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_exhaust( effect )
{
level.vehicle_exhaust[ level.vtmodel ] = _loadfx( effect );
}
/*
=============
///ScriptDocBegin
"Name: build_treadfx()"
"Summary: called in individual vehicle file - enables treadfx"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"Example: build_treadfx();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_treadfx( type )
{
if ( ! IsDefined( type ) )
type = level.vttype;
maps\_treadfx::main( type );
}
/*
=============
///ScriptDocBegin
"Name: build_team( <team> )"
"Summary: called in individual vehicle file - sets team"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <team> : team"
"Example: build_team( "allies" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_team( team )
{
level.vehicle_team[ level.vttype ] = team;
}
/*
=============
///ScriptDocBegin
"Name: build_mainturret( <firetime> , <tag1> , <tag2> , <tag3> , <tag4> )"
"Summary: called in individual vehicle file - enables main( cannon ) turret"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"OptionalArg: <tag1> : additional tags to fire from"
"OptionalArg: <tag2> : additional tags to fire from"
"OptionalArg: <tag3> : additional tags to fire from"
"OptionalArg: <tag4> : additional tags to fire from"
"Example: build_mainturret();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_mainturret( tag1, tag2, tag3, tag4 )
{
level.vehicle_hasMainTurret[ level.vtmodel ] = true;
if ( IsDefined( tag1 ) )
level.vehicle_mainTurrets[ level.vtmodel ][ tag1 ] = true;
if ( IsDefined( tag2 ) )
level.vehicle_mainTurrets[ level.vtmodel ][ tag2 ] = true;
if ( IsDefined( tag3 ) )
level.vehicle_mainTurrets[ level.vtmodel ][ tag3 ] = true;
if ( IsDefined( tag4 ) )
level.vehicle_mainTurrets[ level.vtmodel ][ tag4 ] = true;
}
/*
=============
///ScriptDocBegin
"Name: build_bulletshield( <bShield> )"
"Summary: Set script toggleable bullet shield on a vehicle. must enable bullet damage on the vehicletype asset first."
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <bShield>: set default enable or disable shield on vehicle "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_bulletshield( bShield )
{
Assert( IsDefined( bShield ) );
level.vehicle_bulletshield[ level.vttype ] = bShield;
}
/*
=============
///ScriptDocBegin
"Name: build_grenadeshield( <bShield> )"
"Summary: Set script toggleable grenade shield on a vehicle. must enable grenade damage on the vehicletype asset first."
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <bShield>: set default enable or disable shield on vehicle "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_grenadeshield( bShield )
{
Assert( IsDefined( bShield ) );
level.vehicle_grenadeshield[ level.vttype ] = bShield;
}
/*
=============
///ScriptDocBegin
"Name: build_aianims( <aithread> , <vehiclethread> )"
"Summary: called in individual vehicle file - set threads for ai animation and vehicle animation assignments"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <aithread> : ai thread"
"OptionalArg: <vehiclethread> : vehicle thread"
"Example: build_aianims( ::setanims, ::set_vehicle_anims );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_aianims( aithread, vehiclethread )
{
level.vehicle_aianims[ level.vttype ] = [[ aithread ]]();
if ( IsDefined( vehiclethread ) )
level.vehicle_aianims[ level.vttype ] = [[ vehiclethread ]]( level.vehicle_aianims[ level.vttype ] );
}
/*
=============
///ScriptDocBegin
"Name: build_frontarmor( <armor> )"
"Summary: called in individual vehicle file - sets percentage of health to regen on attacks from the front"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <armor> : ercentage of health to regen on attacks from the front"
"Example: build_frontarmor( .33 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_frontarmor( armor )
{
level.vehicle_frontarmor [ level.vttype ] = armor;
}
build_hidden_riders_until_unload()
{
level.hidden_riders_until_unload [ level.vttype ] = true;
}
/*
=============
///ScriptDocBegin
"Name: build_attach_models( <modelsthread> )"
"Summary: called in individual vehicle file - thread for building attached models( ropes ) with animation"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <modelsthread> : thread"
"Example: build_attach_models( ::set_attached_models );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_attach_models( modelsthread )
{
level.vehicle_attachedmodels[ level.vttype ] = [[ modelsthread ]]();;
}
/*
=============
///ScriptDocBegin
"Name: build_unload_groups( <unloadgroupsthread> )"
"Summary: called in individual vehicle file - thread for building unload groups"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <modelsthread> : thread"
"Example: build_unload_groups( ::Unload_Groups );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_unload_groups( unloadgroupsthread )
{
level.vehicle_unloadgroups[ level.vttype ] = [[ unloadgroupsthread ]]();
}
/*
=============
///ScriptDocBegin
"Name: build_life( <health> , <minhealth> , <maxhealth> , )"
"Summary: called in individual vehicle file - sets health for vehicles"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <health> : health"
"OptionalArg: <minhealth> : randomly chooses between the minhealth, maxhealth"
"OptionalArg: <maxhealth> : randomly chooses between the minhealth, maxhealth"
"Example: build_life( 999, 500, 1500 );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_life( health, minhealth, maxhealth )
{
level.vehicle_life[ level.vttype ] = health;
level.vehicle_life_range_low[ level.vttype ] = minhealth;
level.vehicle_life_range_high[ level.vttype ] = maxhealth;
}
/*
=============
///ScriptDocBegin
"Name: build_compassicon( <type>, <enable> )"
"Summary: called in individual vehicle file - enables vehicle on the compass with the icontype set to the type. defaults to enabled"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <compasstype> : valid types - automobile,tank,plane,helicopter "
"OptionalArg: <enable> : defaults to true, set to false to disable vehicle on compass"
"Example: build_compassicon( "automobile", false );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_compassicon( compasstype, enabled )
{
Assert( IsDefined( compasstype ) );
if ( !isdefined( enabled ) )
enabled = false;
level.vehicle_compassicon[ level.vttype ] = enabled;
level.vehicle_compass_types[ level.vttype ] = compassType;
}
/*
=============
///ScriptDocBegin
"Name: build_deckdust( <effect> )"
"Summary: called in individual vehicle file - sets a deckdust effect on a vehicle?"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <effect> : effect to be assigned as deckdust"
"Example: build_deckdust( "dust/abrams_desk_dust" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_deckdust( effect )
{
level.vehicle_deckdust[ level.vtmodel ] = _loadfx( effect );
}
/*
=============
///ScriptDocBegin
"Name: build_destructible( <model> , <destructible> )"
"Summary: called in individual vehicle file: asigns destructible type to model."
"Module: vehicle_build( vehicle.gsc )"
"CallOn: level "
"MandatoryArg: <model> : vehicles placed in radiant with this model will be asigned the destructible( see _destructible_types.gsc )"
"OptionalArg: <destructible> : the destructible type to asign"
"Example: build_destructible( "vehicle_bm21_mobile_bed_destructible", "vehicle_bm21_mobile_bed" );
"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_destructible( model, destructible )
{
if ( IsDefined( level.vehicle_csv_export ) )
return;
Assert( IsDefined( model ) );
Assert( IsDefined( destructible ) );
if ( model != level.vtmodel )
return;
struct = SpawnStruct();
passer = SpawnStruct();
passer.model = model;//
struct.destuctableInfo = passer common_scripts\_destructible_types::makeType( destructible );;
struct thread common_scripts\_destructible::precache_destructibles();
struct thread common_scripts\_destructible::add_destructible_fx();
level.destructible_model[ level.vtmodel ] = destructible;
}
/*
=============
///ScriptDocBegin
"Name: build_localinit( <init_thread> )"
"Summary: called in individual vehicle file - mandatory for all vehicle files, this sets the individual init thread for those special sequences, it is also used to determine that a vehicle is being precached or not"
"Module: vehicle_build( vehicle.gsc )"
"CallOn: "
"MandatoryArg: <init_thread> : local thread to the vehicle to be called when it spawns"
"Example: build_localinit( ::init_local );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_localinit( init_thread )
{
level.vehicleInitThread[ level.vttype ][ level.vtmodel ] = init_thread;
}
get_from_spawnstruct( target )
{
return getstruct( target, "targetname" );
}
get_from_entity( target )
{
ent = GetEntArray( target, "targetname" );
if ( IsDefined( ent ) && ent.size > 0 )
return ent[ RandomInt( ent.size ) ];
return undefined;
}
get_from_spawnstruct_target( target )
{
return getstruct( target, "target" );
}
get_from_entity_target( target )
{
return GetEnt( target, "target" );
}
get_from_vehicle_node( target )
{
return GetVehicleNode( target, "targetname" );
}
set_lookat_from_dest( dest )
{
viewTarget = GetEnt( dest.script_linkto, "script_linkname" );
// temp fix for an issue with Hunted
// I use script_linktos as well but for some other purpose.
// I don't have the time to fix this propper now.
if ( !isdefined( viewTarget ) || level.script == "hunted" )
return;
self SetLookAtEnt( viewTarget );
self.set_lookat_point = true;
}
get_deletegroups( script_vehiclegroupdelete )
{
deletegroups = [];
vehicles = GetEntArray( "script_vehicle", "code_classname" );
foreach ( vehicle in vehicles )
{
if ( !isdefined( vehicle.script_vehicleGroupDelete )
|| vehicle.script_vehicleGroupDelete != script_vehiclegroupdelete
)
continue;
deletegroups[ deletegroups.size ] = vehicle;
}
return deletegroups;
}
damage_hint_bullet_only()
{
level.armorDamageHints = false;
self.displayingDamageHints = false;
self thread damage_hints_cleanup();
while ( IsDefined( self ) )
{
self waittill( "damage", amount, attacker, direction_vec, point, type );
if ( !isplayer( attacker ) )
continue;
if( isdefined( self.has_semtex_on_it ) )
continue;
type = ToLower( type );
switch( type )
{
case "mod_pistol_bullet":
case "mod_rifle_bullet":
case "bullet":
if ( !level.armorDamageHints )
{
if( isdefined( level.thrown_semtex_grenades ) && level.thrown_semtex_grenades > 0 )
break;
level.armorDamageHints = true;
self.displayingDamageHints = true;
attacker display_hint( "invulerable_bullets" );
wait( 4 );
level.armorDamageHints = false;
self.displayingDamageHints = false;
break;
}
}
}
}
damage_hints()
{
level.armorDamageHints = false;
self.displayingDamageHints = false;
self thread damage_hints_cleanup();
while ( IsDefined( self ) )
{
self waittill( "damage", amount, attacker, direction_vec, point, type );
if ( !isplayer( attacker ) )
continue;
if( isdefined( self.has_semtex_on_it ) )
continue;
type = ToLower( type );
switch( type )
{
case "mod_grenade":
case "mod_grenade_splash":
case "mod_pistol_bullet":
case "mod_rifle_bullet":
case "bullet":
if ( !level.armorDamageHints )
{
if( isdefined( level.thrown_semtex_grenades ) && level.thrown_semtex_grenades > 0 )
break;
level.armorDamageHints = true;
self.displayingDamageHints = true;
if( ( type == "mod_grenade" ) || ( type == "mod_grenade_splash" ) )
attacker display_hint( "invulerable_frags" );
else
attacker display_hint( "invulerable_bullets" );
wait( 4 );
level.armorDamageHints = false;
self.displayingDamageHints = false;
break;
}
}
}
}
damage_hints_cleanup()
{
self waittill( "death" );
if ( self.displayingDamageHints )
level.armorDamageHints = false;
}
copy_destructable_attachments( modeldummy )
{
// does all attachments
attachedModelCount = self GetAttachSize();
attachedModels = [];
for ( i = 0 ; i < attachedModelCount ; i++ )
attachedModels[ i ] = ToLower( self GetAttachModelName( i ) );
for ( i = 0 ; i < attachedModels.size ; i++ )
modeldummy Attach( attachedModels[ i ], ToLower( self GetAttachTagName( i ) ) );
// original model still has it's own attachments because that's too much script to rewrite
// to move them completely to the dummy, I just want to translate the effects of an
// explosion to the dummy so that's all I'm doing. So don't expect a dummy destructable
// vehicle to react to damage and all of that jazz it's the original model that will do this
}
get_dummy()
{
if ( self.modeldummyon )
eModel = self.modeldummy;
else
eModel = self;
return eModel;
}
apply_truckjunk( eVehicle, truckjunk )
{
if ( !isdefined( self.truckjunk ) )
return;
junkarray = self.truckjunk;
self.truckjunk = [];
foreach ( truckjunk in junkarray )
{
model = Spawn( "script_model", self.origin );
model SetModel( truckjunk.model );
model LinkTo( self, "tag_body", truckjunk.origin, truckjunk.angles );
self.truckjunk[ self.truckjunk.size ] = truckjunk;
}
}
truckjunk()
{
Assert( IsDefined( self.target ) );
spawner = GetEnt( self.target, "targetname" );
Assert( IsDefined( spawner ) );
Assert( isSpawner( spawner ) );
ghettotag = ghetto_tag_create( spawner );
if ( IsDefined( self.script_noteworthy ) )
ghettotag.script_noteworthy = self.script_noteworthy;
if ( !isdefined( spawner.truckjunk ) )
spawner.truckjunk = [];
if ( IsDefined( self.script_startingposition ) )
ghettotag.script_startingposition = self.script_startingposition;
spawner.truckjunk[ spawner.truckjunk.size ] = ghettotag;
self Delete();
}
ghetto_tag_create( target )
{
struct = SpawnStruct();
struct.origin = self.origin - target GetTagOrigin( "tag_body" );
struct.angles = self.angles - target GetTagAngles( "tag_body" );
struct.model = self.model;
if ( IsDefined( struct.targetname ) )
level.struct_class_names[ "targetname" ][ struct.targetname ] = undefined;// done with this forever. don't stick around
if ( IsDefined( struct.target ) )
level.struct_class_names[ "target" ][ struct.target ] = undefined;// done with this forever. don't stick around
return struct;
}
//vehicle_dump()
//{
//
// if ( 1 ) return;
///#
//
// // starts a map with the necessary blank layer info and a blank worldspawn.
// // Throught he magic of junction this file ends up in "map_source\xenon_export\jeepride_veh_ref.map"
// // I keep the directory structure somewhat flat because permissions in winnt is painful when dealing with the xenonremote share stuff.
// // junction.bat keeps it in check
//
// // this simple script exports all of the vehicles as script models that have a delete_on_load targetname
//
// predumpvehicles = GetEntArray( "script_vehicle", "code_classname" );
// vehicles = [];
//
// // dumping can jump a frame in which the information could be altered, this stores the necessary info real quick
// for ( i = 0 ; i < predumpvehicles.size ; i++ )
// {
// struct = SpawnStruct();
// struct.classname = predumpvehicles[ i ].classname;
// struct.origin = predumpvehicles[ i ].origin;
// struct.angles = predumpvehicles[ i ].angles;
//// struct.spawner_id = predumpvehicles[ i ].spawner_id;
// struct.speedbeforepause = predumpvehicles[ i ] Vehicle_GetSpeed();
// struct.script_VehicleSpawngroup = predumpvehicles[ i ].script_vehiclespawngroup;
// struct.script_VehicleStartMove = predumpvehicles[ i ].script_vehiclestartmove;
// struct.model = predumpvehicles[ i ].model;
// struct.angles = predumpvehicles[ i ].angles;
// if ( IsDefined( level.playersride ) && predumpvehicles[ i ] == level.playersride )
// struct.playersride = true;
// vehicles[ i ] = struct;
// }
//
// fileprint_map_start( level.script + "_veh_ref" );
//
// foreach ( i, vehicle in vehicles )
// {
// origin = fileprint_radiant_vec( vehicle.origin );// convert these vectors to mapfile keypair format
// angles = fileprint_radiant_vec( vehicle.angles );
//
// fileprint_map_entity_start();
// fileprint_map_keypairprint( "classname", "script_struct" );
// fileprint_map_keypairprint( "spawnflags", "4" );
// fileprint_map_keypairprint( "model", vehicle.model );
// fileprint_map_keypairprint( "origin", origin );
// fileprint_map_keypairprint( "angles", angles );
// if ( IsDefined( vehicle.playersride ) )
// fileprint_map_keypairprint( "target", "delete_on_load" );// _load deletes these.
// else
// {
// fileprint_map_keypairprint( "target", "structtarg" + i );// _load deletes these.
// fileprint_map_keypairprint( "targetname", "delete_on_load" );// _load deletes these.
// }
//
// if ( IsDefined( vehicle.speedbeforepause ) )
// fileprint_map_keypairprint( "current_speed", vehicle.speedbeforepause );
// if ( IsDefined( vehicle.script_VehicleSpawngroup ) )
// fileprint_map_keypairprint( "script_vehiclespawngroup", vehicle.script_VehicleSpawngroup );
// if ( IsDefined( vehicle.script_VehicleStartMove ) )
// fileprint_map_keypairprint( "script_vehiclestartmove", vehicle.script_VehicleStartMove );
// fileprint_map_entity_end();
//#/
//}
//dump_handle()
//{
// /#
// button1 = "r";
// button2 = "CTRL";
// while ( 1 )
// {
// while ( !twobuttonspressed( button1, button2 ) )
// wait .05;
// vehicle_dump();
// while ( twobuttonspressed( button1, button2 ) )
// wait .05;
// }
// #/
//}
twobuttonspressed( button1, button2 )
{
return level.player ButtonPressed( button1 ) && level.player ButtonPressed( button2 );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_load_ai( <ai_array> , <bGoddriver> , <group> )"
"Summary: loads a vehicle with the specified array of guys. Sets entity flag "unloaded""
"Module: Vehicle"
"CallOn: A vehicle"
"OptionalArg: <ai_array>: Defaults to searching for an Ai with same team and .script_vehicleride value"
"OptionalArg: <bGoddriver>: gives driver a magic bullet shield if he doesn't already have one"
"OptionalArg: <group>: some vehicles support special groups that can be unloaded or loaded"
"Example: uaz vehicle_load_ai( friendlies, true );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_load_ai( ai, goddriver, group )
{
maps\_vehicle_aianim::load_ai( ai, undefined, group );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_load_ai_single( <ai_array> , <bGoddriver> , <group> )"
"Summary: loads a vehicle with the specified guys. Sets entity flag "unloaded""
"Module: Vehicle"
"CallOn: A vehicle"
"OptionalArg: <ai_array>: Defaults to searching for an Ai with same team and .script_vehicleride value"
"OptionalArg: <bGoddriver>: gives driver a magic bullet shield if he doesn't already have one"
"OptionalArg: <group>: some vehicles support special groups that can be unloaded or loaded"
"Example: uaz vehicle_load_ai( guy, true );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_load_ai_single( guy, goddriver, group )
{
ai = [];
ai[ 0 ] = guy;
maps\_vehicle_aianim::load_ai( ai, goddriver, group );
}
kill_badplace( type )
{
if ( !isdefined( level.vehicle_death_badplace[ type ] ) )
return;
struct = level.vehicle_death_badplace[ type ];
if ( IsDefined( struct.delay ) )
wait struct.delay;
if ( !isdefined( self ) )
return;
BadPlace_Cylinder( "vehicle_kill_badplace", struct.duration, self.origin, struct.radius, struct.height, struct.team1, struct.team2 );
}
/*
=============
///ScriptDocBegin
"Name: build_death_badplace( <delay> , <duration> , <height> , <radius> , <team1> , <team2> )"
"Summary: builds a badplace on death of a vehicle."
"Module: vehicle_build( vehicle.gsc )"
"CallOn: An entity"
"MandatoryArg: <delay>: delay "
"MandatoryArg: <duration>: duration"
"MandatoryArg: <height>: height"
"MandatoryArg: <radius>: radius"
"MandatoryArg: <team1>: team1"
"MandatoryArg: <team2>: team2"
"Example: build_death_badplace( .5, 3, 512, 700, "axis", "allies" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
build_death_badplace( delay, duration, height, radius, team1, team2 )
{
if ( !isdefined( level.vehicle_death_badplace ) )
level.vehicle_death_badplace = [];
struct = SpawnStruct();
struct.delay = delay;
struct.duration = duration;
struct.height = height;
struct.radius = radius;
struct.team1 = team1;
struct.team2 = team2;
level.vehicle_death_badplace[ level.vttype ] = struct;
}
build_death_jolt( delay )
{
if ( !isdefined( level.vehicle_death_jolt ) )
level.vehicle_death_jolt = [];
struct = SpawnStruct();
struct.delay = delay;
level.vehicle_death_jolt[ level.vttype ] = struct;
}
kill_jolt( type )
{
if ( IsDefined( level.vehicle_death_jolt[ type ] ) )
{
self.dontfreeme = true;
wait level.vehicle_death_jolt[ type ].delay;// this is all that exists currently, not to elaborate untill needed.
}
if ( !isdefined( self ) )
return;
self JoltBody( ( self.origin + ( 23, 33, 64 ) ), 3 );
wait 2;
if ( !isdefined( self ) )
return;
self.dontfreeme = undefined;
}
heli_squashes_stuff( ender )
{
self endon( "death" );
level endon( ender );
for ( ;; )
{
self waittill( "trigger", other );
if ( IsAlive( other ) )
{
if ( other.team == "allies" && !isplayer( other ) )
continue;
other Kill( ( 0, 0, 0 ) );
}
}
}
_getvehiclespawnerarray_by_spawngroup( spawngroup )
{
spawners = _getvehiclespawnerarray();
array = [];
foreach ( spawner in spawners )
if ( IsDefined( spawner.script_VehicleSpawngroup ) && spawner.script_VehicleSpawngroup == spawngroup )
array[ array.size ] = spawner;
return array;
}
_getvehiclespawnerarray( targetname )
{
vehicles = GetEntArray( "script_vehicle", "code_classname" );
if ( IsDefined( targetname ) )
{
newArray = [];
foreach ( vehicle in vehicles )
{
if ( !isdefined( vehicle.targetname ) )
continue;
if ( vehicle.targetname == targetname )
newArray = array_add( newArray, vehicle );
}
vehicles = newArray;
}
array = [];
foreach ( vehicle in vehicles )
{
if ( isSpawner( vehicle ) )
array[ array.size ] = vehicle;
}
return array;
}
get_compassTypeForVehicleType( type )
{
if ( !isdefined( level.vehicle_compass_types[ type ] ) )
{
PrintLn( "Compass - type doesn't exist for type '" + type + "'." );
PrintLn( "Set it in vehicle script:_" + type + ".gsc with build_compassicon." );
AssertMsg( "Compass - type for model doesn't exits, see console for info" );
}
return level.vehicle_compass_types[ type ];
}
setup_gags()
{
if ( !isdefined( self.script_parameters ) )
return;
if ( self.script_parameters == "gag_ride_in" )
setup_gag_ride();
}
setup_gag_ride()
{
Assert( IsDefined( self.targetname ) );
linked = GetEntArray( self.targetname, "target" );
self.script_vehicleride = auto_assign_ride_group();
foreach ( ent in linked )
{
ent.qSetGoalPos = false;
level.vehicle_RideSpawners = array_2dadd( level.vehicle_RideSpawners, self.script_vehicleride, ent );
}
level.gag_heliride_spawner = self;
}
auto_assign_ride_group()
{
if ( !isdefined( level.vehicle_group_autoasign ) )
level.vehicle_group_autoasign = 1000;
else
level.vehicle_group_autoasign++;
return level.vehicle_group_autoasign;
}
vehicle_script_forcecolor_riders( script_forcecolor )
{
foreach ( rider in self.riders )
{
if ( IsAI( rider ) )
rider set_force_color( script_forcecolor );
else if ( IsDefined( rider.spawner ) )
rider.spawner.script_forcecolor = script_forcecolor;
else
AssertMsg( "rider who's not an ai without a spawner.." );
}
}
vehicle_spawn_group_limit_riders( group, ridermax )
{
spawners = sort_by_startingpos( level.vehicle_RideSpawners[ group ] );
array = [];
for ( i = 0; i < ridermax; i++ )
array[ array.size ] = spawners[ i ];
level.vehicle_RideSpawners[ group ] = array;
}
/*
=============
///ScriptDocBegin
"Name: enable_vehicle_compass( )"
"Summary: turns on the compass icon for that vehicle type. different from AddVehicleToCompass in that it finds the type for you. use RemoveVehicleFromCompass() script command to turn it off."
"Module: Vehicle"
"CallOn: A vehicle"
"Example: m1a1 enable_vehicle_compass();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
enable_vehicle_compass()
{
}
update_steering( snowmobile )
{
if ( snowmobile.update_time != GetTime() )
{
snowmobile.update_time = GetTime();
if ( snowmobile.steering_enable )
{
steering_goal = clamp( 0 - snowmobile.angles[ 2 ], 0 - snowmobile.steering_maxroll, snowmobile.steering_maxroll ) / snowmobile.steering_maxroll;
delta = steering_goal - snowmobile.steering;
if ( delta != 0 )
{
factor = snowmobile.steering_maxdelta / abs( delta );
if ( factor < 1 )
delta *= factor;
snowmobile.steering += delta;
}
}
else
{
snowmobile.steering = 0;
}
}
return snowmobile.steering;
}
/*
=============
///ScriptDocBegin
"Name: mount_snowmobile( <vehicle> )"
"Summary: The guy runs to the vehicle and uses the best anim to enter"
"Module: Vehicle"
"CallOn: An AI that is getting in a vehicle"
"MandatoryArg: <vehicle>: The vehicle to ride "
"MandatoryArg: <sit_position>: 0 for driver, 1 for first passenger, etc."
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
mount_snowmobile( vehicle, sit_position )
{
self endon( "death" );
self endon( "long_death" );
if ( doinglongdeath() )
return;
rider_types = [];
rider_types[ 0 ] = "snowmobile_driver";
rider_types[ 1 ] = "snowmobile_passenger";
tags = [];
tags[ "snowmobile_driver" ] = "tag_driver";
tags[ "snowmobile_passenger" ] = "tag_passenger";
rider_type = rider_types[ sit_position ];
AssertEx( IsDefined( rider_type ), "Tried to make a guy mount a snowmobile but it already had 2 riders!" );
tag = tags[ rider_type ];
tag_origin = vehicle GetTagOrigin( tag );
tag_angles = vehicle GetTagAngles( tag );
closest_scene_name = undefined;
closest_org = undefined;
closest_dist = 9999999;
foreach ( scene_name, _ in level.snowmobile_mount_anims[ rider_type ] )
{
animation = getanim_generic( scene_name );
org = GetStartOrigin( tag_origin, tag_angles, animation );
new_dist = Distance( self.origin, org );
if ( new_dist < closest_dist )
{
closest_dist = new_dist;
closest_org = org;
closest_scene_name = scene_name;
}
}
AssertEx( IsDefined( closest_scene_name ), "Somehow an AI could not find an animation to mount a snowmobile" );
closest_org = drop_to_ground( closest_org );
self.goalradius = 8;
self.disablearrivals = true;
self SetGoalPos( closest_org );
self waittill( "goal" );
vehicle anim_generic( self, closest_scene_name, tag );
vehicle thread maps\_vehicle_aianim::guy_enter( self );
self.disablearrivals = false;
}
/*
=============
///ScriptDocBegin
"Name: get_my_spline_node( <org> )"
"Summary: Returns the node of the veihcle spline path that this vehicle is on"
"Module: Vehicle"
"Example: node = get_my_spline_node( self.origin );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_my_spline_node( org )
{
// finds the 3 closest nodes and puts you on the one that is earliest on the path.
org = ( org[ 0 ], org[ 1 ], 0 );
all_nodes = get_array_of_closest( org, level.snowmobile_path );
close_nodes = [];
for ( i = 0; i < 3; i++ )
{
close_nodes[ i ] = all_nodes[ i ];
}
for ( i = 0; i < level.snowmobile_path.size; i++ )
{
foreach ( node in close_nodes )
{
if ( node == level.snowmobile_path[ i ] )
{
return node;
}
}
}
AssertEx( 0, "Found no node to be on!" );
}
spawn_vehicle_and_attach_to_spline_path( default_speed )
{
if ( level.enemy_snowmobiles.size >= 8 )
return;
vehicle = self spawn_vehicle();
if ( IsDefined( default_speed ) )
vehicle VehPhys_SetSpeed( default_speed );
vehicle thread vehicle_becomes_crashable();
vehicle endon( "death" );
vehicle.dontUnloadOnEnd = true;
vehicle gopath( vehicle );
vehicle leave_path_for_spline_path();
}
leave_path_for_spline_path()
{
self endon( "script_crash_vehicle" );
self waittill_either( "enable_spline_path", "reached_end_node" );
node = self get_my_spline_node( self.origin );
//Line( vehicle.origin, node.midpoint, (1, 0, 0 ), 1, 0, 5000 );
node thread [[ level.drive_spline_path_fun ]]( self );
}
kill_vehicle_spawner( trigger )
{
trigger waittill( "trigger" );
foreach ( spawner in level.vehicle_killspawn_groups[ trigger.script_kill_vehicle_spawner ] )
{
spawner Delete();
}
level.vehicle_killspawn_groups[ trigger.script_kill_vehicle_spawner ] = [];
}
/*
=============
///ScriptDocBegin
"Name: spawn_vehicle_and_gopath()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
spawn_vehicle_and_gopath()
{
vehicle = self spawn_vehicle();
if ( IsDefined( self.script_speed ) )
{
if ( !isHelicopter() )
vehicle VehPhys_SetSpeed( self.script_speed );// used to default to 70
}
vehicle thread maps\_vehicle::gopath( vehicle );
return vehicle;
}
/*
=============
///ScriptDocBegin
"Name: attach_vehicle_triggers()"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <param1>: "
"OptionalArg: <param2>: "
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
attach_vehicle_triggers()
{
triggers = GetEntArray( "vehicle_touch_trigger", "targetname" );
other_triggers = GetEntArray( "vehicle_use_trigger", "targetname" );
triggers = array_combine( triggers, other_triggers );
origin = undefined;
// find a trigger with the correct script_noteworthy and then get all the triggers at that origin.
foreach ( trigger in triggers )
{
if ( trigger.script_noteworthy == self.model )
{
origin = trigger.origin;
break;
}
}
vehicle_triggers = [];
foreach ( trigger in triggers )
{
if ( trigger.script_noteworthy != self.model )
continue;
if ( trigger.origin != origin )
continue;
vehicle_triggers[ vehicle_triggers.size ] = trigger;
}
self.vehicle_triggers = [];
foreach ( trigger in vehicle_triggers )
{
// remove the trigger's targetname so other vehicles dont get these triggers.
trigger.targetname = undefined;
trigger thread manual_tag_linkto( self, "tag_origin" );
if ( !isdefined( self.vehicle_triggers[ trigger.code_classname ] ) )
self.vehicle_triggers[ trigger.code_classname ] = [];
self.vehicle_triggers[ trigger.code_classname ][ self.vehicle_triggers[ trigger.code_classname ].size ] = trigger;
}
}
humvee_antenna_animates( anims )
{
self UseAnimTree( #animtree );
humvee_antenna_animates_until_death( anims );
if ( !isdefined( self ) )
return;
self clearanim( anims[ "idle" ], 0 );
self clearanim( anims[ "rot_l" ], 0 );
self clearanim( anims[ "rot_r" ], 0 );
}
humvee_antenna_animates_until_death( anims )
{
self endon( "death" );
// self setanim( anims[ "idle" ], 1, 0, 1 );
for ( ;; )
{
weight = self.veh_speed / 18;
if ( weight <= 0.0001 )
weight = 0.0001;
rate = randomfloatrange( 0.3, 0.7 );
self setanim( anims[ "idle" ], weight, 0, rate );
rate = randomfloatrange( 0.1, 0.8 );
self setanim( anims[ "rot_l" ], 1, 0, rate );
rate = randomfloatrange( 0.1, 0.8 );
self setanim( anims[ "rot_r" ], 1, 0, rate );
wait( 0.5 );
}
}
manual_tag_linkto( entity, tag )
{
for ( ;; )
{
if ( !isdefined( self ) )
break;
if ( !isdefined( entity ) )
break;
org = entity GetTagOrigin( tag );
ang = entity GetTagAngles( tag );
self.origin = org;
self.angles = ang;
wait( 0.05 );
}
}
littlebird_lands_and_unloads( vehicle )
{
vehicle SetDeceleration( 6 );
vehicle SetAcceleration( 4 );
AssertEx( IsDefined( self.angles ), "Landing nodes must have angles set." );
vehicle SetTargetYaw( self.angles[ 1 ] );
vehicle Vehicle_SetSpeed( 20, 7, 7 );
while ( Distance( flat_origin( vehicle.origin ), flat_origin( self.origin ) ) > 512 )
wait .05;
vehicle endon( "death" );
badplace_name = "landing" + RandomInt( 99999 );
BadPlace_Cylinder( badplace_name, 30, self.origin, 200, CONST_bp_height, "axis", "allies", "neutral", "team3" );
vehicle thread vehicle_land_beneath_node( 424, self );
vehicle waittill( "near_goal" );
BadPlace_Delete( badplace_name );
BadPlace_Cylinder( badplace_name, 30, self.origin, 200, CONST_bp_height, "axis", "allies", "neutral", "team3" );
vehicle notify( "groupedanimevent", "pre_unload" );
vehicle thread vehicle_ai_event( "pre_unload" );
vehicle Vehicle_SetSpeed( 20, 22, 7 );
vehicle notify( "nearing_landing" );
if ( isdefined( vehicle.custom_landing ) )
{
switch( vehicle.custom_landing )
{
case "hover_then_land":
vehicle Vehicle_SetSpeed( 10, 22, 7 );
vehicle thread vehicle_land_beneath_node( 32, self, 64 );
vehicle waittill( "near_goal" );
vehicle notify( "hovering" );
wait( 1 );
break;
default:
assertmsg( "Unsupported vehicle.custom_landing" );
break;
}
}
vehicle thread vehicle_land_beneath_node( 16, self );
vehicle waittill( "near_goal" );
BadPlace_Delete( badplace_name );
//BadPlace_Cylinder( badplace_name, 6, self.origin, 200, CONST_bp_height, "axis", "allies", "neutral", "team3" );
self script_delay();
vehicle vehicle_unload();
vehicle waittill_stable();
vehicle Vehicle_SetSpeed( 20, 8, 7 );
wait .2;
vehicle notify( "stable_for_unlink" );
wait .2;
if ( IsDefined( self.script_flag_set ) )
{
flag_set( self.script_flag_set );
}
if ( IsDefined( self.script_flag_wait ) )
{
flag_wait( self.script_flag_wait );
}
vehicle notify( "littlebird_liftoff" );
}
// feel free to tear this apart once you have a real context
setup_gag_stage_littlebird_unload()
{
Assert( IsDefined( self.targetname ) );
Assert( IsDefined( self.angles ) );
while ( 1 )
{
self waittill( "trigger", vehicle );
littlebird_lands_and_unloads( vehicle );
}
// vehicle ent_flag_wait("unloaded");
// ai = GetAIArray("allies");
// foreach( guy in ai )
// {
// relativeorigin = guy.origin-vehicle.origin;
// relativeangles = guy.angles-vehicle.angles;
// PrintLn("Error: relativeorigin " + relativeorigin );
// PrintLn("Error: relativeangles " + relativeangles );
// }
}
setup_gag_stage_littlebird_load()
{
Assert( IsDefined( self.targetname ) );
Assert( IsDefined( self.angles ) );
// nodes = getstructarray(self.targetname,"target");
// Assert(nodes.size);
while ( 1 )
{
self waittill( "trigger", vehicle );
vehicle SetDeceleration( 6 );
vehicle SetAcceleration( 4 );
vehicle SetTargetYaw( self.angles[ 1 ] );
vehicle Vehicle_SetSpeed( 20, 7, 7 );
while ( Distance( flat_origin( vehicle.origin ), flat_origin( self.origin ) ) > 256 )
wait .05;
vehicle endon( "death" );
vehicle thread vehicle_land_beneath_node( 220, self );
vehicle waittill( "near_goal" );
vehicle Vehicle_SetSpeed( 20, 22, 7 );
vehicle thread vehicle_land_beneath_node( 16, self );
vehicle waittill( "near_goal" );
vehicle waittill_stable();
vehicle notify( "touch_down", self );
vehicle Vehicle_SetSpeed( 20, 8, 7 );
}
// vehicle ent_flag_wait("unloaded");
// ai = GetAIArray("allies");
// foreach( guy in ai )
// {
// relativeorigin = guy.origin-vehicle.origin;
// relativeangles = guy.angles-vehicle.angles;
// PrintLn("Error: relativeorigin " + relativeorigin );
// PrintLn("Error: relativeangles " + relativeangles );
// }
}
vehicle_land_beneath_node( neargoal, node, height )
{
if ( !isdefined( height ) )
height = 0;
self notify( "newpath" );
if ( ! IsDefined( neargoal ) )
neargoal = 2;
self SetNearGoalNotifyDist( neargoal );
self SetHoverParams( 0, 0, 0 );
self ClearGoalYaw();
self SetTargetYaw( flat_angle( node.angles )[ 1 ] );
self setvehgoalpos_wrap( groundpos( node.origin ) + ( 0, 0, height ), 1 );
self waittill( "goal" );
}
vehicle_landvehicle( neargoal, node )
{
self notify( "newpath" );
if ( ! IsDefined( neargoal ) )
neargoal = 2;
self SetNearGoalNotifyDist( neargoal );
self SetHoverParams( 0, 0, 0 );
self ClearGoalYaw();
self SetTargetYaw( flat_angle( self.angles )[ 1 ] );
self setvehgoalpos_wrap( groundpos( self.origin ), 1 );
self waittill( "goal" );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_get_riders_by_group( <groupname> )"
"Summary: Some vehicles like the littlebird have predefined unload groups you can use this to get the guys on those groups"
"Module: Vehicle"
"CallOn: A Vehicle"
"MandatoryArg: <groupname>: "
"Example: ai = vehicle vehicle_get_riders_by_group( "right" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_get_riders_by_group( groupname )
{
group = [];
Assert( IsDefined( self.vehicletype ) );
if ( ! IsDefined( level.vehicle_unloadgroups[ self.vehicletype ] ) )
{
return group;
}
vehicles_groups = level.vehicle_unloadgroups[ self.vehicletype ];
if ( ! IsDefined( groupname ) )
{
return group;
}
foreach ( guy in self.riders )
{
Assert( IsDefined( guy.vehicle_position ) );
foreach ( groupid in vehicles_groups[ groupname ] )
{
if ( guy.vehicle_position == groupid )
{
group[ group.size ] = guy;
}
}
}
return group;
}
/*
=============
///ScriptDocBegin
"Name: vehicle_ai_event( <event> )"
"Summary: tell a vehicle to do one of the following actions, provided that it has those anims setup for it: ( idle, duck, duck_once, duck_once, weave,"
"Summary: weave, stand, turn_right, turn_right, turn_left, turn_left, turn_hardright, turn_hardleft, turret_fire, turret_turnleft, turret_turnright,"
"Summary: unload, pre_unload, pre_unload, idle_alert, idle_alert_to_casual, reaction )"
"Summary: returns the ai that did the event"
"Module: Vehicle"
"CallOn: A vehicle"
"MandatoryArg: <param1>: "
"Example: vehicle vehicle_ai_event( "idle_alert" ) "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_ai_event( event )
{
return self maps\_vehicle_aianim::animate_guys( event );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_unload( <group> )"
"Summary: Tells ai to unload from a vehicle, returns the ai"
"Module: Vehicle"
"CallOn: A Vehicle"
"OptionalArg: <group>: some vehicles have groups of ai that you can unload, I'll try to list them from here out on the entity info in radiant"
"Example: ai = bmp vehicle_unload();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_unload( who )
{
self notify( "unloading" ); // added this notify since it no longer does the old "unload" notify
ai = [];
if ( ent_flag_exist( "no_riders_until_unload" ) )
{
ent_flag_set( "no_riders_until_unload" );
ai = spawn_group();
foreach ( a in ai )
spawn_failed( a );
}
if ( IsDefined( who ) )
self.unload_group = who;
// makes ai unload
foreach ( guy in self.riders )
{
if ( IsAlive( guy ) )
guy notify( "unload" );
}
ai = self vehicle_ai_event( "unload" );
// if ( IsDefined( level.vehicle_hasMainTurret[ self.model ] ) && level.vehicle_hasMainTurret[ self.model ] && riders_check() )
// self ClearTurretTarget();
return ai;
}
/*
=============
///ScriptDocBegin
"Name: get_stage_nodes( <pickup_node_before_stage>, <side> )"
"Summary: Used for getting cover nodes in Littlebird staging prefab"
"Module: Vehicle"
"MandatoryArg: <pickup_node_before_stage>: A script_origin or struct on a helicopter path that is right before the linked stage prefab"
"MandatoryArg: <side>: " Left" or "right" side bench of littlebird"
"Example: "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
get_stage_nodes( pickup_node_before_stage, side )
{
Assert( IsDefined( pickup_node_before_stage.target ) );
targeted_nodes = GetNodeArray( pickup_node_before_stage.target, "targetname" );
stage_side_nodes = [];
foreach ( node in targeted_nodes )
{
Assert( IsDefined( node.script_noteworthy ) );
if ( node.script_noteworthy == "stage_" + side )
stage_side_nodes[ stage_side_nodes.size ] = node;
}
return stage_side_nodes;
}
/*
=============
///ScriptDocBegin
"Name: set_stage( <pickup_node_before_stage>, <guys>, <side> )"
"Summary: Used for getting setting up AI around the landing area of a littlebird with benches"
"Module: Vehicle"
"MandatoryArg: <pickup_node_before_stage>: A script_origin or struct on a helicopter path that is right before the linked stage prefab"
"MandatoryArg: <guys>: group of 3 AI that will load on either the right or left side"
"MandatoryArg: <side>: " Left" or "right" side bench of littlebird"
"Example: littlebird_wingman set_stage( pickup_node_before_stage, aRoof_riders_left, "left" );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
set_stage( pickup_node_before_stage, guys, side )
{
if ( !ent_flag_exist( "staged_guy_" + side ) )
ent_flag_init( "staged_guy_" + side );
else
ent_flag_clear( "staged_guy_" + side );
if ( !ent_flag_exist( "guy2_in_" + side ) )
ent_flag_init( "guy2_in_" + side );
else
ent_flag_clear( "guy2_in_" + side );
//if ( ! ent_flag_exist( "all_aboard" ) )
//ent_flag_init( "all_aboard" );
//else
//ent_flag_clear( "all_aboard" );
nodes = get_stage_nodes( pickup_node_before_stage, side );
Assert( nodes.size );
heli_node = getstruct( pickup_node_before_stage.target, "targetname" );
stage_heli = Spawn( "script_model", ( 0, 0, 0 ) );
stage_heli SetModel( self.model );
stage_heli.origin = drop_to_ground( heli_node.origin ) + ( 0, 0, self.originheightoffset );
stage_heli.angles = heli_node.angles;
stage_heli Hide();
hop_on_guy1 = undefined;
patting_back_second_guy = undefined;
stage_guy = undefined;
foreach ( node in nodes )
{
guy = undefined;
//check to see if there is already a guy destined for this node with .script_startingposition
foreach( rider in guys )
{
if ( ( isdefined( rider.script_startingposition ) ) && ( rider.script_startingposition == node.script_startingposition ) )
{
guy = rider;
break;
}
}
if ( !isdefined( guy ) )
{
guy = getClosest( node.origin, guys );
}
Assert( IsDefined( guy ) );
//used to associate this node with a position
Assert( IsDefined( node.script_startingposition ) );
guy.script_startingposition = node.script_startingposition;
if ( guy.script_startingposition == 2 || guy.script_startingposition == 5 )
{
hop_on_guy1 = guy;
guy maps\_spawner::go_to_node_set_goal_node( node );
}
else if ( guy.script_startingposition == 3 || guy.script_startingposition == 6 )
{
stage_guy = guy;
}
else if ( guy.script_startingposition == 4 || guy.script_startingposition == 7 )
{
patting_back_second_guy = guy;
guy maps\_spawner::go_to_node_set_goal_node( node );
}
guys = array_remove( guys, guy );
}
Assert( IsDefined( hop_on_guy1 ) );
Assert( IsDefined( patting_back_second_guy ) );
Assert( IsDefined( stage_guy ) );
self thread stage_guy( stage_guy, side, patting_back_second_guy, stage_heli );
self thread delete_on_death( stage_heli );
}
/*
=============
///ScriptDocBegin
"Name: load_side( <side>, <riders> )"
"Summary: Used for loading AI onto a littlebird with benches"
"Module: Vehicle"
"MandatoryArg: <riders>: group of 3 AI that will load on either the right or left side"
"MandatoryArg: <side>: " Left" or "right" side bench of littlebird"
"Example: littlebird_wingman_02 thread load_side( "left", aRoof_riders_left );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
load_side( side, riders )
{
hop_on_guy1 = undefined;
patting_back_second_guy = undefined;
stage_guy = undefined;
foreach ( rider in riders )
{
Assert( IsDefined( rider.script_startingposition ) );
if ( rider.script_startingposition == 2 || rider.script_startingposition == 5 )
hop_on_guy1 = rider;
else if ( rider.script_startingposition == 3 || rider.script_startingposition == 6 )
stage_guy = rider;
else if ( rider.script_startingposition == 4 || rider.script_startingposition == 7 )
patting_back_second_guy = rider;
}
Assert( IsDefined( hop_on_guy1 ) );
Assert( IsDefined( patting_back_second_guy ) );
Assert( IsDefined( stage_guy ) );
// stage_guy = getClosest( self.origin, riders );
// riders = array_remove( riders, stage_guy );
// hop_on_guy1 = getClosest( self.origin, riders );
// riders = array_remove( riders, hop_on_guy1 );
//this guy kneels down needs to be setup before other guy can be there.
// thread stage_guy( stage_guy, side, patting_back_second_guy );
ent_flag_wait( "staged_guy_" + side );
thread vehicle_load_ai_single( hop_on_guy1, undefined, side );
//waittill he's just starting to play out his animation before sending the other guys to get in the way.
hop_on_guy1 waittill( "boarding_vehicle" );
// send the third guy off to jump in
thread vehicle_load_ai_single( patting_back_second_guy, undefined, side );
patting_back_second_guy waittill( "boarding_vehicle" );
ent_flag_set( "guy2_in_" + side );
}
//Nate's magical Littlebird script
stage_guy( guy, side, otherguy, stag_objected )
{
scene = "stage_littlebird_" + side ;
array = [];
array[ 0 ] = guy;
stag_objected anim_generic_reach( array[ 0 ], scene, "tag_detach_" + side );
stag_objected anim_generic( array[ 0 ], scene, "tag_detach_" + side );
ent_flag_set( "staged_guy_" + side );
guy SetGoalPos( drop_to_ground( guy.origin ) );
guy.goalradius = 16;
ent_flag_wait( "guy2_in_" + side );
thread vehicle_load_ai_single( guy, undefined, side );
//ent_flag_wait( "loaded" );
//ent_flag_set( "all_aboard" );
}
kill_riders( riders )
{
foreach ( rider in riders )
{
if ( !IsAlive( rider ) )
continue;
if ( !isdefined( rider.ridingvehicle ) && !isdefined( rider.drivingVehicle ) )
continue;
if ( isdefined( rider.magic_bullet_shield ) )
rider stop_magic_bullet_shield();
rider Kill();
}
}
vehicle_rider_death_detection( vehicle, riders )
{
// this is just plain wrong.. I'm just going to hack around it for af_chase since we're supposed to be finished with the game.. -Nate
if( level.script == "af_chase" )
if( isdefined( self.vehicle_position ) && self.vehicle_position != 0 )
return;
self.health = 1;
vehicle endon( "death" );
self.baseaccuracy = 0.15;
self waittill( "death" );
vehicle notify( "driver_died" );
kill_riders( riders );
}
vehicle_becomes_crashable()
{
self endon( "death" );
self endon( "enable_spline_path" ); // vehicle spline behavior handles this on its own
waittillframeend; // let .riders get set
self.riders = remove_dead_from_array( self.riders );
if ( self.riders.size )
{
array_thread( self.riders, ::vehicle_rider_death_detection, self, self.riders );
self waittill_either( "veh_collision", "driver_died" );
kill_riders( self.riders );
wait( 0.25 );
}
self notify( "script_crash_vehicle" );
self VehPhys_Crash();
}
/*
=============
///ScriptDocBegin
"Name: vehicle_turret_scan_on()"
"Summary: Call on a tank to make its main turret scan randomly back and forth"
"Module: Vehicle"
"CallOn: A spawned vehicle entity with a main turret cannon (tanks)"
"Example: level.t72 thread vehicle_turret_scan_on(); "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_turret_scan_on()
{
self endon( "death" );
self endon( "stop_scanning_turret" );
positive_range = RandomInt( 2 );
while ( IsDefined( self ) )
{
if ( cointoss() )
{
self vehicle_aim_turret_at_angle( 0 );
wait( RandomFloatRange( 2, 10 ) );
}
if ( positive_range == 0 )
{
angle = RandomIntRange( 10, 30 );
positive_range = 1;
}
else
{
angle = RandomIntRange( -30, -10 );
positive_range = 0;
}
self vehicle_aim_turret_at_angle( angle );
wait( RandomFloatRange( 2, 10 ) );
}
}
/*
=============
///ScriptDocBegin
"Name: vehicle_turret_scan_off()"
"Summary: Call on a tank to make its main turret stop scanning randomly back and forth"
"Module: Vehicle"
"CallOn: A spawned vehicle entity with a main turret cannon (tanks)"
"Example: level.t72 thread vehicle_turret_scan_off(); "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_turret_scan_off()
{
self notify( "stop_scanning_turret" );
}
vehicle_aim_turret_at_angle( iAngle )
{
self endon( "death" );
vec = AnglesToForward( self.angles + ( 0, iAngle, 0 ) );
vec *= 10000;
vec = vec + ( 0, 0, 70 );
self SetTurretTargetVec( vec );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_get_path_array()"
"Summary: Call on a vehicle to get an array of nodes/structs/script_origins it is linked to"
"Module: Vehicle"
"CallOn: A spawned vehicle entity"
"Example: path_array = level.t72 vehicle_get_path_array(); "
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_get_path_array()
{
self endon( "death" );
aPathNodes = [];
eStartNode = self.attachedpath;
if ( !isdefined( self.attachedpath ) )
return aPathNodes;
nextNode = eStartNode;
nextNode.counted = false;
while ( IsDefined( nextNode ) )
{
//end loop if next node links back to some other node already in the array
if ( ( IsDefined( nextNode.counted ) ) && ( nextNode.counted == true ) )
break;
//add the next node to the array
aPathNodes = array_add( aPathNodes, nextNode );
nextNode.counted = true;
//end loop if not targeting a new node
if ( !isdefined( nextNode.target ) )
break;
if ( !isHelicopter() )
nextNode = GetVehicleNode( nextNode.target, "targetname" );
else
nextNode = getent_or_struct( nextNode.target, "targetname" );
}
return aPathNodes;
}
kill_lights( model )
{
lights_off_internal( "all", model );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_lights_on( <group> )"
"Summary: turn on this group of lights on a vehicle."
"Module: Vehicle"
"CallOn: A vehicle"
"MandatoryArg: <group>: "
"Example: vehicle_lights_on( "spotlight" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_lights_on( group )
{
lights_on( group );
}
/*
=============
///ScriptDocBegin
"Name: vehicle_lights_off( <group> )"
"Summary: turn off this group of lights on a vehicle."
"Module: Vehicle"
"CallOn: A vehicle"
"MandatoryArg: <group>: "
"Example: vehicle_lights_off( "spotlight" )"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
vehicle_lights_off( group )
{
lights_off( group );
}