911 lines
22 KiB
Plaintext
911 lines
22 KiB
Plaintext
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\_anim;
|
|
|
|
#using_animtree( "generic_human" );
|
|
|
|
humvee_turret_init( turret, turretType )
|
|
{
|
|
self endon( "killanimscript" );// code
|
|
|
|
Assert( IsDefined( turret ) );
|
|
|
|
animscripts\utility::initialize( turretType );
|
|
|
|
self.no_ai = true;
|
|
self.noDrop = true;
|
|
self.a.movement = "stop";
|
|
self.a.special = turretType;
|
|
self.a.usingTurret = turret;
|
|
self.ignoreme = true;
|
|
self.isCustomAnimating = false;
|
|
self SetTurretAnim( self.primaryTurretAnim );
|
|
self SetAnimKnobRestart( self.primaryTurretAnim, 1, 0.2, 1 );
|
|
|
|
if ( IsDefined( self.weapon ) )
|
|
{
|
|
self animscripts\shared::placeWeaponOn( self.weapon, "none" );
|
|
}
|
|
|
|
self.onRotatingVehicleTurret = true;
|
|
self.getOffVehicleFunc = ::turret_cleanup_on_unload;
|
|
|
|
// end some _vehicle and _mgturret threads that we don't want
|
|
self notify( "guy_man_turret_stop" );
|
|
turret notify( "stop_burst_fire_unmanned" );
|
|
|
|
// setup the turret
|
|
turret.turretState = "start";
|
|
turret.aiOwner = self;
|
|
turret.fireTime = 0;
|
|
turret SetMode( "sentry" );
|
|
turret SetSentryOwner( self );
|
|
turret SetDefaultDropPitch( 0 );
|
|
turret SetTurretCanAIDetach( false );
|
|
|
|
self gunner_pain_init();
|
|
level thread handle_gunner_pain( self, turret );
|
|
level thread handle_gunner_death( self, turret );
|
|
|
|
// start tracking the turret rotation
|
|
turret thread turret_track_rotatedirection( self );
|
|
|
|
// start turret fire director
|
|
turret.doFiring = false;
|
|
self thread fireDirector( turret );
|
|
|
|
wait( 0.05 );
|
|
if ( IsAlive( self ) )
|
|
{
|
|
self thread gunner_turning_anims( turret );
|
|
}
|
|
}
|
|
|
|
gunner_pain_init()
|
|
{
|
|
self.allowPain = false; // we're going to handle it ourselves
|
|
self setFlashbangImmunity( true );
|
|
|
|
self.og_health = self.health;
|
|
self.health = 200;
|
|
}
|
|
|
|
gunner_pain_reset()
|
|
{
|
|
self.allowPain = true;
|
|
self setFlashbangImmunity( false );
|
|
self.health = self.og_health;
|
|
}
|
|
|
|
handle_gunner_pain( gunner, turret )
|
|
{
|
|
gunner endon( "death" );
|
|
turret endon( "death" );
|
|
gunner endon( "dismount" );
|
|
gunner endon( "jumping_out" );
|
|
|
|
while ( 1 )
|
|
{
|
|
flashedNotify = "flashbang";
|
|
|
|
//gunner waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, dflags );
|
|
msg = gunner waittill_any_return( "damage", flashedNotify );
|
|
|
|
/* custom anim pre-empts pain or flash
|
|
if( gunner.isCustomAnimating )
|
|
{
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
painAnim = random( gunner.turretPainAnims );
|
|
if ( msg == flashedNotify )
|
|
{
|
|
painAnim = gunner.turretFlashbangedAnim;
|
|
gunner animscripts\face::SayGenericDialogue( "flashbang" );
|
|
}
|
|
|
|
//turret thread turret_recenter();
|
|
gunner DoCustomAnim( turret, painAnim, false );
|
|
turret notify( "pain_done" );
|
|
}
|
|
}
|
|
|
|
turret_recenter()
|
|
{
|
|
self turret_aim_straight();
|
|
self waittill( "pain_done" );
|
|
self turret_aim_restore();
|
|
}
|
|
|
|
handle_gunner_death( gunner, turret )
|
|
{
|
|
gunner endon( "dismount" );
|
|
turret endon( "turret_cleanup" );
|
|
|
|
gunner.deathanim = gunner.turretDeathAnim;
|
|
gunner.noragdoll = true;
|
|
|
|
gunner waittill( "death" );
|
|
level thread turret_cleanup( gunner, turret );
|
|
}
|
|
|
|
// for when _vehicle_aianim wants to unload the gunner, it doesn't know as much as this script yet
|
|
turret_cleanup_on_unload()
|
|
{
|
|
Assert( IsDefined( self.ridingVehicle ) );
|
|
|
|
turret = self.ridingVehicle.mgturret[ 0 ];
|
|
Assert( IsDefined( turret ) );
|
|
|
|
// clean up AI - moved it here since it's only needed for unloading.
|
|
if ( IsAlive( self ) )
|
|
{
|
|
self.no_ai = undefined;
|
|
self.noDrop = undefined;
|
|
self.ignoreme = false;
|
|
self.a.special = "none";
|
|
self.a.usingTurret = undefined;
|
|
self.deathanim = undefined;
|
|
self gunner_pain_reset();
|
|
self.isCustomAnimating = undefined;
|
|
|
|
self.turretSpecialAnims = undefined;
|
|
self.turretPainAnims = undefined;
|
|
|
|
self.onRotatingVehicleTurret = undefined;
|
|
self.getOffVehicleFunc = undefined;
|
|
|
|
self StopUseTurret();
|
|
|
|
if ( IsDefined( self.weapon ) )
|
|
{
|
|
self animscripts\shared::placeWeaponOn( self.weapon, "right" );
|
|
}
|
|
}
|
|
|
|
level thread turret_cleanup( self, turret );
|
|
}
|
|
|
|
turret_cleanup( gunner, turret )
|
|
{
|
|
if ( !IsDefined( turret ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
turret notify( "kill_fireController" );
|
|
turret notify( "turret_cleanup" );
|
|
turret SetMode( "manual" );
|
|
turret ClearTargetEntity();
|
|
|
|
turret SetDefaultDropPitch( turret.default_drop_pitch );
|
|
|
|
if ( IsDefined( gunner ) )
|
|
{
|
|
gunner ClearAnim( gunner.additiveUsegunRoot, 0 );
|
|
gunner ClearAnim( gunner.additiveRotateRoot, 0 );
|
|
gunner ClearAnim( gunner.turretSpecialAnimsRoot, 0 );
|
|
}
|
|
|
|
// clean up turret
|
|
turret.fireInterval = undefined;
|
|
turret.closeEnoughAimDegrees = undefined;
|
|
turret.fireControllerFunc = undefined;
|
|
|
|
turret.turretState = "free";
|
|
turret.aiOwner = undefined;
|
|
turret.fireTime = undefined;
|
|
|
|
if ( IsDefined( turret.specialCleanupFunc ) )
|
|
{
|
|
level [[ turret.specialCleanupFunc ]]( gunner, turret );
|
|
}
|
|
}
|
|
|
|
// tracks the rotational direction of the turret
|
|
turret_track_rotatedirection( gunner )
|
|
{
|
|
self endon( "turret_cleanup" );
|
|
self endon( "death" );
|
|
gunner endon( "death" );
|
|
gunner endon( "detach" );
|
|
|
|
tag = "tag_aim";
|
|
lastAngles = self GetTagAngles( tag );
|
|
|
|
self turret_update_rotatedirection( "none" );
|
|
|
|
while ( 1 )
|
|
{
|
|
currentAngles = self GetTagAngles( tag );
|
|
|
|
// the vectordot of the old right angles and the current forward angles is going to tell us whether
|
|
// the turret is rotating left, right, or not at all
|
|
oldRight = AnglesToRight( lastAngles );
|
|
currentForward = AnglesToForward( currentAngles );
|
|
|
|
dot = VectorDot( oldRight, currentForward );
|
|
|
|
if ( dot == 0 )
|
|
{
|
|
self turret_update_rotatedirection( "none" );
|
|
}
|
|
else if ( dot > 0 )
|
|
{
|
|
self turret_update_rotatedirection( "right" );
|
|
}
|
|
else
|
|
{
|
|
self turret_update_rotatedirection( "left" );
|
|
}
|
|
|
|
lastAngles = self GetTagAngles( tag );
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
turret_update_rotatedirection( direction )
|
|
{
|
|
if ( !IsDefined( self.rotateDirection ) || self.rotateDirection != direction )
|
|
{
|
|
self.rotateDirection = direction;
|
|
|
|
//println( "spin direction change: " + self.rotateDirection );
|
|
}
|
|
}
|
|
|
|
gunner_turning_anims( turret )
|
|
{
|
|
self endon( "death" );
|
|
turret endon( "death" );
|
|
self endon( "dismount" );
|
|
turret endon( "turret_cleanup" );
|
|
|
|
blendInTime = 0.3;
|
|
blendOutTime = 0.3;
|
|
|
|
while ( 1 )
|
|
{
|
|
turret waittill( "new_fireTarget" );
|
|
wait( 0.05 );// give him a chance to start rotating to the new target so the direction updates
|
|
|
|
if ( !IsDefined( turret.fireTarget ) || self.isCustomAnimating )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
anime = undefined;
|
|
|
|
if ( !turret turret_aiming_near_target( turret.fireTarget, turret.closeEnoughAimDegrees ) )
|
|
{
|
|
if ( turret.rotateDirection == "right" )
|
|
{
|
|
anime = self.additiveTurretRotateRight;
|
|
//println( "gunner anim RIGHT" );
|
|
}
|
|
else if ( turret.rotateDirection == "left" )
|
|
{
|
|
anime = self.additiveTurretRotateLeft;
|
|
//println( "gunner anim LEFT" );
|
|
}
|
|
|
|
if ( IsDefined( anime ) )
|
|
{
|
|
// dial the parent branch up
|
|
self SetAnimLimited( self.additiveRotateRoot, 1, blendInTime, 1 );
|
|
// also tell it which leaf anim to use
|
|
// (this is inheriting its parent's blend in time so we can set the time to 0)
|
|
self SetAnimKnobLimited( anime, 1, 0, 1 );
|
|
|
|
while ( IsDefined( turret.fireTarget ) && !turret turret_aiming_near_target( turret.fireTarget, turret.closeEnoughAimDegrees ) )
|
|
{
|
|
if ( self.isCustomAnimating )
|
|
{
|
|
break;
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
|
|
//println( "gunner anim CLEAR" );
|
|
self ClearAnim( self.additiveRotateRoot, blendOutTime );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// makes a passenger into the turret gunner - call instead of UseTurret()
|
|
vehicle_passenger_2_turret( vehicle, pos, turret, animation )
|
|
{
|
|
vehicle.usedPositions[ self.vehicle_position ] = false;
|
|
//vehicle.riders = array_remove( vehicle.riders, self );
|
|
self maps\_vehicle_aianim::guy_cleanup_vehiclevars();
|
|
guy_gets_on_turret( vehicle, pos, turret, animation );
|
|
}
|
|
|
|
// makes a passenger into the turret gunner - call instead of UseTurret()
|
|
guy_goes_directly_to_turret( vehicle, pos, turret, animation )
|
|
{
|
|
guy_gets_on_turret( vehicle, pos, turret, animation );
|
|
}
|
|
|
|
guy_gets_on_turret( vehicle, pos, turret, animation )
|
|
{
|
|
self endon( "death" );
|
|
turret endon( "death" );
|
|
|
|
// forget about being a regular vehicle rider
|
|
self StopAnimScripted();
|
|
self notify( "newanim" );
|
|
|
|
self.drivingVehicle = undefined;
|
|
|
|
self.no_ai = true;
|
|
animation = %humvee_passenger_2_turret;
|
|
if ( !isdefined( animation ) )
|
|
animation = self.passenger_2_turret_anim;
|
|
|
|
// get the origin/angles of the vehicle tag where this guy is riding
|
|
animpos = maps\_vehicle_aianim::anim_pos( vehicle, pos );
|
|
org = vehicle GetTagOrigin( animpos.sittag );
|
|
angles = vehicle GetTagAngles( animpos.sittag );
|
|
|
|
turret SetDefaultDropPitch( 0 );
|
|
turret thread turret_animate( turret.passenger2turret_anime );
|
|
|
|
// animate into position
|
|
self AnimScripted( "passenger2turret", org, angles, animation );
|
|
wait( GetAnimLength( animation ) );
|
|
self StopAnimScripted();
|
|
|
|
turret turret_aim_restore();
|
|
|
|
// now start running the regular turret scripts - GDT points to minigun_hummer\stand::main()
|
|
self UseTurret( turret );
|
|
}
|
|
|
|
turret_animate( anime )
|
|
{
|
|
if ( IsDefined( self.idleAnim ) )
|
|
{
|
|
self ClearAnim( self.idleAnim, 0 );
|
|
self.idleAnim = undefined;
|
|
}
|
|
|
|
self SetFlaggedAnimKnobRestart( "minigun_turret", anime, 1, 0, 1 );
|
|
self waittillmatch( "minigun_turret", "end" );
|
|
self ClearAnim( anime, 0 );
|
|
}
|
|
|
|
turret_animfirstframe( anime )
|
|
{
|
|
self SetAnimKnobRestart( anime, 1, 0, 0 );
|
|
self.idleAnim = anime;
|
|
}
|
|
|
|
// "directs" the turret about whether it should be firing or not
|
|
// note: works with either code-controlled sentry targets or manually setting turret target
|
|
// - to make an AI aim without firing, set his .ignoreall to true
|
|
fireDirector( turret )
|
|
{
|
|
self endon( "death" );
|
|
turret endon( "death" );
|
|
self endon( "dismount" );
|
|
turret endon( "kill_fireController" );
|
|
|
|
turret thread turret_target_updater( self );
|
|
wait( 0.05 );// let the target updater kick off
|
|
|
|
self thread [[ turret.fireControllerFunc ]]( turret );
|
|
|
|
target = undefined;
|
|
|
|
while ( 1 )
|
|
{
|
|
// get a target
|
|
target = turret.fireTarget;
|
|
|
|
// wait for the right time to start shooting
|
|
while ( turret target_confirm( target ) )
|
|
{
|
|
// tried a CanSee check here too, didn't seem necessary after testing
|
|
if ( turret turret_aiming_near_target( target, turret.closeEnoughAimDegrees ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
if ( turret target_confirm( target ) && !self.ignoreall )
|
|
{
|
|
// shoot at him
|
|
turret.doFiring = true;
|
|
}
|
|
|
|
// wait for his death, or for the code/script to pick/designate a new target
|
|
while ( turret target_confirm( target ) && !self.ignoreall && !self.isCustomAnimating )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
// stop shooting
|
|
if ( turret.Dofiring || self.ignoreall )
|
|
{
|
|
turret.doFiring = false;
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
// makes sure the target that the fireDirector is thinking about is still synced with turret_target_updater
|
|
target_confirm( target )
|
|
{
|
|
if ( IsDefined( self.dontshoot ) )
|
|
{
|
|
AssertEx( self.dontshoot, ".dontshoot must be true or undefined." );
|
|
return false;
|
|
}
|
|
|
|
// maybe the turret can't see the target anymore
|
|
if ( !IsDefined( self.fireTarget ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( !turret_target_validate( target ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( target != self.fireTarget )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// make sure the script knows about the most recent turret target
|
|
// (this is necessary because code sets the turret target silently and behind the scenes)
|
|
turret_target_updater( gunner )
|
|
{
|
|
gunner endon( "death" );
|
|
self endon( "death" );
|
|
gunner endon( "dismount" );
|
|
self endon( "kill_fireController" );
|
|
|
|
|
|
// initialize this, other threads are looking for it
|
|
self.fireTarget = undefined;
|
|
|
|
target = undefined;
|
|
lastTarget = undefined;
|
|
|
|
while ( 1 )
|
|
{
|
|
target = self GetTurretTarget( false );
|
|
|
|
doUpdate = false;
|
|
|
|
// target can come back undefined if the turret loses sight of its target
|
|
if ( turret_target_validate( target ) || !IsDefined( target ) )
|
|
{
|
|
// if the old target was defined and the new target is undefined
|
|
// (e.g., the turret lost its target) we want to update
|
|
if ( !IsDefined( target ) && IsDefined( lastTarget ) )
|
|
{
|
|
doUpdate = true;
|
|
}
|
|
// or, if the new target is defined and the old one isn't, do the update
|
|
else if ( IsDefined( target ) && !IsDefined( lastTarget ) )
|
|
{
|
|
doUpdate = true;
|
|
}
|
|
// or, if the new target is defined and different from before, do the update
|
|
else if ( IsDefined( target ) && target != lastTarget )
|
|
{
|
|
doUpdate = true;
|
|
}
|
|
|
|
if ( doUpdate )
|
|
{
|
|
self.fireTarget = target;
|
|
lastTarget = target;
|
|
self notify( "new_fireTarget" );
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
turret_target_validate( target )
|
|
{
|
|
if ( !IsDefined( target ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( IsDefined( target.ignoreme ) && target.ignoreme )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// can't use IsSentient because the sentient AIs will turn into non-sentient corpses
|
|
if ( IsSubStr( target.code_classname, "actor" ) && !IsAlive( target ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
// manually designate a target for the turret
|
|
// - fireTime_min: minimum amount of time to fire once the turret has centered on the target
|
|
// - fireTime_max (optional): if set, function will pick a random time between min and max
|
|
// - fireTime_message (optional): will fire until the turret is notified the message. will ignore min and max time.
|
|
set_manual_target( target, fireTime_min, fireTime_max, fireTime_message )
|
|
{
|
|
AssertEx( IsDefined( target ), "undefined target passed to set_manual_target()." );
|
|
|
|
self endon( "turret_cleanup" );
|
|
|
|
oldMode = self GetMode();
|
|
if ( oldMode != "manual" )
|
|
{
|
|
self SetMode( "manual" );
|
|
}
|
|
|
|
if ( !IsDefined( fireTime_min ) && !IsDefined( fireTime_max ) )
|
|
{
|
|
fireTime_min = 1.5;
|
|
fireTime_max = 3;
|
|
}
|
|
|
|
self animscripts\hummer_turret\common::custom_anim_wait();
|
|
self SetTargetEntity( target );
|
|
//println( "target set" );
|
|
|
|
self waittill( "turret_on_target" );
|
|
//println( "turret on target" );
|
|
|
|
if ( IsDefined( fireTime_message ) )
|
|
{
|
|
self waittill( fireTime_message );
|
|
}
|
|
else if ( IsDefined( fireTime_max ) )
|
|
{
|
|
wait( RandomFloatRange( fireTime_min, fireTime_max ) );
|
|
}
|
|
else
|
|
{
|
|
wait( fireTime_min );
|
|
}
|
|
|
|
self custom_anim_wait();
|
|
self ClearTargetEntity( target );
|
|
|
|
if ( IsDefined( oldMode ) )
|
|
{
|
|
self SetMode( oldMode );
|
|
}
|
|
}
|
|
|
|
DoShoot( turret )
|
|
{
|
|
self notify( "doshoot_starting" );
|
|
|
|
self SetAnimLimited( self.additiveUsegunRoot, 1, .1 );
|
|
self SetAnimKnobLimited( self.additiveTurretFire, 1, .1 );
|
|
|
|
turret.turretState = "fire";
|
|
turret thread fire( self );
|
|
}
|
|
|
|
fire( gunner )
|
|
{
|
|
gunner endon( "death" );
|
|
self endon( "death" );
|
|
gunner endon( "dismount" );
|
|
self endon( "kill_fireController" );
|
|
self endon( "stopfiring" );
|
|
self endon( "custom_anim" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self ShootTurret();
|
|
wait( self.fireInterval );
|
|
}
|
|
}
|
|
|
|
DoAim( turret )
|
|
{
|
|
turret.turretState = "aim";
|
|
turret notify( "stopfiring" );
|
|
|
|
self thread DoAim_idle_think( turret );
|
|
}
|
|
|
|
DoAim_idle_think( turret )
|
|
{
|
|
self notify( "doaim_idle_think" );
|
|
self endon( "doaim_idle_think" );
|
|
|
|
self endon( "custom_anim" );
|
|
self endon( "doshoot_starting" );
|
|
self endon( "death" );
|
|
|
|
turret endon( "death" );
|
|
assertex( isdefined( turret ), "The turret is gone!" );
|
|
assertex( isalive( self ), "No, I can't die!" );
|
|
|
|
Assert( IsDefined( turret.ownervehicle ) );
|
|
vehicle = turret.ownervehicle;
|
|
assertex( isdefined( vehicle ), "There is no vehicle!" );
|
|
|
|
idle = -1;
|
|
|
|
for ( ;; )
|
|
{
|
|
if ( vehicle Vehicle_GetSpeed() < 1 && idle )
|
|
{
|
|
self SetAnimLimited( self.additiveUsegunRoot, 1, 0.1 );
|
|
self SetAnimKnobLimited( self.additiveTurretIdle, 1, 0.1 );
|
|
idle = 0;
|
|
}
|
|
else
|
|
if ( vehicle Vehicle_GetSpeed() >= 1 && !idle )
|
|
{
|
|
self SetAnimLimited( self.additiveUsegunRoot, 1, 0.1 );
|
|
self SetAnimKnobLimited( self.additiveTurretDriveIdle, 1, 0.1 );
|
|
idle = 1;
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
/*
|
|
=============
|
|
///ScriptDocBegin
|
|
"Name: turret_gunner_custom_anim( <turret> , <animStr> , <centerTurretFirst> )"
|
|
"Summary: Call on a turret gunner do one of his predefined custom animations. All the custom anims for a turret gunner get set up in the array self.turretSpecialAnims, in the main script for the turret type - for example, animscripts\hummer_turret\minigun_stand.gsc."
|
|
"Module: Entity"
|
|
"CallOn: An AI using a vehicle turret. (Note: this only works for 360 degree vehicle turrets, for example those on the hummer or the suburban.)"
|
|
"MandatoryArg: <turret> The turret the AI is using."
|
|
"MandatoryArg: <animStr> The name of the animation, as a string. used as the lookup index for self.turretSpecialAnims."
|
|
"OptionalArg: <centerTurretFirst>: Whether the turret should re-center to its "home" location before the animation plays. Defaults to false."
|
|
"Example: gunner animscripts\hummer_turret\common::turret_gunner_custom_anim( turret, anime, true );"
|
|
"SPMP: singleplayer"
|
|
///ScriptDocEnd
|
|
=============
|
|
*/
|
|
turret_gunner_custom_anim( turret, animStr, centerTurretFirst )
|
|
{
|
|
// this section of endons should be identical to the ones in the function below
|
|
self endon( "death" );
|
|
turret endon( "death" );
|
|
self endon( "dismount" );
|
|
self endon( "jumping_out" );
|
|
|
|
anime = self.turretSpecialAnims[ animStr ];
|
|
Assert( IsDefined( anime ) );
|
|
|
|
self custom_anim_wait();
|
|
|
|
disabledReload = turret reload_disable_safe();
|
|
|
|
self DoCustomAnim( turret, anime, centerTurretFirst );
|
|
|
|
if ( disabledReload )
|
|
{
|
|
turret reload_enable();
|
|
}
|
|
}
|
|
|
|
reload_disable_safe()
|
|
{
|
|
disabledReload = false;
|
|
if ( !IsDefined( self.disableReload ) || !self.disableReload )
|
|
{
|
|
disabledReload = true;
|
|
self.disableReload = true;
|
|
}
|
|
|
|
return disabledReload;
|
|
}
|
|
|
|
reload_enable()
|
|
{
|
|
self.disableReload = false;
|
|
}
|
|
|
|
DoReload( turret )
|
|
{
|
|
if ( IsDefined( turret.disableReload ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// this section of endons should be identical to the ones in the function above
|
|
self endon( "death" );
|
|
turret endon( "death" );
|
|
self endon( "dismount" );
|
|
self endon( "jumping_out" );
|
|
|
|
self thread custom_battlechatter( "inform_reloading" );
|
|
|
|
self DoCustomAnim( turret, self.turretReloadAnim, true );
|
|
}
|
|
|
|
DoCustomAnim( turret, anime, centerTurretFirst )
|
|
{
|
|
// NOTE! To stop behavior, don't notify do_custom_anim, do self notify( "special_anim", "end" );
|
|
self notify( "do_custom_anim" );
|
|
self endon( "do_custom_anim" );
|
|
|
|
/*
|
|
// these endons are dupes
|
|
|
|
self endon( "death" );
|
|
turret endon( "death" );
|
|
self endon( "dismount" );
|
|
self endon( "jumping_out" );
|
|
*/
|
|
|
|
Assert( IsDefined( anime ) );
|
|
|
|
self.isCustomAnimating = true;
|
|
self.customAnim = anime;
|
|
turret.turretState = "customanim";
|
|
turret TurretFireDisable();
|
|
|
|
if ( turret GetBarrelSpinRate() > 0 )
|
|
{
|
|
turret StopBarrelSpin();
|
|
}
|
|
|
|
// turn off threads that can mess up the animtree
|
|
turret notify( "kill_fireController" );
|
|
self notify( "custom_anim" );
|
|
|
|
if ( IsDefined( centerTurretFirst ) && centerTurretFirst )
|
|
{
|
|
turret turret_aim_straight();
|
|
}
|
|
|
|
// "knob" dials down all the siblings of the specialanimsroot, so we don't need to manually
|
|
// dial down self.primaryTurretAnim or self.additiveUsegunRoot right here
|
|
self SetAnimKnobLimitedRestart( self.turretSpecialAnimsRoot, 1, 0.2 );
|
|
self SetFlaggedAnimKnobRestart( "special_anim", anime, 1, 0, 1 );
|
|
|
|
for ( ;; )
|
|
{
|
|
// broke into a loop so I can debug the notetracks
|
|
self waittill( "special_anim", notetrack );
|
|
if ( notetrack == "end" )
|
|
break;
|
|
}
|
|
|
|
// turn the ROOT down, not the anim under the root
|
|
self ClearAnim( self.turretSpecialAnimsRoot, 0.2 );
|
|
// dial these up individually - can't use "knob" cause we want them both to be active
|
|
self SetAnimLimited( self.primaryTurretAnim, 1 ); // 0.2 blend time is the default
|
|
self SetAnimLimited( self.additiveUsegunRoot, 1 );
|
|
|
|
if ( IsDefined( centerTurretFirst ) && centerTurretFirst )
|
|
{
|
|
turret turret_aim_restore();
|
|
}
|
|
|
|
self.customAnim = undefined;
|
|
self.isCustomAnimating = false;
|
|
turret TurretFireEnable();
|
|
|
|
// turn those animtree-messing-up threads back on
|
|
self thread fireDirector( turret );
|
|
}
|
|
|
|
// use this if you're manually setting the turret target entity - call this first (non-threaded)
|
|
// to make sure that the DoCustomAnim function doesn't clear out your custom target
|
|
custom_anim_wait()
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( !IsDefined( self.isCustomAnimating ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
while ( self.isCustomAnimating )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
turret_aim_straight( straightAngles )
|
|
{
|
|
if ( !IsDefined( straightAngles ) )
|
|
{
|
|
currentAngles = self GetTagAngles( "tag_flash" );
|
|
straightAngles = ( 0, currentAngles[ 1 ], currentAngles[ 2 ] );// just keep the yaw
|
|
}
|
|
|
|
self.oldMode = self GetMode();
|
|
self SetMode( "manual" );
|
|
|
|
// use a temp target to make the gun point straight forward
|
|
forward = AnglesToForward( straightAngles );
|
|
scalevec = vector_multiply( forward, 96 );
|
|
targetOrigin = self GetTagOrigin( "tag_aim" ) + scalevec;
|
|
self.tempTarget = Spawn( "script_origin", targetOrigin );
|
|
self.tempTarget.ignoreme = true;
|
|
|
|
self.tempTarget LinkTo( self.ownerVehicle );// if the vehicle is moving we have to link the target to the gun so the gun doesn't rotate around as the vehicle angles change
|
|
|
|
self ClearTargetEntity();
|
|
self SetTargetEntity( self.tempTarget );
|
|
|
|
self waittill( "turret_on_target" );
|
|
}
|
|
|
|
turret_aim_restore()
|
|
{
|
|
self ClearTargetEntity();
|
|
|
|
if ( IsDefined( self.tempTarget ) )
|
|
{
|
|
self.tempTarget Unlink();
|
|
self.tempTarget Delete();
|
|
}
|
|
|
|
if ( IsDefined( self.oldMode ) )
|
|
{
|
|
self SetMode( self.oldMode );
|
|
self.oldMode = undefined;
|
|
}
|
|
}
|
|
|
|
// - closeEnoughAngle: we want to be pointing within [+/- of this value in degrees]
|
|
// to the target to return true
|
|
turret_aiming_near_target( target, closeEnoughAngle )
|
|
{
|
|
delta = self turret_get_angle_to_target( target );
|
|
|
|
if ( delta <= closeEnoughAngle )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
turret_get_angle_to_target( target )
|
|
{
|
|
// get the yaw angle of the vector between the target origin and the turret origin
|
|
yawAngleToTarget = VectorToYaw( target.origin - self.origin );
|
|
|
|
// normalize the difference between the yaw angle and the angles of the end of the barrel:
|
|
// this tells us how far away we are from being perfectly centered on the target
|
|
turretYawAngle = self GetTagAngles( "tag_flash" )[ 1 ];
|
|
delta = animscripts\utility::AbsAngleClamp180( turretYawAngle - yawAngleToTarget );
|
|
|
|
return delta;
|
|
}
|
|
|
|
lerp_out_drop_pitch( time )
|
|
{
|
|
blend = self create_blend( ::blend_dropPitch, 20, 0 );
|
|
blend.time = time;
|
|
}
|
|
|
|
blend_dropPitch( progress, start, end )
|
|
{
|
|
val = start * ( 1 - progress ) + end * progress;
|
|
self SetDefaultDropPitch( val );
|
|
}
|
|
|