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
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ This page lists all the individual contributions to the project by their author.
- Voxel light source position customization
- `UseFixedVoxelLighting`
- Warhead activation target health thresholds
- MP saves support for quicksave command and savegame trigger action
- Ported XNA CnCNet Client MP save handling
- **Uranusian (Thrifinesma)**:
- Mind Control enhancement
- Custom warhead splash list
Expand Down
3 changes: 3 additions & 0 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -197,8 +197,10 @@
<ClCompile Include="src\Phobos.CRT.cpp" />
<ClCompile Include="src\Phobos.Ext.cpp" />
<ClCompile Include="src\Phobos.INI.cpp" />
<ClCompile Include="src\Phobos.Save.cpp" />
<ClCompile Include="src\Phobos.cpp" />
<ClCompile Include="src\Utilities\ShapeTextPrinter.cpp" />
<ClCompile Include="src\Utilities\SpawnerHelper.cpp" />
<ClCompile Include="src\Utilities\Stream.cpp" />
<ClCompile Include="src\Utilities\Constructs.cpp" />
<ClCompile Include="src\Utilities\EnumFunctions.cpp" />
Expand Down Expand Up @@ -292,6 +294,7 @@
<ClInclude Include="src\Utilities\Savegame.h" />
<ClInclude Include="src\Utilities\SavegameDef.h" />
<ClInclude Include="src\Utilities\ShapeTextPrinter.h" />
<ClInclude Include="src\Utilities\SpawnerHelper.h" />
<ClInclude Include="src\Utilities\Stream.h" />
<ClInclude Include="src\Utilities\Swizzle.h" />
<ClInclude Include="src\Phobos.COM.h" />
Expand Down
2 changes: 1 addition & 1 deletion YRpp
Submodule YRpp updated 1 files
+3 −3 ScenarioClass.h
7 changes: 6 additions & 1 deletion docs/AI-Scripting-and-Mapping.md
Original file line number Diff line number Diff line change
Expand Up @@ -498,7 +498,12 @@ This category is empty for now.

### `500` Save Game

- Save the current game immediately (singleplayer game only).
- Save the current game immediately.

```{note}
For this action to work in multiplayer - you need to use a version of [YRpp spawner](https://github.com/CnCNet/yrpp-spawner) with multiplayer saves support.
```

- These vanilla CSF entries will be used: `TXT_SAVING_GAME`, `TXT_GAME_WAS_SAVED` and `TXT_ERROR_SAVING_GAME`.
- The save's description will look like `MapDescName - CSFText`.
- For example: `Allied Mission 25: Esther's Money - Money Stolen`.
Expand Down
13 changes: 13 additions & 0 deletions docs/Fixed-or-Improved-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -274,6 +274,19 @@ This page describes all ingame logics that are fixed or improved in Phobos witho
- Fixed an issue where some units crashed after the deployment transformation.
- Fixed the bug that AlphaImage remained after unit entered tunnel.
- Fixed an issue where Ares' `Convert.Deploy` triggers repeatedly when the unit is turning or moving.
- The game now automatically changes save file name from `SAVEGAME.NET` to `SVGM_XXX.NET` (where `XXX` is a number) when saving to prevent occasional overwriting of the save file when using Phobos with XNA CnCNet Client and saving too frequently.
- 1000 save files are supported, from `SVGM_000.NET` to `SVGM_999.NET`. When the limit is reached, the game will overwrite the latest save file.
- The previous `SVGM_XXX.NET` files are cleaned up before first copy if it's a new game, otherwise the highest numbered `SVGM_XXX.NET` file is found and the index is incremented, if possible.
- The game also automatically copies `spawn.ini` to the save folder as `spawnSG.ini` when saving a game.


```{note}
The described behavior is a replica of and is compliant with XNA CnCNet Client's multiplayer save game support.
```

```{note}
At the moment this is only useful if you use a version of [YRpp Spawner](https://github.com/CnCNet/yrpp-spawner) with multiplayer saves support (along with [XNA CnCNet Client](https://github.com/CnCNet/xna-cncnet-client)).
```

## Aircraft

Expand Down
7 changes: 6 additions & 1 deletion docs/User-Interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,12 @@ DisplayIncome.Offset=0,0 ; X,Y, pixels relative to default

### `[ ]` Quicksave

- Save the current singleplayer game.
- Saves the current game.

```{note}
For this command to work in multiplayer - you need to use a version of [YRpp spawner](https://github.com/CnCNet/yrpp-spawner) with multiplayer saves support.
```

