2396 lines
60 KiB
Plaintext
2396 lines
60 KiB
Plaintext
/*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
|
This is where all the vehicle / ai interactions happen
|
|
|
|
High level functions
|
|
|
|
handle_attached_guys()// this is the setup for slots of guys on a vehicle threads notify handlers
|
|
|
|
guy_runtovehicle( guy, vehicle )// this tells the guy to run to a vehicle and get in
|
|
|
|
guy_enter( guy, vehicle, lastguy )// this puts the guy into the vehicle and tells him to idle
|
|
|
|
guy_handle( guy, pos )// this handles the vehicles animation events( stand, attack, duck, turn, unload )
|
|
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
|
Lets say we want to add a "nose pick" event to the jeep passenger.
|
|
|
|
in _jeep there is a thread called set_anims() where all sorts of animations and
|
|
stuff are asigned to the positions a guy can ride in.
|
|
|
|
the element is the position where 0 is always the driver in the jeeps case 1 is the passenger.
|
|
|
|
in _jeep::set_anims() put add this
|
|
|
|
positions[ 1 ].nosepick = %jeep_passenger_nosepick;
|
|
|
|
|
|
in guy_handle() you have a bunch of pointers like this
|
|
|
|
level.vehicle_aianimthread[ "idle" ] = ::guy_idle;
|
|
level.vehicle_aianimthread[ "duck" ] = ::guy_duck;
|
|
level.vehicle_aianimthread[ "stand" ] = ::guy_stand;
|
|
|
|
add to the list your pointer
|
|
|
|
level.vehicle_aianimthread[ "nosepick" ] = ::guy_picknose;
|
|
|
|
then the event thread would looks something like this:
|
|
|
|
guy_picknose( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );// first gets the animation struct information for the position of the guy.
|
|
anim_endons( guy );// is the standard endons for these functions( vehicle dies, guy dies, new anim event happens )
|
|
if ( IsDefined( animpos.nosepick ) )// from there you put a check for your animation
|
|
animontag( guy, animpos.sittag, animpos.nosepick );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
*/
|
|
|
|
#include maps\_utility;
|
|
#include maps\_vehicle;
|
|
#include common_scripts\utility;
|
|
|
|
#using_animtree( "generic_human" );
|
|
CONST_anim_end_time = 0.25;// use the same number as _anim plz
|
|
|
|
|
|
guy_enter( guy, climbed_in_vehicle )
|
|
{
|
|
AssertEx( !isSpawner( self ), "Tried to make guys enter a spawner" );
|
|
// do stuff that should happen BEFORE _spawner auto spawn logic below this
|
|
AssertEx( !isdefined( guy.ridingvehicle ), "ai can't ride two vehicles at the same time" );
|
|
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
if ( !isdefined( self.vehicletype ) )
|
|
return;
|
|
|
|
type = self.vehicletype;
|
|
|
|
if ( IsSubStr( type, "snowmobile" ) )
|
|
type = "snowmobile";
|
|
|
|
vehicleanim = level.vehicle_aianims[ type ];
|
|
maxpos = level.vehicle_aianims[ type ].size;
|
|
|
|
self.attachedguys[ self.attachedguys.size ] = guy;
|
|
|
|
// set the position
|
|
pos = set_pos( guy, maxpos );
|
|
|
|
if ( !isdefined( pos ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( pos == 0 )
|
|
guy.drivingVehicle = true;
|
|
|
|
animpos = anim_pos( self, pos );
|
|
|
|
self.usedPositions[ pos ] = true;
|
|
guy.vehicle_position = pos;
|
|
guy.vehicle_idling = false;
|
|
|
|
if ( IsDefined( animpos.delay ) )
|
|
{
|
|
guy.delay = animpos.delay;
|
|
if ( IsDefined( animpos.delayinc ) )
|
|
{
|
|
self.delayer = guy.delay;
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( animpos.delayinc ) )
|
|
{
|
|
self.delayer += animpos.delayinc;
|
|
guy.delay = self.delayer;
|
|
}
|
|
|
|
guy.ridingvehicle = self;
|
|
guy.orghealth = guy.health;
|
|
guy.vehicle_idle = animpos.idle; // multiple idle anims
|
|
guy.vehicle_standattack = animpos.standattack;
|
|
|
|
guy.deathanim = animpos.death;
|
|
|
|
guy.deathanimscript = animpos.deathscript;
|
|
guy.standing = 0;
|
|
|
|
guy.allowdeath = false;
|
|
|
|
|
|
if ( IsDefined( guy.deathanim ) && !isdefined( guy.magic_bullet_shield ) && vehicle_allows_rider_death() )
|
|
{
|
|
if( guy.vehicle_position != 0 || vehicle_allows_driver_death() )
|
|
{
|
|
guy.allowdeath = ( !isdefined( guy.script_allowdeath ) || guy.script_allowdeath );
|
|
if( isdefined( animpos.death_no_ragdoll ) )
|
|
guy.noragdoll = animpos.death_no_ragdoll;
|
|
}
|
|
}
|
|
|
|
if( guy.classname == "script_model" )
|
|
if ( IsDefined( animpos.death ) && guy.allowdeath && ( !isdefined( guy.script_allowdeath ) || guy.script_allowdeath ) )
|
|
thread guy_death( guy, animpos );
|
|
|
|
if ( !isdefined( guy.vehicle_idle ) )
|
|
guy.allowdeath = true;// these are the truck guys who are simply attached ai
|
|
|
|
|
|
self.riders[ self.riders.size ] = guy;
|
|
|
|
// if ( !isdefined( animpos.explosion_death ) )
|
|
// thread guy_vehicle_death( guy );
|
|
|
|
|
|
// do stuff that should happen AFTER _spawner auto spawn logic below this
|
|
if ( guy.classname != "script_model" && spawn_failed( guy ) )
|
|
return;
|
|
|
|
org = self GetTagOrigin( animpos.sittag );
|
|
angles = self GetTagAngles( animpos.sittag );
|
|
|
|
link_to_sittag( guy, type, animpos.sittag );
|
|
|
|
// some guys "holster" their weapons while operating a vehicle( flak88 guys ).
|
|
// Some of the cod2 animations don't do anything with the weapon tag and
|
|
// require script to remove the weapon, Ideally we would have guys who are riding
|
|
// stash their gun to the sides( like in the jeep rider animations of cod2 )
|
|
if ( IsAI( guy ) )
|
|
{
|
|
guy Teleport( org, angles );
|
|
|
|
guy.a.disablelongdeath = true;
|
|
if ( IsDefined( animpos.bHasGunWhileRiding ) && !animpos.bHasGunWhileRiding )
|
|
guy gun_remove();
|
|
|
|
if ( guy_should_man_turret( animpos ) )
|
|
thread guy_man_turret( guy, pos, climbed_in_vehicle );// assumes first turret is the only turret for now
|
|
|
|
// changes death anim based on speed of the vehicles
|
|
}
|
|
else
|
|
{
|
|
if ( IsDefined( animpos.bHasGunWhileRiding ) && !animpos.bHasGunWhileRiding )
|
|
detach_models_with_substr( guy, "weapon_" );// drones shouldn't have weapon.
|
|
guy.origin = org;
|
|
guy.angles = angles;
|
|
}
|
|
|
|
// let the vehicle know that it should crash because the driver is dead
|
|
if ( pos == 0 && IsDefined( vehicleanim[ 0 ].death ) )
|
|
thread driverdead( guy );
|
|
|
|
self notify( "guy_entered", guy, pos );
|
|
|
|
thread guy_handle( guy, pos );
|
|
|
|
if ( type == "snowmobile" )
|
|
{
|
|
self.steering = 0;
|
|
guy.onSnowMobile = true;
|
|
return;
|
|
}
|
|
|
|
if ( IsDefined( animpos.getin_idle_func ) )
|
|
thread [[ animpos.getin_idle_func ]]( guy, pos );
|
|
else
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
vehicle_allows_driver_death()
|
|
{
|
|
if( !isdefined( self.script_allow_driver_death ) )
|
|
return false;
|
|
return self.script_allow_driver_death ;
|
|
}
|
|
|
|
vehicle_allows_rider_death()
|
|
{
|
|
if( !isdefined( self.script_allow_rider_deaths ) )
|
|
return true;
|
|
return self.script_allow_rider_deaths ;
|
|
}
|
|
|
|
guy_should_man_turret( animpos )
|
|
{
|
|
if ( !IsDefined( animpos.mgturret ) )
|
|
return false;
|
|
|
|
if ( !IsDefined( self.script_nomg ) )
|
|
return true;
|
|
|
|
return !self.script_nomg;
|
|
}
|
|
|
|
handle_attached_guys()
|
|
{
|
|
type = self.vehicletype;
|
|
|
|
self.attachedguys = [];
|
|
if ( !( IsDefined( level.vehicle_aianims ) && IsDefined( level.vehicle_aianims[ type ] ) ) )
|
|
return;
|
|
|
|
maxpos = level.vehicle_aianims[ type ].size;
|
|
|
|
if ( IsDefined( self.script_noteworthy ) && self.script_noteworthy == "ai_wait_go" )
|
|
thread ai_wait_go();
|
|
|
|
self.runningtovehicle = [];
|
|
self.usedPositions = [];
|
|
self.getinorgs = [];
|
|
self.delayer = 0;
|
|
|
|
vehicleanim = level.vehicle_aianims[ type ];
|
|
for ( i = 0; i < maxpos; i++ )
|
|
{
|
|
self.usedPositions[ i ] = false;
|
|
if ( IsDefined( self.script_nomg ) && self.script_nomg && IsDefined( vehicleanim[ i ].bIsgunner ) && vehicleanim[ i ].bIsgunner )
|
|
self.usedpositions[ 1 ] = true;// if this is a gunner position and script no mg is set then don't autoassign a guy to this position
|
|
}
|
|
}
|
|
|
|
load_ai_goddriver( array )
|
|
{
|
|
load_ai( array, true );
|
|
}
|
|
|
|
guy_death( guy, animpos )
|
|
{
|
|
waittillframeend;// override _spawner set health
|
|
|
|
assert( !IsAI( guy ) );
|
|
guy setcandamage( true );
|
|
guy endon( "death" );
|
|
guy.allowdeath = false;
|
|
guy.script_startinghealth = 100000;
|
|
guy.health = 100000;
|
|
guy endon( "jumping_out" );
|
|
|
|
// if he's got magic bullet shield turned on, wait until it's done
|
|
if( IsDefined( guy.magic_bullet_shield ) && guy.magic_bullet_shield )
|
|
{
|
|
while( IsDefined( guy.magic_bullet_shield ) && guy.magic_bullet_shield )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
guy waittill( "damage" );// fragile guy
|
|
thread guy_deathimate_me( guy, animpos );
|
|
}
|
|
|
|
guy_deathimate_me( guy, animpos )
|
|
{
|
|
animtimer = GetTime() + ( GetAnimLength( animpos.death ) * 1000 );
|
|
angles = guy.angles;
|
|
origin = guy.origin;
|
|
guy = convert_guy_to_drone( guy );
|
|
[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", origin );
|
|
detach_models_with_substr( guy, "weapon_" );
|
|
// guy LinkTo( self, animpos.sittag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
|
guy LinkTo( self );
|
|
guy NotSolid();
|
|
guy setanim( animpos.death );
|
|
// thread animontag( guy, animpos.sittag, animpos.death );
|
|
if( isai( guy ) )
|
|
guy animscripts\shared::DropAllAIWeapons();
|
|
else
|
|
detach_models_with_substr( guy, "weapon_" );// drones shouldn't have weapon.
|
|
|
|
|
|
if ( isdefined( animpos.death_delayed_ragdoll ) )
|
|
{
|
|
guy Unlink();
|
|
guy StartRagdoll();
|
|
wait animpos.death_delayed_ragdoll;
|
|
guy Delete();
|
|
return;
|
|
}
|
|
|
|
// guy Unlink();
|
|
// if ( GetDvar( "ragdoll_enable" ) == "0" )
|
|
// {
|
|
// guy Delete();
|
|
// return;
|
|
// }
|
|
|
|
// while ( GetTime() < animtimer && !guy IsRagdoll() )
|
|
// {
|
|
// guy StartRagdoll();
|
|
// wait .05;
|
|
// }
|
|
// if ( !guy IsRagdoll() )
|
|
// guy Delete();// better gone than doing random crap
|
|
}
|
|
|
|
|
|
load_ai( array, bGoddriver, group )
|
|
{
|
|
if ( !isdefined( bGoddriver ) )
|
|
bGoddriver = false;
|
|
|
|
if ( !isdefined( array ) )
|
|
{
|
|
array = vehicle_get_riders();
|
|
}
|
|
|
|
|
|
ent_flag_clear( "unloaded" );
|
|
ent_flag_clear( "loaded" );
|
|
array_levelthread( array, ::get_in_vehicle, bGoddriver, group );
|
|
}
|
|
|
|
|
|
|
|
is_rider( guy )
|
|
{
|
|
for ( i = 0; i < self.riders.size; i++ )
|
|
{
|
|
if ( self.riders[ i ] == guy )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
vehicle_get_riders()
|
|
{
|
|
// get the AI that are assigned to this vehicle, so either were riding in it or are riding in it
|
|
array = [];
|
|
|
|
ai = GetAIArray( self.script_team );
|
|
for ( i = 0; i < ai.size; i++ )
|
|
{
|
|
guy = ai[ i ];
|
|
if ( !isdefined( guy.script_vehicleride ) )
|
|
continue;
|
|
|
|
if ( guy.script_vehicleride != self.script_vehicleride )
|
|
continue;
|
|
|
|
array[ array.size ] = guy;
|
|
}
|
|
|
|
return array;
|
|
}
|
|
|
|
get_my_vehicleride()
|
|
{
|
|
// get the AI that are assigned to this vehicle, so either were riding in it or are riding in it
|
|
array = [];
|
|
|
|
AssertEx( IsDefined( self.script_vehicleride ), "Tried to get my ride but I have no .script_vehicleride" );
|
|
|
|
vehicles = GetEntArray( "script_vehicle", "code_classname" );
|
|
for ( i = 0; i < vehicles.size; i++ )
|
|
{
|
|
vehicle = vehicles[ i ];
|
|
|
|
if ( !isdefined( vehicle.script_vehicleride ) )
|
|
continue;
|
|
|
|
if ( vehicle.script_vehicleride != self.script_vehicleride )
|
|
continue;
|
|
|
|
array[ array.size ] = vehicle;
|
|
}
|
|
|
|
AssertEx( array.size == 1, "Tried to get my ride but there was zero or multiple rides to choose from" );
|
|
return array[ 0 ];
|
|
}
|
|
|
|
get_in_vehicle( guy, bGoddriver, group )
|
|
{
|
|
if ( is_rider( guy ) )
|
|
{
|
|
// this guy is already riding!
|
|
return;
|
|
}
|
|
|
|
if ( !handle_detached_guys_check() )
|
|
{
|
|
// No more spots available!
|
|
return;
|
|
}
|
|
|
|
AssertEx( IsAlive( guy ), "tried to load a vehicle with dead guy, check your AI count to assure spawnability of ai's" );
|
|
|
|
//TODO, next game: this is very similar to anim_reach but was done around the same time or before I knew such thing existed.
|
|
guy_runtovehicle( guy, self, bGoddriver, group );
|
|
}
|
|
|
|
handle_detached_guys_check()
|
|
{
|
|
if ( vehicle_hasavailablespots() )
|
|
return true;
|
|
|
|
AssertMsg( "script sent too many ai to vehicle( max is: " + level.vehicle_aianims[ self.vehicletype ].size + " )" );
|
|
}
|
|
|
|
vehicle_hasavailablespots()
|
|
{
|
|
// spots available - spots being run to by ai
|
|
// simple check This could get a lot more complicated
|
|
if ( level.vehicle_aianims[ self.vehicletype ].size - self.runningtovehicle.size )
|
|
return true;
|
|
else
|
|
return false;
|
|
}
|
|
|
|
guy_runtovehicle_loaded( guy, vehicle )
|
|
{
|
|
vehicle endon( "death" );
|
|
vehicle endon( "stop_loading" );
|
|
|
|
msg = guy waittill_any_return( "long_death", "death", "enteredvehicle" );
|
|
if ( msg != "enteredvehicle" && IsDefined( guy.forced_startingposition ) )
|
|
{
|
|
vehicle.usedpositions[ guy.forced_startingposition ] = false;// clear the position so someone else can take it
|
|
}
|
|
vehicle.runningtovehicle = array_remove( vehicle.runningtovehicle, guy );
|
|
vehicle_loaded_if_full( vehicle );
|
|
}
|
|
|
|
vehicle_loaded_if_full( vehicle )
|
|
{
|
|
if ( ( IsDefined( vehicle.vehicletype ) ) && ( vehicle.vehicletype == "littlebird" ) )
|
|
{
|
|
if ( vehicle.riders.size == 6 )
|
|
vehicle ent_flag_set( "loaded" );
|
|
}
|
|
|
|
else if ( !vehicle.runningtovehicle.size && vehicle.riders.size )
|
|
{
|
|
if ( vehicle.usedpositions[ 0 ] )
|
|
vehicle ent_flag_set( "loaded" );
|
|
else
|
|
vehicle thread vehicle_reload();// vehicle is loaded but the driver died so reload them all. Might not look the best but what would you do if your driver was shot?
|
|
}
|
|
}
|
|
|
|
vehicle_reload()
|
|
{
|
|
Assert( self.riders.size );
|
|
riders = self.riders;
|
|
self vehicle_unload();
|
|
self ent_flag_wait( "unloaded" );
|
|
riders = array_removeDead( riders );
|
|
self thread vehicle_load_ai( riders );
|
|
}
|
|
|
|
remove_magic_bullet_shield_from_guy_on_unload_or_death( guy )
|
|
{
|
|
// TODO: don't do this.
|
|
self waittill_any( "unload", "death" );
|
|
guy stop_magic_bullet_shield();
|
|
}
|
|
|
|
guy_runtovehicle( guy, vehicle, bGoddriver, group )
|
|
{
|
|
vehicle endon( "stop_loading" );
|
|
climbed_in_vehicle = true;
|
|
|
|
if ( !isdefined( bGoddriver ) )
|
|
bGoddriver = false;
|
|
|
|
vehicleanim = level.vehicle_aianims[ vehicle.vehicletype ];
|
|
if ( IsDefined( vehicle.runtovehicleoverride ) )
|
|
{
|
|
vehicle thread [[ vehicle.runtovehicleoverride ]]( guy );
|
|
return;
|
|
}
|
|
|
|
vehicle endon( "death" );
|
|
guy endon( "death" );
|
|
vehicle.runningtovehicle[ vehicle.runningtovehicle.size ] = guy;
|
|
thread guy_runtovehicle_loaded( guy, vehicle );
|
|
availablepositions = [];
|
|
chosenorg = undefined;
|
|
origin = 0;
|
|
|
|
// check for get in animations and simply stuff the guy into the vehiclee if non exist
|
|
bIsgettin = false;
|
|
for ( i = 0; i < vehicleanim.size; i++ )
|
|
{
|
|
if ( IsDefined( vehicleanim[ i ].getin ) )
|
|
bIsgettin = true;
|
|
}
|
|
|
|
if ( !bIsgettin )
|
|
{
|
|
guy notify( "enteredvehicle" );
|
|
vehicle guy_enter( guy, climbed_in_vehicle );
|
|
return;
|
|
}
|
|
|
|
if ( !isdefined( guy.get_in_moving_vehicle ) )
|
|
{
|
|
while ( vehicle Vehicle_GetSpeed() > 1 )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
positions = vehicle get_availablepositions( group );
|
|
|
|
if ( IsDefined( guy.script_startingposition ) )
|
|
{
|
|
chosenorg = vehicle vehicle_getInstart( guy.script_startingposition );
|
|
}
|
|
else
|
|
if ( !vehicle.usedPositions[ 0 ] )
|
|
{
|
|
chosenorg = vehicle vehicle_getInstart( 0 );// driver first!
|
|
if ( bGoddriver )
|
|
{
|
|
AssertEx( !isdefined( guy.magic_bullet_shield ), "magic_bullet_shield guy told to god mode drive a vehicle, you should simply load_ai without the god function for this guy" );
|
|
guy thread magic_bullet_shield();
|
|
thread remove_magic_bullet_shield_from_guy_on_unload_or_death( guy );
|
|
}
|
|
}
|
|
else
|
|
if ( positions.availablepositions.size )
|
|
{
|
|
chosenorg = getClosest( guy.origin, positions.availablepositions );
|
|
}
|
|
else
|
|
{
|
|
chosenorg = undefined;
|
|
}
|
|
|
|
if ( !positions.availablepositions.size && positions.nonanimatedpositions.size )
|
|
{
|
|
guy notify( "enteredvehicle" );
|
|
vehicle guy_enter( guy, climbed_in_vehicle );
|
|
return;
|
|
}
|
|
else
|
|
if ( !isdefined( chosenorg ) )
|
|
{
|
|
return;// nothing available
|
|
}
|
|
|
|
origin = chosenorg.origin;// + vector_multiply( VectorNormalize( chosenorg.origin - vehicle.origin ), 15 );// move the origin out a bit sometimes the start position of the animation is just inside the colision
|
|
angles = chosenorg.angles;
|
|
|
|
guy.forced_startingposition = chosenorg.vehicle_position;
|
|
// flag it so no others use it
|
|
vehicle.usedpositions[ chosenorg.vehicle_position ] = true;
|
|
|
|
|
|
// short circuit any _spawner auto destination behavior
|
|
guy.script_moveoverride = true;
|
|
guy notify( "stop_going_to_node" );
|
|
|
|
guy set_forcegoal();
|
|
guy disable_arrivals();
|
|
guy.goalradius = 16;
|
|
guy SetGoalPos( origin );
|
|
guy waittill( "goal" );
|
|
guy enable_arrivals();
|
|
guy unset_forcegoal();
|
|
guy notify( "boarding_vehicle" );
|
|
|
|
|
|
animpos = anim_pos( vehicle, chosenorg.vehicle_position );
|
|
if ( IsDefined( animpos.delay ) )
|
|
{
|
|
guy.delay = animpos.delay;
|
|
if ( IsDefined( animpos.delayinc ) )
|
|
{
|
|
self.delayer = guy.delay;
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( animpos.delayinc ) )
|
|
{
|
|
self.delayer += animpos.delayinc;
|
|
guy.delay = self.delayer;
|
|
}
|
|
|
|
vehicle link_to_sittag( guy, vehicle.vehicletype, animpos.sittag );
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
guy.allowdeath = false;// they will get the allowdeath back when they get out or get it turned on if there is a death animation.
|
|
|
|
animpos = vehicleanim[ chosenorg.vehicle_position ];
|
|
if ( IsDefined( chosenorg ) )
|
|
{
|
|
if ( IsDefined( animpos.vehicle_getinanim ) )
|
|
{
|
|
if ( IsDefined( animpos.vehicle_getoutanim ) )
|
|
{
|
|
vehicle ClearAnim( animpos.vehicle_getoutanim, 0 );
|
|
}
|
|
vehicle = vehicle getanimatemodel();
|
|
vehicle thread setanimrestart_once( animpos.vehicle_getinanim, animpos.vehicle_getinanim_clear );
|
|
//thread maps\_anim::animscriptDoNoteTracksThread( vehicle, "vehicle_anim_flag" );
|
|
level thread maps\_anim::start_notetrack_wait( vehicle, "vehicle_anim_flag" );
|
|
}
|
|
|
|
if ( IsDefined( animpos.vehicle_getinsoundtag ) )
|
|
origin = vehicle GetTagOrigin( animpos.vehicle_getinsoundtag );
|
|
else
|
|
origin = vehicle.origin;
|
|
if ( IsDefined( animpos.vehicle_getinsound ) )
|
|
thread play_sound_in_space( animpos.vehicle_getinsound, origin );
|
|
|
|
// if ( IsDefined( animpos.vehicle_getinsound ) )
|
|
// {
|
|
// animatemodel = vehicle getanimatemodel();
|
|
// animatemodel thread play_sound_on_entity( animpos.vehicle_getinsound );
|
|
// }
|
|
|
|
getintags = undefined;
|
|
getinthreads = undefined;
|
|
if ( IsDefined( animpos.getin_enteredvehicletrack ) )
|
|
{
|
|
getintags = [];
|
|
getintags[ 0 ] = animpos.getin_enteredvehicletrack;
|
|
getinthreads = [];
|
|
getinthreads[ 0 ] = ::entered_vehicle_notify;
|
|
vehicle link_to_sittag( guy, vehicle.vehicletype, animpos.sittag );// link them, normally they don't link for whatever reason I don't want tof ind otu.
|
|
}
|
|
|
|
vehicle animontag( guy, animpos.sittag, animpos.getin, getintags, getinthreads );
|
|
}
|
|
guy notify( "enteredvehicle" );
|
|
vehicle guy_enter( guy, climbed_in_vehicle );
|
|
}
|
|
|
|
entered_vehicle_notify()
|
|
{
|
|
self notify( "enteredvehicle" );
|
|
}
|
|
|
|
driverdead( guy )
|
|
{
|
|
if ( maps\_vehicle::isHelicopter() )
|
|
return;
|
|
|
|
self.driver = guy;
|
|
self endon( "death" );
|
|
guy waittill( "death" );
|
|
|
|
if ( isdefined( self.vehicle_keeps_going_after_driver_dies ) )
|
|
return;
|
|
|
|
self notify( "driver dead" );
|
|
self.deaddriver = true;// vehiclechase crash
|
|
self SetWaitSpeed( 0 );
|
|
self Vehicle_SetSpeed( 0, 10 );// nothin fancy here.
|
|
self waittill( "reached_wait_speed" );
|
|
self vehicle_unload();
|
|
}
|
|
|
|
copy_cat()
|
|
{
|
|
model = Spawn( "script_model", self.origin );
|
|
model SetModel( self.model );
|
|
size = self GetAttachSize();
|
|
for ( i = 0; i < size; i++ )
|
|
model Attach( self GetAttachModelName( i ) );
|
|
return model;
|
|
}
|
|
|
|
guy_becomes_real_ai( guy, pos )
|
|
{
|
|
if ( IsAI( guy ) )
|
|
return guy;
|
|
|
|
if ( guy.drone_delete_on_unload == true )
|
|
{
|
|
guy Delete();
|
|
return;
|
|
}
|
|
|
|
guy = makerealai( guy );
|
|
|
|
type = self.vehicletype;
|
|
maxpos = level.vehicle_aianims[ type ].size;
|
|
animpos = anim_pos( self, pos );
|
|
link_to_sittag( guy, type, animpos.sittag );
|
|
guy.vehicle_idle = animpos.idle;
|
|
thread guy_idle( guy, pos );
|
|
return guy;
|
|
}
|
|
|
|
link_to_sittag( guy, type, tag )
|
|
{
|
|
if ( type == "snowmobile" )
|
|
guy LinkToBlendToTag( self, tag, false );
|
|
else
|
|
guy _linkto( self, tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
|
}
|
|
|
|
anim_pos( vehicle, pos )
|
|
{
|
|
return level.vehicle_aianims[ vehicle.vehicletype ][ pos ];
|
|
}
|
|
|
|
guy_deathhandle( guy, pos )
|
|
{
|
|
// self endon( "death" );
|
|
guy waittill( "death" );
|
|
if ( !isdefined( self ) )
|
|
return;
|
|
self.riders = array_remove( self.riders, guy );
|
|
self.usedPositions[ pos ] = false;
|
|
}
|
|
|
|
setup_aianimthreads()
|
|
{
|
|
if ( !isdefined( level.vehicle_aianimthread ) )
|
|
level.vehicle_aianimthread = [];
|
|
|
|
if ( !isdefined( level.vehicle_aianimcheck ) )
|
|
level.vehicle_aianimcheck = [];
|
|
|
|
level.vehicle_aianimthread[ "idle" ] = ::guy_idle;
|
|
level.vehicle_aianimthread[ "duck" ] = ::guy_duck;
|
|
|
|
level.vehicle_aianimthread[ "duck_once" ] = ::guy_duck_once;
|
|
level.vehicle_aianimcheck[ "duck_once" ] = ::guy_duck_once_check;
|
|
|
|
level.vehicle_aianimthread[ "weave" ] = ::guy_weave;
|
|
level.vehicle_aianimcheck[ "weave" ] = ::guy_weave_check;
|
|
|
|
level.vehicle_aianimthread[ "stand" ] = ::guy_stand;
|
|
|
|
level.vehicle_aianimthread[ "turn_right" ] = ::guy_turn_right;
|
|
level.vehicle_aianimcheck[ "turn_right" ] = ::guy_turn_right_check;
|
|
|
|
level.vehicle_aianimthread[ "turn_left" ] = ::guy_turn_left;
|
|
level.vehicle_aianimcheck[ "turn_left" ] = ::guy_turn_right_check;
|
|
|
|
level.vehicle_aianimthread[ "turn_hardright" ] = ::guy_turn_hardright;
|
|
|
|
level.vehicle_aianimthread[ "turn_hardleft" ] = ::guy_turn_hardleft;
|
|
level.vehicle_aianimthread[ "turret_fire" ] = ::guy_turret_fire;
|
|
level.vehicle_aianimthread[ "turret_turnleft" ] = ::guy_turret_turnleft;
|
|
level.vehicle_aianimthread[ "turret_turnright" ] = ::guy_turret_turnright;
|
|
level.vehicle_aianimthread[ "unload" ] = ::guy_unload;
|
|
|
|
level.vehicle_aianimthread[ "pre_unload" ] = ::guy_pre_unload;
|
|
level.vehicle_aianimcheck[ "pre_unload" ] = ::guy_pre_unload_check;
|
|
|
|
level.vehicle_aianimthread[ "idle_alert" ] = ::guy_idle_alert;
|
|
level.vehicle_aianimcheck[ "idle_alert" ] = ::guy_idle_alert_check;
|
|
|
|
level.vehicle_aianimthread[ "idle_alert_to_casual" ] = ::guy_idle_alert_to_casual;
|
|
level.vehicle_aianimcheck[ "idle_alert_to_casual" ] = ::guy_idle_alert_to_casual_check;
|
|
|
|
level.vehicle_aianimthread[ "reaction" ] = ::guy_turret_turnright;
|
|
}
|
|
|
|
guy_handle( guy, pos )
|
|
{
|
|
guy.vehicle_idling = true;
|
|
thread guy_deathhandle( guy, pos );
|
|
}
|
|
|
|
// old kubelwagons..
|
|
guy_stand( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
vehicleanim = level.vehicle_aianims[ self.vehicletype ];
|
|
if ( !isdefined( animpos.standup ) )
|
|
return;
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animontag( guy, animpos.sittag, animpos.standup );
|
|
guy_stand_attack( guy, pos );
|
|
}
|
|
|
|
guy_stand_attack( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
guy.standing = 1;
|
|
mintime = 0;
|
|
while ( 1 )
|
|
{
|
|
timer2 = GetTime() + 2000;
|
|
while ( GetTime() < timer2 && IsDefined( guy.enemy ) )
|
|
animontag( guy, animpos.sittag, guy.vehicle_standattack, undefined, undefined, "firing" );
|
|
rnum = RandomInt( 5 ) + 10;
|
|
for ( i = 0; i < rnum; i++ )
|
|
animontag( guy, animpos.sittag, animpos.standidle );
|
|
}
|
|
}
|
|
|
|
guy_stand_down( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
if ( !isdefined( animpos.standdown ) )
|
|
{
|
|
thread guy_stand_attack( guy, pos );
|
|
return;
|
|
}
|
|
animontag( guy, animpos.sittag, animpos.standdown );
|
|
guy.standing = 0;
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
driver_idle_speed( driver, pos )
|
|
{
|
|
driver endon( "newanim" );
|
|
self endon( "death" );
|
|
driver endon( "death" );
|
|
|
|
animpos = anim_pos( self, pos );
|
|
while ( 1 )
|
|
{
|
|
if ( self Vehicle_GetSpeed() == 0 )
|
|
driver.vehicle_idle = animpos.idle_animstop;
|
|
else
|
|
driver.vehicle_idle = animpos.idle_anim;
|
|
wait .25;
|
|
}
|
|
}
|
|
|
|
guy_reaction( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
if ( IsDefined( animpos.reaction ) )
|
|
animontag( guy, animpos.sittag, animpos.reaction );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_turret_turnleft( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
while ( 1 )
|
|
animontag( guy, animpos.sittag, guy.turret_turnleft );
|
|
}
|
|
|
|
guy_turret_turnright( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = anim_pos( self, pos );
|
|
while ( 1 )
|
|
animontag( guy, animpos.sittag, guy.turret_turnleft );
|
|
}
|
|
|
|
guy_turret_fire( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.turret_fire ) )
|
|
animontag( guy, animpos.sittag, animpos.turret_fire );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_idle( guy, pos, ignoredeath )
|
|
{
|
|
guy endon( "newanim" );
|
|
if ( !isdefined( ignoredeath ) )
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
guy.vehicle_idling = true;
|
|
guy notify( "gotime" );
|
|
|
|
if ( !isdefined( guy.vehicle_idle ) )
|
|
{
|
|
// if ( IsDefined( level.whackamolethread ) )
|
|
// thread [[ level.whackamolethread ]]( guy );
|
|
return;// truck guys just stand there linked.. hack for Halftrack guys
|
|
}
|
|
|
|
animpos = anim_pos( self, pos );
|
|
|
|
if ( IsDefined( animpos.mgturret ) )
|
|
return;// mggunners don't idle.
|
|
if ( IsDefined( animpos.hideidle ) && animpos.hideidle )
|
|
guy Hide();
|
|
if ( IsDefined( animpos.idle_animstop ) && IsDefined( animpos.idle_anim ) )// idle alternates between stopping and going
|
|
thread driver_idle_speed( guy, pos );
|
|
|
|
while ( 1 )
|
|
{
|
|
guy notify( "idle" );
|
|
self play_new_idle( guy, animpos );
|
|
}
|
|
}
|
|
|
|
play_new_idle( guy, animpos )
|
|
{
|
|
// self is the vehicle
|
|
if ( IsDefined( guy.vehicle_idle_override ) )
|
|
{
|
|
self animontag( guy, animpos.sittag, guy.vehicle_idle_override );
|
|
return;
|
|
}
|
|
|
|
if ( IsDefined( animpos.idleoccurrence ) )// kubelwagons have random idles like guy driver pointing forward
|
|
{
|
|
theanim = randomoccurrance( guy, animpos.idleoccurrence );
|
|
self animontag( guy, animpos.sittag, guy.vehicle_idle[ theanim ] );
|
|
return;
|
|
}
|
|
|
|
if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_idle ) )
|
|
{
|
|
self animontag( guy, animpos.sittag, animpos.player_idle );
|
|
return;
|
|
}
|
|
|
|
// animate the vehicle with this guy.( IE: driver with stearing wheel )
|
|
if ( IsDefined( animpos.vehicle_idle ) )
|
|
self thread setanimrestart_once( animpos.vehicle_idle );
|
|
self animontag( guy, animpos.sittag, guy.vehicle_idle );
|
|
}
|
|
|
|
randomoccurrance( guy, occurrences )
|
|
{
|
|
range = [];
|
|
totaloccurrance = 0;
|
|
for ( i = 0; i < occurrences.size; i++ )
|
|
{
|
|
totaloccurrance += occurrences[ i ];
|
|
range[ i ] = totaloccurrance;
|
|
}
|
|
pick = RandomInt( totaloccurrance );
|
|
for ( i = 0; i < occurrences.size; i++ )
|
|
if ( pick < range[ i ] )
|
|
return i;
|
|
}
|
|
|
|
|
|
guy_duck_once_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).duck_once );
|
|
}
|
|
|
|
guy_duck_once( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.duck_once ) )
|
|
{
|
|
if ( IsDefined( animpos.vehicle_duck_once ) )
|
|
self thread setanimrestart_once( animpos.vehicle_duck_once );
|
|
animontag( guy, animpos.sittag, animpos.duck_once );
|
|
}
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_weave_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).weave );
|
|
}
|
|
|
|
guy_weave( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.weave ) )
|
|
{
|
|
if ( IsDefined( animpos.vehicle_weave ) )
|
|
self thread setanimrestart_once( animpos.vehicle_weave );
|
|
animontag( guy, animpos.sittag, animpos.weave );
|
|
}
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_duck( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.duckin ) )
|
|
animontag( guy, animpos.sittag, animpos.duckin );
|
|
thread guy_duck_idle( guy, pos );
|
|
}
|
|
|
|
guy_duck_idle( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animpos = anim_pos( self, pos );
|
|
theanim = randomoccurrance( guy, animpos.duckidleoccurrence );
|
|
while ( 1 )
|
|
animontag( guy, animpos.sittag, animpos.duckidle[ theanim ] );
|
|
}
|
|
|
|
guy_duck_out( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.ducking ) && guy.ducking )
|
|
{
|
|
animontag( guy, animpos.sittag, animpos.duckout );
|
|
guy.ducking = false;
|
|
}
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_unload_que( guy )
|
|
{
|
|
self endon( "death" );
|
|
self.unloadque = array_add( self.unloadque, guy );
|
|
guy waittill_any( "death", "jumpedout" );
|
|
self.unloadque = array_remove( self.unloadque, guy );
|
|
if ( !self.unloadque.size )
|
|
{
|
|
self ent_flag_set( "unloaded" );
|
|
self.unload_group = "default";
|
|
}
|
|
}
|
|
|
|
riders_unloadable( unload_group )
|
|
{
|
|
if ( ! self.riders.size )
|
|
return false;
|
|
for ( i = 0; i < self.riders.size; i++ )
|
|
{
|
|
if( !isalive( self.riders[ i ] ) )
|
|
{
|
|
continue;
|
|
}
|
|
Assert( IsDefined( self.riders[ i ].vehicle_position ) );
|
|
if ( check_unloadgroup( self.riders[ i ].vehicle_position, unload_group ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
get_unload_group()
|
|
{
|
|
// make the unload group into a more useful array
|
|
group = [];
|
|
unloadgroups = [];
|
|
if ( IsDefined( self.unload_group ) )
|
|
{
|
|
unloadgroups = level.vehicle_unloadgroups[ self.vehicletype ][ self.unload_group ];
|
|
}
|
|
|
|
foreach ( pos in unloadgroups )
|
|
{
|
|
group[ pos ] = pos;
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
check_unloadgroup( pos, unload_group )
|
|
{
|
|
if ( !IsDefined( unload_group ) )
|
|
unload_group = self.unload_group;
|
|
|
|
type = self.vehicletype;
|
|
if ( !isdefined( level.vehicle_unloadgroups[ type ] ) )
|
|
return true;// just unloads everybody
|
|
|
|
if ( !isdefined( level.vehicle_unloadgroups[ type ][ unload_group ] ) )
|
|
{
|
|
PrintLn( "Invalid Unload group on node at origin: " + self.currentnode.origin + " with group:( \"" + unload_group + "\" )" );
|
|
PrintLn( "Unloading everybody" );
|
|
return true;
|
|
}
|
|
|
|
group = level.vehicle_unloadgroups[ type ][ unload_group ];
|
|
for ( i = 0; i < group.size; i++ )
|
|
{
|
|
if ( pos == group[ i ] )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
getoutrig_model_idle( model, tag, animation )
|
|
{
|
|
self endon( "unloading" );
|
|
while ( 1 )
|
|
animontag( model, tag, animation );
|
|
}
|
|
|
|
getoutrig_model( animpos, model, tag, animation, bIdletillunload )
|
|
{
|
|
|
|
type = self.vehicletype;
|
|
if ( bIdletillunload )
|
|
{
|
|
thread getoutrig_model_idle( model, tag, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].idleanim );
|
|
self waittill( "unloading" );
|
|
}
|
|
|
|
self.unloadque = array_add( self.unloadque, model );
|
|
|
|
self thread getoutrig_abort( model, tag, animation );
|
|
if ( !isdefined( self.crashing ) )
|
|
animontag( model, tag, animation );
|
|
|
|
model Unlink();
|
|
|
|
// looks like somebody deleted the helicopter while that animation was playing. and errored so I'm throwing in this defensive fix!.
|
|
if ( !isdefined( self ) )
|
|
{
|
|
model Delete();
|
|
return;
|
|
}
|
|
|
|
Assert( IsDefined( self.unloadque ) );
|
|
|
|
self.unloadque = array_remove( self.unloadque, model );
|
|
if ( !self.unloadque.size )
|
|
self notify( "unloaded" );
|
|
self.fastroperig[ animpos.fastroperig ] = undefined;
|
|
wait 10;
|
|
model Delete();// possibly do something to delete when the player is not looking at it.
|
|
}
|
|
|
|
getoutrig_disable_abort_notify_after_riders_out()
|
|
{
|
|
wait .05;
|
|
while ( IsAlive( self ) && self.unloadque.size > 2 )
|
|
wait .05;// 1 unloadque will be there for the rope.
|
|
if ( ! IsAlive( self ) || ( IsDefined( self.crashing ) && self.crashing ) )
|
|
return;
|
|
self notify( "getoutrig_disable_abort" );
|
|
}
|
|
|
|
|
|
getoutrig_abort_while_deploying()
|
|
{
|
|
self endon( "end_getoutrig_abort_while_deploying" );
|
|
while ( !isdefined( self.crashing ) )
|
|
wait 0.05;
|
|
|
|
updatedRiders = [];
|
|
|
|
foreach( rider in self.riders )
|
|
{
|
|
if( isAlive( rider ) )
|
|
{
|
|
add_to_array( updatedRiders , rider );
|
|
}
|
|
}
|
|
|
|
array_levelthread( updatedRiders, ::deleteent );
|
|
self notify( "crashed_while_deploying" );
|
|
|
|
updatedRiders = undefined;
|
|
}
|
|
|
|
|
|
getoutrig_abort( model, tag, animation )
|
|
{
|
|
|
|
totalAnimTime = GetAnimLength( animation );
|
|
ropesFallAnimTime = totalAnimTime - 1.0;
|
|
if ( self.vehicletype == "mi17" )
|
|
ropesFallAnimTime = totalAnimTime - .5;// go go ghetto numbers
|
|
|
|
ropesDeployedAnimTime = 2.5;
|
|
|
|
Assert( totalAnimTime > ropesDeployedAnimTime );
|
|
Assert( ropesFallAnimTime - ropesDeployedAnimTime > 0 );
|
|
|
|
self endon( "getoutrig_disable_abort" );
|
|
|
|
thread getoutrig_disable_abort_notify_after_riders_out();
|
|
// self thread notify_delay( "getoutrig_disable_abort", ropesFallAnimTime - ropesDeployedAnimTime );
|
|
|
|
thread getoutrig_abort_while_deploying();
|
|
|
|
waittill_notify_or_timeout( "crashed_while_deploying", ropesDeployedAnimTime );
|
|
|
|
self notify( "end_getoutrig_abort_while_deploying" );
|
|
|
|
// ropes are deployed, wait for a chopper death if it isn't dead already
|
|
while ( !isdefined( self.crashing ) )
|
|
wait 0.05;
|
|
|
|
// make the rope fall by jumping to the end of it's animation where it falls
|
|
thread animontag( model, tag, animation );
|
|
waittillframeend;
|
|
model SetAnimTime( animation, ropesFallAnimTime / totalAnimTime );
|
|
|
|
attacker = self;
|
|
if( isdefined( self.achievement_attacker ) )
|
|
attacker = self.achievement_attacker;
|
|
|
|
// all the guys on the rope must fall off too
|
|
for ( i = 0; i < self.riders.size; i++ )
|
|
{
|
|
if ( !isdefined( self.riders[ i ] ) )
|
|
continue;
|
|
if ( !isdefined( self.riders[ i ].ragdoll_getout_death ) )
|
|
continue;
|
|
if ( self.riders[ i ].ragdoll_getout_death != 1 )
|
|
continue;
|
|
if ( !isdefined( self.riders[ i ].ridingvehicle ) )
|
|
continue;
|
|
// thread animontag_ragdoll_death( self.riders[ i ] );
|
|
self.riders[ i ].forcefallthroughonropes = 1; // I found a case where the "damage" was registering on the
|
|
// self.riders[ i ] DoDamage( 100, self.riders[ i ] geteye(), self.riders[ i ].ridingvehicle );
|
|
if( isalive( self.riders[ i ] ) )
|
|
thread animontag_ragdoll_death_fall( self.riders[ i ], self, attacker );
|
|
// self.riders[ i ] notify( "damage", 100, self.riders[ i ].ridingvehicle );
|
|
}
|
|
}
|
|
|
|
setanimrestart_once( vehicle_anim, bClearAnim )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "dont_clear_anim" );
|
|
|
|
if ( !isdefined( bClearAnim ) )
|
|
{
|
|
bClearAnim = true;
|
|
}
|
|
|
|
cycletime = GetAnimLength( vehicle_anim );
|
|
self SetFlaggedAnimRestart( "vehicle_anim_flag", vehicle_anim );
|
|
wait( cycletime );
|
|
|
|
if ( bClearAnim )
|
|
{
|
|
self ClearAnim( vehicle_anim, 0 );
|
|
}
|
|
}
|
|
|
|
|
|
getout_rigspawn( animatemodel, pos, bIdletillunload )
|
|
{
|
|
if ( !isdefined( bIdletillunload ) )
|
|
bIdletillunload = true;
|
|
type = self.vehicletype;
|
|
animpos = anim_pos( self, pos );
|
|
|
|
if ( IsDefined( self.attach_model_override ) && IsDefined( self.attach_model_override[ animpos.fastroperig ] ) )
|
|
overrridegetoutrig = true;
|
|
else
|
|
overrridegetoutrig = false;
|
|
if ( !isdefined( animpos.fastroperig ) || IsDefined( self.fastroperig[ animpos.fastroperig ] ) || overrridegetoutrig )
|
|
return;// already one in place
|
|
origin = animatemodel GetTagOrigin( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag );
|
|
angles = animatemodel GetTagAngles( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag );
|
|
|
|
self.fastroperiganimating[ animpos.fastroperig ] = true;
|
|
|
|
getoutrig_model = Spawn( "script_model", origin );
|
|
getoutrig_model.angles = angles;
|
|
getoutrig_model.origin = origin;
|
|
getoutrig_model SetModel( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].model );
|
|
|
|
self.fastroperig[ animpos.fastroperig ] = getoutrig_model;// flag this model as out
|
|
|
|
getoutrig_model UseAnimTree( #animtree );
|
|
// getoutrig_model UseAnimTree( level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].animtree );
|
|
|
|
getoutrig_model LinkTo( animatemodel, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
|
thread getoutrig_model( animpos, getoutrig_model, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].dropanim, bIdletillunload );
|
|
return getoutrig_model;
|
|
}
|
|
|
|
check_sound_tag_dupe( soundtag )
|
|
{
|
|
// long day. this is probably 10 times more complicated than it needs to be.
|
|
|
|
if ( !isdefined( self.sound_tag_dupe ) )
|
|
self.sound_tag_dupe = [];
|
|
|
|
duped = false;
|
|
|
|
if ( !isdefined( self.sound_tag_dupe[ soundtag ] ) )
|
|
self.sound_tag_dupe[ soundtag ] = true;
|
|
else
|
|
duped = true;
|
|
|
|
thread check_sound_tag_dupe_reset( soundtag );
|
|
|
|
return duped;
|
|
}
|
|
|
|
check_sound_tag_dupe_reset( soundtag )
|
|
{
|
|
wait .05;
|
|
if ( ! IsDefined( self ) )
|
|
return;
|
|
self.sound_tag_dupe[ soundtag ] = false;
|
|
|
|
keys = GetArrayKeys( self.sound_tag_dupe );
|
|
|
|
for ( i = 0; i < keys.size; i++ )
|
|
if ( self.sound_tag_dupe[ keys[ i ] ] )
|
|
return;
|
|
|
|
self.sound_tag_dupe = undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
guy_unload( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
type = self.vehicletype;
|
|
// check to see if this guy is in the unload group if not then go to idle and ignore the unload call
|
|
if ( !check_unloadgroup( pos ) )
|
|
{
|
|
thread guy_idle( guy, pos );
|
|
return;
|
|
}
|
|
// no getout for this guy.
|
|
if ( !isdefined( animpos.getout ) )
|
|
{
|
|
thread guy_idle( guy, pos );
|
|
return;
|
|
}
|
|
|
|
if ( IsDefined( animpos.hideidle ) && animpos.hideidle )
|
|
guy Show();// bleh. hacking out nonexitant idle animations on seaknight
|
|
|
|
thread guy_unload_que( guy );
|
|
|
|
self endon( "death" );
|
|
if ( IsAI( guy ) && IsAlive( guy ) )
|
|
guy endon( "death" );
|
|
|
|
|
|
if ( IsDefined( guy.onSnowMobile ) )
|
|
{
|
|
guy gun_recall();
|
|
guy.onSnowMobile = undefined;
|
|
if ( IsDefined( guy.getOffVehicleFunc ) )
|
|
guy [[ guy.getOffVehicleFunc ]]();
|
|
}
|
|
|
|
if ( IsDefined( guy.onRotatingVehicleTurret ) )
|
|
{
|
|
guy.onRotatingVehicleTurret = undefined;
|
|
if ( IsDefined( guy.getOffVehicleFunc ) )
|
|
{
|
|
guy [[ guy.getOffVehicleFunc ]]();
|
|
}
|
|
}
|
|
|
|
animatemodel = getanimatemodel();
|
|
|
|
if ( IsDefined( animpos.vehicle_getoutanim ) )
|
|
{
|
|
animatemodel thread setanimrestart_once( animpos.vehicle_getoutanim, animpos.vehicle_getoutanim_clear );
|
|
sound_tag_dupped = false;
|
|
if ( IsDefined( animpos.vehicle_getoutsoundtag ) )
|
|
{
|
|
sound_tag_dupped = check_sound_tag_dupe( animpos.vehicle_getoutsoundtag );
|
|
origin = animatemodel GetTagOrigin( animpos.vehicle_getoutsoundtag );
|
|
}
|
|
else
|
|
origin = animatemodel.origin;
|
|
|
|
if ( IsDefined( animpos.vehicle_getoutsound ) && ! sound_tag_dupped )
|
|
thread play_sound_in_space( animpos.vehicle_getoutsound, origin );
|
|
|
|
sound_tag_dupped = undefined;
|
|
}
|
|
|
|
delay = 0;
|
|
|
|
if ( IsDefined( animpos.getout_timed_anim ) )
|
|
delay += GetAnimLength( animpos.getout_timed_anim );
|
|
if ( IsDefined( animpos.delay ) )
|
|
delay += animpos.delay;
|
|
if ( IsDefined( guy.delay ) )
|
|
delay += guy.delay;
|
|
if ( delay > 0 )
|
|
{
|
|
thread guy_idle( guy, pos );
|
|
wait delay;
|
|
}
|
|
|
|
// handle those guys who are standing when a vehicle unloads
|
|
hascombatjumpout = IsDefined( animpos.getout_combat );
|
|
if ( !hascombatjumpout && guy.standing )
|
|
guy_stand_down( guy, pos );
|
|
else if ( !hascombatjumpout && !guy.vehicle_idling && IsDefined( guy.vehicle_idle ) )
|
|
guy waittill( "idle" );
|
|
|
|
guy.deathanim = undefined;
|
|
guy.deathanimscript = undefined;
|
|
|
|
guy notify( "newanim" );
|
|
|
|
if ( IsDefined( animpos.bHasGunWhileRiding ) && !animpos.bHasGunWhileRiding )
|
|
guy gun_recall();
|
|
|
|
|
|
if ( IsAI( guy ) )
|
|
guy PushPlayer( true );
|
|
// some vehicles don't require an unload animation like the flak88 where all the guys are animating on the ground
|
|
// some guys don't unload at all and stick to the vehicle till death!
|
|
|
|
bNoanimUnload = false;
|
|
if ( IsDefined( animpos.bNoanimUnload ) )
|
|
bNoanimUnload = true;
|
|
else if ( !isdefined( animpos.getout ) ||
|
|
( !isdefined( self.script_unloadmgguy ) && ( IsDefined( animpos.bIsgunner ) && animpos.bIsgunner ) ) ||
|
|
IsDefined( self.script_keepdriver ) && pos == 0 )
|
|
{
|
|
self thread guy_idle( guy, pos );
|
|
return;
|
|
}
|
|
|
|
if ( guy should_give_orghealth() )
|
|
{
|
|
guy.health = guy.orghealth;
|
|
}
|
|
|
|
guy.orghealth = undefined;
|
|
if ( IsAI( guy ) && IsAlive( guy ) )
|
|
guy endon( "death" );
|
|
guy.allowdeath = false;// nobody should die during the transition
|
|
|
|
// some exits all happen at a special tag the halftrack guys all use the same tag to exit but a different tag to sit at.
|
|
if ( IsDefined( animpos.exittag ) )
|
|
tag = animpos.exittag;
|
|
else
|
|
tag = animpos.sittag;
|
|
|
|
if ( hascombatjumpout && guy.standing )
|
|
animation = animpos.getout_combat;
|
|
else if ( IsDefined( guy.get_out_override ) )
|
|
animation = guy.get_out_override;
|
|
else if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout ) )
|
|
animation = animpos.player_getout;
|
|
else
|
|
animation = animpos.getout;
|
|
|
|
|
|
if ( !bNoanimUnload )
|
|
{
|
|
thread guy_unlink_on_death( guy );
|
|
|
|
// throw out the rope before unloading
|
|
if ( IsDefined( animpos.fastroperig ) )
|
|
{
|
|
if ( ! IsDefined( self.fastroperig[ animpos.fastroperig ] ) )
|
|
{
|
|
thread guy_idle( guy, pos );// idle while rope is deploying
|
|
getoutrig_model = self getout_rigspawn( animatemodel, guy.vehicle_position, false );
|
|
// animontag( getoutrig_model, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].tag, level.vehicle_attachedmodels[ type ][ animpos.fastroperig ].idleanim );
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( animpos.getoutsnd ) )
|
|
guy thread play_sound_on_tag( animpos.getoutsnd, "J_Wrist_RI", true );
|
|
|
|
if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound ) )
|
|
guy thread play_sound_on_entity( animpos.player_getout_sound );
|
|
|
|
if ( IsDefined( animpos.getoutloopsnd ) )
|
|
guy thread play_loop_sound_on_tag( animpos.getoutloopsnd );
|
|
|
|
if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound_loop ) )
|
|
level.player thread play_loop_sound_on_entity( animpos.player_getout_sound_loop );
|
|
|
|
guy notify( "newanim" );
|
|
guy notify( "jumping_out" );
|
|
|
|
// testing, default to this while unloading. should fix drones that die on an exploding vehicle.
|
|
|
|
|
|
add_new_spawned_ai = false;
|
|
if( !IsAI( guy ) )
|
|
add_new_spawned_ai = true;
|
|
|
|
guy = guy_becomes_real_ai( guy, pos );
|
|
if ( !isalive( guy ) )
|
|
return;
|
|
|
|
guy.ragdoll_getout_death = true;
|
|
|
|
if ( isdefined( animpos.rappel_kill_achievement ) )
|
|
guy enable_achievement_harder_they_fall();
|
|
|
|
if ( IsDefined( animpos.ragdoll_getout_death ) )
|
|
{
|
|
guy.ragdoll_getout_death = true;
|
|
if ( IsDefined( animpos.ragdoll_fall_anim ) )
|
|
guy.ragdoll_fall_anim = animpos.ragdoll_fall_anim;
|
|
}
|
|
|
|
if( add_new_spawned_ai )
|
|
{
|
|
// need to re-add the drone guy that became a real ai earlier.
|
|
self.riders = array_add( self.riders , guy);
|
|
thread guy_deathhandle( guy, pos );
|
|
thread guy_unload_que( guy );
|
|
guy.ridingvehicle = self;
|
|
|
|
}
|
|
|
|
if ( IsAI( guy ) )
|
|
guy endon( "death" );
|
|
|
|
// notify these again because it's a different entity now and this will kill its new idle.
|
|
guy notify( "newanim" );
|
|
guy notify( "jumping_out" );
|
|
|
|
if ( IsDefined( animpos.littlebirde_getout_unlinks ) && animpos.littlebirde_getout_unlinks )
|
|
{
|
|
self thread stable_unlink( guy );
|
|
}
|
|
|
|
//this is for a secondary bm21 exit animation
|
|
if ( IsDefined( animpos.getout_secondary ) )
|
|
{
|
|
animontag( guy, tag, animation );
|
|
secondaryunloadtag = tag;
|
|
if ( IsDefined( animpos.getout_secondary_tag ) )
|
|
secondaryunloadtag = animpos.getout_secondary_tag;
|
|
animontag( guy, secondaryunloadtag, animpos.getout_secondary );
|
|
}
|
|
else
|
|
{
|
|
guy.anim_end_early = true;// cut off the anim .25 early so it blends nicely into AI.
|
|
animontag( guy, tag, animation );
|
|
}
|
|
|
|
// end all the loop sounds
|
|
if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound_loop ) )
|
|
level.player thread stop_loop_sound_on_entity( animpos.player_getout_sound_loop );
|
|
|
|
if ( IsDefined( animpos.getoutloopsnd ) )
|
|
guy thread stop_loop_sound_on_entity( animpos.getoutloopsnd );
|
|
|
|
|
|
|
|
if ( IsDefined( guy.playerpiggyback ) && IsDefined( animpos.player_getout_sound_end ) )
|
|
level.player thread play_sound_on_entity( animpos.player_getout_sound_end );
|
|
}
|
|
else
|
|
{
|
|
if ( !isai( guy ) )
|
|
{
|
|
if ( guy.drone_delete_on_unload == true )
|
|
{
|
|
guy Delete();
|
|
return;
|
|
}
|
|
|
|
guy = makerealai( guy );
|
|
}
|
|
|
|
}
|
|
|
|
self.riders = array_remove( self.riders, guy );
|
|
self.usedPositions[ pos ] = false;
|
|
|
|
guy.ridingvehicle = undefined;
|
|
guy.drivingVehicle = undefined;
|
|
|
|
if ( !isalive( self ) && !isdefined( animpos.unload_ondeath ) )
|
|
{
|
|
guy Delete();
|
|
return;
|
|
}
|
|
|
|
guy Unlink();
|
|
if ( !isdefined( guy.magic_bullet_shield ) )
|
|
guy.allowdeath = true;// nobody should die during the transition
|
|
|
|
|
|
if ( IsAlive( guy ) )
|
|
{
|
|
guy.a.disablelongdeath = !( guy IsBadGuy() );
|
|
guy.forced_startingposition = undefined;
|
|
guy notify( "jumpedout" );
|
|
guy disable_achievement_harder_they_fall();
|
|
// guy Unlink();
|
|
|
|
if ( IsDefined( animpos.getoutstance ) )
|
|
{
|
|
guy.desired_anim_pose = animpos.getoutstance;
|
|
guy AllowedStances( "crouch" );
|
|
guy thread animscripts\utility::UpdateAnimPose();
|
|
guy AllowedStances( "stand", "crouch", "prone" );
|
|
}
|
|
|
|
guy PushPlayer( false );
|
|
|
|
// if he doesn't target a node make his new goal position his current position
|
|
if ( guy_resets_goalpos( guy ) )
|
|
{
|
|
guy.goalradius = 600;
|
|
guy SetGoalPos( guy.origin );
|
|
}
|
|
}
|
|
|
|
if ( IsDefined( animpos.getout_delete ) && animpos.getout_delete )
|
|
{
|
|
guy Delete();
|
|
return;
|
|
}
|
|
|
|
guy guy_cleanup_vehiclevars();
|
|
}
|
|
|
|
guy_resets_goalpos( guy )
|
|
{
|
|
if ( IsDefined( guy.script_delayed_playerseek ) )
|
|
return false;
|
|
|
|
if ( guy has_color() )
|
|
return false;
|
|
|
|
if ( IsDefined( guy.qSetGoalPos ) )
|
|
return false;
|
|
|
|
if ( !isdefined( guy.target ) )
|
|
return true;
|
|
|
|
// does the guy target nodes?
|
|
targetedNodes = GetNodeArray( guy.target, "targetname" );
|
|
return !targetedNodes.size;
|
|
}
|
|
|
|
|
|
animontag( guy, tag, animation, notetracks, sthreads, flag )
|
|
{
|
|
guy notify( "animontag_thread" );
|
|
guy endon( "animontag_thread" );
|
|
|
|
if ( !isdefined( flag ) )
|
|
flag = "animontagdone";
|
|
|
|
if ( IsDefined( self.modeldummy ) )
|
|
animatemodel = self.modeldummy;
|
|
else
|
|
animatemodel = self;
|
|
|
|
if ( !isdefined( tag ) )
|
|
{
|
|
org = guy.origin;
|
|
angles = guy.angles;
|
|
}
|
|
else
|
|
{
|
|
org = animatemodel GetTagOrigin( tag );
|
|
angles = animatemodel GetTagAngles( tag );
|
|
}
|
|
|
|
if ( IsDefined( guy.ragdoll_getout_death ) )
|
|
level thread animontag_ragdoll_death( guy, self );
|
|
|
|
guy AnimScripted( flag, org, angles, animation );
|
|
|
|
// todo: make doNotetracks work on ai
|
|
if ( IsAI( guy ) )
|
|
thread DoNoteTracks( guy, animatemodel, flag );
|
|
|
|
if ( IsDefined( guy.anim_end_early ) )
|
|
{
|
|
guy.anim_end_early = undefined;
|
|
animWait = GetAnimLength( animation ) - CONST_anim_end_time;
|
|
if ( animWait > 0 )
|
|
wait( animWait );
|
|
|
|
guy StopAnimScripted();
|
|
guy.interval = 0;
|
|
guy thread recover_interval();
|
|
}
|
|
else
|
|
{
|
|
if ( IsDefined( notetracks ) )
|
|
{
|
|
for ( i = 0; i < notetracks.size; i++ )
|
|
{
|
|
guy waittillmatch( flag, notetracks[ i ] );
|
|
guy thread [[ sthreads[ i ] ]]();
|
|
}
|
|
}
|
|
|
|
guy waittillmatch( flag, "end" );
|
|
}
|
|
|
|
guy notify( "anim_on_tag_done" );
|
|
|
|
guy.ragdoll_getout_death = undefined;
|
|
}
|
|
|
|
recover_interval()
|
|
{
|
|
self endon( "death" );
|
|
wait( 2 );
|
|
if ( self.interval == 0 )
|
|
self.interval = 80;
|
|
}
|
|
|
|
animontag_ragdoll_death( guy, vehicle )
|
|
{
|
|
// thread draw_line_from_ent_to_ent_until_notify( level.player, guy, 1, 0, 0, guy, "anim_on_tag_done" );
|
|
if ( IsDefined( guy.magic_bullet_shield ) && guy.magic_bullet_shield )
|
|
return;
|
|
if ( !isAI( guy ) )
|
|
guy SetCanDamage( true );
|
|
|
|
guy endon( "anim_on_tag_done" );
|
|
|
|
|
|
damage = undefined;
|
|
attacker = undefined;
|
|
vehicleallreadydead = vehicle.health <= 0;
|
|
while ( true )
|
|
{
|
|
if ( !vehicleallreadydead && !( IsDefined( vehicle ) && vehicle.health > 0 ) )
|
|
break;
|
|
guy waittill( "damage", damage, attacker );
|
|
if( isdefined( guy.forcefallthroughonropes ) )
|
|
break;
|
|
if ( !isdefined( damage ) )
|
|
continue;
|
|
if ( damage < 1 )
|
|
continue;
|
|
if ( !isdefined( attacker ) )
|
|
continue;
|
|
if ( ( IsPlayer( attacker ) ) )
|
|
break;
|
|
}
|
|
|
|
if ( !isalive( guy ) )
|
|
return;// guy was deleted between "damage" and the "fastrope_fall" notetrack.
|
|
|
|
thread arcadeMode_kill( guy.origin, "rifle", 300 );
|
|
|
|
thread animontag_ragdoll_death_fall( guy, vehicle, attacker );
|
|
|
|
}
|
|
|
|
animontag_ragdoll_death_fall( guy, vehicle, attacker )
|
|
{
|
|
|
|
guy.deathanim = undefined;
|
|
guy.deathFunction = undefined;
|
|
guy.anim_disablePain = true;
|
|
|
|
if ( IsDefined( guy.ragdoll_fall_anim ) )
|
|
{
|
|
// only do fall animation if the guy is high enough to not fall through the ground
|
|
moveDelta = GetMoveDelta( guy.ragdoll_fall_anim, 0, 1 );
|
|
groundPos = PhysicsTrace( guy.origin + ( 0, 0, 16 ), guy.origin - ( 0, 0, 10000 ) );
|
|
|
|
distanceFromGround = Distance( guy.origin + ( 0, 0, 16 ), groundPos );
|
|
if ( abs( moveDelta[ 2 ] + 16 ) <= abs( distanceFromGround ) )
|
|
{
|
|
guy thread play_sound_on_entity( "generic_death_falling" );
|
|
guy AnimScripted( "fastrope_fall", guy.origin, guy.angles, guy.ragdoll_fall_anim );
|
|
guy waittillmatch( "fastrope_fall", "start_ragdoll" );
|
|
}
|
|
}
|
|
if ( !isdefined( guy ) )
|
|
return;// guy was deleted between "damage" and the "fastrope_fall" notetrack.
|
|
guy.deathanim = undefined;
|
|
guy.deathFunction = undefined;
|
|
guy.anim_disablePain = true;
|
|
|
|
guy notify( "rope_death", attacker );
|
|
guy Kill( attacker.origin, attacker );
|
|
|
|
guy animscripts\shared::DropAllAIWeapons();
|
|
guy StartRagdoll();
|
|
}
|
|
|
|
// applies endons to donotetracks
|
|
DoNoteTracks( guy, vehicle, flag )
|
|
{
|
|
guy endon( "newanim" );
|
|
vehicle endon( "death" );
|
|
guy endon( "death" );
|
|
guy animscripts\shared::DoNoteTracks( flag );
|
|
}
|
|
|
|
animatemoveintoplace( guy, org, angles, movetospotanim )
|
|
{
|
|
guy AnimScripted( "movetospot", org, angles, movetospotanim );
|
|
guy waittillmatch( "movetospot", "end" );
|
|
}
|
|
|
|
guy_vehicle_death( guy, attacker, type )
|
|
{
|
|
if( !isalive( guy ) )
|
|
return;
|
|
|
|
animpos = anim_pos( self, guy.vehicle_position );
|
|
|
|
guy.vehicle_attacker = attacker;
|
|
|
|
if ( IsDefined( animpos.explosion_death ) )
|
|
return guy_blowup( guy );
|
|
|
|
if ( IsDefined( animpos.unload_ondeath ) && IsDefined( self ) )
|
|
{
|
|
thread guy_idle( guy, guy.vehicle_position, true );// hack, idle gets canceled out by the death;
|
|
wait animpos.unload_ondeath;
|
|
if ( IsDefined( guy ) && IsDefined( self ) )
|
|
{
|
|
self.groupedanim_pos = guy.vehicle_position;
|
|
self vehicle_ai_event( "unload" );
|
|
}
|
|
return;
|
|
}
|
|
|
|
if ( IsDefined( guy ) )
|
|
{
|
|
if( isdefined( guy.ragdoll_getout_death ) && type != "bm21_troops" )
|
|
{
|
|
return;
|
|
}
|
|
|
|
|
|
origin = guy.origin;
|
|
|
|
/#
|
|
assertex( !isdefined( guy.magic_bullet_shield ), "Vehicle script is trying to delete magic bullet shield guy " + guy getentnum() );
|
|
#/
|
|
|
|
[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", origin );
|
|
|
|
|
|
// Fix for the driver and front passenger not dying when they are getting out and blown up by an explosion
|
|
// Look below for possibly more appropiate fix for all vehicles.
|
|
if ( type == "bm21_troops" )
|
|
{
|
|
guy.allowdeath = true;
|
|
guy Kill();
|
|
return;
|
|
}
|
|
|
|
guy Delete();
|
|
|
|
// This is probably the more appropiate fix, but since we are so late in the project we'll just isolate the fix to the
|
|
// bm21 vehicletype. If we do decide to go with this. Be sure to pass in is_helicopter from vehicle_kill() and
|
|
// remove the above if( isdefined( guy.ragdoll_getout_death ) && type != "bm21_troops" )
|
|
// if ( IsDefined( is_helicopter ) && is_helicopter )
|
|
// {
|
|
// if( isdefined( guy.ragdoll_getout_death ) )
|
|
// {
|
|
// return;
|
|
// }
|
|
// else
|
|
// {
|
|
// guy Delete();
|
|
// }
|
|
// }
|
|
// else
|
|
// {
|
|
// guy Kill();
|
|
// }
|
|
}
|
|
}
|
|
|
|
guy_turn_right_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).turn_right );
|
|
}
|
|
|
|
guy_turn_right( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.vehicle_turn_right ) )
|
|
thread setanimrestart_once( animpos.vehicle_turn_right );
|
|
animontag( guy, animpos.sittag, animpos.turn_right );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_turn_left( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = anim_pos( self, pos );
|
|
if ( IsDefined( animpos.vehicle_turn_left ) )
|
|
self thread setanimrestart_once( animpos.vehicle_turn_left );
|
|
animontag( guy, animpos.sittag, animpos.turn_left );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_turn_left_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).turn_left );
|
|
}
|
|
|
|
|
|
guy_turn_hardright( guy, pos )
|
|
{
|
|
animpos = level.vehicle_aianims[ self.vehicletype ][ pos ];
|
|
if ( IsDefined( animpos.idle_hardright ) )
|
|
guy.vehicle_idle_override = animpos.idle_hardright;
|
|
}
|
|
|
|
guy_turn_hardleft( guy, pos )
|
|
{
|
|
animpos = level.vehicle_aianims[ self.vehicletype ][ pos ];
|
|
if ( IsDefined( animpos.idle_hardleft ) )
|
|
guy.vehicle_idle_override = animpos.idle_hardleft;
|
|
}
|
|
|
|
ai_wait_go()
|
|
{
|
|
self endon( "death" );
|
|
self waittill( "loaded" );
|
|
maps\_vehicle::gopath( self );
|
|
}
|
|
|
|
set_pos( guy, maxpos )
|
|
{
|
|
pos = guy.script_startingposition;
|
|
|
|
/#
|
|
if ( IsDefined( pos ) )
|
|
{
|
|
AssertEx( ( pos < maxpos ) && ( pos >= 0 ), "script_startingposition on a vehicle rider must be between " + maxpos + " and 0" );
|
|
}
|
|
#/
|
|
|
|
if ( IsDefined( guy.forced_startingposition ) )
|
|
{
|
|
pos = guy.forced_startingposition;
|
|
}
|
|
|
|
if ( IsDefined( pos ) )
|
|
{
|
|
return pos;
|
|
}
|
|
|
|
AssertEx( !isdefined( pos ), "Illegal starting position" );
|
|
|
|
// if there isn't one then set it to the lowest unused spot
|
|
for ( j = 0; j < self.usedPositions.size; j++ )
|
|
{
|
|
if ( self.usedPositions[ j ] )
|
|
continue;
|
|
|
|
return j;
|
|
}
|
|
|
|
if ( IsDefined( guy.script_vehicleride ) )
|
|
AssertMsg( "can't find vehicle rider position , likely too many guys assigned to ride a vehicle ( copy paste in radiant? ) rider number: ", guy.script_vehicleride );
|
|
|
|
AssertMsg( "All spots on this vehicle were used up, too many AI trying to ride." );
|
|
}
|
|
|
|
guy_man_turret( guy, pos, climbed_in_vehicle )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
turret = self.mgturret[ animpos.mgturret ];
|
|
|
|
if( !isalive( guy ) )
|
|
return;
|
|
turret endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
if ( isdefined( climbed_in_vehicle ) && isdefined( animpos.passenger_2_turret_func ) )
|
|
[[ animpos.passenger_2_turret_func ]]( self, guy, pos, turret );
|
|
|
|
set_turret_team( turret );
|
|
turret SetDefaultDropPitch( 0 );
|
|
|
|
wait( 0.1 );
|
|
guy endon( "guy_man_turret_stop" );
|
|
level thread maps\_mgturret::mg42_setdifficulty( turret, getDifficulty() );
|
|
//turret SetMode( "auto_ai" );
|
|
turret SetTurretIgnoreGoals( true );
|
|
|
|
while ( 1 )
|
|
{
|
|
if ( !isdefined( guy GetTurret() ) )
|
|
guy UseTurret( turret );
|
|
wait 1;
|
|
}
|
|
}
|
|
|
|
guy_unlink_on_death( guy )
|
|
{
|
|
guy endon( "jumpedout" );
|
|
guy waittill( "death" );
|
|
if ( IsDefined( guy ) )
|
|
guy Unlink();
|
|
}
|
|
|
|
|
|
guy_blowup( guy )
|
|
{
|
|
if ( ! IsDefined( guy.vehicle_position ) )
|
|
return;
|
|
pos = guy.vehicle_position;
|
|
anim_pos = anim_pos( self, pos );
|
|
if ( !isdefined( anim_pos.explosion_death ) )
|
|
return;
|
|
|
|
[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", guy.origin );
|
|
|
|
guy.deathanim = anim_pos.explosion_death;
|
|
// guy.allowdeath = true;
|
|
angles = self.angles;
|
|
origin = guy.origin;
|
|
|
|
// I think there's a better way to to dthis but I'm lazy
|
|
if ( IsDefined( anim_pos.explosion_death_offset ) )
|
|
{
|
|
origin += vector_multiply( AnglesToForward( angles ), anim_pos.explosion_death_offset[ 0 ] );
|
|
origin += vector_multiply( AnglesToRight( angles ), anim_pos.explosion_death_offset[ 1 ] );
|
|
origin += vector_multiply( AnglesToUp( angles ), anim_pos.explosion_death_offset[ 2 ] );
|
|
}
|
|
guy = convert_guy_to_drone( guy );
|
|
detach_models_with_substr( guy, "weapon_" );
|
|
guy NotSolid();
|
|
guy.origin = origin;
|
|
guy.angles = angles;
|
|
|
|
guy AnimScripted( "deathanim", origin, angles, anim_pos.explosion_death );
|
|
fraction = .3;
|
|
if ( IsDefined( anim_pos.explosion_death_ragdollfraction ) )
|
|
fraction = anim_pos.explosion_death_ragdollfraction;
|
|
animlength = GetAnimLength( anim_pos.explosion_death );
|
|
timer = GetTime() + ( animlength * 1000 );
|
|
wait animlength * fraction;
|
|
|
|
force = ( 0, 0, 1 );
|
|
org = guy.origin;
|
|
|
|
if ( GetDvar( "ragdoll_enable" ) == "0" )
|
|
{
|
|
guy Delete();
|
|
return;
|
|
}
|
|
|
|
if( isai( guy ) )
|
|
guy animscripts\shared::DropAllAIWeapons();
|
|
else
|
|
detach_models_with_substr( guy, "weapon_" );// drones shouldn't have weapon.
|
|
|
|
while ( ! guy IsRagdoll() && GetTime() < timer )
|
|
{
|
|
org = guy.origin;
|
|
wait .05;
|
|
force = guy.origin - org;
|
|
guy StartRagdoll();
|
|
|
|
}
|
|
wait .05;
|
|
force = vector_multiply( force, 20000 );
|
|
for ( i = 0; i < 3; i++ )
|
|
{
|
|
if ( IsDefined( guy ) )
|
|
org = guy.origin;
|
|
// PhysicsJolt( org, 250, 250, force );
|
|
wait( 0.05 );
|
|
}
|
|
if ( !guy IsRagdoll() )
|
|
guy Delete();
|
|
|
|
}
|
|
|
|
// maybe I should make a utility out of this?. could be slow
|
|
convert_guy_to_drone( guy, bKeepguy )
|
|
{
|
|
if ( !isdefined( bKeepguy ) )
|
|
bKeepguy = false;
|
|
model = Spawn( "script_model", guy.origin );
|
|
model.angles = guy.angles;
|
|
model SetModel( guy.model );
|
|
size = guy GetAttachSize();
|
|
for ( i = 0; i < size; i++ )
|
|
{
|
|
model Attach( guy GetAttachModelName( i ), guy GetAttachTagName( i ) );
|
|
// struct.attachedtags[ i ] = guy GetAttachTagName( i );
|
|
}
|
|
model UseAnimTree( #animtree );
|
|
if ( IsDefined( guy.team ) )
|
|
model.team = guy.team;
|
|
if ( !bKeepguy )
|
|
guy Delete();
|
|
model MakeFakeAI();
|
|
return model;
|
|
}
|
|
|
|
vehicle_animate( animation, animtree )
|
|
{
|
|
self UseAnimTree( animtree );
|
|
self SetAnim( animation );
|
|
}
|
|
|
|
vehicle_getInstart( pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
Assert( IsDefined( animpos ) );
|
|
Assert( IsDefined( animpos.sittag ) );
|
|
Assert( IsDefined( animpos.getin ) );
|
|
return vehicle_getanimstart( animpos.getin, animpos.sittag, pos );
|
|
}
|
|
|
|
//TODO: anim_reach is the new and cool way.
|
|
vehicle_getanimstart( animation, tag, pos )
|
|
{
|
|
struct = SpawnStruct();
|
|
|
|
origin = undefined;
|
|
angles = undefined;
|
|
Assert( IsDefined( animation ) );
|
|
org = self GetTagOrigin( tag );
|
|
ang = self GetTagAngles( tag );
|
|
origin = GetStartOrigin( org, ang, animation );
|
|
angles = GetStartAngles( org, ang, animation );
|
|
struct.origin = origin;
|
|
struct.angles = angles;
|
|
struct.vehicle_position = pos;
|
|
return struct;
|
|
}
|
|
|
|
is_position_in_group( vehicle, pos, group )
|
|
{
|
|
if ( !isdefined( group ) )
|
|
return true;
|
|
|
|
Assert( IsDefined( level.vehicle_unloadgroups[ vehicle.vehicletype ][ group ] ) );
|
|
vehicles_group = level.vehicle_unloadgroups[ vehicle.vehicletype ][ group ];
|
|
foreach ( member in vehicles_group )
|
|
{
|
|
if ( member == pos )
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
get_availablepositions( group )
|
|
{
|
|
vehicleanim = level.vehicle_aianims[ self.vehicletype ];
|
|
availablepositions = [];
|
|
nonanimatedpositions = [];
|
|
for ( i = 0; i < self.usedPositions.size; i++ )
|
|
{
|
|
if ( self.usedPositions[ i ] )
|
|
continue;
|
|
|
|
if ( IsDefined( vehicleanim[ i ].getin ) && is_position_in_group( self, i, group ) )
|
|
availablepositions[ availablepositions.size ] = vehicle_getInstart( i );
|
|
else
|
|
nonanimatedpositions[ nonanimatedpositions.size ] = i;
|
|
}
|
|
|
|
struct = SpawnStruct();
|
|
struct.availablepositions = availablepositions;
|
|
struct.nonanimatedpositions = nonanimatedpositions;
|
|
|
|
return struct;
|
|
}
|
|
|
|
getanimatemodel()
|
|
{
|
|
if ( IsDefined( self.modeldummy ) )
|
|
return self.modeldummy;
|
|
else
|
|
return self;
|
|
}
|
|
|
|
animpos_override_standattack( type, pos, animation )
|
|
{
|
|
level.vehicle_aianims[ type ][ pos ].vehicle_standattack = animation;
|
|
}
|
|
|
|
detach_models_with_substr( guy, substr )
|
|
{
|
|
size = guy GetAttachSize();
|
|
modelstodetach = [];
|
|
tagsstodetach = [];
|
|
index = 0;
|
|
for ( i = 0; i < size; i++ )
|
|
{
|
|
modelname = guy GetAttachModelName( i );
|
|
tagname = guy GetAttachTagName( i );
|
|
if ( IsSubStr( modelname, substr ) )
|
|
{
|
|
modelstodetach[ index ] = modelname;
|
|
tagsstodetach[ index ] = tagname;
|
|
}
|
|
}
|
|
for ( i = 0; i < modelstodetach.size; i++ )
|
|
guy Detach( modelstodetach[ i ], tagsstodetach[ i ] );
|
|
}
|
|
|
|
should_give_orghealth()
|
|
{
|
|
if ( !isai( self ) )
|
|
return false;
|
|
if ( !isdefined( self.orghealth ) )
|
|
return false;
|
|
return !isdefined( self.magic_bullet_shield );
|
|
}
|
|
|
|
guy_pre_unload_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).pre_unload );
|
|
}
|
|
|
|
|
|
guy_pre_unload( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
if ( !isdefined( animpos.pre_unload ) )
|
|
return;
|
|
|
|
/*
|
|
guy = guy_becomes_real_ai( guy, pos );
|
|
if ( !isalive( guy ) )
|
|
return;
|
|
*/
|
|
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animontag( guy, animpos.sittag, animpos.pre_unload );
|
|
while ( 1 )
|
|
animontag( guy, animpos.sittag, animpos.pre_unload_idle );
|
|
}
|
|
|
|
guy_idle_alert( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
if ( !isdefined( animpos.idle_alert ) )
|
|
return;
|
|
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
// animontag( guy, animpos.sittag, animpos.idle_alert );
|
|
while ( 1 )
|
|
animontag( guy, animpos.sittag, animpos.idle_alert );
|
|
}
|
|
|
|
guy_idle_alert_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).idle_alert );
|
|
}
|
|
|
|
|
|
|
|
guy_idle_alert_to_casual( guy, pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
if ( !isdefined( animpos.idle_alert ) )
|
|
return;
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
|
|
animontag( guy, animpos.sittag, animpos.idle_alert_to_casual );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_idle_alert_to_casual_check( guy, pos )
|
|
{
|
|
return IsDefined( anim_pos( self, pos ).idle_alert_to_casual );
|
|
}
|
|
|
|
|
|
|
|
|
|
stable_unlink( guy )
|
|
{
|
|
self waittill( "stable_for_unlink" );
|
|
|
|
if( isalive( guy ) )
|
|
guy Unlink();
|
|
}
|
|
|
|
|
|
track_entered_vehicle()
|
|
{
|
|
|
|
}
|
|
|
|
animate_guys( other )
|
|
{
|
|
return_guys = [];
|
|
foreach ( guy in self.riders )
|
|
{
|
|
if ( !isalive( guy ) )
|
|
continue;
|
|
|
|
if ( IsDefined( level.vehicle_aianimcheck[ other ] ) && ! [[ level.vehicle_aianimcheck[ other ] ]]( guy, guy.vehicle_position ) )
|
|
continue;// ignore this if they have a check function and this anim doesn't exist
|
|
|
|
if ( IsDefined( level.vehicle_aianimthread[ other ] ) )
|
|
{
|
|
guy notify( "newanim" );
|
|
guy.queued_anim_threads = [];// sorry que, this animation is more important.
|
|
thread [[ level.vehicle_aianimthread[ other ] ]]( guy, guy.vehicle_position );
|
|
return_guys[ return_guys.size ] = guy;
|
|
}
|
|
else
|
|
PrintLn( "Error: leaaaaaaaaaaaaaak", other );
|
|
}
|
|
return return_guys;
|
|
|
|
}
|
|
|
|
guy_cleanup_vehiclevars()
|
|
{
|
|
self.vehicle_idling = undefined;
|
|
self.standing = undefined;
|
|
self.vehicle_position = undefined;
|
|
self.delay = undefined;
|
|
}
|
|
|
|
delete_corpses_around_vehicle()
|
|
{
|
|
centroid = self getcentroid();
|
|
point_in_bounds = self getpointinbounds( 1,0,0 );
|
|
dist = distance( point_in_bounds, centroid );
|
|
corpses = getcorpsearray();
|
|
foreach( corpse in corpses )
|
|
if( distance( corpse.origin, centroid ) < dist )
|
|
corpse delete();
|
|
|
|
} |