Skip to content
Open
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
1 change: 1 addition & 0 deletions Spawner.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
<ClCompile Include="$(ThisDir)\src\Misc\DisableEdgeScrolling.cpp" />
<ClCompile Include="$(ThisDir)\src\Misc\InGameChat.cpp" />
<ClCompile Include="$(ThisDir)\src\Misc\NoCD.cpp" />
<ClCompile Include="$(ThisDir)\src\Misc\NoSaveLoadMode.cpp" />
<ClCompile Include="$(ThisDir)\src\Misc\Observers.cpp" />
<ClCompile Include="$(ThisDir)\src\Misc\Observers.Visibility.cpp" />
<ClCompile Include="$(ThisDir)\src\Misc\QuickExit.cpp" />
Expand Down
135 changes: 135 additions & 0 deletions src/Misc/NoSaveLoadMode.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#include <Utilities/Macro.h>
#include <Utilities/Debug.h>
#include <Spawner/Spawner.h>
#include <WWMessageBox.h>
#include <LoadProgressManager.h>

static bool IsDisableSaveLoadEnabled()
{
return Spawner::Enabled && Spawner::GetConfig()->DisableSaveLoad;
}

DEFINE_HOOK(0x6BD7CB, WinMain_InitNoSaveLoadMode, 0x5)
{
if (IsDisableSaveLoadEnabled())
{
Patch::Apply_LJMP(0x55DBCD, 0x55DC99); // Skip the initial save, including the Phobos autosave hook.
Patch::Apply_TYPED(0x83D560, { (DWORD)std::rand() }); // Invalidate the save game magic.
}

return 0;
}

DEFINE_HOOK(0x67CEF0, ScenarioClass_SaveGame_NoSaveLoadMode, 0x6)
{
if (IsDisableSaveLoadEnabled())
Debug::FatalErrorAndExit(Debug::ExitCode::SLFail, "Save attempted with DisableSaveLoad enabled. Terminating the game.\n");

return 0;
}

DEFINE_HOOK(0x686089, DoLose_NoSaveLoadModeRetryDialog, 0x7)
{
if (!IsDisableSaveLoadEnabled())
return 0;

enum { Restart = 0x6860F6, Leave = 0x6860EE };

const auto result = WWMessageBox::Instance.Process(
StringTable::TryFetchString("TXT_HARDCORE_FAILURE", L"GG"),
StringTable::LoadString("GUI:Restart"),
StringTable::LoadString("GUI:Leave"), nullptr);

if (result == WWMessageBox::Result::OK)
return Restart;

return Leave;
}

// disable load, save and delete buttons on the ingame menu
DEFINE_HOOK(0x4F17F6, GameOptionsDialog_DisableSaveLoadButtons, 0x6)
{
if (!IsDisableSaveLoadEnabled())
return 0;

GET(HWND, hDialog, EBP);

HWND hLoadButton = GetDlgItem(hDialog, 1310);
SendMessageA(hLoadButton, 1202, 0, (LPARAM)StringTable::TryFetchString("GUI:CantLoad", L"Cannot Load"));
EnableWindow(hLoadButton, FALSE);

HWND hSaveButton = GetDlgItem(hDialog, 1311);
SendMessageA(hSaveButton, 1202, 0, (LPARAM)StringTable::TryFetchString("GUI:CantSave", L"Cannot Save"));
EnableWindow(hSaveButton, FALSE);

HWND hDeleteButton = GetDlgItem(hDialog, 1312);
SendMessageA(hDeleteButton, 1202, 0, (LPARAM)L"----");
EnableWindow(hDeleteButton, FALSE);

return 0x4F1834;
}

std::wstring NoSaveLoadModeText { };
DEFINE_HOOK(0x553076, LoadProgressManager_Draw_NoSaveLoadModeIndicator, 0x5)
{
if (!IsDisableSaveLoadEnabled())
return 0;

GET(LoadProgressManager*, pLoadProgressManager, EBP);
if (NoSaveLoadModeText.empty())
NoSaveLoadModeText = StringTable::TryFetchString("TXT_HARDCORE_MODE", L"HardCore");

Point2D position
{
pLoadProgressManager->TitleBarRect.X + pLoadProgressManager->TitleBarRect.Width - 100,
pLoadProgressManager->TitleBarRect.Y + 10
};

LEA_STACK(RectangleStruct*, pBounds, STACK_OFFSET(0x1268, -0x1204));
if (auto pLogoSHP = FileSystem::LoadSHPFile("hardcorelogo.shp"))
pLoadProgressManager->ProgressSurface->DrawSHP(FileSystem::PALETTE_PAL, pLogoSHP, 0, &position, pBounds, BlitterFlags::bf_400, 0, 0, ZGradient::Ground, 1000, 0, nullptr, 0, 0, 0);
else
pLoadProgressManager->ProgressSurface->DrawText(NoSaveLoadModeText.c_str(), &position, COLOR_RED);

return 0;
}

