diff --git a/CREDITS.md b/CREDITS.md index c235ba3dbf..723291856f 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -454,6 +454,7 @@ This page lists all the individual contributions to the project by their author. - Fire weapon when Warhead kills something - Promotion animation deglobalization - Forcing specific weapon by range + - Allowed player's self-healing effects to be benefited by allied or `PlayerControl=true` houses - **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes - **handama** - AI script action to `16005 Jump Back To Previous Script` - **TaranDahl (航味麻酱)**: diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index 52d8f1c329..71e2eb1c09 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -705,6 +705,8 @@ AlternateFLH.OnTurret=true ; boolean - It is now possible to set a global cap for the effects of `InfantryGainSelfHeal` and `UnitsGainSelfHeal` by setting `InfantryGainSelfHealCap` & `UnitsGainSelfHealCap` under `[General]`, respectively. - Whether or not `MultiplayPassive=true` houses benefit from these effects can be controlled via `GainSelfHealAllowMultiplayPassive`. +- In campaign, whether or not these effects for player can be benefited by houses with `PlayerControl=true` can be controlled via `GainSelfHealFromPlayerControl`. +- Whether or not these effects can be benefited by allied houses can be controlled via `GainSelfHealFromAllies`. - It is also possible to change the pip frames displayed from `pips.shp` individually for infantry, units and buildings by setting the frames for infantry & unit self-healing on `Pips.SelfHeal.Infantry/Units/Buildings` under `[AudioVisual]`, respectively. - `Pips.SelfHeal.Infantry/Units/Buildings.Offset` can be used to customize the pixel offsets for the displayed pips, individually for infantry, units and buildings. - Whether or not a TechnoType benefits from effects of `InfantryGainSelfHeal` or `UnitsGainSelfHeal` buildings or neither can now be controlled by setting `SelfHealGainType`. @@ -716,6 +718,8 @@ In `rulesmd.ini`: InfantryGainSelfHealCap= ; integer, maximum amount of InfantryGainSelfHeal that can be in effect at once, must be 1 or higher UnitsGainSelfHealCap= ; integer, maximum amount of UnitsGainSelfHeal that can be in effect at once, must be 1 or higher GainSelfHealAllowMultiplayPassive=true ; boolean +GainSelfHealFromPlayerControl=false ; boolean +GainSelfHealFromAllies=false ; boolean [AudioVisual] Pips.SelfHeal.Infantry=13,20 ; integer, frames of pips.shp for infantry & unit-self healing pips, respectively diff --git a/docs/Whats-New.md b/docs/Whats-New.md index a3c94fe71d..f6b477c82f 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -360,6 +360,7 @@ New: - [Removed dependency on `blowfish.dll`](Miscellanous.md#blowfish-dependency) (by ZivDero) - [Warhead that can not kill](New-or-Enhanced-Logics.md#warhead-that-cannot-kill) (by FS-21) - [Customize parasite culling targets](Fixed-or-Improved-Logics.md#customizing-parasite-culling-targets) (by NetsuNegi) +- Allowed player's self-healing effects to be benefited by allied or `PlayerControl=true` houses (by Ollerus) Vanilla fixes: - Prevent the units with locomotors that cause problems from entering the tank bunker (by TaranDahl) diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 3178c0ab06..153537d0e9 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -71,6 +71,8 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->InfantryGainSelfHealCap.Read(exINI, GameStrings::General, "InfantryGainSelfHealCap"); this->UnitsGainSelfHealCap.Read(exINI, GameStrings::General, "UnitsGainSelfHealCap"); this->GainSelfHealAllowMultiplayPassive.Read(exINI, GameStrings::General, "GainSelfHealAllowMultiplayPassive"); + this->GainSelfHealFromPlayerControl.Read(exINI, GameStrings::General, "GainSelfHealFromPlayerControl"); + this->GainSelfHealFromAllies.Read(exINI, GameStrings::General, "GainSelfHealFromAllies"); this->EnemyInsignia.Read(exINI, GameStrings::General, "EnemyInsignia"); this->DisguiseBlinkingVisibility.Read(exINI, GameStrings::General, "DisguiseBlinkingVisibility"); this->ChronoSparkleDisplayDelay.Read(exINI, GameStrings::General, "ChronoSparkleDisplayDelay"); @@ -322,6 +324,8 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->InfantryGainSelfHealCap) .Process(this->UnitsGainSelfHealCap) .Process(this->GainSelfHealAllowMultiplayPassive) + .Process(this->GainSelfHealFromPlayerControl) + .Process(this->GainSelfHealFromAllies) .Process(this->EnemyInsignia) .Process(this->DisguiseBlinkingVisibility) .Process(this->ChronoSparkleDisplayDelay) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index b4a5d764c2..9ea0778589 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -35,6 +35,8 @@ class RulesExt Nullable InfantryGainSelfHealCap; Nullable UnitsGainSelfHealCap; Valueable GainSelfHealAllowMultiplayPassive; + Valueable GainSelfHealFromPlayerControl; + Valueable GainSelfHealFromAllies; Valueable EnemyInsignia; Valueable DisguiseBlinkingVisibility; Valueable ChronoSparkleDisplayDelay; @@ -212,6 +214,8 @@ class RulesExt , InfantryGainSelfHealCap {} , UnitsGainSelfHealCap {} , GainSelfHealAllowMultiplayPassive { true } + , GainSelfHealFromPlayerControl { false } + , GainSelfHealFromAllies { false } , EnemyInsignia { true } , DisguiseBlinkingVisibility { AffectedHouse::Owner } , ChronoSparkleDisplayDelay { 24 } diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index b462a4f654..28f61d04b0 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -1,6 +1,7 @@ // methods used in TechnoClass_AI hooks or anything similar #include "Body.h" +#include #include #include @@ -686,33 +687,63 @@ void TechnoExt::ApplyGainedSelfHeal(TechnoClass* pThis) if (selfHealType == SelfHealGainType::NoHeal) return; - bool applyHeal = false; + if ((selfHealType == SelfHealGainType::Infantry) + ? (Unsorted::CurrentFrame % RulesClass::Instance->SelfHealInfantryFrames) + : (Unsorted::CurrentFrame % RulesClass::Instance->SelfHealUnitFrames)) + { + return; + } + int amount = 0; - if (selfHealType == SelfHealGainType::Infantry) - { - int count = RulesExt::Global()->InfantryGainSelfHealCap.isset() ? - std::min(std::max(RulesExt::Global()->InfantryGainSelfHealCap.Get(), 1), pThis->Owner->InfantrySelfHeal) : - pThis->Owner->InfantrySelfHeal; + auto countSelfHealing = [pThis](const bool infantryHeal) + { + auto const pOwner = pThis->Owner; + const bool hasCap = infantryHeal ? RulesExt::Global()->InfantryGainSelfHealCap.isset() : RulesExt::Global()->UnitsGainSelfHealCap.isset(); + const int cap = infantryHeal ? RulesExt::Global()->InfantryGainSelfHealCap.Get() : RulesExt::Global()->UnitsGainSelfHealCap.Get(); + int count = std::max(infantryHeal ? pOwner->InfantrySelfHeal : pOwner->UnitsSelfHeal, 1); - amount = RulesClass::Instance->SelfHealInfantryAmount * count; + if (hasCap && count >= cap) + { + count = cap; + return count; + } - if (!(Unsorted::CurrentFrame % RulesClass::Instance->SelfHealInfantryFrames) && amount) - applyHeal = true; - } - else - { - int count = RulesExt::Global()->UnitsGainSelfHealCap.isset() ? - std::min(std::max(RulesExt::Global()->UnitsGainSelfHealCap.Get(), 1), pThis->Owner->UnitsSelfHeal) : - pThis->Owner->UnitsSelfHeal; + const bool allowPlayerControl = RulesExt::Global()->GainSelfHealFromPlayerControl && SessionClass::IsCampaign(); + const bool allowAlliesInCampaign = RulesExt::Global()->GainSelfHealFromAllies && SessionClass::IsCampaign(); + const bool allowAlliesDefault = RulesExt::Global()->GainSelfHealFromAllies && !SessionClass::IsCampaign(); + + if (allowPlayerControl || allowAlliesInCampaign || allowAlliesDefault) + { + for (auto pHouse : HouseClass::Array) + { + if (pHouse == pOwner) + continue; - amount = RulesClass::Instance->SelfHealUnitAmount * count; + if ((allowPlayerControl && pHouse->IsControlledByCurrentPlayer()) + || (allowAlliesInCampaign && !pHouse->IsControlledByCurrentPlayer() && pHouse->IsAlliedWith(pOwner)) + || (allowAlliesDefault && pHouse->IsAlliedWith(pOwner))) + { + count += infantryHeal ? pHouse->InfantrySelfHeal : pHouse->UnitsSelfHeal; - if (!(Unsorted::CurrentFrame % RulesClass::Instance->SelfHealUnitFrames) && amount) - applyHeal = true; - } + if (hasCap && count >= cap) + { + count = cap; + return count; + } + } + } + } + + return count; + }; + + if (selfHealType == SelfHealGainType::Infantry) + amount = RulesClass::Instance->SelfHealInfantryAmount * countSelfHealing(true); + else + amount = RulesClass::Instance->SelfHealUnitAmount * countSelfHealing(false); - if (applyHeal && amount) + if (amount) { if (amount >= healthDeficit) amount = healthDeficit; diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index ad07a722a8..16e5a666e3 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -1,5 +1,6 @@ #include "Body.h" +#include #include #include @@ -26,13 +27,44 @@ void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, Rectang if (pThis->WhatAmI() == AbstractType::Infantry || (pThis->GetTechnoType()->Organic && pThis->WhatAmI() == AbstractType::Unit)) isOrganic = true; - if (pThis->Owner->InfantrySelfHeal > 0 && (hasInfantrySelfHeal || (isOrganic && !hasUnitSelfHeal))) + auto hasSelfHeal = [pThis](const bool infantryHeal) + { + auto const pOwner = pThis->Owner; + + if (infantryHeal ? pOwner->InfantrySelfHeal > 0 : pOwner->UnitsSelfHeal > 0) + return true; + + const bool allowPlayerControl = RulesExt::Global()->GainSelfHealFromPlayerControl && SessionClass::IsCampaign(); + const bool allowAlliesInCampaign = RulesExt::Global()->GainSelfHealFromAllies && SessionClass::IsCampaign(); + const bool allowAlliesDefault = RulesExt::Global()->GainSelfHealFromAllies && !SessionClass::IsCampaign(); + + if (allowPlayerControl || allowAlliesInCampaign || allowAlliesDefault) + { + for (auto pHouse : HouseClass::Array) + { + if (pHouse == pOwner) + continue; + + if ((allowPlayerControl && pHouse->IsControlledByCurrentPlayer()) + || (allowAlliesInCampaign && !pHouse->IsControlledByCurrentPlayer() && pHouse->IsAlliedWith(pOwner)) + || (allowAlliesDefault && pHouse->IsAlliedWith(pOwner))) + { + if (infantryHeal ? pHouse->InfantrySelfHeal > 0 : pHouse->UnitsSelfHeal > 0) + return true; + } + } + } + + return false; + }; + + if ((hasInfantrySelfHeal || (isOrganic && !hasUnitSelfHeal)) && hasSelfHeal(true)) { drawPip = true; selfHealFrames = RulesClass::Instance->SelfHealInfantryFrames; isInfantryHeal = true; } - else if (pThis->Owner->UnitsSelfHeal > 0 && (hasUnitSelfHeal || (pThis->WhatAmI() == AbstractType::Unit && !isOrganic))) + else if ((hasUnitSelfHeal || (pThis->WhatAmI() == AbstractType::Unit && !isOrganic)) && hasSelfHeal(false)) { drawPip = true; selfHealFrames = RulesClass::Instance->SelfHealUnitFrames;