metaforce/Runtime/CGameState.cpp

289 lines
8.7 KiB
C++
Raw Permalink Normal View History

#include "Runtime/CGameState.hpp"
#include "Runtime/CMemoryCardSys.hpp"
#include "Runtime/CWorldSaveGameInfo.hpp"
#include "Runtime/CSimplePool.hpp"
#include "Runtime/GameGlobalObjects.hpp"
#include "Runtime/IOStreams.hpp"
#include "Runtime/MP1/MP1.hpp"
#include <zeus/Math.hpp>
2021-04-10 08:42:06 +00:00
namespace metaforce {
2018-12-08 05:30:43 +00:00
union BitsToDouble {
struct {
#if BYTE_ORDER == __LITTLE_ENDIAN
2018-12-08 05:30:43 +00:00
u32 high;
u32 low;
#else
2018-12-08 05:30:43 +00:00
u32 low;
u32 high;
#endif
2018-12-08 05:30:43 +00:00
};
double doub;
};
CScriptLayerManager::CScriptLayerManager(CBitStreamReader& reader, const CWorldSaveGameInfo& saveWorld) {
const u32 bitCount = reader.ReadEncoded(10);
2018-12-08 05:30:43 +00:00
x10_saveLayers.reserve(bitCount);
for (u32 i = 0; i < bitCount; ++i) {
const bool bit = reader.ReadEncoded(1) != 0;
if (bit) {
2018-12-08 05:30:43 +00:00
x10_saveLayers.setBit(i);
} else {
2018-12-08 05:30:43 +00:00
x10_saveLayers.unsetBit(i);
}
2018-12-08 05:30:43 +00:00
}
2016-10-09 07:45:04 +00:00
}
void CScriptLayerManager::PutTo(CBitStreamWriter& writer) const {
2018-12-08 05:30:43 +00:00
u32 totalLayerCount = 0;
for (size_t i = 0; i < x0_areaLayers.size(); ++i) {
totalLayerCount += GetAreaLayerCount(s32(i)) - 1;
}
2016-10-09 21:41:23 +00:00
2018-12-08 05:30:43 +00:00
writer.WriteEncoded(totalLayerCount, 10);
2016-10-09 21:41:23 +00:00
for (size_t i = 0; i < x0_areaLayers.size(); ++i) {
const u32 count = GetAreaLayerCount(s32(i));
for (u32 l = 1; l < count; ++l) {
writer.WriteEncoded(IsLayerActive(s32(i), s32(l)), 1);
}
2018-12-08 05:30:43 +00:00
}
2016-10-09 21:41:23 +00:00
}
void CScriptLayerManager::InitializeWorldLayers(const std::vector<CWorldLayers::Area>& layers) {
if (!x0_areaLayers.empty()) {
2018-12-08 05:30:43 +00:00
return;
}
2018-12-08 05:30:43 +00:00
x0_areaLayers = layers;
if (x10_saveLayers.getBitCount() == 0) {
2018-12-08 05:30:43 +00:00
return;
}
2018-12-08 05:30:43 +00:00
u32 a = 0;
u32 b = 0;
for (const CWorldLayers::Area& area : x0_areaLayers) {
for (u32 l = 1; l < area.m_layerCount; ++l)
SetLayerActive(a, l, x10_saveLayers.getBit(b++));
++a;
}
x10_saveLayers.clear();
2016-10-09 07:45:04 +00:00
}
2018-12-08 05:30:43 +00:00
CWorldState::CWorldState(CAssetId id) : x0_mlvlId(id), x4_areaId(0) {
x8_mailbox = std::make_shared<CScriptMailbox>();
2018-12-08 05:30:43 +00:00
xc_mapWorldInfo = std::make_shared<CMapWorldInfo>();
x10_desiredAreaAssetId = {};
x14_layerState = std::make_shared<CScriptLayerManager>();
}
2021-06-07 19:29:18 +00:00
CWorldState::CWorldState(CBitStreamReader& reader, CAssetId mlvlId, const CWorldSaveGameInfo& saveWorld)
: x0_mlvlId(mlvlId) {
2019-08-26 01:29:25 +00:00
x4_areaId = TAreaId(reader.ReadEncoded(32));
2018-12-08 05:30:43 +00:00
x10_desiredAreaAssetId = u32(reader.ReadEncoded(32));
x8_mailbox = std::make_shared<CScriptMailbox>(reader, saveWorld);
2018-12-08 05:30:43 +00:00
xc_mapWorldInfo = std::make_shared<CMapWorldInfo>(reader, saveWorld, mlvlId);
x14_layerState = std::make_shared<CScriptLayerManager>(reader, saveWorld);
2016-09-25 01:58:20 +00:00
}
void CWorldState::PutTo(CBitStreamWriter& writer, const CWorldSaveGameInfo& savw) const {
2018-12-08 05:30:43 +00:00
writer.WriteEncoded(x4_areaId, 32);
writer.WriteEncoded(u32(x10_desiredAreaAssetId.Value()), 32);
x8_mailbox->PutTo(writer, savw);
2018-12-08 05:30:43 +00:00
xc_mapWorldInfo->PutTo(writer, savw, x0_mlvlId);
x14_layerState->PutTo(writer);
2016-10-09 21:41:23 +00:00
}
2018-12-08 05:30:43 +00:00
CGameState::GameFileStateInfo CGameState::LoadGameFileState(const u8* data) {
CBitStreamReader stream(data, 4096);
GameFileStateInfo ret;
2016-12-20 21:51:50 +00:00
for (u32 i = 0; i < 128; i++) {
2018-12-08 05:30:43 +00:00
stream.ReadEncoded(8);
}
2018-12-08 05:30:43 +00:00
ret.x14_timestamp = stream.ReadEncoded(32);
2016-12-20 21:51:50 +00:00
ret.x20_hardMode = stream.ReadEncoded(1) != 0;
2018-12-08 05:30:43 +00:00
stream.ReadEncoded(1);
const CAssetId origMLVL = u32(stream.ReadEncoded(32));
2019-10-01 07:38:03 +00:00
ret.x8_mlvlId = origMLVL;
2016-12-20 21:51:50 +00:00
2018-12-08 05:30:43 +00:00
BitsToDouble conv;
conv.low = stream.ReadEncoded(32);
conv.high = stream.ReadEncoded(32);
ret.x0_playTime = conv.doub;
2016-12-20 21:51:50 +00:00
2018-12-08 05:30:43 +00:00
CPlayerState playerState(stream);
ret.x10_energyTanks = playerState.GetItemCapacity(CPlayerState::EItemType::EnergyTanks);
ret.xc_health = playerState.GetHealthInfo().GetHP();
2016-12-20 21:51:50 +00:00
2018-12-08 05:30:43 +00:00
u32 itemPercent;
if (origMLVL == 0x158EFE17)
itemPercent = 0;
else
itemPercent = playerState.CalculateItemCollectionRate() * 100 / playerState.GetPickupTotal();
2017-02-07 03:52:04 +00:00
2018-12-08 05:30:43 +00:00
ret.x18_itemPercent = itemPercent;
2016-12-20 21:51:50 +00:00
2018-12-08 05:30:43 +00:00
float scanPercent;
if (playerState.GetTotalLogScans() == 0)
scanPercent = 0.f;
else
scanPercent = 100.f * playerState.GetLogScans() / float(playerState.GetTotalLogScans());
ret.x1c_scanPercent = scanPercent;
2016-12-20 21:51:50 +00:00
2018-12-08 05:30:43 +00:00
return ret;
2016-12-20 21:51:50 +00:00
}
2018-12-08 05:30:43 +00:00
CGameState::CGameState() {
x98_playerState = std::make_shared<CPlayerState>();
x9c_transManager = std::make_shared<CWorldTransManager>();
if (g_MemoryCardSys != nullptr) {
2018-12-08 05:30:43 +00:00
InitializeMemoryStates();
}
}
2018-12-08 05:30:43 +00:00
CGameState::CGameState(CBitStreamReader& stream, u32 saveIdx) : x20c_saveFileIdx(saveIdx) {
x9c_transManager = std::make_shared<CWorldTransManager>();
x228_24_hardMode = false;
2018-12-08 05:30:43 +00:00
x228_25_initPowerupsAtFirstSpawn = true;
2015-08-20 02:52:07 +00:00
for (bool& value : x0_) {
value = stream.ReadEncoded(8) != 0;
}
2018-12-08 05:30:43 +00:00
stream.ReadEncoded(32);
2016-12-30 06:37:01 +00:00
x228_24_hardMode = stream.ReadEncoded(1) != 0;
x228_25_initPowerupsAtFirstSpawn = stream.ReadEncoded(1) != 0;
2019-10-01 07:38:03 +00:00
x84_mlvlId = u32(stream.ReadEncoded(32));
2018-12-08 05:30:43 +00:00
MP1::CMain::EnsureWorldPakReady(x84_mlvlId);
2016-10-09 21:41:23 +00:00
2018-12-08 05:30:43 +00:00
BitsToDouble conv;
conv.low = stream.ReadEncoded(32);
conv.high = stream.ReadEncoded(32);
xa0_playTime = conv.doub;
2016-12-23 06:41:39 +00:00
2018-12-08 05:30:43 +00:00
x98_playerState = std::make_shared<CPlayerState>(stream);
x17c_gameOptions = CGameOptions(stream);
x1f8_hintOptions = CHintOptions(stream);
2016-12-23 06:41:39 +00:00
2018-12-08 05:30:43 +00:00
const auto& memWorlds = g_MemoryCardSys->GetMemoryWorlds();
x88_worldStates.reserve(memWorlds.size());
for (const auto& memWorld : memWorlds) {
TLockedToken<CWorldSaveGameInfo> saveWorld =
2018-12-08 05:30:43 +00:00
g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), memWorld.second.GetSaveWorldAssetId()});
x88_worldStates.emplace_back(stream, memWorld.first, *saveWorld);
}
InitializeMemoryWorlds();
WriteBackupBuf();
2016-10-09 21:41:23 +00:00
}
2018-12-08 05:30:43 +00:00
void CGameState::ReadPersistentOptions(CBitStreamReader& r) { xa8_systemOptions = CPersistentOptions(r); }
void CGameState::ImportPersistentOptions(const CPersistentOptions& opts) {
if (opts.xd0_24_fusionLinked)
xa8_systemOptions.xd0_24_fusionLinked = true;
if (opts.xd0_27_fusionBeat)
xa8_systemOptions.xd0_27_fusionBeat = true;
if (&opts != &xa8_systemOptions)
xa8_systemOptions.x0_nesState = opts.x0_nesState;
2018-12-08 05:30:43 +00:00
xa8_systemOptions.SetLogScanPercent(opts.GetLogScanPercent());
xa8_systemOptions.SetAllItemsCollected(opts.GetAllItemsCollected());
xa8_systemOptions.SetPlayerBeatNormalMode(opts.GetPlayerBeatNormalMode());
xa8_systemOptions.SetPlayerBeatHardMode(opts.GetPlayerBeatHardMode());
2015-08-20 02:52:07 +00:00
}
2018-12-08 05:30:43 +00:00
void CGameState::ExportPersistentOptions(CPersistentOptions& opts) const {
if (xa8_systemOptions.xd0_24_fusionLinked)
opts.xd0_24_fusionLinked = true;
if (xa8_systemOptions.xd0_27_fusionBeat)
opts.xd0_27_fusionBeat = true;
if (&opts != &xa8_systemOptions)
opts.x0_nesState = xa8_systemOptions.x0_nesState;
2018-12-08 05:30:43 +00:00
opts.SetPlayerFusionSuitActive(xa8_systemOptions.GetPlayerFusionSuitActive());
2016-03-28 08:54:02 +00:00
}
2018-12-08 05:30:43 +00:00
void CGameState::WriteBackupBuf() {
x218_backupBuf.resize(940);
CBitStreamWriter w(x218_backupBuf.data(), 940);
PutTo(w);
}
void CGameState::PutTo(CBitStreamWriter& writer) {
for (const bool value : x0_) {
writer.WriteEncoded(u32(value), 8);
}
2018-12-08 05:30:43 +00:00
2022-01-30 20:07:24 +00:00
writer.WriteEncoded(CBasics::GetTime() / CBasics::TICKS_PER_SECOND, 32);
2018-12-08 05:30:43 +00:00
writer.WriteEncoded(x228_24_hardMode, 1);
writer.WriteEncoded(x228_25_initPowerupsAtFirstSpawn, 1);
writer.WriteEncoded(u32(x84_mlvlId.Value()), 32);
2018-12-08 05:30:43 +00:00
BitsToDouble conv;
conv.doub = xa0_playTime;
writer.WriteEncoded(conv.low, 32);
writer.WriteEncoded(conv.high, 32);
x98_playerState->PutTo(writer);
x17c_gameOptions.PutTo(writer);
x1f8_hintOptions.PutTo(writer);
const auto& memWorlds = g_MemoryCardSys->GetMemoryWorlds();
for (const auto& memWorld : memWorlds) {
TLockedToken<CWorldSaveGameInfo> saveWorld =
2018-12-08 05:30:43 +00:00
g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), memWorld.second.GetSaveWorldAssetId()});
const CWorldState& wld = StateForWorld(memWorld.first);
2018-12-08 05:30:43 +00:00
wld.PutTo(writer, *saveWorld);
}
}
2018-12-08 05:30:43 +00:00
void CGameState::SetCurrentWorldId(CAssetId id) {
StateForWorld(id);
x84_mlvlId = id;
MP1::CMain::EnsureWorldPakReady(x84_mlvlId);
}
void CGameState::SetTotalPlayTime(double time) { xa0_playTime = zeus::clamp(0.0, time, 359999.0); }
2018-12-08 05:30:43 +00:00
CWorldState& CGameState::StateForWorld(CAssetId mlvlId) {
auto it = x88_worldStates.begin();
for (; it != x88_worldStates.end(); ++it) {
if (it->GetWorldAssetId() == mlvlId)
break;
}
if (it == x88_worldStates.end()) {
x88_worldStates.emplace_back(mlvlId);
return x88_worldStates.back();
}
return *it;
2017-02-06 03:21:58 +00:00
}
2018-12-08 05:30:43 +00:00
float CGameState::GetHardModeDamageMultiplier() const { return g_tweakGame->GetHardModeDamageMultiplier(); }
float CGameState::GetHardModeWeaponMultiplier() const { return g_tweakGame->GetHardModeWeaponMultiplier(); }
void CGameState::InitializeMemoryWorlds() {
const auto& memoryWorlds = g_MemoryCardSys->GetMemoryWorlds();
for (const auto& wld : memoryWorlds) {
const auto& layerState = StateForWorld(wld.first).GetLayerState();
layerState->InitializeWorldLayers(wld.second.GetDefaultLayerStates());
}
2017-02-06 03:21:58 +00:00
}
2018-12-08 05:30:43 +00:00
void CGameState::InitializeMemoryStates() {
x98_playerState->InitializeScanTimes();
x1f8_hintOptions.InitializeMemoryState();
InitializeMemoryWorlds();
WriteBackupBuf();
}
2018-12-08 05:30:43 +00:00
2021-04-10 08:42:06 +00:00
} // namespace metaforce