diff --git a/CREDITS.md b/CREDITS.md index c8555f9fa..a09c52ca2 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -358,6 +358,7 @@ This page lists all the individual contributions to the project by their author. - **Ollerus** - Build limit group enhancement - Customizable rocker amplitude + - Shield respawn animation and weapon - **handama** - AI script action to jump back to previous script - **Ares developers** - YRpp and Syringe which are used, save/load, project foundation and generally useful code from Ares diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index f42cf5bb7..053358967 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -276,6 +276,8 @@ SelfHealing.RestartInCombat=true ; boolean SelfHealing.RestartInCombatDelay=0 ; integer, game frames Respawn=0.0 ; floating point value, percents or absolute Respawn.Rate=0.0 ; floating point value, ingame minutes +Respawn.Anims= ; list of Animation +Respawn.Weapon= ; WeaponType BracketDelta=0 ; integer - pixels Pips=-1,-1,-1 ; integer, frames of pips.shp (zero-based) for Green, Yellow, Red Pips.Building=-1,-1,-1 ; integer, frames of pips.shp (zero-based) for Green, Yellow, Red @@ -330,6 +332,8 @@ Shield.Respawn.Duration=0 ; integer, game frames Shield.Respawn.Amount=0.0 ; floating point value, percents or absolute Shield.Respawn.Rate=-1.0 ; floating point value, ingame minutes Shield.Respawn.RestartTimer=false ; boolean +Shield.Respawn.Anims= ; list of Animation +Shield.Respawn.Weapon= ; WeaponType Shield.SelfHealing.Duration=0 ; integer, game frames Shield.SelfHealing.Amount=0.0 ; floating point value, percents or absolute Shield.SelfHealing.Rate=-1.0 ; floating point value, ingame minutes @@ -366,6 +370,8 @@ Shield.InheritStateOnReplace=false ; boolean - If you want shield recovers/respawns 1 HP per time, currently you need to set tag value to any number between 1 and 2, like `1.1`. - If `SelfHealing.RestartInCombat` is set, self-healing timer pauses and then resumes after `SelfHealing.RestartInCombatDelay` frames have passed when the shield gets damaged. - `SelfHealing.Rate` and `Respawn.Rate` respect the following settings: 0.0 instantly recovers the shield, other values determine the frequency of shield recovers/respawns in ingame minutes. +- `Respawn.Anims`, if set, will be played when the shield respawns. If more than one animation is listed, a random one is selected. +- `Respawn.Weapon`, if set, will be fired at the TechnoType once the shield respawns. - `IdleAnim`, if set, will be played while the shield is intact. This animation is automatically set to loop indefinitely. - `IdleAnim.ConditionYellow` and `IdleAnim.ConditionRed` can be used to set different animations for when shield health is at or below the percentage defined in `[AudioVisual]`->`ConditionYellow`/`ConditionRed`, respectively. If `IdleAnim.ConditionRed` is not set it falls back to `IdleAnim.ConditionYellow`, which in turn falls back to `IdleAnim`. - `IdleAnimDamaged`, `IdleAnimDamaged.ConditionYellow` and `IdleAnimDamaged.ConditionRed` are used in an identical manner, but only when health of the object the shield is attached to is at or below `[AudioVisual]`->`ConditionYellow`. Follows similar fallback sequence to regular `IdleAnim` variants and if none are set, falls back to the regular `IdleAnim` or variants thereof. @@ -402,7 +408,7 @@ Shield.InheritStateOnReplace=false ; boolean - `Shield.PassPercent` overrides the `PassPercent` value set in the ShieldType that is being damaged. - `Shield.ReceivedDamage.Minimum` & `Shield.ReceivedDamage.Maximum` override the values set in in the ShieldType that is being damaged. - `Shield.ReceivedDamage.MinMultiplier` and `Shield.ReceivedDamage.MinMultiplier` are multipliers to the effective `Shield.ReceivedDamage.Minimum` and `Shield.ReceivedDamage.Maximum` respectively that are applied when the Warhead deals damage to a shield. - - `Shield.Respawn.Rate` & `Shield.Respawn.Amount` override ShieldType `Respawn.Rate` and `Respawn.Amount` for duration of `Shield.Respawn.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.Respawn.RestartTimer` is set, currently running shield respawn timer is reset, otherwise the timer's duration is adjusted in proportion to the new `Shield.Respawn.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while respawn timer is running, remaining time is adjusted to proportionally match ShieldType `Respawn.Rate`. Re-applying the effect resets the duration to `Shield.Respawn.Duration` + - `Shield.Respawn.Rate`, `Shield.Respawn.Amount`, `Shield.Respawn.Anims` and `Shield.Respawn.Weapon` override ShieldType `Respawn.Rate`, `Respawn.Amount`, `Respawn.Anims` and `Respawn.Weapon` for duration of `Shield.Respawn.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.Respawn.RestartTimer` is set, currently running shield respawn timer is reset, otherwise the timer's duration is adjusted in proportion to the new `Shield.Respawn.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while respawn timer is running, remaining time is adjusted to proportionally match ShieldType `Respawn.Rate`. Re-applying the effect resets the duration to `Shield.Respawn.Duration` - `Shield.SelfHealing.Rate` & `Shield.SelfHealing.Amount` override ShieldType `SelfHealing.Rate` and `SelfHealing.Amount` for duration of `Shield.SelfHealing.Duration` amount of frames. Negative rate & zero or lower amount default to ShieldType values. If `Shield.SelfHealing.RestartTimer` is set, currently running self-healing timer is restarted, otherwise timer's duration is adjusted in proportion to the new `Shield.SelfHealing.Rate` (e.g timer will be same percentage through before and after) without restarting the timer. If the effect expires while self-healing timer is running, remaining time is adjusted to proportionally match ShieldType `SelfHealing.Rate`. Re-applying the effect resets the duration to `Shield.SelfHealing.Duration`. - Additionally `Shield.SelfHealing.RestartInCombat` & `Shield.SelfHealing.RestartInCombatDelay` can be used to override ShieldType settings. - `Shield.AffectTypes` allows listing which ShieldTypes can be affected by any of the effects listed above. If none are listed, all ShieldTypes are affected. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index dd8bb1a74..7ce42ccc4 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -451,6 +451,7 @@ New: - Nonprovocative Warheads (by Starkku) - Option to restore `PowerSurplus` setting for AI (by Starkku) - `FireOnce` infantry sequence reset toggle (by Starkku) +- Shield respawn animation and weapon (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/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 281a42af6..3ea143e77 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -211,6 +211,8 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Shield_Respawn_Rate_InMinutes.Read(exINI, pSection, "Shield.Respawn.Rate"); this->Shield_Respawn_Rate = (int)(this->Shield_Respawn_Rate_InMinutes * 900); this->Shield_Respawn_RestartTimer.Read(exINI, pSection, "Shield.Respawn.RestartTimer"); + this->Shield_Respawn_Anims.Read(exINI, pSection, "Shield.Respawn.Anims"); + this->Shield_Respawn_Weapon.Read(exINI, pSection, "Shield.Respawn.Weapon"); this->Shield_SelfHealing_Duration.Read(exINI, pSection, "Shield.SelfHealing.Duration"); this->Shield_SelfHealing_Amount.Read(exINI, pSection, "Shield.SelfHealing.Amount"); this->Shield_SelfHealing_Rate_InMinutes.Read(exINI, pSection, "Shield.SelfHealing.Rate"); @@ -423,6 +425,8 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->Shield_Respawn_Amount) .Process(this->Shield_Respawn_Rate) .Process(this->Shield_Respawn_RestartTimer) + .Process(this->Shield_Respawn_Anims) + .Process(this->Shield_Respawn_Weapon) .Process(this->Shield_SelfHealing_Duration) .Process(this->Shield_SelfHealing_Amount) .Process(this->Shield_SelfHealing_Rate) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 90b9968f7..36b0997a3 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -86,6 +86,8 @@ class WarheadTypeExt Nullable Shield_Respawn_Amount; Valueable Shield_Respawn_Rate; Valueable Shield_Respawn_RestartTimer; + ValueableVector Shield_Respawn_Anims; + Nullable Shield_Respawn_Weapon; Valueable Shield_SelfHealing_Duration; Nullable Shield_SelfHealing_Amount; Valueable Shield_SelfHealing_Rate; @@ -233,10 +235,12 @@ class WarheadTypeExt , Shield_ReceivedDamage_MaxMultiplier { 1.0 } , Shield_Respawn_Duration { 0 } - , Shield_Respawn_Amount { 0.0 } + , Shield_Respawn_Amount { } , Shield_Respawn_Rate { -1 } , Shield_Respawn_Rate_InMinutes { -1.0 } , Shield_Respawn_RestartTimer { false } + , Shield_Respawn_Anims { } + , Shield_Respawn_Weapon { } , Shield_SelfHealing_Duration { 0 } , Shield_SelfHealing_Amount { } , Shield_SelfHealing_Rate { -1 } diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 356e1426a..421311307 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -235,7 +235,7 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget, TechnoE pTargetExt->Shield->SetHP((int)(shieldType->Strength * ratio)); if (pTargetExt->Shield->GetHP() == 0) - pTargetExt->Shield->SetRespawn(shieldType->Respawn_Rate, shieldType->Respawn, shieldType->Respawn_Rate, true); + pTargetExt->Shield->SetRespawn(shieldType->Respawn_Rate, shieldType->Respawn, shieldType->Respawn_Rate, true, shieldType->Respawn_Anims); } } } @@ -258,7 +258,8 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget, TechnoE if (this->Shield_Respawn_Duration > 0 && isShieldTypeEligible(this->Shield_Respawn_Types.GetElements(this->Shield_AffectTypes))) { double amount = this->Shield_Respawn_Amount.Get(pTargetExt->Shield->GetType()->Respawn); - pTargetExt->Shield->SetRespawn(this->Shield_Respawn_Duration, amount, this->Shield_Respawn_Rate, this->Shield_Respawn_RestartTimer); + pTargetExt->Shield->SetRespawn(this->Shield_Respawn_Duration, amount, this->Shield_Respawn_Rate, this->Shield_Respawn_RestartTimer, + this->Shield_Respawn_Anims, this->Shield_Respawn_Weapon); } if (this->Shield_SelfHealing_Duration > 0 && isShieldTypeEligible(this->Shield_SelfHealing_Types.GetElements(this->Shield_AffectTypes))) diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index 4d105f780..8901dfb7b 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -93,6 +93,8 @@ bool ShieldClass::Serialize(T& Stm) .Process(this->SelfHealing_RestartInCombatDelay_Warhead) .Process(this->Respawn_Warhead) .Process(this->Respawn_Rate_Warhead) + .Process(this->Respawn_Anims_Warhead) + .Process(this->Respawn_Weapon_Warhead) .Process(this->LastBreakFrame) .Process(this->LastTechnoHealthRatio) .Success(); @@ -711,6 +713,23 @@ void ShieldClass::RespawnShield() double amount = timerWHModifier->InProgress() ? Respawn_Warhead : this->Type->Respawn; this->HP = this->GetPercentageAmount(amount); this->UpdateTint(); + const auto pAnimList = timerWHModifier->InProgress() ? this->Respawn_Anims_Warhead : this->Type->Respawn_Anims; + const auto pWeapon = timerWHModifier->InProgress() ? this->Respawn_Weapon_Warhead : this->Type->Respawn_Weapon; + + if (!pAnimList.empty()) + { + if (const auto pAnimType = pAnimList[ScenarioClass::Instance->Random.RandomRanged(0, pAnimList.size() - 1)]) + { + if (auto const pAnim = GameCreate(pAnimType, this->Techno->Location)) + { + pAnim->SetOwnerObject(this->Techno); + pAnim->Owner = this->Techno->Owner; + } + } + } + + if (pWeapon) + TechnoExt::FireWeaponAtSelf(this->Techno, pWeapon); } else if (timerWHModifier->Completed() && timer->InProgress()) { @@ -719,7 +738,7 @@ void ShieldClass::RespawnShield() } } -void ShieldClass::SetRespawn(int duration, double amount, int rate, bool resetTimer) +void ShieldClass::SetRespawn(int duration, double amount, int rate, bool resetTimer, std::vector anim, WeaponTypeClass* weapon) { const auto timer = &this->Timers.Respawn; const auto timerWHModifier = &this->Timers.Respawn_WHModifier; @@ -727,6 +746,8 @@ void ShieldClass::SetRespawn(int duration, double amount, int rate, bool resetTi bool modifierTimerInProgress = timerWHModifier->InProgress(); this->Respawn_Warhead = amount; this->Respawn_Rate_Warhead = rate >= 0 ? rate : Type->Respawn_Rate; + this->Respawn_Anims_Warhead = anim; + this->Respawn_Weapon_Warhead = weapon ? weapon : Type->Respawn_Weapon; timerWHModifier->Start(duration); diff --git a/src/New/Entity/ShieldClass.h b/src/New/Entity/ShieldClass.h index 74ab5bea2..1a20dd3eb 100644 --- a/src/New/Entity/ShieldClass.h +++ b/src/New/Entity/ShieldClass.h @@ -22,7 +22,7 @@ class ShieldClass bool CanBePenetrated(WarheadTypeClass* pWarhead) const; void BreakShield(AnimTypeClass* pBreakAnim = nullptr, WeaponTypeClass* pBreakWeapon = nullptr); - void SetRespawn(int duration, double amount, int rate, bool resetTimer); + void SetRespawn(int duration, double amount, int rate, bool resetTimer, std::vector anim, WeaponTypeClass* weapon = nullptr); void SetSelfHealing(int duration, double amount, int rate, bool restartInCombat, int restartInCombatDelay, bool resetTimer); void KillAnim(); void AI_Temporal(); @@ -100,6 +100,8 @@ class ShieldClass int SelfHealing_RestartInCombatDelay_Warhead; double Respawn_Warhead; int Respawn_Rate_Warhead; + std::vector Respawn_Anims_Warhead; + WeaponTypeClass* Respawn_Weapon_Warhead; int LastBreakFrame; double LastTechnoHealthRatio; diff --git a/src/New/Type/ShieldTypeClass.cpp b/src/New/Type/ShieldTypeClass.cpp index f913e6447..7fac73587 100644 --- a/src/New/Type/ShieldTypeClass.cpp +++ b/src/New/Type/ShieldTypeClass.cpp @@ -43,6 +43,8 @@ void ShieldTypeClass::LoadFromINI(CCINIClass* pINI) this->Powered.Read(exINI, pSection, "Powered"); this->Respawn.Read(exINI, pSection, "Respawn"); + this->Respawn_Anims.Read(exINI, pSection, "Respawn.Anims"); + this->Respawn_Weapon.Read(exINI, pSection, "Respawn.Weapon"); Nullable Respawn_Rate__InMinutes; Respawn_Rate__InMinutes.Read(exINI, pSection, "Respawn.Rate"); if (Respawn_Rate__InMinutes.isset()) @@ -109,6 +111,8 @@ void ShieldTypeClass::Serialize(T& Stm) .Process(this->Powered) .Process(this->Respawn) .Process(this->Respawn_Rate) + .Process(this->Respawn_Anims) + .Process(this->Respawn_Weapon) .Process(this->SelfHealing) .Process(this->SelfHealing_Rate) .Process(this->SelfHealing_RestartInCombat) diff --git a/src/New/Type/ShieldTypeClass.h b/src/New/Type/ShieldTypeClass.h index 2dfc4d5e5..b02f94e7b 100644 --- a/src/New/Type/ShieldTypeClass.h +++ b/src/New/Type/ShieldTypeClass.h @@ -18,6 +18,8 @@ class ShieldTypeClass final : public Enumerable Valueable Powered; Valueable Respawn; Valueable Respawn_Rate; + ValueableVector Respawn_Anims; + Valueable Respawn_Weapon; Valueable SelfHealing; Valueable SelfHealing_Rate; Valueable SelfHealing_RestartInCombat; @@ -67,6 +69,8 @@ class ShieldTypeClass final : public Enumerable , Powered { false } , Respawn { 0.0 } , Respawn_Rate { 0 } + , Respawn_Anims { } + , Respawn_Weapon { } , SelfHealing { 0.0 } , SelfHealing_Rate { 0 } , SelfHealing_RestartInCombat { true }