diff --git a/CREDITS.md b/CREDITS.md index 57bdd1af2e..4284c76756 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -514,6 +514,7 @@ This page lists all the individual contributions to the project by their author. - Power plant damage factor - Allow faking digital display for `InfoType=Health` at disguise - Display banner improvement and doc + - Shield respawn animation and weapon - **NaotoYuuki** - Vertical & meteor trajectory projectile prototypes - **handama** - AI script action to `16005 Jump Back To Previous Script` - **TaranDahl (航味麻酱)**: diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 2231358a6e..0147faa0c4 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -325,6 +325,10 @@ SelfHealing.RestartInCombatDelay=0 ; integer, game frames SelfHealing.EnabledBy= ; List of BuildingTypes Respawn=0.0 ; floating point value, percents or absolute Respawn.Rate=0.0 ; floating point value, ingame minutes +Respawn.RestartInCombat=true ; boolean +Respawn.RestartInCombatDelay=0 ; integer, game frames +Respawn.Anim= ; 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 @@ -379,7 +383,11 @@ Shield.ReceivedDamage.MaxMultiplier=1.0 ; floating point value 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.RestartInCombat= ; boolean +Shield.Respawn.RestartInCombatDelay=-1 ; integer, game frames Shield.Respawn.RestartTimer=false ; boolean +Shield.Respawn.Anim= ; 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 @@ -418,8 +426,11 @@ Shield.InheritStateOnReplace=false ; boolean - `SelfHealing` and `Respawn` respect the following settings: 0.0 disables the feature, 1%-100% recovers/respawns the shield strength in percentage, other number recovers/respawns the shield strength directly. Specially, `SelfHealing` with a negative number deducts the shield strength. - 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. + - If `Respawn.RestartInCombat` is set, respawn timer pauses and then resumes after `Respawn.RestartInCombatDelay` frames have passed when the TechnoType has a shield that's under respawn process. - `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. -- `SelfHealing.EnabledBy` can be used to control the self-heal of the shield. If the owner has no structures from this list then the shield won't self-heal. +- `Respawn.Anim`, 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. +- `SelfHealing.EnabledBy` can be used to control the self-heal and respawn of the shield. If the owner has no structures from this list then the shield won't self-heal or respawn. - `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. @@ -457,7 +468,8 @@ 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.Anim` 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` + - Additionally `Shield.Respawn.RestartInCombat` & `Shield.Respawn.RestartInCombatDelay` can be used to override ShieldType settings. - `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 b71bafd62b..2e760ec5a1 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -406,6 +406,7 @@ New: - [Display banner](AI-Scripting-and-Mapping.md#display-banner) (by Morton & ststl) - Allows refineries to use multiple ActiveAnim simultaneously (by TaranDahl) - Electric/RadBeam trail for laser tails (by NetsuNegi) +- Shield respawn animation and weapon (by Ollerus) 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/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 22735a7186..d8370f8fbd 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -75,7 +75,7 @@ void HouseExt::ExtData::UpdateVehicleProduction() for (auto unit : UnitClass::Array) { - auto const index = static_cast(unit->GetType()->GetArrayIndex()); + auto const index = static_cast(unit->Type->GetArrayIndex()); if (values[index] > 0 && unit->CanBeRecruited(pThis)) --values[index]; diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index fbd3ff6483..6e8ed3fd05 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -123,6 +123,10 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) if (nDamageLeft == 0) ReceiveDamageTemp::SkipLowDamageCheck = true; } + else if (!pShieldData->IsAvailable() || pShieldData->GetHP() <= 0) + { + pShieldData->SetRespawnRestartInCombat(); + } } if ((!pWHExt->CanKill || pExt->AE.Unkillable) diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index de497ae471..49d5cb9263 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -676,7 +676,7 @@ DEFINE_HOOK(0x73C602, UnitClass_DrawSHP_WaterType_Extra, 0x6) } } - R->ECX(pThis->GetType()); + R->ECX(pThis->Type); return Continue; } diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 608225ba84..45d1b5a8a4 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -202,7 +202,11 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Shield_Respawn_Amount.Read(exINI, pSection, "Shield.Respawn.Amount"); 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_RestartInCombat.Read(exINI, pSection, "Shield.Respawn.RestartInCombat"); + this->Shield_Respawn_RestartInCombatDelay.Read(exINI, pSection, "Shield.Respawn.RestartInCombatDelay"); this->Shield_Respawn_RestartTimer.Read(exINI, pSection, "Shield.Respawn.RestartTimer"); + this->Shield_Respawn_Anim.Read(exINI, pSection, "Shield.Respawn.Anim"); + 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"); @@ -454,8 +458,12 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->Shield_ReceivedDamage_MaxMultiplier) .Process(this->Shield_Respawn_Duration) .Process(this->Shield_Respawn_Amount) + .Process(this->Shield_Respawn_RestartInCombat) + .Process(this->Shield_Respawn_RestartInCombatDelay) .Process(this->Shield_Respawn_Rate) .Process(this->Shield_Respawn_RestartTimer) + .Process(this->Shield_Respawn_Anim) + .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 273699c3bf..e30eebf428 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -89,7 +89,11 @@ class WarheadTypeExt Valueable Shield_Respawn_Duration; Nullable Shield_Respawn_Amount; Valueable Shield_Respawn_Rate; + Nullable Shield_Respawn_RestartInCombat; + Valueable Shield_Respawn_RestartInCombatDelay; Valueable Shield_Respawn_RestartTimer; + ValueableVector Shield_Respawn_Anim; + Nullable Shield_Respawn_Weapon; Valueable Shield_SelfHealing_Duration; Nullable Shield_SelfHealing_Amount; Valueable Shield_SelfHealing_Rate; @@ -271,10 +275,14 @@ 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_RestartInCombat {} + , Shield_Respawn_RestartInCombatDelay { -1 } , Shield_Respawn_RestartTimer { false } + , Shield_Respawn_Anim { } + , 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 b86013bcf7..8f9d0a8d7c 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -383,8 +383,16 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget) { pShield->SetHP((int)(shieldType->Strength * ratio)); - if (pShield->GetHP() == 0) - pShield->SetRespawn(shieldType->Respawn_Rate, shieldType->Respawn, shieldType->Respawn_Rate, true); + if (this->Shield_ReplaceOnly && this->Shield_InheritStateOnReplace) + { + pShield->SetHP((int)(shieldType->Strength * ratio)); + + if (pShield->GetHP() == 0) + { + pShield->SetRespawn(shieldType->Respawn_Rate, shieldType->Respawn, shieldType->Respawn_Rate, + shieldType->Respawn_RestartInCombat, -1, true, shieldType->Respawn_Anim); + } + } } } } @@ -403,16 +411,18 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget) if (this->Shield_Break && pShield->IsActive() && isShieldTypeEligible(this->Shield_Break_Types.GetElements(this->Shield_AffectTypes))) pShield->BreakShield(this->Shield_BreakAnim, this->Shield_BreakWeapon); - if (this->Shield_Respawn_Duration > 0 && isShieldTypeEligible(this->Shield_Respawn_Types.GetElements(this->Shield_AffectTypes))) + if ((this->Shield_Respawn_Duration > 0 || this->Shield_Respawn_RestartTimer) && isShieldTypeEligible(this->Shield_Respawn_Types.GetElements(this->Shield_AffectTypes))) { - double amount = this->Shield_Respawn_Amount.Get(shieldType->Respawn); - pShield->SetRespawn(this->Shield_Respawn_Duration, amount, this->Shield_Respawn_Rate, this->Shield_Respawn_RestartTimer); + const double amount = this->Shield_Respawn_Amount.Get(shieldType->Respawn); + pShield->SetRespawn(this->Shield_Respawn_Duration, amount, this->Shield_Respawn_Rate, + this->Shield_Respawn_RestartInCombat.Get(shieldType->Respawn_RestartInCombat), + this->Shield_Respawn_RestartInCombatDelay, this->Shield_Respawn_RestartTimer, + this->Shield_Respawn_Anim, this->Shield_Respawn_Weapon); } - if (this->Shield_SelfHealing_Duration > 0 && isShieldTypeEligible(this->Shield_SelfHealing_Types.GetElements(this->Shield_AffectTypes))) + if ((this->Shield_SelfHealing_Duration > 0 || this->Shield_SelfHealing_RestartTimer) && isShieldTypeEligible(this->Shield_SelfHealing_Types.GetElements(this->Shield_AffectTypes))) { - double amount = this->Shield_SelfHealing_Amount.Get(shieldType->SelfHealing); - + const double amount = this->Shield_SelfHealing_Amount.Get(shieldType->SelfHealing); pShield->SetSelfHealing(this->Shield_SelfHealing_Duration, amount, this->Shield_SelfHealing_Rate, this->Shield_SelfHealing_RestartInCombat.Get(shieldType->SelfHealing_RestartInCombat), this->Shield_SelfHealing_RestartInCombatDelay, this->Shield_SelfHealing_RestartTimer); diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index 6cf4b91829..161c6dd460 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -107,6 +107,10 @@ bool ShieldClass::Serialize(T& Stm) .Process(this->SelfHealing_RestartInCombatDelay_Warhead) .Process(this->Respawn_Warhead) .Process(this->Respawn_Rate_Warhead) + .Process(this->Respawn_RestartInCombat_Warhead) + .Process(this->Respawn_RestartInCombatDelay_Warhead) + .Process(this->Respawn_Anim_Warhead) + .Process(this->Respawn_Weapon_Warhead) .Process(this->LastBreakFrame) .Process(this->LastTechnoHealthRatio) .Process(this->IsSelfHealingEnabled) @@ -245,6 +249,7 @@ int ShieldClass::ReceiveDamage(args_ReceiveDamage* args) this->Timers.SelfHealing.Start(rate); // when attacked, restart the timer } } + if (!pWHExt->Nonprovocative) this->ResponseAttack(); @@ -428,12 +433,14 @@ void ShieldClass::AI_Temporal() void ShieldClass::AI() { - if (!this->Techno || this->Techno->InLimbo || this->Techno->IsImmobilized || this->Techno->Transporter) + const auto pTechno = this->Techno; + + if (!pTechno || pTechno->InLimbo || pTechno->IsImmobilized || pTechno->Transporter) return; - if (this->Techno->Health <= 0 || !this->Techno->IsAlive || this->Techno->IsSinking) + if (pTechno->Health <= 0 || !pTechno->IsAlive || pTechno->IsSinking) { - if (auto pTechnoExt = TechnoExt::ExtMap.Find(this->Techno)) + if (const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno)) { pTechnoExt->Shield = nullptr; return; @@ -463,14 +470,14 @@ void ShieldClass::AI() this->SelfHealing(); } - double ratio = this->Techno->GetHealthPercentage(); + const double ratio = pTechno->GetHealthPercentage(); if (!this->AreAnimsHidden) { if (GeneralUtils::HasHealthRatioThresholdChanged(LastTechnoHealthRatio, ratio)) UpdateIdleAnim(); - if (!this->Temporal && this->Online && (this->HP > 0 && this->Techno->Health > 0)) + if (!this->Temporal && this->Online && (this->HP > 0 && pTechno->Health > 0)) this->CreateAnim(); } @@ -504,7 +511,7 @@ void ShieldClass::EnabledByCheck() for (auto const pBuilding : pOwner->Buildings) { - bool isActive = !(pBuilding->Deactivated || pBuilding->IsUnderEMP()) && pBuilding->IsPowerOnline(); + const bool isActive = !(pBuilding->Deactivated || pBuilding->IsUnderEMP()) && pBuilding->IsPowerOnline(); if (this->Type->SelfHealing_EnabledBy.Contains(pBuilding->Type) && isActive) { @@ -616,7 +623,7 @@ bool ShieldClass::ConvertCheck() const auto pTechnoExt = TechnoExt::ExtMap.Find(this->Techno); const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(newID); const auto pOldType = this->Type; - bool allowTransfer = this->Type->AllowTransfer.Get(Attached); + const bool allowTransfer = this->Type->AllowTransfer.Get(Attached); // Update shield type. if (!allowTransfer && !pTechnoTypeExt->ShieldType->Strength) @@ -638,8 +645,8 @@ bool ShieldClass::ConvertCheck() // Update shield properties. if (pNewType->Strength && this->Available) { - bool isDamaged = this->Techno->GetHealthPercentage() <= RulesClass::Instance->ConditionYellow; - double healthRatio = this->GetHealthRatio(); + const bool isDamaged = this->Techno->GetHealthPercentage() <= RulesClass::Instance->ConditionYellow; + const double healthRatio = this->GetHealthRatio(); if (pOldType->GetIdleAnimType(isDamaged, healthRatio) != pNewType->GetIdleAnimType(isDamaged, healthRatio)) this->KillAnim(); @@ -674,49 +681,52 @@ bool ShieldClass::ConvertCheck() void ShieldClass::SelfHealing() { - if (this->Timers.SelfHealing_CombatRestart.InProgress()) - { + const auto timerCombatRestart = &this->Timers.SelfHealing_CombatRestart; + + if (timerCombatRestart->InProgress()) return; - } - else if (this->Timers.SelfHealing_CombatRestart.Completed()) - { - const int rate = this->Timers.SelfHealing_WHModifier.InProgress() ? this->SelfHealing_Rate_Warhead : this->Type->SelfHealing_Rate; - this->Timers.SelfHealing.Start(rate); - this->Timers.SelfHealing_CombatRestart.Stop(); - } const auto pType = this->Type; - const auto timer = &this->Timers.SelfHealing; const auto timerWHModifier = &this->Timers.SelfHealing_WHModifier; + const auto timer = &this->Timers.SelfHealing; + + if (timerCombatRestart->Completed()) + { + const int rate = timerWHModifier->InProgress() ? this->SelfHealing_Rate_Warhead : pType->SelfHealing_Rate; + timer->Start(rate); + timerCombatRestart->Stop(); + } if (timerWHModifier->Completed() && timer->InProgress()) { - double mult = this->SelfHealing_Rate_Warhead > 0 ? Type->SelfHealing_Rate / this->SelfHealing_Rate_Warhead : 1.0; + const double mult = this->SelfHealing_Rate_Warhead > 0 ? pType->SelfHealing_Rate / this->SelfHealing_Rate_Warhead : 1.0; timer->TimeLeft = static_cast(timer->GetTimeLeft() * mult); } const double amount = timerWHModifier->InProgress() ? this->SelfHealing_Warhead : pType->SelfHealing; const int rate = timerWHModifier->InProgress() ? this->SelfHealing_Rate_Warhead : pType->SelfHealing_Rate; const auto percentageAmount = this->GetPercentageAmount(amount); + const int strength = pType->Strength; + int& health = this->HP; if (percentageAmount != 0) { - if ((this->HP < this->Type->Strength || percentageAmount < 0) && timer->StartTime == -1) + if ((health < strength || percentageAmount < 0) && timer->StartTime == -1) timer->Start(rate); - if (this->HP > 0 && timer->Completed()) + if (health > 0 && timer->Completed()) { timer->Start(rate); - this->HP += percentageAmount; + health += percentageAmount; this->UpdateIdleAnim(); - if (this->HP > pType->Strength) + if (health > strength) { - this->HP = pType->Strength; + health = strength; timer->Stop(); } - else if (this->HP <= 0) + else if (health <= 0) { this->BreakShield(); } @@ -738,86 +748,147 @@ int ShieldClass::GetPercentageAmount(double iStatus) void ShieldClass::BreakShield(AnimTypeClass* pBreakAnim, WeaponTypeClass* pBreakWeapon) { this->HP = 0; + const auto pType = this->Type; + const auto pTechno = this->Techno; - if (this->Type->Respawn) - this->Timers.Respawn.Start(Timers.Respawn_WHModifier.InProgress() ? Respawn_Rate_Warhead : this->Type->Respawn_Rate); + if (pType->Respawn) + { + this->Timers.Respawn.Start(this->Timers.Respawn_WHModifier.InProgress() ? this->Respawn_Rate_Warhead : pType->Respawn_Rate); + this->SetRespawnRestartInCombat(); + } this->Timers.SelfHealing.Stop(); this->KillAnim(); if (!this->AreAnimsHidden) { - const auto pAnimType = pBreakAnim ? pBreakAnim : this->Type->BreakAnim; + const auto pAnimType = pBreakAnim ? pBreakAnim : pType->BreakAnim; if (pAnimType) { - auto const pAnim = GameCreate(pAnimType, this->Techno->Location); + auto const pAnim = GameCreate(pAnimType, pTechno->Location); - pAnim->SetOwnerObject(this->Techno); - AnimExt::SetAnimOwnerHouseKind(pAnim, this->Techno->Owner, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(this->Techno); + pAnim->SetOwnerObject(pTechno); + AnimExt::SetAnimOwnerHouseKind(pAnim, pTechno->Owner, nullptr, false, true); + AnimExt::ExtMap.Find(pAnim)->SetInvoker(pTechno); } } - const auto pWeaponType = pBreakWeapon ? pBreakWeapon : this->Type->BreakWeapon; + const auto pWeaponType = pBreakWeapon ? pBreakWeapon : pType->BreakWeapon; this->LastBreakFrame = Unsorted::CurrentFrame; this->UpdateTint(); if (pWeaponType) - TechnoExt::FireWeaponAtSelf(this->Techno, pWeaponType); + TechnoExt::FireWeaponAtSelf(pTechno, pWeaponType); } void ShieldClass::RespawnShield() { - const auto timer = &this->Timers.Respawn; + const auto timerCombatRestart = &this->Timers.Respawn_CombatRestart; + + if (timerCombatRestart->InProgress()) + return; + + const auto pType = this->Type; const auto timerWHModifier = &this->Timers.Respawn_WHModifier; + const auto timer = &this->Timers.Respawn; + + if (timerCombatRestart->Completed()) + { + const int rate = timerWHModifier->InProgress() ? this->Respawn_Rate_Warhead : pType->Respawn_Rate; + timer->Start(rate); + timerCombatRestart->Stop(); + } if (this->HP <= 0 && timer->Completed()) { timer->Stop(); - double amount = timerWHModifier->InProgress() ? Respawn_Warhead : this->Type->Respawn; + const double amount = timerWHModifier->InProgress() ? this->Respawn_Warhead : pType->Respawn; this->HP = this->GetPercentageAmount(amount); this->UpdateTint(); + const auto pAnimList = timerWHModifier->InProgress() ? this->Respawn_Anim_Warhead : pType->Respawn_Anim; + const auto pWeapon = timerWHModifier->InProgress() ? this->Respawn_Weapon_Warhead : pType->Respawn_Weapon; + const auto pTechno = this->Techno; + + AnimExt::CreateRandomAnim(pAnimList, pTechno->Location, pTechno, pTechno->Owner, true, true); + + if (pWeapon) + TechnoExt::FireWeaponAtSelf(pTechno, pWeapon); } else if (timerWHModifier->Completed() && timer->InProgress()) { - double mult = this->Respawn_Rate_Warhead > 0 ? Type->Respawn_Rate / this->Respawn_Rate_Warhead : 1.0; + const double mult = this->Respawn_Rate_Warhead > 0 ? pType->Respawn_Rate / this->Respawn_Rate_Warhead : 1.0; timer->TimeLeft = static_cast(timer->GetTimeLeft() * mult); } } -void ShieldClass::SetRespawn(int duration, double amount, int rate, bool resetTimer) +void ShieldClass::SetRespawn(int duration, double amount, int rate, bool restartInCombat, int restartInCombatDelay, bool resetTimer, std::vector anim, WeaponTypeClass* weapon) { const auto timer = &this->Timers.Respawn; const auto timerWHModifier = &this->Timers.Respawn_WHModifier; + const auto pType = this->Type; - bool modifierTimerInProgress = timerWHModifier->InProgress(); + const bool modifierTimerInProgress = timerWHModifier->InProgress(); this->Respawn_Warhead = amount; - this->Respawn_Rate_Warhead = rate >= 0 ? rate : Type->Respawn_Rate; + this->Respawn_Rate_Warhead = rate >= 0 ? rate : pType->Respawn_Rate; + this->Respawn_RestartInCombat_Warhead = restartInCombat; + this->Respawn_RestartInCombatDelay_Warhead = restartInCombatDelay >= 0 ? restartInCombatDelay : pType->Respawn_RestartInCombatDelay; + this->Respawn_Anim_Warhead = anim; + this->Respawn_Weapon_Warhead = weapon ? weapon : pType->Respawn_Weapon; timerWHModifier->Start(duration); - if (this->HP <= 0 && Respawn_Rate_Warhead >= 0 && resetTimer) + if (this->HP > 0) + return; + + if (resetTimer) { - timer->Start(Respawn_Rate_Warhead); + timer->Start(this->Respawn_Rate_Warhead); } - else if (timer->InProgress() && !modifierTimerInProgress && this->Respawn_Rate_Warhead != Type->Respawn_Rate) + else if (timer->InProgress() && !modifierTimerInProgress && this->Respawn_Rate_Warhead != pType->Respawn_Rate) { - double mult = Type->Respawn_Rate > 0 ? this->Respawn_Rate_Warhead / Type->Respawn_Rate : 1.0; + const double mult = pType->Respawn_Rate > 0 ? this->Respawn_Rate_Warhead / pType->Respawn_Rate : 1.0; timer->TimeLeft = static_cast(timer->GetTimeLeft() * mult); } } +void ShieldClass::SetRespawnRestartInCombat() +{ + if (this->Timers.Respawn.HasStarted()) + { + const auto pType = this->Type; + const bool whModifiersApplied = this->Timers.Respawn_WHModifier.InProgress(); + const bool restart = whModifiersApplied ? this->Respawn_RestartInCombat_Warhead : pType->Respawn_RestartInCombat; + + if (restart) + { + const int delay = whModifiersApplied ? this->Respawn_RestartInCombatDelay_Warhead : pType->Respawn_RestartInCombatDelay; + + if (delay > 0) + { + this->Timers.Respawn_CombatRestart.Start(delay); + this->Timers.Respawn.Stop(); + } + else + { + const int rate = whModifiersApplied ? this->Respawn_Rate_Warhead : pType->Respawn_Rate; + this->Timers.Respawn.Start(rate); // when attacked, restart the timer + } + } + } +} + void ShieldClass::SetSelfHealing(int duration, double amount, int rate, bool restartInCombat, int restartInCombatDelay, bool resetTimer) { - auto timer = &this->Timers.SelfHealing; - auto timerWHModifier = &this->Timers.SelfHealing_WHModifier; + const auto timer = &this->Timers.SelfHealing; + const auto timerWHModifier = &this->Timers.SelfHealing_WHModifier; + const auto pType = this->Type; - bool modifierTimerInProgress = timerWHModifier->InProgress(); + const bool modifierTimerInProgress = timerWHModifier->InProgress(); this->SelfHealing_Warhead = amount; - this->SelfHealing_Rate_Warhead = rate >= 0 ? rate : Type->SelfHealing_Rate; + this->SelfHealing_Rate_Warhead = rate >= 0 ? rate : pType->SelfHealing_Rate; this->SelfHealing_RestartInCombat_Warhead = restartInCombat; - this->SelfHealing_RestartInCombatDelay_Warhead = restartInCombatDelay >= 0 ? restartInCombatDelay : Type->SelfHealing_RestartInCombatDelay; + this->SelfHealing_RestartInCombatDelay_Warhead = restartInCombatDelay >= 0 ? restartInCombatDelay : pType->SelfHealing_RestartInCombatDelay; timerWHModifier->Start(duration); @@ -825,16 +896,16 @@ void ShieldClass::SetSelfHealing(int duration, double amount, int rate, bool res { timer->Start(this->SelfHealing_Rate_Warhead); } - else if (timer->InProgress() && !modifierTimerInProgress && this->SelfHealing_Rate_Warhead != Type->SelfHealing_Rate) + else if (timer->InProgress() && !modifierTimerInProgress && this->SelfHealing_Rate_Warhead != pType->SelfHealing_Rate) { - double mult = Type->SelfHealing_Rate > 0 ? this->SelfHealing_Rate_Warhead / Type->SelfHealing_Rate : 1.0; + const double mult = pType->SelfHealing_Rate > 0 ? this->SelfHealing_Rate_Warhead / pType->SelfHealing_Rate : 1.0; timer->TimeLeft = static_cast(timer->GetTimeLeft() * mult); } } void ShieldClass::CreateAnim() { - auto idleAnimType = this->GetIdleAnimType(); + auto const idleAnimType = this->GetIdleAnimType(); if (this->Cloak && (!idleAnimType || AnimTypeExt::ExtMap.Find(idleAnimType)->DetachOnCloak)) return; @@ -888,7 +959,7 @@ AnimTypeClass* ShieldClass::GetIdleAnimType() if (!this->Type || !this->Techno) return nullptr; - bool isDamaged = this->Techno->GetHealthPercentage() <= RulesClass::Instance->ConditionYellow; + const bool isDamaged = this->Techno->GetHealthPercentage() <= RulesClass::Instance->ConditionYellow; return this->Type->GetIdleAnimType(isDamaged, this->GetHealthRatio()); } diff --git a/src/New/Entity/ShieldClass.h b/src/New/Entity/ShieldClass.h index 5f21af9214..90416a7dba 100644 --- a/src/New/Entity/ShieldClass.h +++ b/src/New/Entity/ShieldClass.h @@ -22,8 +22,9 @@ 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 restartInCombat, int restartInCombatDelay, bool resetTimer, std::vector anim, WeaponTypeClass* weapon = nullptr); void SetSelfHealing(int duration, double amount, int rate, bool restartInCombat, int restartInCombatDelay, bool resetTimer); + void SetRespawnRestartInCombat(); void KillAnim(); void AI_Temporal(); void AI(); @@ -125,6 +126,10 @@ class ShieldClass int SelfHealing_RestartInCombatDelay_Warhead; double Respawn_Warhead; int Respawn_Rate_Warhead; + bool Respawn_RestartInCombat_Warhead; + int Respawn_RestartInCombatDelay_Warhead; + std::vector Respawn_Anim_Warhead; + WeaponTypeClass* Respawn_Weapon_Warhead; int LastBreakFrame; double LastTechnoHealthRatio; @@ -137,6 +142,7 @@ class ShieldClass SelfHealing_CombatRestart { } , SelfHealing { } , SelfHealing_WHModifier { } + , Respawn_CombatRestart { } , Respawn { } , Respawn_WHModifier { } { } @@ -144,6 +150,7 @@ class ShieldClass CDTimerClass SelfHealing_CombatRestart; CDTimerClass SelfHealing; CDTimerClass SelfHealing_WHModifier; + CDTimerClass Respawn_CombatRestart; CDTimerClass Respawn; CDTimerClass Respawn_WHModifier; diff --git a/src/New/Type/ShieldTypeClass.cpp b/src/New/Type/ShieldTypeClass.cpp index 0be108324a..0141618af8 100644 --- a/src/New/Type/ShieldTypeClass.cpp +++ b/src/New/Type/ShieldTypeClass.cpp @@ -46,11 +46,17 @@ void ShieldTypeClass::LoadFromINI(CCINIClass* pINI) this->Powered.Read(exINI, pSection, "Powered"); this->Respawn.Read(exINI, pSection, "Respawn"); + this->Respawn_Anim.Read(exINI, pSection, "Respawn.Anim"); + 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()) this->Respawn_Rate = (int)(Respawn_Rate__InMinutes.Get() * 900); + this->Respawn_RestartInCombat.Read(exINI, pSection, "Respawn.RestartInCombat"); + this->Respawn_RestartInCombatDelay.Read(exINI, pSection, "Respawn.RestartInCombatDelay"); + this->SelfHealing.Read(exINI, pSection, "SelfHealing"); Nullable SelfHealing_Rate__InMinutes; SelfHealing_Rate__InMinutes.Read(exINI, pSection, "SelfHealing.Rate"); @@ -116,6 +122,10 @@ void ShieldTypeClass::Serialize(T& Stm) .Process(this->Powered) .Process(this->Respawn) .Process(this->Respawn_Rate) + .Process(this->Respawn_RestartInCombat) + .Process(this->Respawn_RestartInCombatDelay) + .Process(this->Respawn_Anim) + .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 3da9c70373..12cc02e7e4 100644 --- a/src/New/Type/ShieldTypeClass.h +++ b/src/New/Type/ShieldTypeClass.h @@ -20,6 +20,10 @@ class ShieldTypeClass final : public Enumerable Valueable Powered; Valueable Respawn; Valueable Respawn_Rate; + Valueable Respawn_RestartInCombat; + Valueable Respawn_RestartInCombatDelay; + ValueableVector Respawn_Anim; + Valueable Respawn_Weapon; Valueable SelfHealing; Valueable SelfHealing_Rate; Valueable SelfHealing_RestartInCombat; @@ -73,6 +77,10 @@ class ShieldTypeClass final : public Enumerable , Powered { false } , Respawn { 0.0 } , Respawn_Rate { 0 } + , Respawn_RestartInCombat { true } + , Respawn_RestartInCombatDelay { 0 } + , Respawn_Anim { } + , Respawn_Weapon { } , SelfHealing { 0.0 } , SelfHealing_Rate { 0 } , SelfHealing_RestartInCombat { true }