- For localization, add `TXT_QUICKSAVE`, `TXT_QUICKSAVE_DESC`, `TXT_QUICKSAVE_SUFFIX` and `MSG:NotAvailableInMultiplayer` into your `.csf` file.
- These vanilla CSF entries will be used: `TXT_SAVING_GAME`, `TXT_GAME_WAS_SAVED` and `TXT_ERROR_SAVING_GAME`.
- The save should be looks like `Allied Mission 25: Esther's Money - QuickSaved`.
Expand Down
2 changes: 2 additions & 0 deletions docs/Whats-New.md
Original file line number Diff line number Diff line change
Expand Up @@ -834,6 +834,8 @@ Fixes / interactions with other extensions:
- Fixed an issue where some units crashed after the deployment transformation (by ststl & FlyStar)
- Fixed the bug that AlphaImage remained after unit entered tunnel (by NetsuNegi)
- Fixed an issue where Ares' `Convert.Deploy` triggers repeatedly when the unit is turning or moving (by CrimRecya)
- Fixed quicksave command and save game trigger action to work with YRpp spawner's multiplayer saves (by Kerbiter)
- Ported XNA CnCNet Client multiplayer save handling to get rid of occasional multiplayer save file overwriting when saving too fast (by Kerbiter)
```

### 0.3.0.1
Expand Down
19 changes: 9 additions & 10 deletions src/Commands/QuickSave.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
#include <ScenarioClass.h>
#include <HouseClass.h>
#include <SessionClass.h>
#include <EventClass.h>
#include <Utilities/GeneralUtils.h>
#include <Utilities/SpawnerHelper.h>

const char* QuickSaveCommandClass::GetName() const
{
Expand All @@ -22,7 +24,7 @@ const wchar_t* QuickSaveCommandClass::GetUICategory() const

const wchar_t* QuickSaveCommandClass::GetUIDescription() const
{
return GeneralUtils::LoadStringUnlessMissing("TXT_QUICKSAVE_DESC", L"Save the current game (Singleplayer only).");
return GeneralUtils::LoadStringUnlessMissing("TXT_QUICKSAVE_DESC", L"Save the current game.");
}

void QuickSaveCommandClass::Execute(WWKey eInput) const
Expand All @@ -39,15 +41,12 @@ void QuickSaveCommandClass::Execute(WWKey eInput) const

if (SessionClass::IsSingleplayer())
{
*reinterpret_cast<bool*>(0xABCE08) = false;
Phobos::ShouldQuickSave = true;

if (SessionClass::IsCampaign())
Phobos::CustomGameSaveDescription = ScenarioClass::Instance->UINameLoaded;
else
Phobos::CustomGameSaveDescription = ScenarioClass::Instance->Name;
Phobos::CustomGameSaveDescription += L" - ";
Phobos::CustomGameSaveDescription += GeneralUtils::LoadStringUnlessMissing("TXT_QUICKSAVE_SUFFIX", L"Quicksaved");
Phobos::ScheduleGameSave(GeneralUtils::LoadStringUnlessMissing("TXT_QUICKSAVE_SUFFIX", L"Quicksaved"));
}
else if (SpawnerHelper::IsSaveGameEventHooked())
{
// Relinquish handling of the save game to spawner
EventClass::OutList.Add(EventClass { HouseClass::CurrentPlayer->ArrayIndex, EventType::SaveGame });
}
else
{
Expand Down
15 changes: 3 additions & 12 deletions src/Ext/TAction/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include <New/Type/BannerTypeClass.h>

#include <Utilities/SavegameDef.h>
#include <Utilities/SpawnerHelper.h>
#include <Ext/House/Body.h>

//Static init
Expand Down Expand Up @@ -116,18 +117,8 @@ bool TActionExt::PlayAudioAtRandomWP(TActionClass* pThis, HouseClass* pHouse, Ob

bool TActionExt::SaveGame(TActionClass* pThis, HouseClass* pHouse, ObjectClass* pObject, TriggerClass* pTrigger, CellStruct const& location)
{
if (SessionClass::IsSingleplayer())
{
*reinterpret_cast<bool*>(0xABCE08) = false;
Phobos::ShouldQuickSave = true;

if (SessionClass::IsCampaign())
Phobos::CustomGameSaveDescription = ScenarioClass::Instance->UINameLoaded;
else
Phobos::CustomGameSaveDescription = ScenarioClass::Instance->Name;
Phobos::CustomGameSaveDescription += L" - ";
Phobos::CustomGameSaveDescription += StringTable::LoadString(pThis->Text);
}
if (SessionClass::IsSingleplayer() || SpawnerHelper::IsSaveGameEventHooked())
Phobos::ScheduleGameSave(StringTable::LoadString(pThis->Text));

return true;
}
Expand Down
53 changes: 0 additions & 53 deletions src/Phobos.INI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -277,56 +277,3 @@ DEFINE_HOOK(0x52D21F, InitRules_ThingsThatShouldntBeSerailized, 0x6)

return 0;
}


bool Phobos::ShouldQuickSave = false;
std::wstring Phobos::CustomGameSaveDescription {};

void Phobos::PassiveSaveGame()
{
auto PrintMessage = [](const wchar_t* pMessage)
{
MessageListClass::Instance.PrintMessage(
pMessage,
RulesClass::Instance->MessageDelay,
HouseClass::CurrentPlayer->ColorSchemeIndex,
true
);
};

PrintMessage(StringTable::LoadString(GameStrings::TXT_SAVING_GAME));
char fName[0x80];

SYSTEMTIME time;
GetLocalTime(&time);

_snprintf_s(fName, 0x7F, "Map.%04u%02u%02u-%02u%02u%02u-%05u.sav",
time.wYear, time.wMonth, time.wDay, time.wHour, time.wMinute, time.wSecond, time.wMilliseconds);

if (ScenarioClass::SaveGame(fName, Phobos::CustomGameSaveDescription.c_str()))
PrintMessage(StringTable::LoadString(GameStrings::TXT_GAME_WAS_SAVED));
else
PrintMessage(StringTable::LoadString(GameStrings::TXT_ERROR_SAVING_GAME));
}

DEFINE_HOOK(0x55DBCD, MainLoop_SaveGame, 0x6)
{
// This happens right before LogicClass::Update()
enum { SkipSave = 0x55DC99, InitialSave = 0x55DBE6 };

bool& scenario_saved = *reinterpret_cast<bool*>(0xABCE08);
if (SessionClass::IsSingleplayer() && !scenario_saved)
{
scenario_saved = true;
if (Phobos::ShouldQuickSave)
{
Phobos::PassiveSaveGame();
Phobos::ShouldQuickSave = false;
Phobos::CustomGameSaveDescription.clear();
}
else if (Phobos::Config::SaveGameOnScenarioStart && SessionClass::IsCampaign())
return InitialSave;
}

return SkipSave;
}
Loading
Loading