Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -756,4 +756,6 @@ This page lists all the individual contributions to the project by their author.
- **Dmitry Volkov** - extensive and thorough testing
- **Rise of the East community** - extensive playtesting of in-dev features
- **11EJDE11** - Prevent mpdebug number from being drawn when visibility toggled off
- **CnCRazer** - Wall overlay unit sell exploit fix
- **RAZER**:
- Wall overlay unit sell exploit fix
- Multiplayer gamespeed fix for RealTimeTimers
1 change: 1 addition & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -638,6 +638,7 @@ Phobos fixes:
- Fixed an issue where parasites that have infected infantry do not provide a refund when the infected infantry enters a Grinding building (by NetsuNegi)
- Fixed the issue that `PassengerDeletion` dont consider passenger's passenger, parasite and hijacker (by NetsuNegi)
- Fixed the issue that power output of building on tooltip won't consider power enhancer (by NetsuNegi)
- `RealTimeTimers` now support independent gamespeed index values for Multiplayer and Skirmish (by RAZER)

Fixes / interactions with other extensions:
<!-- - Allowed `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades (by Ollerus) -->
Expand Down
85 changes: 63 additions & 22 deletions src/Misc/Hooks.Timers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,35 +16,76 @@ DEFINE_HOOK(0x6D4B50, PrintTimerOnTactical_Start, 0x6)
REF_STACK(int, value, STACK_OFFSET(0, 0x4));
TimerValueTemp::oldValue = value;

const bool isMP = SessionClass::IsMultiplayer();

// In SP/Skirmish, GameSpeed 0 is unlimited (no frame delay) so use adaptive FPS path.
// In MP, GameSpeed 0 is 60 FPS (valid fixed speed), so it must not enter this path.
if (Phobos::Config::RealTimeTimers_Adaptive
|| GameOptionsClass::Instance.GameSpeed == 0
|| (Phobos::Misc::CustomGS && !SessionClass::IsMultiplayer()))
|| (!isMP && GameOptionsClass::Instance.GameSpeed == 0)
|| (Phobos::Misc::CustomGS && !isMP))
{
value = (int)((double)value / (std::max((double)FPSCounter::CurrentFrameRate, 1.0) / 15.0));
return 0;
}

switch (GameOptionsClass::Instance.GameSpeed)
const int gs = GameOptionsClass::Instance.GameSpeed;

if (isMP)
{
// MP GameSpeed indices (0-6): 60, 45, 30, 20, 15, 12, 10 FPS
// MP has an extra 45 FPS option (index 1) that SP/Skirmish does not.
switch (gs)
{
case 0: // 60 FPS
value = value / 4;
break;
case 1: // 45 FPS
value = value / 3;
break;
case 2: // 30 FPS
value = value / 2;
break;
case 3: // 20 FPS
value = (value * 3) / 4;
break;
case 4: // 15 FPS
break;
case 5: // 12 FPS
value = (value * 5) / 4;
break;
case 6: // 10 FPS
value = (value * 3) / 2;
break;
default:
break;
}
}
else
{
case 1: // 60 FPS
value = value / 4;
break;
case 2: // 30 FPS
value = value / 2;
break;
case 3: // 20 FPS
value = (value * 3) / 4;
break;
case 4: // 15 FPS
break;
case 5: // 12 FPS
value = (value * 5) / 4;
break;
case 6: // 10 FPS
value = (value * 3) / 2;
break;
default:
break;
// SP/Skirmish GameSpeed indices (1-6): 60, 30, 20, 15, 12, 10 FPS
// Index 0 (unlimited) is already handled above via the adaptive path.
switch (gs)
{
case 1: // 60 FPS
value = value / 4;
break;
case 2: // 30 FPS
value = value / 2;
break;
case 3: // 20 FPS
value = (value * 3) / 4;
break;
case 4: // 15 FPS
break;
case 5: // 12 FPS
value = (value * 5) / 4;
break;
case 6: // 10 FPS
value = (value * 3) / 2;
break;
default:
break;
}
}

return 0;
Expand Down