jedioutcast/CODE-mp/game/bg_panimate.c
2013-04-04 17:24:29 -05:00

1205 lines
27 KiB
C

// BG_PAnimate.c
#include "q_shared.h"
#include "bg_public.h"
#include "bg_local.h"
#include "anims.h"
#include "../cgame/animtable.h"
/*
==============================================================================
BEGIN: Animation utility functions (sequence checking)
==============================================================================
*/
//Called regardless of pm validity:
qboolean BG_InSpecialJump( int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_WALL_RUN_RIGHT:
case BOTH_WALL_RUN_RIGHT_FLIP:
case BOTH_WALL_RUN_LEFT:
case BOTH_WALL_RUN_LEFT_FLIP:
case BOTH_WALL_FLIP_RIGHT:
case BOTH_WALL_FLIP_LEFT:
case BOTH_FLIP_BACK1:
case BOTH_FLIP_BACK2:
case BOTH_FLIP_BACK3:
case BOTH_WALL_FLIP_BACK1:
case BOTH_BUTTERFLY_LEFT:
case BOTH_BUTTERFLY_RIGHT:
return qtrue;
}
return qfalse;
}
qboolean BG_InSaberStandAnim( int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_SABERFAST_STANCE:
case BOTH_STAND2:
case BOTH_SABERSLOW_STANCE:
return qtrue;
default:
return qfalse;
}
}
qboolean BG_DirectFlippingAnim( int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_FLIP_F: //# Flip forward
case BOTH_FLIP_B: //# Flip backwards
case BOTH_FLIP_L: //# Flip left
case BOTH_FLIP_R: //# Flip right
return qtrue;
break;
}
return qfalse;
}
qboolean BG_SaberInAttack( int move )
{
if ( move >= LS_A_TL2BR && move <= LS_A_T2B )
{
return qtrue;
}
switch ( move )
{
case LS_A_BACK:
case LS_A_BACK_CR:
case LS_A_BACKSTAB:
case LS_A_LUNGE:
case LS_A_JUMP_T__B_:
case LS_A_FLIP_STAB:
case LS_A_FLIP_SLASH:
return qtrue;
break;
}
return qfalse;
}
qboolean BG_SaberInSpecial( int move )
{
switch( move )
{
case LS_A_BACK:
case LS_A_BACK_CR:
case LS_A_BACKSTAB:
case LS_A_LUNGE:
case LS_A_JUMP_T__B_:
case LS_A_FLIP_STAB:
case LS_A_FLIP_SLASH:
return qtrue;
}
return qfalse;
}
qboolean BG_SaberInIdle( int move )
{
switch ( move )
{
case LS_NONE:
case LS_READY:
case LS_DRAW:
case LS_PUTAWAY:
return qtrue;
break;
}
return qfalse;
}
qboolean BG_FlippingAnim( int anim )
{
switch ( anim&~ANIM_TOGGLEBIT )
{
case BOTH_FLIP_F: //# Flip forward
case BOTH_FLIP_B: //# Flip backwards
case BOTH_FLIP_L: //# Flip left
case BOTH_FLIP_R: //# Flip right
case BOTH_WALL_RUN_RIGHT_FLIP:
case BOTH_WALL_RUN_LEFT_FLIP:
case BOTH_WALL_FLIP_RIGHT:
case BOTH_WALL_FLIP_LEFT:
case BOTH_FLIP_BACK1:
case BOTH_FLIP_BACK2:
case BOTH_FLIP_BACK3:
case BOTH_WALL_FLIP_BACK1:
//Not really flips, but...
case BOTH_WALL_RUN_RIGHT:
case BOTH_WALL_RUN_LEFT:
case BOTH_WALL_RUN_RIGHT_STOP:
case BOTH_WALL_RUN_LEFT_STOP:
case BOTH_BUTTERFLY_LEFT:
case BOTH_BUTTERFLY_RIGHT:
//
case BOTH_ARIAL_LEFT:
case BOTH_ARIAL_RIGHT:
case BOTH_ARIAL_F1:
case BOTH_CARTWHEEL_LEFT:
case BOTH_CARTWHEEL_RIGHT:
case BOTH_JUMPFLIPSLASHDOWN1:
case BOTH_JUMPFLIPSTABDOWN:
return qtrue;
break;
}
return qfalse;
}
qboolean BG_SpinningSaberAnim( int anim )
{
switch ( anim&~ANIM_TOGGLEBIT )
{
//level 1 - FIXME: level 1 will have *no* spins
case BOTH_T1_BR_BL:
case BOTH_T1__R__L:
case BOTH_T1__R_BL:
case BOTH_T1_TR_BL:
case BOTH_T1_BR_TL:
case BOTH_T1_BR__L:
case BOTH_T1_TL_BR:
case BOTH_T1__L_BR:
case BOTH_T1__L__R:
case BOTH_T1_BL_BR:
case BOTH_T1_BL__R:
case BOTH_T1_BL_TR:
//level 2
case BOTH_T2_BR__L:
case BOTH_T2_BR_BL:
case BOTH_T2__R_BL:
case BOTH_T2__L_BR:
case BOTH_T2_BL_BR:
case BOTH_T2_BL__R:
//level 3
case BOTH_T3_BR__L:
case BOTH_T3_BR_BL:
case BOTH_T3__R_BL:
case BOTH_T3__L_BR:
case BOTH_T3_BL_BR:
case BOTH_T3_BL__R:
//level 4
case BOTH_T4_BR__L:
case BOTH_T4_BR_BL:
case BOTH_T4__R_BL:
case BOTH_T4__L_BR:
case BOTH_T4_BL_BR:
case BOTH_T4_BL__R:
//level 5
case BOTH_T5_BR_BL:
case BOTH_T5__R__L:
case BOTH_T5__R_BL:
case BOTH_T5_TR_BL:
case BOTH_T5_BR_TL:
case BOTH_T5_BR__L:
case BOTH_T5_TL_BR:
case BOTH_T5__L_BR:
case BOTH_T5__L__R:
case BOTH_T5_BL_BR:
case BOTH_T5_BL__R:
case BOTH_T5_BL_TR:
//special
//case BOTH_A2_STABBACK1:
case BOTH_ATTACK_BACK:
case BOTH_CROUCHATTACKBACK1:
case BOTH_BUTTERFLY_LEFT:
case BOTH_BUTTERFLY_RIGHT:
case BOTH_FJSS_TR_BL:
case BOTH_FJSS_TL_BR:
case BOTH_JUMPFLIPSLASHDOWN1:
case BOTH_JUMPFLIPSTABDOWN:
return qtrue;
break;
}
return qfalse;
}
qboolean BG_SaberInSpecialAttack( int anim )
{
switch ( anim&~ANIM_TOGGLEBIT )
{
case BOTH_A2_STABBACK1:
case BOTH_ATTACK_BACK:
case BOTH_CROUCHATTACKBACK1:
case BOTH_BUTTERFLY_LEFT:
case BOTH_BUTTERFLY_RIGHT:
case BOTH_FJSS_TR_BL:
case BOTH_FJSS_TL_BR:
case BOTH_LUNGE2_B__T_:
case BOTH_FORCELEAP2_T__B_:
case BOTH_JUMPFLIPSLASHDOWN1://#
case BOTH_JUMPFLIPSTABDOWN://#
return qtrue;
}
return qfalse;
}
int BG_BrokenParryForAttack( int move )
{
//Our attack was knocked away by a knockaway parry
//FIXME: need actual anims for this
//FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center
switch ( saberMoveData[move].startQuad )
{
case Q_B:
return LS_V1_B_;
break;
case Q_BR:
return LS_V1_BR;
break;
case Q_R:
return LS_V1__R;
break;
case Q_TR:
return LS_V1_TR;
break;
case Q_T:
return LS_V1_T_;
break;
case Q_TL:
return LS_V1_TL;
break;
case Q_L:
return LS_V1__L;
break;
case Q_BL:
return LS_V1_BL;
break;
}
return LS_NONE;
}
int BG_BrokenParryForParry( int move )
{
//FIXME: need actual anims for this
//FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center
switch ( move )
{
case LS_PARRY_UP:
//Hmm... since we don't know what dir the hit came from, randomly pick knock down or knock back
if ( Q_irand( 0, 1 ) )
{
return LS_H1_B_;
}
else
{
return LS_H1_T_;
}
break;
case LS_PARRY_UR:
return LS_H1_TR;
break;
case LS_PARRY_UL:
return LS_H1_TL;
break;
case LS_PARRY_LR:
return LS_H1_BR;
break;
case LS_PARRY_LL:
return LS_H1_BL;
break;
case LS_READY:
return LS_H1_B_;//???
break;
}
return LS_NONE;
}
int BG_KnockawayForParry( int move )
{
//FIXME: need actual anims for this
//FIXME: need to know which side of the saber was hit! For now, we presume the saber gets knocked away from the center
switch ( move )
{
case BLOCKED_TOP://LS_PARRY_UP:
return LS_K1_T_;//push up
break;
case BLOCKED_UPPER_RIGHT://LS_PARRY_UR:
default://case LS_READY:
return LS_K1_TR;//push up, slightly to right
break;
case BLOCKED_UPPER_LEFT://LS_PARRY_UL:
return LS_K1_TL;//push up and to left
break;
case BLOCKED_LOWER_RIGHT://LS_PARRY_LR:
return LS_K1_BR;//push down and to left
break;
case BLOCKED_LOWER_LEFT://LS_PARRY_LL:
return LS_K1_BL;//push down and to right
break;
}
//return LS_NONE;
}
qboolean BG_InRoll( playerState_t *ps, int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_ROLL_F:
case BOTH_ROLL_B:
case BOTH_ROLL_R:
case BOTH_ROLL_L:
if ( ps->legsTimer > 0 )
{
return qtrue;
}
break;
}
return qfalse;
}
qboolean BG_InDeathAnim( int anim )
{
switch((anim&~ANIM_TOGGLEBIT))
{
case BOTH_DIVE1:
case BOTH_DEATHBACKWARD1:
case BOTH_DEATHBACKWARD2:
case BOTH_DEATHFORWARD1:
case BOTH_DEATHFORWARD2:
case BOTH_DEATH1:
case BOTH_DEATH2:
case BOTH_DEATH3:
case BOTH_DEATH4:
case BOTH_DEATH5:
case BOTH_DEATH6:
case BOTH_DEATH7:
case BOTH_DEATH1IDLE:
case BOTH_LYINGDEATH1:
case BOTH_STUMBLEDEATH1:
case BOTH_FALLDEATH1:
case BOTH_FALLDEATH1INAIR:
case BOTH_FALLDEATH1LAND:
return qtrue;
break;
default:
return qfalse;
break;
}
}
//Called only where pm is valid (not all require pm, but some do):
int PM_SaberBounceForAttack( int move )
{
switch ( saberMoveData[move].startQuad )
{
case Q_B:
case Q_BR:
return LS_B1_BR;
break;
case Q_R:
return LS_B1__R;
break;
case Q_TR:
return LS_B1_TR;
break;
case Q_T:
return LS_B1_T_;
break;
case Q_TL:
return LS_B1_TL;
break;
case Q_L:
return LS_B1__L;
break;
case Q_BL:
return LS_B1_BL;
break;
}
return LS_NONE;
}
int PM_SaberDeflectionForQuad( int quad )
{
switch ( quad )
{
case Q_B:
return LS_D1_B_;
break;
case Q_BR:
return LS_D1_BR;
break;
case Q_R:
return LS_D1__R;
break;
case Q_TR:
return LS_D1_TR;
break;
case Q_T:
return LS_D1_T_;
break;
case Q_TL:
return LS_D1_TL;
break;
case Q_L:
return LS_D1__L;
break;
case Q_BL:
return LS_D1_BL;
break;
}
return LS_NONE;
}
qboolean PM_SaberInDeflect( int move )
{
if ( move >= LS_D1_BR && move <= LS_D1_B_ )
{
return qtrue;
}
return qfalse;
}
qboolean PM_SaberInParry( int move )
{
if ( move >= LS_PARRY_UP && move <= LS_PARRY_LL )
{
return qtrue;
}
return qfalse;
}
qboolean PM_SaberInKnockaway( int move )
{
if ( move >= LS_K1_T_ && move <= LS_K1_BL )
{
return qtrue;
}
return qfalse;
}
qboolean PM_SaberInReflect( int move )
{
if ( move >= LS_REFLECT_UP && move <= LS_REFLECT_LL )
{
return qtrue;
}
return qfalse;
}
qboolean PM_SaberInStart( int move )
{
if ( move >= LS_S_TL2BR && move <= LS_S_T2B )
{
return qtrue;
}
return qfalse;
}
qboolean PM_SaberInReturn( int move )
{
if ( move >= LS_R_TL2BR && move <= LS_R_TL2BR )
{
return qtrue;
}
return qfalse;
}
qboolean PM_InSaberAnim( int anim )
{
if ( (anim&~ANIM_TOGGLEBIT) >= BOTH_A1_T__B_ && (anim&~ANIM_TOGGLEBIT) <= BOTH_H1_S1_BR )
{
return qtrue;
}
return qfalse;
}
qboolean PM_InKnockDown( playerState_t *ps )
{
switch ( (ps->legsAnim&~ANIM_TOGGLEBIT) )
{
case BOTH_KNOCKDOWN1:
case BOTH_KNOCKDOWN2:
case BOTH_KNOCKDOWN3:
case BOTH_KNOCKDOWN4:
case BOTH_KNOCKDOWN5:
return qtrue;
break;
case BOTH_GETUP1:
case BOTH_GETUP2:
case BOTH_GETUP3:
case BOTH_GETUP4:
case BOTH_GETUP5:
case BOTH_FORCE_GETUP_F1:
case BOTH_FORCE_GETUP_F2:
case BOTH_FORCE_GETUP_B1:
case BOTH_FORCE_GETUP_B2:
case BOTH_FORCE_GETUP_B3:
case BOTH_FORCE_GETUP_B4:
case BOTH_FORCE_GETUP_B5:
if ( ps->legsTimer )
{
return qtrue;
}
break;
}
return qfalse;
}
qboolean PM_PainAnim( int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_PAIN1: //# First take pain anim
case BOTH_PAIN2: //# Second take pain anim
case BOTH_PAIN3: //# Third take pain anim
case BOTH_PAIN4: //# Fourth take pain anim
case BOTH_PAIN5: //# Fifth take pain anim - from behind
case BOTH_PAIN6: //# Sixth take pain anim - from behind
case BOTH_PAIN7: //# Seventh take pain anim - from behind
case BOTH_PAIN8: //# Eigth take pain anim - from behind
case BOTH_PAIN9: //#
case BOTH_PAIN10: //#
case BOTH_PAIN11: //#
case BOTH_PAIN12: //#
case BOTH_PAIN13: //#
case BOTH_PAIN14: //#
case BOTH_PAIN15: //#
case BOTH_PAIN16: //#
case BOTH_PAIN17: //#
case BOTH_PAIN18: //#
case BOTH_PAIN19: //#
return qtrue;
break;
}
return qfalse;
}
qboolean PM_JumpingAnim( int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_JUMP1: //# Jump - wind-up and leave ground
case BOTH_INAIR1: //# In air loop (from jump)
case BOTH_LAND1: //# Landing (from in air loop)
case BOTH_LAND2: //# Landing Hard (from a great height)
case BOTH_JUMPBACK1: //# Jump backwards - wind-up and leave ground
case BOTH_INAIRBACK1: //# In air loop (from jump back)
case BOTH_LANDBACK1: //# Landing backwards(from in air loop)
case BOTH_JUMPLEFT1: //# Jump left - wind-up and leave ground
case BOTH_INAIRLEFT1: //# In air loop (from jump left)
case BOTH_LANDLEFT1: //# Landing left(from in air loop)
case BOTH_JUMPRIGHT1: //# Jump right - wind-up and leave ground
case BOTH_INAIRRIGHT1: //# In air loop (from jump right)
case BOTH_LANDRIGHT1: //# Landing right(from in air loop)
case BOTH_FORCEJUMP1: //# Jump - wind-up and leave ground
case BOTH_FORCEINAIR1: //# In air loop (from jump)
case BOTH_FORCELAND1: //# Landing (from in air loop)
case BOTH_FORCEJUMPBACK1: //# Jump backwards - wind-up and leave ground
case BOTH_FORCEINAIRBACK1: //# In air loop (from jump back)
case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop)
case BOTH_FORCEJUMPLEFT1: //# Jump left - wind-up and leave ground
case BOTH_FORCEINAIRLEFT1: //# In air loop (from jump left)
case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop)
case BOTH_FORCEJUMPRIGHT1: //# Jump right - wind-up and leave ground
case BOTH_FORCEINAIRRIGHT1: //# In air loop (from jump right)
case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop)
return qtrue;
break;
}
return qfalse;
}
qboolean PM_LandingAnim( int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_LAND1: //# Landing (from in air loop)
case BOTH_LAND2: //# Landing Hard (from a great height)
case BOTH_LANDBACK1: //# Landing backwards(from in air loop)
case BOTH_LANDLEFT1: //# Landing left(from in air loop)
case BOTH_LANDRIGHT1: //# Landing right(from in air loop)
case BOTH_FORCELAND1: //# Landing (from in air loop)
case BOTH_FORCELANDBACK1: //# Landing backwards(from in air loop)
case BOTH_FORCELANDLEFT1: //# Landing left(from in air loop)
case BOTH_FORCELANDRIGHT1: //# Landing right(from in air loop)
return qtrue;
break;
}
return qfalse;
}
qboolean PM_SpinningAnim( int anim )
{
/*
switch ( anim )
{
//FIXME: list any other spinning anims
default:
break;
}
*/
return BG_SpinningSaberAnim( anim );
}
qboolean PM_InOnGroundAnim ( int anim )
{
switch( anim&~ANIM_TOGGLEBIT )
{
case BOTH_DEAD1:
case BOTH_DEAD2:
case BOTH_DEAD3:
case BOTH_DEAD4:
case BOTH_DEAD5:
case BOTH_DEADFORWARD1:
case BOTH_DEADBACKWARD1:
case BOTH_DEADFORWARD2:
case BOTH_DEADBACKWARD2:
case BOTH_LYINGDEATH1:
case BOTH_LYINGDEAD1:
case BOTH_PAIN2WRITHE1: //# Transition from upright position to writhing on ground anim
case BOTH_WRITHING1: //# Lying on ground writhing in pain
case BOTH_WRITHING1RLEG: //# Lying on ground writhing in pain: holding right leg
case BOTH_WRITHING1LLEG: //# Lying on ground writhing in pain: holding left leg
case BOTH_WRITHING2: //# Lying on stomache writhing in pain
case BOTH_INJURED1: //# Lying down: against wall - can also be sleeping
case BOTH_CRAWLBACK1: //# Lying on back: crawling backwards with elbows
case BOTH_INJURED2: //# Injured pose 2
case BOTH_INJURED3: //# Injured pose 3
case BOTH_INJURED6: //# Injured pose 6
case BOTH_INJURED6ATTACKSTART: //# Start attack while in injured 6 pose
case BOTH_INJURED6ATTACKSTOP: //# End attack while in injured 6 pose
case BOTH_INJURED6COMBADGE: //# Hit combadge while in injured 6 pose
case BOTH_INJURED6POINT: //# Chang points to door while in injured state
case BOTH_KNOCKDOWN1: //#
case BOTH_KNOCKDOWN2: //#
return qtrue;
break;
}
return qfalse;
}
qboolean PM_InRollComplete( playerState_t *ps, int anim )
{
switch ( (anim&~ANIM_TOGGLEBIT) )
{
case BOTH_ROLL_F:
case BOTH_ROLL_B:
case BOTH_ROLL_R:
case BOTH_ROLL_L:
if ( ps->legsTimer < 1 )
{
return qtrue;
}
break;
}
return qfalse;
}
int PM_AnimLength( int index, animNumber_t anim )
{
if (anim >= MAX_ANIMATIONS)
{
return -1;
}
return pm->animations[anim].numFrames * fabs(pm->animations[anim].frameLerp);
}
void PM_DebugLegsAnim(int anim)
{
int oldAnim = (pm->ps->legsAnim & ~ANIM_TOGGLEBIT);
int newAnim = (anim & ~ANIM_TOGGLEBIT);
if (oldAnim < MAX_TOTALANIMATIONS && oldAnim >= BOTH_DEATH1 &&
newAnim < MAX_TOTALANIMATIONS && newAnim >= BOTH_DEATH1)
{
Com_Printf("OLD: %s\n", animTable[oldAnim]);
Com_Printf("NEW: %s\n", animTable[newAnim]);
}
}
/*
==============================================================================
END: Animation utility functions (sequence checking)
==============================================================================
*/
/*
======================
BG_ParseAnimationFile
Read a configuration file containing animation coutns and rates
models/players/visor/animation.cfg, etc
======================
*/
char BGPAFtext[40000];
qboolean BGPAFtextLoaded = qfalse;
animation_t bgGlobalAnimations[MAX_TOTALANIMATIONS];
//#define CONVENIENT_ANIMATION_FILE_DEBUG_THING
#ifdef CONVENIENT_ANIMATION_FILE_DEBUG_THING
void SpewDebugStuffToFile()
{
fileHandle_t f;
int i = 0;
trap_FS_FOpenFile("file_of_debug_stuff_MP.txt", &f, FS_WRITE);
if (!f)
{
return;
}
BGPAFtext[0] = 0;
while (i < MAX_ANIMATIONS)
{
strcat(BGPAFtext, va("%i %i\n", i, bgGlobalAnimations[i].frameLerp));
i++;
}
trap_FS_Write(BGPAFtext, strlen(BGPAFtext), f);
trap_FS_FCloseFile(f);
}
#endif
qboolean BG_ParseAnimationFile(const char *filename)
{
char *text_p;
int len;
int i;
char *token;
float fps;
int skip;
fileHandle_t f;
int animNum;
// load the file
if (!BGPAFtextLoaded)
{ //rww - We are always using the same animation config now. So only load it once.
len = trap_FS_FOpenFile( filename, &f, FS_READ );
if ( len <= 0 )
{
return qfalse;
}
if ( len >= sizeof( BGPAFtext ) - 1 )
{
//Com_Printf( "File %s too long\n", filename );
return qfalse;
}
trap_FS_Read( BGPAFtext, len, f );
BGPAFtext[len] = 0;
trap_FS_FCloseFile( f );
}
else
{
return qtrue;
}
// parse the text
text_p = BGPAFtext;
skip = 0; // quiet the compiler warning
//FIXME: have some way of playing anims backwards... negative numFrames?
//initialize anim array so that from 0 to MAX_ANIMATIONS, set default values of 0 1 0 100
for(i = 0; i < MAX_ANIMATIONS; i++)
{
bgGlobalAnimations[i].firstFrame = 0;
bgGlobalAnimations[i].numFrames = 0;
bgGlobalAnimations[i].loopFrames = -1;
bgGlobalAnimations[i].frameLerp = 100;
bgGlobalAnimations[i].initialLerp = 100;
}
// read information for each frame
while(1)
{
token = COM_Parse( (const char **)(&text_p) );
if ( !token || !token[0])
{
break;
}
animNum = GetIDForString(animTable, token);
if(animNum == -1)
{
//#ifndef FINAL_BUILD
#ifdef _DEBUG
Com_Printf(S_COLOR_RED"WARNING: Unknown token %s in %s\n", token, filename);
#endif
continue;
}
token = COM_Parse( (const char **)(&text_p) );
if ( !token )
{
break;
}
bgGlobalAnimations[animNum].firstFrame = atoi( token );
token = COM_Parse( (const char **)(&text_p) );
if ( !token )
{
break;
}
bgGlobalAnimations[animNum].numFrames = atoi( token );
token = COM_Parse( (const char **)(&text_p) );
if ( !token )
{
break;
}
bgGlobalAnimations[animNum].loopFrames = atoi( token );
token = COM_Parse( (const char **)(&text_p) );
if ( !token )
{
break;
}
fps = atof( token );
if ( fps == 0 )
{
fps = 1;//Don't allow divide by zero error
}
if ( fps < 0 )
{//backwards
bgGlobalAnimations[animNum].frameLerp = floor(1000.0f / fps);
}
else
{
bgGlobalAnimations[animNum].frameLerp = ceil(1000.0f / fps);
}
bgGlobalAnimations[animNum].initialLerp = ceil(1000.0f / fabs(fps));
}
#ifdef _DEBUG
//Check the array, and print the ones that have nothing in them.
for(i = 0; i < MAX_ANIMATIONS; i++)
{
if (animTable[i].name != NULL) // This animation reference exists.
{
if (bgGlobalAnimations[i].firstFrame <= 0 && bgGlobalAnimations[i].numFrames <=0)
{ // This is an empty animation reference.
Com_Printf("***ANIMTABLE reference #%d (%s) is empty!\n", i, animTable[i].name);
}
}
}
#endif // _DEBUG
#ifdef CONVENIENT_ANIMATION_FILE_DEBUG_THING
SpewDebugStuffToFile();
#endif
BGPAFtextLoaded = qtrue;
return qtrue;
}
/*
===================
LEGS Animations
Base animation for overall body
===================
*/
static void PM_StartLegsAnim( int anim ) {
if ( pm->ps->pm_type >= PM_DEAD ) {
return;
}
if ( pm->ps->legsTimer > 0 ) {
return; // a high priority animation is running
}
if (pm->ps->usingATST)
{ //animation is handled mostly client-side with only a few exceptions
return;
}
if (BG_InSaberStandAnim(anim) && pm->ps->weapon == WP_SABER && pm->ps->dualBlade)
{ //a bit of a hack, but dualblade is cheat-only anyway
anim = BOTH_STAND1;
}
pm->ps->legsAnim = ( ( pm->ps->legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
| anim;
if ( pm->debugLevel ) {
Com_Printf("%d: StartLegsAnim %d, on client#%d\n", pm->cmd.serverTime, anim, pm->ps->clientNum);
}
}
void PM_ContinueLegsAnim( int anim ) {
if ( ( pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim ) {
return;
}
if ( pm->ps->legsTimer > 0 ) {
return; // a high priority animation is running
}
PM_StartLegsAnim( anim );
}
void PM_ForceLegsAnim( int anim) {
if (BG_InSpecialJump(pm->ps->legsAnim) &&
pm->ps->legsTimer > 0 &&
!BG_InSpecialJump(anim))
{
return;
}
if (BG_InRoll(pm->ps, pm->ps->legsAnim) &&
pm->ps->legsTimer > 0 &&
!BG_InRoll(pm->ps, anim))
{
return;
}
pm->ps->legsTimer = 0;
PM_StartLegsAnim( anim );
}
/*
===================
TORSO Animations
Override animations for upper body
===================
*/
void PM_StartTorsoAnim( int anim ) {
if ( pm->ps->pm_type >= PM_DEAD ) {
return;
}
if (pm->ps->usingATST)
{ //animation is handled mostly client-side with only a few exceptions
return;
}
if (BG_InSaberStandAnim(anim) && pm->ps->weapon == WP_SABER && pm->ps->dualBlade)
{ //a bit of a hack, but dualblade is cheat-only anyway
anim = BOTH_STAND1;
}
pm->ps->torsoAnim = ( ( pm->ps->torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT )
| anim;
}
/*
-------------------------
PM_SetLegsAnimTimer
-------------------------
*/
void PM_SetLegsAnimTimer(int time )
{
pm->ps->legsTimer = time;
if (pm->ps->legsTimer < 0 && time != -1 )
{//Cap timer to 0 if was counting down, but let it be -1 if that was intentional. NOTENOTE Yeah this seems dumb, but it mirrors SP.
pm->ps->legsTimer = 0;
}
}
/*
-------------------------
PM_SetTorsoAnimTimer
-------------------------
*/
void PM_SetTorsoAnimTimer(int time )
{
pm->ps->torsoTimer = time;
if (pm->ps->torsoTimer < 0 && time != -1 )
{//Cap timer to 0 if was counting down, but let it be -1 if that was intentional. NOTENOTE Yeah this seems dumb, but it mirrors SP.
pm->ps->torsoTimer = 0;
}
}
void BG_SaberStartTransAnim( int saberAnimLevel, int anim, float *animSpeed )
{
if ( ( (anim&~ANIM_TOGGLEBIT) >= BOTH_T1_BR__R &&
(anim&~ANIM_TOGGLEBIT) <= BOTH_T1_BL_TL ) ||
( (anim&~ANIM_TOGGLEBIT) >= BOTH_T2_BR__R &&
(anim&~ANIM_TOGGLEBIT) <= BOTH_T2_BL_TL ) ||
( (anim&~ANIM_TOGGLEBIT) >= BOTH_T3_BR__R &&
(anim&~ANIM_TOGGLEBIT) <= BOTH_T3_BL_TL ) )
{
if ( saberAnimLevel == FORCE_LEVEL_1 )
{
*animSpeed *= 1.5;
}
else if ( saberAnimLevel == FORCE_LEVEL_3 )
{
*animSpeed *= 0.75;
}
}
}
/*
-------------------------
PM_SetAnimFinal
-------------------------
*/
void PM_SetAnimFinal(int setAnimParts,int anim,int setAnimFlags,
int blendTime) // default blendTime=350
{
animation_t *animations = pm->animations;
float editAnimSpeed = 1;
if (!animations)
{
return;
}
//NOTE: Setting blendTime here breaks actual blending..
blendTime = 0;
BG_SaberStartTransAnim(pm->ps->fd.saberAnimLevel, anim, &editAnimSpeed);
// Set torso anim
if (setAnimParts & SETANIM_TORSO)
{
// Don't reset if it's already running the anim
if( !(setAnimFlags & SETANIM_FLAG_RESTART) && (pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) == anim )
{
goto setAnimLegs;
}
// or if a more important anim is running
if( !(setAnimFlags & SETANIM_FLAG_OVERRIDE) && ((pm->ps->torsoTimer > 0)||(pm->ps->torsoTimer == -1)) )
{
goto setAnimLegs;
}
PM_StartTorsoAnim( anim );
if (setAnimFlags & SETANIM_FLAG_HOLD)
{
if (setAnimFlags & SETANIM_FLAG_HOLDLESS)
{ // Make sure to only wait in full 1/20 sec server frame intervals.
int dur;
int speedDif;
dur = (animations[anim].numFrames-1) * fabs(animations[anim].frameLerp);
speedDif = dur - (dur * editAnimSpeed);
dur += speedDif;
if (dur > 1)
{
pm->ps->torsoTimer = dur-1;
}
else
{
pm->ps->torsoTimer = fabs(animations[anim].frameLerp);
}
}
else
{
pm->ps->torsoTimer = ((animations[anim].numFrames ) * fabs(animations[anim].frameLerp));
}
if (pm->ps->fd.forcePowersActive & (1 << FP_RAGE))
{
pm->ps->torsoTimer /= 1.7;
}
}
}
setAnimLegs:
// Set legs anim
if (setAnimParts & SETANIM_LEGS)
{
// Don't reset if it's already running the anim
if( !(setAnimFlags & SETANIM_FLAG_RESTART) && (pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) == anim )
{
goto setAnimDone;
}
// or if a more important anim is running
if( !(setAnimFlags & SETANIM_FLAG_OVERRIDE) && ((pm->ps->legsTimer > 0)||(pm->ps->legsTimer == -1)) )
{
goto setAnimDone;
}
PM_StartLegsAnim(anim);
if (setAnimFlags & SETANIM_FLAG_HOLD)
{
if (setAnimFlags & SETANIM_FLAG_HOLDLESS)
{ // Make sure to only wait in full 1/20 sec server frame intervals.
int dur;
int speedDif;
dur = (animations[anim].numFrames-1) * fabs(animations[anim].frameLerp);
speedDif = dur - (dur * editAnimSpeed);
dur += speedDif;
if (dur > 1)
{
pm->ps->legsTimer = dur-1;
}
else
{
pm->ps->legsTimer = fabs(animations[anim].frameLerp);
}
}
else
{
pm->ps->legsTimer = ((animations[anim].numFrames ) * fabs(animations[anim].frameLerp));
}
if (pm->ps->fd.forcePowersActive & (1 << FP_RAGE))
{
pm->ps->legsTimer /= 1.3;
}
else if (pm->ps->fd.forcePowersActive & (1 << FP_SPEED))
{
pm->ps->legsTimer /= 1.7;
}
}
}
setAnimDone:
return;
}
// Imported from single-player, this function is mainly intended to make porting from SP easier.
void PM_SetAnim(int setAnimParts,int anim,int setAnimFlags, int blendTime)
{
assert( bgGlobalAnimations[anim].firstFrame != 0 ||
bgGlobalAnimations[anim].numFrames != 0);
if (BG_InSpecialJump(anim))
{
setAnimFlags |= SETANIM_FLAG_RESTART;
}
if (BG_InRoll(pm->ps, pm->ps->legsAnim))
{ //never interrupt a roll
return;
}
if (setAnimFlags&SETANIM_FLAG_OVERRIDE)
{
if (setAnimParts & SETANIM_TORSO)
{
if( (setAnimFlags & SETANIM_FLAG_RESTART) || (pm->ps->torsoAnim & ~ANIM_TOGGLEBIT ) != anim )
{
PM_SetTorsoAnimTimer(0);
}
}
if (setAnimParts & SETANIM_LEGS)
{
if( (setAnimFlags & SETANIM_FLAG_RESTART) || (pm->ps->legsAnim & ~ANIM_TOGGLEBIT ) != anim )
{
PM_SetLegsAnimTimer(0);
}
}
}
PM_SetAnimFinal(setAnimParts, anim, setAnimFlags, blendTime);
}