diff --git a/CREDITS.md b/CREDITS.md index 809c481da3..bc94b9e634 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -536,6 +536,7 @@ This page lists all the individual contributions to the project by their author. - Aux technos and TechLevel requirement of superweapon - Allow `AuxBuilding` and Ares' `SW.Aux/NegBuildings` to count building upgrades - Fix the bug where passengers, when their transport unit is removed, would cause incorrect `LimboTracker` counts due to either having their destructor called directly (bypassing `UnInit`) or nested `UnInit` calls resetting the deletion flag too early, thereby breaking auto-death and superweapon auxiliary techno checks + - Improve occupants firing logic - **Apollo** - Translucent SHP drawing patches - **ststl**: - Customizable `ShowTimer` priority of superweapons diff --git a/YRpp b/YRpp index 4f07999249..bc2d038971 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 4f079992496d9e1113d63016861163940133cf8f +Subproject commit bc2d03897106173c7f10368d89e0c945f7273039 diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 7d743f2a52..76ad04c9c8 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1200,6 +1200,19 @@ In `rulesmd.ini`: Cloning.Powered=true ; boolean ``` +### Improve occupants firing logic + +- In vanilla, the range of all occupy weapons can only use the value specified by `[CombatDamage] -> OccupyWeaponRange` . Additionally, the original alternating-fire processing has many limitations; for example, if an occupying unit cannot attack the target due to issues such as `Versus` , `AA` , etc., the entire firing process will get stuck there and be unable to attack. Now you can use a new system. + - `FixOccupyFire` can be used to enable the new system, where all occupying infantry handle firing actions independently without affecting each other. + - `UseGlobalOccupyRange` can be used to determine whether occupy weapons use a unified range. If set to `false` , they will use their own `Range` to determine distance. + +In `rulesmd.ini`: +```ini +[CombatDamage] +FixOccupyFire=false ; boolean +UseGlobalOccupyRange=true ; boolean +``` + ## Infantry ### Auto deploy for GI-like infantry diff --git a/docs/Whats-New.md b/docs/Whats-New.md index e820a418eb..15c2d3a48a 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -581,6 +581,7 @@ New: - [Electric bolt Z-adjust](Fixed-or-Improved-Logics.md#electric-bolt-z-adjust) (by Noble_Fish) - Allow disabling the processing of the Z-depth of EBolt drawn by BuildingType being clamped to non-positive numbers (by Noble_Fish) - Add the `Bolt.ZAdjust` setting item to the LaserTrailType with `DrawType=ebolt` (by Noble_Fish) +- [Improve occupants firing logic](Fixed-or-Improved-Logics.md#improve-occupants-firing-logic) (by NetsuNegi) 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/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po index e883be971f..bbe7450101 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po +++ b/docs/locale/zh_CN/LC_MESSAGES/CREDITS.po @@ -1776,6 +1776,9 @@ msgstr "" msgid "Fix the bug where passengers, when their transport unit is removed, would cause incorrect `LimboTracker` counts due to either having their destructor called directly (bypassing `UnInit`) or nested `UnInit` calls resetting the deletion flag too early, thereby breaking auto-death and superweapon auxiliary techno checks" msgstr "修复了乘客在其所在的运输工具被移除时,因被直接调用析构函数(绕过 `UnInit` )或 `UnInit` 嵌套调用导致删除标志过早重置,从而使 `LimboTracker` 计数错误,进而影响自动死亡及超武辅助单位判定的 Bug" +msgid "Improve occupants firing logic" +msgstr "改进驻扎开火逻辑" + msgid "**Apollo** - Translucent SHP drawing patches" msgstr "**Apollo** - 半透明 SHP 绘制补丁" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po index f721dffa13..8fbfccba0d 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Fixed-or-Improved-Logics.po @@ -3261,6 +3261,33 @@ msgid "" " 2.0, they need power to work. Now you can specific it." msgstr "在原版中,克隆缸即便没电也可以工作。从 Ares 2.0 开始,它们需要电力运作。现在你可以指定。" +msgid "Improve occupants firing logic" +msgstr "改进的驻扎开火逻辑" + +msgid "" +"In vanilla, the range of all occupy weapons can only use the value " +"specified by `[CombatDamage] -> OccupyWeaponRange` . Additionally, the " +"original alternating-fire processing has many limitations; for example, " +"if an occupying unit cannot attack the target due to issues such as " +"`Versus` , `AA` , etc., the entire firing process will get stuck there " +"and be unable to attack. Now you can use a new system." +msgstr "" +"在原版中, 所有驻军武器的射程都只能使用 `[CombatDamage] -> OccupyWeaponRange` 指定的值,并且原有的轮流开火式处理存在诸多限制,例如只要一名驻员由于 `Versus`、`AA` 等问题而无法攻击目标,整个开火流程就会卡在那里而无法攻击。现在你可以使用一套新的系统。" + +msgid "" +"`FixOccupyFire` can be used to enable the new system, where all occupying" +" infantry handle firing actions independently without affecting each " +"other." +msgstr "" +"`FixOccupyFire` 可用于开启新的系统,所有驻军步兵独立处理开火动作,互不影响。" + +msgid "" +"`UseGlobalOccupyRange` can be used to determine whether occupy weapons " +"use a unified range. If set to `false` , they will use their own `Range` " +"to determine distance." +msgstr "" +"`UseGlobalOccupyRange` 可用于决定驻军武器是否使用统一射程。若设为 `false`,它们将会使用自身的 `Range` 决定距离。" + msgid "Infantry" msgstr "步兵" diff --git a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po index c28315fa0e..e583130137 100644 --- a/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po +++ b/docs/locale/zh_CN/LC_MESSAGES/Whats-New.po @@ -1920,6 +1920,13 @@ msgid "" "`DrawType=ebolt` (by Noble_Fish)" msgstr "" +msgid "" +"[Improve occupants firing logic](Fixed-or-Improved-Logics.md#improve-" +"occupants-firing-logic) (by NetsuNegi)" +msgstr "" +"[改进的驻扎开火逻辑](Fixed-or-Improved-Logics.md#improve-occupants-firing-" +"logic)(by NetsuNegi)" + msgid "Vanilla fixes:" msgstr "原版问题修复:" diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 057548877a..c8f0eb05d1 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -6,6 +6,8 @@ BuildingExt::ExtContainer BuildingExt::ExtMap; +bool BuildingExt::ConsideringSpecificOccupant = false; + void BuildingExt::ExtData::DisplayIncomeString() { if (this->AccumulatedIncome) @@ -448,6 +450,72 @@ WeaponStruct* BuildingExt::GetLaserWeapon(BuildingClass* pThis) return pThis->GetPrimaryWeapon(); } +bool BuildingExt::CanOccupantsFire(BuildingClass* pThis, AbstractClass* pTarget) +{ + if (!pTarget) + return false; + + if (RulesExt::Global()->UseGlobalOccupyRange && !pThis->IsCloseEnough(pTarget, 0)) + return false; + + int& firingIdx = pThis->FiringOccupantIndex; + const int originalFiringIdx = firingIdx; + const auto& occupants = pThis->Occupants; + + for (firingIdx = 0; firingIdx < occupants.Count; ++firingIdx) + { + constexpr int weaponIdx = 0; + + switch (pThis->TechnoClass::GetFireError(pTarget, weaponIdx, !RulesExt::Global()->UseGlobalOccupyRange)) + { + case FireError::ILLEGAL: + case FireError::CANT: + case FireError::RANGE: + continue; + + default: + break; + } + + firingIdx = originalFiringIdx; + return true; + } + + firingIdx = originalFiringIdx; + return false; +} + +int BuildingExt::GetOccupantsRange(BuildingClass* pThis) +{ + if (RulesExt::Global()->UseGlobalOccupyRange) + return (pThis->GetOccupyRangeBonus() + RulesClass::Instance->OccupyWeaponRange) << 8; + + int maximumRange = 0; + + for (const auto pOccupier : pThis->Occupants) + { + constexpr int weaponIdx = 0; + auto pWeapon = pOccupier->Veterancy.IsElite() + ? pOccupier->Type->EliteOccupyWeapon.WeaponType + : pOccupier->Type->OccupyWeapon.WeaponType; + + if (!pWeapon) + { + pWeapon = pOccupier->GetWeapon(weaponIdx)->WeaponType; + + if (!pWeapon) + continue; + } + + int range = pWeapon->Range; + + if (maximumRange < range) + maximumRange = range; + } + + return maximumRange; +} + void BuildingExt::KickOutClone(std::pair& info, void*, BuildingClass* pFactory) { if (!pFactory->IsAlive || pFactory->InLimbo || (BuildingTypeExt::ExtMap.Find(pFactory->Type)->Cloning_Powered && !pFactory->IsPowerOnline()) || pFactory->IsBeingWarpedOut()) diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 215d284034..9dd437dbec 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -88,6 +88,8 @@ class BuildingExt static ExtContainer ExtMap; + static bool ConsideringSpecificOccupant; + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -101,5 +103,7 @@ class BuildingExt static void KickOutStuckUnits(BuildingClass* pThis); static const std::vector GetFoundationCells(BuildingClass* pThis, CellStruct baseCoords, bool includeOccupyHeight = false); static WeaponStruct* GetLaserWeapon(BuildingClass* pThis); + static bool CanOccupantsFire(BuildingClass* pThis, AbstractClass* pTarget); + static int GetOccupantsRange(BuildingClass* pThis); static void __fastcall KickOutClone(std::pair& info, void*, BuildingClass* pFactory); }; diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 6f3b790408..c9be89c551 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -954,6 +954,76 @@ DEFINE_HOOK(0x4555E4, BuildingClass_IsPowerOnline_Overpower, 0x6) return overPower < keepOnline ? LowPower : (R->Origin() == 0x4555E4 ? Continue1 : Continue2); } +DEFINE_HOOK(0x44AD07, BuildingClass_Mission_Attack_OccupyFire, 0x6) +{ + enum { ReturnFromFunction = 0x44AFE0 }; + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + return 0; + + GET(BuildingTypeClass*, pType, EAX); + + if (!pType->CanBeOccupied) + return 0; + + GET(BuildingClass*, pThis, ESI); + const auto pTarget = pThis->Target; + bool canFire = false; + const auto& occupants = pThis->Occupants; + + if (pType->CanOccupyFire && occupants.Count && (!RulesExt::Global()->UseGlobalOccupyRange || pThis->IsCloseEnough(pTarget, 0))) + { + int& firingIdx = pThis->FiringOccupantIndex; + const int originalFiringIdx = firingIdx; + BuildingExt::ConsideringSpecificOccupant = true; + + for (firingIdx = 0; firingIdx < occupants.Count;) + { + const auto pOccupier = occupants[firingIdx]; + constexpr int weaponIdx = 0; + const auto fireError = pThis->GetFireError(pTarget, weaponIdx, !RulesExt::Global()->UseGlobalOccupyRange); + + if (fireError == FireError::ILLEGAL + || fireError == FireError::CANT + || fireError == FireError::RANGE) + { + ++firingIdx; + continue; + } + + canFire = true; + + if (fireError != FireError::OK) + { + ++firingIdx; + continue; + } + + pThis->Fire(pTarget, weaponIdx); + + if (pOccupier->IsAlive && pOccupier->Health) + ++firingIdx; + } + + BuildingExt::ConsideringSpecificOccupant = false; + firingIdx = originalFiringIdx; + } + + if (!canFire) + { + pThis->SetTarget(nullptr); + pThis->SupportingPrisms = 0; + + if (pThis->IsNotWarpingIn()) + pThis->TryNextPlanningTokenNode(); + + if (pThis->GetCurrentMission() != Mission::Wait) + pThis->ForceMission(Mission::Guard); + } + + return ReturnFromFunction; +} + #pragma region OwnerChangeBuildupFix static void __fastcall BuildingClass_Place_Wrapper(BuildingClass* pThis, void*, bool captured) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 8f26852261..2dbb97beb6 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -409,6 +409,9 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->Shrapnel_IgnoreHitBuildings.Read(exINI, GameStrings::CombatDamage, "Shrapnel.IgnoreHitBuildings"); + this->FixOccupyFire.Read(exINI, GameStrings::CombatDamage, "FixOccupyFire"); + this->UseGlobalOccupyRange.Read(exINI, GameStrings::CombatDamage, "UseGlobalOccupyRange"); + // Section AITargetTypes int itemsCount = pINI->GetKeyCount("AITargetTypes"); for (int i = 0; i < itemsCount; ++i) @@ -737,6 +740,8 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->FiringAnim_Update) .Process(this->ExtendedPlayerRepair) .Process(this->Shrapnel_IgnoreHitBuildings) + .Process(this->FixOccupyFire) + .Process(this->UseGlobalOccupyRange) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 46fa6c8b83..9fbb5d3111 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -351,6 +351,9 @@ class RulesExt Valueable ShipLocomotorMakesWake; Valueable Shrapnel_IgnoreHitBuildings; + + Valueable FixOccupyFire; + Valueable UseGlobalOccupyRange; ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } @@ -643,6 +646,9 @@ class RulesExt , FiringAnim_Update { false } , ExtendedPlayerRepair { false } , Shrapnel_IgnoreHitBuildings { false } + + , FixOccupyFire { false } + , UseGlobalOccupyRange { true } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index 4c7f814564..6a9864951c 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -540,6 +540,56 @@ DEFINE_HOOK(0x6FC7EB, TechnoClass_CanFire_InterceptBullet, 0x7) return ContinueCheck; } +DEFINE_HOOK(0x6FC94F, TechnoClass_CanFire_Rearm_OccupyFire, 0x6) +{ + enum { ApplyTimer = 0x6FC95B }; + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + return 0; + + GET(TechnoClass*, pThis, ESI); + + if (!pThis->CanOccupyFire()) + return 0; + + const auto pBuilding = static_cast(pThis); + const auto pOccupier = pBuilding->Occupants[pBuilding->FiringOccupantIndex]; + const auto& timer = pOccupier->RearmTimer; + R->EDX(timer.StartTime); + R->EAX(timer.TimeLeft); + return ApplyTimer; +} + +DEFINE_HOOK(0x447F25, BuildingClass_CanFire_OccupyFire, 0x6) +{ + enum { FireErrorIllegal = 0x44805A, ApplyResult = 0x448052 }; + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + return 0; + + if (BuildingExt::ConsideringSpecificOccupant) + return 0; + + GET(BuildingClass*, pThis, ESI); + GET(BuildingTypeClass*, pType, EAX); + + if (!pType->CanOccupyFire || !pThis->Occupants.Count) + return FireErrorIllegal; + + const auto mission = pThis->GetCurrentMission(); + + if (mission == Mission::Construction || mission == Mission::Selling) + return FireErrorIllegal; + + GET_STACK(AbstractClass*, pTarget, STACK_OFFSET(0xC, 0x4)); + + if (!BuildingExt::CanOccupantsFire(pThis, pTarget)) + return FireErrorIllegal; + + R->EBP(FireError::OK); + return ApplyResult; +} + #pragma endregion #pragma region TechnoClass_Fire @@ -662,6 +712,24 @@ DEFINE_HOOK(0x6FDDC0, TechnoClass_FireAt_BeforeTruelyFire, 0x6) return 0; } +DEFINE_HOOK(0x6FDDCA, TechnoClass_FireAt_Suicide_OccupyFire, 0xA) +{ + enum { ReturnFromFunction = 0x6FDE03 }; + + GET(TechnoClass*, pThis, ESI); + TechnoClass* pDie = pThis; + + if (RulesExt::Global()->FixOccupyFire && pThis->CanOccupyFire()) + { + const auto pBuilding = static_cast(pThis); + pDie = pBuilding->Occupants[pBuilding->FiringOccupantIndex]; + } + + int applyDamage = pDie->Health; + pDie->ReceiveDamage(&applyDamage, 0, RulesClass::Instance->C4Warhead, nullptr, true, false, nullptr); + return ReturnFromFunction; +} + DEFINE_HOOK(0x6FE43B, TechnoClass_FireAt_OpenToppedDmgMult, 0x8) { enum { ApplyDamageMult = 0x6FE45A, ContinueCheck = 0x6FE460 }; @@ -750,6 +818,32 @@ DEFINE_HOOK(0x6FE19A, TechnoClass_FireAt_AreaFire, 0x6) return 0; } +DEFINE_HOOK(0x6FE54B, TechnoClass_FireAt_CreateBullet_OccupyFire, 0x7) +{ + enum { SkipCreateBullet = 0x6FE562 }; + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + return 0; + + GET(TechnoClass*, pThis, ESI); + GET(int, damage, EDI); + GET(int, speed, EAX); + GET(WeaponTypeClass*, pWeapon, EBX); + GET(WarheadTypeClass*, pWH, EDX); + GET_BASE(AbstractClass*, pTarget, 0x8); + const bool bridge = R->CL(); + TechnoClass* pOwner = pThis; + + if (pThis->CanOccupyFire()) + { + const auto pBuilding = static_cast(pThis); + pOwner = pBuilding->Occupants[pBuilding->FiringOccupantIndex]; + } + + R->EAX(pWeapon->Projectile->CreateBullet(pTarget, pOwner, damage, pWH, speed, bridge)); + return SkipCreateBullet; +} + DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6) { GET(TechnoClass*, pThis, ESI); @@ -769,6 +863,14 @@ DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6) return 0; } +DEFINE_HOOK(0x6FF031, TechnoClass_FireAt_OccupyFire, 0xA) +{ + GET(TechnoClass*, pThis, ESI); + + R->AL(!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange && pThis->CanOccupyFire()); + return 0x6FF03B; +} + DEFINE_HOOK(0x6FF0DD, TechnoClass_FireAt_TurretRecoil, 0x6) { enum { SkipGameCode = 0x6FF15B }; @@ -778,6 +880,24 @@ DEFINE_HOOK(0x6FF0DD, TechnoClass_FireAt_TurretRecoil, 0x6) return WeaponTypeExt::ExtMap.Find(pWeapon)->TurretRecoil_Suppress ? SkipGameCode : 0; } +DEFINE_HOOK(0x6FF2AA, TechnoClass_FireAt_RearmTimer_OccupyFire, 0x6) +{ + enum { ApplyTimer = 0x6FF2B0 }; + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + return 0; + + GET(TechnoClass*, pThis, ESI); + + if (!pThis->CanOccupyFire()) + return 0; + + const auto pBuilding = static_cast(pThis); + const auto pOccupier = pBuilding->Occupants[pBuilding->FiringOccupantIndex]; + R->EDX(&pOccupier->RearmTimer); + return ApplyTimer; +} + DEFINE_HOOK(0x6FF905, TechnoClass_FireAt_FireOnce, 0x6) { GET(TechnoClass*, pThis, ESI); @@ -792,6 +912,17 @@ DEFINE_HOOK(0x6FF905, TechnoClass_FireAt_FireOnce, 0x6) return 0; } +DEFINE_HOOK(0x6FF923, TechnoClass_FireAt_FireOnce_OccupyFire, 0x6) +{ + enum { SkipResetTarget = 0x6FF92F }; + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + return 0; + + GET(TechnoClass*, pThis, ESI); + return pThis->CanOccupyFire() ? SkipResetTarget : 0; +} + static inline void ToggleLaserWeaponIndex(TechnoClass* pThis, WeaponTypeClass* pWeapon, int weaponIndex) { if (pWeapon->IsLaser) @@ -1199,3 +1330,36 @@ DEFINE_HOOK(0x737086, UnitClass_FiringAI_Gattling, 0x9) } #pragma endregion + +DEFINE_HOOK(0x6FD15E, TechnoClass_GetROF_OccupyFire, 0xA) +{ + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + { + GET(BuildingClass*, pThis, ESI); + + R->EAX(pThis->GetOccupantCount()); + return 0x6FD168; + } + + return 0x6FD183; +} + +DEFINE_HOOK(0x6F7288, TechnoClass_InRange_OccupyFire, 0xA) +{ + enum { SkipGameCode = 0x6F72A2 }; + + GET(BuildingClass*, pThis, ESI); + + if (!RulesExt::Global()->FixOccupyFire && RulesExt::Global()->UseGlobalOccupyRange) + { + R->EDI((RulesClass::Instance->OccupyWeaponRange + pThis->GetOccupyRangeBonus()) << 8); + } + else + { + GET(int, range, EDI); + range += pThis->GetOccupyRangeBonus() << 8; + R->EDI(range); + } + + return SkipGameCode; +} diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index 0663bbe83e..e2f9fd8710 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -1,5 +1,7 @@ #include "Body.h" +#include + // Cursor & target acquisition stuff not directly tied to other features can go here. #pragma region TargetAcquisition @@ -506,3 +508,13 @@ DEFINE_HOOK(0x70CF87, TechnoClass_ThreatCoefficient_CanAttackMeThreatBonus, 0x9) #pragma endregion + +DEFINE_HOOK(0x6F9189, TechnoClass_GreatestThreat_OccupyWeaponRange, 0xA) +{ + enum { ApplyRange = 0x6F91A3 }; + + GET(BuildingClass*, pThis, ESI); + + R->EAX((BuildingExt::GetOccupantsRange(pThis) >> 8) + 1); + return ApplyRange; +}