diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 61c858563..22442c86b 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -196,9 +196,23 @@ jobs: steps: - name: Install dependencies run: | - dpkg --add-architecture i386 - apt-get update - apt-get install -y \ + # Debian 11 reached end of LTS on 2026-08-31: the bullseye-security + # suite is frozen and its pool is being pruned from deb.debian.org. + # Keep the same toolchain, but fetch the base suite from the Debian + # archive and pin the security suite to its last complete snapshot. + # The security suite is fetched from the cloudflare snapshot mirror: + # plain snapshot.debian.org throttles CI bursts with connection + # resets, and https needs ca-certificates, which debian:11-slim + # does not ship, so bootstrap it from the archive first. + cat > /etc/apt/sources.list <<'EOF' + deb http://archive.debian.org/debian bullseye main + deb http://archive.debian.org/debian bullseye-updates main + EOF + apt-get -o Acquire::Check-Valid-Until=false update + apt-get install -y --no-install-recommends ca-certificates + echo 'deb https://snapshot-cloudflare.debian.org/archive/debian-security/20260831T000000Z bullseye-security main' > /etc/apt/sources.list.d/bullseye-security.list + apt-get -o Acquire::Check-Valid-Until=false -o Acquire::Retries=5 update + apt-get -o Acquire::Retries=5 install -y \ gcc-multilib g++-multilib \ build-essential \ libc6-dev libc6-dev-i386 \ diff --git a/README.md b/README.md index d224265e1..d16cf5cc8 100644 --- a/README.md +++ b/README.md @@ -140,6 +140,7 @@ This means that plugins that do binary code analysis (Orpheu for example) probab | mp_stamina_restore_rate | 0 | 0.0 | - | Framerate (FPS), that used as reference when restoring stamina (fuser2) after jump. | | mp_logkills | 1 | 0 | 1 | Log kills.
`0` disabled
`1` enabled | | mp_jump_height | 45 | 0.0 | - | Player jump height. | +| mp_longjump_cooldown | 0.0 | 0.0 | - | Cooldown time (in seconds) between long jumps to prevent abuse.
`0` disabled
`>0` seconds before the player can long jump again. During cooldown, only a regular vertical jump is performed. | | bot_excellent_morale | 0 | 0 | 1 | Bots always have great morale regardless of defeat or victory. | | mp_randomspawn | 0 | 0 | 1 | Random player spawns
`0` disabled
`1` enabled
`NOTE`: Navigation `maps/.nav` file required | | mp_playerid_showhealth | 1 | 0 | 2 | Player ID display mode.
`0` don't show health
`1` show health for teammates only (default CS behaviour)
`2` show health for all players | diff --git a/dist/game.cfg b/dist/game.cfg index fc59a237e..14c752a46 100644 --- a/dist/game.cfg +++ b/dist/game.cfg @@ -670,6 +670,13 @@ mp_stamina_restore_rate "0" // Default value: "45" mp_jump_height "45" +// Cooldown time (in seconds) between long jumps to prevent abuse. +// 0 - disabled (default behaviour) +// >0 - seconds before the player can long jump again +// +// Default value: "0.0" +mp_longjump_cooldown "0.0" + // Bots always have great morale regardless of defeat or victory. // 0 - disabled (default behaviour) // 1 - enabled diff --git a/regamedll/CMakeLists.txt b/regamedll/CMakeLists.txt index b1166d023..c4b22a8e0 100644 --- a/regamedll/CMakeLists.txt +++ b/regamedll/CMakeLists.txt @@ -43,7 +43,7 @@ endif() # do not strip debuginfo during link if (NOT DEBUG) - set(LINK_FLAGS "-s") + set(LINK_FLAGS "") endif() # add -m32 flag only on 64-bit systems, if building for 64-bit wasn't enabled with XASH_COMPAT @@ -437,6 +437,10 @@ if (CMAKE_SIZEOF_VOID_P EQUAL 4 AND NOT ANDROID) set(LINK_FLAGS "${LINK_FLAGS} -L${PROJECT_SOURCE_DIR}/lib/linux32") endif() +if (UNIX AND NOT APPLE AND NOT ANDROID) + set(LINK_FLAGS "${LINK_FLAGS} -Wl,-z,notext") +endif() + if (NOT MSVC) set_target_properties(regamedll PROPERTIES COMPILE_FLAGS ${COMPILE_FLAGS} diff --git a/regamedll/dlls/game.cpp b/regamedll/dlls/game.cpp index 01e07b92a..7f3e8102f 100644 --- a/regamedll/dlls/game.cpp +++ b/regamedll/dlls/game.cpp @@ -10,6 +10,7 @@ cvar_t *g_psv_stopspeed = nullptr; cvar_t *g_psv_stepsize = nullptr; cvar_t *g_psv_clienttrace = nullptr; cvar_t *g_psv_maxvelocity = nullptr; +cvar_t *g_psv_zmax = nullptr; cvar_t displaysoundlist = { "displaysoundlist", "0", 0, 0.0f, nullptr }; cvar_t timelimit = { "mp_timelimit", "0", FCVAR_SERVER, 0.0f, nullptr }; @@ -174,6 +175,7 @@ cvar_t assist_damage_threshold = { "mp_assist_damage_threshold", "40", cvar_t freezetime_duck = { "mp_freezetime_duck", "1", 0, 1.0f, nullptr }; cvar_t freezetime_jump = { "mp_freezetime_jump", "1", 0, 1.0f, nullptr }; cvar_t jump_height = { "mp_jump_height", "45", FCVAR_SERVER, 45.0f, nullptr }; +cvar_t longjump_cooldown = { "mp_longjump_cooldown", "0.0", 0, 0.0f, nullptr }; cvar_t hostages_rescued_ratio = { "mp_hostages_rescued_ratio", "1.0", 0, 1.0f, nullptr }; @@ -272,6 +274,7 @@ void EXT_FUNC GameDLLInit() g_psv_stepsize = CVAR_GET_POINTER("sv_stepsize"); g_psv_clienttrace = CVAR_GET_POINTER("sv_clienttrace"); g_psv_maxvelocity = CVAR_GET_POINTER("sv_maxvelocity"); + g_psv_zmax = CVAR_GET_POINTER("sv_zmax"); CVAR_REGISTER(&displaysoundlist); CVAR_REGISTER(&timelimit); @@ -469,6 +472,7 @@ void EXT_FUNC GameDLLInit() CVAR_REGISTER(&freezetime_duck); CVAR_REGISTER(&freezetime_jump); CVAR_REGISTER(&jump_height); + CVAR_REGISTER(&longjump_cooldown); CVAR_REGISTER(&defuser_allocation); CVAR_REGISTER(&location_area_info); CVAR_REGISTER(&chat_loc_fallback); diff --git a/regamedll/dlls/game.h b/regamedll/dlls/game.h index 741e0a571..32e6ae015 100644 --- a/regamedll/dlls/game.h +++ b/regamedll/dlls/game.h @@ -50,6 +50,7 @@ extern cvar_t *g_psv_stopspeed; extern cvar_t *g_psv_stepsize; extern cvar_t *g_psv_clienttrace; extern cvar_t *g_psv_maxvelocity; +extern cvar_t *g_psv_zmax; extern cvar_t displaysoundlist; extern cvar_t timelimit; @@ -203,6 +204,7 @@ extern cvar_t assist_damage_threshold; extern cvar_t freezetime_duck; extern cvar_t freezetime_jump; extern cvar_t jump_height; +extern cvar_t longjump_cooldown; extern cvar_t defuser_allocation; extern cvar_t location_area_info; extern cvar_t chat_loc_fallback; diff --git a/regamedll/dlls/mapinfo.cpp b/regamedll/dlls/mapinfo.cpp index 48200b6f6..1c81e45e4 100644 --- a/regamedll/dlls/mapinfo.cpp +++ b/regamedll/dlls/mapinfo.cpp @@ -22,7 +22,9 @@ void CMapInfo::OnCreate() void CMapInfo::OnDestroy() { - g_pMapInfo = nullptr; + // Only the instance that owns the global may clear it. + if (g_pMapInfo == this) + g_pMapInfo = nullptr; } void CMapInfo::CheckMapInfo() diff --git a/regamedll/dlls/plats.cpp b/regamedll/dlls/plats.cpp index f0735053b..4069d521c 100644 --- a/regamedll/dlls/plats.cpp +++ b/regamedll/dlls/plats.cpp @@ -1051,12 +1051,19 @@ void CFuncTrackTrain::StopSound() // if sound playing, stop it if (m_soundPlaying && pev->noise) { +#ifdef REGAMEDLL_FIXES + // Stop the sound through the engine sound API: SND_STOP is delivered as a reliable + // broadcast, while the event is PVS-filtered and can be missed by distant players, + // leaving the engine loop running on their side. + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), 0, 0, SND_STOP, PITCH_NORM); +#else unsigned short us_encode; unsigned short us_sound = ((unsigned short)(m_sounds) & 0x0007) << 12; us_encode = us_sound; PLAYBACK_EVENT_FULL(FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 1, 0); +#endif EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_brake1.wav", m_flVolume, ATTN_NORM, 0, 100); } @@ -1070,19 +1077,40 @@ void CFuncTrackTrain::UpdateSound() #ifdef REGAMEDLL_FIXES float flpitch = TRAIN_STARTPITCH + (Q_abs(pev->speed) * (TRAIN_MAXPITCH - TRAIN_STARTPITCH) / TRAIN_MAXSPEED); + + // The engine sound API carries the pitch as a byte, keep it inside the intended range + if (flpitch > TRAIN_MAXPITCH) + flpitch = TRAIN_MAXPITCH; + + // Never report the engine pitch as exactly PITCH_NORM: a looping sound that is + // re-pitched to 100 drops out of the client's pitch-shifting mixer back into the + // plain playback path, which then runs past the end of the wave and mixes in + // whatever sound follows it in the client's sound cache. + // Same guard as CFuncRotating::RampPitchVol() and CAmbientGeneric. + int ipitch = int(flpitch); + if (ipitch == PITCH_NORM) + ipitch = PITCH_NORM - 1; #else float flpitch = TRAIN_STARTPITCH + (Q_abs(int(pev->speed)) * (TRAIN_MAXPITCH - TRAIN_STARTPITCH) / TRAIN_MAXSPEED); + int ipitch = int(flpitch); #endif if (!m_soundPlaying) { // play startup sound for train EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/ttrain_start1.wav", m_flVolume, ATTN_NORM, 0, 100); - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, int(flpitch)); + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, ipitch); m_soundPlaying = 1; } else { +#ifdef REGAMEDLL_FIXES + // Update pitch/volume through the engine sound API instead of the client event: + // the event packs the pitch as pitch / 10 into 6 bits, so the range is quantized + // to steps of 10 and everything in [100, 110) arrives as PITCH_NORM. The event is + // also PVS-filtered, while the sound itself is started with a reliable broadcast. + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH | SND_CHANGE_VOL, ipitch); +#else // update pitch // EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH, int(flpitch)); @@ -1099,6 +1127,7 @@ void CFuncTrackTrain::UpdateSound() us_encode = us_sound | us_pitch | us_volume; PLAYBACK_EVENT_FULL(FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0); +#endif } } diff --git a/regamedll/dlls/player.cpp b/regamedll/dlls/player.cpp index 25b538ef2..8a219ead3 100644 --- a/regamedll/dlls/player.cpp +++ b/regamedll/dlls/player.cpp @@ -4281,7 +4281,19 @@ void CBasePlayer::PlayerUse() if (legacy_vehicle_block.value == 0) return; #endif - ((CFuncVehicle *)pTrain)->m_pDriver = nullptr; + CFuncVehicle *pVehicle = (CFuncVehicle *)pTrain; + pVehicle->m_pDriver = nullptr; +#ifdef REGAMEDLL_FIXES + if (Q_abs(pTrain->pev->speed) < 50) + { + pVehicle->StopSound(); + pVehicle->SetThink(nullptr); + pTrain->pev->speed = 0; + pTrain->pev->velocity = g_vecZero; + pTrain->pev->avelocity = g_vecZero; + pVehicle->m_fEngineOn = FALSE; + } +#endif } return; } @@ -4299,8 +4311,18 @@ void CBasePlayer::PlayerUse() if (pTrain->Classify() == CLASS_VEHICLE) { + CFuncVehicle *pVehicle = (CFuncVehicle *)pTrain; +#ifdef REGAMEDLL_FIXES + if (!pVehicle->m_fEngineOn) + { + EMIT_SOUND(ENT(pev), CHAN_ITEM, "plats/vehicle_ignition.wav", 0.8, ATTN_NORM); + pVehicle->m_fEngineOn = TRUE; + pVehicle->UpdateSound(); + } +#else EMIT_SOUND(ENT(pev), CHAN_ITEM, "plats/vehicle_ignition.wav", 0.8, ATTN_NORM); - ((CFuncVehicle *)pTrain)->m_pDriver = this; +#endif + pVehicle->m_pDriver = this; } else EMIT_SOUND(ENT(pev), CHAN_ITEM, "plats/train_use1.wav", 0.8, ATTN_NORM); @@ -8252,7 +8274,13 @@ void EXT_FUNC CBasePlayer::__API_HOOK(UpdateStatusBar)() UTIL_MakeVectors(pev->v_angle + pev->punchangle); Vector vecSrc = EyePosition(); - Vector vecEnd = vecSrc + (gpGlobals->v_forward * ((pev->flags & FL_SPECTATOR) != 0 ? MAX_SPEC_ID_RANGE : MAX_ID_RANGE)); + Vector vecEnd = vecSrc + (gpGlobals->v_forward * ((pev->flags & FL_SPECTATOR) != 0 ? MAX_SPEC_ID_RANGE : +#ifdef REGAMEDLL_ADD + (g_psv_zmax ? g_psv_zmax->value : MAX_ID_RANGE) +#else + MAX_ID_RANGE +#endif + )); UTIL_TraceLine(vecSrc, vecEnd, dont_ignore_monsters, edict(), &tr); diff --git a/regamedll/dlls/trains.h b/regamedll/dlls/trains.h index ebef8bf5f..33de0b548 100644 --- a/regamedll/dlls/trains.h +++ b/regamedll/dlls/trains.h @@ -198,6 +198,10 @@ class CFuncVehicle: public CBaseEntity Vector m_vSurfaceNormal; Vector m_vVehicleDirection; CBaseEntity *m_pDriver; +#ifdef REGAMEDLL_FIXES + int m_iCollisionCount; + BOOL m_fEngineOn; +#endif private: unsigned short m_usAdjustPitch; diff --git a/regamedll/dlls/vehicle.cpp b/regamedll/dlls/vehicle.cpp index 7bf2d4184..fef3f7833 100644 --- a/regamedll/dlls/vehicle.cpp +++ b/regamedll/dlls/vehicle.cpp @@ -288,10 +288,17 @@ void CFuncVehicle::StopSound() { if (m_soundPlaying && pev->noise) { +#ifdef REGAMEDLL_FIXES + // Stop the sound through the engine sound API: SND_STOP is delivered as a reliable + // broadcast, while the event is PVS-filtered and can be missed by distant players, + // leaving the engine loop running on their side. + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), 0, 0, SND_STOP, PITCH_NORM); +#else unsigned short us_sound = ((unsigned short)m_sounds & 0x0007) << 12; unsigned short us_encode = us_sound; PLAYBACK_EVENT_FULL(FEV_RELIABLE | FEV_UPDATE, edict(), m_usAdjustPitch, 0, (float *)&g_vecZero, (float *)&g_vecZero, 0, 0, us_encode, 0, 1, 0); +#endif } m_soundPlaying = 0; @@ -311,6 +318,19 @@ void CFuncVehicle::UpdateSound() if (flpitch > 200) flpitch = 200; +#ifdef REGAMEDLL_FIXES + // Never report the engine pitch as exactly PITCH_NORM: a looping sound that is + // re-pitched to 100 drops out of the client's pitch-shifting mixer back into the + // plain playback path, which then runs past the end of the wave and mixes in + // whatever sound follows it in the client's sound cache. + // Same guard as CFuncRotating::RampPitchVol() and CAmbientGeneric. + int ipitch = int(flpitch); + if (ipitch == PITCH_NORM) + ipitch = PITCH_NORM - 1; +#else + int ipitch = int(flpitch); +#endif + if (!m_soundPlaying) { if (m_sounds < 5) @@ -318,17 +338,25 @@ void CFuncVehicle::UpdateSound() EMIT_SOUND_DYN(ENT(pev), CHAN_ITEM, "plats/vehicle_brake1.wav", m_flVolume, ATTN_NORM, 0, 100); } - EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, int(flpitch)); + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, 0, ipitch); m_soundPlaying = 1; } else { +#ifdef REGAMEDLL_FIXES + // Update pitch/volume through the engine sound API instead of the client event: + // the event packs the pitch as pitch / 10 into 6 bits, so the range is quantized + // to steps of 10 and everything in [100, 110) arrives as PITCH_NORM. The event is + // also PVS-filtered, while the sound itself is started with a reliable broadcast. + EMIT_SOUND_DYN(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise), m_flVolume, ATTN_NORM, SND_CHANGE_PITCH | SND_CHANGE_VOL, ipitch); +#else unsigned short us_sound = ((unsigned short)(m_sounds) & 0x0007) << 12; unsigned short us_pitch = ((unsigned short)(flpitch / 10.0) & 0x003F) << 6; unsigned short us_volume = ((unsigned short)(m_flVolume * 40) & 0x003F); unsigned short us_encode = us_sound | us_pitch | us_volume; PLAYBACK_EVENT_FULL(FEV_UPDATE, edict(), m_usAdjustPitch, 0.0, (float *)&g_vecZero, (float *)&g_vecZero, 0.0, 0.0, us_encode, 0, 0, 0); +#endif } } @@ -441,6 +469,9 @@ void CFuncVehicle::CollisionDetection() if (tr.flFraction == 1.0f) { +#ifdef REGAMEDLL_FIXES + m_iCollisionCount = 0; +#endif return; } } @@ -454,6 +485,16 @@ void CFuncVehicle::CollisionDetection() } else if (tr.vecPlaneNormal.z < 0.65 || tr.fStartSolid) { +#ifdef REGAMEDLL_FIXES + m_iCollisionCount++; + if (m_iCollisionCount >= 3) + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + } + else +#endif pev->speed *= -1.0; } else @@ -472,6 +513,16 @@ void CFuncVehicle::CollisionDetection() } else if (tr.vecPlaneNormal[2] < 0.65f || tr.fStartSolid) { +#ifdef REGAMEDLL_FIXES + m_iCollisionCount++; + if (m_iCollisionCount >= 3) + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + } + else +#endif pev->speed *= -1.0; } else @@ -502,6 +553,9 @@ void CFuncVehicle::CollisionDetection() if (tr.flFraction == 1.0f) { +#ifdef REGAMEDLL_FIXES + m_iCollisionCount = 0; +#endif return; } } @@ -516,6 +570,16 @@ void CFuncVehicle::CollisionDetection() } else if (tr.vecPlaneNormal.z < 0.65 || tr.fStartSolid) { +#ifdef REGAMEDLL_FIXES + m_iCollisionCount++; + if (m_iCollisionCount >= 3) + { + pev->speed = 0; + pev->velocity = g_vecZero; + pev->avelocity = g_vecZero; + } + else +#endif pev->speed *= -1.0; } else @@ -599,6 +663,12 @@ void CFuncVehicle::Next() pev->avelocity = g_vecZero; pev->velocity = g_vecZero; +#ifdef REGAMEDLL_FIXES + // Ensure idle sound is playing when vehicle stops + if (m_fEngineOn && !m_soundPlaying) + UpdateSound(); +#endif + SetThink(&CFuncVehicle::Next); NextThink(pev->ltime + time, TRUE); return; @@ -805,8 +875,13 @@ void CFuncVehicle::Find() UTIL_SetOrigin(pev, nextPos); NextThink(pev->ltime + 0.1, FALSE); SetThink(&CFuncVehicle::Next); +#ifdef REGAMEDLL_FIXES + if (m_fEngineOn) + pev->speed = m_startSpeed; +#else pev->speed = m_startSpeed; UpdateSound(); +#endif } void CFuncVehicle::NearestPath() @@ -902,6 +977,10 @@ void CFuncVehicle::Spawn() m_dir = 1; m_flTurnStartTime = -1; +#ifdef REGAMEDLL_FIXES + m_iCollisionCount = 0; + m_fEngineOn = FALSE; +#endif if (FStringNull(pev->target)) { @@ -946,6 +1025,10 @@ void CFuncVehicle::Restart() m_flTurnStartTime = -1; m_flUpdateSound = -1; m_pDriver = nullptr; +#ifdef REGAMEDLL_FIXES + m_iCollisionCount = 0; + m_fEngineOn = FALSE; +#endif if (FStringNull(pev->target)) { @@ -954,6 +1037,9 @@ void CFuncVehicle::Restart() UTIL_SetOrigin(pev, pev->oldorigin); STOP_SOUND(ENT(pev), CHAN_STATIC, (char *)STRING(pev->noise)); +#ifdef REGAMEDLL_FIXES + m_soundPlaying = 0; +#endif NextThink(pev->ltime + 0.1f, FALSE); SetThink(&CFuncVehicle::Find); diff --git a/regamedll/pm_shared/pm_shared.cpp b/regamedll/pm_shared/pm_shared.cpp index 846b410d0..b524513e3 100644 --- a/regamedll/pm_shared/pm_shared.cpp +++ b/regamedll/pm_shared/pm_shared.cpp @@ -2762,25 +2762,40 @@ void EXT_FUNC __API_HOOK(PM_Jump)() // UNDONE: note this should be based on forward angles, not current velocity. if (cansuperjump && (pmove->cmd.buttons & IN_DUCK) && pmove->flDuckTime > 0 && Length(pmove->velocity) > 50) { - pmove->punchangle[0] = -5.0f; - -#ifdef REGAMEDLL_API - if (pmoveplayer->m_flLongJumpForce > 0.0) +#ifdef REGAMEDLL_ADD + if (pmove->fuser4 > 0.0f) { - fvel = pmoveplayer->m_flLongJumpForce; + pmove->velocity[2] = PM_JumpHeight(false); } else #endif { - fvel = PLAYER_LONGJUMP_SPEED * 1.6f; - } + pmove->punchangle[0] = -5.0f; - for (int i = 0; i < 2; i++) - { - pmove->velocity[i] = pmove->forward[i] * fvel; - } +#ifdef REGAMEDLL_API + if (pmoveplayer->m_flLongJumpForce > 0.0) + { + fvel = pmoveplayer->m_flLongJumpForce; + } + else +#endif + { + fvel = PLAYER_LONGJUMP_SPEED * 1.6f; + } - pmove->velocity[2] = PM_JumpHeight(true); + for (int i = 0; i < 2; i++) + { + pmove->velocity[i] = pmove->forward[i] * fvel; + } + + pmove->velocity[2] = PM_JumpHeight(true); +#ifdef REGAMEDLL_ADD + if (longjump_cooldown.value > 0.0f) + { + pmove->fuser4 = longjump_cooldown.value * 1000.0f; + } +#endif + } } else { @@ -3112,6 +3127,18 @@ void PM_ReduceTimers() pmove->fuser2 = 0; } } + +#ifdef REGAMEDLL_ADD + if (pmove->fuser4 > 0.0) + { + pmove->fuser4 -= pmove->cmd.msec; + + if (pmove->fuser4 < 0.0) + { + pmove->fuser4 = 0; + } + } +#endif } qboolean PM_ShouldDoSpectMode()