diff --git a/CREDITS.md b/CREDITS.md index a37947eb32..ea681903f2 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -288,6 +288,7 @@ This page lists all the individual contributions to the project by their author. - Customizable paradrop missions - Guard range customizations - Wall overlay unit sell exploit fix + - GapGen + SpySat desync fix - **Morton (MortonPL)**: - `XDrawOffset` for animations - Shield passthrough & absorption diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 647b54db92..1288abc610 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -300,6 +300,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed the bug in AI scripts 56 and 57 that forced the launch of superweapons with index numbers 3 and 4. - Buildings with `NeedsEngineer=true` are now considered to have threat value of 0 under ownership of `MultiplayPassive=true` houses regardless of their `ThreatPosed` value. - Vehicles overlapping `Wall=true` OverlayTypes no longer display sell cursor and cannot be sold. +- Fixed a desync due to an inconsistent shroud state caused by `GapGenerator` and `SpySat` interaction. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a4eaebd6b4..cc0b8bc572 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -651,6 +651,7 @@ Fixes / interactions with other extensions: Vanilla fixes: - Vehicles overlapping `Wall=true` OverlayTypes no longer display sell cursor and cannot be sold (by CnCRAZER & Starkku) +- Fixed a desync due to an inconsistent shroud state caused by `GapGenerator` and `SpySat` interaction (by Starkku) Phobos fixes: - Fixed vehicles disguised as trees incorrectly displaying veterancy insignia when they shouldn't (by Starkku) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index b8e59cbbbf..7e4639a941 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -3077,3 +3077,92 @@ DEFINE_HOOK(0x6EA870, TeamClass_LiberateMember_Start, 0x6) pMember->RecruitableB = true; return 0; } + +#pragma region ShroudFix + +// These map cells are what SpySat skips revealing in MP normally. +static bool inline ShroudFix_IsCellInvalid(CellStruct* pMapCell) +{ + int x = pMapCell->X; + int y = pMapCell->Y; + auto const& rect = MapClass::Instance.MapRect; + + if (x == 7 && y == rect.Width + 5) + return true; + + if (x == 13 && y == rect.Width + 11) + return true; + + if (x == rect.Height + 13 && y == rect.Width + rect.Height -15 ) + return true; + + return false; +} + +DEFINE_HOOK(0x6FB5E5, TechnoClass_DeleteGap_CellCheck, 0x5) +{ + enum { SkipCell = 0x6FB6F3 }; + + GET(CellStruct*, pMapCell, EDX); + + if (ShroudFix_IsCellInvalid(pMapCell)) + return SkipCell; + + return 0; +} + +DEFINE_HOOK(0x6FB2FB, TechnoClass_CreateGap_CellCheck, 0x5) +{ + enum { SkipCell = 0x6FB416 }; + + GET(CellStruct*, pMapCell, EDX); + + if (ShroudFix_IsCellInvalid(pMapCell)) + return SkipCell; + + return 0; +} + +// Replace the entire cell iterator loop for perf reasons. +DEFINE_HOOK(0x577AFF, MapClass_ResetShroud_CellCheck, 0x6) +{ + enum { SkipGameCode = 0x577B75 }; + + auto& map = MapClass::Instance; + map.CellIteratorReset(); + + for (auto pCell = map.CellIteratorNext(); pCell; pCell = map.CellIteratorNext()) + { + if (ShroudFix_IsCellInvalid(&pCell->MapCoords)) + continue; + + pCell->Flags &= ~(CellFlags::CenterRevealed | CellFlags::EdgeRevealed); + pCell->AltFlags &= ~(AltCellFlags::Mapped | AltCellFlags::NoFog); + pCell->ShroudCounter = 1; + pCell->GapsCoveringThisCell = 0; + } + + return SkipGameCode; +} + +// Replace the entire cell iterator loop for perf reasons. +DEFINE_HOOK(0x577BF1, MapClass_ResetShroudForTMission_CellCheck, 0x6) +{ + enum { SkipGameCode = 0x577C57 }; + + auto& map = MapClass::Instance; + map.CellIteratorReset(); + + for (auto pCell = map.CellIteratorNext(); pCell; pCell = map.CellIteratorNext()) + { + if (ShroudFix_IsCellInvalid(&pCell->MapCoords)) + continue; + + pCell->Flags &= ~(CellFlags::CenterRevealed | CellFlags::EdgeRevealed); + pCell->AltFlags &= ~(AltCellFlags::Mapped | AltCellFlags::NoFog); + } + + return SkipGameCode; +} + +#pragma endregion diff --git a/src/Phobos.cpp b/src/Phobos.cpp index c061b50170..60ce544c2f 100644 --- a/src/Phobos.cpp +++ b/src/Phobos.cpp @@ -271,6 +271,18 @@ void Phobos::ApplyOptimizations() if (!SessionClass::IsMultiplayer()) { + // Disable TechnoClass_DeleteGap_CellCheck + Patch::Apply_RAW(0x6FB5E5, { 0xB9, 0xE8, 0xF7, 0x87, 0x00 }); + + // Disable TechnoClass_CreateGap_CellCheck + Patch::Apply_RAW(0x6FB2FB, { 0xB9, 0xE8, 0xF7, 0x87, 0x00 }); + + // Disable MapClass_ResetShroud_CellCheck + Patch::Apply_RAW(0x577AFF, { 0x8B, 0x86, 0xF4, 0x00, 0x00, 0x00 }); + + // Disable MapClass_ResetShroudForTMission_CellCheck + Patch::Apply_RAW(0x577BF1, { 0x8B, 0x86, 0xF4, 0x00, 0x00, 0x00 }); + // Disable Random2Class_Random_SyncLog Patch::Apply_RAW(0x65C7D0, { 0xC3, 0x90, 0x90, 0x90, 0x90 });