From d15667d58bd3255b23cbda1d1ccbcb0950296fb4 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Thu, 5 Sep 2024 17:38:14 +0800 Subject: [PATCH 01/11] Use a different tag to show planning path when debugging put together with unserialized shit cuz this shit shouldn't be serialized probably extensible for future usage --- docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 2 +- src/Misc/Hooks.UI.cpp | 2 +- src/Phobos.INI.cpp | 3 +++ src/Phobos.h | 1 + 5 files changed, 7 insertions(+), 2 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 13392a53c7..dcf8caa7d5 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -165,6 +165,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Building upgrades now consistently use building's `PowerUpN` animation settings corresponding to the upgrade's `PowersUpToLevel` where possible. - Subterranean units are no longer allowed to perform deploy functions like firing weapons or `IsSimpleDeployer` while burrowed or burrowing, they will instead emerge first like they do for transport unloading. - The otherwise unused setting `[AI]` -> `PowerSurplus` (defaults to 50) which determines how much surplus power AI players will strive to have can be restored by setting `[AI]` -> `EnablePowerSurplus` to true. +- Planning paths are now shown for all units under player control or when `[GlobalControls]->DebugPlanningPaths=yes` in singleplayer game modes. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 21fb678592..30e58b83b5 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -429,7 +429,7 @@ New: - Promotion animation (by Fryone) - Allow different technos to share build limit in a group (by ststl & Ollerus) - Map events `604-605` for checking if a specific Techno enters in a cell (by FS-21) -- Waypoint path is drawn for all units, even those not under player control if `DebugKeysEnabled=yes` (by Trsdy) +- Waypoint path is drawn for all units under player control or if `[GlobalControls]->DebugPlanningPaths=yes` (by Trsdy) - `RemoveDisguise` now works on vehicle disguises (by Trsdy) - Allow anchoring extended tooltips to the left side of the sidebar (by Trsdy) - Toggle to allow spawned aircraft to attack immediately after being spawned (by Starkku) diff --git a/src/Misc/Hooks.UI.cpp b/src/Misc/Hooks.UI.cpp index 95d64b576c..f482ac8dc2 100644 --- a/src/Misc/Hooks.UI.cpp +++ b/src/Misc/Hooks.UI.cpp @@ -377,7 +377,7 @@ DEFINE_HOOK(0x604985, GetDialogUIStatusLabels_ShowBriefing, 0x5) bool __fastcall Fake_HouseIsAlliedWith(HouseClass* pThis, void*, HouseClass* CurrentPlayer) { - return Phobos::Config::DevelopmentCommands + return (Phobos::Config::ShowPlanningPath && SessionClass::IsSingleplayer()) || pThis->IsControlledByCurrentPlayer() || pThis->IsAlliedWith(CurrentPlayer); } diff --git a/src/Phobos.INI.cpp b/src/Phobos.INI.cpp index fdf0992d42..daeda05cca 100644 --- a/src/Phobos.INI.cpp +++ b/src/Phobos.INI.cpp @@ -38,6 +38,7 @@ bool Phobos::Config::ToolTipDescriptions = true; bool Phobos::Config::ToolTipBlur = false; bool Phobos::Config::PrioritySelectionFiltering = true; bool Phobos::Config::DevelopmentCommands = true; +bool Phobos::Config::ShowPlanningPath = false; bool Phobos::Config::ArtImageSwap = false; bool Phobos::Config::ShowPlacementPreview = false; bool Phobos::Config::DigitalDisplay_Enable = false; @@ -211,6 +212,8 @@ DEFINE_HOOK(0x52D21F, InitRules_ThingsThatShouldntBeSerailized, 0x6) #ifndef DEBUG Phobos::Config::DevelopmentCommands = pINI_RULESMD->ReadBool("GlobalControls", "DebugKeysEnabled", Phobos::Config::DevelopmentCommands); #endif + Phobos::Config::ShowPlanningPath = pINI_RULESMD->ReadBool("GlobalControls", "DebugPlanningPaths", Phobos::Config::ShowPlanningPath); + return 0; } diff --git a/src/Phobos.h b/src/Phobos.h index 49d25200c6..26e0493112 100644 --- a/src/Phobos.h +++ b/src/Phobos.h @@ -90,6 +90,7 @@ class Phobos static bool ShowPowerDelta; static bool ShowHarvesterCounter; static bool ShowWeedsCounter; + static bool ShowPlanningPath; }; class Misc From 5d5573bdeb164cc881cbf2279844100a319862aa Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 8 Sep 2024 15:09:07 +0300 Subject: [PATCH 02/11] Fix crash caused by Temporal targeting enslaved infantry --- docs/Fixed-or-Improved-Logics.md | 1 + docs/Whats-New.md | 1 + src/Misc/Hooks.BugFixes.cpp | 14 ++++++++++++++ 3 files changed, 16 insertions(+) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index dcf8caa7d5..aa543e800a 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -166,6 +166,7 @@ This page describes all ingame logics that are fixed or improved in Phobos witho - Subterranean units are no longer allowed to perform deploy functions like firing weapons or `IsSimpleDeployer` while burrowed or burrowing, they will instead emerge first like they do for transport unloading. - The otherwise unused setting `[AI]` -> `PowerSurplus` (defaults to 50) which determines how much surplus power AI players will strive to have can be restored by setting `[AI]` -> `EnablePowerSurplus` to true. - Planning paths are now shown for all units under player control or when `[GlobalControls]->DebugPlanningPaths=yes` in singleplayer game modes. +- Fixed `Temporal=true` Warheads potentially crashing game if used to attack `Slaved=true` infantry. ## Fixes / interactions with other extensions diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 30e58b83b5..318a7c561a 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -522,6 +522,7 @@ Vanilla fixes: - Building upgrades now consistently use building's `PowerUpN` animation settings corresponding to the upgrade's `PowersUpToLevel` where possible (by Starkku) - Subterranean units are no longer allowed to perform deploy functions like firing weapons or `IsSimpleDeployer` while burrowed or burrowing, they will instead emerge first like they do for transport unloading (by Starkku) - Subterranean units no longer draw an incorrectly positioned shadow when burrowing etc. (by Starkku) +- Fixed `Temporal=true` Warheads potentially crashing game if used to attack `Slaved=true` infantry (by Starkku) Phobos fixes: - Fixed a few errors of calling for superweapon launch by `LaunchSW` or building infiltration (by Trsdy) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index d595935b42..f5c8178374 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -918,3 +918,17 @@ DEFINE_HOOK(0x705D74, TechnoClass_GetRemapColour_DisguisePalette, 0x8) return SkipGameCode; } + +// Fixes an edge case crash caused by temporal targeting enslaved infantry. +DEFINE_HOOK(0x71ADE4, TemporalClass_Release_SlaveTargetFix, 0x5) +{ + GET(TemporalClass* const, pThis, ESI); + + if (pThis->PrevTemporal == pThis) + pThis->PrevTemporal = nullptr; + + if (pThis->NextTemporal == pThis) + pThis->NextTemporal = nullptr; + + return 0; +} From da248f68b52ae91eb4d4830f4a7b22eb4b0838c9 Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 8 Sep 2024 17:48:50 +0300 Subject: [PATCH 03/11] Refine the fix in previous commit --- src/Misc/Hooks.BugFixes.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index f5c8178374..0760e4d0f7 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -922,13 +922,12 @@ DEFINE_HOOK(0x705D74, TechnoClass_GetRemapColour_DisguisePalette, 0x8) // Fixes an edge case crash caused by temporal targeting enslaved infantry. DEFINE_HOOK(0x71ADE4, TemporalClass_Release_SlaveTargetFix, 0x5) { - GET(TemporalClass* const, pThis, ESI); + enum { ReturnFromFunction = 0x71AE47 }; - if (pThis->PrevTemporal == pThis) - pThis->PrevTemporal = nullptr; + GET(TemporalClass* const, pThis, ESI); - if (pThis->NextTemporal == pThis) - pThis->NextTemporal = nullptr; + if (!pThis->Target) + return ReturnFromFunction; return 0; } From 04d099b5112e8031999b15c4d3b1ed05712ecbd0 Mon Sep 17 00:00:00 2001 From: Starkku Date: Tue, 10 Sep 2024 13:38:31 +0300 Subject: [PATCH 04/11] Allow Warheads to apply multiplier to shield ReceivedDamage caps --- docs/New-or-Enhanced-Logics.md | 3 +++ src/Ext/WarheadType/Body.cpp | 4 ++++ src/Ext/WarheadType/Body.h | 4 ++++ src/New/Entity/ShieldClass.cpp | 8 +++++--- 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 9f9e818b8e..c213edbe7e 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -324,6 +324,8 @@ Shield.AbsorbPercent= ; floating point value Shield.PassPercent= ; floating point value Shield.ReceivedDamage.Minimum= ; integer Shield.ReceivedDamage.Maximum= ; integer +Shield.ReceivedDamage.MinMultiplier=1.0 ; floating point value +Shield.ReceivedDamage.MaxMultiplier=1.0 ; floating point value Shield.Respawn.Duration=0 ; integer, game frames Shield.Respawn.Amount=0.0 ; floating point value, percents or absolute Shield.Respawn.Rate=-1.0 ; floating point value, ingame minutes @@ -399,6 +401,7 @@ Shield.InheritStateOnReplace=false ; boolean - `Shield.AbsorbPercent` overrides the `AbsorbPercent` value set in the ShieldType that is being damaged. - `Shield.PassPercent` overrides the `PassPercent` value set in the ShieldType that is being damaged. - `Shield.ReceivedDamage.Minimum` & `Shield.ReceivedDamage.Maximum` override the values set in in the ShieldType that is being damaged. + - `Shield.ReceivedDamage.MinMultiplier` and `Shield.ReceivedDamage.MinMultiplier` are multipliers to the effective `Shield.ReceivedDamage.Minimum` and `Shield.ReceivedDamage.Maximum` respectively that are applied when the Warhead deals damage to a shield. - `Shield.Respawn.Rate` & `Shield.Respawn.Amount` override ShieldType `Respawn.Rate` and `Respawn.Amount` for duration of `Shield.Respawn.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.Respawn.RestartTimer` is set, currently running shield respawn timer is reset, otherwise the timer's duration is adjusted in proportion to the new `Shield.Respawn.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while respawn timer is running, remaining time is adjusted to proportionally match ShieldType `Respawn.Rate`. Re-applying the effect resets the duration to `Shield.Respawn.Duration` - `Shield.SelfHealing.Rate` & `Shield.SelfHealing.Amount` override ShieldType `SelfHealing.Rate` and `SelfHealing.Amount` for duration of `Shield.SelfHealing.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.SelfHealing.RestartTimer` is set, currently running self-healing timer is restarted, otherwise timer's duration is adjusted in proportion to the new `Shield.SelfHealing.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while self-healing timer is running, remaining time is adjusted to proportionally match ShieldType `SelfHealing.Rate`. Re-applying the effect resets the duration to `Shield.SelfHealing.Duration`. - Additionally `Shield.SelfHealing.RestartInCombat` & `Shield.SelfHealing.RestartInCombatDelay` can be used to override ShieldType settings. diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index f94a1ba724..281a42af65 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -204,6 +204,8 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Shield_PassPercent.Read(exINI, pSection, "Shield.PassPercent"); this->Shield_ReceivedDamage_Minimum.Read(exINI, pSection, "Shield.ReceivedDamage.Minimum"); this->Shield_ReceivedDamage_Maximum.Read(exINI, pSection, "Shield.ReceivedDamage.Maximum"); + this->Shield_ReceivedDamage_MinMultiplier.Read(exINI, pSection, "Shield.ReceivedDamage.MinMultiplier"); + this->Shield_ReceivedDamage_MaxMultiplier.Read(exINI, pSection, "Shield.ReceivedDamage.MaxMultiplier"); this->Shield_Respawn_Duration.Read(exINI, pSection, "Shield.Respawn.Duration"); this->Shield_Respawn_Amount.Read(exINI, pSection, "Shield.Respawn.Amount"); this->Shield_Respawn_Rate_InMinutes.Read(exINI, pSection, "Shield.Respawn.Rate"); @@ -415,6 +417,8 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->Shield_PassPercent) .Process(this->Shield_ReceivedDamage_Minimum) .Process(this->Shield_ReceivedDamage_Maximum) + .Process(this->Shield_ReceivedDamage_MinMultiplier) + .Process(this->Shield_ReceivedDamage_MaxMultiplier) .Process(this->Shield_Respawn_Duration) .Process(this->Shield_Respawn_Amount) .Process(this->Shield_Respawn_Rate) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 39810cc886..90b9968f7d 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -79,6 +79,8 @@ class WarheadTypeExt Nullable Shield_PassPercent; Nullable Shield_ReceivedDamage_Minimum; Nullable Shield_ReceivedDamage_Maximum; + Valueable Shield_ReceivedDamage_MinMultiplier; + Valueable Shield_ReceivedDamage_MaxMultiplier; Valueable Shield_Respawn_Duration; Nullable Shield_Respawn_Amount; @@ -227,6 +229,8 @@ class WarheadTypeExt , Shield_PassPercent {} , Shield_ReceivedDamage_Minimum {} , Shield_ReceivedDamage_Maximum {} + , Shield_ReceivedDamage_MinMultiplier { 1.0 } + , Shield_ReceivedDamage_MaxMultiplier { 1.0 } , Shield_Respawn_Duration { 0 } , Shield_Respawn_Amount { 0.0 } diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index 241a432dcb..4d105f7809 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -189,9 +189,11 @@ int ShieldClass::ReceiveDamage(args_ReceiveDamage* args) } int originalShieldDamage = shieldDamage; - - shieldDamage = Math::clamp(shieldDamage, pWHExt->Shield_ReceivedDamage_Minimum.Get(this->Type->ReceivedDamage_Minimum), - pWHExt->Shield_ReceivedDamage_Maximum.Get(this->Type->ReceivedDamage_Maximum)); + int min = pWHExt->Shield_ReceivedDamage_Minimum.Get(this->Type->ReceivedDamage_Minimum); + int max = pWHExt->Shield_ReceivedDamage_Maximum.Get(this->Type->ReceivedDamage_Maximum); + int minDmg = static_cast(min * pWHExt->Shield_ReceivedDamage_MinMultiplier); + int maxDmg = static_cast(max * pWHExt->Shield_ReceivedDamage_MaxMultiplier); + shieldDamage = Math::clamp(shieldDamage, minDmg, maxDmg); if (Phobos::DisplayDamageNumbers && shieldDamage != 0) GeneralUtils::DisplayDamageNumberString(shieldDamage, DamageDisplayType::Shield, this->Techno->GetRenderCoords(), TechnoExt::ExtMap.Find(this->Techno)->DamageNumberOffset); From 86d8d2258daa61c9af6edeca6f146bf08c704cf3 Mon Sep 17 00:00:00 2001 From: Starkku Date: Wed, 11 Sep 2024 16:17:44 +0300 Subject: [PATCH 05/11] Fix ExtData allocation - Now allocated in CTOR, loading game fetches existing extdata pointers and allocates if missing for any reason - Because AbstractClass::Load() overrides pointer data stored at 0x18, the ExtData pointer is stored in Container in PrepareStream() and restored in LoadStatic() --- src/Ext/Anim/Body.cpp | 8 ++++---- src/Utilities/Container.h | 37 +++++++++++++++++++++++++++---------- 2 files changed, 31 insertions(+), 14 deletions(-) diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 68eb7bf94e..9fb253a058 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -281,10 +281,10 @@ DEFINE_HOOK_AGAIN(0x422126, AnimClass_CTOR, 0x5) DEFINE_HOOK_AGAIN(0x422707, AnimClass_CTOR, 0x5) DEFINE_HOOK(0x4228D2, AnimClass_CTOR, 0x5) { + GET(AnimClass*, pItem, ESI); + if (!Phobos::IsLoadingSaveGame) { - GET(AnimClass*, pItem, ESI); - auto const callerAddress = CTORTemp::callerAddress; // Do this here instead of using a duplicate hook in SyncLogger.cpp @@ -296,10 +296,10 @@ DEFINE_HOOK(0x4228D2, AnimClass_CTOR, 0x5) Debug::Log("Attempting to create animation with null Type (Caller: %08x)!\n", callerAddress); return 0; } - - AnimExt::ExtMap.Allocate(pItem); } + AnimExt::ExtMap.Allocate(pItem); + return 0; } diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 6ca0002fd6..6a1a343285 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -286,6 +286,7 @@ class Container map_type Items; base_type* SavingObject; + extension_type_ptr SavingExtPointer; IStream* SavingStream; const char* Name; @@ -361,10 +362,6 @@ class Container extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) { - // Do not allow allocation when loading save games. - if (Phobos::IsLoadingSaveGame) - return nullptr; - if (!key || (!bCond && !nMessage.empty())) { Debug::Log("%s \n", nMessage.data()); @@ -376,10 +373,6 @@ class Container extension_type_ptr TryAllocate(base_type_ptr key) { - // Do not allow allocation when loading save games. - if (Phobos::IsLoadingSaveGame) - return nullptr; - if (!key) { Debug::Log("Attempted to allocate %s from nullptr!\n", typeid(extension_type).name()); @@ -400,6 +393,22 @@ class Container return this->Items.find(key); } + // Only used on loading, does not check if key is nullptr. + extension_type_ptr FindOrAllocate(base_type_ptr key) + { + extension_type_ptr value = nullptr; + + if constexpr (HasOffset) + value = GetExtensionPointer(key); + else + value = this->Items.find(key); + + if (!value) + value = Allocate(key); + + return value; + } + void Remove(base_type_ptr key) { if (auto Item = Find(key)) @@ -442,6 +451,10 @@ class Container this->SavingObject = key; this->SavingStream = pStm; + + // Loading the base type data might override the ext pointer stored on it so it needs to be saved. + if constexpr (HasOffset) + this->SavingExtPointer = GetExtensionPointer(key); } void SaveStatic() @@ -466,6 +479,10 @@ class Container { if (this->SavingObject && this->SavingStream) { + // Restore stored ext pointer data. + if constexpr (HasOffset) + SetExtensionPointer(this->SavingObject, this->SavingExtPointer); + //Debug::Log("[LoadStatic] Loading object %p as '%s'\n", this->SavingObject, this->Name); if (!this->Load(this->SavingObject, this->SavingStream)) Debug::FatalErrorAndExit("LoadStatic - Loading object %p as '%s' failed!\n", this->SavingObject, this->Name); @@ -550,8 +567,8 @@ class Container return nullptr; } - extension_type_ptr buffer = this->Allocate(key); - + // get or allocate the value data + extension_type_ptr buffer = this->FindOrAllocate(key); if (!buffer) { Debug::Log("LoadKey - Could not find or allocate value.\n"); From 3b0b9cc30c3f4dc86d0648d163539ce201a2d079 Mon Sep 17 00:00:00 2001 From: Kerbiter Date: Wed, 11 Sep 2024 17:33:34 +0300 Subject: [PATCH 06/11] Upgrade to `upload-artifact@v4` to fix GH Actions --- .github/actions/build-phobos/action.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/actions/build-phobos/action.yml b/.github/actions/build-phobos/action.yml index 87bf5079cc..d818a510a3 100644 --- a/.github/actions/build-phobos/action.yml +++ b/.github/actions/build-phobos/action.yml @@ -26,7 +26,7 @@ runs: - name: Upload Artifact if: ${{success()}} - uses: actions/upload-artifact@v2 + uses: actions/upload-artifact@v4 with: name: compiled-dll-${{github.sha}} path: | From 816d9923b4b08b672866bbe62cac7412ae581db6 Mon Sep 17 00:00:00 2001 From: Starkku Date: Fri, 13 Sep 2024 20:15:18 +0300 Subject: [PATCH 07/11] Add `FireOnce` weapon infantry sequence reset toggle --- docs/Fixed-or-Improved-Logics.md | 10 ++++++++++ docs/Whats-New.md | 1 + src/Ext/Techno/Body.cpp | 1 + src/Ext/Techno/Body.h | 2 ++ src/Ext/Techno/Hooks.Firing.cpp | 16 ++++++++++++++++ src/Ext/Techno/Hooks.cpp | 20 ++++++++++++++++++++ src/Ext/WeaponType/Body.cpp | 2 ++ src/Ext/WeaponType/Body.h | 2 ++ 8 files changed, 54 insertions(+) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index aa543e800a..0ab1288ee4 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1335,6 +1335,16 @@ In `rulesmd.ini` KickOutPassengers=true ; boolean ``` +### Disable FireOnce resetting infantry sequence + +- It is now possible to disable `FireOnce=true` weapon resetting infantry sequences after firing via `FireOnce.ResetSequence`. Target will be forgotten like before, the firing sequence will simply continue playing after firing if there are any frames left. + +In `rulesmd.ini` +```ini +[SOMEWEAPON] +FireOnce.ResetSequence=true ; boolean +``` + ### Single-color lasers ![image](_static/images/issinglecolor.gif) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 318a7c561a..47f83706d1 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -450,6 +450,7 @@ New: - Forbidding parallel AI queues for specific TechnoTypes (by Starkku) - Nonprovocative Warheads (by Starkku) - Option to restore `PowerSurplus` setting for AI (by Starkku) +- `FireOnce` infantry sequence reset toggle (by Starkku) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 82368ad6e2..2a0d4bf97d 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -489,6 +489,7 @@ void TechnoExt::ExtData::Serialize(T& Stm) .Process(this->IsBurrowed) .Process(this->HasBeenPlacedOnMap) .Process(this->DeployFireTimer) + .Process(this->SkipTargetChangeResetSequence) .Process(this->ForceFullRearmDelay) .Process(this->CanCloakDuringRearm) .Process(this->WHAnimRemainingCreationInterval) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 39e777208f..0a90b4f688 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -42,6 +42,7 @@ class TechnoExt bool IsBurrowed; bool HasBeenPlacedOnMap; // Set to true on first Unlimbo() call. CDTimerClass DeployFireTimer; + bool SkipTargetChangeResetSequence; bool ForceFullRearmDelay; bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon. int WHAnimRemainingCreationInterval; @@ -75,6 +76,7 @@ class TechnoExt , IsBurrowed { false } , HasBeenPlacedOnMap { false } , DeployFireTimer {} + , SkipTargetChangeResetSequence { false } , ForceFullRearmDelay { false } , CanCloakDuringRearm { false } , WHAnimRemainingCreationInterval { 0 } diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 1d804f1dd8..8cd47933d1 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -509,6 +509,22 @@ DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6) return 0; } +DEFINE_HOOK(0x6FF905, TechnoClass_FireAt_FireOnce, 0x6) +{ + GET(TechnoClass*, pThis, ESI); + + if (auto const pInf = abstract_cast(pThis)) + { + GET(WeaponTypeClass*, pWeapon, EBX); + auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + + if (!pWeaponExt->FireOnce_ResetSequence) + TechnoExt::ExtMap.Find(pInf)->SkipTargetChangeResetSequence = true; + } + + return 0; +} + DEFINE_HOOK(0x6FF660, TechnoClass_FireAt_Interceptor, 0x6) { GET(TechnoClass* const, pSource, ESI); diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index e2c8c22264..29b645195b 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -599,3 +599,23 @@ DEFINE_HOOK(0x6F9FA9, TechnoClass_AI_PromoteAnim, 0x6) // TunnelLocomotionClass_IsToHaveShadow, skip shadow on all but idle. // TODO: Investigate if it is possible to fix the shadows not tilting on the burrowing etc. states. DEFINE_JUMP(LJMP, 0x72A070, 0x72A07F); + + +DEFINE_HOOK(0x51B20E, InfantryClass_AssignTarget_FireOnce, 0x6) +{ + enum { SkipGameCode = 0x51B255 }; + + GET(InfantryClass*, pThis, ESI); + GET(AbstractClass*, pTarget, EBX); + + auto const pExt = TechnoExt::ExtMap.Find(pThis); + + if (!pTarget && pExt->SkipTargetChangeResetSequence) + { + pThis->IsFiring = false; + pExt->SkipTargetChangeResetSequence = false; + return SkipGameCode; + } + + return 0; +} diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index f3c45295bb..ad123810c6 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -95,6 +95,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Laser_IsSingleColor.Read(exINI, pSection, "IsSingleColor"); this->ROF_RandomDelay.Read(exINI, pSection, "ROF.RandomDelay"); this->OmniFire_TurnToTarget.Read(exINI, pSection, "OmniFire.TurnToTarget"); + this->FireOnce_ResetSequence.Read(exINI, pSection, "FireOnce.ResetSequence"); this->ExtraWarheads.Read(exINI, pSection, "ExtraWarheads"); this->ExtraWarheads_DamageOverrides.Read(exINI, pSection, "ExtraWarheads.DamageOverrides"); this->ExtraWarheads_DetonationChances.Read(exINI, pSection, "ExtraWarheads.DetonationChances"); @@ -137,6 +138,7 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->Laser_IsSingleColor) .Process(this->ROF_RandomDelay) .Process(this->OmniFire_TurnToTarget) + .Process(this->FireOnce_ResetSequence) .Process(this->ExtraWarheads) .Process(this->ExtraWarheads_DamageOverrides) .Process(this->ExtraWarheads_DetonationChances) diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 7f70011e83..79da4f0b8f 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -40,6 +40,7 @@ class WeaponTypeExt Valueable Laser_IsSingleColor; Nullable> ROF_RandomDelay; Valueable OmniFire_TurnToTarget; + Valueable FireOnce_ResetSequence; ValueableVector ExtraWarheads; ValueableVector ExtraWarheads_DamageOverrides; ValueableVector ExtraWarheads_DetonationChances; @@ -78,6 +79,7 @@ class WeaponTypeExt , Laser_IsSingleColor { false } , ROF_RandomDelay {} , OmniFire_TurnToTarget { false } + , FireOnce_ResetSequence { true } , ExtraWarheads {} , ExtraWarheads_DamageOverrides {} , ExtraWarheads_DetonationChances {} From adc078fc242aaf8118f61ebbf2d5f99febac16c4 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 14 Sep 2024 17:27:12 +0800 Subject: [PATCH 08/11] fix a typo --- docs/Fixed-or-Improved-Logics.md | 2 +- docs/New-or-Enhanced-Logics.md | 6 +++--- src/Ext/Techno/Body.Update.cpp | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 0ab1288ee4..f5f3cabe27 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1255,7 +1255,7 @@ DecloakDamagedTargets=true ; boolean - You can now make Warheads behave in nonprovocative fashion. Warheads with `Nonprovocative=true` exhibit following behaviours: - They will not generate any EVA announcements upon hitting targets, be it for attacking ore miners, base buildings or ally base buildings. - - They will not spring 'attacked' / 'attacked by' events. Note that if the Warhead deals actual damage, events that check for that can still be sprung. + - They will not spring 'attacked' / 'attacked by' events. Note that if the Warhead deals actual damage, events that check for that can still be sprung. - They will not evoke defense response from AI players when used to attack base buildings, `ToProtect=true` TechnoTypes or members of TeamTypes with `Whiner=true`. - They will not evoke retaliation from TechnoTypes hit by the Warhead. diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index c213edbe7e..f42cf5bb76 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -68,7 +68,7 @@ This page describes all the engine features that are either new and introduced b - `AttachEffect.(Required|Disallowed)MinCounts & (Required|Disallowed)MaxCounts` can be used to set the minimum and maximum number of instances required / disallowed to be on the Techno for `Cumulative=true` types (ignored for other types) respectively. - `AttachEffect.IgnoreFromSameSource` can be set to true to ignore effects that have been attached by the firer of the weapon and its Warhead. - `AttachEffect.CheckOnFirer` is set to true makes it so that the required / disallowed attached effects are checked from the firer of the weapon instead of the target. - + In `rulesmd.ini`: ```ini [AttachEffectTypes] @@ -433,7 +433,7 @@ Shield.InheritStateOnReplace=false ; boolean - `CreateUnit.ConsiderPathfinding`, if set to true, will consider whether or not the cell where the animation is located is occupied by other objects or impassable to the vehicle being created and will attempt to find a nearby cell that is not. Otherwise the vehicle will be created at the animation's location despite these obstacles if possible. - `CreateUnit.SpawnAnim` can be used to play another animation at created unit's location after it has appeared. This animation has same owner and invoker as the parent animation. - `CreateUnit.SpawnHeight` can be set to override the animation's height when determining where to spawn the created unit. Has no effect if `CreateUnit.AlwaysSpawnOnGround` is set to true. - + In `artmd.ini`: ```ini [SOMEANIM] ; AnimationType @@ -682,7 +682,7 @@ Trajectory.Speed=100.0 ; floating point value - `Trajectory.Straight.PassThrough` enables special case logic where the projectile does not detonate in contact with the target but ínstead travels up to a distance defined by `Trajectory.Straight.DetonationDistance`. Note that the firing angle of the projectile is adjusted with this in mind, making it fire straight ahead if the target is on same elevation. In `rulesmd.ini`: -```ini +```ini [SOMEPROJECTILE] ; Projectile Trajectory=Straight ; Trajectory type Trajectory.Straight.DetonationDistance=0.4 ; floating point value diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 5eff39794d..4adfa8df6b 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -192,7 +192,7 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) // death if listed technos exist if (!pTypeExt->AutoDeath_TechnosExist.empty()) { - if (existTechnoTypes(pTypeExt->AutoDeath_TechnosExist, pTypeExt->AutoDeath_TechnosExist_Houses, pTypeExt->AutoDeath_TechnosExist_Any, pTypeExt->AutoDeath_TechnosDontExist_AllowLimboed)) + if (existTechnoTypes(pTypeExt->AutoDeath_TechnosExist, pTypeExt->AutoDeath_TechnosExist_Houses, pTypeExt->AutoDeath_TechnosExist_Any, pTypeExt->AutoDeath_TechnosExist_AllowLimboed)) { TechnoExt::KillSelf(pThis, howToDie, pVanishAnim, isInLimbo); From 9c8d2e8687e991a01979b2380912c5507d960a80 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 14 Sep 2024 17:46:00 +0800 Subject: [PATCH 09/11] Fix cb's multisection destroyer's nospawnalt's shadow someone mind fixing the dock offset later? --- src/Ext/TechnoType/Hooks.cpp | 33 ++++++++++++++++++--------------- 1 file changed, 18 insertions(+), 15 deletions(-) diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index e177510592..8192d2a0a1 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -301,6 +301,17 @@ constexpr double Pade2_2(double in) * (12. - 6 * s + s * s) / (12. + 6 * s + s * s); } +// We need to handle Ares turrets/barrels/waterimage/nospawnalt +struct DummyExtHere // TODO: move it +{ + char _[0xA4]; + std::vector ChargerTurrets; + std::vector ChargerBarrels; + char __[0x120]; + UnitTypeClass* WaterImage; + VoxelStruct NoSpawnAltVXL; +}; + DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) { GET(UnitClass*, pThis, EBP); @@ -355,17 +366,6 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) shadow_matrix.Scale((float)Pade2_2(baseScale_log)); } - // We need to handle Ares turrets/barrels - struct DummyExtHere - { - char _[0xA4]; - std::vector ChargerTurrets; - std::vector ChargerBarrels; - char __[0x120]; - UnitTypeClass* WaterImage; - VoxelStruct NoSpawnAltVXL; - }; - auto GetMainVoxel = [&]() { if (pType->NoSpawnAlt && pThis->SpawnManager && pThis->SpawnManager->CountDockedSpawns() == 0) @@ -636,7 +636,7 @@ DEFINE_JUMP(CALL6, 0x4148AB, 0x5F4300); DEFINE_JUMP(CALL6, 0x4147F3, 0x5F4300); */ -DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7) +DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x6) { GET(FootClass*, pThis, EBX);//Maybe Techno later GET(VoxelStruct*, pVXL, EBP); @@ -660,9 +660,12 @@ DEFINE_HOOK(0x7072A1, suka707280_ChooseTheGoddamnMatrix, 0x7) if (who_are_you[0] == UnitTypeClass::AbsVTable) pType = reinterpret_cast(who_are_you);//you are someone else else - return pThis->TurretAnimFrame % hva->FrameCount; - // you might also be SpawnAlt voxel, but I can't know - // otherwise what would you expect me to do, shift back to ares typeext base and check if ownerobject is technotype? + { + // guess what, someone actually has a multisection nospawnalt + if (!(AresHelper::CanUseAres && pVXL == &reinterpret_cast(pType->align_2FC)->NoSpawnAltVXL)) + return pThis->TurretAnimFrame % hva->FrameCount; + } + // you might also be WaterImage or sth else, but I don't want to care anymore, go fuck yourself } // Main body sections From 6f19e745c3495e4fdc162436c044ba11066edeb6 Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 14 Sep 2024 18:46:20 +0800 Subject: [PATCH 10/11] Revert 7cda0f80 and project the shadow I know it's ugly, just stay in consistency with others for the moment --- docs/Whats-New.md | 1 - src/Ext/Techno/Hooks.cpp | 5 ---- src/Ext/TechnoType/Hooks.cpp | 44 ++++++++++++++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 6 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 47f83706d1..9b82ece325 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -522,7 +522,6 @@ Vanilla fixes: - Fixed disguised units not using the correct palette if target has custom palette (by NetsuNegi) - Building upgrades now consistently use building's `PowerUpN` animation settings corresponding to the upgrade's `PowersUpToLevel` where possible (by Starkku) - Subterranean units are no longer allowed to perform deploy functions like firing weapons or `IsSimpleDeployer` while burrowed or burrowing, they will instead emerge first like they do for transport unloading (by Starkku) -- Subterranean units no longer draw an incorrectly positioned shadow when burrowing etc. (by Starkku) - Fixed `Temporal=true` Warheads potentially crashing game if used to attack `Slaved=true` infantry (by Starkku) Phobos fixes: diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 29b645195b..5ecf15359d 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -596,11 +596,6 @@ DEFINE_HOOK(0x6F9FA9, TechnoClass_AI_PromoteAnim, 0x6) return aresProcess(); } -// TunnelLocomotionClass_IsToHaveShadow, skip shadow on all but idle. -// TODO: Investigate if it is possible to fix the shadows not tilting on the burrowing etc. states. -DEFINE_JUMP(LJMP, 0x72A070, 0x72A07F); - - DEFINE_HOOK(0x51B20E, InfantryClass_AssignTarget_FireOnce, 0x6) { enum { SkipGameCode = 0x51B255 }; diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 8192d2a0a1..cfd9a1b36a 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -21,6 +21,7 @@ #include #include #include +#include DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) { @@ -312,6 +313,49 @@ struct DummyExtHere // TODO: move it VoxelStruct NoSpawnAltVXL; }; +Matrix3D* __stdcall TunnelLocomotionClass_ShadowMatrix(ILocomotion* iloco, Matrix3D* ret,VoxelIndexKey* key) +{ + __assume(iloco != nullptr); + auto tLoco = static_cast(iloco); + *ret = tLoco->LocomotionClass::Shadow_Matrix(key); + if (tLoco->State != TunnelLocomotionClass::State::Idle) + { + double theta = 0.; + switch (tLoco->State) + { + case TunnelLocomotionClass::State::DiggingIn: + if (key)key->Invalidate(); + theta = Math::HalfPi; + if (auto total = tLoco->DigTimer.Rate) + theta *= 1.0 - double(tLoco->DigTimer.GetTimeLeft()) / double(total); + break; + case TunnelLocomotionClass::State::DugIn: + theta = Math::HalfPi; + break; + case TunnelLocomotionClass::State::PreDigOut: + theta = -Math::HalfPi; + break; + case TunnelLocomotionClass::State::DiggingOut: + if (key)key->Invalidate(); + theta = -Math::HalfPi; + if (auto total = tLoco->DigTimer.Rate) + theta *= double(tLoco->DigTimer.GetTimeLeft()) / double(total); + break; + case TunnelLocomotionClass::State::DugOut: + if (key)key->Invalidate(); + theta = Math::HalfPi; + if (auto total = tLoco->DigTimer.Rate) + theta *= double(tLoco->DigTimer.GetTimeLeft()) / double(total); + break; + default:break; + } + ret->ScaleX((float)Math::cos(theta));// I know it's ugly + } + return ret; +} + +DEFINE_JUMP(VTABLE, 0x7F5A4C, GET_OFFSET(TunnelLocomotionClass_ShadowMatrix)); + DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) { GET(UnitClass*, pThis, EBP); From 0455a7755c174c32d053b827c161d10f966b4f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=BB=AF=E7=BA=A2=E7=83=AD=E8=8C=B6?= <335958461@qq.com> Date: Sun, 15 Sep 2024 21:40:11 +0800 Subject: [PATCH 11/11] [Minor] Fix LimboKill function (#1378) Fix a mistake of `LimboKill` that it did not update the iterators correctly that cause it unable to correctly remove all buildings with the same ID. --------- Co-authored-by: Kerbiter --- CREDITS.md | 2 ++ docs/Whats-New.md | 1 + src/Ext/SWType/FireSuperWeapon.cpp | 30 ++++++++++++++---------------- 3 files changed, 17 insertions(+), 16 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 5fa07e8307..c8555f9fa7 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -353,6 +353,8 @@ This page lists all the individual contributions to the project by their author. - Re-enable the Veinhole Monster and Weeds from TS - Recreate the weed-charging of SWs like the TS Chemical Missile - Allow to change the speed of gas particles +- **CrimRecya** + - Fix `LimboKill` not working reliably - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 9b82ece325..dd8bb1a747 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -564,6 +564,7 @@ Phobos fixes: - Fixed frame by frame hotkey description to read `TXT_FRAME_BY_FRAME_DESC` instead of `TXT_DISPLAY_DAMAGE_DESC` (by DeathFishAtEase) - Buildings considered vehicles (`ConsideredVehicle=true` or not set in conjunction with `UndeploysInto` & 1x1 foundation) are now considered units by affected target enum checks (by Starkku) - Fixed Phobos Warhead effects not reliably being applied on damage area as opposed to full weapon-based Warhead detonation (by Starkku) +- Fix `LimboKill` not working reliably (by CrimRecya) Fixes / interactions with other extensions: - `IsSimpleDeployer` units with Hover locomotor and `DeployToLand` no longer get stuck after deploying or play their move sound indefinitely (by Starkku) diff --git a/src/Ext/SWType/FireSuperWeapon.cpp b/src/Ext/SWType/FireSuperWeapon.cpp index 519f39d4a8..2241a2e409 100644 --- a/src/Ext/SWType/FireSuperWeapon.cpp +++ b/src/Ext/SWType/FireSuperWeapon.cpp @@ -120,20 +120,6 @@ inline void LimboCreate(BuildingTypeClass* pType, HouseClass* pOwner, int ID) } } -inline void LimboDelete(BuildingClass* pBuilding, HouseClass* pTargetHouse) -{ - auto pOwnerExt = HouseExt::ExtMap.Find(pTargetHouse); - - // Remove building from list of owned limbo buildings - auto& vec = pOwnerExt->OwnedLimboDeliveredBuildings; - vec.erase(std::remove(vec.begin(), vec.end(), pBuilding), vec.end()); - - pBuilding->Stun(); - pBuilding->Limbo(); - pBuilding->RegisterDestruction(nullptr); - pBuilding->UnInit(); -} - void SWTypeExt::ExtData::ApplyLimboDelivery(HouseClass* pHouse) { // random mode @@ -174,13 +160,25 @@ void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse) if (EnumFunctions::CanTargetHouse(this->LimboKill_Affected, pHouse, pTargetHouse)) { auto const pHouseExt = HouseExt::ExtMap.Find(pTargetHouse); + auto& vec = pHouseExt->OwnedLimboDeliveredBuildings; - for (const auto& pBuilding : pHouseExt->OwnedLimboDeliveredBuildings) + for (auto it = vec.begin(); it != vec.end(); ) { + BuildingClass* const pBuilding = *it; auto const pBuildingExt = BuildingExt::ExtMap.Find(pBuilding); if (pBuildingExt->LimboID == limboKillID) - LimboDelete(pBuilding, pTargetHouse); + { + it = vec.erase(it); + pBuilding->Stun(); + pBuilding->Limbo(); + pBuilding->RegisterDestruction(nullptr); + pBuilding->UnInit(); + } + else + { + ++it; + } } } }