Skip to content

Commit 8351489

Browse files
committed
Partially fix switch player outfit in TR8
1 parent e12672a commit 8351489

File tree

8 files changed

+136
-2
lines changed

8 files changed

+136
-2
lines changed

src/game/Game.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#include "Game.h"
22

33
#include "util/Hooking.h"
4+
#include "game/NtUnlockableCostume.h"
45

56
Instance* Game::GetPlayerInstance() noexcept
67
{
@@ -17,6 +18,23 @@ STracker* Game::GetStreamTracker() noexcept
1718
return (STracker*)GET_ADDRESS(0x11582F8, 0x8AE378, 0xDBAB40);
1819
}
1920

21+
void Game::SwitchPlayerCharacter() noexcept
22+
{
23+
#ifndef TR8
24+
PLAYER_DebugSwitchPlayerCharacter()
25+
#else
26+
// Switch the player costume with costume index 12, this costume has an empty object id
27+
// which allows us to fallback to the default behavior
28+
NtUnlockableCostume costume;
29+
costume.costumeIndex = 12;
30+
31+
NtUnlockableCostume_ScriptType::SwitchPlayerCostume(&costume);
32+
33+
// Post to birth the player weapons
34+
INSTANCE_Post(Game::GetPlayerInstance(), 35, 1);
35+
#endif
36+
}
37+
2038
bool Game::IsInNextGenMode() noexcept
2139
{
2240
return *(bool*)GET_ADDRESS(0x10024E8, 0x7545B4, 0x000000);
@@ -57,6 +75,13 @@ int OBTABLE_GetObjectID(char* name)
5775
return Hooking::CallReturn<int>(addr, name);
5876
}
5977

78+
char* OBTABLE_GetObjectName(int id)
79+
{
80+
auto objectList = *(ObjectList**)GET_ADDRESS(0x000000, 0x000000, 0xDB94D0);
81+
82+
return objectList->object[id - 1].name;
83+
}
84+
6085
void LOAD_ObjectFileName(char* name, char* object, char* extension)
6186
{
6287
return LOAD_UnitFileName(name, object, extension);

src/game/Game.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,18 @@ struct GameTracker
162162

163163
float timeDilation;
164164
};
165+
166+
struct PlayerObjects
167+
{
168+
__int16 numPlayerObjects;
169+
__int16* playerObjectList;
170+
};
171+
172+
struct GlobalInfo
173+
{
174+
int field_0;
175+
PlayerObjects* playerObjects;
176+
};
165177
#endif
166178

167179
class Game
@@ -171,6 +183,8 @@ class Game
171183
static GameTracker* GetGameTracker() noexcept;
172184
static STracker* GetStreamTracker() noexcept;
173185

186+
static void SwitchPlayerCharacter() noexcept;
187+
174188
static bool IsInNextGenMode() noexcept;
175189
};
176190

@@ -179,7 +193,9 @@ bool GAMELOOP_IsWipeDone(int type);
179193
void GAMELOOP_SetScreenWipe(int type, int target, int time);
180194

181195
void PLAYER_DebugSwitchPlayerCharacter();
196+
182197
int OBTABLE_GetObjectID(char* name);
198+
char* OBTABLE_GetObjectName(int id);
183199

184200
void LOAD_ObjectFileName(char* name, char* object, char* extension);
185201
void LOAD_UnitFileName(char* name, char* unit, char* extension);

src/game/NtUnlockableCostume.cpp

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#include "NtUnlockableCostume.h"
2+
#include "util/Hooking.h"
3+
4+
void NtUnlockableCostume_ScriptType::SwitchPlayerCostume(NtUnlockableCostume* costume)
5+
{
6+
Hooking::Call(0x79DB50, costume);
7+
}

src/game/NtUnlockableCostume.h

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
#pragma once
2+
3+
struct NtUnlockableCostume
4+
{
5+
int costumeIndex;
6+
};
7+
8+
class NtUnlockableCostume_ScriptType
9+
{
10+
public:
11+
static void SwitchPlayerCostume(NtUnlockableCostume* costume);
12+
};

src/level/Stream.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,20 @@ struct ObjectTracker
4343
__int16 objectStatus;
4444
};
4545

46+
struct ObjectEntry
47+
{
48+
char* name;
49+
int trackerIndex;
50+
};
51+
52+
struct ObjectList
53+
{
54+
int numObjects;
55+
#ifdef TR8
56+
int size;
57+
#endif
58+
ObjectEntry object[1];
59+
};
60+
4661
ObjectTracker* STREAM_GetObjectTrackerByName(char* name);
4762
bool STREAM_PollLoadQueue();

src/modules/MainMenu.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ void MainMenu::OnDraw()
4848
// TODO fill up ammo
4949
IncrHealth(data->oldData.HealthInitial);
5050
}
51+
#endif
5152

5253
// Switch outfit
5354
static char outfit[64] = "";
@@ -65,6 +66,7 @@ void MainMenu::OnDraw()
6566
SwitchPlayerCharacter();
6667
}
6768

