diff --git a/CREDITS.md b/CREDITS.md index 2b09c3b8e4..9bb1caeb9d 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -620,6 +620,7 @@ This page lists all the individual contributions to the project by their author. - Fix an issue that jumpjet infantries' shadow is always drawn even if they are cloaked - Fix an issue that technos head to building's dock even they are not going to dock - Fix an issue that the jumpjet vehicles cannot stop correctly after going berserk + - Attack and damage technos underground - **solar-III (凤九歌)** - Target scanning delay customization (documentation) - Skip target scanning function calling for unarmed technos (documentation) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index b35a2382e2..b0ea4ed27a 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -747,6 +747,21 @@ OnlyUseLandSequences=false ; boolean ## Projectiles +### Attack technos underground + +- Now, you can enable projectiles to attack technos underground. + - To actually damage the technos, you need [AffectsUnderground](#damage-technos-underground). + +In `rulesmd.ini`: +```ini +[SOMEPROJECTILE] ; Projectile +AU=false ; boolean +``` + +```{note} +Only vanilla projectiles with `Inviso=yes` set or [Phobos projectiles](#projectile-trajectories) `Straight` with `Trajectory.Straight.SubjectToGround=false` enabled and `Bombard` with `Trajectory.Bombard.SubjectToGround=false` enabled can go beneath the ground. Otherwise, the projectile will be forced to detonate upon hitting the ground. +``` + ### Parabombs - Restored feature from Red Alert 1 (also partially implemented in Ares but undocumented, if used together Phobos' version takes priority) that allows projectiles to be parachuted down to ground if fired by an aerial unit. @@ -2271,6 +2286,22 @@ DamageTargetHealthMultiplier=0.0 ; floating point value `DamageAlliesMultiplier` won't affect your own units like `AffectsAllies` did. ``` +### Damage technos underground + +- Now you can make the warhead damage technos underground! + - To allow weapons to target underground technos, you need [AU](#attack-technos-underground). +- Notice that if the projectile detonates underground, its animation effect may look strange. + - You can use `[SOMEWARHEAD] -> PlayAnimUnderground=false` to prevent the warhead animation from playing when the projectile detonates underground. + - You can also use `[SOMEWARHEAD] -> PlayAnimAboveSurface=true` to make the warhead animation play on the ground directly above when the projectile detonates underground. + +In `rulesmd.ini`: +```ini +[SOMEWARHEAD] ; WarheadType +AffectsUnderground=false ; boolean +PlayAnimUnderground=true ; boolean +PlayAnimAboveSurface=false ; boolean +``` + ### Detonate Warhead on all objects on map - Setting `DetonateOnAllMapObjects` to true allows a Warhead that is detonated by a projectile (for an example, this excludes things like animation `Warhead` and Ares' GenericWarhead superweapon but includes `Crit.Warhead` and animation `Weapon`) and consequently any `AirburstWeapon/ShrapnelWeapon` that may follow to detonate on each object currently alive and existing on the map regardless of its actual target, with optional filters. Note that this is done immediately prior Warhead detonation so after `PreImpactAnim` *(Ares feature)* has been displayed. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 06ea6a6365..9481f2e0f9 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -452,6 +452,7 @@ New: - [Customize the chained damage of the wall](Fixed-or-Improved-Logics.md#customize-the-chained-damage-of-the-wall) (by TaranDahl) - Allow the aircraft to enter area guard mission and not crash immediately without any airport (by CrimRecya) - [Unlimbo Detonate warhead](New-or-Enhanced-Logics.md#unlimbo-detonate-warhead) (by FlyStar) +- Attack and damage technos underground (by TaranDahl) 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/Bullet/Hooks.DetonateLogics.cpp b/src/Ext/Bullet/Hooks.DetonateLogics.cpp index 11e5ab6ec9..7a68a207a2 100644 --- a/src/Ext/Bullet/Hooks.DetonateLogics.cpp +++ b/src/Ext/Bullet/Hooks.DetonateLogics.cpp @@ -260,6 +260,15 @@ DEFINE_HOOK(0x469C46, BulletClass_Logics_DamageAnimSelected, 0x8) if (pAnimType) { auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis->WH); + const int cellHeight = MapClass::Instance.GetCellFloorHeight(*coords); + auto const newCrds = pWHExt->PlayAnimAboveSurface ? CoordStruct{ coords->X, coords->Y, Math::max(cellHeight, coords->Z) } : *coords; + + if (cellHeight > newCrds.Z && !pWHExt->PlayAnimUnderground) + { + R->EAX(createdAnim); + return SkipGameCode; + } + auto const pOwner = pThis->Owner; const bool splashed = pWHExt->Splashed; const int creationInterval = splashed ? pWHExt->SplashList_CreationInterval : pWHExt->AnimList_CreationInterval; @@ -311,7 +320,7 @@ DEFINE_HOOK(0x469C46, BulletClass_Logics_DamageAnimSelected, 0x8) if (!pType) continue; - auto animCoords = *coords; + auto animCoords = newCrds; if (allowScatter) { @@ -775,3 +784,69 @@ DEFINE_HOOK(0x469EC0, BulletClass_Logics_AirburstWeapon, 0x6) } #pragma endregion + +#pragma region AffectsUnderground + +// In vanilla, only ground is allowed, and Ares added air and top. +// But it seems that underground and surface is also working fine? +DEFINE_HOOK(0x469453, BulletClass_Logics_TemporalUnderGround, 0x6) +{ + enum { NotOK = 0x469AA4, OK = 0x469475 }; + + GET(FootClass*, pTarget, EAX); + + if (pTarget->InWhichLayer() != Layer::None) + return OK; + + return NotOK; +} + +DEFINE_HOOK(0x4899DA, MapClass_DamageArea_DamageUnderGround, 0x7) +{ + GET_STACK(const bool, isNullified, STACK_OFFSET(0xE0, -0xC9)); + GET_STACK(int, damage, STACK_OFFSET(0xE0, -0xBC)); + GET_STACK(CoordStruct*, pCrd, STACK_OFFSET(0xE0, -0xB8)); + GET_BASE(WarheadTypeClass*, pWH, 0xC); + GET_BASE(TechnoClass*, pSrcTechno, 0x8); + GET_BASE(HouseClass*, pSrcHouse, 0x14); + GET_STACK(bool, hitted, STACK_OFFSET(0xE0, -0xC1)); // bHitted = true + + if (isNullified) + return 0; + + auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + + if (!pWHExt || !pWHExt->AffectsUnderground) + return 0; + + // bool cylinder = pWHExt->CellSpread_Cylinder; + const float spread = pWH->CellSpread; + + for (auto const& pTechno : ScenarioExt::Global()->UndergroundTracker) + { + if (pTechno->InWhichLayer() == Layer::Underground // Layer. + && pTechno->IsAlive && !pTechno->IsIronCurtained() + && !pTechno->IsOnMap // Underground is not on map. + && !pTechno->InLimbo) + { + double dist = 0.0; + auto const technoCoords = pTechno->GetCoords(); + + //if (cylinder) + // dist = CoordStruct{ technoCoords.X - pCrd->X, technoCoords.Y - pCrd->Y, 0 }.Magnitude(); + //else + dist = technoCoords.DistanceFrom(*pCrd); + + if (dist <= spread * Unsorted::LeptonsPerCell) + { + pTechno->ReceiveDamage(&damage, (int)dist, pWH, pSrcTechno, false, false, pSrcHouse); + hitted = true; + } + } + } + + R->Stack8(STACK_OFFSET(0xE0, -0xC1), true); + return 0; +} + +#pragma endregion diff --git a/src/Ext/BulletType/Body.cpp b/src/Ext/BulletType/Body.cpp index 447a856092..a0eadcb2cb 100644 --- a/src/Ext/BulletType/Body.cpp +++ b/src/Ext/BulletType/Body.cpp @@ -73,6 +73,7 @@ void BulletTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Parachuted_FallRate.Read(exINI, pSection, "Parachuted.FallRate"); this->Parachuted_MaxFallRate.Read(exINI, pSection, "Parachuted.MaxFallRate"); this->BombParachute.Read(exINI, pSection, "BombParachute"); + this->AU.Read(exINI, pSection, "AU"); // Ares 0.7 this->BallisticScatter_Min.Read(exINI, pSection, "BallisticScatter.Min"); @@ -170,6 +171,7 @@ void BulletTypeExt::ExtData::Serialize(T& Stm) .Process(this->Parachuted_FallRate) .Process(this->Parachuted_MaxFallRate) .Process(this->BombParachute) + .Process(this->AU) .Process(this->TrajectoryType) // just keep this shit at last ; diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 84cbf8b235..e85909e894 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -72,6 +72,8 @@ class BulletTypeExt Nullable Parachuted_MaxFallRate; Nullable BombParachute; + Valueable AU; + // Ares 0.7 Nullable BallisticScatter_Min; Nullable BallisticScatter_Max; @@ -122,6 +124,7 @@ class BulletTypeExt , Parachuted_FallRate { 1 } , Parachuted_MaxFallRate {} , BombParachute {} + , AU { false } { } virtual ~ExtData() = default; diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index e34e279a85..59dfa4791a 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -169,6 +169,8 @@ void ScenarioExt::ExtData::Serialize(T& Stm) .Process(this->DefaultLS800BkgdPal) .Process(this->MasterDetonationBullet) .Process(this->LimboLaunchers) + .Process(this->UndergroundTracker) + .Process(this->SpecialTracker) ; } diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index 228da80fda..33c851e9ae 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -49,6 +49,9 @@ class ScenarioExt BulletClass* MasterDetonationBullet; // Used to do warhead/weapon detonations on spot without having to create new BulletClass instance every time. std::vector LimboLaunchers; + DynamicVectorClass UndergroundTracker; + DynamicVectorClass SpecialTracker; + ExtData(ScenarioClass* OwnerObject) : Extension(OwnerObject) , ShowBriefing { false } , BriefingTheme { -1 } @@ -64,6 +67,8 @@ class ScenarioExt , DefaultLS800BkgdPal {} , MasterDetonationBullet {} , LimboLaunchers {} + , UndergroundTracker {} + , SpecialTracker {} { } void SetVariableToByID(bool bIsGlobal, int nIndex, char bState); diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index c08ab73c44..195df0c2af 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -57,6 +57,12 @@ TechnoExt::ExtData::~ExtData() } this->ElectricBolts.clear(); + + if (this->UndergroundTracked) + ScenarioExt::Global()->UndergroundTracker.Remove(pThis); + + if (this->SpecialTracked) + ScenarioExt::Global()->SpecialTracker.Remove(pThis); } bool TechnoExt::IsActiveIgnoreEMP(TechnoClass* pThis) @@ -931,6 +937,8 @@ void TechnoExt::ExtData::Serialize(T& Stm) .Process(this->TintIntensityAllies) .Process(this->TintIntensityEnemies) .Process(this->AttackMoveFollowerTempCount) + .Process(this->UndergroundTracked) + .Process(this->SpecialTracked) ; } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index f38356513c..542a437e82 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -96,6 +96,9 @@ class TechnoExt int AttackMoveFollowerTempCount; + bool UndergroundTracked; + bool SpecialTracked; + ExtData(TechnoClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } , Shield {} @@ -156,6 +159,8 @@ class TechnoExt , TintIntensityAllies { 0 } , TintIntensityEnemies { 0 } , AttackMoveFollowerTempCount { 0 } + , UndergroundTracked { false } + , SpecialTracked { false } { } void OnEarlyUpdate(); diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index ac91f6fcec..4b456b6cbe 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -145,10 +145,14 @@ DEFINE_HOOK(0x6F36DB, TechnoClass_WhatWeaponShouldIUse, 0x8) { if (pShield->IsActive()) { - const auto secondary = pThis->GetWeapon(1)->WeaponType; - const bool secondaryIsAA = pTargetTechno && pTargetTechno->IsInAir() && secondary && secondary->Projectile->AA; - - if (secondary && (allowFallback || (allowAAFallback && secondaryIsAA) || TechnoExt::CanFireNoAmmoWeapon(pThis, 1))) + const auto pSecondary = pThis->GetWeapon(1)->WeaponType; + + if (pSecondary + && (allowFallback + || (pTargetTechno + && ((allowAAFallback && pTargetTechno->IsInAir() && pSecondary->Projectile->AA) + || (pTargetTechno->InWhichLayer() == Layer::Underground && BulletTypeExt::ExtMap.Find(pSecondary->Projectile)->AU))) + || TechnoExt::CanFireNoAmmoWeapon(pThis, 1))) { if (!pShield->CanBeTargeted(pThis->GetWeapon(0)->WeaponType)) return Secondary; @@ -171,12 +175,22 @@ DEFINE_HOOK(0x6F37EB, TechnoClass_WhatWeaponShouldIUse_AntiAir, 0x6) GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0x18, -0x4)); GET(WeaponTypeClass*, pSecWeapon, EAX); - if (!pWeapon->Projectile->AA && pSecWeapon->Projectile->AA) + if (const auto pTargetTechno = abstract_cast(pTarget)) { - const auto pTargetTechno = abstract_cast(pTarget); + const auto pPrimaryProj = pWeapon->Projectile; + const auto pSecondaryProj = pSecWeapon->Projectile; - if (pTargetTechno && pTargetTechno->IsInAir()) - return Secondary; + if (!pPrimaryProj->AA && pSecondaryProj->AA) + { + if (pTargetTechno->IsInAir()) + return Secondary; + } + + if (BulletTypeExt::ExtMap.Find(pSecondaryProj)->AU && !BulletTypeExt::ExtMap.Find(pPrimaryProj)->AU) + { + if (pTargetTechno->InWhichLayer() == Layer::Underground) + return Secondary; + } } return Primary; @@ -221,6 +235,11 @@ DEFINE_HOOK(0x6F3432, TechnoClass_WhatWeaponShouldIUse_Gattling, 0xA) { chosenWeaponIndex = evenWeaponIndex; } + else if (pTargetTechno->InWhichLayer() == Layer::Underground) + { + if (BulletTypeExt::ExtMap.Find(pWeaponEven->Projectile)->AU && !BulletTypeExt::ExtMap.Find(pWeaponOdd->Projectile)->AU) + chosenWeaponIndex = evenWeaponIndex; + } else { auto const landType = pTargetTechno->GetCell()->LandType; @@ -430,6 +449,31 @@ DEFINE_HOOK(0x6FCBE6, TechnoClass_CanFire_BridgeAAFix, 0x6) return 0; } +DEFINE_HOOK(0x6FC749, TechnoClass_GetFireError_AntiUnderground, 0x5) +{ + enum { Illegal = 0x6FC86A, GoOtherChecks = 0x6FC762 }; + + GET(const Layer, layer, EAX); + GET(WeaponTypeClass*, pWeapon, EDI); + + switch (layer) + { + case Layer::Air: + case Layer::Top: + if (!pWeapon->Projectile->AA) + return Illegal; + break; + case Layer::Underground: + if (!BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->AU) + return Illegal; + break; + default: + break; + } + + return GoOtherChecks; +} + #pragma endregion #pragma region TechnoClass_Fire diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index d00addb2c2..fb5669c234 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -11,6 +11,7 @@ #include #include #include +#include #include #include #include @@ -1298,3 +1299,163 @@ DEFINE_HOOK(0x71A8BD, TemporalClass_Update_WarpAwayAnim, 0x5) return 0; } + + +#pragma region AttackUnderGround + +DEFINE_HOOK(0x70023B, TechnoClass_MouseOverObject_AttackUnderGround, 0x5) +{ + enum { FireIsOK = 0x700246, FireIsNotOK = 0x70056C }; + + GET(ObjectClass*, pObject, EDI); + GET(TechnoClass*, pThis, ESI); + GET(const int, wpIdx, EAX); + + if (pObject->IsSurfaced()) + return FireIsOK; + + auto const pWeapon = pThis->GetWeapon(wpIdx)->WeaponType; + + return (!pWeapon || !BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->AU) ? FireIsNotOK : FireIsOK; +} + +DEFINE_HOOK_AGAIN(0x729029, TunnelLocomotionClass_Process_Track, 0x7); +DEFINE_HOOK(0x728F9A, TunnelLocomotionClass_Process_Track, 0x7) +{ + // GET(FootClass*, pTechno, ECX); + GET(ILocomotion*, pThis, ESI); + + const auto pLoco = static_cast(pThis); + const auto pTechno = pLoco->LinkedTo; + ScenarioExt::Global()->UndergroundTracker.AddUnique(pTechno); + TechnoExt::ExtMap.Find(pTechno)->UndergroundTracked = true; + + return 0; +} + +DEFINE_HOOK(0x7297F6, TunnelLocomotionClass_ProcessDigging_Track, 0x7) +{ + GET(FootClass*, pTechno, ECX); + + ScenarioExt::Global()->UndergroundTracker.Remove(pTechno); + TechnoExt::ExtMap.Find(pTechno)->UndergroundTracked = false; + + return 0; +} + +DEFINE_HOOK(0x772AB3, WeaponTypeClass_AllowedThreats_AU, 0x5) +{ + GET(BulletTypeClass* const, pType, ECX); + GET(const ThreatType, flags, EAX); + + if (BulletTypeExt::ExtMap.Find(pType)->AU) + R->EAX(static_cast(flags) | 0x20000u); + + return 0; +} + +namespace SelectAutoTarget_Context +{ + bool AU = false; +} + +DEFINE_HOOK(0x6F8DF0, TechnoClass_SelectAutoTarget_Start_AU, 0x9) +{ + GET_STACK(const unsigned int, flags, 0x4); + SelectAutoTarget_Context::AU = (flags & 0x20000u) != 0; + return 0; +} + +DEFINE_HOOK(0x6F8FA8, TechnoClass_SelectAutoTarget_SetCanTargetWhatAmI_AU, 0x6) +{ + REF_STACK(int, canTargetWhatAmI, STACK_OFFSET(0x6C, -0x58)); + + if (SelectAutoTarget_Context::AU || ScenarioExt::Global()->SpecialTracker.Count) + { + canTargetWhatAmI |= 1 << (int)InfantryClass::AbsID; + canTargetWhatAmI |= 1 << (int)UnitClass::AbsID; + canTargetWhatAmI |= 1 << (int)AircraftClass::AbsID; + } + + return 0; +} + +DEFINE_HOOK(0x6F93BB, TechnoClass_SelectAutoTarget_Scan_AU, 0x6) +{ + enum { FuncRet = 0x6F9DA1, Continue = 0x6F93C1 }; + + REF_STACK(const TechnoClass*, pBestTarget, STACK_OFFSET(0x6C, -0x4C)); + REF_STACK(int, bestThreat, STACK_OFFSET(0x6C, -0x50)); + GET_STACK(const bool, transportMCed, STACK_OFFSET(0x6C, -0x59)); + GET_STACK(const bool, onlyTargetEnemyHouse, STACK_OFFSET(0x6C, 0xC)); + GET_STACK(const int, canTargetWhatAmI, STACK_OFFSET(0x6C, -0x58)); + GET_STACK(const int, wantedDist, STACK_OFFSET(0x6C, -0x40)); + GET_STACK(const ThreatType, flags, STACK_OFFSET(0x6C, 0x4)); + GET(TechnoClass* const, pThis, ESI); + + const auto pType = pThis->GetTechnoType(); + const auto pOwner = pThis->Owner; + const bool targetFriendly = pType->AttackFriendlies || pThis->Berzerk || transportMCed || pThis->CombatDamage(-1) < 0; + + int threatBuffer = 0; + auto tempCrd = CoordStruct::Empty; + + for (const auto pCurrent : ScenarioExt::Global()->SpecialTracker) + { + if ((!pOwner->IsAlliedWith(pCurrent) || targetFriendly) + && (!onlyTargetEnemyHouse || pCurrent->Owner->ArrayIndex == pThis->Owner->EnemyHouseIndex) + && pThis->CanAutoTargetObject(flags, canTargetWhatAmI, wantedDist, pCurrent, &threatBuffer, UINT_MAX, &tempCrd)) + { + if (pType->DistributedFire) + { + pThis->CurrentTargets.AddItem(pCurrent); + pThis->CurrentTargetThreatValues.AddItem(threatBuffer); + } + + if (threatBuffer > bestThreat) + { + pBestTarget = pCurrent; + bestThreat = threatBuffer; + } + } + } + + if (SelectAutoTarget_Context::AU) + { + for (const auto pCurrent : ScenarioExt::Global()->UndergroundTracker) + { + if ((!pOwner->IsAlliedWith(pCurrent) || targetFriendly) + && (!onlyTargetEnemyHouse || pCurrent->Owner->ArrayIndex == pThis->Owner->EnemyHouseIndex) + && pThis->CanAutoTargetObject(flags, canTargetWhatAmI, wantedDist, pCurrent, &threatBuffer, UINT_MAX, &tempCrd)) + { + if (pType->DistributedFire) + { + pThis->CurrentTargets.AddItem(pCurrent); + pThis->CurrentTargetThreatValues.AddItem(threatBuffer); + } + + if (threatBuffer > bestThreat) + { + pBestTarget = pCurrent; + bestThreat = threatBuffer; + } + } + } + } + + GET(int, rangeFindingCell, ECX); + + return rangeFindingCell <= 0 ? FuncRet : Continue; +} + +DEFINE_HOOK(0x6F7E1E, TechnoClass_CanAutoTargetObject_AU, 0x6) +{ + enum { Continue = 0x6F7E24, ReturnFalse = 0x6F894F }; + + GET(TechnoClass*, pTarget, ESI); + GET(const int, height, EAX); + + return height >= -20 || SelectAutoTarget_Context::AU || TechnoExt::ExtMap.Find(pTarget)->SpecialTracked ? Continue : ReturnFalse; +} + +#pragma endregion diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 550b19d1b3..f32b351178 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -297,6 +297,10 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->UnlimboDetonate_KeepTarget.Read(exINI, pSection, "UnlimboDetonate.KeepTarget"); this->UnlimboDetonate_KeepSelected.Read(exINI, pSection, "UnlimboDetonate.KeepSelected"); + this->AffectsUnderground.Read(exINI, pSection, "AffectsUnderground"); + this->PlayAnimUnderground.Read(exINI, pSection, "PlayAnimUnderground"); + this->PlayAnimAboveSurface.Read(exINI, pSection, "PlayAnimAboveSurface"); + // Convert.From & Convert.To TypeConvertGroup::Parse(this->Convert_Pairs, exINI, pSection, AffectedHouse::All); @@ -575,6 +579,10 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) .Process(this->UnlimboDetonate_KeepTarget) .Process(this->UnlimboDetonate_KeepSelected) + .Process(this->AffectsUnderground) + .Process(this->PlayAnimUnderground) + .Process(this->PlayAnimAboveSurface) + // Ares tags .Process(this->AffectsEnemies) .Process(this->AffectsOwner) diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 4429a4548f..271d1d36f5 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -200,6 +200,10 @@ class WarheadTypeExt Valueable UnlimboDetonate_KeepTarget; Valueable UnlimboDetonate_KeepSelected; + Valueable AffectsUnderground; + Valueable PlayAnimUnderground; + Valueable PlayAnimAboveSurface; + // Ares tags // http://ares-developers.github.io/Ares-docs/new/warheads/general.html Valueable AffectsEnemies; @@ -420,6 +424,10 @@ class WarheadTypeExt , UnlimboDetonate_ForceLocation { false } , UnlimboDetonate_KeepTarget { true } , UnlimboDetonate_KeepSelected { true } + + , AffectsUnderground { false } + , PlayAnimUnderground { true } + , PlayAnimAboveSurface { false } { } void ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget);