From f88b3782435ad4d2c71fd10d0b44acf32670542b Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Tue, 26 May 2026 21:51:49 +0800 Subject: [PATCH 1/6] Range.Maximum/Minimum --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 3 +++ docs/Whats-New.md | 1 + src/Ext/Bullet/Body.h | 2 +- src/Ext/WeaponType/Body.cpp | 33 +++++++++++++++++++++++++++++++-- src/Ext/WeaponType/Body.h | 8 +++++--- 6 files changed, 42 insertions(+), 6 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 809c481da3..46e7d0600b 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -668,6 +668,7 @@ This page lists all the individual contributions to the project by their author. - Return warhead - `ElectricAssault` weapons can now auto acquire allies' overpowerable defenses - Allow `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades + - Weapon maximum/minimum `Range` - **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes - **handama**: - AI script action to `16005 Jump Back To Previous Script` diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index ab4bf22e77..2a76373ca2 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -47,6 +47,7 @@ This page describes all the engine features that are either new and introduced b - `WeaponRange.Multiplier` and `WeaponRange.ExtraRange` can be used to multiply the weapon firing range of the object the effect is attached to, or give it an increase / decrease (measured in cells), respectively. `ExtraRange` is cumulatively applied from all attached effects after all `Multiplier` values have been applied. - `WeaponRange.AllowWeapons` can be used to list only weapons that can benefit from this range bonus and `WeaponRange.DisallowWeapons` weapons that are not allowed to, respectively. - On TechnoTypes with `OpenTopped=true`, `OpenTopped.UseTransportRangeModifiers` can be set to true to make passengers firing out use the transport's active range bonuses instead. + - The weapon range with all sort of range modifiers can't be greater than `Range.Maximum` or smaller than `Range.Minimum` of the affected weapon. Notice that `Range=-512` is a special case and will be ignored. - `Crit.Multiplier` and `Crit.ExtraChance` can be used to multiply the [critical hit](#chance-based-extra-damage-or-warhead-detonation--critical-hits) chance or grant a fixed bonus to it for the object the effect is attached to, respectively. - `Crit.AllowWarheads` can be used to list only Warheads that can benefit from this critical hit chance multiplier and `Crit.DisallowWarheads` weapons that are not allowed to, respectively. - `RevengeWeapon` can be used to temporarily grant the specified weapon as a [revenge weapon](#revenge-weapon) for the attached object. @@ -172,6 +173,8 @@ AttachEffect.DisallowedMinCounts= ; integer - minimum disallowe AttachEffect.DisallowedMaxCounts= ; integer - maximum disallowed instance count (comma-separated) for cumulative types in order from first to last. AttachEffect.IgnoreFromSameSource=false ; boolean AttachEffect.CheckOnFirer=false ; boolean +Range.Maximum=2147483647 ; integer +Range.Minimum=0 ; integer [SOMEWARHEAD] ; WarheadType AttachEffect.AttachTypes= ; List of AttachEffectTypes diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 9708361b86..2bba0e5332 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -730,6 +730,7 @@ HideShakeEffects=false ; boolean - `DisableWeapons.Duration` now makes `Gattling=yes` rate tick down and stops the sounds from playing, no longer interferes with target acquisition and works together with Phobos' `OpenTopped.CheckTransportDisableWeapons` (by Starkku) - Allowed `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades (by Ollerus & NetsuNegi) - [Aux technos and TechLevel requirement of superweapon](New-or-Enhanced-Logics.md#aux-technos-and-techlevel-requirement-of-superweapon) (by NetsuNegi & Ollerus) +- Weapon maximum/minimum `Range` (by Ollerus) ``` ### 0.4.0.3 diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index 73d7945da0..7b458e51b2 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -72,7 +72,7 @@ class BulletExt static void Detonate(const CoordStruct& coords, TechnoClass* pOwner, int damage, HouseClass* pFiringHouse, AbstractClass* pTarget, bool isBright, WeaponTypeClass* pWeapon, WarheadTypeClass* pWarhead); static void ApplyArcingFix(BulletClass* pThis, const CoordStruct& sourceCoords, const CoordStruct& targetCoords, BulletVelocity& velocity); - static inline CoordStruct GetTargetCoordsForFiring(BulletClass* Bullet); + static inline CoordStruct GetTargetCoordsForFiring(BulletClass* pBullet); static void SimulatedFiringUnlimbo(BulletClass* pBullet, HouseClass* pHouse, WeaponTypeClass* pWeapon, const CoordStruct& sourceCoords, bool randomVelocity); static void SimulatedFiringEffects(BulletClass* pBullet, HouseClass* pHouse, ObjectClass* pAttach, bool firingEffect, bool visualEffect); diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index f98678cfdc..ae701aed0c 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -172,6 +172,25 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AttackNoThreatBuildings.Read(exINI, pSection, "AttackNoThreatBuildings"); this->CylinderRangefinding.Read(exINI, pSection, "CylinderRangefinding"); this->Anim_Update.Read(exINI, pSection, "Anim.Update"); + this->Range_Maximum.Read(exINI, pSection, "Range.Maximum"); + this->Range_Minimum.Read(exINI, pSection, "Range.Minimum"); + + if (this->Range_Maximum < pThis->Range) + { + Debug::Log("[Developer warning][%s] Range.Maximum is smaller than Range, it'll be reset to the value of Range!\n", pSection); + this->Range_Maximum = pThis->Range; + } + + if (this->Range_Minimum < 0) + { + Debug::Log("[Developer warning][%s] Range.Minimum is smaller than 0, it'll be reset to 0!\n", pSection); + this->Range_Minimum = 0; + } + else if (this->Range_Minimum > pThis->Range) + { + Debug::Log("[Developer warning][%s] Range.Minimum is bigger than Range, it'll be reset to the value of Range!\n", pSection); + this->Range_Minimum = pThis->Range; + } // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All @@ -272,6 +291,8 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->AttackNoThreatBuildings) .Process(this->CylinderRangefinding) .Process(this->Anim_Update) + .Process(this->Range_Maximum) + .Process(this->Range_Minimum) ; }; @@ -353,10 +374,18 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF pTechno = pTransport; } + int min = 0, max = INT_MAX; + + if (auto const pExt = WeaponTypeExt::ExtMap.Find(pThis)) + { + min = pExt->Range_Minimum; + max = pExt->Range_Maximum; + } + auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); if (!pTechnoExt->AE.HasRangeModifier) - return range; + return Math::clamp(range, min, max); double extraRange = 0.0; @@ -382,7 +411,7 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF range += static_cast(extraRange * Unsorted::LeptonsPerCell); - return Math::max(range, 0); + return Math::clamp(range, min, max); } int WeaponTypeExt::GetTechnoKeepRange(WeaponTypeClass* pThis, TechnoClass* pFirer, bool isMinimum) diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 5bb8734a4b..9317550f5b 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -98,12 +98,12 @@ class WeaponTypeExt Nullable AttackFriendlies; Nullable AttackCursorOnFriendlies; Nullable AttackNoThreatBuildings; - + Nullable CylinderRangefinding; Nullable Anim_Update; + Valueable Range_Maximum; + Valueable Range_Minimum; bool SkipWeaponPicking; - - Nullable CylinderRangefinding; ExtData(WeaponTypeClass* OwnerObject) : Extension(OwnerObject) , DiskLaser_Radius { DiskLaserClass::Radius } @@ -190,6 +190,8 @@ class WeaponTypeExt , AttackNoThreatBuildings {} , CylinderRangefinding {} , Anim_Update {} + , Range_Maximum { INT_MAX } + , Range_Minimum { 0 } { } int GetBurstDelay(int burstIndex) const; From 03ed5b370ccfd456225a7bb15a62d34adb560242 Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Mon, 1 Jun 2026 16:19:26 +0800 Subject: [PATCH 2/6] rename --- docs/New-or-Enhanced-Logics.md | 6 +++--- src/Ext/WeaponType/Body.cpp | 30 +++++++++++++++--------------- src/Ext/WeaponType/Body.h | 8 ++++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 2a76373ca2..0bac132966 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -47,7 +47,7 @@ This page describes all the engine features that are either new and introduced b - `WeaponRange.Multiplier` and `WeaponRange.ExtraRange` can be used to multiply the weapon firing range of the object the effect is attached to, or give it an increase / decrease (measured in cells), respectively. `ExtraRange` is cumulatively applied from all attached effects after all `Multiplier` values have been applied. - `WeaponRange.AllowWeapons` can be used to list only weapons that can benefit from this range bonus and `WeaponRange.DisallowWeapons` weapons that are not allowed to, respectively. - On TechnoTypes with `OpenTopped=true`, `OpenTopped.UseTransportRangeModifiers` can be set to true to make passengers firing out use the transport's active range bonuses instead. - - The weapon range with all sort of range modifiers can't be greater than `Range.Maximum` or smaller than `Range.Minimum` of the affected weapon. Notice that `Range=-512` is a special case and will be ignored. + - The weapon range with all sort of range modifiers can't be greater than `WeaponRange.Maximum` or smaller than `WeaponRange.Minimum` of the affected weapon. Notice that `Range=-512` is a special case and will be ignored. - `Crit.Multiplier` and `Crit.ExtraChance` can be used to multiply the [critical hit](#chance-based-extra-damage-or-warhead-detonation--critical-hits) chance or grant a fixed bonus to it for the object the effect is attached to, respectively. - `Crit.AllowWarheads` can be used to list only Warheads that can benefit from this critical hit chance multiplier and `Crit.DisallowWarheads` weapons that are not allowed to, respectively. - `RevengeWeapon` can be used to temporarily grant the specified weapon as a [revenge weapon](#revenge-weapon) for the attached object. @@ -173,8 +173,8 @@ AttachEffect.DisallowedMinCounts= ; integer - minimum disallowe AttachEffect.DisallowedMaxCounts= ; integer - maximum disallowed instance count (comma-separated) for cumulative types in order from first to last. AttachEffect.IgnoreFromSameSource=false ; boolean AttachEffect.CheckOnFirer=false ; boolean -Range.Maximum=2147483647 ; integer -Range.Minimum=0 ; integer +WeaponRange.Maximum=2147483647 ; integer +WeaponRange.Minimum=0 ; integer [SOMEWARHEAD] ; WarheadType AttachEffect.AttachTypes= ; List of AttachEffectTypes diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index ae701aed0c..d9b1ac466f 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -172,24 +172,24 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AttackNoThreatBuildings.Read(exINI, pSection, "AttackNoThreatBuildings"); this->CylinderRangefinding.Read(exINI, pSection, "CylinderRangefinding"); this->Anim_Update.Read(exINI, pSection, "Anim.Update"); - this->Range_Maximum.Read(exINI, pSection, "Range.Maximum"); - this->Range_Minimum.Read(exINI, pSection, "Range.Minimum"); + this->WeaponRange_Maximum.Read(exINI, pSection, "WeaponRange.Maximum"); + this->WeaponRange_Minimum.Read(exINI, pSection, "WeaponRange.Minimum"); - if (this->Range_Maximum < pThis->Range) + if (this->WeaponRange_Maximum < pThis->Range) { - Debug::Log("[Developer warning][%s] Range.Maximum is smaller than Range, it'll be reset to the value of Range!\n", pSection); - this->Range_Maximum = pThis->Range; + Debug::Log("[Developer warning][%s] WeaponRange.Maximum is smaller than Range, it'll be reset to the value of Range!\n", pSection); + this->WeaponRange_Maximum = pThis->Range; } - if (this->Range_Minimum < 0) + if (this->WeaponRange_Minimum < 0) { - Debug::Log("[Developer warning][%s] Range.Minimum is smaller than 0, it'll be reset to 0!\n", pSection); - this->Range_Minimum = 0; + Debug::Log("[Developer warning][%s] WeaponRange.Minimum is smaller than 0, it'll be reset to 0!\n", pSection); + this->WeaponRange_Minimum = 0; } - else if (this->Range_Minimum > pThis->Range) + else if (this->WeaponRange_Minimum > pThis->Range) { - Debug::Log("[Developer warning][%s] Range.Minimum is bigger than Range, it'll be reset to the value of Range!\n", pSection); - this->Range_Minimum = pThis->Range; + Debug::Log("[Developer warning][%s] WeaponRange.Minimum is bigger than Range, it'll be reset to the value of Range!\n", pSection); + this->WeaponRange_Minimum = pThis->Range; } // handle SkipWeaponPicking @@ -291,8 +291,8 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->AttackNoThreatBuildings) .Process(this->CylinderRangefinding) .Process(this->Anim_Update) - .Process(this->Range_Maximum) - .Process(this->Range_Minimum) + .Process(this->WeaponRange_Maximum) + .Process(this->WeaponRange_Minimum) ; }; @@ -378,8 +378,8 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF if (auto const pExt = WeaponTypeExt::ExtMap.Find(pThis)) { - min = pExt->Range_Minimum; - max = pExt->Range_Maximum; + min = pExt->WeaponRange_Minimum; + max = pExt->WeaponRange_Maximum; } auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 9317550f5b..fa07fe9b62 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -100,8 +100,8 @@ class WeaponTypeExt Nullable AttackNoThreatBuildings; Nullable CylinderRangefinding; Nullable Anim_Update; - Valueable Range_Maximum; - Valueable Range_Minimum; + Valueable WeaponRange_Maximum; + Valueable WeaponRange_Minimum; bool SkipWeaponPicking; @@ -190,8 +190,8 @@ class WeaponTypeExt , AttackNoThreatBuildings {} , CylinderRangefinding {} , Anim_Update {} - , Range_Maximum { INT_MAX } - , Range_Minimum { 0 } + , WeaponRange_Maximum { INT_MAX } + , WeaponRange_Minimum { 0 } { } int GetBurstDelay(int burstIndex) const; From aa4e5dba402e04580de733e0de714703000c91ca Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Wed, 3 Jun 2026 21:22:58 +0800 Subject: [PATCH 3/6] reduce AE stat calcution - for AE attaching, it'll only calculate the attaching effect instead of all effects --- src/Ext/Techno/Body.Update.cpp | 31 ++++++++++++++++++++++++++-- src/Ext/Techno/Body.h | 2 +- src/New/Entity/AttachEffectClass.cpp | 28 +++++++++++++++++-------- 3 files changed, 49 insertions(+), 12 deletions(-) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 185b608762..ebbbeaee10 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -2080,12 +2080,39 @@ void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pA } // Recalculates AttachEffect stat multipliers and other bonuses. -void TechnoExt::ExtData::RecalculateStatMultipliers() +void TechnoExt::ExtData::RecalculateStatMultipliers(AttachEffectClass* pAttachEffect) { auto const pThis = this->OwnerObject(); auto& pAE = this->AE; - const bool wasTint = pAE.HasTint; + if (pAttachEffect) + { + auto const type = pAttachEffect->GetType(); + pAE.FirepowerMultiplier *= type->FirepowerMultiplier; + pAE.SpeedMultiplier *= type->SpeedMultiplier; + pAE.ROFMultiplier *= type->ROFMultiplier; + pAE.Cloakable |= type->Cloakable; + pAE.ForceDecloak |= type->ForceDecloak; + pAE.DisableWeapons |= type->DisableWeapons; + pAE.Unkillable |= type->Unkillable; + pAE.HasRangeModifier |= (type->WeaponRange_ExtraRange != 0.0 || type->WeaponRange_Multiplier != 0.0); + pAE.HasTint |= type->HasTint(); + pAE.ReflectDamage |= type->ReflectDamage; + pAE.HasOnFireDiscardables |= (type->DiscardOn & DiscardCondition::Firing) != DiscardCondition::None; + pAE.HasCritModifiers |= (type->Crit_Multiplier != 1.0 || type->Crit_ExtraChance != 0.0); + + if (type->ArmorMultiplier != 1.0 && (type->ArmorMultiplier_AllowWarheads.size() > 0 || type->ArmorMultiplier_DisallowWarheads.size() > 0)) + pAE.HasRestrictedArmorMultipliers = true; + else + pAE.ArmorMultiplier *= type->ArmorMultiplier; + + if (pAE.ForceDecloak && pThis->CloakState == CloakState::Cloaked) + pThis->Uncloak(true); + + return; + } + + const bool wasTint = pAE.HasTint; double firepower = 1.0; double armor = 1.0; double speed = 1.0; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index aa87286dfa..4fbbe9cb2f 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -199,7 +199,7 @@ class TechnoExt void UpdateKeepTargetOnMove(); void UpdateWarpInDelay(); void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved = nullptr); - void RecalculateStatMultipliers(); + void RecalculateStatMultipliers(AttachEffectClass* pAttachEffect = nullptr); void UpdateTemporal(); void UpdateMindControlAnim(); void UpdateRecountBurst(); diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index 9ba1d4da58..63043d5a6a 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -170,11 +170,11 @@ void AttachEffectClass::AI() if (!this->HasInitialized && this->InitialDelay == 0) { this->HasInitialized = true; + auto const pExt = TechnoExt::ExtMap.Find(pTechno); if (pType->ROFMultiplier > 0.0 && pType->ROFMultiplier_ApplyOnCurrentTimer) { const double ROFModifier = pType->ROFMultiplier; - auto const pExt = TechnoExt::ExtMap.Find(pTechno); pTechno->RearmTimer.Start(static_cast(pTechno->RearmTimer.GetTimeLeft() * ROFModifier)); if (!pExt->ChargeTurretTimer.HasStarted() && pExt->LastRearmWasFullDelay) @@ -182,9 +182,12 @@ void AttachEffectClass::AI() } if (pType->HasTint()) + { pTechno->MarkForRedraw(); + pExt->UpdateTintValues(); + } - this->NeedsRecalculateStat = true; + pExt->RecalculateStatMultipliers(this); AttachEffectTypeClass::HandleEvent(pTechno); } @@ -206,7 +209,6 @@ void AttachEffectClass::AI() if (!this->ShouldBeDiscardedNow()) { this->RefreshDuration(); - this->NeedsRecalculateStat = true; this->NeedsDurationRefresh = false; AttachEffectTypeClass::HandleEvent(pTechno); } @@ -322,7 +324,15 @@ void AttachEffectClass::OnlineCheck() if (isActive != this->LastActiveStat) { - this->NeedsRecalculateStat = true; + auto const pExt = TechnoExt::ExtMap.Find(this->Techno); + + if (this->Type->HasTint()) + { + pTechno->MarkForRedraw(); + pExt->UpdateTintValues(); + } + + pExt->RecalculateStatMultipliers(this); this->LastActiveStat = isActive; } @@ -632,6 +642,8 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T if (params.InitialDelay <= 0) { + pTargetExt->RecalculateStatMultipliers(pAE); + if (pType->ROFMultiplier > 0.0 && pType->ROFMultiplier_ApplyOnCurrentTimer) ROFModifier *= pType->ROFMultiplier; @@ -652,12 +664,10 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T pTarget->ChargeTurretDelay = static_cast(pTarget->ChargeTurretDelay * ROFModifier); } - if (attachedCount > 0) + if (attachedCount > 0 && markForRedraw) { - pTargetExt->RecalculateStatMultipliers(); - - if (markForRedraw) - pTarget->MarkForRedraw(); + pTarget->MarkForRedraw(); + pTargetExt->UpdateTintValues(); } return attachedCount; From 94093e24e15ceceee3912446b9e870ff3a026a17 Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Wed, 3 Jun 2026 21:31:18 +0800 Subject: [PATCH 4/6] fix leptons calculation --- docs/New-or-Enhanced-Logics.md | 6 +++--- src/Ext/WeaponType/Body.cpp | 18 +++++++++--------- src/Ext/WeaponType/Body.h | 8 ++++---- 3 files changed, 16 insertions(+), 16 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 2f0c8a437b..3eae9c215a 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -47,7 +47,7 @@ This page describes all the engine features that are either new and introduced b - `WeaponRange.Multiplier` and `WeaponRange.ExtraRange` can be used to multiply the weapon firing range of the object the effect is attached to, or give it an increase / decrease (measured in cells), respectively. `ExtraRange` is cumulatively applied from all attached effects after all `Multiplier` values have been applied. - `WeaponRange.AllowWeapons` can be used to list only weapons that can benefit from this range bonus and `WeaponRange.DisallowWeapons` weapons that are not allowed to, respectively. - On TechnoTypes with `OpenTopped=true`, `OpenTopped.UseTransportRangeModifiers` can be set to true to make passengers firing out use the transport's active range bonuses instead. - - The weapon range with all sort of range modifiers can't be greater than `WeaponRange.Maximum` or smaller than `WeaponRange.Minimum` of the affected weapon. Notice that `Range=-512` is a special case and will be ignored. + - The weapon range with all sort of range modifiers can't be greater than `WeaponRange.Maximum` or smaller than `WeaponRange.Minimum` of the affected weapon. Notice that `Range=-2` is a special case and will be ignored. - `Crit.Multiplier` and `Crit.ExtraChance` can be used to multiply the [critical hit](#chance-based-extra-damage-or-warhead-detonation--critical-hits) chance or grant a fixed bonus to it for the object the effect is attached to, respectively. - `Crit.AllowWarheads` can be used to list only Warheads that can benefit from this critical hit chance multiplier and `Crit.DisallowWarheads` weapons that are not allowed to, respectively. - `RevengeWeapon` can be used to temporarily grant the specified weapon as a [revenge weapon](#revenge-weapon) for the attached object. @@ -173,8 +173,8 @@ AttachEffect.DisallowedMinCounts= ; integer - minimum disallowe AttachEffect.DisallowedMaxCounts= ; integer - maximum disallowed instance count (comma-separated) for cumulative types in order from first to last. AttachEffect.IgnoreFromSameSource=false ; boolean AttachEffect.CheckOnFirer=false ; boolean -WeaponRange.Maximum=2147483647 ; integer -WeaponRange.Minimum=0 ; integer +WeaponRange.Maximum=100000 ; floating point value, distance in cells +WeaponRange.Minimum=0 ; floating point value, distance in cells [SOMEWARHEAD] ; WarheadType AttachEffect.AttachTypes= ; List of AttachEffectTypes diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index d9b1ac466f..5ae756c462 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -175,21 +175,21 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->WeaponRange_Maximum.Read(exINI, pSection, "WeaponRange.Maximum"); this->WeaponRange_Minimum.Read(exINI, pSection, "WeaponRange.Minimum"); - if (this->WeaponRange_Maximum < pThis->Range) + if (this->WeaponRange_Maximum.Get() < pThis->Range) { Debug::Log("[Developer warning][%s] WeaponRange.Maximum is smaller than Range, it'll be reset to the value of Range!\n", pSection); - this->WeaponRange_Maximum = pThis->Range; + this->WeaponRange_Maximum = Leptons(pThis->Range / static_cast(Unsorted::LeptonsPerCell)); } - if (this->WeaponRange_Minimum < 0) + if (this->WeaponRange_Minimum.Get() < 0) { Debug::Log("[Developer warning][%s] WeaponRange.Minimum is smaller than 0, it'll be reset to 0!\n", pSection); - this->WeaponRange_Minimum = 0; + this->WeaponRange_Minimum = Leptons(0); } - else if (this->WeaponRange_Minimum > pThis->Range) + else if (this->WeaponRange_Minimum.Get() > pThis->Range) { Debug::Log("[Developer warning][%s] WeaponRange.Minimum is bigger than Range, it'll be reset to the value of Range!\n", pSection); - this->WeaponRange_Minimum = pThis->Range; + this->WeaponRange_Minimum = Leptons(pThis->Range / static_cast(Unsorted::LeptonsPerCell)); } // handle SkipWeaponPicking @@ -374,12 +374,12 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF pTechno = pTransport; } - int min = 0, max = INT_MAX; + int min = 0, max = 25600000; // Unsorted::LeptonsPerCell * 100000 if (auto const pExt = WeaponTypeExt::ExtMap.Find(pThis)) { - min = pExt->WeaponRange_Minimum; - max = pExt->WeaponRange_Maximum; + min = pExt->WeaponRange_Minimum.Get(); + max = pExt->WeaponRange_Maximum.Get(); } auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index fa07fe9b62..b39f73e82c 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -100,8 +100,8 @@ class WeaponTypeExt Nullable AttackNoThreatBuildings; Nullable CylinderRangefinding; Nullable Anim_Update; - Valueable WeaponRange_Maximum; - Valueable WeaponRange_Minimum; + Valueable WeaponRange_Maximum; + Valueable WeaponRange_Minimum; bool SkipWeaponPicking; @@ -190,8 +190,8 @@ class WeaponTypeExt , AttackNoThreatBuildings {} , CylinderRangefinding {} , Anim_Update {} - , WeaponRange_Maximum { INT_MAX } - , WeaponRange_Minimum { 0 } + , WeaponRange_Maximum { Leptons(100000) } + , WeaponRange_Minimum { Leptons(0) } { } int GetBurstDelay(int burstIndex) const; From ecfe05c0ebf4675e6b51002866f3a794459c1d9f Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Wed, 3 Jun 2026 21:35:16 +0800 Subject: [PATCH 5/6] revert --- src/Ext/Techno/Body.Update.cpp | 31 ++-------------------------- src/Ext/Techno/Body.h | 2 +- src/New/Entity/AttachEffectClass.cpp | 28 ++++++++----------------- 3 files changed, 12 insertions(+), 49 deletions(-) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index ebbbeaee10..185b608762 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -2080,39 +2080,12 @@ void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pA } // Recalculates AttachEffect stat multipliers and other bonuses. -void TechnoExt::ExtData::RecalculateStatMultipliers(AttachEffectClass* pAttachEffect) +void TechnoExt::ExtData::RecalculateStatMultipliers() { auto const pThis = this->OwnerObject(); auto& pAE = this->AE; - - if (pAttachEffect) - { - auto const type = pAttachEffect->GetType(); - pAE.FirepowerMultiplier *= type->FirepowerMultiplier; - pAE.SpeedMultiplier *= type->SpeedMultiplier; - pAE.ROFMultiplier *= type->ROFMultiplier; - pAE.Cloakable |= type->Cloakable; - pAE.ForceDecloak |= type->ForceDecloak; - pAE.DisableWeapons |= type->DisableWeapons; - pAE.Unkillable |= type->Unkillable; - pAE.HasRangeModifier |= (type->WeaponRange_ExtraRange != 0.0 || type->WeaponRange_Multiplier != 0.0); - pAE.HasTint |= type->HasTint(); - pAE.ReflectDamage |= type->ReflectDamage; - pAE.HasOnFireDiscardables |= (type->DiscardOn & DiscardCondition::Firing) != DiscardCondition::None; - pAE.HasCritModifiers |= (type->Crit_Multiplier != 1.0 || type->Crit_ExtraChance != 0.0); - - if (type->ArmorMultiplier != 1.0 && (type->ArmorMultiplier_AllowWarheads.size() > 0 || type->ArmorMultiplier_DisallowWarheads.size() > 0)) - pAE.HasRestrictedArmorMultipliers = true; - else - pAE.ArmorMultiplier *= type->ArmorMultiplier; - - if (pAE.ForceDecloak && pThis->CloakState == CloakState::Cloaked) - pThis->Uncloak(true); - - return; - } - const bool wasTint = pAE.HasTint; + double firepower = 1.0; double armor = 1.0; double speed = 1.0; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 4fbbe9cb2f..aa87286dfa 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -199,7 +199,7 @@ class TechnoExt void UpdateKeepTargetOnMove(); void UpdateWarpInDelay(); void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved = nullptr); - void RecalculateStatMultipliers(AttachEffectClass* pAttachEffect = nullptr); + void RecalculateStatMultipliers(); void UpdateTemporal(); void UpdateMindControlAnim(); void UpdateRecountBurst(); diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index 63043d5a6a..9ba1d4da58 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -170,11 +170,11 @@ void AttachEffectClass::AI() if (!this->HasInitialized && this->InitialDelay == 0) { this->HasInitialized = true; - auto const pExt = TechnoExt::ExtMap.Find(pTechno); if (pType->ROFMultiplier > 0.0 && pType->ROFMultiplier_ApplyOnCurrentTimer) { const double ROFModifier = pType->ROFMultiplier; + auto const pExt = TechnoExt::ExtMap.Find(pTechno); pTechno->RearmTimer.Start(static_cast(pTechno->RearmTimer.GetTimeLeft() * ROFModifier)); if (!pExt->ChargeTurretTimer.HasStarted() && pExt->LastRearmWasFullDelay) @@ -182,12 +182,9 @@ void AttachEffectClass::AI() } if (pType->HasTint()) - { pTechno->MarkForRedraw(); - pExt->UpdateTintValues(); - } - pExt->RecalculateStatMultipliers(this); + this->NeedsRecalculateStat = true; AttachEffectTypeClass::HandleEvent(pTechno); } @@ -209,6 +206,7 @@ void AttachEffectClass::AI() if (!this->ShouldBeDiscardedNow()) { this->RefreshDuration(); + this->NeedsRecalculateStat = true; this->NeedsDurationRefresh = false; AttachEffectTypeClass::HandleEvent(pTechno); } @@ -324,15 +322,7 @@ void AttachEffectClass::OnlineCheck() if (isActive != this->LastActiveStat) { - auto const pExt = TechnoExt::ExtMap.Find(this->Techno); - - if (this->Type->HasTint()) - { - pTechno->MarkForRedraw(); - pExt->UpdateTintValues(); - } - - pExt->RecalculateStatMultipliers(this); + this->NeedsRecalculateStat = true; this->LastActiveStat = isActive; } @@ -642,8 +632,6 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T if (params.InitialDelay <= 0) { - pTargetExt->RecalculateStatMultipliers(pAE); - if (pType->ROFMultiplier > 0.0 && pType->ROFMultiplier_ApplyOnCurrentTimer) ROFModifier *= pType->ROFMultiplier; @@ -664,10 +652,12 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T pTarget->ChargeTurretDelay = static_cast(pTarget->ChargeTurretDelay * ROFModifier); } - if (attachedCount > 0 && markForRedraw) + if (attachedCount > 0) { - pTarget->MarkForRedraw(); - pTargetExt->UpdateTintValues(); + pTargetExt->RecalculateStatMultipliers(); + + if (markForRedraw) + pTarget->MarkForRedraw(); } return attachedCount; From 43d91360e5ef062e440204446c90fa8f391d3113 Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Thu, 4 Jun 2026 19:26:27 +0800 Subject: [PATCH 6/6] fix --- src/Ext/WeaponType/Body.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 5ae756c462..5d0c53df7f 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -175,10 +175,10 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->WeaponRange_Maximum.Read(exINI, pSection, "WeaponRange.Maximum"); this->WeaponRange_Minimum.Read(exINI, pSection, "WeaponRange.Minimum"); - if (this->WeaponRange_Maximum.Get() < pThis->Range) + if (this->WeaponRange_Maximum.Get() < pThis->Range && pThis->Range != -512) { Debug::Log("[Developer warning][%s] WeaponRange.Maximum is smaller than Range, it'll be reset to the value of Range!\n", pSection); - this->WeaponRange_Maximum = Leptons(pThis->Range / static_cast(Unsorted::LeptonsPerCell)); + this->WeaponRange_Maximum = Leptons(static_cast(pThis->Range / static_cast(Unsorted::LeptonsPerCell))); } if (this->WeaponRange_Minimum.Get() < 0) @@ -186,10 +186,10 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) Debug::Log("[Developer warning][%s] WeaponRange.Minimum is smaller than 0, it'll be reset to 0!\n", pSection); this->WeaponRange_Minimum = Leptons(0); } - else if (this->WeaponRange_Minimum.Get() > pThis->Range) + else if (this->WeaponRange_Minimum.Get() > pThis->Range && pThis->Range != -512) { Debug::Log("[Developer warning][%s] WeaponRange.Minimum is bigger than Range, it'll be reset to the value of Range!\n", pSection); - this->WeaponRange_Minimum = Leptons(pThis->Range / static_cast(Unsorted::LeptonsPerCell)); + this->WeaponRange_Minimum = Leptons(static_cast(pThis->Range / static_cast(Unsorted::LeptonsPerCell))); } // handle SkipWeaponPicking @@ -374,7 +374,8 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF pTechno = pTransport; } - int min = 0, max = 25600000; // Unsorted::LeptonsPerCell * 100000 + int min = 0; + int max = 25600000; // Unsorted::LeptonsPerCell * 100000 if (auto const pExt = WeaponTypeExt::ExtMap.Find(pThis)) {