From 252140b2783923709634145b9401093f5b23c06b Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 16:27:25 +0800 Subject: [PATCH 01/22] Add veterancy threshold check for TechnoClass objects Introduced IsVeterancyInThreshold to TechnoExt for checking if a TechnoClass object's veterancy level falls within a specified range. Updated relevant headers and added a corresponding method declaration in WarheadType's ExtData for future use. --- src/Ext/Techno/Body.cpp | 19 +++++++++++++++++++ src/Ext/Techno/Body.h | 1 + src/Ext/WarheadType/Body.h | 1 + 3 files changed, 21 insertions(+) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 97263846ad..104b597335 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -743,6 +743,25 @@ bool TechnoExt::IsHealthInThreshold(TechnoClass* pObject, double min, double max return (hp > 0 ? hp > min : hp >= min) && hp <= max; } +bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pObject, int min, int max) +{ + VeterancyStruct* pVeterancy = &pObject->Veterancy; + if (pVeterancy == nullptr) + return true; + + min = std::clamp(min, 0, 2); + max = std::clamp(max, 0, 2); + + int level = 0; + + if (pVeterancy->IsElite()) + level = 2; + else if (pVeterancy->IsVeteran()) + level = 1; + + return level >= min && level <= max; +} + bool TechnoExt::CannotMove(UnitClass* pThis) { const auto loco = pThis->Locomotor; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 89ed549edf..7527978bb9 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -289,6 +289,7 @@ class TechnoExt static void CreateDelayedFireAnim(TechnoClass* pThis, AnimTypeClass* pAnimType, int weaponIndex, bool attach, bool center, bool removeOnNoDelay, bool onTurret, CoordStruct firingCoords); static bool HandleDelayedFireWithPauseSequence(TechnoClass* pThis, WeaponTypeClass* pWeapon, int weaponIndex, int frame, int firingFrame); static bool IsHealthInThreshold(TechnoClass* pObject, double min, double max); + static bool IsVeterancyInThreshold(TechnoClass* pObject, int min, int max); static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt::ExtData* pData); static AircraftTypeClass* GetAircraftTypeExtra(AircraftClass* pAircraft); static bool CannotMove(UnitClass* pThis); diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index f2dc250213..0f2e4214d7 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -457,6 +457,7 @@ class WarheadTypeExt bool CanAffectInvulnerable(TechnoClass* pTarget) const; bool EligibleForFullMapDetonation(TechnoClass* pTechno, TechnoTypeClass* pType, HouseClass* pOwner) const; bool IsHealthInThreshold(TechnoClass* pTarget) const; + bool IsVeterancyInThreshold(TechnoClass* pTarget) const; virtual ~ExtData() = default; virtual void LoadFromINIFile(CCINIClass* pINI) override; From 8a983da83ef9a22f92a0c3d18af1de8429a34fac Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 18:56:13 +0800 Subject: [PATCH 02/22] Add veterancy threshold checks for weapons and warheads Introduces veterancy-based threshold checks for WeaponType and WarheadType, allowing configuration of min/max veterancy for targeting and effect application. Updates relevant methods, serialization, and INI loading to support new AffectsAboveVeterancy, AffectsBelowVeterancy, CanTarget_MinVeterancy, and CanTarget_MaxVeterancy fields. --- src/Ext/Techno/Body.cpp | 19 +++++-------------- src/Ext/Techno/Body.h | 2 +- src/Ext/WarheadType/Body.cpp | 20 ++++++++++++++++++++ src/Ext/WarheadType/Body.h | 6 ++++++ src/Ext/WeaponType/Body.cpp | 10 ++++++++++ src/Ext/WeaponType/Body.h | 5 +++++ 6 files changed, 47 insertions(+), 15 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 104b597335..5abf7bc512 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -743,23 +743,14 @@ bool TechnoExt::IsHealthInThreshold(TechnoClass* pObject, double min, double max return (hp > 0 ? hp > min : hp >= min) && hp <= max; } -bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pObject, int min, int max) +bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pTechno, float min, float max) { - VeterancyStruct* pVeterancy = &pObject->Veterancy; - if (pVeterancy == nullptr) - return true; - - min = std::clamp(min, 0, 2); - max = std::clamp(max, 0, 2); - - int level = 0; + float veterancy = 0.0; - if (pVeterancy->IsElite()) - level = 2; - else if (pVeterancy->IsVeteran()) - level = 1; + if (pTechno->GetTechnoType()->Trainable) + veterancy = pTechno->Veterancy.Veterancy; - return level >= min && level <= max; + return veterancy >= min && veterancy <= max; } bool TechnoExt::CannotMove(UnitClass* pThis) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 7527978bb9..624c2276df 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -289,7 +289,7 @@ class TechnoExt static void CreateDelayedFireAnim(TechnoClass* pThis, AnimTypeClass* pAnimType, int weaponIndex, bool attach, bool center, bool removeOnNoDelay, bool onTurret, CoordStruct firingCoords); static bool HandleDelayedFireWithPauseSequence(TechnoClass* pThis, WeaponTypeClass* pWeapon, int weaponIndex, int frame, int firingFrame); static bool IsHealthInThreshold(TechnoClass* pObject, double min, double max); - static bool IsVeterancyInThreshold(TechnoClass* pObject, int min, int max); + static bool IsVeterancyInThreshold(TechnoClass* pObject, float min, float max); static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt::ExtData* pData); static AircraftTypeClass* GetAircraftTypeExtra(AircraftClass* pAircraft); static bool CannotMove(UnitClass* pThis); diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 1dc39c2a42..35fe1d7467 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -42,6 +42,9 @@ bool WarheadTypeExt::ExtData::CanAffectTarget(TechnoClass* pTarget) const if (!IsHealthInThreshold(pTarget)) return false; + if (!IsVeterancyInThreshold(pTarget)) + return false; + if (!this->EffectsRequireVerses) return true; @@ -56,6 +59,14 @@ bool WarheadTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const return TechnoExt::IsHealthInThreshold(pTarget, this->AffectsAbovePercent, this->AffectsBelowPercent); } +bool WarheadTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const +{ + if (!this->VeterancyCheck) + return true; + + return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy); +} + // Checks if Warhead can affect target that might or might be currently invulnerable. bool WarheadTypeExt::ExtData::CanAffectInvulnerable(TechnoClass* pTarget) const { @@ -286,15 +297,21 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsBelowPercent.Read(exINI, pSection, "AffectsBelowPercent"); this->AffectsAbovePercent.Read(exINI, pSection, "AffectsAbovePercent"); + this->AffectsBelowPercent.Read(exINI, pSection, "AffectsBelowVeterancy"); + this->AffectsAbovePercent.Read(exINI, pSection, "AffectsAboveVeterancy"); this->AffectsNeutral.Read(exINI, pSection, "AffectsNeutral"); this->AffectsGround.Read(exINI, pSection, "AffectsGround"); this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); this->HealthCheck = this->AffectsBelowPercent > 0.0 || this->AffectsAbovePercent < 1.0; + this->VeterancyCheck = this->AffectsBelowVeterancy > 0.0 || this->AffectsAboveVeterancy < 2.0; if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); + if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy) + Debug::Log("[Developer warning][%s] AffectsAboveVeterancy is bigger than AffectsBelowVeterancy, the warhead will never activate!\n", pSection); + this->ReverseEngineer.Read(exINI, pSection, "ReverseEngineer"); this->UnlimboDetonate.Read(exINI, pSection, "UnlimboDetonate"); @@ -538,11 +555,14 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->AffectsBelowPercent) .Process(this->AffectsAbovePercent) + .Process(this->AffectsBelowVeterancy) + .Process(this->AffectsAboveVeterancy) .Process(this->AffectsNeutral) .Process(this->AffectsGround) .Process(this->AffectsAir) .Process(this->CellSpread_Cylinder) .Process(this->HealthCheck) + .Process(this->VeterancyCheck) .Process(this->InflictLocomotor) .Process(this->RemoveInflictedLocomotor) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 0f2e4214d7..deebac17b1 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -192,6 +192,8 @@ class WarheadTypeExt Valueable AffectsBelowPercent; Valueable AffectsAbovePercent; + Valueable AffectsBelowVeterancy; + Valueable AffectsAboveVeterancy; Valueable AffectsNeutral; Valueable AffectsGround; Valueable AffectsAir; @@ -230,6 +232,7 @@ class WarheadTypeExt int RemainingAnimCreationInterval; bool PossibleCellSpreadDetonate; bool HealthCheck; + bool VeterancyCheck; TechnoClass* DamageAreaTarget; private: @@ -402,6 +405,8 @@ class WarheadTypeExt , AffectsBelowPercent { 1.0 } , AffectsAbovePercent { 0.0 } + , AffectsBelowVeterancy { 2.0 } + , AffectsAboveVeterancy { 0.0 } , AffectsNeutral { true } , AffectsGround { true } , AffectsAir { true } @@ -423,6 +428,7 @@ class WarheadTypeExt , RemainingAnimCreationInterval { 0 } , PossibleCellSpreadDetonate { false } , HealthCheck { false } + , VeterancyCheck { false } , DamageAreaTarget {} , CanKill { true } diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index e3cd9c1d5c..06fa135ddb 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -46,6 +46,11 @@ bool WeaponTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const return TechnoExt::IsHealthInThreshold(pTarget, this->CanTarget_MinHealth, this->CanTarget_MaxHealth); } +bool WeaponTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const +{ + return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy); +} + void WeaponTypeExt::ExtData::Initialize() { this->RadType = RadTypeClass::FindOrAllocate(GameStrings::Radiation); @@ -104,6 +109,8 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->CanTargetHouses.Read(exINI, pSection, "CanTargetHouses"); this->CanTarget_MaxHealth.Read(exINI, pSection, "CanTarget.MaxHealth"); this->CanTarget_MinHealth.Read(exINI, pSection, "CanTarget.MinHealth"); + this->CanTarget_MaxVeterancy.Read(exINI, pSection, "CanTarget.MaxVeterancy"); + this->CanTarget_MinVeterancy.Read(exINI, pSection, "CanTarget.MinVeterancy"); this->Burst_Delays.Read(exINI, pSection, "Burst.Delays"); this->Burst_FireWithinSequence.Read(exINI, pSection, "Burst.FireWithinSequence"); this->Burst_NoDelay.Read(exINI, pSection, "Burst.NoDelay"); @@ -157,6 +164,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 + || this->CanTarget_MaxVeterancy < 2.0 || this->CanTarget_MinVeterancy > 0.0 || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) { @@ -186,6 +194,8 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->CanTargetHouses) .Process(this->CanTarget_MaxHealth) .Process(this->CanTarget_MinHealth) + .Process(this->CanTarget_MaxVeterancy) + .Process(this->CanTarget_MinVeterancy) .Process(this->RadType) .Process(this->Burst_Delays) .Process(this->Burst_FireWithinSequence) diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 03d6fe37e0..0b9cd0ae39 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -42,6 +42,8 @@ class WeaponTypeExt Valueable CanTargetHouses; Valueable CanTarget_MaxHealth; Valueable CanTarget_MinHealth; + Valueable CanTarget_MaxVeterancy; + Valueable CanTarget_MinVeterancy; ValueableVector Burst_Delays; Valueable Burst_FireWithinSequence; Valueable Burst_NoDelay; @@ -114,6 +116,8 @@ class WeaponTypeExt , CanTargetHouses { AffectedHouse::All } , CanTarget_MaxHealth { 1.0 } , CanTarget_MinHealth { 0.0 } + , CanTarget_MaxVeterancy { 2.0 } + , CanTarget_MinVeterancy { 0.0 } , Burst_Delays {} , Burst_FireWithinSequence { false } , Burst_NoDelay { false } @@ -169,6 +173,7 @@ class WeaponTypeExt int GetBurstDelay(int burstIndex) const; bool HasRequiredAttachedEffects(TechnoClass* pTechno, TechnoClass* pFirer) const; bool IsHealthInThreshold(TechnoClass* pTarget) const; + bool IsVeterancyInThreshold(TechnoClass* pTarget) const; virtual ~ExtData() = default; From 78305e1b22fdf8310d0b6b0572510bd05e8ea3c7 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 19:38:03 +0800 Subject: [PATCH 03/22] Add debug logs and use float suffixes for veterancy values Added debug logging to IsVeterancyInThreshold for min, max, and veterancy values in TechnoExt. Updated veterancy-related default values in WarheadType and WeaponType headers to use explicit float suffixes for consistency. --- src/Ext/Techno/Body.cpp | 5 ++++- src/Ext/WarheadType/Body.h | 4 ++-- src/Ext/WeaponType/Body.h | 4 ++-- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 5abf7bc512..2f5f5dd353 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -745,11 +745,14 @@ bool TechnoExt::IsHealthInThreshold(TechnoClass* pObject, double min, double max bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pTechno, float min, float max) { - float veterancy = 0.0; + Debug::Log("[Developer] min:[%.2f], max:[%.2f]\n", min, max); + float veterancy = 0.0f; if (pTechno->GetTechnoType()->Trainable) veterancy = pTechno->Veterancy.Veterancy; + Debug::Log("[Developer] veterancy:[%.2f]\n", veterancy); + return veterancy >= min && veterancy <= max; } diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index deebac17b1..7451218dfc 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -405,8 +405,8 @@ class WarheadTypeExt , AffectsBelowPercent { 1.0 } , AffectsAbovePercent { 0.0 } - , AffectsBelowVeterancy { 2.0 } - , AffectsAboveVeterancy { 0.0 } + , AffectsBelowVeterancy { 2.0f } + , AffectsAboveVeterancy { 0.0f } , AffectsNeutral { true } , AffectsGround { true } , AffectsAir { true } diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 0b9cd0ae39..ea2bdb860e 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -116,8 +116,8 @@ class WeaponTypeExt , CanTargetHouses { AffectedHouse::All } , CanTarget_MaxHealth { 1.0 } , CanTarget_MinHealth { 0.0 } - , CanTarget_MaxVeterancy { 2.0 } - , CanTarget_MinVeterancy { 0.0 } + , CanTarget_MaxVeterancy { 2.0f } + , CanTarget_MinVeterancy { 0.0f } , Burst_Delays {} , Burst_FireWithinSequence { false } , Burst_NoDelay { false } From dd1c9b803118ba90bbaa9654f48d60ce48bcf008 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 19:53:24 +0800 Subject: [PATCH 04/22] Fix incorrect INI keys for veterancy percent loading Corrected the code to read 'AffectsBelowVeterancy' and 'AffectsAboveVeterancy' into the appropriate variables instead of incorrectly reading them into percent variables. --- src/Ext/WarheadType/Body.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 35fe1d7467..30cd19d610 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -297,8 +297,8 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsBelowPercent.Read(exINI, pSection, "AffectsBelowPercent"); this->AffectsAbovePercent.Read(exINI, pSection, "AffectsAbovePercent"); - this->AffectsBelowPercent.Read(exINI, pSection, "AffectsBelowVeterancy"); - this->AffectsAbovePercent.Read(exINI, pSection, "AffectsAboveVeterancy"); + this->AffectsBelowVeterancy.Read(exINI, pSection, "AffectsBelowVeterancy"); + this->AffectsAboveVeterancy.Read(exINI, pSection, "AffectsAboveVeterancy"); this->AffectsNeutral.Read(exINI, pSection, "AffectsNeutral"); this->AffectsGround.Read(exINI, pSection, "AffectsGround"); this->AffectsAir.Read(exINI, pSection, "AffectsAir"); From 63dd822d839d0daf5d915f3320db6aabe1969903 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 20:34:20 +0800 Subject: [PATCH 05/22] Add veterancy threshold checks to targeting logic Extended weapon and warhead targeting logic to include veterancy threshold checks alongside health checks. Updated relevant methods and hooks to ensure that actions such as firing, shrapnel targeting, and damage application now also consider the target's veterancy. Removed debug logging from IsVeterancyInThreshold for cleaner output. Adjusted INI loading to use float literals for veterancy thresholds. --- src/Ext/Bullet/Hooks.DetonateLogics.cpp | 7 ++++--- src/Ext/Bullet/Hooks.cpp | 2 +- src/Ext/Techno/Body.cpp | 3 --- src/Ext/Techno/Hooks.Firing.cpp | 1 + src/Ext/Techno/Hooks.ReceiveDamage.cpp | 2 +- src/Ext/Techno/WeaponHelpers.cpp | 2 ++ src/Ext/WarheadType/Body.cpp | 2 +- src/Ext/WeaponType/Body.cpp | 2 +- 8 files changed, 11 insertions(+), 10 deletions(-) diff --git a/src/Ext/Bullet/Hooks.DetonateLogics.cpp b/src/Ext/Bullet/Hooks.DetonateLogics.cpp index ac07a6e057..ce20daa67c 100644 --- a/src/Ext/Bullet/Hooks.DetonateLogics.cpp +++ b/src/Ext/Bullet/Hooks.DetonateLogics.cpp @@ -26,7 +26,7 @@ DEFINE_HOOK(0x4690D4, BulletClass_Logics_NewChecks, 0x6) if (auto const pTarget = abstract_cast(pBullet->Target)) { // Check if the WH should affect the techno target or skip it - if (!pExt->IsHealthInThreshold(pTarget) || (!pExt->AffectsNeutral && pTarget->Owner->IsNeutral())) + if (!pExt->IsHealthInThreshold(pTarget) || !pExt->IsVeterancyInThreshold(pTarget) || (!pExt->AffectsNeutral && pTarget->Owner->IsNeutral())) return GoToExtras; } @@ -380,7 +380,7 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); auto const pTarget = abstract_cast(pThis->Target); - if (pTarget && !pWHExt->IsHealthInThreshold(pTarget)) + if (pTarget && !pWHExt->IsHealthInThreshold(pTarget) && !pWHExt->IsVeterancyInThreshold(pTarget)) continue; int damage = defaultDamage; @@ -554,7 +554,8 @@ static bool IsAllowedSplitsTarget(TechnoClass* pSource, HouseClass* pOwner, Weap if (!EnumFunctions::CanTargetHouse(pWeaponExt->CanTargetHouses, pOwner, pTarget->Owner) || !EnumFunctions::IsCellEligible(pTarget->GetCell(), pWeaponExt->CanTarget, true, true) || !EnumFunctions::IsTechnoEligible(pTarget, pWeaponExt->CanTarget) - || !pWeaponExt->IsHealthInThreshold(pTarget)) + || !pWeaponExt->IsHealthInThreshold(pTarget) + || !pWeaponExt->IsVeterancyInThreshold(pTarget)) { return false; } diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index ad67265d4f..d6bc6e4795 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -280,7 +280,7 @@ DEFINE_HOOK(0x46A4FB, BulletClass_Shrapnel_Targeting, 0x6) if (!pWeaponExt->SkipWeaponPicking) { if (!EnumFunctions::CanTargetHouse(pWeaponExt->CanTargetHouses, pOwner, pTechno->Owner) || !EnumFunctions::IsTechnoEligible(pTechno, pWeaponExt->CanTarget) - || !pWeaponExt->IsHealthInThreshold(pTechno) || !pWeaponExt->HasRequiredAttachedEffects(pTechno, pSource)) + || !pWeaponExt->IsHealthInThreshold(pTechno) || !pWeaponExt->IsVeterancyInThreshold(pTechno) || !pWeaponExt->HasRequiredAttachedEffects(pTechno, pSource)) { return SkipObject; } diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 2f5f5dd353..94395cd6d6 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -745,14 +745,11 @@ bool TechnoExt::IsHealthInThreshold(TechnoClass* pObject, double min, double max bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pTechno, float min, float max) { - Debug::Log("[Developer] min:[%.2f], max:[%.2f]\n", min, max); float veterancy = 0.0f; if (pTechno->GetTechnoType()->Trainable) veterancy = pTechno->Veterancy.Veterancy; - Debug::Log("[Developer] veterancy:[%.2f]\n", veterancy); - return veterancy >= min && veterancy <= max; } diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index e8f9396b1d..2f43ab7fd0 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -345,6 +345,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) if (!EnumFunctions::IsTechnoEligible(pTargetTechno, pWeaponExt->CanTarget) || !EnumFunctions::CanTargetHouse(pWeaponExt->CanTargetHouses, pThis->Owner, pTargetTechno->Owner) || !pWeaponExt->IsHealthInThreshold(pTargetTechno) + || !pWeaponExt->IsVeterancyInThreshold(pTargetTechno) || !pWeaponExt->HasRequiredAttachedEffects(pTargetTechno, pThis)) { return CannotFire; diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index 9e57d97361..eb65b1fd4c 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -24,7 +24,7 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) // AffectsAbove/BelowPercent & AffectsNeutral can ignore IgnoreDefenses like AffectsAllies/Enmies/Owner // They should be checked here to cover all cases that directly use ReceiveDamage to deal damage - if (!pWHExt->IsHealthInThreshold(pThis) || (!pWHExt->AffectsNeutral && pThis->Owner->IsNeutral())) + if (!pWHExt->IsHealthInThreshold(pThis) || !pWHExt->IsVeterancyInThreshold(pThis) || (!pWHExt->AffectsNeutral && pThis->Owner->IsNeutral())) { damage = 0; return 0; diff --git a/src/Ext/Techno/WeaponHelpers.cpp b/src/Ext/Techno/WeaponHelpers.cpp index 9eec2f40cf..59b49d606c 100644 --- a/src/Ext/Techno/WeaponHelpers.cpp +++ b/src/Ext/Techno/WeaponHelpers.cpp @@ -43,6 +43,7 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A if (!EnumFunctions::IsTechnoEligible(pTargetTechno, pSecondExt->CanTarget) || !EnumFunctions::CanTargetHouse(pSecondExt->CanTargetHouses, pThis->Owner, pTargetTechno->Owner) || !pSecondExt->IsHealthInThreshold(pTargetTechno) + || !pSecondExt->IsVeterancyInThreshold(pTargetTechno) || !pSecondExt->HasRequiredAttachedEffects(pTargetTechno, pThis)) { return weaponIndexOne; @@ -73,6 +74,7 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A if (!EnumFunctions::IsTechnoEligible(pTargetTechno, pFirstExt->CanTarget) || !EnumFunctions::CanTargetHouse(pFirstExt->CanTargetHouses, pThis->Owner, pTargetTechno->Owner) || !pFirstExt->IsHealthInThreshold(pTargetTechno) + || !pFirstExt->IsVeterancyInThreshold(pTargetTechno) || !firstAllowedAE) { return weaponIndexTwo; diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 30cd19d610..733c5b88d7 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -304,7 +304,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); this->HealthCheck = this->AffectsBelowPercent > 0.0 || this->AffectsAbovePercent < 1.0; - this->VeterancyCheck = this->AffectsBelowVeterancy > 0.0 || this->AffectsAboveVeterancy < 2.0; + this->VeterancyCheck = this->AffectsBelowVeterancy > 0.0f || this->AffectsAboveVeterancy < 2.0f; if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 06fa135ddb..84bce54a15 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -164,7 +164,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 - || this->CanTarget_MaxVeterancy < 2.0 || this->CanTarget_MinVeterancy > 0.0 + || this->CanTarget_MaxVeterancy < 2.0f || this->CanTarget_MinVeterancy > 0.0f || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) { From 8e7926af98530842ad228e835b7f0f67a4ccfe6f Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 21:39:35 +0800 Subject: [PATCH 06/22] Document veterancy-based targeting and warhead logic Added documentation for new veterancy-based conditions: `AffectsAboveVeterancy` and `AffectsBelowVeterancy` for warhead detonation, and `CanTarget.MinVeterancy` and `CanTarget.MaxVeterancy` for weapon targeting. Clarified how veterancy values are interpreted and how non-trainable TechnoTypes are handled. --- docs/Fixed-or-Improved-Logics.md | 6 ++++++ docs/New-or-Enhanced-Logics.md | 8 ++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index d0494c7685..019e1fa362 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2308,6 +2308,12 @@ Conventional.IgnoreUnits=false ; boolean ### Customizable Warhead trigger conditions - `AffectsBelowPercent` and `AffectsAbovePercent` can be used to set the health percentage thresholds that target needs to be below/equal and/or above of for the Warhead to detonate. If target has zero health left this check is bypassed. +- `AffectsAboveVeterancy` and `AffectsBelowVeterancy` can be used to set the veterancy thresholds that target needs to be below/equal and/or above of for the Warhead to detonate. + - Veterancy values are interpreted as follows: + - `0.0` = Rookie + - `1.0` = Veteran + - `2.0` = Elite + - TechnoTypes with `Trainable=no` are always treated as having a veterancy level of `0.0` (Rookie) for the purpose of this check. - If set to `false`, `AffectsNeutral` makes the warhead can't damage or affect target that belongs to neutral house. - If set to `false`, `EffectsRequireVerses` makes the Phobos-introduced warhead effects trigger even if it can't damage the target because of it's current ArmorType (e.g. 0% in `Verses`). diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 2e81cc12c3..7622232b88 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2826,6 +2826,12 @@ This function is only used as an additional scattering visual display, which is - You can now specify which targets or houses a weapon can fire at. This also affects weapon selection, other than certain special cases where the selection is fixed. - `CanTarget.MaxHealth` and `CanTarget.MinHealth` set health percentage thresholds for allowed targets (TechnoTypes only) that the target's health must be above and/or below/equal to, respectively. If target has zero health left this check is bypassed. + - `CanTarget.MinVeterancy` and `CanTarget.MaxVeterancy` define the allowed veterancy range for targets. The target's veterancy must be greater than or equal to `MinVeterancy` and less than or equal to `MaxVeterancy` in order to be considered a valid target. + - Veterancy values are interpreted as follows: + - `0.0` = Rookie + - `1.0` = Veteran + - `2.0` = Elite + - TechnoTypes with `Trainable=no` are always treated as having a veterancy level of `0.0` (Rookie) for the purpose of this check. In `rulesmd.ini`: ```ini @@ -2834,6 +2840,8 @@ CanTarget=all ; List of Affected Target Enumeration (none|land|water| CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute CanTarget.MinHealth=0.0 ; floating point value, percents or absolute +CanTarget.MaxVeterancy=2.0 ; floating point value, percents or absolute +CanTarget.MinVeterancy=0.0 ; floating point value, percents or absolute ``` ```{note} From 0e57ed60dd4414c68013285c24e9e8eeb7b911f5 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 21:44:07 +0800 Subject: [PATCH 07/22] Update Whats-New.md --- docs/Whats-New.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 6ad7bee8f6..76e71f6a2e 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -466,6 +466,8 @@ New: - [Customize type selection for IFV](Fixed-or-Improved-Logics.md#customize-type-selection-for-ifv) (by NetsuNegi) - CellSpread in cylinder shape (by TaranDahl) - CellSpread damage check if victim is in air or on floor (by TaranDahl) +- [Weapon target filtering by target veterancy](New-or-Enhanced-Logics.md#weapon-targeting-filter) (by Flactine) +- [Warhead effect filtering by target veterancy](Fixed-or-Improved-Logics.md#customizable-warhead-trigger-conditions) (by Flactine) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From 28cf3b2f5df42406391186616a33d3085df44b27 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 23:05:12 +0800 Subject: [PATCH 08/22] Use dynamic veterancy cap for target filtering Replaced hardcoded veterancy limits with RulesClass::Instance->VeteranCap in WarheadType and WeaponType target filtering logic. Updated documentation and credits to reflect the addition of veterancy-based target filtering. --- CREDITS.md | 4 +++- docs/Whats-New.md | 2 -- src/Ext/WarheadType/Body.h | 2 +- src/Ext/WeaponType/Body.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index ff4254c04c..3fa921cfd0 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -656,7 +656,9 @@ This page lists all the individual contributions to the project by their author. - **solar-III (凤九歌)** - Target scanning delay customization (documentation) - Skip target scanning function calling for unarmed technos (documentation) -- **Flactine** - add target filtering options to attacheffect system +- **Flactine** + - add target filtering options to attacheffect system + - add veterancy-based target filtering for weapons and warheads - **tyuah8**: - Drive/Jumpjet/Ship/Teleport locomotor did not power on when it is un-piggybacked bugfix - Destroyed unit leaves sensors bugfix diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 7b0d93c91e..b58b032aa0 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -465,8 +465,6 @@ New: - [Customize if cloning need power](Fixed-or-Improved-Logics.md#customize-if-cloning-need-power) (by NetsuNegi) - [Added Target Filtering Options to AttachEffect System](New-or-Enhanced-Logics.md#attached-effects) (by Flactine) - [Customize type selection for IFV](Fixed-or-Improved-Logics.md#customize-type-selection-for-ifv) (by NetsuNegi) -- CellSpread in cylinder shape (by TaranDahl) -- CellSpread damage check if victim is in air or on floor (by TaranDahl) - [Weapon target filtering by target veterancy](New-or-Enhanced-Logics.md#weapon-targeting-filter) (by Flactine) - [Warhead effect filtering by target veterancy](Fixed-or-Improved-Logics.md#customizable-warhead-trigger-conditions) (by Flactine) - [CellSpread in cylinder shape](New-or-Enhanced-Logics.md#cellspread-enhancement) (by TaranDahl) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 3894c24732..ccc29050a7 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -405,7 +405,7 @@ class WarheadTypeExt , AffectsBelowPercent { 1.0 } , AffectsAbovePercent { 0.0 } - , AffectsBelowVeterancy { 2.0f } + , AffectsBelowVeterancy { RulesClass::Instance->VeteranCap } , AffectsAboveVeterancy { 0.0f } , AffectsNeutral { true } , AffectsGround { true } diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index ea2bdb860e..cb584e1d27 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -116,7 +116,7 @@ class WeaponTypeExt , CanTargetHouses { AffectedHouse::All } , CanTarget_MaxHealth { 1.0 } , CanTarget_MinHealth { 0.0 } - , CanTarget_MaxVeterancy { 2.0f } + , CanTarget_MaxVeterancy { RulesClass::Instance->VeteranCap } , CanTarget_MinVeterancy { 0.0f } , Burst_Delays {} , Burst_FireWithinSequence { false } From 4a9d4663bb40925bc5014f204435b479e55471e8 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Wed, 17 Dec 2025 23:22:40 +0800 Subject: [PATCH 09/22] Cast VeteranCap to float in WarheadType and WeaponType Replaced direct assignment of RulesClass::Instance->VeteranCap with a static_cast to float in both WarheadType and WeaponType Body.h files to ensure type consistency. --- src/Ext/WarheadType/Body.h | 2 +- src/Ext/WeaponType/Body.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index ccc29050a7..08938bc45c 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -405,7 +405,7 @@ class WarheadTypeExt , AffectsBelowPercent { 1.0 } , AffectsAbovePercent { 0.0 } - , AffectsBelowVeterancy { RulesClass::Instance->VeteranCap } + , AffectsBelowVeterancy { static_cast(RulesClass::Instance->VeteranCap) } , AffectsAboveVeterancy { 0.0f } , AffectsNeutral { true } , AffectsGround { true } diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index cb584e1d27..070feb5c2f 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -116,7 +116,7 @@ class WeaponTypeExt , CanTargetHouses { AffectedHouse::All } , CanTarget_MaxHealth { 1.0 } , CanTarget_MinHealth { 0.0 } - , CanTarget_MaxVeterancy { RulesClass::Instance->VeteranCap } + , CanTarget_MaxVeterancy { static_cast(RulesClass::Instance->VeteranCap) } , CanTarget_MinVeterancy { 0.0f } , Burst_Delays {} , Burst_FireWithinSequence { false } From db04976bf4aaee47a3124f8a8239aaf586fb4d84 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 00:27:21 +0800 Subject: [PATCH 10/22] Switch veterancy fields to Nullable in WarheadType and WeaponType Replaced Valueable with Nullable for veterancy-related fields in WarheadType and WeaponType headers. This allows these fields to represent unset/null values, improving flexibility in configuration. --- src/Ext/WarheadType/Body.h | 6 +++--- src/Ext/WeaponType/Body.h | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 08938bc45c..3ae2443f19 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -192,8 +192,8 @@ class WarheadTypeExt Valueable AffectsBelowPercent; Valueable AffectsAbovePercent; - Valueable AffectsBelowVeterancy; - Valueable AffectsAboveVeterancy; + Nullable AffectsBelowVeterancy; + Nullable AffectsAboveVeterancy; Valueable AffectsNeutral; Valueable AffectsGround; Valueable AffectsAir; @@ -405,7 +405,7 @@ class WarheadTypeExt , AffectsBelowPercent { 1.0 } , AffectsAbovePercent { 0.0 } - , AffectsBelowVeterancy { static_cast(RulesClass::Instance->VeteranCap) } + , AffectsBelowVeterancy {} , AffectsAboveVeterancy { 0.0f } , AffectsNeutral { true } , AffectsGround { true } diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 070feb5c2f..7435ed4cda 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -42,8 +42,8 @@ class WeaponTypeExt Valueable CanTargetHouses; Valueable CanTarget_MaxHealth; Valueable CanTarget_MinHealth; - Valueable CanTarget_MaxVeterancy; - Valueable CanTarget_MinVeterancy; + Nullable CanTarget_MaxVeterancy; + Nullable CanTarget_MinVeterancy; ValueableVector Burst_Delays; Valueable Burst_FireWithinSequence; Valueable Burst_NoDelay; @@ -116,7 +116,7 @@ class WeaponTypeExt , CanTargetHouses { AffectedHouse::All } , CanTarget_MaxHealth { 1.0 } , CanTarget_MinHealth { 0.0 } - , CanTarget_MaxVeterancy { static_cast(RulesClass::Instance->VeteranCap) } + , CanTarget_MaxVeterancy {} , CanTarget_MinVeterancy { 0.0f } , Burst_Delays {} , Burst_FireWithinSequence { false } From 986d768cc118f7988f409f6eb59cd8d8f1924560 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 00:50:20 +0800 Subject: [PATCH 11/22] Refactor veterancy threshold handling for warheads and weapons Replaced Nullable with Valueable for AffectsAboveVeterancy and CanTarget_MinVeterancy to allow value resolution with VeteranCap. Updated related logic in WarheadTypeExt and WeaponTypeExt to use the new types and ensure correct threshold checks based on game rules. --- src/Ext/WarheadType/Body.cpp | 6 +++--- src/Ext/WarheadType/Body.h | 2 +- src/Ext/WeaponType/Body.cpp | 4 ++-- src/Ext/WeaponType/Body.h | 2 +- 4 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 733c5b88d7..7674601d3b 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -64,7 +64,7 @@ bool WarheadTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const if (!this->VeterancyCheck) return true; - return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy); + return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap))); } // Checks if Warhead can affect target that might or might be currently invulnerable. @@ -304,12 +304,12 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); this->HealthCheck = this->AffectsBelowPercent > 0.0 || this->AffectsAbovePercent < 1.0; - this->VeterancyCheck = this->AffectsBelowVeterancy > 0.0f || this->AffectsAboveVeterancy < 2.0f; + this->VeterancyCheck = this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) > 0.0f || this->AffectsAboveVeterancy < 2.0f; if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); - if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy) + if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap))) Debug::Log("[Developer warning][%s] AffectsAboveVeterancy is bigger than AffectsBelowVeterancy, the warhead will never activate!\n", pSection); this->ReverseEngineer.Read(exINI, pSection, "ReverseEngineer"); diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 3ae2443f19..f79593d182 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -193,7 +193,7 @@ class WarheadTypeExt Valueable AffectsBelowPercent; Valueable AffectsAbovePercent; Nullable AffectsBelowVeterancy; - Nullable AffectsAboveVeterancy; + Valueable AffectsAboveVeterancy; Valueable AffectsNeutral; Valueable AffectsGround; Valueable AffectsAir; diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 84bce54a15..b5a87e6a73 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -48,7 +48,7 @@ bool WeaponTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const bool WeaponTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const { - return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy); + return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap))); } void WeaponTypeExt::ExtData::Initialize() @@ -164,7 +164,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 - || this->CanTarget_MaxVeterancy < 2.0f || this->CanTarget_MinVeterancy > 0.0f + || this->CanTarget_MaxVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) < 2.0f || this->CanTarget_MinVeterancy > 0.0f || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) { diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 7435ed4cda..bc69469335 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -43,7 +43,7 @@ class WeaponTypeExt Valueable CanTarget_MaxHealth; Valueable CanTarget_MinHealth; Nullable CanTarget_MaxVeterancy; - Nullable CanTarget_MinVeterancy; + Valueable CanTarget_MinVeterancy; ValueableVector Burst_Delays; Valueable Burst_FireWithinSequence; Valueable Burst_NoDelay; From acd4885f1b5b4b1d1b4ee5412972387b013ff28c Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 07:57:10 +0800 Subject: [PATCH 12/22] Fix veterancy cap comparison logic in INI loading Replaced hardcoded veterancy cap values with dynamic checks using RulesClass::Instance->VeteranCap in WarheadTypeExt and WeaponTypeExt INI loading. This ensures correct behavior when the veterancy cap is changed from its default value. --- src/Ext/WarheadType/Body.cpp | 2 +- src/Ext/WeaponType/Body.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 7674601d3b..f198e2c9c8 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -304,7 +304,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); this->HealthCheck = this->AffectsBelowPercent > 0.0 || this->AffectsAbovePercent < 1.0; - this->VeterancyCheck = this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) > 0.0f || this->AffectsAboveVeterancy < 2.0f; + this->VeterancyCheck = this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) > 0.0f || this->AffectsAboveVeterancy < static_cast(RulesClass::Instance->VeteranCap); if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index b5a87e6a73..e4f0e608db 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -164,7 +164,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 - || this->CanTarget_MaxVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) < 2.0f || this->CanTarget_MinVeterancy > 0.0f + || this->CanTarget_MaxVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) < static_cast(RulesClass::Instance->VeteranCap) || this->CanTarget_MinVeterancy > 0.0f || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) { From 1a4e16086f81674d471ccfed9ca70cb82d982410 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 08:46:39 +0800 Subject: [PATCH 13/22] Docs --- docs/Fixed-or-Improved-Logics.md | 2 ++ docs/New-or-Enhanced-Logics.md | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index bb915a070c..6485f8d2c3 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2326,6 +2326,8 @@ In `rulesmd.ini`: [SOMEWARHEAD] ; WarheadType AffectsBelowPercent=1.0 ; floating point value, percents or absolute AffectsAbovePercent=0.0 ; floating point value, percents or absolute +AffectsBelowVeterancy=2.0 ; floating point value, percents or absolute, default to [General] -> VeteranCap +AffectsAboveVeterancy=0.0 ; floating point value, percents or absolute AffectsNeutral=true ; boolean EffectsRequireVerses=false ; boolean ``` diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index c12eca1046..c4667225c4 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2862,7 +2862,7 @@ CanTarget=all ; List of Affected Target Enumeration (none|land|water| CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute CanTarget.MinHealth=0.0 ; floating point value, percents or absolute -CanTarget.MaxVeterancy=2.0 ; floating point value, percents or absolute +CanTarget.MaxVeterancy=2.0 ; floating point value, percents or absolute, default to [General] -> VeteranCap CanTarget.MinVeterancy=0.0 ; floating point value, percents or absolute ``` From ddd6d06951d418779b87a855a8ec688c4d548cbe Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 11:01:40 +0800 Subject: [PATCH 14/22] Refactor veterancy threshold types to double Changed veterancy-related variables and function parameters from float to double in WarheadType and WeaponType extensions for improved precision and consistency. Updated related method calls and initializations accordingly. --- src/Ext/Techno/Body.cpp | 4 ++-- src/Ext/WarheadType/Body.cpp | 6 +++--- src/Ext/WarheadType/Body.h | 6 +++--- src/Ext/WeaponType/Body.cpp | 4 ++-- src/Ext/WeaponType/Body.h | 6 +++--- 5 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 94395cd6d6..116c60e66d 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -743,9 +743,9 @@ bool TechnoExt::IsHealthInThreshold(TechnoClass* pObject, double min, double max return (hp > 0 ? hp > min : hp >= min) && hp <= max; } -bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pTechno, float min, float max) +bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pTechno, double min, double max) { - float veterancy = 0.0f; + double veterancy = 0.0; if (pTechno->GetTechnoType()->Trainable) veterancy = pTechno->Veterancy.Veterancy; diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index f198e2c9c8..86cbffd5e9 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -64,7 +64,7 @@ bool WarheadTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const if (!this->VeterancyCheck) return true; - return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap))); + return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap)); } // Checks if Warhead can affect target that might or might be currently invulnerable. @@ -304,12 +304,12 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); this->HealthCheck = this->AffectsBelowPercent > 0.0 || this->AffectsAbovePercent < 1.0; - this->VeterancyCheck = this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) > 0.0f || this->AffectsAboveVeterancy < static_cast(RulesClass::Instance->VeteranCap); + this->VeterancyCheck = this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap) > 0.0 || this->AffectsAboveVeterancy < RulesClass::Instance->VeteranCap; if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); - if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap))) + if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap)) Debug::Log("[Developer warning][%s] AffectsAboveVeterancy is bigger than AffectsBelowVeterancy, the warhead will never activate!\n", pSection); this->ReverseEngineer.Read(exINI, pSection, "ReverseEngineer"); diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index f79593d182..2ed0779dd2 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -192,8 +192,8 @@ class WarheadTypeExt Valueable AffectsBelowPercent; Valueable AffectsAbovePercent; - Nullable AffectsBelowVeterancy; - Valueable AffectsAboveVeterancy; + Nullable AffectsBelowVeterancy; + Valueable AffectsAboveVeterancy; Valueable AffectsNeutral; Valueable AffectsGround; Valueable AffectsAir; @@ -406,7 +406,7 @@ class WarheadTypeExt , AffectsBelowPercent { 1.0 } , AffectsAbovePercent { 0.0 } , AffectsBelowVeterancy {} - , AffectsAboveVeterancy { 0.0f } + , AffectsAboveVeterancy { 0.0 } , AffectsNeutral { true } , AffectsGround { true } , AffectsAir { true } diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index e4f0e608db..9f4fc18617 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -48,7 +48,7 @@ bool WeaponTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const bool WeaponTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const { - return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap))); + return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy.Get(RulesClass::Instance->VeteranCap)); } void WeaponTypeExt::ExtData::Initialize() @@ -164,7 +164,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 - || this->CanTarget_MaxVeterancy.Get(static_cast(RulesClass::Instance->VeteranCap)) < static_cast(RulesClass::Instance->VeteranCap) || this->CanTarget_MinVeterancy > 0.0f + || this->CanTarget_MaxVeterancy.Get(RulesClass::Instance->VeteranCap) < RulesClass::Instance->VeteranCap || this->CanTarget_MinVeterancy > 0.0 || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) { diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index bc69469335..54cc74fdbb 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -42,8 +42,8 @@ class WeaponTypeExt Valueable CanTargetHouses; Valueable CanTarget_MaxHealth; Valueable CanTarget_MinHealth; - Nullable CanTarget_MaxVeterancy; - Valueable CanTarget_MinVeterancy; + Nullable CanTarget_MaxVeterancy; + Valueable CanTarget_MinVeterancy; ValueableVector Burst_Delays; Valueable Burst_FireWithinSequence; Valueable Burst_NoDelay; @@ -117,7 +117,7 @@ class WeaponTypeExt , CanTarget_MaxHealth { 1.0 } , CanTarget_MinHealth { 0.0 } , CanTarget_MaxVeterancy {} - , CanTarget_MinVeterancy { 0.0f } + , CanTarget_MinVeterancy { 0.0 } , Burst_Delays {} , Burst_FireWithinSequence { false } , Burst_NoDelay { false } From 084f72075b12297f896d5d12601e977d149c8517 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 11:11:04 +0800 Subject: [PATCH 15/22] Fix logic and type for veterancy and health checks Corrected the parameter types for IsVeterancyInThreshold to use double instead of float. Fixed the logic for HealthCheck and VeterancyCheck assignments in WarheadTypeExt::ExtData::LoadFromINIFile to properly reflect the intended threshold checks. --- src/Ext/Techno/Body.h | 2 +- src/Ext/WarheadType/Body.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 624c2276df..26a94bb08b 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -289,7 +289,7 @@ class TechnoExt static void CreateDelayedFireAnim(TechnoClass* pThis, AnimTypeClass* pAnimType, int weaponIndex, bool attach, bool center, bool removeOnNoDelay, bool onTurret, CoordStruct firingCoords); static bool HandleDelayedFireWithPauseSequence(TechnoClass* pThis, WeaponTypeClass* pWeapon, int weaponIndex, int frame, int firingFrame); static bool IsHealthInThreshold(TechnoClass* pObject, double min, double max); - static bool IsVeterancyInThreshold(TechnoClass* pObject, float min, float max); + static bool IsVeterancyInThreshold(TechnoClass* pObject, double min, double max); static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt::ExtData* pData); static AircraftTypeClass* GetAircraftTypeExtra(AircraftClass* pAircraft); static bool CannotMove(UnitClass* pThis); diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 86cbffd5e9..4112852ec0 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -303,8 +303,8 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsGround.Read(exINI, pSection, "AffectsGround"); this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); - this->HealthCheck = this->AffectsBelowPercent > 0.0 || this->AffectsAbovePercent < 1.0; - this->VeterancyCheck = this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap) > 0.0 || this->AffectsAboveVeterancy < RulesClass::Instance->VeteranCap; + this->HealthCheck = this->AffectsAbovePercent > 0.0 || this->AffectsBelowPercent < 1.0; + this->VeterancyCheck = this->AffectsAboveVeterancy > 0.0 || this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap) < RulesClass::Instance->VeteranCap; if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); From 9eb972d9e4b29867f42ed0759c36875c5f4ab021 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 13:08:25 +0800 Subject: [PATCH 16/22] Clarify data types for Veterancy fields in docs Updated documentation to specify that AffectsBelowVeterancy, AffectsAboveVeterancy, CanTarget.MaxVeterancy, and CanTarget.MinVeterancy are of type double, improving clarity for users configuring these values. --- docs/Fixed-or-Improved-Logics.md | 4 ++-- docs/New-or-Enhanced-Logics.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 6485f8d2c3..c9a9326ae6 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2326,8 +2326,8 @@ In `rulesmd.ini`: [SOMEWARHEAD] ; WarheadType AffectsBelowPercent=1.0 ; floating point value, percents or absolute AffectsAbovePercent=0.0 ; floating point value, percents or absolute -AffectsBelowVeterancy=2.0 ; floating point value, percents or absolute, default to [General] -> VeteranCap -AffectsAboveVeterancy=0.0 ; floating point value, percents or absolute +AffectsBelowVeterancy=2.0 ; double, default to [General] -> VeteranCap +AffectsAboveVeterancy=0.0 ; double AffectsNeutral=true ; boolean EffectsRequireVerses=false ; boolean ``` diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index c4667225c4..0537f1efa6 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2862,8 +2862,8 @@ CanTarget=all ; List of Affected Target Enumeration (none|land|water| CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute CanTarget.MinHealth=0.0 ; floating point value, percents or absolute -CanTarget.MaxVeterancy=2.0 ; floating point value, percents or absolute, default to [General] -> VeteranCap -CanTarget.MinVeterancy=0.0 ; floating point value, percents or absolute +CanTarget.MaxVeterancy=2.0 ; double, default to [General] -> VeteranCap +CanTarget.MinVeterancy=0.0 ; double ``` ```{note} From f814cad47fd1e043059ffa01f4abdd1076bdbcc8 Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 13:10:19 +0800 Subject: [PATCH 17/22] Clarify veterancy parameter documentation Updated documentation for AffectsBelowVeterancy and CanTarget.MaxVeterancy to clarify that these parameters accept floating point values, percents, or absolute values, and to indicate their default behavior. This improves accuracy and consistency in the docs. --- docs/Fixed-or-Improved-Logics.md | 4 ++-- docs/New-or-Enhanced-Logics.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index c9a9326ae6..d54af9b007 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2326,8 +2326,8 @@ In `rulesmd.ini`: [SOMEWARHEAD] ; WarheadType AffectsBelowPercent=1.0 ; floating point value, percents or absolute AffectsAbovePercent=0.0 ; floating point value, percents or absolute -AffectsBelowVeterancy=2.0 ; double, default to [General] -> VeteranCap -AffectsAboveVeterancy=0.0 ; double +AffectsBelowVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap +AffectsAboveVeterancy=0.0 ; floating point value, percents or absolute AffectsNeutral=true ; boolean EffectsRequireVerses=false ; boolean ``` diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 0537f1efa6..c443b1c582 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2862,8 +2862,8 @@ CanTarget=all ; List of Affected Target Enumeration (none|land|water| CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute CanTarget.MinHealth=0.0 ; floating point value, percents or absolute -CanTarget.MaxVeterancy=2.0 ; double, default to [General] -> VeteranCap -CanTarget.MinVeterancy=0.0 ; double +CanTarget.MaxVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap +CanTarget.MinVeterancy=0.0 ; floating point value, percents or absolute ``` ```{note} From 176f860868a6075f309296a74e0167ada3b9afde Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 13:39:16 +0800 Subject: [PATCH 18/22] Fix alignment and spacing in logic documentation Corrected indentation and spacing for configuration examples in Fixed-or-Improved-Logics.md and New-or-Enhanced-Logics.md to improve readability and consistency. --- docs/Fixed-or-Improved-Logics.md | 4 ++-- docs/New-or-Enhanced-Logics.md | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index d54af9b007..1134720328 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2326,8 +2326,8 @@ In `rulesmd.ini`: [SOMEWARHEAD] ; WarheadType AffectsBelowPercent=1.0 ; floating point value, percents or absolute AffectsAbovePercent=0.0 ; floating point value, percents or absolute -AffectsBelowVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap -AffectsAboveVeterancy=0.0 ; floating point value, percents or absolute +AffectsBelowVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap +AffectsAboveVeterancy=0.0 ; floating point value, percents or absolute AffectsNeutral=true ; boolean EffectsRequireVerses=false ; boolean ``` diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index c443b1c582..1a16b09ce1 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2857,12 +2857,12 @@ This function is only used as an additional scattering visual display, which is In `rulesmd.ini`: ```ini -[SOMEWEAPON] ; WeaponType -CanTarget=all ; List of Affected Target Enumeration (none|land|water|empty|infantry|units|buildings|all) -CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) -CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute -CanTarget.MinHealth=0.0 ; floating point value, percents or absolute -CanTarget.MaxVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap +[SOMEWEAPON] ; WeaponType +CanTarget=all ; List of Affected Target Enumeration (none|land|water|empty|infantry|units|buildings|all) +CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) +CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute +CanTarget.MinHealth=0.0 ; floating point value, percents or absolute +CanTarget.MaxVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap CanTarget.MinVeterancy=0.0 ; floating point value, percents or absolute ``` From aaa1f96f6e391352c2af7778b2d519fe91795f4d Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 23:47:35 +0800 Subject: [PATCH 19/22] Update src/Ext/Techno/Body.cpp Co-authored-by: Kerbiter --- src/Ext/Techno/Body.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 116c60e66d..23b9b41e33 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -750,7 +750,7 @@ bool TechnoExt::IsVeterancyInThreshold(TechnoClass* pTechno, double min, double if (pTechno->GetTechnoType()->Trainable) veterancy = pTechno->Veterancy.Veterancy; - return veterancy >= min && veterancy <= max; + return veterancy >= min && veterancy < max; } bool TechnoExt::CannotMove(UnitClass* pThis) From 0d47524ddd35a066b7718dd4ddd32a27174eabac Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 23:50:54 +0800 Subject: [PATCH 20/22] Update CREDITS.md --- CREDITS.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 3fa921cfd0..ab2f66aeb9 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -657,8 +657,8 @@ This page lists all the individual contributions to the project by their author. - Target scanning delay customization (documentation) - Skip target scanning function calling for unarmed technos (documentation) - **Flactine** - - add target filtering options to attacheffect system - - add veterancy-based target filtering for weapons and warheads + - Add target filtering options to attacheffect system + - Add veterancy-based target filtering for weapons and warheads - **tyuah8**: - Drive/Jumpjet/Ship/Teleport locomotor did not power on when it is un-piggybacked bugfix - Destroyed unit leaves sensors bugfix From a050f5162da9cb9c54db8ce2da0f3399304357db Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Thu, 18 Dec 2025 23:53:37 +0800 Subject: [PATCH 21/22] Update Whats-New.md --- docs/Whats-New.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index b58b032aa0..113e0c5939 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -465,12 +465,12 @@ New: - [Customize if cloning need power](Fixed-or-Improved-Logics.md#customize-if-cloning-need-power) (by NetsuNegi) - [Added Target Filtering Options to AttachEffect System](New-or-Enhanced-Logics.md#attached-effects) (by Flactine) - [Customize type selection for IFV](Fixed-or-Improved-Logics.md#customize-type-selection-for-ifv) (by NetsuNegi) -- [Weapon target filtering by target veterancy](New-or-Enhanced-Logics.md#weapon-targeting-filter) (by Flactine) -- [Warhead effect filtering by target veterancy](Fixed-or-Improved-Logics.md#customizable-warhead-trigger-conditions) (by Flactine) - [CellSpread in cylinder shape](New-or-Enhanced-Logics.md#cellspread-enhancement) (by TaranDahl) - [CellSpread damage check if victim is in air or on floor](New-or-Enhanced-Logics.md#cellspread-enhancement) (by TaranDahl) - OpenTopped range bonus and damage multiplier customization for passengers (by Ollerus) - AutoDeath upon ownership change (by Ollerus) +- [Weapon target filtering by target veterancy](New-or-Enhanced-Logics.md#weapon-targeting-filter) (by Flactine) +- [Warhead effect filtering by target veterancy](Fixed-or-Improved-Logics.md#customizable-warhead-trigger-conditions) (by Flactine) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From 373140771a60e8653b8d434987e1be6124c3d38f Mon Sep 17 00:00:00 2001 From: Flactine <1716455702@qq.com> Date: Fri, 19 Dec 2025 00:22:07 +0800 Subject: [PATCH 22/22] Adjust veterancy threshold logic for warheads and weapons Updated the default upper bound for veterancy checks to use VeteranCap + 1.0 instead of VeteranCap, ensuring that the threshold is exclusive and matches documentation. Updated related documentation and developer warnings to reflect this change. --- docs/Fixed-or-Improved-Logics.md | 4 ++-- docs/New-or-Enhanced-Logics.md | 4 ++-- src/Ext/WarheadType/Body.cpp | 6 +++--- src/Ext/WeaponType/Body.cpp | 4 ++-- 4 files changed, 9 insertions(+), 9 deletions(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 1134720328..d158cd4d73 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -2312,7 +2312,7 @@ Conventional.IgnoreUnits=false ; boolean ### Customizable Warhead trigger conditions - `AffectsBelowPercent` and `AffectsAbovePercent` can be used to set the health percentage thresholds that target needs to be below/equal and/or above of for the Warhead to detonate. If target has zero health left this check is bypassed. -- `AffectsAboveVeterancy` and `AffectsBelowVeterancy` can be used to set the veterancy thresholds that target needs to be below/equal and/or above of for the Warhead to detonate. +- `AffectsAboveVeterancy` and `AffectsBelowVeterancy` can be used to set veterancy thresholds for Warhead activation. The target's veterancy must be greater than or equal to `AffectsAboveVeterancy`, and strictly less than `AffectsBelowVeterancy`. - Veterancy values are interpreted as follows: - `0.0` = Rookie - `1.0` = Veteran @@ -2326,7 +2326,7 @@ In `rulesmd.ini`: [SOMEWARHEAD] ; WarheadType AffectsBelowPercent=1.0 ; floating point value, percents or absolute AffectsAbovePercent=0.0 ; floating point value, percents or absolute -AffectsBelowVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap +AffectsBelowVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap + 1.0 AffectsAboveVeterancy=0.0 ; floating point value, percents or absolute AffectsNeutral=true ; boolean EffectsRequireVerses=false ; boolean diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 1a16b09ce1..554da38bbf 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2848,7 +2848,7 @@ This function is only used as an additional scattering visual display, which is - You can now specify which targets or houses a weapon can fire at. This also affects weapon selection, other than certain special cases where the selection is fixed. - `CanTarget.MaxHealth` and `CanTarget.MinHealth` set health percentage thresholds for allowed targets (TechnoTypes only) that the target's health must be above and/or below/equal to, respectively. If target has zero health left this check is bypassed. - - `CanTarget.MinVeterancy` and `CanTarget.MaxVeterancy` define the allowed veterancy range for targets. The target's veterancy must be greater than or equal to `MinVeterancy` and less than or equal to `MaxVeterancy` in order to be considered a valid target. + - `CanTarget.MinVeterancy` and `CanTarget.MaxVeterancy` define the allowed veterancy range for targets. The target's veterancy must be greater than or equal to `MinVeterancy` and less than to `MaxVeterancy` in order to be considered a valid target. - Veterancy values are interpreted as follows: - `0.0` = Rookie - `1.0` = Veteran @@ -2862,7 +2862,7 @@ CanTarget=all ; List of Affected Target Enumeration (none|land|wat CanTargetHouses=all ; List of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) CanTarget.MaxHealth=1.0 ; floating point value, percents or absolute CanTarget.MinHealth=0.0 ; floating point value, percents or absolute -CanTarget.MaxVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap +CanTarget.MaxVeterancy= ; floating point value, percents or absolute, default to [General] -> VeteranCap + 1.0 CanTarget.MinVeterancy=0.0 ; floating point value, percents or absolute ``` diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 4112852ec0..6a580d4fb6 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -64,7 +64,7 @@ bool WarheadTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const if (!this->VeterancyCheck) return true; - return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap)); + return TechnoExt::IsVeterancyInThreshold(pTarget, this->AffectsAboveVeterancy, this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap + 1.0)); } // Checks if Warhead can affect target that might or might be currently invulnerable. @@ -304,12 +304,12 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AffectsAir.Read(exINI, pSection, "AffectsAir"); this->CellSpread_Cylinder.Read(exINI, pSection, "CellSpread.Cylinder"); this->HealthCheck = this->AffectsAbovePercent > 0.0 || this->AffectsBelowPercent < 1.0; - this->VeterancyCheck = this->AffectsAboveVeterancy > 0.0 || this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap) < RulesClass::Instance->VeteranCap; + this->VeterancyCheck = this->AffectsAboveVeterancy > 0.0 || this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap + 1.0) < RulesClass::Instance->VeteranCap + 1.0; if (this->AffectsAbovePercent > this->AffectsBelowPercent) Debug::Log("[Developer warning][%s] AffectsAbovePercent is bigger than AffectsBelowPercent, the warhead will never activate!\n", pSection); - if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap)) + if (this->AffectsAboveVeterancy > this->AffectsBelowVeterancy.Get(RulesClass::Instance->VeteranCap + 1.0)) Debug::Log("[Developer warning][%s] AffectsAboveVeterancy is bigger than AffectsBelowVeterancy, the warhead will never activate!\n", pSection); this->ReverseEngineer.Read(exINI, pSection, "ReverseEngineer"); diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 9f4fc18617..1675edfcad 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -48,7 +48,7 @@ bool WeaponTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const bool WeaponTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const { - return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy.Get(RulesClass::Instance->VeteranCap)); + return TechnoExt::IsVeterancyInThreshold(pTarget, this->CanTarget_MinVeterancy, this->CanTarget_MaxVeterancy.Get(RulesClass::Instance->VeteranCap + 1.0)); } void WeaponTypeExt::ExtData::Initialize() @@ -164,7 +164,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All || this->CanTarget_MaxHealth < 1.0 || this->CanTarget_MinHealth > 0.0 - || this->CanTarget_MaxVeterancy.Get(RulesClass::Instance->VeteranCap) < RulesClass::Instance->VeteranCap || this->CanTarget_MinVeterancy > 0.0 + || this->CanTarget_MaxVeterancy.Get(RulesClass::Instance->VeteranCap + 1.0) < RulesClass::Instance->VeteranCap + 1.0 || this->CanTarget_MinVeterancy > 0.0 || this->AttachEffect_RequiredTypes.size() || this->AttachEffect_RequiredGroups.size() || this->AttachEffect_DisallowedTypes.size() || this->AttachEffect_DisallowedGroups.size()) {