2019-12-22 20:04:07 +00:00
|
|
|
#include "Runtime/CGameState.hpp"
|
|
|
|
|
|
|
|
#include "Runtime/CMemoryCardSys.hpp"
|
2021-06-05 05:27:52 +00:00
|
|
|
#include "Runtime/CWorldSaveGameInfo.hpp"
|
2019-12-22 20:04:07 +00:00
|
|
|
#include "Runtime/CSimplePool.hpp"
|
|
|
|
#include "Runtime/GameGlobalObjects.hpp"
|
2022-02-19 13:04:45 +00:00
|
|
|
#include "Runtime/Streams/IOStreams.hpp"
|
2019-12-22 20:04:07 +00:00
|
|
|
#include "Runtime/MP1/MP1.hpp"
|
|
|
|
|
|
|
|
#include <zeus/Math.hpp>
|
2015-08-17 22:05:00 +00:00
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
namespace metaforce {
|
2018-12-08 05:30:43 +00:00
|
|
|
union BitsToDouble {
|
|
|
|
struct {
|
2017-01-24 18:04:57 +00:00
|
|
|
#if BYTE_ORDER == __LITTLE_ENDIAN
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 high;
|
|
|
|
u32 low;
|
2017-01-24 18:04:57 +00:00
|
|
|
#else
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 low;
|
|
|
|
u32 high;
|
2017-01-24 18:04:57 +00:00
|
|
|
#endif
|
2018-12-08 05:30:43 +00:00
|
|
|
};
|
|
|
|
double doub;
|
2017-01-24 18:04:57 +00:00
|
|
|
};
|
2015-08-17 22:05:00 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
CScriptLayerManager::CScriptLayerManager(CInputStream& reader, const CWorldSaveGameInfo& saveWorld) {
|
|
|
|
const u32 bitCount = reader.ReadBits(10);
|
2022-02-22 06:59:47 +00:00
|
|
|
x10_saveLayers.Reserve(bitCount);
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
for (u32 i = 0; i < bitCount; ++i) {
|
2022-02-18 07:37:54 +00:00
|
|
|
const bool bit = reader.ReadBits(1) != 0;
|
2020-04-16 23:55:59 +00:00
|
|
|
if (bit) {
|
2022-02-22 06:59:47 +00:00
|
|
|
x10_saveLayers.SetBit(i);
|
2020-04-16 23:55:59 +00:00
|
|
|
} else {
|
2022-02-22 06:59:47 +00:00
|
|
|
x10_saveLayers.UnsetBit(i);
|
2020-04-16 23:55:59 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-10-09 07:45:04 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
void CScriptLayerManager::PutTo(COutputStream& writer) const {
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 totalLayerCount = 0;
|
2020-03-30 01:03:22 +00:00
|
|
|
for (size_t i = 0; i < x0_areaLayers.size(); ++i) {
|
|
|
|
totalLayerCount += GetAreaLayerCount(s32(i)) - 1;
|
|
|
|
}
|
2016-10-09 21:41:23 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
writer.WriteBits(totalLayerCount, 10);
|
2016-10-09 21:41:23 +00:00
|
|
|
|
2020-03-30 01:03:22 +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) {
|
2022-02-22 06:59:47 +00:00
|
|
|
writer.WriteBits(static_cast<u32>(IsLayerActive(s32(i), s32(l))), 1);
|
2020-03-30 01:03:22 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
}
|
2016-10-09 21:41:23 +00:00
|
|
|
}
|
|
|
|
|
2021-06-04 06:08:05 +00:00
|
|
|
void CScriptLayerManager::InitializeWorldLayers(const std::vector<CWorldLayers::Area>& layers) {
|
2020-04-16 23:55:59 +00:00
|
|
|
if (!x0_areaLayers.empty()) {
|
2018-12-08 05:30:43 +00:00
|
|
|
return;
|
2020-04-16 23:55:59 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
x0_areaLayers = layers;
|
2022-02-22 06:59:47 +00:00
|
|
|
if (x10_saveLayers.GetBitCount() == 0) {
|
2018-12-08 05:30:43 +00:00
|
|
|
return;
|
2020-04-16 23:55:59 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
u32 a = 0;
|
|
|
|
u32 b = 0;
|
|
|
|
for (const CWorldLayers::Area& area : x0_areaLayers) {
|
2022-02-22 06:59:47 +00:00
|
|
|
for (u32 l = 1; l < area.m_layerCount; ++l) {
|
|
|
|
SetLayerActive(a, l, x10_saveLayers.GetBit(b++));
|
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
++a;
|
|
|
|
}
|
|
|
|
|
2022-02-22 06:59:47 +00:00
|
|
|
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) {
|
2021-06-04 06:08:05 +00:00
|
|
|
x8_mailbox = std::make_shared<CScriptMailbox>();
|
2018-12-08 05:30:43 +00:00
|
|
|
xc_mapWorldInfo = std::make_shared<CMapWorldInfo>();
|
|
|
|
x10_desiredAreaAssetId = {};
|
2021-06-04 06:08:05 +00:00
|
|
|
x14_layerState = std::make_shared<CScriptLayerManager>();
|
2016-10-08 20:32:36 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
CWorldState::CWorldState(CInputStream& reader, CAssetId mlvlId, const CWorldSaveGameInfo& saveWorld)
|
2022-02-22 06:59:47 +00:00
|
|
|
: x0_mlvlId(mlvlId), x4_areaId(TAreaId(reader.ReadBits(32))), x10_desiredAreaAssetId(reader.ReadBits(32)) {
|
2021-06-04 06:08:05 +00:00
|
|
|
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);
|
2021-06-04 06:08:05 +00:00
|
|
|
x14_layerState = std::make_shared<CScriptLayerManager>(reader, saveWorld);
|
2016-09-25 01:58:20 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
void CWorldState::PutTo(COutputStream& writer, const CWorldSaveGameInfo& savw) const {
|
|
|
|
writer.WriteBits(x4_areaId, 32);
|
|
|
|
writer.WriteBits(u32(x10_desiredAreaAssetId.Value()), 32);
|
2021-06-04 06:08:05 +00:00
|
|
|
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) {
|
2022-02-18 07:37:54 +00:00
|
|
|
CMemoryInStream stream(data, 4096, CMemoryInStream::EOwnerShip::NotOwned);
|
2018-12-08 05:30:43 +00:00
|
|
|
GameFileStateInfo ret;
|
2016-12-20 21:51:50 +00:00
|
|
|
|
2020-03-30 01:03:22 +00:00
|
|
|
for (u32 i = 0; i < 128; i++) {
|
2022-02-18 07:37:54 +00:00
|
|
|
stream.ReadBits(8);
|
2020-03-30 01:03:22 +00:00
|
|
|
}
|
2022-02-18 07:37:54 +00:00
|
|
|
ret.x14_timestamp = stream.ReadBits(32);
|
2016-12-20 21:51:50 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
ret.x20_hardMode = stream.ReadBits(1) != 0;
|
|
|
|
stream.ReadBits(1);
|
|
|
|
const CAssetId origMLVL = u32(stream.ReadBits(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;
|
2022-02-18 07:37:54 +00:00
|
|
|
conv.low = stream.ReadBits(32);
|
|
|
|
conv.high = stream.ReadBits(32);
|
2018-12-08 05:30:43 +00:00
|
|
|
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);
|
2020-03-24 10:12:59 +00:00
|
|
|
ret.xc_health = playerState.GetHealthInfo().GetHP();
|
2016-12-20 21:51:50 +00:00
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
u32 itemPercent;
|
2022-02-19 13:04:45 +00:00
|
|
|
if (origMLVL == 0x158EFE17u)
|
2018-12-08 05:30:43 +00:00
|
|
|
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>();
|
2020-04-16 23:55:59 +00:00
|
|
|
|
|
|
|
if (g_MemoryCardSys != nullptr) {
|
2018-12-08 05:30:43 +00:00
|
|
|
InitializeMemoryStates();
|
2020-04-16 23:55:59 +00:00
|
|
|
}
|
2016-08-14 21:11:44 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
CGameState::CGameState(CInputStream& stream, u32 saveIdx) : x20c_saveFileIdx(saveIdx) {
|
2018-12-08 05:30:43 +00:00
|
|
|
x9c_transManager = std::make_shared<CWorldTransManager>();
|
2020-04-10 19:02:36 +00:00
|
|
|
x228_24_hardMode = false;
|
2018-12-08 05:30:43 +00:00
|
|
|
x228_25_initPowerupsAtFirstSpawn = true;
|
2015-08-20 02:52:07 +00:00
|
|
|
|
2020-03-30 00:54:29 +00:00
|
|
|
for (bool& value : x0_) {
|
2022-02-18 07:37:54 +00:00
|
|
|
value = stream.ReadBits(8) != 0;
|
2020-03-30 00:54:29 +00:00
|
|
|
}
|
2022-02-18 07:37:54 +00:00
|
|
|
stream.ReadBits(32);
|
2016-12-30 06:37:01 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
x228_24_hardMode = stream.ReadBits(1) != 0;
|
|
|
|
x228_25_initPowerupsAtFirstSpawn = stream.ReadBits(1) != 0;
|
|
|
|
x84_mlvlId = u32(stream.ReadBits(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;
|
2022-02-18 07:37:54 +00:00
|
|
|
conv.low = stream.ReadBits(32);
|
|
|
|
conv.high = stream.ReadBits(32);
|
2018-12-08 05:30:43 +00:00
|
|
|
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) {
|
2021-06-05 05:27:52 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
void CGameState::ReadPersistentOptions(CInputStream& r) { xa8_systemOptions = r.Get<CPersistentOptions>(); }
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
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)
|
2019-08-14 06:49:40 +00:00
|
|
|
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)
|
2019-08-14 06:49:40 +00:00
|
|
|
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);
|
2022-02-18 07:37:54 +00:00
|
|
|
CMemoryStreamOut w(x218_backupBuf.data(), 940);
|
2018-12-08 05:30:43 +00:00
|
|
|
PutTo(w);
|
2016-08-14 03:00:58 +00:00
|
|
|
}
|
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
void CGameState::PutTo(COutputStream& writer) {
|
2020-03-30 00:54:29 +00:00
|
|
|
for (const bool value : x0_) {
|
2022-02-18 07:37:54 +00:00
|
|
|
writer.WriteBits(u32(value), 8);
|
2020-03-30 00:54:29 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
|
2022-02-18 07:37:54 +00:00
|
|
|
writer.WriteBits(CBasics::GetTime() / CBasics::TICKS_PER_SECOND, 32);
|
|
|
|
writer.WriteBits(x228_24_hardMode, 1);
|
|
|
|
writer.WriteBits(x228_25_initPowerupsAtFirstSpawn, 1);
|
|
|
|
writer.WriteBits(u32(x84_mlvlId.Value()), 32);
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
BitsToDouble conv;
|
|
|
|
conv.doub = xa0_playTime;
|
2022-02-18 07:37:54 +00:00
|
|
|
writer.WriteBits(conv.low, 32);
|
|
|
|
writer.WriteBits(conv.high, 32);
|
2018-12-08 05:30:43 +00:00
|
|
|
|
|
|
|
x98_playerState->PutTo(writer);
|
|
|
|
x17c_gameOptions.PutTo(writer);
|
|
|
|
x1f8_hintOptions.PutTo(writer);
|
|
|
|
|
|
|
|
const auto& memWorlds = g_MemoryCardSys->GetMemoryWorlds();
|
|
|
|
for (const auto& memWorld : memWorlds) {
|
2021-06-05 05:27:52 +00:00
|
|
|
TLockedToken<CWorldSaveGameInfo> saveWorld =
|
2018-12-08 05:30:43 +00:00
|
|
|
g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), memWorld.second.GetSaveWorldAssetId()});
|
2020-03-30 00:52:30 +00:00
|
|
|
const CWorldState& wld = StateForWorld(memWorld.first);
|
2018-12-08 05:30:43 +00:00
|
|
|
wld.PutTo(writer, *saveWorld);
|
|
|
|
}
|
2016-12-24 06:08:48 +00:00
|
|
|
}
|
|
|
|
|
2018-12-08 05:30:43 +00:00
|
|
|
void CGameState::SetCurrentWorldId(CAssetId id) {
|
|
|
|
StateForWorld(id);
|
|
|
|
x84_mlvlId = id;
|
|
|
|
MP1::CMain::EnsureWorldPakReady(x84_mlvlId);
|
2016-12-24 06:08:48 +00:00
|
|
|
}
|
|
|
|
|
2019-12-11 04:51:33 +00:00
|
|
|
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();
|
2015-08-17 22:05:00 +00:00
|
|
|
}
|
2018-12-08 05:30:43 +00:00
|
|
|
|
2021-04-10 08:42:06 +00:00
|
|
|
} // namespace metaforce
|