From a2139d4fe2e206458eda6b9cf381fb06aa0894e6 Mon Sep 17 00:00:00 2001 From: Daniel Gibson Date: Fri, 16 Aug 2024 04:00:03 +0200 Subject: [PATCH] Fix oxygen consumption code (idPlayer::airTics) for > 60Hz it already mostly worked, but picking up oxygen bottles gave too little oxygen at >60fps and changing com_gameHz while being "outside" would screw up the remaining oxygen and the implementation was needlessly invasive anyway. I reverted most changes, turned idPlayer::airTics into a float (so they are now virtual 60Hz tics instead of actual number of tics) and adjusted the code that increases or decreases the airTics (idPlayer::UpdateAir()) to scale the values added/subtracted accordingly. Apart from that it's only a few changes to accomodate the fact that it's a float now. --- neo/d3xp/Player.cpp | 40 +++++++++++++++++++--------------------- neo/d3xp/Player.h | 3 ++- neo/game/Player.cpp | 35 ++++++++++++++++++----------------- neo/game/Player.h | 3 ++- 4 files changed, 41 insertions(+), 40 deletions(-) diff --git a/neo/d3xp/Player.cpp b/neo/d3xp/Player.cpp index a4f94366..cded54d5 100644 --- a/neo/d3xp/Player.cpp +++ b/neo/d3xp/Player.cpp @@ -1599,8 +1599,8 @@ void idPlayer::Init( void ) { // stamina always initialized to maximum stamina = pm_stamina.GetFloat(); - // air always initialized to maximum too - DG: pm_airTics must be scaled for actual FPS from com_gameHz - airTics = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); + // air always initialized to maximum too + airTics = pm_airTics.GetFloat(); airless = false; gibDeath = false; @@ -2152,7 +2152,7 @@ void idPlayer::Save( idSaveGame *savefile ) const { savefile->WriteInt( numProjectileHits ); savefile->WriteBool( airless ); - savefile->WriteInt( airTics ); + savefile->WriteInt( (int)airTics ); savefile->WriteInt( lastAirDamage ); savefile->WriteBool( gibDeath ); @@ -2420,7 +2420,11 @@ void idPlayer::Restore( idRestoreGame *savefile ) { savefile->ReadInt( numProjectileHits ); savefile->ReadBool( airless ); - savefile->ReadInt( airTics ); + // DG: I made made airTics float for high-fps (where we have fractions of 60Hz tics), + // but for saving ints should still suffice (and this preserves savegame compat) + int iairTics; + savefile->ReadInt( iairTics ); + airTics = iairTics; savefile->ReadInt( lastAirDamage ); savefile->ReadBool( gibDeath ); @@ -3523,14 +3527,12 @@ bool idPlayer::Give( const char *statname, const char *value ) { } } else if ( !idStr::Icmp( statname, "air" ) ) { - // DG: pm_airTics must be scaled for actual FPS from com_gameHz - int airTicsCnt = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); - if ( airTics >= airTicsCnt ) { + if ( airTics >= pm_airTics.GetFloat() ) { // DG: airTics are floats now for high-fps support return false; } - airTics += atoi( value ) / 100.0 * pm_airTics.GetInteger(); - if ( airTics > airTicsCnt ) { - airTics = airTicsCnt; + airTics += atoi( value ) / 100.0 * pm_airTics.GetFloat(); + if ( airTics > pm_airTics.GetFloat() ) { + airTics = pm_airTics.GetFloat(); } #ifdef _D3XP } else if ( !idStr::Icmp( statname, "enviroTime" ) ) { @@ -6085,9 +6087,6 @@ void idPlayer::UpdateAir( void ) { return; } - // DG: pm_airTics must be scaled for actual FPS from com_gameHz - int airTicsCnt = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); - // see if the player is connected to the info_vacuum bool newAirless = false; @@ -6122,7 +6121,8 @@ void idPlayer::UpdateAir( void ) { hud->HandleNamedEvent( "noAir" ); } } - airTics--; + // DG: was airTics--, but airTics assume 60Hz tics and we support other ticrates now (com_gameHz) + airTics -= 1.0f / gameLocal.gameTicScale; if ( airTics < 0 ) { airTics = 0; // check for damage @@ -6142,16 +6142,16 @@ void idPlayer::UpdateAir( void ) { hud->HandleNamedEvent( "Air" ); } } - airTics+=2; // regain twice as fast as lose - if ( airTics > airTicsCnt ) { - airTics = airTicsCnt; + airTics += 2.0f / gameLocal.gameTicScale; // regain twice as fast as lose - DG: scale for com_gameHz + if ( airTics > pm_airTics.GetFloat() ) { + airTics = pm_airTics.GetFloat(); } } airless = newAirless; if ( hud ) { - hud->SetStateInt( "player_air", 100 * airTics / airTicsCnt ); + hud->SetStateInt( "player_air", 100 * (airTics / pm_airTics.GetFloat()) ); } } @@ -7669,9 +7669,7 @@ bool idPlayer::CanGive( const char *statname, const char *value ) { return true; } else if ( !idStr::Icmp( statname, "air" ) ) { - // DG: pm_airTics must be scaled for actual FPS from com_gameHz - int airTicsCnt = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); - if ( airTics >= airTicsCnt ) { + if ( airTics >= pm_airTics.GetFloat() ) { return false; } return true; diff --git a/neo/d3xp/Player.h b/neo/d3xp/Player.h index aadf8550..b0a9bbe3 100644 --- a/neo/d3xp/Player.h +++ b/neo/d3xp/Player.h @@ -659,7 +659,8 @@ private: int numProjectileHits; // number of hits on mobs bool airless; - int airTics; // set to pm_airTics at start, drops in vacuum + // DG: Note: airTics are tics at 60Hz, so no real tics (unless com_gameHz happens to be 60) + float airTics; // set to pm_airTics at start, drops in vacuum int lastAirDamage; bool gibDeath; diff --git a/neo/game/Player.cpp b/neo/game/Player.cpp index d5656b30..63ddce16 100644 --- a/neo/game/Player.cpp +++ b/neo/game/Player.cpp @@ -1294,8 +1294,8 @@ void idPlayer::Init( void ) { // stamina always initialized to maximum stamina = pm_stamina.GetFloat(); - // air always initialized to maximum too - DG: pm_airTics must be scaled for actual FPS for com_gameHz - airTics = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); + // air always initialized to maximum too + airTics = pm_airTics.GetFloat(); airless = false; gibDeath = false; @@ -1747,7 +1747,7 @@ void idPlayer::Save( idSaveGame *savefile ) const { savefile->WriteInt( numProjectileHits ); savefile->WriteBool( airless ); - savefile->WriteInt( airTics ); + savefile->WriteInt( (int)airTics ); savefile->WriteInt( lastAirDamage ); savefile->WriteBool( gibDeath ); @@ -1979,7 +1979,11 @@ void idPlayer::Restore( idRestoreGame *savefile ) { savefile->ReadInt( numProjectileHits ); savefile->ReadBool( airless ); - savefile->ReadInt( airTics ); + // DG: I made made airTics float for high-fps (where we have fractions of 60Hz tics), + // but for saving ints should still suffice (and this preserves savegame compat) + int iairTics; + savefile->ReadInt( iairTics ); + airTics = iairTics; savefile->ReadInt( lastAirDamage ); savefile->ReadBool( gibDeath ); @@ -2903,14 +2907,12 @@ bool idPlayer::Give( const char *statname, const char *value ) { } } else if ( !idStr::Icmp( statname, "air" ) ) { - // DG: pm_airTics must be scaled for actual FPS from com_gameHz - int airTicsCnt = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); - if ( airTics >= airTicsCnt ) { + if ( airTics >= pm_airTics.GetFloat() ) { // DG: airTics are floats now for high-fps support return false; } - airTics += atoi( value ) / 100.0 * pm_airTics.GetInteger(); - if ( airTics > airTicsCnt ) { - airTics = airTicsCnt; + airTics += atoi( value ) / 100.0 * pm_airTics.GetFloat(); + if ( airTics > pm_airTics.GetFloat() ) { + airTics = pm_airTics.GetFloat(); } } else { return inventory.Give( this, spawnArgs, statname, value, &idealWeapon, true ); @@ -5071,8 +5073,6 @@ void idPlayer::UpdateAir( void ) { // see if the player is connected to the info_vacuum bool newAirless = false; - // DG: pm_airTics must be scaled for actual FPS from com_gameHz - int airTicsCnt = idMath::Rint( pm_airTics.GetFloat() * gameLocal.gameTicScale ); if ( gameLocal.vacuumAreaNum != -1 ) { int num = GetNumPVSAreas(); @@ -5099,7 +5099,8 @@ void idPlayer::UpdateAir( void ) { hud->HandleNamedEvent( "noAir" ); } } - airTics--; + // DG: was airTics--, but airTics assume 60Hz tics and we support other ticrates now (com_gameHz) + airTics -= 1.0f / gameLocal.gameTicScale; if ( airTics < 0 ) { airTics = 0; // check for damage @@ -5119,16 +5120,16 @@ void idPlayer::UpdateAir( void ) { hud->HandleNamedEvent( "Air" ); } } - airTics+=2; // regain twice as fast as lose - if ( airTics > airTicsCnt ) { - airTics = airTicsCnt; + airTics += 2.0f / gameLocal.gameTicScale; // regain twice as fast as lose - DG: scale for com_gameHz + if ( airTics > pm_airTics.GetFloat() ) { + airTics = pm_airTics.GetFloat(); } } airless = newAirless; if ( hud ) { - hud->SetStateInt( "player_air", 100 * airTics / airTicsCnt ); + hud->SetStateInt( "player_air", 100 * (airTics / pm_airTics.GetFloat()) ); } } diff --git a/neo/game/Player.h b/neo/game/Player.h index b411a0c0..46b8ef33 100644 --- a/neo/game/Player.h +++ b/neo/game/Player.h @@ -564,7 +564,8 @@ private: int numProjectileHits; // number of hits on mobs bool airless; - int airTics; // set to pm_airTics at start, drops in vacuum + // DG: Note: airTics are tics at 60Hz, so no real tics (unless com_gameHz happens to be 60) + float airTics; // set to pm_airTics at start, drops in vacuum int lastAirDamage; bool gibDeath;