From b795755985db3c1c5219d0034e9041ac8f3b2438 Mon Sep 17 00:00:00 2001 From: LegendaryGuard Date: Sun, 24 Mar 2024 19:58:46 +0100 Subject: [PATCH] game: Implement block, transfer health damage to ki and deflect some ki attacks --- README.md | 2 +- docs/bfp_cvars_task.md | 8 ++--- source/game/bg_pmove.c | 4 +-- source/game/bg_public.h | 2 ++ source/game/g_active.c | 79 +++++++++++++++++++++++++++++++++++++++++ source/game/g_combat.c | 9 +++-- source/game/g_cvar.h | 10 +++--- source/game/g_missile.c | 16 +++++++++ 8 files changed, 116 insertions(+), 14 deletions(-) diff --git a/README.md b/README.md index cff493d..f8c9cc7 100644 --- a/README.md +++ b/README.md @@ -47,7 +47,7 @@ A legendary 90s era Quake 3 Arena mod. - [ ] Powerlevel and Power Tiers indicated on old docs - [ ] Hit Stun (makes player can't use ki, melee, block and charge) - [ ] Power Struggles (when two beam attacks collide) -- [ ] Blocking (consumes ki energy, transfers all damage to ki instead of health, deflect missile attacks, more info on old docs) +- [x] ~~Blocking (consumes ki energy, transfers all damage to ki instead of health, deflect missile attacks, more info on old docs)~~ - [ ] Short-Range Teleport (when pressing 2 times left or right) - [ ] Transformations (related to Power Tiers) - [ ] Attacksets (configurable for cfgs) diff --git a/docs/bfp_cvars_task.md b/docs/bfp_cvars_task.md index 8d4d955..2c4f29d 100644 --- a/docs/bfp_cvars_task.md +++ b/docs/bfp_cvars_task.md @@ -3,14 +3,10 @@ ## PENDING: - g_basepl [1-999]: set the starting power level from one thousand to one mil. -- g_blockLength = "3" -- g_blockDelay = "2" - g_kiChargePct = "15" - g_kiCharge = "0" - g_kiRegenPct = "0.6" - g_kiRegen = "0" -- g_blockCostPct = "3" -- g_blockCost = "0" - g_boostCostPct = "0" - g_boostCost = "150" - g_flightCostPct = "0" @@ -72,6 +68,10 @@ - [x] ~~cg_polygonAura~~ - [x] ~~cg_highPolyAura~~ - [x] ~~g_noFlight (disables "fly" bind too, original BFP has a leak though)~~ +- [x] ~~g_blockLength~~ +- [x] ~~g_blockDelay~~ +- [x] ~~g_blockCost~~ +- [x] ~~g_blockCostPct~~ #### Cvar Gametypes: diff --git a/source/game/bg_pmove.c b/source/game/bg_pmove.c index 0a7e152..8c6216e 100644 --- a/source/game/bg_pmove.c +++ b/source/game/bg_pmove.c @@ -48,7 +48,7 @@ float pm_spectatorfriction = 5.0f; int c_pmove = 0; // BFP - TODO: Macro for torso handling, since the code looked repetitive, so this macro makes the code a bit shorter -#define TORSOSTATUS_ANIM_HANDLING(other_torsostatus) ( pm->ps->pm_flags & PMF_INVULEXPAND ) ? PM_ContinueTorsoAnim( TORSO_BLOCK ) : PM_ContinueTorsoAnim( other_torsostatus ) +#define TORSOSTATUS_ANIM_HANDLING(other_torsostatus) ( pm->ps->pm_flags & PMF_BLOCK ) ? PM_ContinueTorsoAnim( TORSO_BLOCK ) : PM_ContinueTorsoAnim( other_torsostatus ) // BFP - Macro for jump handling, since the code looked repetitive, so this macro makes the code a bit shorter #define FORCEJUMP_ANIM_HANDLING() ( pm->cmd.forwardmove >= 0 ) ? PM_ForceLegsAnim( LEGS_JUMP ) : PM_ForceLegsAnim( LEGS_JUMPB ) @@ -623,7 +623,7 @@ static void PM_FlyMove( void ) { VectorCopy (wishvel, wishdir); wishspeed = VectorNormalize(wishdir); - if ( !( pm->ps->pm_flags & PMF_INVULEXPAND ) // BFP - Don't increase the speed when blocking + if ( !( pm->ps->pm_flags & PMF_BLOCK ) // BFP - Don't increase the speed when blocking && ( pm->ps->powerups[PW_HASTE] > 0 || ( pm->cmd.buttons & BUTTON_KI_USE ) ) ) { wishspeed *= scale; } diff --git a/source/game/bg_public.h b/source/game/bg_public.h index 02f3900..fc541a8 100644 --- a/source/game/bg_public.h +++ b/source/game/bg_public.h @@ -142,6 +142,7 @@ typedef enum { // pmove->pm_flags #define PMF_DUCKED 1 #define PMF_JUMP_HELD 2 +#define PMF_BLOCK 4 // BFP - Block // BFP - PMF_BACKWARDS_JUMP is unused // #define PMF_BACKWARDS_JUMP 8 // go into backwards land #define PMF_NEARGROUND 8 // BFP - Near ground check @@ -218,6 +219,7 @@ void Pmove (pmove_t *pmove); typedef enum { STAT_HEALTH, STAT_KI, // BFP - KI amount + STAT_BLOCK, // BFP - Block amount treated as time STAT_HOLDABLE_ITEM, STAT_WEAPONS, // 16 bit fields STAT_ARMOR, diff --git a/source/game/g_active.c b/source/game/g_active.c index afaf801..11f77a7 100644 --- a/source/game/g_active.c +++ b/source/game/g_active.c @@ -735,6 +735,7 @@ void ClientThink_real( gentity_t *ent ) { // BFP - Ki use has 2 options: "kiusetoggle" to toggle and "+button8" when key is being hold if ( !( client->ps.pm_flags & PMF_HITSTUN ) + && !( client->ps.pm_flags & PMF_BLOCK ) && ( ( ucmd->buttons & BUTTON_KI_USE ) // BFP - Using Ki || client->ps.powerups[PW_HASTE] > 0 ) ) { // BFP - When "kiusetoggle" is binded, enables/disables if ( client->ps.powerups[PW_FLIGHT] <= 0 ) { @@ -747,6 +748,84 @@ void ClientThink_real( gentity_t *ent ) { } } + // BFP - Block, reflect ki attacks and reduce health damage + // Block handling: + // Initialize the blocking and start the block length duration, specifically, ki boost and aura are disabled + if ( !( client->ps.pm_flags & PMF_BLOCK ) + && ( ucmd->buttons & BUTTON_BLOCK ) + && client->ps.stats[STAT_BLOCK] <= 0 ) { + client->ps.pm_flags |= PMF_BLOCK; + client->ps.powerups[PW_HASTE] = 0; + client->ps.eFlags &= ~EF_AURA; + ucmd->buttons &= ~BUTTON_KI_USE; + client->ps.stats[STAT_BLOCK] = (g_blockLength.integer * 100); + } + + // Handle block status (it's like block time) + if ( client->ps.stats[STAT_BLOCK] >= 0 ) { + client->ps.stats[STAT_BLOCK]--; + // Print debug +#if 0 + if ( ( client->ps.pm_flags & PMF_BLOCK ) && client->ps.stats[STAT_BLOCK] <= (g_blockLength.integer * 100) ) + Com_Printf( "BLOCK LENGTH: %d\n", client->ps.stats[STAT_BLOCK] ); + else if ( !( client->ps.pm_flags & PMF_BLOCK ) && client->ps.stats[STAT_BLOCK] <= (g_blockDelay.integer * 100) ) + Com_Printf( "BLOCK DELAY: %d\n", client->ps.stats[STAT_BLOCK] ); +#endif + } + + // BFP - Blocking status. Ki energy is being consumed and ki boost can't be used + if ( ( client->ps.pm_flags & PMF_BLOCK ) + && client->ps.stats[STAT_BLOCK] <= (g_blockLength.integer * 100) + && client->ps.stats[STAT_BLOCK] >= 0 ) { + + // BFP - NOTE: Approximate calculation of ki consumption while blocking + float blockCostPct = g_blockCostPct.integer * 0.1; // Percentage of ki consumed per millisecond + float bCost = g_blockCost.integer > 0 ? g_blockCost.integer : 1; // Absolute value of ki consumed per millisecond + float kiBlockConsume = bCost / (g_blockLength.integer * 1000.0); + float totalBlockConsume; + // Random variable to make an approximate calculation of the ki consumption while using block + float rndkiConsume = rand() % 2; + + // BFP - TODO: Implement random calculations correctly? + if ( crandom() > 0.5 && crandom() < 0.8 ) { + rndkiConsume = rand() % 1; + } else if ( crandom() > 0.2 && crandom() < 0.5 ) { + rndkiConsume = random() + 0.38; + } + + // Calculate total ki being consumed + totalBlockConsume = kiBlockConsume * (g_blockLength.integer * 1000.0) * rndkiConsume; + + client->ps.stats[STAT_KI] -= totalBlockConsume; + } + + // When the block length duration has been expired, then start the delay to avoid user + if ( ( client->ps.pm_flags & PMF_BLOCK ) + && client->ps.stats[STAT_BLOCK] <= (g_blockLength.integer * 100) + && client->ps.stats[STAT_BLOCK] <= 0 ) { + client->ps.pm_flags &= ~PMF_BLOCK; + client->ps.stats[STAT_BLOCK] = (g_blockDelay.integer * 100); + } + + // If the block length duration hasn't been expired yet and + // pressing ki charge (if the aura is lighting) or attack buttons, then stop blocking and start the delay + if ( ( client->ps.pm_flags & PMF_BLOCK ) + && ( ( ucmd->buttons & BUTTON_MELEE ) + || ( ucmd->buttons & BUTTON_ATTACK ) + || ( client->ps.eFlags & EF_AURA ) ) ) { + client->ps.pm_flags &= ~PMF_BLOCK; + client->ps.stats[STAT_BLOCK] = (g_blockDelay.integer * 100); + } + + // Handle the delay and don't leave the user get away with it + if ( !( client->ps.pm_flags & PMF_BLOCK ) + && client->ps.stats[STAT_BLOCK] <= (g_blockDelay.integer * 100) + && client->ps.stats[STAT_BLOCK] >= 0 ) { + client->ps.pm_flags &= ~PMF_BLOCK; + ucmd->buttons &= ~BUTTON_BLOCK; // If the user holds the key, when that ends, then immediately enters to this status again + } + // BFP - End of block handling + // BFP - Ki Charge if ( ( ucmd->buttons & BUTTON_KI_CHARGE ) && client->ps.pm_time <= 0 && ( client->ps.pm_flags & PMF_KI_CHARGE ) ) { diff --git a/source/game/g_combat.c b/source/game/g_combat.c index e75b1da..5cc36b8 100644 --- a/source/game/g_combat.c +++ b/source/game/g_combat.c @@ -775,7 +775,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, // add to the attacker's hit counter (if the target isn't a general entity like a prox mine) if ( attacker->client && targ != attacker && targ->health > 0 && targ->s.eType != ET_MISSILE - && targ->s.eType != ET_GENERAL) { + && targ->s.eType != ET_GENERAL + && !( client->ps.pm_flags & PMF_BLOCK ) ) { // BFP - When blocking, don't receive any hit if ( OnSameTeam( targ, attacker ) ) { attacker->client->ps.persistant[PERS_HITS]--; } else { @@ -808,7 +809,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, // add to the damage inflicted on a player this frame // the total will be turned into screen blends and view angle kicks // at the end of the frame - if ( client ) { + if ( client + && !( client->ps.pm_flags & PMF_BLOCK ) ) { // BFP - When blocking, don't receive screams of pain if ( attacker ) { client->ps.persistant[PERS_ATTACKER] = attacker->s.number; } else { @@ -838,7 +840,8 @@ void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker, } // do the damage - if (take) { + if (take + && !( client->ps.pm_flags & PMF_BLOCK )) { // BFP - When blocking, don't receive damage targ->health = targ->health - take; if ( targ->client ) { targ->client->ps.stats[STAT_HEALTH] = targ->health; diff --git a/source/game/g_cvar.h b/source/game/g_cvar.h index 941c391..565378d 100644 --- a/source/game/g_cvar.h +++ b/source/game/g_cvar.h @@ -76,15 +76,17 @@ G_CVAR( g_maxSpawnPL, "g_maxSpawnPL", "999", 0, 0, qtrue ) // BFP - Max spawn po G_CVAR( g_flightCost, "g_flightCost", "50", 0, 0, qtrue ) // BFP - Flight cost G_CVAR( g_flightCostPct, "g_flightCostPct", "0", 0, 0, qtrue ) // BFP - Flight cost percentage G_CVAR( g_boostCost, "g_boostCost", "350", 0, 0, qtrue ) // BFP - Boost cost -G_CVAR( g_boostCostPct, "g_boostCostPct", "3", 0, 0, qtrue ) // BFP - Boost cost percentage +G_CVAR( g_boostCostPct, "g_boostCostPct", "0", 0, 0, qtrue ) // BFP - Boost cost percentage + G_CVAR( g_blockCost, "g_blockCost", "2", 0, 0, qtrue ) // BFP - Block cost -G_CVAR( g_blockCostPct, "g_blockCostPct", "3", 0, 0, qtrue ) // BFP - Block cost percentage +G_CVAR( g_blockCostPct, "g_blockCostPct", "0", 0, 0, qtrue ) // BFP - Block cost percentage +G_CVAR( g_blockDelay, "g_blockDelay", "2", 0, 0, qtrue ) // BFP - Block delay +G_CVAR( g_blockLength, "g_blockLength", "2", 0, 0, qtrue ) // BFP - Block length + G_CVAR( g_kiRegen, "g_kiRegen", "0", 0, 0, qtrue ) // BFP - Ki regeneration G_CVAR( g_kiRegenPct, "g_kiRegenPct", "0.6", 0, 0, qtrue ) // BFP - Ki regeneration percentage G_CVAR( g_kiCharge, "g_kiCharge", "0", 0, 0, qtrue ) // BFP - Ki charge G_CVAR( g_kiChargePct, "g_kiChargePct", "15", 0, 0, qtrue ) // BFP - Ki charge percentage -G_CVAR( g_blockDelay, "g_blockDelay", "2", 0, 0, qtrue ) // BFP - Block delay -G_CVAR( g_blockLength, "g_blockLength", "3", 0, 0, qtrue ) // BFP - Block length G_CVAR( g_podiumDist, "g_podiumDist", "80", 0, 0, qfalse ) G_CVAR( g_podiumDrop, "g_podiumDrop", "70", 0, 0, qfalse ) diff --git a/source/game/g_missile.c b/source/game/g_missile.c index d56e45b..77eb1e8 100644 --- a/source/game/g_missile.c +++ b/source/game/g_missile.c @@ -116,6 +116,22 @@ void G_MissileImpact( gentity_t *ent, trace_t *trace ) { if ( ent->damage ) { vec3_t velocity; + // BFP - When blocking... Deflect the projectile! + if ( other->client->ps.pm_flags & PMF_BLOCK ) { + // PUSH! + BG_EvaluateTrajectoryDelta( &ent->s.pos, level.time, velocity ); + if ( VectorLength( velocity ) == 0 ) { + velocity[2] = 1; + } + G_Damage (other, ent, &g_entities[ent->r.ownerNum], velocity, + ent->s.origin, ent->damage, + 0, ent->methodOfDeath); + + // DEFLECT! + G_BounceMissile( ent, trace ); + return; + } + if( LogAccuracyHit( other, &g_entities[ent->r.ownerNum] ) ) { g_entities[ent->r.ownerNum].client->accuracy_hits++; hitClient = qtrue;