/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * 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(); }