From 267547dedd7d70adb24439986f0520e9f4556326 Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Mon, 17 Aug 2026 12:48:25 +0800 Subject: [PATCH 1/6] CRITIMMUNE --- docs/New-or-Enhanced-Logics.md | 2 +- src/Ext/Techno/Hooks.Firing.cpp | 2 +- src/Ext/TechnoType/Body.cpp | 1 + src/Ext/WarheadType/Detonate.cpp | 2 +- src/Utilities/Enum.h | 1 + 5 files changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 2266f70d42..7a64c62a4c 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2781,7 +2781,7 @@ AffectsGround=true ; boolean - `Crit.AnimOnAffectedTargets`, if set, makes the animation(s) from `Crit.AnimList` play on each affected target *in addition* to animation from Warhead's `AnimList` playing as normal instead of replacing `AnimList` animation. Note that because these animations are independent from `AnimList`, `Crit.AnimList.PickRandom` and `Crit.AnimList.CreateAll` will not default to their `AnimList` counterparts here and need to be explicitly set if needed. - `Crit.ActiveChanceAnims` can be used to set animation to be always displayed at the Warhead's detonation coordinates if the current Warhead has a chance to critically hit. If more than one animation is listed, a random one is selected. - `Crit.SuppressWhenIntercepted`, if set, prevents critical hits from occuring at all if the warhead was detonated from a [projectile that was intercepted](#projectile-interception-logic). - - `ImmuneToCrit` can be set on TechnoTypes and ShieldTypes to make them immune to critical hits. + - `ImmuneToCrit` can be set on TechnoTypes and ShieldTypes to make them immune to critical hits. You can also add `CRITIMMUNE` to the TechnoTypes' `VeteranAbilities` and `EliteAbilities` to grant them immunity when promoting. In `rulesmd.ini`: ```ini diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 7417067eaa..3b46a6a64c 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -1170,7 +1170,7 @@ static inline int ScaleReloadDurationForVeterancy(TechnoClass* pThis, int durati const auto pRulesExt = RulesExt::Global(); const double multiplier = ability == AdditionalAbility::EmptyReload - ? pTypeExt->VeteranEmptyReload.Get(pRulesExt->VeteranEmptyReload.Get(RulesExt::Global()->VeteranReload)) + ? pTypeExt->VeteranEmptyReload.Get(pRulesExt->VeteranEmptyReload.Get(pRulesExt->VeteranReload)) : pTypeExt->VeteranReload.Get(pRulesExt->VeteranReload); return Math::max(1, GeneralUtils::SafeMultiply(duration, multiplier)); diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index b903cd3e35..cf7382d166 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -17,6 +17,7 @@ namespace constexpr std::pair AbilityTokens[] = { { "RELOAD", AdditionalAbility::Reload }, { "EMPTY_RELOAD", AdditionalAbility::EmptyReload }, + { "CRITIMMUNE", AdditionalAbility::CritImmune } }; void ReadAdditionalAbilities( diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 1fe0f66fff..deb0f90171 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -534,7 +534,7 @@ void WarheadTypeExt::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoC auto const pTargetExt = TechnoExt::Fetch(pTarget); - if (pTargetExt->TypeExtData->ImmuneToCrit) + if (pTargetExt->TypeExtData->ImmuneToCrit || TechnoExt::HasAdditionalAbility(pTarget, AdditionalAbility::CritImmune)) return; auto const pSld = pTargetExt->Shield.get(); diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 98430c02e5..27e70b14c7 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -449,6 +449,7 @@ enum class AdditionalAbility : unsigned char { Reload = 0, EmptyReload = 1, + CritImmune = 2, Count }; From d97b475b693b1873df22764733ec02357fc95dcc Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Mon, 17 Aug 2026 14:43:27 +0800 Subject: [PATCH 2/6] RANGE --- docs/New-or-Enhanced-Logics.md | 19 +++++++++++++++++++ src/Ext/Rules/Body.cpp | 2 ++ src/Ext/Rules/Body.h | 2 ++ src/Ext/Techno/Hooks.Firing.cpp | 7 +++---- src/Ext/TechnoType/Body.cpp | 3 +++ src/Ext/TechnoType/Body.h | 2 ++ src/Ext/WeaponType/Body.cpp | 3 +++ src/Utilities/Enum.h | 3 ++- 8 files changed, 36 insertions(+), 5 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 7a64c62a4c..9c923051f5 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2332,6 +2332,25 @@ CombatAlert.EVA=EVA_UnitsInCombat ; EVA entry CombatAlert.Suppress= ; boolean ``` +### Weapon range adjustment on promotion + +- Now you can add `RANGE` to `VeteranAbilities` and `EliteAbilities` to adjust the weapon range of this TechnoType. + - `ProjectileRange` of the weapon's projectile will also be affected if `Projectile.ApplyModifiers` set to true. + +```{hint} +- Abilities from `VeteranAbilities` keep working at elite level, and `EliteAbilities` are added on top, matching how vanilla abilities accumulate. +- `VeteranRange` will be applied to the range calculation. Values greater than `1.0` lengthen the range, values smaller than `1.0` shorten it, and `1.0` (the default) leaves it unchanged. +``` + +In `rulesmd.ini`: +```ini +[General] +VeteranRange=1.0 ; floating point value, multiplier + +[SOMETECHNO] ; TechnoType, with VeteranAbilities and/or EliteAbilities containing RANGE. +VeteranRange= ; floating point value, multiplier, default to [General] -> VeteranRange +``` + ### Recount burst index - You can now make technos recount their current burst index when they have changed the firing weapon or have maintained for a period of time without any targets (take the larger value of last firing weapon's `ROF` and 30 frames). diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 3d8447cb91..b485487ad4 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -357,6 +357,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->VeteranReload.Read(exINI, GameStrings::General, "VeteranReload"); this->VeteranEmptyReload.Read(exINI, GameStrings::General, "VeteranEmptyReload"); + this->VeteranRange.Read(exINI, GameStrings::General, "VeteranRange"); this->NoTurret_TrackTarget.Read(exINI, GameStrings::General, "NoTurret.TrackTarget"); @@ -913,6 +914,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->NoReload_Temporal) .Process(this->VeteranReload) .Process(this->VeteranEmptyReload) + .Process(this->VeteranRange) .Process(this->NoTurret_TrackTarget) .Process(this->GatherWhenMCVDeploy) .Process(this->AIFireSale) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 808521c38f..7ec9319698 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -290,6 +290,7 @@ class RulesExt Valueable NoReload_Temporal; Valueable VeteranReload; Nullable VeteranEmptyReload; + Valueable VeteranRange; Valueable NoTurret_TrackTarget; Valueable GatherWhenMCVDeploy; @@ -779,6 +780,7 @@ class RulesExt , NoReload_Temporal { false } , VeteranReload { 1.0 } , VeteranEmptyReload {} + , VeteranRange { 1.0 } , NoTurret_TrackTarget { false } , GatherWhenMCVDeploy { true } , AIFireSale { true } diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 3b46a6a64c..a553081896 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -1022,7 +1022,7 @@ DEFINE_HOOK(0x6F3AEB, TechnoClass_GetFLH, 0x6) GET(TechnoTypeClass*, pType, EAX); GET(const int, weaponIndex, ESI); GET_STACK(CoordStruct*, pCoords, STACK_OFFSET(0xD8, 0x4)); - REF_STACK(CoordStruct, offset, STACK_OFFSET(0xD8, 0xC)); + REF_STACK(const CoordStruct, offset, STACK_OFFSET(0xD8, 0xC)); bool allowOnTurret = true; CoordStruct flh = CoordStruct::Empty; @@ -1167,11 +1167,10 @@ static inline int ScaleReloadDurationForVeterancy(TechnoClass* pThis, int durati return duration; const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; - const auto pRulesExt = RulesExt::Global(); const double multiplier = ability == AdditionalAbility::EmptyReload - ? pTypeExt->VeteranEmptyReload.Get(pRulesExt->VeteranEmptyReload.Get(pRulesExt->VeteranReload)) - : pTypeExt->VeteranReload.Get(pRulesExt->VeteranReload); + ? pTypeExt->VeteranEmptyReload.Get(RulesExt::Global()->VeteranEmptyReload.Get(RulesExt::Global()->VeteranReload)) + : pTypeExt->VeteranReload.Get(RulesExt::Global()->VeteranReload); return Math::max(1, GeneralUtils::SafeMultiply(duration, multiplier)); } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index cf7382d166..7b5f682a7e 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -17,6 +17,7 @@ namespace constexpr std::pair AbilityTokens[] = { { "RELOAD", AdditionalAbility::Reload }, { "EMPTY_RELOAD", AdditionalAbility::EmptyReload }, + { "RANGE", AdditionalAbility::Range }, { "CRITIMMUNE", AdditionalAbility::CritImmune } }; @@ -1052,6 +1053,7 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->VeteranReload.Read(exINI, pSection, "VeteranReload"); this->VeteranEmptyReload.Read(exINI, pSection, "VeteranEmptyReload"); + this->VeteranRange.Read(exINI, pSection, "VeteranRange"); this->Wake.Read(exINI, pSection, "Wake"); this->Wake_Grapple.Read(exINI, pSection, "Wake.Grapple"); @@ -1688,6 +1690,7 @@ void TechnoTypeExt::Serialize(T& Stm) .Process(this->AdditionalEliteAbilities) .Process(this->VeteranReload) .Process(this->VeteranEmptyReload) + .Process(this->VeteranRange) .Process(this->Wake) .Process(this->Wake_Grapple) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index e4ee3ab3e7..88a4954f9b 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -298,6 +298,7 @@ class TechnoTypeExt : public ObjectTypeExt std::bitset AdditionalEliteAbilities; Nullable VeteranReload; Nullable VeteranEmptyReload; + Nullable VeteranRange; Nullable Wake; Nullable Wake_Grapple; @@ -699,6 +700,7 @@ class TechnoTypeExt : public ObjectTypeExt , AdditionalEliteAbilities {} , VeteranReload {} , VeteranEmptyReload {} + , VeteranRange {} , Wake { } , Wake_Grapple { } diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index ccccfe8648..c0b7b89a3d 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -393,6 +393,9 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF auto const pTechnoExt = TechnoExt::Fetch(pTechno); + if (TechnoExt::HasAdditionalAbility(pTechno, AdditionalAbility::Range)) + range = GeneralUtils::SafeMultiply(range, Math::max(pTechnoExt->TypeExtData->VeteranRange.Get(RulesExt::Global()->VeteranRange), 0.0)); + if (!pTechnoExt->AE.HasRangeModifier) return range; diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 27e70b14c7..18bce17f80 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -449,7 +449,8 @@ enum class AdditionalAbility : unsigned char { Reload = 0, EmptyReload = 1, - CritImmune = 2, + Range = 2, + CritImmune = 3, Count }; From 8f91ea331e92235b64d0ffd0cce533aacaef2ada Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Wed, 19 Aug 2026 21:15:28 +0800 Subject: [PATCH 3/6] CRITCHANCE --- docs/New-or-Enhanced-Logics.md | 12 ++++++++++++ src/Ext/Rules/Body.cpp | 2 ++ src/Ext/Rules/Body.h | 2 ++ src/Ext/TechnoType/Body.cpp | 5 ++++- src/Ext/TechnoType/Body.h | 2 ++ src/Ext/WarheadType/Detonate.cpp | 3 +++ src/Utilities/Enum.h | 1 + 7 files changed, 26 insertions(+), 1 deletion(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 9c923051f5..b8dbd7d652 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2801,9 +2801,18 @@ AffectsGround=true ; boolean - `Crit.ActiveChanceAnims` can be used to set animation to be always displayed at the Warhead's detonation coordinates if the current Warhead has a chance to critically hit. If more than one animation is listed, a random one is selected. - `Crit.SuppressWhenIntercepted`, if set, prevents critical hits from occuring at all if the warhead was detonated from a [projectile that was intercepted](#projectile-interception-logic). - `ImmuneToCrit` can be set on TechnoTypes and ShieldTypes to make them immune to critical hits. You can also add `CRITIMMUNE` to the TechnoTypes' `VeteranAbilities` and `EliteAbilities` to grant them immunity when promoting. + - You can add `CRITCHANCE` to `VeteranAbilities` and `EliteAbilities` to adjust the critical hit chance of this TechnoType. + +```{hint} +- Abilities from `VeteranAbilities` keep working at elite level, and `EliteAbilities` are added on top, matching how vanilla abilities accumulate. +- `VeteranCritChance` will be applied to the range calculation. Values greater than `1.0` increase the chance, values smaller than `1.0` decrease it, and `1.0` (the default) leaves it unchanged. +``` In `rulesmd.ini`: ```ini +[General] +VeteranCritChance=1.0 ; floating point value + [CombatDamage] Crit.ApplyChancePerTarget=false ; boolean Crit.ExtraDamage.ApplyFirepowerMult=false ; boolean @@ -2830,6 +2839,9 @@ Crit.SuppressWhenIntercepted= ; boolean, default to [CombatDamage] [SOMETECHNO] ; TechnoType ImmuneToCrit=false ; boolean + +[SOMETECHNO] ; TechnoType, with VeteranAbilities and/or EliteAbilities containing CRITCHANCE. +VeteranCritChance= ; floating point value, multiplier, default to [General] -> VeteranCritChance ``` ```{warning} diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index b485487ad4..eaf0128d3a 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -358,6 +358,7 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->VeteranReload.Read(exINI, GameStrings::General, "VeteranReload"); this->VeteranEmptyReload.Read(exINI, GameStrings::General, "VeteranEmptyReload"); this->VeteranRange.Read(exINI, GameStrings::General, "VeteranRange"); + this->VeteranCritChance.Read(exINI, GameStrings::General, "VeteranCritChance"); this->NoTurret_TrackTarget.Read(exINI, GameStrings::General, "NoTurret.TrackTarget"); @@ -915,6 +916,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->VeteranReload) .Process(this->VeteranEmptyReload) .Process(this->VeteranRange) + .Process(this->VeteranCritChance) .Process(this->NoTurret_TrackTarget) .Process(this->GatherWhenMCVDeploy) .Process(this->AIFireSale) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 7ec9319698..648d1e8110 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -291,6 +291,7 @@ class RulesExt Valueable VeteranReload; Nullable VeteranEmptyReload; Valueable VeteranRange; + Valueable VeteranCritChance; Valueable NoTurret_TrackTarget; Valueable GatherWhenMCVDeploy; @@ -781,6 +782,7 @@ class RulesExt , VeteranReload { 1.0 } , VeteranEmptyReload {} , VeteranRange { 1.0 } + , VeteranCritChance { 1.0 } , NoTurret_TrackTarget { false } , GatherWhenMCVDeploy { true } , AIFireSale { true } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 7b5f682a7e..7fb1a807c5 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -18,7 +18,8 @@ namespace { "RELOAD", AdditionalAbility::Reload }, { "EMPTY_RELOAD", AdditionalAbility::EmptyReload }, { "RANGE", AdditionalAbility::Range }, - { "CRITIMMUNE", AdditionalAbility::CritImmune } + { "CRITIMMUNE", AdditionalAbility::CritImmune }, + { "CRITCHANCE", AdditionalAbility::CritChance } }; void ReadAdditionalAbilities( @@ -1054,6 +1055,7 @@ void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) this->VeteranReload.Read(exINI, pSection, "VeteranReload"); this->VeteranEmptyReload.Read(exINI, pSection, "VeteranEmptyReload"); this->VeteranRange.Read(exINI, pSection, "VeteranRange"); + this->VeteranCritChance.Read(exINI, pSection, "VeteranCritChance"); this->Wake.Read(exINI, pSection, "Wake"); this->Wake_Grapple.Read(exINI, pSection, "Wake.Grapple"); @@ -1691,6 +1693,7 @@ void TechnoTypeExt::Serialize(T& Stm) .Process(this->VeteranReload) .Process(this->VeteranEmptyReload) .Process(this->VeteranRange) + .Process(this->VeteranCritChance) .Process(this->Wake) .Process(this->Wake_Grapple) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 88a4954f9b..22e8567bbd 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -299,6 +299,7 @@ class TechnoTypeExt : public ObjectTypeExt Nullable VeteranReload; Nullable VeteranEmptyReload; Nullable VeteranRange; + Nullable VeteranCritChance; Nullable Wake; Nullable Wake_Grapple; @@ -701,6 +702,7 @@ class TechnoTypeExt : public ObjectTypeExt , VeteranReload {} , VeteranEmptyReload {} , VeteranRange {} + , VeteranCritChance {} , Wake { } , Wake_Grapple { } diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index deb0f90171..75d1c2c75d 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -733,6 +733,9 @@ double WarheadTypeExt::GetCritChance(TechnoClass* pFirer) const auto const pExt = TechnoExt::Fetch(pFirer); + if (TechnoExt::HasAdditionalAbility(pFirer, AdditionalAbility::CritChance)) + critChance = GeneralUtils::SafeMultiply(critChance, Math::max(pExt->TypeExtData->VeteranCritChance.Get(RulesExt::Global()->VeteranCritChance), 0.0)); + if (!pExt->AE.HasCritModifiers) return critChance; diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 18bce17f80..3fcd8d1288 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -451,6 +451,7 @@ enum class AdditionalAbility : unsigned char EmptyReload = 1, Range = 2, CritImmune = 3, + CritChance = 4, Count }; From 689e65662b43fece0f886d09bd99c368543cca1d Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Mon, 24 Aug 2026 22:38:23 +0800 Subject: [PATCH 4/6] doc --- CREDITS.md | 1 + docs/Whats-New.md | 1 + 2 files changed, 2 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index d486713e9d..2fe5311051 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -781,6 +781,7 @@ This page lists all the individual contributions to the project by their author. - Customize whether or not passenger can fire out when the transport is moving - Show game time - Fix a game crash when parsing string list with null entry + - More veteran and elite abilities - **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes - **handama**: - AI script action to `16005 Jump Back To Previous Script` diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 8225c1eaa0..9d73612f15 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -421,6 +421,7 @@ HideShakeEffects=false ; boolean #### New: - [Customized transport plane for teams](AI-Scripting-and-Mapping.md#customized-transport-plane-for-teams) (by FlyStar) - [Modify ammo on impact](New-or-Enhanced-Logics.md#modify-ammo-on-impact) (by FS-21) +- More veteran and elite abilities (by Ollerus) #### Vanilla fixes: - Fixed the bug where a building with `Factory=BuildingType` owned by the AI did not play `ProductionAnim` when placing a produced building (by Noble_Fish) From 51bbaf3d824a205c6a2fe1ee4ececbe83f39ff6a Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Tue, 25 Aug 2026 09:53:58 +0800 Subject: [PATCH 5/6] fix --- src/Ext/Rules/Body.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b08c897d67..3da156353b 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -291,8 +291,8 @@ class RulesExt Valueable NoReload_Temporal; Valueable VeteranReload; Nullable VeteranEmptyReload; - Valueable VeteranRange; - Valueable VeteranCritChance; + Valueable VeteranRange; + Valueable VeteranCritChance; Valueable NoTurret_TrackTarget; Valueable GatherWhenMCVDeploy; From 80e5b958f114889262b186a5a7662a988a6e2e1f Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Wed, 26 Aug 2026 14:48:23 +0800 Subject: [PATCH 6/6] fix --- src/Ext/WarheadType/Detonate.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index c2799b4ce1..64cd96367a 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -748,7 +748,7 @@ double WarheadTypeExt::GetCritChance(TechnoClass* pFirer) const auto const pExt = TechnoExt::Fetch(pFirer); if (TechnoExt::HasAdditionalAbility(pFirer, AdditionalAbility::CritChance)) - critChance = GeneralUtils::SafeMultiply(critChance, Math::max(pExt->TypeExtData->VeteranCritChance.Get(RulesExt::Global()->VeteranCritChance), 0.0)); + critChance = critChance * Math::max(pExt->TypeExtData->VeteranCritChance.Get(RulesExt::Global()->VeteranCritChance), 0); if (!pExt->AE.HasCritModifiers) return critChance;