Skip to content

Commit

Permalink
Partially fix switch player outfit in TR8
Browse files Browse the repository at this point in the history
  • Loading branch information
TheIndra55 committed Nov 9, 2024
1 parent e12672a commit 8351489
Show file tree
Hide file tree
Showing 8 changed files with 136 additions and 2 deletions.
25 changes: 25 additions & 0 deletions src/game/Game.cpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#include "Game.h"

#include "util/Hooking.h"
#include "game/NtUnlockableCostume.h"

Instance* Game::GetPlayerInstance() noexcept
{
Expand All @@ -17,6 +18,23 @@ STracker* Game::GetStreamTracker() noexcept
return (STracker*)GET_ADDRESS(0x11582F8, 0x8AE378, 0xDBAB40);
}

void Game::SwitchPlayerCharacter() noexcept
{
#ifndef TR8
PLAYER_DebugSwitchPlayerCharacter()
#else
// Switch the player costume with costume index 12, this costume has an empty object id
// which allows us to fallback to the default behavior
NtUnlockableCostume costume;
costume.costumeIndex = 12;

NtUnlockableCostume_ScriptType::SwitchPlayerCostume(&costume);

// Post to birth the player weapons
INSTANCE_Post(Game::GetPlayerInstance(), 35, 1);
#endif
}

bool Game::IsInNextGenMode() noexcept
{
return *(bool*)GET_ADDRESS(0x10024E8, 0x7545B4, 0x000000);
Expand Down Expand Up @@ -57,6 +75,13 @@ int OBTABLE_GetObjectID(char* name)
return Hooking::CallReturn<int>(addr, name);
}

char* OBTABLE_GetObjectName(int id)
{
auto objectList = *(ObjectList**)GET_ADDRESS(0x000000, 0x000000, 0xDB94D0);

return objectList->object[id - 1].name;
}

void LOAD_ObjectFileName(char* name, char* object, char* extension)
{
return LOAD_UnitFileName(name, object, extension);
Expand Down
16 changes: 16 additions & 0 deletions src/game/Game.h
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,18 @@ struct GameTracker

float timeDilation;
};

struct PlayerObjects
{
__int16 numPlayerObjects;
__int16* playerObjectList;
};

struct GlobalInfo
{
int field_0;
PlayerObjects* playerObjects;
};
#endif

class Game
Expand All @@ -171,6 +183,8 @@ class Game
static GameTracker* GetGameTracker() noexcept;
static STracker* GetStreamTracker() noexcept;

static void SwitchPlayerCharacter() noexcept;

static bool IsInNextGenMode() noexcept;
};

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

void PLAYER_DebugSwitchPlayerCharacter();

int OBTABLE_GetObjectID(char* name);
char* OBTABLE_GetObjectName(int id);

void LOAD_ObjectFileName(char* name, char* object, char* extension);
void LOAD_UnitFileName(char* name, char* unit, char* extension);
7 changes: 7 additions & 0 deletions src/game/NtUnlockableCostume.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
#include "NtUnlockableCostume.h"
#include "util/Hooking.h"

void NtUnlockableCostume_ScriptType::SwitchPlayerCostume(NtUnlockableCostume* costume)
{
Hooking::Call(0x79DB50, costume);
}
12 changes: 12 additions & 0 deletions src/game/NtUnlockableCostume.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

struct NtUnlockableCostume
{
int costumeIndex;
};

class NtUnlockableCostume_ScriptType
{
public:
static void SwitchPlayerCostume(NtUnlockableCostume* costume);
};
15 changes: 15 additions & 0 deletions src/level/Stream.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,20 @@ struct ObjectTracker
__int16 objectStatus;
};

struct ObjectEntry
{
char* name;
int trackerIndex;
};

struct ObjectList
{
int numObjects;
#ifdef TR8
int size;
#endif
ObjectEntry object[1];
};

