mirror of
https://github.com/AxioDL/metaforce.git
synced 2025-05-13 18:31:21 +00:00
Makes the data a little more strongly typed; preventing implicit array->pointer decay. It also allows simplifying assignments within the CGameState code. While we're at it, we can also eliminate several instances of magic numbers related to the array sizes throughout the code.
274 lines
8.6 KiB
C++
274 lines
8.6 KiB
C++
#include "CGameState.hpp"
|
|
#include "IOStreams.hpp"
|
|
#include "zeus/Math.hpp"
|
|
#include "GameGlobalObjects.hpp"
|
|
#include "CMemoryCardSys.hpp"
|
|
#include "CSimplePool.hpp"
|
|
#include "CSaveWorld.hpp"
|
|
#include "MP1/MP1.hpp"
|
|
|
|
namespace urde {
|
|
union BitsToDouble {
|
|
struct {
|
|
#if BYTE_ORDER == __LITTLE_ENDIAN
|
|
u32 high;
|
|
u32 low;
|
|
#else
|
|
u32 low;
|
|
u32 high;
|
|
#endif
|
|
};
|
|
double doub;
|
|
};
|
|
|
|
CWorldLayerState::CWorldLayerState(CBitStreamReader& reader, const CSaveWorld& saveWorld) {
|
|
u32 bitCount = reader.ReadEncoded(10);
|
|
x10_saveLayers.reserve(bitCount);
|
|
|
|
for (u32 i = 0; i < bitCount; ++i) {
|
|
bool bit = reader.ReadEncoded(1);
|
|
if (bit)
|
|
x10_saveLayers.setBit(i);
|
|
else
|
|
x10_saveLayers.unsetBit(i);
|
|
}
|
|
}
|
|
|
|
void CWorldLayerState::PutTo(CBitStreamWriter& writer) const {
|
|
u32 totalLayerCount = 0;
|
|
for (int i = 0; i < x0_areaLayers.size(); ++i)
|
|
totalLayerCount += GetAreaLayerCount(i) - 1;
|
|
|
|
writer.WriteEncoded(totalLayerCount, 10);
|
|
|
|
for (int i = 0; i < x0_areaLayers.size(); ++i) {
|
|
u32 count = GetAreaLayerCount(i);
|
|
for (u32 l = 1; l < count; ++l)
|
|
writer.WriteEncoded(IsLayerActive(i, l), 1);
|
|
}
|
|
}
|
|
|
|
void CWorldLayerState::InitializeWorldLayers(const std::vector<CWorldLayers::Area>& layers) {
|
|
if (x0_areaLayers.size())
|
|
return;
|
|
x0_areaLayers = layers;
|
|
if (x10_saveLayers.getBitCount() == 0)
|
|
return;
|
|
|
|
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();
|
|
}
|
|
|
|
CWorldState::CWorldState(CAssetId id) : x0_mlvlId(id), x4_areaId(0) {
|
|
x8_relayTracker = std::make_shared<CRelayTracker>();
|
|
xc_mapWorldInfo = std::make_shared<CMapWorldInfo>();
|
|
x10_desiredAreaAssetId = {};
|
|
x14_layerState = std::make_shared<CWorldLayerState>();
|
|
}
|
|
|
|
CWorldState::CWorldState(CBitStreamReader& reader, CAssetId mlvlId, const CSaveWorld& saveWorld) : x0_mlvlId(mlvlId) {
|
|
x4_areaId = reader.ReadEncoded(32);
|
|
x10_desiredAreaAssetId = u32(reader.ReadEncoded(32));
|
|
x8_relayTracker = std::make_shared<CRelayTracker>(reader, saveWorld);
|
|
xc_mapWorldInfo = std::make_shared<CMapWorldInfo>(reader, saveWorld, mlvlId);
|
|
x14_layerState = std::make_shared<CWorldLayerState>(reader, saveWorld);
|
|
}
|
|
|
|
void CWorldState::PutTo(CBitStreamWriter& writer, const CSaveWorld& savw) const {
|
|
writer.WriteEncoded(x4_areaId, 32);
|
|
writer.WriteEncoded(x10_desiredAreaAssetId.Value(), 32);
|
|
x8_relayTracker->PutTo(writer, savw);
|
|
xc_mapWorldInfo->PutTo(writer, savw, x0_mlvlId);
|
|
x14_layerState->PutTo(writer);
|
|
}
|
|
|
|
CGameState::GameFileStateInfo CGameState::LoadGameFileState(const u8* data) {
|
|
CBitStreamReader stream(data, 4096);
|
|
GameFileStateInfo ret;
|
|
|
|
for (u32 i = 0; i < 128; i++)
|
|
stream.ReadEncoded(8);
|
|
ret.x14_timestamp = stream.ReadEncoded(32);
|
|
|
|
ret.x20_hardMode = stream.ReadEncoded(1);
|
|
stream.ReadEncoded(1);
|
|
CAssetId origMLVL = u32(stream.ReadEncoded(32));
|
|
ret.x8_mlvlId = g_ResFactory->TranslateOriginalToNew(origMLVL);
|
|
|
|
BitsToDouble conv;
|
|
conv.low = stream.ReadEncoded(32);
|
|
conv.high = stream.ReadEncoded(32);
|
|
ret.x0_playTime = conv.doub;
|
|
|
|
CPlayerState playerState(stream);
|
|
ret.x10_energyTanks = playerState.GetItemCapacity(CPlayerState::EItemType::EnergyTanks);
|
|
|
|
u32 itemPercent;
|
|
if (origMLVL == 0x158EFE17)
|
|
itemPercent = 0;
|
|
else
|
|
itemPercent = playerState.CalculateItemCollectionRate() * 100 / playerState.GetPickupTotal();
|
|
|
|
ret.x18_itemPercent = itemPercent;
|
|
|
|
float scanPercent;
|
|
if (playerState.GetTotalLogScans() == 0)
|
|
scanPercent = 0.f;
|
|
else
|
|
scanPercent = 100.f * playerState.GetLogScans() / float(playerState.GetTotalLogScans());
|
|
ret.x1c_scanPercent = scanPercent;
|
|
|
|
return ret;
|
|
}
|
|
|
|
CGameState::CGameState() {
|
|
x98_playerState = std::make_shared<CPlayerState>();
|
|
x9c_transManager = std::make_shared<CWorldTransManager>();
|
|
x228_25_initPowerupsAtFirstSpawn = true;
|
|
if (g_MemoryCardSys)
|
|
InitializeMemoryStates();
|
|
}
|
|
|
|
CGameState::CGameState(CBitStreamReader& stream, u32 saveIdx) : x20c_saveFileIdx(saveIdx) {
|
|
x9c_transManager = std::make_shared<CWorldTransManager>();
|
|
x228_25_initPowerupsAtFirstSpawn = true;
|
|
|
|
for (u32 i = 0; i < 128; i++)
|
|
x0_[i] = stream.ReadEncoded(8);
|
|
stream.ReadEncoded(32);
|
|
|
|
x228_24_hardMode = stream.ReadEncoded(1);
|
|
x228_25_initPowerupsAtFirstSpawn = stream.ReadEncoded(1);
|
|
x84_mlvlId = g_ResFactory->TranslateOriginalToNew(u32(stream.ReadEncoded(32)));
|
|
MP1::CMain::EnsureWorldPakReady(x84_mlvlId);
|
|
|
|
BitsToDouble conv;
|
|
conv.low = stream.ReadEncoded(32);
|
|
conv.high = stream.ReadEncoded(32);
|
|
xa0_playTime = conv.doub;
|
|
|
|
x98_playerState = std::make_shared<CPlayerState>(stream);
|
|
|
|
x17c_gameOptions = CGameOptions(stream);
|
|
x1f8_hintOptions = CHintOptions(stream);
|
|
|
|
const auto& memWorlds = g_MemoryCardSys->GetMemoryWorlds();
|
|
x88_worldStates.reserve(memWorlds.size());
|
|
for (const auto& memWorld : memWorlds) {
|
|
TLockedToken<CSaveWorld> saveWorld =
|
|
g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), memWorld.second.GetSaveWorldAssetId()});
|
|
x88_worldStates.emplace_back(stream, memWorld.first, *saveWorld);
|
|
}
|
|
|
|
InitializeMemoryWorlds();
|
|
WriteBackupBuf();
|
|
}
|
|
|
|
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;
|
|
xa8_systemOptions.SetLogScanPercent(opts.GetLogScanPercent());
|
|
xa8_systemOptions.SetAllItemsCollected(opts.GetAllItemsCollected());
|
|
xa8_systemOptions.SetPlayerBeatNormalMode(opts.GetPlayerBeatNormalMode());
|
|
xa8_systemOptions.SetPlayerBeatHardMode(opts.GetPlayerBeatHardMode());
|
|
}
|
|
|
|
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;
|
|
opts.SetPlayerFusionSuitActive(xa8_systemOptions.GetPlayerFusionSuitActive());
|
|
}
|
|
|
|
void CGameState::WriteBackupBuf() {
|
|
x218_backupBuf.resize(940);
|
|
CBitStreamWriter w(x218_backupBuf.data(), 940);
|
|
PutTo(w);
|
|
}
|
|
|
|
void CGameState::PutTo(CBitStreamWriter& writer) const {
|
|
for (u32 i = 0; i < 128; i++)
|
|
writer.WriteEncoded(x0_[i], 8);
|
|
|
|
writer.WriteEncoded(CBasics::ToWiiTime(std::chrono::system_clock::now()) / CBasics::TICKS_PER_SECOND, 32);
|
|
writer.WriteEncoded(x228_24_hardMode, 1);
|
|
writer.WriteEncoded(x228_25_initPowerupsAtFirstSpawn, 1);
|
|
writer.WriteEncoded(g_ResFactory->TranslateNewToOriginal(x84_mlvlId).Value(), 32);
|
|
|
|
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<CSaveWorld> saveWorld =
|
|
g_SimplePool->GetObj(SObjectTag{FOURCC('SAVW'), memWorld.second.GetSaveWorldAssetId()});
|
|
const CWorldState& wld = const_cast<CGameState&>(*this).StateForWorld(memWorld.first);
|
|
wld.PutTo(writer, *saveWorld);
|
|
}
|
|
}
|
|
|
|
void CGameState::SetCurrentWorldId(CAssetId id) {
|
|
StateForWorld(id);
|
|
x84_mlvlId = id;
|
|
MP1::CMain::EnsureWorldPakReady(x84_mlvlId);
|
|
}
|
|
|
|
void CGameState::SetTotalPlayTime(float time) { xa0_playTime = zeus::clamp<double>(0.0, time, 359999.0); }
|
|
|
|
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;
|
|
}
|
|
|
|
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());
|
|
}
|
|
}
|
|
|
|
void CGameState::InitializeMemoryStates() {
|
|
x98_playerState->InitializeScanTimes();
|
|
x1f8_hintOptions.InitializeMemoryState();
|
|
InitializeMemoryWorlds();
|
|
WriteBackupBuf();
|
|
}
|
|
|
|
} // namespace urde
|