From cc29999e0f165e18f9d906887c7fb4cf26d4f181 Mon Sep 17 00:00:00 2001 From: Coronia <2217891145@qq.com> Date: Wed, 16 Oct 2024 21:48:01 +0800 Subject: [PATCH] Forcing specific weapon by range --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 28 +++++++++++++--------- docs/Whats-New.md | 1 + src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.Firing.cpp | 24 +++++++++++++------ src/Ext/Techno/WeaponHelpers.cpp | 41 ++++++++++++++++++++++++++++++++ src/Ext/TechnoType/Body.cpp | 6 +++++ src/Ext/TechnoType/Body.h | 6 +++++ src/Ext/WeaponType/Body.cpp | 4 ++-- 9 files changed, 92 insertions(+), 20 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index df74398907..ed0e68e8cc 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -371,6 +371,7 @@ This page lists all the individual contributions to the project by their author. - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude + - Forcing specific weapon by range - **handama** - AI script action to jump back to previous script - **TaranDahl (航味麻酱)** - Skirmish AI "sell all buildings and set all technos to hunt" behavior dehardcode diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index d4ff01af1e..d1162d3a80 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -125,7 +125,7 @@ ReflectDamage.Multiplier=1.0 ; floating point value, perce ReflectDamage.AffectsHouses=all ; list of Affected House Enumeration (none|owner/self|allies/ally|team|enemies/enemy|all) DisableWeapons=false ; boolean Groups= ; comma-separated list of strings (group IDs) - + [SOMETECHNO] ; TechnoType AttachEffect.AttachTypes= ; List of AttachEffectTypes AttachEffect.DurationOverrides= ; integer - duration overrides (comma-separated) for AttachTypes in order from first to last. @@ -134,7 +134,7 @@ AttachEffect.InitialDelays= ; integer - initial delays (c AttachEffect.RecreationDelays= ; integer - recreation delays (comma-separated) for AttachTypes in order from first to last. OpenTopped.UseTransportRangeModifiers=false ; boolean OpenTopped.CheckTransportDisableWeapons=false ; boolean - + [SOMEWEAPON] ; WeaponType AttachEffect.RequiredTypes= ; List of AttachEffectTypes AttachEffect.DisallowedTypes= ; List of AttachEffectTypes @@ -146,8 +146,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 - -[SOMEWARHEAD] + +[SOMEWARHEAD] AttachEffect.AttachTypes= ; List of AttachEffectTypes AttachEffect.CumulativeRefreshAll=false ; boolean AttachEffect.CumulativeRefreshAll.OnAttach=false ; boolean @@ -511,7 +511,7 @@ DetachedReport= ; sound entry - `Adjacent.Allowed` lists BuildingTypes this BuildingType can be placed off (within distance defined by `Adjacent`). If empty, any BuildingType not listed in `Adjacent.Disallowed` is okay. - `Adjacent.Disallowed` lists BuildingTypes this BuildingType cannot be placed next to. If empty, any BuildingTypes are okay as long as `Adjacent.Allowed` is empty or they are listed on it. - If `NoBuildAreaOnBuildup` is set to true, no building can be built next to this building regardless of any other settings if it is currently displaying its buildup animation. - + In `rulesmd.ini`: ```ini [SOMEBUILDING] ; BuildingType @@ -544,7 +544,7 @@ PowersUp.Buildings= ; list of BuildingTypes - It is possible to make buildings be considered pathfinding obstacles that can be destroyed by setting `IsDestroyableBlockage` to true. What this does is make the building be considered impassable and impenetrable pathfinding obstacle to every unit that is not flying or have appropriate `MovementZone` (ones that allow destroyable obstacles to be overcome, e.g `(Infantry|Amphibious)Destroyer`) akin to wall overlays and TerrainTypes. - Keep in mind that if an unit has appropriate `MovementZone` but no means to actually destroy an obstacle (such as a weapon that can fire and deal damage at them), they will get stuck trying to go through them instead of pathing around. - + In `rulesmd.ini`: ```ini [SOMEBUILDING] ; BuildingType @@ -1133,13 +1133,19 @@ FLHKEY.BurstN= ; integer - Forward,Lateral,Height. FLHKey refers to weapon-spec - `ForceWeapon.Naval.Decloaked` forces specified weapon to be used against uncloaked naval targets. Useful if your naval unit has one weapon only for underwater and another weapon for surface targets. - `ForceWeapon.Cloaked` forces specified weapon to be used against any cloaked targets. - `ForceWeapon.Disguised` forces specified weapon to be used against any disguised targets. + - `ForceWeapon.InRange` forces specified a list of weapons to be used once the target is within their `Range`. The first weapon in the listed order satisfied will be selected. + - `ForceWeapon.InRange.Overrides` overrides the range when decides which weapon to use. Value from position matching the position from `ForceWeapon.InRange` is used if found, or the weapon's own `Range` if not found or set to a value below 0. Specifically, if a position has `ForceWeapon.InRange` set to -1 and `ForceWeapon.InRange.Overrides` set to a positive value, it'll use default weapon selection logic once satisfied. + - If `ForceWeapon.InRange.ApplyRangeModifiers` is set to true, any applicable weapon range modifiers from the firer are applied to the decision range. In `rulesmd.ini`: ```ini -[SOMETECHNO] ; TechnoType -ForceWeapon.Naval.Decloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable -ForceWeapon.Cloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable -ForceWeapon.Disguised=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable +[SOMETECHNO] ; TechnoType +ForceWeapon.Naval.Decloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable +ForceWeapon.Cloaked=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable +ForceWeapon.Disguised=-1 ; integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable +ForceWeapon.InRange= ; list of integer. 0 for primary weapon, 1 for secondary weapon, -1 to disable +ForceWeapon.InRange.Overrides= ; list of floating point value +ForceWeapon.InRange.ApplyRangeModifiers=false ; boolean ``` ### Make units try turning to target when firing with `OmniFire=yes` @@ -1700,7 +1706,7 @@ FeedbackWeapon= ; WeaponType - `Strafing.Shots` controls the number of times the weapon is fired during a single strafe run, defaults to 5 if not set. `Ammo` is only deducted at the end of the strafe run, regardless of the number of shots fired. - `Strafing.SimulateBurst` controls whether or not the shots fired during strafing simulate behavior of `Burst`, allowing for alternating firing offset. Only takes effect if weapon has `Burst` set to 1 or undefined. - `Strafing.UseAmmoPerShot`, if set to `true` overrides the usual behaviour of only deducting ammo after a strafing run and instead doing it after each individual shot. -- There is a special case for aircraft spawned by `Type=SpyPlane` superweapons on `SpyPlane Approach` or `SpyPlane Overfly` mission where `Strafing.Shots` only if explicitly set on its primary weapon, determines the maximum number of times the map revealing effect can activate irregardless of other factors. +- There is a special case for aircraft spawned by `Type=SpyPlane` superweapons on `SpyPlane Approach` or `SpyPlane Overfly` mission where `Strafing.Shots` only if explicitly set on its primary weapon, determines the maximum number of times the map revealing effect can activate irregardless of other factors. In `rulesmd.ini`: ```ini diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 039bee9470..920d19f9b0 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -463,6 +463,7 @@ New: - Toggle to disallow buildings from providing build area during buildup (by Starkku) - Allow customizing which building types provide build area for a building (by Starkku) - `Scorch` / `Flamer` fire animation customization (by Starkku) +- Forcing specific weapon by range (by Ollerus) Vanilla fixes: - Allow AI to repair structures built from base nodes/trigger action 125/SW delivery in single player missions (by Trsdy) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a82a46f30b..af76a8e83f 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -180,4 +180,5 @@ class TechnoExt static WeaponTypeClass* GetCurrentWeapon(TechnoClass* pThis, int& weaponIndex, bool getSecondary = false); static WeaponTypeClass* GetCurrentWeapon(TechnoClass* pThis, bool getSecondary = false); static int GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* pWallOverlayType); + static int ApplyForceWeaponInRange(TechnoClass* pThis, std::vector weaponIndices, std::vector rangeOverrides, bool applyRangeModifiers, int currentDistance); }; diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 52b2b8c04a..58f7287f72 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -75,16 +75,20 @@ DEFINE_HOOK(0x6F3428, TechnoClass_WhatWeaponShouldIUse_ForceWeapon, 0x6) GET(TechnoClass*, pThis, ECX); - if (pThis && pThis->Target) + if (!pThis) + return 0; + + int forceWeaponIndex = -1; + auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + + if (pThis->Target) { auto const pTarget = abstract_cast(pThis->Target); if (!pTarget) return 0; - int forceWeaponIndex = -1; auto const pTargetType = pTarget->GetTechnoType(); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); if (pTypeExt->ForceWeapon_Naval_Decloaked >= 0 && pTargetType->Cloakable && pTargetType->Naval && @@ -102,14 +106,20 @@ DEFINE_HOOK(0x6F3428, TechnoClass_WhatWeaponShouldIUse_ForceWeapon, 0x6) { forceWeaponIndex = pTypeExt->ForceWeapon_Disguised; } - - if (forceWeaponIndex >= 0) + else if (!pTypeExt->ForceWeapon_InRange.empty()) { - R->EAX(forceWeaponIndex); - return UseWeaponIndex; + int currentDistance = pThis->DistanceFrom(pTarget); + forceWeaponIndex = TechnoExt::ApplyForceWeaponInRange(pThis, pTypeExt->ForceWeapon_InRange, pTypeExt->ForceWeapon_InRange_Overrides, + pTypeExt->ForceWeapon_InRange_ApplyRangeModifiers, currentDistance); } } + if (forceWeaponIndex >= 0) + { + R->EAX(forceWeaponIndex); + return UseWeaponIndex; + } + return 0; } diff --git a/src/Ext/Techno/WeaponHelpers.cpp b/src/Ext/Techno/WeaponHelpers.cpp index 5310fdf18c..77bd99fa29 100644 --- a/src/Ext/Techno/WeaponHelpers.cpp +++ b/src/Ext/Techno/WeaponHelpers.cpp @@ -198,3 +198,44 @@ int TechnoExt::GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* p return weaponIndex; } + +int TechnoExt::ApplyForceWeaponInRange(TechnoClass* pThis, std::vector weaponIndices, std::vector rangeOverrides, bool applyRangeModifiers, int currentDistance) +{ + int forceWeaponIndex = -1; + + for (size_t i = 0; i < weaponIndices.size(); i++) + { + int distance = 0; + + // Value below 0 means Range won't be overriden + if (i < rangeOverrides.size() && rangeOverrides[i] > 0) + distance = static_cast(rangeOverrides[i] * 256.0); + + if (weaponIndices[i] >= 0) + { + auto const pWeapon = pThis->GetWeapon(weaponIndices[i])->WeaponType; + distance = distance > 0 ? distance : pWeapon->Range; + + if (applyRangeModifiers) + distance = WeaponTypeExt::GetRangeWithModifiers(pWeapon, pThis, distance); + + if (currentDistance <= distance) + { + forceWeaponIndex = weaponIndices[i]; + break; + } + } + else + { + // Apply range modifiers regardless of weapon + if (applyRangeModifiers) + distance = WeaponTypeExt::GetRangeWithModifiers(nullptr, pThis, distance); + + // Don't force weapon if range satisfied + if (currentDistance <= distance) + break; + } + } + + return forceWeaponIndex; +} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 61e6b4c2cf..deff3fa157 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -389,6 +389,9 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ForceWeapon_Naval_Decloaked.Read(exINI, pSection, "ForceWeapon.Naval.Decloaked"); this->ForceWeapon_Cloaked.Read(exINI, pSection, "ForceWeapon.Cloaked"); this->ForceWeapon_Disguised.Read(exINI, pSection, "ForceWeapon.Disguised"); + this->ForceWeapon_InRange.Read(exINI, pSection, "ForceWeapon.InRange"); + this->ForceWeapon_InRange_Overrides.Read(exINI, pSection, "ForceWeapon.InRange.Overrides"); + this->ForceWeapon_InRange_ApplyRangeModifiers.Read(exINI, pSection, "ForceWeapon.InRange.ApplyRangeModifiers"); this->Ammo_Shared.Read(exINI, pSection, "Ammo.Shared"); this->Ammo_Shared_Group.Read(exINI, pSection, "Ammo.Shared.Group"); this->SelfHealGainType.Read(exINI, pSection, "SelfHealGainType"); @@ -740,6 +743,9 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->ForceWeapon_Naval_Decloaked) .Process(this->ForceWeapon_Cloaked) .Process(this->ForceWeapon_Disguised) + .Process(this->ForceWeapon_InRange) + .Process(this->ForceWeapon_InRange_Overrides) + .Process(this->ForceWeapon_InRange_ApplyRangeModifiers) .Process(this->Ammo_Shared) .Process(this->Ammo_Shared_Group) .Process(this->SelfHealGainType) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 0a1fd72853..6c91ee1e2e 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -149,6 +149,9 @@ class TechnoTypeExt Valueable ForceWeapon_Naval_Decloaked; Valueable ForceWeapon_Cloaked; Valueable ForceWeapon_Disguised; + ValueableVector ForceWeapon_InRange; + ValueableVector ForceWeapon_InRange_Overrides; + Valueable ForceWeapon_InRange_ApplyRangeModifiers; Valueable Ammo_Shared; Valueable Ammo_Shared_Group; @@ -367,6 +370,9 @@ class TechnoTypeExt , ForceWeapon_Naval_Decloaked { -1 } , ForceWeapon_Cloaked { -1 } , ForceWeapon_Disguised { -1 } + , ForceWeapon_InRange {} + , ForceWeapon_InRange_Overrides {} + , ForceWeapon_InRange_ApplyRangeModifiers { false } , Ammo_Shared { false } , Ammo_Shared_Group { -1 } diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index df292a8d83..555144d197 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -270,10 +270,10 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF if (type->WeaponRange_Multiplier == 1.0 && type->WeaponRange_ExtraRange == 0.0) continue; - if (type->WeaponRange_AllowWeapons.size() > 0 && !type->WeaponRange_AllowWeapons.Contains(pThis)) + if (type->WeaponRange_AllowWeapons.size() > 0 && pThis && !type->WeaponRange_AllowWeapons.Contains(pThis)) continue; - if (type->WeaponRange_DisallowWeapons.size() > 0 && type->WeaponRange_DisallowWeapons.Contains(pThis)) + if (type->WeaponRange_DisallowWeapons.size() > 0 && pThis && type->WeaponRange_DisallowWeapons.Contains(pThis)) continue; range = static_cast(range * Math::max(type->WeaponRange_Multiplier, 0.0));