776 lines
24 KiB
Plaintext
776 lines
24 KiB
Plaintext
//
|
|
// _alien_jump.gsc
|
|
//
|
|
// Common jump functionality.
|
|
// Jump is called from multiple states. It does not correspond to any particular anim state.
|
|
//
|
|
|
|
#include maps\mp\agents\_scriptedAgents;
|
|
|
|
Jump( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName )
|
|
{
|
|
|
|
maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( endPos - startPos );
|
|
oldTurnRate = self ScrAgentGetMaxTurnSpeed();
|
|
self thread JumpInternal( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName );
|
|
self waittill( "jump_finished" );
|
|
self JumpCleanup( oldTurnRate, endAngles );
|
|
}
|
|
|
|
JumpCleanup( oldTurnRate, endAngles )
|
|
{
|
|
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
|
self ScrAgentSetMaxTurnSpeed( oldTurnRate );
|
|
if ( self maps\mp\alien\_utility::is_normal_upright( AnglesToUp( endAngles ) ) )
|
|
{
|
|
self ScrAgentSetPhysicsMode( "gravity" );
|
|
self.oriented = false;
|
|
self.ignoreme = false;
|
|
}
|
|
else
|
|
{
|
|
self ScrAgentSetPhysicsMode( "noclip" );
|
|
self.oriented = true;
|
|
self.ignoreme = true;
|
|
}
|
|
|
|
|
|
}
|
|
|
|
JumpInternal( startPos, startAngles, endPos, endAngles, nextPos, jumpCBs, scriptableName )
|
|
{
|
|
self endon( "death" );
|
|
self endon ("killanimscript" );
|
|
|
|
self maps\mp\agents\alien\_alien_anim_utils::turnTowardsVector( endPos - startPos );
|
|
|
|
if ( isDefined ( scriptableName ) )
|
|
maps\mp\agents\alien\_alien_anim_utils::resetScriptable( scriptableName, endPos );
|
|
|
|
self.trajectoryActive = false;
|
|
|
|
// figure out initial jump stuff
|
|
jumpAnimStates = SpawnStruct();
|
|
|
|
jumpInfo = self GetJumpInfo( startPos, startAngles, endPos, endAngles, nextPos );
|
|
|
|
self GetJumpAnimStates( jumpInfo, jumpAnimStates );
|
|
|
|
if ( IsDefined( jumpCBs ) && IsDefined( jumpCBs.fnSetAnimStates ) )
|
|
self [[ jumpCBs.fnSetAnimStates ]]( jumpInfo, jumpAnimStates );
|
|
|
|
anglesToEnd = GetJumpStartAngles( startPos, startAngles, endPos );
|
|
|
|
self ScrAgentSetPhysicsMode( "noclip" );
|
|
self ScrAgentSetOrientMode( "face angle abs", anglesToEnd );
|
|
|
|
t = 0;
|
|
|
|
beginAnim = self GetAnimEntry( jumpAnimStates.launchAnimState, jumpAnimStates.launchAnimEntry );
|
|
|
|
/////////////
|
|
// calculate the landing point such that the final position is at our goal
|
|
endAnim = self GetAnimEntry( jumpAnimStates.landAnimState, jumpAnimStates.landAnimEntry );
|
|
|
|
endFinish = GetNotetrackTimes( endAnim, "finish" );
|
|
if ( endFinish.size > 0 )
|
|
{
|
|
endAnimLength = endFinish[0] * GetAnimLength( endAnim );
|
|
}
|
|
else
|
|
{
|
|
endAnimLength = GetAnimLength( endAnim );
|
|
}
|
|
|
|
endAnimTime = endAnimLength / jumpAnimStates.playbackRate;
|
|
|
|
endLandMoveFrame = floor( endAnimTime * 20.0 );
|
|
endLandMoveTime = (endLandMoveFrame / 20.0) / endAnimTime;
|
|
|
|
endLand = GetNotetrackTimes( endAnim, "stop_teleport" );
|
|
if ( endLand.size > 0 )
|
|
{
|
|
endLandTime = endLand[0] * endAnimTime;
|
|
beginLandMoveFrame = ceil( endLandTime * 20.0 );
|
|
beginLandMoveTime = (beginLandMoveFrame / 20.0) / endAnimTime;
|
|
|
|
endLandMove = GetMoveDelta( endAnim, beginLandMoveTime, endLandMoveTime );
|
|
}
|
|
else
|
|
{
|
|
endLandTime = 0.8 * endAnimTime;
|
|
beginLandMoveFrame = ceil( endLandTime * 20.0 );
|
|
beginLandMoveTime = (beginLandMoveFrame / 20.0) / endAnimTime;
|
|
|
|
endLandMove = GetMoveDelta( endAnim, beginLandMoveTime, endLandMoveTime );
|
|
}
|
|
|
|
endAngles = GetJumpEndAngles( startPos, endPos, endAngles );
|
|
endLandMoveRot = RotateVector( endLandMove, endAngles );
|
|
endLandOrigin = endPos - endLandMoveRot;
|
|
|
|
/////////////
|
|
// jump begin
|
|
self ScrAgentSetAnimMode( "anim deltas" );
|
|
self PlaySoundOnMovingEnt( get_jump_SFX_alias() );
|
|
|
|
if ( AnimHasNotetrack( beginAnim, "start_teleport" ) )
|
|
{
|
|
self PlayAnimNAtRateUntilNotetrack(jumpAnimStates.launchAnimState,
|
|
jumpAnimStates.launchAnimEntry,
|
|
jumpAnimStates.playbackRate,
|
|
"jump_launch",
|
|
"start_teleport" );
|
|
}
|
|
else
|
|
{
|
|
self PlayAnimNAtRateForTime(jumpAnimStates.launchAnimState,
|
|
jumpAnimStates.launchAnimEntry,
|
|
jumpAnimStates.playabackRate,
|
|
0.5 * GetAnimLength( beginAnim ) / jumpAnimStates.playbackRate );
|
|
}
|
|
|
|
/////////////
|
|
// do the trajectory
|
|
startTime = gettime();
|
|
t = self ScrAgentDoTrajectory( self.origin, endLandOrigin, jumpInfo.jumpSpeed2D );
|
|
self.trajectoryActive = true;
|
|
|
|
/////////////
|
|
// Handle pain
|
|
self endon( "jump_pain_interrupt" );
|
|
self thread JumpPain( t, endPos );
|
|
|
|
self notify( "jump_launching" ); // for cloaker jump
|
|
|
|
/////////////
|
|
// orient the agent to the plane of the landing position
|
|
oldTurnRate = self ScrAgentGetMaxTurnSpeed();
|
|
self thread jumpOrient( jumpInfo, endAngles, oldTurnRate, t );
|
|
|
|
/////////////
|
|
// finish the launch animation
|
|
self WaitUntilNotetrack( "jump_launch", "end" );
|
|
beginTime = ( gettime() - startTime ) / 1000;
|
|
|
|
|
|
/////////////
|
|
// calculate the time to spend in the air
|
|
loopTime = t - beginTime - endLandTime;
|
|
|
|
/////////////
|
|
// jump loop
|
|
|
|
if ( loopTime > 0 )
|
|
{
|
|
self PlayAnimNAtRateForTime(jumpAnimStates.inAirAnimState,
|
|
jumpAnimStates.inAirAnimEntry,
|
|
jumpAnimStates.playbackRate,
|
|
loopTime );
|
|
}
|
|
|
|
/////////////
|
|
// jump land
|
|
|
|
// Allow a last minute animState change
|
|
if ( IsDefined( jumpCBs ) && IsDefined( jumpCBs.fnLandAnimStateChoice ) )
|
|
self [[ jumpCBs.fnLandAnimStateChoice ]]( jumpInfo, jumpAnimStates );
|
|
|
|
self SetAnimState( jumpAnimStates.landAnimState, jumpAnimStates.landAnimEntry, jumpAnimStates.playbackRate );
|
|
self waittill( "traverse_complete" );
|
|
self.trajectoryActive = false;
|
|
|
|
if ( isDefined ( scriptableName ) )
|
|
maps\mp\agents\alien\_alien_anim_utils::playAnimOnScriptable( scriptableName, endPos );
|
|
|
|
self ScrAgentSetAnimScale( 1.0, 0.0 );
|
|
|
|
self ScrAgentSetMaxTurnSpeed( 20.28318 ); // 2 pi
|
|
self ScrAgentSetAnimMode( "anim deltas" );
|
|
|
|
// make sure our rotation ends up at endAngles.
|
|
// this would be even better if it was accounting for rotating entirely to the
|
|
// direction of the next negotiation node
|
|
self ScrAgentSetOrientMode( "face angle abs", endAngles );
|
|
|
|
self thread waitForLandImpact( "jump_land" );
|
|
self WaitUntilNotetrack( "jump_land", "end" );
|
|
|
|
self ScrAgentSetAnimScale( 1.0, 1.0 );
|
|
|
|
/////////////
|
|
// yay!
|
|
|
|
//delta_from_goal = endPos - self.origin;
|
|
//iprintln( "Jump Delta: " + delta_from_goal );
|
|
|
|
self SetOrigin( endPos, false ); // Boo!
|
|
|
|
self notify( "jump_finished" );
|
|
|
|
}
|
|
|
|
waitForLandImpact( animName )
|
|
{
|
|
alienType = self maps\mp\alien\_utility::get_alien_type();
|
|
|
|
switch( alienType )
|
|
{
|
|
case "elite":
|
|
self WaitUntilNotetrack( animName, "jump_land_impact" );
|
|
maps\mp\agents\alien\_alien_elite::on_jump_impact();
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
jumpOrient( jumpInfo, endAngles, oldTurnRate, timeInAir )
|
|
{
|
|
self endon( "death" );
|
|
UPRIGHT_VECTOR = ( 0, 0, 1 );
|
|
UPRIGHT_DOT = 0.85;
|
|
startUpright = maps\mp\alien\_utility::is_normal_upright( jumpInfo.startUpVector );
|
|
endUpright = maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector );
|
|
|
|
if ( startUpright && !endUpright )
|
|
{
|
|
start_orient_time = 0.5;
|
|
end_orient_time = 1.0;
|
|
}
|
|
else if ( !startUpright && endUpright )
|
|
{
|
|
start_orient_time = 0.0;
|
|
end_orient_time = 0.5;
|
|
}
|
|
else
|
|
{
|
|
start_orient_time = 0.0;
|
|
end_orient_time = 1.0;
|
|
}
|
|
|
|
total_orient_time = end_orient_time - start_orient_time;
|
|
|
|
if ( start_orient_time > 0 )
|
|
{
|
|
wait ( timeInAir * start_orient_time );
|
|
}
|
|
|
|
threshold = 1.0;
|
|
if ( DistanceSquared( self.angles, endAngles ) > threshold )
|
|
{
|
|
anglesDelta = AnglesDelta( self.angles, endAngles );
|
|
|
|
turnRate = anglesDelta / ( timeInAir * total_orient_time );
|
|
turnRate = turnRate * 3.1415926 / 180.0; // deg to rad
|
|
turnRate = turnRate / 20; // rads per frame
|
|
self ScrAgentSetMaxTurnSpeed( turnRate );
|
|
}
|
|
self ScrAgentSetOrientMode( "face angle abs", endAngles );
|
|
}
|
|
|
|
GetJumpInfo( startPos, startAngles, endPos, endAngles, nextPos )
|
|
{
|
|
jumpInfo = SpawnStruct();
|
|
|
|
startToEnd = endPos - startPos;
|
|
startToEnd2D = startToEnd * ( 1, 1, 0 );
|
|
startToEnd2D = VectorNormalize( startToEnd2D );
|
|
|
|
AssertEx( IsDefined( level.alienAnimData.jumpLaunchGroundDelta ), "Jump launch table has not been initialized" );
|
|
|
|
jumpInfo.launchOrigin = startPos + startToEnd2D * level.alienAnimData.jumpLaunchGroundDelta;
|
|
jumpInfo.landOrigin = endPos;
|
|
|
|
jumpInfo.jumpVector = jumpInfo.landOrigin - jumpInfo.launchOrigin;
|
|
jumpInfo.jumpVector2D = jumpInfo.jumpVector * ( 1, 1, 0 );
|
|
jumpInfo.jumpDistance2D = Length( jumpInfo.jumpVector2D );
|
|
AssertEx( jumpInfo.jumpDistance2D != 0, "Trying to jump vertically. This is not handled." );
|
|
|
|
jumpInfo.jumpDirection2D = jumpInfo.jumpVector2D / jumpInfo.jumpDistance2D;
|
|
|
|
if ( IsDefined( nextPos ) )
|
|
jumpInfo.landVector = nextPos - endPos;
|
|
else if ( IsDefined( self.enemy ) )
|
|
jumpInfo.landVector = self.enemy.origin - endPos;
|
|
else
|
|
jumpInfo.landVector = AnglesToForward( self.angles );
|
|
|
|
jumpInfo.startAngles = GetJumpAngles( jumpInfo.jumpVector, AnglesToUp( startAngles ) );
|
|
jumpInfo.endAngles = GetJumpAngles( jumpInfo.jumpVector, AnglesToUp( endAngles ) );
|
|
|
|
jumpInfo.startUpVector = AnglesToUp( jumpInfo.startAngles );
|
|
jumpInfo.endUpVector = AnglesToUp( jumpInfo.endAngles );
|
|
|
|
GetJumpVelocity( jumpInfo );
|
|
|
|
return jumpInfo;
|
|
}
|
|
|
|
GetJumpAngles( jumpVector, vUp )
|
|
{
|
|
forwardVector = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpVector, vUp );
|
|
right = VectorCross( forwardVector, vUp );
|
|
angles = AxisToAngles( forwardVector, right, vUp );
|
|
return angles;
|
|
}
|
|
|
|
//GetLaunchAngle( speed, gravity, x, y )
|
|
//{
|
|
// // From: http://en.wikipedia.org/wiki/Trajectory_of_a_projectile
|
|
// val = speed * speed * speed * speed - gravity * ( gravity * x * x + 2 * y * speed * speed );
|
|
// AssertEx ( val >= 0, "The given velocity is unable to reach the target. Increase the velocity." );
|
|
//
|
|
// //requiredAngleHigh = ATan( ( speed * speed + Sqrt( val ) ) / ( gravity * x ) );
|
|
// requiredAngleLow = ATan( ( speed * speed - Sqrt( val ) ) / ( gravity * x ) );
|
|
//
|
|
// return requiredAngleLow;
|
|
//}
|
|
|
|
//GetMinimumLaunchSpeed( gravity, x, y )
|
|
//{
|
|
// // From: http://en.wikipedia.org/wiki/Range_of_a_projectile
|
|
// // This code calculates the minimum speed required to reach a point by assuming
|
|
// // the point being reached is the maximum range of the projectile fired at the
|
|
// // desired velocity. The result is derived from the equation for the maximum
|
|
// // range of a projectile: Range = ( vel / g ) * sqrt( vel ^ 2 + 2 * g * height )
|
|
// a = 2 * gravity * y;
|
|
// b = gravity * gravity * x * x;
|
|
//
|
|
// result = Sqrt( (a + Sqrt( a * a + 4 * b ) ) / 2 );
|
|
//
|
|
// return result;
|
|
//}
|
|
|
|
GetJumpVelocity( jumpInfo )
|
|
{
|
|
x = jumpInfo.jumpDistance2D;
|
|
y = jumpInfo.jumpVector[ 2 ];
|
|
isWallJump = !maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector );
|
|
g = GetJumpGravity( isWallJump );
|
|
MIN_JUMP_SPEED_MULTIPLIER = 1.01;
|
|
|
|
minJumpSpeed = TrajectoryCalculateMinimumVelocity( jumpInfo.launchOrigin, jumpInfo.landOrigin, g );//GetMinimumLaunchSpeed( g, x, y );
|
|
jumpSpeedMultiplier = GetJumpSpeedMultiplier( isWallJump );
|
|
jumpSpeed = minJumpSpeed * MIN_JUMP_SPEED_MULTIPLIER * jumpSpeedMultiplier;
|
|
AssertEx( jumpSpeed != 0, "Trying to jump but the jump doesn't go anywhere." );
|
|
|
|
jumpAngle = TrajectoryCalculateExitAngle( jumpSpeed, g, x, y );//GetLaunchAngle( jumpSpeed, g, x, y );
|
|
|
|
jumpAngleCos = Cos( jumpAngle );
|
|
AssertEx( jumpAngleCos != 0, "Trying to jump vertically. This is not handled." );
|
|
|
|
jumpInfo.jumpTime = jumpInfo.jumpDistance2D / ( jumpSpeed * jumpAngleCos );
|
|
|
|
gravityVector = g * ( 0, 0, -1 );
|
|
jumpInfo.launchVelocity = TrajectoryCalculateInitialVelocity( jumpInfo.launchOrigin, jumpInfo.landOrigin, gravityVector, jumpInfo.jumpTime ); //(jumpInfo.jumpVector - 0.5 * gravityVector * jumpInfo.jumpTime * jumpInfo.jumpTime) / jumpInfo.jumpTime;
|
|
jumpInfo.launchVelocity2D = jumpInfo.launchVelocity * ( 1, 1, 0 );
|
|
jumpInfo.jumpSpeed2D = Length( jumpInfo.launchVelocity2D );
|
|
}
|
|
|
|
GetJumpSpeedMultiplier( is_wall_jump )
|
|
{
|
|
if ( IsDefined( self.melee_jumping ) && self.melee_jumping )
|
|
{
|
|
AssertEx( IsDefined( level.alien_jump_melee_speed, "Alien jump speed is not defined" ) );
|
|
return level.alien_jump_melee_speed;
|
|
}
|
|
else if ( is_wall_jump )
|
|
{
|
|
return GetDvarFloat( "agent_jumpWallSpeed" );
|
|
}
|
|
else
|
|
{
|
|
return GetDvarFloat( "agent_jumpSpeed" );
|
|
}
|
|
}
|
|
|
|
GetJumpGravity( is_wall_jump )
|
|
{
|
|
if ( IsDefined( self.melee_jumping ) && self.melee_jumping )
|
|
{
|
|
AssertEx( IsDefined( level.alien_jump_melee_gravity, "Alien jump gravity is not defined" ) );
|
|
return level.alien_jump_melee_gravity;
|
|
}
|
|
else if ( is_wall_jump )
|
|
{
|
|
return GetDvarFloat( "agent_jumpWallGravity" );
|
|
}
|
|
else
|
|
{
|
|
return GetDvarFloat( "agent_jumpGravity" );
|
|
}
|
|
}
|
|
|
|
GetJumpPlaybackRate( jumpInfo, animStates )
|
|
{
|
|
AssertEx( jumpInfo.jumpTime != 0);
|
|
|
|
launchAnim = self GetAnimEntry( animStates.launchAnimState, animStates.launchAnimEntry );
|
|
inAirAnim = self GetAnimEntry( animStates.inAirAnimState, animStates.inAirAnimEntry );
|
|
landAnim = self GetAnimEntry( animStates.landAnimState, animStates.landAnimEntry );
|
|
|
|
launchAnimLength = GetAnimLength( launchAnim );
|
|
launchAnimInAirTime = launchAnimLength * 0.5;
|
|
|
|
launchAnimTakeoff = GetNotetrackTimes( launchAnim, "start_teleport" );
|
|
if ( IsDefined( launchAnimTakeoff ) && launchAnimTakeoff.size > 0 )
|
|
launchAnimInAirTime = launchAnimLength - launchAnimTakeoff[0] * launchAnimLength;
|
|
Assert( launchAnimInAirTime < launchAnimLength );
|
|
|
|
landAnimLength = GetAnimLength( landAnim );
|
|
landAnimInAirTime = landAnimLength * 0.5;
|
|
|
|
landAnimArrive = GetNotetrackTimes( landAnim, "stop_teleport" );
|
|
if ( IsDefined( landAnimArrive ) && landAnimArrive.size > 0 )
|
|
landAnimInAirTime = landAnimArrive[0] * landAnimLength;
|
|
Assert( landAnimInAirTime < landAnimLength );
|
|
|
|
inAirAnimLength = GetAnimLength( inAirAnim );
|
|
Assert( inAirAnimLength > 0 );
|
|
|
|
// calculate how long the physics needs to run and round up to the nearest frame
|
|
// this ensures that the trajectory finishes before we move out of the trajectory
|
|
// anim mode
|
|
trajectoryFrameCount = ceil( jumpInfo.jumpTime * 20.0 );
|
|
trajectoryPhysicsTime = trajectoryFrameCount / 20.0;
|
|
|
|
// calculate the amount to scale all animations to achieve the required time in the air
|
|
trajectoryAnimTime = inAirAnimLength + launchAnimInAirTime + landAnimInAirTime;
|
|
trajectoryAnimScale = trajectoryAnimTime / trajectoryPhysicsTime;
|
|
|
|
// add a two frames of trim since the in air animation will play for a fixed time
|
|
// and that time may straddle a frame boundary on both ends
|
|
inAirAnimTime = inAirAnimLength / trajectoryAnimScale + 0.1;
|
|
inAirAnimScale = inAirAnimLength / inAirAnimTime;
|
|
|
|
return inAirAnimScale;
|
|
}
|
|
|
|
GetJumpAnimStates( jumpInfo, animStates )
|
|
{
|
|
animStates.launchAnimState = GetLaunchAnimState( jumpInfo );
|
|
animStates.launchAnimEntry = GetLaunchAnimEntry( jumpInfo, animStates.launchAnimState );
|
|
|
|
animStates.landAnimState = GetLandAnimState( jumpInfo );
|
|
animStates.landAnimEntry = GetLandAnimEntry( jumpInfo, animStates.landAnimState );
|
|
|
|
animStates.inAirAnimState = GetInAirAnimState( jumpInfo, animStates.launchAnimState, animStates.landAnimState );
|
|
animStates.inAirAnimEntry = GetInAirAnimEntry( jumpInfo, animStates.launchAnimState, animStates.landAnimState );
|
|
|
|
animStates.playbackRate = self GetJumpPlaybackRate( jumpInfo, animStates );
|
|
}
|
|
|
|
GetJumpStartAngles( startPos, startAngles, endPos )
|
|
{
|
|
startUp = AnglesToUp( startAngles );
|
|
startForward = VectorNormalize( endPos - startPos );
|
|
if ( VectorDot( startUp, startForward ) > 0.98 )
|
|
startForward = ( 0, 0, 1 );
|
|
startLeft = VectorCross( startUp, startForward );
|
|
startForward = VectorCross( startLeft, startUp );
|
|
return AxisToAngles( startForward, -1 * startLeft, startUp );
|
|
}
|
|
|
|
GetLaunchAnimState( jumpInfo )
|
|
{
|
|
LEVEL_DEGREE_RANGE = 20;
|
|
cosLimitForLevel = Cos( 90 - LEVEL_DEGREE_RANGE );
|
|
|
|
startToEnd = VectorNormalize( jumpInfo.jumpVector );
|
|
startToEndDotUp = VectorDot( startToEnd, jumpInfo.startUpVector );
|
|
|
|
if ( abs( startToEndDotUp ) <= cosLimitForLevel )
|
|
{
|
|
return "jump_launch_level";
|
|
}
|
|
else if ( startToEndDotUp > 0 )
|
|
{
|
|
return "jump_launch_up";
|
|
}
|
|
else if ( startToEndDotUp < 0 )
|
|
{
|
|
return "jump_launch_down";
|
|
}
|
|
}
|
|
|
|
GetLaunchAnimEntry( jumpInfo, launchAnimState )
|
|
{
|
|
launchDirection = VectorNormalize( jumpInfo.launchVelocity );
|
|
launchDirection = RotateVector( launchDirection, jumpInfo.startAngles );
|
|
|
|
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection ), "Alien jump table has not been initialized" );
|
|
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ] ),
|
|
"Alien jump table has not been initialized for launch state " + launchAnimState );
|
|
|
|
launchEntryCount = self GetAnimEntryCount( launchAnimState );
|
|
AssertEx( launchEntryCount > 0, "Alien launch state " + launchAnimState + " as no animations." );
|
|
|
|
launchEntry = 0;
|
|
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ launchEntry ] ),
|
|
"Alien launch entry " + launchEntry + " for state " + launchAnimState + " has no direction." );
|
|
|
|
launchEntryDot = VectorDot( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ launchEntry ], launchDirection );
|
|
|
|
for ( nextLaunchEntry = 1; nextLaunchEntry < launchEntryCount; nextLaunchEntry++ )
|
|
{
|
|
AssertEx( IsDefined( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ nextLaunchEntry ] ),
|
|
"Alien launch entry " + nextLaunchEntry + " for state " + launchAnimState + " has no direction." );
|
|
|
|
nextLaunchEntryDot = VectorDot( level.alienAnimData.jumpLaunchDirection[ launchAnimState ][ nextLaunchEntry ], launchDirection );
|
|
if ( nextLaunchEntryDot > launchEntryDot )
|
|
{
|
|
launchEntry = nextLaunchEntry;
|
|
launchEntryDot = nextLaunchEntryDot;
|
|
}
|
|
}
|
|
|
|
return launchEntry;
|
|
}
|
|
|
|
GetInAirAnimState( jumpInfo, launchAnimState, landAnimState )
|
|
{
|
|
return "jump_in_air";
|
|
}
|
|
|
|
GetInAirAnimEntry( jumpInfo, launchAnimState, landAnimState )
|
|
{
|
|
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry ), "Alien in air table has not been initialized" );
|
|
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry[ launchAnimState ] ),
|
|
"Alien in air table has not been initialized for launch state " + launchAnimState );
|
|
AssertEx( IsDefined( level.alienAnimData.inAirAnimEntry[ launchAnimState ][ landAnimState ] ),
|
|
"Alien in air table has not been initialized for launch state " + launchAnimState + " and land anim state " + landAnimState );
|
|
|
|
return level.alienAnimData.inAirAnimEntry[ launchAnimState ][ landAnimState ];
|
|
}
|
|
|
|
GetJumpEndAngles( startPos, endPos, endAngles )
|
|
{
|
|
endUp = AnglesToUp( endAngles );
|
|
endForward = VectorNormalize( endPos - startPos );
|
|
if ( VectorDot( endUp, endForward ) > 0.98 )
|
|
endForward = ( 0, 0, 1 );
|
|
endLeft = VectorCross( endUp, endForward );
|
|
endForward = VectorCross( endLeft, endUp );
|
|
return AxisToAngles( endForward, -1 * endLeft, endUp );
|
|
}
|
|
|
|
GetLandAnimState( jumpInfo )
|
|
{
|
|
jumpVectorLength = length( jumpInfo.jumpVector );
|
|
PITCH_THRESHOLD = 0.342; // sin(20)
|
|
|
|
if ( !maps\mp\alien\_utility::is_normal_upright( jumpInfo.endUpVector ) )
|
|
{
|
|
WORLD_UP = ( 0, 0, 1 );
|
|
pitch = VectorDot( jumpInfo.jumpVector, WORLD_UP ) / jumpVectorLength;
|
|
|
|
if ( pitch > PITCH_THRESHOLD )
|
|
{
|
|
return "jump_land_sidewall_low";
|
|
}
|
|
else
|
|
{
|
|
return "jump_land_sidewall_high";
|
|
}
|
|
}
|
|
|
|
pitch = VectorDot( jumpInfo.jumpVector, jumpInfo.endUpVector ) / jumpVectorLength;
|
|
|
|
if ( pitch > PITCH_THRESHOLD )
|
|
{
|
|
return "jump_land_down";
|
|
}
|
|
else if ( pitch < ( PITCH_THRESHOLD * -1 ) )
|
|
{
|
|
return "jump_land_up";
|
|
}
|
|
else
|
|
{
|
|
return "jump_land_level";
|
|
}
|
|
}
|
|
|
|
GetLandAnimEntry( jumpInfo, landAnimState )
|
|
{
|
|
incomingVectorWithoutNormal = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpInfo.jumpVector, jumpInfo.endUpVector );
|
|
outgoingVectorWithoutNormal = maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( jumpInfo.landVector, jumpInfo.endUpVector );
|
|
thirdVector = incomingVectorWithoutNormal - outgoingVectorWithoutNormal;
|
|
|
|
outgoingRightVector = VectorCross( outgoingVectorWithoutNormal, jumpInfo.endUpVector );
|
|
outgoingRightVectorWithoutNormal = VectorNormalize( maps\mp\agents\alien\_alien_anim_utils::ProjectVectorToPlane( outgoingRightVector, jumpInfo.endUpVector ) ) * 100;
|
|
|
|
projectionIncomingToOutgoingRight = VectorDot ( incomingVectorWithoutNormal * -1, outgoingRightVectorWithoutNormal );
|
|
|
|
//Law of cosine
|
|
a = Length( incomingVectorWithoutNormal );
|
|
b = Length( outgoingVectorWithoutNormal );
|
|
c = Length( thirdVector );
|
|
|
|
MIN_LENGTH = 0.001;
|
|
|
|
// Edge case: Return forward;
|
|
if ( a < MIN_LENGTH || b < MIN_LENGTH )
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
ratio = ( a * a + b * b - c * c ) / ( 2 * a * b );
|
|
if ( ratio <= -1 )
|
|
{
|
|
return 6;
|
|
}
|
|
else if ( ratio >= 1 )
|
|
{
|
|
return 1;
|
|
}
|
|
else
|
|
{
|
|
rotatedYaw = Acos( ratio );
|
|
if ( projectionIncomingToOutgoingRight > 0 ) //Entering from the right
|
|
{
|
|
if ( 0 <= rotatedYaw && rotatedYaw < 22.5 )
|
|
{
|
|
return 1;
|
|
}
|
|
else if ( 22.5 <= rotatedYaw && rotatedYaw < 67.5 )
|
|
{
|
|
return 2;
|
|
}
|
|
else if ( 67.5 <= rotatedYaw && rotatedYaw < 112.5 )
|
|
{
|
|
return 4;
|
|
}
|
|
else if ( 112.5 <= rotatedYaw && rotatedYaw < 157.5 )
|
|
{
|
|
return 7;
|
|
}
|
|
else
|
|
{
|
|
return 6;
|
|
}
|
|
}
|
|
else //Entering from the left
|
|
{
|
|
if ( 0 <= rotatedYaw && rotatedYaw < 22.5 )
|
|
{
|
|
return 1;
|
|
}
|
|
else if ( 22.5 <= rotatedYaw && rotatedYaw < 67.5 )
|
|
{
|
|
return 0;
|
|
}
|
|
else if ( 67.5 <= rotatedYaw && rotatedYaw < 112.5 )
|
|
{
|
|
return 3;
|
|
}
|
|
else if ( 112.5 <= rotatedYaw && rotatedYaw < 157.5 )
|
|
{
|
|
return 5;
|
|
}
|
|
else
|
|
{
|
|
return 6;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
JumpPain( duration, endPos )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "killanimscript" );
|
|
self endon( "jump_finished" );
|
|
|
|
start_time = gettime();
|
|
duration_msec = duration*1000;
|
|
self waittill( "jump_pain", damageDirection, hitLocation, iDamage, stun );
|
|
|
|
// Make sure we're still jumping
|
|
if ( !self.trajectoryActive )
|
|
{
|
|
return; // Too late!
|
|
}
|
|
|
|
// Stop normal jump animations and play the pain
|
|
self notify( "jump_pain_interrupt" );
|
|
|
|
jump_pain_state = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimState( "jump_pain", iDamage, stun );
|
|
jump_pain_index = self maps\mp\agents\alien\_alien_anim_utils::getPainAnimIndex( "jump", damageDirection, hitLocation );
|
|
damage_degree = self maps\mp\agents\alien\_alien_anim_utils::getDamageDegree( iDamage, stun );
|
|
jump_end_time_sec = start_time * 0.001 + duration;
|
|
PlayInAirJumpPainAnims( jump_pain_state, jump_pain_index, jump_end_time_sec, damage_degree );
|
|
|
|
self ScrAgentSetAnimScale( 1.0, 0.0 );
|
|
self ScrAgentSetAnimMode( "anim deltas" );
|
|
|
|
// this would be even better if it was accounting for rotating entirely to the
|
|
// direction of the next negotiation node
|
|
self ScrAgentSetOrientMode( "face angle abs", self.angles );
|
|
|
|
impact_pain_anim = self GetImpactPainAnimState( damage_degree );
|
|
impact_pain_index = self maps\mp\agents\alien\_alien_anim_utils::GetImpactPainAnimIndex( jump_pain_index );
|
|
self SetAnimState( impact_pain_anim, impact_pain_index, 1.0 );
|
|
self WaitUntilNotetrack( impact_pain_anim, "code_move" );
|
|
|
|
self notify ( "jump_finished" );
|
|
}
|
|
|
|
PlayInAirJumpPainAnims( jump_pain_state, jump_pain_entry, jump_end_time_sec, damage_degree )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "killanimscript" );
|
|
self endon( "jump_finished" );
|
|
|
|
self SetAnimState( jump_pain_state, jump_pain_entry, 1.0 );
|
|
msg = self common_scripts\utility::waittill_any_return( "jump_pain", "traverse_complete" );
|
|
if ( msg == "traverse_complete" )
|
|
return;
|
|
|
|
idle_time_remaining = jump_end_time_sec - GetTime() * 0.001;
|
|
|
|
if ( idle_time_remaining > 0 )
|
|
{
|
|
MAX_RATE_SCALE = 2.0;
|
|
jump_pain_idle_state = self GetJumpPainIdleAnimState( damage_degree );
|
|
jump_pain_idle_anim = self GetAnimEntry( jump_pain_idle_state, jump_pain_entry );
|
|
jump_pain_idle_anim_length = GetAnimLength( jump_pain_idle_anim );
|
|
jump_pain_idle_rate = Min( MAX_RATE_SCALE, jump_pain_idle_anim_length / idle_time_remaining );
|
|
|
|
self SetAnimState( jump_pain_idle_state, jump_pain_entry, jump_pain_idle_rate );
|
|
}
|
|
|
|
self waittill( "traverse_complete" );
|
|
}
|
|
|
|
GetJumpPainIdleAnimState( damage_degree )
|
|
{
|
|
return ( "jump_pain_idle_" + damage_degree );
|
|
}
|
|
|
|
GetImpactPainAnimState( damage_degree )
|
|
{
|
|
return ( "jump_impact_pain_" + damage_degree );
|
|
}
|
|
|
|
get_jump_SFX_alias()
|
|
{
|
|
switch( maps\mp\alien\_utility::get_alien_type() )
|
|
{
|
|
case "elite":
|
|
return "null"; // "No sound is better than the wrong sound" - Tim S.
|
|
|
|
case "spitter":
|
|
return "spitter_jump";
|
|
|
|
case "seeder":
|
|
return "seed_jump";
|
|
|
|
case "gargoyle":
|
|
return "gg_jump";
|
|
|
|
default:
|
|
return "alien_jump";
|
|
}
|
|
} |