DEFINE_HOOK(0x6D0E20, TabClass_Draw_NoSaveLoadModeText, 0x6)
{
if (NoSaveLoadModeText.empty())
return 0;

auto surfaceRect = DSurface::Temp->GetRect();
auto textDimensions = Drawing::GetTextDimensions(NoSaveLoadModeText.c_str(), { 0,0 }, 0);
Point2D position {
surfaceRect.Width - textDimensions.Width - 40,
surfaceRect.Height - 24
};
DSurface::Temp->DrawText(NoSaveLoadModeText.c_str(), &position, COLOR_WHITE);

return 0;
}

DEFINE_HOOK(0x68A1BD, ScenarioClass_ReadMapINI_SetEndOfGame, 0x6)
{
if (!Spawner::Enabled || (!Spawner::GetConfig()->DisableSaveLoad && Spawner::GetConfig()->CustomMissionID == 0))
return 0;

GET(ScenarioClass*, pScenario, ESI);
pScenario->EndOfGame = true;
pScenario->SkipScore = false;

return 0x68A1DB;
}

DEFINE_HOOK(0x6C9357, MissionEndDialog_SetExitGameText, 0x5)
{
if (ScenarioClass::Instance->EndOfGame)
{
GET(HWND, hDialog, EBX);
auto hContinueButton = GetDlgItem(hDialog, 1059);
SendMessageA(hContinueButton, 1202, 0, (LPARAM)StringTable::LoadString("GUI:ExitGame"));
}

return 0;
}
1 change: 1 addition & 0 deletions src/Spawner/Spawner.Config.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,6 +119,7 @@ void SpawnerConfig::LoadFromINIFile(CCINIClass* pINI)
DefeatedBecomesObserver = pINI->ReadBool(pSettingsSection, "DefeatedBecomesObserver", DefeatedBecomesObserver);
Observer_ShowAIOnSidebar = pINI->ReadBool(pSettingsSection, "Observer.ShowAIOnSidebar", Observer_ShowAIOnSidebar);
DisableChat = pINI->ReadBool(pSettingsSection, "DisableChat", DisableChat);
DisableSaveLoad = pINI->ReadBool(pSettingsSection, "DisableSaveLoad", false) && IsCampaign && !LoadSaveGame;
}
}

Expand Down
2 changes: 2 additions & 0 deletions src/Spawner/Spawner.Config.h
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ class SpawnerConfig
bool DefeatedBecomesObserver;
bool Observer_ShowAIOnSidebar;
bool DisableChat;
bool DisableSaveLoad;

SpawnerConfig() // default values
// Game Mode Options
Expand Down Expand Up @@ -244,6 +245,7 @@ class SpawnerConfig
, DefeatedBecomesObserver { false }
, Observer_ShowAIOnSidebar { false }
, DisableChat { false }
, DisableSaveLoad { false }
{ }

void LoadFromINIFile(CCINIClass* pINI);
Expand Down
1 change: 1 addition & 0 deletions src/Spawner/Spawner.Hook.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ DEFINE_HOOK(0x6BD7C5, WinMain_SpawnerInit, 0x6)

// Skip load *.PKT, *.YRO and *.YRM map files
Patch::Apply_LJMP(0x699AD9, 0x69A1B2); // SessionClass::Read_Scenario_Descriptions

}

return 0;
Expand Down
3 changes: 2 additions & 1 deletion src/Spawner/Spawner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -608,7 +608,8 @@ void Spawner::After_Main_Loop()
&& SessionClass::Instance.GameMode == GameMode::LAN
&& pConfig->AutoSaveInterval > 0;

const bool isAutoSaving = (doSaveSP || doSaveMP)
const bool isAutoSaving = !pConfig->DisableSaveLoad
&& (doSaveSP || doSaveMP)
&& Unsorted::CurrentFrame == Spawner::NextAutoSaveFrame;

// Schedule to make a save if it's time to autosave.
Expand Down