From 230b7743b8c6dff78eeb814760cc77c736a87172 Mon Sep 17 00:00:00 2001
From: Trsdy <914137150@qq.com>
Date: Sat, 2 Mar 2024 21:29:32 +0800
Subject: [PATCH] DropPod properties per InfantryType (#1196)
---
CREDITS.md | 1 +
Phobos.vcxproj | 2 +
docs/Fixed-or-Improved-Logics.md | 27 +++
docs/Whats-New.md | 1 +
src/Ext/Rules/Body.cpp | 9 +
src/Ext/Rules/Body.h | 4 +
src/Ext/Techno/Body.Update.cpp | 39 +++-
src/Ext/Techno/Body.h | 2 +
src/Ext/Techno/Hooks.cpp | 19 +-
src/Ext/TechnoType/Body.cpp | 12 ++
src/Ext/TechnoType/Body.h | 3 +
src/New/Type/Affiliated/DroppodTypeClass.cpp | 201 +++++++++++++++++++
src/New/Type/Affiliated/DroppodTypeClass.h | 31 +++
src/New/Type/LaserTrailTypeClass.cpp | 2 +
src/New/Type/LaserTrailTypeClass.h | 2 +
15 files changed, 333 insertions(+), 22 deletions(-)
create mode 100644 src/New/Type/Affiliated/DroppodTypeClass.cpp
create mode 100644 src/New/Type/Affiliated/DroppodTypeClass.h
diff --git a/CREDITS.md b/CREDITS.md
index 1f4054da67..12b4d88669 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -292,6 +292,7 @@ This page lists all the individual contributions to the project by their author.
- Teleport timer reset after load game fix
- Teleport and Tunnel loco visual tilt fix
- Skip units' turret rotation and jumpjets' wobbling under EMP
+ - Droppod properties dehardcode
- Misc code refactor & maintenance, CN doc fixes, bugfixes
- **FlyStar**
- Campaign load screen PCX support
diff --git a/Phobos.vcxproj b/Phobos.vcxproj
index f51d06a1e9..8240b6cc6f 100644
--- a/Phobos.vcxproj
+++ b/Phobos.vcxproj
@@ -65,6 +65,7 @@
+
@@ -200,6 +201,7 @@
+
diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md
index 7738d13fc4..e34e437dfc 100644
--- a/docs/Fixed-or-Improved-Logics.md
+++ b/docs/Fixed-or-Improved-Logics.md
@@ -1038,3 +1038,30 @@ In `rulesmd.ini`:
[AudioVisual]
SelectionFlashDuration=0 ; integer, number of frames
```
+
+## DropPod
+
+DropPod properties can now be customized on a per-InfantryType basis.
+- Note that the DropPod is actually the infantry itself with a different shp image.
+- If you want to attach the trailer animation to the pod, set `DropPod.Trailer.Attached` to yes.
+- By default LaserTrails that are attached to the infantry will not be drawn if it's on DropPod.
+ - If you really want to use it, set `DropPodOnly` on the LaserTrail's type entry in art.
+- If you want `DropPod.Weapon` to be fired only upon hard landing, set `DropPod.Weapon.HitLandOnly` to true.
+- The landing speed is not smaller than it's current height /10 + 2 for unknown reason. A small `DropPod.Speed` value therefore results in exponential deceleration.
+
+In `rulesmd.ini`
+```ini
+[SOMEINFANTRY]
+DropPod.Angle = ; double, default to [General]->DropPodAngle, measured in radians
+DropPod.AtmosphereEntry = ; anim, default to [AudioVisual]->AtmosphereEntry
+DropPod.GroundAnim = ; 2 anims, default to [General]->DropPod
+DropPod.AirImage = ; SHP file, the pod's shape, default to POD
+DropPod.Height = ; int, default to [General]->DropPodHeight
+DropPod.Puff = ; anim, default to [General]->DropPodPuff
+DropPod.Speed = ; int, default to [General]->DropPodSpeed
+DropPod.Trailer = ; anim, default to [General]->DropPodTrailer, which by default is SMOKEY
+DropPod.Trailer.Attached = ; boolean, default to no
+DropPod.Trailer.SpawnDelay = ; int, number of frames between each spawn of DropPod.Trailer, default to 6
+DropPod.Weapon = ; weapon, default to [General]->DropPodWeapon
+DropPod.Weapon.HitLandOnly = ; boolean, default to no
+```
diff --git a/docs/Whats-New.md b/docs/Whats-New.md
index a6e9c4fa86..5f876ab47f 100644
--- a/docs/Whats-New.md
+++ b/docs/Whats-New.md
@@ -356,6 +356,7 @@ New:
- Allow toggling whether or not fire particle systems adjust target coordinates when firer rotates (by Starkku)
- `AmbientDamage` warhead & main target ignore customization (by Starkku)
- Flashing Technos on selecting (by Fryone)
+- Customizable DropPod properties on a per-InfantryType basis (by Trsdy)
- Projectile return weapon (by Starkku)
- Allow customizing aircraft landing direction per aircraft or per dock (by Starkku)
diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp
index d3c27b6903..97e1a97991 100644
--- a/src/Ext/Rules/Body.cpp
+++ b/src/Ext/Rules/Body.cpp
@@ -137,6 +137,13 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI)
this->IsVoiceCreatedGlobal.Read(exINI, GameStrings::AudioVisual, "IsVoiceCreatedGlobal");
this->SelectionFlashDuration.Read(exINI, GameStrings::AudioVisual, "SelectionFlashDuration");
+ Nullable droppod_trailer {};
+ droppod_trailer.Read(exINI, GameStrings::General, "DropPodTrailer");
+ droppod_trailer = droppod_trailer.Get(AnimTypeClass::Find("SMOKEY")); // Ares convention
+ if (!droppod_trailer.Get())
+ this->DropPodTrailer = droppod_trailer.Get();
+ this->PodImage = FileSystem::LoadSHPFile("POD.SHP");
+
this->Buildings_DefaultDigitalDisplayTypes.Read(exINI, GameStrings::AudioVisual, "Buildings.DefaultDigitalDisplayTypes");
this->Infantry_DefaultDigitalDisplayTypes.Read(exINI, GameStrings::AudioVisual, "Infantry.DefaultDigitalDisplayTypes");
this->Vehicles_DefaultDigitalDisplayTypes.Read(exINI, GameStrings::AudioVisual, "Vehicles.DefaultDigitalDisplayTypes");
@@ -277,6 +284,8 @@ void RulesExt::ExtData::Serialize(T& Stm)
.Process(this->Vehicles_DefaultDigitalDisplayTypes)
.Process(this->Aircraft_DefaultDigitalDisplayTypes)
.Process(this->ShowDesignatorRange)
+ .Process(this->DropPodTrailer)
+ .Process(this->PodImage)
;
}
diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h
index dcbec69213..00ef1f8bb8 100644
--- a/src/Ext/Rules/Body.h
+++ b/src/Ext/Rules/Body.h
@@ -108,6 +108,8 @@ class RulesExt
Valueable ShowDesignatorRange;
Valueable IsVoiceCreatedGlobal;
Valueable SelectionFlashDuration;
+ AnimTypeClass* DropPodTrailer;
+ SHPStruct* PodImage;
ExtData(RulesClass* OwnerObject) : Extension(OwnerObject)
, Storage_TiberiumIndex { -1 }
@@ -178,6 +180,8 @@ class RulesExt
, Vehicles_DefaultDigitalDisplayTypes {}
, Aircraft_DefaultDigitalDisplayTypes {}
, ShowDesignatorRange { true }
+ , DropPodTrailer { }
+ , PodImage { }
{ }
virtual ~ExtData() = default;
diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp
index 51bdc0bc39..33477166ee 100644
--- a/src/Ext/Techno/Body.Update.cpp
+++ b/src/Ext/Techno/Body.Update.cpp
@@ -1,3 +1,4 @@
+// methods used in TechnoClass_AI hooks or anything similar
#include "Body.h"
#include
@@ -9,7 +10,31 @@
#include
#include
-// methods used in TechnoClass_AI hooks or anything similar
+
+// TechnoClass_AI_0x6F9E50
+// It's not recommended to do anything more here it could have a better place for performance consideration
+void TechnoExt::ExtData::OnEarlyUpdate()
+{
+ auto pType = this->OwnerObject()->GetTechnoType();
+
+ // Set only if unset or type is changed
+ // Notice that Ares may handle type conversion in the same hook here, which is executed right before this one thankfully
+ if (!this->TypeExtData || this->TypeExtData->OwnerObject() != pType)
+ this->UpdateTypeData(pType);
+
+ this->IsInTunnel = false; // TechnoClass::AI is only called when not in tunnel.
+
+ if (this->CheckDeathConditions())
+ return;
+
+ this->ApplyInterceptor();
+ this->EatPassengers();
+ this->UpdateShield();
+ this->ApplySpawnLimitRange();
+ this->UpdateLaserTrails();
+ this->DepletedAmmoActions();
+}
+
void TechnoExt::ExtData::ApplyInterceptor()
{
@@ -454,12 +479,18 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType)
void TechnoExt::ExtData::UpdateLaserTrails()
{
- auto const pThis = this->OwnerObject();
+ auto const pThis = generic_cast(this->OwnerObject());
+ if (!pThis)
+ return;
- // LaserTrails update routine is in TechnoClass::AI hook because TechnoClass::Draw
- // doesn't run when the object is off-screen which leads to visual bugs - Kerbiter
+ // LaserTrails update routine is in TechnoClass::AI hook because LaserDrawClass-es are updated in LogicClass::AI
for (auto& trail : this->LaserTrails)
{
+ // @Kerbiter if you want to limit it to certain locos you do it here
+ // with vtable check you can avoid the tedious process of Query IPersit/IUnknown Interface, GetClassID, compare with loco GUID, which is omnipresent in vanilla code
+ if (VTable::Get(pThis->Locomotor.GetInterfacePtr()) != 0x7E8278 && trail.Type->DroppodOnly)
+ continue;
+
if (pThis->CloakState == CloakState::Cloaked && !trail.Type->CloakVisible)
continue;
diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h
index 84feb66b15..815fa37883 100644
--- a/src/Ext/Techno/Body.h
+++ b/src/Ext/Techno/Body.h
@@ -63,6 +63,8 @@ class TechnoExt
, WHAnimRemainingCreationInterval { 0 }
{ }
+ void OnEarlyUpdate();
+
void ApplyInterceptor();
bool CheckDeathConditions(bool isInLimbo = false);
void DepletedAmmoActions();
diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp
index a3c77019e8..d130a2d1fc 100644
--- a/src/Ext/Techno/Hooks.cpp
+++ b/src/Ext/Techno/Hooks.cpp
@@ -13,24 +13,7 @@ DEFINE_HOOK(0x6F9E50, TechnoClass_AI, 0x5)
// Do not search this up again in any functions called here because it is costly for performance - Starkku
auto pExt = TechnoExt::ExtMap.Find(pThis);
- auto pType = pThis->GetTechnoType();
-
- // Set only if unset or type is changed
- // Notice that Ares may handle type conversion in the same hook here, which is executed right before this one thankfully
- if (!pExt->TypeExtData || pExt->TypeExtData->OwnerObject() != pType)
- pExt->UpdateTypeData(pType);
-
- pExt->IsInTunnel = false; // TechnoClass::AI is only called when not in tunnel.
-
- if (pExt->CheckDeathConditions())
- return 0;
-
- pExt->ApplyInterceptor();
- pExt->EatPassengers();
- pExt->UpdateShield();
- pExt->ApplySpawnLimitRange();
- pExt->UpdateLaserTrails();
- pExt->DepletedAmmoActions();
+ pExt->OnEarlyUpdate();
TechnoExt::ApplyMindControlRangeLimit(pThis);
diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp
index 4785b96757..725c5e870d 100644
--- a/src/Ext/TechnoType/Body.cpp
+++ b/src/Ext/TechnoType/Body.cpp
@@ -405,6 +405,17 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI)
{
this->InterceptorType.reset();
}
+
+ if (this->OwnerObject()->WhatAmI() == AbstractType::InfantryType)
+ {
+ if (this->DroppodType == nullptr)
+ this->DroppodType = std::make_unique();
+ this->DroppodType->LoadFromINI(pINI, pSection);
+ }
+ else
+ {
+ this->DroppodType.reset();
+ }
}
template
@@ -571,6 +582,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm)
.Process(this->SpawnDistanceFromTarget)
.Process(this->SpawnHeight)
.Process(this->LandingDir)
+ .Process(this->DroppodType)
;
}
void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h
index 2b9b5a2cee..384b7caf3c 100644
--- a/src/Ext/TechnoType/Body.h
+++ b/src/Ext/TechnoType/Body.h
@@ -10,6 +10,7 @@
#include
#include
#include
+#include
class Matrix3D;
@@ -52,6 +53,7 @@ class TechnoTypeExt
Valueable ShieldType;
std::unique_ptr PassengerDeletionType;
+ std::unique_ptr DroppodType;
Nullable Ammo_AddOnDeploy;
Valueable Ammo_AutoDeployMinimumAmount;
@@ -361,6 +363,7 @@ class TechnoTypeExt
, SpawnDistanceFromTarget {}
, SpawnHeight {}
, LandingDir {}
+ , DroppodType {}
{ }
virtual ~ExtData() = default;
diff --git a/src/New/Type/Affiliated/DroppodTypeClass.cpp b/src/New/Type/Affiliated/DroppodTypeClass.cpp
new file mode 100644
index 0000000000..82a6049c46
--- /dev/null
+++ b/src/New/Type/Affiliated/DroppodTypeClass.cpp
@@ -0,0 +1,201 @@
+#include "DroppodTypeClass.h"
+#include
+
+#include
+#include
+#include
+
+void DroppodTypeClass::LoadFromINI(CCINIClass* pINI, const char* pSection)
+{
+ INI_EX exINI(pINI);
+ this->Angle.Read(exINI, pSection, "DropPod.Angle");
+ this->AtmosphereEntry.Read(exINI, pSection, "DropPod.AtmosphereEntry");
+ ValueableVector anims;
+ anims.Read(exINI, pSection, "DropPod.GroundAnim");
+ if (!anims.empty())
+ {
+ this->GroundAnim[0] = anims[0];
+ this->GroundAnim[1] = anims[anims.size() > 1];
+ }
+ this->AirImage.Read(exINI, pSection, "DropPod.AirImage");
+ this->Height.Read(exINI, pSection, "DropPod.Height");
+ this->Puff.Read(exINI, pSection, "DropPod.Puff");
+ this->Speed.Read(exINI, pSection, "DropPod.Speed");
+ this->Trailer.Read(exINI, pSection, "DropPod.Trailer");
+ this->Trailer_SpawnDelay.Read(exINI, pSection, "DropPod.Trailer.SpawnDelay");
+ this->Trailer_Attached.Read(exINI, pSection, "DropPod.Trailer.Attached");
+ this->Weapon.Read(exINI, pSection, "DropPod.Weapon");
+ this->Weapon_HitLandOnly.Read(exINI, pSection, "DropPod.Weapon.HitLandOnly");
+}
+
+#pragma region(save/load)
+
+template
+bool DroppodTypeClass::Serialize(T& stm)
+{
+ return stm
+ .Process(this->Angle)
+ .Process(this->GroundAnim[0])
+ .Process(this->GroundAnim[1])
+ .Process(this->AtmosphereEntry)
+ .Process(this->Height)
+ .Process(this->Puff)
+ .Process(this->Speed)
+ .Process(this->Trailer)
+ .Process(this->Trailer_SpawnDelay)
+ .Process(this->Trailer_Attached)
+ .Process(this->Weapon)
+ .Process(this->Weapon_HitLandOnly)
+ .Process(this->AirImage)
+ .Success();
+}
+
+bool DroppodTypeClass::Load(PhobosStreamReader& stm, bool registerForChange)
+{
+ return this->Serialize(stm);
+}
+
+bool DroppodTypeClass::Save(PhobosStreamWriter& stm) const
+{
+ return const_cast(this)->Serialize(stm);
+}
+
+#pragma endregion(save/load)
+
+// DropPod loco is so far the easiest loco to rewrite completely
+
+DEFINE_HOOK(0x4B5B70, DroppodLocomotionClass_ILoco_Process, 0x5)
+{
+ GET_STACK(ILocomotion*, iloco, 0x4);
+ __assume(iloco != nullptr);
+ auto const lThis = static_cast(iloco);
+ auto const pLinked = lThis->LinkedTo;
+ auto const linkedExt = TechnoExt::ExtMap.Find(pLinked);
+ const auto podType = linkedExt->TypeExtData->DroppodType.get();
+
+ CoordStruct oldLoc = pLinked->Location;
+
+ const double angle = podType->Angle.Get(RulesClass::Instance->DropPodAngle);
+ // exponentially decelerate
+ int speed = std::max(podType->Speed.Get(RulesClass::Instance->DropPodSpeed), pLinked->GetHeight() / 10 + 2);
+
+ CoordStruct coords = pLinked->Location;
+ coords.X += int(Math::cos(angle) * speed * (lThis->OutOfMap ? 1 : -1));
+ coords.Z -= int(Math::sin(angle) * speed);
+
+ auto dWpn = podType->Weapon.Get(RulesClass::Instance->DropPodWeapon);
+
+ if (pLinked->GetHeight() > 0)
+ {
+ pLinked->SetLocation(coords);
+ const int timeSinceCreation = Unsorted::CurrentFrame - pLinked->ParalysisTimer.StartTime;//or anything
+
+ if (timeSinceCreation % podType->Trailer_SpawnDelay == 1)
+ {
+ if (auto trailerType = podType->Trailer.Get(RulesExt::Global()->DropPodTrailer))
+ {
+ auto trailer = GameCreate(trailerType, oldLoc);
+ trailer->Owner = pLinked->Owner;
+ if (podType->Trailer_Attached)
+ trailer->SetOwnerObject(pLinked);
+ }
+ }
+
+ if (dWpn && !podType->Weapon_HitLandOnly)
+ {
+ if (timeSinceCreation % std::max(dWpn->ROF, 3) == 1)
+ {
+ auto cell = MapClass::Instance->GetCellAt(lThis->DestinationCoords);
+ auto techno = cell->FindTechnoNearestTo({ 0,0 }, false);
+ if (!pLinked->Owner->IsAlliedWith(techno))
+ {
+ // really? not firing for real?
+ auto locnear = MapClass::GetRandomCoordsNear(lThis->DestinationCoords, 85, false);
+ if (int count = dWpn->Report.Count)
+ VocClass::PlayAt(dWpn->Report[Randomizer::Global->Random() % count], coords);
+ MapClass::DamageArea(locnear, 2 * dWpn->Damage, pLinked, dWpn->Warhead, true, pLinked->Owner);
+ if (auto dmgAnim = MapClass::SelectDamageAnimation(2 * dWpn->Damage, dWpn->Warhead, LandType::Clear, locnear))
+ GameCreate(dmgAnim, locnear, 0, 1, 0x2600, -15, 0)->Owner = pLinked->Owner;
+ }
+ }
+ }
+ }
+ else
+ {
+ pLinked->SetHeight(0);
+ pLinked->Limbo();
+ lThis->AddRef();
+ lThis->End_Piggyback(&pLinked->Locomotor);
+
+ if (pLinked->Unlimbo(pLinked->Location, DirType::North))
+ {
+ if (auto puff = podType->Puff.Get(RulesClass::Instance->DropPodPuff))
+ GameCreate(puff, pLinked->Location)->Owner = pLinked->Owner;
+
+ if (auto podAnim = podType->GroundAnim[lThis->OutOfMap].Get(RulesClass::Instance->DropPod[lThis->OutOfMap]))
+ GameCreate(podAnim, pLinked->Location)->Owner = pLinked->Owner;
+
+ if (dWpn && podType->Weapon_HitLandOnly)
+ WeaponTypeExt::DetonateAt(dWpn, pLinked->Location, pLinked, pLinked->Owner);
+
+ pLinked->Mark(MarkType::Down);
+ pLinked->SetHeight(0);
+ pLinked->EnterIdleMode(false, true);
+ pLinked->NextMission();
+ pLinked->Scatter(CoordStruct::Empty, false, false);
+ }
+ else
+ {
+ MapClass::DamageArea(coords, 100, pLinked, RulesClass::Instance->C4Warhead, true, pLinked->Owner);
+ if (auto dmgAnim = MapClass::SelectDamageAnimation(100, RulesClass::Instance->C4Warhead, LandType::Clear, coords))
+ GameCreate(dmgAnim, coords, 0, 1, 0x2600, -15, 0)->Owner = pLinked->Owner;
+ }
+ lThis->Release();
+ }
+
+
+ R->AL(true);
+ return 0x4B6036;
+}
+
+DEFINE_HOOK(0x4B607D, DroppodLocomotionClass_ILoco_MoveTo, 0x8)
+{
+ GET(ILocomotion*, iloco, EDI);
+ REF_STACK(CoordStruct, to, STACK_OFFSET(0x1C, 0x8));
+ __assume(iloco != nullptr);
+ auto const lThis = static_cast(iloco);
+ auto const pLinked = lThis->LinkedTo;
+
+ lThis->DestinationCoords = to;
+ lThis->DestinationCoords.Z = MapClass::Instance->GetCellFloorHeight(to);
+
+ const auto podType = TechnoTypeExt::ExtMap.Find(pLinked->GetTechnoType())->DroppodType.get();
+ const int height = podType->Height.Get(RulesClass::Instance->DropPodHeight);
+ const double angle = podType->Angle.Get(RulesClass::Instance->DropPodAngle);
+
+ lThis->OutOfMap = !MapClass::Instance->IsWithinUsableArea(to);
+ to.X -= (lThis->OutOfMap ? 1 : -1) * int(height / Math::tan(angle));
+ to.Z += height;
+
+ pLinked->SetLocation(to);
+
+ if (pLinked->Unlimbo(to, DirType::South))
+ {
+ pLinked->PrimaryFacing.SetCurrent(DirStruct { DirType::South });
+ if (auto entryAnim = podType->AtmosphereEntry.Get(RulesClass::Instance->AtmosphereEntry))
+ GameCreate(entryAnim, to)->Owner = pLinked->Owner;
+ }
+
+ return 0x4B61F0;
+}
+
+DEFINE_HOOK(0x519168, InfantryClass_Draw_Droppod, 0x5)
+{
+ GET(InfantryClass*, pThis, EBP);
+ REF_STACK(SHPStruct*, shp, STACK_OFFSET(0x54, -0x34));
+
+ auto const podType = TechnoTypeExt::ExtMap.Find(pThis->Type)->DroppodType.get();
+ shp = podType->AirImage.Get(RulesExt::Global()->PodImage);
+
+ return 0x519176;
+}
diff --git a/src/New/Type/Affiliated/DroppodTypeClass.h b/src/New/Type/Affiliated/DroppodTypeClass.h
new file mode 100644
index 0000000000..644d6ab623
--- /dev/null
+++ b/src/New/Type/Affiliated/DroppodTypeClass.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#include
+
+class DroppodTypeClass
+{
+public:
+ Nullable Speed {};
+ Nullable Angle {};
+ Nullable Height {};
+ Nullable Weapon {};
+ Nullable GroundAnim[2] { {},{} };
+ Nullable Puff {};
+ Nullable Trailer {};
+ Valueable Trailer_SpawnDelay { 6 };
+ Nullable AtmosphereEntry {};
+ Nullable AirImage { };
+ Valueable Trailer_Attached { false };
+ Valueable Weapon_HitLandOnly { false };
+
+ DroppodTypeClass() = default;
+
+ void LoadFromINI(CCINIClass* pINI, const char* pSection);
+ bool Load(PhobosStreamReader& stm, bool registerForChange);
+ bool Save(PhobosStreamWriter& stm) const;
+
+private:
+
+ template
+ bool Serialize(T& stm);
+};
diff --git a/src/New/Type/LaserTrailTypeClass.cpp b/src/New/Type/LaserTrailTypeClass.cpp
index c10d2deea3..8233630fc9 100644
--- a/src/New/Type/LaserTrailTypeClass.cpp
+++ b/src/New/Type/LaserTrailTypeClass.cpp
@@ -24,6 +24,7 @@ void LaserTrailTypeClass::LoadFromINI(CCINIClass* pINI)
this->IgnoreVertical.Read(exINI, section, "IgnoreVertical");
this->IsIntense.Read(exINI, section, "IsIntense");
this->CloakVisible.Read(exINI, section, "CloakVisible");
+ this->DroppodOnly.Read(exINI, section, "DropPodOnly");
}
template
@@ -38,6 +39,7 @@ void LaserTrailTypeClass::Serialize(T& Stm)
.Process(this->IgnoreVertical)
.Process(this->IsIntense)
.Process(this->CloakVisible)
+ .Process(this->DroppodOnly)
;
}
diff --git a/src/New/Type/LaserTrailTypeClass.h b/src/New/Type/LaserTrailTypeClass.h
index c5f5f64ce0..5de0dfa575 100644
--- a/src/New/Type/LaserTrailTypeClass.h
+++ b/src/New/Type/LaserTrailTypeClass.h
@@ -14,6 +14,7 @@ class LaserTrailTypeClass final : public Enumerable
Valueable IgnoreVertical;
Valueable IsIntense;
Valueable CloakVisible;
+ Valueable DroppodOnly;
LaserTrailTypeClass(const char* pTitle = NONE_STR) : Enumerable(pTitle)
, IsHouseColor { false }
@@ -24,6 +25,7 @@ class LaserTrailTypeClass final : public Enumerable
, IgnoreVertical { false }
, IsIntense { false }
, CloakVisible { false }
+ , DroppodOnly { false }
{ }
virtual ~LaserTrailTypeClass() override = default;