From 497393eea60a7dabb21bcbd5e1ec3271149e00b2 Mon Sep 17 00:00:00 2001 From: Chris Dawalt Date: Sat, 2 Oct 2021 22:01:09 -0400 Subject: [PATCH] burst fire view kickback fix, accuracy kickback time proper, compiles --- src/shared/player.h | 33 ++++++++---- src/shared/player.qc | 69 +++++++++++++++++++++---- src/shared/weapons.qc | 19 ++++--- src/shared/weapons/weapon_m61grenade.qc | 39 +++++++------- 4 files changed, 113 insertions(+), 47 deletions(-) diff --git a/src/shared/player.h b/src/shared/player.h index 5590217..6b68807 100644 --- a/src/shared/player.h +++ b/src/shared/player.h @@ -31,6 +31,7 @@ enum PLAYER_STATE{ #define ATTR_CHANGED_ARY(ary, i) (ary ##_net[i] != ary[i]) #define PREDICTED_ARY_INT(ary, size) int ary[size]; int ary ##_net[size] +#define PREDICTED_ARY_FLOAT(ary, size) float ary[size]; float ary ##_net[size] #define ROLL_BACK_ARY(ary, i) ary[i] = ary ##_net[i] #define SAVE_STATE_ARY(ary, i) ary ##_net[i] = ary[i] @@ -339,10 +340,19 @@ class player:base_player // Shared, but don't network! I think? - int aryNextBurstShotTime_softLength; - float aryNextBurstShotTime[5]; - int aryNextBurstShotTime_listenIndex; + // apparently we do need to do this, at least network somewhat. + // Unsure how much, maybe the 'aryNextBurstShotTime' every single frame + // is a bit overkill, should only need to be set at the start of a burst-fire. + // So no need for networking as the client/server should set those to the same + // values independently, but no idea of the prediction-rollbacks on clientside + // would complicate that, maybe still do the SAVE/ROLL_BACK stuff? + // aryNextBurstShotTime_listenIndex depends on the time and updates a lot, + // that one most likely has to stay networked and predicted. + PREDICTED_INT(aryNextBurstShotTime_softLength); + PREDICTED_ARY_FLOAT(aryNextBurstShotTime, 5); + PREDICTED_INT(aryNextBurstShotTime_listenIndex); + PREDICTED_ARY_INT(ary_ammoTotal, AMMO_ID::LAST_ID); //shotgun stuff ////////////////////////////////////////////////////////////////////////////// @@ -384,21 +394,22 @@ class player:base_player ////////////////////////////////////////////////////////////////////////////// - // -1 = nothing special, holding grenade normally. Pressing primary/secondary + // 0 = nothing special, holding grenade normally. Pressing primary/secondary // starts the next phase. - // 0 = pull-pin animation, and stays this way until the animation finishes and + // 1 = pull-pin animation, and stays this way until the animation finishes and // neither input is held down. Then the next phase starts. - // 1 = throw or throw-slide anim picked, based on primary or secondary being + // 2 = throw or throw-slide anim picked, based on primary or secondary being // pressed the most recently. Jumps to phase 2. - // 2 = throw/slide anim playing. Grenade spawns very soon. After the anim + // 3 = throw/slide anim playing. Grenade spawns very soon. After the anim // finishes, decide whether to bring another grenade up (has ammo), other- // wise, discard the weapon and pick another. PREDICTED_INT(grenadeFireIndex); - // at what time do I throw the grenade after starting the throw/toss anim? - float grenadeSpawnTime; - float grenadeHeldDuration; + //TAGGG - should these be predicted too? Unsure. Possibly not. + PREDICTED_FLOAT(grenadeSpawnTime); + PREDICTED_FLOAT(grenadeHeldDuration); + // Same for this, but it has been for a while PREDICTED_FLOAT(bGrenadeToss); //set to FALSE if it's a typical throw instead. @@ -472,7 +483,7 @@ class player:base_player // to. Sounds like a lot of extra work for a single ammo counter per type of ammo which // is all this is. //int ary_ammoTotal[AMMO_ID::LAST_ID]; - PREDICTED_ARY_INT(ary_ammoTotal,AMMO_ID::LAST_ID); + PREDICTED_ARY_INT(ary_ammoTotal, AMMO_ID::LAST_ID); // Provided for quick reference. How many slots does the current loadout take? // There is a record of how much money has been spent, similar to config, but that isn't diff --git a/src/shared/player.qc b/src/shared/player.qc index ef27cf7..ca0687d 100644 --- a/src/shared/player.qc +++ b/src/shared/player.qc @@ -236,7 +236,10 @@ player::ReceiveEntity(float new, float fl) nextAkimboAttackPreference = readbyte(); akimboDualFireToleranceTime = readfloat(); - grenadeFireIndex = readbyte() - 1; + + grenadeFireIndex = readbyte(); + grenadeHeldDuration = readfloat(); + grenadeSpawnTime = readfloat(); bGrenadeToss = readbyte(); @@ -276,6 +279,12 @@ player::ReceiveEntity(float new, float fl) fKarateStamina = readfloat(); + aryNextBurstShotTime_softLength = readbyte(); + for(i = 0; i < aryNextBurstShotTime_softLength; i++){ + aryNextBurstShotTime[i] = readfloat(); + } + aryNextBurstShotTime_listenIndex = readbyte() - 1; + shotgunReloadIndex = readbyte(); shotgunAddAmmoTime = readfloat(); shotgunAddAmmoSoundTime = readfloat(); @@ -381,6 +390,7 @@ so we can roll them back later. void player::PredictPreFrame(void) { + int i; //printfline("---PREDICT PRE FRAME"); @@ -407,7 +417,10 @@ player::PredictPreFrame(void) SAVE_STATE(iZoomLevel); SAVE_STATE(nextAkimboAttackPreference); SAVE_STATE(akimboDualFireToleranceTime); + SAVE_STATE(grenadeFireIndex); + SAVE_STATE(grenadeHeldDuration); + SAVE_STATE(grenadeSpawnTime); SAVE_STATE(bGrenadeToss); @@ -428,6 +441,12 @@ player::PredictPreFrame(void) SAVE_STATE(fKarateStamina); + SAVE_STATE(aryNextBurstShotTime_softLength); + for(i = 0; i < aryNextBurstShotTime_softLength; i++){ + SAVE_STATE_ARY(ary_ammoTotal, i); + } + SAVE_STATE(aryNextBurstShotTime_listenIndex); + SAVE_STATE(shotgunReloadIndex); @@ -436,7 +455,7 @@ player::PredictPreFrame(void) SAVE_STATE(ary_myWeapons_softMax); - for(int i = 0; i < ary_myWeapons_softMax; i++){ + for(i = 0; i < ary_myWeapons_softMax; i++){ SAVE_STATE(ary_myWeapons[i].weaponID); SAVE_STATE(ary_myWeapons[i].weaponTypeID); SAVE_STATE(ary_myWeapons[i].iBitsUpgrade); @@ -455,7 +474,7 @@ player::PredictPreFrame(void) //UNNECESSARY. This array is of fixed length, so known at all times. //WriteByte(MSG_ENTITY, ary_ammoTotal_softMax); - for(int i = 0; i < AMMO_ID::LAST_ID; i++){ + for(i = 0; i < AMMO_ID::LAST_ID; i++){ // See serverside equivalent, too much info was lost from some pools being over 255 // (well I guess that's all there is to it) SAVE_STATE_ARY(ary_ammoTotal, i); @@ -482,6 +501,7 @@ Where we roll back our values to the ones last sent/verified by the server. void player::PredictPostFrame(void) { + int i; //printfline("---PREDICT POST FRAME"); sendevent("ViewOffsetR", "fff", vViewAngleOffsetTotalChange[0], vViewAngleOffsetTotalChange[1], vViewAngleOffsetTotalChange[2]); @@ -513,7 +533,10 @@ player::PredictPostFrame(void) ROLL_BACK(nextAkimboAttackPreference); ROLL_BACK(akimboDualFireToleranceTime); + ROLL_BACK(grenadeFireIndex); + ROLL_BACK(grenadeHeldDuration); + ROLL_BACK(grenadeSpawnTime); ROLL_BACK(bGrenadeToss); //flKarateBlockCooldown @@ -532,15 +555,21 @@ player::PredictPostFrame(void) ROLL_BACK(fUncrouchBlockDelay); ROLL_BACK(fKarateStamina); + ROLL_BACK(aryNextBurstShotTime_softLength); + for(i = 0; i < aryNextBurstShotTime_softLength; i++){ + ROLL_BACK_ARY(ary_ammoTotal, i); + } + ROLL_BACK(aryNextBurstShotTime_listenIndex); + + ROLL_BACK(shotgunReloadIndex); - ROLL_BACK(shotgunAddAmmoTime); ROLL_BACK(shotgunAddAmmoSoundTime); ROLL_BACK(ary_myWeapons_softMax); - for(int i = 0; i < ary_myWeapons_softMax; i++){ + for(i = 0; i < ary_myWeapons_softMax; i++){ ROLL_BACK(ary_myWeapons[i].weaponID); ROLL_BACK(ary_myWeapons[i].weaponTypeID); ROLL_BACK(ary_myWeapons[i].iBitsUpgrade); @@ -559,7 +588,7 @@ player::PredictPostFrame(void) // UNNECESSARY. This array is of fixed length, so known at all times. //WriteByte(MSG_ENTITY, ary_ammoTotal_softMax); - for(int i = 0; i < AMMO_ID::LAST_ID; i++){ + for(i = 0; i < AMMO_ID::LAST_ID; i++){ // See serverside equivalent, too much info was lost from some pools being over 255 // (well I guess that's all there is to it) ROLL_BACK_ARY(ary_ammoTotal, i); @@ -659,7 +688,10 @@ player::EvaluateEntity(void) SAVE_STATE(iZoomLevel); SAVE_STATE(nextAkimboAttackPreference); SAVE_STATE(akimboDualFireToleranceTime); + SAVE_STATE(grenadeFireIndex); + SAVE_STATE(grenadeHeldDuration); + SAVE_STATE(grenadeSpawnTime); SAVE_STATE(bGrenadeToss); //flKarateBlockCooldown @@ -679,6 +711,13 @@ player::EvaluateEntity(void) SAVE_STATE(fKarateStamina); + SAVE_STATE(aryNextBurstShotTime_softLength); + for(i = 0; i < aryNextBurstShotTime_softLength; i++){ + SAVE_STATE_ARY(ary_ammoTotal, i); + } + SAVE_STATE(aryNextBurstShotTime_listenIndex); + + SAVE_STATE(shotgunReloadIndex); SAVE_STATE(shotgunAddAmmoTime); @@ -789,7 +828,10 @@ player::SendEntity(entity ePEnt, float fChanged) } WriteByte(MSG_ENTITY, nextAkimboAttackPreference); WriteFloat(MSG_ENTITY, akimboDualFireToleranceTime); - WriteByte(MSG_ENTITY, grenadeFireIndex + 1); + + WriteByte(MSG_ENTITY, grenadeFireIndex); + WriteFloat(MSG_ENTITY, grenadeHeldDuration); + WriteFloat(MSG_ENTITY, grenadeSpawnTime); WriteByte(MSG_ENTITY, bGrenadeToss); WriteByte(MSG_ENTITY, armor ); @@ -813,6 +855,12 @@ player::SendEntity(entity ePEnt, float fChanged) WriteFloat(MSG_ENTITY, fKarateStamina); + WriteByte(MSG_ENTITY, aryNextBurstShotTime_softLength); + for(i = 0; i < aryNextBurstShotTime_softLength; i++){ + WriteFloat(MSG_ENTITY, aryNextBurstShotTime[i]); + } + WriteByte(MSG_ENTITY, aryNextBurstShotTime_listenIndex + 1); + WriteByte(MSG_ENTITY, shotgunReloadIndex ); WriteFloat(MSG_ENTITY, shotgunAddAmmoTime ); @@ -1013,7 +1061,7 @@ player::reset(BOOL resetInventory){ shotgunAddAmmoSoundTime = -1; //Grenade stuff - grenadeFireIndex = -1; + grenadeFireIndex = 0; grenadeHeldDuration = -1; grenadeSpawnTime = -1; bGrenadeToss = FALSE; @@ -1152,7 +1200,7 @@ player::updateTimers(void){ if(fAccuracyKickbackStartCooldown <= 0){ // begin reducing //fAccuracyKickback -= frametime * 0.1; - fAccuracyKickback = max(0, fAccuracyKickback - input_timelength); + fAccuracyKickback = max(0, fAccuracyKickback - input_timelength * 0.1); if(fAccuracyKickback <= 0){ // stop! @@ -1211,9 +1259,10 @@ player::updateTimers(void){ // test? main one #ifndef CLIENT GET_MY_VIEW_ANGLES = GET_MY_VIEW_ANGLES + finalChange; -#else // (this might be pointless?) this.vViewAngleOffsetTotalChangeAlt += finalChange; +#else + #endif diff --git a/src/shared/weapons.qc b/src/shared/weapons.qc index 522ef21..b9c51c0 100644 --- a/src/shared/weapons.qc +++ b/src/shared/weapons.qc @@ -307,6 +307,10 @@ weapon_base_onAttack(player pl, weapondata_basic_t* basePRef, weapondynamic_t ar // For firing once the left or right has been picked for firing, does not matter which as this has // no effect on the weapon stats. Also applies kickback for firing once. +// TODO: restore random-ness of kickback, going left/right angle-wise and +// varrying the intensity, 65% to 100%, as shown commented out below. +// Need shared randoms for that to work, input_sequence is not relayed +// to the server on processing an input it seems. void weapon_base_onAttack_individual(player pl, weapondata_basic_t* basePRef, weapondynamic_t arg_thisWeapon, int shellCount){ @@ -391,12 +395,15 @@ weapon_base_onAttack_individual(player pl, weapondata_basic_t* basePRef, weapond finalAng = ( 90 - (angleRange/2) + randoAngFactor*( angleRange ) ) * (M_PI/180); pl.addViewAngleOffset(finalAng, finalAmount); - // TODO - be better shared later? - // Syncing view kickback would need the random-ness to be handled between server/client, - // maybe seeded randoms? I forget. - // Properly predicted shotgun firing would be a good demonstration of that too, - // so what happens instantly (client sees) and what actually happened (server sees; everyone - // else sees) line up. + // separate accuracy-kickback that spaces the crosshairs out and causes the weapon-spread + // to be higher to discourage firing too much, even beyon the view-kickback that forces + // the camera up. I don't think it has any random component, determined by weapon + // stats only. + // Also this is going off what makes sense with the as-is game stats, 0.1 is the + // highest accuracy kickback allowed and it slows down at a rate of 10% real-time. + // When reached, the 0.1 cap takes 1 second of not-firing to be completely reset. + // fAccuracyKickbackStartCooldown is still real-time, so 0.14 seconds, and the reduction + // starts happening after it runs out. pl.fAccuracyKickbackStartCooldown = 0.14; pl.fAccuracyKickback += baseRef.firestats.fAccuracyKickback; if(pl.fAccuracyKickback >= 0.1){ diff --git a/src/shared/weapons/weapon_m61grenade.qc b/src/shared/weapons/weapon_m61grenade.qc index ae1ff8d..99e536e 100644 --- a/src/shared/weapons/weapon_m61grenade.qc +++ b/src/shared/weapons/weapon_m61grenade.qc @@ -38,7 +38,7 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){ // If using to keep track of what animation to play (like HL having different throw anims for // range), that should be networked so that this gives an accurate viewmodel. // ALTHOUGH, this applies to the time of picking a throw anim, not spawning the grenade. - if(pl.grenadeSpawnTime != -1 && pl.grenadeFireIndex > 0){ + if(pl.grenadeSpawnTime != -1 && pl.grenadeFireIndex > 1){ pl.grenadeHeldDuration = min(pl.grenadeHeldDuration + input_timelength, 3); if(pl.w_attack_next <= pl.grenadeSpawnTime){ @@ -71,44 +71,43 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){ printfline("!!! w_attack_next FINISHED WITH GRENFIREIN: %i", pl.grenadeFireIndex); prevChoice = pl.grenadeFireIndex; } - if(pl.grenadeFireIndex > 0){ + if(pl.grenadeFireIndex > 1){ printfline("pl.grenadeFireIndex resolved: %i", pl.grenadeFireIndex); } */ - if(pl.grenadeFireIndex == 0){ + if(pl.grenadeFireIndex == 1){ // pin pull animation is finished, ready to throw when the input is not held down. pl.grenadeHeldDuration = min(pl.grenadeHeldDuration + input_timelength, 3); if(!(input_buttons & INPUT_BUTTON0) && !(input_buttons & INPUT_BUTTON3)){ // neither input held down? Move on //printfline("!!! RELEASE"); - pl.grenadeFireIndex = 1; + pl.grenadeFireIndex = 2; } } // should this be joined by else or no? - if(pl.grenadeFireIndex == 1){ + if(pl.grenadeFireIndex == 2){ // Release detected, do the throw anim + spawn the grenade soon if(!pl.bGrenadeToss){ TS_Weapons_ViewAnimation(weaponseq_m61grenade::throw, 11.0f/30.0f); }else{ TS_Weapons_ViewAnimation(weaponseq_m61grenade::throw_slide, 11.0f/30.0f); } - pl.grenadeFireIndex = 2; + pl.grenadeFireIndex = 3; // re-use shotgunAddAmmoTime instead, make the var more genericly named maybe??? - //pl.grenadeSpawnTime = time + 4.0f/30.0f; weapon_base_setWholeAttackDelay(pl, (11.0f/30.0f) * 1); - pl.grenadeSpawnTime = pl.w_attack_next - 4.0f/30.0f; - }else if(pl.grenadeFireIndex == 2){ + + }else if(pl.grenadeFireIndex == 3){ // Throw anim is over, decide whether to deploy another grenade or remove the // weapon if out of grenades. //printfline("Remove grenade? Count:%i", arg_thisWeapon.iCount); if(arg_thisWeapon.iCount > 0){ TS_Weapons_ViewAnimation(weaponseq_m61grenade::draw, 31.0f / 40.0f ); - printfline("I set grenadeFireIndex to -1, B!"); - pl.grenadeFireIndex = -1; + printfline("I set grenadeFireIndex to 0, B!"); + pl.grenadeFireIndex = 0; weapon_base_setWholeAttackDelay(pl, (31.0f/40.0f)); }else{ @@ -122,8 +121,8 @@ void weapon_m61grenade_onThink(player pl, weapondynamic_t arg_thisWeapon){ //printfline("removeWeaponFromInventory POST cur weapo: %i", pl.inventoryEquippedIndex); - printfline("I set grenadeFireIndex to -1, C!"); - pl.grenadeFireIndex = -1; // paranoia? + printfline("I set grenadeFireIndex to 0, C!"); + pl.grenadeFireIndex = 0; // paranoia? return; } @@ -183,8 +182,8 @@ void weapon_grenade_onInputPress(player pl, weapondynamic_t arg_thisWeapon, int // Initiating a throw is only do-able when the attack delay is up, and a fresh key press. // Don't allow to interrupt the deploy anim. if(pl.w_attack_next <= 0 && (INPUT_PRIMARY_TAP_CHECK(pl) || INPUT_SECONDARY_TAP_CHECK(pl))){ - if(pl.grenadeFireIndex == -1){ - pl.grenadeFireIndex = 0; + if(pl.grenadeFireIndex == 0){ + pl.grenadeFireIndex = 1; printfline("WHAT THE heckin heck IS THIS %.2f", time); pl.grenadeSpawnTime = -1; // not yet! pl.grenadeHeldDuration = 0; @@ -194,7 +193,7 @@ void weapon_grenade_onInputPress(player pl, weapondynamic_t arg_thisWeapon, int } // Once already started, this can affect being a normal throw or slide-throw. - if(pl.grenadeFireIndex == 0){ + if(pl.grenadeFireIndex == 1){ if(attackTypeUsed & BITS_AKIMBOCHOICE_LEFT){ pl.bGrenadeToss = FALSE; //throw, this was primary. }else if(attackTypeUsed & BITS_AKIMBOCHOICE_RIGHT){ @@ -221,12 +220,12 @@ void weapon_grenade_onInputRelease(player pl, weapondynamic_t arg_thisWeapon){ // (except above, checks a bit differently for spawning the grenade) return; } - if(pl.grenadeFireIndex == 0){ + if(pl.grenadeFireIndex == 1){ // pin pull animation is finished, ready to throw when the input is not held down. if(!(input_buttons & INPUT_BUTTON0) && !(input_buttons & INPUT_BUTTON3)){ // primary not held down? Move on then //printfline("!!! RELEASE DETECT"); - pl.grenadeFireIndex = 1; + pl.grenadeFireIndex = 2; } } } @@ -277,8 +276,8 @@ w_m61grenade_draw(void) player pl = (player)self; weapondynamic_t arg_thisWeapon = pl.ary_myWeapons[pl.inventoryEquippedIndex]; - printfline("I set grenadeFireIndex to -1, A!"); - pl.grenadeFireIndex = -1; + printfline("I set grenadeFireIndex to 0, A!"); + pl.grenadeFireIndex = 0; pl.grenadeSpawnTime = -1; pl.grenadeHeldDuration = 0;