From 64c26a7467eac96f24df82be94bfda3de715bac7 Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 20 Feb 2022 15:08:11 +0200 Subject: [PATCH 1/2] Small shield improvements / additions - Allocate BreakWeapon if not found to prevent potential problems and for consistency - Add Shield.MinimumReplaceDelay that controls how long after shield was broken can it be replaced if Shield.ReplaceNonRespawning is set. --- docs/New-or-Enhanced-Logics.md | 4 +++- src/Ext/WarheadType/Body.cpp | 4 +++- src/Ext/WarheadType/Body.h | 2 ++ src/Ext/WarheadType/Detonate.cpp | 3 ++- src/New/Entity/ShieldClass.cpp | 7 +++++++ src/New/Entity/ShieldClass.h | 3 +++ src/New/Type/ShieldTypeClass.cpp | 2 +- 7 files changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 5227d73bbb..49678b01fb 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -63,6 +63,7 @@ Shield.AttachTypes= ; List of ShieldType names Shield.RemoveTypes= ; List of ShieldType names Shield.ReplaceOnly=false ; boolean Shield.ReplaceNonRespawning=false ; boolean +Shield.MinimumReplaceDelay=0 ; integer, game frames Shield.InheritStateOnReplace=false ; boolean ``` - Now you can have a shield for any TechnoType. It serves as a second health pool with independent `Armor` and `Strength` values. @@ -83,7 +84,7 @@ Shield.InheritStateOnReplace=false ; boolean - `IdleAnim.TemporalAction` indicates what happens to the animation when the shield is attacked by temporal weapons. - `BreakAnim`, if set, will be played when the shield has been broken. - `HitAnim`, if set, will be played when the shield is attacked, similar to `WeaponNullifyAnim` for Iron Curtain. -- `BreakWeapon`, if set, will be fired at the TechnoType once the shield breaks. Note: for this to work it requires that the WeaponType used has been explicitly listed in `[WeaponTypes]`, which is only available with Ares. +- `BreakWeapon`, if set, will be fired at the TechnoType once the shield breaks. - `AbsorbPercent` controls the percentage of damage that will be absorbed by the shield. Defaults to 1.0, meaning full damage absorption. - `PassPercent` controls the percentage of damage that will *not* be absorbed by the shield, and will be dealt to the unit directly even if the shield is active. Defaults to 0.0 - no penetration. - `AllowTransfer` controls whether or not the shield can be transferred if the TechnoType changes (such as `(Un)DeploysInto` or Ares type conversion). If not set, defaults to true if shield was attached via `Shield.AttachTypes`, otherwise false. @@ -105,6 +106,7 @@ Shield.InheritStateOnReplace=false ; boolean - `Shield.AttachTypes` & `Shield.RemoveTypes` allows listing ShieldTypes that are attached or removed, respectively from any targets affected by the warhead (positive `Verses` values). Normally only first listed ShieldType in `Shield.AttachTypes` is applied. - If `Shield.ReplaceOnly` is set, shields from `Shield.AttachTypes` are only applied to affected targets from which shields were simultaneously removed, matching the order listed in `Shield.RemoveTypes`. If `Shield.AttachTypes` contains less items than `Shield.RemoveTypes`, last item from the former is used for any remaining removed shields. - If `Shield.ReplaceNonRespawning` is set, shield from `Shield.AttachTypes` replaces existing shields that have been broken and cannot respawn on their own. + - `Shield.MinimumReplaceDelay` can be used to control how long after the shield has been broken (in game frames) can it be replaced. If not enough frames have passed, it won't be replaced. - If `Shield.InheritStateOnReplace` is set, shields replaced via `Shield.ReplaceOnly` inherit the current strength (relative to ShieldType `Strength`) of the previous shield and whether or not the shield was currently broken. Self-healing and respawn timers are always reset. ### Laser Trails diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 5643af8934..78d815bfbc 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -68,7 +68,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Shield_Break.Read(exINI, pSection, "Shield.Break"); this->Shield_BreakAnim.Read(exINI, pSection, "Shield.BreakAnim"); this->Shield_HitAnim.Read(exINI, pSection, "Shield.HitAnim"); - this->Shield_BreakWeapon.Read(exINI, pSection, "Shield.BreakWeapon"); + this->Shield_BreakWeapon.Read(exINI, pSection, "Shield.BreakWeapon", true); this->Shield_AbsorbPercent.Read(exINI, pSection, "Shield.AbsorbPercent"); this->Shield_PassPercent.Read(exINI, pSection, "Shield.PassPercent"); this->Shield_Respawn_Duration.Read(exINI, pSection, "Shield.Respawn.Duration"); @@ -86,6 +86,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Shield_ReplaceOnly.Read(exINI, pSection, "Shield.ReplaceOnly"); this->Shield_ReplaceNonRespawning.Read(exINI, pSection, "Shield.ReplaceNonRespawning"); this->Shield_InheritStateOnReplace.Read(exINI, pSection, "Shield.InheritStateOnReplace"); + this->Shield_MinimumReplaceDelay.Read(exINI, pSection, "Shield.MinimumReplaceDelay"); this->Shield_AffectTypes.Read(exINI, pSection, "Shield.AffectTypes"); this->NotHuman_DeathSequence.Read(exINI, pSection, "NotHuman.DeathSequence"); @@ -141,6 +142,7 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->Shield_ReplaceOnly) .Process(this->Shield_ReplaceNonRespawning) .Process(this->Shield_InheritStateOnReplace) + .Process(this->Shield_MinimumReplaceDelay) .Process(this->Shield_AffectTypes) .Process(this->NotHuman_DeathSequence) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 720d82a0b1..caa3ee6762 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -64,6 +64,7 @@ class WarheadTypeExt Valueable Shield_ReplaceOnly; Valueable Shield_ReplaceNonRespawning; Valueable Shield_InheritStateOnReplace; + Valueable Shield_MinimumReplaceDelay; ValueableVector Shield_AffectTypes; private: @@ -116,6 +117,7 @@ class WarheadTypeExt , Shield_ReplaceOnly { false } , Shield_ReplaceNonRespawning { false } , Shield_InheritStateOnReplace { false } + , Shield_MinimumReplaceDelay { 0 } , Shield_AffectTypes {} , NotHuman_DeathSequence { -1 } diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 3d8a9aa359..08289cf724 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -125,7 +125,8 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget) if (shieldType) { - if (shieldType->Strength && (!pExt->Shield || (this->Shield_ReplaceNonRespawning && pExt->Shield->IsBrokenAndNonRespawning()))) + if (shieldType->Strength && (!pExt->Shield || (this->Shield_ReplaceNonRespawning && pExt->Shield->IsBrokenAndNonRespawning() && + pExt->Shield->GetFramesSinceLastBroken() >= this->Shield_MinimumReplaceDelay))) { pExt->CurrentShieldType = shieldType; pExt->Shield = std::make_unique(pTarget, true); diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index fe97f17918..90cd905172 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -525,6 +525,8 @@ void ShieldClass::BreakShield(AnimTypeClass* pBreakAnim, WeaponTypeClass* pBreak const auto pWeaponType = pBreakWeapon ? pBreakWeapon : this->Type->BreakWeapon.Get(nullptr); + this->LastBreakFrame = Unsorted::CurrentFrame; + if (pWeaponType) TechnoExt::FireWeaponAtSelf(this->Techno, pWeaponType); } @@ -768,6 +770,11 @@ ShieldTypeClass* ShieldClass::GetType() return this->Type; } +int ShieldClass::GetFramesSinceLastBroken() +{ + return Unsorted::CurrentFrame - this->LastBreakFrame; +} + bool ShieldClass::IsActive() { return diff --git a/src/New/Entity/ShieldClass.h b/src/New/Entity/ShieldClass.h index 7dc3fc4591..57cdb95393 100644 --- a/src/New/Entity/ShieldClass.h +++ b/src/New/Entity/ShieldClass.h @@ -38,6 +38,7 @@ class ShieldClass bool IsAvailable(); bool IsBrokenAndNonRespawning(); ShieldTypeClass* GetType(); + int GetFramesSinceLastBroken(); static void SyncShieldToAnother(TechnoClass* pFrom, TechnoClass* pTo); @@ -86,6 +87,8 @@ class ShieldClass double Respawn_Warhead; int Respawn_Rate_Warhead; + int LastBreakFrame; + ShieldTypeClass* Type; struct Timers diff --git a/src/New/Type/ShieldTypeClass.cpp b/src/New/Type/ShieldTypeClass.cpp index 8d5e6d8a0b..71a534cd60 100644 --- a/src/New/Type/ShieldTypeClass.cpp +++ b/src/New/Type/ShieldTypeClass.cpp @@ -43,7 +43,7 @@ void ShieldTypeClass::LoadFromINI(CCINIClass* pINI) this->BreakAnim.Read(exINI, pSection, "BreakAnim"); this->HitAnim.Read(exINI, pSection, "HitAnim"); - this->BreakWeapon.Read(exINI, pSection, "BreakWeapon"); + this->BreakWeapon.Read(exINI, pSection, "BreakWeapon", true); this->AbsorbPercent.Read(exINI, pSection, "AbsorbPercent"); this->PassPercent.Read(exINI, pSection, "PassPercent"); From 854292edfbd18fa5e5bda16c73ed6b4a98ec1533 Mon Sep 17 00:00:00 2001 From: Starkku Date: Mon, 21 Feb 2022 13:54:46 +0200 Subject: [PATCH 2/2] Fix negative self-healing not starting without entering combat on unpowered shields. --- src/New/Entity/ShieldClass.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index 90cd905172..c5c614bf28 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -463,7 +463,7 @@ void ShieldClass::SelfHealing() if (percentageAmount != 0) { - if (this->HP < this->Type->Strength && timer->StartTime == -1) + if ((this->HP < this->Type->Strength || percentageAmount < 0) && timer->StartTime == -1) timer->Start(rate); if (this->HP > 0 && timer->Completed())