69+
#ifndef TR8
6870
// Player flags
6971
auto flags = (unsigned int*)GET_ADDRESS(0x1075B88, 0x7C7C78, 0x000000);
7072

@@ -190,7 +192,7 @@ void MainMenu::SwitchPlayerCharacter(char* name) noexcept
190192
void MainMenu::OnFrame()
191193
{
192194
#ifndef TR8
193-
// Shows the watermark in th main menu
195+
// Shows the watermark in the main menu
194196
auto mainState = *(int*)GET_ADDRESS(0x10E5868, 0x838838, 0xE7ED60);
195197

196198
if (mainState == MS_DISPLAY_MAIN_MENU && !m_noWatermark.GetValue())
@@ -208,7 +210,7 @@ void MainMenu::OnLoop()
208210
if (m_switchPlayerNextFrame)
209211
{
210212
m_switchPlayerNextFrame = false;
211-
PLAYER_DebugSwitchPlayerCharacter();
213+
Game::SwitchPlayerCharacter();
212214
}
213215
}
214216

@@ -228,6 +230,12 @@ void MainMenu::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
228230
Input::DisablePlayerControl(Input::IsPlayerControlEnabled());
229231
}
230232

233+
// Switch player outfit
234+
if (msg == WM_KEYUP && wParam == VK_F9)
235+
{
236+
m_switchPlayerNextFrame = true;
237+
}
238+
231239
// Ragdoll death
232240
if (msg == WM_KEYUP && wParam == VK_F11)
233241
{

src/modules/Patches.cpp

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include "patches/Multicore.h"
1212
#include "game/Camera.h"
1313
#include "render/Draw.h"
14+
#include "file/FileSystem.h"
1415

1516
// Instance of patches so we can get it in our hooks without calling GetModule<T> each call
1617
static Patches* s_patches;
@@ -90,13 +91,25 @@ static void __stdcall DeathState_Process(int player, int data)
9091
}
9192
}
9293

94+
#ifdef TR8
95+
static void(*s_MAIN_DoMainInit)();
96+
9397
static unsigned char CPUCount(unsigned int* TotAvailLogical, unsigned int* TotAvailCore, unsigned int* PhysicalNum)
9498
{
9599
*TotAvailLogical = std::thread::hardware_concurrency();
96100

97101
return 0;
98102
}
99103

104+
static void MAIN_DoMainInit()
105+
{
106+
s_MAIN_DoMainInit();
107+
108+
// Patch the player list once globalInfo has been loaded
109+
s_patches->PatchPlayersList();
110+
}
111+
#endif
112+
100113
Patches::Patches()
101114
{
102115
s_patches = this;
@@ -144,6 +157,8 @@ Patches::Patches()
144157
// Fix cdcMultiCore breaking on high number of CPU cores
145158
MH_CreateHook((void*)0x4A21D0, CPUCount, nullptr);
146159
MH_CreateHook((void*)0x4A2680, cdc::JobChainImplWithThreads::StartSystem, (void**)&cdc::JobChainImplWithThreads::s_StartSystem);
160+
161+
MH_CreateHook((void*)0x5DEF70, MAIN_DoMainInit, (void**)&s_MAIN_DoMainInit);
147162
#endif
148163

149164
#ifdef TR7
@@ -191,6 +206,39 @@ void Patches::PatchShadowMap() const noexcept
191206
}
192207
#endif
193208

209+
#ifdef TR8
210+
// Iterates the player list from globalInfo and removes any player objects that don't actually exist
211+
// rather than using a hardcoded list this actually checks if it exists to account for mods
212+
void Patches::PatchPlayersList() const noexcept
213+
{
214+
auto globalInfo = *(GlobalInfo**)0xE7EE50;
215+
auto players = globalInfo->playerObjects;
216+
217+
auto fileSystem = GetFS();
218+
char fileName[256];
219+
220+
for (int i = 0; i < players->numPlayerObjects; )
221+
{
222+
auto name = OBTABLE_GetObjectName(players->playerObjectList[i]);
223+
224+
LOAD_UnitFileName(fileName, name, "drm");
225+
226+
// Check if the object exist
227+
if (!fileSystem->FileExists(fileName))
228+
{
229+
// If the file does not exist, remove it from the list
230+
for (int j = i; j < players->numPlayerObjects - 1; j++) players->playerObjectList[j] = players->playerObjectList[j + 1];
231+
232+
players->numPlayerObjects--;
233+
}
234+
else
235+
{
236+
i++;
237+
}
238+
}
239+
}
240+
#endif
241+
194242
void Patches::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
195243
{
196244
// Remove the quit message

src/modules/Patches.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,8 @@ class Patches : public Module
6464
bool IsNoMotionBlur() const noexcept { return m_noMotionBlur.GetValue(); }
6565
bool IsNoCinematicBars() const noexcept { return m_noCinematicBars.GetValue(); }
6666

67+
// Needs to be public since we call it from a hook
68+
void PatchPlayersList() const noexcept;
69+
6770
void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
6871
};

0 commit comments

Comments
 (0)