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

2007 lines
61 KiB
C

#include "q_shared.h"
#include "bg_public.h"
#include "bg_local.h"
int PM_irand_timesync(int val1, int val2)
{
int i;
i = (val1-1) + (Q_random( &pm->cmd.serverTime )*(val2 - val1)) + 1;
if (i < val1)
{
i = val1;
}
if (i > val2)
{
i = val2;
}
return i;
}
void BG_ForcePowerDrain( playerState_t *ps, forcePowers_t forcePower, int overrideAmt )
{
//take away the power
int drain = overrideAmt;
/*
if (ps->powerups[PW_FORCE_BOON])
{
return;
}
*/
//No longer grant infinite force with boon.
if ( !drain )
{
drain = forcePowerNeeded[ps->fd.forcePowerLevel[forcePower]][forcePower];
}
if ( !drain )
{
return;
}
if (forcePower == FP_LEVITATION)
{ //special case
int jumpDrain = 0;
if (ps->velocity[2] > 250)
{
jumpDrain = 20;
}
else if (ps->velocity[2] > 200)
{
jumpDrain = 16;
}
else if (ps->velocity[2] > 150)
{
jumpDrain = 12;
}
else if (ps->velocity[2] > 100)
{
jumpDrain = 8;
}
else if (ps->velocity[2] > 50)
{
jumpDrain = 6;
}
else if (ps->velocity[2] > 0)
{
jumpDrain = 4;
}
if (jumpDrain)
{
jumpDrain /= ps->fd.forcePowerLevel[FP_LEVITATION];
}
ps->fd.forcePower -= jumpDrain;
if ( ps->fd.forcePower < 0 )
{
ps->fd.forcePower = 0;
}
return;
}
ps->fd.forcePower -= drain;
if ( ps->fd.forcePower < 0 )
{
ps->fd.forcePower = 0;
}
}
// Silly, but I'm replacing these macros so they are shorter!
#define AFLAG_IDLE (SETANIM_FLAG_NORMAL)
#define AFLAG_ACTIVE (/*SETANIM_FLAG_OVERRIDE | */SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS)
#define AFLAG_WAIT (SETANIM_FLAG_HOLD | SETANIM_FLAG_HOLDLESS)
#define AFLAG_FINISH (SETANIM_FLAG_HOLD)
saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized
// name anim startQ endQ setanimflag blend, blocking chain_idle chain_attack trailLen
{"None", BOTH_STAND1, Q_R, Q_R, AFLAG_IDLE, 350, BLK_NO, LS_NONE, LS_NONE, 0 }, // LS_NONE = 0,
// General movements with saber
{"Ready", BOTH_STAND2, Q_R, Q_R, AFLAG_IDLE, 350, BLK_WIDE, LS_READY, LS_S_R2L, 0 }, // LS_READY,
{"Draw", BOTH_STAND1TO2, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_DRAW,
{"Putaway", BOTH_STAND2TO1, Q_R, Q_R, AFLAG_FINISH, 350, BLK_NO, LS_READY, LS_S_R2L, 0 }, // LS_PUTAWAY,
// Attacks
//UL2LR
{"TL2BR Att", BOTH_A1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TL2BR, LS_R_TL2BR, 200 }, // LS_A_TL2BR
//SLASH LEFT
{"L2R Att", BOTH_A1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_L2R, LS_R_L2R, 200 }, // LS_A_L2R
//LL2UR
{"BL2TR Att", BOTH_A1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_TIGHT, LS_R_BL2TR, LS_R_BL2TR, 200 }, // LS_A_BL2TR
//LR2UL
{"BR2TL Att", BOTH_A1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_BR2TL, LS_R_BR2TL, 200 }, // LS_A_BR2TL
//SLASH RIGHT
{"R2L Att", BOTH_A1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_R2L, LS_R_R2L, 200 },// LS_A_R2L
//UR2LL
{"TR2BL Att", BOTH_A1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_TR2BL, LS_R_TR2BL, 200 }, // LS_A_TR2BL
//SLASH DOWN
{"T2B Att", BOTH_A1_T__B_, Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_R_T2B, LS_R_T2B, 200 }, // LS_A_T2B
//special attacks
{"Back Stab", BOTH_A2_STABBACK1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACKSTAB
{"Back Att", BOTH_ATTACK_BACK, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK
{"CR Back Att", BOTH_CROUCHATTACKBACK1,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_BACK_CR
{"Lunge Att", BOTH_LUNGE2_B__T_, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_LUNGE
{"Jump Att", BOTH_FORCELEAP2_T__B_,Q_T, Q_B, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_A_JUMP_T__B_
{"Flip Stab", BOTH_JUMPFLIPSTABDOWN,Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_FLIP_STAB
{"Flip Slash", BOTH_JUMPFLIPSLASHDOWN1,Q_L,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R_T_, 200 }, // LS_A_FLIP_SLASH
//starts
{"TL2BR St", BOTH_S1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TL2BR, LS_A_TL2BR, 200 }, // LS_S_TL2BR
{"L2R St", BOTH_S1_S1__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_L2R, LS_A_L2R, 200 }, // LS_S_L2R
{"BL2TR St", BOTH_S1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BL2TR, LS_A_BL2TR, 200 }, // LS_S_BL2TR
{"BR2TL St", BOTH_S1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_BR2TL, LS_A_BR2TL, 200 }, // LS_S_BR2TL
{"R2L St", BOTH_S1_S1__R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_R2L, LS_A_R2L, 200 }, // LS_S_R2L
{"TR2BL St", BOTH_S1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_TR2BL, LS_A_TR2BL, 200 }, // LS_S_TR2BL
{"T2B St", BOTH_S1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_A_T2B, LS_A_T2B, 200 }, // LS_S_T2B
//returns
{"TL2BR Ret", BOTH_R1_BR_S1, Q_BR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TL2BR
{"L2R Ret", BOTH_R1__R_S1, Q_R, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_L2R
{"BL2TR Ret", BOTH_R1_TR_S1, Q_TR, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BL2TR
{"BR2TL Ret", BOTH_R1_TL_S1, Q_TL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_BR2TL
{"R2L Ret", BOTH_R1__L_S1, Q_L, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_R2L
{"TR2BL Ret", BOTH_R1_BL_S1, Q_BL, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_TR2BL
{"T2B Ret", BOTH_R1_B__S1, Q_B, Q_R, AFLAG_FINISH, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_R_T2B
//Transitions
{"BR2R Trans", BOTH_T1_BR__R, Q_BR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc bottom right to right
{"BR2TR Trans", BOTH_T1_BR_TR, Q_BR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc bottom right to top right (use: BOTH_T1_TR_BR)
{"BR2T Trans", BOTH_T1_BR_T_, Q_BR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom right to top (use: BOTH_T1_T__BR)
{"BR2TL Trans", BOTH_T1_BR_TL, Q_BR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast weak spin bottom right to top left
{"BR2L Trans", BOTH_T1_BR__L, Q_BR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin bottom right to left
{"BR2BL Trans", BOTH_T1_BR_BL, Q_BR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin bottom right to bottom left
{"R2BR Trans", BOTH_T1__R_BR, Q_R, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc right to bottom right (use: BOTH_T1_BR__R)
{"R2TR Trans", BOTH_T1__R_TR, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc right to top right
{"R2T Trans", BOTH_T1__R_T_, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast ar right to top (use: BOTH_T1_T___R)
{"R2TL Trans", BOTH_T1__R_TL, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc right to top left
{"R2L Trans", BOTH_T1__R__L, Q_R, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast weak spin right to left
{"R2BL Trans", BOTH_T1__R_BL, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin right to bottom left
{"TR2BR Trans", BOTH_T1_TR_BR, Q_TR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top right to bottom right
{"TR2R Trans", BOTH_T1_TR__R, Q_TR, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top right to right (use: BOTH_T1__R_TR)
{"TR2T Trans", BOTH_T1_TR_T_, Q_TR, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top right to top (use: BOTH_T1_T__TR)
{"TR2TL Trans", BOTH_T1_TR_TL, Q_TR, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top right to top left
{"TR2L Trans", BOTH_T1_TR__L, Q_TR, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top right to left
{"TR2BL Trans", BOTH_T1_TR_BL, Q_TR, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast weak spin top right to bottom left
{"T2BR Trans", BOTH_T1_T__BR, Q_T, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast arc top to bottom right
{"T2R Trans", BOTH_T1_T___R, Q_T, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top to right
{"T2TR Trans", BOTH_T1_T__TR, Q_T, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top to top right
{"T2TL Trans", BOTH_T1_T__TL, Q_T, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc top to top left
{"T2L Trans", BOTH_T1_T___L, Q_T, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top to left
{"T2BL Trans", BOTH_T1_T__BL, Q_T, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top to bottom left
{"TL2BR Trans", BOTH_T1_TL_BR, Q_TL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin top left to bottom right
{"TL2R Trans", BOTH_T1_TL__R, Q_TL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast arc top left to right (use: BOTH_T1__R_TL)
{"TL2TR Trans", BOTH_T1_TL_TR, Q_TL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc top left to top right (use: BOTH_T1_TR_TL)
{"TL2T Trans", BOTH_T1_TL_T_, Q_TL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc top left to top (use: BOTH_T1_T__TL)
{"TL2L Trans", BOTH_T1_TL__L, Q_TL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc top left to left (use: BOTH_T1__L_TL)
{"TL2BL Trans", BOTH_T1_TL_BL, Q_TL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc top left to bottom left
{"L2BR Trans", BOTH_T1__L_BR, Q_L, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin left to bottom right
{"L2R Trans", BOTH_T1__L__R, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin left to right
{"L2TR Trans", BOTH_T1__L_TR, Q_L, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast arc left to top right (use: BOTH_T1_TR__L)
{"L2T Trans", BOTH_T1__L_T_, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc left to top (use: BOTH_T1_T___L)
{"L2TL Trans", BOTH_T1__L_TL, Q_L, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc left to top left
{"L2BL Trans", BOTH_T1__L_BL, Q_L, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_A_BL2TR, 150 }, //# Fast arc left to bottom left (use: BOTH_T1_BL__L)
{"BL2BR Trans", BOTH_T1_BL_BR, Q_BL, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_A_BR2TL, 150 }, //# Fast weak spin bottom left to bottom right
{"BL2R Trans", BOTH_T1_BL__R, Q_BL, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_A_R2L, 150 }, //# Fast weak spin bottom left to right
{"BL2TR Trans", BOTH_T1_BL_TR, Q_BL, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_TR2BL, 150 }, //# Fast weak spin bottom left to top right
{"BL2T Trans", BOTH_T1_BL_T_, Q_BL, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_A_T2B, 150 }, //# Fast arc bottom left to top (use: BOTH_T1_T__BL)
{"BL2TL Trans", BOTH_T1_BL_TL, Q_BL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_A_TL2BR, 150 }, //# Fast arc bottom left to top left (use: BOTH_T1_TL_BL)
{"BL2L Trans", BOTH_T1_BL__L, Q_BL, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_A_L2R, 150 }, //# Fast arc bottom left to left
//Bounces
{"Bounce BR", BOTH_B1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 },
{"Bounce R", BOTH_B1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 },
{"Bounce TR", BOTH_B1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 },
{"Bounce T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 },
{"Bounce TL", BOTH_B1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 },
{"Bounce L", BOTH_B1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 },
{"Bounce BL", BOTH_B1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 },
//Deflected attacks (like bounces, but slide off enemy saber, not straight back)
{"Deflect BR", BOTH_D1_BR___, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TL2BR, LS_T1_BR_TR, 150 },
{"Deflect R", BOTH_D1__R___, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_R_L2R, LS_T1__R__L, 150 },
{"Deflect TR", BOTH_D1_TR___, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_TR_TL, 150 },
{"Deflect T", BOTH_B1_T____, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 },
{"Deflect TL", BOTH_D1_TL___, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BR2TL, LS_T1_TL_TR, 150 },
{"Deflect L", BOTH_D1__L___, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_R_R2L, LS_T1__L__R, 150 },
{"Deflect BL", BOTH_D1_BL___, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_R_TR2BL, LS_T1_BL_TR, 150 },
{"Deflect B", BOTH_D1_B____, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_R_BL2TR, LS_T1_T__BL, 150 },
//Reflected attacks
{"Reflected BR",BOTH_V1_BR_S1, Q_BR, Q_BR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BR
{"Reflected R", BOTH_V1__R_S1, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__R
{"Reflected TR",BOTH_V1_TR_S1, Q_TR, Q_TR, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TR
{"Reflected T", BOTH_V1_T__S1, Q_T, Q_T, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_T_
{"Reflected TL",BOTH_V1_TL_S1, Q_TL, Q_TL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_TL
{"Reflected L", BOTH_V1__L_S1, Q_L, Q_L, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1__L
{"Reflected BL",BOTH_V1_BL_S1, Q_BL, Q_BL, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_BL
{"Reflected B", BOTH_V1_B__S1, Q_B, Q_B, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_READY, 150 },// LS_V1_B_
// Broken parries
{"BParry Top", BOTH_H1_S1_T_, Q_T, Q_B, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UP,
{"BParry UR", BOTH_H1_S1_TR, Q_TR, Q_BL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UR,
{"BParry UL", BOTH_H1_S1_TL, Q_TL, Q_BR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_UL,
{"BParry LR", BOTH_H1_S1_BL, Q_BL, Q_TR, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LR,
{"BParry Bot", BOTH_H1_S1_B_, Q_B, Q_T, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL
{"BParry LL", BOTH_H1_S1_BR, Q_BR, Q_TL, AFLAG_ACTIVE, 50, BLK_NO, LS_READY, LS_READY, 150 }, // LS_PARRY_LL
// Knockaways
{"Knock Top", BOTH_K1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_T__BR, 150 }, // LS_PARRY_UP,
{"Knock UR", BOTH_K1_S1_TR, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_T1_TR__R, 150 }, // LS_PARRY_UR,
{"Knock UL", BOTH_K1_S1_TL, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_T1_TL__L, 150 }, // LS_PARRY_UL,
{"Knock LR", BOTH_K1_S1_BL, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_T1_BL_TL, 150 }, // LS_PARRY_LR,
{"Knock LL", BOTH_K1_S1_BR, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_T1_BR_TR, 150 }, // LS_PARRY_LL
// Parry
{"Parry Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 150 }, // LS_PARRY_UP,
{"Parry UR", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 150 }, // LS_PARRY_UR,
{"Parry UL", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 150 }, // LS_PARRY_UL,
{"Parry LR", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 150 }, // LS_PARRY_LR,
{"Parry LL", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 150 }, // LS_PARRY_LL
// Reflecting a missile
{"Reflect Top", BOTH_P1_S1_T_, Q_R, Q_T, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_T2B, 300 }, // LS_PARRY_UP,
{"Reflect UR", BOTH_P1_S1_TL, Q_R, Q_TR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BR2TL, LS_A_TL2BR, 300 }, // LS_PARRY_UR,
{"Reflect UL", BOTH_P1_S1_TR, Q_R, Q_TL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_BL2TR, LS_A_TR2BL, 300 }, // LS_PARRY_UL,
{"Reflect LR", BOTH_P1_S1_BR, Q_R, Q_BL, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TR2BL, LS_A_BL2TR, 300 }, // LS_PARRY_LR
{"Reflect LL", BOTH_P1_S1_BL, Q_R, Q_BR, AFLAG_ACTIVE, 50, BLK_WIDE, LS_R_TL2BR, LS_A_BR2TL, 300 }, // LS_PARRY_LL,
};
int transitionMove[Q_NUM_QUADS][Q_NUM_QUADS] =
{
LS_NONE, //Can't transition to same pos!
LS_T1_BR__R,//40
LS_T1_BR_TR,
LS_T1_BR_T_,
LS_T1_BR_TL,
LS_T1_BR__L,
LS_T1_BR_BL,
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1__R_BR,//46
LS_NONE, //Can't transition to same pos!
LS_T1__R_TR,
LS_T1__R_T_,
LS_T1__R_TL,
LS_T1__R__L,
LS_T1__R_BL,
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1_TR_BR,//52
LS_T1_TR__R,
LS_NONE, //Can't transition to same pos!
LS_T1_TR_T_,
LS_T1_TR_TL,
LS_T1_TR__L,
LS_T1_TR_BL,
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1_T__BR,//58
LS_T1_T___R,
LS_T1_T__TR,
LS_NONE, //Can't transition to same pos!
LS_T1_T__TL,
LS_T1_T___L,
LS_T1_T__BL,
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1_TL_BR,//64
LS_T1_TL__R,
LS_T1_TL_TR,
LS_T1_TL_T_,
LS_NONE, //Can't transition to same pos!
LS_T1_TL__L,
LS_T1_TL_BL,
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1__L_BR,//70
LS_T1__L__R,
LS_T1__L_TR,
LS_T1__L_T_,
LS_T1__L_TL,
LS_NONE, //Can't transition to same pos!
LS_T1__L_BL,
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1_BL_BR,//76
LS_T1_BL__R,
LS_T1_BL_TR,
LS_T1_BL_T_,
LS_T1_BL_TL,
LS_T1_BL__L,
LS_NONE, //Can't transition to same pos!
LS_NONE, //No transitions to bottom, and no anims start there, so shouldn't need any
LS_T1_BL_BR,//NOTE: there are no transitions from bottom, so re-use the bottom right transitions
LS_T1_BR__R,
LS_T1_BR_TR,
LS_T1_BR_T_,
LS_T1_BR_TL,
LS_T1_BR__L,
LS_T1_BR_BL,
LS_NONE //No transitions to bottom, and no anims start there, so shouldn't need any
};
saberMoveName_t PM_AttackMoveForQuad( int quad )
{
switch ( quad )
{
case Q_B:
case Q_BR:
return LS_A_BR2TL;
break;
case Q_R:
return LS_A_R2L;
break;
case Q_TR:
return LS_A_TR2BL;
break;
case Q_T:
return LS_A_T2B;
break;
case Q_TL:
return LS_A_TL2BR;
break;
case Q_L:
return LS_A_L2R;
break;
case Q_BL:
return LS_A_BL2TR;
break;
}
return LS_NONE;
}
qboolean PM_SaberKataDone(int curmove, int newmove);
int PM_SaberAnimTransitionAnim( int curmove, int newmove )
{
int retmove = newmove;
if ( curmove == LS_READY )
{//just standing there
switch ( newmove )
{
case LS_A_TL2BR:
case LS_A_L2R:
case LS_A_BL2TR:
case LS_A_BR2TL:
case LS_A_R2L:
case LS_A_TR2BL:
case LS_A_T2B:
//transition is the start
retmove = LS_S_TL2BR + (newmove-LS_A_TL2BR);
break;
}
}
else
{
switch ( newmove )
{
//transitioning to ready pose
case LS_READY:
switch ( curmove )
{
//transitioning from an attack
case LS_A_TL2BR:
case LS_A_L2R:
case LS_A_BL2TR:
case LS_A_BR2TL:
case LS_A_R2L:
case LS_A_TR2BL:
case LS_A_T2B:
//transition is the return
retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR);
break;
}
break;
//transitioning to an attack
case LS_A_TL2BR:
case LS_A_L2R:
case LS_A_BL2TR:
case LS_A_BR2TL:
case LS_A_R2L:
case LS_A_TR2BL:
case LS_A_T2B:
if ( newmove == curmove )
{
//going into an attack
if ( PM_SaberKataDone( curmove, newmove ) )
{//done with this kata, must return to ready before attack again
retmove = LS_R_TL2BR + (newmove-LS_A_TL2BR);
}
else
{//okay to chain to another attack
retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad];
}
}
else if ( saberMoveData[curmove].endQuad == saberMoveData[newmove].startQuad )
{//new move starts from same quadrant
retmove = newmove;
}
else
{
switch ( curmove )
{
//transitioning from an attack
case LS_A_TL2BR:
case LS_A_L2R:
case LS_A_BL2TR:
case LS_A_BR2TL:
case LS_A_R2L:
case LS_A_TR2BL:
case LS_A_T2B:
case LS_D1_BR:
case LS_D1__R:
case LS_D1_TR:
case LS_D1_T_:
case LS_D1_TL:
case LS_D1__L:
case LS_D1_BL:
case LS_D1_B_:
retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad];
break;
//transitioning from a return
case LS_R_TL2BR:
case LS_R_L2R:
case LS_R_BL2TR:
case LS_R_BR2TL:
case LS_R_R2L:
case LS_R_TR2BL:
case LS_R_T2B:
//transitioning from a bounce
/*
case LS_BOUNCE_UL2LL:
case LS_BOUNCE_LL2UL:
case LS_BOUNCE_L2LL:
case LS_BOUNCE_L2UL:
case LS_BOUNCE_UR2LR:
case LS_BOUNCE_LR2UR:
case LS_BOUNCE_R2LR:
case LS_BOUNCE_R2UR:
case LS_BOUNCE_TOP:
case LS_OVER_UR2UL:
case LS_OVER_UL2UR:
case LS_BOUNCE_UR:
case LS_BOUNCE_UL:
case LS_BOUNCE_LR:
case LS_BOUNCE_LL:
*/
//transitioning from a parry/reflection/knockaway/broken parry
case LS_PARRY_UP:
case LS_PARRY_UR:
case LS_PARRY_UL:
case LS_PARRY_LR:
case LS_PARRY_LL:
case LS_REFLECT_UP:
case LS_REFLECT_UR:
case LS_REFLECT_UL:
case LS_REFLECT_LR:
case LS_REFLECT_LL:
case LS_K1_T_:
case LS_K1_TR:
case LS_K1_TL:
case LS_K1_BR:
case LS_K1_BL:
case LS_V1_BR:
case LS_V1__R:
case LS_V1_TR:
case LS_V1_T_:
case LS_V1_TL:
case LS_V1__L:
case LS_V1_BL:
case LS_V1_B_:
case LS_H1_T_:
case LS_H1_TR:
case LS_H1_TL:
case LS_H1_BR:
case LS_H1_BL:
retmove = transitionMove[saberMoveData[curmove].endQuad][saberMoveData[newmove].startQuad];
break;
//NB: transitioning from transitions is fine
}
}
break;
//transitioning to any other anim is not supported
}
}
if ( retmove == LS_NONE )
{
return newmove;
}
return retmove;
}
int PM_SaberMoveQuadrantForMovement( usercmd_t *ucmd )
{
if ( ucmd->rightmove > 0 )
{//moving right
if ( ucmd->forwardmove > 0 )
{//forward right = TL2BR slash
return Q_TL;
}
else if ( ucmd->forwardmove < 0 )
{//backward right = BL2TR uppercut
return Q_BL;
}
else
{//just right is a left slice
return Q_L;
}
}
else if ( ucmd->rightmove < 0 )
{//moving left
if ( ucmd->forwardmove > 0 )
{//forward left = TR2BL slash
return Q_TR;
}
else if ( ucmd->forwardmove < 0 )
{//backward left = BR2TL uppercut
return Q_BR;
}
else
{//just left is a right slice
return Q_R;
}
}
else
{//not moving left or right
if ( ucmd->forwardmove > 0 )
{//forward= T2B slash
return Q_T;
}
else if ( ucmd->forwardmove < 0 )
{//backward= T2B slash //or B2T uppercut?
return Q_T;
}
else
{//Not moving at all
return Q_R;
}
}
}
//===================================================================
qboolean PM_SaberInBounce( int move )
{
if ( move >= LS_B1_BR && move <= LS_B1_BL )
{
return qtrue;
}
if ( move >= LS_D1_BR && move <= LS_D1_BL )
{
return qtrue;
}
return qfalse;
}
qboolean PM_SaberInTransition( int move )
{
if ( move >= LS_T1_BR__R && move <= LS_T1_BL__L )
{
return qtrue;
}
return qfalse;
}
int saberMoveTransitionAngle[Q_NUM_QUADS][Q_NUM_QUADS] =
{
0,//Q_BR,Q_BR,
45,//Q_BR,Q_R,
90,//Q_BR,Q_TR,
135,//Q_BR,Q_T,
180,//Q_BR,Q_TL,
215,//Q_BR,Q_L,
270,//Q_BR,Q_BL,
45,//Q_BR,Q_B,
45,//Q_R,Q_BR,
0,//Q_R,Q_R,
45,//Q_R,Q_TR,
90,//Q_R,Q_T,
135,//Q_R,Q_TL,
180,//Q_R,Q_L,
215,//Q_R,Q_BL,
90,//Q_R,Q_B,
90,//Q_TR,Q_BR,
45,//Q_TR,Q_R,
0,//Q_TR,Q_TR,
45,//Q_TR,Q_T,
90,//Q_TR,Q_TL,
135,//Q_TR,Q_L,
180,//Q_TR,Q_BL,
135,//Q_TR,Q_B,
135,//Q_T,Q_BR,
90,//Q_T,Q_R,
45,//Q_T,Q_TR,
0,//Q_T,Q_T,
45,//Q_T,Q_TL,
90,//Q_T,Q_L,
135,//Q_T,Q_BL,
180,//Q_T,Q_B,
180,//Q_TL,Q_BR,
135,//Q_TL,Q_R,
90,//Q_TL,Q_TR,
45,//Q_TL,Q_T,
0,//Q_TL,Q_TL,
45,//Q_TL,Q_L,
90,//Q_TL,Q_BL,
135,//Q_TL,Q_B,
215,//Q_L,Q_BR,
180,//Q_L,Q_R,
135,//Q_L,Q_TR,
90,//Q_L,Q_T,
45,//Q_L,Q_TL,
0,//Q_L,Q_L,
45,//Q_L,Q_BL,
90,//Q_L,Q_B,
270,//Q_BL,Q_BR,
215,//Q_BL,Q_R,
180,//Q_BL,Q_TR,
135,//Q_BL,Q_T,
90,//Q_BL,Q_TL,
45,//Q_BL,Q_L,
0,//Q_BL,Q_BL,
45,//Q_BL,Q_B,
45,//Q_B,Q_BR,
90,//Q_B,Q_R,
135,//Q_B,Q_TR,
180,//Q_B,Q_T,
135,//Q_B,Q_TL,
90,//Q_B,Q_L,
45,//Q_B,Q_BL,
0//Q_B,Q_B,
};
int PM_SaberAttackChainAngle( int move1, int move2 )
{
if ( move1 == -1 || move2 == -1 )
{
return -1;
}
return saberMoveTransitionAngle[saberMoveData[move1].endQuad][saberMoveData[move2].startQuad];
}
qboolean PM_SaberKataDone(int curmove, int newmove)
{
if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_3 )
{
if ( curmove == LS_NONE || newmove == LS_NONE )
{
if ( pm->ps->fd.saberAnimLevel >= FORCE_LEVEL_3 && pm->ps->saberAttackChainCount > PM_irand_timesync( 0, 1 ) )
{
return qtrue;
}
}
else if ( pm->ps->saberAttackChainCount > PM_irand_timesync( 2, 3 ) )
{
return qtrue;
}
else if ( pm->ps->saberAttackChainCount > 0 )
{
int chainAngle = PM_SaberAttackChainAngle( curmove, newmove );
if ( chainAngle < 135 || chainAngle > 215 )
{//if trying to chain to a move that doesn't continue the momentum
return qtrue;
}
else if ( chainAngle == 180 )
{//continues the momentum perfectly, allow it to chain 66% of the time
if ( pm->ps->saberAttackChainCount > 1 )
{
return qtrue;
}
}
else
{//would continue the movement somewhat, 50% chance of continuing
if ( pm->ps->saberAttackChainCount > 2 )
{
return qtrue;
}
}
}
}
else
{//Perhaps have chainAngle influence fast and medium chains as well? For now, just do level 3.
if (newmove == LS_A_TL2BR ||
newmove == LS_A_L2R ||
newmove == LS_A_BL2TR ||
newmove == LS_A_BR2TL ||
newmove == LS_A_R2L ||
newmove == LS_A_TR2BL )
{ //lower chaining tolerance for spinning saber anims
int chainTolerance;
if (pm->ps->fd.saberAnimLevel == FORCE_LEVEL_1)
{
chainTolerance = 5;
}
else
{
chainTolerance = 3;
}
if (pm->ps->saberAttackChainCount >= chainTolerance && PM_irand_timesync(1, pm->ps->saberAttackChainCount) > chainTolerance)
{
return qtrue;
}
}
if ( pm->ps->fd.saberAnimLevel == FORCE_LEVEL_2 && pm->ps->saberAttackChainCount > PM_irand_timesync( 2, 5 ) )
{
return qtrue;
}
}
return qfalse;
}
void PM_SetAnimFrame( playerState_t *gent, int frame, qboolean torso, qboolean legs )
{
gent->saberLockFrame = frame;
}
void PM_SaberLockBreak( playerState_t *genemy, qboolean victory )
{
int winAnim = BOTH_STAND1, loseAnim = BOTH_STAND1;
qboolean punishLoser = qfalse;
switch ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) )
{
case BOTH_BF2LOCK:
pm->ps->saberMove = LS_A_T2B;
winAnim = BOTH_A3_T__B_;
if ( !victory )
{//no-one won
genemy->saberMove = LS_A_T2B;
loseAnim = winAnim;
}
else
{
punishLoser = qtrue;
}
break;
case BOTH_BF1LOCK:
pm->ps->saberMove = LS_K1_T_;
winAnim = BOTH_K1_S1_T_;
if ( !victory )
{//no-one won
genemy->saberMove = LS_K1_T_;
loseAnim = winAnim;
}
else
{
punishLoser = qtrue;
}
break;
case BOTH_CWCIRCLELOCK:
winAnim = BOTH_CWCIRCLEBREAK;
if ( !victory )
{//no-one won
loseAnim = winAnim;
}
else
{
genemy->saberMove = LS_H1_BL;
genemy->saberBlocked = BLOCKED_PARRY_BROKEN;
punishLoser = qtrue;
}
break;
case BOTH_CCWCIRCLELOCK:
winAnim = BOTH_CCWCIRCLEBREAK;
if ( !victory )
{//no-one won
loseAnim = winAnim;
}
else
{
genemy->saberMove = LS_H1_BR;
genemy->saberBlocked = BLOCKED_PARRY_BROKEN;
punishLoser = qtrue;
}
break;
}
PM_SetAnim( SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, -1 );
if (punishLoser)
{ //someone lost the lock, so punish them by knocking them down
vec3_t oppDir;
int strength = 8;
VectorSubtract(genemy->origin, pm->ps->origin, oppDir);
VectorNormalize(oppDir);
genemy->forceHandExtend = HANDEXTEND_KNOCKDOWN;
genemy->forceHandExtendTime = pm->cmd.serverTime + 1100;
genemy->forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
genemy->otherKiller = pm->ps->clientNum;
genemy->otherKillerTime = pm->cmd.serverTime + 5000;
genemy->otherKillerDebounceTime = pm->cmd.serverTime + 100;
genemy->velocity[0] = oppDir[0]*(strength*40);
genemy->velocity[1] = oppDir[1]*(strength*40);
genemy->velocity[2] = 100;
pm->checkDuelLoss = genemy->clientNum+1;
}
else
{ //If no one lost, then shove each player away from the other
vec3_t oppDir;
int strength = 4;
VectorSubtract(genemy->origin, pm->ps->origin, oppDir);
VectorNormalize(oppDir);
genemy->velocity[0] = oppDir[0]*(strength*40);
genemy->velocity[1] = oppDir[1]*(strength*40);
genemy->velocity[2] = 150;
VectorSubtract(pm->ps->origin, genemy->origin, oppDir);
VectorNormalize(oppDir);
pm->ps->velocity[0] = oppDir[0]*(strength*40);
pm->ps->velocity[1] = oppDir[1]*(strength*40);
pm->ps->velocity[2] = 150;
genemy->forceHandExtend = HANDEXTEND_WEAPONREADY;
}
pm->ps->weaponTime = 0;
genemy->weaponTime = 0;
pm->ps->saberLockTime = genemy->saberLockTime = 0;
pm->ps->saberLockFrame = genemy->saberLockFrame = 0;
pm->ps->saberLockEnemy = genemy->saberLockEnemy = 0;
pm->ps->forceHandExtend = HANDEXTEND_WEAPONREADY;
PM_AddEvent( EV_JUMP );
if ( !victory )
{//no-one won
BG_AddPredictableEventToPlayerstate(EV_JUMP, 0, genemy);
}
else
{
if ( PM_irand_timesync( 0, 1 ) )
{
BG_AddPredictableEventToPlayerstate(EV_JUMP, PM_irand_timesync( 0, 75 ), genemy);
}
}
}
extern qboolean ValidAnimFileIndex ( int index );
void PM_SaberLocked( void )
{
int remaining = 0;
playerState_t *genemy = pm->bgClients[pm->ps->saberLockEnemy];
if ( !genemy )
{
return;
}
if ( ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF2LOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF1LOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CWCIRCLELOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CCWCIRCLELOCK )
&& ( (genemy->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF2LOCK ||
(genemy->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF1LOCK ||
(genemy->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CWCIRCLELOCK ||
(genemy->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CCWCIRCLELOCK )
)
{
float dist = 0;
pm->ps->torsoTimer = 0;
pm->ps->weaponTime = 0;
genemy->torsoTimer = 0;
genemy->weaponTime = 0;
dist = DistanceSquared(pm->ps->origin,genemy->origin);
if ( dist < 64 || dist > 6400 )
{//between 8 and 80 from each other
PM_SaberLockBreak( genemy, qfalse );
return;
}
if ( (pm->cmd.buttons & BUTTON_ATTACK) || pm->ps->saberLockAdvance )
{//holding attack
animation_t *anim;
if (pm->ps->saberLockAdvance)
{//tapping
animation_t *anim;
float currentFrame;
int curFrame;
int strength = 1;
pm->ps->saberLockAdvance = qfalse;
anim = &pm->animations[pm->ps->torsoAnim&~ANIM_TOGGLEBIT];
currentFrame = pm->ps->saberLockFrame;
strength = pm->ps->fd.forcePowerLevel[FP_SABERATTACK]+1;
if ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CCWCIRCLELOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF2LOCK )
{
curFrame = floor( currentFrame )-strength;
//drop my frame one
if ( curFrame <= anim->firstFrame )
{//I won! Break out
PM_SaberLockBreak( genemy, qtrue );
return;
}
else
{
PM_SetAnimFrame( pm->ps, curFrame, qtrue, qtrue );
remaining = curFrame-anim->firstFrame;
}
}
else
{
curFrame = ceil( currentFrame )+strength;
//advance my frame one
if ( curFrame >= anim->firstFrame+anim->numFrames )
{//I won! Break out
PM_SaberLockBreak( genemy, qtrue );
return;
}
else
{
PM_SetAnimFrame( pm->ps, curFrame, qtrue, qtrue );
remaining = anim->firstFrame+anim->numFrames-curFrame;
}
}
if ( !PM_irand_timesync( 0, 2 ) )
{
PM_AddEvent( EV_JUMP );
}
}
else
{
return;
}
anim = &pm->animations[(genemy->torsoAnim&~ANIM_TOGGLEBIT)];
if ( (genemy->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CWCIRCLELOCK ||
(genemy->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF1LOCK )
{
if ( !PM_irand_timesync( 0, 2 ) )
{
BG_AddPredictableEventToPlayerstate(EV_PAIN, floor((float)80/100*100.0f), genemy);
}
PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue );
}
else
{
PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue );
}
}
}
else
{//something broke us out of it
PM_SaberLockBreak( genemy, qfalse );
}
}
qboolean PM_SaberInBrokenParry( int move )
{
if ( move >= LS_V1_BR && move <= LS_V1_B_ )
{
return qtrue;
}
if ( move >= LS_H1_T_ && move <= LS_H1_BL )
{
return qtrue;
}
return qfalse;
}
int PM_BrokenParryForParry( int move )
{
switch ( move )
{
case LS_PARRY_UP:
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_BL;
break;
case LS_PARRY_LL:
return LS_H1_BR;
break;
case LS_READY:
return LS_H1_B_;
break;
}
return LS_NONE;
}
#define BACK_STAB_DISTANCE 128
qboolean PM_CanBackstab(void)
{
trace_t tr;
vec3_t flatAng;
vec3_t fwd, back;
vec3_t trmins = {-15, -15, -8};
vec3_t trmaxs = {15, 15, 8};
VectorCopy(pm->ps->viewangles, flatAng);
flatAng[PITCH] = 0;
AngleVectors(flatAng, fwd, 0, 0);
back[0] = pm->ps->origin[0] - fwd[0]*BACK_STAB_DISTANCE;
back[1] = pm->ps->origin[1] - fwd[1]*BACK_STAB_DISTANCE;
back[2] = pm->ps->origin[2] - fwd[2]*BACK_STAB_DISTANCE;
pm->trace(&tr, pm->ps->origin, trmins, trmaxs, back, pm->ps->clientNum, MASK_PLAYERSOLID);
if (tr.fraction != 1.0 && tr.entityNum >= 0 && tr.entityNum < MAX_CLIENTS)
{ //We don't have real entity access here so we can't do an indepth check. But if it's a client and it's behind us, I guess that's reason enough to stab backward
return qtrue;
}
return qfalse;
}
saberMoveName_t PM_SaberFlipOverAttackMove(trace_t *tr)
{
vec3_t fwdAngles, jumpFwd;
float zDiff = 0;
playerState_t *psData;
VectorCopy( pm->ps->viewangles, fwdAngles );
fwdAngles[PITCH] = fwdAngles[ROLL] = 0;
AngleVectors( fwdAngles, jumpFwd, NULL, NULL );
VectorScale( jumpFwd, 50, pm->ps->velocity );
pm->ps->velocity[2] = 400;
psData = pm->bgClients[tr->entityNum];
//go higher for enemies higher than you, lower for those lower than you
if (psData)
{
zDiff = psData->origin[2] - pm->ps->origin[2];
}
else
{
zDiff = 0;
}
pm->ps->velocity[2] += (zDiff)*1.5f;
//clamp to decent-looking values
if ( zDiff <= 0 && pm->ps->velocity[2] < 200 )
{//if we're on same level, don't let me jump so low, I clip into the ground
pm->ps->velocity[2] = 200;
}
else if ( pm->ps->velocity[2] < 100 )
{
pm->ps->velocity[2] = 100;
}
else if ( pm->ps->velocity[2] > 400 )
{
pm->ps->velocity[2] = 400;
}
PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
PM_AddEvent( EV_JUMP );
pm->ps->fd.forceJumpSound = 1;
pm->cmd.upmove = 0;
if ( PM_irand_timesync( 0, 1 ) )
{
return LS_A_FLIP_STAB;
}
else
{
return LS_A_FLIP_SLASH;
}
}
#define FLIPHACK_DISTANCE 200
qboolean PM_SomeoneInFront(trace_t *tr)
{ //Also a very simplified version of the sp counterpart
vec3_t flatAng;
vec3_t fwd, back;
vec3_t trmins = {-15, -15, -8};
vec3_t trmaxs = {15, 15, 8};
VectorCopy(pm->ps->viewangles, flatAng);
flatAng[PITCH] = 0;
AngleVectors(flatAng, fwd, 0, 0);
back[0] = pm->ps->origin[0] + fwd[0]*FLIPHACK_DISTANCE;
back[1] = pm->ps->origin[1] + fwd[1]*FLIPHACK_DISTANCE;
back[2] = pm->ps->origin[2] + fwd[2]*FLIPHACK_DISTANCE;
pm->trace(tr, pm->ps->origin, trmins, trmaxs, back, pm->ps->clientNum, MASK_PLAYERSOLID);
if (tr->fraction != 1.0 && tr->entityNum >= 0 && tr->entityNum < MAX_CLIENTS)
{
return qtrue;
}
return qfalse;
}
saberMoveName_t PM_SaberLungeAttackMove( void )
{
vec3_t fwdAngles, jumpFwd;
VectorCopy( pm->ps->viewangles, fwdAngles );
fwdAngles[PITCH] = fwdAngles[ROLL] = 0;
//do the lunge
AngleVectors( fwdAngles, jumpFwd, NULL, NULL );
VectorScale( jumpFwd, 150, pm->ps->velocity );
PM_AddEvent( EV_JUMP );
return LS_A_LUNGE;
}
saberMoveName_t PM_SaberJumpAttackMove( void )
{
vec3_t fwdAngles, jumpFwd;
VectorCopy( pm->ps->viewangles, fwdAngles );
fwdAngles[PITCH] = fwdAngles[ROLL] = 0;
AngleVectors( fwdAngles, jumpFwd, NULL, NULL );
VectorScale( jumpFwd, 300, pm->ps->velocity );
pm->ps->velocity[2] = 280;
PM_SetForceJumpZStart(pm->ps->origin[2]);//so we don't take damage if we land at same height
PM_AddEvent( EV_JUMP );
pm->ps->fd.forceJumpSound = 1;
pm->cmd.upmove = 0;
return LS_A_JUMP_T__B_;
}
float PM_GroundDistance(void)
{
trace_t tr;
vec3_t down;
VectorCopy(pm->ps->origin, down);
down[2] -= 4096;
pm->trace(&tr, pm->ps->origin, pm->mins, pm->maxs, down, pm->ps->clientNum, MASK_SOLID);
VectorSubtract(pm->ps->origin, tr.endpos, down);
return VectorLength(down);
}
saberMoveName_t PM_SaberAttackForMovement(saberMoveName_t curmove)
{
saberMoveName_t newmove = -1;
if ( pm->cmd.rightmove > 0 )
{//moving right
if ( pm->cmd.forwardmove > 0 )
{//forward right = TL2BR slash
newmove = LS_A_TL2BR;
}
else if ( pm->cmd.forwardmove < 0 )
{//backward right = BL2TR uppercut
newmove = LS_A_BL2TR;
}
else
{//just right is a left slice
newmove = LS_A_L2R;
}
}
else if ( pm->cmd.rightmove < 0 )
{//moving left
if ( pm->cmd.forwardmove > 0 )
{//forward left = TR2BL slash
newmove = LS_A_TR2BL;
}
else if ( pm->cmd.forwardmove < 0 )
{//backward left = BR2TL uppercut
newmove = LS_A_BR2TL;
}
else
{//just left is a right slice
newmove = LS_A_R2L;
}
}
else
{//not moving left or right
if ( pm->cmd.forwardmove > 0 )
{//forward= T2B slash
if (pm->ps->fd.saberAnimLevel == FORCE_LEVEL_2 &&
pm->ps->velocity[2] > 100 &&
PM_GroundDistance() < 32 &&
!BG_InSpecialJump(pm->ps->legsAnim) &&
!BG_SaberInSpecialAttack(pm->ps->torsoAnim))
{ //FLIP AND DOWNWARD ATTACK
trace_t tr;
if (PM_SomeoneInFront(&tr))
{
newmove = PM_SaberFlipOverAttackMove(&tr);
}
}
else if (pm->ps->fd.saberAnimLevel == FORCE_LEVEL_1 &&
pm->ps->groundEntityNum != ENTITYNUM_NONE &&
(pm->ps->pm_flags & PMF_DUCKED) &&
pm->ps->weaponTime <= 0 &&
!BG_SaberInSpecialAttack(pm->ps->torsoAnim))
{ //LUNGE (weak)
newmove = PM_SaberLungeAttackMove();
}
else
{
newmove = LS_A_T2B;
}
}
else if ( pm->cmd.forwardmove < 0 )
{//backward= T2B slash//B2T uppercut?
if (PM_CanBackstab() && !BG_SaberInSpecialAttack(pm->ps->torsoAnim))
{ //BACKSTAB (attack varies by level)
if (pm->ps->fd.saberAnimLevel >= FORCE_LEVEL_2)
{//medium and higher attacks
if ( (pm->ps->pm_flags&PMF_DUCKED) || pm->cmd.upmove < 0 )
{
newmove = LS_A_BACK_CR;
}
else
{
newmove = LS_A_BACK;
}
}
else
{ //weak attack
newmove = LS_A_BACKSTAB;
}
}
else
{
newmove = LS_A_T2B;
}
}
else if ( PM_SaberInBounce( curmove ) )
{//bounces should go to their default attack if you don't specify a direction but are attacking
newmove = saberMoveData[curmove].chain_attack;
if ( PM_SaberKataDone(curmove, newmove) )
{
newmove = saberMoveData[curmove].chain_idle;
}
else
{
newmove = saberMoveData[curmove].chain_attack;
}
}
else if ( curmove == LS_READY )
{//Not moving at all, shouldn't have gotten here...?
//for now, just pick a random attack
//newmove = Q_irand( LS_A_TL2BR, LS_A_T2B );
//rww - If we don't seed with a "common" value, the client and server will get mismatched
//prediction values. Under laggy conditions this will cause the appearance of rapid swing
//sequence changes.
newmove = LS_A_T2B; //decided we don't like random attacks when idle, use an overhead instead.
}
}
return newmove;
}
/*
=================
PM_WeaponLightsaber
Consults a chart to choose what to do with the lightsaber.
While this is a little different than the Quake 3 code, there is no clean way of using the Q3 code for this kind of thing.
=================
*/
// Ultimate goal is to set the sabermove to the proper next location
// Note that if the resultant animation is NONE, then the animation is essentially "idle", and is set in WP_TorsoAnim
void PM_WeaponLightsaber(void)
{
int addTime,amount;
qboolean delayed_fire = qfalse;
int anim=-1, curmove, newmove=LS_NONE;
qboolean saberInAir = qtrue;
qboolean checkOnlyWeap = qfalse;
if ( pm->ps->saberLockTime > pm->cmd.serverTime )
{
pm->ps->saberMove = LS_NONE;
PM_SaberLocked();
return;
}
else
{
if ( ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF2LOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF1LOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CWCIRCLELOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CCWCIRCLELOCK ||
pm->ps->saberLockFrame )
)
{
if (pm->ps->saberLockEnemy < ENTITYNUM_NONE &&
pm->ps->saberLockEnemy >= 0)
{
playerState_t *en;
en = pm->bgClients[pm->ps->saberLockEnemy];
if (en)
{
PM_SaberLockBreak(en, qfalse);
return;
}
}
if ( ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF2LOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_BF1LOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CWCIRCLELOCK ||
(pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == BOTH_CCWCIRCLELOCK ||
pm->ps->saberLockFrame )
)
{
pm->ps->torsoTimer = 0;
PM_SetAnim(SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_OVERRIDE, 100);
pm->ps->saberLockFrame = 0;
}
}
}
if (pm->ps->saberHolstered)
{
if (pm->ps->saberMove != LS_READY)
{
PM_SetSaberMove( LS_READY );
}
if ((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) != (pm->ps->torsoAnim & ~ANIM_TOGGLEBIT))
{
PM_SetAnim(SETANIM_TORSO,(pm->ps->legsAnim & ~ANIM_TOGGLEBIT),SETANIM_FLAG_OVERRIDE, 100);
}
if (BG_InSaberStandAnim(pm->ps->torsoAnim))
{
PM_SetAnim(SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_OVERRIDE, 100);
}
if (pm->ps->weaponTime < 1 && ((pm->cmd.buttons & BUTTON_ALT_ATTACK) || (pm->cmd.buttons & BUTTON_ATTACK)))
{
if (pm->ps->duelTime < pm->cmd.serverTime)
{
pm->ps->saberHolstered = qfalse;
PM_AddEvent(EV_SABER_UNHOLSTER);
}
}
if ( pm->ps->weaponTime > 0 )
{
pm->ps->weaponTime -= pml.msec;
}
checkOnlyWeap = qtrue;
goto weapChecks;
}
if ((pm->cmd.buttons & BUTTON_ALT_ATTACK) &&
pm->ps->weaponTime < 1 &&
pm->ps->saberCanThrow &&
pm->ps->fd.forcePower >= forcePowerNeeded[pm->ps->fd.forcePowerLevel[FP_SABERTHROW]][FP_SABERTHROW] &&
!BG_HasYsalamiri(pm->gametype, pm->ps) &&
BG_CanUseFPNow(pm->gametype, pm->ps, pm->cmd.serverTime, FP_SABERTHROW)
)
{ //might as well just check for a saber throw right here
//This will get set to false again once the saber makes it back to its owner game-side
if (!pm->ps->saberInFlight)
{
pm->ps->fd.forcePower -= forcePowerNeeded[pm->ps->fd.forcePowerLevel[FP_SABERTHROW]][FP_SABERTHROW];
}
pm->ps->saberInFlight = qtrue;
}
if ( pm->ps->saberInFlight )
{//guiding saber
PM_SetAnim(SETANIM_TORSO, BOTH_SABERPULL, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
pm->ps->torsoTimer = 1;
return;
}
// don't allow attack until all buttons are up
//This is bad. It freezes the attack state and the animations if you hold the button after respawning, and it looks strange.
/*
if ( pm->ps->pm_flags & PMF_RESPAWNED ) {
return;
}
*/
// check for dead player
if ( pm->ps->stats[STAT_HEALTH] <= 0 ) {
return;
}
if (pm->ps->weaponstate == WEAPON_READY ||
pm->ps->weaponstate == WEAPON_IDLE)
{
if (pm->ps->saberMove != LS_READY && pm->ps->weaponTime <= 0 && !pm->ps->saberBlocked)
{
PM_SetSaberMove( LS_READY );
}
}
if( (pm->ps->torsoAnim & ~ANIM_TOGGLEBIT) == BOTH_RUN2 ||
(pm->ps->torsoAnim & ~ANIM_TOGGLEBIT) == BOTH_RUN1 )
{
if ((pm->ps->torsoAnim & ~ANIM_TOGGLEBIT) != (pm->ps->legsAnim & ~ANIM_TOGGLEBIT))
{
PM_SetAnim(SETANIM_TORSO,(pm->ps->legsAnim & ~ANIM_TOGGLEBIT),SETANIM_FLAG_OVERRIDE, 100);
}
}
// make weapon function
if ( pm->ps->weaponTime > 0 ) {
pm->ps->weaponTime -= pml.msec;
if (pm->ps->saberBlocked && pm->ps->torsoAnim != saberMoveData[pm->ps->saberMove].animToUse)
{ //rww - keep him in the blocking pose until he can attack again
PM_SetAnim(SETANIM_TORSO,saberMoveData[pm->ps->saberMove].animToUse,saberMoveData[pm->ps->saberMove].animSetFlags|SETANIM_FLAG_HOLD, saberMoveData[pm->ps->saberMove].blendTime);
return;
}
}
else
{
pm->ps->weaponstate = WEAPON_READY;
}
// Now we react to a block action by the player's lightsaber.
if ( pm->ps->saberBlocked )
{
int firstSet = 0;
if (!pm->ps->weaponTime)
{
firstSet = 1;
}
switch ( pm->ps->saberBlocked )
{
case BLOCKED_BOUNCE_MOVE:
{ //act as a bounceMove and reset the saberMove instead of using a seperate value for it
PM_SetSaberMove( pm->ps->saberMove );
pm->ps->weaponTime = pm->ps->torsoTimer;
pm->ps->saberBlocked = 0;
}
break;
case BLOCKED_PARRY_BROKEN:
//whatever parry we were is in now broken, play the appropriate knocked-away anim
{
int nextMove;
if ( PM_SaberInBrokenParry( pm->ps->saberMove ) )
{//already have one...?
nextMove = pm->ps->saberMove;
}
else
{
nextMove = PM_BrokenParryForParry( pm->ps->saberMove );
}
if ( nextMove != LS_NONE )
{
PM_SetSaberMove( nextMove );
pm->ps->weaponTime = pm->ps->torsoTimer;
}
else
{//Maybe in a knockaway?
if (pm->ps->weaponTime <= 0)
{
pm->ps->saberBlocked = 0;
}
}
}
break;
case BLOCKED_ATK_BOUNCE:
// If there is absolutely no blocked move in the chart, don't even mess with the animation.
// OR if we are already in a block or parry.
if (pm->ps->saberMove >= LS_T1_BR__R)
{//an actual bounce? Other bounces before this are actually transitions?
pm->ps->saberBlocked = BLOCKED_NONE;
}
else
{
int bounceMove;
if ( pm->cmd.buttons & BUTTON_ATTACK )
{//transition to a new attack
int newQuad = PM_SaberMoveQuadrantForMovement( &pm->cmd );
while ( newQuad == saberMoveData[pm->ps->saberMove].startQuad )
{//player is still in same attack quad, don't repeat that attack because it looks bad,
//FIXME: try to pick one that might look cool?
//newQuad = Q_irand( Q_BR, Q_BL );
newQuad = PM_irand_timesync( Q_BR, Q_BL );
//FIXME: sanity check, just in case?
}//else player is switching up anyway, take the new attack dir
bounceMove = transitionMove[saberMoveData[pm->ps->saberMove].startQuad][newQuad];
}
else
{//return to ready
if ( saberMoveData[pm->ps->saberMove].startQuad == Q_T )
{
bounceMove = LS_R_BL2TR;
}
else if ( saberMoveData[pm->ps->saberMove].startQuad < Q_T )
{
bounceMove = LS_R_TL2BR+saberMoveData[pm->ps->saberMove].startQuad-Q_BR;
}
else// if ( saberMoveData[pm->ps->saberMove].startQuad > Q_T )
{
bounceMove = LS_R_BR2TL+saberMoveData[pm->ps->saberMove].startQuad-Q_TL;
}
}
PM_SetSaberMove( bounceMove );
pm->ps->weaponTime = pm->ps->torsoTimer;//+saberMoveData[bounceMove].blendTime+SABER_BLOCK_DUR;
}
break;
case BLOCKED_UPPER_RIGHT:
PM_SetSaberMove( LS_PARRY_UR );
break;
case BLOCKED_UPPER_RIGHT_PROJ:
PM_SetSaberMove( LS_REFLECT_UR );
break;
case BLOCKED_UPPER_LEFT:
PM_SetSaberMove( LS_PARRY_UL );
break;
case BLOCKED_UPPER_LEFT_PROJ:
PM_SetSaberMove( LS_REFLECT_UL );
break;
case BLOCKED_LOWER_RIGHT:
PM_SetSaberMove( LS_PARRY_LR );
break;
case BLOCKED_LOWER_RIGHT_PROJ:
PM_SetSaberMove( LS_REFLECT_LR );
break;
case BLOCKED_LOWER_LEFT:
PM_SetSaberMove( LS_PARRY_LL );
break;
case BLOCKED_LOWER_LEFT_PROJ:
PM_SetSaberMove( LS_REFLECT_LL);
break;
case BLOCKED_TOP:
PM_SetSaberMove( LS_PARRY_UP );
break;
case BLOCKED_TOP_PROJ:
PM_SetSaberMove( LS_REFLECT_UP );
break;
default:
pm->ps->saberBlocked = BLOCKED_NONE;
break;
}
if (pm->ps->saberBlocked != BLOCKED_ATK_BOUNCE && pm->ps->saberBlocked != BLOCKED_PARRY_BROKEN && pm->ps->weaponTime < 1)
{
pm->ps->torsoTimer = SABER_BLOCK_DUR;
pm->ps->weaponTime = pm->ps->torsoTimer;
}
if (firstSet)
{
return;
}
// Charging is like a lead-up before attacking again. This is an appropriate use, or we can create a new weaponstate for blocking
pm->ps->weaponstate = WEAPON_READY;
// Done with block, so stop these active weapon branches.
return;
}
weapChecks:
// check for weapon change
// can't change if weapon is firing, but can change again if lowering or raising
if ( pm->ps->weaponTime <= 0 || pm->ps->weaponstate != WEAPON_FIRING ) {
if ( pm->ps->weapon != pm->cmd.weapon ) {
PM_BeginWeaponChange( pm->cmd.weapon );
}
}
if ( pm->ps->weaponTime > 0 )
{
return;
}
// *********************************************************
// WEAPON_DROPPING
// *********************************************************
// change weapon if time
if ( pm->ps->weaponstate == WEAPON_DROPPING ) {
PM_FinishWeaponChange();
return;
}
// *********************************************************
// WEAPON_RAISING
// *********************************************************
if ( pm->ps->weaponstate == WEAPON_RAISING )
{//Just selected the weapon
pm->ps->weaponstate = WEAPON_IDLE;
if((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) == BOTH_WALK1 )
{
PM_SetAnim(SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL, 100);
}
else if((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) == BOTH_RUN2 )
{
PM_SetAnim(SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL, 100);
}
else if((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) == BOTH_WALK2 )
{
PM_SetAnim(SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL, 100);
}
else
{
PM_SetAnim(SETANIM_TORSO,PM_GetSaberStance(),SETANIM_FLAG_NORMAL, 100);
}
if (pm->ps->weaponstate == WEAPON_RAISING)
{
return;
}
}
if (checkOnlyWeap)
{
return;
}
// *********************************************************
// Check for WEAPON ATTACK
// *********************************************************
if(!delayed_fire)
{
// Start with the current move, and cross index it with the current control states.
if ( pm->ps->saberMove > LS_NONE && pm->ps->saberMove < LS_MOVE_MAX )
{
curmove = pm->ps->saberMove;
}
else
{
curmove = LS_READY;
}
// check for fire
if ( !(pm->cmd.buttons & (BUTTON_ATTACK)) )
{
if (pm->ps->weaponTime != 0)
{//Still firing
pm->ps->weaponstate = WEAPON_FIRING;
}
else if ( pm->ps->weaponstate != WEAPON_READY )
{
pm->ps->weaponstate = WEAPON_IDLE;
}
//Check for finishing an anim if necc.
if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B )
{//started a swing, must continue from here
newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR);
}
else if ( curmove >= LS_A_TL2BR && curmove <= LS_A_T2B )
{//finished an attack, must continue from here
newmove = LS_R_TL2BR + (curmove-LS_A_TL2BR);
}
else if ( PM_SaberInTransition( curmove ) )
{//in a transition, must play sequential attack
newmove = saberMoveData[curmove].chain_attack;
}
else if ( PM_SaberInBounce( curmove ) )
{//in a bounce
newmove = saberMoveData[curmove].chain_idle;//oops, not attacking, so don't chain
}
else
{//FIXME: what about returning from a parry?
PM_SetSaberMove( LS_READY );
return;
}
}
// ***************************************************
// Pressing attack, so we must look up the proper attack move.
saberInAir = qtrue;
if ( pm->ps->weaponTime > 0 )
{ // Last attack is not yet complete.
pm->ps->weaponstate = WEAPON_FIRING;
return;
}
else
{
int both = qfalse;
if ( curmove >= LS_PARRY_UP && curmove <= LS_REFLECT_LL )
{//from a parry or deflection, can go directly into an attack (?)
switch ( saberMoveData[curmove].endQuad )
{
case Q_T:
newmove = LS_A_T2B;
break;
case Q_TR:
newmove = LS_A_TL2BR;
break;
case Q_TL:
newmove = LS_A_TR2BL;
break;
case Q_BR:
newmove = LS_A_BR2TL;
break;
case Q_BL:
newmove = LS_A_BL2TR;
break;
//shouldn't be a parry that ends at L, R or B
}
}
if ( newmove != LS_NONE )
{//have a valid, final LS_ move picked, so skip findingt he transition move and just get the anim
anim = saberMoveData[newmove].animToUse;
}
//FIXME: diagonal dirs use the figure-eight attacks from ready pose?
if ( anim == -1 )
{
//FIXME: take FP_SABER_OFFENSE into account here somehow?
if ( PM_SaberInTransition( curmove ) )
{//in a transition, must play sequential attack
newmove = saberMoveData[curmove].chain_attack;
}
else if ( curmove >= LS_S_TL2BR && curmove <= LS_S_T2B )
{//started a swing, must continue from here
newmove = LS_A_TL2BR + (curmove-LS_S_TL2BR);
}
else if ( PM_SaberInBrokenParry( curmove ) )
{//broken parries must always return to ready
newmove = LS_READY;
}
else//if ( pm->cmd.buttons&BUTTON_ATTACK && !(pm->ps->pm_flags&PMF_ATTACK_HELD) )//only do this if just pressed attack button?
{//get attack move from movement command
saberMoveName_t checkMove = PM_SaberAttackForMovement(curmove);
if (checkMove != -1)
{
newmove = checkMove;
}
if ( (PM_SaberInBounce( curmove )||PM_SaberInBrokenParry( curmove ))
&& saberMoveData[newmove].startQuad == saberMoveData[curmove].endQuad )
{//this attack would be a repeat of the last (which was blocked), so don't actually use it, use the default chain attack for this bounce
newmove = saberMoveData[curmove].chain_attack;
}
if ( PM_SaberKataDone(curmove, newmove) )
{//we came from a bounce and cannot chain to another attack because our kata is done
newmove = saberMoveData[curmove].chain_idle;
}
}
if ( newmove != LS_NONE )
{
//Now get the proper transition move
newmove = PM_SaberAnimTransitionAnim( curmove, newmove );
assert( bgGlobalAnimations[saberMoveData[newmove].animToUse].firstFrame != 0 ||
bgGlobalAnimations[saberMoveData[newmove].animToUse].numFrames != 0);
anim = saberMoveData[newmove].animToUse;
}
}
if (anim == -1)
{//not side-stepping, pick neutral anim
// Add randomness for prototype?
newmove = saberMoveData[curmove].chain_attack;
anim= saberMoveData[newmove].animToUse;
if ( !pm->cmd.forwardmove && !pm->cmd.rightmove && pm->cmd.upmove >= 0 && pm->ps->groundEntityNum != ENTITYNUM_NONE )
{//not moving at all, so set the anim on entire body
both = qtrue;
}
}
if ( anim == -1)
{
if((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) == BOTH_WALK1 )
{
anim = BOTH_WALK1;
}
else if((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) == BOTH_RUN2 )
{
anim = BOTH_RUN2;
}
else if((pm->ps->legsAnim & ~ANIM_TOGGLEBIT) == BOTH_WALK2 )
{
anim = BOTH_WALK2;
}
else
{
anim = PM_GetSaberStance();
}
if (anim == BOTH_RUN2 && !pm->cmd.forwardmove && !pm->cmd.rightmove)
{ //semi-hacky (if not moving on x-y and still playing the running anim, force the player out of it)
anim = PM_GetSaberStance();
}
newmove = LS_READY;
}
if ( !pm->ps->saberActive )
{//turn on the saber if it's not on
pm->ps->saberActive = qtrue;
}
PM_SetSaberMove( newmove );
if ( both )
{
PM_SetAnim(SETANIM_LEGS,anim,SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 100);
}
//don't fire again until anim is done
pm->ps->weaponTime = pm->ps->torsoTimer;
}
}
// *********************************************************
// WEAPON_FIRING
// *********************************************************
pm->ps->weaponstate = WEAPON_FIRING;
amount = weaponData[pm->ps->weapon].energyPerShot;
addTime = pm->ps->weaponTime;
pm->ps->saberAttackSequence = pm->ps->torsoAnim;
if ( !addTime )
{
addTime = weaponData[pm->ps->weapon].fireTime;
}
pm->ps->weaponTime = addTime;
}
void PM_SetSaberMove(short newMove)
{
unsigned int setflags = saberMoveData[newMove].animSetFlags;
int anim = saberMoveData[newMove].animToUse;
int parts = SETANIM_TORSO;
if ( newMove == LS_READY || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH )
{//finished with a kata (or in a special move) reset attack counter
pm->ps->saberAttackChainCount = 0;
}
else if ( BG_SaberInAttack( newMove ) )
{//continuing with a kata, increment attack counter
pm->ps->saberAttackChainCount++;
}
if (pm->ps->saberAttackChainCount > 16)
{ //for the sake of being able to send the value over the net within a reasonable bit count
pm->ps->saberAttackChainCount = 16;
}
if ( pm->ps->fd.saberAnimLevel > FORCE_LEVEL_1 &&
!BG_SaberInIdle( newMove ) && !PM_SaberInParry( newMove ) && !PM_SaberInKnockaway( newMove ) && !PM_SaberInBrokenParry( newMove ) && !PM_SaberInReflect( newMove ) && !BG_SaberInSpecial(newMove))
{//readies, parries and reflections have only 1 level
//increment the anim to the next level of saber anims
anim += (pm->ps->fd.saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE;
}
// If the move does the same animation as the last one, we need to force a restart...
if ( saberMoveData[pm->ps->saberMove].animToUse == anim && newMove > LS_PUTAWAY)
{
setflags |= SETANIM_FLAG_RESTART;
}
//saber torso anims should always be highest priority (4/12/02 - for special anims only)
if ( newMove == LS_A_LUNGE
|| newMove == LS_A_JUMP_T__B_
|| newMove == LS_A_BACKSTAB
|| newMove == LS_A_BACK
|| newMove == LS_A_BACK_CR
|| newMove == LS_A_FLIP_STAB
|| newMove == LS_A_FLIP_SLASH )
{
setflags |= SETANIM_FLAG_OVERRIDE;
}
if ( BG_InSaberStandAnim(anim) || anim == BOTH_STAND1 )
{
anim = (pm->ps->legsAnim & ~ANIM_TOGGLEBIT);
if ((anim >= BOTH_STAND1 && anim <= BOTH_STAND4TOATTACK2) ||
(anim >= TORSO_DROPWEAP1 && anim <= TORSO_WEAPONIDLE12))
{ //If standing then use the special saber stand anim
anim = PM_GetSaberStance();
}
if (pm->ps->pm_flags & PMF_DUCKED)
{ //Playing torso walk anims while crouched makes you look like a monkey
anim = PM_GetSaberStance();
}
if (anim == BOTH_WALKBACK1 || anim == BOTH_WALKBACK2)
{ //normal stance when walking backward so saber doesn't look like it's cutting through leg
anim = PM_GetSaberStance();
}
parts = SETANIM_TORSO;
}
if ( newMove == LS_A_LUNGE
|| newMove == LS_A_JUMP_T__B_
|| newMove == LS_A_BACKSTAB
|| newMove == LS_A_BACK
|| newMove == LS_A_BACK_CR
|| newMove == LS_A_FLIP_STAB
|| newMove == LS_A_FLIP_SLASH )
{
parts = SETANIM_BOTH;
}
else if ( BG_SpinningSaberAnim( anim ) )
{//spins must be played on entire body
parts = SETANIM_BOTH;
}
else if ( (!pm->cmd.forwardmove&&!pm->cmd.rightmove&&!pm->cmd.upmove))
{//not trying to run, duck or jump
if ( !BG_FlippingAnim( pm->ps->legsAnim ) &&
!BG_InRoll( pm->ps, pm->ps->legsAnim ) &&
!PM_InKnockDown( pm->ps ) &&
!PM_JumpingAnim( pm->ps->legsAnim ) &&
!BG_InSpecialJump( pm->ps->legsAnim ) &&
anim != PM_GetSaberStance() &&
pm->ps->groundEntityNum != ENTITYNUM_NONE &&
!(pm->ps->pm_flags & PMF_DUCKED))
{
parts = SETANIM_BOTH;
}
}
PM_SetAnim(parts, anim, setflags, saberMoveData[newMove].blendTime);
if ( (pm->ps->torsoAnim&~ANIM_TOGGLEBIT) == anim )
{//successfully changed anims
//special check for *starting* a saber swing
//playing at attack
if ( BG_SaberInAttack( newMove ) || BG_SaberInSpecialAttack( anim ) )
{
if ( pm->ps->saberMove != newMove )
{//wasn't playing that attack before
PM_AddEvent(EV_SABER_ATTACK);
}
}
pm->ps->saberMove = newMove;
pm->ps->saberBlocking = saberMoveData[newMove].blocking;
pm->ps->torsoAnim = anim;
if (pm->ps->weaponTime <= 0)
{
pm->ps->saberBlocked = BLOCKED_NONE;
}
}
}