@@ -95,16 +95,64 @@ namespace {
9595};
9696
9797#if DEBUG_MSG
98+ static size_t s_debugContextLevel = 0 ;
99+ static constexpr char DEBUG_INDENT[] = " " ;
100+ static std::string s_debugIndent = " " ;
101+
98102 static void LogDebug (const char * format, ...)
99103 {
100104 va_list args;
101105 va_start (args, format);
102106 std::string message = REPENTOGON::StringFormat (format, args);
103107 va_end (args);
104108
109+ message = LOG_DEBUG_HEADER + s_debugIndent + message;
105110 KAGE::_LogMessage (0 , message.c_str ());
106111 }
112+
113+ static void BeginDebugContext (const char * message)
114+ {
115+ LogDebug (" Start %s\n " , message);
116+ s_debugContextLevel++;
117+ s_debugIndent += DEBUG_INDENT;
118+ }
119+
120+ static void EndDebugContext (const char * message)
121+ {
122+ s_debugContextLevel--;
123+ s_debugIndent.resize (s_debugIndent.size () - (sizeof (DEBUG_INDENT) - 1 ));
124+ LogDebug (" End %s\n " , message);
125+ }
126+
127+ namespace {
128+ struct ScopedDebugContext
129+ {
130+ private:
131+ const char * m_message;
132+
133+ public:
134+ ScopedDebugContext (const char * message)
135+ : m_message(message)
136+ {
137+ BeginDebugContext (message);
138+ }
139+
140+ ~ScopedDebugContext ()
141+ {
142+ EndDebugContext (m_message);
143+ }
144+ };
145+ }
146+
107147#else
148+ namespace
149+ {
150+ struct ScopedDebugContext
151+ {
152+ ScopedDebugContext (const char * message) {}
153+ };
154+ }
155+
108156 static void LogDebug (const char * format, ...) {};
109157#endif
110158
@@ -449,21 +497,21 @@ namespace ESSM::IdManager
449497 {
450498 uint32_t id = s_systemData.reusableIds .back ();
451499 s_systemData.reusableIds .pop_back ();
452- LogDebug (LOG_DEBUG_HEADER " New ID: %u\n " , id);
500+ LogDebug (" New ID: %u\n " , id);
453501 return id;
454502 }
455503
456504 uint32_t id = s_systemData.totalIds ;
457505 s_systemData.totalIds ++;
458506 s_systemData.hijackedStates .emplace_back ();
459- LogDebug (LOG_DEBUG_HEADER " New ID: %u\n " , id);
507+ LogDebug (" New ID: %u\n " , id);
460508 return id;
461509 }
462510
463511 static void ClearId (uint32_t id)
464512 {
465513 s_systemData.reusableIds .push_back (id);
466- LogDebug (LOG_DEBUG_HEADER " Cleared ID: %u\n " , id);
514+ LogDebug (" Cleared ID: %u\n " , id);
467515 }
468516}
469517
@@ -480,6 +528,7 @@ namespace ESSM
480528 static_assert (std::is_integral_v<Marker> && sizeof (Marker) >= 2 , " Chosen MarkerType cannot be used." );
481529
482530 private:
531+ static const Marker DEBUG_CLEARED_MARKER = 0x5250 ; // used to identify incorrect clears more reliably in debug
483532 static const Marker CLEARED_MARKER = 0x5249 ;
484533 static const Marker READ_MARKER = 0x5248 ;
485534 static const Marker HIJACK_MARKER = 0x5247 ;
@@ -516,6 +565,15 @@ namespace ESSM
516565 return IsHijacked (data) || IsWritten (data) || IsRead (data) || IsCleared (data);
517566 }
518567
568+ #ifndef NDEBUG
569+ static void SetDebugCleared (Data& data)
570+ {
571+ Traits::SetMarker (data, DEBUG_CLEARED_MARKER);
572+ }
573+ #else
574+ static void SetDebugCleared (Data& data) {}
575+ #endif
576+
519577 static void SetHijacked (Data& data)
520578 {
521579 Traits::SetMarker (data, HIJACK_MARKER);
@@ -1011,20 +1069,30 @@ namespace ESSM::Utils
10111069
10121070namespace ESSM ::Core
10131071{
1014- static auto ClearState = [](const auto * obj, ClearedIds& clearedIds)
1072+ static auto NewState = [](auto * obj)
1073+ {
1074+ using T = std::remove_cv_t <std::remove_pointer_t <decltype (obj)>>;
1075+ using Traits = ESSM::Traits::TraitsFor<T>;
1076+ using Manager = typename Traits::Hijack;
1077+
1078+ Manager::NewHijack (*obj);
1079+ };
1080+
1081+ static auto ClearState = [](auto * obj, ClearedIds& clearedIds)
10151082 {
10161083 using T = std::remove_cv_t <std::remove_pointer_t <decltype (obj)>>;
10171084 using Traits = ESSM::Traits::TraitsFor<T>;
10181085 using Manager = typename Traits::Hijack;
10191086
10201087 assert (Manager::IsHijacked (*obj));
10211088 uint32_t id = Manager::GetId (*obj);
1089+ Manager::SetDebugCleared (*obj);
10221090 IdManager::ClearId (id);
10231091
10241092 clearedIds.emplace_back (id);
10251093 };
10261094
1027- static auto ClearState_HijackedOnly = [](const auto * obj, ClearedIds& clearedIds)
1095+ static auto ClearState_HijackedOnly = [](auto * obj, ClearedIds& clearedIds)
10281096 {
10291097 using T = std::remove_cv_t <std::remove_pointer_t <decltype (obj)>>;
10301098 using Traits = ESSM::Traits::TraitsFor<T>;
@@ -1047,7 +1115,7 @@ namespace ESSM::Core
10471115 uint32_t targetId = IdManager::NewId ();
10481116 Manager::SetId (*obj, targetId);
10491117 s_systemData.hijackedStates [targetId] = s_systemData.hijackedStates [sourceId];
1050- LogDebug (LOG_DEBUG_HEADER " Copied Entity %d -> %d\n " , sourceId, targetId);
1118+ LogDebug (" Copied Entity %d -> %d\n " , sourceId, targetId);
10511119
10521120 return std::make_pair (sourceId, targetId);
10531121 };
@@ -1057,6 +1125,14 @@ namespace ESSM::Core
10571125 copiedIds.emplace_back (CopyState_NoBatch (obj));
10581126 };
10591127
1128+ static void NewStates (CollectedStates& collectedStates)
1129+ {
1130+ for (auto & entity : collectedStates)
1131+ {
1132+ std::visit ([](auto * obj) { NewState (obj); }, entity);
1133+ }
1134+ }
1135+
10601136 static void ClearSaveStates (CollectedStates& collectedSaves)
10611137 {
10621138 ClearedIds clearedIds;
@@ -1109,7 +1185,7 @@ namespace ESSM::Core
11091185// Public API
11101186namespace ESSM
11111187{
1112- void EntitySaveState_ClearBatch (const std::vector<EntitySaveState>& vector)
1188+ void EntitySaveState_ClearBatch (std::vector<EntitySaveState>& vector)
11131189 {
11141190 ClearedIds clearedIds;
11151191 clearedIds.reserve (vector.size ());
@@ -2311,6 +2387,71 @@ HOOK_METHOD(Game, SaveBackwardsStage, (int stage) -> void)
23112387 ESSM::Core::CopySaveStates (collection);
23122388}
23132389
2390+ HOOK_STATIC (Entity_NPC, moms_heart_mausoleum_death, () -> void, __cdecl)
2391+ {
2392+ constexpr size_t STATE_MAUSOLEUM_HEART_KILLED_FLAG_IDX = 46 ;
2393+ constexpr size_t STATE_MAUSOLEUM_HEART_KILLED_WORD_OFFSET = offsetof (Game, _gameStateFlags) + (STATE_MAUSOLEUM_HEART_KILLED_FLAG_IDX / 32 ) * sizeof (uint32_t );
2394+ constexpr uint32_t STATE_MAUSOLEUM_HEART_KILLED_FLAG = 1 << (STATE_MAUSOLEUM_HEART_KILLED_FLAG_IDX % 32 );
2395+
2396+ Game* game = g_Game;
2397+ uint32_t bitset = *(uint32_t *)((uintptr_t )game + STATE_MAUSOLEUM_HEART_KILLED_WORD_OFFSET);
2398+ uint32_t deathTriggered = (bitset & STATE_MAUSOLEUM_HEART_KILLED_FLAG) == 0 ;
2399+
2400+
2401+ if (!deathTriggered)
2402+ {
2403+ return super ();
2404+ }
2405+
2406+ ScopedDebugContext context (" Mom Heart Mausoleum Death" );
2407+
2408+ CollectedStates collection;
2409+ collection.reserve (DEFAULT_COLLECT_RESERVE);
2410+
2411+ RoomDescriptor* rooms = game->_gridRooms ;
2412+ const RoomDescriptor& currentRoom = *game->_room ->_descriptor ;
2413+
2414+ for (size_t i = 0 ; i < MAX_ROOMS; i++)
2415+ {
2416+ RoomDescriptor& room = rooms[i];
2417+ if (!room.Data || &room == ¤tRoom || room.Data ->Type == eRoomType::ROOM_ULTRASECRET || room.GridIndex == eGridRooms::ROOM_LIL_PORTAL_IDX)
2418+ {
2419+ continue ;
2420+ }
2421+
2422+ std::vector<EntitySaveState>& entityStates = room.SavedEntities ;
2423+ for (size_t i = 0 ; i < entityStates.size (); i++)
2424+ {
2425+ EntitySaveState& entityState = entityStates[i];
2426+ if (entityState.type == eEntityType::ENTITY_PICKUP)
2427+ {
2428+ collection.emplace_back (&entityState);
2429+ }
2430+ }
2431+ }
2432+
2433+ ESSM::Core::ClearSaveStates (collection);
2434+
2435+ super ();
2436+
2437+ constexpr uint32_t FLAG_CLEAR = 1 << 0 ;
2438+ RoomDescriptor& lastBossRoom = game->_gridRooms [game->_lastBossRoomListIdx ];
2439+ if (lastBossRoom.Data && (lastBossRoom.Flags & FLAG_CLEAR) != 0 )
2440+ {
2441+ // bugs and flies were added to the room
2442+ collection.clear ();
2443+ for (auto & entity : lastBossRoom.SavedEntities )
2444+ {
2445+ if (!ESSM::EntityHijackManager::HasMarker (entity))
2446+ {
2447+ collection.emplace_back (&entity);
2448+ }
2449+ }
2450+
2451+ ESSM::Core::NewStates (collection);
2452+ }
2453+ }
2454+
23142455// Clear backwards stage save state, even though the game simply sets room count to 0, to avoid having to iterate "uninitialized" RoomDescriptors in the BackwardsStage.
23152456HOOK_METHOD (Game, ResetState, () -> void)
23162457{
@@ -2324,7 +2465,7 @@ HOOK_METHOD(Game, ResetState, () -> void)
23242465
23252466HOOK_METHOD (GameState, Clear, () -> void)
23262467{
2327- LogDebug (LOG_DEBUG_HEADER " Start GameState::Clear\n " );
2468+ ScopedDebugContext context ( " GameState::Clear" );
23282469
23292470 CollectedStates collection;
23302471 collection.reserve (DEFAULT_COLLECT_RESERVE);
@@ -2334,13 +2475,11 @@ HOOK_METHOD(GameState, Clear, () -> void)
23342475
23352476 // player save states are handled by GameStatePlayer::Init
23362477 super ();
2337-
2338- LogDebug (LOG_DEBUG_HEADER " End GameState::Clear\n " );
23392478}
23402479
23412480HOOK_METHOD (Game, SaveState, (GameState* state) -> void)
23422481{
2343- LogDebug (LOG_DEBUG_HEADER " Start Game::SaveState\n " );
2482+ ScopedDebugContext context ( " Game::SaveState" );
23442483
23452484 // ASSUMPTION: It is assumed that the state has already been cleared.
23462485 // This is because SaveState always calls GameState::Clear before saving
@@ -2353,13 +2492,11 @@ HOOK_METHOD(Game, SaveState, (GameState* state) -> void)
23532492 ESSM::Core::CopySaveStates (collection);
23542493
23552494 // players are handled by Entity_Player::Init
2356-
2357- LogDebug (LOG_DEBUG_HEADER " End Game::SaveState\n " );
23582495}
23592496
23602497HOOK_METHOD (Game, RestoreState, (GameState* state, bool startGame) -> void)
23612498{
2362- LogDebug (LOG_DEBUG_HEADER " Start Game::RestoreState\n " );
2499+ ScopedDebugContext context ( " Game::RestoreState" );
23632500
23642501 CollectedStates collection;
23652502 collection.reserve (DEFAULT_COLLECT_RESERVE);
@@ -2368,8 +2505,6 @@ HOOK_METHOD(Game, RestoreState, (GameState* state, bool startGame) -> void)
23682505
23692506 super (state, startGame);
23702507 // copy is in a separate patch as copying it here might cause problems due to callbacks running in the mean time.
2371-
2372- LogDebug (LOG_DEBUG_HEADER " End Game::RestoreState\n " );
23732508}
23742509
23752510HOOK_METHOD (Level, RestoreGameState, (GameState* state) -> void)
@@ -2873,7 +3008,7 @@ static void __fastcall asm_clear_smart_pointer(EntitySaveState* saveState)
28733008 CollectedStates collection = {saveState};
28743009 ESSM::Core::ClearSaveStates (collection);
28753010
2876- LogDebug (LOG_DEBUG_HEADER " Smart pointer Cleared: %u\n " , id);
3011+ LogDebug (" Smart pointer Cleared: %u\n " , id);
28773012 }
28783013
28793014 saveState->destructor ();
@@ -2893,7 +3028,7 @@ static void Patch_ReferenceCount_EntitySaveStateDestructor()
28933028static void __stdcall asm_hijack_new_flip_state (EntitySaveState& saveState)
28943029{
28953030 uint32_t id = ESSM::EntityHijackManager::NewHijack (saveState);
2896- LogDebug (LOG_DEBUG_HEADER " New Flip State: %u\n " , id);
3031+ LogDebug (" New Flip State: %u\n " , id);
28973032}
28983033
28993034static void Patch_PickupInitFlipState_CreateSaveState ()
0 commit comments