2159 lines
No EOL
60 KiB
Text
2159 lines
No EOL
60 KiB
Text
/*
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
* * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
|
|
|
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 ) // 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 common_scripts\utility;
|
|
|
|
#using_animtree( "generic_human" );
|
|
|
|
guy_enter( guy, vehicle )
|
|
{
|
|
// 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" );
|
|
|
|
type = self.vehicletype;
|
|
vehicleanim = level.vehicle_aianims[ type ];
|
|
maxpos = level.vehicle_aianims[ type ].size;
|
|
|
|
// walkers are special
|
|
if( isdefined( guy.script_vehiclewalk ) )
|
|
{
|
|
pos = set_walkerpos( guy, level.vehicle_walkercount[ type ] );
|
|
thread WalkWithVehicle( guy, pos );
|
|
return;
|
|
}
|
|
|
|
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.pos = pos;
|
|
|
|
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
|
|
|
|
//Sumeet ( 05/30/08 ) : added key to allow specifying combat_idle anims on vehicle spawners
|
|
guy.vehicle_idle_combat = animpos.idle_combat;
|
|
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 ) )
|
|
guy.allowdeath = true;
|
|
|
|
if ( isdefined( animpos.death ) )
|
|
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 );
|
|
guy linkto( self, animpos.sittag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
|
|
|
|
|
// 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(); // these guys don't ever get their gun back through this script
|
|
|
|
if( isdefined( animpos.mgturret ) && !( isdefined( self.script_nomg ) && self.script_nomg > 0 ) )
|
|
thread guy_man_turret( guy, pos ); // assumes first turret is the only turret for now
|
|
|
|
// SRS 3/25/08 added ability to specify combat getout anims on the spawner
|
|
if( IsDefined( guy.script_combat_getout ) && guy.script_combat_getout )
|
|
{
|
|
guy.do_combat_getout = true;
|
|
}
|
|
|
|
//Sumeet ( 05/30/08 ) : added key to allow specifying combat_idle anims on vehicle spawners
|
|
if( IsDefined( guy.script_combat_idle ) && guy.script_combat_idle )
|
|
{
|
|
guy.do_combat_idle = true;
|
|
}
|
|
|
|
// 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 );
|
|
|
|
// reacts to groupedanimevent notify
|
|
thread guy_handle( guy, pos );
|
|
thread guy_idle( guy, pos );
|
|
|
|
}
|
|
|
|
guy_array_enter( guysarray, vehicle )
|
|
{
|
|
guysarray = maps\_vehicle::sort_by_startingpos( guysarray );
|
|
|
|
lastguy = false;
|
|
for( i = 0;i < guysarray.size;i ++ )
|
|
{ if( !( i + 1 < guysarray.size ) )
|
|
lastguy = true;
|
|
guy_enter( guysarray[ i ], vehicle );
|
|
}
|
|
}
|
|
|
|
handle_attached_guys()
|
|
{
|
|
type = self.vehicletype;
|
|
|
|
if( isdefined( self.script_vehiclewalk ) )
|
|
{
|
|
for( i = 0;i < 6;i ++ ) // any other number of walker tags will break this script
|
|
{
|
|
self.walk_tags[ i ] = ( "tag_walker" + i );
|
|
self.walk_tags_used[ i ] = false;
|
|
}
|
|
}
|
|
|
|
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
|
|
guy endon ("death");
|
|
guy.allowdeath = false;
|
|
guy.script_startinghealth = 100000;
|
|
guy.health = 100000;
|
|
guy endon ( "jumping_out" );
|
|
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 notsolid();
|
|
thread animontag( guy, animpos.sittag, animpos.death );
|
|
if(!isdefined(animpos.death_delayed_ragdoll))
|
|
guy waittillmatch ( "animontagdone" , "start_ragdoll" );
|
|
else
|
|
{
|
|
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 )
|
|
{
|
|
if(!isdefined(bGoddriver))
|
|
bGoddriver = false;
|
|
if ( !isdefined( array ) )
|
|
{
|
|
array = vehicle_get_riders();
|
|
}
|
|
|
|
array_levelthread( array, ::get_in_vehicle, bGoddriver );
|
|
}
|
|
|
|
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", "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 )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
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" );
|
|
guy waittill_any( "long_death", "death", "enteredvehicle" );
|
|
vehicle.runningtovehicle = array_remove( vehicle.runningtovehicle, guy );
|
|
if ( !vehicle.runningtovehicle.size && vehicle.riders.size && vehicle.usedpositions[0] )
|
|
vehicle notify( "loaded" );
|
|
}
|
|
|
|
remove_magic_bullet_shield_from_guy_on_unload_or_death( guy )
|
|
{
|
|
self waittill_any( "unload","death" );
|
|
guy stop_magic_bullet_shield();
|
|
}
|
|
|
|
guy_runtovehicle( guy, vehicle, bGoddriver )
|
|
{
|
|
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" );
|
|
guy_enter_vehicle( guy, vehicle );
|
|
return;
|
|
}
|
|
|
|
while( vehicle getspeedmph() > 1 )
|
|
wait .05;
|
|
|
|
positions = vehicle get_availablepositions();
|
|
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" );
|
|
guy_enter_vehicle( guy, 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.pos;
|
|
// flag it so no others use it
|
|
vehicle.usedpositions[ chosenorg.pos ] = true;
|
|
|
|
|
|
// short circuit any _spawner auto destination behavior
|
|
guy.script_moveoverride = true;
|
|
guy notify( "stop_going_to_node" );
|
|
|
|
guy set_forcegoal();
|
|
guy.goalradius = 16;
|
|
guy setgoalpos( origin );
|
|
guy waittill( "goal" );
|
|
guy unset_forcegoal();
|
|
|
|
guy.allowdeath = false; // they will get the allowdeath back when they get out or get it turned on if there is a death animation.
|
|
|
|
if( isdefined( chosenorg ) )
|
|
{
|
|
if( isdefined( vehicleanim[ chosenorg.pos ].vehicle_getinanim ) )
|
|
{
|
|
vehicle = vehicle getanimatemodel();
|
|
vehicle thread setanimrestart_once( vehicleanim[ chosenorg.pos ].vehicle_getinanim, vehicleanim[ chosenorg.pos ].vehicle_getinanim_clear );
|
|
}
|
|
|
|
if ( isdefined( vehicleanim[ chosenorg.pos ].vehicle_getinsoundtag ) )
|
|
origin = vehicle gettagorigin( vehicleanim[ chosenorg.pos ].vehicle_getinsoundtag );
|
|
else
|
|
origin = vehicle.origin;
|
|
if ( isdefined( vehicleanim[ chosenorg.pos ].vehicle_getinsound ) )
|
|
sound = vehicleanim[ chosenorg.pos ].vehicle_getinsound;
|
|
else
|
|
sound = "truck_door_open";
|
|
thread maps\_utility::play_sound_in_space( sound, origin );
|
|
|
|
// if ( isdefined( vehicleanim[ chosenorg.pos ].vehicle_getinsound ) )
|
|
// {
|
|
// animatemodel = vehicle getanimatemodel();
|
|
// animatemodel thread play_sound_on_entity( vehicleanim[ chosenorg.pos ].vehicle_getinsound );
|
|
// }
|
|
vehicle animontag( guy, vehicleanim[ chosenorg.pos ].sittag, vehicleanim[ chosenorg.pos ].getin );
|
|
}
|
|
guy notify( "enteredvehicle" );
|
|
guy_enter_vehicle( guy, vehicle );
|
|
}
|
|
|
|
driverdead( guy )
|
|
{
|
|
//if ( maps\_vehicle::isHelicopter() )
|
|
// return;
|
|
|
|
self.driver = guy;
|
|
self endon( "death" );
|
|
guy waittill( "death" );
|
|
self.deaddriver = true; // vehiclechase crash
|
|
self setwaitspeed(0);
|
|
self setspeed(0,4); // nothin fancy here.
|
|
self waittill( "reached_wait_speed" );
|
|
self notify ("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;
|
|
}
|
|
|
|
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[ "reaction" ] = ::guy_turret_turnright;
|
|
|
|
// Added drive reaction and death_fire anim threads (Alex Liu 6-1-08)
|
|
level.vehicle_aianimthread[ "drive_reaction" ] = ::guy_drive_reaction;
|
|
level.vehicle_aianimcheck[ "drive_reaction" ] = ::guy_drive_reaction_check;
|
|
level.vehicle_aianimthread[ "death_fire" ] = ::guy_death_fire;
|
|
level.vehicle_aianimcheck[ "death_fire" ] = ::guy_death_fire_check;
|
|
}
|
|
|
|
guy_handle( guy, pos )
|
|
{
|
|
|
|
guy.vehicle_idling = true;
|
|
guy.queued_anim_threads = [];
|
|
thread guy_deathhandle( guy, pos );
|
|
thread guy_queue_anim( guy, pos );
|
|
guy endon( "death" );
|
|
guy endon( "jumpedout" );
|
|
while( 1 )
|
|
{
|
|
//TODO NEXT GAME.. This shouldn't wait, should be a function but will likely be rewritten anyway to follow the anim_single conventions
|
|
self waittill( "groupedanimevent", other );
|
|
if ( isdefined( level.vehicle_aianimcheck[ other ] ) && ! [[ level.vehicle_aianimcheck[ other ] ]]( guy, pos ) )
|
|
continue;// ignore this if they have a check function and this anim doesn't exist
|
|
|
|
if ( isdefined( self.groupedanim_pos ) )
|
|
{
|
|
if(pos != self.groupedanim_pos)
|
|
continue;
|
|
waittillframeend;
|
|
self.groupedanim_pos = undefined; // set before the groupedanimevent call.
|
|
}
|
|
|
|
if( isdefined( level.vehicle_aianimthread[ other ] ) )
|
|
{
|
|
if ( isdefined( self.queueanim ) && self.queueanim )
|
|
{
|
|
add_anim_queue( guy, level.vehicle_aianimthread[ other ] );
|
|
waittillframeend;// allows all of the guys to queue their anims if necessary.
|
|
self.queueanim = false;
|
|
}
|
|
else
|
|
{
|
|
guy notify( "newanim" );
|
|
guy.queued_anim_threads = [];// sorry que, this animation is more important.
|
|
thread [[ level.vehicle_aianimthread[ other ] ]]( guy, pos );
|
|
}
|
|
}
|
|
else
|
|
println( "leaaaaaaaaaaaaaak", other );
|
|
}
|
|
}
|
|
|
|
|
|
// attempt at fixing pops by using a que. I tried this once in CoD1 once, maybe I can make it work now.
|
|
add_anim_queue( guy, sthread )
|
|
{
|
|
guy.queued_anim_threads[ guy.queued_anim_threads.size ] = sthread;
|
|
|
|
}
|
|
|
|
guy_queue_anim( guy, pos )
|
|
{
|
|
guy endon( "death" );
|
|
self endon( "death" );
|
|
lastanimframe = gettime() - 100;
|
|
while ( 1 )
|
|
{
|
|
if ( guy.queued_anim_threads.size )
|
|
{
|
|
if ( gettime() != lastanimframe )
|
|
guy waittill( "anim_on_tag_done" );
|
|
if ( !guy.queued_anim_threads.size )
|
|
continue;// there has been an interuption
|
|
guy notify( "newanim" );
|
|
thread [[ guy.queued_anim_threads[ 0 ] ]]( guy, pos );
|
|
guy.queued_anim_threads = array_remove( guy.queued_anim_threads, guy.queued_anim_threads[ 0 ] );
|
|
wait .05;
|
|
}
|
|
else
|
|
{
|
|
// wait .05;// maybe wait on some notify.
|
|
guy waittill( "anim_on_tag_done" );
|
|
lastanimframe = gettime();
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// 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 getspeedmph() == 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" );
|
|
if( isdefined( guy.vehicle_idle_override ) )
|
|
animontag( guy, animpos.sittag, guy.vehicle_idle_override );
|
|
else if( isdefined( animpos.idleoccurrence ) ) // kubelwagons have random idles like guy driver pointing forward
|
|
{
|
|
theanim = randomoccurrance( guy, animpos.idleoccurrence );
|
|
animontag( guy, animpos.sittag, guy.vehicle_idle[ theanim ] );
|
|
}
|
|
else if( isdefined( guy.playerpiggyback ) && isdefined( animpos.player_idle ) )
|
|
animontag( guy, animpos.sittag, animpos.player_idle );
|
|
else // jeeps just have one idle
|
|
{
|
|
// animate the vehicle with this guy.( IE: driver with stearing wheel )
|
|
if ( isdefined( animpos.vehicle_idle ) )
|
|
self thread setanimrestart_once( animpos.vehicle_idle );
|
|
|
|
//Sumeet ( 05/30/08 ) : added key to allow specifying combat_idle anims on vehicle spawners
|
|
if ( isdefined( guy.do_combat_idle ) && guy.do_combat_idle && isdefined( guy.vehicle_idle_combat ) )
|
|
{
|
|
animontag( guy, animpos.sittag, guy.vehicle_idle_combat );
|
|
}
|
|
else
|
|
{
|
|
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 notify( "unloaded" );
|
|
self.unload_group = "default";
|
|
}
|
|
}
|
|
|
|
riders_unloadable( unload_group )
|
|
{
|
|
if( ! self.riders.size )
|
|
return false;
|
|
for ( i = 0; i < self.riders.size; i++ )
|
|
{
|
|
assert( isdefined( self.riders[i].pos ) );
|
|
if( check_unloadgroup( self.riders[i].pos, unload_group ) )
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
|
|
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( "unload" );
|
|
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.getoutrig ].idleanim );
|
|
self waittill( "unload" );
|
|
}
|
|
|
|
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.getoutrig[ animpos.getoutrig ] = 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;
|
|
array_levelthread( self.riders, ::deleteent );
|
|
self notify ("crashed_while_deploying");
|
|
}
|
|
|
|
|
|
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 );
|
|
|
|
// 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 ] 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 SetAnimRestart( 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.getoutrig ] ) )
|
|
overrridegetoutrig = true;
|
|
else
|
|
overrridegetoutrig = false;
|
|
if ( !isdefined( animpos.getoutrig ) || isdefined( self.getoutrig[ animpos.getoutrig ] ) || overrridegetoutrig )
|
|
return; // already one in place
|
|
origin = animatemodel gettagorigin( level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].tag );
|
|
angles = animatemodel gettagangles( level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].tag );
|
|
|
|
self.getoutriganimating[ animpos.getoutrig ] = true;
|
|
|
|
getoutrig_model = spawn( "script_model", origin );
|
|
getoutrig_model.angles = angles;
|
|
getoutrig_model.origin = origin;
|
|
getoutrig_model setmodel( level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].model );
|
|
|
|
self.getoutrig[ animpos.getoutrig ] = getoutrig_model;// flag this model as out
|
|
|
|
getoutrig_model UseAnimTree( #animtree );
|
|
// getoutrig_model UseAnimTree( level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].animtree );
|
|
|
|
getoutrig_model linkto( animatemodel, level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].tag, ( 0, 0, 0 ), ( 0, 0, 0 ) );
|
|
thread getoutrig_model( animpos, getoutrig_model, level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].tag , level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].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" );
|
|
|
|
|
|
animatemodel = getanimatemodel();
|
|
|
|
if ( isdefined( animpos.vehicle_getoutanim ) )
|
|
{
|
|
animatemodel thread setanimrestart_once( animpos.vehicle_getoutanim, animpos.vehicle_getoutanim_clear );
|
|
self notify( "open_door_climbout" );
|
|
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 = animpos.vehicle_getoutsound;
|
|
else
|
|
sound = "truck_door_open";
|
|
if( ! sound_tag_dupped )
|
|
thread maps\_utility::play_sound_in_space( sound, 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( 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( hascombatjumpout && isdefined( guy.do_combat_getout ) && ( guy.do_combat_getout ))
|
|
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.getoutrig ) )
|
|
{
|
|
if ( ! isdefined( self.getoutrig[ animpos.getoutrig ] ) )
|
|
{
|
|
thread guy_idle( guy, pos ); // idle while rope is deploying
|
|
getoutrig_model = self getout_rigspawn( animatemodel, guy.pos, false );
|
|
// animontag( getoutrig_model, level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].tag , level.vehicle_attachedmodels[ type ][ animpos.getoutrig ].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 ) )
|
|
get_players()[0] 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.
|
|
guy.ragdoll_getout_death = true;
|
|
|
|
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;
|
|
}
|
|
|
|
animontag( guy, tag, animation );
|
|
//this is for a secondary bm21 exit animation
|
|
if ( isdefined( animpos.getout_secondary ) )
|
|
{
|
|
secondaryunloadtag = tag;
|
|
if ( isdefined( animpos.getout_secondary_tag ) )
|
|
secondaryunloadtag = animpos.getout_secondary_tag;
|
|
animontag( guy, secondaryunloadtag, animpos.getout_secondary);
|
|
}
|
|
|
|
|
|
// end all the loop sounds
|
|
if( isdefined( guy.playerpiggyback ) && isdefined( animpos.player_getout_sound_loop ) )
|
|
get_players()[0] 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 ) )
|
|
get_players()[0] thread play_sound_on_entity( animpos.player_getout_sound_end );
|
|
}
|
|
|
|
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( !isai( guy ) )
|
|
{
|
|
if( guy.drone_delete_on_unload == true )
|
|
{
|
|
guy delete();
|
|
return;
|
|
}
|
|
|
|
guy = makerealai( guy );
|
|
}
|
|
|
|
|
|
if( isalive( guy ) )
|
|
{
|
|
guy.a.disablelongdeath = false;
|
|
guy.forced_startingposition = undefined;
|
|
guy notify( "jumpedout" );
|
|
// 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
|
|
qSetGoalPos = false;
|
|
|
|
if( guy has_color() )
|
|
{
|
|
qSetGoalPos = false;
|
|
}
|
|
else
|
|
if( !isdefined( guy.target ) )
|
|
{
|
|
qSetGoalPos = true;
|
|
}
|
|
else
|
|
{
|
|
// change to support script_origins or make mymapents update nodes
|
|
targetedNodes = getNodeArray( guy.target, "targetname" );
|
|
|
|
if( targetedNodes.size == 0 )
|
|
{
|
|
qSetGoalPos = true;
|
|
}
|
|
}
|
|
|
|
if( qSetGoalPos )
|
|
{
|
|
guy.goalradius = 600;
|
|
guy setGoalPos( guy.origin );
|
|
}
|
|
}
|
|
|
|
if( isdefined( animpos.getout_delete ) && animpos.getout_delete )
|
|
{
|
|
guy delete();
|
|
return;
|
|
}
|
|
}
|
|
|
|
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( 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;
|
|
}
|
|
|
|
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( damage ) )
|
|
continue;
|
|
if ( damage < 1 )
|
|
continue;
|
|
if( !isdefined( attacker ) )
|
|
continue;
|
|
if ( ( IsPlayer(attacker) ) || ( ( isdefined( guy.ridingvehicle ) ) && ( attacker == guy.ridingvehicle ) ) )
|
|
break;
|
|
}
|
|
|
|
if( !isdefined(guy) )
|
|
return; // guy was deleted between "damage" and the "fastrope_fall" notetrack.
|
|
|
|
// CODER MOD: TOMMY K - 07/30/08
|
|
arcademode_assignpoints( "arcademode_score_enemyexitingcar", 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 doDamage( guy.health + 100, attacker.origin, attacker );
|
|
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 )
|
|
{
|
|
animpos = anim_pos( self, guy.pos );
|
|
if ( isdefined( animpos.getout ) )
|
|
self endon( "unload" );
|
|
guy endon ("death");
|
|
self endon( "forcedremoval" );// pile on teh hacks. I need to clean this up next game and make the vehicles simply sort through the riders and figure it out rather than thread each guy like this.
|
|
self waittill( "death" );
|
|
// waittillframeend;
|
|
if ( isdefined(animpos.unload_ondeath) && isdefined( self ) )
|
|
{
|
|
thread guy_idle( guy, guy.pos, true ); // hack, idle gets canceled out by the death;
|
|
wait animpos.unload_ondeath;
|
|
self.groupedanim_pos = guy.pos;
|
|
self notify ("groupedanimevent","unload");
|
|
return;
|
|
}
|
|
if ( isdefined( guy ) )
|
|
{
|
|
origin = guy.origin;
|
|
guy delete();
|
|
[[ level.global_kill_func ]]( "MOD_RIFLE_BULLET", "torso_upper", origin );
|
|
}
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
guy_drive_reaction( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = anim_pos( self, pos );
|
|
animontag( guy, animpos.sittag, animpos.drive_reaction );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_drive_reaction_check( guy, pos )
|
|
{
|
|
return isdefined( anim_pos( self, pos ).drive_reaction );
|
|
}
|
|
|
|
guy_death_fire( guy, pos )
|
|
{
|
|
guy endon( "newanim" );
|
|
self endon( "death" );
|
|
guy endon( "death" );
|
|
animpos = anim_pos( self, pos );
|
|
animontag( guy, animpos.sittag, animpos.death_fire );
|
|
thread guy_idle( guy, pos );
|
|
}
|
|
|
|
guy_death_fire_check( guy, pos )
|
|
{
|
|
return isdefined( anim_pos( self, pos ).death_fire );
|
|
}
|
|
|
|
|
|
ai_wait_go()
|
|
{
|
|
self endon( "death" );
|
|
self waittill( "loaded" );
|
|
maps\_vehicle::gopath( self );
|
|
}
|
|
|
|
set_pos( guy, maxpos )
|
|
{
|
|
pos = undefined;
|
|
script_startingposition = isdefined(guy.script_startingposition);
|
|
|
|
if( isdefined( guy.forced_startingposition ) )
|
|
{
|
|
pos = guy.forced_startingposition;
|
|
assertEx( (( pos < maxpos ) && ( pos >= 0 ) ), "script_startingposition on a vehicle rider must be between " + maxpos + " and 0" );
|
|
return pos;
|
|
}
|
|
if ( script_startingposition && !self.usedpositions[ guy.script_startingposition ] )
|
|
{
|
|
pos = guy.script_startingposition;
|
|
assertEx( (( pos < maxpos ) && ( pos >= 0 ) ), "script_startingposition on a vehicle rider must be between " + maxpos + " and 0" );
|
|
}
|
|
else
|
|
{
|
|
if( script_startingposition )
|
|
{
|
|
println("vehicle rider with script_startingposition: "+guy.script_startingposition+" and script_vehicleride: "+self.script_vehicleride+" that's been taken" );
|
|
assertmsg("startingposition conflict, see console");
|
|
|
|
}
|
|
assert( !script_startingposition );
|
|
// 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 ] == true )
|
|
continue;
|
|
pos = j;
|
|
break;
|
|
}
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
guy_man_turret( guy , pos )
|
|
{
|
|
animpos = anim_pos( self, pos );
|
|
turret = self.mgturret[ animpos.mgturret ];
|
|
turret setdefaultdroppitch( 0 );
|
|
wait( 0.1 );
|
|
turret endon( "death" );
|
|
guy endon( "death" );
|
|
level thread maps\_mgturret::mg42_setdifficulty( turret, getdifficulty() );
|
|
turret setmode( "auto_ai" );
|
|
turret setturretignoregoals( true );
|
|
|
|
// Alex Liu (5-14-08) Give the MG guy an identifier, so he can be pinpointed later in script
|
|
guy.script_on_vehicle_turret = 1;
|
|
|
|
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();
|
|
}
|
|
|
|
blowup_riders()
|
|
{
|
|
self array_levelthread( self.riders, ::guy_blowup );
|
|
}
|
|
|
|
guy_blowup( guy )
|
|
{
|
|
if( ! isdefined( guy.pos ) )
|
|
return; // the fast ropes are a rider with no .pos assigned.
|
|
pos = guy.pos;
|
|
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 stopanimscripted();
|
|
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;
|
|
}
|
|
|
|
|
|
while ( ! guy isragdoll() && gettime() < timer )
|
|
{
|
|
org = guy.origin;
|
|
wait .05;
|
|
force = guy.origin-org;
|
|
guy startragdoll();
|
|
|
|
}
|
|
wait .05;
|
|
force = vectorscale( 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 );
|
|
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.pos = pos;
|
|
return struct;
|
|
}
|
|
|
|
get_availablepositions()
|
|
{
|
|
vehicleanim = level.vehicle_aianims[ self.vehicletype ];
|
|
availablepositions = [];
|
|
nonanimatedpositions = [];
|
|
for( i = 0;i < self.usedPositions.size;i ++ )
|
|
if( !self.usedPositions[ i ] )
|
|
if( isdefined( vehicleanim[ i ].getin ) )
|
|
availablepositions[ availablepositions.size ] = vehicle_getInstart( i );
|
|
else
|
|
nonanimatedpositions[ nonanimatedpositions.size ] = i;
|
|
|
|
struct = spawnstruct();
|
|
struct.availablepositions = availablepositions;
|
|
struct.nonanimatedpositions = nonanimatedpositions;
|
|
return struct;
|
|
}
|
|
|
|
set_walkerpos( guy, maxpos )
|
|
{
|
|
pos = undefined;
|
|
if( isdefined( guy.script_startingposition ) )
|
|
{
|
|
pos = guy.script_startingposition;
|
|
assertEx( (( pos < maxpos ) && ( pos >= 0 ) ), "script_startingposition on a vehicle rider must be between " + maxpos + " and 0" );
|
|
}
|
|
else
|
|
{
|
|
// if there isn't one then set it to the lowest unused spot
|
|
pos = -1;
|
|
for( j = 0;j < self.walk_tags_used.size;j ++ )
|
|
{
|
|
if( self.walk_tags_used[ j ] == true )
|
|
continue;
|
|
pos = j;
|
|
self.walk_tags_used[ j ] = true;
|
|
break;
|
|
}
|
|
assertEX( pos >= 0, "Vehicle ran out of walking spots. This is usually caused by making more than 6 AI walk with a vehicle." );
|
|
}
|
|
return pos;
|
|
}
|
|
|
|
WalkWithVehicle( guy, pos )
|
|
{
|
|
// CODER_MOD - JamesS make sure we have a walkers array first
|
|
if( !IsDefined(self.walkers) )
|
|
self.walkers = [];
|
|
|
|
self.walkers[ self.walkers.size ] = guy;
|
|
if( !isdefined( guy.FollowMode ) )
|
|
guy.FollowMode = "close";
|
|
guy.WalkingVehicle = self;
|
|
if( guy.FollowMode == "close" )
|
|
{
|
|
guy.vehiclewalkmember = pos;
|
|
level thread vehiclewalker_freespot_ondeath( guy );
|
|
}
|
|
guy notify( "stop friendly think" );
|
|
guy vehiclewalker_updateGoalPos( self, "once" );
|
|
guy thread vehiclewalker_removeonunload( self );
|
|
guy thread vehiclewalker_updateGoalPos( self );
|
|
guy thread vehiclewalker_teamUnderAttack();
|
|
}
|
|
|
|
vehiclewalker_removeonunload( vehicle )
|
|
{
|
|
vehicle endon( "death" );
|
|
vehicle waittill( "unload" );
|
|
vehicle.walkers = array_remove( vehicle.walkers, self );
|
|
}
|
|
|
|
// Makes guys walking on the right side of a vehicle shift to the left side
|
|
// of the vehicle until all spots available on the left side are filled up
|
|
shiftSides( side )
|
|
{
|
|
if( !isdefined( side ) )
|
|
return;
|
|
if( ( side != "left" ) && ( side != "right" ) )
|
|
{
|
|
iprintln( "Valid sides are 'left' and 'right' only" );
|
|
return;
|
|
}
|
|
|
|
// find out if this guy is still part of a tank...
|
|
if( !isdefined( self.WalkingVehicle ) )
|
|
return;
|
|
|
|
// see if this guy is already on the requested side...
|
|
if( self.WalkingVehicle.walk_tags[ self.vehiclewalkmember ].side == side )
|
|
return;
|
|
|
|
// move this guy to the next available spot on the new side of the tank
|
|
for( i = 0;i < self.WalkingVehicle.walk_tags.size;i ++ )
|
|
{
|
|
if( self.WalkingVehicle.walk_tags[ i ].side != side )
|
|
continue;
|
|
if( self.WalkingVehicle.walk_tags_used[ i ] == false )
|
|
{
|
|
if( self.WalkingVehicle getspeedMPH() > 0 )
|
|
{
|
|
// tank is moving to make the AI get to the other side by walking behind the tank
|
|
self notify( "stop updating goalpos" );
|
|
self setgoalpos( self.WalkingVehicle.backpos.origin );
|
|
self.WalkingVehicle.walk_tags_used[ self.vehiclewalkmember ] = false;
|
|
self.vehiclewalkmember = i;
|
|
self.WalkingVehicle.walk_tags_used[ self.vehiclewalkmember ] = true;
|
|
self waittill( "goal" );
|
|
self thread vehiclewalker_updateGoalPos( self.WalkingVehicle );
|
|
}
|
|
else
|
|
self.vehiclewalkmember = i;
|
|
return;
|
|
}
|
|
iprintln( "TANKAI: Guy couldn't move to the " + side + " side of the tank because no positions on that side are free" );
|
|
}
|
|
}
|
|
|
|
vehiclewalker_freespot_ondeath( guy )
|
|
{
|
|
guy waittill( "death" );
|
|
if( !isdefined( guy.WalkingVehicle ) )
|
|
return;
|
|
guy.WalkingVehicle.walk_tags_used[ guy.vehiclewalkmember ] = false;
|
|
}
|
|
|
|
vehiclewalker_teamUnderAttack()
|
|
{
|
|
self endon( "death" );
|
|
for( ;; )
|
|
{
|
|
self waittill( "damage", amount, attacker );
|
|
if( !isdefined( attacker ) )
|
|
continue;
|
|
if( ( !isdefined( attacker.team ) ) || ( isplayer( attacker ) ) )
|
|
continue;
|
|
|
|
if( ( isdefined( self.RidingTank ) ) && ( isdefined( self.RidingTank.allowUnloadIfAttacked ) ) && ( self.RidingTank.allowUnloadIfAttacked == false ) )
|
|
continue;
|
|
if( ( isdefined( self.WalkingVehicle ) ) && ( isdefined( self.WalkingVehicle.allowUnloadIfAttacked ) ) && ( self.WalkingVehicle.allowUnloadIfAttacked == false ) )
|
|
continue;
|
|
|
|
self.WalkingVehicle.teamUnderAttack = true;
|
|
self.WalkingVehicle notify( "unload" );
|
|
return;
|
|
}
|
|
}
|
|
|
|
GetNewNodePositionAheadofVehicle( guy )
|
|
{
|
|
minimumDistance = 300 + ( 50 * ( self getspeedMPH() ) );
|
|
// get the next node in front of the tank
|
|
nextNode = undefined;
|
|
if( !isdefined( self.CurrentNode.target ) )
|
|
return self.origin;
|
|
|
|
nextNode = getVehicleNode( self.CurrentNode.target, "targetname" );
|
|
|
|
if( !isdefined( nextNode ) )
|
|
{
|
|
if( isdefined( guy.NodeAfterVehicleWalk ) )
|
|
return guy.NodeAfterVehicleWalk.origin;
|
|
else
|
|
return self.origin;
|
|
}
|
|
|
|
// Is this position far enough from the tank?
|
|
if( distance( self.origin, nextNode.origin ) >= minimumDistance )
|
|
return nextNode.origin;
|
|
|
|
for( ;; )
|
|
{
|
|
// Is this position far enough from the tank?
|
|
if( distance( self.origin, nextNode.origin ) >= minimumDistance )
|
|
return nextNode.origin;
|
|
if( !isdefined( nextNode.target ) )
|
|
break;
|
|
nextNode = getVehicleNode( nextNode.target, "targetname" );
|
|
}
|
|
|
|
if( isdefined( guy.NodeAfterVehicleWalk ) )
|
|
return guy.NodeAfterVehicleWalk.origin;
|
|
else
|
|
return self.origin;
|
|
}
|
|
|
|
vehiclewalker_updateGoalPos( tank, option )
|
|
{
|
|
self endon( "death" );
|
|
tank endon( "death" );
|
|
self endon( "stop updating goalpos" );
|
|
self endon( "unload" );
|
|
for( ;; )
|
|
{
|
|
if( self.FollowMode == "cover nodes" )
|
|
{
|
|
self.oldgoalradius = self.goalradius;
|
|
self.goalradius = 300;
|
|
self.walkdist = 64;
|
|
position = tank GetNewNodePositionAheadofVehicle( self );
|
|
}
|
|
else // followmode = "close"
|
|
{
|
|
self.oldgoalradius = self.goalradius;
|
|
self.goalradius = 2;
|
|
self.walkdist = 64;
|
|
position = tank gettagOrigin( tank.walk_tags[ self.vehiclewalkmember ] );
|
|
}
|
|
|
|
// SETS THE GOAL POSITION ONLY ONCE IF THAT OPTION IS SPECIFIED
|
|
if( ( isdefined( option ) ) && ( option == "once" ) )
|
|
{
|
|
trace = bulletTrace( ( position + ( 0, 0, 100 ) ), ( position - ( 0, 0, 500 ) ), false, undefined );
|
|
if( self.FollowMode == "close" )
|
|
self teleport( trace[ "position" ] );
|
|
self setGoalPos( trace[ "position" ] );
|
|
return;
|
|
}
|
|
// -- -- -- -- -- -- -- --
|
|
|
|
tankSpeed = tank getspeedmph();
|
|
if( tankSpeed > 0 )
|
|
{
|
|
trace = bulletTrace( ( position + ( 0, 0, 100 ) ), ( position - ( 0, 0, 500 ) ), false, undefined );
|
|
self setGoalPos( trace[ "position" ] );
|
|
}
|
|
wait 0.5;
|
|
}
|
|
}
|
|
|
|
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 );
|
|
} |