2007 lines
61 KiB
C
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;
|
|
}
|
|
}
|
|
}
|
|
|