#include "q_shared.h" #include "bg_public.h" #include "bg_local.h" #include "w_saber.h" #include "../namespace_begin.h" extern qboolean BG_SabersOff( playerState_t *ps ); saberInfo_t *BG_MySaber( int clientNum, int saberNum ); 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) { if (ps->fd.forcePowerLevel[FP_LEVITATION]) { //don't divide by 0! 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; } } qboolean BG_EnoughForcePowerForMove( int cost ) { if ( pm->ps->fd.forcePower < cost ) { PM_AddEvent( EV_NOAMMO ); return qfalse; } return qtrue; } // 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) //FIXME: add the alternate anims for each style? saberMoveData_t saberMoveData[LS_MOVE_MAX] = {// NB:randomized // name anim(do all styles?)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 {"RollStab", BOTH_ROLL_STAB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_ROLL_STAB {"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 {"DualJump Atk",BOTH_JUMPATTACK6, Q_R, Q_BL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_BL_TR, 200 }, // LS_JUMPATTACK_DUAL {"DualJumpAtkL_A",BOTH_ARIAL_LEFT, Q_R, Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TL2BR, 200 }, // LS_JUMPATTACK_ARIAL_LEFT {"DualJumpAtkR_A",BOTH_ARIAL_RIGHT, Q_R, Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_A_TR2BL, 200 }, // LS_JUMPATTACK_ARIAL_RIGHT {"DualJumpAtkL_A",BOTH_CARTWHEEL_LEFT, Q_R,Q_TL, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TL_BR, 200 }, // LS_JUMPATTACK_CART_LEFT {"DualJumpAtkR_A",BOTH_CARTWHEEL_RIGHT, Q_R,Q_TR, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_TR_BL, 200 }, // LS_JUMPATTACK_CART_RIGHT {"DualJumpAtkLStaff", BOTH_BUTTERFLY_FL1,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_JUMPATTACK_STAFF_LEFT {"DualJumpAtkRStaff", BOTH_BUTTERFLY_FR1,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_JUMPATTACK_STAFF_RIGHT {"ButterflyLeft", BOTH_BUTTERFLY_LEFT,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__L__R, 200 }, // LS_BUTTERFLY_LEFT {"ButterflyRight", BOTH_BUTTERFLY_RIGHT,Q_R,Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1__R__L, 200 }, // LS_BUTTERFLY_RIGHT {"BkFlip Atk", BOTH_JUMPATTACK7, Q_B, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_T1_T___R, 200 }, // LS_A_BACKFLIP_ATK {"DualSpinAtk", BOTH_SPINATTACK6, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_DUAL {"StfSpinAtk", BOTH_SPINATTACK7, Q_L, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK {"LngLeapAtk", BOTH_FORCELONGLEAP_ATTACK,Q_R,Q_L, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_LEAP_ATTACK {"SwoopAtkR", BOTH_VS_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_RIGHT {"SwoopAtkL", BOTH_VS_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SWOOP_ATTACK_LEFT {"TauntaunAtkR",BOTH_VT_ATR_S, Q_R, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_RIGHT {"TauntaunAtkL",BOTH_VT_ATL_S, Q_L, Q_T, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_TAUNTAUN_ATTACK_LEFT {"StfKickFwd", BOTH_A7_KICK_F, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F {"StfKickBack", BOTH_A7_KICK_B, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B {"StfKickRight",BOTH_A7_KICK_R, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R {"StfKickLeft", BOTH_A7_KICK_L, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L {"StfKickSpin", BOTH_A7_KICK_S, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_S {"StfKickBkFwd",BOTH_A7_KICK_BF, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_BF {"StfKickSplit",BOTH_A7_KICK_RL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_NO, LS_READY, LS_S_R2L, 200 }, // LS_KICK_RL {"StfKickFwdAir",BOTH_A7_KICK_F_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_F_AIR {"StfKickBackAir",BOTH_A7_KICK_B_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_B_AIR {"StfKickRightAir",BOTH_A7_KICK_R_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_R_AIR {"StfKickLeftAir",BOTH_A7_KICK_L_AIR,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_KICK_L_AIR {"StabDown", BOTH_STABDOWN, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN {"StabDownStf", BOTH_STABDOWN_STAFF,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_STAFF {"StabDownDual",BOTH_STABDOWN_DUAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_S_R2L, 200 }, // LS_STABDOWN_DUAL {"dualspinprot",BOTH_A6_SABERPROTECT,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_DUAL_SPIN_PROTECT {"StfSoulCal", BOTH_A7_SOULCAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 500 }, // LS_STAFF_SOULCAL {"specialfast", BOTH_A1_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A1_SPECIAL {"specialmed", BOTH_A2_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A2_SPECIAL {"specialstr", BOTH_A3_SPECIAL, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 2000}, // LS_A3_SPECIAL {"upsidedwnatk",BOTH_FLIP_ATTACK7, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_UPSIDE_DOWN_ATTACK {"pullatkstab", BOTH_PULL_IMPALE_STAB,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_STAB {"pullatkswing",BOTH_PULL_IMPALE_SWING,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200}, // LS_PULL_ATTACK_SWING {"AloraSpinAtk",BOTH_ALORA_SPIN_SLASH,Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_SPINATTACK_ALORA {"Dual FB Atk", BOTH_A6_FB, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_FB {"Dual LR Atk", BOTH_A6_LR, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_DUAL_LR {"StfHiltBash", BOTH_A7_HILT, Q_R, Q_R, AFLAG_ACTIVE, 100, BLK_TIGHT, LS_READY, LS_READY, 200 }, // LS_HILT_BASH //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; } extern qboolean BG_InKnockDown( int anim ); saberMoveName_t PM_CheckStabDown( void ) { vec3_t faceFwd, facingAngles; vec3_t fwd; bgEntity_t *ent = NULL; trace_t tr; //yeah, vm's may complain, but.. who cares! vec3_t trmins = {-15, -15, -15}; vec3_t trmaxs = {15, 15, 15}; saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber1 && (saber1->saberFlags&SFL_NO_STABDOWN) ) { return LS_NONE; } if ( saber2 && (saber2->saberFlags&SFL_NO_STABDOWN) ) { return LS_NONE; } if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {//sorry must be on ground! return LS_NONE; } if ( pm->ps->clientNum < MAX_CLIENTS ) {//player pm->ps->velocity[2] = 0; pm->cmd.upmove = 0; } VectorSet(facingAngles, 0, pm->ps->viewangles[YAW], 0); AngleVectors( facingAngles, faceFwd, NULL, NULL ); //FIXME: need to only move forward until we bump into our target...? VectorMA(pm->ps->origin, 164.0f, faceFwd, fwd); pm->trace(&tr, pm->ps->origin, trmins, trmaxs, fwd, pm->ps->clientNum, MASK_PLAYERSOLID); if (tr.entityNum < ENTITYNUM_WORLD) { ent = PM_BGEntForNum(tr.entityNum); } if ( ent && (ent->s.eType == ET_PLAYER || ent->s.eType == ET_NPC) && BG_InKnockDown( ent->s.legsAnim ) ) {//guy is on the ground below me, do a top-down attack if ( pm->ps->fd.saberAnimLevel == SS_DUAL ) { return LS_STABDOWN_DUAL; } else if ( pm->ps->fd.saberAnimLevel == SS_STAFF ) { return LS_STABDOWN_STAFF; } else { return LS_STABDOWN; } } return LS_NONE; } 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 ); 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->m_iVehicleNum) { //never continue kata on vehicle if (pm->ps->saberAttackChainCount > 0) { return qtrue; } } if ( pm->ps->fd.saberAnimLevel == SS_DESANN || pm->ps->fd.saberAnimLevel == SS_TAVION ) {//desann and tavion can link up as many attacks as they want return qfalse; } if ( pm->ps->fd.saberAnimLevel == SS_STAFF ) { //TEMP: for now, let staff attacks infinitely chain return qfalse; } else if ( pm->ps->fd.saberAnimLevel == SS_DUAL ) { //TEMP: for now, let staff attacks infinitely chain return qfalse; } else 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; } int PM_SaberLockWinAnim( qboolean victory, qboolean superBreak ) { int winAnim = -1; switch ( pm->ps->torsoAnim ) { /* default: #ifndef FINAL_BUILD Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", pm->gent->NPC_type, pm->ps->torsoAnim, animTable[pm->ps->torsoAnim].name ); #endif */ case BOTH_BF2LOCK: if ( superBreak ) { winAnim = BOTH_LK_S_S_T_SB_1_W; } else if ( !victory ) { winAnim = BOTH_BF1BREAK; } else { pm->ps->saberMove = LS_A_T2B; winAnim = BOTH_A3_T__B_; } break; case BOTH_BF1LOCK: if ( superBreak ) { winAnim = BOTH_LK_S_S_T_SB_1_W; } else if ( !victory ) { winAnim = BOTH_KNOCKDOWN4; } else { pm->ps->saberMove = LS_K1_T_; winAnim = BOTH_K1_S1_T_; } break; case BOTH_CWCIRCLELOCK: if ( superBreak ) { winAnim = BOTH_LK_S_S_S_SB_1_W; } else if ( !victory ) { pm->ps->saberMove = LS_V1_BL;//pm->ps->saberBounceMove = pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN; winAnim = BOTH_V1_BL_S1; } else { winAnim = BOTH_CWCIRCLEBREAK; } break; case BOTH_CCWCIRCLELOCK: if ( superBreak ) { winAnim = BOTH_LK_S_S_S_SB_1_W; } else if ( !victory ) { pm->ps->saberMove = LS_V1_BR;//pm->ps->saberBounceMove = pm->ps->saberBlocked = BLOCKED_PARRY_BROKEN; winAnim = BOTH_V1_BR_S1; } else { winAnim = BOTH_CCWCIRCLEBREAK; } break; default: //must be using new system: break; } if ( winAnim != -1 ) { PM_SetAnim( SETANIM_BOTH, winAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); pm->ps->weaponTime = pm->ps->torsoTimer; pm->ps->saberBlocked = BLOCKED_NONE; pm->ps->weaponstate = WEAPON_FIRING; /* if ( superBreak && winAnim != BOTH_LK_ST_DL_T_SB_1_W ) {//going to attack with saber, do a saber trail pm->ps->SaberActivateTrail( 200 ); } */ } return winAnim; } // Need to avoid nesting namespaces! #include "../namespace_end.h" #ifdef QAGAME //including game headers on cgame is FORBIDDEN ^_^ #include "g_local.h" extern void NPC_SetAnim(gentity_t *ent, int setAnimParts, int anim, int setAnimFlags); extern gentity_t g_entities[]; #elif defined CGAME #include "../cgame/cg_local.h" //ahahahahhahahaha@$!$! #endif #include "../namespace_begin.h" int PM_SaberLockLoseAnim( playerState_t *genemy, qboolean victory, qboolean superBreak ) { int loseAnim = -1; switch ( genemy->torsoAnim ) { /* default: #ifndef FINAL_BUILD Com_Printf( S_COLOR_RED"ERROR-PM_SaberLockBreak: %s not in saberlock anim, anim = (%d)%s\n", genemy->NPC_type, genemy->client->ps.torsoAnim, animTable[genemy->client->ps.torsoAnim].name ); #endif */ case BOTH_BF2LOCK: if ( superBreak ) { loseAnim = BOTH_LK_S_S_T_SB_1_L; } else if ( !victory ) { loseAnim = BOTH_BF1BREAK; } else { if ( !victory ) {//no-one won genemy->saberMove = LS_K1_T_; loseAnim = BOTH_K1_S1_T_; } else {//FIXME: this anim needs to transition back to ready when done loseAnim = BOTH_BF1BREAK; } } break; case BOTH_BF1LOCK: if ( superBreak ) { loseAnim = BOTH_LK_S_S_T_SB_1_L; } else if ( !victory ) { loseAnim = BOTH_KNOCKDOWN4; } else { if ( !victory ) {//no-one won genemy->saberMove = LS_A_T2B; loseAnim = BOTH_A3_T__B_; } else { loseAnim = BOTH_KNOCKDOWN4; } } break; case BOTH_CWCIRCLELOCK: if ( superBreak ) { loseAnim = BOTH_LK_S_S_S_SB_1_L; } else if ( !victory ) { genemy->saberMove = LS_V1_BL;//genemy->saberBounceMove = genemy->saberBlocked = BLOCKED_PARRY_BROKEN; loseAnim = BOTH_V1_BL_S1; } else { if ( !victory ) {//no-one won loseAnim = BOTH_CCWCIRCLEBREAK; } else { genemy->saberMove = LS_V1_BL;//genemy->saberBounceMove = genemy->saberBlocked = BLOCKED_PARRY_BROKEN; loseAnim = BOTH_V1_BL_S1; /* genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_H1_BR; genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; loseAnim = BOTH_H1_S1_BL; */ } } break; case BOTH_CCWCIRCLELOCK: if ( superBreak ) { loseAnim = BOTH_LK_S_S_S_SB_1_L; } else if ( !victory ) { genemy->saberMove = LS_V1_BR;//genemy->saberBounceMove = genemy->saberBlocked = BLOCKED_PARRY_BROKEN; loseAnim = BOTH_V1_BR_S1; } else { if ( !victory ) {//no-one won loseAnim = BOTH_CWCIRCLEBREAK; } else { genemy->saberMove = LS_V1_BR;//genemy->saberBounceMove = genemy->saberBlocked = BLOCKED_PARRY_BROKEN; loseAnim = BOTH_V1_BR_S1; /* genemy->client->ps.saberMove = genemy->client->ps.saberBounceMove = LS_H1_BL; genemy->client->ps.saberBlocked = BLOCKED_PARRY_BROKEN; loseAnim = BOTH_H1_S1_BR; */ } } break; } if ( loseAnim != -1 ) { #ifdef QAGAME NPC_SetAnim( &g_entities[genemy->clientNum], SETANIM_BOTH, loseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); genemy->weaponTime = genemy->torsoTimer;// + 250; #endif genemy->saberBlocked = BLOCKED_NONE; genemy->weaponstate = WEAPON_READY; } return loseAnim; } int PM_SaberLockResultAnim( playerState_t *duelist, qboolean superBreak, qboolean won ) { int baseAnim = duelist->torsoAnim; switch ( baseAnim ) { case BOTH_LK_S_S_S_L_2: //lock if I'm using single vs. a single and other intitiated baseAnim = BOTH_LK_S_S_S_L_1; break; case BOTH_LK_S_S_T_L_2: //lock if I'm using single vs. a single and other initiated baseAnim = BOTH_LK_S_S_T_L_1; break; case BOTH_LK_DL_DL_S_L_2: //lock if I'm using dual vs. dual and other initiated baseAnim = BOTH_LK_DL_DL_S_L_1; break; case BOTH_LK_DL_DL_T_L_2: //lock if I'm using dual vs. dual and other initiated baseAnim = BOTH_LK_DL_DL_T_L_1; break; case BOTH_LK_ST_ST_S_L_2: //lock if I'm using staff vs. a staff and other initiated baseAnim = BOTH_LK_ST_ST_S_L_1; break; case BOTH_LK_ST_ST_T_L_2: //lock if I'm using staff vs. a staff and other initiated baseAnim = BOTH_LK_ST_ST_T_L_1; break; } //what kind of break? if ( !superBreak ) { baseAnim -= 2; } else if ( superBreak ) { baseAnim += 1; } else {//WTF? Not a valid result return -1; } //win or lose? if ( won ) { baseAnim += 1; } //play the anim and hold it #ifdef QAGAME //server-side: set it on the other guy, too if ( duelist->clientNum == pm->ps->clientNum ) {//me PM_SetAnim( SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); } else {//other guy NPC_SetAnim( &g_entities[duelist->clientNum], SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD ); } #else PM_SetAnim( SETANIM_BOTH, baseAnim, SETANIM_FLAG_OVERRIDE|SETANIM_FLAG_HOLD, 0 ); #endif if ( superBreak && !won ) {//if you lose a superbreak, you're defenseless /* //Taken care of in SetSaberBoxSize() //make saberent not block gentity_t *saberent = &g_entities[duelist->client->ps.saberEntityNum]; if ( saberent ) { VectorClear(saberent->mins); VectorClear(saberent->maxs); G_SetOrigin(saberent, duelist->currentOrigin); } */ #ifdef QAGAME if ( 1 ) #else if ( duelist->clientNum == pm->ps->clientNum ) #endif { //set sabermove to none duelist->saberMove = LS_NONE; //Hold the anim a little longer than it is duelist->torsoTimer += 250; } } #ifdef QAGAME if ( 1 ) #else if ( duelist->clientNum == pm->ps->clientNum ) #endif { //no attacking during this anim duelist->weaponTime = duelist->torsoTimer; duelist->saberBlocked = BLOCKED_NONE; /* if ( superBreak && won && baseAnim != BOTH_LK_ST_DL_T_SB_1_W ) {//going to attack with saber, do a saber trail duelist->client->ps.SaberActivateTrail( 200 ); } */ } return baseAnim; } void PM_SaberLockBreak( playerState_t *genemy, qboolean victory, int strength ) { int winAnim = BOTH_STAND1, loseAnim = BOTH_STAND1; //qboolean punishLoser = qfalse; qboolean noKnockdown = qfalse; qboolean singleVsSingle = qtrue; qboolean superBreak = (strength+pm->ps->saberLockHits > Q_irand(2,4)); winAnim = PM_SaberLockWinAnim( victory, superBreak ); if ( winAnim != -1 ) {//a single vs. single break loseAnim = PM_SaberLockLoseAnim( genemy, victory, superBreak ); } else {//must be a saberlock that's not between single and single... singleVsSingle = qfalse; winAnim = PM_SaberLockResultAnim( pm->ps, superBreak, qtrue ); pm->ps->weaponstate = WEAPON_FIRING; loseAnim = PM_SaberLockResultAnim( genemy, superBreak, qfalse ); genemy->weaponstate = WEAPON_READY; } if ( victory ) { //someone lost the lock, so punish them by knocking them down if ( pm->ps->saberLockHits && !superBreak ) {//there was some over-power in the win, but not enough to superbreak vec3_t oppDir; int strength = 8; VectorSubtract(genemy->origin, pm->ps->origin, oppDir); VectorNormalize(oppDir); if (noKnockdown) { if (!genemy->saberEntityNum) { //if he has already lost his saber then just knock him down noKnockdown = qfalse; } } if (!noKnockdown && BG_KnockDownable(genemy)) { 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->otherKillerMOD = MOD_UNKNOWN; //genemy->otherKillerVehWeapon = 0; //genemy->otherKillerWeaponType = WP_NONE; genemy->velocity[0] = oppDir[0]*(strength*40); genemy->velocity[1] = oppDir[1]*(strength*40); genemy->velocity[2] = 100; } pm->checkDuelLoss = genemy->clientNum+1; pm->ps->saberEventFlags |= SEF_LOCK_WON; } } 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); } } } qboolean BG_CheckIncrementLockAnim( int anim, int winOrLose ) { qboolean increment = qfalse;//??? //RULE: if you are the first style in the lock anim, you advance from LOSING position to WINNING position // if you are the second style in the lock anim, you advance from WINNING position to LOSING position switch ( anim ) { //increment to win: case BOTH_LK_DL_DL_S_L_1: //lock if I'm using dual vs. dual and I initiated case BOTH_LK_DL_DL_S_L_2: //lock if I'm using dual vs. dual and other initiated case BOTH_LK_DL_DL_T_L_1: //lock if I'm using dual vs. dual and I initiated case BOTH_LK_DL_DL_T_L_2: //lock if I'm using dual vs. dual and other initiated case BOTH_LK_DL_S_S_L_1: //lock if I'm using dual vs. a single case BOTH_LK_DL_S_T_L_1: //lock if I'm using dual vs. a single case BOTH_LK_DL_ST_S_L_1: //lock if I'm using dual vs. a staff case BOTH_LK_DL_ST_T_L_1: //lock if I'm using dual vs. a staff case BOTH_LK_S_S_S_L_1: //lock if I'm using single vs. a single and I initiated case BOTH_LK_S_S_T_L_2: //lock if I'm using single vs. a single and other initiated case BOTH_LK_ST_S_S_L_1: //lock if I'm using staff vs. a single case BOTH_LK_ST_S_T_L_1: //lock if I'm using staff vs. a single case BOTH_LK_ST_ST_T_L_1: //lock if I'm using staff vs. a staff and I initiated case BOTH_LK_ST_ST_T_L_2: //lock if I'm using staff vs. a staff and other initiated if ( winOrLose == SABERLOCK_WIN ) { increment = qtrue; } else { increment = qfalse; } break; //decrement to win: case BOTH_LK_S_DL_S_L_1: //lock if I'm using single vs. a dual case BOTH_LK_S_DL_T_L_1: //lock if I'm using single vs. a dual case BOTH_LK_S_S_S_L_2: //lock if I'm using single vs. a single and other intitiated case BOTH_LK_S_S_T_L_1: //lock if I'm using single vs. a single and I initiated case BOTH_LK_S_ST_S_L_1: //lock if I'm using single vs. a staff case BOTH_LK_S_ST_T_L_1: //lock if I'm using single vs. a staff case BOTH_LK_ST_DL_S_L_1: //lock if I'm using staff vs. dual case BOTH_LK_ST_DL_T_L_1: //lock if I'm using staff vs. dual case BOTH_LK_ST_ST_S_L_1: //lock if I'm using staff vs. a staff and I initiated case BOTH_LK_ST_ST_S_L_2: //lock if I'm using staff vs. a staff and other initiated if ( winOrLose == SABERLOCK_WIN ) { increment = qfalse; } else { increment = qtrue; } break; default: break; } return increment; } extern qboolean ValidAnimFileIndex ( int index ); void PM_SaberLocked( void ) { int remaining = 0; playerState_t *genemy; bgEntity_t *eGenemy = PM_BGEntForNum(pm->ps->saberLockEnemy); if (!eGenemy) { return; } genemy = eGenemy->playerState; if ( !genemy ) { return; } /*if ( ( (pm->ps->torsoAnim) == BOTH_BF2LOCK || (pm->ps->torsoAnim) == BOTH_BF1LOCK || (pm->ps->torsoAnim) == BOTH_CWCIRCLELOCK || (pm->ps->torsoAnim) == BOTH_CCWCIRCLELOCK ) && ( (genemy->torsoAnim) == BOTH_BF2LOCK || (genemy->torsoAnim) == BOTH_BF1LOCK || (genemy->torsoAnim) == BOTH_CWCIRCLELOCK || (genemy->torsoAnim) == BOTH_CCWCIRCLELOCK ) ) */ //yeah.. if (pm->ps->saberLockFrame && genemy->saberLockFrame && BG_InSaberLock(pm->ps->torsoAnim) && BG_InSaberLock(genemy->torsoAnim)) { 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, 0 ); return; } /* //NOTE: time-out is handled around where PM_SaberLocked is called if ( pm->ps->saberLockTime <= pm->cmd.serverTime + 500 ) {//lock just ended PM_SaberLockBreak( genemy, qfalse, 0 ); return; } */ if ( pm->ps->saberLockAdvance ) {//holding attack animation_t *anim; float currentFrame; int curFrame; int strength = 1; pm->ps->saberLockAdvance = qfalse; anim = &pm->animations[pm->ps->torsoAnim]; currentFrame = pm->ps->saberLockFrame; strength = pm->ps->fd.forcePowerLevel[FP_SABER_OFFENSE]+1; //advance/decrement my frame number if ( BG_InSaberLockOld( pm->ps->torsoAnim ) ) { //old locks if ( (pm->ps->torsoAnim) == BOTH_CCWCIRCLELOCK || (pm->ps->torsoAnim) == BOTH_BF2LOCK ) { curFrame = floor( currentFrame )-strength; //drop my frame one if ( curFrame <= anim->firstFrame ) {//I won! Break out PM_SaberLockBreak( genemy, qtrue, strength ); 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, strength ); return; } else { PM_SetAnimFrame( pm->ps, curFrame, qtrue, qtrue ); remaining = anim->firstFrame+anim->numFrames-curFrame; } } } else { //new locks if ( BG_CheckIncrementLockAnim( pm->ps->torsoAnim, SABERLOCK_WIN ) ) { curFrame = ceil( currentFrame )+strength; //advance my frame one if ( curFrame >= anim->firstFrame+anim->numFrames ) {//I won! Break out PM_SaberLockBreak( genemy, qtrue, strength ); return; } else { PM_SetAnimFrame( pm->ps, curFrame, qtrue, qtrue ); remaining = anim->firstFrame+anim->numFrames-curFrame; } } else { curFrame = floor( currentFrame )-strength; //drop my frame one if ( curFrame <= anim->firstFrame ) {//I won! Break out PM_SaberLockBreak( genemy, qtrue, strength ); return; } else { PM_SetAnimFrame( pm->ps, curFrame, qtrue, qtrue ); remaining = curFrame-anim->firstFrame; } } } if ( !PM_irand_timesync( 0, 2 ) ) { PM_AddEvent( EV_JUMP ); } //advance/decrement enemy frame number anim = &pm->animations[(genemy->torsoAnim)]; if ( BG_InSaberLockOld( genemy->torsoAnim ) ) { if ( (genemy->torsoAnim) == BOTH_CWCIRCLELOCK || (genemy->torsoAnim) == 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 {//new locks if ( BG_CheckIncrementLockAnim( genemy->torsoAnim, SABERLOCK_LOSE ) ) { if ( !PM_irand_timesync( 0, 2 ) ) { BG_AddPredictableEventToPlayerstate(EV_PAIN, floor((float)80/100*100.0f), genemy); } PM_SetAnimFrame( genemy, anim->firstFrame+anim->numFrames-remaining, qtrue, qtrue ); } else { PM_SetAnimFrame( genemy, anim->firstFrame+remaining, qtrue, qtrue ); } } } } else {//something broke us out of it PM_SaberLockBreak( genemy, qfalse, 0 ); } } 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 < ENTITYNUM_NONE) { bgEntity_t *bgEnt = PM_BGEntForNum(tr.entityNum); if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC)) { return qtrue; } } return qfalse; } saberMoveName_t PM_SaberFlipOverAttackMove(void) { vec3_t fwdAngles, jumpFwd; // float zDiff = 0; // playerState_t *psData; // bgEntity_t *bgEnt; saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); //see if we have an overridden (or cancelled) lunge move if ( saber1 && saber1->jumpAtkFwdMove != LS_INVALID ) { if ( saber1->jumpAtkFwdMove != LS_NONE ) { return (saberMoveName_t)saber1->jumpAtkFwdMove; } } if ( saber2 && saber2->jumpAtkFwdMove != LS_INVALID ) { if ( saber2->jumpAtkFwdMove != LS_NONE ) { return (saberMoveName_t)saber2->jumpAtkFwdMove; } } //no overrides, cancelled? if ( saber1 && saber1->jumpAtkFwdMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } if ( saber2 && saber2->jumpAtkFwdMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } //just do it VectorCopy( pm->ps->viewangles, fwdAngles ); fwdAngles[PITCH] = fwdAngles[ROLL] = 0; AngleVectors( fwdAngles, jumpFwd, NULL, NULL ); VectorScale( jumpFwd, 150, pm->ps->velocity );//was 50 pm->ps->velocity[2] = 400; /* bgEnt = PM_BGEntForNum(tr->entityNum); if (!bgEnt) { return LS_A_FLIP_STAB; } psData = bgEnt->playerState; //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; } } int PM_SaberBackflipAttackMove( void ) { saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); //see if we have an overridden (or cancelled) lunge move if ( saber1 && saber1->jumpAtkBackMove != LS_INVALID ) { if ( saber1->jumpAtkBackMove != LS_NONE ) { return (saberMoveName_t)saber1->jumpAtkBackMove; } } if ( saber2 && saber2->jumpAtkBackMove != LS_INVALID ) { if ( saber2->jumpAtkBackMove != LS_NONE ) { return (saberMoveName_t)saber2->jumpAtkBackMove; } } //no overrides, cancelled? if ( saber1 && saber1->jumpAtkBackMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } if ( saber2 && saber2->jumpAtkBackMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } //just do it pm->cmd.upmove = 127; pm->ps->velocity[2] = 500; return LS_A_BACKFLIP_ATK; } int PM_SaberDualJumpAttackMove( void ) { //FIXME: to make this move easier to execute, should be allowed to do it // after you've already started your jump... but jump is delayed in // this anim, so how do we undo the jump? pm->cmd.upmove = 0;//no jump just yet return LS_JUMPATTACK_DUAL; } #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 < ENTITYNUM_NONE) { bgEntity_t *bgEnt = PM_BGEntForNum(tr->entityNum); if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC)) { return qtrue; } } return qfalse; } saberMoveName_t PM_SaberLungeAttackMove( qboolean noSpecials ) { vec3_t fwdAngles, jumpFwd; saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); //see if we have an overridden (or cancelled) lunge move if ( saber1 && saber1->lungeAtkMove != LS_INVALID ) { if ( saber1->lungeAtkMove != LS_NONE ) { return (saberMoveName_t)saber1->lungeAtkMove; } } if ( saber2 && saber2->lungeAtkMove != LS_INVALID ) { if ( saber2->lungeAtkMove != LS_NONE ) { return (saberMoveName_t)saber2->lungeAtkMove; } } //no overrides, cancelled? if ( saber1 && saber1->lungeAtkMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } if ( saber2 && saber2->lungeAtkMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } //just do it if (pm->ps->fd.saberAnimLevel == SS_FAST) { 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; } else if ( !noSpecials && pm->ps->fd.saberAnimLevel == SS_STAFF) { return LS_SPINATTACK; } else if ( !noSpecials ) { return LS_SPINATTACK_DUAL; } return LS_A_T2B; } saberMoveName_t PM_SaberJumpAttackMove2( void ) { saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); //see if we have an overridden (or cancelled) lunge move if ( saber1 && saber1->jumpAtkFwdMove != LS_INVALID ) { if ( saber1->jumpAtkFwdMove != LS_NONE ) { return (saberMoveName_t)saber1->jumpAtkFwdMove; } } if ( saber2 && saber2->jumpAtkFwdMove != LS_INVALID ) { if ( saber2->jumpAtkFwdMove != LS_NONE ) { return (saberMoveName_t)saber2->jumpAtkFwdMove; } } //no overrides, cancelled? if ( saber1 && saber1->jumpAtkFwdMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } if ( saber2 && saber2->jumpAtkFwdMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } //just do it if (pm->ps->fd.saberAnimLevel == SS_DUAL) { return PM_SaberDualJumpAttackMove(); } else { //rwwFIXMEFIXME I don't like randomness for this sort of thing, gives people reason to //complain combat is unpredictable. Maybe do something more clever to determine //if we should do a left or right? /* if (PM_irand_timesync(0, 1)) { newmove = LS_JUMPATTACK_STAFF_LEFT; } else */ { return LS_JUMPATTACK_STAFF_RIGHT; } } return LS_A_T2B; } saberMoveName_t PM_SaberJumpAttackMove( void ) { vec3_t fwdAngles, jumpFwd; saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); //see if we have an overridden (or cancelled) lunge move if ( saber1 && saber1->jumpAtkFwdMove != LS_INVALID ) { if ( saber1->jumpAtkFwdMove != LS_NONE ) { return (saberMoveName_t)saber1->jumpAtkFwdMove; } } if ( saber2 && saber2->jumpAtkFwdMove != LS_INVALID ) { if ( saber2->jumpAtkFwdMove != LS_NONE ) { return (saberMoveName_t)saber2->jumpAtkFwdMove; } } //no overrides, cancelled? if ( saber1 && saber1->jumpAtkFwdMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } if ( saber2 && saber2->jumpAtkFwdMove == LS_NONE ) { return LS_A_T2B;//LS_NONE; } //just do it 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); } float PM_WalkableGroundDistance(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); if ( tr.plane.normal[2] < MIN_WALK_NORMAL ) {//can't stand on this plane return 4096; } VectorSubtract(pm->ps->origin, tr.endpos, down); return VectorLength(down); } qboolean BG_SaberInTransitionAny( int move ); static qboolean PM_CanDoDualDoubleAttacks(void) { if ( pm->ps->weapon == WP_SABER ) { saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 ); if ( saber && (saber->saberFlags&SFL_NO_MIRROR_ATTACKS) ) { return qfalse; } saber = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber && (saber->saberFlags&SFL_NO_MIRROR_ATTACKS) ) { return qfalse; } } if (BG_SaberInSpecialAttack(pm->ps->torsoAnim) || BG_SaberInSpecialAttack(pm->ps->legsAnim)) { return qfalse; } return qtrue; } static qboolean PM_CheckEnemyPresence( int dir, float radius ) { //anyone in this dir? vec3_t angles; vec3_t checkDir; vec3_t tTo; vec3_t tMins, tMaxs; trace_t tr; const float tSize = 12.0f; //sp uses a bbox ent list check, but.. that's not so easy/fast to //do in predicted code. So I'll just do a single box trace in the proper direction, //and take whatever is first hit. VectorSet(tMins, -tSize, -tSize, -tSize); VectorSet(tMaxs, tSize, tSize, tSize); VectorCopy(pm->ps->viewangles, angles); angles[PITCH] = 0.0f; switch( dir ) { case DIR_RIGHT: AngleVectors( angles, NULL, checkDir, NULL ); break; case DIR_LEFT: AngleVectors( angles, NULL, checkDir, NULL ); VectorScale( checkDir, -1, checkDir ); break; case DIR_FRONT: AngleVectors( angles, checkDir, NULL, NULL ); break; case DIR_BACK: AngleVectors( angles, checkDir, NULL, NULL ); VectorScale( checkDir, -1, checkDir ); break; } VectorMA(pm->ps->origin, radius, checkDir, tTo); pm->trace(&tr, pm->ps->origin, tMins, tMaxs, tTo, pm->ps->clientNum, MASK_PLAYERSOLID); if (tr.fraction != 1.0f && tr.entityNum < ENTITYNUM_WORLD) { //let's see who we hit bgEntity_t *bgEnt = PM_BGEntForNum(tr.entityNum); if (bgEnt && (bgEnt->s.eType == ET_PLAYER || bgEnt->s.eType == ET_NPC)) { //this guy can be considered an "enemy"... if he is on the same team, oh well. can't bg-check that (without a whole lot of hassle). return qtrue; } } //no one in the trace return qfalse; } #define SABER_ALT_ATTACK_POWER 50//75? #define SABER_ALT_ATTACK_POWER_LR 10//30? #define SABER_ALT_ATTACK_POWER_FB 25//30/50? extern qboolean PM_SaberInReturn( int move ); //bg_panimate.c saberMoveName_t PM_CheckPullAttack( void ) { #if 0 //disabling these for MP, they aren't useful if (!(pm->cmd.buttons & BUTTON_ATTACK)) { return LS_NONE; } if ( (pm->ps->saberMove == LS_READY||PM_SaberInReturn(pm->ps->saberMove)||PM_SaberInReflect(pm->ps->saberMove))//ready //&& (pm->ps->clientNum < MAX_CLIENTS||PM_ControlledByPlayer())//PLAYER ONLY && pm->ps->fd.saberAnimLevel >= SS_FAST//single saber styles - FIXME: Tavion? && pm->ps->fd.saberAnimLevel <= SS_STRONG//single saber styles - FIXME: Tavion? //&& G_TryingPullAttack( pm->gent, &pm->cmd, qfalse ) //&& pm->ps->fd.forcePowerLevel[FP_PULL] //rwwFIXMEFIXME: rick has the damn msg.cpp file checked out exclusively so I can't update the bloody psf to send this for prediction && pm->ps->powerups[PW_DISINT_4] > pm->cmd.serverTime && !(pm->ps->fd.forcePowersActive & (1<ps->powerups[PW_PULL] > pm->cmd.serverTime //&& pm->cmd.forwardmove<0//pulling back && (pm->cmd.buttons&BUTTON_ATTACK)//attacking && BG_EnoughForcePowerForMove( SABER_ALT_ATTACK_POWER_FB ) )//pm->ps->forcePower >= SABER_ALT_ATTACK_POWER_FB//have enough power {//FIXME: some NPC logic to do this? qboolean doMove = qtrue; // if ( g_saberNewControlScheme->integer // || g_crosshairEntNum < ENTITYNUM_WORLD )//in old control scheme, there has to be someone there { saberMoveName_t pullAttackMove = LS_NONE; if ( pm->ps->fd.saberAnimLevel == SS_FAST ) { pullAttackMove = LS_PULL_ATTACK_STAB; } else { pullAttackMove = LS_PULL_ATTACK_SWING; } /* if ( g_crosshairEntNum < ENTITYNUM_WORLD && pm->gent && pm->gent->client ) { gentity_t *targEnt = &g_entities[g_crosshairEntNum]; if ( targEnt->client && targEnt->health > 0 //FIXME: check other things like in knockdown, saberlock, uninterruptable anims, etc. && !PM_InOnGroundAnim( &targEnt->client->ps ) && !PM_LockedAnim( targEnt->client->ps.legsAnim ) && !PM_SuperBreakLoseAnim( targEnt->client->ps.legsAnim ) && !PM_SuperBreakWinAnim( targEnt->client->ps.legsAnim ) && targEnt->client->ps.saberLockTime <= 0 && WP_ForceThrowable( targEnt, targEnt, pm->gent, qtrue, 1.0f, 0.0f, NULL ) ) { if ( !g_saberNewControlScheme->integer ) {//in old control scheme, make sure they're close or far enough away for the move we'll be doing float targDist = Distance( targEnt->currentOrigin, pm->ps->origin ); if ( pullAttackMove == LS_PULL_ATTACK_STAB ) {//must be closer than 512 if ( targDist > 384.0f ) { return LS_NONE; } } else//if ( pullAttackMove == LS_PULL_ATTACK_SWING ) {//must be farther than 256 if ( targDist > 512.0f ) { return LS_NONE; } if ( targDist < 192.0f ) { return LS_NONE; } } } vec3_t targAngles = {0,targEnt->client->ps.viewangles[YAW],0}; if ( InFront( pm->ps->origin, targEnt->currentOrigin, targAngles ) ) { NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_F, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); } else { NPC_SetAnim( targEnt, SETANIM_BOTH, BOTH_PULLED_INAIR_B, SETANIM_FLAG_OVERRIDE, SETANIM_FLAG_HOLD ); } //hold the anim until I'm with done pull anim targEnt->client->ps.legsAnimTimer = targEnt->client->ps.torsoAnimTimer = PM_AnimLength( pm->gent->client->clientInfo.animFileIndex, (animNumber_t)saberMoveData[pullAttackMove].animToUse ); //set pullAttackTime pm->gent->client->ps.pullAttackTime = targEnt->client->ps.pullAttackTime = level.time+targEnt->client->ps.legsAnimTimer; //make us know about each other pm->gent->client->ps.pullAttackEntNum = g_crosshairEntNum; targEnt->client->ps.pullAttackEntNum = pm->ps->clientNum; //do effect and sound on me pm->ps->powerups[PW_FORCE_PUSH] = level.time + 1000; if ( pm->gent ) { G_Sound( pm->gent, G_SoundIndex( "sound/weapons/force/pull.wav" ) ); } doMove = qtrue; } } */ if ( doMove ) { BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB ); return pullAttackMove; } } } #endif return LS_NONE; } qboolean PM_InSecondaryStyle( void ) { if ( pm->ps->fd.saberAnimLevelBase == SS_STAFF || pm->ps->fd.saberAnimLevelBase == SS_DUAL ) { if ( pm->ps->fd.saberAnimLevel != pm->ps->fd.saberAnimLevelBase ) { return qtrue; } } return qfalse; } saberMoveName_t PM_SaberAttackForMovement(saberMoveName_t curmove) { saberMoveName_t newmove = LS_NONE; qboolean noSpecials = PM_InSecondaryStyle(); qboolean allowCartwheels = qtrue; saberMoveName_t overrideJumpRightAttackMove = LS_INVALID; saberMoveName_t overrideJumpLeftAttackMove = LS_INVALID; if ( pm->ps->weapon == WP_SABER ) { saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber1 && saber1->jumpAtkRightMove != LS_INVALID ) { if ( saber1->jumpAtkRightMove != LS_NONE ) {//actually overriding overrideJumpRightAttackMove = (saberMoveName_t)saber1->jumpAtkRightMove; } else if ( saber2 && saber2->jumpAtkRightMove > LS_NONE ) {//would be cancelling it, but check the second saber, too overrideJumpRightAttackMove = (saberMoveName_t)saber2->jumpAtkRightMove; } else {//nope, just cancel it overrideJumpRightAttackMove = LS_NONE; } } else if ( saber2 && saber2->jumpAtkRightMove != LS_INVALID ) {//first saber not overridden, check second overrideJumpRightAttackMove = (saberMoveName_t)saber2->jumpAtkRightMove; } if ( saber1 && saber1->jumpAtkLeftMove != LS_INVALID ) { if ( saber1->jumpAtkLeftMove != LS_NONE ) {//actually overriding overrideJumpLeftAttackMove = (saberMoveName_t)saber1->jumpAtkLeftMove; } else if ( saber2 && saber2->jumpAtkLeftMove > LS_NONE ) {//would be cancelling it, but check the second saber, too overrideJumpLeftAttackMove = (saberMoveName_t)saber2->jumpAtkLeftMove; } else {//nope, just cancel it overrideJumpLeftAttackMove = LS_NONE; } } else if ( saber2 && saber2->jumpAtkLeftMove != LS_INVALID ) {//first saber not overridden, check second overrideJumpLeftAttackMove = (saberMoveName_t)saber1->jumpAtkLeftMove; } if ( saber1 && (saber1->saberFlags&SFL_NO_CARTWHEELS) ) { allowCartwheels = qfalse; } if ( saber2 && (saber2->saberFlags&SFL_NO_CARTWHEELS) ) { allowCartwheels = qfalse; } } if ( pm->cmd.rightmove > 0 ) {//moving right if ( !noSpecials && overrideJumpRightAttackMove != LS_NONE && pm->ps->velocity[2] > 20.0f //pm->ps->groundEntityNum != ENTITYNUM_NONE//on ground && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack && PM_GroundDistance() < 70.0f //not too high above ground && ( pm->cmd.upmove > 0 || (pm->ps->pm_flags & PMF_JUMP_HELD) )//focus-holding player && BG_EnoughForcePowerForMove( SABER_ALT_ATTACK_POWER_LR ) )//have enough power {//cartwheel right BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_LR); if ( overrideJumpRightAttackMove != LS_INVALID ) {//overridden with another move return overrideJumpRightAttackMove; } else { vec3_t right, fwdAngles; VectorSet(fwdAngles, 0.0f, pm->ps->viewangles[YAW], 0.0f); AngleVectors( fwdAngles, NULL, right, NULL ); pm->ps->velocity[0] = pm->ps->velocity[1] = 0.0f; VectorMA( pm->ps->velocity, 190.0f, right, pm->ps->velocity ); if ( pm->ps->fd.saberAnimLevel == SS_STAFF ) { newmove = LS_BUTTERFLY_RIGHT; pm->ps->velocity[2] = 350.0f; } else if ( allowCartwheels ) { //PM_SetJumped( JUMP_VELOCITY, qtrue ); PM_AddEvent( EV_JUMP ); pm->ps->velocity[2] = 300.0f; //if ( !Q_irand( 0, 1 ) ) //if (PM_GroundDistance() >= 25.0f) if (1) { newmove = LS_JUMPATTACK_ARIAL_RIGHT; } else { newmove = LS_JUMPATTACK_CART_RIGHT; } } } } else 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 ( !noSpecials && overrideJumpLeftAttackMove != LS_NONE && pm->ps->velocity[2] > 20.0f //pm->ps->groundEntityNum != ENTITYNUM_NONE//on ground && (pm->cmd.buttons&BUTTON_ATTACK)//hitting attack && PM_GroundDistance() < 70.0f //not too high above ground && ( pm->cmd.upmove > 0 || (pm->ps->pm_flags & PMF_JUMP_HELD) )//focus-holding player && BG_EnoughForcePowerForMove( SABER_ALT_ATTACK_POWER_LR ) )//have enough power {//cartwheel left BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_LR); if ( overrideJumpLeftAttackMove != LS_INVALID ) {//overridden with another move return overrideJumpLeftAttackMove; } else { vec3_t right, fwdAngles; VectorSet(fwdAngles, 0.0f, pm->ps->viewangles[YAW], 0.0f); AngleVectors( fwdAngles, NULL, right, NULL ); pm->ps->velocity[0] = pm->ps->velocity[1] = 0.0f; VectorMA( pm->ps->velocity, -190.0f, right, pm->ps->velocity ); if ( pm->ps->fd.saberAnimLevel == SS_STAFF ) { newmove = LS_BUTTERFLY_LEFT; pm->ps->velocity[2] = 250.0f; } else if ( allowCartwheels ) { //PM_SetJumped( JUMP_VELOCITY, qtrue ); PM_AddEvent( EV_JUMP ); pm->ps->velocity[2] = 350.0f; //if ( !Q_irand( 0, 1 ) ) //if (PM_GroundDistance() >= 25.0f) if (1) { newmove = LS_JUMPATTACK_ARIAL_LEFT; } else { newmove = LS_JUMPATTACK_CART_LEFT; } } } } else 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 (!noSpecials&& (pm->ps->fd.saberAnimLevel == SS_DUAL || pm->ps->fd.saberAnimLevel == SS_STAFF) && pm->ps->fd.forceRageRecoveryTime < pm->cmd.serverTime && //pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 && (pm->ps->groundEntityNum != ENTITYNUM_NONE || PM_GroundDistance() <= 40) && pm->ps->velocity[2] >= 0 && (pm->cmd.upmove > 0 || pm->ps->pm_flags & PMF_JUMP_HELD) && !BG_SaberInTransitionAny(pm->ps->saberMove) && !BG_SaberInAttack(pm->ps->saberMove) && pm->ps->weaponTime <= 0 && pm->ps->forceHandExtend == HANDEXTEND_NONE && (pm->cmd.buttons & BUTTON_ATTACK)&& BG_EnoughForcePowerForMove(SABER_ALT_ATTACK_POWER_FB) ) { //DUAL/STAFF JUMP ATTACK newmove = PM_SaberJumpAttackMove2(); if ( newmove != LS_A_T2B && newmove != LS_NONE ) { BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB); } } else if (!noSpecials&& pm->ps->fd.saberAnimLevel == SS_MEDIUM && pm->ps->velocity[2] > 100 && PM_GroundDistance() < 32 && !BG_InSpecialJump(pm->ps->legsAnim) && !BG_SaberInSpecialAttack(pm->ps->torsoAnim)&& BG_EnoughForcePowerForMove(SABER_ALT_ATTACK_POWER_FB)) { //FLIP AND DOWNWARD ATTACK //trace_t tr; //if (PM_SomeoneInFront(&tr)) { newmove = PM_SaberFlipOverAttackMove(); if ( newmove != LS_A_T2B && newmove != LS_NONE ) { BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB); } } } else if (!noSpecials&& pm->ps->fd.saberAnimLevel == SS_STRONG && pm->ps->velocity[2] > 100 && PM_GroundDistance() < 32 && !BG_InSpecialJump(pm->ps->legsAnim) && !BG_SaberInSpecialAttack(pm->ps->torsoAnim)&& BG_EnoughForcePowerForMove( SABER_ALT_ATTACK_POWER_FB )) { //DFA //trace_t tr; //if (PM_SomeoneInFront(&tr)) { newmove = PM_SaberJumpAttackMove(); if ( newmove != LS_A_T2B && newmove != LS_NONE ) { BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB); } } } else if ((pm->ps->fd.saberAnimLevel == SS_FAST || pm->ps->fd.saberAnimLevel == SS_DUAL || pm->ps->fd.saberAnimLevel == SS_STAFF) && pm->ps->groundEntityNum != ENTITYNUM_NONE && (pm->ps->pm_flags & PMF_DUCKED) && pm->ps->weaponTime <= 0 && !BG_SaberInSpecialAttack(pm->ps->torsoAnim)&& BG_EnoughForcePowerForMove(SABER_ALT_ATTACK_POWER_FB)) { //LUNGE (weak) newmove = PM_SaberLungeAttackMove( noSpecials ); if ( newmove != LS_A_T2B && newmove != LS_NONE ) { BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB); } } else if ( !noSpecials ) { saberMoveName_t stabDownMove = PM_CheckStabDown(); if (stabDownMove != LS_NONE && BG_EnoughForcePowerForMove(SABER_ALT_ATTACK_POWER_FB) ) { newmove = stabDownMove; BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB); } else { newmove = LS_A_T2B; } } } else if ( pm->cmd.forwardmove < 0 ) {//backward= T2B slash//B2T uppercut? if (!noSpecials&& pm->ps->fd.saberAnimLevel == SS_STAFF && pm->ps->fd.forceRageRecoveryTime < pm->cmd.serverTime && pm->ps->fd.forcePowerLevel[FP_LEVITATION] > FORCE_LEVEL_1 && (pm->ps->groundEntityNum != ENTITYNUM_NONE || PM_GroundDistance() <= 40) && pm->ps->velocity[2] >= 0 && (pm->cmd.upmove > 0 || pm->ps->pm_flags & PMF_JUMP_HELD) && !BG_SaberInTransitionAny(pm->ps->saberMove) && !BG_SaberInAttack(pm->ps->saberMove) && pm->ps->weaponTime <= 0 && pm->ps->forceHandExtend == HANDEXTEND_NONE && (pm->cmd.buttons & BUTTON_ATTACK)) { //BACKFLIP ATTACK newmove = PM_SaberBackflipAttackMove(); } else if (PM_CanBackstab() && !BG_SaberInSpecialAttack(pm->ps->torsoAnim)) { //BACKSTAB (attack varies by level) if (pm->ps->fd.saberAnimLevel >= FORCE_LEVEL_2 && pm->ps->fd.saberAnimLevel != SS_STAFF) {//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. } } if (pm->ps->fd.saberAnimLevel == SS_DUAL) { if ( ( newmove == LS_A_R2L || newmove == LS_S_R2L || newmove == LS_A_L2R || newmove == LS_S_L2R ) && PM_CanDoDualDoubleAttacks() && PM_CheckEnemyPresence( DIR_RIGHT, 100.0f ) && PM_CheckEnemyPresence( DIR_LEFT, 100.0f ) ) {//enemy both on left and right newmove = LS_DUAL_LR; //probably already moved, but... pm->cmd.rightmove = 0; } else if ( (newmove == LS_A_T2B || newmove == LS_S_T2B || newmove == LS_A_BACK || newmove == LS_A_BACK_CR ) && PM_CanDoDualDoubleAttacks() && PM_CheckEnemyPresence( DIR_FRONT, 100.0f ) && PM_CheckEnemyPresence( DIR_BACK, 100.0f ) ) {//enemy both in front and back newmove = LS_DUAL_FB; //probably already moved, but... pm->cmd.forwardmove = 0; } } return newmove; } int PM_KickMoveForConditions(void) { int kickMove = -1; //FIXME: only if FP_SABER_OFFENSE >= 3 if ( pm->cmd.rightmove ) {//kick to side if ( pm->cmd.rightmove > 0 ) {//kick right kickMove = LS_KICK_R; } else {//kick left kickMove = LS_KICK_L; } pm->cmd.rightmove = 0; } else if ( pm->cmd.forwardmove ) {//kick front/back if ( pm->cmd.forwardmove > 0 ) {//kick fwd /* if (pm->ps->groundEntityNum != ENTITYNUM_NONE && PM_CheckEnemyPresence( DIR_FRONT, 64.0f )) { kickMove = LS_HILT_BASH; } else */ { kickMove = LS_KICK_F; } } else {//kick back kickMove = LS_KICK_B; } pm->cmd.forwardmove = 0; } else { //if (pm->cmd.buttons & BUTTON_ATTACK) //if (pm->ps->pm_flags & PMF_JUMP_HELD) if (0) { //ok, let's try some fancy kicks //qboolean is actually of type int anyway, but just for safeness. int front = (int)PM_CheckEnemyPresence( DIR_FRONT, 100.0f ); int back = (int)PM_CheckEnemyPresence( DIR_BACK, 100.0f ); int right = (int)PM_CheckEnemyPresence( DIR_RIGHT, 100.0f ); int left = (int)PM_CheckEnemyPresence( DIR_LEFT, 100.0f ); int numEnemy = front+back+right+left; if (numEnemy >= 3 || ((!right || !left) && numEnemy >= 2)) { //> 2 enemies near, or, >= 2 enemies near and they are not to the right and left. kickMove = LS_KICK_S; } else if (right && left) { //enemies on both sides kickMove = LS_KICK_RL; } else { //oh well, just do a forward kick kickMove = LS_KICK_F; } pm->cmd.upmove = 0; } } return kickMove; } qboolean BG_InSlopeAnim( int anim ); qboolean PM_RunningAnim( int anim ); qboolean PM_SaberMoveOkayForKata( void ) { if ( pm->ps->saberMove == LS_READY || PM_SaberInStart( pm->ps->saberMove ) ) { return qtrue; } else { return qfalse; } } qboolean PM_CanDoKata( void ) { if ( PM_InSecondaryStyle() ) { return qfalse; } if ( !pm->ps->saberInFlight//not throwing saber && PM_SaberMoveOkayForKata() && !BG_SaberInKata(pm->ps->saberMove) && !BG_InKataAnim(pm->ps->legsAnim) && !BG_InKataAnim(pm->ps->torsoAnim) /* && pm->ps->saberAnimLevel >= SS_FAST//fast, med or strong style && pm->ps->saberAnimLevel <= SS_STRONG//FIXME: Tavion, too? */ && pm->ps->groundEntityNum != ENTITYNUM_NONE//not in the air && (pm->cmd.buttons&BUTTON_ATTACK)//pressing attack && (pm->cmd.buttons&BUTTON_ALT_ATTACK)//pressing alt attack && !pm->cmd.forwardmove//not moving f/b && !pm->cmd.rightmove//not moving r/l && pm->cmd.upmove <= 0//not jumping...? && BG_EnoughForcePowerForMove(SABER_ALT_ATTACK_POWER) )// have enough power {//FIXME: check rage, etc... saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 ); if ( saber && saber->kataMove == LS_NONE ) {//kata move has been overridden in a way that should stop you from doing it at all return qfalse; } saber = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber && saber->kataMove == LS_NONE ) {//kata move has been overridden in a way that should stop you from doing it at all return qfalse; } return qtrue; } return qfalse; } qboolean PM_CheckAltKickAttack( void ) { if ( pm->ps->weapon == WP_SABER ) { saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 ); if ( saber && (saber->saberFlags&SFL_NO_KICKS) ) { return qfalse; } saber = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber && (saber->saberFlags&SFL_NO_KICKS) ) { return qfalse; } } if ( (pm->cmd.buttons&BUTTON_ALT_ATTACK) //&& (!(pm->ps->pm_flags&PMF_ALT_ATTACK_HELD)||PM_SaberInReturn(pm->ps->saberMove)) && (!BG_FlippingAnim(pm->ps->legsAnim)||pm->ps->legsTimer<=250) && (pm->ps->fd.saberAnimLevel == SS_STAFF/*||!pm->ps->saber[0].throwable*/) && !pm->ps->saberHolstered ) { return qtrue; } return qfalse; } int bg_parryDebounce[NUM_FORCE_POWER_LEVELS] = { 500,//if don't even have defense, can't use defense! 300, 150, 50 }; qboolean PM_SaberPowerCheck(void) { if (pm->ps->saberInFlight) { //so we don't keep doing stupid force out thing while guiding saber. if (pm->ps->fd.forcePower > forcePowerNeeded[pm->ps->fd.forcePowerLevel[FP_SABERTHROW]][FP_SABERTHROW]) { return qtrue; } } else { return BG_EnoughForcePowerForMove(forcePowerNeeded[pm->ps->fd.forcePowerLevel[FP_SABERTHROW]][FP_SABERTHROW]); } return qfalse; } qboolean PM_CanDoRollStab( void ) { if ( pm->ps->weapon == WP_SABER ) { saberInfo_t *saber = BG_MySaber( pm->ps->clientNum, 0 ); if ( saber && (saber->saberFlags&SFL_NO_ROLL_STAB) ) { return qfalse; } saber = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber && (saber->saberFlags&SFL_NO_ROLL_STAB) ) { return qfalse; } } return qtrue; } /* ================= 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 qboolean PM_WalkingAnim( int anim ); qboolean PM_SwimmingAnim( int anim ); int PM_SaberBounceForAttack( int move ); qboolean BG_SuperBreakLoseAnim( int anim ); qboolean BG_SuperBreakWinAnim( int anim ); void PM_WeaponLightsaber(void) { int addTime,amount; qboolean delayed_fire = qfalse; int anim=-1, curmove, newmove=LS_NONE; qboolean checkOnlyWeap = qfalse; if ( PM_InKnockDown( pm->ps ) || BG_InRoll( pm->ps, pm->ps->legsAnim )) {//in knockdown // make weapon function if ( pm->ps->weaponTime > 0 ) { pm->ps->weaponTime -= pml.msec; if ( pm->ps->weaponTime <= 0 ) { pm->ps->weaponTime = 0; } } if ( pm->ps->legsAnim == BOTH_ROLL_F && pm->ps->legsTimer <= 250 ) { if ( (pm->cmd.buttons&BUTTON_ATTACK) ) { if ( BG_EnoughForcePowerForMove(SABER_ALT_ATTACK_POWER_FB) && !pm->ps->saberInFlight ) { if ( PM_CanDoRollStab() ) { //make sure the saber is on for this move! if ( pm->ps->saberHolstered == 2 ) {//all the way off pm->ps->saberHolstered = 0; PM_AddEvent(EV_SABER_UNHOLSTER); } PM_SetSaberMove( LS_ROLL_STAB ); BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER_FB); } } } } return; } if ( pm->ps->saberLockTime > pm->cmd.serverTime ) { pm->ps->saberMove = LS_NONE; PM_SaberLocked(); return; } else { if ( /*( (pm->ps->torsoAnim) == BOTH_BF2LOCK || (pm->ps->torsoAnim) == BOTH_BF1LOCK || (pm->ps->torsoAnim) == BOTH_CWCIRCLELOCK || (pm->ps->torsoAnim) == BOTH_CCWCIRCLELOCK ||*/ pm->ps->saberLockFrame ) { if (pm->ps->saberLockEnemy < ENTITYNUM_NONE && pm->ps->saberLockEnemy >= 0) { bgEntity_t *bgEnt; playerState_t *en; bgEnt = PM_BGEntForNum(pm->ps->saberLockEnemy); if (bgEnt) { en = bgEnt->playerState; if (en) { PM_SaberLockBreak(en, qfalse, 0); return; } } } if (/* ( (pm->ps->torsoAnim) == BOTH_BF2LOCK || (pm->ps->torsoAnim) == BOTH_BF1LOCK || (pm->ps->torsoAnim) == BOTH_CWCIRCLELOCK || (pm->ps->torsoAnim) == BOTH_CCWCIRCLELOCK ||*/ pm->ps->saberLockFrame ) { pm->ps->torsoTimer = 0; PM_SetAnim(SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_OVERRIDE, 100); pm->ps->saberLockFrame = 0; } } } if ( BG_KickingAnim( pm->ps->legsAnim ) || BG_KickingAnim( pm->ps->torsoAnim )) { if ( pm->ps->legsTimer > 0 ) {//you're kicking, no interruptions return; } //done? be immeditately ready to do an attack pm->ps->saberMove = LS_READY; pm->ps->weaponTime = 0; } if ( BG_SuperBreakLoseAnim( pm->ps->torsoAnim ) || BG_SuperBreakWinAnim( pm->ps->torsoAnim ) ) { if ( pm->ps->torsoTimer > 0 ) {//never interrupt these return; } } if (BG_SabersOff( pm->ps )) { if (pm->ps->saberMove != LS_READY) { PM_SetSaberMove( LS_READY ); } if ((pm->ps->legsAnim) != (pm->ps->torsoAnim) && !BG_InSlopeAnim(pm->ps->legsAnim) && pm->ps->torsoTimer <= 0) { PM_SetAnim(SETANIM_TORSO,(pm->ps->legsAnim),SETANIM_FLAG_OVERRIDE, 100); } else if (BG_InSlopeAnim(pm->ps->legsAnim) && pm->ps->torsoTimer <= 0) { PM_SetAnim(SETANIM_TORSO,PM_GetSaberStance(),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) { if (!pm->ps->m_iVehicleNum) { //don't let em unholster the saber by attacking while on vehicle pm->ps->saberHolstered = 0; PM_AddEvent(EV_SABER_UNHOLSTER); } else { pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; pm->cmd.buttons &= ~BUTTON_ATTACK; } } } if ( pm->ps->weaponTime > 0 ) { pm->ps->weaponTime -= pml.msec; } checkOnlyWeap = qtrue; goto weapChecks; } if (!pm->ps->saberEntityNum && pm->ps->saberInFlight) { //this means our saber has been knocked away /* if (pm->ps->saberMove != LS_READY) { PM_SetSaberMove( LS_READY ); } if ((pm->ps->legsAnim) != (pm->ps->torsoAnim) && !BG_InSlopeAnim(pm->ps->legsAnim)) { PM_SetAnim(SETANIM_TORSO,(pm->ps->legsAnim),SETANIM_FLAG_OVERRIDE, 100); } if (BG_InSaberStandAnim(pm->ps->torsoAnim) || pm->ps->torsoAnim == BOTH_SABERPULL) { PM_SetAnim(SETANIM_TORSO,BOTH_STAND1,SETANIM_FLAG_OVERRIDE, 100); } return; */ //Old method, don't want to do this now because we want to finish up reflected attacks and things //if our saber is pried out of our hands from one. if ( pm->ps->fd.saberAnimLevel == SS_DUAL ) { if ( pm->ps->saberHolstered > 1 ) { pm->ps->saberHolstered = 1; } } else { pm->cmd.buttons &= ~BUTTON_ATTACK; } pm->cmd.buttons &= ~BUTTON_ALT_ATTACK; } if ( (pm->cmd.buttons & BUTTON_ALT_ATTACK) ) { //might as well just check for a saber throw right here if (pm->ps->fd.saberAnimLevel == SS_STAFF) { //kick instead of doing a throw //if in a saber attack return anim, can interrupt it with a kick if ( pm->ps->weaponTime > 0//can't fire yet && PM_SaberInReturn( pm->ps->saberMove )//in a saber return move - FIXME: what about transitions? //&& pm->ps->weaponTime <= 250//should be able to fire soon //&& pm->ps->torsoTimer <= 250//torso almost done && pm->ps->saberBlocked == BLOCKED_NONE//not interacting with any other saber && !(pm->cmd.buttons&BUTTON_ATTACK) )//not trying to swing the saber { if ( (pm->cmd.forwardmove||pm->cmd.rightmove)//trying to kick in a specific direction && PM_CheckAltKickAttack() )//trying to do a kick {//allow them to do the kick now! int kickMove = PM_KickMoveForConditions(); if (kickMove != -1) { pm->ps->weaponTime = 0; PM_SetSaberMove( kickMove ); return; } } } } else if ( 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) && pm->ps->fd.forcePowerLevel[FP_SABERTHROW] > 0 && PM_SaberPowerCheck() ) { trace_t sabTr; vec3_t fwd, minFwd, sabMins, sabMaxs; VectorSet( sabMins, SABERMINS_X, SABERMINS_Y, SABERMINS_Z ); VectorSet( sabMaxs, SABERMAXS_X, SABERMAXS_Y, SABERMAXS_Z ); AngleVectors( pm->ps->viewangles, fwd, NULL, NULL ); VectorMA( pm->ps->origin, SABER_MIN_THROW_DIST, fwd, minFwd ); pm->trace(&sabTr, pm->ps->origin, sabMins, sabMaxs, minFwd, pm->ps->clientNum, MASK_PLAYERSOLID); if ( sabTr.allsolid || sabTr.startsolid || sabTr.fraction < 1.0f ) {//not enough room to throw } else {//throw it //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 && pm->ps->saberEntityNum ) {//guiding saber if ( (pm->ps->fd.saberAnimLevel != SS_DUAL //not using 2 sabers || pm->ps->saberHolstered //left one off - FIXME: saberHolstered 1 should be left one off, 0 should be both on, 2 should be both off || (!(pm->cmd.buttons&BUTTON_ATTACK)//not trying to start an attack AND... && (pm->ps->torsoAnim == BOTH_SABERDUAL_STANCE//not already attacking || pm->ps->torsoAnim == BOTH_SABERPULL//not already attacking || pm->ps->torsoAnim == BOTH_STAND1//not already attacking || PM_RunningAnim( pm->ps->torsoAnim ) //not already attacking || PM_WalkingAnim( pm->ps->torsoAnim ) //not already attacking || PM_JumpingAnim( pm->ps->torsoAnim )//not already attacking || PM_SwimmingAnim( pm->ps->torsoAnim ))//not already attacking ) ) ) { 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_RunningAnim(pm->ps->torsoAnim)) { if ((pm->ps->torsoAnim) != (pm->ps->legsAnim)) { PM_SetAnim(SETANIM_TORSO,(pm->ps->legsAnim),SETANIM_FLAG_OVERRIDE, 100); } } */ // make weapon function if ( pm->ps->weaponTime > 0 ) { //check for special pull move while busy saberMoveName_t pullmove = PM_CheckPullAttack(); if (pullmove != LS_NONE) { pm->ps->weaponTime = 0; pm->ps->torsoTimer = 0; pm->ps->legsTimer = 0; pm->ps->forceHandExtend = HANDEXTEND_NONE; pm->ps->weaponstate = WEAPON_READY; PM_SetSaberMove(pullmove); return; } pm->ps->weaponTime -= pml.msec; //This was stupid and didn't work right. Looks like things are fine without it. // 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 ) { if ( pm->ps->saberBlocked >= BLOCKED_UPPER_RIGHT && pm->ps->saberBlocked < BLOCKED_UPPER_RIGHT_PROJ) {//hold the parry for a bit pm->ps->weaponTime = bg_parryDebounce[pm->ps->fd.forcePowerLevel[FP_SABER_DEFENSE]]+200; } 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->ps->torsoTimer = 0; 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? } } 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_SaberInBounce( pm->ps->saberMove ) || !BG_SaberInAttack( pm->ps->saberMove ) ) { 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; } } } else {//start the bounce bounceMove = PM_SaberBounceForAttack( (saberMoveName_t)pm->ps->saberMove ); } 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_UPPER_RIGHT && pm->ps->saberBlocked < BLOCKED_UPPER_RIGHT_PROJ) {//hold the parry for a bit if ( pm->ps->torsoTimer < pm->ps->weaponTime ) { pm->ps->torsoTimer = pm->ps->weaponTime; } } //what the? I don't know why I was doing this. /* 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; } */ //clear block pm->ps->saberBlocked = 0; // 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: if (pm->ps->saberEntityNum) { //only check if we have our saber with us // 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->weaponTime <= 0 && pm->ps->torsoTimer <= 0) { if ( pm->ps->weapon != pm->cmd.weapon ) { PM_BeginWeaponChange( pm->cmd.weapon ); } } } if ( PM_CanDoKata() ) { saberMoveName_t overrideMove = LS_INVALID; saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); //see if we have an overridden (or cancelled) kata move if ( saber1 && saber1->kataMove != LS_INVALID ) { if ( saber1->kataMove != LS_NONE ) { overrideMove = (saberMoveName_t)saber1->kataMove; } } if ( overrideMove == LS_INVALID ) {//not overridden by first saber, check second if ( saber2 && saber2->kataMove != LS_INVALID ) { if ( saber2->kataMove != LS_NONE ) { overrideMove = (saberMoveName_t)saber2->kataMove; } } } //no overrides, cancelled? if ( overrideMove == LS_INVALID ) { if ( saber2 && saber2->kataMove == LS_NONE ) { overrideMove = LS_NONE; } else if ( saber2 && saber2->kataMove == LS_NONE ) { overrideMove = LS_NONE; } } if ( overrideMove == LS_INVALID ) {//not overridden //FIXME: make sure to turn on saber(s)! switch ( pm->ps->fd.saberAnimLevel ) { case SS_FAST: case SS_TAVION: PM_SetSaberMove( LS_A1_SPECIAL ); break; case SS_MEDIUM: PM_SetSaberMove( LS_A2_SPECIAL ); break; case SS_STRONG: case SS_DESANN: PM_SetSaberMove( LS_A3_SPECIAL ); break; case SS_DUAL: PM_SetSaberMove( LS_DUAL_SPIN_PROTECT );//PM_CheckDualSpinProtect(); break; case SS_STAFF: PM_SetSaberMove( LS_STAFF_SOULCAL ); break; } pm->ps->weaponstate = WEAPON_FIRING; //G_DrainPowerForSpecialMove( pm->gent, FP_SABER_OFFENSE, SABER_ALT_ATTACK_POWER );//FP_SPEED, SINGLE_SPECIAL_POWER ); BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER); } else if ( overrideMove != LS_NONE ) { PM_SetSaberMove( overrideMove ); pm->ps->weaponstate = WEAPON_FIRING; BG_ForcePowerDrain(pm->ps, FP_GRIP, SABER_ALT_ATTACK_POWER); } if ( overrideMove != LS_NONE ) {//not cancelled return; } } 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) == BOTH_WALK1 ) { PM_SetAnim(SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL, 100); } else if((pm->ps->legsAnim) == BOTH_RUN1 ) { PM_SetAnim(SETANIM_TORSO,BOTH_RUN1,SETANIM_FLAG_NORMAL, 100); } else if((pm->ps->legsAnim) == BOTH_RUN2 ) { PM_SetAnim(SETANIM_TORSO,BOTH_RUN2,SETANIM_FLAG_NORMAL, 100); } else if( pm->ps->legsAnim == BOTH_RUN_STAFF) { PM_SetAnim(SETANIM_TORSO,BOTH_RUN_STAFF,SETANIM_FLAG_NORMAL, 100); } else if( pm->ps->legsAnim == BOTH_RUN_DUAL) { PM_SetAnim(SETANIM_TORSO,BOTH_RUN_DUAL,SETANIM_FLAG_NORMAL, 100); } else if((pm->ps->legsAnim) == BOTH_WALK1 ) { PM_SetAnim(SETANIM_TORSO,BOTH_WALK1,SETANIM_FLAG_NORMAL, 100); } else if( pm->ps->legsAnim == BOTH_WALK2) { PM_SetAnim(SETANIM_TORSO,BOTH_WALK2,SETANIM_FLAG_NORMAL, 100); } else if( pm->ps->legsAnim == BOTH_WALK_STAFF) { PM_SetAnim(SETANIM_TORSO,BOTH_WALK_STAFF,SETANIM_FLAG_NORMAL, 100); } else if( pm->ps->legsAnim == BOTH_WALK_DUAL) { PM_SetAnim(SETANIM_TORSO,BOTH_WALK_DUAL,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 (pm->ps->fd.saberAnimLevel == SS_STAFF && (pm->cmd.buttons & BUTTON_ALT_ATTACK)) { //ok, try a kick I guess. int kickMove = -1; if ( !BG_KickingAnim(pm->ps->torsoAnim) && !BG_KickingAnim(pm->ps->legsAnim) && !BG_InRoll(pm->ps, pm->ps->legsAnim) && // !BG_KickMove( pm->ps->saberMove )//not already in a kick pm->ps->saberMove == LS_READY && !(pm->ps->pm_flags&PMF_DUCKED)//not ducked && (pm->cmd.upmove >= 0 ) //not trying to duck )//&& pm->ps->groundEntityNum != ENTITYNUM_NONE) {//player kicks kickMove = PM_KickMoveForConditions(); } if (kickMove != -1) { if ( pm->ps->groundEntityNum == ENTITYNUM_NONE ) {//if in air, convert kick to an in-air kick float gDist = PM_GroundDistance(); //let's only allow air kicks if a certain distance from the ground //it's silly to be able to do them right as you land. //also looks wrong to transition from a non-complete flip anim... if ((!BG_FlippingAnim( pm->ps->legsAnim ) || pm->ps->legsTimer <= 0) && gDist > 64.0f && //strict minimum gDist > (-pm->ps->velocity[2])-64.0f //make sure we are high to ground relative to downward velocity as well ) { switch ( kickMove ) { case LS_KICK_F: kickMove = LS_KICK_F_AIR; break; case LS_KICK_B: kickMove = LS_KICK_B_AIR; break; case LS_KICK_R: kickMove = LS_KICK_R_AIR; break; case LS_KICK_L: kickMove = LS_KICK_L_AIR; break; default: //oh well, can't do any other kick move while in-air kickMove = -1; break; } } else {//leave it as a normal kick unless we're too high up if ( gDist > 128.0f || pm->ps->velocity[2] >= 0 ) { //off ground, but too close to ground kickMove = -1; } } } if (kickMove != -1) { PM_SetSaberMove( kickMove ); return; } } } //this is never a valid regular saber attack button pm->cmd.buttons &= ~BUTTON_ALT_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; } if ( curmove == LS_A_JUMP_T__B_ || pm->ps->torsoAnim == BOTH_FORCELEAP2_T__B_ ) {//must transition back to ready from this anim newmove = LS_R_T2B; } // check for fire else if ( !(pm->cmd.buttons & (BUTTON_ATTACK|BUTTON_ALT_ATTACK)) ) {//not attacking pm->ps->weaponTime = 0; 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 ); //if ( pm->ps->saberBlockingTime > pm->cmd.serverTime ) { PM_SetSaberMove( LS_READY ); } return; } } // *************************************************** // Pressing attack, so we must look up the proper attack move. if ( pm->ps->weaponTime > 0 ) { // Last attack is not yet complete. pm->ps->weaponstate = WEAPON_FIRING; return; } else { int both = qfalse; if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_ATTACK || pm->ps->torsoAnim == BOTH_FORCELONGLEAP_LAND ) {//can't attack in these anims return; } else if ( pm->ps->torsoAnim == BOTH_FORCELONGLEAP_START ) {//only 1 attack you can do from this anim if ( pm->ps->torsoTimer >= 200 ) {//hit it early enough to do the attack PM_SetSaberMove( LS_LEAP_ATTACK ); } return; } if ( curmove >= LS_PARRY_UP && curmove <= LS_REFLECT_LL ) {//from a parry or reflection, can go directly into an attack switch ( saberMoveData[curmove].endQuad ) { case Q_T: newmove = LS_A_T2B; break; case Q_TR: newmove = LS_A_TR2BL; break; case Q_TL: newmove = LS_A_TL2BR; 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 /* if ( PM_SaberKataDone() ) {//we came from a bounce and cannot chain to another attack because our kata is done newmove = saberMoveData[curmove].chain_idle; } else */ newmove = PM_SaberAttackForMovement( curmove ); 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 ) ) {//cannot chain this time newmove = saberMoveData[curmove].chain_idle; } } /* if ( newmove == LS_NONE ) {//FIXME: should we allow this? Are there some anims that you should never be able to chain into an attack? //only curmove that might get in here is LS_NONE, LS_DRAW, LS_PUTAWAY and the LS_R_ returns... all of which are in Q_R newmove = PM_AttackMoveForQuad( saberMoveData[curmove].endQuad ); } */ if ( newmove != LS_NONE ) { //Now get the proper transition move newmove = PM_SaberAnimTransitionAnim( curmove, newmove ); 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) { switch ( pm->ps->legsAnim ) { case BOTH_WALK1: case BOTH_WALK2: case BOTH_WALK_STAFF: case BOTH_WALK_DUAL: case BOTH_WALKBACK1: case BOTH_WALKBACK2: case BOTH_WALKBACK_STAFF: case BOTH_WALKBACK_DUAL: case BOTH_RUN1: case BOTH_RUN2: case BOTH_RUN_STAFF: case BOTH_RUN_DUAL: case BOTH_RUNBACK1: case BOTH_RUNBACK2: case BOTH_RUNBACK_STAFF: anim = pm->ps->legsAnim; break; default: anim = PM_GetSaberStance(); break; } // if (PM_RunningAnim(anim) && !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; } PM_SetSaberMove( newmove ); if ( both && pm->ps->torsoAnim == anim ) { 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 ( newMove == LS_DRAW ) { saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber1 && saber1->drawAnim != -1 ) { anim = saber1->drawAnim; } else if ( saber2 && saber2->drawAnim != -1 ) { anim = saber2->drawAnim; } else if ( pm->ps->fd.saberAnimLevel == SS_STAFF ) { anim = BOTH_S1_S7; } else if ( pm->ps->fd.saberAnimLevel == SS_DUAL ) { anim = BOTH_S1_S6; } } else if ( newMove == LS_PUTAWAY ) { saberInfo_t *saber1 = BG_MySaber( pm->ps->clientNum, 0 ); saberInfo_t *saber2 = BG_MySaber( pm->ps->clientNum, 1 ); if ( saber1 && saber1->putawayAnim != -1 ) { anim = saber1->putawayAnim; } else if ( saber2 && saber2->putawayAnim != -1 ) { anim = saber2->putawayAnim; } else if ( pm->ps->fd.saberAnimLevel == SS_STAFF ) { anim = BOTH_S7_S1; } else if ( pm->ps->fd.saberAnimLevel == SS_DUAL ) { anim = BOTH_S6_S1; } } else if ( pm->ps->fd.saberAnimLevel == SS_STAFF && newMove >= LS_S_TL2BR && newMove < LS_REFLECT_LL ) {//staff has an entirely new set of anims, besides special attacks //FIXME: include ready and draw/putaway? //FIXME: get hand-made bounces and deflections? if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL ) {//there aren't 1-7, just 1, 6 and 7, so just set it anim = BOTH_P7_S7_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set } else {//add the appropriate animLevel anim += (pm->ps->fd.saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; } } else if ( pm->ps->fd.saberAnimLevel == SS_DUAL && newMove >= LS_S_TL2BR && newMove < LS_REFLECT_LL ) { //akimbo has an entirely new set of anims, besides special attacks //FIXME: include ready and draw/putaway? //FIXME: get hand-made bounces and deflections? if ( newMove >= LS_V1_BR && newMove <= LS_REFLECT_LL ) {//there aren't 1-7, just 1, 6 and 7, so just set it anim = BOTH_P6_S6_T_ + (anim-BOTH_P1_S1_T_);//shift it up to the proper set } else {//add the appropriate animLevel anim += (pm->ps->fd.saberAnimLevel-FORCE_LEVEL_1) * SABER_ANIM_GROUP_SIZE; } } /* else if ( newMove == LS_DRAW && pm->ps->SaberStaff() ) {//hold saber out front as we turn it on //FIXME: need a real "draw" anim for this (and put-away) anim = BOTH_SABERSTAFF_STANCE; } */ else 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 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 (!pm->ps->m_iVehicleNum) { //if not riding a vehicle if (BG_SaberInSpecial(newMove)) { setflags |= SETANIM_FLAG_OVERRIDE; } /* 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 || newMove == LS_JUMPATTACK_DUAL || newMove == LS_A_BACKFLIP_ATK) { setflags |= SETANIM_FLAG_OVERRIDE; } */ } if ( BG_InSaberStandAnim(anim) || anim == BOTH_STAND1 ) { anim = (pm->ps->legsAnim); if ((anim >= BOTH_STAND1 && anim <= BOTH_STAND4TOATTACK2) || (anim >= TORSO_DROPWEAP1 && anim <= TORSO_WEAPONIDLE10)) { //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 || anim == BOTH_WALK1) { //normal stance when walking backward so saber doesn't look like it's cutting through leg anim = PM_GetSaberStance(); } if (BG_InSlopeAnim( anim )) { anim = PM_GetSaberStance(); } parts = SETANIM_TORSO; } if (!pm->ps->m_iVehicleNum) { //if not riding a vehicle if (newMove == LS_JUMPATTACK_ARIAL_RIGHT || newMove == LS_JUMPATTACK_ARIAL_LEFT) { //force only on legs parts = SETANIM_LEGS; } else 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_ROLL_STAB || newMove == LS_A_FLIP_STAB || newMove == LS_A_FLIP_SLASH || newMove == LS_JUMPATTACK_DUAL || newMove == LS_JUMPATTACK_ARIAL_LEFT || newMove == LS_JUMPATTACK_ARIAL_RIGHT || newMove == LS_JUMPATTACK_CART_LEFT || newMove == LS_JUMPATTACK_CART_RIGHT || newMove == LS_JUMPATTACK_STAFF_LEFT || newMove == LS_JUMPATTACK_STAFF_RIGHT || newMove == LS_A_BACKFLIP_ATK || newMove == LS_STABDOWN || newMove == LS_STABDOWN_STAFF || newMove == LS_STABDOWN_DUAL || newMove == LS_DUAL_SPIN_PROTECT || newMove == LS_STAFF_SOULCAL || newMove == LS_A1_SPECIAL || newMove == LS_A2_SPECIAL || newMove == LS_A3_SPECIAL || newMove == LS_UPSIDE_DOWN_ATTACK || newMove == LS_PULL_ATTACK_STAB || newMove == LS_PULL_ATTACK_SWING || BG_KickMove( newMove ) ) { 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; } else if ( !(pm->ps->pm_flags & PMF_DUCKED) && ( newMove == LS_SPINATTACK_DUAL || newMove == LS_SPINATTACK ) ) { parts = SETANIM_BOTH; } } PM_SetAnim(parts, anim, setflags, saberMoveData[newMove].blendTime); if (parts != SETANIM_LEGS && (pm->ps->legsAnim == BOTH_ARIAL_LEFT || pm->ps->legsAnim == BOTH_ARIAL_RIGHT)) { if (pm->ps->legsTimer > pm->ps->torsoTimer) { pm->ps->legsTimer = pm->ps->torsoTimer; } } } if ( (pm->ps->torsoAnim) == 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 if ( newMove != LS_KICK_F && newMove != LS_KICK_B && newMove != LS_KICK_R && newMove != LS_KICK_L && newMove != LS_KICK_F_AIR && newMove != LS_KICK_B_AIR && newMove != LS_KICK_R_AIR && newMove != LS_KICK_L_AIR ) { PM_AddEvent(EV_SABER_ATTACK); } if (pm->ps->brokenLimbs) { //randomly make pain sounds with a broken arm because we are suffering. int iFactor = -1; if (pm->ps->brokenLimbs & (1<ps->brokenLimbs & (1<ps); } } } } } if (BG_SaberInSpecial(newMove) && pm->ps->weaponTime < pm->ps->torsoTimer) { //rww 01-02-03 - I think this will solve the issue of special attacks being interruptable, hopefully without side effects pm->ps->weaponTime = pm->ps->torsoTimer; } pm->ps->saberMove = newMove; pm->ps->saberBlocking = saberMoveData[newMove].blocking; pm->ps->torsoAnim = anim; if (pm->ps->weaponTime <= 0) { pm->ps->saberBlocked = BLOCKED_NONE; } } } saberInfo_t *BG_MySaber( int clientNum, int saberNum ) { //returns a pointer to the requested saberNum #ifdef QAGAME gentity_t *ent = &g_entities[clientNum]; if ( ent->inuse && ent->client ) { if ( !ent->client->saber[saberNum].model || !ent->client->saber[saberNum].model[0] ) { //don't have saber anymore! return NULL; } return &ent->client->saber[saberNum]; } #elif defined CGAME clientInfo_t *ci = NULL; if (clientNum < MAX_CLIENTS) { ci = &cgs.clientinfo[clientNum]; } else { centity_t *cent = &cg_entities[clientNum]; if (cent->npcClient) { ci = cent->npcClient; } } if ( ci && ci->infoValid ) { if ( !ci->saber[saberNum].model || !ci->saber[saberNum].model[0] ) { //don't have sabers anymore! return NULL; } return &ci->saber[saberNum]; } #endif return NULL; } #include "../namespace_end.h"