From 7ac4d9b6d8751bd2450f01451955f6ef1ec54019 Mon Sep 17 00:00:00 2001 From: RAZER <79311432+CnCRAZER@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:42:52 -0500 Subject: [PATCH 1/6] Update Hooks.BugFixes.cpp --- src/Misc/Hooks.BugFixes.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 67db318703..8f16686c72 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -2874,6 +2874,25 @@ DEFINE_HOOK(0x4DB874, FootClass_SetLocation_Extra, 0xA) return SkipGameCode; } +// Clean up dead aircraft stuck off-map after crash descent fails outside map bounds. +// FlyLocomotionClass guards crash position updates with an In_Radar check, so aircraft +// that die off-map never complete descent and never reach the UnInit() at the end of it. +DEFINE_HOOK(0x414DB6, AircraftClass_Update_CrashingOffMap, 0x6) +{ + enum { ExitFunction = 0x4151B0 }; + + GET(AircraftClass* const, pThis, ESI); + + if (pThis->IsCrashing && pThis->Health <= 0 + && !MapClass::Instance.IsWithinUsableArea(pThis->GetCoords())) + { + pThis->UnInit(); + return ExitFunction; + } + + return 0; +} + DEFINE_HOOK(0x4DEC7F, FootClass_Crash_FallingDownFix, 0x7) { GET(FootClass*, pThis, ESI); From a0f454b6807237c6f0aeccbd5ad337485d4b452c Mon Sep 17 00:00:00 2001 From: RAZER <79311432+CnCRAZER@users.noreply.github.com> Date: Tue, 24 Mar 2026 13:50:30 -0500 Subject: [PATCH 2/6] Document fix: off-map crashing aircraft cleanup Add documentation and credit for a bug fix where aircraft destroyed while crashing off-map were not fully cleaned up, which could permanently block production slots and count toward unit limits. Updates CREDITS.md and adds entries to Fixed-or-Improved-Logics.md and Whats-New.md (credit to RAZER). --- CREDITS.md | 1 + docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 1 + 3 files changed, 3 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index fbe33c4733..67417b1501 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -775,3 +775,4 @@ This page lists all the individual contributions to the project by their author. - Wall overlay unit sell exploit fix - Multiplayer gamespeed fix for RealTimeTimers - Revert Ares patch to allow OpenTopped transport customization + - Fix for aircraft destroyed while crashing off-map not being cleaned up diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 2ee15a0dd5..82bb3d5204 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -273,6 +273,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed the bug that vehicle fall on infantry will make all cell content has been removed. - Fixed buildings that have their owner changed during buildup skipping buildup and sometimes not correctly clearing the state. - Fixed preplaced aircraft outside visible map being incorrectly flagged as crashing under certain conditions. +- Fixed aircraft destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits. - Fixed `MovementZone=Subterannean` harvesters being unable to find docks if in area enclosed by water, cliffs etc. - Fixed an issue where some effects pointing to a unit were not properly cleared when the unit changed its owner. - Allow Reveal Crate to take effect when picking up by another player controlled house in campaign. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 6760638c91..a6f34a7457 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -658,6 +658,7 @@ Phobos fixes: - Fixed the bug that the upgrade building's power-enhancing effect depends only on its parent building and is not related to the upgrade building itself (by NetsuNegi) - Fixed an issue where hover vehicles could not be destroyed after malfunctioning on water surfaces (by FlyStar) - Fixed an issue where shadow matrix scaling was incorrectly applied to `TurretOffset` causing turret shadow misplacement (by Noble_Fish) +- Fixed aircraft destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits (by RAZER) Fixes / interactions with other extensions: From 93b14e9a5faec719ccb8b259fda42bea45bd9997 Mon Sep 17 00:00:00 2001 From: RAZER <79311432+CnCRAZER@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:00:14 -0500 Subject: [PATCH 3/6] Fix off-map crash cleanup for flying units Handle units with Fly, Jumpjet or Rocket locomotors that die/crash off-map by adding locomotor-specific hooks. Replaces the previous aircraft-only approach by adding three hooks in src/Misc/Hooks.BugFixes.cpp to treat off-map as ground-touch and force the cleanup/detonation path (Fly, Jumpjet, Rocket), including a stack init for the jumpjet "fellOnSomething" flag. Also updates CREDITS.md, docs/Fixed-or-Improved-Logics.md and docs/Whats-New.md to reflect the broader scope of the fix. --- CREDITS.md | 2 +- docs/Fixed-or-Improved-Logics.md | 2 +- docs/Whats-New.md | 2 +- src/Misc/Hooks.BugFixes.cpp | 54 ++++++++++++++++++++++++++------ 4 files changed, 47 insertions(+), 13 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 67417b1501..fff2f03cfc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -775,4 +775,4 @@ This page lists all the individual contributions to the project by their author. - Wall overlay unit sell exploit fix - Multiplayer gamespeed fix for RealTimeTimers - Revert Ares patch to allow OpenTopped transport customization - - Fix for aircraft destroyed while crashing off-map not being cleaned up + - Fix for units with Fly, Jumpjet or Rocket locomotors crashing off-map not being cleaned up diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 82bb3d5204..0d2e01bb75 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -273,7 +273,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Fixed the bug that vehicle fall on infantry will make all cell content has been removed. - Fixed buildings that have their owner changed during buildup skipping buildup and sometimes not correctly clearing the state. - Fixed preplaced aircraft outside visible map being incorrectly flagged as crashing under certain conditions. -- Fixed aircraft destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits. +- Fixed units with Fly, Jumpjet or Rocket locomotors destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits. - Fixed `MovementZone=Subterannean` harvesters being unable to find docks if in area enclosed by water, cliffs etc. - Fixed an issue where some effects pointing to a unit were not properly cleared when the unit changed its owner. - Allow Reveal Crate to take effect when picking up by another player controlled house in campaign. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a6f34a7457..416fca730a 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -658,7 +658,7 @@ Phobos fixes: - Fixed the bug that the upgrade building's power-enhancing effect depends only on its parent building and is not related to the upgrade building itself (by NetsuNegi) - Fixed an issue where hover vehicles could not be destroyed after malfunctioning on water surfaces (by FlyStar) - Fixed an issue where shadow matrix scaling was incorrectly applied to `TurretOffset` causing turret shadow misplacement (by Noble_Fish) -- Fixed aircraft destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits (by RAZER) +- Fixed units with Fly, Jumpjet or Rocket locomotors destroyed while crashing off-map never being fully cleaned up, permanently blocking production slots and counting towards unit limits (by RAZER) Fixes / interactions with other extensions: diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 8f16686c72..bdd7ed0d27 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -2874,25 +2874,59 @@ DEFINE_HOOK(0x4DB874, FootClass_SetLocation_Extra, 0xA) return SkipGameCode; } -// Clean up dead aircraft stuck off-map after crash descent fails outside map bounds. -// FlyLocomotionClass guards crash position updates with an In_Radar check, so aircraft -// that die off-map never complete descent and never reach the UnInit() at the end of it. -DEFINE_HOOK(0x414DB6, AircraftClass_Update_CrashingOffMap, 0x6) +// Fix crash descent for aircraft/units off-map. +// In all three locomotors below, MapClass::In_Radar blocks position/coordinate updates +// when outside the map. This means crashing units off-map never descend to ground level +// (or reach their destination), so the cleanup code (fire death weapon, UnInit) never runs. +// The fix: at each locomotor's height/health check, treat off-map as ground-touch. +// FlyLocomotionClass::Process - height check after crash descent calculation. +// If off-map, skip the height > 0 check and go straight to ground-touch cleanup. +DEFINE_HOOK(0x4CD797, FlyLocomotionClass_CrashDescent_OffMap, 0x5) { - enum { ExitFunction = 0x4151B0 }; + enum { GroundTouchCleanup = 0x4CD7AA }; - GET(AircraftClass* const, pThis, ESI); + GET(LocomotionClass*, pThis, ESI); - if (pThis->IsCrashing && pThis->Health <= 0 - && !MapClass::Instance.IsWithinUsableArea(pThis->GetCoords())) + if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) + return GroundTouchCleanup; + + return 0; +} + +// JumpjetLocomotionClass::Process - height check before IsCrashing gate. +// If off-map, skip height check and go to the IsCrashing check directly. +DEFINE_HOOK(0x54CC16, JumpjetLocomotionClass_CrashDescent_OffMap, 0x8) +{ + enum { IsCrashingCheck = 0x54CC36 }; + + GET(LocomotionClass*, pThis, EDI); + + if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) { - pThis->UnInit(); - return ExitFunction; + // Replicate the stack init from the stolen bytes (mov byte ptr [esp+21h], 0) + // so the "fell on something" flag is properly zeroed for the crash path. + REF_STACK(BYTE, fellOnSomething, 0x21); + fellOnSomething = 0; + return IsCrashingCheck; } return 0; } +// RocketLocomotionClass::Process - health check after position update. +// If off-map, bypass the Health > 0 skip and force detonation/cleanup. +DEFINE_HOOK(0x662FD5, RocketLocomotionClass_Process_OffMap, 0x6) +{ + enum { ForceCleanup = 0x662FDF }; + + GET(LocomotionClass*, pThis, EDI); + + if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) + return ForceCleanup; + + return 0; +} + DEFINE_HOOK(0x4DEC7F, FootClass_Crash_FallingDownFix, 0x7) { GET(FootClass*, pThis, ESI); From 5a89bfef89d33916cea76b8d931acfe2e1ddce2b Mon Sep 17 00:00:00 2001 From: RAZER <79311432+CnCRAZER@users.noreply.github.com> Date: Wed, 25 Mar 2026 11:02:44 -0500 Subject: [PATCH 4/6] Update Hooks.BugFixes.cpp --- src/Misc/Hooks.BugFixes.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index bdd7ed0d27..87224e9635 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -2885,7 +2885,7 @@ DEFINE_HOOK(0x4CD797, FlyLocomotionClass_CrashDescent_OffMap, 0x5) { enum { GroundTouchCleanup = 0x4CD7AA }; - GET(LocomotionClass*, pThis, ESI); + GET(FlyLocomotionClass*, pThis, ESI); if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) return GroundTouchCleanup; @@ -2899,7 +2899,7 @@ DEFINE_HOOK(0x54CC16, JumpjetLocomotionClass_CrashDescent_OffMap, 0x8) { enum { IsCrashingCheck = 0x54CC36 }; - GET(LocomotionClass*, pThis, EDI); + GET(JumpjetLocomotionClass*, pThis, EDI); if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) { @@ -2919,7 +2919,7 @@ DEFINE_HOOK(0x662FD5, RocketLocomotionClass_Process_OffMap, 0x6) { enum { ForceCleanup = 0x662FDF }; - GET(LocomotionClass*, pThis, EDI); + GET(RocketLocomotionClass*, pThis, EDI); if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) return ForceCleanup; From e33489e21b5ff9ed94bec98114e815d34c5fed04 Mon Sep 17 00:00:00 2001 From: RAZER <79311432+CnCRAZER@users.noreply.github.com> Date: Wed, 25 Mar 2026 12:06:40 -0500 Subject: [PATCH 5/6] Update Hooks.BugFixes.cpp --- src/Misc/Hooks.BugFixes.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 87224e9635..01d6aed0d4 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -2880,14 +2880,18 @@ DEFINE_HOOK(0x4DB874, FootClass_SetLocation_Extra, 0xA) // (or reach their destination), so the cleanup code (fire death weapon, UnInit) never runs. // The fix: at each locomotor's height/health check, treat off-map as ground-touch. // FlyLocomotionClass::Process - height check after crash descent calculation. -// If off-map, skip the height > 0 check and go straight to ground-touch cleanup. +// If off-map and crashing, skip the height > 0 check and go straight to ground-touch cleanup. +// The IsCrashing guard is needed because healthy non-moving airborne aircraft also reach this +// code path (Is_Moving()==false && Height>0) and must not be sent to cleanup. DEFINE_HOOK(0x4CD797, FlyLocomotionClass_CrashDescent_OffMap, 0x5) { enum { GroundTouchCleanup = 0x4CD7AA }; GET(FlyLocomotionClass*, pThis, ESI); - if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) + const auto pLinkedTo = pThis->LinkedTo; + + if (pLinkedTo->IsCrashing && !MapClass::Instance.IsWithinUsableArea(pLinkedTo->GetCoords())) return GroundTouchCleanup; return 0; @@ -2905,7 +2909,7 @@ DEFINE_HOOK(0x54CC16, JumpjetLocomotionClass_CrashDescent_OffMap, 0x8) { // Replicate the stack init from the stolen bytes (mov byte ptr [esp+21h], 0) // so the "fell on something" flag is properly zeroed for the crash path. - REF_STACK(BYTE, fellOnSomething, 0x21); + REF_STACK(BYTE, fellOnSomething, STACK_OFFSET(0x34, -0x13)); fellOnSomething = 0; return IsCrashingCheck; } From f44b664171527656be01c6b6060fc826eaa07975 Mon Sep 17 00:00:00 2001 From: RAZER <79311432+CnCRAZER@users.noreply.github.com> Date: Fri, 27 Mar 2026 14:38:50 -0500 Subject: [PATCH 6/6] Correct STACK_OFFSET Values --- src/Misc/Hooks.BugFixes.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 01d6aed0d4..bff2deb288 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -2907,9 +2907,9 @@ DEFINE_HOOK(0x54CC16, JumpjetLocomotionClass_CrashDescent_OffMap, 0x8) if (!MapClass::Instance.IsWithinUsableArea(pThis->LinkedTo->GetCoords())) { - // Replicate the stack init from the stolen bytes (mov byte ptr [esp+21h], 0) + // Replicate the stack init from the stolen bytes (mov byte ptr [esp+11h], 0) // so the "fell on something" flag is properly zeroed for the crash path. - REF_STACK(BYTE, fellOnSomething, STACK_OFFSET(0x34, -0x13)); + REF_STACK(BYTE, fellOnSomething, STACK_OFFSET(0x34, -0x23)); fellOnSomething = 0; return IsCrashingCheck; }