IW4-Dump-Files/animscripts/move.gsc

1159 lines
28 KiB
Plaintext

#include animscripts\SetPoseMovement;
#include animscripts\combat_utility;
#include animscripts\utility;
#include animscripts\shared;
#include animscripts\melee;
#include common_scripts\utility;
#include maps\_utility;
#using_animtree( "generic_human" );
main()
{
self endon( "killanimscript" );
[[ self.exception[ "move" ] ]]();
moveInit();
getUpIfProne();
animscripts\utility::initialize( "move" );
wasInCover = self wasPreviouslyInCover();
if ( wasInCover && isdefined( self.shuffleMove ) )
{
moveCoverToCover();
moveCoverToCoverFinish();
}
else if ( IsDefined( self.battleChatter ) && self.battleChatter )
{
self moveStartBattleChatter( wasInCover );
self animscripts\battlechatter::playBattleChatter();
}
self thread stairsCheck();
self thread pathChangeCheck();
self thread animDodgeObstacle();
self animscripts\cover_arrival::startMoveTransition();
self.doingReacquireStep = undefined;
self.ignorePathChange = undefined;
self thread startThreadsToRunWhileMoving();
self thread animscripts\cover_arrival::setupApproachNode( true );
self.shoot_while_moving_thread = undefined;
self.aim_while_moving_thread = undefined;
/#
assert( !isdefined( self.trackLoopThread ) );
self.trackLoopThread = undefined;
#/
self.runNGun = undefined;
MoveMainLoop( true );
}
// called by code on ending this script
end_script()
{
if ( isdefined( self.oldGrenadeWeapon ) )
{
self.grenadeWeapon = self.oldGrenadeWeapon;
self.oldGrenadeWeapon = undefined;
}
self.teamFlashbangImmunity = undefined;
self.minInDoorTime = undefined;
self.ignorePathChange = undefined;
self.shuffleMove = undefined;
self.shuffleNode = undefined;
self.runNGun = undefined;
self.reactingToBullet = undefined;
self.requestReactToBullet = undefined;
self.currentDodgeAnim = undefined;
self.moveLoopOverrideFunc = undefined;
}
moveInit()
{
self.reactingToBullet = undefined;
self.requestReactToBullet = undefined;
self.update_move_anim_type = undefined;
self.update_move_front_bias = undefined;
self.runNGunWeight = 0;
self.arrivalStartDist = undefined;
}
getUpIfProne()
{
if ( self.a.pose == "prone" )
{
newPose = self animscripts\utility::choosePose( "stand" );
if ( newPose != "prone" )
{
self orientMode( "face current" );
self animMode( "zonly_physics", false );
rate = 1;
if ( isdefined( self.grenade ) )
rate = 2;
self animscripts\cover_prone::proneTo( newPose, rate );
self animMode( "none", false );
self orientMode( "face default" );
}
}
}
wasPreviouslyInCover()
{
switch( self.prevScript )
{
case "cover_crouch":
case "cover_left":
case "cover_prone":
case "cover_right":
case "cover_stand":
case "concealment_crouch":
case "concealment_prone":
case "concealment_stand":
case "cover_wide_left":
case "cover_wide_right":
case "hide":
case "turret":
return true;
}
return false;
}
moveStartBattleChatter( wasInCover )
{
if ( self.moveMode == "run" )
{
// SRS 10/30/08: removed a bunch of unnecessary logic here
self animscripts\battleChatter_ai::evaluateMoveEvent( wasInCover );
}
}
MoveMainLoop( doWalkCheck )
{
MoveMainLoopInternal( doWalkCheck );
self notify( "abort_reload" ); // in case a reload was going and MoveMainLoopInternal hit an endon
}
ChangeMoveMode( moveMode )
{
if ( moveMode != self.prevMoveMode )
{
if ( isdefined( self.customMoveAnimSet ) && isdefined( self.customMoveAnimSet[ moveMode ] ) )
{
self.a.moveAnimSet = self.customMoveAnimSet[ moveMode ];
}
else
{
self.a.moveAnimSet = anim.animsets.move[ moveMode ];
if ( ( self.combatMode == "ambush" || self.combatMode == "ambush_nodes_only" ) &&
( isdefined( self.pathGoalPos ) && distanceSquared( self.origin, self.pathGoalPos ) > squared( 100 ) ) )
{
self.sideStepRate = 1;
animscripts\animset::set_ambush_sidestep_anims();
}
else
{
self.sideStepRate = 1.35;
}
}
self.prevMoveMode = moveMode;
}
}
MoveMainLoopInternal( doWalkCheck )
{
self endon( "killanimscript" );
self endon( "move_interrupt" );
prevLoopTime = self getAnimTime( %walk_and_run_loops );
self.a.runLoopCount = randomint( 10000 );// integer that is incremented each time we complete a run loop
self.prevMoveMode = "none";
self.moveLoopCleanupFunc = undefined;
// if initial destination is closer than 64 walk to it.
for ( ;; )
{
loopTime = self getAnimTime( %walk_and_run_loops );
if ( loopTime < prevLoopTime )
self.a.runLoopCount++ ;
prevLoopTime = loopTime;
ChangeMoveMode( self.moveMode );
MoveMainLoopProcess( self.moveMode );
if ( isDefined( self.moveLoopCleanupFunc ) )
{
self [[self.moveLoopCleanupFunc]]();
self.moveLoopCleanupFunc = undefined;
}
self notify( "abort_reload" ); // in case a reload was going and MoveMainLoopProcess hit an endon
}
}
MoveMainLoopProcess( moveMode )
{
self endon( "move_loop_restart" );
//prof_begin("MoveMainLoop");
self animscripts\face::SetIdleFaceDelayed( anim.alertface );
if ( isdefined( self.moveLoopOverrideFunc ) )
{
self [[ self.moveLoopOverrideFunc ]]();
}
else if ( self shouldCQB() )
{
self animscripts\cqb::MoveCQB();
}
else
{
if ( moveMode == "run" )
{
self animscripts\run::MoveRun();
}
else
{
assert( moveMode == "walk" );
self animscripts\walk::MoveWalk();
}
}
self.requestReactToBullet = undefined;
//prof_end("MoveMainLoop");
}
MayShootWhileMoving()
{
if ( self.weapon == "none" )
return false;
weapclass = weaponClass( self.weapon );
if ( !usingRifleLikeWeapon() )
return false;
if ( self isSniper() )
{
if ( !( self isCQBWalking() ) && self.faceMotion )
return false;
}
if ( isdefined( self.dontShootWhileMoving ) )
{
assert( self.dontShootWhileMoving );// true or undefined
return false;
}
return true;
}
shootWhileMoving()
{
self endon( "killanimscript" );
// it's possible for this to be called by CQB while it's already running from run.gsc,
// even though run.gsc will kill it on the next frame. We can't let it run twice at once.
self notify( "doing_shootWhileMoving" );
self endon( "doing_shootWhileMoving" );
self.a.array[ "fire" ] = %exposed_shoot_auto_v3;
if ( isdefined( self.weapon ) && weapon_pump_action_shotgun() )
self.a.array[ "single" ] = array( %shotgun_stand_fire_1A, %shotgun_stand_fire_1B );
else
self.a.array[ "single" ] = array( %exposed_shoot_semi1 );
self.a.array[ "burst2" ] = %exposed_shoot_burst3;
self.a.array[ "burst3" ] = %exposed_shoot_burst3;
self.a.array[ "burst4" ] = %exposed_shoot_burst4;
self.a.array[ "burst5" ] = %exposed_shoot_burst5;
self.a.array[ "burst6" ] = %exposed_shoot_burst6;
self.a.array[ "semi2" ] = %exposed_shoot_semi2;
self.a.array[ "semi3" ] = %exposed_shoot_semi3;
self.a.array[ "semi4" ] = %exposed_shoot_semi4;
self.a.array[ "semi5" ] = %exposed_shoot_semi5;
while ( 1 )
{
if ( !self.bulletsInClip )
{
if ( self isCQBWalkingOrFacingEnemy() )
{
self.ammoCheatTime = 0;
cheatAmmoIfNecessary();
}
if ( !self.bulletsInClip )
{
wait 0.5;
continue;
}
}
self shootUntilShootBehaviorChange();
// can't clear %exposed_modern because there are transition animations within it that we might play when going to prone
self clearAnim( %exposed_aiming, 0.2 );
}
}
startThreadsToRunWhileMoving()
{
self endon( "killanimscript" );
// wait a frame so MoveMainLoop can start. Otherwise one of the following threads could unsuccesfully try to interrupt movement before it starts
wait 0.05;
self thread bulletWhizbyCheck_whileMoving();
self thread meleeAttackCheck_whileMoving();
self thread animscripts\door::inDoorCqbToggleCheck();
self thread animscripts\door::doorEnterExitCheck();
}
stairsCheck()
{
self endon( "killanimscript" );
self.prevStairsState = self.stairsState;
while ( 1 )
{
wait .05;
if ( self.prevStairsState != self.stairsState )
{
// don't interrupt path change animation if getting off stairs to flat ground
if ( !isdefined( self.ignorePathChange ) || self.stairsState != "none" )
self notify( "move_loop_restart" );
}
self.prevStairsState = self.stairsState;
}
}
restartMoveLoop( skipMoveTransition )
{
self endon( "killanimscript" );
if ( !skipMoveTransition )
animscripts\cover_arrival::startMoveTransition();
self.ignorePathChange = undefined;
self clearanim( %root, 0.1 );
self OrientMode( "face default" );
self animMode( "none", false );
self.requestArrivalNotify = true;
MoveMainLoop( !skipMoveTransition );
}
pathChangeCheck()
{
self endon( "killanimscript" );
self endon( "move_interrupt" );
self.ignorePathChange = true; // this will be turned on / off in other threads at appropriate times
while ( 1 )
{
// no other thread should end on "path_changed"
self waittill( "path_changed", doingReacquire, newDir );
// no need to check for doingReacquire since faceMotion should be a good check
assert( !isdefined( self.ignorePathChange ) || self.ignorePathChange ); // should be true or undefined
if ( isdefined( self.ignorePathChange ) || isdefined( self.noTurnAnims ) )
continue;
if ( !self.faceMotion || abs( self getMotionAngle() ) > 15 )
continue;
if ( self.a.movement != "run" && self.a.movement != "walk" )
continue;
if ( self.a.pose != "stand" )
continue;
self notify( "stop_move_anim_update" );
self.update_move_anim_type = undefined;
angleDiff = AngleClamp180( self.angles[ 1 ] - vectortoyaw( newDir ) );
turnAnim = pathChange_getTurnAnim( angleDiff );
if ( isdefined( turnAnim ) )
{
self.turnAnim = turnAnim;
self.turnTime = getTime();
self.moveLoopOverrideFunc = ::pathChange_doTurnAnim;
self notify( "move_loop_restart" );
self animscripts\run::endFaceEnemyAimTracking();
}
}
}
pathChange_getTurnAnim( angleDiff )
{
if ( isdefined( self.pathTurnAnimOverrideFunc ) )
return [[ self.pathTurnAnimOverrideFunc ]]( angleDiff );
turnAnim = undefined;
secondTurnAnim = undefined;
if ( self shouldCQB() || self.movemode == "walk" )
animArray = anim.cqbTurnAnims;
else
animArray = anim.runTurnAnims;
if ( angleDiff < -30 )
{
if ( angleDiff > -60 ) // bias for 45 turns
{
turnAnim = animArray[ "L45" ];
// awkward pivot turn after doing animation
//if ( angleDiff < -45 )
// secondTurnAnim = animArray[ "L90" ];
}
else if ( angleDiff > -112.5 )
{
turnAnim = animArray[ "L90" ];
if ( angleDiff > -90 )
secondTurnAnim = animArray[ "L45" ];
else
secondTurnAnim = animArray[ "L135" ];
}
else if ( angleDiff > -157.5 )
{
turnAnim = animArray[ "L135" ];
if ( angleDiff > -135 )
secondTurnAnim = animArray[ "L90" ];
else
secondTurnAnim = animArray[ "180" ];
}
else
{
turnAnim = animArray[ "180" ];
secondTurnAnim = animArray[ "L135" ];
}
}
else if ( angleDiff > 30 )
{
if ( angleDiff < 60 )
{
turnAnim = animArray[ "R45" ];
// awkward pivot turn after doing animation
//if ( angleDiff > 45 )
// secondTurnAnim = animArray[ "R90" ];
}
else if ( angleDiff < 112.5 )
{
turnAnim = animArray[ "R90" ];
if ( angleDiff < 90 )
secondTurnAnim = animArray[ "R45" ];
else
secondTurnAnim = animArray[ "R135" ];
}
else if ( angleDiff < 157.5 )
{
turnAnim = animArray[ "R135" ];
if ( angleDiff < 135 )
secondTurnAnim = animArray[ "R90" ];
else
secondTurnAnim = animArray[ "180" ];
}
else
{
turnAnim = animArray[ "180" ];
secondTurnAnim = animArray[ "R135" ];
}
}
if ( isdefined( turnAnim ) )
{
if ( pathChange_canDoTurnAnim( turnAnim ) )
return turnAnim;
}
if ( isdefined( secondTurnAnim ) )
{
if ( pathChange_canDoTurnAnim( secondTurnAnim ) )
return secondTurnAnim;
}
return undefined;
}
pathChange_canDoTurnAnim( turnAnim )
{
if ( !isdefined( self.pathgoalpos ) )
return false;
codeMoveTimes = getNotetrackTimes( turnAnim, "code_move" );
assert( codeMoveTimes.size == 1 );
codeMoveTime = codeMoveTimes[ 0 ];
assert( codeMoveTime <= 1 );
moveDelta = getMoveDelta( turnAnim, 0, codeMoveTime );
codeMovePoint = self localToWorldCoords( moveDelta );
/#
animscripts\utility::drawDebugLine( self.origin, codeMovePoint, ( 1, 1, 0 ), 20 );
animscripts\utility::drawDebugLine( self.origin, self.pathgoalpos, ( 0, 1, 0 ), 20 );
#/
//if ( distanceSquared( self.origin, codeMovePoint ) > distanceSquared( self.origin, self.pathgoalpos ) )
if ( isdefined( self.arrivalStartDist ) && ( squared( self.arrivalStartDist ) > distanceSquared( self.pathgoalpos, codeMovePoint ) ) )
return false;
moveDelta = getMoveDelta( turnAnim, 0, 1 );
endPoint = self localToWorldCoords( moveDelta );
endPoint = codeMovePoint + vectornormalize( endPoint - codeMovePoint ) * 20;
/# animscripts\utility::drawDebugLine( codeMovePoint, endPoint, ( 1, 1, 0 ), 20 ); #/
return self mayMoveFromPointToPoint( codeMovePoint, endPoint, true, true );
}
pathChange_doTurnAnim()
{
self endon( "killanimscript" );
self.moveLoopOverrideFunc = undefined;
turnAnim = self.turnAnim;
if ( gettime() > self.turnTime + 50 )
return; // too late
self animMode( "zonly_physics", false );
self clearanim( %body, 0.1 );
self.moveLoopCleanupFunc = ::pathChange_cleanupTurnAnim;
self.ignorePathChange = true;
blendTime = 0.05;
if ( isdefined( self.pathTurnAnimBlendTime ) )
blendTime = isdefined( self.pathTurnAnimBlendTime );
self setflaggedanimrestart( "turnAnim", turnAnim, 1, blendTime, self.movePlaybackRate );
self OrientMode( "face current" );
assert( animHasNotetrack( turnAnim, "code_move" ) );
self animscripts\shared::DoNoteTracks( "turnAnim" ); // until "code_move"
self.ignorePathChange = undefined;
self OrientMode( "face motion" ); // want to face motion, don't do l / r / b anims
self animmode( "none", false );
//assert( animHasNotetrack( turnAnim, "finish" ) );
self animscripts\shared::DoNoteTracks( "turnAnim" );
}
pathChange_doMoveTransition()
{
self.moveLoopOverrideFunc = undefined;
if ( gettime() > self.turnTime + 50 )
return; // too late
self.moveLoopCleanupFunc = ::pathChange_cleanupTurnAnim;
animscripts\cover_arrival::startMoveTransition();
}
pathChange_cleanupTurnAnim()
{
self.ignorePathChange = undefined;
self OrientMode( "face default" );
self clearanim( %root, 0.1 );
self animMode( "none", false );
}
dodgeMoveLoopOverride()
{
self pushplayer( true );
self animMode( "zonly_physics", false );
self clearanim( %body, 0.2 );
self setflaggedanimrestart( "dodgeAnim", self.currentDodgeAnim, 1, 0.2, 1 );
self animscripts\shared::DoNoteTracks( "dodgeAnim" );
self animmode( "none", false );
self orientMode( "face default" );
if ( animHasNotetrack( self.currentDodgeAnim, "code_move" ) )
self animscripts\shared::DoNoteTracks( "dodgeAnim" ); // return on code_move
self clearanim( %civilian_dodge, 0.2 );
self pushplayer( false );
self.currentDodgeAnim = undefined;
self.moveLoopOverrideFunc = undefined;
return true;
}
tryDodgeWithAnim( dodgeAnim, dodgeAnimDelta )
{
rightDir = ( self.lookAheadDir[1], -1 * self.lookAheadDir[0], 0 );
forward = self.lookAheadDir * dodgeAnimDelta[0];
right = rightDir * dodgeAnimDelta[1];
dodgePos = self.origin + forward - right;
self pushPlayer( true );
if ( self mayMoveToPoint( dodgePos ) )
{
self.currentDodgeAnim = dodgeAnim;
self.moveLoopOverrideFunc = ::dodgeMoveLoopOverride;
self notify( "move_loop_restart" );
/#
if ( getdvar( "scr_debugdodge" ) == "1" )
thread debugline( self.origin, dodgePos, ( 0, 1, 0 ), 3 );
#/
return true;
}
/#
if ( getdvar( "scr_debugdodge" ) == "1" )
thread debugline( self.origin, dodgePos, ( 0.5, 0.5, 0 ), 3 );
#/
self pushPlayer( false );
return false;
}
animDodgeObstacle()
{
if ( !isdefined( self.dodgeLeftAnim ) || !isdefined( self.dodgeRightAnim ) )
return;
self endon( "killanimscript" );
self endon( "move_interrupt" );
while ( 1 )
{
// no other thread should end on "path_changed"
self waittill( "path_need_dodge", dodgeEnt, dodgeEntPos );
if ( self animscripts\utility::IsInCombat() )
{
self.noDodgeMove = false;
return;
}
if ( !isSentient( dodgeEnt ) )
continue;
/#
if ( getdvar( "scr_debugdodge" ) == "1" )
{
thread debugline( dodgeEnt.origin + (0, 0, 10), dodgeEntPos, ( 1, 1, 0 ), 3 );
thread debugline( self.origin, dodgeEntPos, ( 1, 0, 0 ), 3 );
}
#/
dirToDodgeEnt = vectorNormalize( dodgeEntPos - self.origin );
if ( ( self.lookAheadDir[0] * dirToDodgeEnt[1] ) - ( dirToDodgeEnt[0] * self.lookAheadDir[1] ) > 0 )
{
// right first
if ( !tryDodgeWithAnim( self.dodgeRightAnim, self.dodgeRightAnimOffset ) )
tryDodgeWithAnim( self.dodgeLeftAnim, self.dodgeLeftAnimOffset );
}
else
{
// left first
if ( !tryDodgeWithAnim( self.dodgeLeftAnim, self.dodgeLeftAnimOffset ) )
tryDodgeWithAnim( self.dodgeRightAnim, self.dodgeRightAnimOffset );
}
if ( isdefined( self.currentDodgeAnim ) )
wait getanimlength( self.currentDodgeAnim );
else
wait 0.1;
}
}
setDodgeAnims( leftAnim, rightAnim )
{
self.noDodgeMove = true; // don't let code path around obstacle to dodge
//self pushplayer( true );
self.dodgeLeftAnim = leftAnim;
self.dodgeRightAnim = rightAnim;
time = 1;
if ( animHasNoteTrack( leftAnim, "code_move" ) )
time = getNotetrackTimes( leftAnim, "code_move" )[0];
self.dodgeLeftAnimOffset = getMoveDelta( leftAnim, 0, time );
time = 1;
if ( animHasNoteTrack( rightAnim, "code_move" ) )
time = getNotetrackTimes( rightAnim, "code_move" )[0];
self.dodgeRightAnimOffset = getMoveDelta( rightAnim, 0, time );
self.interval = 80; // good value for civilian dodge animations
}
clearDodgeAnims()
{
self.noDodgeMove = false;
self.dodgeLeftAnim = undefined;
self.dodgeRightAnim = undefined;
self.dodgeLeftAnimOffset = undefined;
self.dodgeRightAnimOffset = undefined;
}
meleeAttackCheck_whileMoving()
{
self endon( "killanimscript" );
while ( 1 )
{
// Try to melee our enemy if it's another AI
if ( isDefined( self.enemy ) && ( isAI( self.enemy ) || isdefined( self.meleePlayerWhileMoving ) ) )
{
if ( abs( self GetMotionAngle() ) <= 135 ) // only when moving forward or sideways
animscripts\melee::Melee_TryExecuting();
}
wait 0.1;
}
}
bulletWhizbyCheck_whileMoving()
{
self endon( "killanimscript" );
if ( isdefined( self.disableBulletWhizbyReaction ) )
return;
while ( 1 )
{
self waittill( "bulletwhizby", shooter );
if ( self.moveMode != "run" || !self.faceMotion || self.a.pose != "stand" || isdefined( self.reactingToBullet ) )
continue;
if ( self.stairsState != "none" )
continue;
if ( !isdefined( self.enemy ) && !self.ignoreAll && isDefined( shooter.team ) && isEnemyTeam( self.team, shooter.team ) )
{
self.whizbyEnemy = shooter;
self animcustom( animscripts\reactions::bulletWhizbyReaction ); // this will end move script
continue;
}
if ( self.lookaheadHitsStairs || self.lookaheadDist < 100 )
continue;
if ( isdefined( self.pathGoalPos ) && distanceSquared( self.origin, self.pathGoalPos ) < 10000 )
{
wait 0.2;
continue;
}
self.requestReactToBullet = gettime();
self notify( "move_loop_restart" );
self animscripts\run::endFaceEnemyAimTracking();
}
}
get_shuffle_to_corner_start_anim( shuffleLeft, startNode )
{
if ( startNode.type == "Cover Left" )
{
assert( !shuffleLeft );
return %CornerCrL_alert_2_shuffle;
}
else if ( startNode.type == "Cover Right" )
{
assert( shuffleLeft );
return %CornerCrR_alert_2_shuffle;
}
else
{
if ( shuffleLeft )
return %covercrouch_hide_2_shuffleL;
else
return %covercrouch_hide_2_shuffleR;
}
}
setup_shuffle_anim_array( shuffleLeft, startNode, endNode )
{
anim_array = [];
assert( isdefined( startNode ) );
assert( isdefined( endNode ) );
if ( endNode.type == "Cover Left" )
{
assert( shuffleLeft );
anim_array[ "shuffle_start" ] = get_shuffle_to_corner_start_anim( shuffleLeft, startNode );
anim_array[ "shuffle" ] = %covercrouch_shuffleL;
anim_array[ "shuffle_end" ] = %CornerCrL_shuffle_2_alert;
}
else if ( endNode.type == "Cover Right" )
{
assert( !shuffleLeft );
anim_array[ "shuffle_start" ] = get_shuffle_to_corner_start_anim( shuffleLeft, startNode );
anim_array[ "shuffle" ] = %covercrouch_shuffleR;
anim_array[ "shuffle_end" ] = %CornerCrR_shuffle_2_alert;
}
else if ( endNode.type == "Cover Stand" && startNode.type == endNode.type )
{
if ( shuffleLeft )
{
anim_array[ "shuffle_start" ] = %coverstand_hide_2_shuffleL;
anim_array[ "shuffle" ] = %coverstand_shuffleL;
anim_array[ "shuffle_end" ] = %coverstand_shuffleL_2_hide;
}
else
{
anim_array[ "shuffle_start" ] = %coverstand_hide_2_shuffleR;
anim_array[ "shuffle" ] = %coverstand_shuffleR;
anim_array[ "shuffle_end" ] = %coverstand_shuffleR_2_hide;
}
}
else
{
//assert( endNode.type == "Cover Crouch" || endNode.type == "Cover Crouch Window" );
if ( shuffleLeft )
{
anim_array[ "shuffle_start" ] = get_shuffle_to_corner_start_anim( shuffleLeft, startNode );
anim_array[ "shuffle" ] = %covercrouch_shuffleL;
if ( endNode.type == "Cover Stand" )
anim_array[ "shuffle_end" ] = %coverstand_shuffleL_2_hide;
else
anim_array[ "shuffle_end" ] = %covercrouch_shuffleL_2_hide;
}
else
{
anim_array[ "shuffle_start" ] = get_shuffle_to_corner_start_anim( shuffleLeft, startNode );
anim_array[ "shuffle" ] = %covercrouch_shuffleR;
if ( endNode.type == "Cover Stand" )
anim_array[ "shuffle_end" ] = %coverstand_shuffleR_2_hide;
else
anim_array[ "shuffle_end" ] = %covercrouch_shuffleR_2_hide;
}
}
self.a.array = anim_array;
}
moveCoverToCover_checkStartPose( startNode, endNode )
{
if ( self.a.pose == "stand" && ( endNode.type != "Cover Stand" || startNode.type != "Cover Stand" ) )
{
self.a.pose = "crouch";
return false;
}
return true;
}
moveCoverToCover_checkEndPose( endNode )
{
if ( self.a.pose == "crouch" && endNode.type == "Cover Stand" )
{
self.a.pose = "stand";
return false;
}
return true;
}
serverFPS = 20;
serverSPF = 0.05;
moveCoverToCover()
{
self endon( "killanimscript" );
self endon( "goal_changed" );
shuffleNode = self.shuffleNode;
self.shuffleMove = undefined;
self.shuffleNode = undefined;
self.shuffleMoveInterrupted = true;
if ( !isdefined( self.prevNode ) )
return;
if ( !isdefined( self.node ) || !isdefined( shuffleNode ) || self.node != shuffleNode )
return;
shuffleNodeType = self.prevNode;
node = self.node;
moveDir = node.origin - self.origin;
if ( lengthSquared( moveDir ) < 1 )
return;
moveDir = vectornormalize( moveDir );
forward = anglestoforward( node.angles );
shuffleLeft = ( ( forward[ 0 ] * moveDir[ 1 ] ) - ( forward[ 1 ] * moveDir[ 0 ] ) ) > 0;
if ( moveDoorSideToSide( shuffleLeft, shuffleNodeType, node ) )
return;
if ( moveCoverToCover_checkStartPose( shuffleNodeType, node ) )
blendTime = 0.1;
else
blendTime = 0.4;
setup_shuffle_anim_array( shuffleLeft, shuffleNodeType, node );
self animMode( "zonly_physics", false );
self clearanim( %body, blendTime );
startAnim = animarray( "shuffle_start" );
shuffleAnim = animarray( "shuffle" );
endAnim = animarray( "shuffle_end" );
//assertEx( animhasnotetrack( startAnim, "finish" ), "animation doesn't have finish notetrack " + startAnim );
if ( animhasnotetrack( startAnim, "finish" ) )
startEndTime = getNotetrackTimes( startAnim, "finish" )[ 0 ];
else
startEndTime = 1;
startDist = length( getMoveDelta( startAnim, 0, startEndTime ) );
shuffleDist = length( getMoveDelta( shuffleAnim, 0, 1 ) );
endDist = length( getMoveDelta( endAnim, 0, 1 ) );
remainingDist = distance( self.origin, node.origin );
if ( remainingDist > startDist )
{
self OrientMode( "face angle", getNodeForwardYaw( shuffleNodeType ) );
self setflaggedanimrestart( "shuffle_start", startAnim, 1, blendTime );
self animscripts\shared::DoNoteTracks( "shuffle_start" );
self clearAnim( startAnim, 0.2 );
remainingDist -= startDist;
blendTime = 0.2; // reset blend for looping move
}
else
{
self OrientMode( "face angle", node.angles[1] );
}
playEnd = false;
if ( remainingDist > endDist )
{
playEnd = true;
remainingDist -= endDist;
}
loopTime = getAnimLength( shuffleAnim );
playTime = loopTime * ( remainingDist / shuffleDist ) * 0.9;
playTime = floor( playTime * serverFPS ) * serverSPF;
self setflaggedanim( "shuffle", shuffleAnim, 1, blendTime );
self animscripts\shared::DoNoteTracksForTime( playTime, "shuffle" );
// account for loopTime not being exact since loop animation delta isn't uniform over time
for ( i = 0; i < 2; i++ )
{
remainingDist = distance( self.origin, node.origin );
if ( playEnd )
remainingDist -= endDist;
if ( remainingDist < 4 )
break;
playTime = loopTime * ( remainingDist / shuffleDist ) * 0.9; // don't overshoot
playTime = floor( playTime * serverFPS ) * serverSPF;
if ( playTime < 0.05 )
break;
self animscripts\shared::DoNoteTracksForTime( playTime, "shuffle" );
}
if ( playEnd )
{
if ( moveCoverToCover_checkEndPose( node ) )
blendTime = 0.2;
else
blendTime = 0.4;
self clearAnim( shuffleAnim, blendTime );
self setflaggedanim( "shuffle_end", endAnim, 1, blendTime );
self animscripts\shared::DoNoteTracks( "shuffle_end" );
// clear animation in moveCoverToCoverFinish if needed
}
self safeTeleport( node.origin );
self animMode( "normal" );
self.shuffleMoveInterrupted = undefined;
}
moveCoverToCoverFinish()
{
if ( isdefined( self.shuffleMoveInterrupted ) )
{
self clearanim( %cover_shuffle, 0.2 );
self.shuffleMoveInterrupted = undefined;
self animmode( "none", false );
self orientmode( "face default" );
}
else
{
wait 0.2; // don't clear animation, wait for cover script to take over
self clearanim( %cover_shuffle, 0.2 );
}
}
moveDoorSideToSide( shuffleLeft, startNode, endNode )
{
sideToSideAnim = undefined;
if ( startNode.type == "Cover Right" && endNode.type == "Cover Left" && !shuffleLeft )
sideToSideAnim = %corner_standR_Door_R2L;
else if ( startNode.type == "Cover Left" && endNode.type == "Cover Right" && shuffleLeft )
sideToSideAnim = %corner_standL_Door_L2R;
if ( !isdefined( sideToSideAnim ) )
return false;
self animMode( "zonly_physics", false );
self orientmode( "face current" );
self setflaggedanimrestart( "sideToSide", sideToSideAnim, 1, 0.2 );
assert( animHasNoteTrack( sideToSideAnim, "slide_start" ) );
assert( animHasNoteTrack( sideToSideAnim, "slide_end" ) );
self animscripts\shared::DoNoteTracks( "sideToSide", ::handleSideToSideNotetracks );
slideStartTime = self getAnimTime( sideToSideAnim );
slideDir = endNode.origin - startNode.origin;
slideDir = vectornormalize( ( slideDir[0], slideDir[1], 0 ) );
animDelta = getMoveDelta( sideToSideAnim, slideStartTime, 1 );
remainingVec = endNode.origin - self.origin;
remainingVec = ( remainingVec[0], remainingVec[1], 0 );
slideDist = vectordot( remainingVec, slideDir ) - abs( animDelta[1] );
if ( slideDist > 2 )
{
slideEndTime = getNoteTrackTimes( sideToSideAnim, "slide_end" )[0];
slideTime = ( slideEndTime - slideStartTime ) * getAnimLength( sideToSideAnim );
assert( slideTime > 0 );
slideFrames = int( ceil( slideTime / 0.05 ) );
slideIncrement = slideDir * slideDist / slideFrames;
self thread slideForTime( slideIncrement, slideFrames );
}
self animscripts\shared::DoNoteTracks( "sideToSide" );
self safeTeleport( endNode.origin );
self animMode( "none" );
self orientmode( "face default" );
self.shuffleMoveInterrupted = undefined;
wait 0.2;
return true;
}
handleSideToSideNotetracks( note )
{
if ( note == "slide_start" )
return true;
}
slideForTime( slideIncrement, slideFrames )
{
self endon( "killanimscript" );
self endon( "goal_changed" );
while ( slideFrames > 0 )
{
self safeTeleport( self.origin + slideIncrement );
slideFrames--;
wait 0.05;
}
}
MoveStandMoveOverride( override_anim, weights )
{
self endon( "movemode" );
self clearanim( %combatrun, 0.6 );
self setanimknoball( %combatrun, %body, 1, 0.5, self.moveplaybackrate );
if ( isdefined( self.requestReactToBullet ) && gettime() - self.requestReactToBullet < 100 && isdefined( self.run_overrideBulletReact ) && randomFloat( 1 ) < self.a.reactToBulletChance )
{
animscripts\run::CustomRunningReactToBullets();
return;
}
if ( isarray( override_anim ) )
{
if ( isdefined( self.run_override_weights ) )
moveAnim = choose_from_weighted_array( override_anim, weights );
else
moveAnim = override_anim[ randomint( override_anim.size ) ];
}
else
{
moveAnim = override_anim;
}
self setflaggedanimknob( "moveanim", moveAnim, 1, 0.2 );
animscripts\shared::DoNoteTracks( "moveanim" );
}