-
-
Notifications
You must be signed in to change notification settings - Fork 131
Expand file tree
/
Copy pathHooks.Gamespeed.cpp
More file actions
115 lines (95 loc) · 3.64 KB
/
Hooks.Gamespeed.cpp
File metadata and controls
115 lines (95 loc) · 3.64 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#include <Phobos.h>
#include <Utilities/Macro.h>
#include <algorithm>
#include <SessionClass.h>
#include <GameOptionsClass.h>
DEFINE_HOOK(0x69BAE7, SessionClass_Resume_CampaignGameSpeed, 0xA)
{
GameOptionsClass::Instance.GameSpeed = Phobos::Config::CampaignDefaultGameSpeed;
return 0x69BAF1;
}
// For custom game speeds:
// Add to rulesmd.ini under [General]:
// EnableCustomFPS=yes ; Enable/disable custom FPS
// CustomGameSpeedFPS.0=120 ; Per-speed FPS. 0 = vanilla. Vanilla: 0=60, 1=45, 2=30, 3=20, 4=15, 5=12, 6=10
//
// -Each speed slot with a non-zero CustomGameSpeedFPS.N runs at that target FPS
// -Slots with 0 (or unset) use vanilla FPS for that position
// -Practical max is ~1000 FPS (timeGetTime() resolution)
// Queue_AI_Multiplayer
// Patch v26 to INT_MAX disables the 60fps cap on multiplayer.
DEFINE_PATCH(0x647C28, 0xBE, 0xFF, 0xFF, 0xFF, 0x7F); // mov esi, INT_MAX
// Queue_AI_Multiplayer
// Override the GameSpeed-to-fps calculation in multiplayer.
DEFINE_HOOK(0x647C4D, Queue_AI_Multiplayer_CustomFPSCalculation, 0x1F)
{
int gameSpeed = GameOptionsClass::Instance.GameSpeed;
int calculatedFPS;
if (Phobos::Misc::EnableCustomFPS && Phobos::Misc::CustomGameSpeedFPS[gameSpeed] > 0)
{
calculatedFPS = Phobos::Misc::CustomGameSpeedFPS[gameSpeed];
}
else if (gameSpeed == 0)
{
// Vanilla: GameSpeed 0 = 60 FPS
calculatedFPS = 60;
}
else if (gameSpeed == 1)
{
// Vanilla: GameSpeed 1 = 45 FPS
calculatedFPS = 45;
}
else
{
// Vanilla: GameSpeed 2+ = 60 / GameSpeed
calculatedFPS = 60 / gameSpeed;
}
R->EAX(calculatedFPS);
return 0x647C6C;
}
// Hook MainLoop skirmish/campaign FPS calculation
// The normal route FrameTimer.TimeLeft rounds to 0 for >60 FPS (tick-based timeGetTime >> 4),
// NetworkFrameTimer (ms-based timeGetTime) provides the frame timing that SyncDelay's NetworkFrameTimer loop uses.
// We need to set up NetworkFrameTimer like multiplayer mode does for >60 FPS support.
DEFINE_HOOK(0x55D7B6, MainLoop_SkirmishFPSFix, 0xC)
{
const DWORD timerValue = R->ECX();
const int gameSpeed = R->ESI();
const bool shouldUseCustomFPS = Phobos::Misc::EnableCustomFPS
&& Phobos::Misc::CustomGameSpeedFPS[gameSpeed] > 0
&& (SessionClass::IsSkirmish() || SessionClass::IsCampaign());
if (!shouldUseCustomFPS)
{
// Use vanilla behavior
Unsorted::GameFrameTimer.CurrentTime = timerValue;
Unsorted::GameFrameTimer.TimeLeft = gameSpeed;
return 0x55D7C2;
}
const int customFPS = Phobos::Misc::CustomGameSpeedFPS[gameSpeed];
// calc frame timings
const int targetFrameDelayTicks = 60 / customFPS;
const int targetFrameTimeMs = std::max(1, 1000 / customFPS); // 1ms floor = ~1000 FPS ceiling
const DWORD currentTime = timeGetTime();
// just in case
Unsorted::GameFrameTimer.CurrentTime = timerValue;
Unsorted::GameFrameTimer.TimeLeft = targetFrameDelayTicks;
// SyncDelay compares elapsed time since CurrentTime against TimeLeft.
Unsorted::NetworkFrameTimer.StartTime = currentTime;
Unsorted::NetworkFrameTimer.CurrentTime = currentTime;
Unsorted::NetworkFrameTimer.TimeLeft = targetFrameTimeMs;
// "Requested FPS"
SessionClass::Instance.DesiredFrameRate = customFPS;
return 0x55D7C2; // Past the two MOVs we replaced
}
// SyncDelay
// Redirect skirmish/campaign to the NetworkFrameTimer path
DEFINE_HOOK(0x55E1B6, SyncDelay_RedirectSkirmishToNetworkFrameTimer, 0x6)
{
if (SessionClass::IsSkirmish() || SessionClass::IsCampaign())
{
if (Phobos::Misc::EnableCustomFPS && Phobos::Misc::CustomGameSpeedFPS[GameOptionsClass::Instance.GameSpeed] > 0)
return 0x55E1BC; // Custom FPS: use NetworkFrameTimer path like multiplayer
return 0x55E2B4; // Vanilla FrameTimer path
}
return 0x55E1BC;
}