diff --git a/CREDITS.md b/CREDITS.md index 278e3e5d60..70b3233f74 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -399,6 +399,7 @@ This page lists all the individual contributions to the project by their author. - Fast access structure - Iron Curtain/Custom Tint Support for SHP Turreted Vehicles - Reactivate unused trigger events 2, 53, and 54 + - Attack non-threatening structures extensions - **NetsuNegi**: - Forbidding parallel AI queues by type - Jumpjet crash speed fix when crashing onto building diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index d04603a742..0800428191 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -1314,6 +1314,18 @@ In `rulesmd.ini`: AttackMove.IgnoreWeaponCheck=false ; boolean ``` +### Attack non-threatening structures + +- You can now freely configure whether units can automatically target non-threatening structures. + - `AutoTarget.NoThreatBuildings` affects player-controlled units, `AutoTargetAI.NoThreatBuildings` affects other units. + +In `rulesmd.ini`: +```ini +[General] +AutoTarget.NoThreatBuildings=false ; boolean +AutoTargetAI.NoThreatBuildings=true ; boolean +``` + ### Aircraft spawner customizations ![image](_static/images/spawnrange-01.gif) @@ -2577,6 +2589,16 @@ In `rulesmd.ini`: AreaFire.Target=base ; AreaFire Target Enumeration (base|self|random) ``` +### Attack non-threatening structures + +- `AttackNoThreatBuildings` permits shooters to attack non-threatening structures. This setting overrides other configurations. + +In `rulesmd.ini`: +```ini +[SOMEWEAPON] ; WeaponType +AttackNoThreatBuildings= ; boolean +``` + ### Burst delay customizations - `Burst.Delays` allows specifying weapon-specific burst shot delays. Takes precedence over the old `BurstDelayX` logic available on VehicleTypes, functions with Infantry & BuildingType weapons (AircraftTypes are not supported due to their weapon firing system being completely different) and allows every shot of `Burst` to have a separate delay instead of only first four shots. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index c4831eee2f..03fe8ef57b 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -459,6 +459,7 @@ New: - Toggle off laser trail and shake effects (by Ollerus) - [Dehardcode the `ZAdjust` of warhead anim](Fixed-or-Improved-Logics.md#dehardcode-the-zadjust-of-warhead-anim) (by TaranDahl) - [Interceptor target scan delay customization](New-or-Enhanced-Logics.md#projectile-interception-logic) (by Starkku) +- Attack non-threatening structures extensions (by FlyStar) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index b2a105fbca..e4421199f8 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -331,6 +331,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->FallingDownTargetingFix.Read(exINI, GameStrings::General, "FallingDownTargetingFix"); this->AIAirTargetingFix.Read(exINI, GameStrings::General, "AIAirTargetingFix"); + this->AutoTarget_NoThreatBuildings.Read(exINI, GameStrings::General, "AutoTarget.NoThreatBuildings"); + this->AutoTargetAI_NoThreatBuildings.Read(exINI, GameStrings::General, "AutoTargetAI.NoThreatBuildings"); // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); @@ -605,6 +607,8 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->IvanBombAttachToCenter) .Process(this->FallingDownTargetingFix) .Process(this->AIAirTargetingFix) + .Process(this->AutoTarget_NoThreatBuildings) + .Process(this->AutoTargetAI_NoThreatBuildings) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 6ddc6ed06c..6b4b97e13f 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -281,6 +281,9 @@ class RulesExt Valueable FallingDownTargetingFix; Valueable AIAirTargetingFix; + Valueable AutoTarget_NoThreatBuildings; + Valueable AutoTargetAI_NoThreatBuildings; + ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } , HarvesterDumpAmount { 0.0f } @@ -500,6 +503,8 @@ class RulesExt , FallingDownTargetingFix { false } , AIAirTargetingFix { false } + , AutoTarget_NoThreatBuildings { false } + , AutoTargetAI_NoThreatBuildings { true } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index 10daf28aac..98a36f150f 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -203,8 +203,10 @@ DEFINE_HOOK(0x6F85AB, TechnoClass_CanAutoTargetObject_AggressiveAttackMove, 0x6) GET(TechnoClass* const, pThis, EDI); - if (!pThis->Owner->IsControlledByHuman()) - return CanTarget; + // Now, it is possible to customize which types of national active attacks on non-threatening buildings, so this part has been commented out. + // The new judgment code is in TechnoClass_CanAutoTarget_AttackNoThreatBuildings of Hook.cpp. + // if (!pThis->Owner->IsControlledByHuman()) + // return CanTarget; if (!pThis->MegaMissionIsAttackMove()) return ContinueCheck; diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index cdfc60dc15..66148703bf 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -1528,3 +1528,44 @@ DEFINE_HOOK(0x6F9398, TechnoClass_SelectAutoTarget_Scan_FallingDown, 0x9) } #pragma endregion + +#pragma region AutoTargetExtension + +namespace CanAutoTargetTemp +{ + TechnoTypeExt::ExtData* TypeExtData = nullptr; + WeaponTypeExt::ExtData* WeaponExt = nullptr; +} + +DEFINE_HOOK(0x6F7E30, TechnoClass_CanAutoTarget_SetContent, 0x6) +{ + GET(TechnoClass*, pThis, EDI); + GET(WeaponTypeClass*, pWeapon, EBP); + + CanAutoTargetTemp::TypeExtData = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + CanAutoTargetTemp::WeaponExt = WeaponTypeExt::ExtMap.TryFind(pWeapon); + + return 0; +} + +DEFINE_HOOK(0x6F85CF, TechnoClass_CanAutoTarget_AttackNoThreatBuildings, 0xA) +{ + enum { CanAttack = 0x6F8604, Continue = 0x6F85D9 }; + + GET(TechnoClass*, pThis, EDI); + GET(BuildingClass*, pTarget, ESI); + + bool canAttack = pThis->Owner->IsControlledByHuman() ? RulesExt::Global()->AutoTarget_NoThreatBuildings : RulesExt::Global()->AutoTargetAI_NoThreatBuildings; + + if (const auto pWeaponExt = CanAutoTargetTemp::WeaponExt) + canAttack = pWeaponExt->AttackNoThreatBuildings.Get(canAttack); + + if (canAttack) + return CanAttack; + + R->EAX(pTarget->GetTurretWeapon()); + return Continue; +} + +#pragma endregion + diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 7f2037c577..3968ece227 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -153,6 +153,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->DelayedFire_OnlyOnInitialBurst.Read(exINI, pSection, "DelayedFire.OnlyOnInitialBurst"); this->DelayedFire_AnimOffset.Read(exINI, pSection, "DelayedFire.AnimOffset"); this->DelayedFire_AnimOnTurret.Read(exINI, pSection, "DelayedFire.AnimOnTurret"); + this->AttackNoThreatBuildings.Read(exINI, pSection, "AttackNoThreatBuildings"); // handle SkipWeaponPicking if (this->CanTarget != AffectedTarget::All || this->CanTargetHouses != AffectedHouse::All @@ -237,6 +238,7 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) .Process(this->DelayedFire_OnlyOnInitialBurst) .Process(this->DelayedFire_AnimOffset) .Process(this->DelayedFire_AnimOnTurret) + .Process(this->AttackNoThreatBuildings) ; }; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 03d6fe37e0..438f085f29 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -91,6 +91,7 @@ class WeaponTypeExt Valueable DelayedFire_OnlyOnInitialBurst; Nullable DelayedFire_AnimOffset; Valueable DelayedFire_AnimOnTurret; + Nullable AttackNoThreatBuildings; bool SkipWeaponPicking; @@ -164,6 +165,7 @@ class WeaponTypeExt , DelayedFire_OnlyOnInitialBurst { false } , DelayedFire_AnimOffset {} , DelayedFire_AnimOnTurret { true } + , AttackNoThreatBuildings {} { } int GetBurstDelay(int burstIndex) const;