ObjectTracker* STREAM_GetObjectTrackerByName(char* name);
bool STREAM_PollLoadQueue();
12 changes: 10 additions & 2 deletions src/modules/MainMenu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ void MainMenu::OnDraw()
// TODO fill up ammo
IncrHealth(data->oldData.HealthInitial);
}
#endif

// Switch outfit
static char outfit[64] = "";
Expand All @@ -65,6 +66,7 @@ void MainMenu::OnDraw()
SwitchPlayerCharacter();
}

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

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

if (mainState == MS_DISPLAY_MAIN_MENU && !m_noWatermark.GetValue())
Expand All @@ -208,7 +210,7 @@ void MainMenu::OnLoop()
if (m_switchPlayerNextFrame)
{
m_switchPlayerNextFrame = false;
PLAYER_DebugSwitchPlayerCharacter();
Game::SwitchPlayerCharacter();
}
}

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

// Switch player outfit
if (msg == WM_KEYUP && wParam == VK_F9)
{
m_switchPlayerNextFrame = true;
}

// Ragdoll death
if (msg == WM_KEYUP && wParam == VK_F11)
{
Expand Down
48 changes: 48 additions & 0 deletions src/modules/Patches.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
#include "patches/Multicore.h"
#include "game/Camera.h"
#include "render/Draw.h"
#include "file/FileSystem.h"

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

#ifdef TR8
static void(*s_MAIN_DoMainInit)();

static unsigned char CPUCount(unsigned int* TotAvailLogical, unsigned int* TotAvailCore, unsigned int* PhysicalNum)
{
*TotAvailLogical = std::thread::hardware_concurrency();

return 0;
}

static void MAIN_DoMainInit()
{
s_MAIN_DoMainInit();

// Patch the player list once globalInfo has been loaded
s_patches->PatchPlayersList();
}
#endif

Patches::Patches()
{
s_patches = this;
Expand Down Expand Up @@ -144,6 +157,8 @@ Patches::Patches()
// Fix cdcMultiCore breaking on high number of CPU cores
MH_CreateHook((void*)0x4A21D0, CPUCount, nullptr);
MH_CreateHook((void*)0x4A2680, cdc::JobChainImplWithThreads::StartSystem, (void**)&cdc::JobChainImplWithThreads::s_StartSystem);

MH_CreateHook((void*)0x5DEF70, MAIN_DoMainInit, (void**)&s_MAIN_DoMainInit);
#endif

#ifdef TR7
Expand Down Expand Up @@ -191,6 +206,39 @@ void Patches::PatchShadowMap() const noexcept
}
#endif

#ifdef TR8
// Iterates the player list from globalInfo and removes any player objects that don't actually exist
// rather than using a hardcoded list this actually checks if it exists to account for mods
void Patches::PatchPlayersList() const noexcept
{
auto globalInfo = *(GlobalInfo**)0xE7EE50;
auto players = globalInfo->playerObjects;

auto fileSystem = GetFS();
char fileName[256];

for (int i = 0; i < players->numPlayerObjects; )
{
auto name = OBTABLE_GetObjectName(players->playerObjectList[i]);

LOAD_UnitFileName(fileName, name, "drm");

// Check if the object exist
if (!fileSystem->FileExists(fileName))
{
// If the file does not exist, remove it from the list
for (int j = i; j < players->numPlayerObjects - 1; j++) players->playerObjectList[j] = players->playerObjectList[j + 1];

players->numPlayerObjects--;
}
else
{
i++;
}
}
}
#endif

void Patches::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam)
{
// Remove the quit message
Expand Down
3 changes: 3 additions & 0 deletions src/modules/Patches.h
Original file line number Diff line number Diff line change
Expand Up @@ -64,5 +64,8 @@ class Patches : public Module
bool IsNoMotionBlur() const noexcept { return m_noMotionBlur.GetValue(); }
bool IsNoCinematicBars() const noexcept { return m_noCinematicBars.GetValue(); }

// Needs to be public since we call it from a hook
void PatchPlayersList() const noexcept;

void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);
};

0 comments on commit 8351489

Please sign in to comment.