From 4d82dcb8a403b90358be9b136996f1bfb2b40975 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Mon, 22 Dec 2025 18:27:42 +0800 Subject: [PATCH 1/3] code --- src/Ext/Techno/Hooks.ReceiveDamage.cpp | 4 ++++ src/Ext/WarheadType/Body.cpp | 4 ++++ src/Ext/WarheadType/Body.h | 6 +++++- src/Ext/WarheadType/Detonate.cpp | 4 ++-- 4 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index 9e57d97361..856ad99fc9 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -34,6 +34,10 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) const auto pSourceHouse = args->SourceHouse; const auto pTargetHouse = pThis->Owner; + // Apply warhead effects + if (damage && !pWHExt->ApplyPerTargetEffectsOnDetonate) + pWHExt->DetonateOnOneUnit(args->SourceHouse, pThis, args->Attacker); + // Calculate Damage Multiplier if (!args->IgnoreDefenses && damage) { diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index b0dc81fb02..72ef93c89e 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -310,6 +310,8 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AnimZAdjust.Read(exINI, pSection, "AnimZAdjust"); + this->ApplyPerTargetEffectsOnDetonate.Read(exINI, pSection, "ApplyPerTargetEffectsOnDetonate"); + // Convert.From & Convert.To TypeConvertGroup::Parse(this->Convert_Pairs, exINI, pSection, AffectedHouse::All); @@ -603,6 +605,8 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->AnimZAdjust) + .Process(this->ApplyPerTargetEffectsOnDetonate) + // Ares tags .Process(this->AffectsEnemies) .Process(this->AffectsOwner) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index b61bb4a20c..1e969e56f8 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -214,6 +214,8 @@ class WarheadTypeExt Nullable AnimZAdjust; + Valueable ApplyPerTargetEffectsOnDetonate; + // Ares tags // http://ares-developers.github.io/Ares-docs/new/warheads/general.html Valueable AffectsEnemies; @@ -450,6 +452,8 @@ class WarheadTypeExt , PlayAnimAboveSurface { false } , AnimZAdjust {} + + , ApplyPerTargetEffectsOnDetonate { false } { } void ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget); @@ -475,10 +479,10 @@ class WarheadTypeExt public: // Detonate.cpp void Detonate(TechnoClass* pOwner, HouseClass* pHouse, BulletExt::ExtData* pBullet, CoordStruct coords); + void DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner = nullptr, bool bulletWasIntercepted = false); void InterceptBullets(TechnoClass* pOwner, BulletClass* pInterceptor, const CoordStruct& coords); DamageAreaResult DamageAreaWithTarget(const CoordStruct& coords, int damage, TechnoClass* pSource, WarheadTypeClass* pWH, bool affectsTiberium, HouseClass* pSourceHouse, TechnoClass* pTarget); private: - void DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner = nullptr, bool bulletWasIntercepted = false); void ApplyRemoveDisguise(TechnoClass* pTarget); HouseClass* ApplyRemoveMindControl(HouseClass* pHouse, TechnoClass* pTarget); void ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* Owner); diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index d64548ebd1..63df1ab4ef 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -149,7 +149,7 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse, this->Crit_Active = false; this->Crit_CurrentChance = this->GetCritChance(pOwner); - if (this->PossibleCellSpreadDetonate || this->Crit_CurrentChance > 0.0) + if ((this->PossibleCellSpreadDetonate || this->Crit_CurrentChance > 0.0) && this->ApplyPerTargetEffectsOnDetonate) { if (!this->Crit_ApplyChancePerTarget) this->Crit_RandomBuffer = ScenarioClass::Instance->Random.RandomDouble(); @@ -471,7 +471,7 @@ HouseClass* WarheadTypeExt::ExtData::ApplyRemoveMindControl(HouseClass* pHouse, void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner) { - const double dice = this->Crit_ApplyChancePerTarget ? ScenarioClass::Instance->Random.RandomDouble() : this->Crit_RandomBuffer; + const double dice = this->Crit_ApplyChancePerTarget || !this->ApplyPerTargetEffectsOnDetonate ? ScenarioClass::Instance->Random.RandomDouble() : this->Crit_RandomBuffer; if (this->Crit_CurrentChance < dice) return; From 249ac0d46059f055494dde65e33908a82a3b0099 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:05:35 +0800 Subject: [PATCH 2/3] docs and global value --- docs/New-or-Enhanced-Logics.md | 25 +++++++++++++++++++++++++ src/Ext/Rules/Body.cpp | 3 +++ src/Ext/Rules/Body.h | 4 ++++ src/Ext/Techno/Hooks.ReceiveDamage.cpp | 2 +- src/Ext/WarheadType/Body.h | 4 ++-- src/Ext/WarheadType/Detonate.cpp | 4 ++-- 6 files changed, 37 insertions(+), 5 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 6f6d23aaa1..bd1217361a 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2628,6 +2628,31 @@ SpawnsCrate(N).Type= ; Powerup crate type enum (money|unit|healbase|cloak|ex SpawnsCrate(N).Weight=1 ; integer ``` +### Toggle per-target warhead effects apply timing + +- Now you can set the following flag to `false` to apply the Phobos warhead effects that take effect on each target when taking damage, rather than when the projectiles detonate. + - This will allow such effects to be applied through damage without projectiles, including but not limited to damage from particles, vanilla radiation, and Ares' `GenericWarhead` superweapon. + - If you use a warhead with CellSpread to damage a building multiple times, then these effects will be applied multiple times. If you don't want this to happen, use [`MergeBuildingDamage`](#allow-merging-aoe-damage-to-buildings-into-one). + - The affected effects include: + - [Remove mind-control](#break-mind-control-on-impact) + - [Type convertion](#convert-technotype-on-impact) + - [`BuildingSell` & `BuildingUndeploy`](#sell-or-undeploy-building-on-impact) + - [`RemoveDisguise`](#remove-disguise-on-impact) + - [`ReverseEngineer`](#reverse-engineer-warhead) + - [Modify shield](#shields) + - [Modify attach-effects](#attached-effects) + - [Critical hits](#chance-based-extra-damage-or-warhead-detonation--critical-hits) + - Due to technical reasons, `Crit.SuppressWhenIntercepted=false` and `Crit.ApplyChancePerTarget=true` will forced to be used. + +In `rulesmd.ini`: +```ini +[CombatDamage] ; WarheadType +ApplyPerTargetEffectsOnDetonate=true ; boolean + +[SOMEWARHEAD] ; WarheadType +ApplyPerTargetEffectsOnDetonate= ; boolean, default to [CombatDamage] -> ApplyPerTargetEffectsOnDetonate +``` + ### Trigger specific NotHuman infantry Death anim sequence - Warheads are now able to trigger specific `NotHuman=yes` infantry `Death` anim sequence using the corresponding tag. It's value represents sequences from `Die1` to `Die5`. diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 6b9da866f3..8f0fad01b5 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -339,6 +339,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->SortCameoByName.Read(exINI, GameStrings::General, "SortCameoByName"); this->MergeBuildingDamage.Read(exINI, GameStrings::CombatDamage, "MergeBuildingDamage"); + + this->ApplyPerTargetEffectsOnDetonate.Read(exINI, GameStrings::CombatDamage, "ApplyPerTargetEffectsOnDetonate"); // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); @@ -617,6 +619,7 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->AIAirTargetingFix) .Process(this->SortCameoByName) .Process(this->MergeBuildingDamage) + .Process(this->ApplyPerTargetEffectsOnDetonate) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index c3a004edb2..b07d419243 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -288,6 +288,8 @@ class RulesExt Valueable AIAirTargetingFix; Valueable SortCameoByName; + + Valueable ApplyPerTargetEffectsOnDetonate; ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } @@ -516,6 +518,8 @@ class RulesExt , SortCameoByName { false } , MergeBuildingDamage { false } + + , ApplyPerTargetEffectsOnDetonate { true } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index 856ad99fc9..9c14e7c8d9 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -35,7 +35,7 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) const auto pTargetHouse = pThis->Owner; // Apply warhead effects - if (damage && !pWHExt->ApplyPerTargetEffectsOnDetonate) + if (damage && !pWHExt->ApplyPerTargetEffectsOnDetonate.Get(RulesExt::Global()->ApplyPerTargetEffectsOnDetonate)) pWHExt->DetonateOnOneUnit(args->SourceHouse, pThis, args->Attacker); // Calculate Damage Multiplier diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 1e969e56f8..aace9f5887 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -214,7 +214,7 @@ class WarheadTypeExt Nullable AnimZAdjust; - Valueable ApplyPerTargetEffectsOnDetonate; + Nullable ApplyPerTargetEffectsOnDetonate; // Ares tags // http://ares-developers.github.io/Ares-docs/new/warheads/general.html @@ -453,7 +453,7 @@ class WarheadTypeExt , AnimZAdjust {} - , ApplyPerTargetEffectsOnDetonate { false } + , ApplyPerTargetEffectsOnDetonate {} { } void ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget); diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 63df1ab4ef..32f18249bb 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -149,7 +149,7 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse, this->Crit_Active = false; this->Crit_CurrentChance = this->GetCritChance(pOwner); - if ((this->PossibleCellSpreadDetonate || this->Crit_CurrentChance > 0.0) && this->ApplyPerTargetEffectsOnDetonate) + if ((this->PossibleCellSpreadDetonate || this->Crit_CurrentChance > 0.0) && this->ApplyPerTargetEffectsOnDetonate.Get(RulesExt::Global()->ApplyPerTargetEffectsOnDetonate)) { if (!this->Crit_ApplyChancePerTarget) this->Crit_RandomBuffer = ScenarioClass::Instance->Random.RandomDouble(); @@ -471,7 +471,7 @@ HouseClass* WarheadTypeExt::ExtData::ApplyRemoveMindControl(HouseClass* pHouse, void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner) { - const double dice = this->Crit_ApplyChancePerTarget || !this->ApplyPerTargetEffectsOnDetonate ? ScenarioClass::Instance->Random.RandomDouble() : this->Crit_RandomBuffer; + const double dice = this->Crit_ApplyChancePerTarget || !this->ApplyPerTargetEffectsOnDetonate.Get(RulesExt::Global()->ApplyPerTargetEffectsOnDetonate) ? ScenarioClass::Instance->Random.RandomDouble() : this->Crit_RandomBuffer; if (this->Crit_CurrentChance < dice) return; From d8d0229b4ccd24afb47b57ec3e6df2f2bf22c464 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=88=AA=E5=91=B3=E9=BA=BB=E9=85=B1?= <93972760+TaranDahl@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:12:09 +0800 Subject: [PATCH 3/3] update docs --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 1 + docs/Whats-New.md | 1 + 3 files changed, 3 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index 692247f19a..7c6caf064a 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -656,6 +656,7 @@ This page lists all the individual contributions to the project by their author. - CellSpread in cylinder shape - CellSpread damage check if victim is in air or on floor - Fix an issue where non-repairer units needed sensors to attack cloaked friendly units + - Toggle per-target warhead effects apply timing - **solar-III (凤九歌)** - Target scanning delay customization (documentation) - Skip target scanning function calling for unarmed technos (documentation) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index bd1217361a..b17c43c5e4 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -2632,6 +2632,7 @@ SpawnsCrate(N).Weight=1 ; integer - Now you can set the following flag to `false` to apply the Phobos warhead effects that take effect on each target when taking damage, rather than when the projectiles detonate. - This will allow such effects to be applied through damage without projectiles, including but not limited to damage from particles, vanilla radiation, and Ares' `GenericWarhead` superweapon. + - This will also cause all effects that can completely prevent damage to also prevent these warhead effects, including but not limited to `DamageSelf`, `DamageAirThreshold`, `AffectsAllies`, `AffectsAir`. - If you use a warhead with CellSpread to damage a building multiple times, then these effects will be applied multiple times. If you don't want this to happen, use [`MergeBuildingDamage`](#allow-merging-aoe-damage-to-buildings-into-one). - The affected effects include: - [Remove mind-control](#break-mind-control-on-impact) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index ef5401ed78..c9715d5b13 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -514,6 +514,7 @@ Vanilla fixes: - Fixed a bug where units can be promoted when created via trigger actions even if they have `Trainable=false` (by NetsuNegi) - Fixed the bug that ai will try to product aircraft even the airport has no free dock for it (by NetsuNegi) - Fixed the issue where non-repairer units needed sensors to attack cloaked friendly units (by TaranDahl) +- Toggle per-target warhead effects apply timing (by TaranDahl) Phobos fixes: - Fixed the bug that `AllowAirstrike=no` cannot completely prevent air strikes from being launched against it (by NetsuNegi)