diff --git a/README.md b/README.md index 327226c265..6f8e1e227a 100644 --- a/README.md +++ b/README.md @@ -70,6 +70,7 @@ Credits - Xkein - general assistance, YRpp edits - thomassneddon - general assistance - Starkku - Warhead shield penetration & breaking, strafing aircraft weapon customization +- SukaHati (Erzoid) - Minimum interceptor guard range Thanks to everyone who uses Phobos, tests changes and reports bugs! You can show your appreciation and help project by displaying the logo (monochrome version can be found [here](https://github.com/Phobos-developers/Phobos/logo-mono.png)) in your client/launcher, contributing or donating to us via links on the right and the `Sponsor` button on top of the repo. diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 721364d064..2341461e85 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -273,13 +273,19 @@ SplashList.PickRandom=no ; play a random animation from the list? boolean, defau *Interception logic used in [Tiberium Crisis](https://www.moddb.com/mods/tiberium-crisis) mod* - Projectiles can now be made targetable by certain TechnoTypes. Interceptor TechnoType's projectile must be `Inviso=yes` in order for it to work and the projectile must be used in a primary Weapon. + - `Interceptor.GuardRange` is maximum range of the unit to intercept projectile. The unit weapon range will limit the unit interception range though. + - `Interceptor.EliteGuardRange` value is used if the unit veterancy is Elite. + - `Interceptor.MinimumGuardRange` is the minimum range of the unit to intercept projectile. Any projectile under this range will not be intercepted. + - `Interceptor.EliteMinimumGuardRange` value is used if the unit veterancy is Elite. In `rulesmd.ini`: ```ini -[SOMETECHNO] ; TechnoType -Interceptor=no ; boolean -Interceptor.GuardRange=0.0 ; double -Interceptor.EliteGuardRange=0.0 ; double +[SOMETECHNO] ; TechnoType +Interceptor=no ; boolean +Interceptor.GuardRange=0.0 ; double +Interceptor.EliteGuardRange=0.0 ; double +Interceptor.MinimumGuardRange=0.0 ; double +Interceptor.EliteMinimumGuardRange=0.0 ; double [SOMEPROJECTILE] ; Projectile Interceptable=no ; boolean diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 015979d40b..7cd3ce8595 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -14,141 +14,147 @@ TechnoExt::ExtContainer TechnoExt::ExtMap; void TechnoExt::ApplyMindControlRangeLimit(TechnoClass* pThis) { - if (auto Capturer = pThis->MindControlledBy) - { - auto pCapturerExt = TechnoTypeExt::ExtMap.Find(Capturer->GetTechnoType()); - if (pCapturerExt && pCapturerExt->MindControlRangeLimit > 0 && pThis->DistanceFrom(Capturer) > pCapturerExt->MindControlRangeLimit * 256.0) - { - Capturer->CaptureManager->FreeUnit(pThis); - - if (!pThis->IsHumanControlled) - pThis->QueueMission(Mission::Hunt, 0); - } - } + if (auto Capturer = pThis->MindControlledBy) + { + auto pCapturerExt = TechnoTypeExt::ExtMap.Find(Capturer->GetTechnoType()); + if (pCapturerExt && pCapturerExt->MindControlRangeLimit > 0 && pThis->DistanceFrom(Capturer) > pCapturerExt->MindControlRangeLimit * 256.0) + { + Capturer->CaptureManager->FreeUnit(pThis); + + if (!pThis->IsHumanControlled) + pThis->QueueMission(Mission::Hunt, 0); + } + } } void TechnoExt::ApplyInterceptor(TechnoClass* pThis) { - auto pData = TechnoExt::ExtMap.Find(pThis); - auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); - if (pData && pTypeData && pTypeData->Interceptor && !pThis->Target && - !(pThis->WhatAmI() == AbstractType::Aircraft && pThis->GetHeight() <= 0)) - { - for (auto const& pBullet : *BulletClass::Array) - { - if (auto pBulletTypeData = BulletTypeExt::ExtMap.Find(pBullet->Type)) - { - if (!pBulletTypeData->Interceptable) - continue; - } - - const double guardRange = pThis->Veterancy.IsElite() ? - pTypeData->Interceptor_EliteGuardRange * 256 : pTypeData->Interceptor_GuardRange * 256; - - if (pBullet->Location.DistanceFrom(pThis->Location) > guardRange) - continue; - - if (pBullet->Location.DistanceFrom(pBullet->TargetCoords) > - double(ScenarioClass::Instance->Random.RandomRanged(128, (int)guardRange / 10)) * 10) - { - continue; - } - - if (!pThis->Owner->IsAlliedWith(pBullet->Owner)) - { - pThis->SetTarget(pBullet); - pData->InterceptedBullet = pBullet; - break; - } - } - } + auto pData = TechnoExt::ExtMap.Find(pThis); + auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + + if (pData && pTypeData && pTypeData->Interceptor && !pThis->Target && + !(pThis->WhatAmI() == AbstractType::Aircraft && pThis->GetHeight() <= 0)) + { + for (auto const& pBullet : *BulletClass::Array) + { + if (auto pBulletTypeData = BulletTypeExt::ExtMap.Find(pBullet->Type)) + { + if (!pBulletTypeData->Interceptable) + continue; + } + + const double guardRange = pThis->Veterancy.IsElite() ? + pTypeData->Interceptor_EliteGuardRange * 256 : pTypeData->Interceptor_GuardRange * 256; + const double minguardRange = pThis->Veterancy.IsElite() ? + pTypeData->Interceptor_EliteMinimumGuardRange * 256 : pTypeData->Interceptor_MinimumGuardRange * 256; + + double distance = pBullet->Location.DistanceFrom(pThis->Location); + if (distance > guardRange || distance < minguardRange) + continue; + + /* + if (pBullet->Location.DistanceFrom(pBullet->TargetCoords) > + double(ScenarioClass::Instance->Random.RandomRanged(128, (int)guardRange / 10)) * 10) + { + continue; + } + */ + + if (!pThis->Owner->IsAlliedWith(pBullet->Owner)) + { + pThis->SetTarget(pBullet); + pData->InterceptedBullet = pBullet; + break; + } + } + } } void TechnoExt::ApplyPowered_KillSpawns(TechnoClass* pThis) { - auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); - if (pTypeData && pThis->WhatAmI() == AbstractType::Building) - { - auto pBuilding = abstract_cast(pThis); - if (pTypeData->Powered_KillSpawns && pBuilding->Type->Powered && !pBuilding->IsPowerOnline()) - { - if (auto pManager = pBuilding->SpawnManager) - { - pManager->ResetTarget(); - for (auto pItem : pManager->SpawnedNodes) - { - if (pItem->Status == SpawnNodeStatus::Attacking || pItem->Status == SpawnNodeStatus::Returning) - { - pItem->Unit->ReceiveDamage(&pItem->Unit->Health, 0, - RulesClass::Global()->C4Warhead, nullptr, false, false, nullptr); - } - } - } - } - } + auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + if (pTypeData && pThis->WhatAmI() == AbstractType::Building) + { + auto pBuilding = abstract_cast(pThis); + if (pTypeData->Powered_KillSpawns && pBuilding->Type->Powered && !pBuilding->IsPowerOnline()) + { + if (auto pManager = pBuilding->SpawnManager) + { + pManager->ResetTarget(); + for (auto pItem : pManager->SpawnedNodes) + { + if (pItem->Status == SpawnNodeStatus::Attacking || pItem->Status == SpawnNodeStatus::Returning) + { + pItem->Unit->ReceiveDamage(&pItem->Unit->Health, 0, + RulesClass::Global()->C4Warhead, nullptr, false, false, nullptr); + } + } + } + } + } } void TechnoExt::ApplySpawn_LimitRange(TechnoClass* pThis) { - auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); - if (pTypeData && pTypeData->Spawn_LimitedRange) - { - if (auto pManager = pThis->SpawnManager) - { - auto pTechnoType = pThis->GetTechnoType(); - int weaponRange = 0; - int weaponRangeExtra = pTypeData->Spawn_LimitedExtraRange * 256; - - auto setWeaponRange = [&weaponRange](WeaponTypeClass* pWeaponType) - { - if (pWeaponType && pWeaponType->Spawner && pWeaponType->Range > weaponRange) - weaponRange = pWeaponType->Range; - }; - - setWeaponRange(pTechnoType->Weapon[0].WeaponType); - setWeaponRange(pTechnoType->Weapon[1].WeaponType); - setWeaponRange(pTechnoType->EliteWeapon[0].WeaponType); - setWeaponRange(pTechnoType->EliteWeapon[1].WeaponType); - - weaponRange += weaponRangeExtra; - - if (pManager->Target && (pThis->DistanceFrom(pManager->Target) > weaponRange)) - pManager->ResetTarget(); - } - } + auto pTypeData = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + if (pTypeData && pTypeData->Spawn_LimitedRange) + { + if (auto pManager = pThis->SpawnManager) + { + auto pTechnoType = pThis->GetTechnoType(); + int weaponRange = 0; + int weaponRangeExtra = pTypeData->Spawn_LimitedExtraRange * 256; + + auto setWeaponRange = [&weaponRange](WeaponTypeClass* pWeaponType) + { + if (pWeaponType && pWeaponType->Spawner && pWeaponType->Range > weaponRange) + weaponRange = pWeaponType->Range; + }; + + setWeaponRange(pTechnoType->Weapon[0].WeaponType); + setWeaponRange(pTechnoType->Weapon[1].WeaponType); + setWeaponRange(pTechnoType->EliteWeapon[0].WeaponType); + setWeaponRange(pTechnoType->EliteWeapon[1].WeaponType); + + weaponRange += weaponRangeExtra; + + if (pManager->Target && (pThis->DistanceFrom(pManager->Target) > weaponRange)) + pManager->ResetTarget(); + } + } } bool TechnoExt::IsHarvesting(TechnoClass* pThis) { - if (!pThis || pThis->InLimbo) - return false; - - auto slave = pThis->SlaveManager; - if (slave && slave->State != SlaveManagerStatus::Ready) - return true; + if (!pThis || pThis->InLimbo) + return false; - if (pThis->WhatAmI() == AbstractType::Building && pThis->IsPowerOnline()) - return true; + auto slave = pThis->SlaveManager; + if (slave && slave->State != SlaveManagerStatus::Ready) + return true; - auto mission = pThis->GetCurrentMission(); - if ((mission == Mission::Harvest || mission == Mission::Unload || mission == Mission::Enter) - && TechnoExt::HasAvailableDock(pThis)) - { - return true; - } + if (pThis->WhatAmI() == AbstractType::Building && pThis->IsPowerOnline()) + return true; - return false; + auto mission = pThis->GetCurrentMission(); + if ((mission == Mission::Harvest || mission == Mission::Unload || mission == Mission::Enter) + && TechnoExt::HasAvailableDock(pThis)) + { + return true; + } + + return false; } bool TechnoExt::HasAvailableDock(TechnoClass* pThis) { - for (auto pBld : pThis->GetTechnoType()->Dock) - { - if (pThis->Owner->CountOwnedAndPresent(pBld)) - return true; - } + for (auto pBld : pThis->GetTechnoType()->Dock) + { + if (pThis->Owner->CountOwnedAndPresent(pBld)) + return true; + } - return false; + return false; } // ============================= @@ -157,34 +163,34 @@ bool TechnoExt::HasAvailableDock(TechnoClass* pThis) template void TechnoExt::ExtData::Serialize(T& Stm) { - Stm - .Process(this->InterceptedBullet) - .Process(this->ShieldData) - ; + Stm + .Process(this->InterceptedBullet) + .Process(this->ShieldData) + ; } void TechnoExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); - this->Serialize(Stm); + Extension::LoadFromStream(Stm); + this->Serialize(Stm); } void TechnoExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); - this->Serialize(Stm); + Extension::SaveToStream(Stm); + this->Serialize(Stm); } bool TechnoExt::LoadGlobals(PhobosStreamReader& Stm) { - return Stm - .Success(); + return Stm + .Success(); } bool TechnoExt::SaveGlobals(PhobosStreamWriter& Stm) { - return Stm - .Success(); + return Stm + .Success(); } // ============================= @@ -201,43 +207,43 @@ void TechnoExt::ExtContainer::InvalidatePointer(void* ptr, bool bRemoved) { } DEFINE_HOOK(6F3260, TechnoClass_CTOR, 5) { - GET(TechnoClass*, pItem, ESI); + GET(TechnoClass*, pItem, ESI); - TechnoExt::ExtMap.FindOrAllocate(pItem); + TechnoExt::ExtMap.FindOrAllocate(pItem); - return 0; + return 0; } DEFINE_HOOK(6F4500, TechnoClass_DTOR, 5) { - GET(TechnoClass*, pItem, ECX); + GET(TechnoClass*, pItem, ECX); - TechnoExt::ExtMap.Remove(pItem); + TechnoExt::ExtMap.Remove(pItem); - return 0; + return 0; } DEFINE_HOOK_AGAIN(70C250, TechnoClass_SaveLoad_Prefix, 8) DEFINE_HOOK(70BF50, TechnoClass_SaveLoad_Prefix, 5) { - GET_STACK(TechnoClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); + GET_STACK(TechnoClass*, pItem, 0x4); + GET_STACK(IStream*, pStm, 0x8); - TechnoExt::ExtMap.PrepareStream(pItem, pStm); + TechnoExt::ExtMap.PrepareStream(pItem, pStm); - return 0; + return 0; } DEFINE_HOOK(70C249, TechnoClass_Load_Suffix, 5) { - TechnoExt::ExtMap.LoadStatic(); + TechnoExt::ExtMap.LoadStatic(); - return 0; + return 0; } DEFINE_HOOK(70C264, TechnoClass_Save_Suffix, 5) { - TechnoExt::ExtMap.SaveStatic(); + TechnoExt::ExtMap.SaveStatic(); - return 0; + return 0; } \ No newline at end of file diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index b373d0e066..cecd3c8745 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -89,7 +89,9 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->MindControlRangeLimit.Read(exINI, pSection, "MindControlRangeLimit"); this->Interceptor.Read(exINI, pSection, "Interceptor"); this->Interceptor_GuardRange.Read(exINI, pSection, "Interceptor.GuardRange"); + this->Interceptor_MinimumGuardRange.Read(exINI, pSection, "Interceptor.MinimumGuardRange"); this->Interceptor_EliteGuardRange.Read(exINI, pSection, "Interceptor.EliteGuardRange"); + this->Interceptor_EliteMinimumGuardRange.Read(exINI, pSection, "Interceptor.EliteMinimumGuardRange"); this->Powered_KillSpawns.Read(exINI, pSection, "Powered.KillSpawns"); this->Spawn_LimitedRange.Read(exINI, pSection, "Spawner.LimitRange"); this->Spawn_LimitedExtraRange.Read(exINI, pSection, "Spawner.ExtraLimitRange"); @@ -136,7 +138,9 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->MindControlRangeLimit) .Process(this->Interceptor) .Process(this->Interceptor_GuardRange) + .Process(this->Interceptor_MinimumGuardRange) .Process(this->Interceptor_EliteGuardRange) + .Process(this->Interceptor_EliteMinimumGuardRange) .Process(this->GroupAs) .Process(this->TurretOffset) .Process(this->Powered_KillSpawns) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 0731acfb2d..7992123d59 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -22,7 +22,9 @@ class TechnoTypeExt Valueable MindControlRangeLimit; Valueable Interceptor; Valueable Interceptor_GuardRange; + Valueable Interceptor_MinimumGuardRange; Valueable Interceptor_EliteGuardRange; + Valueable Interceptor_EliteMinimumGuardRange; Valueable TurretOffset; Valueable Powered_KillSpawns; Valueable Spawn_LimitedRange; @@ -53,7 +55,9 @@ class TechnoTypeExt MindControlRangeLimit(-1.0), Interceptor(false), Interceptor_GuardRange(0.0), + Interceptor_MinimumGuardRange(0.0), Interceptor_EliteGuardRange(0.0), + Interceptor_EliteMinimumGuardRange(0.0), TurretOffset({0, 0, 0}), Powered_KillSpawns(false), Spawn_LimitedRange(false), @@ -89,25 +93,26 @@ class TechnoTypeExt void ApplyTurretOffset(Matrix3D* mtx, double factor = 1.0); bool IsCountedAsHarvester(); - // Ares 0.A - const char* GetSelectionGroupID() const; + // Ares 0.A + const char* GetSelectionGroupID() const; - private: - template - void Serialize(T& Stm); - }; + private: + template + void Serialize(T& Stm); + }; - class ExtContainer final : public Container { - public: - ExtContainer(); - ~ExtContainer(); - }; + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; - static ExtContainer ExtMap; + static ExtContainer ExtMap; - static void ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor = 1.0); + static void ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor = 1.0); - // Ares 0.A - static const char* GetSelectionGroupID(ObjectTypeClass* pType); - static bool HasSelectionGroupID(ObjectTypeClass* pType, const char* pID); -}; \ No newline at end of file + // Ares 0.A + static const char* GetSelectionGroupID(ObjectTypeClass* pType); + static bool HasSelectionGroupID(ObjectTypeClass* pType, const char* pID); +};