From 881e5ce4ac808dfc7738d659d56eabd5e942f1b7 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 20:57:28 +0300 Subject: [PATCH 01/47] Extract non-template AbstractClassExtension base --- src/Utilities/Container.h | 42 +++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 13 deletions(-) diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index d1becf0ef9..90fd551127 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -50,28 +50,23 @@ enum class InitState * */ -template -class Extension +// the non-template root of every extension, mirroring Vinifera's AbstractClassExtension. +// it owns the back-pointer and the staged init state, so all extensions share a common base. +class AbstractClassExtension { - T* AttachedToObject; + void* AttachedToObject; InitState Initialized; public: - Extension(T* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } + explicit AbstractClassExtension(void* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } { } - Extension(const Extension& other) = delete; - - void operator=(const Extension& RHS) = delete; + AbstractClassExtension(const AbstractClassExtension& other) = delete; - virtual ~Extension() = default; + void operator=(const AbstractClassExtension& RHS) = delete; - // the object this Extension expands - T* const& OwnerObject() const - { - return this->AttachedToObject; - } + virtual ~AbstractClassExtension() = default; void EnsureConstanted() { @@ -122,6 +117,11 @@ class Extension } protected: + void* GetAttachedObject() const + { + return this->AttachedToObject; + } + // right after construction. only basic initialization tasks possible; // owner object is only partially constructed! do not use global state! virtual void InitializeConstants() { } @@ -138,6 +138,22 @@ class Extension virtual void LoadFromINIFile(CCINIClass* pINI) { } }; +// the typed layer over AbstractClassExtension: gives a strongly-typed owner accessor. +template +class Extension : public AbstractClassExtension +{ +public: + + explicit Extension(T* const OwnerObject) : AbstractClassExtension(OwnerObject) + { } + + // the object this Extension expands + T* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; + // a non-virtual base class for a pointer to pointer map. // pointers are not owned by this map, so be cautious. class ContainerMapBase final From f9a962ce78eb55354a08a39adb592e0ab86ea50d Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 21:14:00 +0300 Subject: [PATCH 02/47] Move single-extension classes to the unified 0x18 slot --- src/Ext/Anim/Body.h | 2 +- src/Ext/Cell/Body.h | 2 +- src/Ext/House/Body.h | 2 +- src/Ext/HouseType/Body.h | 2 +- src/Ext/TeamType/Body.h | 2 +- src/Ext/Techno/Body.h | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 11542da421..5f2a73a26c 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -9,7 +9,7 @@ class AnimExt using base_type = AnimClass; static constexpr DWORD Canary = 0xAAAAAAAA; - static constexpr size_t ExtPointerOffset = 0xD0; + static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = false; // Sheer volume of animations in an average game makes a bespoke solution for pointer invalidation worthwhile. class ExtData final : public Extension diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 5947c207e3..e1bccf8636 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -10,7 +10,7 @@ class CellExt using base_type = CellClass; static constexpr DWORD Canary = 0x13371337; - static constexpr size_t ExtPointerOffset = 0x144; + static constexpr size_t ExtPointerOffset = 0x18; struct RadLevel { diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index ab0276b395..78b8fdd103 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -12,7 +12,7 @@ class HouseExt using base_type = HouseClass; static constexpr DWORD Canary = 0x11111111; - static constexpr size_t ExtPointerOffset = 0x16098; + static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; class ExtData final : public Extension diff --git a/src/Ext/HouseType/Body.h b/src/Ext/HouseType/Body.h index cecb3229f9..fe634482b2 100644 --- a/src/Ext/HouseType/Body.h +++ b/src/Ext/HouseType/Body.h @@ -13,7 +13,7 @@ class HouseTypeExt using base_type = HouseTypeClass; static constexpr DWORD Canary = 0xAFFEAFFE; - static constexpr size_t ExtPointerOffset = 0x1AC; + static constexpr size_t ExtPointerOffset = 0x18; class ExtData final : public Extension { diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 61f22a45bd..5986577580 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -10,7 +10,7 @@ class TeamTypeExt using base_type = TeamTypeClass; static constexpr DWORD Canary = 0xABCDEF01; - static constexpr size_t ExtPointerOffset = 0xBC; + static constexpr size_t ExtPointerOffset = 0x18; class ExtData final : public Extension { diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index bb31ec98a5..59aaa7b4ad 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -15,7 +15,7 @@ class TechnoExt using base_type = TechnoClass; static constexpr DWORD Canary = 0x55555555; - static constexpr size_t ExtPointerOffset = 0x34C; + static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; class ExtData final : public Extension From 5284f7db103f89641497cfc81ede27920027a18e Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 21:37:44 +0300 Subject: [PATCH 03/47] Merge BuildingClass extension into the TechnoClass hierarchy --- src/Ext/Building/Body.cpp | 59 +++++++-------------------------------- src/Ext/Building/Body.h | 41 +++++++++++++++------------ src/Ext/Techno/Body.h | 3 +- src/Utilities/Container.h | 16 +++++++++++ 4 files changed, 51 insertions(+), 68 deletions(-) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index af9b7a5bf4..1ad5d730eb 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -4,7 +4,7 @@ #include #include -BuildingExt::ExtContainer BuildingExt::ExtMap; +BuildingExt::ExtMapFacade BuildingExt::ExtMap; void BuildingExt::ExtData::DisplayIncomeString() { @@ -552,7 +552,6 @@ void BuildingExt::ExtData::Serialize(T& Stm) { Stm .Process(this->TypeExtData) - .Process(this->TechnoExtData) .Process(this->DeployedTechno) .Process(this->IsCreatedFromMapFile) .Process(this->LimboID) @@ -572,13 +571,13 @@ void BuildingExt::ExtData::Serialize(T& Stm) void BuildingExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + TechnoExt::ExtData::LoadFromStream(Stm); this->Serialize(Stm); } void BuildingExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + TechnoExt::ExtData::SaveToStream(Stm); this->Serialize(Stm); } @@ -595,11 +594,7 @@ bool BuildingExt::SaveGlobals(PhobosStreamWriter& Stm) } // ============================= -// container - -BuildingExt::ExtContainer::ExtContainer() : Container("BuildingClass") {} - -BuildingExt::ExtContainer::~ExtContainer() = default; +// container facade defined at the top of this file // ============================= // container hooks @@ -608,36 +603,19 @@ DEFINE_HOOK(0x43BCBD, BuildingClass_CTOR, 0x6) { GET(BuildingClass*, pItem, ESI); - auto const pExt = BuildingExt::ExtMap.TryAllocate(pItem); + // The TechnoClass constructor already created a plain TechnoClassExtension for this object; + // upgrade it to a BuildingClassExtension leaf, still owned by the TechnoClass container. + TechnoExt::ExtMap.Remove(pItem); + auto const pExt = static_cast(TechnoExt::ExtMap.Adopt(new BuildingExt::ExtData(pItem))); if (pExt) - { pExt->TypeExtData = BuildingTypeExt::ExtMap.Find(pItem->Type); - pExt->TechnoExtData = TechnoExt::ExtMap.Find(pItem); - } - - return 0; -} - -DEFINE_HOOK(0x43C022, BuildingClass_DTOR, 0x6) -{ - GET(BuildingClass*, pItem, ESI); - - BuildingExt::ExtMap.Remove(pItem); return 0; } -DEFINE_HOOK_AGAIN(0x454190, BuildingClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x453E20, BuildingClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(BuildingClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - BuildingExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} +// BuildingClass destruction, save and load of the extension are handled by the TechnoClass +// container hooks now that a building has a single TechnoClass-derived extension at 0x18. DEFINE_HOOK(0x454174, BuildingClass_Load_LightSource, 0xA) { @@ -648,23 +626,6 @@ DEFINE_HOOK(0x454174, BuildingClass_Load_LightSource, 0xA) return 0x45417E; } -DEFINE_HOOK(0x45417E, BuildingClass_Load_Suffix, 0x5) -{ - BuildingExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x454244, BuildingClass_Save_Suffix, 0x7) -{ - BuildingExt::ExtMap.SaveStatic(); - - return 0; -} - -// Removes setting otherwise unused field (0x6FC) in BuildingClass when building has airstrike applied on it so that it can safely be used to store BuildingExt pointer. -DEFINE_JUMP(LJMP, 0x41D9FB, 0x41DA05); - static void __fastcall BuildingClass_InfiltratedBy_Wrapper(BuildingClass* pThis, void*, HouseClass* pInfiltratorHouse) { const int oldBalance = pThis->Owner->Available_Money(); diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 0ed94a2be5..2e75b94538 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -8,10 +8,11 @@ class BuildingExt using base_type = BuildingClass; static constexpr DWORD Canary = 0x87654321; - static constexpr size_t ExtPointerOffset = 0x6FC; static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public Extension + // BuildingClassExtension is a leaf of the TechnoClass extension hierarchy: one extension + // per object, stored inline in the shared 0x18 slot and owned by the TechnoClass container. + class ExtData final : public TechnoExt::ExtData { public: BuildingTypeExt::ExtData* TypeExtData; @@ -31,9 +32,9 @@ class BuildingExt int TurretAnimFiringFrame; int TurretAnimRateTick; - ExtData(BuildingClass* OwnerObject) : Extension(OwnerObject) + ExtData(BuildingClass* OwnerObject) : TechnoExt::ExtData(OwnerObject) , TypeExtData { nullptr } - , TechnoExtData { nullptr } + , TechnoExtData { this } , DeployedTechno { false } , IsCreatedFromMapFile { false } , LimboID { -1 } @@ -50,6 +51,12 @@ class BuildingExt , TurretAnimRateTick { 0 } { } + // typed owner accessor (shadows the TechnoClass one from the base) + BuildingClass* OwnerObject() const + { + return static_cast(this->TechnoExt::ExtData::OwnerObject()); + } + void DisplayIncomeString(); void ApplyPoweredKillSpawns(); bool HasSuperWeapon(int index) const; @@ -61,6 +68,8 @@ class BuildingExt virtual void InvalidatePointer(void* ptr, bool bRemoved) override { + TechnoExt::ExtData::InvalidatePointer(ptr, bRemoved); + if (bRemoved) AnnounceInvalidPointer(this->CurrentAirFactory, ptr); } @@ -73,27 +82,23 @@ class BuildingExt void Serialize(T& Stm); }; - class ExtContainer final : public Container + // BuildingClassExtension lives in the TechnoClass container (single 0x18 slot), so this + // is a thin typed accessor facade over that container instead of an owning container. + class ExtMapFacade { public: - ExtContainer(); - ~ExtContainer(); + ExtData* Find(BuildingClass* key) const + { + return static_cast(TechnoExt::ExtMap.Find(key)); + } - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override + ExtData* TryFind(BuildingClass* key) const { - auto const abs = static_cast(ptr)->WhatAmI(); - - switch (abs) - { - case AbstractType::Building: - return false; - default: - return true; - } + return static_cast(TechnoExt::ExtMap.TryFind(key)); } }; - static ExtContainer ExtMap; + static ExtMapFacade ExtMap; static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 59aaa7b4ad..1bfe741fd7 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -18,7 +18,7 @@ class TechnoExt static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public Extension + class ExtData : public Extension { public: TechnoTypeExt::ExtData* TypeExtData; @@ -248,6 +248,7 @@ class TechnoExt switch (abs) { case AbstractType::Airstrike: + case AbstractType::Building: // BuildingClassExtension leaves live in this container now return false; default: return true; diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 90fd551127..27e3076a63 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -376,6 +376,22 @@ class Container return val; } + // Registers an already-constructed extension (possibly of a derived leaf type) + // into this container: stores the inline pointer and tracks it for iteration. + extension_type_ptr Adopt(extension_type_ptr val) + { + val->EnsureConstanted(); + + if constexpr (HasOffset) + SetExtensionPointer(val->OwnerObject(), val); + else + this->MappedItems.insert(val->OwnerObject(), val); + + Items.emplace_back(val); + + return val; + } + extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) { if (!key || (!bCond && !nMessage.empty())) From 83bbf85d3250aa17575e04cb81217fbb2c909721 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 21:50:08 +0300 Subject: [PATCH 04/47] Merge BuildingTypeClass extension into the TechnoTypeClass hierarchy --- src/Ext/Building/Body.h | 4 +- src/Ext/BuildingType/Body.cpp | 70 ++++++++--------------------------- src/Ext/BuildingType/Body.h | 35 +++++++++++++----- src/Ext/TechnoType/Body.h | 2 +- 4 files changed, 44 insertions(+), 67 deletions(-) diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 2e75b94538..b88e635775 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -87,12 +87,12 @@ class BuildingExt class ExtMapFacade { public: - ExtData* Find(BuildingClass* key) const + ExtData* Find(const BuildingClass* key) const { return static_cast(TechnoExt::ExtMap.Find(key)); } - ExtData* TryFind(BuildingClass* key) const + ExtData* TryFind(const BuildingClass* key) const { return static_cast(TechnoExt::ExtMap.TryFind(key)); } diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 44e6217482..221783898a 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -3,7 +3,7 @@ #include #include -BuildingTypeExt::ExtContainer BuildingTypeExt::ExtMap; +BuildingTypeExt::ExtMapFacade BuildingTypeExt::ExtMap; // Assuming SuperWeapon & SuperWeapon2 are used (for the moment) int BuildingTypeExt::ExtData::GetSuperWeaponCount() const @@ -148,13 +148,17 @@ int BuildingTypeExt::GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* void BuildingTypeExt::ExtData::Initialize() -{ } +{ + TechnoTypeExt::ExtData::Initialize(); +} // ============================= // load / save void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) { + TechnoTypeExt::ExtData::LoadFromINIFile(pINI); + auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; const char* pArtSection = pThis->ImageFile; @@ -433,23 +437,16 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) void BuildingTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + TechnoTypeExt::ExtData::LoadFromStream(Stm); this->Serialize(Stm); } void BuildingTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + TechnoTypeExt::ExtData::SaveToStream(Stm); this->Serialize(Stm); } -bool BuildingTypeExt::ExtContainer::Load(BuildingTypeClass* pThis, IStream* pStm) -{ - BuildingTypeExt::ExtData* pData = this->LoadKey(pThis, pStm); - - return pData != nullptr; -}; - bool BuildingTypeExt::LoadGlobals(PhobosStreamReader& Stm) { return Stm.Success(); @@ -463,9 +460,7 @@ bool BuildingTypeExt::SaveGlobals(PhobosStreamWriter& Stm) // ============================= // container -BuildingTypeExt::ExtContainer::ExtContainer() : Container("BuildingTypeClass") { } - -BuildingTypeExt::ExtContainer::~ExtContainer() = default; +// container facade defined at the top of this file // ============================= // container hooks @@ -474,48 +469,13 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) { GET(BuildingTypeClass*, pItem, EAX); - BuildingTypeExt::ExtMap.TryAllocate(pItem); - - return 0; -} - -DEFINE_HOOK(0x45E707, BuildingTypeClass_DTOR, 0x6) -{ - GET(BuildingTypeClass*, pItem, ESI); - - BuildingTypeExt::ExtMap.Remove(pItem); - return 0; -} - -DEFINE_HOOK_AGAIN(0x465300, BuildingTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x465010, BuildingTypeClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(BuildingTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - BuildingTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} + // The TechnoTypeClass constructor already created a plain TechnoTypeClassExtension; upgrade + // it to a BuildingTypeClassExtension leaf owned by the TechnoTypeClass container. + TechnoTypeExt::ExtMap.Remove(pItem); + TechnoTypeExt::ExtMap.Adopt(new BuildingTypeExt::ExtData(pItem)); -DEFINE_HOOK(0x4652ED, BuildingTypeClass_Load_Suffix, 0x7) -{ - BuildingTypeExt::ExtMap.LoadStatic(); return 0; } -DEFINE_HOOK(0x46536A, BuildingTypeClass_Save_Suffix, 0x7) -{ - BuildingTypeExt::ExtMap.SaveStatic(); - return 0; -} - -//DEFINE_HOOK_AGAIN(0x464A56, BuildingTypeClass_LoadFromINI, 0xA)// Section dont exist! -DEFINE_HOOK(0x464A49, BuildingTypeClass_LoadFromINI, 0xA) -{ - GET(BuildingTypeClass*, pItem, EBP); - GET_STACK(CCINIClass*, pINI, 0x364); - - BuildingTypeExt::ExtMap.LoadFromINI(pItem, pINI); - return 0; -} +// BuildingTypeClass destruction, save, load and INI parsing of the extension are handled by +// the TechnoTypeClass container hooks now that a building type has a single extension at 0x18. diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index c49efbfc53..0b78eaad08 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -10,7 +11,8 @@ class BuildingTypeExt static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + // BuildingTypeClassExtension is a leaf of the TechnoTypeClass extension hierarchy. + class ExtData final : public TechnoTypeExt::ExtData { public: Valueable PowersUp_Owner; @@ -125,7 +127,7 @@ class BuildingTypeExt // Ares 3.0 Nullable UnitSell; - ExtData(BuildingTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(BuildingTypeClass* OwnerObject) : TechnoTypeExt::ExtData(OwnerObject) , PowersUp_Owner { AffectedHouse::Owner } , PowersUp_Buildings {} , PowerPlant_DamageFactor { 1.0 } @@ -215,6 +217,12 @@ class BuildingTypeExt , UnitSell {} { } + // typed owner accessor (shadows the TechnoTypeClass one from the base) + BuildingTypeClass* OwnerObject() const + { + return static_cast(this->TechnoTypeExt::ExtData::OwnerObject()); + } + // Ares 0.A functions int GetSuperWeaponCount() const; int GetSuperWeaponIndex(int index, HouseClass* pHouse) const; @@ -226,7 +234,10 @@ class BuildingTypeExt virtual void Initialize() override; virtual void CompleteInitialization(); - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } + virtual void InvalidatePointer(void* ptr, bool bRemoved) override + { + TechnoTypeExt::ExtData::InvalidatePointer(ptr, bRemoved); + } virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -236,16 +247,22 @@ class BuildingTypeExt void Serialize(T& Stm); }; - class ExtContainer final : public Container + // BuildingTypeClassExtension lives in the TechnoTypeClass container; thin accessor facade. + class ExtMapFacade { public: - ExtContainer(); - ~ExtContainer(); - - virtual bool Load(BuildingTypeClass* pThis, IStream* pStm) override; + ExtData* Find(const BuildingTypeClass* key) const + { + return static_cast(TechnoTypeExt::ExtMap.Find(key)); + } + + ExtData* TryFind(const BuildingTypeClass* key) const + { + return static_cast(TechnoTypeExt::ExtMap.TryFind(key)); + } }; - static ExtContainer ExtMap; + static ExtMapFacade ExtMap; static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index dc3819edff..592b8048a1 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -23,7 +23,7 @@ class TechnoTypeExt static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0xDF4; - class ExtData final : public Extension + class ExtData : public Extension { public: Valueable HealthBar_Hide; From 26b56237244a4a5b605005476b0e9ca5402e0ae7 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 21:56:25 +0300 Subject: [PATCH 05/47] Add top-level ClassExtension names for the Techno/Building hierarchy --- src/Ext/Building/Body.h | 3 +++ src/Ext/BuildingType/Body.h | 3 +++ src/Ext/Techno/Body.h | 3 +++ src/Ext/TechnoType/Body.h | 3 +++ 4 files changed, 12 insertions(+) diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index b88e635775..9f9edd8e67 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -116,3 +116,6 @@ class BuildingExt static void __fastcall KickOutClone(std::pair& info, void*, BuildingClass* pFactory); static int GetTurretFrame(BuildingClass* pThis); }; + +// top-level name for the BuildingClass extension leaf (derives from TechnoClassExtension) +using BuildingClassExtension = BuildingExt::ExtData; diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 0b78eaad08..99264a6287 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -273,3 +273,6 @@ class BuildingTypeExt static int CountOwnedNowWithDeployOrUpgrade(BuildingTypeClass* pBuilding, HouseClass* pHouse); static int GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* pHouse); }; + +// top-level name for the BuildingTypeClass extension leaf (derives from TechnoTypeClassExtension) +using BuildingTypeClassExtension = BuildingTypeExt::ExtData; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 1bfe741fd7..abbe9325a4 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -338,3 +338,6 @@ class TechnoExt static bool HasWeaponsDisabled(TechnoClass* pThis); static FireError GetFireErrorIgnoreDisableWeapons(TechnoClass* pThis, AbstractClass* pTarget, int weaponIndex, bool ignoreRange); }; + +// top-level name for the TechnoClass extension (the base of the techno extension hierarchy) +using TechnoClassExtension = TechnoExt::ExtData; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 592b8048a1..a8e8c29f8a 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1066,3 +1066,6 @@ class TechnoTypeExt static const char* GetSelectionGroupID(ObjectTypeClass* pType); static bool HasSelectionGroupID(ObjectTypeClass* pType, const char* pID); }; + +// top-level name for the TechnoTypeClass extension (the base of the techno-type extension hierarchy) +using TechnoTypeClassExtension = TechnoTypeExt::ExtData; From 42addd17c29a4caa21177becf50c3e950143409a Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 22:21:38 +0300 Subject: [PATCH 06/47] Consolidate trigger extensions on the 0x18 slot and drop redundant self-pointer --- src/Ext/Building/Body.h | 2 -- src/Ext/Building/Hooks.cpp | 2 +- src/Ext/Script/Body.h | 1 + src/Ext/TAction/Body.h | 1 + src/Ext/TEvent/Body.h | 1 + 5 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 9f9edd8e67..6baca5dfae 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -16,7 +16,6 @@ class BuildingExt { public: BuildingTypeExt::ExtData* TypeExtData; - TechnoExt::ExtData* TechnoExtData; bool DeployedTechno; bool IsCreatedFromMapFile; int LimboID; @@ -34,7 +33,6 @@ class BuildingExt ExtData(BuildingClass* OwnerObject) : TechnoExt::ExtData(OwnerObject) , TypeExtData { nullptr } - , TechnoExtData { this } , DeployedTechno { false } , IsCreatedFromMapFile { false } , LimboID { -1 } diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index fb0d1762fd..cfa1f4c648 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -16,7 +16,7 @@ DEFINE_HOOK(0x43FE69, BuildingClass_AI, 0xA) const auto pBuildingExt = BuildingExt::ExtMap.Find(pThis); pBuildingExt->DisplayIncomeString(); - const auto pTechnoExt = pBuildingExt->TechnoExtData; + TechnoExt::ExtData* const pTechnoExt = pBuildingExt; // the building extension is a TechnoClassExtension pTechnoExt->UpdateLaserTrails(); // Mainly for on turret trails // Force airstrike targets to redraw every frame to account for tint intensity fluctuations. diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index a40f048268..0ca8db9116 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -152,6 +152,7 @@ class ScriptExt using base_type = ScriptClass; static constexpr DWORD Canary = 0x3B3B3B3B; + static constexpr size_t ExtPointerOffset = 0x18; class ExtData final : public Extension { diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index 3a1317714c..cdee8344e9 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -37,6 +37,7 @@ class TActionExt using base_type = TActionClass; static constexpr DWORD Canary = 0x91919191; + static constexpr size_t ExtPointerOffset = 0x18; class ExtData final : public Extension { diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index 478dfa3c55..460ba3b5b3 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -62,6 +62,7 @@ class TEventExt using base_type = TEventClass; static constexpr DWORD Canary = 0x91919191; + static constexpr size_t ExtPointerOffset = 0x18; class ExtData final : public Extension { From ff991fc2511e1702c73245f98504824e9a08a1d4 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 22:27:35 +0300 Subject: [PATCH 07/47] Add top-level ClassExtension names for the remaining extensions --- src/Ext/Anim/Body.h | 3 +++ src/Ext/AnimType/Body.h | 3 +++ src/Ext/Bullet/Body.h | 3 +++ src/Ext/BulletType/Body.h | 3 +++ src/Ext/Cell/Body.h | 3 +++ src/Ext/EBolt/Body.h | 3 +++ src/Ext/House/Body.h | 3 +++ src/Ext/HouseType/Body.h | 3 +++ src/Ext/OverlayType/Body.h | 3 +++ src/Ext/ParticleSystemType/Body.h | 3 +++ src/Ext/ParticleType/Body.h | 3 +++ src/Ext/RadSite/Body.h | 3 +++ src/Ext/SWType/Body.h | 3 +++ src/Ext/Script/Body.h | 3 +++ src/Ext/Side/Body.h | 3 +++ src/Ext/TAction/Body.h | 3 +++ src/Ext/TEvent/Body.h | 3 +++ src/Ext/Team/Body.h | 3 +++ src/Ext/TeamType/Body.h | 3 +++ src/Ext/TerrainType/Body.h | 3 +++ src/Ext/Tiberium/Body.h | 3 +++ src/Ext/VoxelAnim/Body.h | 3 +++ src/Ext/VoxelAnimType/Body.h | 3 +++ src/Ext/WarheadType/Body.h | 3 +++ src/Ext/WeaponType/Body.h | 3 +++ 25 files changed, 75 insertions(+) diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 5f2a73a26c..9a4a34e2e0 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -104,3 +104,6 @@ class AnimExt static void InvalidateParticleSystemPointers(ParticleSystemClass* pParticleSystem); static void CreateRandomAnim(const std::vector& AnimList, CoordStruct coords, TechnoClass* pTechno = nullptr, HouseClass* pHouse = nullptr, bool invoker = false, bool ownedObject = false); }; + +// top-level name for the AnimExt extension +using AnimClassExtension = AnimExt::ExtData; diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index 779703749a..1e3d946003 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -138,3 +138,6 @@ class AnimTypeExt static void ProcessDestroyAnims(UnitClass* pThis, HouseClass* pKiller = nullptr); }; + +// top-level name for the AnimTypeExt extension +using AnimTypeClassExtension = AnimTypeExt::ExtData; diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index 2fd695624d..dae0d092d1 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -93,3 +93,6 @@ class BulletExt static inline void SimulatedFiringParticleSystem(BulletClass* pBullet, HouseClass* pHouse); static inline BulletVelocity ApplyRadialFireVelocityWarp(BulletVelocity velocity, const RadialFireStruct& radialFire); }; + +// top-level name for the BulletExt extension +using BulletClassExtension = BulletExt::ExtData; diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 8842c8ba19..74c1e24037 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -172,3 +172,6 @@ class BulletTypeExt static double GetAdjustedGravity(BulletTypeClass* pType); static BulletTypeClass* GetDefaultBulletType(); }; + +// top-level name for the BulletTypeExt extension +using BulletTypeClassExtension = BulletTypeExt::ExtData; diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index e1bccf8636..07e5ca41ea 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -62,3 +62,6 @@ class CellExt static ExtContainer ExtMap; }; + +// top-level name for the CellExt extension +using CellClassExtension = CellExt::ExtData; diff --git a/src/Ext/EBolt/Body.h b/src/Ext/EBolt/Body.h index 51cf0cf0d0..e6a3d0fdfc 100644 --- a/src/Ext/EBolt/Body.h +++ b/src/Ext/EBolt/Body.h @@ -61,3 +61,6 @@ class EBoltExt static EBolt* CreateEBolt(WeaponTypeClass* pWeapon); static DWORD _cdecl _EBolt_Draw_Colors(REGISTERS* R); }; + +// top-level name for the EBoltExt extension +using EBoltExtension = EBoltExt::ExtData; diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index 78b8fdd103..fa1cdb73e0 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -211,3 +211,6 @@ class HouseExt static void CalculatePowerSurplus(HouseClass* pThis); }; + +// top-level name for the HouseExt extension +using HouseClassExtension = HouseExt::ExtData; diff --git a/src/Ext/HouseType/Body.h b/src/Ext/HouseType/Body.h index fe634482b2..3d5f886e33 100644 --- a/src/Ext/HouseType/Body.h +++ b/src/Ext/HouseType/Body.h @@ -50,3 +50,6 @@ class HouseTypeExt static ExtContainer ExtMap; }; + +// top-level name for the HouseTypeExt extension +using HouseTypeClassExtension = HouseTypeExt::ExtData; diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index 1acdffce48..e9afc87e2f 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -51,3 +51,6 @@ class OverlayTypeExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the OverlayTypeExt extension +using OverlayTypeClassExtension = OverlayTypeExt::ExtData; diff --git a/src/Ext/ParticleSystemType/Body.h b/src/Ext/ParticleSystemType/Body.h index b613a3c5bf..955d360794 100644 --- a/src/Ext/ParticleSystemType/Body.h +++ b/src/Ext/ParticleSystemType/Body.h @@ -47,3 +47,6 @@ class ParticleSystemTypeExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the ParticleSystemTypeExt extension +using ParticleSystemTypeClassExtension = ParticleSystemTypeExt::ExtData; diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h index fe2c2d283f..dfc2715479 100644 --- a/src/Ext/ParticleType/Body.h +++ b/src/Ext/ParticleType/Body.h @@ -48,3 +48,6 @@ class ParticleTypeExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the ParticleTypeExt extension +using ParticleTypeClassExtension = ParticleTypeExt::ExtData; diff --git a/src/Ext/RadSite/Body.h b/src/Ext/RadSite/Body.h index 58fcf0319d..cb5d3c32ee 100644 --- a/src/Ext/RadSite/Body.h +++ b/src/Ext/RadSite/Body.h @@ -82,3 +82,6 @@ class RadSiteExt static ExtContainer ExtMap; }; + +// top-level name for the RadSiteExt extension +using RadSiteClassExtension = RadSiteExt::ExtData; diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 5743030a4f..8b3973144d 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -270,3 +270,6 @@ class SWTypeExt static SuperClass* __stdcall IsSuperAvailable(int swIdx, HouseClass* pHouse); }; + +// top-level name for the SWTypeExt extension +using SuperWeaponTypeClassExtension = SWTypeExt::ExtData; diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 0ca8db9116..d750916742 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -233,3 +233,6 @@ class ScriptExt static bool MoveMissionEndStatus(TeamClass* pTeam, TechnoClass* pFocus, FootClass* pLeader = nullptr, int mode = 0); static void ChronoshiftTeamToTarget(TeamClass* pTeam, TechnoClass* pTeamLeader, AbstractClass* pTarget); }; + +// top-level name for the ScriptExt extension +using ScriptClassExtension = ScriptExt::ExtData; diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index 2b85a0c621..f9d9e1566b 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -102,3 +102,6 @@ class SideExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the SideExt extension +using SideClassExtension = SideExt::ExtData; diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index cdee8344e9..a013291a32 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -101,3 +101,6 @@ class TActionExt static ExtContainer ExtMap; }; + +// top-level name for the TActionExt extension +using TActionClassExtension = TActionExt::ExtData; diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index 460ba3b5b3..54ea8ec422 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -110,3 +110,6 @@ class TEventExt static ExtContainer ExtMap; }; + +// top-level name for the TEventExt extension +using TEventClassExtension = TEventExt::ExtData; diff --git a/src/Ext/Team/Body.h b/src/Ext/Team/Body.h index 373b559c5a..39cc45db4e 100644 --- a/src/Ext/Team/Body.h +++ b/src/Ext/Team/Body.h @@ -83,3 +83,6 @@ class TeamExt static ExtContainer ExtMap; }; + +// top-level name for the TeamExt extension +using TeamClassExtension = TeamExt::ExtData; diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 5986577580..6e0a4e35db 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -45,3 +45,6 @@ class TeamTypeExt static ExtContainer ExtMap; }; + +// top-level name for the TeamTypeExt extension +using TeamTypeClassExtension = TeamTypeExt::ExtData; diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index c1e5f1f87c..d63eba4154 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -84,3 +84,6 @@ class TerrainTypeExt static void Remove(TerrainClass* pTerrain); }; + +// top-level name for the TerrainTypeExt extension +using TerrainTypeClassExtension = TerrainTypeExt::ExtData; diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index ccb1406061..a04a39f618 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -47,3 +47,6 @@ class TiberiumExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the TiberiumExt extension +using TiberiumClassExtension = TiberiumExt::ExtData; diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index d467e9f58a..ace2014bc3 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -48,3 +48,6 @@ class VoxelAnimExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the VoxelAnimExt extension +using VoxelAnimClassExtension = VoxelAnimExt::ExtData; diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 406ca818ca..1467a6cc1c 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -62,3 +62,6 @@ class VoxelAnimTypeExt static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; + +// top-level name for the VoxelAnimTypeExt extension +using VoxelAnimTypeClassExtension = VoxelAnimTypeExt::ExtData; diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 9e1e3d1cc0..eb2a68d421 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -596,3 +596,6 @@ class WarheadTypeExt static void DetonateAt(WarheadTypeClass* pThis, AbstractClass* pTarget, TechnoClass* pOwner, int damage, HouseClass* pFiringHouse = nullptr); static void DetonateAt(WarheadTypeClass* pThis, const CoordStruct& coords, TechnoClass* pOwner, int damage, HouseClass* pFiringHouse = nullptr, AbstractClass* pTarget = nullptr); }; + +// top-level name for the WarheadTypeExt extension +using WarheadTypeClassExtension = WarheadTypeExt::ExtData; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 5135863882..e568191735 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -236,3 +236,6 @@ class WeaponTypeExt static int GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pFirer, int range); static int GetTechnoKeepRange(WeaponTypeClass* pThis, TechnoClass* pFirer, bool isMinimum); }; + +// top-level name for the WeaponTypeExt extension +using WeaponTypeClassExtension = WeaponTypeExt::ExtData; From d6544907df5f9a7b3050532fc3a257847a9bdb7e Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 22:57:41 +0300 Subject: [PATCH 08/47] Add intermediate extension bases and reparent Techno/TechnoType onto the game hierarchy --- src/Ext/AbstractType/Body.h | 17 +++++++++++++++++ src/Ext/Foot/Body.h | 18 ++++++++++++++++++ src/Ext/Mission/Body.h | 17 +++++++++++++++++ src/Ext/Object/Body.h | 19 +++++++++++++++++++ src/Ext/ObjectType/Body.h | 17 +++++++++++++++++ src/Ext/Radio/Body.h | 17 +++++++++++++++++ src/Ext/Techno/Body.cpp | 4 ++-- src/Ext/Techno/Body.h | 11 +++++++++-- src/Ext/TechnoType/Body.cpp | 4 ++-- src/Ext/TechnoType/Body.h | 11 +++++++++-- 10 files changed, 127 insertions(+), 8 deletions(-) create mode 100644 src/Ext/AbstractType/Body.h create mode 100644 src/Ext/Foot/Body.h create mode 100644 src/Ext/Mission/Body.h create mode 100644 src/Ext/Object/Body.h create mode 100644 src/Ext/ObjectType/Body.h create mode 100644 src/Ext/Radio/Body.h diff --git a/src/Ext/AbstractType/Body.h b/src/Ext/AbstractType/Body.h new file mode 100644 index 0000000000..86412940f4 --- /dev/null +++ b/src/Ext/AbstractType/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Empty intermediate base mirroring AbstractTypeClass in the extension hierarchy. +class AbstractTypeClassExtension : public AbstractClassExtension +{ +public: + explicit AbstractTypeClassExtension(AbstractTypeClass* const OwnerObject) : AbstractClassExtension(OwnerObject) + { } + + AbstractTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Foot/Body.h b/src/Ext/Foot/Body.h new file mode 100644 index 0000000000..c7539c4c5b --- /dev/null +++ b/src/Ext/Foot/Body.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + +// Empty intermediate base mirroring FootClass in the extension hierarchy. +// UnitClassExtension / InfantryClassExtension / AircraftClassExtension derive from this. +class FootClassExtension : public TechnoClassExtension +{ +public: + explicit FootClassExtension(FootClass* const OwnerObject) : TechnoClassExtension(OwnerObject) + { } + + FootClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Mission/Body.h b/src/Ext/Mission/Body.h new file mode 100644 index 0000000000..0478380052 --- /dev/null +++ b/src/Ext/Mission/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Empty intermediate base mirroring MissionClass in the extension hierarchy. +class MissionClassExtension : public ObjectClassExtension +{ +public: + explicit MissionClassExtension(MissionClass* const OwnerObject) : ObjectClassExtension(OwnerObject) + { } + + MissionClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Object/Body.h b/src/Ext/Object/Body.h new file mode 100644 index 0000000000..8cc0bd3fa5 --- /dev/null +++ b/src/Ext/Object/Body.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +// Empty intermediate base mirroring ObjectClass in the extension hierarchy. +// It carries no data of its own; it only exists so the chain of extensions +// matches the game's class hierarchy (AbstractClass -> ObjectClass -> ...). +class ObjectClassExtension : public AbstractClassExtension +{ +public: + explicit ObjectClassExtension(ObjectClass* const OwnerObject) : AbstractClassExtension(OwnerObject) + { } + + ObjectClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/ObjectType/Body.h b/src/Ext/ObjectType/Body.h new file mode 100644 index 0000000000..339c065969 --- /dev/null +++ b/src/Ext/ObjectType/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Empty intermediate base mirroring ObjectTypeClass in the extension hierarchy. +class ObjectTypeClassExtension : public AbstractTypeClassExtension +{ +public: + explicit ObjectTypeClassExtension(ObjectTypeClass* const OwnerObject) : AbstractTypeClassExtension(OwnerObject) + { } + + ObjectTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Radio/Body.h b/src/Ext/Radio/Body.h new file mode 100644 index 0000000000..709c34b7c2 --- /dev/null +++ b/src/Ext/Radio/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Empty intermediate base mirroring RadioClass in the extension hierarchy. +class RadioClassExtension : public MissionClassExtension +{ +public: + explicit RadioClassExtension(RadioClass* const OwnerObject) : MissionClassExtension(OwnerObject) + { } + + RadioClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index ec12b91bb2..5b8fc81eda 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1353,13 +1353,13 @@ void TechnoExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) void TechnoExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + RadioClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void TechnoExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + RadioClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index abbe9325a4..f676e8246d 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include #include @@ -18,9 +19,15 @@ class TechnoExt static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData : public Extension + class ExtData : public RadioClassExtension { public: + // typed owner accessor (the base chain stores the owner as RadioClass*) + TechnoClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + TechnoTypeExt::ExtData* TypeExtData; std::unique_ptr Shield; std::vector> LaserTrails; @@ -109,7 +116,7 @@ class TechnoExt bool HasDeployConverted; bool HasUndeployConverted; - ExtData(TechnoClass* OwnerObject) : Extension(OwnerObject) + ExtData(TechnoClass* OwnerObject) : RadioClassExtension(OwnerObject) , TypeExtData { nullptr } , Shield {} , LaserTrails {} diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 6c620af2b2..782a028b7d 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -1987,13 +1987,13 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void TechnoTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index a8e8c29f8a..75d4b104a2 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -23,9 +24,15 @@ class TechnoTypeExt static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0xDF4; - class ExtData : public Extension + class ExtData : public ObjectTypeClassExtension { public: + // typed owner accessor (the base chain stores the owner as ObjectTypeClass*) + TechnoTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable HealthBar_Hide; Valueable HealthBar_HidePips; Valueable HealthBar_Permanent; @@ -528,7 +535,7 @@ class TechnoTypeExt Valueable Missile_TakeOffAnim; Valueable Missile_TakeOffSeparation; - ExtData(TechnoTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(TechnoTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , HealthBar_Hide { false } , HealthBar_HidePips { false } , HealthBar_Permanent { false } From 16f1ef4305795f02f3459899888ea7df437eaac6 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Wed, 15 Jul 2026 23:04:05 +0300 Subject: [PATCH 09/47] Instantiate concrete Unit/Infantry/Aircraft leaves instead of the TechnoClass base --- Phobos.vcxproj | 16 ++++++++++++++++ src/Ext/Aircraft/Body.cpp | 10 ++++++++++ src/Ext/Aircraft/Body.h | 15 +++++++++++++++ src/Ext/AircraftType/Body.cpp | 10 ++++++++++ src/Ext/AircraftType/Body.h | 17 +++++++++++++++++ src/Ext/Building/Body.cpp | 4 +--- src/Ext/BuildingType/Body.cpp | 4 +--- src/Ext/Infantry/Body.cpp | 10 ++++++++++ src/Ext/Infantry/Body.h | 17 +++++++++++++++++ src/Ext/InfantryType/Body.cpp | 10 ++++++++++ src/Ext/InfantryType/Body.h | 17 +++++++++++++++++ src/Ext/Techno/Body.cpp | 10 ++-------- src/Ext/TechnoType/Body.cpp | 4 ++-- src/Ext/Unit/Body.cpp | 11 +++++++++++ src/Ext/Unit/Body.h | 19 +++++++++++++++++++ src/Ext/UnitType/Body.cpp | 10 ++++++++++ src/Ext/UnitType/Body.h | 17 +++++++++++++++++ 17 files changed, 185 insertions(+), 16 deletions(-) create mode 100644 src/Ext/AircraftType/Body.cpp create mode 100644 src/Ext/AircraftType/Body.h create mode 100644 src/Ext/Infantry/Body.cpp create mode 100644 src/Ext/Infantry/Body.h create mode 100644 src/Ext/InfantryType/Body.cpp create mode 100644 src/Ext/InfantryType/Body.h create mode 100644 src/Ext/Unit/Body.cpp create mode 100644 src/Ext/Unit/Body.h create mode 100644 src/Ext/UnitType/Body.cpp create mode 100644 src/Ext/UnitType/Body.h diff --git a/Phobos.vcxproj b/Phobos.vcxproj index d601082f6f..a9fbf76118 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -61,6 +61,11 @@ + + + + + @@ -317,6 +322,17 @@ + + + + + + + + + + + diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index cbd4205f8c..da5908bb22 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -3,6 +3,16 @@ #include #include +// An aircraft's extension is a concrete AircraftClassExtension leaf, owned by the TechnoClass container. +DEFINE_HOOK(0x413D24, AircraftClass_CTOR, 0x6) +{ + GET(AircraftClass*, pItem, ECX); + + TechnoExt::ExtMap.Adopt(new AircraftClassExtension(pItem)); + + return 0; +} + // TODO: Implement proper extended AircraftClass. void AircraftExt::FireWeapon(AircraftClass* pThis, AbstractClass* pTarget) diff --git a/src/Ext/Aircraft/Body.h b/src/Ext/Aircraft/Body.h index fc8f644f92..9f0c39dcb3 100644 --- a/src/Ext/Aircraft/Body.h +++ b/src/Ext/Aircraft/Body.h @@ -1,5 +1,20 @@ #pragma once #include +#include +#include + +// Concrete leaf extension for AircraftClass (empty; techno data lives in TechnoClassExtension). +class AircraftClassExtension : public FootClassExtension +{ +public: + explicit AircraftClassExtension(AircraftClass* const OwnerObject) : FootClassExtension(OwnerObject) + { } + + AircraftClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; // TODO: Implement proper extended AircraftClass. diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp new file mode 100644 index 0000000000..303ead24ec --- /dev/null +++ b/src/Ext/AircraftType/Body.cpp @@ -0,0 +1,10 @@ +#include "Body.h" + +DEFINE_HOOK(0x41C8B4, AircraftTypeClass_CTOR, 0x6) +{ + GET(AircraftTypeClass*, pItem, ECX); + + TechnoTypeExt::ExtMap.Adopt(new AircraftTypeClassExtension(pItem)); + + return 0; +} diff --git a/src/Ext/AircraftType/Body.h b/src/Ext/AircraftType/Body.h new file mode 100644 index 0000000000..f8bca0619a --- /dev/null +++ b/src/Ext/AircraftType/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Concrete leaf extension for AircraftTypeClass (empty). +class AircraftTypeClassExtension : public TechnoTypeClassExtension +{ +public: + explicit AircraftTypeClassExtension(AircraftTypeClass* const OwnerObject) : TechnoTypeClassExtension(OwnerObject) + { } + + AircraftTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 1ad5d730eb..f3f48f6e1e 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -603,9 +603,7 @@ DEFINE_HOOK(0x43BCBD, BuildingClass_CTOR, 0x6) { GET(BuildingClass*, pItem, ESI); - // The TechnoClass constructor already created a plain TechnoClassExtension for this object; - // upgrade it to a BuildingClassExtension leaf, still owned by the TechnoClass container. - TechnoExt::ExtMap.Remove(pItem); + // A building's extension is a concrete BuildingClassExtension leaf, owned by the TechnoClass container. auto const pExt = static_cast(TechnoExt::ExtMap.Adopt(new BuildingExt::ExtData(pItem))); if (pExt) diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 221783898a..2733f3ef43 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -469,9 +469,7 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) { GET(BuildingTypeClass*, pItem, EAX); - // The TechnoTypeClass constructor already created a plain TechnoTypeClassExtension; upgrade - // it to a BuildingTypeClassExtension leaf owned by the TechnoTypeClass container. - TechnoTypeExt::ExtMap.Remove(pItem); + // A building type's extension is a concrete BuildingTypeClassExtension leaf, owned by the TechnoTypeClass container. TechnoTypeExt::ExtMap.Adopt(new BuildingTypeExt::ExtData(pItem)); return 0; diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp new file mode 100644 index 0000000000..0789533757 --- /dev/null +++ b/src/Ext/Infantry/Body.cpp @@ -0,0 +1,10 @@ +#include "Body.h" + +DEFINE_HOOK(0x517A54, InfantryClass_CTOR, 0x6) +{ + GET(InfantryClass*, pItem, ECX); + + TechnoExt::ExtMap.Adopt(new InfantryClassExtension(pItem)); + + return 0; +} diff --git a/src/Ext/Infantry/Body.h b/src/Ext/Infantry/Body.h new file mode 100644 index 0000000000..f3f0f7fcd0 --- /dev/null +++ b/src/Ext/Infantry/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Concrete leaf extension for InfantryClass (empty; techno data lives in TechnoClassExtension). +class InfantryClassExtension : public FootClassExtension +{ +public: + explicit InfantryClassExtension(InfantryClass* const OwnerObject) : FootClassExtension(OwnerObject) + { } + + InfantryClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp new file mode 100644 index 0000000000..f7b13034be --- /dev/null +++ b/src/Ext/InfantryType/Body.cpp @@ -0,0 +1,10 @@ +#include "Body.h" + +DEFINE_HOOK(0x5236A4, InfantryTypeClass_CTOR, 0x5) +{ + GET(InfantryTypeClass*, pItem, ECX); + + TechnoTypeExt::ExtMap.Adopt(new InfantryTypeClassExtension(pItem)); + + return 0; +} diff --git a/src/Ext/InfantryType/Body.h b/src/Ext/InfantryType/Body.h new file mode 100644 index 0000000000..9423b7ca89 --- /dev/null +++ b/src/Ext/InfantryType/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Concrete leaf extension for InfantryTypeClass (empty). +class InfantryTypeClassExtension : public TechnoTypeClassExtension +{ +public: + explicit InfantryTypeClassExtension(InfantryTypeClass* const OwnerObject) : TechnoTypeClassExtension(OwnerObject) + { } + + InfantryTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 5b8fc81eda..30bce48af4 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1386,14 +1386,8 @@ TechnoExt::ExtContainer::~ExtContainer() = default; // ============================= // container hooks -DEFINE_HOOK(0x6F3260, TechnoClass_CTOR, 0x5) -{ - GET(TechnoClass*, pItem, ESI); - - TechnoExt::ExtMap.TryAllocate(pItem); - - return 0; -} +// The extension is allocated by the concrete leaf constructors (UnitClass/InfantryClass/ +// BuildingClass/AircraftClass), not here at the abstract TechnoClass level. DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5) { diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 782a028b7d..0d30862c2e 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -2010,8 +2010,8 @@ DEFINE_HOOK(0x711835, TechnoTypeClass_CTOR, 0x5) { GET(TechnoTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.TryAllocate(pItem); - + // The extension is allocated by the concrete type leaf constructors + // (UnitType/InfantryType/Building/AircraftType), not here at the TechnoType level. pItem->DefaultToGuardArea = RulesExt::Global()->DefaultToGuardArea; return 0; diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp new file mode 100644 index 0000000000..140716add1 --- /dev/null +++ b/src/Ext/Unit/Body.cpp @@ -0,0 +1,11 @@ +#include "Body.h" + +// A unit's extension is a concrete UnitClassExtension leaf, owned by the TechnoClass container. +DEFINE_HOOK(0x7353C4, UnitClass_CTOR, 0x5) +{ + GET(UnitClass*, pItem, ECX); + + TechnoExt::ExtMap.Adopt(new UnitClassExtension(pItem)); + + return 0; +} diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h new file mode 100644 index 0000000000..1b91312016 --- /dev/null +++ b/src/Ext/Unit/Body.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +// Concrete leaf extension for UnitClass. Empty for now: all techno-level data lives +// in TechnoClassExtension; this leaf only exists so a unit's extension has its own +// concrete type (TechnoClassExtension itself is never instantiated). +class UnitClassExtension : public FootClassExtension +{ +public: + explicit UnitClassExtension(UnitClass* const OwnerObject) : FootClassExtension(OwnerObject) + { } + + UnitClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp new file mode 100644 index 0000000000..9d47f2099f --- /dev/null +++ b/src/Ext/UnitType/Body.cpp @@ -0,0 +1,10 @@ +#include "Body.h" + +DEFINE_HOOK(0x7470D4, UnitTypeClass_CTOR, 0x6) +{ + GET(UnitTypeClass*, pItem, ECX); + + TechnoTypeExt::ExtMap.Adopt(new UnitTypeClassExtension(pItem)); + + return 0; +} diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h new file mode 100644 index 0000000000..22f49da7d2 --- /dev/null +++ b/src/Ext/UnitType/Body.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +// Concrete leaf extension for UnitTypeClass (empty; techno-type data lives in TechnoTypeClassExtension). +class UnitTypeClassExtension : public TechnoTypeClassExtension +{ +public: + explicit UnitTypeClassExtension(UnitTypeClass* const OwnerObject) : TechnoTypeClassExtension(OwnerObject) + { } + + UnitTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } +}; From 9a59cbed3b48c3ef65872d6ec0d2211382332aa3 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 11:49:47 +0300 Subject: [PATCH 10/47] Fix leaf extension ctor hooks to run after the base ctor chain --- src/Ext/Aircraft/Body.cpp | 4 ++-- src/Ext/AircraftType/Body.cpp | 4 ++-- src/Ext/Infantry/Body.cpp | 4 ++-- src/Ext/InfantryType/Body.cpp | 4 ++-- src/Ext/Unit/Body.cpp | 4 ++-- src/Ext/UnitType/Body.cpp | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index da5908bb22..53ef267f9a 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -4,9 +4,9 @@ #include // An aircraft's extension is a concrete AircraftClassExtension leaf, owned by the TechnoClass container. -DEFINE_HOOK(0x413D24, AircraftClass_CTOR, 0x6) +DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) { - GET(AircraftClass*, pItem, ECX); + GET(AircraftClass*, pItem, ESI); TechnoExt::ExtMap.Adopt(new AircraftClassExtension(pItem)); diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index 303ead24ec..d3ef02fa02 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -1,8 +1,8 @@ #include "Body.h" -DEFINE_HOOK(0x41C8B4, AircraftTypeClass_CTOR, 0x6) +DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) { - GET(AircraftTypeClass*, pItem, ECX); + GET(AircraftTypeClass*, pItem, ESI); TechnoTypeExt::ExtMap.Adopt(new AircraftTypeClassExtension(pItem)); diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp index 0789533757..64066b5e23 100644 --- a/src/Ext/Infantry/Body.cpp +++ b/src/Ext/Infantry/Body.cpp @@ -1,8 +1,8 @@ #include "Body.h" -DEFINE_HOOK(0x517A54, InfantryClass_CTOR, 0x6) +DEFINE_HOOK(0x517A60, InfantryClass_CTOR, 0xE) { - GET(InfantryClass*, pItem, ECX); + GET(InfantryClass*, pItem, ESI); TechnoExt::ExtMap.Adopt(new InfantryClassExtension(pItem)); diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index f7b13034be..3516c4b080 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -1,8 +1,8 @@ #include "Body.h" -DEFINE_HOOK(0x5236A4, InfantryTypeClass_CTOR, 0x5) +DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) { - GET(InfantryTypeClass*, pItem, ECX); + GET(InfantryTypeClass*, pItem, ESI); TechnoTypeExt::ExtMap.Adopt(new InfantryTypeClassExtension(pItem)); diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 140716add1..97525245f9 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -1,9 +1,9 @@ #include "Body.h" // A unit's extension is a concrete UnitClassExtension leaf, owned by the TechnoClass container. -DEFINE_HOOK(0x7353C4, UnitClass_CTOR, 0x5) +DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) { - GET(UnitClass*, pItem, ECX); + GET(UnitClass*, pItem, ESI); TechnoExt::ExtMap.Adopt(new UnitClassExtension(pItem)); diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index 9d47f2099f..e2c8b1bad1 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -1,8 +1,8 @@ #include "Body.h" -DEFINE_HOOK(0x7470D4, UnitTypeClass_CTOR, 0x6) +DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) { - GET(UnitTypeClass*, pItem, ECX); + GET(UnitTypeClass*, pItem, ESI); TechnoTypeExt::ExtMap.Adopt(new UnitTypeClassExtension(pItem)); From 739ed0eaf6fde3ed8fd1a117a6942affc3ecb511 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 11:51:10 +0300 Subject: [PATCH 11/47] Rename extension root class to AbstractExt --- src/Ext/AbstractType/Body.h | 4 ++-- src/Ext/Object/Body.h | 4 ++-- src/Utilities/Container.h | 16 ++++++++-------- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/Ext/AbstractType/Body.h b/src/Ext/AbstractType/Body.h index 86412940f4..884e2f7b4b 100644 --- a/src/Ext/AbstractType/Body.h +++ b/src/Ext/AbstractType/Body.h @@ -4,10 +4,10 @@ #include // Empty intermediate base mirroring AbstractTypeClass in the extension hierarchy. -class AbstractTypeClassExtension : public AbstractClassExtension +class AbstractTypeClassExtension : public AbstractExt { public: - explicit AbstractTypeClassExtension(AbstractTypeClass* const OwnerObject) : AbstractClassExtension(OwnerObject) + explicit AbstractTypeClassExtension(AbstractTypeClass* const OwnerObject) : AbstractExt(OwnerObject) { } AbstractTypeClass* OwnerObject() const diff --git a/src/Ext/Object/Body.h b/src/Ext/Object/Body.h index 8cc0bd3fa5..bd585e7269 100644 --- a/src/Ext/Object/Body.h +++ b/src/Ext/Object/Body.h @@ -6,10 +6,10 @@ // Empty intermediate base mirroring ObjectClass in the extension hierarchy. // It carries no data of its own; it only exists so the chain of extensions // matches the game's class hierarchy (AbstractClass -> ObjectClass -> ...). -class ObjectClassExtension : public AbstractClassExtension +class ObjectClassExtension : public AbstractExt { public: - explicit ObjectClassExtension(ObjectClass* const OwnerObject) : AbstractClassExtension(OwnerObject) + explicit ObjectClassExtension(ObjectClass* const OwnerObject) : AbstractExt(OwnerObject) { } ObjectClass* OwnerObject() const diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 27e3076a63..4292fa441b 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -52,21 +52,21 @@ enum class InitState // the non-template root of every extension, mirroring Vinifera's AbstractClassExtension. // it owns the back-pointer and the staged init state, so all extensions share a common base. -class AbstractClassExtension +class AbstractExt { void* AttachedToObject; InitState Initialized; public: - explicit AbstractClassExtension(void* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } + explicit AbstractExt(void* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } { } - AbstractClassExtension(const AbstractClassExtension& other) = delete; + AbstractExt(const AbstractExt& other) = delete; - void operator=(const AbstractClassExtension& RHS) = delete; + void operator=(const AbstractExt& RHS) = delete; - virtual ~AbstractClassExtension() = default; + virtual ~AbstractExt() = default; void EnsureConstanted() { @@ -138,13 +138,13 @@ class AbstractClassExtension virtual void LoadFromINIFile(CCINIClass* pINI) { } }; -// the typed layer over AbstractClassExtension: gives a strongly-typed owner accessor. +// the typed layer over AbstractExt: gives a strongly-typed owner accessor. template -class Extension : public AbstractClassExtension +class Extension : public AbstractExt { public: - explicit Extension(T* const OwnerObject) : AbstractClassExtension(OwnerObject) + explicit Extension(T* const OwnerObject) : AbstractExt(OwnerObject) { } // the object this Extension expands From 1314db42a0a06d1ce993cf0f321f9b17f29c7001 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 12:04:56 +0300 Subject: [PATCH 12/47] Reparent remaining extensions onto the game mirror hierarchy --- src/Ext/Anim/Body.cpp | 4 ++-- src/Ext/Anim/Body.h | 11 +++++++++-- src/Ext/AnimType/Body.cpp | 4 ++-- src/Ext/AnimType/Body.h | 11 +++++++++-- src/Ext/Bullet/Body.cpp | 4 ++-- src/Ext/Bullet/Body.h | 11 +++++++++-- src/Ext/BulletType/Body.cpp | 4 ++-- src/Ext/BulletType/Body.h | 11 +++++++++-- src/Ext/Cell/Body.cpp | 4 ++-- src/Ext/Cell/Body.h | 10 ++++++++-- src/Ext/House/Body.cpp | 4 ++-- src/Ext/House/Body.h | 10 ++++++++-- src/Ext/HouseType/Body.cpp | 4 ++-- src/Ext/HouseType/Body.h | 11 +++++++++-- src/Ext/OverlayType/Body.cpp | 4 ++-- src/Ext/OverlayType/Body.h | 11 +++++++++-- src/Ext/ParticleSystemType/Body.cpp | 4 ++-- src/Ext/ParticleSystemType/Body.h | 11 +++++++++-- src/Ext/ParticleType/Body.cpp | 4 ++-- src/Ext/ParticleType/Body.h | 11 +++++++++-- src/Ext/RadSite/Body.cpp | 4 ++-- src/Ext/RadSite/Body.h | 10 ++++++++-- src/Ext/SWType/Body.cpp | 4 ++-- src/Ext/SWType/Body.h | 11 +++++++++-- src/Ext/Script/Body.h | 10 ++++++++-- src/Ext/Side/Body.cpp | 4 ++-- src/Ext/Side/Body.h | 11 +++++++++-- src/Ext/TAction/Body.cpp | 4 ++-- src/Ext/TAction/Body.h | 10 ++++++++-- src/Ext/TEvent/Body.cpp | 4 ++-- src/Ext/TEvent/Body.h | 10 ++++++++-- src/Ext/Team/Body.cpp | 4 ++-- src/Ext/Team/Body.h | 10 ++++++++-- src/Ext/TeamType/Body.cpp | 4 ++-- src/Ext/TeamType/Body.h | 11 +++++++++-- src/Ext/TerrainType/Body.cpp | 4 ++-- src/Ext/TerrainType/Body.h | 11 +++++++++-- src/Ext/Tiberium/Body.cpp | 4 ++-- src/Ext/Tiberium/Body.h | 11 +++++++++-- src/Ext/VoxelAnim/Body.cpp | 4 ++-- src/Ext/VoxelAnim/Body.h | 11 +++++++++-- src/Ext/VoxelAnimType/Body.cpp | 4 ++-- src/Ext/VoxelAnimType/Body.h | 11 +++++++++-- src/Ext/WarheadType/Body.cpp | 4 ++-- src/Ext/WarheadType/Body.h | 11 +++++++++-- src/Ext/WeaponType/Body.cpp | 4 ++-- src/Ext/WeaponType/Body.h | 11 +++++++++-- 47 files changed, 255 insertions(+), 94 deletions(-) diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 16c00bda9a..8931ba910b 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -459,7 +459,7 @@ void AnimExt::ExtData::Serialize(T& Stm) void AnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectClassExtension::LoadFromStream(Stm); this->Serialize(Stm); if (this->AttachedSystem) @@ -468,7 +468,7 @@ void AnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) void AnimExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 9a4a34e2e0..7b3175e233 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -2,6 +2,7 @@ #include #include #include +#include class AnimExt { @@ -12,9 +13,15 @@ class AnimExt static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = false; // Sheer volume of animations in an average game makes a bespoke solution for pointer invalidation worthwhile. - class ExtData final : public Extension + class ExtData final : public ObjectClassExtension { public: + // typed owner accessor + AnimClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + DirType DeathUnitFacing; DirStruct DeathUnitTurretFacing; bool FromDeathUnit; @@ -34,7 +41,7 @@ class AnimExt CoordStruct FiringAnim_LastCoords; double FirepowerMult; - ExtData(AnimClass* OwnerObject) : Extension(OwnerObject) + ExtData(AnimClass* OwnerObject) : ObjectClassExtension(OwnerObject) , DeathUnitFacing { 0 } , DeathUnitTurretFacing {} , FromDeathUnit { false } diff --git a/src/Ext/AnimType/Body.cpp b/src/Ext/AnimType/Body.cpp index 77fd102436..190f809b48 100644 --- a/src/Ext/AnimType/Body.cpp +++ b/src/Ext/AnimType/Body.cpp @@ -188,13 +188,13 @@ void AnimTypeExt::ExtData::Serialize(T& Stm) void AnimTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void AnimTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index 1e3d946003..ef7fdb95f5 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -2,6 +2,7 @@ #include +#include #include #include @@ -22,9 +23,15 @@ class AnimTypeExt static constexpr DWORD Canary = 0xEEEEEEEE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + AnimTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + CustomPalette Palette; std::unique_ptr CreateUnitType; Valueable XDrawOffset; @@ -68,7 +75,7 @@ class AnimTypeExt Valueable Tiled_Interval; Valueable Tiled_AlignToCenter; - ExtData(AnimTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(AnimTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , Palette { CustomPalette::PaletteMode::Temperate } , CreateUnitType { nullptr } , XDrawOffset { 0 } diff --git a/src/Ext/Bullet/Body.cpp b/src/Ext/Bullet/Body.cpp index ad16905d82..4658ed6351 100644 --- a/src/Ext/Bullet/Body.cpp +++ b/src/Ext/Bullet/Body.cpp @@ -494,13 +494,13 @@ void BulletExt::ExtData::Serialize(T& Stm) void BulletExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void BulletExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index dae0d092d1..5212bdab16 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -4,6 +4,7 @@ #include #include #include +#include struct RadialFireStruct { @@ -20,9 +21,15 @@ class BulletExt static constexpr DWORD Canary = 0x2A2A2A2A; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectClassExtension { public: + // typed owner accessor + BulletClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + BulletTypeExt::ExtData* TypeExtData; HouseClass* FirerHouse; int CurrentStrength; @@ -38,7 +45,7 @@ class BulletExt TrajectoryPointer Trajectory; - ExtData(BulletClass* OwnerObject) : Extension(OwnerObject) + ExtData(BulletClass* OwnerObject) : ObjectClassExtension(OwnerObject) , TypeExtData { nullptr } , FirerHouse { nullptr } , CurrentStrength { 0 } diff --git a/src/Ext/BulletType/Body.cpp b/src/Ext/BulletType/Body.cpp index 63dd3ca4f7..13663c465f 100644 --- a/src/Ext/BulletType/Body.cpp +++ b/src/Ext/BulletType/Body.cpp @@ -196,13 +196,13 @@ void BulletTypeExt::ExtData::Serialize(T& Stm) void BulletTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void BulletTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 74c1e24037..040124bac1 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -16,9 +17,15 @@ class BulletTypeExt static constexpr DWORD Canary = 0xF00DF00D; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + BulletTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + // Valueable Strength; //Use OwnerObject()->ObjectTypeClass::Strength Nullable Armor; Valueable Interceptable; @@ -86,7 +93,7 @@ class BulletTypeExt Nullable BallisticScatter_Min; Nullable BallisticScatter_Max; - ExtData(BulletTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(BulletTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , Armor {} , Interceptable { false } , Interceptable_DeleteOnIntercept { false } diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index f5c5c3eda8..f26c8af575 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -17,13 +17,13 @@ void CellExt::ExtData::Serialize(T& Stm) void CellExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } void CellExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 07e5ca41ea..a64e243440 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -29,14 +29,20 @@ class CellExt bool Serialize(T& stm); }; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: + // typed owner accessor + CellClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + std::vector RadSites {}; std::vector RadLevels { }; int InfantryCount{ 0 }; - ExtData(CellClass* OwnerObject) : Extension(OwnerObject) + ExtData(CellClass* OwnerObject) : AbstractExt(OwnerObject) { } virtual ~ExtData() = default; diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 49176529ef..7e5b1c0141 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -718,13 +718,13 @@ void HouseExt::ExtData::Serialize(T& Stm) void HouseExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } void HouseExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index fa1cdb73e0..c864b5a551 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -15,9 +15,15 @@ class HouseExt static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: + // typed owner accessor + HouseClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + std::vector PowerPlantEnhancers; std::vector OwnedLimboDeliveredBuildings; std::vector OwnedCountedHarvesters; @@ -73,7 +79,7 @@ class HouseExt std::array BeaconsPlacedOrder; - ExtData(HouseClass* OwnerObject) : Extension(OwnerObject) + ExtData(HouseClass* OwnerObject) : AbstractExt(OwnerObject) , PowerPlantEnhancers {} , OwnedLimboDeliveredBuildings {} , OwnedCountedHarvesters {} diff --git a/src/Ext/HouseType/Body.cpp b/src/Ext/HouseType/Body.cpp index 9bba50f7ac..1f94ad3eb6 100644 --- a/src/Ext/HouseType/Body.cpp +++ b/src/Ext/HouseType/Body.cpp @@ -30,13 +30,13 @@ void HouseTypeExt::ExtData::Serialize(T& Stm) void HouseTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void HouseTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/HouseType/Body.h b/src/Ext/HouseType/Body.h index 3d5f886e33..ccb30280b4 100644 --- a/src/Ext/HouseType/Body.h +++ b/src/Ext/HouseType/Body.h @@ -2,6 +2,7 @@ #include +#include #include #include @@ -15,12 +16,18 @@ class HouseTypeExt static constexpr DWORD Canary = 0xAFFEAFFE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: + // typed owner accessor + HouseTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + EVAType EVATag; - ExtData(HouseTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(HouseTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , EVATag { -2 } { } diff --git a/src/Ext/OverlayType/Body.cpp b/src/Ext/OverlayType/Body.cpp index 568f54e84e..f6fdbf8ea2 100644 --- a/src/Ext/OverlayType/Body.cpp +++ b/src/Ext/OverlayType/Body.cpp @@ -38,14 +38,14 @@ void OverlayTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) void OverlayTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); this->Palette = GeneralUtils::BuildPalette(this->PaletteFile); } void OverlayTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index e9afc87e2f..1f8b3f0550 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,14 +13,20 @@ class OverlayTypeExt static constexpr DWORD Canary = 0xADF48498; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + OverlayTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable ZAdjust; PhobosFixedString<32u> PaletteFile; DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. - ExtData(OverlayTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(OverlayTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , ZAdjust { 0 } , PaletteFile {} , Palette {} diff --git a/src/Ext/ParticleSystemType/Body.cpp b/src/Ext/ParticleSystemType/Body.cpp index bb0263d5d9..f608e28abd 100644 --- a/src/Ext/ParticleSystemType/Body.cpp +++ b/src/Ext/ParticleSystemType/Body.cpp @@ -24,13 +24,13 @@ void ParticleSystemTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) void ParticleSystemTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void ParticleSystemTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/ParticleSystemType/Body.h b/src/Ext/ParticleSystemType/Body.h index 955d360794..4a91be143e 100644 --- a/src/Ext/ParticleSystemType/Body.h +++ b/src/Ext/ParticleSystemType/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,12 +13,18 @@ class ParticleSystemTypeExt static constexpr DWORD Canary = 0xF9984EFE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + ParticleSystemTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable AdjustTargetCoordsOnRotation; - ExtData(ParticleSystemTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(ParticleSystemTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , AdjustTargetCoordsOnRotation { true } { } diff --git a/src/Ext/ParticleType/Body.cpp b/src/Ext/ParticleType/Body.cpp index 5143a78976..d0c5c4751c 100644 --- a/src/Ext/ParticleType/Body.cpp +++ b/src/Ext/ParticleType/Body.cpp @@ -31,13 +31,13 @@ void ParticleTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) void ParticleTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void ParticleTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h index dfc2715479..245ec05610 100644 --- a/src/Ext/ParticleType/Body.h +++ b/src/Ext/ParticleType/Body.h @@ -2,6 +2,7 @@ #include +#include #include #include @@ -13,12 +14,18 @@ class ParticleTypeExt static constexpr DWORD Canary = 0xEAFEEAFE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + ParticleTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable Gas_MaxDriftSpeed; - ExtData(ParticleTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(ParticleTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , Gas_MaxDriftSpeed { 2 } { } diff --git a/src/Ext/RadSite/Body.cpp b/src/Ext/RadSite/Body.cpp index 2841b62646..304b9353b6 100644 --- a/src/Ext/RadSite/Body.cpp +++ b/src/Ext/RadSite/Body.cpp @@ -175,13 +175,13 @@ void RadSiteExt::ExtData::Serialize(T& Stm) void RadSiteExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } void RadSiteExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/RadSite/Body.h b/src/Ext/RadSite/Body.h index cb5d3c32ee..32dbf6920f 100644 --- a/src/Ext/RadSite/Body.h +++ b/src/Ext/RadSite/Body.h @@ -18,15 +18,21 @@ class RadSiteExt static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: + // typed owner accessor + RadSiteClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + WeaponTypeClass* Weapon; RadTypeClass* Type; HouseClass* RadHouse; TechnoClass* RadInvoker; - ExtData(RadSiteClass* OwnerObject) : Extension(OwnerObject) + ExtData(RadSiteClass* OwnerObject) : AbstractExt(OwnerObject) , RadHouse { nullptr } , RadInvoker { nullptr } , Type {} diff --git a/src/Ext/SWType/Body.cpp b/src/Ext/SWType/Body.cpp index 2d35b68969..75a4abb99f 100644 --- a/src/Ext/SWType/Body.cpp +++ b/src/Ext/SWType/Body.cpp @@ -310,13 +310,13 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) void SWTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void SWTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 8b3973144d..4b9994d4c0 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -12,9 +13,15 @@ class SWTypeExt static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: + // typed owner accessor + SuperWeaponTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + PhobosFixedString<0x20> TypeID; @@ -119,7 +126,7 @@ class SWTypeExt ValueableIdx EVA_Activated_Allies; ValueableIdx EVA_Activated_Enemies; - ExtData(SuperWeaponTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(SuperWeaponTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , TypeID { "" } , Money_Amount { 0 } , EVA_Impatient { -1 } diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index d750916742..563a98cf94 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -154,12 +154,18 @@ class ScriptExt static constexpr DWORD Canary = 0x3B3B3B3B; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: + // typed owner accessor + ScriptClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + // Nothing yet - ExtData(ScriptClass* OwnerObject) : Extension(OwnerObject) + ExtData(ScriptClass* OwnerObject) : AbstractExt(OwnerObject) // Nothing yet { } diff --git a/src/Ext/Side/Body.cpp b/src/Ext/Side/Body.cpp index ab85e7e756..d2c5717513 100644 --- a/src/Ext/Side/Body.cpp +++ b/src/Ext/Side/Body.cpp @@ -98,13 +98,13 @@ void SideExt::ExtData::Serialize(T& Stm) void SideExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void SideExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index f9d9e1566b..8549fce0a9 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,9 +13,15 @@ class SideExt static constexpr DWORD Canary = 0x05B10501; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: + // typed owner accessor + SideClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable ArrayIndex; Valueable Sidebar_GDIPositions; Valueable IngameScore_WinTheme; @@ -45,7 +52,7 @@ class SideExt PhobosPCXFile SuperWeaponSidebar_CenterPCX; PhobosPCXFile SuperWeaponSidebar_BottomPCX; - ExtData(SideClass* OwnerObject) : Extension(OwnerObject) + ExtData(SideClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , ArrayIndex { -1 } , Sidebar_GDIPositions { false } , IngameScore_WinTheme { -2 } diff --git a/src/Ext/TAction/Body.cpp b/src/Ext/TAction/Body.cpp index 2f2d4e0047..e061c1a74e 100644 --- a/src/Ext/TAction/Body.cpp +++ b/src/Ext/TAction/Body.cpp @@ -21,13 +21,13 @@ void TActionExt::ExtData::Serialize(T& Stm) void TActionExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } void TActionExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index a013291a32..b440dd7c5b 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -39,10 +39,16 @@ class TActionExt static constexpr DWORD Canary = 0x91919191; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: - ExtData(TActionClass* const OwnerObject) : Extension(OwnerObject) + // typed owner accessor + TActionClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + + ExtData(TActionClass* const OwnerObject) : AbstractExt(OwnerObject) { } virtual ~ExtData() = default; diff --git a/src/Ext/TEvent/Body.cpp b/src/Ext/TEvent/Body.cpp index f1b15bfbfe..c9310608b7 100644 --- a/src/Ext/TEvent/Body.cpp +++ b/src/Ext/TEvent/Body.cpp @@ -17,13 +17,13 @@ void TEventExt::ExtData::Serialize(T& Stm) void TEventExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } void TEventExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index 54ea8ec422..2f77501b86 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -64,10 +64,16 @@ class TEventExt static constexpr DWORD Canary = 0x91919191; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: - ExtData(TEventClass* const OwnerObject) : Extension(OwnerObject) + // typed owner accessor + TEventClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + + ExtData(TEventClass* const OwnerObject) : AbstractExt(OwnerObject) { } virtual ~ExtData() = default; diff --git a/src/Ext/Team/Body.cpp b/src/Ext/Team/Body.cpp index aea55b64eb..13a31bf2bb 100644 --- a/src/Ext/Team/Body.cpp +++ b/src/Ext/Team/Body.cpp @@ -27,13 +27,13 @@ void TeamExt::ExtData::Serialize(T& Stm) void TeamExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } void TeamExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Team/Body.h b/src/Ext/Team/Body.h index 39cc45db4e..de182a0966 100644 --- a/src/Ext/Team/Body.h +++ b/src/Ext/Team/Body.h @@ -13,9 +13,15 @@ class TeamExt static constexpr size_t ExtPointerOffset = 0x18; static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public Extension + class ExtData final : public AbstractExt { public: + // typed owner accessor + TeamClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + int WaitNoTargetAttempts; double NextSuccessWeightAward; int IdxSelectedObjectFromAIList; @@ -30,7 +36,7 @@ class TeamExt FootClass* TeamLeader; std::vector PreviousScriptList; - ExtData(TeamClass* OwnerObject) : Extension(OwnerObject) + ExtData(TeamClass* OwnerObject) : AbstractExt(OwnerObject) , WaitNoTargetAttempts { 0 } , NextSuccessWeightAward { 0 } , IdxSelectedObjectFromAIList { -1 } diff --git a/src/Ext/TeamType/Body.cpp b/src/Ext/TeamType/Body.cpp index c57fc99973..76deeb6328 100644 --- a/src/Ext/TeamType/Body.cpp +++ b/src/Ext/TeamType/Body.cpp @@ -24,13 +24,13 @@ void TeamTypeExt::ExtData::Serialize(T& Stm) void TeamTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void TeamTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 6e0a4e35db..1793797b44 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,10 +13,16 @@ class TeamTypeExt static constexpr DWORD Canary = 0xABCDEF01; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: - ExtData(TeamTypeClass* OwnerObject) : Extension(OwnerObject) + // typed owner accessor + TeamTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + + ExtData(TeamTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , SetRecruitableOnLiberate { } { } diff --git a/src/Ext/TerrainType/Body.cpp b/src/Ext/TerrainType/Body.cpp index 6ae480807b..0bff1c48ce 100644 --- a/src/Ext/TerrainType/Body.cpp +++ b/src/Ext/TerrainType/Body.cpp @@ -94,14 +94,14 @@ void TerrainTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) void TerrainTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); this->Palette = GeneralUtils::BuildPalette(this->PaletteFile); } void TerrainTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index d63eba4154..ab84607c8c 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,9 +13,15 @@ class TerrainTypeExt static constexpr DWORD Canary = 0xBEE78007; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + TerrainTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable SpawnsTiberium_Type; Valueable SpawnsTiberium_Range; Valueable> SpawnsTiberium_GrowthStage; @@ -33,7 +40,7 @@ class TerrainTypeExt PhobosFixedString<32u> PaletteFile; DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. - ExtData(TerrainTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(TerrainTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , SpawnsTiberium_Type { 0 } , SpawnsTiberium_Range { 1 } , SpawnsTiberium_GrowthStage { { 3 } } diff --git a/src/Ext/Tiberium/Body.cpp b/src/Ext/Tiberium/Body.cpp index 1809dd9c55..6d227d03dc 100644 --- a/src/Ext/Tiberium/Body.cpp +++ b/src/Ext/Tiberium/Body.cpp @@ -24,13 +24,13 @@ void TiberiumExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) void TiberiumExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void TiberiumExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index a04a39f618..b04dcc2db9 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -12,12 +13,18 @@ class TiberiumExt static constexpr DWORD Canary = 0xAABBCCDD; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: + // typed owner accessor + TiberiumClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Nullable MinimapColor; - ExtData(TiberiumClass* OwnerObject) : Extension(OwnerObject) + ExtData(TiberiumClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , MinimapColor {} { } diff --git a/src/Ext/VoxelAnim/Body.cpp b/src/Ext/VoxelAnim/Body.cpp index 26d5ce5c78..0e8b69ee6e 100644 --- a/src/Ext/VoxelAnim/Body.cpp +++ b/src/Ext/VoxelAnim/Body.cpp @@ -32,13 +32,13 @@ void VoxelAnimExt::ExtData::Serialize(T& Stm) void VoxelAnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void VoxelAnimExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index ace2014bc3..658677596b 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -4,6 +4,7 @@ #include #include +#include class VoxelAnimExt { @@ -13,14 +14,20 @@ class VoxelAnimExt static constexpr DWORD Canary = 0xAAAAAACC; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectClassExtension { public: + // typed owner accessor + VoxelAnimClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + std::vector> LaserTrails; CDTimerClass TrailerSpawnTimer; - ExtData(VoxelAnimClass* OwnerObject) : Extension(OwnerObject) + ExtData(VoxelAnimClass* OwnerObject) : ObjectClassExtension(OwnerObject) , LaserTrails() , TrailerSpawnTimer() { } diff --git a/src/Ext/VoxelAnimType/Body.cpp b/src/Ext/VoxelAnimType/Body.cpp index 07e880b6db..4357d8eb96 100644 --- a/src/Ext/VoxelAnimType/Body.cpp +++ b/src/Ext/VoxelAnimType/Body.cpp @@ -36,13 +36,13 @@ void VoxelAnimTypeExt::ExtData::Serialize(T& Stm) void VoxelAnimTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + ObjectTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void VoxelAnimTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + ObjectTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 1467a6cc1c..34a77596bb 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -1,6 +1,7 @@ #pragma once #include +#include #include #include @@ -14,9 +15,15 @@ class VoxelAnimTypeExt static constexpr DWORD Canary = 0xAAAEEEEE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public ObjectTypeClassExtension { public: + // typed owner accessor + VoxelAnimTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + ValueableIdxVector LaserTrail_Types; Valueable ExplodeOnWater; @@ -26,7 +33,7 @@ class VoxelAnimTypeExt Valueable SplashAnims_PickRandom; Valueable Trailer_SpawnDelay; - ExtData(VoxelAnimTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(VoxelAnimTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) , LaserTrail_Types() , ExplodeOnWater { false } , Warhead_Detonate { false } diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 8114e7e91f..754641610c 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -796,13 +796,13 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) void WarheadTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void WarheadTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index eb2a68d421..31c5ba687b 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include #include #include @@ -14,9 +15,15 @@ class WarheadTypeExt static constexpr DWORD Canary = 0x22222222; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: + // typed owner accessor + WarheadTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable Reveal; Valueable CreateGap; @@ -282,7 +289,7 @@ class WarheadTypeExt Valueable Shield_SelfHealing_Rate_InMinutes; public: - ExtData(WarheadTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(WarheadTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , Reveal { 0 } , CreateGap { 0 } , TransactMoney { 0 } diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 7998f9b30d..7f270c84a4 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -307,14 +307,14 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) void WeaponTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { - Extension::LoadFromStream(Stm); + AbstractTypeClassExtension::LoadFromStream(Stm); this->Serialize(Stm); } void WeaponTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) { - Extension::SaveToStream(Stm); + AbstractTypeClassExtension::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index e568191735..24b60a270f 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include @@ -13,9 +14,15 @@ class WeaponTypeExt static constexpr DWORD Canary = 0x22222222; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public Extension + class ExtData final : public AbstractTypeClassExtension { public: + // typed owner accessor + WeaponTypeClass* OwnerObject() const + { + return static_cast(this->GetAttachedObject()); + } + Valueable DiskLaser_Radius; Valueable ProjectileRange; @@ -107,7 +114,7 @@ class WeaponTypeExt Nullable CylinderRangefinding; - ExtData(WeaponTypeClass* OwnerObject) : Extension(OwnerObject) + ExtData(WeaponTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) , DiskLaser_Radius { DiskLaserClass::Radius } , ProjectileRange { Leptons(100000) } , RadType {} From 1432241ab2611732105bd98e8e8dd33bcd10fe59 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 12:17:50 +0300 Subject: [PATCH 13/47] Replace pointer invalidation broadcast with typed detach listener registry --- Phobos.vcxproj | 2 + src/Ext/Anim/Body.h | 3 - src/Ext/AnimType/Body.h | 2 - src/Ext/Building/Body.h | 11 +-- src/Ext/BuildingType/Body.h | 5 - src/Ext/Bullet/Body.h | 2 - src/Ext/BulletType/Body.h | 2 - src/Ext/Cell/Body.cpp | 8 -- src/Ext/Cell/Body.h | 3 - src/Ext/EBolt/Body.cpp | 8 -- src/Ext/EBolt/Body.h | 3 - src/Ext/House/Body.cpp | 43 ++++---- src/Ext/House/Body.h | 19 +--- src/Ext/HouseType/Body.h | 2 - src/Ext/OverlayType/Body.h | 2 - src/Ext/ParticleSystemType/Body.h | 2 - src/Ext/ParticleType/Body.h | 2 - src/Ext/RadSite/Body.h | 24 +---- src/Ext/Rules/Body.h | 7 -- src/Ext/SWType/Body.h | 2 - src/Ext/Scenario/Body.h | 8 -- src/Ext/Script/Body.h | 2 - src/Ext/Side/Body.h | 2 - src/Ext/Sidebar/Body.h | 7 -- src/Ext/TAction/Body.h | 2 - src/Ext/TEvent/Body.h | 2 - src/Ext/Team/Body.cpp | 6 +- src/Ext/Team/Body.h | 21 +--- src/Ext/TeamType/Body.h | 2 - src/Ext/Techno/Body.cpp | 6 +- src/Ext/Techno/Body.h | 21 +--- src/Ext/TechnoType/Body.h | 2 - src/Ext/TerrainType/Body.h | 2 - src/Ext/Tiberium/Body.h | 2 - src/Ext/VoxelAnim/Body.h | 1 - src/Ext/VoxelAnimType/Body.h | 2 - src/Ext/WarheadType/Body.h | 1 - src/Ext/WeaponType/Body.h | 1 - src/Phobos.Ext.cpp | 11 +-- src/Utilities/Container.h | 24 ----- src/Utilities/Detach.cpp | 159 ++++++++++++++++++++++++++++++ src/Utilities/Detach.h | 74 ++++++++++++++ 42 files changed, 283 insertions(+), 227 deletions(-) create mode 100644 src/Utilities/Detach.cpp create mode 100644 src/Utilities/Detach.h diff --git a/Phobos.vcxproj b/Phobos.vcxproj index a9fbf76118..a6d155fb7c 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -282,6 +282,7 @@ + @@ -419,6 +420,7 @@ + diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 7b3175e233..c83545d2eb 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -11,7 +11,6 @@ class AnimExt static constexpr DWORD Canary = 0xAAAAAAAA; static constexpr size_t ExtPointerOffset = 0x18; - static constexpr bool ShouldConsiderInvalidatePointer = false; // Sheer volume of animations in an average game makes a bespoke solution for pointer invalidation worthwhile. class ExtData final : public ObjectClassExtension { @@ -71,8 +70,6 @@ class AnimExt virtual ~ExtData() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void InitializeConstants() override; virtual void LoadFromStream(PhobosStreamReader& Stm) override; diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index ef7fdb95f5..2d3a3c40c8 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -124,8 +124,6 @@ class AnimTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 6baca5dfae..8718223464 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -8,11 +8,10 @@ class BuildingExt using base_type = BuildingClass; static constexpr DWORD Canary = 0x87654321; - static constexpr bool ShouldConsiderInvalidatePointer = true; // BuildingClassExtension is a leaf of the TechnoClass extension hierarchy: one extension // per object, stored inline in the shared 0x18 slot and owned by the TechnoClass container. - class ExtData final : public TechnoExt::ExtData + class ExtData final : public TechnoExt::ExtData, public Detach::Listener { public: BuildingTypeExt::ExtData* TypeExtData; @@ -64,12 +63,10 @@ class BuildingExt // virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override + virtual void OnDetach(BuildingClass* pTarget, bool removed) override { - TechnoExt::ExtData::InvalidatePointer(ptr, bRemoved); - - if (bRemoved) - AnnounceInvalidPointer(this->CurrentAirFactory, ptr); + if (removed) + AnnounceInvalidPointer(this->CurrentAirFactory, pTarget); } virtual void LoadFromStream(PhobosStreamReader& Stm) override; diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 99264a6287..8c24584bd8 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -234,11 +234,6 @@ class BuildingTypeExt virtual void Initialize() override; virtual void CompleteInitialization(); - virtual void InvalidatePointer(void* ptr, bool bRemoved) override - { - TechnoTypeExt::ExtData::InvalidatePointer(ptr, bRemoved); - } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index 5212bdab16..9285fe5b10 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -63,8 +63,6 @@ class BulletExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 040124bac1..65d40a6373 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -155,8 +155,6 @@ class BulletTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; // virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index f26c8af575..58d9aa980f 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -27,9 +27,6 @@ void CellExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) this->Serialize(Stm); } -void CellExt::ExtData::InvalidatePointer(void* ptr, bool removed) -{ } - bool CellExt::RadLevel::Load(PhobosStreamReader& stm, bool registerForChange) { return this->Serialize(stm); @@ -55,11 +52,6 @@ bool CellExt::RadLevel::Serialize(T& stm) CellExt::ExtContainer::ExtContainer() : Container("CellClass") { } CellExt::ExtContainer::~ExtContainer() = default; -bool CellExt::ExtContainer::InvalidateExtDataIgnorable(void* const ptr) const -{ - return true; -} - // ============================= // container hooks diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index a64e243440..6e41fa6cf7 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -47,8 +47,6 @@ class CellExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool removed) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -63,7 +61,6 @@ class CellExt ExtContainer(); ~ExtContainer(); - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override; }; static ExtContainer ExtMap; diff --git a/src/Ext/EBolt/Body.cpp b/src/Ext/EBolt/Body.cpp index 63e68cdfab..c686b23280 100644 --- a/src/Ext/EBolt/Body.cpp +++ b/src/Ext/EBolt/Body.cpp @@ -67,20 +67,12 @@ bool EBoltExt::SaveGlobals(PhobosStreamWriter& Stm) return Stm.Success(); } -void EBoltExt::ExtData::InvalidatePointer(void* ptr, bool removed) -{ } - // ============================= // container EBoltExt::ExtContainer::ExtContainer() : Container("EBolt") { } EBoltExt::ExtContainer::~ExtContainer() = default; -bool EBoltExt::ExtContainer::InvalidateExtDataIgnorable(void* const ptr) const -{ - return true; -} - // ============================= // container hooks diff --git a/src/Ext/EBolt/Body.h b/src/Ext/EBolt/Body.h index e6a3d0fdfc..bc803453ce 100644 --- a/src/Ext/EBolt/Body.h +++ b/src/Ext/EBolt/Body.h @@ -26,8 +26,6 @@ class EBoltExt virtual void Initialize() override { }; - virtual void InvalidatePointer(void* ptr, bool removed) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -42,7 +40,6 @@ class EBoltExt ExtContainer(); ~ExtContainer(); - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override; }; static ExtContainer ExtMap; diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 7e5b1c0141..4b0297bf89 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -740,35 +740,32 @@ bool HouseExt::SaveGlobals(PhobosStreamWriter& Stm) .Success(); } -void HouseExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) +void HouseExt::ExtData::OnDetach(BuildingClass* pTarget, bool removed) { - if (bRemoved) + if (removed) { - AnnounceInvalidPointer(this->Factory_BuildingType, ptr); - AnnounceInvalidPointer(this->Factory_InfantryType, ptr); - AnnounceInvalidPointer(this->Factory_VehicleType, ptr); - AnnounceInvalidPointer(this->Factory_NavyType, ptr); - AnnounceInvalidPointer(this->Factory_AircraftType, ptr); + AnnounceInvalidPointer(this->Factory_BuildingType, pTarget); + AnnounceInvalidPointer(this->Factory_InfantryType, pTarget); + AnnounceInvalidPointer(this->Factory_VehicleType, pTarget); + AnnounceInvalidPointer(this->Factory_NavyType, pTarget); + AnnounceInvalidPointer(this->Factory_AircraftType, pTarget); - if (ptr != nullptr) + if (!this->PowerPlantEnhancers.empty()) { - if (!this->PowerPlantEnhancers.empty()) - { - auto& vec = this->PowerPlantEnhancers; - vec.erase(std::remove(vec.begin(), vec.end(), reinterpret_cast(ptr)), vec.end()); - } + auto& vec = this->PowerPlantEnhancers; + vec.erase(std::remove(vec.begin(), vec.end(), pTarget), vec.end()); + } - if (!this->OwnedLimboDeliveredBuildings.empty()) - { - auto& vec = this->OwnedLimboDeliveredBuildings; - vec.erase(std::remove(vec.begin(), vec.end(), reinterpret_cast(ptr)), vec.end()); - } + if (!this->OwnedLimboDeliveredBuildings.empty()) + { + auto& vec = this->OwnedLimboDeliveredBuildings; + vec.erase(std::remove(vec.begin(), vec.end(), pTarget), vec.end()); + } - if (!this->RestrictedFactoryPlants.empty()) - { - auto& vec = this->RestrictedFactoryPlants; - vec.erase(std::remove(vec.begin(), vec.end(), reinterpret_cast(ptr)), vec.end()); - } + if (!this->RestrictedFactoryPlants.empty()) + { + auto& vec = this->RestrictedFactoryPlants; + vec.erase(std::remove(vec.begin(), vec.end(), pTarget), vec.end()); } } } diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index c864b5a551..aec21302a9 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -2,6 +2,7 @@ #include #include +#include #include #include @@ -13,9 +14,8 @@ class HouseExt static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0x18; - static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public AbstractExt + class ExtData final : public AbstractExt, public Detach::Listener { public: // typed owner accessor @@ -131,7 +131,7 @@ class HouseExt virtual void LoadFromINIFile(CCINIClass* pINI) override; //virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override; + virtual void OnDetach(BuildingClass* pTarget, bool removed) override; void UpdateVehicleProduction(); @@ -149,19 +149,6 @@ class HouseExt public: ExtContainer(); ~ExtContainer(); - - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override - { - auto const abs = static_cast(ptr)->WhatAmI(); - - switch (abs) - { - case AbstractType::Building: - return false; - } - - return true; - } }; static ExtContainer ExtMap; diff --git a/src/Ext/HouseType/Body.h b/src/Ext/HouseType/Body.h index ccb30280b4..47131dc9ad 100644 --- a/src/Ext/HouseType/Body.h +++ b/src/Ext/HouseType/Body.h @@ -35,8 +35,6 @@ class HouseTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index 1f8b3f0550..47e8081449 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -36,8 +36,6 @@ class OverlayTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/ParticleSystemType/Body.h b/src/Ext/ParticleSystemType/Body.h index 4a91be143e..24154087be 100644 --- a/src/Ext/ParticleSystemType/Body.h +++ b/src/Ext/ParticleSystemType/Body.h @@ -32,8 +32,6 @@ class ParticleSystemTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h index 245ec05610..3dd65e8787 100644 --- a/src/Ext/ParticleType/Body.h +++ b/src/Ext/ParticleType/Body.h @@ -33,8 +33,6 @@ class ParticleTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/RadSite/Body.h b/src/Ext/RadSite/Body.h index 32dbf6920f..be098e9599 100644 --- a/src/Ext/RadSite/Body.h +++ b/src/Ext/RadSite/Body.h @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -16,9 +17,8 @@ class RadSiteExt static constexpr DWORD Canary = 0x88446622; static constexpr size_t ExtPointerOffset = 0x18; - static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public AbstractExt + class ExtData final : public AbstractExt, public Detach::Listener { public: // typed owner accessor @@ -51,10 +51,10 @@ class RadSiteExt virtual void SaveToStream(PhobosStreamWriter& Stm) override; virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override + virtual void OnDetach(TechnoClass* pTarget, bool removed) override { - if (bRemoved) - AnnounceInvalidPointer(this->RadInvoker, ptr); + if (removed) + AnnounceInvalidPointer(this->RadInvoker, pTarget); } private: @@ -70,20 +70,6 @@ class RadSiteExt ExtContainer(); ~ExtContainer(); - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override - { - auto const abs = static_cast(ptr)->WhatAmI(); - switch (abs) - { - case AbstractType::Aircraft: - case AbstractType::Building: - case AbstractType::Infantry: - case AbstractType::Unit: - return false; - default: - return true; - } - } }; static ExtContainer ExtMap; diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 8a22c05b26..85959c1598 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -727,8 +727,6 @@ class RulesExt void InitializeAfterTypeData(RulesClass* pThis); void InitializeAfterAllLoaded(); - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -761,9 +759,4 @@ class RulesExt { Allocate(RulesClass::Instance); } - - static void PointerGotInvalid(void* ptr, bool removed) - { - Global()->InvalidatePointer(ptr, removed); - } }; diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 4b9994d4c0..5d88bd1c02 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -248,8 +248,6 @@ class SWTypeExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index b8aedbe5ea..213449b1ae 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -79,8 +79,6 @@ class ScenarioExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -113,10 +111,4 @@ class ScenarioExt { Allocate(ScenarioClass::Instance); } - - static void PointerGotInvalid(void* ptr, bool removed) - { - Global()->InvalidatePointer(ptr, removed); - } - }; diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 563a98cf94..b9670e0741 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -171,8 +171,6 @@ class ScriptExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index 8549fce0a9..5c5a45ab89 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -88,8 +88,6 @@ class SideExt virtual void LoadFromINIFile(CCINIClass* pINI) override; virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Sidebar/Body.h b/src/Ext/Sidebar/Body.h index 86644822b5..96b7d59045 100644 --- a/src/Ext/Sidebar/Body.h +++ b/src/Ext/Sidebar/Body.h @@ -21,8 +21,6 @@ class SidebarExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; private: @@ -51,10 +49,5 @@ class SidebarExt Allocate(&SidebarClass::Instance); } - static void PointerGotInvalid(void* ptr, bool removed) - { - Global()->InvalidatePointer(ptr, removed); - } - static bool __stdcall AresTabCameo_RemoveCameo(BuildType* pItem); }; diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index b440dd7c5b..b44e223386 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -53,8 +53,6 @@ class TActionExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index 2f77501b86..a21fa41d3a 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -78,8 +78,6 @@ class TEventExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Team/Body.cpp b/src/Ext/Team/Body.cpp index 13a31bf2bb..7e06eb3124 100644 --- a/src/Ext/Team/Body.cpp +++ b/src/Ext/Team/Body.cpp @@ -37,10 +37,10 @@ void TeamExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) this->Serialize(Stm); } -void TeamExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) +void TeamExt::ExtData::OnDetach(FootClass* pTarget, bool removed) { - if (bRemoved) - AnnounceInvalidPointer(this->TeamLeader, ptr); + if (removed) + AnnounceInvalidPointer(this->TeamLeader, pTarget); } // ============================= diff --git a/src/Ext/Team/Body.h b/src/Ext/Team/Body.h index de182a0966..3a218f7088 100644 --- a/src/Ext/Team/Body.h +++ b/src/Ext/Team/Body.h @@ -2,6 +2,7 @@ #include #include +#include #include class TeamExt @@ -11,9 +12,8 @@ class TeamExt static constexpr DWORD Canary = 0x414B4B41; static constexpr size_t ExtPointerOffset = 0x18; - static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData final : public AbstractExt + class ExtData final : public AbstractExt, public Detach::Listener { public: // typed owner accessor @@ -54,7 +54,7 @@ class TeamExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override; + virtual void OnDetach(FootClass* pTarget, bool removed) override; virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -69,21 +69,6 @@ class TeamExt public: ExtContainer(); ~ExtContainer(); - - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override - { - auto const abs = static_cast(ptr)->WhatAmI(); - - switch (abs) - { - case AbstractType::Infantry: - case AbstractType::Unit: - case AbstractType::Aircraft: - return false; - } - - return true; - } }; static ExtContainer ExtMap; diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 1793797b44..4f80041126 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -33,8 +33,6 @@ class TeamTypeExt Nullable SetRecruitableOnLiberate; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 30bce48af4..c8026d0369 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1345,10 +1345,10 @@ void TechnoExt::ExtData::Serialize(T& Stm) ; } -void TechnoExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) +void TechnoExt::ExtData::OnDetach(AirstrikeClass* pTarget, bool removed) { - if (bRemoved) - AnnounceInvalidPointer(this->AirstrikeTargetingMe, ptr); + if (removed) + AnnounceInvalidPointer(this->AirstrikeTargetingMe, pTarget); } void TechnoExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index f676e8246d..7237208590 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -3,11 +3,13 @@ #include #include #include +#include #include #include #include #include +class AirstrikeClass; class BulletClass; class TechnoExt @@ -17,9 +19,8 @@ class TechnoExt static constexpr DWORD Canary = 0x55555555; static constexpr size_t ExtPointerOffset = 0x18; - static constexpr bool ShouldConsiderInvalidatePointer = true; - class ExtData : public RadioClassExtension + class ExtData : public RadioClassExtension, public Detach::Listener { public: // typed owner accessor (the base chain stores the owner as RadioClass*) @@ -233,7 +234,7 @@ class TechnoExt int GetSight(); virtual ~ExtData() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override; + virtual void OnDetach(AirstrikeClass* pTarget, bool removed) override; virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -247,20 +248,6 @@ class TechnoExt public: ExtContainer(); ~ExtContainer(); - - virtual bool InvalidateExtDataIgnorable(void* const ptr) const override - { - auto const abs = static_cast(ptr)->WhatAmI(); - - switch (abs) - { - case AbstractType::Airstrike: - case AbstractType::Building: // BuildingClassExtension leaves live in this container now - return false; - default: - return true; - } - } }; static ExtContainer ExtMap; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 75d4b104a2..4240bfe6ef 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1021,8 +1021,6 @@ class TechnoTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index ab84607c8c..497e5566eb 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -63,8 +63,6 @@ class TerrainTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index b04dcc2db9..9c7fdb69c0 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -32,8 +32,6 @@ class TiberiumExt virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index 658677596b..70c567ac76 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -33,7 +33,6 @@ class VoxelAnimExt { } virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } virtual void LoadFromStream(PhobosStreamReader& Stm)override; virtual void SaveToStream(PhobosStreamWriter& Stm)override; virtual void Initialize() override; diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 34a77596bb..2b60491e99 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -46,8 +46,6 @@ class VoxelAnimTypeExt virtual ~ExtData() = default; virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } - virtual void Initialize() override; virtual void LoadFromStream(PhobosStreamReader& Stm)override; virtual void SaveToStream(PhobosStreamWriter& Stm)override; diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 31c5ba687b..1f0c0ca6bb 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -560,7 +560,6 @@ class WarheadTypeExt virtual ~ExtData() = default; virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 24b60a270f..7901945b50 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -212,7 +212,6 @@ class WeaponTypeExt virtual void LoadFromINIFile(CCINIClass* pINI) override; virtual void Initialize() override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override { } virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index f0ef55225e..5a54d0dd3f 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -42,6 +42,8 @@ #include #include +#include + #include #pragma region Implementation details @@ -59,11 +61,6 @@ concept DerivedFromSpecializationOf = template concept HasExtMap = requires { { TExt::ExtMap } -> DerivedFromSpecializationOf; }; -template -concept ExtDataConsiderPointerInvalidation = HasExtMap && requires{ - { TExt::ShouldConsiderInvalidatePointer }->std::convertible_to; -}&& TExt::ShouldConsiderInvalidatePointer == true; - template concept Clearable = requires { T::Clear(); }; @@ -103,7 +100,6 @@ struct ClearAction // calls: // T::PointerGotInvalid(void*, bool) -// T::ExtMap.PointerGotInvalid(void*, bool) struct InvalidatePointerAction { template @@ -111,8 +107,6 @@ struct InvalidatePointerAction { if constexpr (PointerInvalidationSubscribable) T::PointerGotInvalid(ptr, removed); - else if constexpr (ExtDataConsiderPointerInvalidation) - T::ExtMap.PointerGotInvalid(ptr, removed); return true; } @@ -255,6 +249,7 @@ DEFINE_HOOK(0x7258D0, AnnounceInvalidPointer, 0x6) GET(AbstractClass* const, pInvalid, ECX); GET(bool const, removed, EDX); + Detach::NotifyAbstract(pInvalid, removed); PhobosTypeRegistry::InvalidatePointer(pInvalid, removed); return 0; diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 4292fa441b..c20f279fb3 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -102,8 +102,6 @@ class AbstractExt } } - virtual void InvalidatePointer(void* ptr, bool bRemoved) = 0; - virtual inline void SaveToStream(PhobosStreamWriter& Stm) { //Stm.Save(this->AttachedToObject); @@ -318,28 +316,6 @@ class Container virtual ~Container() = default; - void PointerGotInvalid(void* ptr, bool bRemoved) - { - //this->InvalidatePointer(ptr, bRemoved); - - if (!this->InvalidateExtDataIgnorable(ptr)) - this->InvalidateExtDataPointer(ptr, bRemoved); - } - -protected: - //virtual void InvalidatePointer(void* ptr, bool bRemoved) { } - - virtual bool InvalidateExtDataIgnorable(void* const ptr) const - { - return true; - } - - void InvalidateExtDataPointer(void* const ptr, bool bRemoved) const - { - for (const auto& i : this->Items) - i->InvalidatePointer(ptr, bRemoved); - } - private: extension_type_ptr GetExtensionPointer(const_base_type_ptr key) const { diff --git a/src/Utilities/Detach.cpp b/src/Utilities/Detach.cpp new file mode 100644 index 0000000000..90bf4e8e23 --- /dev/null +++ b/src/Utilities/Detach.cpp @@ -0,0 +1,159 @@ +#include "Detach.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace Detach +{ + template + struct TypeList { }; + + // Every type a Listener may watch. A listener is notified when an object of T + // or of any class derived from T is detached. + using NotifiableTypes = TypeList< + AbstractClass, + ObjectClass, MissionClass, RadioClass, TechnoClass, FootClass, + UnitClass, AircraftClass, InfantryClass, BuildingClass, + AnimClass, BulletClass, OverlayClass, ParticleClass, ParticleSystemClass, + SmudgeClass, TerrainClass, VoxelAnimClass, WaveClass, + CellClass, FactoryClass, HouseClass, SuperClass, TeamClass, + TriggerClass, TagClass, ScriptClass, TActionClass, TEventClass, + AirstrikeClass, RadSiteClass, + AbstractTypeClass, ObjectTypeClass, TechnoTypeClass, + UnitTypeClass, AircraftTypeClass, InfantryTypeClass, BuildingTypeClass, + AnimTypeClass, BulletTypeClass, OverlayTypeClass, ParticleTypeClass, + ParticleSystemTypeClass, SmudgeTypeClass, TerrainTypeClass, VoxelAnimTypeClass, + HouseTypeClass, SideClass, TeamTypeClass, TaskForceClass, ScriptTypeClass, + TriggerTypeClass, TagTypeClass, TiberiumClass, SuperWeaponTypeClass, + WeaponTypeClass, WarheadTypeClass + >; + + template + static void NotifyChain(TypeList, AbstractClass* const pTarget, const bool removed) + { + const auto visit = [&]() + { + if constexpr (std::is_base_of_v) + Registry::Notify(static_cast(pTarget), removed); + }; + + (visit.template operator()(), ...); + } + + template + static void NotifyChain(AbstractClass* const pTarget, const bool removed) + { + NotifyChain(NotifiableTypes {}, pTarget, removed); + } + + void NotifyAbstract(AbstractClass* const pTarget, const bool removed) + { + switch (pTarget->WhatAmI()) + { + case AbstractType::Unit: NotifyChain(pTarget, removed); break; + case AbstractType::Aircraft: NotifyChain(pTarget, removed); break; + case AbstractType::Infantry: NotifyChain(pTarget, removed); break; + case AbstractType::Building: NotifyChain(pTarget, removed); break; + case AbstractType::Anim: NotifyChain(pTarget, removed); break; + case AbstractType::Bullet: NotifyChain(pTarget, removed); break; + case AbstractType::Overlay: NotifyChain(pTarget, removed); break; + case AbstractType::Particle: NotifyChain(pTarget, removed); break; + case AbstractType::ParticleSystem: NotifyChain(pTarget, removed); break; + case AbstractType::Smudge: NotifyChain(pTarget, removed); break; + case AbstractType::Terrain: NotifyChain(pTarget, removed); break; + case AbstractType::VoxelAnim: NotifyChain(pTarget, removed); break; + case AbstractType::Wave: NotifyChain(pTarget, removed); break; + case AbstractType::Cell: NotifyChain(pTarget, removed); break; + case AbstractType::Factory: NotifyChain(pTarget, removed); break; + case AbstractType::House: NotifyChain(pTarget, removed); break; + case AbstractType::Super: NotifyChain(pTarget, removed); break; + case AbstractType::Team: NotifyChain(pTarget, removed); break; + case AbstractType::Trigger: NotifyChain(pTarget, removed); break; + case AbstractType::Tag: NotifyChain(pTarget, removed); break; + case AbstractType::Script: NotifyChain(pTarget, removed); break; + case AbstractType::Action: NotifyChain(pTarget, removed); break; + case AbstractType::Event: NotifyChain(pTarget, removed); break; + case AbstractType::Airstrike: NotifyChain(pTarget, removed); break; + case AbstractType::RadSite: NotifyChain(pTarget, removed); break; + case AbstractType::UnitType: NotifyChain(pTarget, removed); break; + case AbstractType::AircraftType: NotifyChain(pTarget, removed); break; + case AbstractType::InfantryType: NotifyChain(pTarget, removed); break; + case AbstractType::BuildingType: NotifyChain(pTarget, removed); break; + case AbstractType::AnimType: NotifyChain(pTarget, removed); break; + case AbstractType::BulletType: NotifyChain(pTarget, removed); break; + case AbstractType::OverlayType: NotifyChain(pTarget, removed); break; + case AbstractType::ParticleType: NotifyChain(pTarget, removed); break; + case AbstractType::ParticleSystemType: NotifyChain(pTarget, removed); break; + case AbstractType::SmudgeType: NotifyChain(pTarget, removed); break; + case AbstractType::TerrainType: NotifyChain(pTarget, removed); break; + case AbstractType::VoxelAnimType: NotifyChain(pTarget, removed); break; + case AbstractType::HouseType: NotifyChain(pTarget, removed); break; + case AbstractType::Side: NotifyChain(pTarget, removed); break; + case AbstractType::TeamType: NotifyChain(pTarget, removed); break; + case AbstractType::TaskForce: NotifyChain(pTarget, removed); break; + case AbstractType::ScriptType: NotifyChain(pTarget, removed); break; + case AbstractType::TriggerType: NotifyChain(pTarget, removed); break; + case AbstractType::TagType: NotifyChain(pTarget, removed); break; + case AbstractType::Tiberium: NotifyChain(pTarget, removed); break; + case AbstractType::SuperWeaponType: NotifyChain(pTarget, removed); break; + case AbstractType::WeaponType: NotifyChain(pTarget, removed); break; + case AbstractType::WarheadType: NotifyChain(pTarget, removed); break; + default: Registry::Notify(pTarget, removed); break; + } + } +} diff --git a/src/Utilities/Detach.h b/src/Utilities/Detach.h new file mode 100644 index 0000000000..276cc629a0 --- /dev/null +++ b/src/Utilities/Detach.h @@ -0,0 +1,74 @@ +#pragma once + +#include + +class AbstractClass; + +// Typed pointer-invalidation registry, mirroring Vinifera's detach listener system. +// An extension (or a static aggregate for high-volume types) subscribes to deletions +// of a game type T by inheriting Detach::Listener and overriding OnDetach. +// The game's Detach_This_From_All funnel (hook at 0x7258D0) dispatches the dying +// object to Registry::Notify for its concrete type and every base of it. +namespace Detach +{ + template + class Listener; + + template + class Registry final + { + public: + Registry() = delete; + + static void Add(Listener* const pListener) + { + pListener->RegistryIndex = Listeners.size(); + Listeners.push_back(pListener); + } + + static void Remove(Listener* const pListener) + { + const size_t index = pListener->RegistryIndex; + Listeners[index] = Listeners.back(); + Listeners[index]->RegistryIndex = index; + Listeners.pop_back(); + } + + static void Notify(T* const pTarget, const bool removed) + { + // backwards: a listener may unregister itself from within OnDetach + for (size_t i = Listeners.size(); i-- > 0;) + Listeners[i]->OnDetach(pTarget, removed); + } + + private: + static inline std::vector*> Listeners {}; + }; + + template + class Listener + { + public: + Listener() + { + Registry::Add(this); + } + + Listener(const Listener&) = delete; + Listener& operator=(const Listener&) = delete; + + virtual ~Listener() + { + Registry::Remove(this); + } + + virtual void OnDetach(T* pTarget, bool removed) = 0; + + private: + size_t RegistryIndex; + + friend class Registry; + }; + + void NotifyAbstract(AbstractClass* pTarget, bool removed); +} From 98fa43d8fa004257be978d13e56c0542a9b0f182 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 12:37:16 +0300 Subject: [PATCH 14/47] Centralize extension save/load through per-container streams and post-swizzle relink --- src/Ext/Anim/Body.cpp | 37 +--- src/Ext/Anim/Body.h | 1 + src/Ext/AnimType/Body.cpp | 24 --- src/Ext/Bullet/Body.cpp | 23 --- src/Ext/BulletType/Body.cpp | 23 --- src/Ext/Cell/Body.cpp | 22 --- src/Ext/House/Body.cpp | 25 --- src/Ext/HouseType/Body.cpp | 24 --- src/Ext/OverlayType/Body.cpp | 25 --- src/Ext/ParticleSystemType/Body.cpp | 25 --- src/Ext/ParticleType/Body.cpp | 25 --- src/Ext/RadSite/Body.cpp | 24 --- src/Ext/SWType/Body.cpp | 23 --- src/Ext/Side/Body.cpp | 23 --- src/Ext/Sidebar/Body.cpp | 1 + src/Ext/TAction/Body.cpp | 40 ----- src/Ext/TEvent/Body.cpp | 40 ----- src/Ext/Team/Body.cpp | 23 --- src/Ext/TeamType/Body.cpp | 25 --- src/Ext/Techno/Body.cpp | 47 +++-- src/Ext/Techno/Body.h | 3 + src/Ext/TechnoType/Body.cpp | 46 +++-- src/Ext/TechnoType/Body.h | 3 + src/Ext/TerrainType/Body.cpp | 25 --- src/Ext/Tiberium/Body.cpp | 25 --- src/Ext/VoxelAnim/Body.cpp | 22 --- src/Ext/VoxelAnimType/Body.cpp | 23 --- src/Ext/WarheadType/Body.cpp | 25 --- src/Ext/WeaponType/Body.cpp | 25 --- src/Phobos.Ext.cpp | 76 ++++++++- src/Utilities/Container.h | 255 +++++++++++++++------------- 31 files changed, 264 insertions(+), 764 deletions(-) diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 8931ba910b..415fe440c9 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -461,7 +461,10 @@ void AnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) { ObjectClassExtension::LoadFromStream(Stm); this->Serialize(Stm); +} +void AnimExt::ExtData::PostLoad() +{ if (this->AttachedSystem) AnimExt::AnimsWithAttachedParticles.push_back(this->OwnerObject()); } @@ -547,15 +550,6 @@ DEFINE_HOOK(0x422126, AnimClass_CTOR_NullType, 0x5) return 0; } -DEFINE_HOOK(0x4228D2, AnimClass_CTOR_Load, 0x5) -{ - GET(AnimClass*, pItem, ESI); - - AnimExt::ExtMap.Allocate(pItem); - - return 0; -} - DEFINE_HOOK(0x4226F6, AnimClass_CTOR, 0x6) { GET(AnimClass*, pItem, ESI); @@ -591,31 +585,6 @@ DEFINE_HOOK(0x426598, AnimClass_SDDTOR, 0x7) } */ -DEFINE_HOOK_AGAIN(0x425280, AnimClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x4253B0, AnimClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(AnimClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - AnimExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK_AGAIN(0x425391, AnimClass_Load_Suffix, 0x7) -DEFINE_HOOK_AGAIN(0x4253A2, AnimClass_Load_Suffix, 0x7) -DEFINE_HOOK(0x425358, AnimClass_Load_Suffix, 0x7) -{ - AnimExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x4253FF, AnimClass_Save_Suffix, 0x5) -{ - AnimExt::ExtMap.SaveStatic(); - return 0; -} - // Field D0 in AnimClass is mostly unused so by removing the few uses it has it can be used to store AnimExt pointer. DEFINE_JUMP(LJMP, 0x42543A, 0x425448) diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index c83545d2eb..40141ab4a7 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -74,6 +74,7 @@ class AnimExt virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void PostLoad() override; private: template diff --git a/src/Ext/AnimType/Body.cpp b/src/Ext/AnimType/Body.cpp index 190f809b48..b620be54cf 100644 --- a/src/Ext/AnimType/Body.cpp +++ b/src/Ext/AnimType/Body.cpp @@ -217,30 +217,6 @@ DEFINE_HOOK(0x428EA8, AnimTypeClass_SDDTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x428970, AnimTypeClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x428800, AnimTypeClass_SaveLoad_Prefix, 0xA) -{ - GET_STACK(AnimTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - AnimTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK_AGAIN(0x42892C, AnimTypeClass_Load_Suffix, 0x6) -DEFINE_HOOK(0x428958, AnimTypeClass_Load_Suffix, 0x6) -{ - AnimTypeExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x42898A, AnimTypeClass_Save_Suffix, 0x3) -{ - AnimTypeExt::ExtMap.SaveStatic(); - return 0; -} - //DEFINE_HOOK_AGAIN(0x4287E9, AnimTypeClass_LoadFromINI, 0xA)// Section dont exist! DEFINE_HOOK(0x4287DC, AnimTypeClass_LoadFromINI, 0xA) { diff --git a/src/Ext/Bullet/Body.cpp b/src/Ext/Bullet/Body.cpp index 4658ed6351..fbd17355d2 100644 --- a/src/Ext/Bullet/Body.cpp +++ b/src/Ext/Bullet/Body.cpp @@ -530,26 +530,3 @@ DEFINE_HOOK(0x4665E9, BulletClass_DTOR, 0xA) return 0; } -DEFINE_HOOK_AGAIN(0x46AFB0, BulletClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x46AE70, BulletClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(BulletClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - BulletExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK_AGAIN(0x46AF97, BulletClass_Load_Suffix, 0x7) -DEFINE_HOOK(0x46AF9E, BulletClass_Load_Suffix, 0x7) -{ - BulletExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x46AFC4, BulletClass_Save_Suffix, 0x3) -{ - BulletExt::ExtMap.SaveStatic(); - return 0; -} diff --git a/src/Ext/BulletType/Body.cpp b/src/Ext/BulletType/Body.cpp index 13663c465f..28922e6f9b 100644 --- a/src/Ext/BulletType/Body.cpp +++ b/src/Ext/BulletType/Body.cpp @@ -233,29 +233,6 @@ DEFINE_HOOK(0x46C8B6, BulletTypeClass_SDDTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x46C730, BulletTypeClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x46C6A0, BulletTypeClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(BulletTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - BulletTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x46C722, BulletTypeClass_Load_Suffix, 0x4) -{ - BulletTypeExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x46C74A, BulletTypeClass_Save_Suffix, 0x3) -{ - BulletTypeExt::ExtMap.SaveStatic(); - return 0; -} - //DEFINE_HOOK_AGAIN(0x46C429, BulletTypeClass_LoadFromINI, 0xA)// Section dont exist! DEFINE_HOOK(0x46C41C, BulletTypeClass_LoadFromINI, 0xA) { diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 58d9aa980f..bd656b5875 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -73,25 +73,3 @@ DEFINE_HOOK(0x47BB60, CellClass_DTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x483C10, CellClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x4839F0, CellClass_SaveLoad_Prefix, 0x7) -{ - GET_STACK(CellClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - CellExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x483C00, CellClass_Load_Suffix, 5) -{ - CellExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x483C79, CellClass_Save_Suffix, 0x6) -{ - CellExt::ExtMap.SaveStatic(); - return 0; -} diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 4b0297bf89..b178dd6577 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -800,31 +800,6 @@ DEFINE_HOOK(0x4F7371, HouseClass_DTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x504080, HouseClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x503040, HouseClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(HouseClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - HouseExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x504069, HouseClass_Load_Suffix, 0x7) -{ - HouseExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x5046DE, HouseClass_Save_Suffix, 0x7) -{ - HouseExt::ExtMap.SaveStatic(); - - return 0; -} - DEFINE_HOOK(0x50114D, HouseClass_InitFromINI, 0x5) { GET(HouseClass* const, pThis, EBX); diff --git a/src/Ext/HouseType/Body.cpp b/src/Ext/HouseType/Body.cpp index 1f94ad3eb6..850920847b 100644 --- a/src/Ext/HouseType/Body.cpp +++ b/src/Ext/HouseType/Body.cpp @@ -85,30 +85,6 @@ DEFINE_HOOK(0x5127CF, HouseTypeClass_DTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x512480, HouseTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x512290, HouseTypeClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(HouseTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - HouseTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x51246D, HouseTypeClass_Load_Suffix, 0x5) -{ - - HouseTypeExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x51255C, HouseTypeClass_Save_Suffix, 0x5) -{ - HouseTypeExt::ExtMap.SaveStatic(); - return 0; -} - DEFINE_HOOK_AGAIN(0x51215A, HouseTypeClass_LoadFromINI, 0x5) DEFINE_HOOK(0x51214F, HouseTypeClass_LoadFromINI, 0x5) { diff --git a/src/Ext/OverlayType/Body.cpp b/src/Ext/OverlayType/Body.cpp index f6fdbf8ea2..7ab84349b0 100644 --- a/src/Ext/OverlayType/Body.cpp +++ b/src/Ext/OverlayType/Body.cpp @@ -89,31 +89,6 @@ DEFINE_HOOK(0x5FEF61, OverlayTypeClass_SDDTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x5FEAF0, OverlayTypeClass_SaveLoad_Prefix, 0xA) -DEFINE_HOOK(0x5FEC10, OverlayTypeClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(OverlayTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - OverlayTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x5FEBFA, OverlayTypeClass_Load_Suffix, 0x6) -{ - OverlayTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x5FEC2A, OverlayTypeClass_Save_Suffix, 0x6) -{ - OverlayTypeExt::ExtMap.SaveStatic(); - - return 0; -} - //DEFINE_HOOK_AGAIN(0x5FEA1E, OverlayTypeClass_LoadFromINI, 0xA)// Section dont exist! DEFINE_HOOK(0x5FEA11, OverlayTypeClass_LoadFromINI, 0xA) { diff --git a/src/Ext/ParticleSystemType/Body.cpp b/src/Ext/ParticleSystemType/Body.cpp index f608e28abd..67739de351 100644 --- a/src/Ext/ParticleSystemType/Body.cpp +++ b/src/Ext/ParticleSystemType/Body.cpp @@ -73,31 +73,6 @@ DEFINE_HOOK(0x644986, ParticleSystemTypeClass_SDDTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x644830, ParticleSystemTypeClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x6447E0, ParticleSystemTypeClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(ParticleSystemTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - ParticleSystemTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x64481F, ParticleSystemTypeClass_Load_Suffix, 0x6) -{ - ParticleSystemTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x644844, ParticleSystemTypeClass_Save_Suffix, 0x5) -{ - ParticleSystemTypeExt::ExtMap.SaveStatic(); - - return 0; -} - //DEFINE_HOOK_AGAIN(0x644620, ParticleSystemTypeClass_LoadFromINI, 0x5)// Section dont exist! DEFINE_HOOK(0x644615, ParticleSystemTypeClass_LoadFromINI, 0x5) { diff --git a/src/Ext/ParticleType/Body.cpp b/src/Ext/ParticleType/Body.cpp index d0c5c4751c..0b7c7a618e 100644 --- a/src/Ext/ParticleType/Body.cpp +++ b/src/Ext/ParticleType/Body.cpp @@ -71,31 +71,6 @@ DEFINE_HOOK(0x644DBB, ParticleTypeClass_CTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x6457A0, ParticleTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x645660, ParticleTypeClass_SaveLoad_Prefix, 0x7) -{ - GET_STACK(ParticleTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - ParticleTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x64578C, ParticleTypeClass_Load_Suffix, 0x5) -{ - ParticleTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x64580A, ParticleTypeClass_Save_Suffix, 0x7) -{ - ParticleTypeExt::ExtMap.SaveStatic(); - - return 0; -} - DEFINE_HOOK(0x6453FF, ParticleTypeClass_LoadFromINI, 0x6) { GET(ParticleTypeClass*, pItem, ESI); diff --git a/src/Ext/RadSite/Body.cpp b/src/Ext/RadSite/Body.cpp index 304b9353b6..41aa0e99ae 100644 --- a/src/Ext/RadSite/Body.cpp +++ b/src/Ext/RadSite/Body.cpp @@ -237,27 +237,3 @@ DEFINE_HOOK(0x65B2F4, RadSiteClass_DTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x65B3D0, RadSiteClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x65B450, RadSiteClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(RadSiteClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - RadSiteExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x65B43F, RadSiteClass_Load_Suffix, 0x7) -{ - RadSiteExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x65B464, RadSiteClass_Save_Suffix, 0x5) -{ - RadSiteExt::ExtMap.SaveStatic(); - - return 0; -} diff --git a/src/Ext/SWType/Body.cpp b/src/Ext/SWType/Body.cpp index 75a4abb99f..6a768676f8 100644 --- a/src/Ext/SWType/Body.cpp +++ b/src/Ext/SWType/Body.cpp @@ -373,29 +373,6 @@ DEFINE_HOOK(0x6CEFE0, SuperWeaponTypeClass_SDDTOR, 0x8) return 0; } -DEFINE_HOOK_AGAIN(0x6CE8D0, SuperWeaponTypeClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x6CE800, SuperWeaponTypeClass_SaveLoad_Prefix, 0xA) -{ - GET_STACK(SuperWeaponTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - SWTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x6CE8BE, SuperWeaponTypeClass_Load_Suffix, 0x7) -{ - SWTypeExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x6CE8EA, SuperWeaponTypeClass_Save_Suffix, 0x3) -{ - SWTypeExt::ExtMap.SaveStatic(); - return 0; -} - //DEFINE_HOOK_AGAIN(0x6CEE50, SuperWeaponTypeClass_LoadFromINI, 0xA)// Section dont exist! DEFINE_HOOK(0x6CEE43, SuperWeaponTypeClass_LoadFromINI, 0xA) { diff --git a/src/Ext/Side/Body.cpp b/src/Ext/Side/Body.cpp index d2c5717513..c73b2ff779 100644 --- a/src/Ext/Side/Body.cpp +++ b/src/Ext/Side/Body.cpp @@ -145,29 +145,6 @@ DEFINE_HOOK(0x6A499F, SideClass_SDDTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x6A48A0, SideClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x6A4780, SideClass_SaveLoad_Prefix, 0x6) -{ - GET_STACK(SideClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - SideExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x6A488B, SideClass_Load_Suffix, 0x6) -{ - SideExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x6A48FC, SideClass_Save_Suffix, 0x5) -{ - SideExt::ExtMap.SaveStatic(); - return 0; -} - DEFINE_HOOK(0x679A10, SideClass_LoadAllFromINI, 0x5) { GET_STACK(CCINIClass*, pINI, 0x4); diff --git a/src/Ext/Sidebar/Body.cpp b/src/Ext/Sidebar/Body.cpp index 57ee601f0a..532b640d1d 100644 --- a/src/Ext/Sidebar/Body.cpp +++ b/src/Ext/Sidebar/Body.cpp @@ -196,3 +196,4 @@ DEFINE_HOOK(0x6AC5EA, SidebarClass_Save_Suffix, 0x6) return 0; } + diff --git a/src/Ext/TAction/Body.cpp b/src/Ext/TAction/Body.cpp index e061c1a74e..4fcee58310 100644 --- a/src/Ext/TAction/Body.cpp +++ b/src/Ext/TAction/Body.cpp @@ -786,43 +786,3 @@ TActionExt::ExtContainer::~ExtContainer() = default; // ============================= // container hooks -#ifdef MAKE_GAME_SLOWER_FOR_NO_REASON -DEFINE_HOOK(0x6DD176, TActionClass_CTOR, 0x5) -{ - GET(TActionClass*, pItem, ESI); - - TActionExt::ExtMap.TryAllocate(pItem); - return 0; -} - -DEFINE_HOOK(0x6E4761, TActionClass_SDDTOR, 0x6) -{ - GET(TActionClass*, pItem, ESI); - - TActionExt::ExtMap.Remove(pItem); - return 0; -} - -DEFINE_HOOK_AGAIN(0x6E3E30, TActionClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x6E3DB0, TActionClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(TActionClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TActionExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x6E3E29, TActionClass_Load_Suffix, 0x4) -{ - TActionExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x6E3E4A, TActionClass_Save_Suffix, 0x3) -{ - TActionExt::ExtMap.SaveStatic(); - return 0; -} -#endif diff --git a/src/Ext/TEvent/Body.cpp b/src/Ext/TEvent/Body.cpp index c9310608b7..f88669c475 100644 --- a/src/Ext/TEvent/Body.cpp +++ b/src/Ext/TEvent/Body.cpp @@ -349,43 +349,3 @@ TEventExt::ExtContainer::~ExtContainer() = default; // ============================= // container hooks -#ifdef MAKE_GAME_SLOWER_FOR_NO_REASON -DEFINE_HOOK(0x6DD176, TActionClass_CTOR, 0x5) -{ - GET(TActionClass*, pItem, ESI); - - TActionExt::ExtMap.TryAllocate(pItem); - return 0; -} - -DEFINE_HOOK(0x6E4761, TActionClass_SDDTOR, 0x6) -{ - GET(TActionClass*, pItem, ESI); - - TActionExt::ExtMap.Remove(pItem); - return 0; -} - -DEFINE_HOOK_AGAIN(0x6E3E30, TActionClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x6E3DB0, TActionClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(TActionClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TActionExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x6E3E29, TActionClass_Load_Suffix, 0x4) -{ - TActionExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x6E3E4A, TActionClass_Save_Suffix, 0x3) -{ - TActionExt::ExtMap.SaveStatic(); - return 0; -} -#endif diff --git a/src/Ext/Team/Body.cpp b/src/Ext/Team/Body.cpp index 7e06eb3124..9cacdb9119 100644 --- a/src/Ext/Team/Body.cpp +++ b/src/Ext/Team/Body.cpp @@ -72,26 +72,3 @@ DEFINE_HOOK(0x6E8EC6, TeamClass_DTOR, 0x9) return 0; } -DEFINE_HOOK_AGAIN(0x6EC450, TeamClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x6EC540, TeamClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(TeamClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TeamExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x6EC52F, TeamClass_Load_Suffix, 0x6) -{ - TeamExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x6EC55A, TeamClass_Save_Suffix, 0x5) -{ - TeamExt::ExtMap.SaveStatic(); - return 0; -} diff --git a/src/Ext/TeamType/Body.cpp b/src/Ext/TeamType/Body.cpp index 76deeb6328..7d720a71a6 100644 --- a/src/Ext/TeamType/Body.cpp +++ b/src/Ext/TeamType/Body.cpp @@ -62,31 +62,6 @@ DEFINE_HOOK(0x6F20D0, TeamTypeClass_DTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x6F1BB0, TeamTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x6F1B90, TeamTypeClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(TeamTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TeamTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x6F1C35, TeamTypeClass_Load_Suffix, 0x5) -{ - TeamTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x6F1BAA, TeamTypeClass_Save_Suffix, 0x5) -{ - TeamTypeExt::ExtMap.SaveStatic(); - - return 0; -} - DEFINE_HOOK_AGAIN(0x6F1535, TeamTypeClass_LoadFromINI, 0xA) DEFINE_HOOK(0x6F1528, TeamTypeClass_LoadFromINI, 0xA) { diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index c8026d0369..3cc08cff12 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1,8 +1,12 @@ #include "Body.h" +#include #include +#include #include #include +#include +#include #include #include #include @@ -1382,6 +1386,24 @@ TechnoExt::ExtContainer::ExtContainer() : Container("TechnoClass") { } TechnoExt::ExtContainer::~ExtContainer() = default; +TechnoExt::ExtData* TechnoExt::ExtContainer::CreateExtData(AbstractType tag, TechnoClass* pOwner) const +{ + switch (tag) + { + case AbstractType::Unit: + return new UnitClassExtension(static_cast(pOwner)); + case AbstractType::Infantry: + return new InfantryClassExtension(static_cast(pOwner)); + case AbstractType::Aircraft: + return new AircraftClassExtension(static_cast(pOwner)); + case AbstractType::Building: + return new BuildingExt::ExtData(static_cast(pOwner)); + default: + Debug::FatalErrorAndExit("TechnoExt - unexpected extension tag %d in the save stream!\n", static_cast(tag)); + return nullptr; + } +} + // ============================= // container hooks @@ -1401,31 +1423,6 @@ DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x70C250, TechnoClass_SaveLoad_Prefix, 0x8) -DEFINE_HOOK(0x70BF50, TechnoClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(TechnoClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TechnoExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x70C249, TechnoClass_Load_Suffix, 0x5) -{ - TechnoExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x70C264, TechnoClass_Save_Suffix, 0x5) -{ - TechnoExt::ExtMap.SaveStatic(); - - return 0; -} - DEFINE_HOOK(0x710415, TechnoClass_DetachAnim, 0x6) { GET(TechnoClass*, pThis, ECX); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 7237208590..1e4d8c61d9 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -248,6 +248,9 @@ class TechnoExt public: ExtContainer(); ~ExtContainer(); + + protected: + virtual ExtData* CreateExtData(AbstractType tag, TechnoClass* pOwner) const override; }; static ExtContainer ExtMap; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 0d30862c2e..d868c25021 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -2,10 +2,13 @@ #include +#include #include #include #include +#include #include +#include #include #include @@ -2003,6 +2006,24 @@ void TechnoTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) TechnoTypeExt::ExtContainer::ExtContainer() : Container("TechnoTypeClass") { } TechnoTypeExt::ExtContainer::~ExtContainer() = default; +TechnoTypeExt::ExtData* TechnoTypeExt::ExtContainer::CreateExtData(AbstractType tag, TechnoTypeClass* pOwner) const +{ + switch (tag) + { + case AbstractType::UnitType: + return new UnitTypeClassExtension(static_cast(pOwner)); + case AbstractType::InfantryType: + return new InfantryTypeClassExtension(static_cast(pOwner)); + case AbstractType::AircraftType: + return new AircraftTypeClassExtension(static_cast(pOwner)); + case AbstractType::BuildingType: + return new BuildingTypeExt::ExtData(static_cast(pOwner)); + default: + Debug::FatalErrorAndExit("TechnoTypeExt - unexpected extension tag %d in the save stream!\n", static_cast(tag)); + return nullptr; + } +} + // ============================= // container hooks @@ -2026,31 +2047,6 @@ DEFINE_HOOK(0x711AE0, TechnoTypeClass_DTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x716DC0, TechnoTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x7162F0, TechnoTypeClass_SaveLoad_Prefix, 0x6) -{ - GET_STACK(TechnoTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TechnoTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x716DAC, TechnoTypeClass_Load_Suffix, 0xA) -{ - TechnoTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x717094, TechnoTypeClass_Save_Suffix, 0x5) -{ - TechnoTypeExt::ExtMap.SaveStatic(); - - return 0; -} - //DEFINE_HOOK_AGAIN(0x716132, TechnoTypeClass_LoadFromINI, 0x5)// Section dont exist! DEFINE_HOOK(0x716123, TechnoTypeClass_LoadFromINI, 0x5) { diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 4240bfe6ef..67f7b4f7cb 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1054,6 +1054,9 @@ class TechnoTypeExt public: ExtContainer(); ~ExtContainer(); + + protected: + virtual ExtData* CreateExtData(AbstractType tag, TechnoTypeClass* pOwner) const override; }; static ExtContainer ExtMap; diff --git a/src/Ext/TerrainType/Body.cpp b/src/Ext/TerrainType/Body.cpp index 0bff1c48ce..254b5751d2 100644 --- a/src/Ext/TerrainType/Body.cpp +++ b/src/Ext/TerrainType/Body.cpp @@ -147,31 +147,6 @@ DEFINE_HOOK(0x71E364, TerrainTypeClass_SDDTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x71E1D0, TerrainTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x71E240, TerrainTypeClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(TerrainTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TerrainTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x71E235, TerrainTypeClass_Load_Suffix, 0x5) -{ - TerrainTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x71E25A, TerrainTypeClass_Save_Suffix, 0x5) -{ - TerrainTypeExt::ExtMap.SaveStatic(); - - return 0; -} - DEFINE_HOOK(0x71E0A6, TerrainTypeClass_LoadFromINI, 0x5) { GET(TerrainTypeClass*, pItem, ESI); diff --git a/src/Ext/Tiberium/Body.cpp b/src/Ext/Tiberium/Body.cpp index 6d227d03dc..d11359ca3f 100644 --- a/src/Ext/Tiberium/Body.cpp +++ b/src/Ext/Tiberium/Body.cpp @@ -73,31 +73,6 @@ DEFINE_HOOK(0x721888, TiberiumClass_DTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x721E80, TiberiumClass_SaveLoad_Prefix, 0x7) -DEFINE_HOOK(0x7220D0, TiberiumClass_SaveLoad_Prefix, 0x5) -{ - GET_STACK(TiberiumClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - TiberiumExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x72208C, TiberiumClass_Load_Suffix, 0x7) -{ - TiberiumExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x72212C, TiberiumClass_Save_Suffix, 0x5) -{ - TiberiumExt::ExtMap.SaveStatic(); - - return 0; -} - //DEFINE_HOOK_AGAIN(0x721CE9, TiberiumClass_LoadFromINI, 0xA)// Section dont exist! DEFINE_HOOK_AGAIN(0x721CDC, TiberiumClass_LoadFromINI, 0xA) DEFINE_HOOK(0x721C7B, TiberiumClass_LoadFromINI, 0xA) diff --git a/src/Ext/VoxelAnim/Body.cpp b/src/Ext/VoxelAnim/Body.cpp index 0e8b69ee6e..bceae1b6d1 100644 --- a/src/Ext/VoxelAnim/Body.cpp +++ b/src/Ext/VoxelAnim/Body.cpp @@ -83,25 +83,3 @@ DEFINE_HOOK(0x7499F1, VoxelAnimClass_DTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x74A970, VoxelAnimClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x74AA10, VoxelAnimClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(VoxelAnimClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - VoxelAnimExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x74A9FB, VoxelAnimClass_Load_Suffix, 0x7) -{ - VoxelAnimExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x74AA24, VoxelAnimClass_Save_Suffix, 0x5) -{ - VoxelAnimExt::ExtMap.SaveStatic(); - return 0; -} diff --git a/src/Ext/VoxelAnimType/Body.cpp b/src/Ext/VoxelAnimType/Body.cpp index 4357d8eb96..9f7fc518fa 100644 --- a/src/Ext/VoxelAnimType/Body.cpp +++ b/src/Ext/VoxelAnimType/Body.cpp @@ -84,29 +84,6 @@ DEFINE_HOOK(0x74BA31, VoxelAnimTypeClass_DTOR, 0x5) return 0; } -DEFINE_HOOK_AGAIN(0x74B810, VoxelAnimTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x74B8D0, VoxelAnimTypeClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(VoxelAnimTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - VoxelAnimTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x74B8C2, VoxelAnimTypeClass_Load_Suffix, 0x5) -{ - VoxelAnimTypeExt::ExtMap.LoadStatic(); - return 0; -} - -DEFINE_HOOK(0x74B8EA, VoxelAnimTypeClass_Save_Suffix, 0x5) -{ - VoxelAnimTypeExt::ExtMap.SaveStatic(); - return 0; -} - DEFINE_HOOK_AGAIN(0x74B607, VoxelAnimTypeClass_LoadFromINI, 0x5) DEFINE_HOOK_AGAIN(0x74B561, VoxelAnimTypeClass_LoadFromINI, 0x5) DEFINE_HOOK_AGAIN(0x74B54A, VoxelAnimTypeClass_LoadFromINI, 0x5) diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index 754641610c..a8d94cf97c 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -844,31 +844,6 @@ DEFINE_HOOK(0x75E5C8, WarheadTypeClass_SDDTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x75E2C0, WarheadTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x75E0C0, WarheadTypeClass_SaveLoad_Prefix, 0x8) -{ - GET_STACK(WarheadTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - WarheadTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x75E2AE, WarheadTypeClass_Load_Suffix, 0x7) -{ - WarheadTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x75E39C, WarheadTypeClass_Save_Suffix, 0x5) -{ - WarheadTypeExt::ExtMap.SaveStatic(); - - return 0; -} - //DEFINE_HOOK_AGAIN(0x75DEAF, WarheadTypeClass_LoadFromINI, 0x5)// Section dont exist! DEFINE_HOOK(0x75DEA0, WarheadTypeClass_LoadFromINI, 0x5) { diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 7f270c84a4..9cdbf853fc 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -507,31 +507,6 @@ DEFINE_HOOK(0x77311D, WeaponTypeClass_SDDTOR, 0x6) return 0; } -DEFINE_HOOK_AGAIN(0x772EB0, WeaponTypeClass_SaveLoad_Prefix, 0x5) -DEFINE_HOOK(0x772CD0, WeaponTypeClass_SaveLoad_Prefix, 0x7) -{ - GET_STACK(WeaponTypeClass*, pItem, 0x4); - GET_STACK(IStream*, pStm, 0x8); - - WeaponTypeExt::ExtMap.PrepareStream(pItem, pStm); - - return 0; -} - -DEFINE_HOOK(0x772EA6, WeaponTypeClass_Load_Suffix, 0x6) -{ - WeaponTypeExt::ExtMap.LoadStatic(); - - return 0; -} - -DEFINE_HOOK(0x772F8C, WeaponTypeClass_Save, 0x5) -{ - WeaponTypeExt::ExtMap.SaveStatic(); - - return 0; -} - //DEFINE_HOOK_AGAIN(0x7729D6, WeaponTypeClass_LoadFromINI, 0x5)// Section dont exist! DEFINE_HOOK_AGAIN(0x7729C7, WeaponTypeClass_LoadFromINI, 0x5) DEFINE_HOOK(0x7729B0, WeaponTypeClass_LoadFromINI, 0x5) diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 5a54d0dd3f..aa5af50558 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -155,6 +155,48 @@ struct SaveGlobalsAction } }; +// calls: +// T::ExtMap.SaveAllToStream(IStream*) +struct SaveExtensionsAction +{ + template + static bool Process(IStream* pStm) + { + if constexpr (HasExtMap) + return T::ExtMap.SaveAllToStream(pStm); + else + return true; + } +}; + +// calls: +// T::ExtMap.LoadAllFromStream(IStream*) +struct LoadExtensionsAction +{ + template + static bool Process(IStream* pStm) + { + if constexpr (HasExtMap) + return T::ExtMap.LoadAllFromStream(pStm); + else + return true; + } +}; + +// calls: +// T::ExtMap.RelinkExtensionPointers() +struct RelinkExtensionsAction +{ + template + static bool Process() + { + if constexpr (HasExtMap) + T::ExtMap.RelinkExtensionPointers(); + + return true; + } +}; + // this is a complicated thing that calls methods on classes. add types to the // instantiation of this type, and the most appropriate method for each type // will be called with no overhead of virtual functions. @@ -181,6 +223,21 @@ struct TypeRegistry return dispatch_mass_action(pStm); } + __forceinline static bool SaveExtensions(IStream* pStm) + { + return dispatch_mass_action(pStm); + } + + __forceinline static bool LoadExtensions(IStream* pStm) + { + return dispatch_mass_action(pStm); + } + + __forceinline static void RelinkExtensions() + { + dispatch_mass_action(); + } + private: // TAction: the method dispatcher class to call with each type // ArgTypes: the argument types to call the method dispatcher's Process() method @@ -268,14 +325,29 @@ DEFINE_HOOK(0x685659, Scenario_ClearClasses, 0xa) DEFINE_HOOK(0x67D32C, SaveGame_Phobos, 0x5) { GET(IStream*, pStm, ESI); - PhobosTypeRegistry::SaveGlobals(pStm); + + if (!PhobosTypeRegistry::SaveGlobals(pStm) || !PhobosTypeRegistry::SaveExtensions(pStm)) + Debug::FatalErrorAndExit("SaveGame - Failed to save Phobos data!\n"); + return 0; } DEFINE_HOOK(0x67E826, LoadGame_Phobos, 0x6) { GET(IStream*, pStm, ESI); - PhobosTypeRegistry::LoadGlobals(pStm); + + if (!PhobosTypeRegistry::LoadGlobals(pStm) || !PhobosTypeRegistry::LoadExtensions(pStm)) + Debug::FatalErrorAndExit("LoadGame - Failed to load Phobos data!\n"); + + return 0; +} + +// First instruction after SwizzleManagerClass::Process has remapped every registered +// pointer: extension owners are valid again, restore the owners' inline ext pointers +// (their loaded bytes still hold the stale save-time values). +DEFINE_HOOK(0x67E685, LoadGame_PostSwizzle_Phobos, 0x5) +{ + PhobosTypeRegistry::RelinkExtensions(); return 0; } diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index c20f279fb3..e08c3cc33e 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -104,16 +104,24 @@ class AbstractExt virtual inline void SaveToStream(PhobosStreamWriter& Stm) { - //Stm.Save(this->AttachedToObject); Stm.Save(this->Initialized); } virtual inline void LoadFromStream(PhobosStreamReader& Stm) { - //Stm.Load(this->AttachedToObject); Stm.Load(this->Initialized); } + // on load the extension is constructed with the save-time owner pointer; + // this queues it for remapping when the swizzle manager resolves pointers. + void RegisterOwnerForChange() + { + PhobosSwizzle::RegisterForChange(&this->AttachedToObject); + } + + // called after loading once all pointers (including the owner) have been remapped + virtual void PostLoad() { } + protected: void* GetAttachedObject() const { @@ -300,17 +308,12 @@ class Container map_type MappedItems; std::vector Items; - base_type* SavingObject; - extension_type_ptr SavingExtPointer; - IStream* SavingStream; const char* Name; public: explicit Container(const char* pName) : MappedItems(), Items(), - SavingObject(nullptr), - SavingStream(nullptr), Name(pName) { } @@ -335,6 +338,10 @@ class Container public: extension_type_ptr Allocate(base_type_ptr key) { + // during savegame load extensions are restored from the stream instead + if (Phobos::IsLoadingSaveGame) + return nullptr; + if constexpr (HasOffset) ResetExtensionPointer(key); @@ -356,6 +363,13 @@ class Container // into this container: stores the inline pointer and tracks it for iteration. extension_type_ptr Adopt(extension_type_ptr val) { + // during savegame load extensions are restored from the stream instead + if (Phobos::IsLoadingSaveGame) + { + delete val; + return nullptr; + } + val->EnsureConstanted(); if constexpr (HasOffset) @@ -475,152 +489,155 @@ class Container ptr->LoadFromINI(pINI); } - void PrepareStream(base_type_ptr key, IStream* pStm) + // Writes every live extension of this container into the savegame stream: + // a header block (canary + count), then one length-prefixed block per extension + // carrying its save-time address (for pointer swizzling), its owner's identity + // and the serialized members. + bool SaveAllToStream(IStream* pStm) { - //Debug::Log("[PrepareStream] Next is %p of type '%s'\n", key, this->Name); - - this->SavingObject = key; - this->SavingStream = pStm; - - // Loading the base type data might override the ext pointer stored on it so it needs to be saved. - if constexpr (HasOffset) - this->SavingExtPointer = GetExtensionPointer(key); - } - - void SaveStatic() - { - if (this->SavingObject && this->SavingStream) + if constexpr (!HasOffset) { - //Debug::Log("[SaveStatic] Saving object %p as '%s'\n", this->SavingObject, this->Name); - if (!this->Save(this->SavingObject, this->SavingStream)) - Debug::FatalErrorAndExit("SaveStatic - Saving object %p as '%s' failed!\n", this->SavingObject, this->Name); + return true; // map-mode extensions (EBolt) are not serialized } else { - Debug::Log("SaveStatic - Object or Stream not set for '%s': %p, %p\n", - this->Name, this->SavingObject, this->SavingStream); - } + PhobosByteStream headerStm(sizeof(DWORD) * 2); + PhobosStreamWriter headerWriter(headerStm); + headerWriter.Save(T::Canary); + headerWriter.Save(this->Items.size()); + + if (!headerStm.WriteBlockToStream(pStm)) + { + Debug::Log("SaveAllToStream - Failed to save header for '%s'.\n", this->Name); + return false; + } + + for (const auto& item : this->Items) + { + PhobosByteStream saver(sizeof(*item)); + PhobosStreamWriter writer(saver); + + // the extension's own save-time address, so pointers to it can be remapped + writer.RegisterChange(item); + // which concrete leaf to construct on load + writer.Save(item->OwnerObject()->WhatAmI()); + // the save-time owner address, remapped to the loaded owner by the swizzle manager + writer.Save(static_cast(item->OwnerObject())); - this->SavingObject = nullptr; - this->SavingStream = nullptr; + item->SaveToStream(writer); + + if (!saver.WriteBlockToStream(pStm)) + { + Debug::Log("SaveAllToStream - Failed to save an item of '%s'.\n", this->Name); + return false; + } + } + + return true; + } } - void LoadStatic() + // Recreates every extension of this container from the savegame stream. Owners do + // not exist yet; each extension holds the save-time owner pointer until the swizzle + // manager remaps it, and the owners' inline pointers are restored by + // RelinkExtensionPointers afterwards. + bool LoadAllFromStream(IStream* pStm) { - if (this->SavingObject && this->SavingStream) + if constexpr (!HasOffset) { - // Restore stored ext pointer data. - if constexpr (HasOffset) - SetExtensionPointer(this->SavingObject, this->SavingExtPointer); - - //Debug::Log("[LoadStatic] Loading object %p as '%s'\n", this->SavingObject, this->Name); - if (!this->Load(this->SavingObject, this->SavingStream)) - Debug::FatalErrorAndExit("LoadStatic - Loading object %p as '%s' failed!\n", this->SavingObject, this->Name); + return true; } else { - Debug::Log("LoadStatic - Object or Stream not set for '%s': %p, %p\n", - this->Name, this->SavingObject, this->SavingStream); - } - - this->SavingObject = nullptr; - this->SavingStream = nullptr; - } + PhobosByteStream headerStm(0); + if (!headerStm.ReadBlockFromStream(pStm)) + { + Debug::Log("LoadAllFromStream - Failed to read header for '%s'.\n", this->Name); + return false; + } - decltype(auto) begin() const = delete; + PhobosStreamReader headerReader(headerStm); + size_t count = 0; - decltype(auto) end() const = delete; + if (!headerReader.Expect(T::Canary) || !headerReader.Load(count) || !headerReader.ExpectEndOfBlock()) + { + Debug::Log("LoadAllFromStream - Invalid header for '%s'.\n", this->Name); + return false; + } - size_t size() const - { - return this->Items.size(); - } + this->Items.reserve(count); -protected: - // override this method to do type-specific stuff - virtual bool Save(base_type_ptr key, IStream* pStm) - { - return this->SaveKey(key, pStm) != nullptr; - } + for (size_t i = 0; i < count; ++i) + { + PhobosByteStream loader(0); + if (!loader.ReadBlockFromStream(pStm)) + { + Debug::Log("LoadAllFromStream - Failed to read an item of '%s'.\n", this->Name); + return false; + } - // override this method to do type-specific stuff - virtual bool Load(base_type_ptr key, IStream* pStm) - { - return this->LoadKey(key, pStm) != nullptr; - } + PhobosStreamReader reader(loader); - extension_type_ptr SaveKey(base_type_ptr key, IStream* pStm) - { - // this really shouldn't happen - if (!key) - { - Debug::Log("SaveKey - Attempted for a null pointer! WTF!\n"); - return nullptr; - } + void* oldPtr = nullptr; + AbstractType tag = AbstractType::None; + void* oldOwner = nullptr; - // get the value data - auto buffer = this->Find(key); - if (!buffer) - { - Debug::Log("SaveKey - Could not find value.\n"); - return nullptr; - } + if (!reader.Load(oldPtr) || !reader.Load(tag) || !reader.Load(oldOwner)) + { + Debug::Log("LoadAllFromStream - Invalid item header in '%s'.\n", this->Name); + return false; + } - // write the current pointer, the size of the block, and the canary - PhobosByteStream saver(sizeof(*buffer)); - PhobosStreamWriter writer(saver); + auto const buffer = this->CreateExtData(tag, static_cast(oldOwner)); + buffer->RegisterOwnerForChange(); + PhobosSwizzle::RegisterChange(oldPtr, buffer); + this->Items.emplace_back(buffer); - writer.Save(T::Canary); - writer.Save(buffer); + buffer->LoadFromStream(reader); - // save the data - buffer->SaveToStream(writer); + if (!reader.ExpectEndOfBlock()) + return false; + } - // save the block - if (!saver.WriteBlockToStream(pStm)) - { - Debug::Log("SaveKey - Failed to save data.\n"); - return nullptr; + return true; } - - //Debug::Log("[SaveKey] Save used up 0x%X bytes\n", saver.Size()); - - return buffer; } - extension_type_ptr LoadKey(base_type_ptr key, IStream* pStm) + // After the swizzle manager has remapped all pointers, write each extension back + // into its owner's inline slot (the owner's loaded bytes still hold the stale + // save-time value). + void RelinkExtensionPointers() { - // this really shouldn't happen - if (!key) + if constexpr (HasOffset) { - Debug::Log("LoadKey - Attempted for a null pointer! WTF!\n"); - return nullptr; - } + for (const auto& item : this->Items) + { + auto const key = item->OwnerObject(); - // get or allocate the value data - extension_type_ptr buffer = this->FindOrAllocate(key); - if (!buffer) - { - Debug::Log("LoadKey - Could not find or allocate value.\n"); - return nullptr; - } + if (!key) + Debug::FatalErrorAndExit("RelinkExtensionPointers - '%s' extension has no owner!\n", this->Name); - PhobosByteStream loader(0); - if (!loader.ReadBlockFromStream(pStm)) - { - Debug::Log("LoadKey - Failed to read data from save stream?!\n"); - return nullptr; + SetExtensionPointer(key, item); + item->PostLoad(); + } } + } - PhobosStreamReader reader(loader); - if (reader.Expect(T::Canary) && reader.RegisterChange(buffer)) - { - buffer->LoadFromStream(reader); - if (reader.ExpectEndOfBlock()) - return buffer; - } + decltype(auto) begin() const = delete; - return nullptr; + decltype(auto) end() const = delete; + + size_t size() const + { + return this->Items.size(); + } + +protected: + // constructs the concrete extension on load; containers whose base class has + // multiple concrete leaves override this to pick the leaf matching the tag + virtual extension_type_ptr CreateExtData(AbstractType tag, base_type_ptr pOwner) const + { + return new extension_type(pOwner); } private: From 8c1273e0be26575cdc76ebe5609c54885430c141 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 12:58:55 +0300 Subject: [PATCH 15/47] Flatten extension data classes onto the mirror hierarchy and adopt Fetch naming --- src/Commands/NextIdleHarvester.cpp | 2 +- src/Commands/ObjectInfo.cpp | 4 +- src/Ext/AbstractType/Body.h | 4 +- src/Ext/Aircraft/Body.cpp | 16 +- src/Ext/Aircraft/Body.h | 12 +- src/Ext/Aircraft/Hooks.cpp | 102 +- src/Ext/AircraftType/Body.cpp | 2 +- src/Ext/AircraftType/Body.h | 4 +- src/Ext/Anim/Body.cpp | 54 +- src/Ext/Anim/Body.h | 147 +- src/Ext/Anim/Hooks.AnimCreateUnit.cpp | 10 +- src/Ext/Anim/Hooks.cpp | 62 +- src/Ext/AnimType/Body.cpp | 18 +- src/Ext/AnimType/Body.h | 227 +- src/Ext/Building/Body.cpp | 45 +- src/Ext/Building/Body.h | 154 +- src/Ext/Building/Hooks.Grinding.cpp | 6 +- src/Ext/Building/Hooks.Production.cpp | 22 +- src/Ext/Building/Hooks.Refinery.cpp | 10 +- src/Ext/Building/Hooks.Selling.cpp | 10 +- src/Ext/Building/Hooks.cpp | 102 +- src/Ext/BuildingType/Body.cpp | 41 +- src/Ext/BuildingType/Body.h | 476 ++-- src/Ext/BuildingType/Hooks.Upgrade.cpp | 12 +- src/Ext/BuildingType/Hooks.cpp | 30 +- src/Ext/Bullet/Body.cpp | 46 +- src/Ext/Bullet/Body.h | 117 +- src/Ext/Bullet/Hooks.DetonateLogics.cpp | 60 +- src/Ext/Bullet/Hooks.Obstacles.cpp | 16 +- src/Ext/Bullet/Hooks.cpp | 48 +- .../Bullet/Trajectories/BombardTrajectory.cpp | 8 +- .../Trajectories/ParabolaTrajectory.cpp | 6 +- .../Bullet/Trajectories/PhobosTrajectory.cpp | 16 +- .../Bullet/Trajectories/SampleTrajectory.cpp | 2 +- .../Trajectories/StraightTrajectory.cpp | 6 +- src/Ext/BulletType/Body.cpp | 16 +- src/Ext/BulletType/Body.h | 305 +-- src/Ext/CaptureManager/Body.cpp | 16 +- src/Ext/CaptureManager/Hooks.cpp | 6 +- src/Ext/Cell/Body.cpp | 6 +- src/Ext/Cell/Body.h | 51 +- src/Ext/EBolt/Body.cpp | 2 +- src/Ext/EBolt/Body.h | 1 - src/Ext/EBolt/Hooks.cpp | 12 +- src/Ext/Event/Body.cpp | 2 +- src/Ext/Foot/Body.h | 6 +- src/Ext/House/Body.cpp | 76 +- src/Ext/House/Body.h | 267 +-- src/Ext/House/Hooks.AINavalProduction.cpp | 16 +- src/Ext/House/Hooks.ForceEnemy.cpp | 4 +- src/Ext/House/Hooks.UnitFromFactory.cpp | 2 +- src/Ext/House/Hooks.cpp | 56 +- src/Ext/HouseType/Body.cpp | 14 +- src/Ext/HouseType/Body.h | 53 +- src/Ext/HouseType/Hooks.cpp | 4 +- src/Ext/Infantry/Body.cpp | 2 +- src/Ext/Infantry/Body.h | 6 +- src/Ext/Infantry/Hooks.Firing.cpp | 8 +- src/Ext/Infantry/Hooks.cpp | 10 +- src/Ext/InfantryType/Body.cpp | 2 +- src/Ext/InfantryType/Body.h | 4 +- src/Ext/Mission/Body.h | 4 +- src/Ext/Object/Body.h | 4 +- src/Ext/ObjectType/Body.h | 4 +- src/Ext/OverlayType/Body.cpp | 12 +- src/Ext/OverlayType/Body.h | 59 +- src/Ext/OverlayType/Hooks.cpp | 4 +- src/Ext/ParticleSystemType/Body.cpp | 12 +- src/Ext/ParticleSystemType/Body.h | 51 +- src/Ext/ParticleType/Body.cpp | 12 +- src/Ext/ParticleType/Body.h | 51 +- src/Ext/ParticleType/Hooks.cpp | 2 +- src/Ext/RadSite/Body.cpp | 32 +- src/Ext/RadSite/Body.h | 101 +- src/Ext/RadSite/Hooks.cpp | 30 +- src/Ext/Radio/Body.h | 4 +- src/Ext/Rules/Body.cpp | 2 +- src/Ext/SWType/Body.cpp | 20 +- src/Ext/SWType/Body.h | 499 +++-- src/Ext/SWType/FireSuperWeapon.cpp | 44 +- src/Ext/SWType/Hooks.cpp | 16 +- src/Ext/SWType/NewSWType/NewSWType.h | 4 +- src/Ext/SWType/SWHelpers.cpp | 42 +- src/Ext/Scenario/Body.cpp | 2 +- src/Ext/Scenario/Body.h | 6 +- src/Ext/Script/Body.cpp | 28 +- src/Ext/Script/Body.h | 43 +- src/Ext/Script/Hooks.cpp | 2 +- src/Ext/Script/Mission.Attack.cpp | 24 +- src/Ext/Script/Mission.Move.cpp | 8 +- src/Ext/Side/Body.cpp | 16 +- src/Ext/Side/Body.h | 165 +- src/Ext/Side/Hooks.SidebarGDIPositions.cpp | 2 +- src/Ext/Side/Hooks.cpp | 2 +- src/Ext/Sidebar/Hooks.cpp | 2 +- src/Ext/Sidebar/SWSidebar/SWButtonClass.cpp | 4 +- src/Ext/Sidebar/SWSidebar/SWColumnClass.cpp | 8 +- src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp | 10 +- .../Sidebar/SWSidebar/ToggleSWButtonClass.cpp | 4 +- src/Ext/TAction/Body.cpp | 14 +- src/Ext/TAction/Body.h | 47 +- src/Ext/TEvent/Body.cpp | 8 +- src/Ext/TEvent/Body.h | 47 +- src/Ext/Team/Body.cpp | 8 +- src/Ext/Team/Body.h | 111 +- src/Ext/Team/Hooks.cpp | 10 +- src/Ext/TeamType/Body.cpp | 12 +- src/Ext/TeamType/Body.h | 53 +- src/Ext/Techno/Body.Internal.cpp | 18 +- src/Ext/Techno/Body.Update.cpp | 88 +- src/Ext/Techno/Body.Visuals.cpp | 24 +- src/Ext/Techno/Body.cpp | 82 +- src/Ext/Techno/Body.h | 457 ++-- src/Ext/Techno/Hooks.Airstrike.cpp | 34 +- src/Ext/Techno/Hooks.Cloak.cpp | 14 +- src/Ext/Techno/Hooks.Firing.cpp | 94 +- src/Ext/Techno/Hooks.Misc.cpp | 80 +- src/Ext/Techno/Hooks.Pips.cpp | 20 +- src/Ext/Techno/Hooks.ReceiveDamage.cpp | 28 +- src/Ext/Techno/Hooks.TargetEvaluation.cpp | 16 +- src/Ext/Techno/Hooks.Targeting.cpp | 4 +- src/Ext/Techno/Hooks.Tint.cpp | 12 +- src/Ext/Techno/Hooks.Transport.cpp | 68 +- src/Ext/Techno/Hooks.WeaponEffects.cpp | 18 +- src/Ext/Techno/Hooks.WeaponRange.cpp | 10 +- src/Ext/Techno/Hooks.cpp | 172 +- src/Ext/Techno/WeaponHelpers.cpp | 32 +- src/Ext/TechnoType/Body.cpp | 60 +- src/Ext/TechnoType/Body.h | 1989 +++++++++-------- src/Ext/TechnoType/Hooks.MatrixOp.cpp | 14 +- src/Ext/TechnoType/Hooks.MultiWeapon.cpp | 14 +- src/Ext/TechnoType/Hooks.Teleport.cpp | 10 +- src/Ext/TechnoType/Hooks.cpp | 12 +- src/Ext/TerrainType/Body.cpp | 18 +- src/Ext/TerrainType/Body.h | 133 +- src/Ext/TerrainType/Hooks.Passable.cpp | 18 +- src/Ext/TerrainType/Hooks.cpp | 18 +- src/Ext/Tiberium/Body.cpp | 12 +- src/Ext/Tiberium/Body.h | 51 +- src/Ext/Tiberium/Hooks.cpp | 2 +- src/Ext/Unit/Body.cpp | 4 +- src/Ext/Unit/Body.h | 8 +- src/Ext/Unit/Hooks.Crushing.cpp | 14 +- src/Ext/Unit/Hooks.DeployFire.cpp | 4 +- src/Ext/Unit/Hooks.DeploysInto.cpp | 12 +- src/Ext/Unit/Hooks.DisallowMoving.cpp | 2 +- src/Ext/Unit/Hooks.DrawIt.cpp | 4 +- src/Ext/Unit/Hooks.Firing.cpp | 6 +- src/Ext/Unit/Hooks.Harvester.cpp | 14 +- src/Ext/Unit/Hooks.Jumpjet.cpp | 20 +- src/Ext/Unit/Hooks.SimpleDeployer.cpp | 18 +- src/Ext/Unit/Hooks.Sinking.cpp | 6 +- src/Ext/Unit/Hooks.Unload.cpp | 6 +- src/Ext/UnitType/Body.cpp | 2 +- src/Ext/UnitType/Body.h | 6 +- src/Ext/VoxelAnim/Body.cpp | 16 +- src/Ext/VoxelAnim/Body.h | 55 +- src/Ext/VoxelAnim/Hooks.cpp | 10 +- src/Ext/VoxelAnimType/Body.cpp | 14 +- src/Ext/VoxelAnimType/Body.h | 89 +- src/Ext/WarheadType/Body.cpp | 34 +- src/Ext/WarheadType/Body.h | 1147 +++++----- src/Ext/WarheadType/Detonate.cpp | 60 +- src/Ext/WarheadType/Hooks.cpp | 66 +- src/Ext/WeaponType/Body.cpp | 30 +- src/Ext/WeaponType/Body.h | 421 ++-- src/Ext/WeaponType/Hooks.DiskLaserRadius.cpp | 2 +- src/Ext/WeaponType/Hooks.cpp | 2 +- src/Interop/BulletExt.cpp | 2 +- src/Misc/Hooks.AlphaImage.cpp | 2 +- src/Misc/Hooks.Ares.cpp | 14 +- src/Misc/Hooks.BugFixes.cpp | 42 +- src/Misc/Hooks.Crates.cpp | 2 +- src/Misc/Hooks.LaserDraw.cpp | 2 +- src/Misc/Hooks.LightEffects.cpp | 4 +- src/Misc/Hooks.UI.cpp | 22 +- src/Misc/MessageColumn.cpp | 2 +- src/Misc/PhobosToolTip.cpp | 14 +- src/Misc/PhobosToolTip.h | 4 +- src/Misc/Selection.cpp | 8 +- src/Misc/SyncLogging.cpp | 2 +- src/New/Entity/Ares/RadarJammerClass.cpp | 2 +- src/New/Entity/AttachEffectClass.cpp | 44 +- src/New/Entity/ShieldClass.cpp | 30 +- src/New/Type/Affiliated/DroppodTypeClass.cpp | 6 +- src/Utilities/AresAddressInit.cpp | 4 +- src/Utilities/AresFunctions.h | 4 +- src/Utilities/Container.h | 105 +- src/Utilities/GeneralUtils.cpp | 2 +- 189 files changed, 5356 insertions(+), 5144 deletions(-) diff --git a/src/Commands/NextIdleHarvester.cpp b/src/Commands/NextIdleHarvester.cpp index 7811e78ae7..b87b7a1719 100644 --- a/src/Commands/NextIdleHarvester.cpp +++ b/src/Commands/NextIdleHarvester.cpp @@ -43,7 +43,7 @@ void NextIdleHarvesterCommandClass::Execute(WWKey eInput) const { if (auto const pTechno = abstract_cast(pNextObject)) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; if (pTypeExt->Harvester_Counted && !TechnoExt::IsHarvesting(pTechno)) { diff --git a/src/Commands/ObjectInfo.cpp b/src/Commands/ObjectInfo.cpp index 47637330ac..1db3589e1a 100644 --- a/src/Commands/ObjectInfo.cpp +++ b/src/Commands/ObjectInfo.cpp @@ -133,7 +133,7 @@ void ObjectInfoCommandClass::Execute(WWKey eInput) const append("Current HP = (%d / %d)", pFoot->Health, pType->Strength); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pFoot); + auto const pTechnoExt = TechnoExt::Fetch(pFoot); auto const pShieldData = pTechnoExt->Shield.get(); if (pTechnoExt->CurrentShieldType && pShieldData) @@ -202,7 +202,7 @@ void ObjectInfoCommandClass::Execute(WWKey eInput) const append("Current HP = (%d / %d)\n", pBuilding->Health, pBuilding->Type->Strength); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pBuilding); + auto const pTechnoExt = TechnoExt::Fetch(pBuilding); auto const pShieldData = pTechnoExt->Shield.get(); if (pTechnoExt->CurrentShieldType && pShieldData) diff --git a/src/Ext/AbstractType/Body.h b/src/Ext/AbstractType/Body.h index 884e2f7b4b..bef9f1a589 100644 --- a/src/Ext/AbstractType/Body.h +++ b/src/Ext/AbstractType/Body.h @@ -4,10 +4,10 @@ #include // Empty intermediate base mirroring AbstractTypeClass in the extension hierarchy. -class AbstractTypeClassExtension : public AbstractExt +class AbstractTypeExt : public AbstractExt { public: - explicit AbstractTypeClassExtension(AbstractTypeClass* const OwnerObject) : AbstractExt(OwnerObject) + explicit AbstractTypeExt(AbstractTypeClass* const OwnerObject) : AbstractExt(OwnerObject) { } AbstractTypeClass* OwnerObject() const diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index 53ef267f9a..c6771b95d1 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -3,12 +3,12 @@ #include #include -// An aircraft's extension is a concrete AircraftClassExtension leaf, owned by the TechnoClass container. +// An aircraft's extension is a concrete AircraftExt leaf, owned by the TechnoClass container. DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) { GET(AircraftClass*, pItem, ESI); - TechnoExt::ExtMap.Adopt(new AircraftClassExtension(pItem)); + TechnoExt::ExtMap.Adopt(new AircraftExt(pItem)); return 0; } @@ -17,10 +17,10 @@ DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) void AircraftExt::FireWeapon(AircraftClass* pThis, AbstractClass* pTarget) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); const int weaponIndex = pExt->CurrentAircraftWeaponIndex; auto const pWeapon = pThis->GetWeapon(weaponIndex)->WeaponType; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const int burstCount = pWeapon->Burst; const bool isStrafe = pThis->Is_Strafe(); @@ -61,7 +61,7 @@ void AircraftExt::FireWeapon(AircraftClass* pThis, AbstractClass* pTarget) bool AircraftExt::PlaceReinforcementAircraft(AircraftClass* pThis, CoordStruct edgeCoords) { auto const pType = pThis->Type; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); auto dir = DirType::North; auto coords = edgeCoords; coords.Z = 0; @@ -93,7 +93,7 @@ bool AircraftExt::PlaceReinforcementAircraft(AircraftClass* pThis, CoordStruct e CellStruct AircraftExt::PickEdgeCellForPlane(AircraftTypeClass* pPlaneType, CellStruct destCell, Edge edge, bool isOnRetreat) { - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pPlaneType); + auto const pTypeExt = TechnoTypeExt::Fetch(pPlaneType); auto const edgeMode = !isOnRetreat ? pTypeExt->SpawnFromEdge : pTypeExt->RetreatToEdge; auto spawnEdge = edge; auto refCell = CellStruct::Empty; @@ -153,7 +153,7 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) if (auto const pBuilding = pDock ? pDock : abstract_cast(pLink)) { auto const pBuildingType = pBuilding->Type; - auto const pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); + auto const pBuildingTypeExt = BuildingTypeExt::Fetch(pBuildingType); const int docks = pBuildingType->NumberOfDocks; const int linkIndex = pBuilding->FindLinkIndex(pThis); @@ -169,7 +169,7 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) return pLink->PrimaryFacing.Current().GetDir(); } - const int landingDir = TechnoTypeExt::ExtMap.Find(pType)->LandingDir.Get((int)poseDir); + const int landingDir = TechnoTypeExt::Fetch(pType)->LandingDir.Get((int)poseDir); if (!pType->AirportBound && landingDir < 0) return pThis->PrimaryFacing.Current().GetDir(); diff --git a/src/Ext/Aircraft/Body.h b/src/Ext/Aircraft/Body.h index 9f0c39dcb3..008396badd 100644 --- a/src/Ext/Aircraft/Body.h +++ b/src/Ext/Aircraft/Body.h @@ -3,24 +3,18 @@ #include #include -// Concrete leaf extension for AircraftClass (empty; techno data lives in TechnoClassExtension). -class AircraftClassExtension : public FootClassExtension +// Concrete leaf extension for AircraftClass (empty; techno data lives in TechnoExt). +class AircraftExt : public FootExt { public: - explicit AircraftClassExtension(AircraftClass* const OwnerObject) : FootClassExtension(OwnerObject) + explicit AircraftExt(AircraftClass* const OwnerObject) : FootExt(OwnerObject) { } AircraftClass* OwnerObject() const { return static_cast(this->GetAttachedObject()); } -}; - -// TODO: Implement proper extended AircraftClass. -class AircraftExt -{ -public: static void FireWeapon(AircraftClass* pThis, AbstractClass* pTarget); static bool PlaceReinforcementAircraft(AircraftClass* pThis, CoordStruct edgeCoords); static CellStruct PickEdgeCellForPlane(AircraftTypeClass* pPlaneType, CellStruct destCell, Edge edge, bool isOnRetreat = false); diff --git a/src/Ext/Aircraft/Hooks.cpp b/src/Ext/Aircraft/Hooks.cpp index c189062ccd..661c1c2b32 100644 --- a/src/Ext/Aircraft/Hooks.cpp +++ b/src/Ext/Aircraft/Hooks.cpp @@ -13,7 +13,7 @@ DEFINE_HOOK(0x417FF1, AircraftClass_Mission_Attack_StrafeShots, 0x6) { GET(AircraftClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); AirAttackStatus const state = (AirAttackStatus)pThis->MissionStatus; // Re-evaluate weapon choice due to potentially changing targeting conditions here @@ -33,7 +33,7 @@ DEFINE_HOOK(0x417FF1, AircraftClass_Mission_Attack_StrafeShots, 0x6) if (pExt->Strafe_BombsDroppedThisRound) { auto const pWeapon = pThis->GetWeapon(pExt->CurrentAircraftWeaponIndex)->WeaponType; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); int const strafingShots = pWeaponExt->Strafing_Shots.Get(5); if (strafingShots > 5) @@ -57,7 +57,7 @@ DEFINE_HOOK(0x4197FC, AircraftClass_GetFireLocation_WeaponRange, 0x6) GET(AircraftClass*, pThis, EDI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); R->EAX(pThis->GetWeaponRange(pExt->CurrentAircraftWeaponIndex)); return SkipGameCode; @@ -75,7 +75,7 @@ DEFINE_HOOK(0x4197F3, AircraftClass_GetFireLocation_Strafing, 0x5) if (!pObject || !pObject->IsInAir()) return 0; - auto const fireError = pThis->GetFireError(pTarget, TechnoExt::ExtMap.Find(pThis)->CurrentAircraftWeaponIndex, false); + auto const fireError = pThis->GetFireError(pTarget, TechnoExt::Fetch(pThis)->CurrentAircraftWeaponIndex, false); if (fireError == FireError::ILLEGAL || fireError == FireError::CANT) return 0; @@ -90,16 +90,16 @@ static long __stdcall AircraftClass_IFlyControl_IsStrafe(IFlyControl const* ifly __assume(ifly != nullptr); auto const pThis = static_cast(ifly); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); WeaponTypeClass* pWeapon = nullptr; pWeapon = pThis->GetWeapon(pExt->CurrentAircraftWeaponIndex)->WeaponType; if (pWeapon) { - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); auto const pBulletType = pWeapon->Projectile; - return pWeaponExt->Strafing.isset() ? pWeaponExt->Strafing.Get() : (pBulletType->ROT <= 1 && !pBulletType->Inviso && !BulletTypeExt::ExtMap.Find(pBulletType)->TrajectoryType); + return pWeaponExt->Strafing.isset() ? pWeaponExt->Strafing.Get() : (pBulletType->ROT <= 1 && !pBulletType->Inviso && !BulletTypeExt::Fetch(pBulletType)->TrajectoryType); } return false; @@ -113,7 +113,7 @@ DEFINE_HOOK(0x4180F4, AircraftClass_Mission_Attack_WeaponRange, 0x5) GET(AircraftClass*, pThis, ESI); - R->EAX(pThis->GetWeapon(TechnoExt::ExtMap.Find(pThis)->CurrentAircraftWeaponIndex)); + R->EAX(pThis->GetWeapon(TechnoExt::Fetch(pThis)->CurrentAircraftWeaponIndex)); return SkipGameCode; } @@ -176,7 +176,7 @@ DEFINE_HOOK(0x418B1F, AircraftClass_Mission_Attack_FireAtTarget5Strafe_BurstFix, static int __fastcall AircraftClass_SelectWeapon_Wrapper(AircraftClass* pThis, void* _, AbstractClass* pTarget) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); // Re-evaluate weapon selection only if not mid-strafing run before firing. if (!pExt->Strafe_BombsDroppedThisRound) @@ -209,9 +209,9 @@ DEFINE_HOOK(0x418544, AircraftClass_Mission_Attack_StrafingDestinationFix, 0x6) static inline int GetDelay(AircraftClass* pThis, bool isLastShot) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pWeapon = pThis->GetWeapon(pExt->CurrentAircraftWeaponIndex)->WeaponType; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); int delay = pWeapon->ROF; if (isLastShot || pExt->Strafe_BombsDroppedThisRound == pWeaponExt->Strafing_Shots.Get(5) || (pWeaponExt->Strafing_UseAmmoPerShot && !pThis->Ammo)) @@ -228,9 +228,9 @@ DEFINE_HOOK(0x4184CC, AircraftClass_Mission_Attack_Delay1A, 0x6) { GET(AircraftClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); - if (WeaponTypeExt::ExtMap.Find(pThis->GetWeapon(pExt->CurrentAircraftWeaponIndex)->WeaponType)->Strafing_TargetCell) + if (WeaponTypeExt::Fetch(pThis->GetWeapon(pExt->CurrentAircraftWeaponIndex)->WeaponType)->Strafing_TargetCell) pExt->Strafe_TargetCell = MapClass::Instance.GetCellAt(pThis->Target->GetCoords()); pThis->IsLocked = true; @@ -303,7 +303,7 @@ DEFINE_HOOK(0x41879D, AircraftClass_Mission_Attack_StrafeCell, 0x6) GET(AircraftClass*, pThis, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (const auto pTargetCell = pExt->Strafe_TargetCell) { @@ -344,13 +344,13 @@ DEFINE_HOOK_AGAIN(0x418B46, AircraftClass_MissionAttack_ScatterCell1, 0x6) DEFINE_HOOK(0x41847E, AircraftClass_MissionAttack_ScatterCell1, 0x6) { GET(AircraftClass*, pThis, ESI); - return TechnoTypeExt::ExtMap.Find(pThis->Type)->FiringForceScatter ? 0 : (R->Origin() + 0x44); + return TechnoTypeExt::Fetch(pThis->Type)->FiringForceScatter ? 0 : (R->Origin() + 0x44); } DEFINE_HOOK(0x4186DD, AircraftClass_MissionAttack_ScatterCell2, 0x5) { GET(AircraftClass*, pThis, ESI); - return TechnoTypeExt::ExtMap.Find(pThis->Type)->FiringForceScatter ? 0 : (R->Origin() + 0x43); + return TechnoTypeExt::Fetch(pThis->Type)->FiringForceScatter ? 0 : (R->Origin() + 0x43); } #pragma endregion @@ -363,7 +363,7 @@ DEFINE_HOOK(0x414F10, AircraftClass_AI_Trailer, 0x5) REF_STACK(const CoordStruct, coords, STACK_OFFSET(0x40, -0xC)); auto const pTrailerAnim = GameCreate(pThis->Type->Trailer, coords, 1, 1); - auto const pTrailerAnimExt = AnimExt::ExtMap.Find(pTrailerAnim); + auto const pTrailerAnimExt = AnimExt::Fetch(pTrailerAnim); AnimExt::SetAnimOwnerHouseKind(pTrailerAnim, pThis->Owner, nullptr, false, true); pTrailerAnimExt->SetInvoker(pThis); pTrailerAnimExt->IsTechnoTrailerAnim = true; @@ -441,7 +441,7 @@ DEFINE_HOOK(0x44402E, BuildingClass_ExitObject_PoseDir2, 0x5) auto const dir = DirStruct(AircraftExt::GetLandingDir(pAircraft, pThis)); - if (TechnoTypeExt::ExtMap.Find(pAircraft->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) + if (TechnoTypeExt::Fetch(pAircraft->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) pAircraft->PrimaryFacing.SetCurrent(dir); pAircraft->SecondaryFacing.SetCurrent(dir); @@ -463,7 +463,7 @@ DEFINE_HOOK(0x415EEE, AircraftClass_Fire_KickOutPassengers, 0x6) if (!pWeapon) return 0; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->KickOutPassengers) return 0; @@ -477,7 +477,7 @@ DEFINE_HOOK(0x415EEE, AircraftClass_Fire_KickOutPassengers, 0x6) // Waypoint: enable and smooth moving action static bool __fastcall AircraftTypeClass_CanUseWaypoint(AircraftTypeClass* pThis) { - return TechnoTypeExt::ExtMap.Find(pThis)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); + return TechnoTypeExt::Fetch(pThis)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); } DEFINE_FUNCTION_JUMP(VTABLE, 0x7E2908, AircraftTypeClass_CanUseWaypoint) @@ -507,9 +507,9 @@ DEFINE_HOOK(0x416A0A, AircraftClass_Mission_Move_SmoothMoving, 0x5) if (!pType->AirportBound) return 0; - const bool extendedMissions = TechnoTypeExt::ExtMap.Find(pType)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); + const bool extendedMissions = TechnoTypeExt::Fetch(pType)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); - if (!TechnoTypeExt::ExtMap.Find(pType)->ExtendedAircraftMissions_SmoothMoving.Get(extendedMissions)) + if (!TechnoTypeExt::Fetch(pType)->ExtendedAircraftMissions_SmoothMoving.Get(extendedMissions)) return 0; const int distance = static_cast(Point2D { pCoords->X, pCoords->Y }.DistanceFrom(Point2D { pThis->Location.X, pThis->Location.Y })); @@ -560,7 +560,7 @@ DEFINE_HOOK(0x419EF6, AircraftClass_Mission_Enter_FixNotCarryall, 0x7) // Skip set chaotic ArchiveTarget static void __fastcall AircraftClass_SetArchiveTarget_Wrapper(AircraftClass* pThis, void* _, AbstractClass* pTarget) { - if (!TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) + if (!TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) pThis->SetArchiveTarget(pTarget); } DEFINE_FUNCTION_JUMP(CALL, 0x41AB09, AircraftClass_SetArchiveTarget_Wrapper); @@ -592,8 +592,8 @@ DEFINE_HOOK(0x4CF190, FlyLocomotionClass_FlightUpdate_SetPrimaryFacing, 0x6) // const auto pAircraft = abstract_cast(pFoot); // Rewrite vanilla implement - if (!pAircraft || !TechnoTypeExt::ExtMap.Find(pAircraft->Type)->ExtendedAircraftMissions_RearApproach - .Get(TechnoTypeExt::ExtMap.Find(pAircraft->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions))) + if (!pAircraft || !TechnoTypeExt::Fetch(pAircraft->Type)->ExtendedAircraftMissions_RearApproach + .Get(TechnoTypeExt::Fetch(pAircraft->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions))) { const auto footCoords = pFoot->GetCoords(); const auto desired = DirStruct(Math::atan2(footCoords.Y - destination.Y, destination.X - footCoords.X)); @@ -664,7 +664,7 @@ DEFINE_HOOK(0x4CF3D0, FlyLocomotionClass_FlightUpdate_SetFlightLevel, 0x7) // Ma if (pType->HunterSeeker) return 0; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); const bool extendedMissions = pTypeExt->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); if (!pTypeExt->ExtendedAircraftMissions_EarlyDescend.Get(extendedMissions)) @@ -722,7 +722,7 @@ DEFINE_HOOK(0x4CE42A, FlyLocomotionClass_StateUpdate_NoLanding, 0x6) // Prevent const auto pAircraft = abstract_cast(pLinkTo); - if (!pAircraft || !TechnoTypeExt::ExtMap.Find(pAircraft->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) + if (!pAircraft || !TechnoTypeExt::Fetch(pAircraft->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) return 0; if (pAircraft->Airstrike || pAircraft->Spawned || pAircraft->GetCurrentMission() == Mission::Enter) @@ -739,7 +739,7 @@ DEFINE_HOOK(0x414DA8, AircraftClass_Update_UnlandableDamage, 0x6) // After FootC if (pThis->IsAlive && pType->AirportBound && !pThis->Airstrike && !pThis->Spawned) { - const bool extendedMissions = TechnoTypeExt::ExtMap.Find(pType)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); + const bool extendedMissions = TechnoTypeExt::Fetch(pType)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); if (extendedMissions) { @@ -769,7 +769,7 @@ DEFINE_HOOK(0x414DA8, AircraftClass_Update_UnlandableDamage, 0x6) // After FootC } else if (pThis->IsInAir()) { - int damage = TechnoTypeExt::ExtMap.Find(pType)->ExtendedAircraftMissions_UnlandDamage.Get(RulesExt::Global()->ExtendedAircraftMissions_UnlandDamage); + int damage = TechnoTypeExt::Fetch(pType)->ExtendedAircraftMissions_UnlandDamage.Get(RulesExt::Global()->ExtendedAircraftMissions_UnlandDamage); if (damage > 0) { @@ -799,7 +799,7 @@ DEFINE_HOOK(0x41A5C7, AircraftClass_Mission_Guard_StartAreaGuard, 0x6) GET(AircraftClass* const, pThis, ESI); - if (!TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + if (!TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) || pThis->Team || !pThis->IsArmed() || pThis->Airstrike || pThis->Spawned) { return 0; @@ -823,7 +823,7 @@ DEFINE_HOOK(0x41A96C, AircraftClass_Mission_AreaGuard, 0x6) GET(AircraftClass* const, pThis, ESI); - if (!TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + if (!TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) || pThis->Team || !pThis->IsArmed() || pThis->Airstrike || pThis->Spawned) { return 0; @@ -910,7 +910,7 @@ DEFINE_HOOK(0x7001B0, TechnoClass_MouseOverObject_EnableGuardObject, 0x7) GET(TechnoClass* const, pThis, ESI); return pThis->WhatAmI() == AbstractType::Aircraft - && !TechnoExt::ExtMap.Find(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + && !TechnoExt::Fetch(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) ? SkipCheckCanGuard : ContinueCheckCanGuard; } @@ -928,7 +928,7 @@ DEFINE_FUNCTION_JUMP(VTABLE, 0x7E24A8, AircraftClass_Mission_Sleep) // AttackMove: return when no ammo or arrived destination static bool __fastcall AircraftTypeClass_CanAttackMove(AircraftTypeClass* pThis) { - return TechnoTypeExt::ExtMap.Find(pThis)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); + return TechnoTypeExt::Fetch(pThis)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); } DEFINE_FUNCTION_JUMP(VTABLE, 0x7E290C, AircraftTypeClass_CanAttackMove) @@ -939,7 +939,7 @@ DEFINE_HOOK(0x6FA68B, TechnoClass_Update_AttackMovePaused, 0xA) // To make aircr GET(TechnoClass* const, pThis, ESI); const bool skip = pThis->WhatAmI() == AbstractType::Aircraft - && TechnoExt::ExtMap.Find(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + && TechnoExt::Fetch(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) && (!pThis->Ammo || !pThis->IsInAir()); return skip ? SkipGameCode : 0; @@ -953,7 +953,7 @@ DEFINE_HOOK(0x4DF3BA, FootClass_UpdateAttackMove_AircraftHoldAttackMoveTarget1, // The aircraft is constantly moving, which may cause its target to constantly enter and leave its range, so it is fixed to hold the target. if (pThis->WhatAmI() == AbstractType::Aircraft - && TechnoExt::ExtMap.Find(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) + && TechnoExt::Fetch(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) { return HoldTarget; } @@ -969,7 +969,7 @@ DEFINE_HOOK(0x4DF42A, FootClass_UpdateAttackMove_AircraftHoldAttackMoveTarget2, // Although if the target selected by CS is an object rather than cell. return (pThis->WhatAmI() == AbstractType::Aircraft - && TechnoExt::ExtMap.Find(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) + && TechnoExt::Fetch(pThis)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions)) ? HoldTarget : ContinueCheck; } @@ -981,7 +981,7 @@ DEFINE_HOOK(0x418CD1, AircraftClass_Mission_Attack_ContinueFlyToDestination, 0x6 if (!pThis->Target) { - if (!TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) || pThis->Airstrike || pThis->Spawned) + if (!TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) || pThis->Airstrike || pThis->Spawned) return Continue; if (pThis->MegaMissionIsAttackMove() && pThis->MegaDestination) @@ -1019,7 +1019,7 @@ DEFINE_HOOK(0x414D4D, AircraftClass_Update_ClearTargetIfNoAmmo, 0x6) GET(AircraftClass* const, pThis, ESI); - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + if (TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) && !pThis->Ammo && !pThis->Airstrike && !pThis->Spawned) { if (!SessionClass::IsCampaign()) // To avoid AI's aircrafts team repeatedly attempting to attack the target when no ammo @@ -1045,7 +1045,7 @@ DEFINE_HOOK(0x4179F7, AircraftClass_EnterIdleMode_NoCrash, 0x6) if (pThis->Airstrike || pThis->Spawned) return 0; - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions_UnlandDamage.Get(RulesExt::Global()->ExtendedAircraftMissions_UnlandDamage) < 0) + if (TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions_UnlandDamage.Get(RulesExt::Global()->ExtendedAircraftMissions_UnlandDamage) < 0) return 0; if (!pThis->Team && (pThis->CurrentMission != Mission::Area_Guard || !pThis->ArchiveTarget)) @@ -1083,7 +1083,7 @@ DEFINE_HOOK(0x4425B6, BuildingClass_ReceiveDamage_NoDestroyLink, 0xA) if (!pAircraft) return 0; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pAircraft->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pAircraft->Type); const bool extendedMissions = pTypeExt->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions); if (!pTypeExt->ExtendedAircraftMissions_FastScramble.Get(extendedMissions)) @@ -1095,7 +1095,7 @@ DEFINE_HOOK(0x4425B6, BuildingClass_ReceiveDamage_NoDestroyLink, 0xA) // GreatestThreat: for all the mission that should let the aircraft auto select a target static AbstractClass* __fastcall AircraftClass_GreatestThreat(AircraftClass* pThis, void* _, ThreatType threatType, CoordStruct* pSelectCoords, bool onlyTargetHouseEnemy) { - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + if (TechnoTypeExt::Fetch(pThis->Type)->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) && !pThis->Team && pThis->Ammo && !pThis->Airstrike && !pThis->Spawned) { if (const auto pPrimaryWeapon = pThis->GetWeapon(0)->WeaponType) @@ -1118,7 +1118,7 @@ DEFINE_HOOK(0x4C7403, EventClass_Execute_AircraftAreaGuard, 0x6) GET(TechnoClass* const, pTechno, EDI); if (pTechno->WhatAmI() == AbstractType::Aircraft - && TechnoExt::ExtMap.Find(pTechno)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + && TechnoExt::Fetch(pTechno)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) && !pTechno->Ammo) { // Skip assigning destination / target here. @@ -1138,7 +1138,7 @@ DEFINE_HOOK(0x4C72F2, EventClass_Execute_AircraftAreaGuard_Unlink, 0x6) GET(TechnoClass* const, pTechno, EDI); if (pTechno->WhatAmI() == AbstractType::Aircraft - && TechnoExt::ExtMap.Find(pTechno)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) + && TechnoExt::Fetch(pTechno)->TypeExtData->ExtendedAircraftMissions.Get(RulesExt::Global()->ExtendedAircraftMissions) && pThis->MegaMission.Mission == (char)Mission::Area_Guard && !pTechno->Ammo) { @@ -1162,12 +1162,12 @@ DEFINE_HOOK(0x418CF3, AircraftClass_Mission_Attack_PlanningFix, 0x5) static __forceinline bool CheckSpyPlaneCameraCount(AircraftClass* pThis) { - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pThis->GetWeapon(0)->WeaponType); + auto const pWeaponExt = WeaponTypeExt::Fetch(pThis->GetWeapon(0)->WeaponType); if (!pWeaponExt->Strafing_Shots.isset()) return true; - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->Strafe_BombsDroppedThisRound >= pWeaponExt->Strafing_Shots) return false; @@ -1228,7 +1228,7 @@ DEFINE_HOOK(0x66295A, RocketLocomotionClass_Process_IsHighEnoughForCruise, 0x8) DEFINE_HOOK(0x4183C3, AircraftClass_CurleyShuffle_FireAtTarget, 0x6) { GET(AircraftClass*, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->DL(pTypeExt->CurleyShuffle.Get(RulesClass::Instance->CurleyShuffle)); return 0x4183C9; } @@ -1236,7 +1236,7 @@ DEFINE_HOOK(0x4183C3, AircraftClass_CurleyShuffle_FireAtTarget, 0x6) DEFINE_HOOK(0x418671, AircraftClass_CurleyShuffle_FireOk, 0x6) { GET(AircraftClass*, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->AL(pTypeExt->CurleyShuffle.Get(RulesClass::Instance->CurleyShuffle)); return 0x418677; } @@ -1244,7 +1244,7 @@ DEFINE_HOOK(0x418671, AircraftClass_CurleyShuffle_FireOk, 0x6) DEFINE_HOOK(0x418733, AircraftClass_CurleyShuffle_FireFacing, 0x6) { GET(AircraftClass*, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->CL(pTypeExt->CurleyShuffle.Get(RulesClass::Instance->CurleyShuffle)); return 0x418739; } @@ -1252,7 +1252,7 @@ DEFINE_HOOK(0x418733, AircraftClass_CurleyShuffle_FireFacing, 0x6) DEFINE_HOOK(0x418782, AircraftClass_CurleyShuffle_Default, 0x6) { GET(AircraftClass*, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->DL(pTypeExt->CurleyShuffle.Get(RulesClass::Instance->CurleyShuffle)); return 0x418788; } @@ -1265,7 +1265,7 @@ DEFINE_HOOK(0x415A00, AircraftClass_Mission_Paradrop_Overfly_Delay, 0x5) { GET(AircraftClass*, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); int delay; if (pThis->Passengers.NumPassengers) @@ -1292,7 +1292,7 @@ DEFINE_HOOK(0x4CD54C, FlyLocomotionClass_EdgeOfTheWorldAI_Paradrop, 0x8) GET(AircraftClass*, pLinkedTo, ECX); if (pLinkedTo->CurrentMission == Mission::Retreat || (pLinkedTo->CurrentMission == Mission::ParadropOverfly - && TechnoTypeExt::ExtMap.Find(pLinkedTo->Type)->ParadropEndDelay.Get(RulesExt::Global()->ParadropEndDelay) < 0 + && TechnoTypeExt::Fetch(pLinkedTo->Type)->ParadropEndDelay.Get(RulesExt::Global()->ParadropEndDelay) < 0 && !pLinkedTo->Passengers.NumPassengers)) { return Continue; diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index d3ef02fa02..f9a1ac1afe 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -4,7 +4,7 @@ DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) { GET(AircraftTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.Adopt(new AircraftTypeClassExtension(pItem)); + TechnoTypeExt::ExtMap.Adopt(new AircraftTypeExt(pItem)); return 0; } diff --git a/src/Ext/AircraftType/Body.h b/src/Ext/AircraftType/Body.h index f8bca0619a..49c22b8eb4 100644 --- a/src/Ext/AircraftType/Body.h +++ b/src/Ext/AircraftType/Body.h @@ -4,10 +4,10 @@ #include // Concrete leaf extension for AircraftTypeClass (empty). -class AircraftTypeClassExtension : public TechnoTypeClassExtension +class AircraftTypeExt : public TechnoTypeExt { public: - explicit AircraftTypeClassExtension(AircraftTypeClass* const OwnerObject) : TechnoTypeClassExtension(OwnerObject) + explicit AircraftTypeExt(AircraftTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) { } AircraftTypeClass* OwnerObject() const diff --git a/src/Ext/Anim/Body.cpp b/src/Ext/Anim/Body.cpp index 415fe440c9..cce8fe9dbb 100644 --- a/src/Ext/Anim/Body.cpp +++ b/src/Ext/Anim/Body.cpp @@ -10,40 +10,40 @@ AnimExt::ExtContainer AnimExt::ExtMap; std::vector AnimExt::AnimsWithAttachedParticles; -AnimExt::ExtData::~ExtData() +AnimExt::~AnimExt() { this->DeleteAttachedSystem(); if (this->Invoker) - TechnoExt::ExtMap.Find(this->Invoker)->AnimRefCount--; + TechnoExt::Fetch(this->Invoker)->AnimRefCount--; if (this->ParentBuilding) - TechnoExt::ExtMap.Find(this->ParentBuilding)->AnimRefCount--; + TechnoExt::Fetch(this->ParentBuilding)->AnimRefCount--; } -void AnimExt::ExtData::SetInvoker(TechnoClass* pInvoker) +void AnimExt::SetInvoker(TechnoClass* pInvoker) { this->SetInvoker(pInvoker, pInvoker ? pInvoker->Owner : nullptr); } -void AnimExt::ExtData::SetInvoker(TechnoClass* pInvoker, HouseClass* pInvokerHouse) +void AnimExt::SetInvoker(TechnoClass* pInvoker, HouseClass* pInvokerHouse) { if (pInvoker && this->Invoker != pInvoker) { if (this->Invoker) - TechnoExt::ExtMap.Find(this->Invoker)->AnimRefCount--; + TechnoExt::Fetch(this->Invoker)->AnimRefCount--; - TechnoExt::ExtMap.Find(pInvoker)->AnimRefCount++; + TechnoExt::Fetch(pInvoker)->AnimRefCount++; } this->Invoker = pInvoker; this->InvokerHouse = pInvokerHouse; } -void AnimExt::ExtData::CreateAttachedSystem() +void AnimExt::CreateAttachedSystem() { const auto pThis = this->OwnerObject(); - const auto pTypeExt = AnimTypeExt::ExtMap.TryFind(pThis->Type); + const auto pTypeExt = AnimTypeExt::TryFetch(pThis->Type); if (pTypeExt && pTypeExt->AttachedSystem && !this->AttachedSystem) { @@ -52,7 +52,7 @@ void AnimExt::ExtData::CreateAttachedSystem() } } -void AnimExt::ExtData::DeleteAttachedSystem() +void AnimExt::DeleteAttachedSystem() { if (this->AttachedSystem) { @@ -65,7 +65,7 @@ void AnimExt::ExtData::DeleteAttachedSystem() } } -void AnimExt::ExtData::UpdateAsFiringAnim() +void AnimExt::UpdateAsFiringAnim() { if (!this->FiringAnim_Weapon) return; @@ -106,7 +106,7 @@ void AnimExt::ExtData::UpdateAsFiringAnim() bool AnimExt::SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner, bool defaultToInvokerOwner) { auto const pType = pAnim->Type; - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pType); + auto const pTypeExt = AnimTypeExt::Fetch(pType); const bool makeInf = pType->MakeInfantry > -1; const bool createUnit = pTypeExt->CreateUnitType != nullptr; auto ownerKind = OwnerHouseKind::Default; @@ -307,8 +307,8 @@ void AnimExt::HandleDebrisImpact(AnimTypeClass* pExpireAnim, const std::vectorType; - auto const pExt = AnimExt::ExtMap.Find(pThis); - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pType); + auto const pExt = AnimExt::Fetch(pThis); + auto const pTypeExt = AnimTypeExt::Fetch(pType); auto const coords = pThis->GetCoords(); auto random = ScenarioClass::Instance->Random; @@ -330,7 +330,7 @@ void AnimExt::SpawnFireAnims(AnimClass* pThis) auto const loopCount = random.RandomRanged(1, 2); auto const pAnim = GameCreate(pType, newCoords, 0, loopCount, 0x600u, 0, false); AnimExt::SetAnimOwnerHouseKind(pAnim, pThis->Owner, nullptr, false, true); - auto const pExtNew = AnimExt::ExtMap.Find(pAnim); + auto const pExtNew = AnimExt::Fetch(pAnim); pExtNew->SetInvoker(pExt->Invoker, pExt->InvokerHouse); if (attach && pThis->OwnerObject) @@ -420,7 +420,7 @@ void AnimExt::CreateRandomAnim(const std::vector& AnimList, Coor if (invoker) { - auto const pAnimExt = AnimExt::ExtMap.Find(pAnim); + auto const pAnimExt = AnimExt::Fetch(pAnim); if (pHouse) pAnimExt->SetInvoker(pTechno, pHouse); @@ -433,7 +433,7 @@ void AnimExt::CreateRandomAnim(const std::vector& AnimList, Coor // load / save template -void AnimExt::ExtData::Serialize(T& Stm) +void AnimExt::Serialize(T& Stm) { Stm .Process(this->DeathUnitFacing) @@ -457,25 +457,25 @@ void AnimExt::ExtData::Serialize(T& Stm) ; } -void AnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void AnimExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectClassExtension::LoadFromStream(Stm); + ObjectExt::LoadFromStream(Stm); this->Serialize(Stm); } -void AnimExt::ExtData::PostLoad() +void AnimExt::PostLoad() { if (this->AttachedSystem) AnimExt::AnimsWithAttachedParticles.push_back(this->OwnerObject()); } -void AnimExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void AnimExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectClassExtension::SaveToStream(Stm); + ObjectExt::SaveToStream(Stm); this->Serialize(Stm); } -void AnimExt::ExtData::InitializeConstants() +void AnimExt::InitializeConstants() { // Something about creating this in constructor messes with debris anims, so it has to be done for them later. if (!this->OwnerObject()->HasExtras) @@ -486,7 +486,7 @@ void AnimExt::InvalidateTechnoPointers(TechnoClass* pTechno) { for (auto const& pAnim : AnimClass::Array) { - auto const pExt = AnimExt::ExtMap.TryFind(pAnim); + auto const pExt = AnimExt::TryFetch(pAnim); if (!pExt) continue; // Skip animation, chances are it is a null type anim in process of being removed. @@ -503,7 +503,7 @@ void AnimExt::InvalidateParticleSystemPointers(ParticleSystemClass* pParticleSys { for (auto const& pAnim : AnimExt::AnimsWithAttachedParticles) { - auto const pExt = AnimExt::ExtMap.TryFind(pAnim); + auto const pExt = AnimExt::TryFetch(pAnim); if (!pExt) continue; // Skip animation, chances are it is a null type anim in process of being removed. @@ -559,7 +559,7 @@ DEFINE_HOOK(0x4226F6, AnimClass_CTOR, 0x6) SyncLogger::AddAnimCreationSyncLogEvent(CTORTemp::coords, CTORTemp::callerAddress); AnimExt::ExtMap.Allocate(pItem); - pItem->UseCellLightConvert = AnimTypeExt::ExtMap.Find(pItem->Type)->TheaterPalette.Get(false); + pItem->UseCellLightConvert = AnimTypeExt::Fetch(pItem->Type)->TheaterPalette.Get(false); return 0; } @@ -578,7 +578,7 @@ DEFINE_HOOK(0x426598, AnimClass_SDDTOR, 0x7) { GET(AnimClass*, pItem, ESI); - if(AnimExt::ExtMap.Find(pItem)) + if(AnimExt::Fetch(pItem)) AnimExt::ExtMap.Remove(pItem); return 0; diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 40141ab4a7..8d3d399086 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -4,83 +4,82 @@ #include #include -class AnimExt +class AnimExt final : public ObjectExt { public: using base_type = AnimClass; + using ExtData = AnimExt; static constexpr DWORD Canary = 0xAAAAAAAA; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectClassExtension +public: + // typed owner accessor + AnimClass* OwnerObject() const { - public: - // typed owner accessor - AnimClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - DirType DeathUnitFacing; - DirStruct DeathUnitTurretFacing; - bool FromDeathUnit; - bool DeathUnitHasTurret; - TechnoClass* Invoker; - HouseClass* InvokerHouse; - ParticleSystemClass* AttachedSystem; - BuildingClass* ParentBuilding; // Only set on building anims, used for tinting the anims etc. especially when not on same cell as building - bool IsTechnoTrailerAnim; - bool DelayedFireRemoveOnNoDelay; - bool IsAttachedEffectAnim; - bool IsShieldIdleAnim; - WeaponTypeClass* FiringAnim_Weapon; - int FiringAnim_WeaponIndex; - int FiringAnim_BurstIndex; - DirStruct FiringAnim_LastFacing; - CoordStruct FiringAnim_LastCoords; - double FirepowerMult; - - ExtData(AnimClass* OwnerObject) : ObjectClassExtension(OwnerObject) - , DeathUnitFacing { 0 } - , DeathUnitTurretFacing {} - , FromDeathUnit { false } - , DeathUnitHasTurret { false } - , Invoker {} - , InvokerHouse {} - , AttachedSystem {} - , ParentBuilding {} - , IsTechnoTrailerAnim { false } - , DelayedFireRemoveOnNoDelay { false } - , IsAttachedEffectAnim { false } - , IsShieldIdleAnim { false } - , FiringAnim_Weapon {} - , FiringAnim_WeaponIndex {} - , FiringAnim_BurstIndex {} - , FiringAnim_LastFacing {} - , FiringAnim_LastCoords {} - , FirepowerMult { 1.0 } - { } - - void SetInvoker(TechnoClass* pInvoker); - void SetInvoker(TechnoClass* pInvoker, HouseClass* pInvokerHouse); - void CreateAttachedSystem(); - void DeleteAttachedSystem(); - - void UpdateAsFiringAnim(); - - virtual ~ExtData() override; - - virtual void InitializeConstants() override; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - virtual void PostLoad() override; - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + DirType DeathUnitFacing; + DirStruct DeathUnitTurretFacing; + bool FromDeathUnit; + bool DeathUnitHasTurret; + TechnoClass* Invoker; + HouseClass* InvokerHouse; + ParticleSystemClass* AttachedSystem; + BuildingClass* ParentBuilding; // Only set on building anims, used for tinting the anims etc. especially when not on same cell as building + bool IsTechnoTrailerAnim; + bool DelayedFireRemoveOnNoDelay; + bool IsAttachedEffectAnim; + bool IsShieldIdleAnim; + WeaponTypeClass* FiringAnim_Weapon; + int FiringAnim_WeaponIndex; + int FiringAnim_BurstIndex; + DirStruct FiringAnim_LastFacing; + CoordStruct FiringAnim_LastCoords; + double FirepowerMult; + + AnimExt(AnimClass* OwnerObject) : ObjectExt(OwnerObject) + , DeathUnitFacing { 0 } + , DeathUnitTurretFacing {} + , FromDeathUnit { false } + , DeathUnitHasTurret { false } + , Invoker {} + , InvokerHouse {} + , AttachedSystem {} + , ParentBuilding {} + , IsTechnoTrailerAnim { false } + , DelayedFireRemoveOnNoDelay { false } + , IsAttachedEffectAnim { false } + , IsShieldIdleAnim { false } + , FiringAnim_Weapon {} + , FiringAnim_WeaponIndex {} + , FiringAnim_BurstIndex {} + , FiringAnim_LastFacing {} + , FiringAnim_LastCoords {} + , FirepowerMult { 1.0 } + { } + + void SetInvoker(TechnoClass* pInvoker); + void SetInvoker(TechnoClass* pInvoker, HouseClass* pInvokerHouse); + void CreateAttachedSystem(); + void DeleteAttachedSystem(); + + void UpdateAsFiringAnim(); + + virtual ~AnimExt() override; + + virtual void InitializeConstants() override; + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void PostLoad() override; + +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -96,6 +95,16 @@ class AnimExt static std::vector AnimsWithAttachedParticles; static ExtContainer ExtMap; + static AnimExt* Fetch(const AnimClass* pThis) + { + return ExtMap.Find(pThis); + } + + static AnimExt* TryFetch(const AnimClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner = false, bool defaultToInvokerOwner = false); static HouseClass* GetOwnerHouse(AnimClass* pAnim, HouseClass* pDefaultOwner = nullptr); static void VeinAttackAI(AnimClass* pAnim); @@ -110,5 +119,3 @@ class AnimExt static void CreateRandomAnim(const std::vector& AnimList, CoordStruct coords, TechnoClass* pTechno = nullptr, HouseClass* pHouse = nullptr, bool invoker = false, bool ownedObject = false); }; -// top-level name for the AnimExt extension -using AnimClassExtension = AnimExt::ExtData; diff --git a/src/Ext/Anim/Hooks.AnimCreateUnit.cpp b/src/Ext/Anim/Hooks.AnimCreateUnit.cpp index aeb9cd73d5..944c4c61dc 100644 --- a/src/Ext/Anim/Hooks.AnimCreateUnit.cpp +++ b/src/Ext/Anim/Hooks.AnimCreateUnit.cpp @@ -11,7 +11,7 @@ DEFINE_HOOK(0x737F6D, UnitClass_TakeDamage_Destroy, 0x7) REF_STACK(args_ReceiveDamage const, receiveDamageArgs, STACK_OFFSET(0x44, 0x4)); R->ECX(R->ESI()); - TechnoExt::ExtMap.Find(pThis)->ReceiveDamage = true; + TechnoExt::Fetch(pThis)->ReceiveDamage = true; auto pAttacker = receiveDamageArgs.Attacker; AnimTypeExt::ProcessDestroyAnims(pThis, pAttacker ? pAttacker->Owner : receiveDamageArgs.SourceHouse); pThis->Destroy(); @@ -23,7 +23,7 @@ DEFINE_HOOK(0x738807, UnitClass_Destroy_DestroyAnim, 0x8) { GET(UnitClass* const, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (!pExt->ReceiveDamage) AnimTypeExt::ProcessDestroyAnims(pThis); @@ -37,7 +37,7 @@ DEFINE_HOOK(0x4226F0, AnimClass_CTOR_CreateUnit_MarkOccupationBits, 0x6) { GET(AnimClass* const, pThis, ESI); - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (pTypeExt->CreateUnitType) pThis->MarkAllOccupationBits(pThis->GetCell()->GetCoordsWithBridge()); @@ -50,12 +50,12 @@ DEFINE_HOOK(0x424932, AnimClass_AI_CreateUnit_ActualEffects, 0x6) GET(AnimClass* const, pThis, ESI); auto const pType = pThis->Type; - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pType); + auto const pTypeExt = AnimTypeExt::Fetch(pType); if (auto const pCreateUnit = pTypeExt->CreateUnitType.get()) { auto const pUnitType = pCreateUnit->Type; - auto const pExt = AnimExt::ExtMap.Find(pThis); + auto const pExt = AnimExt::Fetch(pThis); pThis->UnmarkAllOccupationBits(pThis->GetCell()->GetCoordsWithBridge()); auto const facing = pCreateUnit->RandomFacing diff --git a/src/Ext/Anim/Hooks.cpp b/src/Ext/Anim/Hooks.cpp index 3dfbdc385b..f86390f2c7 100644 --- a/src/Ext/Anim/Hooks.cpp +++ b/src/Ext/Anim/Hooks.cpp @@ -15,7 +15,7 @@ DEFINE_HOOK(0x423B95, AnimClass_AI_Early, 0x8) { GET(AnimClass* const, pThis, ESI); - AnimExt::ExtMap.Find(pThis)->UpdateAsFiringAnim(); + AnimExt::Fetch(pThis)->UpdateAsFiringAnim(); auto const pType = pThis->Type; AnimLoggingTemp::UniqueID = pThis->UniqueID; @@ -24,7 +24,7 @@ DEFINE_HOOK(0x423B95, AnimClass_AI_Early, 0x8) // Replace vanilla HideIfNoOre check. if (pType->HideIfNoOre) { - const int nThreshold = abs(AnimTypeExt::ExtMap.Find(pType)->HideIfNoOre_Threshold.Get()); + const int nThreshold = abs(AnimTypeExt::Fetch(pType)->HideIfNoOre_Threshold.Get()); pThis->Invisible = pThis->GetCell()->GetContainedTiberiumValue() <= nThreshold; } @@ -45,7 +45,7 @@ DEFINE_HOOK(0x42453E, AnimClass_AI_Damage, 0x6) return SkipDamage; const auto pType = pThis->Type; - const auto pTypeExt = AnimTypeExt::ExtMap.Find(pType); + const auto pTypeExt = AnimTypeExt::Fetch(pType); const auto pOwnerObject = pThis->OwnerObject; const int delay = pTypeExt->Damage_Delay.Get(); const bool isTerrain = pOwnerObject && pOwnerObject->WhatAmI() == AbstractType::Terrain; @@ -99,7 +99,7 @@ DEFINE_HOOK(0x42453E, AnimClass_AI_Damage, 0x6) if (pTypeExt->Damage_DealtByInvoker) { - const auto pExt = AnimExt::ExtMap.Find(pThis); + const auto pExt = AnimExt::Fetch(pThis); pInvoker = pExt->Invoker; if (!pInvoker) @@ -134,7 +134,7 @@ DEFINE_HOOK(0x42453E, AnimClass_AI_Damage, 0x6) if (pOwnerObject) pOwner = pOwnerObject->GetOwningHouse(); else if (pThis->IsBuildingAnim) - pOwner = AnimExt::ExtMap.Find(pThis)->ParentBuilding->Owner; + pOwner = AnimExt::Fetch(pThis)->ParentBuilding->Owner; } if (pTypeExt->Weapon) @@ -187,8 +187,8 @@ DEFINE_HOOK(0x4242E1, AnimClass_AI_TrailerAnim, 0x5) auto const pTrailerAnim = GameCreate(pThis->Type->TrailerAnim, pThis->GetCoords(), 1, 1); - auto const pTrailerAnimExt = AnimExt::ExtMap.Find(pTrailerAnim); - auto const pExt = AnimExt::ExtMap.Find(pThis); + auto const pTrailerAnimExt = AnimExt::Fetch(pTrailerAnim); + auto const pExt = AnimExt::Fetch(pThis); AnimExt::SetAnimOwnerHouseKind(pTrailerAnim, pThis->Owner, nullptr, false, true); pTrailerAnimExt->SetInvoker(pExt->Invoker, pExt->InvokerHouse); @@ -200,7 +200,7 @@ DEFINE_HOOK(0x423939, AnimClass_BounceAI_AttachedSystem, 0x6) { GET(AnimClass*, pThis, EBP); - AnimExt::ExtMap.Find(pThis)->CreateAttachedSystem(); + AnimExt::Fetch(pThis)->CreateAttachedSystem(); return 0; } @@ -230,7 +230,7 @@ DEFINE_HOOK(0x423CC7, AnimClass_AI_HasExtras_Expired, 0x6) if (!pType) return SkipGameCode; - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pType); + auto const pTypeExt = AnimTypeExt::Fetch(pType); auto const splashAnims = pTypeExt->SplashAnims.GetElements(RulesClass::Instance->SplashList); auto const nDamage = static_cast(pType->Damage); auto const pOwner = AnimExt::GetOwnerHouse(pThis); @@ -245,8 +245,8 @@ DEFINE_HOOK(0x424807, AnimClass_AI_Next, 0x6) { GET(AnimClass*, pThis, ESI); - const auto pExt = AnimExt::ExtMap.Find(pThis); - const auto pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + const auto pExt = AnimExt::Fetch(pThis); + const auto pTypeExt = AnimTypeExt::Fetch(pThis->Type); pThis->UseCellLightConvert = pTypeExt->TheaterPalette.Get(pThis->UseCellLightConvert); if (pExt->AttachedSystem && pExt->AttachedSystem->Type != pTypeExt->AttachedSystem.Get()) @@ -268,7 +268,7 @@ DEFINE_HOOK(0x424CF1, AnimClass_Start_DetachedReport, 0x6) { GET(AnimClass*, pThis, ESI); - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (pTypeExt->DetachedReport >= 0) VocClass::PlayAt(pTypeExt->DetachedReport.Get(), pThis->GetCoords()); @@ -283,7 +283,7 @@ DEFINE_HOOK(0x423122, AnimClass_DrawIt_XDrawOffset, 0x6) GET(AnimClass* const, pThis, ESI); GET_STACK(Point2D*, pLocation, STACK_OFFSET(0x110, 0x4)); - if (auto const pTypeExt = AnimTypeExt::ExtMap.TryFind(pThis->Type)) + if (auto const pTypeExt = AnimTypeExt::TryFetch(pThis->Type)) pLocation->X += pTypeExt->XDrawOffset; return 0; @@ -299,7 +299,7 @@ DEFINE_HOOK(0x424CB0, AnimClass_InWhichLayer_AttachedObjectLayer, 0x6) if (pThis->OwnerObject) { - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (pTypeExt->Layer_UseObjectLayer.isset()) { @@ -322,7 +322,7 @@ DEFINE_HOOK(0x424C3D, AnimClass_AttachTo_AttachedAnimPosition, 0x6) GET(AnimClass*, pThis, ESI); - auto const pExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pExt = AnimTypeExt::Fetch(pThis->Type); if (pExt->AttachedAnimPosition != AttachedAnimPosition::Default) { @@ -348,7 +348,7 @@ CoordStruct* AnimClassFake::_GetCenterCoords(CoordStruct* pCrd) const { *coords += pObject->GetCoords(); - if (AnimTypeExt::ExtMap.Find(this->Type)->AttachedAnimPosition == AttachedAnimPosition::Ground) + if (AnimTypeExt::Fetch(this->Type)->AttachedAnimPosition == AttachedAnimPosition::Ground) coords->Z = MapClass::Instance.GetCellFloorHeight(*coords); } @@ -363,7 +363,7 @@ DEFINE_HOOK(0x4236F0, AnimClass_DrawIt_Tiled_Palette, 0x6) { GET(AnimClass*, pThis, ESI); - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); R->EDX(pTypeExt->Palette.GetOrDefaultConvert(FileSystem::ANIM_PAL)); @@ -378,7 +378,7 @@ DEFINE_HOOK(0x423654, AnimClass_DrawIt_Tiled_Interval, 0x5) int height = pBounds->Height; - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (pTypeExt->Tiled_Interval > 0) height = pTypeExt->Tiled_Interval; @@ -393,7 +393,7 @@ DEFINE_HOOK(0x423660, AnimClass_DrawIt_Tiled_Center, 0x5) GET(const int, height, EAX); R->EDX(VTable::Get(pThis)); // Restore overriden instruction - const auto pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (pTypeExt->Tiled_AlignToCenter) R->EAX(0); else @@ -410,7 +410,7 @@ DEFINE_HOOK(0x423365, AnimClass_DrawIt_ExtraShadow, 0x8) if (pThis->HasExtras) { - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (!pTypeExt->ExtraShadow) return SkipExtraShadow; @@ -469,7 +469,7 @@ DEFINE_HOOK(0x423061, AnimClass_DrawIt_Visibility, 0x6) GET(AnimClass* const, pThis, ESI); - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = AnimTypeExt::Fetch(pThis->Type); if (!pTypeExt->RestrictVisibilityIfCloaked && pTypeExt->VisibleTo == AffectedHouse::All) return 0; @@ -479,7 +479,7 @@ DEFINE_HOOK(0x423061, AnimClass_DrawIt_Visibility, 0x6) if (!pTechno) { - auto const pExt = AnimExt::ExtMap.Find(pThis); + auto const pExt = AnimExt::Fetch(pThis); if (pExt->IsTechnoTrailerAnim) pTechno = pExt->Invoker; @@ -497,7 +497,7 @@ DEFINE_HOOK(0x423061, AnimClass_DrawIt_Visibility, 0x6) if (pTypeExt->VisibleTo_ConsiderInvokerAsOwner) { - auto const pExt = AnimExt::ExtMap.Find(pThis); + auto const pExt = AnimExt::Fetch(pThis); if (pExt->Invoker) pOwner = pExt->Invoker->Owner; @@ -527,7 +527,7 @@ DEFINE_HOOK(0x42308D, AnimClass_DrawIt_Transparency, 0x6) const int currentFrame = pThis->Animation.Value; const int frames = pType->End; - auto const pTypeExt = AnimTypeExt::ExtMap.Find(pType); + auto const pTypeExt = AnimTypeExt::Fetch(pType); if (!pType->Translucent) { @@ -597,7 +597,7 @@ DEFINE_HOOK(0x4232E2, AnimClass_DrawIt_AltPalette, 0x6) GET(AnimClass*, pThis, ESI); int schemeIndex = pThis->Owner ? pThis->Owner->ColorSchemeIndex - 1 : RulesExt::Global()->AnimRemapDefaultColorScheme; - schemeIndex += AnimTypeExt::ExtMap.Find(pThis->Type)->AltPalette_ApplyLighting ? 1 : 0; + schemeIndex += AnimTypeExt::Fetch(pThis->Type)->AltPalette_ApplyLighting ? 1 : 0; auto const scheme = ColorScheme::Array[schemeIndex]; R->ECX(scheme); @@ -624,13 +624,13 @@ DEFINE_HOOK(0x425174, AnimClass_Detach_Cloak, 0x6) GET(AnimClass*, pThis, ESI); GET(AbstractClass*, pTarget, EDI); - auto const pTypeExt = AnimTypeExt::ExtMap.TryFind(pThis->Type); + auto const pTypeExt = AnimTypeExt::TryFetch(pThis->Type); if (pTypeExt && !pTypeExt->DetachOnCloak) { if (auto const pTechno = abstract_cast(pTarget)) { - auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTechnoExt = TechnoExt::Fetch(pTechno); if (pTechnoExt->IsDetachingForCloak) return SkipDetaching; @@ -665,7 +665,7 @@ DEFINE_HOOK(0x4250E1, AnimClass_Middle_CraterDestroyTiberium, 0x6) { enum { SkipDestroyTiberium = 0x4250EC }; GET(AnimTypeClass*, pType, EDX); - return AnimTypeExt::ExtMap.Find(pType)->Crater_DestroyTiberium.Get(RulesExt::Global()->AnimCraterDestroyTiberium) ? 0 : SkipDestroyTiberium; + return AnimTypeExt::Fetch(pType)->Crater_DestroyTiberium.Get(RulesExt::Global()->AnimCraterDestroyTiberium) ? 0 : SkipDestroyTiberium; } #pragma region FiringAnimUpdate @@ -679,9 +679,9 @@ DEFINE_HOOK(0x6FF42B, TechnoClass_Fire_Anim, 0x7) GET(WeaponTypeClass*, pWeapon, EBX); GET_BASE(const int, wpIdx, 0xC); - const auto pAnimExt = AnimExt::ExtMap.Find(pAnim); + const auto pAnimExt = AnimExt::Fetch(pAnim); - if (WeaponTypeExt::ExtMap.Find(pWeapon)->Anim_Update.Get(RulesExt::Global()->FiringAnim_Update)) + if (WeaponTypeExt::Fetch(pWeapon)->Anim_Update.Get(RulesExt::Global()->FiringAnim_Update)) { pAnimExt->FiringAnim_Weapon = pWeapon; pAnimExt->FiringAnim_WeaponIndex = wpIdx; @@ -700,7 +700,7 @@ DEFINE_HOOK(0x47DA74, CellClass_RecalcAttributes_TileAnimDrawer, 0x7) GET(AnimClass*, pAnim, EAX); - pAnim->UseCellLightConvert = AnimTypeExt::ExtMap.Find(pAnim->Type)->TheaterPalette.Get(true); + pAnim->UseCellLightConvert = AnimTypeExt::Fetch(pAnim->Type)->TheaterPalette.Get(true); return SkipGameCode; } diff --git a/src/Ext/AnimType/Body.cpp b/src/Ext/AnimType/Body.cpp index b620be54cf..7d717ce922 100644 --- a/src/Ext/AnimType/Body.cpp +++ b/src/Ext/AnimType/Body.cpp @@ -16,7 +16,7 @@ void AnimTypeExt::ProcessDestroyAnims(UnitClass* pThis, HouseClass* pKiller) { auto const facing = pThis->PrimaryFacing.Current().GetDir(); AnimTypeClass* pAnimType = nullptr; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (!pTypeExt->DestroyAnim_Random.Get()) { @@ -46,8 +46,8 @@ void AnimTypeExt::ProcessDestroyAnims(UnitClass* pThis, HouseClass* pKiller) //auto VictimOwner = pThis->IsMindControlled() && pThis->GetOriginalOwner() // ? pThis->GetOriginalOwner() : pThis->Owner; - auto const pAnimTypeExt = AnimTypeExt::ExtMap.Find(pAnim->Type); - auto const pAnimExt = AnimExt::ExtMap.Find(pAnim); + auto const pAnimTypeExt = AnimTypeExt::Fetch(pAnim->Type); + auto const pAnimExt = AnimExt::Fetch(pAnim); AnimExt::SetAnimOwnerHouseKind(pAnim, pInvoker, pThis->Owner); @@ -72,7 +72,7 @@ void AnimTypeExt::ProcessDestroyAnims(UnitClass* pThis, HouseClass* pKiller) } } -void AnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI) +void AnimTypeExt::LoadFromINIFile(CCINIClass* pINI) { const char* pID = this->OwnerObject()->ID; @@ -138,7 +138,7 @@ void AnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI) } template -void AnimTypeExt::ExtData::Serialize(T& Stm) +void AnimTypeExt::Serialize(T& Stm) { Stm .Process(this->Palette) @@ -186,15 +186,15 @@ void AnimTypeExt::ExtData::Serialize(T& Stm) ; } -void AnimTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void AnimTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void AnimTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void AnimTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index 2d3a3c40c8..abb755a304 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -15,123 +15,122 @@ enum class AttachedAnimPosition : BYTE Ground = 2 }; -class AnimTypeExt +class AnimTypeExt final : public ObjectTypeExt { public: using base_type = AnimTypeClass; + using ExtData = AnimTypeExt; static constexpr DWORD Canary = 0xEEEEEEEE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + AnimTypeClass* OwnerObject() const { - public: - // typed owner accessor - AnimTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - CustomPalette Palette; - std::unique_ptr CreateUnitType; - Valueable XDrawOffset; - Valueable HideIfNoOre_Threshold; - Nullable Layer_UseObjectLayer; - Valueable AttachedAnimPosition; - Valueable Weapon; - Valueable Damage_Delay; - Valueable Damage_DealtByInvoker; - Valueable Damage_ApplyOncePerLoop; - Valueable Damage_ApplyFirepowerMult; - Valueable ExplodeOnWater; - Valueable Warhead_Detonate; - ValueableVector WakeAnim; - NullableVector SplashAnims; - Valueable SplashAnims_PickRandom; - Valueable AttachedSystem; - Valueable AltPalette_ApplyLighting; - Valueable MakeInfantryOwner; - Valueable ExtraShadow; - ValueableIdx DetachedReport; - Valueable VisibleTo; - Valueable VisibleTo_ConsiderInvokerAsOwner; - Valueable RestrictVisibilityIfCloaked; - Valueable DetachOnCloak; - Animatable Translucency; - Animatable Translucency_Cloaked; - Valueable ConstrainFireAnimsToCellSpots; - Nullable FireAnimDisallowedLandTypes; - Nullable AttachFireAnimsToParent; - Nullable SmallFireCount; - ValueableVector SmallFireAnims; - ValueableVector SmallFireChances; - ValueableVector SmallFireDistances; - Valueable LargeFireCount; - ValueableVector LargeFireAnims; - ValueableVector LargeFireChances; - ValueableVector LargeFireDistances; - Nullable Crater_DestroyTiberium; - Nullable TheaterPalette; - Valueable Tiled_Interval; - Valueable Tiled_AlignToCenter; - - ExtData(AnimTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , Palette { CustomPalette::PaletteMode::Temperate } - , CreateUnitType { nullptr } - , XDrawOffset { 0 } - , HideIfNoOre_Threshold { 0 } - , Layer_UseObjectLayer {} - , AttachedAnimPosition { AttachedAnimPosition::Default } - , Weapon {} - , Damage_Delay { 0 } - , Damage_DealtByInvoker { false } - , Damage_ApplyOncePerLoop { false } - , Damage_ApplyFirepowerMult { false } - , ExplodeOnWater { false } - , Warhead_Detonate { false } - , WakeAnim {} - , SplashAnims {} - , SplashAnims_PickRandom { false } - , AttachedSystem {} - , AltPalette_ApplyLighting { false } - , MakeInfantryOwner { OwnerHouseKind::Victim } - , ExtraShadow { true } - , DetachedReport {} - , VisibleTo { AffectedHouse::All } - , VisibleTo_ConsiderInvokerAsOwner { false } - , RestrictVisibilityIfCloaked { false } - , DetachOnCloak { true } - , Translucency { TranslucencyLevel {} } - , Translucency_Cloaked { TranslucencyLevel {} } - , ConstrainFireAnimsToCellSpots { true } - , FireAnimDisallowedLandTypes {} - , AttachFireAnimsToParent {} - , SmallFireCount {} - , SmallFireAnims {} - , SmallFireChances {} - , SmallFireDistances {} - , LargeFireCount { 1 } - , LargeFireAnims {} - , LargeFireChances {} - , LargeFireDistances {} - , Crater_DestroyTiberium {} - , TheaterPalette {} - , Tiled_Interval { 0 } - , Tiled_AlignToCenter { false } - { } - - virtual ~ExtData() = default; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + CustomPalette Palette; + std::unique_ptr CreateUnitType; + Valueable XDrawOffset; + Valueable HideIfNoOre_Threshold; + Nullable Layer_UseObjectLayer; + Valueable AttachedAnimPosition; + Valueable Weapon; + Valueable Damage_Delay; + Valueable Damage_DealtByInvoker; + Valueable Damage_ApplyOncePerLoop; + Valueable Damage_ApplyFirepowerMult; + Valueable ExplodeOnWater; + Valueable Warhead_Detonate; + ValueableVector WakeAnim; + NullableVector SplashAnims; + Valueable SplashAnims_PickRandom; + Valueable AttachedSystem; + Valueable AltPalette_ApplyLighting; + Valueable MakeInfantryOwner; + Valueable ExtraShadow; + ValueableIdx DetachedReport; + Valueable VisibleTo; + Valueable VisibleTo_ConsiderInvokerAsOwner; + Valueable RestrictVisibilityIfCloaked; + Valueable DetachOnCloak; + Animatable Translucency; + Animatable Translucency_Cloaked; + Valueable ConstrainFireAnimsToCellSpots; + Nullable FireAnimDisallowedLandTypes; + Nullable AttachFireAnimsToParent; + Nullable SmallFireCount; + ValueableVector SmallFireAnims; + ValueableVector SmallFireChances; + ValueableVector SmallFireDistances; + Valueable LargeFireCount; + ValueableVector LargeFireAnims; + ValueableVector LargeFireChances; + ValueableVector LargeFireDistances; + Nullable Crater_DestroyTiberium; + Nullable TheaterPalette; + Valueable Tiled_Interval; + Valueable Tiled_AlignToCenter; + + AnimTypeExt(AnimTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , Palette { CustomPalette::PaletteMode::Temperate } + , CreateUnitType { nullptr } + , XDrawOffset { 0 } + , HideIfNoOre_Threshold { 0 } + , Layer_UseObjectLayer {} + , AttachedAnimPosition { AttachedAnimPosition::Default } + , Weapon {} + , Damage_Delay { 0 } + , Damage_DealtByInvoker { false } + , Damage_ApplyOncePerLoop { false } + , Damage_ApplyFirepowerMult { false } + , ExplodeOnWater { false } + , Warhead_Detonate { false } + , WakeAnim {} + , SplashAnims {} + , SplashAnims_PickRandom { false } + , AttachedSystem {} + , AltPalette_ApplyLighting { false } + , MakeInfantryOwner { OwnerHouseKind::Victim } + , ExtraShadow { true } + , DetachedReport {} + , VisibleTo { AffectedHouse::All } + , VisibleTo_ConsiderInvokerAsOwner { false } + , RestrictVisibilityIfCloaked { false } + , DetachOnCloak { true } + , Translucency { TranslucencyLevel {} } + , Translucency_Cloaked { TranslucencyLevel {} } + , ConstrainFireAnimsToCellSpots { true } + , FireAnimDisallowedLandTypes {} + , AttachFireAnimsToParent {} + , SmallFireCount {} + , SmallFireAnims {} + , SmallFireChances {} + , SmallFireDistances {} + , LargeFireCount { 1 } + , LargeFireAnims {} + , LargeFireChances {} + , LargeFireDistances {} + , Crater_DestroyTiberium {} + , TheaterPalette {} + , Tiled_Interval { 0 } + , Tiled_AlignToCenter { false } + { } + + virtual ~AnimTypeExt() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -141,8 +140,16 @@ class AnimTypeExt static ExtContainer ExtMap; + static AnimTypeExt* Fetch(const AnimTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static AnimTypeExt* TryFetch(const AnimTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static void ProcessDestroyAnims(UnitClass* pThis, HouseClass* pKiller = nullptr); }; -// top-level name for the AnimTypeExt extension -using AnimTypeClassExtension = AnimTypeExt::ExtData; diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index f3f48f6e1e..4fde518f02 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -4,9 +4,8 @@ #include #include -BuildingExt::ExtMapFacade BuildingExt::ExtMap; -void BuildingExt::ExtData::DisplayIncomeString() +void BuildingExt::DisplayIncomeString() { if (this->AccumulatedIncome) { @@ -33,10 +32,10 @@ void BuildingExt::ExtData::DisplayIncomeString() } } -bool BuildingExt::ExtData::HasSuperWeapon(const int index) const +bool BuildingExt::HasSuperWeapon(const int index) const { const auto pThis = this->OwnerObject(); - const auto pExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + const auto pExt = BuildingTypeExt::Fetch(pThis->Type); const auto pOwner = pThis->Owner; const int count = pExt->GetSuperWeaponCount(); @@ -53,7 +52,7 @@ bool BuildingExt::ExtData::HasSuperWeapon(const int index) const { for (auto const& pUpgrade : pThis->Upgrades) { - if (const auto pUpgradeExt = BuildingTypeExt::ExtMap.TryFind(pUpgrade)) + if (const auto pUpgradeExt = BuildingTypeExt::TryFetch(pUpgrade)) { const int countUpgrade = pUpgradeExt->GetSuperWeaponCount(); @@ -79,7 +78,7 @@ void BuildingExt::StoreTiberium(BuildingClass* pThis, float amount, int idxTiber if (amount > 0.0f) { - auto const pExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pExt = BuildingTypeExt::Fetch(pThis->Type); if (pExt->Refinery_UseStorage) { @@ -90,7 +89,7 @@ void BuildingExt::StoreTiberium(BuildingClass* pThis, float amount, int idxTiber } } -void BuildingExt::ExtData::UpdatePrimaryFactoryAI() +void BuildingExt::UpdatePrimaryFactoryAI() { auto const pOwner = this->OwnerObject()->Owner; @@ -237,7 +236,7 @@ bool BuildingExt::CanGrindTechno(BuildingClass* pBuilding, TechnoClass* pTechno) return false; } - auto const pExt = BuildingTypeExt::ExtMap.Find(pBldType); + auto const pExt = BuildingTypeExt::Fetch(pBldType); if (pBuilding->Owner == pTechno->Owner && !pExt->Grinding_AllowOwner) return false; @@ -261,7 +260,7 @@ bool BuildingExt::CanGrindTechno(BuildingClass* pBuilding, TechnoClass* pTechno) bool BuildingExt::DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechno, int refund) { - if (auto const pExt = BuildingExt::ExtMap.TryFind(pBuilding)) + if (auto const pExt = BuildingExt::TryFetch(pBuilding)) { auto const pTypeExt = pExt->TypeExtData; @@ -288,7 +287,7 @@ bool BuildingExt::DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechn } // Building only or allow units too? -void BuildingExt::ExtData::ApplyPoweredKillSpawns() +void BuildingExt::ApplyPoweredKillSpawns() { auto const pThis = this->OwnerObject(); auto const pTypeExt = this->TypeExtData; @@ -311,7 +310,7 @@ void BuildingExt::ExtData::ApplyPoweredKillSpawns() } } -bool BuildingExt::ExtData::HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore) +bool BuildingExt::HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore) { const auto pThis = this->OwnerObject(); const auto pVictimHouse = pThis->Owner; @@ -442,7 +441,7 @@ const std::vector BuildingExt::GetFoundationCells(BuildingClass* con WeaponStruct* BuildingExt::GetLaserWeapon(BuildingClass* pThis) { - auto const pExt = BuildingExt::ExtMap.Find(pThis); + auto const pExt = BuildingExt::Fetch(pThis); if (pExt->CurrentLaserWeaponIndex.has_value()) return pThis->GetWeapon(pExt->CurrentLaserWeaponIndex.value()); @@ -452,7 +451,7 @@ WeaponStruct* BuildingExt::GetLaserWeapon(BuildingClass* pThis) void BuildingExt::KickOutClone(std::pair& info, void*, BuildingClass* pFactory) { - if (!pFactory->IsAlive || pFactory->InLimbo || (BuildingTypeExt::ExtMap.Find(pFactory->Type)->Cloning_Powered && !pFactory->IsPowerOnline()) || pFactory->IsBeingWarpedOut()) + if (!pFactory->IsAlive || pFactory->InLimbo || (BuildingTypeExt::Fetch(pFactory->Type)->Cloning_Powered && !pFactory->IsPowerOnline()) || pFactory->IsBeingWarpedOut()) return; const auto pClone = static_cast(info.first->CreateObject(info.second)); @@ -463,7 +462,7 @@ void BuildingExt::KickOutClone(std::pair& info, v int BuildingExt::GetTurretFrame(BuildingClass* pThis) { - auto const pExt = BuildingExt::ExtMap.Find(pThis); + auto const pExt = BuildingExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; const int facing = pThis->PrimaryFacing.Current().GetValue<5>(); const int shapeFacing = ObjectClass::BodyShape[facing]; @@ -548,7 +547,7 @@ int BuildingExt::GetTurretFrame(BuildingClass* pThis) // load / save template -void BuildingExt::ExtData::Serialize(T& Stm) +void BuildingExt::Serialize(T& Stm) { Stm .Process(this->TypeExtData) @@ -569,15 +568,15 @@ void BuildingExt::ExtData::Serialize(T& Stm) ; } -void BuildingExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void BuildingExt::LoadFromStream(PhobosStreamReader& Stm) { - TechnoExt::ExtData::LoadFromStream(Stm); + TechnoExt::LoadFromStream(Stm); this->Serialize(Stm); } -void BuildingExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void BuildingExt::SaveToStream(PhobosStreamWriter& Stm) { - TechnoExt::ExtData::SaveToStream(Stm); + TechnoExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -603,11 +602,11 @@ DEFINE_HOOK(0x43BCBD, BuildingClass_CTOR, 0x6) { GET(BuildingClass*, pItem, ESI); - // A building's extension is a concrete BuildingClassExtension leaf, owned by the TechnoClass container. - auto const pExt = static_cast(TechnoExt::ExtMap.Adopt(new BuildingExt::ExtData(pItem))); + // A building's extension is a concrete BuildingExt leaf, owned by the TechnoClass container. + auto const pExt = static_cast(TechnoExt::ExtMap.Adopt(new BuildingExt(pItem))); if (pExt) - pExt->TypeExtData = BuildingTypeExt::ExtMap.Find(pItem->Type); + pExt->TypeExtData = BuildingTypeExt::Fetch(pItem->Type); return 0; } @@ -630,7 +629,7 @@ static void __fastcall BuildingClass_InfiltratedBy_Wrapper(BuildingClass* pThis, // explicitly call because Ares rewrote it reinterpret_cast(0x4571E0)(pThis, pInfiltratorHouse); - BuildingExt::ExtMap.Find(pThis)->HandleInfiltrate(pInfiltratorHouse, oldBalance); + BuildingExt::Fetch(pThis)->HandleInfiltrate(pInfiltratorHouse, oldBalance); } DEFINE_FUNCTION_JUMP(CALL, 0x51A00B, BuildingClass_InfiltratedBy_Wrapper); diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 8718223464..349f3068ab 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -2,98 +2,86 @@ #include #include -class BuildingExt +class BuildingExt final : public TechnoExt, public Detach::Listener { public: using base_type = BuildingClass; + using ExtData = BuildingExt; static constexpr DWORD Canary = 0x87654321; - // BuildingClassExtension is a leaf of the TechnoClass extension hierarchy: one extension - // per object, stored inline in the shared 0x18 slot and owned by the TechnoClass container. - class ExtData final : public TechnoExt::ExtData, public Detach::Listener + BuildingTypeExt* TypeExtData; + bool DeployedTechno; + bool IsCreatedFromMapFile; + int LimboID; + int GrindingWeapon_LastFiredFrame; + int GrindingWeapon_AccumulatedCredits; + BuildingClass* CurrentAirFactory; + int AccumulatedIncome; + std::optional CurrentLaserWeaponIndex; + int PoweredUpToLevel; // Distinct from UpgradeLevel, and set to highest PowersUpToLevel out of applied upgrades regardless of how many are currently applied to this building. + SuperClass* CurrentEMPulseSW; + bool IsFiringNow; + int TurretAnimIdleFrame; + int TurretAnimFiringFrame; + int TurretAnimRateTick; + + BuildingExt(BuildingClass* OwnerObject) : TechnoExt(OwnerObject) + , TypeExtData { nullptr } + , DeployedTechno { false } + , IsCreatedFromMapFile { false } + , LimboID { -1 } + , GrindingWeapon_LastFiredFrame { 0 } + , GrindingWeapon_AccumulatedCredits { 0 } + , CurrentAirFactory { nullptr } + , AccumulatedIncome { 0 } + , CurrentLaserWeaponIndex {} + , PoweredUpToLevel { 0 } + , CurrentEMPulseSW {} + , IsFiringNow { false } + , TurretAnimIdleFrame { 0 } + , TurretAnimFiringFrame { -1 } + , TurretAnimRateTick { 0 } + { } + + // typed owner accessor (shadows the TechnoClass one from the base) + BuildingClass* OwnerObject() const { - public: - BuildingTypeExt::ExtData* TypeExtData; - bool DeployedTechno; - bool IsCreatedFromMapFile; - int LimboID; - int GrindingWeapon_LastFiredFrame; - int GrindingWeapon_AccumulatedCredits; - BuildingClass* CurrentAirFactory; - int AccumulatedIncome; - std::optional CurrentLaserWeaponIndex; - int PoweredUpToLevel; // Distinct from UpgradeLevel, and set to highest PowersUpToLevel out of applied upgrades regardless of how many are currently applied to this building. - SuperClass* CurrentEMPulseSW; - bool IsFiringNow; - int TurretAnimIdleFrame; - int TurretAnimFiringFrame; - int TurretAnimRateTick; - - ExtData(BuildingClass* OwnerObject) : TechnoExt::ExtData(OwnerObject) - , TypeExtData { nullptr } - , DeployedTechno { false } - , IsCreatedFromMapFile { false } - , LimboID { -1 } - , GrindingWeapon_LastFiredFrame { 0 } - , GrindingWeapon_AccumulatedCredits { 0 } - , CurrentAirFactory { nullptr } - , AccumulatedIncome { 0 } - , CurrentLaserWeaponIndex {} - , PoweredUpToLevel { 0 } - , CurrentEMPulseSW {} - , IsFiringNow { false } - , TurretAnimIdleFrame { 0 } - , TurretAnimFiringFrame { -1 } - , TurretAnimRateTick { 0 } - { } - - // typed owner accessor (shadows the TechnoClass one from the base) - BuildingClass* OwnerObject() const - { - return static_cast(this->TechnoExt::ExtData::OwnerObject()); - } - - void DisplayIncomeString(); - void ApplyPoweredKillSpawns(); - bool HasSuperWeapon(int index) const; - bool HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore); - void UpdatePrimaryFactoryAI(); - virtual ~ExtData() = default; - - // virtual void LoadFromINIFile(CCINIClass* pINI) override; - - virtual void OnDetach(BuildingClass* pTarget, bool removed) override - { - if (removed) - AnnounceInvalidPointer(this->CurrentAirFactory, pTarget); - } - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - }; - - // BuildingClassExtension lives in the TechnoClass container (single 0x18 slot), so this - // is a thin typed accessor facade over that container instead of an owning container. - class ExtMapFacade + return static_cast(this->TechnoExt::OwnerObject()); + } + + void DisplayIncomeString(); + void ApplyPoweredKillSpawns(); + bool HasSuperWeapon(int index) const; + bool HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefore); + void UpdatePrimaryFactoryAI(); + virtual ~BuildingExt() = default; + + // virtual void LoadFromINIFile(CCINIClass* pINI) override; + + virtual void OnDetach(BuildingClass* pTarget, bool removed) override { - public: - ExtData* Find(const BuildingClass* key) const - { - return static_cast(TechnoExt::ExtMap.Find(key)); - } + if (removed) + AnnounceInvalidPointer(this->CurrentAirFactory, pTarget); + } + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - ExtData* TryFind(const BuildingClass* key) const - { - return static_cast(TechnoExt::ExtMap.TryFind(key)); - } - }; +private: + template + void Serialize(T& Stm); - static ExtMapFacade ExtMap; +public: + static BuildingExt* Fetch(const BuildingClass* pThis) + { + return static_cast(TechnoExt::Fetch(pThis)); + } + + static BuildingExt* TryFetch(const BuildingClass* pThis) + { + return static_cast(TechnoExt::TryFetch(pThis)); + } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -112,5 +100,3 @@ class BuildingExt static int GetTurretFrame(BuildingClass* pThis); }; -// top-level name for the BuildingClass extension leaf (derives from TechnoClassExtension) -using BuildingClassExtension = BuildingExt::ExtData; diff --git a/src/Ext/Building/Hooks.Grinding.cpp b/src/Ext/Building/Hooks.Grinding.cpp index f2f2a0b4e5..c97c566125 100644 --- a/src/Ext/Building/Hooks.Grinding.cpp +++ b/src/Ext/Building/Hooks.Grinding.cpp @@ -70,7 +70,7 @@ DEFINE_HOOK(0x51F0AF, InfantryClass_WhatAction_Grinding, 0x0) && pThis->Owner->IsControlledByCurrentPlayer() && !pBuilding->IsBeingWarpedOut() && pThis->Owner->IsAlliedWith(pTarget) - && (BuildingTypeExt::ExtMap.Find(pType)->Grinding_AllowAllies || action == Action::Select)) + && (BuildingTypeExt::Fetch(pType)->Grinding_AllowAllies || action == Action::Select)) { action = BuildingExt::CanGrindTechno(pBuilding, pThis) ? Action::Repair : Action::NoEnter; R->EBP(action); @@ -176,7 +176,7 @@ DEFINE_HOOK(0x519790, InfantryClass_PerCellProcess_SkipDieSoundBeforeGrinding, 0 enum { SkipVoiceDie = 0x51986A }; GET(BuildingClass*, pBuilding, EBX); - if (BuildingTypeExt::ExtMap.Find(pBuilding->Type)->Grinding_PlayDieSound.Get()) + if (BuildingTypeExt::Fetch(pBuilding->Type)->Grinding_PlayDieSound.Get()) return 0; return SkipVoiceDie; @@ -219,7 +219,7 @@ DEFINE_HOOK(0x739FBC, UnitClass_PerCellProcess_BeforeGrinding, 0x5) GET(BuildingClass*, pBuilding, EBX); GrinderRefundTemp::BalanceBefore = pBuilding->Owner->Balance; - if (BuildingTypeExt::ExtMap.Find(pBuilding->Type)->Grinding_PlayDieSound) + if (BuildingTypeExt::Fetch(pBuilding->Type)->Grinding_PlayDieSound) return 0; return SkipDieSound; diff --git a/src/Ext/Building/Hooks.Production.cpp b/src/Ext/Building/Hooks.Production.cpp index 2df3224ce1..af01c15d0b 100644 --- a/src/Ext/Building/Hooks.Production.cpp +++ b/src/Ext/Building/Hooks.Production.cpp @@ -10,7 +10,7 @@ DEFINE_HOOK(0x4401BB, BuildingClass_AI_PickWithFreeDocks, 0x6) const int index = pOwner->ProducingAircraftTypeIndex; auto const pType = index >= 0 ? AircraftTypeClass::Array.GetItem(index) : nullptr; - if (RulesExt::Global()->AllowParallelAIQueues && !RulesExt::Global()->ForbidParallelAIQueues_Aircraft && (!pType || !TechnoTypeExt::ExtMap.Find(pType)->ForbidParallelAIQueues)) + if (RulesExt::Global()->AllowParallelAIQueues && !RulesExt::Global()->ForbidParallelAIQueues_Aircraft && (!pType || !TechnoTypeExt::Fetch(pType)->ForbidParallelAIQueues)) return 0; if (pOwner->Type->MultiplayPassive @@ -23,7 +23,7 @@ DEFINE_HOOK(0x4401BB, BuildingClass_AI_PickWithFreeDocks, 0x6) if (pBuilding->Factory && !BuildingExt::HasFreeDocks(pBuilding)) { - if (auto const pBldExt = BuildingExt::ExtMap.TryFind(pBuilding)) + if (auto const pBldExt = BuildingExt::TryFetch(pBuilding)) pBldExt->UpdatePrimaryFactoryAI(); } } @@ -38,7 +38,7 @@ DEFINE_HOOK(0x4502F4, BuildingClass_Update_Factory_Phobos, 0x6) if (pOwner->Production && RulesExt::Global()->AllowParallelAIQueues) { - auto const pOwnerExt = HouseExt::ExtMap.Find(pOwner); + auto const pOwnerExt = HouseExt::Fetch(pOwner); auto const pFactory = pThis->Type->Factory; const bool naval = pThis->Type->Naval; BuildingClass** currFactory = nullptr; @@ -101,14 +101,14 @@ DEFINE_HOOK(0x4502F4, BuildingClass_Update_Factory_Phobos, 0x6) if (naval ? RulesExt::Global()->ForbidParallelAIQueues_Navy : RulesExt::Global()->ForbidParallelAIQueues_Vehicle) return Skip; - index = naval ? HouseExt::ExtMap.Find(pOwner)->ProducingNavalUnitTypeIndex : pOwner->ProducingUnitTypeIndex; + index = naval ? HouseExt::Fetch(pOwner)->ProducingNavalUnitTypeIndex : pOwner->ProducingUnitTypeIndex; pType = index >= 0 ? UnitTypeClass::Array.GetItem(index) : nullptr; break; default: break; } - if (pType && TechnoTypeExt::ExtMap.Find(pType)->ForbidParallelAIQueues) + if (pType && TechnoTypeExt::Fetch(pType)->ForbidParallelAIQueues) return Skip; } } @@ -138,9 +138,9 @@ DEFINE_HOOK(0x4CA07A, FactoryClass_AbandonProduction_Phobos, 0x8) if (!RulesExt::Global()->AllowParallelAIQueues) return 0; - auto const pOwnerExt = HouseExt::ExtMap.Find(pFactory->Owner); + auto const pOwnerExt = HouseExt::Fetch(pFactory->Owner); auto const pType = pTechno->GetTechnoType(); - const bool forbid = TechnoTypeExt::ExtMap.Find(pType)->ForbidParallelAIQueues; + const bool forbid = TechnoTypeExt::Fetch(pType)->ForbidParallelAIQueues; switch (pTechno->WhatAmI()) { @@ -180,7 +180,7 @@ DEFINE_HOOK(0x444119, BuildingClass_KickOutUnit_UnitType_Phobos, 0x6) GET(UnitClass*, pUnit, EDI); GET(BuildingClass*, pFactory, ESI); - auto const pHouseExt = HouseExt::ExtMap.Find(pFactory->Owner); + auto const pHouseExt = HouseExt::Fetch(pFactory->Owner); if (pUnit->Type->Naval && pHouseExt->Factory_NavyType == pFactory) pHouseExt->Factory_NavyType = nullptr; @@ -194,7 +194,7 @@ DEFINE_HOOK(0x444131, BuildingClass_KickOutUnit_InfantryType_Phobos, 0x6) { GET(BuildingClass*, pFactory, ESI); - auto const pHouseExt = HouseExt::ExtMap.Find(pFactory->Owner); + auto const pHouseExt = HouseExt::Fetch(pFactory->Owner); if (pHouseExt->Factory_InfantryType == pFactory) pHouseExt->Factory_InfantryType = nullptr; @@ -206,7 +206,7 @@ DEFINE_HOOK(0x44531F, BuildingClass_KickOutUnit_BuildingType_Phobos, 0xA) { GET(BuildingClass*, pFactory, ESI); - auto const pHouseExt = HouseExt::ExtMap.Find(pFactory->Owner); + auto const pHouseExt = HouseExt::Fetch(pFactory->Owner); if (pHouseExt->Factory_BuildingType == pFactory) pHouseExt->Factory_BuildingType = nullptr; @@ -218,7 +218,7 @@ DEFINE_HOOK(0x443CCA, BuildingClass_KickOutUnit_AircraftType_Phobos, 0xA) { GET(BuildingClass*, pFactory, ESI); - auto const pHouseExt = HouseExt::ExtMap.Find(pFactory->Owner); + auto const pHouseExt = HouseExt::Fetch(pFactory->Owner); if (pHouseExt->Factory_AircraftType == pFactory) pHouseExt->Factory_AircraftType = nullptr; diff --git a/src/Ext/Building/Hooks.Refinery.cpp b/src/Ext/Building/Hooks.Refinery.cpp index 667078e8f7..a0149167fa 100644 --- a/src/Ext/Building/Hooks.Refinery.cpp +++ b/src/Ext/Building/Hooks.Refinery.cpp @@ -25,7 +25,7 @@ DEFINE_HOOK(0x73E4D0, UnitClass_Mission_Unload_CheckBalanceAfter, 0xA) GET(HouseClass* const, pHouse, EBX); GET(BuildingClass* const, pDock, EDI); - if (auto const pBldExt = BuildingExt::ExtMap.TryFind(pDock)) + if (auto const pBldExt = BuildingExt::TryFetch(pDock)) { pBldExt->AccumulatedIncome += pHouse->Available_Money() - OwnerBalanceBefore::HarversterUnloads; } @@ -48,10 +48,10 @@ DEFINE_HOOK(0x522E4F, InfantryClass_SlaveGiveMoney_CheckBalanceAfter, 0x6) if (auto const pBld = abstract_cast(slaveMiner)) { - auto const pBldExt = BuildingExt::ExtMap.Find(pBld); + auto const pBldExt = BuildingExt::Fetch(pBld); pBldExt->AccumulatedIncome += money; } - else if (auto const pBldTypeExt = BuildingTypeExt::ExtMap.TryFind(slaveMiner->GetTechnoType()->DeploysInto)) + else if (auto const pBldTypeExt = BuildingTypeExt::TryFetch(slaveMiner->GetTechnoType()->DeploysInto)) { if (pBldTypeExt->DisplayIncome.Get(RulesExt::Global()->DisplayIncome.Get())) FlyingStrings::AddMoneyString(money, slaveMiner, slaveMiner->Owner, RulesExt::Global()->DisplayIncome_Houses.Get(), slaveMiner->Location); @@ -64,12 +64,12 @@ DEFINE_HOOK(0x445FE4, BuildingClass_Place_RefineryActiveAnim, 0x6) { GET(BuildingTypeClass*, pType, ESI); - return BuildingTypeExt::ExtMap.Find(pType)->Refinery_UseNormalActiveAnim ? 0x446183 : 0; + return BuildingTypeExt::Fetch(pType)->Refinery_UseNormalActiveAnim ? 0x446183 : 0; } DEFINE_HOOK(0x450DAA, BuildingClass_UpdateAnimations_RefineryActiveAnim, 0x6) { GET(BuildingTypeClass*, pType, EDX); - return BuildingTypeExt::ExtMap.Find(pType)->Refinery_UseNormalActiveAnim ? 0x450F9E : 0; + return BuildingTypeExt::Fetch(pType)->Refinery_UseNormalActiveAnim ? 0x450F9E : 0; } diff --git a/src/Ext/Building/Hooks.Selling.cpp b/src/Ext/Building/Hooks.Selling.cpp index c1e00a71da..012b6a69fd 100644 --- a/src/Ext/Building/Hooks.Selling.cpp +++ b/src/Ext/Building/Hooks.Selling.cpp @@ -14,7 +14,7 @@ DEFINE_HOOK(0x4D9F7B, FootClass_Sell, 0x6) if (pOwner->IsControlledByCurrentPlayer()) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; VoxClass::PlayIndex(pTypeExt->EVA_Sold.isset() ? pTypeExt->EVA_Sold.Get() : VoxClass::FindIndex(GameStrings::EVA_UnitSold)); //WW used VocClass::PlayGlobal to play the SellSound, why did they do that? VocClass::PlayAt(pTypeExt->SellSound.Get(RulesClass::Instance->SellSound), pThis->Location); @@ -46,7 +46,7 @@ bool __forceinline BuildingExt::CanUndeployOnSell(BuildingClass* pThis) } else { - const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + const auto pTypeExt = BuildingTypeExt::Fetch(pType); if (!pTypeExt->UndeploysInto_Sellable) return true; } @@ -69,7 +69,7 @@ DEFINE_HOOK(0x449CC1, BuildingClass_Mi_Selling_EVASold_UndeploysInto, 0x6) // Fix Conyards can't play EVA_StructureSold if (pThis->IsOwnedByCurrentPlayer && (!pThis->ArchiveTarget || !pType->UndeploysInto)) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); VoxClass::PlayIndex(pTypeExt->EVA_Sold.isset() ? pTypeExt->EVA_Sold.Get() : VoxClass::FindIndex(GameStrings::EVA_StructureSold)); } @@ -82,7 +82,7 @@ DEFINE_HOOK(0x44A7CF, BuildingClass_Mi_Selling_PlaySellSound, 0x6) GET(BuildingClass*, pThis, EBP); if (!BuildingExt::CanUndeployOnSell(pThis)) - VocClass::PlayAt(TechnoTypeExt::ExtMap.Find(pThis->Type)->SellSound.Get(RulesClass::Instance->SellSound), pThis->Location); + VocClass::PlayAt(TechnoTypeExt::Fetch(pThis->Type)->SellSound.Get(RulesClass::Instance->SellSound), pThis->Location); return FinishPlaying; } @@ -110,7 +110,7 @@ DEFINE_HOOK(0x44AB22, BuildingClass_Mi_Selling_EVASold_Plug, 0x6) GET(BuildingClass*, pThis, EBP); if (pThis->IsOwnedByCurrentPlayer) - VoxClass::PlayIndex(TechnoTypeExt::ExtMap.Find(pThis->Type)->EVA_Sold.Get(VoxClass::FindIndex(GameStrings::EVA_StructureSold))); + VoxClass::PlayIndex(TechnoTypeExt::Fetch(pThis->Type)->EVA_Sold.Get(VoxClass::FindIndex(GameStrings::EVA_StructureSold))); #endif return SkipVoxPlay; } diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index cfa1f4c648..84fd2fcc2a 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -13,10 +13,10 @@ DEFINE_HOOK(0x43FE69, BuildingClass_AI, 0xA) { GET(BuildingClass*, pThis, ESI); - const auto pBuildingExt = BuildingExt::ExtMap.Find(pThis); + const auto pBuildingExt = BuildingExt::Fetch(pThis); pBuildingExt->DisplayIncomeString(); - TechnoExt::ExtData* const pTechnoExt = pBuildingExt; // the building extension is a TechnoClassExtension + TechnoExt* const pTechnoExt = pBuildingExt; // the building extension is a TechnoExt pTechnoExt->UpdateLaserTrails(); // Mainly for on turret trails // Force airstrike targets to redraw every frame to account for tint intensity fluctuations. @@ -30,7 +30,7 @@ DEFINE_HOOK(0x43FBEF, BuildingClass_AI_PoweredKillSpawns, 0x6) { GET(BuildingClass*, pThis, ESI); - BuildingExt::ExtMap.Find(pThis)->ApplyPoweredKillSpawns(); + BuildingExt::Fetch(pThis)->ApplyPoweredKillSpawns(); return 0; } @@ -59,7 +59,7 @@ DEFINE_HOOK(0x4403D4, BuildingClass_AI_ChronoSparkle, 0x6) { if (!((Unsorted::CurrentFrame + i) % RulesExt::Global()->ChronoSparkleDisplayDelay)) { - auto const muzzleOffset = pType->MaxNumberOccupants <= 10 ? pType->MuzzleFlash[i] : BuildingTypeExt::ExtMap.Find(pType)->OccupierMuzzleFlashes.at(i); + auto const muzzleOffset = pType->MaxNumberOccupants <= 10 ? pType->MuzzleFlash[i] : BuildingTypeExt::Fetch(pType)->OccupierMuzzleFlashes.at(i); auto coords = CoordStruct::Empty; auto offset = TacticalClass::Instance->ApplyMatrix_Pixel(muzzleOffset); coords.X += offset.X; @@ -91,7 +91,7 @@ DEFINE_HOOK(0x443C81, BuildingClass_ExitObject_InitialClonedHealth, 0x7) { if (pBuilding && pBuilding->Type->Cloning) { - const double percentage = GeneralUtils::GetRangedRandomOrSingleValue(BuildingTypeExt::ExtMap.Find(pBuilding->Type)->InitialStrength_Cloning); + const double percentage = GeneralUtils::GetRangedRandomOrSingleValue(BuildingTypeExt::Fetch(pBuilding->Type)->InitialStrength_Cloning); const int health = pInf->Type->Strength; const int strength = Math::clamp(static_cast(health * percentage), 1, health); pInf->Health = strength; @@ -106,7 +106,7 @@ DEFINE_HOOK(0x449ADA, BuildingClass_MissionConstruction_DeployToFireFix, 0x0) { GET(BuildingClass*, pThis, ESI); - auto const pExt = BuildingExt::ExtMap.Find(pThis); + auto const pExt = BuildingExt::Fetch(pThis); if (pExt->DeployedTechno && pThis->LastTarget) { @@ -134,13 +134,13 @@ DEFINE_HOOK(0x44CEEC, BuildingClass_Mission_Missile_EMPulseSelectWeapon, 0x6) GET(BuildingClass*, pThis, ESI); - auto const pExt = BuildingExt::ExtMap.Find(pThis); + auto const pExt = BuildingExt::Fetch(pThis); if (!pExt->CurrentEMPulseSW) return 0; int weaponIndex = 0; - auto const pSWExt = SWTypeExt::ExtMap.Find(pExt->CurrentEMPulseSW->Type); + auto const pSWExt = SWTypeExt::Fetch(pExt->CurrentEMPulseSW->Type); auto const pOwner = pThis->Owner; if (pSWExt->EMPulse_WeaponIndex >= 0) @@ -164,7 +164,7 @@ DEFINE_HOOK(0x44CEEC, BuildingClass_Mission_Missile_EMPulseSelectWeapon, 0x6) if (pSWExt->EMPulse_SuspendOthers) { - auto const pHouseExt = HouseExt::ExtMap.Find(pOwner); + auto const pHouseExt = HouseExt::Fetch(pOwner); const int index = pExt->CurrentEMPulseSW->Type->ArrayIndex; if (pHouseExt->SuspendedEMPulseSWs.count(index)) @@ -289,7 +289,7 @@ DEFINE_HOOK(0x44FBBF, CreateBuildingFromINIFile_AfterCTOR_BeforeUnlimbo, 0x8) { GET(BuildingClass* const, pBld, ESI); - if (auto const pExt = BuildingExt::ExtMap.TryFind(pBld)) + if (auto const pExt = BuildingExt::TryFetch(pBld)) pExt->IsCreatedFromMapFile = true; return 0; @@ -304,11 +304,11 @@ DEFINE_HOOK(0x440B4F, BuildingClass_Unlimbo_SetShouldRebuild, 0x5) GET(BuildingClass* const, pThis, ESI); // Preplaced structures are already managed before - if (BuildingExt::ExtMap.Find(pThis)->IsCreatedFromMapFile) + if (BuildingExt::Fetch(pThis)->IsCreatedFromMapFile) return SkipSetShouldRebuild; // Per-house dehardcoding: BaseNodes + SW-Delivery - if (!HouseExt::ExtMap.Find(pThis->Owner)->RepairBaseNodes[GameOptionsClass::Instance.Difficulty].Get(RulesExt::Global()->RepairBaseNodes)) + if (!HouseExt::Fetch(pThis->Owner)->RepairBaseNodes[GameOptionsClass::Instance.Difficulty].Get(RulesExt::Global()->RepairBaseNodes)) return SkipSetShouldRebuild; } // Vanilla instruction: always repairable in other game modes @@ -319,7 +319,7 @@ DEFINE_HOOK(0x440EBB, BuildingClass_Unlimbo_NaturalParticleSystem_CampaignSkip, { enum { DoNotCreateParticle = 0x440F61 }; GET(BuildingClass* const, pThis, ESI); - return BuildingExt::ExtMap.Find(pThis)->IsCreatedFromMapFile ? DoNotCreateParticle : 0; + return BuildingExt::Fetch(pThis)->IsCreatedFromMapFile ? DoNotCreateParticle : 0; } DEFINE_HOOK(0x4519A2, BuildingClass_UpdateAnim_SetParentBuilding, 0x6) @@ -327,8 +327,8 @@ DEFINE_HOOK(0x4519A2, BuildingClass_UpdateAnim_SetParentBuilding, 0x6) GET(BuildingClass*, pThis, ESI); GET(AnimClass*, pAnim, EBP); - AnimExt::ExtMap.Find(pAnim)->ParentBuilding = pThis; - TechnoExt::ExtMap.Find(pThis)->AnimRefCount++; + AnimExt::Fetch(pAnim)->ParentBuilding = pThis; + TechnoExt::Fetch(pThis)->AnimRefCount++; return 0; } @@ -344,7 +344,7 @@ DEFINE_HOOK(0x43D6E5, BuildingClass_Draw_ZShapePointMove, 0x5) GET(BuildingClass*, pThis, ESI); - if (BuildingTypeExt::ExtMap.Find(pThis->Type)->ZShapePointMove_OnBuildup) + if (BuildingTypeExt::Fetch(pThis->Type)->ZShapePointMove_OnBuildup) return Apply; return Skip; @@ -356,7 +356,7 @@ DEFINE_HOOK(0x4511D6, BuildingClass_AnimationAI_SellBuildup, 0x7) GET(BuildingClass*, pThis, ESI); - return BuildingTypeExt::ExtMap.Find(pThis->Type)->SellBuildupLength == pThis->Animation.Value ? Continue : Skip; + return BuildingTypeExt::Fetch(pThis->Type)->SellBuildupLength == pThis->Animation.Value ? Continue : Skip; } #pragma region PowerPlantEnhancer @@ -364,8 +364,8 @@ DEFINE_HOOK(0x4511D6, BuildingClass_AnimationAI_SellBuildup, 0x7) DEFINE_HOOK(0x441553, BuildingClass_Unlimbo_AddOwned, 0x6) { GET(BuildingClass*, pThis, ESI); - const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); - const auto pOwnerExt = HouseExt::ExtMap.Find(pThis->Owner); + const auto pTypeExt = BuildingTypeExt::Fetch(pThis->Type); + const auto pOwnerExt = HouseExt::Fetch(pThis->Owner); if (!pTypeExt->PowerPlantEnhancer_Buildings.empty() && (pTypeExt->PowerPlantEnhancer_Amount != 0 || pTypeExt->PowerPlantEnhancer_Factor != 1.0f)) pOwnerExt->PowerPlantEnhancers.push_back(pThis); @@ -377,8 +377,8 @@ DEFINE_HOOK(0x448A78, BuildingClass_SetOwningHouse_RemoveOwned, 0x6) { GET(BuildingClass*, pThis, ESI); GET(HouseClass*, pOwner, EBX); - const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); - const auto pOwnerExt = HouseExt::ExtMap.Find(pOwner); + const auto pTypeExt = BuildingTypeExt::Fetch(pThis->Type); + const auto pOwnerExt = HouseExt::Fetch(pOwner); if (!pTypeExt->PowerPlantEnhancer_Buildings.empty() && (pTypeExt->PowerPlantEnhancer_Amount != 0 || pTypeExt->PowerPlantEnhancer_Factor != 1.0f)) { @@ -393,8 +393,8 @@ DEFINE_HOOK(0x449197, BuildingClass_SetOwningHouse_AddOwned, 0x6) { GET(BuildingClass*, pThis, ESI); GET(HouseClass*, pNewOwner, EBP); - const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); - const auto pNewOwnerExt = HouseExt::ExtMap.Find(pNewOwner); + const auto pTypeExt = BuildingTypeExt::Fetch(pThis->Type); + const auto pNewOwnerExt = HouseExt::Fetch(pNewOwner); if (!pTypeExt->PowerPlantEnhancer_Buildings.empty() && (pTypeExt->PowerPlantEnhancer_Amount != 0 || pTypeExt->PowerPlantEnhancer_Factor != 1.0f)) pNewOwnerExt->PowerPlantEnhancers.push_back(pThis); @@ -412,11 +412,11 @@ DEFINE_HOOK(0x441501, BuildingClass_Unlimbo_FactoryPlant, 0x6) GET(BuildingClass*, pThis, ESI); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 || pTypeExt->FactoryPlant_DisallowTypes.size() > 0) { - auto const pHouseExt = HouseExt::ExtMap.Find(pThis->Owner); + auto const pHouseExt = HouseExt::Fetch(pThis->Owner); pHouseExt->RestrictedFactoryPlants.push_back(pThis); return Skip; @@ -431,11 +431,11 @@ DEFINE_HOOK(0x448A31, BuildingClass_Captured_FactoryPlant1, 0x6) GET(BuildingClass*, pThis, ESI); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 || pTypeExt->FactoryPlant_DisallowTypes.size() > 0) { - auto const pHouseExt = HouseExt::ExtMap.Find(pThis->Owner); + auto const pHouseExt = HouseExt::Fetch(pThis->Owner); if (!pHouseExt->RestrictedFactoryPlants.empty()) { @@ -455,13 +455,13 @@ DEFINE_HOOK(0x449149, BuildingClass_Captured_FactoryPlant2, 0x6) GET(BuildingClass*, pThis, ESI); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); if (pTypeExt->FactoryPlant_AllowTypes.size() > 0 || pTypeExt->FactoryPlant_DisallowTypes.size() > 0) { GET(HouseClass*, pNewOwner, EBP); - auto const pHouseExt = HouseExt::ExtMap.Find(pNewOwner); + auto const pHouseExt = HouseExt::Fetch(pNewOwner); pHouseExt->RestrictedFactoryPlants.push_back(pThis); return Skip; @@ -502,7 +502,7 @@ DEFINE_HOOK(0x440D01, BuildingClass_Unlimbo_DestroyableObstacle, 0x6) { GET(BuildingClass*, pThis, ESI); - if (BuildingTypeExt::ExtMap.Find(pThis->Type)->IsDestroyableObstacle) + if (BuildingTypeExt::Fetch(pThis->Type)->IsDestroyableObstacle) RecalculateCells(pThis); return 0; @@ -512,7 +512,7 @@ DEFINE_HOOK(0x445D87, BuildingClass_Limbo_DestroyableObstacle, 0x6) { GET(BuildingClass*, pThis, ESI); - if (BuildingTypeExt::ExtMap.Find(pThis->Type)->IsDestroyableObstacle) + if (BuildingTypeExt::Fetch(pThis->Type)->IsDestroyableObstacle) RecalculateCells(pThis); // only remove animation when the building is destroyed or sold @@ -537,7 +537,7 @@ DEFINE_HOOK(0x483D8E, CellClass_CheckPassability_DestroyableObstacle, 0x6) GET(BuildingClass*, pBuilding, ESI); - if (BuildingTypeExt::ExtMap.Find(pBuilding->Type)->IsDestroyableObstacle) + if (BuildingTypeExt::Fetch(pBuilding->Type)->IsDestroyableObstacle) return IsBlockage; return 0; @@ -558,7 +558,7 @@ DEFINE_HOOK(0x44C836, BuildingClass_Mission_Repair_UnitReload, 0x6) if (pThis->Type->UnitReload) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); if (pTypeExt->Units_RepairRate.isset()) { @@ -596,7 +596,7 @@ DEFINE_HOOK(0x44B8F1, BuildingClass_Mission_Repair_Hospital, 0x6) GET(BuildingClass*, pThis, EBP); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); const double repairRate = pTypeExt->Units_RepairRate.Get(RulesClass::Instance->IRepairRate); __asm { fld repairRate } @@ -609,7 +609,7 @@ DEFINE_HOOK(0x44BD38, BuildingClass_Mission_Repair_UnitRepair, 0x6) GET(BuildingClass*, pThis, EBP); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); const double repairRate = pTypeExt->Units_RepairRate.Get(RulesClass::Instance->URepairRate); __asm { fld repairRate } @@ -626,7 +626,7 @@ DEFINE_HOOK(0x6F4D1A, TechnoClass_ReceiveCommand_Repair, 0x5) if (auto const pBuilding = abstract_cast(pFrom)) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pBuilding->Type); if (pBuilding->Type->UnitReload && pTypeExt->Units_RepairRate.isset() && !UnitRepairTemp::SeparateRepair) return SkipEffects; @@ -770,7 +770,7 @@ DEFINE_HOOK(0x44EFD8, BuildingClass_FindExitCell_BarracksExitCell, 0x6) GET(BuildingClass*, pThis, EBX); REF_STACK(CellStruct, resultCell, STACK_OFFSET(0x30, -0x20)); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); if (pTypeExt->BarracksExitCell.isset()) { @@ -807,7 +807,7 @@ DEFINE_HOOK(0x444B83, BuildingClass_ExitObject_BarracksExitCell, 0x7) REF_STACK(CoordStruct, resultCoords, STACK_OFFSET(0x140, -0x108)); auto const pType = pThis->Type; - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); if (pTypeExt->BarracksExitCell.isset()) { @@ -825,7 +825,7 @@ DEFINE_HOOK(0x54BC99, JumpjetLocomotionClass_Ascending_BarracksExitCell, 0x6) GET(BuildingTypeClass*, pType, EAX); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); if (pTypeExt->BarracksExitCell.isset()) return Continue; @@ -841,7 +841,7 @@ DEFINE_HOOK(0x44B630, BuildingClass_MissionAttack_AnimDelayedFire, 0x6) { enum { JustFire = 0x44B6C4, VanillaCheck = 0 }; GET(BuildingClass* const, pThis, ESI); - return (pThis->CurrentBurstIndex != 0 && !BuildingTypeExt::ExtMap.Find(pThis->Type)->IsAnimDelayedBurst) ? JustFire : VanillaCheck; + return (pThis->CurrentBurstIndex != 0 && !BuildingTypeExt::Fetch(pThis->Type)->IsAnimDelayedBurst) ? JustFire : VanillaCheck; } #pragma endregion @@ -900,11 +900,11 @@ DEFINE_HOOK(0x4400F9, BuildingClass_AI_UpdateOverpower, 0x6) continue; } - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWeapon->Warhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWeapon->Warhead); overPower += pWHExt->ElectricAssaultLevel; } - const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + const auto pBuildingTypeExt = BuildingTypeExt::Fetch(pThis->Type); const int charge = pBuildingTypeExt->Overpower_ChargeWeapon; if (charge >= 0) @@ -932,7 +932,7 @@ DEFINE_HOOK(0x4555E4, BuildingClass_IsPowerOnline_Overpower, 0x6) return R->Origin() == 0x4555E4 ? Continue1 : Continue2; GET(BuildingClass*, pThis, ESI); - const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + const auto pBuildingTypeExt = BuildingTypeExt::Fetch(pThis->Type); const int keepOnline = pBuildingTypeExt->Overpower_KeepOnline; if (keepOnline < 0) @@ -946,7 +946,7 @@ DEFINE_HOOK(0x4555E4, BuildingClass_IsPowerOnline_Overpower, 0x6) if (pWeapon && pWeapon->Warhead) { - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWeapon->Warhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWeapon->Warhead); overPower += pWHExt->ElectricAssaultLevel; } } @@ -986,7 +986,7 @@ DEFINE_HOOK(0x4485DB, BuildingClass_SetOwningHouse_SyncLinkedOwner, 0x6) { enum { SkipGameCode = 0x4486C8 }; GET(BuildingClass*, pThis, ESI); - return BuildingTypeExt::ExtMap.Find(pThis->Type)->BuildingRadioLink_SyncOwner.Get(RulesExt::Global()->BuildingRadioLink_SyncOwner) ? 0 : SkipGameCode; + return BuildingTypeExt::Fetch(pThis->Type)->BuildingRadioLink_SyncOwner.Get(RulesExt::Global()->BuildingRadioLink_SyncOwner) ? 0 : SkipGameCode; } #pragma region PrefiringMark @@ -994,14 +994,14 @@ DEFINE_HOOK(0x4485DB, BuildingClass_SetOwningHouse_SyncLinkedOwner, 0x6) DEFINE_HOOK(0x440045, BuildingClass_UpdateDelayedFiring_PrefiringMark1, 0x6) { GET(BuildingClass*, pThis, ESI); - BuildingExt::ExtMap.Find(pThis)->IsFiringNow = (int)pThis->PrismStage && pThis->DelayBeforeFiring <= 1; + BuildingExt::Fetch(pThis)->IsFiringNow = (int)pThis->PrismStage && pThis->DelayBeforeFiring <= 1; return 0; } DEFINE_HOOK(0x4400F9, BuildingClass_UpdateDelayedFiring_PrefiringMar2, 0x7) { GET(BuildingClass*, pThis, ESI); - BuildingExt::ExtMap.Find(pThis)->IsFiringNow = false; + BuildingExt::Fetch(pThis)->IsFiringNow = false; return 0; } @@ -1076,7 +1076,7 @@ DEFINE_HOOK(0x45670D, BuildingClass_GetRadialIndicatorRange_Extras, 0x7) enum { ApplyRange = 0x45674B, ApplyTurretWeapon = 0x456714 }; GET(BuildingClass*, pThis, ESI); - const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = BuildingTypeExt::Fetch(pThis->Type); if (!pTypeExt->PowerPlantEnhancer_Buildings.empty() && (pTypeExt->PowerPlantEnhancer_Amount != 0 || pTypeExt->PowerPlantEnhancer_Factor != 1.0f)) { @@ -1099,7 +1099,7 @@ static int HandleArmedBuildingGuard(BuildingClass* pThis) // but kept them here just in case removing them would break something. if (pType->EMPulseCannon || pThis->FirstActiveSWIdx() >= 0 || (pType->CanBeOccupied && pThis->Occupants.Count <= 0) || !pThis->Target) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); auto const& delay = pTypeExt->GuardRetryDelay.isset() ? pTypeExt->GuardRetryDelay : RulesExt::Global()->BuildingGuardRetryDelay; if (delay.isset()) @@ -1155,7 +1155,7 @@ DEFINE_HOOK(0x44B6C7, BuildingClass_Mission_Attack_TurretAnim, 0x6) { if (auto const pAnim = pThis->Anims[(int)BuildingAnimSlot::Turret]) { - auto const pExt = BuildingExt::ExtMap.Find(pThis); + auto const pExt = BuildingExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; const bool isLowPower = !pThis->StuffEnabled || !pThis->IsPowerOnline(); const int firingFrames = isLowPower ? pTypeExt->TurretAnim_LowPowerFiringFrames : pTypeExt->TurretAnim_FiringFrames; @@ -1294,7 +1294,7 @@ DEFINE_HOOK(0x44C976, BuildingClass_Mission_Repair_TankBunker, 0x5) auto const pType = pThis->Type; if (pType->Bunker && (pThis->TankBunkerState > TankBunkerState::Idle && pThis->TankBunkerState < TankBunkerState::Bunkered)) - R->EAX(BuildingTypeExt::ExtMap.Find(pType)->BunkerStateUpdateDelay.Get(RulesExt::Global()->BunkerStateUpdateDelay)); + R->EAX(BuildingTypeExt::Fetch(pType)->BunkerStateUpdateDelay.Get(RulesExt::Global()->BunkerStateUpdateDelay)); return 0; } diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 2733f3ef43..b4158c888d 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -3,10 +3,9 @@ #include #include -BuildingTypeExt::ExtMapFacade BuildingTypeExt::ExtMap; // Assuming SuperWeapon & SuperWeapon2 are used (for the moment) -int BuildingTypeExt::ExtData::GetSuperWeaponCount() const +int BuildingTypeExt::GetSuperWeaponCount() const { // The user should only use SuperWeapon and SuperWeapon2 if the attached sw count isn't bigger than 2 const auto pThis = this->OwnerObject(); @@ -15,13 +14,13 @@ int BuildingTypeExt::ExtData::GetSuperWeaponCount() const return count + this->SuperWeapons.size(); } -int BuildingTypeExt::ExtData::GetSuperWeaponIndex(const int index, HouseClass* pHouse) const +int BuildingTypeExt::GetSuperWeaponIndex(const int index, HouseClass* pHouse) const { const int idxSW = this->GetSuperWeaponIndex(index); if (const auto pSuper = pHouse->Supers.GetItemOrDefault(idxSW)) { - const auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pExt = SWTypeExt::Fetch(pSuper->Type); if (!pExt->IsAvailable(pHouse)) return -1; @@ -30,7 +29,7 @@ int BuildingTypeExt::ExtData::GetSuperWeaponIndex(const int index, HouseClass* p return idxSW; } -int BuildingTypeExt::ExtData::GetSuperWeaponIndex(const int index) const +int BuildingTypeExt::GetSuperWeaponIndex(const int index) const { const auto pThis = this->OwnerObject(); @@ -45,7 +44,7 @@ int BuildingTypeExt::ExtData::GetSuperWeaponIndex(const int index) const std::pair BuildingTypeExt::GetEnhancedPower(BuildingTypeClass* pBuilding, int output, HouseClass* pHouse, BuildingClass* pPowerPlant) { - const auto pHouseExt = HouseExt::ExtMap.Find(pHouse); + const auto pHouseExt = HouseExt::Fetch(pHouse); int amount = 0; float factor = 1.0f; std::map applied; // index, count @@ -56,7 +55,7 @@ std::pair BuildingTypeExt::GetEnhancedPower(BuildingTypeClass* pBuildi continue; const auto pEnhancerType = pEnhancer->Type; - const auto pEnhancerTypeExt = BuildingTypeExt::ExtMap.Find(pEnhancerType); + const auto pEnhancerTypeExt = BuildingTypeExt::Fetch(pEnhancerType); if (!pEnhancerTypeExt->PowerPlantEnhancer_Buildings.Contains(pBuilding)) continue; @@ -86,7 +85,7 @@ std::pair BuildingTypeExt::GetEnhancedPower(BuildingTypeClass* pBuildi void BuildingTypeExt::PlayBunkerSound(BuildingClass const* pThis, bool buildUp) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis->Type); auto const nSound = buildUp ? pTypeExt->BunkerWallsUpSound.Get(RulesClass::Instance->BunkerWallsUpSound) : pTypeExt->BunkerWallsDownSound.Get(RulesClass::Instance->BunkerWallsDownSound); @@ -140,24 +139,24 @@ int BuildingTypeExt::GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* checkUpgrade(pTPowersUp); }*/ - for (auto const pTPowersUp : BuildingTypeExt::ExtMap.Find(pBuilding)->PowersUp_Buildings) + for (auto const pTPowersUp : BuildingTypeExt::Fetch(pBuilding)->PowersUp_Buildings) checkUpgrade(pTPowersUp); return isUpgrade ? result : -1; } -void BuildingTypeExt::ExtData::Initialize() +void BuildingTypeExt::Initialize() { - TechnoTypeExt::ExtData::Initialize(); + TechnoTypeExt::Initialize(); } // ============================= // load / save -void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void BuildingTypeExt::LoadFromINIFile(CCINIClass* const pINI) { - TechnoTypeExt::ExtData::LoadFromINIFile(pINI); + TechnoTypeExt::LoadFromINIFile(pINI); auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -333,14 +332,14 @@ void BuildingTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->UnitSell.Read(exINI, pSection, "UnitSell"); } -void BuildingTypeExt::ExtData::CompleteInitialization() +void BuildingTypeExt::CompleteInitialization() { auto const pThis = this->OwnerObject(); UNREFERENCED_PARAMETER(pThis); } template -void BuildingTypeExt::ExtData::Serialize(T& Stm) +void BuildingTypeExt::Serialize(T& Stm) { Stm .Process(this->PowersUp_Owner) @@ -435,15 +434,15 @@ void BuildingTypeExt::ExtData::Serialize(T& Stm) ; } -void BuildingTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void BuildingTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - TechnoTypeExt::ExtData::LoadFromStream(Stm); + TechnoTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void BuildingTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void BuildingTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - TechnoTypeExt::ExtData::SaveToStream(Stm); + TechnoTypeExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -469,8 +468,8 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) { GET(BuildingTypeClass*, pItem, EAX); - // A building type's extension is a concrete BuildingTypeClassExtension leaf, owned by the TechnoTypeClass container. - TechnoTypeExt::ExtMap.Adopt(new BuildingTypeExt::ExtData(pItem)); + // A building type's extension is a concrete BuildingTypeExt leaf, owned by the TechnoTypeClass container. + TechnoTypeExt::ExtMap.Adopt(new BuildingTypeExt(pItem)); return 0; } diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 8c24584bd8..8d9024a9ae 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -3,261 +3,253 @@ #include #include -class BuildingTypeExt +class BuildingTypeExt final : public TechnoTypeExt { public: using base_type = BuildingTypeClass; + using ExtData = BuildingTypeExt; static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0x18; - // BuildingTypeClassExtension is a leaf of the TechnoTypeClass extension hierarchy. - class ExtData final : public TechnoTypeExt::ExtData - { - public: - Valueable PowersUp_Owner; - ValueableVector PowersUp_Buildings; - ValueableIdxVector SuperWeapons; - - Valueable PowerPlant_DamageFactor; - ValueableVector PowerPlantEnhancer_Buildings; - Valueable PowerPlantEnhancer_Range; - Valueable PowerPlantEnhancer_Amount; - Nullable PowerPlantEnhancer_Factor; - Valueable PowerPlantEnhancer_MaxCount; - - std::vector OccupierMuzzleFlashes; - Valueable Powered_KillSpawns; - Valueable CanC4_AllowZeroDamage; - Valueable Refinery_UseStorage; - Valueable> InitialStrength_Cloning; - Valueable Cloning_Powered { true }; - Valueable ExcludeFromMultipleFactoryBonus; - - ValueableIdx Grinding_Sound; - Valueable Grinding_Weapon; - Valueable Grinding_Weapon_RequiredCredits; - ValueableVector Grinding_AllowTypes; - ValueableVector Grinding_DisallowTypes; - Valueable Grinding_AllowAllies; - Valueable Grinding_AllowOwner; - Valueable Grinding_PlayDieSound; - - Nullable DisplayIncome; - Nullable DisplayIncome_Delay; - Nullable DisplayIncome_Houses; - Valueable DisplayIncome_Offset; - - Valueable PlacementPreview; - TheaterSpecificSHP PlacementPreview_Shape; - Nullable PlacementPreview_ShapeFrame; - Valueable PlacementPreview_Offset; - Valueable PlacementPreview_Remap; - CustomPalette PlacementPreview_Palette; - Nullable PlacementPreview_Translucency; - - Valueable SpyEffect_Custom; - ValueableIdx SpyEffect_VictimSuperWeapon; - ValueableIdx SpyEffect_InfiltratorSuperWeapon; - - Nullable ConsideredVehicle; - Valueable ZShapePointMove_OnBuildup; - Valueable SellBuildupLength; - Valueable IsDestroyableObstacle; - - Valueable IsAnimDelayedBurst; - - std::vector> AircraftDockingDirs; - - ValueableVector FactoryPlant_AllowTypes; - ValueableVector FactoryPlant_DisallowTypes; - Valueable FactoryPlant_MaxCount; - - Nullable Units_RepairRate; - Nullable Units_RepairStep; - Nullable Units_RepairPercent; - Nullable Units_UseRepairCost; - - Valueable NoBuildAreaOnBuildup; - ValueableVector Adjacent_Allowed; - ValueableVector Adjacent_Disallowed; - Valueable Adjacent_Disallowed_Prohibit; - Valueable Adjacent_Disallowed_ProhibitDistance; - - Nullable BarracksExitCell; - - Valueable Overpower_KeepOnline; - Valueable Overpower_ChargeWeapon; - - Valueable DisableDamageSound; - Nullable BuildingOccupyDamageMult; - Nullable BuildingOccupyROFMult; - Nullable BuildingBunkerDamageMult; - Nullable BuildingBunkerROFMult; - NullableIdx BunkerWallsUpSound; - NullableIdx BunkerWallsDownSound; - Nullable BunkerStateUpdateDelay; - - NullableIdx BuildingRepairedSound; - - Valueable Refinery_UseNormalActiveAnim; - - ValueableVector HasPowerUpAnim; - - Valueable UndeploysInto_Sellable; - - Nullable BuildingRadioLink_SyncOwner; - - Nullable> GuardRetryDelay; - - Valueable TurretAnim_IdleFrames; - Valueable TurretAnim_LowPowerIdleFrames; - Valueable TurretAnim_FiringFrames; - Valueable TurretAnim_LowPowerFiringFrames; - Valueable TurretAnim_IdleRate; - Valueable TurretAnim_FiringRate; +public: + Valueable PowersUp_Owner; + ValueableVector PowersUp_Buildings; + ValueableIdxVector SuperWeapons; + + Valueable PowerPlant_DamageFactor; + ValueableVector PowerPlantEnhancer_Buildings; + Valueable PowerPlantEnhancer_Range; + Valueable PowerPlantEnhancer_Amount; + Nullable PowerPlantEnhancer_Factor; + Valueable PowerPlantEnhancer_MaxCount; + + std::vector OccupierMuzzleFlashes; + Valueable Powered_KillSpawns; + Valueable CanC4_AllowZeroDamage; + Valueable Refinery_UseStorage; + Valueable> InitialStrength_Cloning; + Valueable Cloning_Powered { true }; + Valueable ExcludeFromMultipleFactoryBonus; + + ValueableIdx Grinding_Sound; + Valueable Grinding_Weapon; + Valueable Grinding_Weapon_RequiredCredits; + ValueableVector Grinding_AllowTypes; + ValueableVector Grinding_DisallowTypes; + Valueable Grinding_AllowAllies; + Valueable Grinding_AllowOwner; + Valueable Grinding_PlayDieSound; + + Nullable DisplayIncome; + Nullable DisplayIncome_Delay; + Nullable DisplayIncome_Houses; + Valueable DisplayIncome_Offset; + + Valueable PlacementPreview; + TheaterSpecificSHP PlacementPreview_Shape; + Nullable PlacementPreview_ShapeFrame; + Valueable PlacementPreview_Offset; + Valueable PlacementPreview_Remap; + CustomPalette PlacementPreview_Palette; + Nullable PlacementPreview_Translucency; + + Valueable SpyEffect_Custom; + ValueableIdx SpyEffect_VictimSuperWeapon; + ValueableIdx SpyEffect_InfiltratorSuperWeapon; + + Nullable ConsideredVehicle; + Valueable ZShapePointMove_OnBuildup; + Valueable SellBuildupLength; + Valueable IsDestroyableObstacle; + + Valueable IsAnimDelayedBurst; + + std::vector> AircraftDockingDirs; + + ValueableVector FactoryPlant_AllowTypes; + ValueableVector FactoryPlant_DisallowTypes; + Valueable FactoryPlant_MaxCount; + + Nullable Units_RepairRate; + Nullable Units_RepairStep; + Nullable Units_RepairPercent; + Nullable Units_UseRepairCost; + + Valueable NoBuildAreaOnBuildup; + ValueableVector Adjacent_Allowed; + ValueableVector Adjacent_Disallowed; + Valueable Adjacent_Disallowed_Prohibit; + Valueable Adjacent_Disallowed_ProhibitDistance; + + Nullable BarracksExitCell; + + Valueable Overpower_KeepOnline; + Valueable Overpower_ChargeWeapon; + + Valueable DisableDamageSound; + Nullable BuildingOccupyDamageMult; + Nullable BuildingOccupyROFMult; + Nullable BuildingBunkerDamageMult; + Nullable BuildingBunkerROFMult; + NullableIdx BunkerWallsUpSound; + NullableIdx BunkerWallsDownSound; + Nullable BunkerStateUpdateDelay; + + NullableIdx BuildingRepairedSound; + + Valueable Refinery_UseNormalActiveAnim; + + ValueableVector HasPowerUpAnim; + + Valueable UndeploysInto_Sellable; + + Nullable BuildingRadioLink_SyncOwner; + + Nullable> GuardRetryDelay; + + Valueable TurretAnim_IdleFrames; + Valueable TurretAnim_LowPowerIdleFrames; + Valueable TurretAnim_FiringFrames; + Valueable TurretAnim_LowPowerFiringFrames; + Valueable TurretAnim_IdleRate; + Valueable TurretAnim_FiringRate; + + // Ares 0.2 + Valueable CloningFacility; + + // Ares 0.A + Valueable RubbleIntact; + Valueable RubbleIntactRemove; + + // Ares 3.0 + Nullable UnitSell; + + BuildingTypeExt(BuildingTypeClass* OwnerObject) : TechnoTypeExt(OwnerObject) + , PowersUp_Owner { AffectedHouse::Owner } + , PowersUp_Buildings {} + , PowerPlant_DamageFactor { 1.0 } + , PowerPlantEnhancer_Buildings {} + , PowerPlantEnhancer_Range { Leptons(0) } + , PowerPlantEnhancer_Amount { 0 } + , PowerPlantEnhancer_Factor { 1.0f } + , PowerPlantEnhancer_MaxCount { -1 } + , OccupierMuzzleFlashes() + , Powered_KillSpawns { false } + , CanC4_AllowZeroDamage { false } + , InitialStrength_Cloning { { 1.0 } } + , ExcludeFromMultipleFactoryBonus { false } + , Refinery_UseStorage { false } + , Grinding_AllowAllies { false } + , Grinding_AllowOwner { true } + , Grinding_AllowTypes {} + , Grinding_DisallowTypes {} + , Grinding_Sound {} + , Grinding_PlayDieSound { true } + , Grinding_Weapon {} + , Grinding_Weapon_RequiredCredits { 0 } + , DisplayIncome { } + , DisplayIncome_Delay { } + , DisplayIncome_Houses { } + , DisplayIncome_Offset { { 0,0 } } + , PlacementPreview { true } + , PlacementPreview_Shape {} + , PlacementPreview_ShapeFrame {} + , PlacementPreview_Remap { true } + , PlacementPreview_Offset { {0,-15,1} } + , PlacementPreview_Palette {} + , PlacementPreview_Translucency {} + , SpyEffect_Custom { false } + , SpyEffect_VictimSuperWeapon {} + , SpyEffect_InfiltratorSuperWeapon {} + , ConsideredVehicle {} + , ZShapePointMove_OnBuildup { false } + , SellBuildupLength { 23 } + , AircraftDockingDirs {} + , FactoryPlant_AllowTypes {} + , FactoryPlant_DisallowTypes {} + , FactoryPlant_MaxCount { -1 } + , IsAnimDelayedBurst { true } + , IsDestroyableObstacle { false } + , Units_RepairRate {} + , Units_RepairStep {} + , Units_RepairPercent {} + , Units_UseRepairCost {} + , NoBuildAreaOnBuildup { false } + , Adjacent_Allowed {} + , Adjacent_Disallowed {} + , Adjacent_Disallowed_Prohibit { false } + , Adjacent_Disallowed_ProhibitDistance { 0 } + , BarracksExitCell {} + , Overpower_KeepOnline { 2 } + , Overpower_ChargeWeapon { 1 } + , DisableDamageSound { false } + , BuildingOccupyDamageMult {} + , BuildingOccupyROFMult {} + , BuildingBunkerDamageMult {} + , BuildingBunkerROFMult {} + , BunkerWallsUpSound {} + , BunkerWallsDownSound {} + , BunkerStateUpdateDelay {} + , BuildingRepairedSound {} + , Refinery_UseNormalActiveAnim { false } + , HasPowerUpAnim {} + , UndeploysInto_Sellable { false } + , BuildingRadioLink_SyncOwner {} + , GuardRetryDelay {} + , TurretAnim_IdleFrames { 1 } + , TurretAnim_LowPowerIdleFrames { 0 } + , TurretAnim_FiringFrames { 0 } + , TurretAnim_LowPowerFiringFrames { 0 } + , TurretAnim_IdleRate { 1 } + , TurretAnim_FiringRate { 1 } // Ares 0.2 - Valueable CloningFacility; + , CloningFacility { false } // Ares 0.A - Valueable RubbleIntact; - Valueable RubbleIntactRemove; + , RubbleIntact { nullptr } + , RubbleIntactRemove { false } // Ares 3.0 - Nullable UnitSell; - - ExtData(BuildingTypeClass* OwnerObject) : TechnoTypeExt::ExtData(OwnerObject) - , PowersUp_Owner { AffectedHouse::Owner } - , PowersUp_Buildings {} - , PowerPlant_DamageFactor { 1.0 } - , PowerPlantEnhancer_Buildings {} - , PowerPlantEnhancer_Range { Leptons(0) } - , PowerPlantEnhancer_Amount { 0 } - , PowerPlantEnhancer_Factor { 1.0f } - , PowerPlantEnhancer_MaxCount { -1 } - , OccupierMuzzleFlashes() - , Powered_KillSpawns { false } - , CanC4_AllowZeroDamage { false } - , InitialStrength_Cloning { { 1.0 } } - , ExcludeFromMultipleFactoryBonus { false } - , Refinery_UseStorage { false } - , Grinding_AllowAllies { false } - , Grinding_AllowOwner { true } - , Grinding_AllowTypes {} - , Grinding_DisallowTypes {} - , Grinding_Sound {} - , Grinding_PlayDieSound { true } - , Grinding_Weapon {} - , Grinding_Weapon_RequiredCredits { 0 } - , DisplayIncome { } - , DisplayIncome_Delay { } - , DisplayIncome_Houses { } - , DisplayIncome_Offset { { 0,0 } } - , PlacementPreview { true } - , PlacementPreview_Shape {} - , PlacementPreview_ShapeFrame {} - , PlacementPreview_Remap { true } - , PlacementPreview_Offset { {0,-15,1} } - , PlacementPreview_Palette {} - , PlacementPreview_Translucency {} - , SpyEffect_Custom { false } - , SpyEffect_VictimSuperWeapon {} - , SpyEffect_InfiltratorSuperWeapon {} - , ConsideredVehicle {} - , ZShapePointMove_OnBuildup { false } - , SellBuildupLength { 23 } - , AircraftDockingDirs {} - , FactoryPlant_AllowTypes {} - , FactoryPlant_DisallowTypes {} - , FactoryPlant_MaxCount { -1 } - , IsAnimDelayedBurst { true } - , IsDestroyableObstacle { false } - , Units_RepairRate {} - , Units_RepairStep {} - , Units_RepairPercent {} - , Units_UseRepairCost {} - , NoBuildAreaOnBuildup { false } - , Adjacent_Allowed {} - , Adjacent_Disallowed {} - , Adjacent_Disallowed_Prohibit { false } - , Adjacent_Disallowed_ProhibitDistance { 0 } - , BarracksExitCell {} - , Overpower_KeepOnline { 2 } - , Overpower_ChargeWeapon { 1 } - , DisableDamageSound { false } - , BuildingOccupyDamageMult {} - , BuildingOccupyROFMult {} - , BuildingBunkerDamageMult {} - , BuildingBunkerROFMult {} - , BunkerWallsUpSound {} - , BunkerWallsDownSound {} - , BunkerStateUpdateDelay {} - , BuildingRepairedSound {} - , Refinery_UseNormalActiveAnim { false } - , HasPowerUpAnim {} - , UndeploysInto_Sellable { false } - , BuildingRadioLink_SyncOwner {} - , GuardRetryDelay {} - , TurretAnim_IdleFrames { 1 } - , TurretAnim_LowPowerIdleFrames { 0 } - , TurretAnim_FiringFrames { 0 } - , TurretAnim_LowPowerFiringFrames { 0 } - , TurretAnim_IdleRate { 1 } - , TurretAnim_FiringRate { 1 } - - // Ares 0.2 - , CloningFacility { false } - - // Ares 0.A - , RubbleIntact { nullptr } - , RubbleIntactRemove { false } - - // Ares 3.0 - , UnitSell {} - { } - - // typed owner accessor (shadows the TechnoTypeClass one from the base) - BuildingTypeClass* OwnerObject() const - { - return static_cast(this->TechnoTypeExt::ExtData::OwnerObject()); - } - - // Ares 0.A functions - int GetSuperWeaponCount() const; - int GetSuperWeaponIndex(int index, HouseClass* pHouse) const; - int GetSuperWeaponIndex(int index) const; - - virtual ~ExtData() = default; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void Initialize() override; - virtual void CompleteInitialization(); - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - }; - - // BuildingTypeClassExtension lives in the TechnoTypeClass container; thin accessor facade. - class ExtMapFacade + , UnitSell {} + { } + + // typed owner accessor (shadows the TechnoTypeClass one from the base) + BuildingTypeClass* OwnerObject() const + { + return static_cast(this->TechnoTypeExt::OwnerObject()); + } + + // Ares 0.A functions + int GetSuperWeaponCount() const; + int GetSuperWeaponIndex(int index, HouseClass* pHouse) const; + int GetSuperWeaponIndex(int index) const; + + virtual ~BuildingTypeExt() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; + virtual void CompleteInitialization(); + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); + +public: + + static BuildingTypeExt* Fetch(const BuildingTypeClass* pThis) + { + return static_cast(TechnoTypeExt::Fetch(pThis)); + } + + static BuildingTypeExt* TryFetch(const BuildingTypeClass* pThis) { - public: - ExtData* Find(const BuildingTypeClass* key) const - { - return static_cast(TechnoTypeExt::ExtMap.Find(key)); - } - - ExtData* TryFind(const BuildingTypeClass* key) const - { - return static_cast(TechnoTypeExt::ExtMap.TryFind(key)); - } - }; - - static ExtMapFacade ExtMap; + return static_cast(TechnoTypeExt::TryFetch(pThis)); + } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -269,5 +261,3 @@ class BuildingTypeExt static int GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* pHouse); }; -// top-level name for the BuildingTypeClass extension leaf (derives from TechnoTypeClassExtension) -using BuildingTypeClassExtension = BuildingTypeExt::ExtData; diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index b1a78b31ab..e29728f668 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -5,7 +5,7 @@ bool BuildingTypeExt::CanUpgrade(BuildingClass* pBuilding, BuildingTypeClass* pUpgradeType, HouseClass* pUpgradeOwner) { - auto const pUpgradeExt = BuildingTypeExt::ExtMap.TryFind(pUpgradeType); + auto const pUpgradeExt = BuildingTypeExt::TryFetch(pUpgradeType); if (pUpgradeExt && EnumFunctions::CanTargetHouse(pUpgradeExt->PowersUp_Owner, pUpgradeOwner, pBuilding->Owner)) { auto const pType = pBuilding->Type; @@ -102,7 +102,7 @@ DEFINE_HOOK(0x4F8361, HouseClass_CanBuild_UpgradesInteraction, 0x5) if (auto const pBuilding = abstract_cast(pItem)) { - if (resultOfAres == CanBuildResult::Buildable && BuildingTypeExt::ExtMap.Find(pBuilding)->PowersUp_Buildings.size() > 0) + if (resultOfAres == CanBuildResult::Buildable && BuildingTypeExt::Fetch(pBuilding)->PowersUp_Buildings.size() > 0) R->EAX(CheckBuildLimit(pThis, pBuilding, includeInProduction)); } @@ -143,7 +143,7 @@ DEFINE_HOOK(0x464749, BuildingTypeClass_ReadINI_PowerUpAnims, 0x6) GET(BuildingTypeClass*, pThis, EBP); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pThis); + auto const pTypeExt = BuildingTypeExt::Fetch(pThis); auto const pINI = &CCINIClass::INI_Art; int index = 1; @@ -200,7 +200,7 @@ DEFINE_HOOK(0x440988, BuildingClass_Unlimbo_UpgradeAnims, 0x7) GET(BuildingClass*, pThis, ESI); GET(BuildingClass*, pTarget, EDI); - auto const pTargetExt = BuildingExt::ExtMap.Find(pTarget); + auto const pTargetExt = BuildingExt::Fetch(pTarget); auto const pType = pThis->Type; pTargetExt->PoweredUpToLevel = pTarget->UpgradeLevel + 1; int animIndex = pTarget->UpgradeLevel; @@ -226,7 +226,7 @@ DEFINE_HOOK(0x451630, BuildingClass_CreateUpgradeAnims_AnimIndex, 0x7) GET(BuildingClass*, pThis, EBP); - const int animIndex = BuildingExt::ExtMap.Find(pThis)->PoweredUpToLevel - 1; + const int animIndex = BuildingExt::Fetch(pThis)->PoweredUpToLevel - 1; if (animIndex) { @@ -244,7 +244,7 @@ static __forceinline bool AllowUpgradeAnim(BuildingClass* pBuilding, BuildingAni if (pType->Upgrades != 0 && anim >= BuildingAnimSlot::Upgrade1 && anim <= BuildingAnimSlot::Upgrade3 && !pBuilding->Anims[int(anim)]) { - const int animIndex = BuildingExt::ExtMap.Find(pBuilding)->PoweredUpToLevel - 1; + const int animIndex = BuildingExt::Fetch(pBuilding)->PoweredUpToLevel - 1; if (animIndex < 0 || (int)anim != animIndex) return false; diff --git a/src/Ext/BuildingType/Hooks.cpp b/src/Ext/BuildingType/Hooks.cpp index 954715099c..461b1f07e6 100644 --- a/src/Ext/BuildingType/Hooks.cpp +++ b/src/Ext/BuildingType/Hooks.cpp @@ -26,7 +26,7 @@ DEFINE_HOOK(0x44043D, BuildingClass_AI_Temporaled_Chronosparkle_MuzzleFix, 0x8) if (pType->MaxNumberOccupants > 10) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); R->EAX(&pTypeExt->OccupierMuzzleFlashes[nFiringIndex]); } @@ -41,7 +41,7 @@ DEFINE_HOOK(0x45387A, BuildingClass_FireOffset_Replace_MuzzleFix, 0xA) if (pType->MaxNumberOccupants > 10) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); R->EDX(&pTypeExt->OccupierMuzzleFlashes[pThis->FiringOccupantIndex]); } @@ -57,7 +57,7 @@ DEFINE_HOOK(0x458623, BuildingClass_KillOccupiers_Replace_MuzzleFix, 0x7) if (pType->MaxNumberOccupants > 10) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); R->ECX(&pTypeExt->OccupierMuzzleFlashes[nFiringIndex]); } @@ -71,7 +71,7 @@ DEFINE_HOOK(0x6D528A, TacticalClass_DrawPlacement_PlacementPreview, 0x6) const auto pBuilding = specific_cast(DisplayClass::Instance.CurrentBuilding); const auto pType = pBuilding ? pBuilding->Type : nullptr; - const auto pTypeExt = BuildingTypeExt::ExtMap.TryFind(pType); + const auto pTypeExt = BuildingTypeExt::TryFetch(pType); const bool isShow = pTypeExt && pTypeExt->PlacementPreview; if (isShow) @@ -156,7 +156,7 @@ DEFINE_HOOK(0x465D40, BuildingTypeClass_IsVehicle, 0x6) GET(BuildingTypeClass*, pThis, ECX); - const auto pExt = BuildingTypeExt::ExtMap.Find(pThis); + const auto pExt = BuildingTypeExt::Fetch(pThis); if (pExt->ConsideredVehicle.isset()) { @@ -180,7 +180,7 @@ DEFINE_HOOK(0x5F5416, ObjectClass_ReceiveDamage_CanC4DamageRounding, 0x6) if (!pType->CanC4) { - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); if (!pTypeExt->CanC4_AllowZeroDamage) *pDamage = 1; @@ -208,7 +208,7 @@ DEFINE_HOOK(0x4A8F3E, DisplayClass_BuildingProximityCheck_BeforeChecks, 0x6) GET_STACK(CellStruct*, foundationData, STACK_OFFSET(0x30, 0xC)); GET_STACK(CellStruct*, currentPosition, STACK_OFFSET(0x30, 0x10)); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); ProximityTemp::pType = pType; ProximityTemp::SkipDisallowed = false; @@ -239,12 +239,12 @@ DEFINE_HOOK(0x4A8FD7, DisplayClass_BuildingProximityCheck_BuildArea, 0x6) GET(BuildingClass*, pCellBuilding, ESI); GET_STACK(const int, houseArrayIndex, STACK_OFFSET(0x30, 0x8)); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pCellBuilding->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pCellBuilding->Type); if (pTypeExt->NoBuildAreaOnBuildup && pCellBuilding->CurrentMission == Mission::Construction) return SkipBuilding; - auto const pTmpTypeExt = BuildingTypeExt::ExtMap.Find(ProximityTemp::pType); + auto const pTmpTypeExt = BuildingTypeExt::Fetch(ProximityTemp::pType); auto const& pBuildingsAllowed = pTmpTypeExt->Adjacent_Allowed; if (pBuildingsAllowed.size() > 0 && !pBuildingsAllowed.Contains(pCellBuilding->Type)) @@ -282,7 +282,7 @@ DEFINE_HOOK(0x6FE3F1, TechnoClass_FireAt_OccupyDamageBonus, 0xB) if (const auto Building = specific_cast(pThis)) { GET_STACK(const int, damage, STACK_OFFSET(0xC8, -0x9C)); - R->EAX(static_cast(damage * BuildingTypeExt::ExtMap.Find(Building->Type)->BuildingOccupyDamageMult.Get(RulesClass::Instance->OccupyDamageMultiplier))); + R->EAX(static_cast(damage * BuildingTypeExt::Fetch(Building->Type)->BuildingOccupyDamageMult.Get(RulesClass::Instance->OccupyDamageMultiplier))); return ApplyDamageBonus; } @@ -298,7 +298,7 @@ DEFINE_HOOK(0x6FE421, TechnoClass_FireAt_BunkerDamageBonus, 0xB) if (const auto Building = specific_cast(pThis->BunkerLinkedItem)) { GET_STACK(const int, damage, STACK_OFFSET(0xC8, -0x9C)); - R->EAX(static_cast(damage * BuildingTypeExt::ExtMap.Find(Building->Type)->BuildingBunkerDamageMult.Get(RulesClass::Instance->OccupyDamageMultiplier))); + R->EAX(static_cast(damage * BuildingTypeExt::Fetch(Building->Type)->BuildingBunkerDamageMult.Get(RulesClass::Instance->OccupyDamageMultiplier))); return ApplyDamageBonus; } @@ -313,7 +313,7 @@ DEFINE_HOOK(0x6FD183, TechnoClass_RearmDelay_BuildingOccupyROFMult, 0xC) if (const auto Building = specific_cast(pThis)) { - const auto multiplier = BuildingTypeExt::ExtMap.Find(Building->Type)->BuildingOccupyROFMult.Get(RulesClass::Instance->OccupyROFMultiplier); + const auto multiplier = BuildingTypeExt::Fetch(Building->Type)->BuildingOccupyROFMult.Get(RulesClass::Instance->OccupyROFMultiplier); if (multiplier > 0.0f) { @@ -336,7 +336,7 @@ DEFINE_HOOK(0x6FD1C7, TechnoClass_RearmDelay_BuildingBunkerROFMult, 0xC) if (const auto Building = specific_cast(pThis->BunkerLinkedItem)) { - const auto multiplier = BuildingTypeExt::ExtMap.Find(Building->Type)->BuildingBunkerROFMult.Get(RulesClass::Instance->BunkerROFMultiplier); + const auto multiplier = BuildingTypeExt::Fetch(Building->Type)->BuildingBunkerROFMult.Get(RulesClass::Instance->BunkerROFMultiplier); if (multiplier > 0.0f) { @@ -405,7 +405,7 @@ DEFINE_HOOK(0x70272E, BuildingClass_ReceiveDamage_DisableDamageSound, 0x8) if (auto const pBuilding = specific_cast(pThis)) { - if (BuildingTypeExt::ExtMap.Find(pBuilding->Type)->DisableDamageSound) + if (BuildingTypeExt::Fetch(pBuilding->Type)->DisableDamageSound) { switch (R->Origin()) { @@ -450,7 +450,7 @@ DEFINE_HOOK(0x44E826, BuildingClass_GetPowerOutput_Enhancer, 0x6) if (power + extraPower <= 0) return ReturnZero; - const double factor = BuildingTypeExt::ExtMap.Find(pThis->Type)->PowerPlant_DamageFactor; + const double factor = BuildingTypeExt::Fetch(pThis->Type)->PowerPlant_DamageFactor; if (factor == 1.0) power = static_cast(power * pThis->GetHealthPercentage()); diff --git a/src/Ext/Bullet/Body.cpp b/src/Ext/Bullet/Body.cpp index fbd17355d2..782cf6df8c 100644 --- a/src/Ext/Bullet/Body.cpp +++ b/src/Ext/Bullet/Body.cpp @@ -9,11 +9,11 @@ BulletExt::ExtContainer BulletExt::ExtMap; -void BulletExt::ExtData::InterceptBullet(TechnoClass* pSource, BulletClass* pInterceptor) +void BulletExt::InterceptBullet(TechnoClass* pSource, BulletClass* pInterceptor) { const auto pThis = this->OwnerObject(); auto pTypeExt = this->TypeExtData; - const auto pInterceptorType = BulletExt::ExtMap.Find(pInterceptor)->InterceptorTechnoType->InterceptorType.get(); + const auto pInterceptorType = BulletExt::Fetch(pInterceptor)->InterceptorTechnoType->InterceptorType.get(); if (!pTypeExt->Armor.isset()) { @@ -57,7 +57,7 @@ void BulletExt::ExtData::InterceptBullet(TechnoClass* pSource, BulletClass* pInt { pThis->Speed = pWeaponOverride->Speed; pThis->Type = pWeaponOverride->Projectile; - pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + pTypeExt = BulletTypeExt::Fetch(pThis->Type); this->TypeExtData = pTypeExt; if (this->LaserTrails.size()) @@ -73,7 +73,7 @@ void BulletExt::ExtData::InterceptBullet(TechnoClass* pSource, BulletClass* pInt } } -void BulletExt::ExtData::ApplyRadiationToCell(CellStruct cell, int spread, int radLevel) +void BulletExt::ApplyRadiationToCell(CellStruct cell, int spread, int radLevel) { const auto pCell = MapClass::Instance.TryGetCellAt(cell); @@ -82,14 +82,14 @@ void BulletExt::ExtData::ApplyRadiationToCell(CellStruct cell, int spread, int r const auto pThis = this->OwnerObject(); const auto pWeapon = pThis->GetWeaponType(); - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const auto pRadType = pWeaponExt->RadType; - const auto pCellExt = CellExt::ExtMap.Find(pCell); + const auto pCellExt = CellExt::Fetch(pCell); const auto it = std::find_if(pCellExt->RadSites.cbegin(), pCellExt->RadSites.cend(), [=](const auto pSite) { - const auto pRadExt = RadSiteExt::ExtMap.Find(pSite); + const auto pRadExt = RadSiteExt::Fetch(pSite); if (pRadExt->Type != pRadType || spread != pSite->Spread) return false; @@ -103,7 +103,7 @@ void BulletExt::ExtData::ApplyRadiationToCell(CellStruct cell, int spread, int r if (it != pCellExt->RadSites.cend()) { - const auto pRadExt = RadSiteExt::ExtMap.Find(*it); + const auto pRadExt = RadSiteExt::Fetch(*it); // Handle It pRadExt->Add(std::min(radLevel, pRadType->GetLevelMax() - (*it)->GetRadLevel())); return; @@ -113,13 +113,13 @@ void BulletExt::ExtData::ApplyRadiationToCell(CellStruct cell, int spread, int r RadSiteExt::CreateInstance(cell, spread, radLevel, pWeaponExt, pThisHouse, pThis->Owner); } -void BulletExt::ExtData::InitializeLaserTrails() +void BulletExt::InitializeLaserTrails() { if (this->LaserTrails.size()) return; auto const pThis = this->OwnerObject(); - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); auto const pOwner = pThis->Owner ? pThis->Owner->Owner : nullptr; this->LaserTrails.reserve(pTypeExt->LaserTrail_Types.size()); @@ -165,7 +165,7 @@ inline void BulletExt::SimulatedFiringAnim(BulletClass* pBullet, HouseClass* pHo const auto pAnim = GameCreate(pAnimType, pBullet->SourceCoords); AnimExt::SetAnimOwnerHouseKind(pAnim, pHouse, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pFirer, pHouse); + AnimExt::Fetch(pAnim)->SetInvoker(pFirer, pHouse); if (pAttach) { @@ -202,7 +202,7 @@ inline void BulletExt::SimulatedFiringLaser(BulletClass* pBullet, HouseClass* pH if (!pWeapon->IsLaser) return; - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeapon->IsHouseColor || pWeaponExt->Laser_IsSingleColor) { @@ -238,7 +238,7 @@ inline void BulletExt::SimulatedFiringElectricBolt(BulletClass* pBullet) pBolt->AlternateColor = pWeapon->IsAlternateColor; const auto targetCoords = BulletExt::GetTargetCoordsForFiring(pBullet); - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); int zAdjust = pWeaponExt->EBoltZAdjust.Get(RulesExt::Global()->EBoltZAdjust); const auto pOwner = pBullet->Owner; @@ -251,7 +251,7 @@ inline void BulletExt::SimulatedFiringElectricBolt(BulletClass* pBullet) pBolt->Fire(pBullet->SourceCoords, targetCoords, zAdjust); - if (const auto particle = WeaponTypeExt::ExtMap.Find(pWeapon)->Bolt_ParticleSystem.Get(RulesClass::Instance->DefaultSparkSystem)) + if (const auto particle = WeaponTypeExt::Fetch(pWeapon)->Bolt_ParticleSystem.Get(RulesClass::Instance->DefaultSparkSystem)) GameCreate(particle, targetCoords, nullptr, nullptr, CoordStruct::Empty, nullptr); } @@ -270,7 +270,7 @@ inline void BulletExt::SimulatedFiringRadBeam(BulletClass* pBullet, HouseClass* pRadBeam->SetCoordsSource(pBullet->SourceCoords); pRadBeam->SetCoordsTarget(BulletExt::GetTargetCoordsForFiring(pBullet)); - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); pRadBeam->Color = (pWeaponExt->Beam_IsHouseColor && pHouse) ? pHouse->LaserColor : pWeaponExt->Beam_Color.Get(isTemporal ? RulesClass::Instance->ChronoBeamColor : RulesClass::Instance->RadColor); @@ -293,11 +293,11 @@ void BulletExt::SimulatedFiringUnlimbo(BulletClass* pBullet, HouseClass* pHouse, { // Initialize bullet characteristics such as weapon type, range, house etc. const auto pType = pBullet->Type; - const int projectileRange = WeaponTypeExt::ExtMap.Find(pWeapon)->ProjectileRange.Get(); + const int projectileRange = WeaponTypeExt::Fetch(pWeapon)->ProjectileRange.Get(); auto velocity = BulletVelocity::Empty; pBullet->WeaponType = pWeapon; pBullet->Range = projectileRange; - BulletExt::ExtMap.Find(pBullet)->FirerHouse = pHouse; + BulletExt::Fetch(pBullet)->FirerHouse = pHouse; if (pType->FirersPalette) pBullet->InheritedColor = pHouse->ColorSchemeIndex; @@ -457,7 +457,7 @@ void BulletExt::Detonate(const CoordStruct& coords, TechnoClass* pOwner, int dam auto const pBullet = pType->CreateBullet(pTarget, pOwner, damage, pWarhead, 100, isBright); pBullet->WeaponType = pWeapon; - auto const pBulletExt = BulletExt::ExtMap.Find(pBullet); + auto const pBulletExt = BulletExt::Fetch(pBullet); pBulletExt->IsInstantDetonation = true; if (pFiringHouse) @@ -472,7 +472,7 @@ void BulletExt::Detonate(const CoordStruct& coords, TechnoClass* pOwner, int dam // load / save template -void BulletExt::ExtData::Serialize(T& Stm) +void BulletExt::Serialize(T& Stm) { Stm .Process(this->TypeExtData) @@ -492,15 +492,15 @@ void BulletExt::ExtData::Serialize(T& Stm) ; } -void BulletExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void BulletExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectClassExtension::LoadFromStream(Stm); + ObjectExt::LoadFromStream(Stm); this->Serialize(Stm); } -void BulletExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void BulletExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectClassExtension::SaveToStream(Stm); + ObjectExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index 9285fe5b10..26a0b77eb0 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -13,68 +13,67 @@ struct RadialFireStruct DirStruct Direction {}; }; -class BulletExt +class BulletExt final : public ObjectExt { public: using base_type = BulletClass; + using ExtData = BulletExt; static constexpr DWORD Canary = 0x2A2A2A2A; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectClassExtension +public: + // typed owner accessor + BulletClass* OwnerObject() const { - public: - // typed owner accessor - BulletClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - BulletTypeExt::ExtData* TypeExtData; - HouseClass* FirerHouse; - int CurrentStrength; - TechnoTypeExt::ExtData* InterceptorTechnoType; - InterceptedStatus InterceptedStatus; - bool DetonateOnInterception; - std::vector> LaserTrails; - bool SnappedToTarget; // Used for custom trajectory projectile target snap checks - int DamageNumberOffset; - int ParabombFallRate; - bool IsInstantDetonation; - double FirepowerMult; - - TrajectoryPointer Trajectory; - - ExtData(BulletClass* OwnerObject) : ObjectClassExtension(OwnerObject) - , TypeExtData { nullptr } - , FirerHouse { nullptr } - , CurrentStrength { 0 } - , InterceptorTechnoType { nullptr } - , InterceptedStatus { InterceptedStatus::None } - , DetonateOnInterception { true } - , LaserTrails {} - , Trajectory { nullptr } - , SnappedToTarget { false } - , DamageNumberOffset { INT32_MIN } - , ParabombFallRate { 0 } - , IsInstantDetonation { false } - , FirepowerMult { 1.0 } - { } - - virtual ~ExtData() = default; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - void InterceptBullet(TechnoClass* pSource, BulletClass* pInterceptor); - void ApplyRadiationToCell(CellStruct cell, int spread, int radLevel); - void InitializeLaserTrails(); - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + BulletTypeExt* TypeExtData; + HouseClass* FirerHouse; + int CurrentStrength; + TechnoTypeExt* InterceptorTechnoType; + InterceptedStatus InterceptedStatus; + bool DetonateOnInterception; + std::vector> LaserTrails; + bool SnappedToTarget; // Used for custom trajectory projectile target snap checks + int DamageNumberOffset; + int ParabombFallRate; + bool IsInstantDetonation; + double FirepowerMult; + + TrajectoryPointer Trajectory; + + BulletExt(BulletClass* OwnerObject) : ObjectExt(OwnerObject) + , TypeExtData { nullptr } + , FirerHouse { nullptr } + , CurrentStrength { 0 } + , InterceptorTechnoType { nullptr } + , InterceptedStatus { InterceptedStatus::None } + , DetonateOnInterception { true } + , LaserTrails {} + , Trajectory { nullptr } + , SnappedToTarget { false } + , DamageNumberOffset { INT32_MIN } + , ParabombFallRate { 0 } + , IsInstantDetonation { false } + , FirepowerMult { 1.0 } + { } + + virtual ~BulletExt() = default; + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + + void InterceptBullet(TechnoClass* pSource, BulletClass* pInterceptor); + void ApplyRadiationToCell(CellStruct cell, int spread, int radLevel); + void InitializeLaserTrails(); + +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -84,6 +83,16 @@ class BulletExt static ExtContainer ExtMap; + static BulletExt* Fetch(const BulletClass* pThis) + { + return ExtMap.Find(pThis); + } + + static BulletExt* TryFetch(const BulletClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static void Detonate(const CoordStruct& coords, TechnoClass* pOwner, int damage, HouseClass* pFiringHouse, AbstractClass* pTarget, bool isBright, WeaponTypeClass* pWeapon, WarheadTypeClass* pWarhead); static void ApplyArcingFix(BulletClass* pThis, const CoordStruct& sourceCoords, const CoordStruct& targetCoords, BulletVelocity& velocity); static CoordStruct GetTargetCoordsForFiring(BulletClass* pBullet); @@ -99,5 +108,3 @@ class BulletExt static inline BulletVelocity ApplyRadialFireVelocityWarp(BulletVelocity velocity, const RadialFireStruct& radialFire); }; -// top-level name for the BulletExt extension -using BulletClassExtension = BulletExt::ExtData; diff --git a/src/Ext/Bullet/Hooks.DetonateLogics.cpp b/src/Ext/Bullet/Hooks.DetonateLogics.cpp index c50ba526a8..f9ad8f8ea7 100644 --- a/src/Ext/Bullet/Hooks.DetonateLogics.cpp +++ b/src/Ext/Bullet/Hooks.DetonateLogics.cpp @@ -14,12 +14,12 @@ DEFINE_HOOK(0x4690D4, BulletClass_Logics_NewChecks, 0x6) GET(WarheadTypeClass*, pWarhead, EAX); GET_BASE(CoordStruct const* const, pCoords, 0x8); - auto const pExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pExt = WarheadTypeExt::Fetch(pWarhead); if (auto const pTarget = abstract_cast(pBullet->Target)) { // Check if the WH should affect the techno target or skip it - if (BulletTypeExt::ExtMap.Find(pBullet->Type)->Shrapnel_ObeyWarheadTriggerConditions.Get(RulesExt::Global()->Shrapnel_ObeyWarheadTriggerConditions)) + if (BulletTypeExt::Fetch(pBullet->Type)->Shrapnel_ObeyWarheadTriggerConditions.Get(RulesExt::Global()->Shrapnel_ObeyWarheadTriggerConditions)) { if (!pExt->IsHealthInThreshold(pTarget) || !pExt->IsVeterancyInThreshold(pTarget) @@ -47,7 +47,7 @@ DEFINE_HOOK(0x4692BD, BulletClass_Logics_ApplyMindControl, 0x6) GET(BulletClass*, pThis, ESI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis->WH); + auto const pWHExt = WarheadTypeExt::Fetch(pThis->WH); auto const pControlledAnimType = pWHExt->MindControl_Anim.Get(RulesClass::Instance->ControlledAnimationType); auto const threatDelay = pWHExt->MindControl_ThreatDelay.Get(RulesExt::Global()->MindControl_ThreatDelay); R->AL(CaptureManagerExt::CaptureUnit(pThis->Owner->CaptureManager, pThis->Target, pControlledAnimType, threatDelay)); @@ -70,7 +70,7 @@ DEFINE_HOOK(0x469A75, BulletClass_Logics_DamageHouse, 0x7) GET(HouseClass*, pHouse, ECX); if (!pHouse) - R->ECX(BulletExt::ExtMap.Find(pThis)->FirerHouse); + R->ECX(BulletExt::Fetch(pThis)->FirerHouse); return 0; } @@ -83,7 +83,7 @@ DEFINE_HOOK(0x4690C1, BulletClass_Logics_DetonateOnAllMapObjects, 0x8) GET(BulletClass*, pThis, ESI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis->WH); + auto const pWHExt = WarheadTypeExt::Fetch(pThis->WH); if (pWHExt->DetonateOnAllMapObjects && !pWHExt->WasDetonatedOnAllMapObjects && pWHExt->DetonateOnAllMapObjects_AffectsTarget != AffectedTarget::None @@ -93,7 +93,7 @@ DEFINE_HOOK(0x4690C1, BulletClass_Logics_DetonateOnAllMapObjects, 0x8) auto const originalLocation = pThis->Location; auto const pOriginalTarget = pThis->Target; auto const isFull = pWHExt->DetonateOnAllMapObjects_Full; - auto const pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; + auto const pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::Fetch(pThis)->FirerHouse; auto copy_dvc = [](const DynamicVectorClass&dvc) { @@ -171,7 +171,7 @@ DEFINE_HOOK(0x469D1A, BulletClass_Logics_Debris, 0x6) GET(BulletClass*, pThis, ESI); const auto pWH = pThis->WH; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->Debris_Conventional && pThis->GetCell()->LandType == LandType::Water) return SkipGameCode; @@ -184,7 +184,7 @@ DEFINE_HOOK(0x469D1A, BulletClass_Logics_Debris, 0x6) const auto& debrisTypes = pWH->DebrisTypes; const auto& debrisMaximums = pWH->DebrisMaximums; - const auto pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; + const auto pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::Fetch(pThis)->FirerHouse; auto coord = pThis->GetCoords(); const int count = Math::min(debrisTypes.Count, debrisMaximums.Count); @@ -250,7 +250,7 @@ DEFINE_HOOK(0x469B44, BulletClass_Logics_LandTypeCheck, 0x6) GET(BulletClass*, pThis, ESI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis->WH); + auto const pWHExt = WarheadTypeExt::Fetch(pThis->WH); if (pWHExt->Conventional_IgnoreUnits) return SkipChecks; @@ -272,7 +272,7 @@ DEFINE_HOOK(0x469C46, BulletClass_Logics_DamageAnimSelected, 0x8) if (pAnimType) { - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis->WH); + auto const pWHExt = WarheadTypeExt::Fetch(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; @@ -291,13 +291,13 @@ DEFINE_HOOK(0x469C46, BulletClass_Logics_DamageAnimSelected, 0x8) const bool allowScatter = scatterMax >= 0 || scatterMin >= 0 || pThis->Type->Inviso; if (creationInterval > 0 && pOwner) - remainingInterval = &TechnoExt::ExtMap.Find(pOwner)->WHAnimRemainingCreationInterval; + remainingInterval = &TechnoExt::Fetch(pOwner)->WHAnimRemainingCreationInterval; if (creationInterval < 1 || *remainingInterval <= 0) { *remainingInterval = creationInterval; - HouseClass* pInvoker = pOwner ? pOwner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; + HouseClass* pInvoker = pOwner ? pOwner->Owner : BulletExt::Fetch(pThis)->FirerHouse; HouseClass* pVictim = nullptr; if (auto const pTarget = abstract_cast(pThis->Target)) @@ -354,7 +354,7 @@ DEFINE_HOOK(0x469C46, BulletClass_Logics_DamageAnimSelected, 0x8) if (pOwner) { - auto const pExt = AnimExt::ExtMap.Find(pAnim); + auto const pExt = AnimExt::Fetch(pAnim); pExt->SetInvoker(pOwner); } } @@ -375,13 +375,13 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) GET_BASE(CoordStruct const* const, coords, 0x8); auto const pTechno = pThis->Owner; - auto const pBulletExt = BulletExt::ExtMap.Find(pThis); + auto const pBulletExt = BulletExt::Fetch(pThis); auto const pOwner = pTechno ? pTechno->Owner : pBulletExt->FirerHouse; // Extra warheads if (auto const pWeapon = pThis->WeaponType) { - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); auto const& extraWarheads = pWeaponExt->ExtraWarheads; auto const& damageOverrides = pWeaponExt->ExtraWarheads_DamageOverrides; auto const& detonationChances = pWeaponExt->ExtraWarheads_DetonationChances; @@ -398,7 +398,7 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) return; auto const pWH = extraWarheads[index]; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); if (pTarget && (!pWHExt->IsHealthInThreshold(pTarget) || !pWHExt->IsVeterancyInThreshold(pTarget) @@ -479,7 +479,7 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) // Return to sender if (pTechno) { - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); if (auto const pWeapon = pTypeExt->ReturnWeapon) { @@ -491,7 +491,7 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) if (auto const pBullet = pWeapon->Projectile->CreateBullet(pTechno, pTechno, damage, pWeapon->Warhead, pWeapon->Speed, pWeapon->Bright)) { - BulletExt::ExtMap.Find(pBullet)->FirepowerMult = pBulletExt->FirepowerMult; + BulletExt::Fetch(pBullet)->FirepowerMult = pBulletExt->FirepowerMult; BulletExt::SimulatedFiringUnlimbo(pBullet, pOwner, pWeapon, pThis->Location, true); BulletExt::SimulatedFiringEffects(pBullet, pOwner, nullptr, false, true); } @@ -500,7 +500,7 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) // Unlimbo Detonate const auto pWH = pThis->WH; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); pWHExt->InDamageArea = true; if (pTechno && pTechno->InLimbo && !pWH->Parasite && pWHExt->UnlimboDetonate) @@ -548,14 +548,14 @@ DEFINE_HOOK(0x469AA4, BulletClass_Logics_Extras, 0x5) --Unsorted::ScenarioInit; } - const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + const auto pTechnoExt = TechnoExt::Fetch(pTechno); if (success) { if (isInAir) { pTechno->IsFallingDown = true; - TechnoExt::ExtMap.Find(pTechno)->OnParachuted = true; + TechnoExt::Fetch(pTechno)->OnParachuted = true; } if (pWHExt->UnlimboDetonate_KeepTarget @@ -606,7 +606,7 @@ static bool IsAllowedSplitsTarget(TechnoClass* pSource, HouseClass* pOwner, Weap if (!pType->LegalTarget || GeneralUtils::GetWarheadVersusArmor(pWH, pTarget, pType) == 0.0) return false; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->SkipWeaponPicking) return true; @@ -625,7 +625,7 @@ static bool IsAllowedSplitsTarget(TechnoClass* pSource, HouseClass* pOwner, Weap } else { - if (!WarheadTypeExt::ExtMap.Find(pWH)->CanTargetHouse(pOwner, pTarget)) + if (!WarheadTypeExt::Fetch(pWH)->CanTargetHouse(pOwner, pTarget)) return false; } @@ -645,7 +645,7 @@ static bool SplitsProjectileCheck(BulletTypeClass* pProjectile, WeaponTypeClass* if (pType->AA && !inAir) { - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BulletTypeExt::Fetch(pType); if (pTypeExt->AAOnly) return false; @@ -666,7 +666,7 @@ DEFINE_HOOK(0x468EB3, BulletClass_Explodes_AirburstCheck1, 0x6) auto const pType = pThis->Type; R->EAX(pType); - return !(pType->Airburst || BulletTypeExt::ExtMap.Find(pType)->Splits) ? Continue : Skip; + return !(pType->Airburst || BulletTypeExt::Fetch(pType)->Splits) ? Continue : Skip; } DEFINE_HOOK(0x468FF4, BulletClass_Explodes_AirburstCheck2, 0x6) @@ -678,7 +678,7 @@ DEFINE_HOOK(0x468FF4, BulletClass_Explodes_AirburstCheck2, 0x6) auto const pType = pThis->Type; R->EAX(pType); - return (pType->Airburst || BulletTypeExt::ExtMap.Find(pType)->Splits) ? Continue : Skip; + return (pType->Airburst || BulletTypeExt::Fetch(pType)->Splits) ? Continue : Skip; } DEFINE_HOOK(0x469EC0, BulletClass_Logics_AirburstWeapon, 0x6) @@ -688,13 +688,13 @@ DEFINE_HOOK(0x469EC0, BulletClass_Logics_AirburstWeapon, 0x6) GET(BulletClass*, pThis, ESI); auto const pType = pThis->Type; - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BulletTypeExt::Fetch(pType); auto const pWeapon = pType->AirburstWeapon; if ((pType->Airburst || pTypeExt->Splits) && pWeapon) { auto const pSource = pThis->Owner; - auto const pBulletExt = BulletExt::ExtMap.Find(pThis); + auto const pBulletExt = BulletExt::Fetch(pThis); auto pOwner = pSource ? pSource->Owner : pBulletExt->FirerHouse; if (!pOwner || pOwner->Defeated) @@ -900,7 +900,7 @@ DEFINE_HOOK(0x469EC0, BulletClass_Logics_AirburstWeapon, 0x6) radialFireCounter = (radialFireCounter + 1) % radialFireSegments; } - BulletExt::ExtMap.Find(pBullet)->FirepowerMult = pBulletExt->FirepowerMult; + BulletExt::Fetch(pBullet)->FirepowerMult = pBulletExt->FirepowerMult; BulletExt::SimulatedFiringUnlimbo(pBullet, pOwner, pWeapon, coords, headToTarget, radialFire); BulletExt::SimulatedFiringEffects(pBullet, pOwner, nullptr, useFiringEffects, true); } @@ -942,7 +942,7 @@ DEFINE_HOOK(0x4899DA, MapClass_DamageArea_DamageUnderGround, 0x7) if (isNullified) return 0; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); if (!pWHExt->AffectsUnderground) return 0; diff --git a/src/Ext/Bullet/Hooks.Obstacles.cpp b/src/Ext/Bullet/Hooks.Obstacles.cpp index fcc01d4e3d..eeaa5e8fea 100644 --- a/src/Ext/Bullet/Hooks.Obstacles.cpp +++ b/src/Ext/Bullet/Hooks.Obstacles.cpp @@ -9,7 +9,7 @@ class BulletObstacleHelper public: static CellClass* GetObstacle(CellClass* pSourceCell, CellClass* pTargetCell, CellClass* pCurrentCell, CoordStruct currentCoords, AbstractClass const* const pSource, - AbstractClass const* const pTarget, HouseClass* pOwner, BulletTypeClass* pBulletType, BulletTypeExt::ExtData*& pBulletTypeExt, bool isTargetingCheck = false) + AbstractClass const* const pTarget, HouseClass* pOwner, BulletTypeClass* pBulletType, BulletTypeExt*& pBulletTypeExt, bool isTargetingCheck = false) { CellClass* pObstacleCell = nullptr; @@ -25,7 +25,7 @@ class BulletObstacleHelper static CellClass* FindFirstObstacle(CoordStruct const& pSourceCoords, CoordStruct const& pTargetCoords, AbstractClass const* const pSource, AbstractClass const* const pTarget, HouseClass* pOwner, BulletTypeClass* pBulletType, bool isTargetingCheck = false, bool subjectToGround = false) { - BulletTypeExt::ExtData* pBulletTypeExt = BulletTypeExt::ExtMap.Find(pBulletType); + BulletTypeExt* pBulletTypeExt = BulletTypeExt::Fetch(pBulletType); if (SubjectToObstacles(pBulletType, pBulletTypeExt) || subjectToGround) { @@ -64,14 +64,14 @@ class BulletObstacleHelper return FindFirstObstacle(pSourceCoords, pTargetCoords, pSource, pTarget, pOwner, pWeapon->Projectile, isTargetingCheck, subjectToGround); } - static bool SubjectToObstacles(BulletTypeClass* pBulletType, BulletTypeExt::ExtData*& pBulletTypeExt) + static bool SubjectToObstacles(BulletTypeClass* pBulletType, BulletTypeExt*& pBulletTypeExt) { bool subjectToTerrain = pBulletTypeExt->SubjectToLand.isset() || pBulletTypeExt->SubjectToWater.isset(); return subjectToTerrain ? true : pBulletType->Level; } - static bool SubjectToTerrain(CellClass* pCurrentCell, BulletTypeClass* pBulletType, BulletTypeExt::ExtData*& pBulletTypeExt, bool isTargetingCheck) + static bool SubjectToTerrain(CellClass* pCurrentCell, BulletTypeClass* pBulletType, BulletTypeExt*& pBulletTypeExt, bool isTargetingCheck) { const bool isCellWater = pCurrentCell->LandType == LandType::Water || pCurrentCell->LandType == LandType::Beach; const bool isLevel = pBulletType->Level ? pCurrentCell->IsOnFloor() : false; @@ -144,7 +144,7 @@ DEFINE_HOOK(0x4688A9, BulletClass_Unlimbo_Obstacles, 0x6) if (pType->Inviso) { auto const pThisOwner = pThis->Owner; - auto const pOwner = pThisOwner ? pThisOwner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; + auto const pOwner = pThisOwner ? pThisOwner->Owner : BulletExt::Fetch(pThis)->FirerHouse; const auto pObstacleCell = BulletObstacleHelper::FindFirstObstacle(*sourceCoords, targetCoords, pThisOwner, pThis->Target, pOwner, pType, false, false); if (pObstacleCell) @@ -170,14 +170,14 @@ DEFINE_HOOK(0x468C86, BulletClass_ShouldExplode_Obstacles, 0xA) GET(BulletClass*, pThis, ESI); auto const pType = pThis->Type; - auto pBulletTypeExt = BulletTypeExt::ExtMap.Find(pType); + auto pBulletTypeExt = BulletTypeExt::Fetch(pType); if (BulletObstacleHelper::SubjectToObstacles(pType, pBulletTypeExt)) { auto const pCellSource = MapClass::Instance.GetCellAt(pThis->SourceCoords); auto const pCellTarget = MapClass::Instance.GetCellAt(pThis->TargetCoords); auto const pCellCurrent = MapClass::Instance.GetCellAt(pThis->LastMapCoords); - auto const pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; + auto const pOwner = pThis->Owner ? pThis->Owner->Owner : BulletExt::Fetch(pThis)->FirerHouse; auto const pObstacleCell = BulletObstacleHelper::GetObstacle(pCellSource, pCellTarget, pCellCurrent, pThis->Location, pThis->Owner, pThis->Target, pOwner, pType, pBulletTypeExt, false); if (pObstacleCell) @@ -232,7 +232,7 @@ DEFINE_HOOK(0x6F7647, TechnoClass_InRange_Obstacles, 0x5) if (!pObstacleCell) { - bool subjectToGround = BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->SubjectToGround.Get(); + bool subjectToGround = BulletTypeExt::Fetch(pWeapon->Projectile)->SubjectToGround.Get(); const auto newSourceCoords = subjectToGround ? BulletObstacleHelper::AddFLHToSourceCoords(*pSourceCoords, targetCoords, pTechno, pTarget, pWeapon, subjectToGround) : *pSourceCoords; pObstacleCell = BulletObstacleHelper::FindFirstImpenetrableObstacle(newSourceCoords, targetCoords, pTechno, pTarget, pTechno->Owner, pWeapon, true, subjectToGround); } diff --git a/src/Ext/Bullet/Hooks.cpp b/src/Ext/Bullet/Hooks.cpp index 1e708b2fc4..0ff23325fb 100644 --- a/src/Ext/Bullet/Hooks.cpp +++ b/src/Ext/Bullet/Hooks.cpp @@ -9,7 +9,7 @@ DEFINE_HOOK(0x466556, BulletClass_Init, 0x6) { GET(BulletClass*, pThis, ECX); - if (auto const pExt = BulletExt::ExtMap.TryFind(pThis)) + if (auto const pExt = BulletExt::TryFetch(pThis)) { if (pThis->Owner) { @@ -19,7 +19,7 @@ DEFINE_HOOK(0x466556, BulletClass_Init, 0x6) auto const pType = pThis->Type; pExt->CurrentStrength = pType->Strength; - pExt->TypeExtData = BulletTypeExt::ExtMap.Find(pType); + pExt->TypeExtData = BulletTypeExt::Fetch(pType); if (!pType->Inviso) pExt->InitializeLaserTrails(); @@ -31,15 +31,15 @@ DEFINE_HOOK(0x466556, BulletClass_Init, 0x6) // Set in BulletClass::AI and guaranteed to be valid within it. namespace BulletAITemp { - BulletExt::ExtData* ExtData; - BulletTypeExt::ExtData* TypeExtData; + BulletExt* ExtData; + BulletTypeExt* TypeExtData; } DEFINE_HOOK(0x4666F7, BulletClass_AI, 0x6) { GET(BulletClass*, pThis, EBP); - const auto pBulletExt = BulletExt::ExtMap.Find(pThis); + const auto pBulletExt = BulletExt::Fetch(pThis); const auto pBulletTypeExt = pBulletExt->TypeExtData; BulletAITemp::ExtData = pBulletExt; BulletAITemp::TypeExtData = pBulletTypeExt; @@ -48,7 +48,7 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI, 0x6) { if (const auto pTarget = abstract_cast(pThis->Target)) { - const auto pTargetExt = BulletExt::ExtMap.Find(pTarget); + const auto pTargetExt = BulletExt::Fetch(pTarget); if (!pTargetExt->TypeExtData->Armor.isset()) pTargetExt->InterceptedStatus |= InterceptedStatus::Locked; @@ -58,7 +58,7 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI, 0x6) if (pBulletExt->InterceptedStatus & InterceptedStatus::Intercepted) { if (const auto pTarget = abstract_cast(pThis->Target)) - BulletExt::ExtMap.Find(pTarget)->InterceptedStatus &= ~InterceptedStatus::Locked; + BulletExt::Fetch(pTarget)->InterceptedStatus &= ~InterceptedStatus::Locked; if (pBulletExt->DetonateOnInterception) pThis->Detonate(pThis->GetCoords()); @@ -133,7 +133,7 @@ DEFINE_HOOK(0x466897, BulletClass_AI_Trailer, 0x6) REF_STACK(const CoordStruct, coords, STACK_OFFSET(0x1A8, -0x184)); auto const pTrailerAnim = GameCreate(pThis->Type->Trailer, coords, 1, 1); - auto const pTrailerAnimExt = AnimExt::ExtMap.Find(pTrailerAnim); + auto const pTrailerAnimExt = AnimExt::Fetch(pTrailerAnim); auto const pOwner = pThis->Owner ? pThis->Owner->Owner : BulletAITemp::ExtData->FirerHouse; AnimExt::SetAnimOwnerHouseKind(pTrailerAnim, pOwner, nullptr, false, true); pTrailerAnimExt->SetInvoker(pThis->Owner); @@ -240,7 +240,7 @@ DEFINE_HOOK(0x46A3D6, BulletClass_Shrapnel_Forced, 0xA) GET(BulletClass*, pThis, EDI); - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); ShrapnelTemp::InitialTargetBuilding = nullptr; ShrapnelTemp::TargetsToIgnore.clear(); @@ -275,7 +275,7 @@ DEFINE_HOOK(0x46A4FB, BulletClass_Shrapnel_Targeting, 0x6) GET(TechnoClass*, pSource, EAX); GET(WeaponTypeClass*, pShrapnelWeapon, ESI); - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); const bool isBuilding = pObject->WhatAmI() == AbstractType::Building; const bool ignorePreviouslyHit = pTypeExt->Shrapnel_IgnoreHitBuildings.Get(RulesExt::Global()->Shrapnel_IgnoreHitBuildings); @@ -293,7 +293,7 @@ DEFINE_HOOK(0x46A4FB, BulletClass_Shrapnel_Targeting, 0x6) if (pTypeExt->Shrapnel_UseWeaponTargeting) { - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pShrapnelWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pShrapnelWeapon); auto const pType = pObject->GetType(); if (!pType->LegalTarget) @@ -316,7 +316,7 @@ DEFINE_HOOK(0x46A4FB, BulletClass_Shrapnel_Targeting, 0x6) } } - auto const pShield = TechnoExt::ExtMap.Find(pTechno)->Shield.get(); + auto const pShield = TechnoExt::Fetch(pTechno)->Shield.get(); if (pShield && pShield->IsActive() && !pShield->CanBePenetrated(pWH)) armorType = pShield->GetArmorType(); @@ -343,7 +343,7 @@ DEFINE_HOOK(0x46902C, BulletClass_Explode_Cluster, 0x6) GET(BulletClass*, pThis, ESI); REF_STACK(const CoordStruct, origCoords, STACK_OFFSET(0x3C, -0x30)); - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); const int min = pTypeExt->ClusterScatter_Min.Get(); const int max = pTypeExt->ClusterScatter_Max.Get(); auto coords = origCoords; @@ -403,7 +403,7 @@ DEFINE_HOOK(0x468E61, BulletClass_Explode_TargetSnapChecks1, 0x6) GET(BulletClass*, pThis, ESI); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (pExt->IsInstantDetonation) return SkipChecks; @@ -436,7 +436,7 @@ DEFINE_HOOK(0x468E9F, BulletClass_Explode_TargetSnapChecks2, 0x6) GET(BulletClass*, pThis, ESI); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (pExt->IsInstantDetonation) return SkipChecks; @@ -468,7 +468,7 @@ DEFINE_HOOK(0x468D3F, BulletClass_ShouldExplode_AirTarget, 0x6) GET(BulletClass*, pThis, ESI); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (pExt->Trajectory && CheckTrajectoryCanNotAlwaysSnap(pExt->Trajectory->Flag())) return SkipCheck; @@ -483,7 +483,7 @@ DEFINE_HOOK(0x4687F8, BulletClass_Unlimbo_FlakScatter, 0x6) if (pThis->WeaponType) { - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); const int defaultValue = RulesClass::Instance->BallisticScatter; const int min = pTypeExt->BallisticScatter_Min.Get(Leptons(0)); const int max = pTypeExt->BallisticScatter_Max.Get(Leptons(defaultValue)); @@ -504,7 +504,7 @@ DEFINE_HOOK(0x6FF008, TechnoClass_Fire_BeforeMoveTo, 0x8) const auto pBulletType = pBullet->Type; - if (pBulletType->Arcing && !BulletTypeExt::ExtMap.Find(pBulletType)->Arcing_AllowElevationInaccuracy) + if (pBulletType->Arcing && !BulletTypeExt::Fetch(pBulletType)->Arcing_AllowElevationInaccuracy) { REF_STACK(BulletVelocity, velocity, STACK_OFFSET(0xB0, -0x60)); REF_STACK(const CoordStruct, crdSrc, STACK_OFFSET(0xB0, -0x6C)); @@ -524,7 +524,7 @@ DEFINE_HOOK(0x44D46E, BuildingClass_Mission_Missile_BeforeMoveTo, 0x8) const auto pBulletType = pBullet->Type; - if (pBulletType->Arcing && !BulletTypeExt::ExtMap.Find(pBulletType)->Arcing_AllowElevationInaccuracy) + if (pBulletType->Arcing && !BulletTypeExt::Fetch(pBulletType)->Arcing_AllowElevationInaccuracy) { REF_STACK(BulletVelocity, velocity, STACK_OFFSET(0xE8, -0xD0)); REF_STACK(const CoordStruct, crdSrc, STACK_OFFSET(0xE8, -0x8C)); @@ -543,7 +543,7 @@ DEFINE_HOOK(0x415F25, AircraftClass_Fire_TrajectorySkipInertiaEffect, 0x6) GET(BulletClass*, pThis, ESI); - if (BulletExt::ExtMap.Find(pThis)->Trajectory) + if (BulletExt::Fetch(pThis)->Trajectory) return SkipCheck; return 0; @@ -557,7 +557,7 @@ DEFINE_PATCH(0x46867F, 0x6A, 0x00, 0x8B, 0xD9, 0x50); // Add in our own. static bool __fastcall ObjectClass_Unlimbo_Parachuted_Wrapper(BulletClass* pThis, void*, const CoordStruct& coords, DirType facing) { - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); if (pTypeExt->Parachuted) return pThis->SpawnParachuted(coords); @@ -575,14 +575,14 @@ DEFINE_HOOK(0x5F5A62, ObjectClass_SpawnParachuted_BombParachute, 0x5) GET(BulletClass*, pThis, ESI); GET(CoordStruct*, coords, EDI); - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); auto const pAnimType = pTypeExt->BombParachute.Get(RulesClass::Instance->BombParachute); AnimClass* pAnim = nullptr; if (pAnimType) { pAnim = GameCreate(pAnimType, *coords); - pAnim->Owner = pThis->Owner ? pThis->Owner->Owner : BulletExt::ExtMap.Find(pThis)->FirerHouse; + pAnim->Owner = pThis->Owner ? pThis->Owner->Owner : BulletExt::Fetch(pThis)->FirerHouse; const int schemeIndex = pAnim->Owner ? pAnim->Owner->ColorSchemeIndex : RulesExt::Global()->AnimRemapDefaultColorScheme; pAnim->LightConvert = ColorScheme::Array[schemeIndex]->LightConvert; pThis->Parachute = pAnim; @@ -611,7 +611,7 @@ DEFINE_HOOK(0x4683F2, BulletClass_Draw_ZAdjust, 0x5) GET(BulletClass*, pThis, ESI); GET(int, height, ECX); - auto const pTypeExt = BulletTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = BulletTypeExt::Fetch(pThis->Type); R->EAX(TacticalClass::AdjustForZ(height) - pTypeExt->ZAdjust); diff --git a/src/Ext/Bullet/Trajectories/BombardTrajectory.cpp b/src/Ext/Bullet/Trajectories/BombardTrajectory.cpp index a78e91a527..87fc092797 100644 --- a/src/Ext/Bullet/Trajectories/BombardTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/BombardTrajectory.cpp @@ -168,7 +168,7 @@ void BombardTrajectory::OnAIPreDetonate(BulletClass* pBullet) if (pCoords.DistanceFrom(pBullet->Location) <= pType->TargetSnapDistance.Get()) { - const auto pExt = BulletExt::ExtMap.Find(pBullet); + const auto pExt = BulletExt::Fetch(pBullet); pExt->SnappedToTarget = true; pBullet->SetLocation(pCoords); } @@ -228,7 +228,7 @@ void BombardTrajectory::PrepareForOpenFire(BulletClass* pBullet) middleLocation = CoordStruct { pBullet->TargetCoords.X, pBullet->TargetCoords.Y, static_cast(this->Height) }; } - const auto pExt = BulletExt::ExtMap.Find(pBullet); + const auto pExt = BulletExt::Fetch(pBullet); if (pExt->LaserTrails.size()) { @@ -313,7 +313,7 @@ void BombardTrajectory::CalculateTargetCoords(BulletClass* pBullet) // Add random offset value if (pBullet->Type->Inaccurate) { - const auto pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); + const auto pTypeExt = BulletTypeExt::Fetch(pBullet->Type); const auto offsetMult = 0.0004 * pBullet->SourceCoords.DistanceFrom(pBullet->TargetCoords); const auto offsetMin = static_cast(offsetMult * pTypeExt->BallisticScatter_Min.Get(Leptons(0))); const auto offsetMax = static_cast(offsetMult * pTypeExt->BallisticScatter_Max.Get(Leptons(RulesClass::Instance->BallisticScatter))); @@ -553,7 +553,7 @@ void BombardTrajectory::BulletVelocityChange(BulletClass* pBullet) pBullet->Velocity = BulletVelocity::Empty; } - const auto pExt = BulletExt::ExtMap.Find(pBullet); + const auto pExt = BulletExt::Fetch(pBullet); if (pExt->LaserTrails.size()) { diff --git a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp index ad854caf3b..4149c75406 100644 --- a/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/ParabolaTrajectory.cpp @@ -186,7 +186,7 @@ void ParabolaTrajectory::OnAIPreDetonate(BulletClass* pBullet) if (coords.DistanceFrom(pBullet->Location) <= targetSnapDistance) { - const auto pExt = BulletExt::ExtMap.Find(pBullet); + const auto pExt = BulletExt::Fetch(pBullet); pExt->SnappedToTarget = true; pBullet->SetLocation(coords); return; @@ -247,7 +247,7 @@ void ParabolaTrajectory::PrepareForOpenFire(BulletClass* pBullet) // Add random offset value if (pBullet->Type->Inaccurate) { - const auto pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); + const auto pTypeExt = BulletTypeExt::Fetch(pBullet->Type); const auto offsetMult = 0.0004 * theSourceCoords.DistanceFrom(theTargetCoords); const auto offsetMin = static_cast(offsetMult * pTypeExt->BallisticScatter_Min.Get(Leptons(0))); const auto offsetMax = static_cast(offsetMult * pTypeExt->BallisticScatter_Max.Get(Leptons(RulesClass::Instance->BallisticScatter))); @@ -882,7 +882,7 @@ bool ParabolaTrajectory::CalculateBulletVelocityAfterBounce(BulletClass* pBullet if (pType->BounceDetonate) { const auto pFirer = pBullet->Owner; - const auto pOwner = pFirer ? pFirer->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse; + const auto pOwner = pFirer ? pFirer->Owner : BulletExt::Fetch(pBullet)->FirerHouse; WarheadTypeExt::DetonateAt(pBullet->WH, pBullet->Location, pFirer, pBullet->Health, pOwner); } diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index b3aeacb4b9..b99a33b5c9 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -439,7 +439,7 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI_Trajectories, 0x6) GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); bool detonate = false; if (auto const pTraj = pExt->Trajectory.get()) @@ -455,7 +455,7 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI_Trajectories, 0x6) DEFINE_HOOK(0x46703E, BulletClass_AI_SkipBridgeCheck1, 0x6) { GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (pExt && pExt->Trajectory) return 0x467B7A; return 0; @@ -465,7 +465,7 @@ DEFINE_HOOK(0x46703E, BulletClass_AI_SkipBridgeCheck1, 0x6) DEFINE_HOOK(0x4674D4, BulletClass_AI_SkipBridgeCheck2, 0x6) { GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (pExt && pExt->Trajectory && pExt->Trajectory->ShouldSkipBridgeCheck()) return 0x467519; return 0; @@ -475,7 +475,7 @@ DEFINE_HOOK(0x467E53, BulletClass_AI_PreDetonation_Trajectories, 0x6) { GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (auto const pTraj = pExt->Trajectory.get()) pTraj->OnAIPreDetonate(pThis); @@ -489,7 +489,7 @@ DEFINE_HOOK(0x46745C, BulletClass_AI_Position_Trajectories, 0x7) LEA_STACK(BulletVelocity*, pSpeed, STACK_OFFSET(0x1AC, -0x11C)); LEA_STACK(BulletVelocity*, pPosition, STACK_OFFSET(0x1AC, -0x144)); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (auto const pTraj = pExt->Trajectory.get()) pTraj->OnAIVelocity(pThis, pSpeed, pPosition); @@ -523,7 +523,7 @@ DEFINE_HOOK(0x4677D3, BulletClass_AI_TargetCoordCheck_Trajectories, 0x5) GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (auto const pTraj = pExt->Trajectory.get()) { @@ -553,7 +553,7 @@ DEFINE_HOOK(0x467927, BulletClass_AI_TechnoCheck_Trajectories, 0x5) GET(BulletClass*, pThis, EBP); GET(TechnoClass*, pTechno, ESI); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); if (auto const pTraj = pExt->Trajectory.get()) { @@ -579,7 +579,7 @@ DEFINE_HOOK(0x468B72, BulletClass_Unlimbo_Trajectories, 0x5) GET_STACK(CoordStruct*, pCoord, STACK_OFFSET(0x54, 0x4)); GET_STACK(BulletVelocity*, pVelocity, STACK_OFFSET(0x54, 0x8)); - auto const pExt = BulletExt::ExtMap.Find(pThis); + auto const pExt = BulletExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; if (pTypeExt->TrajectoryType) diff --git a/src/Ext/Bullet/Trajectories/SampleTrajectory.cpp b/src/Ext/Bullet/Trajectories/SampleTrajectory.cpp index b2ed5778c6..fd7c9ac2a8 100644 --- a/src/Ext/Bullet/Trajectories/SampleTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/SampleTrajectory.cpp @@ -90,7 +90,7 @@ void SampleTrajectory::OnAIPreDetonate(BulletClass* pBullet) if (pCoords.DistanceFrom(pBullet->Location) <= this->TargetSnapDistance) { - BulletExt::ExtMap.Find(pBullet)->SnappedToTarget = true; + BulletExt::Fetch(pBullet)->SnappedToTarget = true; pBullet->SetLocation(pCoords); } } diff --git a/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp b/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp index f577cd9457..3063c5be09 100644 --- a/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/StraightTrajectory.cpp @@ -184,7 +184,7 @@ bool StraightTrajectory::OnAI(BulletClass* pBullet) if (this->WaitOneFrame && this->BulletPrepareCheck(pBullet)) return false; - const auto pOwner = pBullet->Owner ? pBullet->Owner->Owner : BulletExt::ExtMap.Find(pBullet)->FirerHouse; + const auto pOwner = pBullet->Owner ? pBullet->Owner->Owner : BulletExt::Fetch(pBullet)->FirerHouse; const auto pType = this->Type; if (this->BulletDetonatePreCheck(pBullet)) @@ -234,7 +234,7 @@ void StraightTrajectory::OnAIPreDetonate(BulletClass* pBullet) // Whether to snap to target? if (coords.DistanceFrom(pBullet->Location) <= targetSnapDistance) { - const auto pExt = BulletExt::ExtMap.Find(pBullet); + const auto pExt = BulletExt::Fetch(pBullet); pExt->SnappedToTarget = true; pBullet->SetLocation(coords); } @@ -347,7 +347,7 @@ void StraightTrajectory::PrepareForOpenFire(BulletClass* pBullet) // Add random offset value if (pBullet->Type->Inaccurate) { - const auto pTypeExt = BulletTypeExt::ExtMap.Find(pBullet->Type); + const auto pTypeExt = BulletTypeExt::Fetch(pBullet->Type); const auto offsetMult = 0.0004 * theSourceCoords.DistanceFrom(theTargetCoords); const auto offsetMin = static_cast(offsetMult * pTypeExt->BallisticScatter_Min.Get(Leptons(0))); const auto offsetMax = static_cast(offsetMult * pTypeExt->BallisticScatter_Max.Get(Leptons(RulesClass::Instance->BallisticScatter))); diff --git a/src/Ext/BulletType/Body.cpp b/src/Ext/BulletType/Body.cpp index 28922e6f9b..197375377c 100644 --- a/src/Ext/BulletType/Body.cpp +++ b/src/Ext/BulletType/Body.cpp @@ -4,7 +4,7 @@ BulletTypeExt::ExtContainer BulletTypeExt::ExtMap; double BulletTypeExt::GetAdjustedGravity(BulletTypeClass* pType) { - auto const pData = BulletTypeExt::ExtMap.Find(pType); + auto const pData = BulletTypeExt::Fetch(pType); auto const nGravity = pData->Gravity.Get(RulesClass::Instance->Gravity); return pType->Floater ? nGravity * 0.5 : nGravity; } @@ -22,7 +22,7 @@ BulletTypeClass* BulletTypeExt::GetDefaultBulletType() // ============================= // load / save -void BulletTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void BulletTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -98,7 +98,7 @@ void BulletTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->TrajectoryValidation(); } -void BulletTypeExt::ExtData::TrajectoryValidation() const +void BulletTypeExt::TrajectoryValidation() const { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -133,7 +133,7 @@ void BulletTypeExt::ExtData::TrajectoryValidation() const } template -void BulletTypeExt::ExtData::Serialize(T& Stm) +void BulletTypeExt::Serialize(T& Stm) { Stm .Process(this->Armor) @@ -194,15 +194,15 @@ void BulletTypeExt::ExtData::Serialize(T& Stm) ; } -void BulletTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void BulletTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void BulletTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void BulletTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 65d40a6373..fdadda4c5c 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -9,162 +9,161 @@ #include -class BulletTypeExt +class BulletTypeExt final : public ObjectTypeExt { public: using base_type = BulletTypeClass; + using ExtData = BulletTypeExt; static constexpr DWORD Canary = 0xF00DF00D; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + BulletTypeClass* OwnerObject() const { - public: - // typed owner accessor - BulletTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - // Valueable Strength; //Use OwnerObject()->ObjectTypeClass::Strength - Nullable Armor; - Valueable Interceptable; - Valueable Interceptable_DeleteOnIntercept; - Valueable Interceptable_WeaponOverride; - ValueableIdxVector LaserTrail_Types; - Nullable Gravity; - Valueable Vertical_AircraftFix; - Nullable VerticalInitialFacing; - - TrajectoryTypePointer TrajectoryType; - - Valueable Shrapnel_AffectsGround; - Valueable Shrapnel_AffectsBuildings; - Valueable Shrapnel_UseWeaponTargeting; - Nullable Shrapnel_IgnoreHitBuildings; - Nullable Shrapnel_ObeyWarheadTriggerConditions; - Nullable SubjectToLand; - Valueable SubjectToLand_Detonate; - Nullable SubjectToWater; - Valueable SubjectToWater_Detonate; - - Valueable ClusterScatter_Min; - Valueable ClusterScatter_Max; - - Valueable AAOnly; - Valueable Arcing_AllowElevationInaccuracy; - Valueable ReturnWeapon; - Valueable ReturnWeapon_ApplyFirepowerMult; - - Valueable SubjectToGround; - - Valueable Splits; - Valueable AirburstSpread; - Valueable RetargetAccuracy; - Valueable RetargetSelf; - Valueable RetargetSelf_Probability; - Nullable AroundTarget; - Valueable Airburst_UseCluster; - Valueable Airburst_RandomClusters; - Valueable Airburst_TargetAsSource; - Valueable Airburst_TargetAsSource_SkipHeight; - Valueable Splits_TargetingDistance; - Valueable Splits_TargetingDistance_Cylindrical; - Valueable Splits_AllowRepeatTargets; - Valueable Splits_TargetCellRange; - Valueable Splits_UseWeaponTargeting; - Valueable AirburstWeapon_ApplyFirepowerMult; - Valueable AirburstWeapon_SourceScatterMin; - Valueable AirburstWeapon_SourceScatterMax; - Valueable AirburstWeapon_UseFiringEffects; - Valueable AirburstWeapon_HeadToTarget; - Valueable AirburstWeapon_RadialFireSegments; - - Valueable Parachuted; - Valueable Parachuted_FallRate; - Nullable Parachuted_MaxFallRate; - Nullable BombParachute; - - Valueable AU; - - Valueable ZAdjust; - - // Ares 0.7 - Nullable BallisticScatter_Min; - Nullable BallisticScatter_Max; - - ExtData(BulletTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , Armor {} - , Interceptable { false } - , Interceptable_DeleteOnIntercept { false } - , Interceptable_WeaponOverride {} - , LaserTrail_Types {} - , Gravity {} - , Vertical_AircraftFix { true } - , VerticalInitialFacing {} - , TrajectoryType { } - , Shrapnel_AffectsGround { false } - , Shrapnel_AffectsBuildings { false } - , Shrapnel_UseWeaponTargeting { false } - , Shrapnel_IgnoreHitBuildings {} - , Shrapnel_ObeyWarheadTriggerConditions {} - , ClusterScatter_Min { Leptons(256) } - , ClusterScatter_Max { Leptons(512) } - , BallisticScatter_Min {} - , BallisticScatter_Max {} - , SubjectToLand {} - , SubjectToLand_Detonate { true } - , SubjectToWater {} - , SubjectToWater_Detonate { true } - , AAOnly { false } - , Arcing_AllowElevationInaccuracy { true } - , ReturnWeapon {} - , ReturnWeapon_ApplyFirepowerMult { false } - , SubjectToGround { false } - , Splits { false } - , AirburstSpread { 1.5 } - , RetargetAccuracy { 0.0 } - , RetargetSelf { true } - , RetargetSelf_Probability { 0.5 } - , AroundTarget {} - , Airburst_UseCluster { false } - , Airburst_RandomClusters { false } - , Airburst_TargetAsSource { false } - , Airburst_TargetAsSource_SkipHeight { false } - , Splits_TargetingDistance{ Leptons(1280) } - , Splits_TargetingDistance_Cylindrical { false } - , Splits_AllowRepeatTargets { false } - , Splits_TargetCellRange { 3 } - , Splits_UseWeaponTargeting { false } - , AirburstWeapon_ApplyFirepowerMult { false } - , AirburstWeapon_SourceScatterMin { Leptons(0) } - , AirburstWeapon_SourceScatterMax { Leptons(0) } - , AirburstWeapon_UseFiringEffects { false } - , AirburstWeapon_HeadToTarget { false } - , AirburstWeapon_RadialFireSegments { 0 } - , Parachuted { false } - , Parachuted_FallRate { 1 } - , Parachuted_MaxFallRate {} - , BombParachute {} - , AU { false } - , ZAdjust { 0 } - { } - - virtual ~ExtData() = default; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - // virtual void Initialize() override; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - - void TrajectoryValidation() const; - }; + return static_cast(this->GetAttachedObject()); + } + + // Valueable Strength; //Use OwnerObject()->ObjectTypeClass::Strength + Nullable Armor; + Valueable Interceptable; + Valueable Interceptable_DeleteOnIntercept; + Valueable Interceptable_WeaponOverride; + ValueableIdxVector LaserTrail_Types; + Nullable Gravity; + Valueable Vertical_AircraftFix; + Nullable VerticalInitialFacing; + + TrajectoryTypePointer TrajectoryType; + + Valueable Shrapnel_AffectsGround; + Valueable Shrapnel_AffectsBuildings; + Valueable Shrapnel_UseWeaponTargeting; + Nullable Shrapnel_IgnoreHitBuildings; + Nullable Shrapnel_ObeyWarheadTriggerConditions; + Nullable SubjectToLand; + Valueable SubjectToLand_Detonate; + Nullable SubjectToWater; + Valueable SubjectToWater_Detonate; + + Valueable ClusterScatter_Min; + Valueable ClusterScatter_Max; + + Valueable AAOnly; + Valueable Arcing_AllowElevationInaccuracy; + Valueable ReturnWeapon; + Valueable ReturnWeapon_ApplyFirepowerMult; + + Valueable SubjectToGround; + + Valueable Splits; + Valueable AirburstSpread; + Valueable RetargetAccuracy; + Valueable RetargetSelf; + Valueable RetargetSelf_Probability; + Nullable AroundTarget; + Valueable Airburst_UseCluster; + Valueable Airburst_RandomClusters; + Valueable Airburst_TargetAsSource; + Valueable Airburst_TargetAsSource_SkipHeight; + Valueable Splits_TargetingDistance; + Valueable Splits_TargetingDistance_Cylindrical; + Valueable Splits_AllowRepeatTargets; + Valueable Splits_TargetCellRange; + Valueable Splits_UseWeaponTargeting; + Valueable AirburstWeapon_ApplyFirepowerMult; + Valueable AirburstWeapon_SourceScatterMin; + Valueable AirburstWeapon_SourceScatterMax; + Valueable AirburstWeapon_UseFiringEffects; + Valueable AirburstWeapon_HeadToTarget; + Valueable AirburstWeapon_RadialFireSegments; + + Valueable Parachuted; + Valueable Parachuted_FallRate; + Nullable Parachuted_MaxFallRate; + Nullable BombParachute; + + Valueable AU; + + Valueable ZAdjust; + + // Ares 0.7 + Nullable BallisticScatter_Min; + Nullable BallisticScatter_Max; + + BulletTypeExt(BulletTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , Armor {} + , Interceptable { false } + , Interceptable_DeleteOnIntercept { false } + , Interceptable_WeaponOverride {} + , LaserTrail_Types {} + , Gravity {} + , Vertical_AircraftFix { true } + , VerticalInitialFacing {} + , TrajectoryType { } + , Shrapnel_AffectsGround { false } + , Shrapnel_AffectsBuildings { false } + , Shrapnel_UseWeaponTargeting { false } + , Shrapnel_IgnoreHitBuildings {} + , Shrapnel_ObeyWarheadTriggerConditions {} + , ClusterScatter_Min { Leptons(256) } + , ClusterScatter_Max { Leptons(512) } + , BallisticScatter_Min {} + , BallisticScatter_Max {} + , SubjectToLand {} + , SubjectToLand_Detonate { true } + , SubjectToWater {} + , SubjectToWater_Detonate { true } + , AAOnly { false } + , Arcing_AllowElevationInaccuracy { true } + , ReturnWeapon {} + , ReturnWeapon_ApplyFirepowerMult { false } + , SubjectToGround { false } + , Splits { false } + , AirburstSpread { 1.5 } + , RetargetAccuracy { 0.0 } + , RetargetSelf { true } + , RetargetSelf_Probability { 0.5 } + , AroundTarget {} + , Airburst_UseCluster { false } + , Airburst_RandomClusters { false } + , Airburst_TargetAsSource { false } + , Airburst_TargetAsSource_SkipHeight { false } + , Splits_TargetingDistance{ Leptons(1280) } + , Splits_TargetingDistance_Cylindrical { false } + , Splits_AllowRepeatTargets { false } + , Splits_TargetCellRange { 3 } + , Splits_UseWeaponTargeting { false } + , AirburstWeapon_ApplyFirepowerMult { false } + , AirburstWeapon_SourceScatterMin { Leptons(0) } + , AirburstWeapon_SourceScatterMax { Leptons(0) } + , AirburstWeapon_UseFiringEffects { false } + , AirburstWeapon_HeadToTarget { false } + , AirburstWeapon_RadialFireSegments { 0 } + , Parachuted { false } + , Parachuted_FallRate { 1 } + , Parachuted_MaxFallRate {} + , BombParachute {} + , AU { false } + , ZAdjust { 0 } + { } + + virtual ~BulletTypeExt() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + // virtual void Initialize() override; + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); + + void TrajectoryValidation() const; +public: class ExtContainer final : public Container { public: @@ -174,9 +173,17 @@ class BulletTypeExt static ExtContainer ExtMap; + static BulletTypeExt* Fetch(const BulletTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static BulletTypeExt* TryFetch(const BulletTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static double GetAdjustedGravity(BulletTypeClass* pType); static BulletTypeClass* GetDefaultBulletType(); }; -// top-level name for the BulletTypeExt extension -using BulletTypeClassExtension = BulletTypeExt::ExtData; diff --git a/src/Ext/CaptureManager/Body.cpp b/src/Ext/CaptureManager/Body.cpp index 332029c3f6..1f36629c92 100644 --- a/src/Ext/CaptureManager/Body.cpp +++ b/src/Ext/CaptureManager/Body.cpp @@ -10,7 +10,7 @@ int CaptureManagerExt::GetControlledTotalSize(CaptureManagerClass* pManager) for (const auto pNode : pManager->ControlNodes) { if (const auto pTechno = pNode->Unit) - totalSize += TechnoExt::ExtMap.Find(pTechno)->TypeExtData->MindControlSize; + totalSize += TechnoExt::Fetch(pTechno)->TypeExtData->MindControlSize; } return totalSize; @@ -79,14 +79,14 @@ bool CaptureManagerExt::CanCapture(CaptureManagerClass* pManager, TechnoClass* p // free slot? (move on if infinite or single slot which will be freed if used) if (!pManager->InfiniteMindControl && pManager->MaxControlNodes != 1) { - const auto pOwnerTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData; + const auto pOwnerTypeExt = TechnoExt::Fetch(pOwner)->TypeExtData; if (!pOwnerTypeExt->MindControl_IgnoreSize) { const int totalSize = CaptureManagerExt::GetControlledTotalSize(pManager); const int available = pOwnerTypeExt->MultiMindControl_ReleaseVictim ? pManager->MaxControlNodes : pManager->MaxControlNodes - totalSize; - if (TechnoTypeExt::ExtMap.Find(pTargetType)->MindControlSize > available) + if (TechnoTypeExt::Fetch(pTargetType)->MindControlSize > available) return false; } else @@ -138,7 +138,7 @@ bool CaptureManagerExt::FreeUnit(CaptureManagerClass* pManager, TechnoClass* pTa const auto pOriginOwner = pNode->OriginalOwner->Defeated ? HouseClass::FindNeutral() : pNode->OriginalOwner; - TechnoExt::ExtMap.Find(pTarget)->BeControlledThreatFrame = 0; + TechnoExt::Fetch(pTarget)->BeControlledThreatFrame = 0; pTarget->SetOwningHouse(pOriginOwner, !silent); pManager->DecideUnitFate(pTarget); @@ -172,7 +172,7 @@ bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass* } else if (pManager->ControlNodes.Count > 0 && removeFirst) { - const auto pOwnerTypeExt = TechnoTypeExt::ExtMap.Find(pManager->Owner->GetTechnoType()); + const auto pOwnerTypeExt = TechnoTypeExt::Fetch(pManager->Owner->GetTechnoType()); if (pOwnerTypeExt->MindControl_IgnoreSize) { @@ -181,7 +181,7 @@ bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass* } else { - const auto pTargetTypeExt = TechnoTypeExt::ExtMap.Find(pTarget->GetTechnoType()); + const auto pTargetTypeExt = TechnoTypeExt::Fetch(pTarget->GetTechnoType()); while (pManager->ControlNodes.Count && pTargetTypeExt->MindControlSize > pManager->MaxControlNodes - CaptureManagerExt::GetControlledTotalSize(pManager)) CaptureManagerExt::FreeUnit(pManager, pManager->ControlNodes[0]->Unit); @@ -197,7 +197,7 @@ bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, TechnoClass* pControlNode->LinkDrawTimer.Start(RulesClass::Instance->MindControlAttackLineFrames); if (threatDelay > 0) - TechnoExt::ExtMap.Find(pTarget)->BeControlledThreatFrame = Unsorted::CurrentFrame + threatDelay; + TechnoExt::Fetch(pTarget)->BeControlledThreatFrame = Unsorted::CurrentFrame + threatDelay; auto const pOwner = pManager->Owner; @@ -237,7 +237,7 @@ bool CaptureManagerExt::CaptureUnit(CaptureManagerClass* pManager, AbstractClass { if (const auto pTarget = generic_cast(pTechno)) { - const auto pTechnoTypeExt = TechnoExt::ExtMap.Find(pManager->Owner)->TypeExtData; + const auto pTechnoTypeExt = TechnoExt::Fetch(pManager->Owner)->TypeExtData; return CaptureManagerExt::CaptureUnit(pManager, pTarget, pTechnoTypeExt->MultiMindControl_ReleaseVictim, pControlledAnimType, false, threatDelay); } diff --git a/src/Ext/CaptureManager/Hooks.cpp b/src/Ext/CaptureManager/Hooks.cpp index bcd59daf6e..b7e971d732 100644 --- a/src/Ext/CaptureManager/Hooks.cpp +++ b/src/Ext/CaptureManager/Hooks.cpp @@ -34,7 +34,7 @@ DEFINE_HOOK(0x471C90, CaptureManagerClass_CanCapture, 0x6) static int __fastcall _GetControlledCount(CaptureManagerClass* pThis) { - const auto pOwnerTypeExt = TechnoExt::ExtMap.Find(pThis->Owner)->TypeExtData; + const auto pOwnerTypeExt = TechnoExt::Fetch(pThis->Owner)->TypeExtData; if (!pOwnerTypeExt->MindControl_IgnoreSize) return CaptureManagerExt::GetControlledTotalSize(pThis); @@ -94,7 +94,7 @@ DEFINE_HOOK(0x4721E6, CaptureManagerClass_DrawLinkToVictim, 0x6) GET_STACK(const int, nNodeCount, STACK_OFFSET(0x30, -0x1C)); auto const pAttacker = pThis->Owner; - auto const pExt = TechnoExt::ExtMap.Find(pAttacker)->TypeExtData; + auto const pExt = TechnoExt::Fetch(pAttacker)->TypeExtData; if (EnumFunctions::CanTargetHouse(pExt->MindControlLink_VisibleToHouse, pAttacker->Owner, HouseClass::CurrentPlayer)) { @@ -111,7 +111,7 @@ DEFINE_HOOK(0x4721E6, CaptureManagerClass_DrawLinkToVictim, 0x6) static void __fastcall CaptureManagerClass_Overload_AI(CaptureManagerClass* pThis, void* _) { auto const pOwner = pThis->Owner; - auto const pOwnerTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData; + auto const pOwnerTypeExt = TechnoExt::Fetch(pOwner)->TypeExtData; if (!pOwnerTypeExt) // we cant find type Ext for this , just return to original function ! { diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index bd656b5875..a65ce39e82 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -6,7 +6,7 @@ CellExt::ExtContainer CellExt::ExtMap; // load / save template -void CellExt::ExtData::Serialize(T& Stm) +void CellExt::Serialize(T& Stm) { Stm .Process(this->RadSites) @@ -15,13 +15,13 @@ void CellExt::ExtData::Serialize(T& Stm) ; } -void CellExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void CellExt::LoadFromStream(PhobosStreamReader& Stm) { AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } -void CellExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void CellExt::SaveToStream(PhobosStreamWriter& Stm) { AbstractExt::SaveToStream(Stm); this->Serialize(Stm); diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 6e41fa6cf7..4b77fb5b50 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -4,10 +4,11 @@ #include #include -class CellExt +class CellExt final : public AbstractExt { public: using base_type = CellClass; + using ExtData = CellExt; static constexpr DWORD Canary = 0x13371337; static constexpr size_t ExtPointerOffset = 0x18; @@ -29,32 +30,30 @@ class CellExt bool Serialize(T& stm); }; - class ExtData final : public AbstractExt +public: + // typed owner accessor + CellClass* OwnerObject() const { - public: - // typed owner accessor - CellClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - std::vector RadSites {}; - std::vector RadLevels { }; - int InfantryCount{ 0 }; + std::vector RadSites {}; + std::vector RadLevels { }; + int InfantryCount{ 0 }; - ExtData(CellClass* OwnerObject) : AbstractExt(OwnerObject) - { } + CellExt(CellClass* OwnerObject) : AbstractExt(OwnerObject) + { } - virtual ~ExtData() = default; + virtual ~CellExt() = default; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -64,7 +63,15 @@ class CellExt }; static ExtContainer ExtMap; + + static CellExt* Fetch(const CellClass* pThis) + { + return ExtMap.Find(pThis); + } + + static CellExt* TryFetch(const CellClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; -// top-level name for the CellExt extension -using CellClassExtension = CellExt::ExtData; diff --git a/src/Ext/EBolt/Body.cpp b/src/Ext/EBolt/Body.cpp index c686b23280..e4450f2ca4 100644 --- a/src/Ext/EBolt/Body.cpp +++ b/src/Ext/EBolt/Body.cpp @@ -12,7 +12,7 @@ EBolt* EBoltExt::CreateEBolt(WeaponTypeClass* pWeapon) const int alternateIdx = pWeapon->IsAlternateColor ? 5 : 10; const int defaultAlternate = EBoltExt::GetDefaultColor_Int(FileSystem::PALETTE_PAL, alternateIdx); const int defaultWhite = EBoltExt::GetDefaultColor_Int(FileSystem::PALETTE_PAL, 15); - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const auto& boltDisable = pWeaponExt->Bolt_Disable; const auto& boltColor = pWeaponExt->Bolt_Color; diff --git a/src/Ext/EBolt/Body.h b/src/Ext/EBolt/Body.h index bc803453ce..5af7c3302c 100644 --- a/src/Ext/EBolt/Body.h +++ b/src/Ext/EBolt/Body.h @@ -59,5 +59,4 @@ class EBoltExt static DWORD _cdecl _EBolt_Draw_Colors(REGISTERS* R); }; -// top-level name for the EBoltExt extension using EBoltExtension = EBoltExt::ExtData; diff --git a/src/Ext/EBolt/Hooks.cpp b/src/Ext/EBolt/Hooks.cpp index bf07dc0c69..750b1421ca 100644 --- a/src/Ext/EBolt/Hooks.cpp +++ b/src/Ext/EBolt/Hooks.cpp @@ -29,7 +29,7 @@ DEFINE_HOOK(0x6FD55F, TechnoClass_FireEBolt_ParticleSystem, 0x5) GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0x30, 0x8)); GET_STACK(EBolt*, pBolt, STACK_OFFSET(0x30, -0x20)); - if (const auto particle = WeaponTypeExt::ExtMap.Find(pWeapon)->Bolt_ParticleSystem.Get(RulesClass::Instance->DefaultSparkSystem)) + if (const auto particle = WeaponTypeExt::Fetch(pWeapon)->Bolt_ParticleSystem.Get(RulesClass::Instance->DefaultSparkSystem)) GameCreate(particle, pBolt->Point2, nullptr, nullptr, CoordStruct::Empty, nullptr); return 0; @@ -108,7 +108,7 @@ void EBoltFake::_SetOwner(TechnoClass* pTechno, int weaponIndex) if (pTechno && pTechno->IsAlive) { auto const pWeapon = pTechno->GetWeapon(weaponIndex)->WeaponType; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (!pWeaponExt->Bolt_FollowFLH.Get(pTechno->WhatAmI() == AbstractType::Unit)) return; @@ -116,14 +116,14 @@ void EBoltFake::_SetOwner(TechnoClass* pTechno, int weaponIndex) this->Owner = pTechno; this->WeaponSlot = weaponIndex; - auto const pExt = TechnoExt::ExtMap.Find(pTechno); + auto const pExt = TechnoExt::Fetch(pTechno); pExt->ElectricBolts.push_back(this); } } void EBoltFake::_RemoveFromOwner() { - auto const pExt = TechnoExt::ExtMap.Find(this->Owner); + auto const pExt = TechnoExt::Fetch(this->Owner); auto& vec = pExt->ElectricBolts; vec.erase(std::remove(vec.begin(), vec.end(), this), vec.end()); this->Owner = nullptr; @@ -194,7 +194,7 @@ DEFINE_HOOK(0x6FD4E4, TechnoClass_FireEBolt_Building_ClampPositive, 0x9) int zAdjust = Y - pV13->Y; - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const bool clamp = pWeaponExt->EBoltZAdjust_ClampInitialDepthForBuilding.Get(RulesExt::Global()->EBoltZAdjust_ClampInitialDepthForBuilding); if (clamp && zAdjust > 0) @@ -211,7 +211,7 @@ DEFINE_HOOK(0x6FD4ED, TechnoClass_FireEBolt_ZAdjust, 0x7) GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0x30, 0x8)); GET(int, zAdjust, ESI); - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); zAdjust += pWeaponExt->EBoltZAdjust.Get(RulesExt::Global()->EBoltZAdjust); R->ESI(zAdjust); diff --git a/src/Ext/Event/Body.cpp b/src/Ext/Event/Body.cpp index da66f35b02..dc81499600 100644 --- a/src/Ext/Event/Body.cpp +++ b/src/Ext/Event/Body.cpp @@ -120,7 +120,7 @@ void EventExt::RespondToTogglePlayerAutoRepair() return; auto pHouse = HouseClass::Array.GetItem(this->HouseIndex); - auto pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto pHouseExt = HouseExt::Fetch(pHouse); pHouseExt->PlayerAutoRepair = !pHouseExt->PlayerAutoRepair; if (HouseClass::CurrentPlayer == pHouse) diff --git a/src/Ext/Foot/Body.h b/src/Ext/Foot/Body.h index c7539c4c5b..6187d94437 100644 --- a/src/Ext/Foot/Body.h +++ b/src/Ext/Foot/Body.h @@ -4,11 +4,11 @@ #include // Empty intermediate base mirroring FootClass in the extension hierarchy. -// UnitClassExtension / InfantryClassExtension / AircraftClassExtension derive from this. -class FootClassExtension : public TechnoClassExtension +// UnitExt / InfantryExt / AircraftExt derive from this. +class FootExt : public TechnoExt { public: - explicit FootClassExtension(FootClass* const OwnerObject) : TechnoClassExtension(OwnerObject) + explicit FootExt(FootClass* const OwnerObject) : TechnoExt(OwnerObject) { } FootClass* OwnerObject() const diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index b178dd6577..893c657b8b 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -13,7 +13,7 @@ std::vector HouseExt::AIProduction_BestChoices; std::vector HouseExt::AIProduction_BestChoicesNaval; // Based on Ares' rewrite of 0x4FEA60 for 100 unit bugfix. -void HouseExt::ExtData::UpdateVehicleProduction() +void HouseExt::UpdateVehicleProduction() { const auto pThis = this->OwnerObject(); const bool skipGround = pThis->ProducingUnitTypeIndex != -1; @@ -152,7 +152,7 @@ void HouseExt::ExtData::UpdateVehicleProduction() } } -bool HouseExt::ExtData::UpdateHarvesterProduction() +bool HouseExt::UpdateHarvesterProduction() { auto const pThis = this->OwnerObject(); const int AIDifficulty = pThis->GetAIDifficultyIndex(); @@ -201,7 +201,7 @@ bool HouseExt::ExtData::UpdateHarvesterProduction() return false; } -bool HouseExt::ExtData::OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const +bool HouseExt::OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const { if (!pBuilding) return false; @@ -278,7 +278,7 @@ size_t HouseExt::FindBuildableIndex( int HouseExt::ActiveHarvesterCount(HouseClass* pThis) { int result = 0; - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); for (auto const pTechno : pExt->OwnedCountedHarvesters) { @@ -291,11 +291,11 @@ int HouseExt::ActiveHarvesterCount(HouseClass* pThis) int HouseExt::TotalHarvesterCount(HouseClass* pThis) { int result = 0; - auto const pHouseExt = HouseExt::ExtMap.Find(pThis); + auto const pHouseExt = HouseExt::Fetch(pThis); for (auto const pTechno : pHouseExt->OwnedCountedHarvesters) { - auto const pExt = TechnoExt::ExtMap.Find(pTechno); + auto const pExt = TechnoExt::Fetch(pTechno); result += pExt->HasBeenPlacedOnMap; } @@ -342,7 +342,7 @@ void HouseExt::GetAIChronoshiftSupers(HouseClass* pThis, SuperClass*& pSuperCSph if (idxCW < 0) { - auto const pSWTypeExt = SWTypeExt::ExtMap.Find(pSuperCSphere->Type); + auto const pSWTypeExt = SWTypeExt::Fetch(pSuperCSphere->Type); if (pSWTypeExt->SW_PostDependent >= 0) pSuperCWarp = pThis->Supers[pSWTypeExt->SW_PostDependent]; @@ -394,7 +394,7 @@ HouseClass* HouseExt::GetHouseKind(OwnerHouseKind const kind, bool const allowRa } } -void HouseExt::ExtData::AddToLimboTracking(TechnoTypeClass* pTechnoType) +void HouseExt::AddToLimboTracking(TechnoTypeClass* pTechnoType) { if (pTechnoType) { @@ -420,7 +420,7 @@ void HouseExt::ExtData::AddToLimboTracking(TechnoTypeClass* pTechnoType) } } -void HouseExt::ExtData::RemoveFromLimboTracking(TechnoTypeClass* pTechnoType) +void HouseExt::RemoveFromLimboTracking(TechnoTypeClass* pTechnoType) { if (pTechnoType) { @@ -446,7 +446,7 @@ void HouseExt::ExtData::RemoveFromLimboTracking(TechnoTypeClass* pTechnoType) } } -int HouseExt::ExtData::CountOwnedPresentAndLimboed(TechnoTypeClass* pTechnoType) const +int HouseExt::CountOwnedPresentAndLimboed(TechnoTypeClass* pTechnoType) const { int count = this->OwnerObject()->CountOwnedAndPresent(pTechnoType); const int arrayIndex = pTechnoType->GetArrayIndex(); @@ -472,7 +472,7 @@ int HouseExt::ExtData::CountOwnedPresentAndLimboed(TechnoTypeClass* pTechnoType) return count; } -void HouseExt::ExtData::UpdateNonMFBFactoryCounts(AbstractType rtti, bool remove, bool isNaval) +void HouseExt::UpdateNonMFBFactoryCounts(AbstractType rtti, bool remove, bool isNaval) { int* count = nullptr; @@ -502,7 +502,7 @@ void HouseExt::ExtData::UpdateNonMFBFactoryCounts(AbstractType rtti, bool remove *count += remove ? -1 : 1; } -int HouseExt::ExtData::GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNaval) const +int HouseExt::GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNaval) const { auto const pThis = this->OwnerObject(); int count = 0; @@ -535,16 +535,16 @@ int HouseExt::ExtData::GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNa return Math::max(count, 0); } -float HouseExt::ExtData::GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoType) const +float HouseExt::GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoType) const { float mult = 1.0f; - auto const pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + auto const pTechnoTypeExt = TechnoTypeExt::Fetch(pTechnoType); std::unordered_map counts; for (auto const pBuilding : this->RestrictedFactoryPlants) { auto const pType = pBuilding->Type; - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); const int max = pTypeExt->FactoryPlant_MaxCount; if (max > -1 && counts[pType->ArrayIndex] >= max) @@ -586,9 +586,9 @@ float HouseExt::ExtData::GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoT return 1.0f - ((1.0f - mult) * pTechnoTypeExt->FactoryPlant_Multiplier); } -void HouseExt::ForceOnlyTargetHouseEnemy(HouseClass* pThis, int mode) +void HouseExt::SetForceOnlyTargetHouseEnemy(HouseClass* pThis, int mode) { - const auto pHouseExt = HouseExt::ExtMap.Find(pThis); + const auto pHouseExt = HouseExt::Fetch(pThis); if (mode < 0 || mode > 2) mode = -1; @@ -616,7 +616,7 @@ void HouseExt::ForceOnlyTargetHouseEnemy(HouseClass* pThis, int mode) } } -void HouseExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void HouseExt::LoadFromINIFile(CCINIClass* const pINI) { const char* pSection = this->OwnerObject()->PlainName; @@ -635,7 +635,7 @@ void HouseExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) } } -int HouseExt::ExtData::GetForceEnemyIndex() +int HouseExt::GetForceEnemyIndex() { auto const pHouse = this->OwnerObject(); if (!pHouse) @@ -644,7 +644,7 @@ int HouseExt::ExtData::GetForceEnemyIndex() return this->ForceEnemyIndex; } -void HouseExt::ExtData::SetForceEnemyIndex(int EnemyIndex) +void HouseExt::SetForceEnemyIndex(int EnemyIndex) { if (EnemyIndex < 0 && EnemyIndex != -2) this->ForceEnemyIndex = -1; @@ -676,7 +676,7 @@ void HouseExt::CalculatePowerSurplus(HouseClass* pThis) // load / save template -void HouseExt::ExtData::Serialize(T& Stm) +void HouseExt::Serialize(T& Stm) { Stm .Process(this->PowerPlantEnhancers) @@ -716,13 +716,13 @@ void HouseExt::ExtData::Serialize(T& Stm) ; } -void HouseExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void HouseExt::LoadFromStream(PhobosStreamReader& Stm) { AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } -void HouseExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void HouseExt::SaveToStream(PhobosStreamWriter& Stm) { AbstractExt::SaveToStream(Stm); this->Serialize(Stm); @@ -740,7 +740,7 @@ bool HouseExt::SaveGlobals(PhobosStreamWriter& Stm) .Success(); } -void HouseExt::ExtData::OnDetach(BuildingClass* pTarget, bool removed) +void HouseExt::OnDetach(BuildingClass* pTarget, bool removed) { if (removed) { @@ -820,7 +820,7 @@ static int CountOwnedIncludeDeploy(const HouseClass* pThis, const TechnoTypeClas CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const TechnoTypeClass* pItem, bool buildLimitOnly, bool includeQueued) { - const auto pItemExt = TechnoTypeExt::ExtMap.Find(pItem); + const auto pItemExt = TechnoTypeExt::Fetch(pItem); if (pItemExt->BuildLimitGroup_Types.empty()) return CanBuildResult::Buildable; @@ -839,7 +839,7 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec auto const pTmpType = extraLimitTypes[i]; auto const pBuildingType = abstract_cast(pTmpType); - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); else count = pThis->CountOwnedNow(pTmpType); @@ -870,11 +870,11 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec for (size_t i = 0; i < std::min(buildLimit.size(), pItemExt->BuildLimitGroup_Nums.size()); i++) { const auto pType = buildLimit[i]; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); const auto pBuildingType = abstract_cast(pType); int ownedNow = 0; - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) ownedNow = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); else ownedNow = CountOwnedIncludeDeploy(pThis, pType); @@ -896,11 +896,11 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec for (const auto pType : buildLimit) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); const auto pBuildingType = abstract_cast(pType); int owned = 0; - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) owned = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); else owned = CountOwnedIncludeDeploy(pThis, pType); @@ -923,11 +923,11 @@ CanBuildResult HouseExt::BuildLimitGroupCheck(const HouseClass* pThis, const Tec for (size_t i = 0; i < std::min(buildLimit.size(), limits.size()); i++) { const auto pType = buildLimit[i]; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); const auto pBuildingType = abstract_cast(pType); int ownedNow = 0; - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) ownedNow = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pThis)); else ownedNow = CountOwnedIncludeDeploy(pThis, pType); @@ -989,7 +989,7 @@ static void RemoveProduction(const HouseClass* pHouse, const TechnoTypeClass* pT bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass* pType, bool ignoreQueued) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); if (pTypeExt->BuildLimitGroup_Types.empty() || pTypeExt->BuildLimitGroup_Nums.empty()) return false; @@ -1008,7 +1008,7 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass const auto pBuildingType = abstract_cast(pTmpType); int count = 0; - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) count = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pHouse)); else count = pHouse->CountOwnedNow(pTmpType); @@ -1040,7 +1040,7 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass for (const auto pTmpType : pTypeExt->BuildLimitGroup_Types) { - const auto pTmpTypeExt = TechnoTypeExt::ExtMap.Find(pTmpType); + const auto pTmpTypeExt = TechnoTypeExt::Fetch(pTmpType); if (!ignoreQueued) queued += QueuedNum(pHouse, pTmpType) * pTmpTypeExt->BuildLimitGroup_Factor; @@ -1048,7 +1048,7 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass int owned = 0; const auto pBuildingType = abstract_cast(pTmpType); - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) owned = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pHouse)); else owned = pHouse->CountOwnedNow(pTmpType); @@ -1081,12 +1081,12 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass for (size_t i = 0; i < size; i++) { const auto pTmpType = pTypeExt->BuildLimitGroup_Types[i]; - const auto pTmpTypeExt = TechnoTypeExt::ExtMap.Find(pTmpType); + const auto pTmpTypeExt = TechnoTypeExt::Fetch(pTmpType); const int queued = ignoreQueued ? 0 : QueuedNum(pHouse, pTmpType) * pTmpTypeExt->BuildLimitGroup_Factor; int num = 0; const auto pBuildingType = abstract_cast(pTmpType); - if (pBuildingType && (BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) + if (pBuildingType && (BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.size() > 0 || BuildingTypeClass::Find(pBuildingType->PowersUpBuilding))) num = BuildingTypeExt::GetUpgradesAmount(pBuildingType, const_cast(pHouse)); else num = pHouse->CountOwnedNow(pTmpType); diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index aec21302a9..2367e48c25 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -7,143 +7,142 @@ #include -class HouseExt +class HouseExt final : public AbstractExt, public Detach::Listener { public: using base_type = HouseClass; + using ExtData = HouseExt; static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractExt, public Detach::Listener +public: + // typed owner accessor + HouseClass* OwnerObject() const { - public: - // typed owner accessor - HouseClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - std::vector PowerPlantEnhancers; - std::vector OwnedLimboDeliveredBuildings; - std::vector OwnedCountedHarvesters; - bool ForceOnlyTargetHouseEnemy; - int ForceOnlyTargetHouseEnemyMode; - - CounterClass LimboAircraft; // Currently owned aircraft in limbo - CounterClass LimboBuildings; // Currently owned buildings in limbo - CounterClass LimboInfantry; // Currently owned infantry in limbo - CounterClass LimboVehicles; // Currently owned vehicles in limbo - - BuildingClass* Factory_BuildingType; - BuildingClass* Factory_InfantryType; - BuildingClass* Factory_VehicleType; - BuildingClass* Factory_NavyType; - BuildingClass* Factory_AircraftType; - - CDTimerClass CombatAlertTimer; - CDTimerClass AISuperWeaponDelayTimer; - CDTimerClass AIFireSaleDelayTimer; - - //Read from INI - Nullable RepairBaseNodes[3]; - - // FactoryPlants with Allow/DisallowTypes set. - std::vector RestrictedFactoryPlants; - - int LastBuiltNavalVehicleType; - int ProducingNavalUnitTypeIndex; - - // Factories that exist but don't count towards multiple factory bonus. - int NumAirpads_NonMFB; - int NumBarracks_NonMFB; - int NumWarFactories_NonMFB; - int NumConYards_NonMFB; - int NumShipyards_NonMFB; - - std::map> SuspendedEMPulseSWs; - - // standalone? no need and not a good idea - struct SWExt - { - int ShotCount; - }; - std::vector SuperExts; - - int ForceEnemyIndex; - int TeamDelay; - bool FreeRadar; - bool ForceRadar; - - bool PlayerAutoRepair; - - std::array BeaconsPlacedOrder; - - ExtData(HouseClass* OwnerObject) : AbstractExt(OwnerObject) - , PowerPlantEnhancers {} - , OwnedLimboDeliveredBuildings {} - , OwnedCountedHarvesters {} - , LimboAircraft {} - , LimboBuildings {} - , LimboInfantry {} - , LimboVehicles {} - , Factory_BuildingType { nullptr } - , Factory_InfantryType { nullptr } - , Factory_VehicleType { nullptr } - , Factory_NavyType { nullptr } - , Factory_AircraftType { nullptr } - , AISuperWeaponDelayTimer {} - , RepairBaseNodes { } - , RestrictedFactoryPlants {} - , LastBuiltNavalVehicleType { -1 } - , ProducingNavalUnitTypeIndex { -1 } - , CombatAlertTimer {} - , NumAirpads_NonMFB { 0 } - , NumBarracks_NonMFB { 0 } - , NumWarFactories_NonMFB { 0 } - , NumConYards_NonMFB { 0 } - , NumShipyards_NonMFB { 0 } - , AIFireSaleDelayTimer {} - , SuspendedEMPulseSWs {} - , SuperExts(SuperWeaponTypeClass::Array.Count) - , ForceEnemyIndex(-1) - , ForceOnlyTargetHouseEnemy { false } - , ForceOnlyTargetHouseEnemyMode { -1 } - , TeamDelay(-1) - , FreeRadar(false) - , ForceRadar(false) - , PlayerAutoRepair(true) - , BeaconsPlacedOrder { 0, 0, 0 } - { } - - bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const; - void AddToLimboTracking(TechnoTypeClass* pTechnoType); - void RemoveFromLimboTracking(TechnoTypeClass* pTechnoType); - int CountOwnedPresentAndLimboed(TechnoTypeClass* pTechnoType) const; - void UpdateNonMFBFactoryCounts(AbstractType rtti, bool remove, bool isNaval); - int GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNaval) const; - float GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoType) const; - - int GetForceEnemyIndex(); - void SetForceEnemyIndex(int EnemyIndex); - - virtual ~ExtData() = default; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - //virtual void Initialize() override; - virtual void OnDetach(BuildingClass* pTarget, bool removed) override; - - void UpdateVehicleProduction(); - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - bool UpdateHarvesterProduction(); + return static_cast(this->GetAttachedObject()); + } + + std::vector PowerPlantEnhancers; + std::vector OwnedLimboDeliveredBuildings; + std::vector OwnedCountedHarvesters; + bool ForceOnlyTargetHouseEnemy; + int ForceOnlyTargetHouseEnemyMode; + + CounterClass LimboAircraft; // Currently owned aircraft in limbo + CounterClass LimboBuildings; // Currently owned buildings in limbo + CounterClass LimboInfantry; // Currently owned infantry in limbo + CounterClass LimboVehicles; // Currently owned vehicles in limbo + + BuildingClass* Factory_BuildingType; + BuildingClass* Factory_InfantryType; + BuildingClass* Factory_VehicleType; + BuildingClass* Factory_NavyType; + BuildingClass* Factory_AircraftType; + + CDTimerClass CombatAlertTimer; + CDTimerClass AISuperWeaponDelayTimer; + CDTimerClass AIFireSaleDelayTimer; + + //Read from INI + Nullable RepairBaseNodes[3]; + + // FactoryPlants with Allow/DisallowTypes set. + std::vector RestrictedFactoryPlants; + + int LastBuiltNavalVehicleType; + int ProducingNavalUnitTypeIndex; + + // Factories that exist but don't count towards multiple factory bonus. + int NumAirpads_NonMFB; + int NumBarracks_NonMFB; + int NumWarFactories_NonMFB; + int NumConYards_NonMFB; + int NumShipyards_NonMFB; + + std::map> SuspendedEMPulseSWs; + + // standalone? no need and not a good idea + struct SWExt + { + int ShotCount; }; + std::vector SuperExts; + + int ForceEnemyIndex; + int TeamDelay; + bool FreeRadar; + bool ForceRadar; + + bool PlayerAutoRepair; + + std::array BeaconsPlacedOrder; + + HouseExt(HouseClass* OwnerObject) : AbstractExt(OwnerObject) + , PowerPlantEnhancers {} + , OwnedLimboDeliveredBuildings {} + , OwnedCountedHarvesters {} + , LimboAircraft {} + , LimboBuildings {} + , LimboInfantry {} + , LimboVehicles {} + , Factory_BuildingType { nullptr } + , Factory_InfantryType { nullptr } + , Factory_VehicleType { nullptr } + , Factory_NavyType { nullptr } + , Factory_AircraftType { nullptr } + , AISuperWeaponDelayTimer {} + , RepairBaseNodes { } + , RestrictedFactoryPlants {} + , LastBuiltNavalVehicleType { -1 } + , ProducingNavalUnitTypeIndex { -1 } + , CombatAlertTimer {} + , NumAirpads_NonMFB { 0 } + , NumBarracks_NonMFB { 0 } + , NumWarFactories_NonMFB { 0 } + , NumConYards_NonMFB { 0 } + , NumShipyards_NonMFB { 0 } + , AIFireSaleDelayTimer {} + , SuspendedEMPulseSWs {} + , SuperExts(SuperWeaponTypeClass::Array.Count) + , ForceEnemyIndex(-1) + , ForceOnlyTargetHouseEnemy { false } + , ForceOnlyTargetHouseEnemyMode { -1 } + , TeamDelay(-1) + , FreeRadar(false) + , ForceRadar(false) + , PlayerAutoRepair(true) + , BeaconsPlacedOrder { 0, 0, 0 } + { } + + bool OwnsLimboDeliveredBuilding(BuildingClass* pBuilding) const; + void AddToLimboTracking(TechnoTypeClass* pTechnoType); + void RemoveFromLimboTracking(TechnoTypeClass* pTechnoType); + int CountOwnedPresentAndLimboed(TechnoTypeClass* pTechnoType) const; + void UpdateNonMFBFactoryCounts(AbstractType rtti, bool remove, bool isNaval); + int GetFactoryCountWithoutNonMFB(AbstractType rtti, bool isNaval) const; + float GetRestrictedFactoryPlantMult(TechnoTypeClass* pTechnoType) const; + + int GetForceEnemyIndex(); + void SetForceEnemyIndex(int EnemyIndex); + + virtual ~HouseExt() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + //virtual void Initialize() override; + virtual void OnDetach(BuildingClass* pTarget, bool removed) override; + + void UpdateVehicleProduction(); + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); + bool UpdateHarvesterProduction(); +public: class ExtContainer final : public Container { public: @@ -153,6 +152,16 @@ class HouseExt static ExtContainer ExtMap; + static HouseExt* Fetch(const HouseClass* pThis) + { + return ExtMap.Find(pThis); + } + + static HouseExt* TryFetch(const HouseClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -162,7 +171,7 @@ class HouseExt static CellClass* GetEnemyBaseGatherCell(HouseClass* pTargetHouse, HouseClass* pCurrentHouse, CoordStruct defaultCurrentCoords, SpeedType speedTypeZone, int extraDistance = 0); static void GetAIChronoshiftSupers(HouseClass* pThis, SuperClass*& pSuperCSphere, SuperClass*& pSuperCWarp); - static void ForceOnlyTargetHouseEnemy(HouseClass* pThis, int mode = -1); + static void SetForceOnlyTargetHouseEnemy(HouseClass* pThis, int mode = -1); static void SetSkirmishHouseName(HouseClass* pHouse); static bool IsDisabledFromShell( @@ -205,5 +214,3 @@ class HouseExt static void CalculatePowerSurplus(HouseClass* pThis); }; -// top-level name for the HouseExt extension -using HouseClassExtension = HouseExt::ExtData; diff --git a/src/Ext/House/Hooks.AINavalProduction.cpp b/src/Ext/House/Hooks.AINavalProduction.cpp index 52a5b72bb5..986d0f553e 100644 --- a/src/Ext/House/Hooks.AINavalProduction.cpp +++ b/src/Ext/House/Hooks.AINavalProduction.cpp @@ -18,7 +18,7 @@ DEFINE_HOOK(0x444113, BuildingClass_ExitObject_NavalProductionFix1, 0x6) if (pObject->WhatAmI() == AbstractType::Unit && pObject->GetTechnoType()->Naval) { - auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto const pHouseExt = HouseExt::Fetch(pHouse); pHouseExt->ProducingNavalUnitTypeIndex = -1; ExitObjectTemp::ProducingUnitIndex = pHouse->ProducingUnitTypeIndex; } @@ -85,7 +85,7 @@ DEFINE_HOOK(0x450319, BuildingClass_AI_Factory_NavalProductionFix, 0x6) case AbstractType::Unit: case AbstractType::UnitType: - index = !pThis->Type->Naval ? pHouse->ProducingUnitTypeIndex : HouseExt::ExtMap.Find(pHouse)->ProducingNavalUnitTypeIndex; + index = !pThis->Type->Naval ? pHouse->ProducingUnitTypeIndex : HouseExt::Fetch(pHouse)->ProducingNavalUnitTypeIndex; if (index >= 0) pTechnoType = UnitTypeClass::Array.GetItem(index); @@ -107,7 +107,7 @@ DEFINE_HOOK(0x4CA0A1, FactoryClass_Abandon_NavalProductionFix, 0x5) if (pObject->WhatAmI() == AbstractType::Unit && pObject->GetTechnoType()->Naval) { - if (auto const pHouseExt = HouseExt::ExtMap.TryFind(pThis->Owner)) + if (auto const pHouseExt = HouseExt::TryFetch(pThis->Owner)) { pHouseExt->ProducingNavalUnitTypeIndex = -1; return SkipUnitTypeCheck; @@ -123,7 +123,7 @@ DEFINE_HOOK(0x4F91A4, HouseClass_AI_BuildingProductionCheck, 0x6) GET(HouseClass* const, pThis, ESI); - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); bool cantBuild = pThis->ProducingUnitTypeIndex == -1 && pThis->ProducingInfantryTypeIndex == -1 @@ -153,7 +153,7 @@ DEFINE_HOOK(0x4FE0A3, HouseClass_AI_RaiseMoney_NavalProductionFix, 0x6) { GET(HouseClass* const, pThis, ESI); - if (auto const pExt = HouseExt::ExtMap.TryFind(pThis)) + if (auto const pExt = HouseExt::TryFetch(pThis)) pExt->ProducingNavalUnitTypeIndex = -1; return 0; @@ -166,7 +166,7 @@ DEFINE_HOOK(0x4F9250, HouseClass_AI_NavalProductionFix, 0x7) GET(HouseClass* const, pThis, ESI); - HouseExt::ExtMap.Find(pThis)->UpdateVehicleProduction(); + HouseExt::Fetch(pThis)->UpdateVehicleProduction(); return R->Origin() == 0x4F9250 ? SkipGameCodeOne : SkipGameCodeTwo; } @@ -181,7 +181,7 @@ DEFINE_HOOK(0x4FB6FC, HouseClass_JustBuilt_NavalProductionFix, 0x6) if (pUnitType->Naval) { - HouseExt::ExtMap.Find(pThis)->LastBuiltNavalVehicleType = ID; + HouseExt::Fetch(pThis)->LastBuiltNavalVehicleType = ID; return SkipGameCode; } @@ -196,7 +196,7 @@ DEFINE_HOOK(0x71F003, TEventClass_Execute_NavalProductionFix, 0x6) GET(HouseClass* const, pHouse, EAX); if (pHouse->LastBuiltVehicleType != pThis->Value - && HouseExt::ExtMap.Find(pHouse)->LastBuiltNavalVehicleType != pThis->Value) + && HouseExt::Fetch(pHouse)->LastBuiltNavalVehicleType != pThis->Value) { return Skip; } diff --git a/src/Ext/House/Hooks.ForceEnemy.cpp b/src/Ext/House/Hooks.ForceEnemy.cpp index 7257a0a4e7..3930e6b159 100644 --- a/src/Ext/House/Hooks.ForceEnemy.cpp +++ b/src/Ext/House/Hooks.ForceEnemy.cpp @@ -7,7 +7,7 @@ DEFINE_HOOK(0x5047D0, HouseClass_UpdateAngerNodes_SetForceEnemy, 0x6) if (pThis) { - const int forceIndex = HouseExt::ExtMap.Find(pThis)->GetForceEnemyIndex(); + const int forceIndex = HouseExt::Fetch(pThis)->GetForceEnemyIndex(); if (forceIndex >= 0 || forceIndex == -2) { @@ -31,7 +31,7 @@ DEFINE_HOOK(0x4FD772, HouseClass_ClearForceEnemy, 0xA) // HouseClass_UpdateAI if (pThis) { - HouseExt::ExtMap.Find(pThis)->SetForceEnemyIndex(-1); + HouseExt::Fetch(pThis)->SetForceEnemyIndex(-1); pThis->UpdateAngerNodes(0, pThis); return R->Origin() + 0xA; } diff --git a/src/Ext/House/Hooks.UnitFromFactory.cpp b/src/Ext/House/Hooks.UnitFromFactory.cpp index b776706e2e..96fa8fb87b 100644 --- a/src/Ext/House/Hooks.UnitFromFactory.cpp +++ b/src/Ext/House/Hooks.UnitFromFactory.cpp @@ -15,7 +15,7 @@ DEFINE_HOOK(0x4FB64B, HouseClass_UnitFromFactory_VoiceCreated, 0x5) GET(TechnoClass* const, pThisTechno, ESI); GET(FactoryClass* const, pThisFactory, EBX); - auto const pThisTechnoType = TechnoExt::ExtMap.Find(pThisTechno)->TypeExtData; + auto const pThisTechnoType = TechnoExt::Fetch(pThisTechno)->TypeExtData; if (pThisTechno->Owner->IsControlledByCurrentPlayer() && pThisTechnoType->VoiceCreated.isset()) { if (RulesExt::Global()->IsVoiceCreatedGlobal.Get()) diff --git a/src/Ext/House/Hooks.cpp b/src/Ext/House/Hooks.cpp index 15f2a4e92b..65d06149cc 100644 --- a/src/Ext/House/Hooks.cpp +++ b/src/Ext/House/Hooks.cpp @@ -37,7 +37,7 @@ DEFINE_HOOK(0x508D8D, HouseClass_UpdatePower_AfterBuildings, 0x6) const int count = pThis->CountOwnedAndPresent(pType); if (count == 0) return; - const auto pExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pExt = TechnoTypeExt::Fetch(pType); if (pExt->Power > 0) pThis->PowerOutput += pExt->Power * count; else @@ -64,7 +64,7 @@ DEFINE_HOOK(0x73E474, UnitClass_Unload_Storage, 0x6) GET(int const, idxTiberium, EBP); REF_STACK(float, amount, 0x1C); - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pBuilding->Type); + auto const pTypeExt = BuildingTypeExt::Fetch(pBuilding->Type); auto const storageTiberiumIndex = RulesExt::Global()->Storage_TiberiumIndex; @@ -79,14 +79,14 @@ DEFINE_HOOK(0x73E474, UnitClass_Unload_Storage, 0x6) namespace RecalcCenterTemp { - HouseExt::ExtData* pExtData; + HouseExt* pExtData; } DEFINE_HOOK(0x4FD166, HouseClass_RecalcCenter_SetContext, 0x5) { GET(HouseClass* const, pThis, EDI); - RecalcCenterTemp::pExtData = HouseExt::ExtMap.Find(pThis); + RecalcCenterTemp::pExtData = HouseExt::Fetch(pThis); return 0; } @@ -100,7 +100,7 @@ DEFINE_HOOK(0x4FD1CD, HouseClass_RecalcCenter_LimboDelivery, 0x6) if (!MapClass::Instance.CoordinatesLegal(pBuilding->GetMapCoords()) || (RecalcCenterTemp::pExtData && RecalcCenterTemp::pExtData->OwnsLimboDeliveredBuilding(pBuilding)) - || TechnoTypeExt::ExtMap.Find(pBuilding->Type)->IgnoreForBaseCenter) + || TechnoTypeExt::Fetch(pBuilding->Type)->IgnoreForBaseCenter) { return R->Origin() == 0x4FD1CD ? SkipBuilding1 : SkipBuilding2; } @@ -114,7 +114,7 @@ DEFINE_HOOK(0x4AC534, DisplayClass_ComputeStartPosition_IllegalCoords, 0x6) GET(TechnoClass* const, pTechno, ECX); - if (!MapClass::Instance.CoordinatesLegal(pTechno->GetMapCoords()) || TechnoExt::ExtMap.Find(pTechno)->TypeExtData->IgnoreForBaseCenter) + if (!MapClass::Instance.CoordinatesLegal(pTechno->GetMapCoords()) || TechnoExt::Fetch(pTechno)->TypeExtData->IgnoreForBaseCenter) return SkipTechno; return 0; @@ -140,7 +140,7 @@ DEFINE_HOOK(0x687B18, ScenarioClass_ReadINI_StartTracking, 0x7) if (!pType->Insignificant && !pType->DontScore && pTechno->WhatAmI() != AbstractType::Building && pTechno->InLimbo) { - auto const pOwnerExt = HouseExt::ExtMap.Find(pTechno->Owner); + auto const pOwnerExt = HouseExt::Fetch(pTechno->Owner); pOwnerExt->AddToLimboTracking(pType); } } @@ -158,7 +158,7 @@ static void __fastcall TechnoClass_UnInit_Wrapper(TechnoClass* pThis) auto const pType = pThis->GetTechnoType(); if (!pType->Insignificant && !pType->DontScore) - HouseExt::ExtMap.Find(pThis->Owner)->RemoveFromLimboTracking(pType); + HouseExt::Fetch(pThis->Owner)->RemoveFromLimboTracking(pType); } ++LimboTrackingTemp::IsBeingDeleted; @@ -177,7 +177,7 @@ DEFINE_HOOK(0x6F6BC9, TechnoClass_Limbo_AddTracking, 0x6) if (LimboTrackingTemp::Enabled && !pType->Insignificant && !pType->DontScore && !LimboTrackingTemp::IsBeingDeleted) { - auto const pOwnerExt = HouseExt::ExtMap.Find(pThis->Owner); + auto const pOwnerExt = HouseExt::Fetch(pThis->Owner); pOwnerExt->AddToLimboTracking(pType); } @@ -189,11 +189,11 @@ DEFINE_HOOK(0x6F6D85, TechnoClass_Unlimbo_RemoveTracking, 0x6) GET(TechnoClass* const, pThis, ESI); auto const pType = pThis->GetTechnoType(); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (LimboTrackingTemp::Enabled && !pType->Insignificant && !pType->DontScore && pExt->HasBeenPlacedOnMap) { - auto const pOwnerExt = HouseExt::ExtMap.Find(pThis->Owner); + auto const pOwnerExt = HouseExt::Fetch(pThis->Owner); pOwnerExt->RemoveFromLimboTracking(pType); } else if (!pExt->HasBeenPlacedOnMap) @@ -212,7 +212,7 @@ DEFINE_HOOK(0x7015C9, TechnoClass_Captured_UpdateTracking, 0x6) GET(TechnoClass* const, pThis, ESI); GET(HouseClass* const, pNewOwner, EBP); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; if (pTypeExt->AutoDeath_Behavior.isset()) @@ -241,8 +241,8 @@ DEFINE_HOOK(0x7015C9, TechnoClass_Captured_UpdateTracking, 0x6) } auto const pType = pTypeExt->OwnerObject(); - auto const pOwnerExt = HouseExt::ExtMap.Find(pThis->Owner); - auto const pNewOwnerExt = HouseExt::ExtMap.Find(pNewOwner); + auto const pOwnerExt = HouseExt::Fetch(pThis->Owner); + auto const pNewOwnerExt = HouseExt::Fetch(pNewOwner); if (LimboTrackingTemp::Enabled && !pType->Insignificant && !pType->DontScore && pThis->InLimbo) { @@ -470,7 +470,7 @@ DEFINE_HOOK(0x4F9038, HouseClass_AI_Superweapons, 0x5) if (delay > 0) { - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); if (pExt->AISuperWeaponDelayTimer.HasTimeLeft()) return 0; @@ -491,12 +491,12 @@ DEFINE_HOOK(0x4FF9C9, HouseClass_ExcludeFromMultipleFactoryBonus, 0x6) auto const pType = pBuilding->Type; - if (BuildingTypeExt::ExtMap.Find(pType)->ExcludeFromMultipleFactoryBonus) + if (BuildingTypeExt::Fetch(pType)->ExcludeFromMultipleFactoryBonus) { GET(HouseClass*, pThis, EDI); GET(const bool, isNaval, ECX); - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); pExt->UpdateNonMFBFactoryCounts(pType->Factory, R->Origin() == 0x4FF9C9, isNaval); } @@ -511,7 +511,7 @@ DEFINE_HOOK(0x500910, HouseClass_GetFactoryCount, 0x5) GET_STACK(AbstractType, rtti, 0x4); GET_STACK(const bool, isNaval, 0x8); - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); R->EAX(pExt->GetFactoryCountWithoutNonMFB(rtti, isNaval)); return SkipGameCode; @@ -526,7 +526,7 @@ DEFINE_HOOK(0x4FD8F7, HouseClass_UpdateAI_OnLastLegs, 0x10) if (RulesExt::Global()->AIFireSale) { - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); if (RulesExt::Global()->AIFireSaleDelay <= 0 || pExt->AIFireSaleDelayTimer.Completed()) pThis->Fire_Sale(); @@ -546,7 +546,7 @@ DEFINE_HOOK(0x4F8ACC, HouseClass_Update_ResetTeamDelay, 0x6) GET(HouseClass*, pThis, ESI); - const auto pHouseExt = HouseExt::ExtMap.Find(pThis); + const auto pHouseExt = HouseExt::Fetch(pThis); const int teamDelay = pHouseExt->TeamDelay; if (teamDelay >= 0) @@ -624,7 +624,7 @@ DEFINE_HOOK(0x508E17, HouseClass_UpdateRadar_FreeRadar, 0x8) GET(HouseClass*, pThis, ECX); REF_STACK(bool, enableRadar, STACK_OFFSET(0x1C, -0xC)); - auto const pExt = HouseExt::ExtMap.Find(pThis); + auto const pExt = HouseExt::Fetch(pThis); bool const freeRadar = pExt->FreeRadar; enableRadar = false; @@ -684,7 +684,7 @@ DEFINE_HOOK(0x50BF60, HouseClass_CalculateCostMultipliers, 0x5) for (auto const& pBuilding : pThis->FactoryPlants) { auto const pType = pBuilding->Type; - auto const pTypeExt = BuildingTypeExt::ExtMap.Find(pType); + auto const pTypeExt = BuildingTypeExt::Fetch(pType); const int max = pTypeExt->FactoryPlant_MaxCount; if (max > -1 && counts[pType->ArrayIndex] >= max) @@ -708,7 +708,7 @@ DEFINE_HOOK(0x6A5395, SidebarClass_InitIO_InitRepairButton, 0x6) if (!RulesExt::Global()->ExtendedPlayerRepair) return 0; - if (HouseExt::ExtMap.Find(HouseClass::CurrentPlayer)->PlayerAutoRepair) + if (HouseExt::Fetch(HouseClass::CurrentPlayer)->PlayerAutoRepair) { SidebarClass::Instance.SidebarNeedsRedraw = true; SidebarClass::ToggleRepairButton.IsOn = true; @@ -740,7 +740,7 @@ DEFINE_HOOK(0x6A7AE1, SidebarClass_Update_RepairButton, 0x6) if (!RulesExt::Global()->ExtendedPlayerRepair) return 0; - R->AL(HouseExt::ExtMap.Find(HouseClass::CurrentPlayer)->PlayerAutoRepair); + R->AL(HouseExt::Fetch(HouseClass::CurrentPlayer)->PlayerAutoRepair); return 0x6A7AE7; } @@ -756,7 +756,7 @@ DEFINE_HOOK(0x45063F, BuildingClass_UpdateRepairSell_PlayerAutoRepair, 0x6) if (!pThis->Owner->IsControlledByHuman()) return 0; - if (HouseExt::ExtMap.Find(pThis->Owner)->PlayerAutoRepair) + if (HouseExt::Fetch(pThis->Owner)->PlayerAutoRepair) { return CanAutoRepair; } @@ -781,7 +781,7 @@ DEFINE_HOOK(0x43131B, BeaconManagerClass_DeleteBeacon_RecordOrder, 0x5) GET(const int, houseIdx, ECX); const auto pHouse = HouseClass::Array.GetItem(houseIdx); - const auto pExt = HouseExt::ExtMap.Find(pHouse); + const auto pExt = HouseExt::Fetch(pHouse); const int oldValue = std::exchange(pExt->BeaconsPlacedOrder[beaconIdx], 0); @@ -807,7 +807,7 @@ DEFINE_HOOK(0x430C64, BeaconManagerClass_PlaceBeacon_RecordOrder, 0x5) GET(const int, houseIdx, EBX); const auto pHouse = HouseClass::Array.GetItem(houseIdx); - const auto pExt = HouseExt::ExtMap.Find(pHouse); + const auto pExt = HouseExt::Fetch(pHouse); const int maxVal = std::max({ pExt->BeaconsPlacedOrder[0], pExt->BeaconsPlacedOrder[1], pExt->BeaconsPlacedOrder[2] }); pExt->BeaconsPlacedOrder[beaconIdx] = maxVal + 1; @@ -831,7 +831,7 @@ DEFINE_HOOK(0x4AC9B2, MouseClass_ToggleBeaconMode_AllUsed, 0x6) } const auto pHouse = HouseClass::CurrentPlayer; - const auto pExt = HouseExt::ExtMap.Find(pHouse); + const auto pExt = HouseExt::Fetch(pHouse); for (int i = 0; i < 3; ++i) { diff --git a/src/Ext/HouseType/Body.cpp b/src/Ext/HouseType/Body.cpp index 850920847b..08c0adfd4e 100644 --- a/src/Ext/HouseType/Body.cpp +++ b/src/Ext/HouseType/Body.cpp @@ -5,9 +5,9 @@ HouseTypeExt::ExtContainer HouseTypeExt::ExtMap; -void HouseTypeExt::ExtData::Initialize() { } +void HouseTypeExt::Initialize() { } -void HouseTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI) +void HouseTypeExt::LoadFromINIFile(CCINIClass* pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -21,22 +21,22 @@ void HouseTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI) } template -void HouseTypeExt::ExtData::Serialize(T& Stm) +void HouseTypeExt::Serialize(T& Stm) { Stm .Process(this->EVATag) ; } -void HouseTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void HouseTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void HouseTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void HouseTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/HouseType/Body.h b/src/Ext/HouseType/Body.h index 47131dc9ad..9ee47af239 100644 --- a/src/Ext/HouseType/Body.h +++ b/src/Ext/HouseType/Body.h @@ -8,41 +8,40 @@ #include -class HouseTypeExt +class HouseTypeExt final : public AbstractTypeExt { public: using base_type = HouseTypeClass; + using ExtData = HouseTypeExt; static constexpr DWORD Canary = 0xAFFEAFFE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + HouseTypeClass* OwnerObject() const { - public: - // typed owner accessor - HouseTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - EVAType EVATag; + EVAType EVATag; - ExtData(HouseTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , EVATag { -2 } - { } + HouseTypeExt(HouseTypeClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , EVATag { -2 } + { } - virtual ~ExtData() = default; + virtual ~HouseTypeExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void Initialize() override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -54,7 +53,15 @@ class HouseTypeExt static bool SaveGlobals(PhobosStreamWriter& Stm); static ExtContainer ExtMap; + + static HouseTypeExt* Fetch(const HouseTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static HouseTypeExt* TryFetch(const HouseTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; -// top-level name for the HouseTypeExt extension -using HouseTypeClassExtension = HouseTypeExt::ExtData; diff --git a/src/Ext/HouseType/Hooks.cpp b/src/Ext/HouseType/Hooks.cpp index cee51169d8..605bf0bbf7 100644 --- a/src/Ext/HouseType/Hooks.cpp +++ b/src/Ext/HouseType/Hooks.cpp @@ -13,7 +13,7 @@ DEFINE_HOOK(0x535005, ScenarioClass_LoadSide_SetEVAIndex, 0x6) { if (const auto pHouse = HouseClass::CurrentPlayer) { - const int EVAIndex = HouseTypeExt::ExtMap.Find(pHouse->Type)->EVATag; + const int EVAIndex = HouseTypeExt::Fetch(pHouse->Type)->EVATag; if (EVAIndex != -2) VoxClass::EVAIndex = EVAIndex; @@ -27,7 +27,7 @@ DEFINE_HOOK(0x68AD0C, ScenarioClass_ReadMap_SetEVAIndex, 0x7) { if (const auto pHouse = HouseClass::CurrentPlayer) { - const int EVAIndex = HouseTypeExt::ExtMap.Find(pHouse->Type)->EVATag; + const int EVAIndex = HouseTypeExt::Fetch(pHouse->Type)->EVATag; if (EVAIndex != -2) VoxClass::EVAIndex = EVAIndex; diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp index 64066b5e23..f327ead400 100644 --- a/src/Ext/Infantry/Body.cpp +++ b/src/Ext/Infantry/Body.cpp @@ -4,7 +4,7 @@ DEFINE_HOOK(0x517A60, InfantryClass_CTOR, 0xE) { GET(InfantryClass*, pItem, ESI); - TechnoExt::ExtMap.Adopt(new InfantryClassExtension(pItem)); + TechnoExt::ExtMap.Adopt(new InfantryExt(pItem)); return 0; } diff --git a/src/Ext/Infantry/Body.h b/src/Ext/Infantry/Body.h index f3f0f7fcd0..368d6e6536 100644 --- a/src/Ext/Infantry/Body.h +++ b/src/Ext/Infantry/Body.h @@ -3,11 +3,11 @@ #include #include -// Concrete leaf extension for InfantryClass (empty; techno data lives in TechnoClassExtension). -class InfantryClassExtension : public FootClassExtension +// Concrete leaf extension for InfantryClass (empty; techno data lives in TechnoExt). +class InfantryExt : public FootExt { public: - explicit InfantryClassExtension(InfantryClass* const OwnerObject) : FootClassExtension(OwnerObject) + explicit InfantryExt(InfantryClass* const OwnerObject) : FootExt(OwnerObject) { } InfantryClass* OwnerObject() const diff --git a/src/Ext/Infantry/Hooks.Firing.cpp b/src/Ext/Infantry/Hooks.Firing.cpp index 6648aa4e8a..c6f218a15c 100644 --- a/src/Ext/Infantry/Hooks.Firing.cpp +++ b/src/Ext/Infantry/Hooks.Firing.cpp @@ -42,7 +42,7 @@ DEFINE_HOOK(0x5206D2, InfantryClass_FiringAI_SetContext, 0x6) const auto pTarget = pThis->Target; FiringAITemp::WeaponIndex = weaponIndex; - FiringAITemp::IsSecondary = TechnoTypeExt::ExtMap.Find(pThis->Type)->IsSecondary(weaponIndex); + FiringAITemp::IsSecondary = TechnoTypeExt::Fetch(pThis->Type)->IsSecondary(weaponIndex); FiringAITemp::WeaponType = pWeapon; FiringAITemp::FireErrorResult = pThis->GetFireError(pTarget, weaponIndex, true); FiringAITemp::CanFire = true; @@ -90,7 +90,7 @@ DEFINE_HOOK(0x5209AF, InfantryClass_FiringAI, 0x6) int cumulativeDelay = 0; int projectedDelay = 0; const int weaponIndex = FiringAITemp::WeaponIndex; - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(FiringAITemp::WeaponType); + const auto pWeaponExt = WeaponTypeExt::Fetch(FiringAITemp::WeaponType); const bool allowBurst = pWeaponExt->Burst_FireWithinSequence; // Calculate cumulative burst delay as well cumulative delay after next shot (projected delay). @@ -126,7 +126,7 @@ DEFINE_HOOK(0x5209AF, InfantryClass_FiringAI, 0x6) // If projected frame for firing next shot goes beyond the sequence frame count, cease firing after this shot and start rearm timer. if (fireUp + projectedDelay > frameCount) - TechnoExt::ExtMap.Find(pThis)->ForceFullRearmDelay = true; + TechnoExt::Fetch(pThis)->ForceFullRearmDelay = true; } R->EAX(weaponIndex); // Reuse the weapon index to save some time. @@ -184,7 +184,7 @@ DEFINE_HOOK(0x5209EE, InfantryClass_UpdateFiring_BurstNoDelay, 0x5) { if (pWeapon->Burst > 1) { - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->Burst_NoDelay && (!pWeaponExt->DelayedFire_Duration.isset() || pWeaponExt->DelayedFire_OnlyOnInitialBurst)) { diff --git a/src/Ext/Infantry/Hooks.cpp b/src/Ext/Infantry/Hooks.cpp index 9664b74077..c322c6af2e 100644 --- a/src/Ext/Infantry/Hooks.cpp +++ b/src/Ext/Infantry/Hooks.cpp @@ -16,7 +16,7 @@ DEFINE_HOOK(0x51B2BD, InfantryClass_UpdateTarget_IsControlledByHuman, 0x6) DEFINE_HOOK(0x520B3E, InfantryClass_DoingAI_DeployConvert_Deploy, 0x6) { GET(InfantryClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; if (pTypeExt->Convert_Deploy && !pExt->HasDeployConverted) @@ -33,7 +33,7 @@ DEFINE_HOOK(0x520B3E, InfantryClass_DoingAI_DeployConvert_Deploy, 0x6) DEFINE_HOOK(0x520B99, InfantryClass_DoingAI_DeployConvert_Undeploy, 0x6) { GET(InfantryClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; if (pTypeExt->Convert_Undeploy && !pExt->HasUndeployConverted) @@ -54,7 +54,7 @@ DEFINE_HOOK(0x520E75, InfantryClass_DoingAI_DeployConvert_ResetFlags, 0x6) if (curSeq != Sequence::Deploy && curSeq != Sequence::Undeploy) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); pExt->HasDeployConverted = false; pExt->HasUndeployConverted = false; } @@ -119,7 +119,7 @@ DEFINE_HOOK(0x51E4FB, InfantryClass_WhatAction_ObjectClass_EnigneerEnterBuilding if (pBuilding->Health >= pBuildingType->Strength) { - const auto pTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); + const auto pTypeExt = BuildingTypeExt::Fetch(pBuildingType); if (!pTypeExt->RubbleIntact && !pTypeExt->RubbleIntactRemove) return Skip; @@ -191,7 +191,7 @@ DEFINE_HOOK(0x522373, InfantryClass_ApproachTarget_InfantryAutoDeploy, 0x5) { enum { Deploy = 0x522378 }; GET(InfantryClass*, pThis, ESI); - return TechnoTypeExt::ExtMap.Find(pThis->Type)->InfantryAutoDeploy.Get(RulesExt::Global()->InfantryAutoDeploy) ? Deploy : 0; + return TechnoTypeExt::Fetch(pThis->Type)->InfantryAutoDeploy.Get(RulesExt::Global()->InfantryAutoDeploy) ? Deploy : 0; } DEFINE_HOOK(0x51A002, InfantryClass_UpdatePosition_InfiltrateBuilding, 0x6) diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index 3516c4b080..d3710d9b46 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -4,7 +4,7 @@ DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) { GET(InfantryTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.Adopt(new InfantryTypeClassExtension(pItem)); + TechnoTypeExt::ExtMap.Adopt(new InfantryTypeExt(pItem)); return 0; } diff --git a/src/Ext/InfantryType/Body.h b/src/Ext/InfantryType/Body.h index 9423b7ca89..5191931a40 100644 --- a/src/Ext/InfantryType/Body.h +++ b/src/Ext/InfantryType/Body.h @@ -4,10 +4,10 @@ #include // Concrete leaf extension for InfantryTypeClass (empty). -class InfantryTypeClassExtension : public TechnoTypeClassExtension +class InfantryTypeExt : public TechnoTypeExt { public: - explicit InfantryTypeClassExtension(InfantryTypeClass* const OwnerObject) : TechnoTypeClassExtension(OwnerObject) + explicit InfantryTypeExt(InfantryTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) { } InfantryTypeClass* OwnerObject() const diff --git a/src/Ext/Mission/Body.h b/src/Ext/Mission/Body.h index 0478380052..12917d62cc 100644 --- a/src/Ext/Mission/Body.h +++ b/src/Ext/Mission/Body.h @@ -4,10 +4,10 @@ #include // Empty intermediate base mirroring MissionClass in the extension hierarchy. -class MissionClassExtension : public ObjectClassExtension +class MissionExt : public ObjectExt { public: - explicit MissionClassExtension(MissionClass* const OwnerObject) : ObjectClassExtension(OwnerObject) + explicit MissionExt(MissionClass* const OwnerObject) : ObjectExt(OwnerObject) { } MissionClass* OwnerObject() const diff --git a/src/Ext/Object/Body.h b/src/Ext/Object/Body.h index bd585e7269..7d20c01717 100644 --- a/src/Ext/Object/Body.h +++ b/src/Ext/Object/Body.h @@ -6,10 +6,10 @@ // Empty intermediate base mirroring ObjectClass in the extension hierarchy. // It carries no data of its own; it only exists so the chain of extensions // matches the game's class hierarchy (AbstractClass -> ObjectClass -> ...). -class ObjectClassExtension : public AbstractExt +class ObjectExt : public AbstractExt { public: - explicit ObjectClassExtension(ObjectClass* const OwnerObject) : AbstractExt(OwnerObject) + explicit ObjectExt(ObjectClass* const OwnerObject) : AbstractExt(OwnerObject) { } ObjectClass* OwnerObject() const diff --git a/src/Ext/ObjectType/Body.h b/src/Ext/ObjectType/Body.h index 339c065969..83e11816ef 100644 --- a/src/Ext/ObjectType/Body.h +++ b/src/Ext/ObjectType/Body.h @@ -4,10 +4,10 @@ #include // Empty intermediate base mirroring ObjectTypeClass in the extension hierarchy. -class ObjectTypeClassExtension : public AbstractTypeClassExtension +class ObjectTypeExt : public AbstractTypeExt { public: - explicit ObjectTypeClassExtension(ObjectTypeClass* const OwnerObject) : AbstractTypeClassExtension(OwnerObject) + explicit ObjectTypeExt(ObjectTypeClass* const OwnerObject) : AbstractTypeExt(OwnerObject) { } ObjectTypeClass* OwnerObject() const diff --git a/src/Ext/OverlayType/Body.cpp b/src/Ext/OverlayType/Body.cpp index 7ab84349b0..3819095a18 100644 --- a/src/Ext/OverlayType/Body.cpp +++ b/src/Ext/OverlayType/Body.cpp @@ -6,7 +6,7 @@ OverlayTypeExt::ExtContainer OverlayTypeExt::ExtMap; // load / save template -void OverlayTypeExt::ExtData::Serialize(T& Stm) +void OverlayTypeExt::Serialize(T& Stm) { Stm .Process(this->ZAdjust) @@ -14,7 +14,7 @@ void OverlayTypeExt::ExtData::Serialize(T& Stm) ; } -void OverlayTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void OverlayTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); @@ -36,16 +36,16 @@ void OverlayTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) Debug::Log("[Developer warning] [%s] has Palette=%s set but no palette file was loaded (missing file or wrong filename). Missing palettes cause issues with lighting recalculations.\n", pArtSection, this->PaletteFile.data()); } -void OverlayTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void OverlayTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); this->Palette = GeneralUtils::BuildPalette(this->PaletteFile); } -void OverlayTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void OverlayTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index 47e8081449..020da8c79f 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -5,45 +5,44 @@ #include #include -class OverlayTypeExt +class OverlayTypeExt final : public ObjectTypeExt { public: using base_type = OverlayTypeClass; + using ExtData = OverlayTypeExt; static constexpr DWORD Canary = 0xADF48498; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + OverlayTypeClass* OwnerObject() const { - public: - // typed owner accessor - OverlayTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - Valueable ZAdjust; - PhobosFixedString<32u> PaletteFile; - DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. + Valueable ZAdjust; + PhobosFixedString<32u> PaletteFile; + DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. - ExtData(OverlayTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , ZAdjust { 0 } - , PaletteFile {} - , Palette {} - { } + OverlayTypeExt(OverlayTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , ZAdjust { 0 } + , PaletteFile {} + , Palette {} + { } - virtual ~ExtData() = default; + virtual ~OverlayTypeExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -53,9 +52,17 @@ class OverlayTypeExt static ExtContainer ExtMap; + static OverlayTypeExt* Fetch(const OverlayTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static OverlayTypeExt* TryFetch(const OverlayTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the OverlayTypeExt extension -using OverlayTypeClassExtension = OverlayTypeExt::ExtData; diff --git a/src/Ext/OverlayType/Hooks.cpp b/src/Ext/OverlayType/Hooks.cpp index 029ef78bde..70795a198a 100644 --- a/src/Ext/OverlayType/Hooks.cpp +++ b/src/Ext/OverlayType/Hooks.cpp @@ -5,7 +5,7 @@ DEFINE_HOOK(0x47F71D, CellClass_DrawOverlay_ZAdjust, 0x5) GET(const int, zAdjust, EDI); GET_STACK(OverlayTypeClass*, pOverlayType, STACK_OFFSET(0x24, -0x14)); - auto const pTypeExt = OverlayTypeExt::ExtMap.Find(pOverlayType); + auto const pTypeExt = OverlayTypeExt::Fetch(pOverlayType); if (pTypeExt->ZAdjust != 0) R->EDI(zAdjust - pTypeExt->ZAdjust); @@ -32,7 +32,7 @@ DEFINE_HOOK(0x47F974, CellClass_DrawOverlay_Walls, 0x5) colorSchemeIndex = HouseClass::Array[wallOwnerIndex]->ColorSchemeIndex; LightConvertClass* pConvert = nullptr; - auto const pTypeExt = OverlayTypeExt::ExtMap.Find(pOverlayType); + auto const pTypeExt = OverlayTypeExt::Fetch(pOverlayType); if (pTypeExt->Palette) pConvert = pTypeExt->Palette->Items[colorSchemeIndex]->LightConvert; diff --git a/src/Ext/ParticleSystemType/Body.cpp b/src/Ext/ParticleSystemType/Body.cpp index 67739de351..7209009af7 100644 --- a/src/Ext/ParticleSystemType/Body.cpp +++ b/src/Ext/ParticleSystemType/Body.cpp @@ -6,14 +6,14 @@ ParticleSystemTypeExt::ExtContainer ParticleSystemTypeExt::ExtMap; // load / save template -void ParticleSystemTypeExt::ExtData::Serialize(T& Stm) +void ParticleSystemTypeExt::Serialize(T& Stm) { Stm .Process(this->AdjustTargetCoordsOnRotation) ; } -void ParticleSystemTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void ParticleSystemTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -22,15 +22,15 @@ void ParticleSystemTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AdjustTargetCoordsOnRotation.Read(exINI, pSection, "AdjustTargetCoordsOnRotation"); } -void ParticleSystemTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void ParticleSystemTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void ParticleSystemTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void ParticleSystemTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/ParticleSystemType/Body.h b/src/Ext/ParticleSystemType/Body.h index 24154087be..c263d1036a 100644 --- a/src/Ext/ParticleSystemType/Body.h +++ b/src/Ext/ParticleSystemType/Body.h @@ -5,41 +5,40 @@ #include #include -class ParticleSystemTypeExt +class ParticleSystemTypeExt final : public ObjectTypeExt { public: using base_type = ParticleSystemTypeClass; + using ExtData = ParticleSystemTypeExt; static constexpr DWORD Canary = 0xF9984EFE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + ParticleSystemTypeClass* OwnerObject() const { - public: - // typed owner accessor - ParticleSystemTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - Valueable AdjustTargetCoordsOnRotation; + Valueable AdjustTargetCoordsOnRotation; - ExtData(ParticleSystemTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , AdjustTargetCoordsOnRotation { true } - { } + ParticleSystemTypeExt(ParticleSystemTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , AdjustTargetCoordsOnRotation { true } + { } - virtual ~ExtData() = default; + virtual ~ParticleSystemTypeExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -49,9 +48,17 @@ class ParticleSystemTypeExt static ExtContainer ExtMap; + static ParticleSystemTypeExt* Fetch(const ParticleSystemTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static ParticleSystemTypeExt* TryFetch(const ParticleSystemTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the ParticleSystemTypeExt extension -using ParticleSystemTypeClassExtension = ParticleSystemTypeExt::ExtData; diff --git a/src/Ext/ParticleType/Body.cpp b/src/Ext/ParticleType/Body.cpp index 0b7c7a618e..fecb563bf6 100644 --- a/src/Ext/ParticleType/Body.cpp +++ b/src/Ext/ParticleType/Body.cpp @@ -6,14 +6,14 @@ ParticleTypeExt::ExtContainer ParticleTypeExt::ExtMap; // load / save template -void ParticleTypeExt::ExtData::Serialize(T& Stm) +void ParticleTypeExt::Serialize(T& Stm) { Stm .Process(this->Gas_MaxDriftSpeed) ; } -void ParticleTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void ParticleTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -29,15 +29,15 @@ void ParticleTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) } } -void ParticleTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void ParticleTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void ParticleTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void ParticleTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h index 3dd65e8787..81a86a96c0 100644 --- a/src/Ext/ParticleType/Body.h +++ b/src/Ext/ParticleType/Body.h @@ -6,41 +6,40 @@ #include #include -class ParticleTypeExt +class ParticleTypeExt final : public ObjectTypeExt { public: using base_type = ParticleTypeClass; + using ExtData = ParticleTypeExt; static constexpr DWORD Canary = 0xEAFEEAFE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + ParticleTypeClass* OwnerObject() const { - public: - // typed owner accessor - ParticleTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - Valueable Gas_MaxDriftSpeed; + Valueable Gas_MaxDriftSpeed; - ExtData(ParticleTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , Gas_MaxDriftSpeed { 2 } - { } + ParticleTypeExt(ParticleTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , Gas_MaxDriftSpeed { 2 } + { } - virtual ~ExtData() = default; + virtual ~ParticleTypeExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -50,9 +49,17 @@ class ParticleTypeExt static ExtContainer ExtMap; + static ParticleTypeExt* Fetch(const ParticleTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static ParticleTypeExt* TryFetch(const ParticleTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the ParticleTypeExt extension -using ParticleTypeClassExtension = ParticleTypeExt::ExtData; diff --git a/src/Ext/ParticleType/Hooks.cpp b/src/Ext/ParticleType/Hooks.cpp index 8877735b13..121c320669 100644 --- a/src/Ext/ParticleType/Hooks.cpp +++ b/src/Ext/ParticleType/Hooks.cpp @@ -6,7 +6,7 @@ DEFINE_HOOK(0x62BE30, ParticleClass_Gas_AI_DriftSpeed, 0x5) GET(ParticleClass*, pParticle, EBP); - const auto pExt = ParticleTypeExt::ExtMap.Find(pParticle->Type); + const auto pExt = ParticleTypeExt::Fetch(pParticle->Type); const int maxDriftSpeed = pExt->Gas_MaxDriftSpeed; const int minDriftSpeed = -maxDriftSpeed; diff --git a/src/Ext/RadSite/Body.cpp b/src/Ext/RadSite/Body.cpp index 41aa0e99ae..7937de849c 100644 --- a/src/Ext/RadSite/Body.cpp +++ b/src/Ext/RadSite/Body.cpp @@ -8,12 +8,12 @@ RadSiteExt::ExtContainer RadSiteExt::ExtMap; -void RadSiteExt::ExtData::Initialize() +void RadSiteExt::Initialize() { this->Type = RadTypeClass::FindOrAllocate(GameStrings::Radiation); } -bool RadSiteExt::ExtData::ApplyRadiationDamage(TechnoClass* pTarget, int& damage) +bool RadSiteExt::ApplyRadiationDamage(TechnoClass* pTarget, int& damage) { const auto pType = this->Type; const auto pWarhead = pType->GetWarhead(); @@ -28,7 +28,7 @@ bool RadSiteExt::ExtData::ApplyRadiationDamage(TechnoClass* pTarget, int& damage if (pType->GetWarheadDetonateFull()) WarheadTypeExt::DetonateAt(pWarhead, pTarget, this->RadInvoker, damage, this->RadHouse); else - WarheadTypeExt::ExtMap.Find(pWarhead)->DamageAreaWithTarget(pTarget->GetCoords(), damage, this->RadInvoker, pWarhead, true, this->RadHouse, pTarget); + WarheadTypeExt::Fetch(pWarhead)->DamageAreaWithTarget(pTarget->GetCoords(), damage, this->RadInvoker, pWarhead, true, this->RadHouse, pTarget); if (!pTarget->IsAlive) return false; @@ -38,11 +38,11 @@ bool RadSiteExt::ExtData::ApplyRadiationDamage(TechnoClass* pTarget, int& damage } -void RadSiteExt::CreateInstance(CellStruct location, int spread, int radLevel, WeaponTypeExt::ExtData* pWeaponExt, HouseClass* const pOwner, TechnoClass* const pInvoker) +void RadSiteExt::CreateInstance(CellStruct location, int spread, int radLevel, WeaponTypeExt* pWeaponExt, HouseClass* const pOwner, TechnoClass* const pInvoker) { // use real ctor const auto pRadSite = GameCreate(); - const auto pRadExt = RadSiteExt::ExtMap.Find(pRadSite); + const auto pRadExt = RadSiteExt::Fetch(pRadSite); pRadExt->Weapon = pWeaponExt->OwnerObject(); pRadExt->Type = pWeaponExt->RadType; const auto pRadType = pRadExt->Type; @@ -59,12 +59,12 @@ void RadSiteExt::CreateInstance(CellStruct location, int spread, int radLevel, W pRadExt->SetRadLevel(std::min(radLevel, pRadType->GetLevelMax())); pRadExt->CreateLight(); - if (const auto pCellExt = CellExt::ExtMap.TryFind(MapClass::Instance.TryGetCellAt(location))) + if (const auto pCellExt = CellExt::TryFetch(MapClass::Instance.TryGetCellAt(location))) pCellExt->RadSites.emplace_back(pRadSite); } //RadSiteClass Activate , Rewritten -void RadSiteExt::ExtData::CreateLight() +void RadSiteExt::CreateLight() { const auto pThis = this->OwnerObject(); const auto pType = this->Type; @@ -113,10 +113,10 @@ void RadSiteExt::ExtData::CreateLight() } // Rewrite because of crashing craziness -void RadSiteExt::ExtData::Add(int amount) +void RadSiteExt::Add(int amount) { const auto pThis = this->OwnerObject(); - const auto pRadExt = RadSiteExt::ExtMap.Find(pThis); + const auto pRadExt = RadSiteExt::Fetch(pThis); const int value = pThis->RadLevel * pThis->RadTimeLeft / pThis->RadDuration; pThis->Deactivate(); pThis->RadLevel = value + amount; @@ -125,7 +125,7 @@ void RadSiteExt::ExtData::Add(int amount) this->CreateLight(); } -void RadSiteExt::ExtData::SetRadLevel(int amount) +void RadSiteExt::SetRadLevel(int amount) { const auto pThis = this->OwnerObject(); const int mult = this->Type->GetDurationMultiple(); @@ -135,7 +135,7 @@ void RadSiteExt::ExtData::SetRadLevel(int amount) } // helper function provided by AlexB -//double RadSiteExt::ExtData::GetRadLevelAt(CellStruct const& cell) const +//double RadSiteExt::GetRadLevelAt(CellStruct const& cell) const //{ // const auto pThis = this->OwnerObject(); // const auto base = MapClass::Instance.GetCellAt(pThis->BaseCell)->GetCoords(); @@ -163,7 +163,7 @@ void RadSiteExt::ExtData::SetRadLevel(int amount) // load / save template -void RadSiteExt::ExtData::Serialize(T& Stm) +void RadSiteExt::Serialize(T& Stm) { Stm .Process(this->Weapon) @@ -173,13 +173,13 @@ void RadSiteExt::ExtData::Serialize(T& Stm) ; } -void RadSiteExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void RadSiteExt::LoadFromStream(PhobosStreamReader& Stm) { AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } -void RadSiteExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void RadSiteExt::SaveToStream(PhobosStreamWriter& Stm) { AbstractExt::SaveToStream(Stm); this->Serialize(Stm); @@ -212,7 +212,7 @@ DEFINE_HOOK(0x65B2F4, RadSiteClass_DTOR, 0x5) if (pBaseCell) { - const auto pBaseCellExt = CellExt::ExtMap.Find(pBaseCell); + const auto pBaseCellExt = CellExt::Fetch(pBaseCell); const auto it_Rad = std::find(pBaseCellExt->RadSites.begin(), pBaseCellExt->RadSites.end(), pThis); if (it_Rad != pBaseCellExt->RadSites.end()) @@ -223,7 +223,7 @@ DEFINE_HOOK(0x65B2F4, RadSiteClass_DTOR, 0x5) { if (const auto pCell = MapClass::Instance.TryGetCellAt(*it)) { - const auto pCellExt = CellExt::ExtMap.Find(pCell); + const auto pCellExt = CellExt::Fetch(pCell); const auto it_Rad = std::find_if(pCellExt->RadLevels.begin(), pCellExt->RadLevels.end(), [pThis](CellExt::RadLevel const& item) { return item.Rad == pThis; }); if (it_Rad != pCellExt->RadLevels.end()) diff --git a/src/Ext/RadSite/Body.h b/src/Ext/RadSite/Body.h index be098e9599..f94a95dc1d 100644 --- a/src/Ext/RadSite/Body.h +++ b/src/Ext/RadSite/Body.h @@ -10,60 +10,61 @@ class RadTypeClass; -class RadSiteExt +class RadSiteExt final : public AbstractExt, public Detach::Listener { public: using base_type = RadSiteClass; + using ExtData = RadSiteExt; static constexpr DWORD Canary = 0x88446622; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractExt, public Detach::Listener +public: + // typed owner accessor + RadSiteClass* OwnerObject() const { - public: - // typed owner accessor - RadSiteClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - WeaponTypeClass* Weapon; - RadTypeClass* Type; - HouseClass* RadHouse; - TechnoClass* RadInvoker; - - ExtData(RadSiteClass* OwnerObject) : AbstractExt(OwnerObject) - , RadHouse { nullptr } - , RadInvoker { nullptr } - , Type {} - , Weapon { nullptr } - { } - - virtual ~ExtData() = default; - - bool ApplyRadiationDamage(TechnoClass* pTarget, int& damage); - void Add(int amount); - void SetRadLevel(int amount); - // double GetRadLevelAt(CellStruct const& cell) const; - void CreateLight(); - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - virtual void Initialize() override; - - virtual void OnDetach(TechnoClass* pTarget, bool removed) override - { - if (removed) - AnnounceInvalidPointer(this->RadInvoker, pTarget); - } - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + WeaponTypeClass* Weapon; + RadTypeClass* Type; + HouseClass* RadHouse; + TechnoClass* RadInvoker; + + RadSiteExt(RadSiteClass* OwnerObject) : AbstractExt(OwnerObject) + , RadHouse { nullptr } + , RadInvoker { nullptr } + , Type {} + , Weapon { nullptr } + { } + + virtual ~RadSiteExt() = default; + + bool ApplyRadiationDamage(TechnoClass* pTarget, int& damage); + void Add(int amount); + void SetRadLevel(int amount); + // double GetRadLevelAt(CellStruct const& cell) const; + void CreateLight(); + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void Initialize() override; + + virtual void OnDetach(TechnoClass* pTarget, bool removed) override + { + if (removed) + AnnounceInvalidPointer(this->RadInvoker, pTarget); + } + +private: + template + void Serialize(T& Stm); + +public: - static void CreateInstance(CellStruct location, int spread, int radLevel, WeaponTypeExt::ExtData* pWeaponExt, HouseClass* const pOwner, TechnoClass* const pInvoker); + static void CreateInstance(CellStruct location, int spread, int radLevel, WeaponTypeExt* pWeaponExt, HouseClass* const pOwner, TechnoClass* const pInvoker); +public: class ExtContainer final : public Container { public: @@ -73,7 +74,15 @@ class RadSiteExt }; static ExtContainer ExtMap; + + static RadSiteExt* Fetch(const RadSiteClass* pThis) + { + return ExtMap.Find(pThis); + } + + static RadSiteExt* TryFetch(const RadSiteClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; -// top-level name for the RadSiteExt extension -using RadSiteClassExtension = RadSiteExt::ExtData; diff --git a/src/Ext/RadSite/Hooks.cpp b/src/Ext/RadSite/Hooks.cpp index 40cdc98cb9..295b02c220 100644 --- a/src/Ext/RadSite/Hooks.cpp +++ b/src/Ext/RadSite/Hooks.cpp @@ -26,7 +26,7 @@ DEFINE_HOOK(0x469150, BulletClass_Detonate_ApplyRadiation, 0x5) if (pWeapon && pWeapon->RadLevel > 0 && MapClass::Instance.IsWithinUsableArea((*pCoords))) { - const auto pExt = BulletExt::ExtMap.Find(pThis); + const auto pExt = BulletExt::Fetch(pThis); const auto pWH = pThis->WH; const auto cell = CellClass::Coord2Cell(*pCoords); const auto spread = static_cast(pWH->CellSpread); @@ -52,20 +52,20 @@ DEFINE_HOOK(0x5213B4, InfantryClass_AIDeployment_CheckRad, 0x7) GET(InfantryClass*, pInfantry, ESI); GET(const int, weaponRadLevel, EBX); const auto pCell = pInfantry->GetCell(); - const auto pCellExt = CellExt::ExtMap.Find(pCell); + const auto pCellExt = CellExt::Fetch(pCell); int radLevel = 0; if (!pCellExt->RadSites.empty()) { if (const auto pWeapon = pInfantry->GetDeployWeapon()->WeaponType) { - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const auto pRadType = pWeaponExt->RadType; const float cellSpread = pWeapon->Warhead->CellSpread; for (const auto radSite : pCellExt->RadSites) { - if (radSite->Spread == static_cast(cellSpread) && RadSiteExt::ExtMap.Find(radSite)->Type == pRadType) + if (radSite->Spread == static_cast(cellSpread) && RadSiteExt::Fetch(radSite)->Type == pRadType) { radLevel = radSite->GetRadLevel(); break; @@ -131,7 +131,7 @@ DEFINE_HOOK(0x43FB23, BuildingClass_AI_Radiation, 0x5) if (!pCell) continue; - const auto pCellExt = CellExt::ExtMap.Find(pCell); + const auto pCellExt = CellExt::Fetch(pCell); std::vector>>> typeMap; typeMap.reserve(RadTypeClass::Array.size()); @@ -140,7 +140,7 @@ DEFINE_HOOK(0x43FB23, BuildingClass_AI_Radiation, 0x5) if (radLevel <= 0) continue; - const auto pRadExt = RadSiteExt::ExtMap.Find(pRadSite); + const auto pRadExt = RadSiteExt::Fetch(pRadSite); const auto pRadType = pRadExt->Type; const int maxDamageCount = pRadType->GetBuildingDamageMaxCount(); @@ -186,7 +186,7 @@ DEFINE_HOOK(0x43FB23, BuildingClass_AI_Radiation, 0x5) const int remain = radLevelMax - radLevelSum; int damage = static_cast(std::min(radLevel, remain) * pRadType->GetLevelFactor()); - if (pBuilding->IsAlive && !RadSiteExt::ExtMap.Find(pRadSite)->ApplyRadiationDamage(pBuilding, damage)) + if (pBuilding->IsAlive && !RadSiteExt::Fetch(pRadSite)->ApplyRadiationDamage(pBuilding, damage)) return 0; if (radLevel >= remain) @@ -215,7 +215,7 @@ DEFINE_HOOK(0x4DA59F, FootClass_AI_Radiation, 0x5) || Unsorted::CurrentFrame % RulesClass::Instance->RadApplicationDelay == 0)) { const auto pCell = pFoot->GetCell(); - const auto pCellExt = CellExt::ExtMap.Find(pCell); + const auto pCellExt = CellExt::Fetch(pCell); std::vector>>> typeMap; typeMap.reserve(RadTypeClass::Array.size()); @@ -224,7 +224,7 @@ DEFINE_HOOK(0x4DA59F, FootClass_AI_Radiation, 0x5) if (radLevel <= 0) continue; - const auto pRadExt = RadSiteExt::ExtMap.Find(pRadSite); + const auto pRadExt = RadSiteExt::Fetch(pRadSite); const auto pRadType = pRadExt->Type; if (!pRadType->GetWarhead()) @@ -266,7 +266,7 @@ DEFINE_HOOK(0x4DA59F, FootClass_AI_Radiation, 0x5) const int remain = radLevelMax - radLevelSum; int damage = static_cast(std::min(radLevel, remain) * pRadType->GetLevelFactor()); - if ((pFoot->IsAlive || !pFoot->IsSinking) && !RadSiteExt::ExtMap.Find(pRadSite)->ApplyRadiationDamage(pFoot, damage)) + if ((pFoot->IsAlive || !pFoot->IsSinking) && !RadSiteExt::Fetch(pRadSite)->ApplyRadiationDamage(pFoot, damage)) return ReturnFromFunction; if (radLevel >= remain) @@ -282,7 +282,7 @@ DEFINE_HOOK(0x4DA59F, FootClass_AI_Radiation, 0x5) #define GET_RADSITE(reg, value)\ GET(RadSiteClass* const, pThis, reg);\ - RadSiteExt::ExtData* pExt = RadSiteExt::ExtMap.Find(pThis);\ + RadSiteExt* pExt = RadSiteExt::Fetch(pThis);\ auto output = pExt->Type-> value ; /* @@ -290,7 +290,7 @@ DEFINE_HOOK(0x4DA59F, FootClass_AI_Radiation, 0x5) DEFINE_HOOK(65B593, RadSiteClass_Activate_Delay, 6) { GET(RadSiteClass* const, pThis, ECX); - const auto pExt = RadSiteExt::ExtMap.Find(pThis); + const auto pExt = RadSiteExt::Fetch(pThis); const auto currentLevel = pThis->GetRadLevel(); auto levelDelay = pExt->Type->GetLevelDelay(); @@ -400,7 +400,7 @@ DEFINE_HOOK(0x65BAC1, RadSiteClass_UpdateLevel, 0x8)// RadSiteClass_Radiate_Incr else cell = R->lea_Stack(STACK_OFFSET(0x60, -0x50)); - if (const auto pCellExt = CellExt::ExtMap.TryFind(MapClass::Instance.TryGetCellAt(*cell))) + if (const auto pCellExt = CellExt::TryFetch(MapClass::Instance.TryGetCellAt(*cell))) { auto& radLevels = pCellExt->RadLevels; @@ -411,7 +411,7 @@ DEFINE_HOOK(0x65BAC1, RadSiteClass_UpdateLevel, 0x8)// RadSiteClass_Radiate_Incr const int level = static_cast(static_cast(max - distance) / max * pThis->RadLevel); if (it != radLevels.end()) - it->Level = std::min(it->Level + level, RadSiteExt::ExtMap.Find(pThis)->Type->GetLevelMax()); + it->Level = std::min(it->Level + level, RadSiteExt::Fetch(pThis)->Type->GetLevelMax()); else radLevels.emplace_back(pThis, level); } @@ -428,7 +428,7 @@ DEFINE_HOOK(0x65BAC1, RadSiteClass_UpdateLevel, 0x8)// RadSiteClass_Radiate_Incr { if (it != radLevels.end()) { - const int stepCount = pThis->RadTimeLeft / RadSiteExt::ExtMap.Find(pThis)->Type->GetLevelDelay(); + const int stepCount = pThis->RadTimeLeft / RadSiteExt::Fetch(pThis)->Type->GetLevelDelay(); const int level = static_cast(static_cast(max - distance) / max * pThis->RadLevel / pThis->LevelSteps * stepCount); it->Level = std::max(level, 0); } diff --git a/src/Ext/Radio/Body.h b/src/Ext/Radio/Body.h index 709c34b7c2..ad47767db9 100644 --- a/src/Ext/Radio/Body.h +++ b/src/Ext/Radio/Body.h @@ -4,10 +4,10 @@ #include // Empty intermediate base mirroring RadioClass in the extension hierarchy. -class RadioClassExtension : public MissionClassExtension +class RadioExt : public MissionExt { public: - explicit RadioClassExtension(RadioClass* const OwnerObject) : MissionClassExtension(OwnerObject) + explicit RadioExt(RadioClass* const OwnerObject) : MissionExt(OwnerObject) { } RadioClass* OwnerObject() const diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 759d0f5134..b501e210d5 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -45,7 +45,7 @@ void RulesExt::LoadAfterTypeData(RulesClass* pThis, CCINIClass* pINI) { for (const auto& pTechnoType : TechnoTypeClass::Array) { - if (const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.TryFind(pTechnoType)) + if (const auto pTechnoTypeExt = TechnoTypeExt::TryFetch(pTechnoType)) { // Spawner range if (pTechnoTypeExt->Spawner_LimitRange) diff --git a/src/Ext/SWType/Body.cpp b/src/Ext/SWType/Body.cpp index 6a768676f8..531a66fb0d 100644 --- a/src/Ext/SWType/Body.cpp +++ b/src/Ext/SWType/Body.cpp @@ -4,7 +4,7 @@ SWTypeExt::ExtContainer SWTypeExt::ExtMap; -void SWTypeExt::ExtData::Initialize() +void SWTypeExt::Initialize() { this->EVA_InsufficientFunds = VoxClass::FindIndex(GameStrings::EVA_InsufficientFunds); this->EVA_SelectTarget = VoxClass::FindIndex("EVA_SelectTarget"); @@ -16,7 +16,7 @@ void SWTypeExt::ExtData::Initialize() // load / save template -void SWTypeExt::ExtData::Serialize(T& Stm) +void SWTypeExt::Serialize(T& Stm) { Stm .Process(this->TypeID) @@ -109,7 +109,7 @@ void SWTypeExt::ExtData::Serialize(T& Stm) ; } -void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void SWTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -303,20 +303,20 @@ void SWTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) if (newidx != -1) { NewSWType* pNewSWType = NewSWType::GetNthItem(newidx); - pNewSWType->Initialize(const_cast(this), OwnerObject()); - pNewSWType->LoadFromINI(const_cast(this), OwnerObject(), pINI); + pNewSWType->Initialize(const_cast(this), OwnerObject()); + pNewSWType->LoadFromINI(const_cast(this), OwnerObject(), pINI); } } -void SWTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void SWTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void SWTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void SWTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -334,7 +334,7 @@ bool SWTypeExt::SaveGlobals(PhobosStreamWriter& Stm) bool SWTypeExt::Activate(SuperClass* pSuper, CellStruct cell, bool isPlayer) { - const auto pSWTypeExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pSWTypeExt = SWTypeExt::Fetch(pSuper->Type); const int newIdx = NewSWType::GetNewSWTypeIdx(pSWTypeExt->TypeID.data()); Debug::Log("[Phobos::SW::Active] %s\n", pSWTypeExt->TypeID.data()); diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 5d88bd1c02..0d46c69841 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -5,259 +5,258 @@ #include #include -class SWTypeExt +class SWTypeExt final : public AbstractTypeExt { public: using base_type = SuperWeaponTypeClass; + using ExtData = SWTypeExt; static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + SuperWeaponTypeClass* OwnerObject() const { - public: - // typed owner accessor - SuperWeaponTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - - PhobosFixedString<0x20> TypeID; - - //Ares 0.A - Valueable Money_Amount; - ValueableIdx EVA_Impatient; - ValueableIdx EVA_InsufficientFunds; - ValueableIdx EVA_SelectTarget; - Valueable SW_UseAITargeting; - Valueable SW_AutoFire; - Valueable SW_ManualFire; - Valueable SW_ShowCameo; - Valueable SW_Unstoppable; - Valueable SW_AllowPlayer; - Valueable SW_AllowAI; - ValueableVector SW_Inhibitors; - Valueable SW_AnyInhibitor; - ValueableVector SW_Designators; - Valueable SW_AnyDesignator; - Valueable SW_RangeMinimum; - Valueable SW_RangeMaximum; - Valueable SW_Shots; - - DWORD SW_RequiredHouses; - DWORD SW_ForbiddenHouses; - ValueableVector SW_AuxBuildings; - ValueableVector SW_NegBuildings; - ValueableVector SW_AuxTechnos; - ValueableVector SW_NegTechnos; - Valueable SW_TechLevel; - Valueable SW_InitialReady; - ValueableIdx SW_PostDependent; - Valueable SW_MaxCount; - - Valueable Message_CannotFire; - Valueable Message_InsufficientFunds; - ValueableIdx Message_ColorScheme; - Valueable Message_FirerColor; - - Valueable UIDescription; - Valueable CameoPriority; - ValueableVector LimboDelivery_Types; - ValueableVector LimboDelivery_IDs; - ValueableVector LimboDelivery_RollChances; - Valueable LimboKill_AffectsHouse; - ValueableVector LimboKill_IDs; - ValueableVector LimboKill_Counts; - Valueable RandomBuffer; - ValueableIdxVector SW_Next; - Valueable SW_Next_RealLaunch; - Valueable SW_Next_IgnoreInhibitors; - Valueable SW_Next_IgnoreDesignators; - ValueableVector SW_Next_RollChances; - - Valueable ShowTimer_Priority; - Nullable ShowTimer_Percentage; - - Valueable Detonate_Warhead; - Valueable Detonate_Weapon; - Nullable Detonate_Damage; - Valueable Detonate_Warhead_Full; - Valueable Detonate_AtFirer; - Valueable ShowDesignatorRange; - - Valueable TabIndex; - - Nullable SuperWeaponSidebar_Allow; - DWORD SuperWeaponSidebar_PriorityHouses; - DWORD SuperWeaponSidebar_RequiredHouses; - Valueable SuperWeaponSidebar_Significance; - - CustomPalette SidebarPal; - PhobosPCXFile SidebarPCX; - - std::vector> LimboDelivery_RandomWeightsData; - std::vector> SW_Next_RandomWeightsData; - std::vector> SW_Link_RandomWeightsData; - - std::vector Convert_Pairs; - - Valueable UseWeeds; - Valueable UseWeeds_Amount; - Valueable UseWeeds_StorageTimer; - Valueable UseWeeds_ReadinessAnimationPercentage; - - Valueable EMPulse_WeaponIndex; - Valueable EMPulse_SuspendOthers; - ValueableVector EMPulse_Cannons; - Valueable EMPulse_TargetSelf; - - ValueableIdxVector SW_Link; - Valueable SW_Link_Grant; - Valueable SW_Link_Ready; - Valueable SW_Link_Reset; - ValueableVector SW_Link_RollChances; - Valueable Message_LinkedSWAcquired; - NullableIdx EVA_LinkedSWAcquired; - Valueable Message_Activated_Owner; - Valueable Message_Activated_Allies; - Valueable Message_Activated_Enemies; - ValueableIdx EVA_Activated_Owner; - ValueableIdx EVA_Activated_Allies; - ValueableIdx EVA_Activated_Enemies; - - ExtData(SuperWeaponTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , TypeID { "" } - , Money_Amount { 0 } - , EVA_Impatient { -1 } - , EVA_InsufficientFunds { -1 } - , EVA_SelectTarget { -1 } - , SW_UseAITargeting { false } - , SW_AutoFire { false } - , SW_ManualFire { true } - , SW_ShowCameo { true } - , SW_Unstoppable { false } - , SW_AllowPlayer { true } - , SW_AllowAI { true } - , SW_Inhibitors {} - , SW_AnyInhibitor { false } - , SW_Designators { } - , SW_AnyDesignator { false } - , SW_RangeMinimum { -1.0 } - , SW_RangeMaximum { -1.0 } - , SW_RequiredHouses { 0xFFFFFFFFu } - , SW_ForbiddenHouses { 0u } - , SW_AuxBuildings {} - , SW_NegBuildings {} - , SW_AuxTechnos {} - , SW_NegTechnos {} - , SW_TechLevel { 0 } - , SW_InitialReady { false } - , SW_PostDependent {} - , SW_MaxCount { -1 } - , SW_Shots { -1 } - , Message_CannotFire {} - , Message_InsufficientFunds {} - , Message_ColorScheme { -1 } - , Message_FirerColor { false } - , UIDescription {} - , CameoPriority { 0 } - , LimboDelivery_Types {} - , LimboDelivery_IDs {} - , LimboDelivery_RollChances {} - , LimboDelivery_RandomWeightsData {} - , LimboKill_AffectsHouse { AffectedHouse::Owner } - , LimboKill_IDs {} - , LimboKill_Counts {} - , RandomBuffer { 0.0 } - , Detonate_Warhead {} - , Detonate_Weapon {} - , Detonate_Damage {} - , Detonate_Warhead_Full { true } - , Detonate_AtFirer { false } - , SW_Next {} - , SW_Next_RealLaunch { true } - , SW_Next_IgnoreInhibitors { false } - , SW_Next_IgnoreDesignators { true } - , SW_Next_RollChances {} - , SW_Next_RandomWeightsData {} - , ShowTimer_Priority { 0 } - , ShowTimer_Percentage { false } - , Convert_Pairs {} - , ShowDesignatorRange { true } - , TabIndex { 1 } - , SuperWeaponSidebar_Allow {} - , SuperWeaponSidebar_PriorityHouses { 0u } - , SuperWeaponSidebar_RequiredHouses { 0xFFFFFFFFu } - , SuperWeaponSidebar_Significance { 0 } - , SidebarPal {} - , SidebarPCX {} - , UseWeeds { false } - , UseWeeds_Amount { RulesClass::Instance->WeedCapacity } - , UseWeeds_StorageTimer { false } - , UseWeeds_ReadinessAnimationPercentage { 0.9 } - , EMPulse_WeaponIndex { 0 } - , EMPulse_SuspendOthers { false } - , EMPulse_Cannons {} - , EMPulse_TargetSelf { false } - , SW_Link {} - , SW_Link_Grant { false } - , SW_Link_Ready { false } - , SW_Link_Reset { false } - , SW_Link_RollChances {} - , SW_Link_RandomWeightsData {} - , Message_LinkedSWAcquired {} - , EVA_LinkedSWAcquired {} - , Message_Activated_Owner {} - , Message_Activated_Allies {} - , Message_Activated_Enemies {} - , EVA_Activated_Owner { -1 } - , EVA_Activated_Allies { -1 } - , EVA_Activated_Enemies { -1 } - { } - - // Ares 0.A functions - bool IsInhibitor(HouseClass* pOwner, TechnoClass* pTechno) const; - bool HasInhibitor(HouseClass* pOwner, const CellStruct& coords) const; - bool IsInhibitorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const; - bool IsDesignator(HouseClass* pOwner, TechnoClass* pTechno) const; - bool HasDesignator(HouseClass* pOwner, const CellStruct& coords) const; - bool IsDesignatorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const; - bool IsLaunchSiteEligible(const CellStruct& Coords, BuildingClass* pBuilding, bool ignoreRange) const; - bool IsLaunchSite(BuildingClass* pBuilding) const; - std::pair GetLaunchSiteRange(BuildingClass* pBuilding = nullptr) const; - bool IsAvailable(HouseClass* pHouse) const; - void PrintMessage(const CSFText& message, HouseClass* pFirer) const; - - void ApplyLimboDelivery(HouseClass* pHouse); - void ApplyLimboKill(HouseClass* pHouse); - void ApplyDetonation(HouseClass* pHouse, const CellStruct& cell); - void ApplySWNext(SuperClass* pSW, const CellStruct& cell); - void ApplyTypeConversion(SuperClass* pSW); - void HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& cell) const; - std::vector GetEMPulseCannons(HouseClass* pOwner, const CellStruct& cell) const; - std::pair GetEMPulseCannonRange(BuildingClass* pBuilding) const; - - void ApplyLinkedSW(SuperClass* pSW); - - void ApplyActivatedMessage(SuperClass* pSW) const; - void ApplyActivatedEva(SuperClass* pSW) const; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void Initialize() override; - - virtual ~ExtData() = default; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - std::vector WeightedRollsHandler(ValueableVector* chances, std::vector>* weights, size_t size); - - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + + PhobosFixedString<0x20> TypeID; + + //Ares 0.A + Valueable Money_Amount; + ValueableIdx EVA_Impatient; + ValueableIdx EVA_InsufficientFunds; + ValueableIdx EVA_SelectTarget; + Valueable SW_UseAITargeting; + Valueable SW_AutoFire; + Valueable SW_ManualFire; + Valueable SW_ShowCameo; + Valueable SW_Unstoppable; + Valueable SW_AllowPlayer; + Valueable SW_AllowAI; + ValueableVector SW_Inhibitors; + Valueable SW_AnyInhibitor; + ValueableVector SW_Designators; + Valueable SW_AnyDesignator; + Valueable SW_RangeMinimum; + Valueable SW_RangeMaximum; + Valueable SW_Shots; + + DWORD SW_RequiredHouses; + DWORD SW_ForbiddenHouses; + ValueableVector SW_AuxBuildings; + ValueableVector SW_NegBuildings; + ValueableVector SW_AuxTechnos; + ValueableVector SW_NegTechnos; + Valueable SW_TechLevel; + Valueable SW_InitialReady; + ValueableIdx SW_PostDependent; + Valueable SW_MaxCount; + + Valueable Message_CannotFire; + Valueable Message_InsufficientFunds; + ValueableIdx Message_ColorScheme; + Valueable Message_FirerColor; + + Valueable UIDescription; + Valueable CameoPriority; + ValueableVector LimboDelivery_Types; + ValueableVector LimboDelivery_IDs; + ValueableVector LimboDelivery_RollChances; + Valueable LimboKill_AffectsHouse; + ValueableVector LimboKill_IDs; + ValueableVector LimboKill_Counts; + Valueable RandomBuffer; + ValueableIdxVector SW_Next; + Valueable SW_Next_RealLaunch; + Valueable SW_Next_IgnoreInhibitors; + Valueable SW_Next_IgnoreDesignators; + ValueableVector SW_Next_RollChances; + + Valueable ShowTimer_Priority; + Nullable ShowTimer_Percentage; + + Valueable Detonate_Warhead; + Valueable Detonate_Weapon; + Nullable Detonate_Damage; + Valueable Detonate_Warhead_Full; + Valueable Detonate_AtFirer; + Valueable ShowDesignatorRange; + + Valueable TabIndex; + + Nullable SuperWeaponSidebar_Allow; + DWORD SuperWeaponSidebar_PriorityHouses; + DWORD SuperWeaponSidebar_RequiredHouses; + Valueable SuperWeaponSidebar_Significance; + + CustomPalette SidebarPal; + PhobosPCXFile SidebarPCX; + + std::vector> LimboDelivery_RandomWeightsData; + std::vector> SW_Next_RandomWeightsData; + std::vector> SW_Link_RandomWeightsData; + + std::vector Convert_Pairs; + + Valueable UseWeeds; + Valueable UseWeeds_Amount; + Valueable UseWeeds_StorageTimer; + Valueable UseWeeds_ReadinessAnimationPercentage; + + Valueable EMPulse_WeaponIndex; + Valueable EMPulse_SuspendOthers; + ValueableVector EMPulse_Cannons; + Valueable EMPulse_TargetSelf; + + ValueableIdxVector SW_Link; + Valueable SW_Link_Grant; + Valueable SW_Link_Ready; + Valueable SW_Link_Reset; + ValueableVector SW_Link_RollChances; + Valueable Message_LinkedSWAcquired; + NullableIdx EVA_LinkedSWAcquired; + Valueable Message_Activated_Owner; + Valueable Message_Activated_Allies; + Valueable Message_Activated_Enemies; + ValueableIdx EVA_Activated_Owner; + ValueableIdx EVA_Activated_Allies; + ValueableIdx EVA_Activated_Enemies; + + SWTypeExt(SuperWeaponTypeClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , TypeID { "" } + , Money_Amount { 0 } + , EVA_Impatient { -1 } + , EVA_InsufficientFunds { -1 } + , EVA_SelectTarget { -1 } + , SW_UseAITargeting { false } + , SW_AutoFire { false } + , SW_ManualFire { true } + , SW_ShowCameo { true } + , SW_Unstoppable { false } + , SW_AllowPlayer { true } + , SW_AllowAI { true } + , SW_Inhibitors {} + , SW_AnyInhibitor { false } + , SW_Designators { } + , SW_AnyDesignator { false } + , SW_RangeMinimum { -1.0 } + , SW_RangeMaximum { -1.0 } + , SW_RequiredHouses { 0xFFFFFFFFu } + , SW_ForbiddenHouses { 0u } + , SW_AuxBuildings {} + , SW_NegBuildings {} + , SW_AuxTechnos {} + , SW_NegTechnos {} + , SW_TechLevel { 0 } + , SW_InitialReady { false } + , SW_PostDependent {} + , SW_MaxCount { -1 } + , SW_Shots { -1 } + , Message_CannotFire {} + , Message_InsufficientFunds {} + , Message_ColorScheme { -1 } + , Message_FirerColor { false } + , UIDescription {} + , CameoPriority { 0 } + , LimboDelivery_Types {} + , LimboDelivery_IDs {} + , LimboDelivery_RollChances {} + , LimboDelivery_RandomWeightsData {} + , LimboKill_AffectsHouse { AffectedHouse::Owner } + , LimboKill_IDs {} + , LimboKill_Counts {} + , RandomBuffer { 0.0 } + , Detonate_Warhead {} + , Detonate_Weapon {} + , Detonate_Damage {} + , Detonate_Warhead_Full { true } + , Detonate_AtFirer { false } + , SW_Next {} + , SW_Next_RealLaunch { true } + , SW_Next_IgnoreInhibitors { false } + , SW_Next_IgnoreDesignators { true } + , SW_Next_RollChances {} + , SW_Next_RandomWeightsData {} + , ShowTimer_Priority { 0 } + , ShowTimer_Percentage { false } + , Convert_Pairs {} + , ShowDesignatorRange { true } + , TabIndex { 1 } + , SuperWeaponSidebar_Allow {} + , SuperWeaponSidebar_PriorityHouses { 0u } + , SuperWeaponSidebar_RequiredHouses { 0xFFFFFFFFu } + , SuperWeaponSidebar_Significance { 0 } + , SidebarPal {} + , SidebarPCX {} + , UseWeeds { false } + , UseWeeds_Amount { RulesClass::Instance->WeedCapacity } + , UseWeeds_StorageTimer { false } + , UseWeeds_ReadinessAnimationPercentage { 0.9 } + , EMPulse_WeaponIndex { 0 } + , EMPulse_SuspendOthers { false } + , EMPulse_Cannons {} + , EMPulse_TargetSelf { false } + , SW_Link {} + , SW_Link_Grant { false } + , SW_Link_Ready { false } + , SW_Link_Reset { false } + , SW_Link_RollChances {} + , SW_Link_RandomWeightsData {} + , Message_LinkedSWAcquired {} + , EVA_LinkedSWAcquired {} + , Message_Activated_Owner {} + , Message_Activated_Allies {} + , Message_Activated_Enemies {} + , EVA_Activated_Owner { -1 } + , EVA_Activated_Allies { -1 } + , EVA_Activated_Enemies { -1 } + { } + + // Ares 0.A functions + bool IsInhibitor(HouseClass* pOwner, TechnoClass* pTechno) const; + bool HasInhibitor(HouseClass* pOwner, const CellStruct& coords) const; + bool IsInhibitorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const; + bool IsDesignator(HouseClass* pOwner, TechnoClass* pTechno) const; + bool HasDesignator(HouseClass* pOwner, const CellStruct& coords) const; + bool IsDesignatorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const; + bool IsLaunchSiteEligible(const CellStruct& Coords, BuildingClass* pBuilding, bool ignoreRange) const; + bool IsLaunchSite(BuildingClass* pBuilding) const; + std::pair GetLaunchSiteRange(BuildingClass* pBuilding = nullptr) const; + bool IsAvailable(HouseClass* pHouse) const; + void PrintMessage(const CSFText& message, HouseClass* pFirer) const; + + void ApplyLimboDelivery(HouseClass* pHouse); + void ApplyLimboKill(HouseClass* pHouse); + void ApplyDetonation(HouseClass* pHouse, const CellStruct& cell); + void ApplySWNext(SuperClass* pSW, const CellStruct& cell); + void ApplyTypeConversion(SuperClass* pSW); + void HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& cell) const; + std::vector GetEMPulseCannons(HouseClass* pOwner, const CellStruct& cell) const; + std::pair GetEMPulseCannonRange(BuildingClass* pBuilding) const; + + void ApplyLinkedSW(SuperClass* pSW); + + void ApplyActivatedMessage(SuperClass* pSW) const; + void ApplyActivatedEva(SuperClass* pSW) const; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; + + virtual ~SWTypeExt() = default; + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + + virtual void SaveToStream(PhobosStreamWriter& Stm) override; +private: + std::vector WeightedRollsHandler(ValueableVector* chances, std::vector>* weights, size_t size); + + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -268,6 +267,16 @@ class SWTypeExt static void FireSuperWeaponExt(SuperClass* pSW, const CellStruct& cell); static ExtContainer ExtMap; + + static SWTypeExt* Fetch(const SuperWeaponTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static SWTypeExt* TryFetch(const SuperWeaponTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -276,5 +285,3 @@ class SWTypeExt }; -// top-level name for the SWTypeExt extension -using SuperWeaponTypeClassExtension = SWTypeExt::ExtData; diff --git a/src/Ext/SWType/FireSuperWeapon.cpp b/src/Ext/SWType/FireSuperWeapon.cpp index bdc9aaabea..b6acdac4d1 100644 --- a/src/Ext/SWType/FireSuperWeapon.cpp +++ b/src/Ext/SWType/FireSuperWeapon.cpp @@ -14,7 +14,7 @@ void SWTypeExt::FireSuperWeaponExt(SuperClass* pSW, const CellStruct& cell) { const auto pHouse = pSW->Owner; const auto pType = pSW->Type; - auto const pTypeExt = SWTypeExt::ExtMap.Find(pType); + auto const pTypeExt = SWTypeExt::Fetch(pType); if (pTypeExt->LimboDelivery_Types.size() > 0) pTypeExt->ApplyLimboDelivery(pHouse); @@ -41,7 +41,7 @@ void SWTypeExt::FireSuperWeaponExt(SuperClass* pSW, const CellStruct& cell) pTypeExt->ApplyActivatedEva(pSW); - auto& sw_ext = HouseExt::ExtMap.Find(pHouse)->SuperExts[pType->ArrayIndex]; + auto& sw_ext = HouseExt::Fetch(pHouse)->SuperExts[pType->ArrayIndex]; sw_ext.ShotCount++; const auto pTags = &pHouse->RelatedTags; @@ -123,8 +123,8 @@ static inline void LimboCreate(BuildingTypeClass* pType, HouseClass* pOwner, int if (pType->SecretLab) pOwner->SecretLabs.AddItem(pBuilding); - auto const pBuildingExt = BuildingExt::ExtMap.Find(pBuilding); - auto const pOwnerExt = HouseExt::ExtMap.Find(pOwner); + auto const pBuildingExt = BuildingExt::Fetch(pBuilding); + auto const pOwnerExt = HouseExt::Fetch(pOwner); if (!pBuildingExt->TypeExtData->PowerPlantEnhancer_Buildings.empty() && (pBuildingExt->TypeExtData->PowerPlantEnhancer_Amount != 0 || pBuildingExt->TypeExtData->PowerPlantEnhancer_Factor != 1.0f)) @@ -156,7 +156,7 @@ static inline void LimboCreate(BuildingTypeClass* pType, HouseClass* pOwner, int if (!pBldType->Insignificant && !pBldType->DontScore) pOwnerExt->AddToLimboTracking(pBldType); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pBuilding); + auto const pTechnoExt = TechnoExt::Fetch(pBuilding); auto const pTechnoTypeExt = pTechnoExt->TypeExtData; if (pTechnoTypeExt->AutoDeath_Behavior.isset()) @@ -170,7 +170,7 @@ static inline void LimboCreate(BuildingTypeClass* pType, HouseClass* pOwner, int } } -void SWTypeExt::ExtData::ApplyLimboDelivery(HouseClass* pHouse) +void SWTypeExt::ApplyLimboDelivery(HouseClass* pHouse) { // random mode if (this->LimboDelivery_RandomWeightsData.size()) @@ -202,7 +202,7 @@ void SWTypeExt::ExtData::ApplyLimboDelivery(HouseClass* pHouse) } } -void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse) +void SWTypeExt::ApplyLimboKill(HouseClass* pHouse) { const int idAmount = static_cast(this->LimboKill_IDs.size()); @@ -216,7 +216,7 @@ void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse) if (!EnumFunctions::CanTargetHouse(this->LimboKill_AffectsHouse, pHouse, pTargetHouse)) continue; - const auto pHouseExt = HouseExt::ExtMap.Find(pTargetHouse); + const auto pHouseExt = HouseExt::Fetch(pTargetHouse); auto& buildings = pHouseExt->OwnedLimboDeliveredBuildings; if (buildings.empty()) @@ -233,7 +233,7 @@ void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse) continue; const int maxCount = idx < static_cast(this->LimboKill_Counts.size()) ? this->LimboKill_Counts[idx] : std::numeric_limits::max(); - auto IsEligible = [id](BuildingClass* pBuilding) { return BuildingExt::ExtMap.Find(pBuilding)->LimboID == id; }; + auto IsEligible = [id](BuildingClass* pBuilding) { return BuildingExt::Fetch(pBuilding)->LimboID == id; }; Helpers::Alex::for_each_if_n(buildings.begin(), buildings.end(), maxCount, IsEligible, [&limboKills, &removes](BuildingClass* pBuilding) { limboKills.emplace_back(pBuilding); @@ -253,7 +253,7 @@ void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse) // Remove limbo buildings' tracking here because their are not truely InLimbo if (!pBuildingType->Insignificant && !pBuildingType->DontScore) - HouseExt::ExtMap.Find(pBuilding->Owner)->RemoveFromLimboTracking(pBuildingType); + HouseExt::Fetch(pBuilding->Owner)->RemoveFromLimboTracking(pBuildingType); pBuilding->Stun(); pBuilding->Limbo(); @@ -264,7 +264,7 @@ void SWTypeExt::ExtData::ApplyLimboKill(HouseClass* pHouse) #pragma endregion -void SWTypeExt::ExtData::ApplyDetonation(HouseClass* pHouse, const CellStruct& cell) +void SWTypeExt::ApplyDetonation(HouseClass* pHouse, const CellStruct& cell) { auto coords = MapClass::Instance.GetCellAt(cell)->GetCoords(); BuildingClass* pFirer = nullptr; @@ -302,7 +302,7 @@ void SWTypeExt::ExtData::ApplyDetonation(HouseClass* pHouse, const CellStruct& c } } -void SWTypeExt::ExtData::ApplySWNext(SuperClass* pSW, const CellStruct& cell) +void SWTypeExt::ApplySWNext(SuperClass* pSW, const CellStruct& cell) { // SW.Next proper launching mechanic auto LaunchTheSW = [=](const int swIdxToLaunch) @@ -310,7 +310,7 @@ void SWTypeExt::ExtData::ApplySWNext(SuperClass* pSW, const CellStruct& cell) const auto pHouse = pSW->Owner; if (const auto pSuper = pHouse->Supers.GetItem(swIdxToLaunch)) { - const auto pNextTypeExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pNextTypeExt = SWTypeExt::Fetch(pSuper->Type); if (!this->SW_Next_RealLaunch || (pSuper->IsPresent && pSuper->IsReady && !pSuper->IsSuspended && pHouse->CanTransactMoney(pNextTypeExt->Money_Amount))) { @@ -347,13 +347,13 @@ void SWTypeExt::ExtData::ApplySWNext(SuperClass* pSW, const CellStruct& cell) } } -void SWTypeExt::ExtData::ApplyTypeConversion(SuperClass* pSW) +void SWTypeExt::ApplyTypeConversion(SuperClass* pSW) { for (const auto pTargetFoot : FootClass::Array) TypeConvertGroup::Convert(pTargetFoot, this->Convert_Pairs, pSW->Owner); } -void SWTypeExt::ExtData::HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& cell) const +void SWTypeExt::HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& cell) const { auto const& pBuildings = this->GetEMPulseCannons(pSW->Owner, cell); auto const count = this->SW_MaxCount >= 0 ? static_cast(this->SW_MaxCount) : std::numeric_limits::max(); @@ -361,7 +361,7 @@ void SWTypeExt::ExtData::HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& for (size_t i = 0; i < pBuildings.size(); i++) { auto const pBuilding = pBuildings[i]; - auto const pExt = BuildingExt::ExtMap.Find(pBuilding); + auto const pExt = BuildingExt::Fetch(pBuilding); pExt->CurrentEMPulseSW = pSW; if (i + 1 == count) @@ -371,14 +371,14 @@ void SWTypeExt::ExtData::HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& if (this->EMPulse_SuspendOthers) { auto const pHouse = pSW->Owner; - auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto const pHouseExt = HouseExt::Fetch(pHouse); for (auto const& pSuper : pHouse->Supers) { if (static_cast(pSuper->Type->Type) != 28 || pSuper == pSW) continue; - auto const pTypeExt = SWTypeExt::ExtMap.Find(pSuper->Type); + auto const pTypeExt = SWTypeExt::Fetch(pSuper->Type); bool suspend = false; if (this->EMPulse_Cannons.empty() && pTypeExt->EMPulse_Cannons.empty()) @@ -406,7 +406,7 @@ void SWTypeExt::ExtData::HandleEMPulseLaunch(SuperClass* pSW, const CellStruct& } } -void SWTypeExt::ExtData::ApplyLinkedSW(SuperClass* pSW) +void SWTypeExt::ApplyLinkedSW(SuperClass* pSW) { const auto pHouse = pSW->Owner; const bool notObserver = !pHouse->IsObserver() || !pHouse->IsCurrentPlayerObserver(); @@ -430,7 +430,7 @@ void SWTypeExt::ExtData::ApplyLinkedSW(SuperClass* pSW) isActive = true; } // check SW.Link.Ready, which will default to SW.InitialReady for granted superweapon - else if (this->SW_Link_Ready || (granted && SWTypeExt::ExtMap.Find(pSuper->Type)->SW_InitialReady)) + else if (this->SW_Link_Ready || (granted && SWTypeExt::Fetch(pSuper->Type)->SW_InitialReady)) { pSuper->RechargeTimer.TimeLeft = 0; pSuper->SetReadiness(true); @@ -488,7 +488,7 @@ void SWTypeExt::ExtData::ApplyLinkedSW(SuperClass* pSW) } } -void SWTypeExt::ExtData::ApplyActivatedMessage(SuperClass* pSW) const +void SWTypeExt::ApplyActivatedMessage(SuperClass* pSW) const { const auto pHouse = pSW->Owner; @@ -509,7 +509,7 @@ void SWTypeExt::ExtData::ApplyActivatedMessage(SuperClass* pSW) const ); } -void SWTypeExt::ExtData::ApplyActivatedEva(SuperClass* pSW) const +void SWTypeExt::ApplyActivatedEva(SuperClass* pSW) const { const auto pHouse = pSW->Owner; diff --git a/src/Ext/SWType/Hooks.cpp b/src/Ext/SWType/Hooks.cpp index 9c9b041aad..8623c6c3ae 100644 --- a/src/Ext/SWType/Hooks.cpp +++ b/src/Ext/SWType/Hooks.cpp @@ -41,8 +41,8 @@ DEFINE_HOOK(0x6CB5EB, SuperClass_Grant_ShowTimer, 0x5) std::sort(SuperClass::ShowTimers.begin(), SuperClass::ShowTimers.end(), [](SuperClass* a, SuperClass* b) { - const auto aExt = SWTypeExt::ExtMap.Find(a->Type); - const auto bExt = SWTypeExt::ExtMap.Find(b->Type); + const auto aExt = SWTypeExt::Fetch(a->Type); + const auto bExt = SWTypeExt::Fetch(b->Type); return aExt->ShowTimer_Priority.Get() > bExt->ShowTimer_Priority.Get(); } ); @@ -57,7 +57,7 @@ DEFINE_HOOK(0x6DBE74, Tactical_SuperLinesCircles_ShowDesignatorRange, 0x7) return 0; const auto pSuperType = SuperWeaponTypeClass::Array.GetItem(Unsorted::CurrentSWType); - const auto pExt = SWTypeExt::ExtMap.Find(pSuperType); + const auto pExt = SWTypeExt::Fetch(pSuperType); if (!pExt->ShowDesignatorRange) return 0; @@ -76,7 +76,7 @@ DEFINE_HOOK(0x6DBE74, Tactical_SuperLinesCircles_ShowDesignatorRange, 0x7) continue; } - const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pCurrentTechnoType); + const auto pTechnoTypeExt = TechnoTypeExt::Fetch(pCurrentTechnoType); const float radius = pOwner == HouseClass::CurrentPlayer ? (float)(pTechnoTypeExt->DesignatorRange.Get(pCurrentTechnoType->Sight)) @@ -105,7 +105,7 @@ DEFINE_HOOK(0x6CBEF4, SuperClass_AnimStage_UseWeeds, 0x6) GET(SuperClass*, pSuper, ECX); GET(SuperWeaponTypeClass*, pSWType, EBX); - const auto pExt = SWTypeExt::ExtMap.Find(pSWType); + const auto pExt = SWTypeExt::Fetch(pSWType); if (pExt->UseWeeds) { @@ -148,7 +148,7 @@ DEFINE_HOOK(0x6CBD2C, SuperClass_AI_UseWeeds, 0x6) GET(SuperClass*, pSuper, ESI); - const auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pExt = SWTypeExt::Fetch(pSuper->Type); if (pExt->UseWeeds) { @@ -192,7 +192,7 @@ DEFINE_HOOK(0x6CC1E6, SuperClass_SetSWCharge_UseWeeds, 0x5) GET(SuperClass*, pSuper, EDI); - const auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pExt = SWTypeExt::Fetch(pSuper->Type); if (pExt->UseWeeds) return Skip; @@ -244,7 +244,7 @@ DEFINE_HOOK(0x6ABC9D, SidebarClass_GetObjectTabIndex_Super, 0x5) return 0; const auto pSWType = SuperWeaponTypeClass::Array[typeIdx]; - const auto pSWTypExt = SWTypeExt::ExtMap.Find(pSWType); + const auto pSWTypExt = SWTypeExt::Fetch(pSWType); R->EAX(pSWTypExt->TabIndex); return ApplyTabIndex; diff --git a/src/Ext/SWType/NewSWType/NewSWType.h b/src/Ext/SWType/NewSWType/NewSWType.h index 84709aa8ce..c4368c7d8b 100644 --- a/src/Ext/SWType/NewSWType/NewSWType.h +++ b/src/Ext/SWType/NewSWType/NewSWType.h @@ -16,8 +16,8 @@ class NewSWType // selectable override - virtual void Initialize(SWTypeExt::ExtData* pData, SuperWeaponTypeClass* pSW) { } - virtual void LoadFromINI(SWTypeExt::ExtData* pData, SuperWeaponTypeClass* pSW, CCINIClass* pINI) { } + virtual void Initialize(SWTypeExt* pData, SuperWeaponTypeClass* pSW) { } + virtual void LoadFromINI(SWTypeExt* pData, SuperWeaponTypeClass* pSW, CCINIClass* pINI) { } // must be override diff --git a/src/Ext/SWType/SWHelpers.cpp b/src/Ext/SWType/SWHelpers.cpp index 19ef7a81af..050b355bf8 100644 --- a/src/Ext/SWType/SWHelpers.cpp +++ b/src/Ext/SWType/SWHelpers.cpp @@ -4,7 +4,7 @@ #include // Universal handler of the rolls-weights system -std::vector SWTypeExt::ExtData::WeightedRollsHandler(ValueableVector* rolls, std::vector>* weights, size_t size) +std::vector SWTypeExt::WeightedRollsHandler(ValueableVector* rolls, std::vector>* weights, size_t size) { bool rollOnce = false; size_t rollsSize = rolls->size(); @@ -44,7 +44,7 @@ std::vector SWTypeExt::ExtData::WeightedRollsHandler(ValueableVector // ============================= // Ares 0.A helpers // Inhibitors check -bool SWTypeExt::ExtData::IsInhibitor(HouseClass* pOwner, TechnoClass* pTechno) const +bool SWTypeExt::IsInhibitor(HouseClass* pOwner, TechnoClass* pTechno) const { if (pTechno->IsAlive && pTechno->Health && !pTechno->InLimbo && !pTechno->Deactivated) { @@ -63,12 +63,12 @@ bool SWTypeExt::ExtData::IsInhibitor(HouseClass* pOwner, TechnoClass* pTechno) c return false; } -bool SWTypeExt::ExtData::IsInhibitorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const +bool SWTypeExt::IsInhibitorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const { if (this->IsInhibitor(pOwner, pTechno)) { const auto pType = pTechno->GetTechnoType(); - const auto pExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pExt = TechnoTypeExt::Fetch(pType); // get the inhibitor's center const auto center = pTechno->GetCenterCoords(); @@ -81,7 +81,7 @@ bool SWTypeExt::ExtData::IsInhibitorEligible(HouseClass* pOwner, const CellStruc return false; } -bool SWTypeExt::ExtData::HasInhibitor(HouseClass* pOwner, const CellStruct& coords) const +bool SWTypeExt::HasInhibitor(HouseClass* pOwner, const CellStruct& coords) const { // does not allow inhibitors if (this->SW_Inhibitors.empty() && !this->SW_AnyInhibitor) @@ -94,7 +94,7 @@ bool SWTypeExt::ExtData::HasInhibitor(HouseClass* pOwner, const CellStruct& coor } // Designators check -bool SWTypeExt::ExtData::IsDesignator(HouseClass* pOwner, TechnoClass* pTechno) const +bool SWTypeExt::IsDesignator(HouseClass* pOwner, TechnoClass* pTechno) const { if (pTechno->Owner == pOwner && pTechno->IsAlive && pTechno->Health && !pTechno->InLimbo && !pTechno->Deactivated) return this->SW_AnyDesignator || this->SW_Designators.Contains(pTechno->GetTechnoType()); @@ -102,12 +102,12 @@ bool SWTypeExt::ExtData::IsDesignator(HouseClass* pOwner, TechnoClass* pTechno) return false; } -bool SWTypeExt::ExtData::IsDesignatorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const +bool SWTypeExt::IsDesignatorEligible(HouseClass* pOwner, const CellStruct& coords, TechnoClass* pTechno) const { if (this->IsDesignator(pOwner, pTechno)) { const auto pType = pTechno->GetTechnoType(); - const auto pExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pExt = TechnoTypeExt::Fetch(pType); // get the designator's center const auto center = pTechno->GetCenterCoords(); @@ -120,7 +120,7 @@ bool SWTypeExt::ExtData::IsDesignatorEligible(HouseClass* pOwner, const CellStru return false; } -bool SWTypeExt::ExtData::HasDesignator(HouseClass* pOwner, const CellStruct& coords) const +bool SWTypeExt::HasDesignator(HouseClass* pOwner, const CellStruct& coords) const { // does not require designators if (this->SW_Designators.empty() && !this->SW_AnyDesignator) @@ -131,7 +131,7 @@ bool SWTypeExt::ExtData::HasDesignator(HouseClass* pOwner, const CellStruct& coo { return this->IsDesignatorEligible(pOwner, coords, pTechno); }); } -bool SWTypeExt::ExtData::IsLaunchSiteEligible(const CellStruct& Coords, BuildingClass* pBuilding, bool ignoreRange) const +bool SWTypeExt::IsLaunchSiteEligible(const CellStruct& Coords, BuildingClass* pBuilding, bool ignoreRange) const { if (!this->IsLaunchSite(pBuilding)) return false; @@ -153,23 +153,23 @@ bool SWTypeExt::ExtData::IsLaunchSiteEligible(const CellStruct& Coords, Building && (maxRange < 0.0 || (double)distance <= maxRange); } -bool SWTypeExt::ExtData::IsLaunchSite(BuildingClass* pBuilding) const +bool SWTypeExt::IsLaunchSite(BuildingClass* pBuilding) const { if (pBuilding->IsAlive && pBuilding->Health && !pBuilding->InLimbo && pBuilding->IsPowerOnline()) { - auto const pExt = BuildingExt::ExtMap.Find(pBuilding); + auto const pExt = BuildingExt::Fetch(pBuilding); return pExt->HasSuperWeapon(this->OwnerObject()->ArrayIndex); } return false; } -std::pair SWTypeExt::ExtData::GetLaunchSiteRange(BuildingClass* pBuilding) const +std::pair SWTypeExt::GetLaunchSiteRange(BuildingClass* pBuilding) const { return std::make_pair(this->SW_RangeMinimum.Get(), this->SW_RangeMaximum.Get()); } -bool SWTypeExt::ExtData::IsAvailable(HouseClass* pHouse) const +bool SWTypeExt::IsAvailable(HouseClass* pHouse) const { if (pHouse->TechLevel < this->SW_TechLevel) return false; @@ -177,7 +177,7 @@ bool SWTypeExt::ExtData::IsAvailable(HouseClass* pHouse) const const auto pThis = this->OwnerObject(); const int shots = this->SW_Shots; - if (shots >= 0 && HouseExt::ExtMap.Find(pHouse)->SuperExts[pThis->ArrayIndex].ShotCount >= shots) + if (shots >= 0 && HouseExt::Fetch(pHouse)->SuperExts[pThis->ArrayIndex].ShotCount >= shots) return false; if (pHouse->IsControlledByHuman() ? (!this->SW_AllowPlayer) : (!this->SW_AllowAI)) @@ -195,10 +195,10 @@ bool SWTypeExt::ExtData::IsAvailable(HouseClass* pHouse) const // June 7, 2026 - Starkku: PowersUpBuilding is now put in PowersUp_Buildings // so removed BuildingTypeClass::Find(pBuildingType->PowersUpBuilding check here. - if (pBuildingType && !BuildingTypeExt::ExtMap.Find(pBuildingType)->PowersUp_Buildings.empty()) + if (pBuildingType && !BuildingTypeExt::Fetch(pBuildingType)->PowersUp_Buildings.empty()) return BuildingTypeExt::GetUpgradesAmount(pBuildingType, pHouse) > 0; - return HouseExt::ExtMap.Find(pHouse)->CountOwnedPresentAndLimboed(pType) > 0; + return HouseExt::Fetch(pHouse)->CountOwnedPresentAndLimboed(pType) > 0; }; // check whether the optional aux building exists @@ -230,7 +230,7 @@ bool SWTypeExt::ExtData::IsAvailable(HouseClass* pHouse) const return true; } -std::vector SWTypeExt::ExtData::GetEMPulseCannons(HouseClass* pOwner, const CellStruct& cell) const +std::vector SWTypeExt::GetEMPulseCannons(HouseClass* pOwner, const CellStruct& cell) const { std::vector emCannons; @@ -268,7 +268,7 @@ std::vector SWTypeExt::ExtData::GetEMPulseCannons(HouseClass* pO return emCannons; } -std::pair SWTypeExt::ExtData::GetEMPulseCannonRange(BuildingClass* pBuilding) const +std::pair SWTypeExt::GetEMPulseCannonRange(BuildingClass* pBuilding) const { if (this->EMPulse_TargetSelf) return std::make_pair(-1.0, -1.0); @@ -294,7 +294,7 @@ std::pair SWTypeExt::ExtData::GetEMPulseCannonRange(BuildingClas return std::make_pair(this->SW_RangeMinimum.Get(), this->SW_RangeMaximum.Get()); } -void SWTypeExt::ExtData::PrintMessage(const CSFText& message, HouseClass* pFirer) const +void SWTypeExt::PrintMessage(const CSFText& message, HouseClass* pFirer) const { if (message.empty()) return; @@ -330,7 +330,7 @@ SuperClass* __stdcall SWTypeExt::IsSuperAvailable(int swIdx, HouseClass* pHouse) { if (const auto pSuper = pHouse->Supers.GetItemOrDefault(swIdx)) { - const auto pExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pExt = SWTypeExt::Fetch(pSuper->Type); if (pExt->IsAvailable(pHouse)) return pSuper; diff --git a/src/Ext/Scenario/Body.cpp b/src/Ext/Scenario/Body.cpp index c834987eb0..2cb15fd6e1 100644 --- a/src/Ext/Scenario/Body.cpp +++ b/src/Ext/Scenario/Body.cpp @@ -94,7 +94,7 @@ void ScenarioExt::LoadFromINIFile(ScenarioClass* pThis, CCINIClass* pINI) for (auto const pHouse : HouseClass::Array) { - HouseExt::ExtMap.Find(pHouse)->FreeRadar = ScenarioClass::Instance->FreeRadar; + HouseExt::Fetch(pHouse)->FreeRadar = ScenarioClass::Instance->FreeRadar; } } diff --git a/src/Ext/Scenario/Body.h b/src/Ext/Scenario/Body.h index 213449b1ae..751e3c5a0d 100644 --- a/src/Ext/Scenario/Body.h +++ b/src/Ext/Scenario/Body.h @@ -30,8 +30,8 @@ class ScenarioExt std::map Waypoints; std::map Variables[2]; // 0 for local, 1 for global - std::vector AutoDeathObjects; - std::vector TransportReloaders; // Objects that can reload ammo in limbo + std::vector AutoDeathObjects; + std::vector TransportReloaders; // Objects that can reload ammo in limbo bool SWSidebar_Enable; std::vector SWSidebar_Indices; @@ -42,7 +42,7 @@ class ScenarioExt PhobosFixedString<64u> DefaultLS800BkgdName; PhobosFixedString<64u> DefaultLS800BkgdPal; - std::vector LimboLaunchers; + std::vector LimboLaunchers; DynamicVectorClass UndergroundTracker; // Technos that are underground. DynamicVectorClass SpecialTracker; // For special purposes, like tracking technos that are forced moving. Currently unused. diff --git a/src/Ext/Script/Body.cpp b/src/Ext/Script/Body.cpp index 2a20086315..b5022b42ed 100644 --- a/src/Ext/Script/Body.cpp +++ b/src/Ext/Script/Body.cpp @@ -7,12 +7,12 @@ ScriptExt::ExtContainer ScriptExt::ExtMap; // ============================= // load / save -void ScriptExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void ScriptExt::LoadFromStream(PhobosStreamReader& Stm) { // Nothing yet } -void ScriptExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void ScriptExt::SaveToStream(PhobosStreamWriter& Stm) { // Nothing yet } @@ -323,7 +323,7 @@ void ScriptExt::LoadIntoTransports(TeamClass* pTeam) return; } - auto const pExt = TeamExt::ExtMap.Find(pTeam); + auto const pExt = TeamExt::Fetch(pTeam); auto const pLeaderUnit = ScriptExt::FindTheTeamLeader(pTeam); pExt->TeamLeader = pLeaderUnit; @@ -374,7 +374,7 @@ void ScriptExt::Mission_Gather_NearTheLeader(TeamClass* pTeam, int countdown) const auto currentMission = pScript->CurrentMission; const auto initialCountdown = scriptActions[currentMission].Argument; bool gatherUnits = false; - auto const pExt = TeamExt::ExtMap.Find(pTeam); + auto const pExt = TeamExt::Fetch(pTeam); // Load countdown if (pExt->Countdown_RegroupAtLeader >= 0) @@ -598,7 +598,7 @@ void ScriptExt::WaitIfNoTarget(TeamClass* pTeam, int attempts) if (attempts < 0) attempts = pTeam->CurrentScript->Type->ScriptActions[pTeam->CurrentScript->CurrentMission].Argument; - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); if (attempts <= 0) pTeamData->WaitNoTargetAttempts = -1; // Infinite waits if no target @@ -616,7 +616,7 @@ void ScriptExt::TeamWeightReward(TeamClass* pTeam, double award) if (award <= 0) award = pTeam->CurrentScript->Type->ScriptActions[pTeam->CurrentScript->CurrentMission].Argument; - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); if (award > 0) pTeamData->NextSuccessWeightAward = award; @@ -648,7 +648,7 @@ void ScriptExt::PickRandomScript(TeamClass* pTeam, int idxScriptsList) if (pNewScript->ActionsCount > 0) { changeFailed = false; - TeamExt::ExtMap.Find(pTeam)->PreviousScriptList.push_back(pTeam->CurrentScript); + TeamExt::Fetch(pTeam)->PreviousScriptList.push_back(pTeam->CurrentScript); pTeam->CurrentScript = nullptr; pTeam->CurrentScript = GameCreate(pNewScript); @@ -683,7 +683,7 @@ void ScriptExt::SetCloseEnoughDistance(TeamClass* pTeam, double distance) if (distance <= 0) distance = pTeam->CurrentScript->Type->ScriptActions[pTeam->CurrentScript->CurrentMission].Argument; - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); if (distance > 0) pTeamData->CloseEnough = distance; @@ -709,7 +709,7 @@ void ScriptExt::SetMoveMissionEndMode(TeamClass* pTeam, int mode) if (mode < 0 || mode > 2) mode = pTeam->CurrentScript->Type->ScriptActions[pTeam->CurrentScript->CurrentMission].Argument; - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); if (mode >= 0 && mode <= 2) pTeamData->MoveMissionEndMode = mode; @@ -726,7 +726,7 @@ bool ScriptExt::MoveMissionEndStatus(TeamClass* pTeam, TechnoClass* pFocus, Foot return false; double closeEnough = RulesClass::Instance->CloseEnough; - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); if (pTeamData->CloseEnough > 0) closeEnough = pTeamData->CloseEnough * (double)Unsorted::LeptonsPerCell; @@ -1079,7 +1079,7 @@ bool ScriptExt::IsExtVariableAction(int action) void ScriptExt::Set_ForceJump_Countdown(TeamClass* pTeam, bool repeatLine, int count) { - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); //auto const pScript = pTeam->CurrentScript; if (count <= 0) @@ -1105,7 +1105,7 @@ void ScriptExt::Set_ForceJump_Countdown(TeamClass* pTeam, bool repeatLine, int c void ScriptExt::Stop_ForceJump_Countdown(TeamClass* pTeam) { - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); //auto const pScript = pTeam->CurrentScript; pTeamData->ForceJump_InitialCountdown = -1; pTeamData->ForceJump_Countdown.Stop(); @@ -1118,7 +1118,7 @@ void ScriptExt::Stop_ForceJump_Countdown(TeamClass* pTeam) void ScriptExt::JumpBackToPreviousScript(TeamClass* pTeam) { - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); if (!pTeamData->PreviousScriptList.empty()) { @@ -1233,7 +1233,7 @@ void ScriptExt::ForceGlobalOnlyTargetHouseEnemy(TeamClass* pTeam, int mode) if (mode < 0 || mode > 2) mode = pTeam->CurrentScript->Type->ScriptActions[pTeam->CurrentScript->CurrentMission].Argument; - HouseExt::ForceOnlyTargetHouseEnemy(pTeam->Owner, mode); + HouseExt::SetForceOnlyTargetHouseEnemy(pTeam->Owner, mode); // This action finished pTeam->StepCompleted = true; diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index b9670e0741..45cd73002b 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -146,36 +146,35 @@ enum class PhobosScripts : unsigned int // Range 19000-19999 are miscellanous/uncategorized actions }; -class ScriptExt +class ScriptExt final : public AbstractExt { public: using base_type = ScriptClass; + using ExtData = ScriptExt; static constexpr DWORD Canary = 0x3B3B3B3B; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractExt +public: + // typed owner accessor + ScriptClass* OwnerObject() const { - public: - // typed owner accessor - ScriptClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - // Nothing yet + // Nothing yet - ExtData(ScriptClass* OwnerObject) : AbstractExt(OwnerObject) - // Nothing yet - { } + ScriptExt(ScriptClass* OwnerObject) : AbstractExt(OwnerObject) + // Nothing yet + { } - virtual ~ExtData() = default; + virtual ~ScriptExt() = default; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - }; +public: class ExtContainer final : public Container { public: @@ -185,6 +184,16 @@ class ScriptExt static ExtContainer ExtMap; + static ScriptExt* Fetch(const ScriptClass* pThis) + { + return ExtMap.Find(pThis); + } + + static ScriptExt* TryFetch(const ScriptClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static void ProcessAction(TeamClass* pTeam); static void ExecuteTimedAreaGuardAction(TeamClass* pTeam); static void LoadIntoTransports(TeamClass* pTeam); @@ -238,5 +247,3 @@ class ScriptExt static void ChronoshiftTeamToTarget(TeamClass* pTeam, TechnoClass* pTeamLeader, AbstractClass* pTarget); }; -// top-level name for the ScriptExt extension -using ScriptClassExtension = ScriptExt::ExtData; diff --git a/src/Ext/Script/Hooks.cpp b/src/Ext/Script/Hooks.cpp index 057a38ce4a..2d3bcd93be 100644 --- a/src/Ext/Script/Hooks.cpp +++ b/src/Ext/Script/Hooks.cpp @@ -4,7 +4,7 @@ DEFINE_HOOK(0x6E9443, TeamClass_AI, 0x8) { GET(TeamClass*, pTeam, ESI); - auto const pTeamData = TeamExt::ExtMap.Find(pTeam); + auto const pTeamData = TeamExt::Fetch(pTeam); // Force a line jump. This should support vanilla YR Actions if (pTeamData->ForceJump_InitialCountdown > 0 && pTeamData->ForceJump_Countdown.Expired()) diff --git a/src/Ext/Script/Mission.Attack.cpp b/src/Ext/Script/Mission.Attack.cpp index 417402ec53..df37027c43 100644 --- a/src/Ext/Script/Mission.Attack.cpp +++ b/src/Ext/Script/Mission.Attack.cpp @@ -12,12 +12,12 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, int calcThreatMode, bool repeat bool bAircraftsWithoutAmmo = false; bool agentMode = false; bool pacifistTeam = true; - const auto pTeamData = TeamExt::ExtMap.Find(pTeam); + const auto pTeamData = TeamExt::Fetch(pTeam); auto& waitNoTargetCounter = pTeamData->WaitNoTargetCounter; auto& waitNoTargetTimer = pTeamData->WaitNoTargetTimer; auto& waitNoTargetAttempts = pTeamData->WaitNoTargetAttempts; - const auto pHouseExt = HouseExt::ExtMap.Find(pTeam->Owner); + const auto pHouseExt = HouseExt::Fetch(pTeam->Owner); // When the new target wasn't found it sleeps some few frames before the new attempt. This can save cycles and cycles of unnecessary executed lines. if (waitNoTargetCounter > 0) { @@ -56,7 +56,7 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, int calcThreatMode, bool repeat for (auto pFoot = pFirstUnit; pFoot; pFoot = pFoot->NextTeamMember) { - const auto pKillerTechnoData = TechnoExt::ExtMap.Find(pFoot); + const auto pKillerTechnoData = TechnoExt::Fetch(pFoot); if (pKillerTechnoData->LastKillWasTeamTarget) { @@ -78,7 +78,7 @@ void ScriptExt::Mission_Attack(TeamClass* pTeam, int calcThreatMode, bool repeat for (auto pFootTeam = pFirstUnit; pFootTeam; pFootTeam = pFootTeam->NextTeamMember) { // Let's reset all Team Members objective - const auto pKillerTeamUnitData = TechnoExt::ExtMap.Find(pFootTeam); + const auto pKillerTeamUnitData = TechnoExt::Fetch(pFootTeam); pKillerTeamUnitData->LastKillWasTeamTarget = false; if (pFootTeam->WhatAmI() == AbstractType::Aircraft) @@ -456,7 +456,7 @@ TechnoClass* ScriptExt::GreatestThreat(TechnoClass* pTechno, int method, int cal double bestVal = -1; bool unitWeaponsHaveAA = false; bool unitWeaponsHaveAG = false; - const auto pTechnoTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + const auto pTechnoTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; const auto pTechnoType = pTechnoTypeExt->OwnerObject(); const auto pTechnoOwner = pTechno->Owner; const auto targetZoneScanType = pTechnoTypeExt->TargetZoneScanType; @@ -965,7 +965,7 @@ bool ScriptExt::EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attac if (!pTechno->Owner->IsNeutral()) { - const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + const auto pTechnoTypeExt = TechnoTypeExt::Fetch(pTechnoType); if (pTechnoTypeExt->RadarJamRadius > 0 || pTechnoTypeExt->InhibitorRange.isset()) @@ -1163,7 +1163,7 @@ bool ScriptExt::EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attac { if (pBuildingType->SuperWeapon >= 0 || pBuildingType->SuperWeapon2 >= 0 - || BuildingTypeExt::ExtMap.Find(pBuildingType)->SuperWeapons.size() > 0) + || BuildingTypeExt::Fetch(pBuildingType)->SuperWeapons.size() > 0) { return true; } @@ -1232,7 +1232,7 @@ bool ScriptExt::EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attac // Radar Jammer if (!pTechno->Owner->IsNeutral() - && TechnoTypeExt::ExtMap.Find(pTechnoType)->RadarJamRadius > 0) + && TechnoTypeExt::Fetch(pTechnoType)->RadarJamRadius > 0) { return true; } @@ -1243,7 +1243,7 @@ bool ScriptExt::EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attac // Inhibitor if (!pTechno->Owner->IsNeutral() - && TechnoTypeExt::ExtMap.Find(pTechnoType)->InhibitorRange.isset()) + && TechnoTypeExt::Fetch(pTechnoType)->InhibitorRange.isset()) { return true; } @@ -1361,7 +1361,7 @@ bool ScriptExt::EvaluateObjectWithMask(TechnoClass* pTechno, int mask, int attac void ScriptExt::Mission_Attack_List(TeamClass* pTeam, int calcThreatMode, bool repeatAction, int attackAITargetType) { - const auto pTeamData = TeamExt::ExtMap.Find(pTeam); + const auto pTeamData = TeamExt::Fetch(pTeam); pTeamData->IdxSelectedObjectFromAIList = -1; if (attackAITargetType < 0) @@ -1382,7 +1382,7 @@ void ScriptExt::Mission_Attack_List1Random(TeamClass* pTeam, int calcThreatMode, bool selected = false; int idxSelectedObject = -1; std::vector validIndexes; - const auto pTeamData = TeamExt::ExtMap.Find(pTeam); + const auto pTeamData = TeamExt::Fetch(pTeam); if (pTeamData->IdxSelectedObjectFromAIList >= 0) { @@ -1479,7 +1479,7 @@ bool ScriptExt::CheckUnitTargetingCapability(TechnoClass* pTechno, bool targetIn if (const auto pWeapon = TechnoExt::GetCurrentWeapon(pTechno, pType, secondary)) { const auto pBulletType = pWeapon->Projectile; - return (targetInAir ? pBulletType->AA : (pBulletType->AG && !BulletTypeExt::ExtMap.Find(pBulletType)->AAOnly)); + return (targetInAir ? pBulletType->AA : (pBulletType->AG && !BulletTypeExt::Fetch(pBulletType)->AAOnly)); } return false; diff --git a/src/Ext/Script/Mission.Move.cpp b/src/Ext/Script/Mission.Move.cpp index 8366326a1b..06e89b9c95 100644 --- a/src/Ext/Script/Mission.Move.cpp +++ b/src/Ext/Script/Mission.Move.cpp @@ -8,7 +8,7 @@ void ScriptExt::Mission_Move(TeamClass* pTeam, int calcThreatMode, bool pickAlli { bool noWaitLoop = false; bool bAircraftsWithoutAmmo = false; - const auto pTeamData = TeamExt::ExtMap.Find(pTeam); + const auto pTeamData = TeamExt::Fetch(pTeam); auto& waitNoTargetCounter = pTeamData->WaitNoTargetCounter; auto& waitNoTargetTimer = pTeamData->WaitNoTargetTimer; auto& waitNoTargetAttempts = pTeamData->WaitNoTargetAttempts; @@ -251,7 +251,7 @@ TechnoClass* ScriptExt::FindBestObject(TechnoClass* pTechno, int method, int cal if (const auto pFoot = abstract_cast(pTechno)) { const auto pTeam = pFoot->Team; - const auto pHouseExt = HouseExt::ExtMap.Find(pTeam->Owner); + const auto pHouseExt = HouseExt::Fetch(pTeam->Owner); const int enemyHouseIndex = pTeam->FirstUnit->Owner->EnemyHouseIndex; bool onlyTargetHouseEnemy = pFoot->Team->Type->OnlyTargetHouseEnemy; @@ -414,7 +414,7 @@ TechnoClass* ScriptExt::FindBestObject(TechnoClass* pTechno, int method, int cal void ScriptExt::Mission_Move_List(TeamClass* pTeam, int calcThreatMode, bool pickAllies, int attackAITargetType) { - const auto pTeamData = TeamExt::ExtMap.Find(pTeam); + const auto pTeamData = TeamExt::Fetch(pTeam); pTeamData->IdxSelectedObjectFromAIList = -1; if (attackAITargetType < 0) @@ -435,7 +435,7 @@ void ScriptExt::Mission_Move_List1Random(TeamClass* pTeam, int calcThreatMode, b bool selected = false; int idxSelectedObject = -1; std::vector validIndexes; - const auto pTeamData = TeamExt::ExtMap.Find(pTeam); + const auto pTeamData = TeamExt::Fetch(pTeam); if (pTeamData->IdxSelectedObjectFromAIList >= 0) { diff --git a/src/Ext/Side/Body.cpp b/src/Ext/Side/Body.cpp index c73b2ff779..e7a42294e4 100644 --- a/src/Ext/Side/Body.cpp +++ b/src/Ext/Side/Body.cpp @@ -2,7 +2,7 @@ SideExt::ExtContainer SideExt::ExtMap; -void SideExt::ExtData::Initialize() +void SideExt::Initialize() { const char* pID = this->OwnerObject()->ID; @@ -18,7 +18,7 @@ void SideExt::ExtData::Initialize() this->MessageTextColor = 21; }; -void SideExt::ExtData::LoadFromINIFile(CCINIClass* pINI) +void SideExt::LoadFromINIFile(CCINIClass* pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -61,7 +61,7 @@ void SideExt::ExtData::LoadFromINIFile(CCINIClass* pINI) // load / save template -void SideExt::ExtData::Serialize(T& Stm) +void SideExt::Serialize(T& Stm) { Stm .Process(this->ArrayIndex) @@ -96,15 +96,15 @@ void SideExt::ExtData::Serialize(T& Stm) ; } -void SideExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void SideExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void SideExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void SideExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -150,7 +150,7 @@ DEFINE_HOOK(0x679A10, SideClass_LoadAllFromINI, 0x5) GET_STACK(CCINIClass*, pINI, 0x4); for (auto const pSide : SideClass::Array) - SideExt::ExtMap.Find(pSide)->LoadFromINI(pINI); + SideExt::Fetch(pSide)->LoadFromINI(pINI); return 0; } diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index 5c5a45ab89..2b49b9c046 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -5,97 +5,96 @@ #include #include -class SideExt +class SideExt final : public AbstractTypeExt { public: using base_type = SideClass; + using ExtData = SideExt; static constexpr DWORD Canary = 0x05B10501; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + SideClass* OwnerObject() const { - public: - // typed owner accessor - SideClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - Valueable ArrayIndex; - Valueable Sidebar_GDIPositions; - Valueable IngameScore_WinTheme; - Valueable IngameScore_LoseTheme; - Valueable Sidebar_HarvesterCounter_Offset; - Valueable Sidebar_HarvesterCounter_HideMaxValue; - Valueable Sidebar_HarvesterCounter_OnlyMaxValue; - Nullable Sidebar_HarvesterCounter_ColorGreen; - Valueable Sidebar_HarvesterCounter_ColorYellow; - Valueable Sidebar_HarvesterCounter_ColorRed; - Valueable Sidebar_WeedsCounter_Offset; - Nullable Sidebar_WeedsCounter_Color; - Valueable Sidebar_ProducingProgress_Offset; - Valueable Sidebar_PowerDelta_Offset; - Valueable Sidebar_PowerDelta_ColorGreen; - Valueable Sidebar_PowerDelta_ColorYellow; - Valueable Sidebar_PowerDelta_ColorRed; - Valueable Sidebar_PowerDelta_ColorGrey; - Valueable Sidebar_PowerDelta_Align; - Nullable ToolTip_Background_Color; - Nullable ToolTip_Background_Opacity; - Nullable ToolTip_Background_BlurSize; - Valueable BriefingTheme; - ValueableIdx MessageTextColor; - PhobosPCXFile SuperWeaponSidebar_OnPCX; - PhobosPCXFile SuperWeaponSidebar_OffPCX; - PhobosPCXFile SuperWeaponSidebar_TopPCX; - PhobosPCXFile SuperWeaponSidebar_CenterPCX; - PhobosPCXFile SuperWeaponSidebar_BottomPCX; + Valueable ArrayIndex; + Valueable Sidebar_GDIPositions; + Valueable IngameScore_WinTheme; + Valueable IngameScore_LoseTheme; + Valueable Sidebar_HarvesterCounter_Offset; + Valueable Sidebar_HarvesterCounter_HideMaxValue; + Valueable Sidebar_HarvesterCounter_OnlyMaxValue; + Nullable Sidebar_HarvesterCounter_ColorGreen; + Valueable Sidebar_HarvesterCounter_ColorYellow; + Valueable Sidebar_HarvesterCounter_ColorRed; + Valueable Sidebar_WeedsCounter_Offset; + Nullable Sidebar_WeedsCounter_Color; + Valueable Sidebar_ProducingProgress_Offset; + Valueable Sidebar_PowerDelta_Offset; + Valueable Sidebar_PowerDelta_ColorGreen; + Valueable Sidebar_PowerDelta_ColorYellow; + Valueable Sidebar_PowerDelta_ColorRed; + Valueable Sidebar_PowerDelta_ColorGrey; + Valueable Sidebar_PowerDelta_Align; + Nullable ToolTip_Background_Color; + Nullable ToolTip_Background_Opacity; + Nullable ToolTip_Background_BlurSize; + Valueable BriefingTheme; + ValueableIdx MessageTextColor; + PhobosPCXFile SuperWeaponSidebar_OnPCX; + PhobosPCXFile SuperWeaponSidebar_OffPCX; + PhobosPCXFile SuperWeaponSidebar_TopPCX; + PhobosPCXFile SuperWeaponSidebar_CenterPCX; + PhobosPCXFile SuperWeaponSidebar_BottomPCX; - ExtData(SideClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , ArrayIndex { -1 } - , Sidebar_GDIPositions { false } - , IngameScore_WinTheme { -2 } - , IngameScore_LoseTheme { -2 } - , Sidebar_HarvesterCounter_Offset { { 0, 0 } } - , Sidebar_HarvesterCounter_HideMaxValue { false } - , Sidebar_HarvesterCounter_OnlyMaxValue { false } - , Sidebar_HarvesterCounter_ColorGreen { } - , Sidebar_HarvesterCounter_ColorYellow { { 255, 255, 0 } } - , Sidebar_HarvesterCounter_ColorRed { { 255, 0, 0 } } - , Sidebar_WeedsCounter_Offset { { 0, 0 } } - , Sidebar_WeedsCounter_Color {} - , Sidebar_ProducingProgress_Offset { { 0, 0 } } - , Sidebar_PowerDelta_Offset { { 0, 0 } } - , Sidebar_PowerDelta_ColorGreen { { 0, 255, 0 } } - , Sidebar_PowerDelta_ColorYellow { { 255, 255, 0 } } - , Sidebar_PowerDelta_ColorRed { { 255, 0, 0 } } - , Sidebar_PowerDelta_ColorGrey { { 0x80,0x80,0x80 } } - , Sidebar_PowerDelta_Align { TextAlign::Left } - , ToolTip_Background_Color { } - , ToolTip_Background_Opacity { } - , ToolTip_Background_BlurSize { } - , BriefingTheme { -1 } - , MessageTextColor { -1 } - , SuperWeaponSidebar_OnPCX {} - , SuperWeaponSidebar_OffPCX {} - , SuperWeaponSidebar_TopPCX {} - , SuperWeaponSidebar_CenterPCX {} - , SuperWeaponSidebar_BottomPCX {} - { } + SideExt(SideClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , ArrayIndex { -1 } + , Sidebar_GDIPositions { false } + , IngameScore_WinTheme { -2 } + , IngameScore_LoseTheme { -2 } + , Sidebar_HarvesterCounter_Offset { { 0, 0 } } + , Sidebar_HarvesterCounter_HideMaxValue { false } + , Sidebar_HarvesterCounter_OnlyMaxValue { false } + , Sidebar_HarvesterCounter_ColorGreen { } + , Sidebar_HarvesterCounter_ColorYellow { { 255, 255, 0 } } + , Sidebar_HarvesterCounter_ColorRed { { 255, 0, 0 } } + , Sidebar_WeedsCounter_Offset { { 0, 0 } } + , Sidebar_WeedsCounter_Color {} + , Sidebar_ProducingProgress_Offset { { 0, 0 } } + , Sidebar_PowerDelta_Offset { { 0, 0 } } + , Sidebar_PowerDelta_ColorGreen { { 0, 255, 0 } } + , Sidebar_PowerDelta_ColorYellow { { 255, 255, 0 } } + , Sidebar_PowerDelta_ColorRed { { 255, 0, 0 } } + , Sidebar_PowerDelta_ColorGrey { { 0x80,0x80,0x80 } } + , Sidebar_PowerDelta_Align { TextAlign::Left } + , ToolTip_Background_Color { } + , ToolTip_Background_Opacity { } + , ToolTip_Background_BlurSize { } + , BriefingTheme { -1 } + , MessageTextColor { -1 } + , SuperWeaponSidebar_OnPCX {} + , SuperWeaponSidebar_OffPCX {} + , SuperWeaponSidebar_TopPCX {} + , SuperWeaponSidebar_CenterPCX {} + , SuperWeaponSidebar_BottomPCX {} + { } - virtual ~ExtData() = default; + virtual ~SideExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void Initialize() override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -104,9 +103,17 @@ class SideExt }; static ExtContainer ExtMap; + + static SideExt* Fetch(const SideClass* pThis) + { + return ExtMap.Find(pThis); + } + + static SideExt* TryFetch(const SideClass* pThis) + { + return ExtMap.TryFind(pThis); + } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the SideExt extension -using SideClassExtension = SideExt::ExtData; diff --git a/src/Ext/Side/Hooks.SidebarGDIPositions.cpp b/src/Ext/Side/Hooks.SidebarGDIPositions.cpp index c8e65ad0c8..e63c615311 100644 --- a/src/Ext/Side/Hooks.SidebarGDIPositions.cpp +++ b/src/Ext/Side/Hooks.SidebarGDIPositions.cpp @@ -6,7 +6,7 @@ DEFINE_HOOK(0x534FA7, Prep_For_Side, 0x5) { GET(const int, sideIndex, ECX); const auto pSide = SideClass::Array.GetItemOrDefault(sideIndex); - const auto pSideExt = SideExt::ExtMap.TryFind(pSide); + const auto pSideExt = SideExt::TryFetch(pSide); isNODSidebar = pSideExt ? !pSideExt->Sidebar_GDIPositions : sideIndex; return 0; diff --git a/src/Ext/Side/Hooks.cpp b/src/Ext/Side/Hooks.cpp index c49735a83b..1e2fe547a3 100644 --- a/src/Ext/Side/Hooks.cpp +++ b/src/Ext/Side/Hooks.cpp @@ -7,7 +7,7 @@ DEFINE_HOOK(0x4FCD66, HouseClass_WinLoseTheme, 0x5) // HouseClass::Flag_T { const auto pThis = HouseClass::CurrentPlayer; const auto pSide = SideClass::Array.GetItemOrDefault(pThis->SideIndex); - const auto pSideExt = SideExt::ExtMap.TryFind(pSide); + const auto pSideExt = SideExt::TryFetch(pSide); if (pSideExt) { diff --git a/src/Ext/Sidebar/Hooks.cpp b/src/Ext/Sidebar/Hooks.cpp index cf19a93322..0000934544 100644 --- a/src/Ext/Sidebar/Hooks.cpp +++ b/src/Ext/Sidebar/Hooks.cpp @@ -36,7 +36,7 @@ DEFINE_HOOK(0x6A6EB1, SidebarClass_DrawIt_ProducingProgress, 0x6) if (Phobos::UI::ProducingProgress_Show) { const auto pPlayer = HouseClass::CurrentPlayer; - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.GetItem(HouseClass::CurrentPlayer->SideIndex)); + const auto pSideExt = SideExt::Fetch(SideClass::Array.GetItem(HouseClass::CurrentPlayer->SideIndex)); const int XOffset = pSideExt->Sidebar_GDIPositions ? 29 : 32; const int XBase = (pSideExt->Sidebar_GDIPositions ? 26 : 20) + pSideExt->Sidebar_ProducingProgress_Offset.Get().X; const int YBase = 197 + pSideExt->Sidebar_ProducingProgress_Offset.Get().Y; diff --git a/src/Ext/Sidebar/SWSidebar/SWButtonClass.cpp b/src/Ext/Sidebar/SWSidebar/SWButtonClass.cpp index 49e283e631..31b4e2f023 100644 --- a/src/Ext/Sidebar/SWSidebar/SWButtonClass.cpp +++ b/src/Ext/Sidebar/SWSidebar/SWButtonClass.cpp @@ -36,7 +36,7 @@ bool SWButtonClass::Draw(bool forced) const auto pCurrent = HouseClass::CurrentPlayer; const auto pSuper = pCurrent->Supers[this->SuperIndex]; const auto pType = pSuper->Type; - const auto pSWExt = SWTypeExt::ExtMap.Find(pType); + const auto pSWExt = SWTypeExt::Fetch(pType); // support for pcx cameos if (const auto pPCXCameo = pSWExt->SidebarPCX.GetSurface()) @@ -181,7 +181,7 @@ bool SWButtonClass::LaunchSuper() const const auto pCurrent = HouseClass::CurrentPlayer; const auto pSuper = pCurrent->Supers[this->SuperIndex]; const auto pType = pSuper->Type; - const auto pSWExt = SWTypeExt::ExtMap.Find(pType); + const auto pSWExt = SWTypeExt::Fetch(pType); const bool manual = !pSWExt->SW_ManualFire && pSWExt->SW_AutoFire; const bool unstoppable = pType->UseChargeDrain && pSuper->ChargeDrainState == ChargeDrainState::Draining && pSWExt->SW_Unstoppable; diff --git a/src/Ext/Sidebar/SWSidebar/SWColumnClass.cpp b/src/Ext/Sidebar/SWSidebar/SWColumnClass.cpp index 8bfff873f5..17de09daec 100644 --- a/src/Ext/Sidebar/SWSidebar/SWColumnClass.cpp +++ b/src/Ext/Sidebar/SWSidebar/SWColumnClass.cpp @@ -22,7 +22,7 @@ bool SWColumnClass::Draw(bool forced) if (!SWSidebarClass::IsEnabled()) return false; - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); + const auto pSideExt = SideExt::Fetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); const int cameoWidth = 60, cameoHeight = 48; const int cameoBackgroundWidth = Phobos::UI::SuperWeaponSidebar_Interval + cameoWidth; const int coordX = this->X; @@ -90,8 +90,8 @@ bool SWColumnClass::AddButton(int superIdx) auto Compare = [ownerBits](const int left, const int right) { - const auto pExtA = SWTypeExt::ExtMap.TryFind(SuperWeaponTypeClass::Array.GetItemOrDefault(left)); - const auto pExtB = SWTypeExt::ExtMap.TryFind(SuperWeaponTypeClass::Array.GetItemOrDefault(right)); + const auto pExtA = SWTypeExt::TryFetch(SuperWeaponTypeClass::Array.GetItemOrDefault(left)); + const auto pExtB = SWTypeExt::TryFetch(SuperWeaponTypeClass::Array.GetItemOrDefault(right)); if (pExtB && (pExtB->SuperWeaponSidebar_PriorityHouses & ownerBits) && (!pExtA || !(pExtA->SuperWeaponSidebar_PriorityHouses & ownerBits))) return false; @@ -172,7 +172,7 @@ void SWColumnClass::ClearButtons(bool remove) void SWColumnClass::SetHeight(int height) { - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); + const auto pSideExt = SideExt::Fetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); this->Height = height; diff --git a/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp b/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp index 5d4eec40f8..95812e5b60 100644 --- a/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp +++ b/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp @@ -78,7 +78,7 @@ void SWSidebarClass::InitIO() if (!Phobos::UI::SuperWeaponSidebar || Unsorted::ArmageddonMode) return; - if (const auto pSideExt = SideExt::ExtMap.TryFind(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])) + if (const auto pSideExt = SideExt::TryFetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])) { const auto pOnPCX = pSideExt->SuperWeaponSidebar_OnPCX.GetSurface(); const auto pOffPCX = pSideExt->SuperWeaponSidebar_OffPCX.GetSurface(); @@ -127,7 +127,7 @@ bool SWSidebarClass::AddButton(int superIdx) if (!pSWType) return false; - const auto pSWExt = SWTypeExt::ExtMap.Find(pSWType); + const auto pSWExt = SWTypeExt::Fetch(pSWType); if (!pSWExt->SW_ShowCameo && pSWExt->SW_AutoFire) return false; @@ -181,8 +181,8 @@ void SWSidebarClass::SortButtons() std::stable_sort(vec_Buttons.begin(), vec_Buttons.end(), [ownerBits](SWButtonClass* const a, SWButtonClass* const b) { - const auto pExtA = SWTypeExt::ExtMap.TryFind(SuperWeaponTypeClass::Array.GetItemOrDefault(a->SuperIndex)); - const auto pExtB = SWTypeExt::ExtMap.TryFind(SuperWeaponTypeClass::Array.GetItemOrDefault(b->SuperIndex)); + const auto pExtA = SWTypeExt::TryFetch(SuperWeaponTypeClass::Array.GetItemOrDefault(a->SuperIndex)); + const auto pExtB = SWTypeExt::TryFetch(SuperWeaponTypeClass::Array.GetItemOrDefault(b->SuperIndex)); if (pExtB && (pExtB->SuperWeaponSidebar_PriorityHouses & ownerBits) && (!pExtA || !(pExtA->SuperWeaponSidebar_PriorityHouses & ownerBits))) return false; @@ -193,7 +193,7 @@ void SWSidebarClass::SortButtons() return BuildType::SortsBefore(AbstractType::Special, a->SuperIndex, AbstractType::Special, b->SuperIndex); }); - const auto pTopPCX = SideExt::ExtMap.TryFind(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])->SuperWeaponSidebar_TopPCX.GetSurface(); + const auto pTopPCX = SideExt::TryFetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])->SuperWeaponSidebar_TopPCX.GetSurface(); const int buttonCount = static_cast(vec_Buttons.size()); const int cameoWidth = 60, cameoHeight = 48; const int firstColumn = Phobos::UI::SuperWeaponSidebar_Max; diff --git a/src/Ext/Sidebar/SWSidebar/ToggleSWButtonClass.cpp b/src/Ext/Sidebar/SWSidebar/ToggleSWButtonClass.cpp index 5cf360465a..85a69bd85e 100644 --- a/src/Ext/Sidebar/SWSidebar/ToggleSWButtonClass.cpp +++ b/src/Ext/Sidebar/SWSidebar/ToggleSWButtonClass.cpp @@ -17,7 +17,7 @@ bool ToggleSWButtonClass::Draw(bool forced) if (columns.empty()) return false; - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); + const auto pSideExt = SideExt::Fetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); const auto pTogglePCX = SWSidebarClass::IsEnabled() ? pSideExt->SuperWeaponSidebar_OnPCX.GetSurface() : pSideExt->SuperWeaponSidebar_OffPCX.GetSurface(); if (!pTogglePCX) @@ -60,7 +60,7 @@ bool ToggleSWButtonClass::Action(GadgetFlag flags, DWORD* pKey, KeyModifier modi if (columns.empty()) return false; - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); + const auto pSideExt = SideExt::Fetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex]); if (SWSidebarClass::IsEnabled() ? !pSideExt->SuperWeaponSidebar_OnPCX.GetSurface() : !pSideExt->SuperWeaponSidebar_OffPCX.GetSurface()) return false; diff --git a/src/Ext/TAction/Body.cpp b/src/Ext/TAction/Body.cpp index 4fcee58310..87b825137b 100644 --- a/src/Ext/TAction/Body.cpp +++ b/src/Ext/TAction/Body.cpp @@ -14,18 +14,18 @@ TActionExt::ExtContainer TActionExt::ExtMap; // load / save template -void TActionExt::ExtData::Serialize(T& Stm) +void TActionExt::Serialize(T& Stm) { //Stm; } -void TActionExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TActionExt::LoadFromStream(PhobosStreamReader& Stm) { AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TActionExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TActionExt::SaveToStream(PhobosStreamWriter& Stm) { AbstractExt::SaveToStream(Stm); this->Serialize(Stm); @@ -422,7 +422,7 @@ bool TActionExt::UndeployToWaypoint(TActionClass* const pThis, HouseClass* const if (!allBuilding && !pBuildingType) return true; - const auto& limboDelivereds = HouseExt::ExtMap.Find(vHouse)->OwnedLimboDeliveredBuildings; + const auto& limboDelivereds = HouseExt::Fetch(vHouse)->OwnedLimboDeliveredBuildings; const bool existLimboBuilding = !limboDelivereds.empty(); const auto vectorBegin = limboDelivereds.begin(); const auto vectorEnd = limboDelivereds.end(); @@ -631,7 +631,7 @@ bool TActionExt::ClearAngerNode(TActionClass* pThis, HouseClass* pHouse, ObjectC bool TActionExt::SetForceEnemy(TActionClass* pThis, HouseClass* pHouse, ObjectClass* pObject, TriggerClass* pTrigger, CellStruct const& location) { - auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto const pHouseExt = HouseExt::Fetch(pHouse); const int value = pThis->Param3; if (value >= 0 || value == -2) @@ -669,7 +669,7 @@ bool TActionExt::SetFreeRadar(TActionClass* const pThis, HouseClass* const pHous { if (pHouse->IsControlledByHuman()) { - auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto const pHouseExt = HouseExt::Fetch(pHouse); switch (pThis->Param3) { @@ -701,7 +701,7 @@ bool TActionExt::SetTeamDelay(TActionClass* const pThis, HouseClass* const pHous { const int value = pThis->Param3; const int timer = value < 0 ? RulesClass::Instance->TeamDelays.Items[pHouse->GetAIDifficultyIndex()] : value; - HouseExt::ExtMap.Find(pHouse)->TeamDelay = value; + HouseExt::Fetch(pHouse)->TeamDelay = value; auto& Timer = pHouse->TeamDelayTimer; const int time = std::min(Timer.GetTimeLeft(), timer); diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index b44e223386..ff2f0160e6 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -31,35 +31,35 @@ enum class PhobosTriggerAction : unsigned int DeleteBanner = 802, }; -class TActionExt +class TActionExt final : public AbstractExt { public: using base_type = TActionClass; + using ExtData = TActionExt; static constexpr DWORD Canary = 0x91919191; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractExt +public: + // typed owner accessor + TActionClass* OwnerObject() const { - public: - // typed owner accessor - TActionClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - ExtData(TActionClass* const OwnerObject) : AbstractExt(OwnerObject) - { } + TActionExt(TActionClass* const OwnerObject) : AbstractExt(OwnerObject) + { } - virtual ~ExtData() = default; + virtual ~TActionExt() = default; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); + +public: static bool Execute(TActionClass* pThis, HouseClass* pHouse, ObjectClass* pObject, TriggerClass* pTrigger, CellStruct const& location, bool& bHandled); @@ -96,6 +96,7 @@ class TActionExt #undef ACTION_FUNC #pragma pop_macro("ACTION_FUNC") +public: class ExtContainer final : public Container { public: @@ -104,7 +105,15 @@ class TActionExt }; static ExtContainer ExtMap; + + static TActionExt* Fetch(const TActionClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TActionExt* TryFetch(const TActionClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; -// top-level name for the TActionExt extension -using TActionClassExtension = TActionExt::ExtData; diff --git a/src/Ext/TEvent/Body.cpp b/src/Ext/TEvent/Body.cpp index f88669c475..80f4033c84 100644 --- a/src/Ext/TEvent/Body.cpp +++ b/src/Ext/TEvent/Body.cpp @@ -10,18 +10,18 @@ TEventExt::ExtContainer TEventExt::ExtMap; // load / save template -void TEventExt::ExtData::Serialize(T& Stm) +void TEventExt::Serialize(T& Stm) { //Stm; } -void TEventExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TEventExt::LoadFromStream(PhobosStreamReader& Stm) { AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TEventExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TEventExt::SaveToStream(PhobosStreamWriter& Stm) { AbstractExt::SaveToStream(Stm); this->Serialize(Stm); @@ -333,7 +333,7 @@ bool TEventExt::AttachedIsUnderAttachedEffectTEvent(TEventClass* pThis, ObjectCl return false; } - if (TechnoExt::ExtMap.Find(pTechno)->HasAttachedEffects({ pDesiredType }, false, false, nullptr, nullptr, nullptr, nullptr)) + if (TechnoExt::Fetch(pTechno)->HasAttachedEffects({ pDesiredType }, false, false, nullptr, nullptr, nullptr, nullptr)) return true; return false; diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index a21fa41d3a..c19a48cefd 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -56,35 +56,35 @@ enum PhobosTriggerEvent _DummyMaximum, }; -class TEventExt +class TEventExt final : public AbstractExt { public: using base_type = TEventClass; + using ExtData = TEventExt; static constexpr DWORD Canary = 0x91919191; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractExt +public: + // typed owner accessor + TEventClass* OwnerObject() const { - public: - // typed owner accessor - TEventClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - ExtData(TEventClass* const OwnerObject) : AbstractExt(OwnerObject) - { } + TEventExt(TEventClass* const OwnerObject) : AbstractExt(OwnerObject) + { } - virtual ~ExtData() = default; + virtual ~TEventExt() = default; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); + +public: static int GetFlags(int iEvent); @@ -105,6 +105,7 @@ class TEventExt static bool AttachedIsUnderAttachedEffectTEvent(TEventClass* pThis, ObjectClass* pObject); +public: class ExtContainer final : public Container { public: @@ -113,7 +114,15 @@ class TEventExt }; static ExtContainer ExtMap; + + static TEventExt* Fetch(const TEventClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TEventExt* TryFetch(const TEventClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; -// top-level name for the TEventExt extension -using TEventClassExtension = TEventExt::ExtData; diff --git a/src/Ext/Team/Body.cpp b/src/Ext/Team/Body.cpp index 9cacdb9119..1d8be2bc29 100644 --- a/src/Ext/Team/Body.cpp +++ b/src/Ext/Team/Body.cpp @@ -6,7 +6,7 @@ TeamExt::ExtContainer TeamExt::ExtMap; // load / save template -void TeamExt::ExtData::Serialize(T& Stm) +void TeamExt::Serialize(T& Stm) { Stm .Process(this->WaitNoTargetAttempts) @@ -25,19 +25,19 @@ void TeamExt::ExtData::Serialize(T& Stm) ; } -void TeamExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TeamExt::LoadFromStream(PhobosStreamReader& Stm) { AbstractExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TeamExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TeamExt::SaveToStream(PhobosStreamWriter& Stm) { AbstractExt::SaveToStream(Stm); this->Serialize(Stm); } -void TeamExt::ExtData::OnDetach(FootClass* pTarget, bool removed) +void TeamExt::OnDetach(FootClass* pTarget, bool removed) { if (removed) AnnounceInvalidPointer(this->TeamLeader, pTarget); diff --git a/src/Ext/Team/Body.h b/src/Ext/Team/Body.h index 3a218f7088..cab4ae1cd2 100644 --- a/src/Ext/Team/Body.h +++ b/src/Ext/Team/Body.h @@ -5,65 +5,64 @@ #include #include -class TeamExt +class TeamExt final : public AbstractExt, public Detach::Listener { public: using base_type = TeamClass; + using ExtData = TeamExt; static constexpr DWORD Canary = 0x414B4B41; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractExt, public Detach::Listener +public: + // typed owner accessor + TeamClass* OwnerObject() const { - public: - // typed owner accessor - TeamClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - int WaitNoTargetAttempts; - double NextSuccessWeightAward; - int IdxSelectedObjectFromAIList; - double CloseEnough; - int Countdown_RegroupAtLeader; - int MoveMissionEndMode; - int WaitNoTargetCounter; - CDTimerClass WaitNoTargetTimer; - CDTimerClass ForceJump_Countdown; - int ForceJump_InitialCountdown; - bool ForceJump_RepeatMode; - FootClass* TeamLeader; - std::vector PreviousScriptList; - - ExtData(TeamClass* OwnerObject) : AbstractExt(OwnerObject) - , WaitNoTargetAttempts { 0 } - , NextSuccessWeightAward { 0 } - , IdxSelectedObjectFromAIList { -1 } - , CloseEnough { -1 } - , Countdown_RegroupAtLeader { -1 } - , MoveMissionEndMode { 0 } - , WaitNoTargetCounter { 0 } - , WaitNoTargetTimer { } - , ForceJump_Countdown { } - , ForceJump_InitialCountdown { -1 } - , ForceJump_RepeatMode { false } - , TeamLeader { nullptr } - , PreviousScriptList { } - { } - - virtual ~ExtData() = default; - - virtual void OnDetach(FootClass* pTarget, bool removed) override; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + int WaitNoTargetAttempts; + double NextSuccessWeightAward; + int IdxSelectedObjectFromAIList; + double CloseEnough; + int Countdown_RegroupAtLeader; + int MoveMissionEndMode; + int WaitNoTargetCounter; + CDTimerClass WaitNoTargetTimer; + CDTimerClass ForceJump_Countdown; + int ForceJump_InitialCountdown; + bool ForceJump_RepeatMode; + FootClass* TeamLeader; + std::vector PreviousScriptList; + + TeamExt(TeamClass* OwnerObject) : AbstractExt(OwnerObject) + , WaitNoTargetAttempts { 0 } + , NextSuccessWeightAward { 0 } + , IdxSelectedObjectFromAIList { -1 } + , CloseEnough { -1 } + , Countdown_RegroupAtLeader { -1 } + , MoveMissionEndMode { 0 } + , WaitNoTargetCounter { 0 } + , WaitNoTargetTimer { } + , ForceJump_Countdown { } + , ForceJump_InitialCountdown { -1 } + , ForceJump_RepeatMode { false } + , TeamLeader { nullptr } + , PreviousScriptList { } + { } + + virtual ~TeamExt() = default; + + virtual void OnDetach(FootClass* pTarget, bool removed) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); + +public: class ExtContainer final : public Container { public: @@ -73,7 +72,15 @@ class TeamExt static ExtContainer ExtMap; + static TeamExt* Fetch(const TeamClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TeamExt* TryFetch(const TeamClass* pThis) + { + return ExtMap.TryFind(pThis); + } + }; -// top-level name for the TeamExt extension -using TeamClassExtension = TeamExt::ExtData; diff --git a/src/Ext/Team/Hooks.cpp b/src/Ext/Team/Hooks.cpp index 5e7b90839b..135d7a01ff 100644 --- a/src/Ext/Team/Hooks.cpp +++ b/src/Ext/Team/Hooks.cpp @@ -64,7 +64,7 @@ DEFINE_HOOK(0x6EA6BE, TeamClass_CanAddMember_Consideration, 0x6) GET(TeamClass*, pTeam, EBP); GET(FootClass*, pFoot, ESI); GET(int*, idx, EBX); - const auto pFootTypeExt = TechnoExt::ExtMap.Find(pFoot)->TypeExtData; + const auto pFootTypeExt = TechnoExt::Fetch(pFoot)->TypeExtData; const auto pFootType = pFootTypeExt->OwnerObject(); const auto pTaskForce = pTeam->Type->TaskForce; @@ -89,7 +89,7 @@ DEFINE_HOOK(0x6EA8E7, TeamClass_LiberateMember_Consideration, 0x5) GET(TeamClass*, pTeam, EDI); GET(FootClass*, pMember, EBP); int idx = 0; - const auto pMemberTypeExt = TechnoExt::ExtMap.Find(pMember)->TypeExtData; + const auto pMemberTypeExt = TechnoExt::Fetch(pMember)->TypeExtData; const auto pMemberType = pMemberTypeExt->OwnerObject(); const auto pTaskForce = pTeam->Type->TaskForce; @@ -119,7 +119,7 @@ DEFINE_HOOK(0x6EAD73, TeamClass_RecruitMember_Consideration, 0x7) const auto pTaskForce = pTeam->Type->TaskForce; const auto pSearchType = pTaskForce->Entries[idx].Type; - return pSearchType == pMemberType || TechnoTypeExt::ExtMap.Find(pMemberType)->TeamMember_ConsideredAs.Contains(pSearchType) ? ContinueCheck : SkipThisMember; + return pSearchType == pMemberType || TechnoTypeExt::Fetch(pMemberType)->TeamMember_ConsideredAs.Contains(pSearchType) ? ContinueCheck : SkipThisMember; } DEFINE_HOOK(0x6EF57F, TeamClass_GetTaskForceMissingMemberTypes_Consideration, 0x5) @@ -133,7 +133,7 @@ DEFINE_HOOK(0x6EF57F, TeamClass_GetTaskForceMissingMemberTypes_Consideration, 0x GET(DynamicVectorClass*, vector, ESI); GET(FootClass*, pMember, EDI); - const auto pMemberTypeExt = TechnoExt::ExtMap.Find(pMember)->TypeExtData; + const auto pMemberTypeExt = TechnoExt::Fetch(pMember)->TypeExtData; for (const auto pConsideType : pMemberTypeExt->TeamMember_ConsideredAs) { @@ -154,7 +154,7 @@ DEFINE_HOOK(0x6EA870, TeamClass_LiberateMember_Start, 0x6) GET_STACK(FootClass*, pMember, 0x4); GET(TeamClass*, pTeam, ECX); - const auto pTeamTypeExt = TeamTypeExt::ExtMap.Find(pTeam->Type); + const auto pTeamTypeExt = TeamTypeExt::Fetch(pTeam->Type); const int value = pTeamTypeExt->SetRecruitableOnLiberate.Get(RulesExt::Global()->SetRecruitableOnLiberate); if (value > 0) diff --git a/src/Ext/TeamType/Body.cpp b/src/Ext/TeamType/Body.cpp index 7d720a71a6..19ce01e6f7 100644 --- a/src/Ext/TeamType/Body.cpp +++ b/src/Ext/TeamType/Body.cpp @@ -5,7 +5,7 @@ TeamTypeExt::ExtContainer TeamTypeExt::ExtMap; // ============================= // load / save -void TeamTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void TeamTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -15,22 +15,22 @@ void TeamTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) } template -void TeamTypeExt::ExtData::Serialize(T& Stm) +void TeamTypeExt::Serialize(T& Stm) { Stm .Process(this->SetRecruitableOnLiberate) ; } -void TeamTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TeamTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TeamTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TeamTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 4f80041126..2962bd38e0 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -5,42 +5,41 @@ #include #include -class TeamTypeExt +class TeamTypeExt final : public AbstractTypeExt { public: using base_type = TeamTypeClass; + using ExtData = TeamTypeExt; static constexpr DWORD Canary = 0xABCDEF01; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + TeamTypeClass* OwnerObject() const { - public: - // typed owner accessor - TeamTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - ExtData(TeamTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , SetRecruitableOnLiberate { } - { } + TeamTypeExt(TeamTypeClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , SetRecruitableOnLiberate { } + { } - virtual ~ExtData() = default; + virtual ~TeamTypeExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; - // virtual void Initialize() override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; + // virtual void Initialize() override; - Nullable SetRecruitableOnLiberate; + Nullable SetRecruitableOnLiberate; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -49,7 +48,15 @@ class TeamTypeExt }; static ExtContainer ExtMap; + + static TeamTypeExt* Fetch(const TeamTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TeamTypeExt* TryFetch(const TeamTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; -// top-level name for the TeamTypeExt extension -using TeamTypeClassExtension = TeamTypeExt::ExtData; diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index 7251d5ae27..c22f517e9a 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -2,7 +2,7 @@ // Unsorted methods -void TechnoExt::ExtData::InitializeLaserTrails() +void TechnoExt::InitializeLaserTrails() { if (this->LaserTrails.size()) return; @@ -24,7 +24,7 @@ void TechnoExt::ObjectKilledBy(TechnoClass* pVictim, TechnoClass* pKiller) { if (auto const pFootKiller = generic_cast(pObjectKiller)) { - auto const pKillerTechnoData = TechnoExt::ExtMap.Find(pObjectKiller); + auto const pKillerTechnoData = TechnoExt::Fetch(pObjectKiller); pKillerTechnoData->LastKillWasTeamTarget = pFootKiller->Team->Focus == pVictim; } } @@ -72,7 +72,7 @@ CoordStruct TechnoExt::GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FL FLHFound = false; CoordStruct FLH = CoordStruct::Empty; - auto const pExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const pInf = abstract_cast(pThis); std::span> pickedFLHs = pExt->WeaponBurstFLHs; @@ -114,7 +114,7 @@ CoordStruct TechnoExt::GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& FLHFound = false; CoordStruct FLH = CoordStruct::Empty; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TechnoTypeExt::Fetch(pThis->Type); Nullable pickedFLH; if (pThis->IsDeployed()) @@ -144,7 +144,7 @@ CoordStruct TechnoExt::GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& return FLH; } -void TechnoExt::ExtData::InitializeDisplayInfo() +void TechnoExt::InitializeDisplayInfo() { const auto pThis = this->OwnerObject(); const auto pPrimary = pThis->GetWeapon(0)->WeaponType; @@ -157,7 +157,7 @@ void TechnoExt::ExtData::InitializeDisplayInfo() pThis->RearmTimer.StartTime = Math::min(-2, -pThis->RearmTimer.TimeLeft); } -void TechnoExt::ExtData::InitializeAttachEffects() +void TechnoExt::InitializeAttachEffects() { auto const pTypeExt = this->TypeExtData; @@ -182,11 +182,11 @@ int TechnoExt::GetTintColor(TechnoClass* pThis, bool invulnerability, bool airst if (airstrike) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (auto const pAirstrike = pExt->AirstrikeTargetingMe) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pAirstrike->Owner)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pAirstrike->Owner)->TypeExtData; tintColor |= pTypeExt->TintColorAirstrike; } } @@ -221,7 +221,7 @@ int TechnoExt::GetCustomTintIntensity(TechnoClass* pThis) // Applies custom tint color and intensity from TechnoTypes and any AttachEffects and shields it might have on provided values. void TechnoExt::ApplyCustomTintValues(TechnoClass* pThis, int& color, int& intensity) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pOwner = pThis->Owner; if (pOwner == HouseClass::CurrentPlayer) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index f9573e4a58..a0c560698d 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -16,7 +16,7 @@ // 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() +void TechnoExt::OnEarlyUpdate() { auto const pType = this->OwnerObject()->GetTechnoType(); @@ -43,7 +43,7 @@ void TechnoExt::ExtData::OnEarlyUpdate() this->ApplyInterceptor(); } -void TechnoExt::ExtData::ApplyInterceptor() +void TechnoExt::ApplyInterceptor() { const auto pTypeExt = this->TypeExtData; const auto pInterceptorType = pTypeExt->InterceptorType.get(); @@ -63,7 +63,7 @@ void TechnoExt::ExtData::ApplyInterceptor() if (pTarget->WhatAmI() != AbstractType::Bullet) return; - const auto pTargetExt = BulletExt::ExtMap.Find(static_cast(pTarget)); + const auto pTargetExt = BulletExt::Fetch(static_cast(pTarget)); if ((pTargetExt->InterceptedStatus & InterceptedStatus::Locked) == InterceptedStatus::None) return; @@ -87,7 +87,7 @@ void TechnoExt::ExtData::ApplyInterceptor() for (auto const pBullet : BulletClass::Array) { - const auto pBulletExt = BulletExt::ExtMap.Find(pBullet); + const auto pBulletExt = BulletExt::Fetch(pBullet); const auto pBulletTypeExt = pBulletExt->TypeExtData; if (!pBulletTypeExt->Interceptable || pBullet->SpawnNextAnim) @@ -148,7 +148,7 @@ void TechnoExt::ExtData::ApplyInterceptor() } } -void TechnoExt::ExtData::DepletedAmmoActions() +void TechnoExt::DepletedAmmoActions() { auto const pTypeExt = this->TypeExtData; const int min = pTypeExt->Ammo_AutoDeployMinimumAmount; @@ -191,7 +191,7 @@ void TechnoExt::ExtData::DepletedAmmoActions() } } -void TechnoExt::ExtData::AmmoAutoConvertActions() +void TechnoExt::AmmoAutoConvertActions() { const auto pTypeExt = this->TypeExtData; @@ -218,7 +218,7 @@ void TechnoExt::ExtData::AmmoAutoConvertActions() } // Subterranean harvester factory exit state machine. -void TechnoExt::ExtData::UpdateSubterraneanHarvester() +void TechnoExt::UpdateSubterraneanHarvester() { auto const pThis = static_cast(this->OwnerObject()); @@ -275,7 +275,7 @@ void TechnoExt::ExtData::UpdateSubterraneanHarvester() } // TODO : Merge into new AttachEffects -bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) +bool TechnoExt::CheckDeathConditions(bool isInLimbo) { auto const pTypeExt = this->TypeExtData; @@ -315,12 +315,12 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) auto existSingleType = [pOwner, affectedHouse, allowLimbo](TechnoTypeClass* pType) { if (affectedHouse == AffectedHouse::Owner) - return allowLimbo ? HouseExt::ExtMap.Find(pOwner)->CountOwnedPresentAndLimboed(pType) > 0 : pOwner->CountOwnedAndPresent(pType) > 0; + return allowLimbo ? HouseExt::Fetch(pOwner)->CountOwnedPresentAndLimboed(pType) > 0 : pOwner->CountOwnedAndPresent(pType) > 0; for (auto const pHouse : HouseClass::Array) { if (EnumFunctions::CanTargetHouse(affectedHouse, pOwner, pHouse) - && (allowLimbo ? HouseExt::ExtMap.Find(pHouse)->CountOwnedPresentAndLimboed(pType) > 0 : pHouse->CountOwnedAndPresent(pType) > 0)) + && (allowLimbo ? HouseExt::Fetch(pHouse)->CountOwnedPresentAndLimboed(pType) > 0 : pHouse->CountOwnedAndPresent(pType) > 0)) return true; } @@ -357,7 +357,7 @@ bool TechnoExt::ExtData::CheckDeathConditions(bool isInLimbo) return false; } -void TechnoExt::ExtData::EatPassengers() +void TechnoExt::EatPassengers() { auto const pTypeExt = this->TypeExtData; @@ -539,7 +539,7 @@ void TechnoExt::ExtData::EatPassengers() } } -void TechnoExt::ExtData::UpdateTiberiumEater() +void TechnoExt::UpdateTiberiumEater() { const auto pEaterType = this->TypeExtData->TiberiumEaterType.get(); @@ -640,13 +640,13 @@ void TechnoExt::ExtData::UpdateTiberiumEater() this->TiberiumEater_Timer.Start(pEaterType->TransDelay); } -void TechnoExt::ExtData::UpdateShield() +void TechnoExt::UpdateShield() { if (const auto pShieldData = this->Shield.get()) pShieldData->AI(); } -void TechnoExt::ExtData::UpdateOnTunnelEnter() +void TechnoExt::UpdateOnTunnelEnter() { if (!this->IsInTunnel) { @@ -663,7 +663,7 @@ void TechnoExt::ExtData::UpdateOnTunnelEnter() } } -void TechnoExt::ExtData::UpdateOnTunnelExit() +void TechnoExt::UpdateOnTunnelExit() { this->IsInTunnel = false; @@ -671,7 +671,7 @@ void TechnoExt::ExtData::UpdateOnTunnelExit() pShieldData->SetAnimationVisibility(true); } -void TechnoExt::ExtData::ApplySpawnLimitRange() +void TechnoExt::ApplySpawnLimitRange() { auto const pTypeExt = this->TypeExtData; @@ -689,11 +689,11 @@ void TechnoExt::ExtData::ApplySpawnLimitRange() } } -void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType) +void TechnoExt::UpdateTypeData(TechnoTypeClass* pCurrentType) { auto const pThis = this->OwnerObject(); auto const pOldType = this->TypeExtData->OwnerObject(); - auto const pOldTypeExt = TechnoTypeExt::ExtMap.Find(pOldType); + auto const pOldTypeExt = TechnoTypeExt::Fetch(pOldType); auto const pOwner = pThis->Owner; auto& pSlaveManager = pThis->SlaveManager; auto& pSpawnManager = pThis->SpawnManager; @@ -703,7 +703,7 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType) // Cache the new type data this->PreviousType = pOldType; - auto const pNewTypeExt = TechnoTypeExt::ExtMap.Find(pCurrentType); + auto const pNewTypeExt = TechnoTypeExt::Fetch(pCurrentType); this->TypeExtData = pNewTypeExt; this->UpdateSelfOwnedAttachEffects(); @@ -764,14 +764,14 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType) { if (!pNewTypeExt->Harvester_Counted) { - auto& vec = HouseExt::ExtMap.Find(pOwner)->OwnedCountedHarvesters; + auto& vec = HouseExt::Fetch(pOwner)->OwnedCountedHarvesters; vec.erase(std::remove(vec.begin(), vec.end(), pThis), vec.end()); } } // Add to harvesters list if it's a harvester. else if (pNewTypeExt->Harvester_Counted) { - HouseExt::ExtMap.Find(pOwner)->OwnedCountedHarvesters.push_back(pThis); + HouseExt::Fetch(pOwner)->OwnedCountedHarvesters.push_back(pThis); } // Remove from limbo reloaders if no longer applicable @@ -1170,13 +1170,13 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType) } } -void TechnoExt::ExtData::UpdateTypeData_Foot() +void TechnoExt::UpdateTypeData_Foot() { auto const pThis = static_cast(this->OwnerObject()); auto const pOldType = this->PreviousType; auto const pCurrentType = this->TypeExtData->OwnerObject(); auto const abs = pThis->WhatAmI(); - //auto const pOldTypeExt = TechnoTypeExt::ExtMap.Find(pOldType); + //auto const pOldTypeExt = TechnoTypeExt::Fetch(pOldType); // Update movement sound if still moving while type changed. if (pThis->IsMoveSoundPlaying && pThis->Locomotor->Is_Moving()) @@ -1364,7 +1364,7 @@ void TechnoExt::ExtData::UpdateTypeData_Foot() this->PreviousType = nullptr; } -void TechnoExt::ExtData::UpdateLaserTrails() +void TechnoExt::UpdateLaserTrails() { if (this->LaserTrails.size() <= 0) return; @@ -1410,7 +1410,7 @@ void TechnoExt::ExtData::UpdateLaserTrails() } } -void TechnoExt::ExtData::UpdateMindControlAnim() +void TechnoExt::UpdateMindControlAnim() { auto const pThis = this->OwnerObject(); @@ -1445,7 +1445,7 @@ void TechnoExt::ExtData::UpdateMindControlAnim() } } -void TechnoExt::ExtData::UpdateRecountBurst() +void TechnoExt::UpdateRecountBurst() { const auto pThis = this->OwnerObject(); @@ -1469,7 +1469,7 @@ void TechnoExt::ExtData::UpdateRecountBurst() } } -void TechnoExt::ExtData::UpdateGattlingRateDownReset() +void TechnoExt::UpdateGattlingRateDownReset() { const auto pTypeExt = this->TypeExtData; @@ -1497,7 +1497,7 @@ void TechnoExt::ApplyGainedSelfHeal(TechnoClass* pThis) if (!RulesExt::Global()->GainSelfHealAllowMultiplayPassive && pThis->Owner->Type->MultiplayPassive) return; - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const pType = pTypeExt->OwnerObject(); const int healthDeficit = pType->Strength - pThis->Health; @@ -1604,13 +1604,13 @@ void TechnoExt::ApplyGainedSelfHeal(TechnoClass* pThis) return; } -void TechnoExt::ExtData::ApplyMindControlRangeLimit() +void TechnoExt::ApplyMindControlRangeLimit() { auto const pThis = this->OwnerObject(); if (auto const pCapturer = pThis->MindControlledBy) { - auto const pCapturerExt = TechnoExt::ExtMap.Find(pCapturer)->TypeExtData; + auto const pCapturerExt = TechnoExt::Fetch(pCapturer)->TypeExtData; if (pCapturerExt->MindControlRangeLimit.Get() > 0 && pCapturer->DistanceFrom(pThis) > pCapturerExt->MindControlRangeLimit.Get()) @@ -1637,7 +1637,7 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, cons auto const pBldType = pBuilding->Type; if (!pBuilding->InLimbo && !pBldType->Insignificant && !pBldType->DontScore) - HouseExt::ExtMap.Find(pBuilding->Owner)->RemoveFromLimboTracking(pBldType); + HouseExt::Fetch(pBuilding->Owner)->RemoveFromLimboTracking(pBldType); } auto const pTransport = pThis->Transporter; @@ -1728,7 +1728,7 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, cons void TechnoExt::UpdateSharedAmmo(TechnoClass* pThis) { - const auto pExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pExt->Ammo_Shared) { @@ -1738,7 +1738,7 @@ void TechnoExt::UpdateSharedAmmo(TechnoClass* pThis) { for (auto pPassenger = pThis->Passengers.GetFirstPassenger(); pPassenger; pPassenger = abstract_cast(pPassenger->NextObject)) { - const auto pPassengerExt = TechnoExt::ExtMap.Find(pPassenger)->TypeExtData; + const auto pPassengerExt = TechnoExt::Fetch(pPassenger)->TypeExtData; if (pPassengerExt->Ammo_Shared) { @@ -1756,7 +1756,7 @@ void TechnoExt::UpdateSharedAmmo(TechnoClass* pThis) } } -void TechnoExt::ExtData::UpdateTemporal() +void TechnoExt::UpdateTemporal() { if (const auto pShieldData = this->Shield.get()) { @@ -1770,7 +1770,7 @@ void TechnoExt::ExtData::UpdateTemporal() this->UpdateRearmInTemporal(); } -void TechnoExt::ExtData::UpdateRearmInEMPState() +void TechnoExt::UpdateRearmInEMPState() { const auto pThis = this->OwnerObject(); @@ -1786,7 +1786,7 @@ void TechnoExt::ExtData::UpdateRearmInEMPState() pThis->ReloadTimer.StartTime++; } -void TechnoExt::ExtData::UpdateRearmInTemporal() +void TechnoExt::UpdateRearmInTemporal() { const auto pThis = this->OwnerObject(); const auto pTypeExt = this->TypeExtData; @@ -1799,7 +1799,7 @@ void TechnoExt::ExtData::UpdateRearmInTemporal() } // Resets target if KeepTargetOnMove unit moves beyond weapon range. -void TechnoExt::ExtData::UpdateKeepTargetOnMove() +void TechnoExt::UpdateKeepTargetOnMove() { if (!this->KeepTargetOnMove) return; @@ -1853,7 +1853,7 @@ void TechnoExt::ExtData::UpdateKeepTargetOnMove() } } -void TechnoExt::ExtData::UpdateWarpInDelay() +void TechnoExt::UpdateWarpInDelay() { if (this->HasRemainingWarpInDelay) { @@ -1871,7 +1871,7 @@ void TechnoExt::ExtData::UpdateWarpInDelay() } // Updates state of all AttachEffects on techno. -void TechnoExt::ExtData::UpdateAttachEffects() +void TechnoExt::UpdateAttachEffects() { if (!this->AttachedEffects.size()) return; @@ -1965,7 +1965,7 @@ void TechnoExt::ExtData::UpdateAttachEffects() } // Updates self-owned (defined on TechnoType) AttachEffects, called on type conversion. -void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() +void TechnoExt::UpdateSelfOwnedAttachEffects() { auto const pThis = this->OwnerObject(); auto const pTypeExt = this->TypeExtData; @@ -2028,7 +2028,7 @@ void TechnoExt::ExtData::UpdateSelfOwnedAttachEffects() } // Updates CumulativeAnimations AE's on techno. -void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved) +void TechnoExt::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved) { AttachEffectClass* pAELargestDuration = nullptr; AttachEffectClass* pAEWithAnim = nullptr; @@ -2070,7 +2070,7 @@ void TechnoExt::ExtData::UpdateCumulativeAttachEffects(AttachEffectTypeClass* pA } // Recalculates AttachEffect stat multipliers and other bonuses. -bool TechnoExt::ExtData::RecalculateStatMultipliers(AttachEffectClass* pAttachEffect) +bool TechnoExt::RecalculateStatMultipliers(AttachEffectClass* pAttachEffect) { auto const pThis = this->OwnerObject(); auto& pAE = this->AE; @@ -2162,7 +2162,7 @@ bool TechnoExt::ExtData::RecalculateStatMultipliers(AttachEffectClass* pAttachEf } // Recalculates tint values. -void TechnoExt::ExtData::UpdateTintValues() +void TechnoExt::UpdateTintValues() { // reset values this->TintColorOwner = 0; @@ -2224,7 +2224,7 @@ void TechnoExt::ExtData::UpdateTintValues() } } -void TechnoExt::ExtData::UpdateLastTargetCrd() +void TechnoExt::UpdateLastTargetCrd() { if (!this->TypeExtData->ExtraThreat_Enabled) return; diff --git a/src/Ext/Techno/Body.Visuals.cpp b/src/Ext/Techno/Body.Visuals.cpp index ff47accb46..e465ebc335 100644 --- a/src/Ext/Techno/Body.Visuals.cpp +++ b/src/Ext/Techno/Body.Visuals.cpp @@ -9,7 +9,7 @@ void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, Rectang if (!RulesExt::Global()->GainSelfHealAllowMultiplayPassive && pThis->Owner->Type->MultiplayPassive) return; - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pTypeExt->SelfHealGainType.isset() && pTypeExt->SelfHealGainType.Get() == SelfHealGainType::NoHeal) return; @@ -127,7 +127,7 @@ void TechnoExt::DrawSelfHealPips(TechnoClass* pThis, Point2D* pLocation, Rectang void TechnoExt::DrawInsignia(TechnoClass* pThis, Point2D* pLocation, RectangleStruct* pBounds) { - auto pTechnoTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto pTechnoTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto pTechnoType = pTechnoTypeExt->OwnerObject(); auto pOwner = pThis->Owner; const bool isObserver = HouseClass::IsCurrentPlayerObserver(); @@ -138,7 +138,7 @@ void TechnoExt::DrawInsignia(TechnoClass* pThis, Point2D* pLocation, RectangleSt if (auto const pType = TechnoTypeExt::GetTechnoType(pThis->Disguise)) { pTechnoType = pType; - pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pType); + pTechnoTypeExt = TechnoTypeExt::Fetch(pType); pOwner = pThis->DisguisedAsHouse; } else if (pThis->Disguise->WhatAmI() == AbstractType::TerrainType && (!isObserver && !pOwner->IsAlliedWith(HouseClass::CurrentPlayer))) @@ -335,7 +335,7 @@ Point2D TechnoExt::GetBuildingSelectBracketPosition(TechnoClass* pThis, Building void TechnoExt::DrawSelectBox(TechnoClass* pThis, const Point2D* pLocation, const RectangleStruct* pBounds, bool drawBefore) { const auto whatAmI = pThis->WhatAmI(); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; const auto pType = pTypeExt->OwnerObject(); SelectBoxTypeClass* pSelectBox = nullptr; @@ -415,7 +415,7 @@ void TechnoExt::ProcessDigitalDisplays(TechnoClass* pThis) if (!Phobos::Config::DigitalDisplay_Enable) return; - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pTypeExt->DigitalDisplay_Disable) return; @@ -462,7 +462,7 @@ void TechnoExt::ProcessDigitalDisplays(TechnoClass* pThis) } const auto pType = pTypeExt->OwnerObject(); - const auto pShield = TechnoExt::ExtMap.Find(pThis)->Shield.get(); + const auto pShield = TechnoExt::Fetch(pThis)->Shield.get(); const bool hasShield = pShield && !pShield->IsBrokenAndNonRespawning(); const bool isBuilding = whatAmI == AbstractType::Building; const bool isInfantry = whatAmI == AbstractType::Infantry; @@ -516,7 +516,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, } case DisplayInfoType::Shield: { - const auto pShield = TechnoExt::ExtMap.Find(pThis)->Shield.get(); + const auto pShield = TechnoExt::Fetch(pThis)->Shield.get(); if (!pShield || pShield->IsBrokenAndNonRespawning()) return; @@ -685,7 +685,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, } case DisplayInfoType::PassengerKill: { - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (!pExt->TypeExtData->PassengerDeletionType) return; @@ -697,7 +697,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, } case DisplayInfoType::AutoDeath: { - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto pTypeExt = pExt->TypeExtData; if (!pTypeExt->AutoDeath_Behavior.isset()) @@ -726,7 +726,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, { const auto pHouse = pThis->Owner; const auto pBuildingType = static_cast(pType); - const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); + const auto pBuildingTypeExt = BuildingTypeExt::Fetch(pBuildingType); if (infoIndex && infoIndex <= pBuildingTypeExt->GetSuperWeaponCount()) { @@ -850,7 +850,7 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, TechnoTypeClass* pType, void TechnoExt::GetDigitalDisplayFakeHealth(TechnoClass* pThis, int& value, int& maxValue) { - if (TechnoExt::ExtMap.Find(pThis)->TypeExtData->DigitalDisplay_Health_FakeAtDisguise) + if (TechnoExt::Fetch(pThis)->TypeExtData->DigitalDisplay_Health_FakeAtDisguise) { if (const auto pType = TechnoTypeExt::GetTechnoType(pThis->Disguise)) { @@ -864,7 +864,7 @@ void TechnoExt::GetDigitalDisplayFakeHealth(TechnoClass* pThis, int& value, int& void TechnoExt::ShowPromoteAnim(TechnoClass* pThis) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const& veteranAnims = !pTypeExt->Promote_VeteranAnimation.empty() ? pTypeExt->Promote_VeteranAnimation : RulesExt::Global()->Promote_VeteranAnimation; auto const& eliteAnims = !pTypeExt->Promote_EliteAnimation.empty() ? pTypeExt->Promote_EliteAnimation : RulesExt::Global()->Promote_EliteAnimation; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 3cc08cff12..c716c63ea0 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -18,7 +18,7 @@ TechnoExt::ExtContainer TechnoExt::ExtMap; UnitClass* TechnoExt::Deployer = nullptr; -TechnoExt::ExtData::~ExtData() +TechnoExt::~TechnoExt() { auto const pTypeExt = this->TypeExtData; auto const pType = pTypeExt->OwnerObject(); @@ -50,7 +50,7 @@ TechnoExt::ExtData::~ExtData() if (pTypeExt->Harvester_Counted) { - auto& vec = HouseExt::ExtMap.Find(pThis->Owner)->OwnedCountedHarvesters; + auto& vec = HouseExt::Fetch(pThis->Owner)->OwnedCountedHarvesters; vec.erase(std::remove(vec.begin(), vec.end(), pThis), vec.end()); } @@ -172,7 +172,7 @@ void TechnoExt::SyncInvulnerability(TechnoClass* pFrom, TechnoClass* pTo) { if (pFrom->IsIronCurtained()) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pFrom)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pFrom)->TypeExtData; const bool isForceShielded = pFrom->ForceShielded; const bool allowSyncing = !isForceShielded ? pTypeExt->IronCurtain_KeptOnDeploy.Get(RulesExt::Global()->IronCurtain_KeptOnDeploy) @@ -199,13 +199,13 @@ double TechnoExt::GetCurrentSpeedMultiplier(FootClass* pThis) else houseMultiplier = pThis->Owner->Type->SpeedUnitsMult; - return pThis->SpeedMultiplier * houseMultiplier * TechnoExt::ExtMap.Find(pThis)->AE.SpeedMultiplier * + return pThis->SpeedMultiplier * houseMultiplier * TechnoExt::Fetch(pThis)->AE.SpeedMultiplier * (pThis->HasAbility(Ability::Faster) ? RulesClass::Instance->VeteranSpeed : 1.0); } double TechnoExt::GetCurrentFirepowerMultiplier(TechnoClass* pThis) { - double mult = pThis->FirepowerMultiplier * pThis->Owner->FirepowerMultiplier * TechnoExt::ExtMap.Find(pThis)->AE.FirepowerMultiplier * + double mult = pThis->FirepowerMultiplier * pThis->Owner->FirepowerMultiplier * TechnoExt::Fetch(pThis)->AE.FirepowerMultiplier * (pThis->HasAbility(Ability::Firepower) ? RulesClass::Instance->VeteranCombat : 1.0); if (const auto pBuilding = abstract_cast(pThis)) @@ -214,20 +214,20 @@ double TechnoExt::GetCurrentFirepowerMultiplier(TechnoClass* pThis) if (pBuildingType->CanBeOccupied && pBuildingType->CanOccupyFire && pBuildingType->MaxNumberOccupants) { - const auto pBuildingTypeExt = BuildingTypeExt::ExtMap.Find(pBuildingType); + const auto pBuildingTypeExt = BuildingTypeExt::Fetch(pBuildingType); mult *= pBuildingTypeExt->BuildingOccupyDamageMult.Get(RulesClass::Instance->OccupyDamageMultiplier); } } else if (const auto pBunker = abstract_cast(pThis->BunkerLinkedItem)) { - const auto pBunkerTypeExt = BuildingTypeExt::ExtMap.Find(pBunker->Type); + const auto pBunkerTypeExt = BuildingTypeExt::Fetch(pBunker->Type); mult *= pBunkerTypeExt->BuildingBunkerDamageMult.Get(RulesClass::Instance->BunkerDamageMultiplier); } else if (pThis->InOpenToppedTransport && pThis->Transporter) { - const auto pTransporterTypeExt = TechnoExt::ExtMap.Find(pThis->Transporter)->TypeExtData; + const auto pTransporterTypeExt = TechnoExt::Fetch(pThis->Transporter)->TypeExtData; mult *= pTransporterTypeExt->OpenTopped_DamageMultiplier.Get(RulesClass::Instance->OpenToppedDamageMultiplier); - mult *= TechnoExt::ExtMap.Find(pThis)->TypeExtData->OpenTransport_DamageMultiplier; + mult *= TechnoExt::Fetch(pThis)->TypeExtData->OpenTransport_DamageMultiplier; } return mult; @@ -361,7 +361,7 @@ bool TechnoExt::ConvertToType(FootClass* pThis, TechnoTypeClass* pToType) { if (AresFunctions::ConvertTypeTo(pThis, pToType)) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis); + auto const pTypeExt = TechnoExt::Fetch(pThis); pTypeExt->UpdateTypeData(pToType); pTypeExt->UpdateTypeData_Foot(); return true; @@ -441,7 +441,7 @@ bool TechnoExt::ConvertToType(FootClass* pThis, TechnoTypeClass* pToType) else pThis->PrimaryFacing.SetROT(pToType->ROT); // Adjust Ares TurretROT -- skipped - // pThis->SecondaryFacing.SetROT(TechnoTypeExt::ExtMap.Find(pToType)->TurretROT.Get(pToType->ROT)); + // pThis->SecondaryFacing.SetROT(TechnoTypeExt::Fetch(pToType)->TurretROT.Get(pToType->ROT)); // Locomotor change, referenced from Ares 0.A's abduction code, not sure if correct, untested CLSID nowLocoID; @@ -465,7 +465,7 @@ bool TechnoExt::ConvertToType(FootClass* pThis, TechnoTypeClass* pToType) if (pToType->BalloonHover && pToType->DeployToLand && prevType->Locomotor != jjLoco && toLoco == jjLoco) pThis->Locomotor->Move_To(pThis->Location); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis); + auto const pTypeExt = TechnoExt::Fetch(pThis); pTypeExt->UpdateTypeData(pToType); pTypeExt->UpdateTypeData_Foot(); return true; @@ -521,7 +521,7 @@ bool TechnoExt::IsTypeImmune(TechnoClass* pThis, TechnoClass* pSource) /// Invoker Techno used for same source check. /// Source AbstractClass instance used for same source check. /// True if techno has active AttachEffects that satisfy the source, false if not. -bool TechnoExt::ExtData::HasAttachedEffects(std::vector attachEffectTypes, bool requireAll, bool ignoreSameSource, +bool TechnoExt::HasAttachedEffects(std::vector attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector const* minCounts, std::vector const* maxCounts) const { unsigned int foundCount = 0; @@ -586,7 +586,7 @@ bool TechnoExt::ExtData::HasAttachedEffects(std::vector /// Invoker Techno used for same source check. /// Source AbstractClass instance used for same source check. /// Number of active cumulative AttachEffect type instances on the techno. 0 if the AttachEffect type is not cumulative. -int TechnoExt::ExtData::GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource) const +int TechnoExt::GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource) const { if (!pAttachEffectType->Cumulative) return 0; @@ -608,7 +608,7 @@ int TechnoExt::ExtData::GetAttachedEffectCumulativeCount(AttachEffectTypeClass* return foundCount; } -UnitTypeClass* TechnoExt::GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt::ExtData* pData) +UnitTypeClass* TechnoExt::GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt* pData) { if (pUnit->IsGreenHP()) { @@ -651,7 +651,7 @@ UnitTypeClass* TechnoExt::GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt::ExtD AircraftTypeClass* TechnoExt::GetAircraftTypeExtra(AircraftClass* pAircraft) { auto const pType = pAircraft->Type; - auto const pData = TechnoTypeExt::ExtMap.Find(pType); + auto const pData = TechnoTypeExt::Fetch(pType); if (!pData->NeedDamagedImage || pAircraft->IsGreenHP()) { @@ -674,7 +674,7 @@ AircraftTypeClass* TechnoExt::GetAircraftTypeExtra(AircraftClass* pAircraft) } -void TechnoExt::ExtData::ResetDelayedFireTimer() +void TechnoExt::ResetDelayedFireTimer() { this->DelayedFireTimer.Stop(); this->DelayedFireWeaponIndex = -1; @@ -682,7 +682,7 @@ void TechnoExt::ExtData::ResetDelayedFireTimer() if (this->CurrentDelayedFireAnim) { - if (AnimExt::ExtMap.Find(this->CurrentDelayedFireAnim)->DelayedFireRemoveOnNoDelay) + if (AnimExt::Fetch(this->CurrentDelayedFireAnim)->DelayedFireRemoveOnNoDelay) this->CurrentDelayedFireAnim->UnInit(); } } @@ -701,23 +701,23 @@ void TechnoExt::CreateDelayedFireAnim(TechnoClass* pThis, AnimTypeClass* pAnimTy if (attach) pAnim->SetOwnerObject(pThis); - auto const pAnimExt = AnimExt::ExtMap.Find(pAnim); + auto const pAnimExt = AnimExt::Fetch(pAnim); pAnim->Owner = pThis->Owner; pAnimExt->SetInvoker(pThis); if (attach) { pAnimExt->DelayedFireRemoveOnNoDelay = removeOnNoDelay; - TechnoExt::ExtMap.Find(pThis)->CurrentDelayedFireAnim = pAnim; + TechnoExt::Fetch(pThis)->CurrentDelayedFireAnim = pAnim; } } } bool TechnoExt::HandleDelayedFireWithPauseSequence(TechnoClass* pThis, WeaponTypeClass* pWeapon, int weaponIndex, int frame, int firingFrame) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto& timer = pExt->DelayedFireTimer; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pExt->DelayedFireWeaponIndex >= 0 && pExt->DelayedFireWeaponIndex != weaponIndex) { @@ -806,7 +806,7 @@ bool TechnoExt::CannotMove(UnitClass* pThis) bool TechnoExt::HasAmmoToDeploy(TechnoClass* pThis) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; const int min = pTypeExt->Ammo_DeployUnlockMinimumAmount; const int max = pTypeExt->Ammo_DeployUnlockMaximumAmount; @@ -824,7 +824,7 @@ bool TechnoExt::HasAmmoToDeploy(TechnoClass* pThis) void TechnoExt::HandleOnDeployAmmoChange(TechnoClass* pThis, int maxAmmoOverride) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (const int add = pTypeExt->Ammo_AddOnDeploy) { @@ -847,7 +847,7 @@ bool TechnoExt::SimpleDeployerAllowedToDeploy(UnitClass* pThis, bool defaultValu if (!pType->IsSimpleDeployer) return defaultValue; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (alwaysCheckLandTypes || pTypeExt->IsSimpleDeployer_ConsiderPathfinding) { @@ -1121,7 +1121,7 @@ bool __fastcall TechnoExt::ApplyKillDriver(TechnoClass** pData, void*, HouseClas } } - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); if (passive && pTypeExt->DriverKilled_KeptPassengers) break; @@ -1216,7 +1216,7 @@ bool __fastcall TechnoExt::ApplyKillDriver(TechnoClass** pData, void*, HouseClas return true; } -int TechnoExt::ExtData::GetSight() +int TechnoExt::GetSight() { double sight = this->TypeExtData->OwnerObject()->Sight; @@ -1231,7 +1231,7 @@ int TechnoExt::ExtData::GetSight() bool TechnoExt::HasWeaponsDisabled(TechnoClass* pThis) { - if (TechnoExt::ExtMap.Find(pThis)->AE.DisableWeapons) + if (TechnoExt::Fetch(pThis)->AE.DisableWeapons) return true; if (AresHelper::CanUseAres) @@ -1247,7 +1247,7 @@ bool TechnoExt::HasWeaponsDisabled(TechnoClass* pThis) FireError TechnoExt::GetFireErrorIgnoreDisableWeapons(TechnoClass* pThis, AbstractClass* pTarget, int weaponIndex, bool ignoreRange) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pExt_Ares = reinterpret_cast(pThis->align_154); bool const canUseAres = AresHelper::CanUseAres; bool const disableWeapons = pExt->AE.DisableWeapons; @@ -1274,7 +1274,7 @@ FireError TechnoExt::GetFireErrorIgnoreDisableWeapons(TechnoClass* pThis, Abstra // load / save template -void TechnoExt::ExtData::Serialize(T& Stm) +void TechnoExt::Serialize(T& Stm) { Stm .Process(this->TypeExtData) @@ -1349,21 +1349,21 @@ void TechnoExt::ExtData::Serialize(T& Stm) ; } -void TechnoExt::ExtData::OnDetach(AirstrikeClass* pTarget, bool removed) +void TechnoExt::OnDetach(AirstrikeClass* pTarget, bool removed) { if (removed) AnnounceInvalidPointer(this->AirstrikeTargetingMe, pTarget); } -void TechnoExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TechnoExt::LoadFromStream(PhobosStreamReader& Stm) { - RadioClassExtension::LoadFromStream(Stm); + RadioExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TechnoExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TechnoExt::SaveToStream(PhobosStreamWriter& Stm) { - RadioClassExtension::SaveToStream(Stm); + RadioExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -1386,18 +1386,18 @@ TechnoExt::ExtContainer::ExtContainer() : Container("TechnoClass") { } TechnoExt::ExtContainer::~ExtContainer() = default; -TechnoExt::ExtData* TechnoExt::ExtContainer::CreateExtData(AbstractType tag, TechnoClass* pOwner) const +TechnoExt* TechnoExt::ExtContainer::CreateExtData(AbstractType tag, TechnoClass* pOwner) const { switch (tag) { case AbstractType::Unit: - return new UnitClassExtension(static_cast(pOwner)); + return new UnitExt(static_cast(pOwner)); case AbstractType::Infantry: - return new InfantryClassExtension(static_cast(pOwner)); + return new InfantryExt(static_cast(pOwner)); case AbstractType::Aircraft: - return new AircraftClassExtension(static_cast(pOwner)); + return new AircraftExt(static_cast(pOwner)); case AbstractType::Building: - return new BuildingExt::ExtData(static_cast(pOwner)); + return new BuildingExt(static_cast(pOwner)); default: Debug::FatalErrorAndExit("TechnoExt - unexpected extension tag %d in the save stream!\n", static_cast(tag)); return nullptr; @@ -1428,7 +1428,7 @@ DEFINE_HOOK(0x710415, TechnoClass_DetachAnim, 0x6) GET(TechnoClass*, pThis, ECX); GET(AbstractClass*, pTarget, EAX); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->CurrentDelayedFireAnim == pTarget) pExt->CurrentDelayedFireAnim = nullptr; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 1e4d8c61d9..50cb96ed03 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -12,237 +12,236 @@ class AirstrikeClass; class BulletClass; -class TechnoExt +class TechnoExt : public RadioExt, public Detach::Listener { public: using base_type = TechnoClass; + using ExtData = TechnoExt; static constexpr DWORD Canary = 0x55555555; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData : public RadioClassExtension, public Detach::Listener +public: + // typed owner accessor (the base chain stores the owner as RadioClass*) + TechnoClass* OwnerObject() const { - public: - // typed owner accessor (the base chain stores the owner as RadioClass*) - TechnoClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - TechnoTypeExt::ExtData* TypeExtData; - std::unique_ptr Shield; - std::vector> LaserTrails; - std::vector> AttachedEffects; - AttachEffectTechnoProperties AE; - TechnoTypeClass* PreviousType; // Type change registered in TechnoClass::AI on current frame and used in FootClass::AI on same frame and reset after. - std::vector ElectricBolts; // EBolts are not serialized so do not serialize this either. - int AnimRefCount; // Used to keep track of how many times this techno is referenced in anims f.ex Invoker, ParentBuilding etc., for pointer invalidation. - int SubterraneanHarvStatus; // 0 = none, 1 = created, 2 = out from factory - AbstractClass* SubterraneanHarvRallyPoint; - bool ReceiveDamage; - bool LastKillWasTeamTarget; - CDTimerClass PassengerDeletionTimer; - ShieldTypeClass* CurrentShieldType; - int LastWarpDistance; - int JumpjetSpeed; - CDTimerClass ChargeTurretTimer; // Used for charge turrets instead of RearmTimer if weapon has ChargeTurret.Delays set. - CDTimerClass AutoDeathTimer; - AnimTypeClass* MindControlRingAnimType; - int DamageNumberOffset; - int Strafe_BombsDroppedThisRound; - CellClass* Strafe_TargetCell; - int CurrentAircraftWeaponIndex; - bool IsInTunnel; - bool IsBurrowed; - bool HasBeenPlacedOnMap; // Set to true on first Unlimbo() call. - CDTimerClass DeployFireTimer; - bool SkipTargetChangeResetSequence; - bool ForceFullRearmDelay; - bool LastRearmWasFullDelay; - bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon. - int WHAnimRemainingCreationInterval; - WeaponTypeClass* LastWeaponType; - CellClass* FiringObstacleCell; // Set on firing if there is an obstacle cell between target and techno, used for updating WaveClass target etc. - bool IsDetachingForCloak; // Used for checking animation detaching, set to true before calling Detach_All() on techno when this anim is attached to and to false after when cloaking only. - int BeControlledThreatFrame; - DWORD LastTargetID; - int AccumulatedGattlingValue; - bool ShouldUpdateGattlingValue; - int AttachedEffectInvokerCount; - - // Used for Passengers.SyncOwner.RevertOnExit instead of TechnoClass::InitialOwner / OriginallyOwnedByHouse, - // as neither is guaranteed to point to the house the TechnoClass had prior to entering transport and cannot be safely overridden. - HouseClass* OriginalPassengerOwner; - bool HasRemainingWarpInDelay; // Converted from object with Teleport Locomotor to one with a different Locomotor while still phasing in OR set if ChronoSphereDelay > 0. - int LastWarpInDelay; // Last-warp in delay for this unit, used by HasCarryoverWarpInDelay. - bool IsBeingChronoSphered; // Set to true on units currently being ChronoSphered, does not apply to Ares-ChronoSphere'd buildings or Chrono reinforcements. - bool KeepTargetOnMove; - CellStruct LastSensorsMapCoords; - CDTimerClass TiberiumEater_Timer; - bool DelayedFireSequencePaused; - int DelayedFireWeaponIndex; - CDTimerClass DelayedFireTimer; - AnimClass* CurrentDelayedFireAnim; - - AirstrikeClass* AirstrikeTargetingMe; - - bool IsSelected; - bool ResetLocomotor; - - // Replaces use of TechnoClass->Animation StageClass timer for IsSimpleDeployer to simplify - // the deploy animation timer calcs and eliminate possibility of outside interference. - CDTimerClass SimpleDeployerAnimationTimer; - - // cache tint values - int TintColorOwner; - int TintColorAllies; - int TintColorEnemies; - int TintIntensityOwner; - int TintIntensityAllies; - int TintIntensityEnemies; - - int AttackMoveFollowerTempCount; - - bool UndergroundTracked; - bool SpecialTracked; - bool FallingDownTracked; - - bool JumpjetStraightAscend; // Is set to true jumpjet units will ascend straight and do not adjust rotation or position during it. - - bool OnParachuted; // This is just a temporary patch. TODO: fully check HasParachuted and correct its maintenance method. - bool HoverShutdown; - CoordStruct LastTargetCrd; - CDTimerClass LastTargetCrdClearTimer; - - bool HasDeployConverted; - bool HasUndeployConverted; - - ExtData(TechnoClass* OwnerObject) : RadioClassExtension(OwnerObject) - , TypeExtData { nullptr } - , Shield {} - , LaserTrails {} - , AttachedEffects {} - , AE {} - , PreviousType { nullptr } - , ElectricBolts {} - , AnimRefCount { 0 } - , SubterraneanHarvStatus { 0 } - , SubterraneanHarvRallyPoint { nullptr } - , ReceiveDamage { false } - , LastKillWasTeamTarget { false } - , PassengerDeletionTimer {} - , CurrentShieldType { nullptr } - , LastWarpDistance {} - , JumpjetSpeed { 14 } // 0x7115B8 - , ChargeTurretTimer {} - , AutoDeathTimer {} - , MindControlRingAnimType { nullptr } - , DamageNumberOffset { INT32_MIN } - , Strafe_BombsDroppedThisRound { 0 } - , Strafe_TargetCell { nullptr } - , CurrentAircraftWeaponIndex {} - , IsInTunnel { false } - , IsBurrowed { false } - , HasBeenPlacedOnMap { false } - , DeployFireTimer {} - , SkipTargetChangeResetSequence { false } - , ForceFullRearmDelay { false } - , LastRearmWasFullDelay { false } - , CanCloakDuringRearm { false } - , WHAnimRemainingCreationInterval { 0 } - , LastWeaponType {} - , FiringObstacleCell {} - , IsDetachingForCloak { false } - , BeControlledThreatFrame { 0 } - , LastTargetID { 0xFFFFFFFF } - , AccumulatedGattlingValue { 0 } - , ShouldUpdateGattlingValue { false } - , OriginalPassengerOwner {} - , HasRemainingWarpInDelay { false } - , LastWarpInDelay { 0 } - , IsBeingChronoSphered { false } - , KeepTargetOnMove { false } - , LastSensorsMapCoords { CellStruct::Empty } - , TiberiumEater_Timer {} - , AirstrikeTargetingMe { nullptr } - , SimpleDeployerAnimationTimer {} - , DelayedFireSequencePaused { false } - , DelayedFireWeaponIndex { -1 } - , DelayedFireTimer {} - , CurrentDelayedFireAnim { nullptr } - , AttachedEffectInvokerCount { 0 } - , IsSelected { false } - , ResetLocomotor { false } - , TintColorOwner { 0 } - , TintColorAllies { 0 } - , TintColorEnemies { 0 } - , TintIntensityOwner { 0 } - , TintIntensityAllies { 0 } - , TintIntensityEnemies { 0 } - , AttackMoveFollowerTempCount { 0 } - , UndergroundTracked { false } - , SpecialTracked { false } - , FallingDownTracked { false } - , JumpjetStraightAscend { false } - , OnParachuted { false } - , HoverShutdown { false } - , LastTargetCrd { CoordStruct::Empty } - , LastTargetCrdClearTimer {} - , HasDeployConverted { false } - , HasUndeployConverted { false } - { } - - void OnEarlyUpdate(); - - void ApplyInterceptor(); - bool CheckDeathConditions(bool isInLimbo = false); - void DepletedAmmoActions(); - void UpdateSubterraneanHarvester(); - void EatPassengers(); - void UpdateTiberiumEater(); - void UpdateShield(); - void UpdateOnTunnelEnter(); - void UpdateOnTunnelExit(); - void ApplySpawnLimitRange(); - void UpdateTypeData(TechnoTypeClass* pCurrentType); - void UpdateTypeData_Foot(); - void UpdateLaserTrails(); - void UpdateAttachEffects(); - void UpdateGattlingRateDownReset(); - void UpdateKeepTargetOnMove(); - void UpdateWarpInDelay(); - void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved = nullptr); - bool RecalculateStatMultipliers(AttachEffectClass* pAttachEffect = nullptr); - void UpdateTemporal(); - void UpdateMindControlAnim(); - void UpdateRecountBurst(); - void UpdateRearmInEMPState(); - void UpdateRearmInTemporal(); - void InitializeLaserTrails(); - void InitializeAttachEffects(); - void UpdateSelfOwnedAttachEffects(); - bool HasAttachedEffects(std::vector attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector const* minCounts, std::vector const* maxCounts) const; - int GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource = false, TechnoClass* pInvoker = nullptr, AbstractClass* pSource = nullptr) const; - void InitializeDisplayInfo(); - void ApplyMindControlRangeLimit(); - int ApplyForceWeaponInRange(AbstractClass* pTarget); - void ResetDelayedFireTimer(); - void UpdateTintValues(); - - void AmmoAutoConvertActions(); - void UpdateLastTargetCrd(); - int GetSight(); - - virtual ~ExtData() override; - virtual void OnDetach(AirstrikeClass* pTarget, bool removed) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + TechnoTypeExt* TypeExtData; + std::unique_ptr Shield; + std::vector> LaserTrails; + std::vector> AttachedEffects; + AttachEffectTechnoProperties AE; + TechnoTypeClass* PreviousType; // Type change registered in TechnoClass::AI on current frame and used in FootClass::AI on same frame and reset after. + std::vector ElectricBolts; // EBolts are not serialized so do not serialize this either. + int AnimRefCount; // Used to keep track of how many times this techno is referenced in anims f.ex Invoker, ParentBuilding etc., for pointer invalidation. + int SubterraneanHarvStatus; // 0 = none, 1 = created, 2 = out from factory + AbstractClass* SubterraneanHarvRallyPoint; + bool ReceiveDamage; + bool LastKillWasTeamTarget; + CDTimerClass PassengerDeletionTimer; + ShieldTypeClass* CurrentShieldType; + int LastWarpDistance; + int JumpjetSpeed; + CDTimerClass ChargeTurretTimer; // Used for charge turrets instead of RearmTimer if weapon has ChargeTurret.Delays set. + CDTimerClass AutoDeathTimer; + AnimTypeClass* MindControlRingAnimType; + int DamageNumberOffset; + int Strafe_BombsDroppedThisRound; + CellClass* Strafe_TargetCell; + int CurrentAircraftWeaponIndex; + bool IsInTunnel; + bool IsBurrowed; + bool HasBeenPlacedOnMap; // Set to true on first Unlimbo() call. + CDTimerClass DeployFireTimer; + bool SkipTargetChangeResetSequence; + bool ForceFullRearmDelay; + bool LastRearmWasFullDelay; + bool CanCloakDuringRearm; // Current rearm timer was started by DecloakToFire=no weapon. + int WHAnimRemainingCreationInterval; + WeaponTypeClass* LastWeaponType; + CellClass* FiringObstacleCell; // Set on firing if there is an obstacle cell between target and techno, used for updating WaveClass target etc. + bool IsDetachingForCloak; // Used for checking animation detaching, set to true before calling Detach_All() on techno when this anim is attached to and to false after when cloaking only. + int BeControlledThreatFrame; + DWORD LastTargetID; + int AccumulatedGattlingValue; + bool ShouldUpdateGattlingValue; + int AttachedEffectInvokerCount; + + // Used for Passengers.SyncOwner.RevertOnExit instead of TechnoClass::InitialOwner / OriginallyOwnedByHouse, + // as neither is guaranteed to point to the house the TechnoClass had prior to entering transport and cannot be safely overridden. + HouseClass* OriginalPassengerOwner; + bool HasRemainingWarpInDelay; // Converted from object with Teleport Locomotor to one with a different Locomotor while still phasing in OR set if ChronoSphereDelay > 0. + int LastWarpInDelay; // Last-warp in delay for this unit, used by HasCarryoverWarpInDelay. + bool IsBeingChronoSphered; // Set to true on units currently being ChronoSphered, does not apply to Ares-ChronoSphere'd buildings or Chrono reinforcements. + bool KeepTargetOnMove; + CellStruct LastSensorsMapCoords; + CDTimerClass TiberiumEater_Timer; + bool DelayedFireSequencePaused; + int DelayedFireWeaponIndex; + CDTimerClass DelayedFireTimer; + AnimClass* CurrentDelayedFireAnim; + + AirstrikeClass* AirstrikeTargetingMe; + + bool IsSelected; + bool ResetLocomotor; + + // Replaces use of TechnoClass->Animation StageClass timer for IsSimpleDeployer to simplify + // the deploy animation timer calcs and eliminate possibility of outside interference. + CDTimerClass SimpleDeployerAnimationTimer; + + // cache tint values + int TintColorOwner; + int TintColorAllies; + int TintColorEnemies; + int TintIntensityOwner; + int TintIntensityAllies; + int TintIntensityEnemies; + + int AttackMoveFollowerTempCount; + + bool UndergroundTracked; + bool SpecialTracked; + bool FallingDownTracked; + + bool JumpjetStraightAscend; // Is set to true jumpjet units will ascend straight and do not adjust rotation or position during it. + + bool OnParachuted; // This is just a temporary patch. TODO: fully check HasParachuted and correct its maintenance method. + bool HoverShutdown; + CoordStruct LastTargetCrd; + CDTimerClass LastTargetCrdClearTimer; + + bool HasDeployConverted; + bool HasUndeployConverted; + + TechnoExt(TechnoClass* OwnerObject) : RadioExt(OwnerObject) + , TypeExtData { nullptr } + , Shield {} + , LaserTrails {} + , AttachedEffects {} + , AE {} + , PreviousType { nullptr } + , ElectricBolts {} + , AnimRefCount { 0 } + , SubterraneanHarvStatus { 0 } + , SubterraneanHarvRallyPoint { nullptr } + , ReceiveDamage { false } + , LastKillWasTeamTarget { false } + , PassengerDeletionTimer {} + , CurrentShieldType { nullptr } + , LastWarpDistance {} + , JumpjetSpeed { 14 } // 0x7115B8 + , ChargeTurretTimer {} + , AutoDeathTimer {} + , MindControlRingAnimType { nullptr } + , DamageNumberOffset { INT32_MIN } + , Strafe_BombsDroppedThisRound { 0 } + , Strafe_TargetCell { nullptr } + , CurrentAircraftWeaponIndex {} + , IsInTunnel { false } + , IsBurrowed { false } + , HasBeenPlacedOnMap { false } + , DeployFireTimer {} + , SkipTargetChangeResetSequence { false } + , ForceFullRearmDelay { false } + , LastRearmWasFullDelay { false } + , CanCloakDuringRearm { false } + , WHAnimRemainingCreationInterval { 0 } + , LastWeaponType {} + , FiringObstacleCell {} + , IsDetachingForCloak { false } + , BeControlledThreatFrame { 0 } + , LastTargetID { 0xFFFFFFFF } + , AccumulatedGattlingValue { 0 } + , ShouldUpdateGattlingValue { false } + , OriginalPassengerOwner {} + , HasRemainingWarpInDelay { false } + , LastWarpInDelay { 0 } + , IsBeingChronoSphered { false } + , KeepTargetOnMove { false } + , LastSensorsMapCoords { CellStruct::Empty } + , TiberiumEater_Timer {} + , AirstrikeTargetingMe { nullptr } + , SimpleDeployerAnimationTimer {} + , DelayedFireSequencePaused { false } + , DelayedFireWeaponIndex { -1 } + , DelayedFireTimer {} + , CurrentDelayedFireAnim { nullptr } + , AttachedEffectInvokerCount { 0 } + , IsSelected { false } + , ResetLocomotor { false } + , TintColorOwner { 0 } + , TintColorAllies { 0 } + , TintColorEnemies { 0 } + , TintIntensityOwner { 0 } + , TintIntensityAllies { 0 } + , TintIntensityEnemies { 0 } + , AttackMoveFollowerTempCount { 0 } + , UndergroundTracked { false } + , SpecialTracked { false } + , FallingDownTracked { false } + , JumpjetStraightAscend { false } + , OnParachuted { false } + , HoverShutdown { false } + , LastTargetCrd { CoordStruct::Empty } + , LastTargetCrdClearTimer {} + , HasDeployConverted { false } + , HasUndeployConverted { false } + { } + + void OnEarlyUpdate(); + + void ApplyInterceptor(); + bool CheckDeathConditions(bool isInLimbo = false); + void DepletedAmmoActions(); + void UpdateSubterraneanHarvester(); + void EatPassengers(); + void UpdateTiberiumEater(); + void UpdateShield(); + void UpdateOnTunnelEnter(); + void UpdateOnTunnelExit(); + void ApplySpawnLimitRange(); + void UpdateTypeData(TechnoTypeClass* pCurrentType); + void UpdateTypeData_Foot(); + void UpdateLaserTrails(); + void UpdateAttachEffects(); + void UpdateGattlingRateDownReset(); + void UpdateKeepTargetOnMove(); + void UpdateWarpInDelay(); + void UpdateCumulativeAttachEffects(AttachEffectTypeClass* pAttachEffectType, AttachEffectClass* pRemoved = nullptr); + bool RecalculateStatMultipliers(AttachEffectClass* pAttachEffect = nullptr); + void UpdateTemporal(); + void UpdateMindControlAnim(); + void UpdateRecountBurst(); + void UpdateRearmInEMPState(); + void UpdateRearmInTemporal(); + void InitializeLaserTrails(); + void InitializeAttachEffects(); + void UpdateSelfOwnedAttachEffects(); + bool HasAttachedEffects(std::vector attachEffectTypes, bool requireAll, bool ignoreSameSource, TechnoClass* pInvoker, AbstractClass* pSource, std::vector const* minCounts, std::vector const* maxCounts) const; + int GetAttachedEffectCumulativeCount(AttachEffectTypeClass* pAttachEffectType, bool ignoreSameSource = false, TechnoClass* pInvoker = nullptr, AbstractClass* pSource = nullptr) const; + void InitializeDisplayInfo(); + void ApplyMindControlRangeLimit(); + int ApplyForceWeaponInRange(AbstractClass* pTarget); + void ResetDelayedFireTimer(); + void UpdateTintValues(); + + void AmmoAutoConvertActions(); + void UpdateLastTargetCrd(); + int GetSight(); + + virtual ~TechnoExt() override; + virtual void OnDetach(AirstrikeClass* pTarget, bool removed) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -255,6 +254,16 @@ class TechnoExt static ExtContainer ExtMap; + static TechnoExt* Fetch(const TechnoClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TechnoExt* TryFetch(const TechnoClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static UnitClass* Deployer; static bool LoadGlobals(PhobosStreamReader& Stm); @@ -305,7 +314,7 @@ class TechnoExt static bool HandleDelayedFireWithPauseSequence(TechnoClass* pThis, WeaponTypeClass* pWeapon, int weaponIndex, int frame, int firingFrame); static bool IsHealthInThreshold(TechnoClass* pObject, double min, double max); static bool IsVeterancyInThreshold(TechnoClass* pObject, double min, double max); - static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt::ExtData* pData); + static UnitTypeClass* GetUnitTypeExtra(UnitClass* pUnit, TechnoTypeExt* pData); static AircraftTypeClass* GetAircraftTypeExtra(AircraftClass* pAircraft); static bool CannotMove(UnitClass* pThis); static bool HasAmmoToDeploy(TechnoClass* pThis); @@ -336,5 +345,3 @@ class TechnoExt static FireError GetFireErrorIgnoreDisableWeapons(TechnoClass* pThis, AbstractClass* pTarget, int weaponIndex, bool ignoreRange); }; -// top-level name for the TechnoClass extension (the base of the techno extension hierarchy) -using TechnoClassExtension = TechnoExt::ExtData; diff --git a/src/Ext/Techno/Hooks.Airstrike.cpp b/src/Ext/Techno/Hooks.Airstrike.cpp index 38795d54c8..5dc93df749 100644 --- a/src/Ext/Techno/Hooks.Airstrike.cpp +++ b/src/Ext/Techno/Hooks.Airstrike.cpp @@ -15,13 +15,13 @@ DEFINE_HOOK(0x6F348F, TechnoClass_WhatWeaponShouldIUse_Airstrike, 0x7) if (!pTargetTechno) return Primary; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pSecondaryWH); + const auto pWHExt = WarheadTypeExt::Fetch(pSecondaryWH); if (!EnumFunctions::IsTechnoEligible(pTargetTechno, pWHExt->AirstrikeTargets)) return Primary; const auto pTargetType = pTargetTechno->GetTechnoType(); - const auto pTargetTypeExt = TechnoTypeExt::ExtMap.Find(pTargetType); + const auto pTargetTypeExt = TechnoTypeExt::Fetch(pTargetType); if (pTargetTechno->AbstractFlags & AbstractFlags::Foot) return pTargetTypeExt->AllowAirstrike.Get(true) ? Secondary : Primary; @@ -38,7 +38,7 @@ DEFINE_HOOK(0x41D97B, AirstrikeClass_Fire_SetAirstrike, 0x7) GET(AirstrikeClass*, pThis, EDI); GET(TechnoClass*, pTarget, ESI); - TechnoExt::ExtMap.Find(pTarget)->AirstrikeTargetingMe = pThis; + TechnoExt::Fetch(pTarget)->AirstrikeTargetingMe = pThis; pTarget->StartAirstrikeTimer(100000); return pTarget->WhatAmI() == AbstractType::Building ? ContinueIn : Skip; @@ -68,7 +68,7 @@ DEFINE_HOOK(0x41DAA4, AirstrikeClass_ResetTarget_ResetForOldTarget, 0xA) GET(TechnoClass*, pTargetTechno, EDI); - TechnoExt::ExtMap.Find(pTargetTechno)->AirstrikeTargetingMe = nullptr; + TechnoExt::Fetch(pTargetTechno)->AirstrikeTargetingMe = nullptr; return SkipGameCode; } @@ -80,7 +80,7 @@ DEFINE_HOOK(0x41DAD4, AirstrikeClass_ResetTarget_ResetForNewTarget, 0x6) GET(AirstrikeClass*, pThis, EBP); GET(TechnoClass*, pTargetTechno, ESI); - TechnoExt::ExtMap.Find(pTargetTechno)->AirstrikeTargetingMe = pThis; + TechnoExt::Fetch(pTargetTechno)->AirstrikeTargetingMe = pThis; return SkipGameCode; } @@ -111,7 +111,7 @@ DEFINE_HOOK(0x41DBD4, AirstrikeClass_Stop_ResetForTarget, 0x7) // Sometimes the target will DTOR first before it announce invalid pointer, so sanity check is necessary! // At this point, the target's vtable has already been reset to AbstractClass_vtbl. // If a virtual function that AbstractClass does not have is called without checking this, it will cause the vtable to exceed its bounds. - if (const auto pTargetExt = TechnoExt::ExtMap.TryFind(pTargetTechno)) + if (const auto pTargetExt = TechnoExt::TryFetch(pTargetTechno)) { pTargetExt->AirstrikeTargetingMe = pLastTargetingMe; @@ -129,7 +129,7 @@ DEFINE_HOOK(0x41D604, AirstrikeClass_PointerGotInvalid_ResetForTarget, 0x6) GET(ObjectClass*, pTarget, EAX); - if (const auto pTargetTechnoExt = TechnoExt::ExtMap.TryFind(abstract_cast(pTarget))) + if (const auto pTargetTechnoExt = TechnoExt::TryFetch(abstract_cast(pTarget))) pTargetTechnoExt->AirstrikeTargetingMe = nullptr; return SkipGameCode; @@ -165,7 +165,7 @@ DEFINE_HOOK(0x51EAE0, TechnoClass_WhatAction_AllowAirstrike, 0x7) if (const auto pTechno = abstract_cast(pObject)) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; if (const auto pBuilding = abstract_cast(pTechno)) { @@ -190,7 +190,7 @@ DEFINE_HOOK(0x70E92F, TechnoClass_UpdateAirstrikeTint, 0x5) GET(TechnoClass*, pThis, ESI); - return TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe ? ContinueIn : Skip; + return TechnoExt::Fetch(pThis)->AirstrikeTargetingMe ? ContinueIn : Skip; } // Jun 9, 2025 - Starkku: Moved to BuildingClass_AI hook in Buildings/Hooks.cpp for optimization's sake. @@ -202,7 +202,7 @@ DEFINE_HOOK(0x43FDD6, BuildingClass_AI_Airstrike, 0x6) GET(BuildingClass*, pThis, ESI); - if (TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe) + if (TechnoExt::Fetch(pThis)->AirstrikeTargetingMe) pThis->Mark(MarkType::Change); return SkipGameCode; @@ -214,7 +214,7 @@ DEFINE_HOOK(0x43F9E0, BuildingClass_Mark_Airstrike, 0x6) GET(BuildingClass*, pThis, EDI); - return TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; + return TechnoExt::Fetch(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; } DEFINE_HOOK(0x448DF1, BuildingClass_SetOwningHouse_Airstrike, 0x6) @@ -223,7 +223,7 @@ DEFINE_HOOK(0x448DF1, BuildingClass_SetOwningHouse_Airstrike, 0x6) GET(BuildingClass*, pThis, ESI); - return TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; + return TechnoExt::Fetch(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; } DEFINE_HOOK(0x451ABC, BuildingClass_PlayAnim_Airstrike, 0x6) @@ -232,7 +232,7 @@ DEFINE_HOOK(0x451ABC, BuildingClass_PlayAnim_Airstrike, 0x6) GET(BuildingClass*, pThis, ESI); - return TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; + return TechnoExt::Fetch(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; } DEFINE_HOOK(0x452041, BuildingClass_452000_Airstrike, 0x6) @@ -241,7 +241,7 @@ DEFINE_HOOK(0x452041, BuildingClass_452000_Airstrike, 0x6) GET(BuildingClass*, pThis, ESI); - return TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; + return TechnoExt::Fetch(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; } DEFINE_HOOK(0x456E5A, BuildingClass_Flash_Airstrike, 0x6) @@ -250,7 +250,7 @@ DEFINE_HOOK(0x456E5A, BuildingClass_Flash_Airstrike, 0x6) GET(BuildingClass*, pThis, ESI); - return TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; + return TechnoExt::Fetch(pThis)->AirstrikeTargetingMe ? ContinueTintIntensity : NonAirstrike; } class BuildingClassFake final : public BuildingClass @@ -263,7 +263,7 @@ int BuildingClassFake::_GetAirstrikeInvulnerabilityIntensity(int currentIntensit auto const pBuilding = (BuildingClass*)this; int newIntensity = pBuilding->GetFlashingIntensity(currentIntensity); - if (pBuilding->IsIronCurtained() || TechnoExt::ExtMap.Find(pBuilding)->AirstrikeTargetingMe) + if (pBuilding->IsIronCurtained() || TechnoExt::Fetch(pBuilding)->AirstrikeTargetingMe) newIntensity = pBuilding->GetEffectTintIntensity(newIntensity); return newIntensity; @@ -305,7 +305,7 @@ DEFINE_HOOK(0x7058F6, TechnoClass_DrawAirstrikeFlare_LineColor, 0x5) // Allow custom colors. auto const pThis = DrawAirstrikeFlareTemp::pTechno; - auto const baseColor = TechnoExt::ExtMap.Find(pThis)->TypeExtData->AirstrikeLineColor.Get(RulesExt::Global()->AirstrikeLineColor); + auto const baseColor = TechnoExt::Fetch(pThis)->TypeExtData->AirstrikeLineColor.Get(RulesExt::Global()->AirstrikeLineColor); double percentage = Randomizer::Global.RandomRanged(745, 1000) / 1000.0; color = { (BYTE)(baseColor.R * percentage), (BYTE)(baseColor.G * percentage), (BYTE)(baseColor.B * percentage) }; R->ESI(Drawing::RGB_To_Int(baseColor)); diff --git a/src/Ext/Techno/Hooks.Cloak.cpp b/src/Ext/Techno/Hooks.Cloak.cpp index 786ae96eef..f23e67d163 100644 --- a/src/Ext/Techno/Hooks.Cloak.cpp +++ b/src/Ext/Techno/Hooks.Cloak.cpp @@ -8,7 +8,7 @@ namespace CloakTemp static bool __fastcall TechnoClass_IsReadyToCloak_Wrapper(TechnoClass* pThis) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); const bool cloakable = pThis->Cloakable; int rearm = -1; pThis->Cloakable |= pExt->AE.Cloakable; @@ -36,7 +36,7 @@ static bool __fastcall TechnoClass_IsReadyToCloak_Wrapper(TechnoClass* pThis) static bool __fastcall TechnoClass_ShouldNotCloak_Wrapper(TechnoClass* pThis) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); const bool cloakable = pThis->Cloakable; pThis->Cloakable |= pExt->AE.Cloakable; @@ -96,7 +96,7 @@ DEFINE_HOOK(0x703789, TechnoClass_Cloak_BeforeDetach, 0x6) // TechnoClass { GET(TechnoClass*, pThis, ESI); - if (auto const pExt = TechnoExt::ExtMap.TryFind(pThis)) + if (auto const pExt = TechnoExt::TryFetch(pThis)) { if (!pExt->MindControlRingAnimType) pExt->UpdateMindControlAnim(); @@ -112,7 +112,7 @@ DEFINE_HOOK(0x703799, TechnoClass_Cloak_AfterDetach, 0xA) // TechnoClass_ { GET(TechnoClass*, pThis, ESI); - if (auto const pExt = TechnoExt::ExtMap.TryFind(pThis)) + if (auto const pExt = TechnoExt::TryFetch(pThis)) pExt->IsDetachingForCloak = false; return 0; @@ -122,7 +122,7 @@ DEFINE_HOOK(0x6FB9D7, TechnoClass_Cloak_RestoreMCAnim, 0x6) { GET(TechnoClass*, pThis, ESI); - if (auto const pExt = TechnoExt::ExtMap.TryFind(pThis)) + if (auto const pExt = TechnoExt::TryFetch(pThis)) pExt->UpdateMindControlAnim(); return 0; @@ -190,7 +190,7 @@ DEFINE_HOOK(0x6FCA26, TechnoClass_CanFire_RevertAresOpenTopCloakFix, 0x6) if (pThis->InOpenToppedTransport && pThis->Transporter) { - auto const pTransporterTypeExt = TechnoExt::ExtMap.Find(pThis->Transporter)->TypeExtData; + auto const pTransporterTypeExt = TechnoExt::Fetch(pThis->Transporter)->TypeExtData; if (pTransporterTypeExt->OpenTopped_DecloakToFire.Get(RulesExt::Global()->OpenTopped_DecloakToFire)) return NotApplicable; } @@ -206,7 +206,7 @@ DEFINE_HOOK(0x6FCD1D, TechnoClass_CanFire_OpenTopCloakFix, 0x5) if (checkIfTargetInRange && pThis->InOpenToppedTransport && pThis->Transporter) { - auto const pTransporterTypeExt = TechnoExt::ExtMap.Find(pThis->Transporter)->TypeExtData; + auto const pTransporterTypeExt = TechnoExt::Fetch(pThis->Transporter)->TypeExtData; if (pTransporterTypeExt->OpenTopped_DecloakToFire.Get(RulesExt::Global()->OpenTopped_DecloakToFire)) pThis->Transporter->Uncloak(true); } diff --git a/src/Ext/Techno/Hooks.Firing.cpp b/src/Ext/Techno/Hooks.Firing.cpp index ff0c86bbcb..f338b02123 100644 --- a/src/Ext/Techno/Hooks.Firing.cpp +++ b/src/Ext/Techno/Hooks.Firing.cpp @@ -15,7 +15,7 @@ DEFINE_HOOK(0x6F3339, TechnoClass_WhatWeaponShouldIUse_Interceptor, 0x8) GET(TechnoClass*, pThis, ESI); GET_STACK(AbstractClass*, pTarget, STACK_OFFSET(0x18, 0x4)); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; const auto pType = pTypeExt->OwnerObject(); if (pTarget && pTarget->WhatAmI() == AbstractType::Bullet) @@ -38,7 +38,7 @@ DEFINE_HOOK(0x6F3360, TechnoClass_WhatWeaponShouldIUse_MultiWeapon, 0x6) GET(TechnoTypeClass*, pType, EAX); enum { SkipGameCode = 0x6F3379 }; - if (TechnoTypeExt::ExtMap.Find(pType)->MultiWeapon.Get() + if (TechnoTypeExt::Fetch(pType)->MultiWeapon.Get() && (pType->WhatAmI() != AbstractType::UnitType || !pType->Gunner)) { return SkipGameCode; @@ -58,12 +58,12 @@ DEFINE_HOOK(0x6F33CD, TechnoClass_WhatWeaponShouldIUse_ForceFire, 0x6) { auto const pWeaponPrimary = pThis->GetWeapon(0)->WeaponType; auto const pWeaponSecondary = pThis->GetWeapon(1)->WeaponType; - auto const pPrimaryExt = WeaponTypeExt::ExtMap.Find(pWeaponPrimary); + auto const pPrimaryExt = WeaponTypeExt::Fetch(pWeaponPrimary); if (pWeaponSecondary && !pPrimaryExt->SkipWeaponPicking && (!EnumFunctions::IsCellEligible(pCell, pPrimaryExt->CanTarget, true, true) || (pPrimaryExt->AttachEffect_CheckOnFirer && !pPrimaryExt->HasRequiredAttachedEffects(pThis, pThis))) - && (!TechnoExt::ExtMap.Find(pThis)->TypeExtData->NoSecondaryWeaponFallback + && (!TechnoExt::Fetch(pThis)->TypeExtData->NoSecondaryWeaponFallback || TechnoExt::CanFireNoAmmoWeapon(pThis, 1))) { R->EAX(1); @@ -91,7 +91,7 @@ DEFINE_HOOK(0x6F3428, TechnoClass_WhatWeaponShouldIUse_ForceWeapon, 0x6) GET(TechnoClass*, pThis, ECX); GET_STACK(AbstractClass*, pTarget, STACK_OFFSET(0x18, 0x4)); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; // Force weapon const int forceWeaponIndex = pTypeExt->SelectForceWeapon(pThis, pTarget); @@ -122,7 +122,7 @@ DEFINE_HOOK(0x6F36DB, TechnoClass_WhatWeaponShouldIUse, 0x8) enum { Primary = 0x6F37AD, Secondary = 0x6F3745, OriginalCheck = 0x6F36E3 }; const auto pTargetTechno = abstract_cast(pTarget); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; const bool allowFallback = !pTypeExt->NoSecondaryWeaponFallback; const bool allowAAFallback = allowFallback ? true : pTypeExt->NoSecondaryWeaponFallback_AllowAA; const int weaponIndex = TechnoExt::PickWeaponIndex(pThis, pTargetTechno, pTarget, 0, 1, allowFallback, allowAAFallback); @@ -133,7 +133,7 @@ DEFINE_HOOK(0x6F36DB, TechnoClass_WhatWeaponShouldIUse, 0x8) if (!pTargetTechno) return Primary; - const auto pTargetExt = TechnoExt::ExtMap.Find(pTargetTechno); + const auto pTargetExt = TechnoExt::Fetch(pTargetTechno); if (const auto pShield = pTargetExt->Shield.get()) { @@ -144,7 +144,7 @@ DEFINE_HOOK(0x6F36DB, TechnoClass_WhatWeaponShouldIUse, 0x8) if (pSecondary && (allowFallback || ((allowAAFallback && pTargetTechno->IsInAir() && pSecondary->Projectile->AA) - || (pTargetTechno->InWhichLayer() == Layer::Underground && BulletTypeExt::ExtMap.Find(pSecondary->Projectile)->AU)) + || (pTargetTechno->InWhichLayer() == Layer::Underground && BulletTypeExt::Fetch(pSecondary->Projectile)->AU)) || TechnoExt::CanFireNoAmmoWeapon(pThis, pTypeExt->OwnerObject(), 1))) { if (!pShield->CanBeTargeted(pThis->GetWeapon(0)->WeaponType)) @@ -179,7 +179,7 @@ DEFINE_HOOK(0x6F37EB, TechnoClass_WhatWeaponShouldIUse_AntiAir, 0x6) return Secondary; } - if (BulletTypeExt::ExtMap.Find(pSecondaryProj)->AU && !BulletTypeExt::ExtMap.Find(pPrimaryProj)->AU) + if (BulletTypeExt::Fetch(pSecondaryProj)->AU && !BulletTypeExt::Fetch(pPrimaryProj)->AU) { if (pTargetTechno->InWhichLayer() == Layer::Underground) return Secondary; @@ -211,7 +211,7 @@ DEFINE_HOOK(0x6F3432, TechnoClass_WhatWeaponShouldIUse_Gattling, 0xA) if (pTargetTechno) { - auto const pTargetExt = TechnoExt::ExtMap.Find(pTargetTechno); + auto const pTargetExt = TechnoExt::Fetch(pTargetTechno); auto const pWeaponEven = pThis->GetWeapon(evenWeaponIndex)->WeaponType; auto const pShield = pTargetExt->Shield.get(); auto const armor = pTargetTechno->GetTechnoType()->Armor; @@ -222,7 +222,7 @@ DEFINE_HOOK(0x6F3432, TechnoClass_WhatWeaponShouldIUse_Gattling, 0xA) { if (inAir && !pWeapon->Projectile->AA) return false; - if (isUnderground && !BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->AU) + if (isUnderground && !BulletTypeExt::Fetch(pWeapon->Projectile)->AU) return false; if (pShield && pShield->IsActive() && !pShield->CanBeTargeted(pWeapon)) return false; @@ -297,7 +297,7 @@ DEFINE_HOOK(0x5218F3, InfantryClass_WhatWeaponShouldIUse_DeployFireWeapon, 0x6) if (pType->DeployFireWeapon == -1) return 0x52194E; - if (pType->IsGattling || TechnoTypeExt::ExtMap.Find(pType)->MultiWeapon.Get()) + if (pType->IsGattling || TechnoTypeExt::Fetch(pType)->MultiWeapon.Get()) return !pThis->IsDeployed() ? 0x52194E : 0x52190D; return 0; @@ -317,7 +317,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) // Checking for nullptr is not required here, since the game has already executed them before calling the hook -- Belonit const auto pWH = pWeapon->Warhead; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); const int nMoney = pWHExt->TransactMoney; if (nMoney < 0 && pThis->Owner->Available_Money() < -nMoney) @@ -325,7 +325,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) // AAOnly doesn't need to be checked if LandTargeting=1. if (pThis->GetTechnoType()->LandTargeting != LandTargetingType::Land_Not_OK && pWeapon->Projectile->AA - && pTarget && !pTarget->IsInAir() && BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->AAOnly) + && pTarget && !pTarget->IsInAir() && BulletTypeExt::Fetch(pWeapon->Projectile)->AAOnly) { return CannotFire; } @@ -346,7 +346,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) } } - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (!pWeaponExt->SkipWeaponPicking && pTargetCell) { @@ -383,7 +383,7 @@ DEFINE_HOOK(0x6FC339, TechnoClass_CanFire, 0x6) if (!EnumFunctions::IsTechnoEligible(pTargetTechno, pWHExt->AirstrikeTargets)) return CannotFire; - const auto pTypeExt = TechnoExt::ExtMap.Find(pTargetTechno)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pTargetTechno)->TypeExtData; if (pTypeExt->AllowAirstrike.isset() ? !pTypeExt->AllowAirstrike.Get() : (pTargetTechno->AbstractFlags & AbstractFlags::Foot ? false : !static_cast(pTargetTechno)->Type->CanC4)) return CannotFire; @@ -403,7 +403,7 @@ DEFINE_HOOK(0x6FC0C5, TechnoClass_CanFire_DisableWeapons, 0x6) if (pThis->SlaveOwner) return FireErrorIllegal; - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->AE.DisableWeapons && pThis->GetWeapon(weaponIndex)->WeaponType) return FireErrorRearm; @@ -431,7 +431,7 @@ DEFINE_HOOK(0x6FC5C7, TechnoClass_CanFire_OpenTopped, 0x6) GET(TechnoClass*, pTransport, EAX); GET_STACK(const int, weaponIndex, STACK_OFFSET(0x20, 0x8)); - auto const pTypeExt = TechnoExt::ExtMap.Find(pTransport)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pTransport)->TypeExtData; if (pTransport->Deactivated && !pTypeExt->OpenTopped_AllowFiringIfDeactivated) return Illegal; @@ -450,7 +450,7 @@ DEFINE_HOOK(0x6FC5C7, TechnoClass_CanFire_OpenTopped, 0x6) return Illegal; if (!pTypeExt->OpenTopped_FireWhileMoving.Get(RulesExt::Global()->OpenTopped_FireWhileMoving) - || !TechnoExt::ExtMap.Find(pThis)->TypeExtData->OpenTransport_FireWhileMoving.Get(RulesExt::Global()->OpenTransport_FireWhileMoving) + || !TechnoExt::Fetch(pThis)->TypeExtData->OpenTransport_FireWhileMoving.Get(RulesExt::Global()->OpenTransport_FireWhileMoving) || (pWeapon && !pWeapon->FireWhileMoving)) { if (pTypeExt->OwnerObject()->BalloonHover) @@ -534,7 +534,7 @@ DEFINE_HOOK(0x6FC749, TechnoClass_CanFire_AntiUnderground, 0x5) return Illegal; break; case Layer::Underground: - if (!BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->AU) + if (!BulletTypeExt::Fetch(pWeapon->Projectile)->AU) return Illegal; break; default: @@ -571,7 +571,7 @@ DEFINE_HOOK(0x6FDD7D, TechnoClass_FireAt_UpdateWeaponType, 0x5) { const auto pWH = pWeapon->Warhead; - if (!pWH->Parasite && WarheadTypeExt::ExtMap.Find(pWH)->UnlimboDetonate) + if (!pWH->Parasite && WarheadTypeExt::Fetch(pWH)->UnlimboDetonate) { if (const auto pFoot = abstract_cast(pThis)) { @@ -581,7 +581,7 @@ DEFINE_HOOK(0x6FDD7D, TechnoClass_FireAt_UpdateWeaponType, 0x5) } } - if (const auto pExt = TechnoExt::ExtMap.TryFind(pThis)) + if (const auto pExt = TechnoExt::TryFetch(pThis)) { if (pThis->CurrentBurstIndex && pWeapon != pExt->LastWeaponType && pExt->TypeExtData->RecountBurst.Get(RulesExt::Global()->RecountBurst)) { @@ -619,8 +619,8 @@ DEFINE_HOOK(0x6FDDC0, TechnoClass_FireAt_BeforeTruelyFire, 0x6) GET(WeaponTypeClass* const, pWeapon, EBX); GET_BASE(const int, weaponIndex, 0xC); - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); + auto const pExt = TechnoExt::Fetch(pThis); auto& timer = pExt->DelayedFireTimer; if (pExt->DelayedFireWeaponIndex >= 0 && pExt->DelayedFireWeaponIndex != weaponIndex) @@ -693,13 +693,13 @@ DEFINE_HOOK(0x6FE43B, TechnoClass_FireAt_OpenToppedDmgMult, 0x8) if (auto const pTransport = pThis->Transporter) { - auto const pExt = TechnoExt::ExtMap.Find(pTransport)->TypeExtData; + auto const pExt = TechnoExt::Fetch(pTransport)->TypeExtData; //it is float isnt it YRPP ? , check tomson26 YR-IDB ! nDamageMult = pExt->OpenTopped_DamageMultiplier.Get(nDamageMult); } - nDamageMult *= TechnoExt::ExtMap.Find(pThis)->TypeExtData->OpenTransport_DamageMultiplier; + nDamageMult *= TechnoExt::Fetch(pThis)->TypeExtData->OpenTransport_DamageMultiplier; R->EAX(static_cast(nDamage * nDamageMult)); return ApplyDamageMult; @@ -716,7 +716,7 @@ DEFINE_HOOK(0x6FE19A, TechnoClass_FireAt_AreaFire, 0x6) GET(CellClass* const, pCell, EAX); GET_STACK(WeaponTypeClass*, pWeaponType, STACK_OFFSET(0xB0, -0x70)); - if (const auto pExt = WeaponTypeExt::ExtMap.TryFind(pWeaponType)) + if (const auto pExt = WeaponTypeExt::TryFetch(pWeaponType)) { const auto canTarget = pExt->CanTarget; const auto canTargetHouses = pExt->CanTargetHouses; @@ -772,7 +772,7 @@ DEFINE_HOOK(0x6FF43F, TechnoClass_FireAt_FeedbackWeapon, 0x6) GET(TechnoClass*, pThis, ESI); GET(WeaponTypeClass*, pWeapon, EBX); - if (auto const pWeaponExt = WeaponTypeExt::ExtMap.TryFind(pWeapon)) + if (auto const pWeaponExt = WeaponTypeExt::TryFetch(pWeapon)) { if (auto const pWeaponFeedback = pWeaponExt->FeedbackWeapon) { @@ -792,7 +792,7 @@ DEFINE_HOOK(0x6FF0DD, TechnoClass_FireAt_TurretRecoil, 0x6) GET_STACK(WeaponTypeClass* const, pWeapon, STACK_OFFSET(0xB0, -0x70)); - return WeaponTypeExt::ExtMap.Find(pWeapon)->TurretRecoil_Suppress ? SkipGameCode : 0; + return WeaponTypeExt::Fetch(pWeapon)->TurretRecoil_Suppress ? SkipGameCode : 0; } DEFINE_HOOK(0x6FF905, TechnoClass_FireAt_FireOnce, 0x6) @@ -802,8 +802,8 @@ DEFINE_HOOK(0x6FF905, TechnoClass_FireAt_FireOnce, 0x6) if (auto const pInf = abstract_cast(pThis)) { - if (!WeaponTypeExt::ExtMap.Find(pWeapon)->FireOnce_ResetSequence) - TechnoExt::ExtMap.Find(pInf)->SkipTargetChangeResetSequence = true; + if (!WeaponTypeExt::Fetch(pWeapon)->FireOnce_ResetSequence) + TechnoExt::Fetch(pInf)->SkipTargetChangeResetSequence = true; } return 0; @@ -813,7 +813,7 @@ static inline void ToggleLaserWeaponIndex(TechnoClass* pThis, WeaponTypeClass* p { if (pWeapon->IsLaser) { - if (auto const pExt = BuildingExt::ExtMap.TryFind(abstract_cast(pThis))) + if (auto const pExt = BuildingExt::TryFetch(abstract_cast(pThis))) { if (!pExt->CurrentLaserWeaponIndex.has_value()) pExt->CurrentLaserWeaponIndex = weaponIndex; @@ -831,7 +831,7 @@ DEFINE_HOOK(0x6FF660, TechnoClass_FireAt_LateLogic, 0x6) GET(WeaponTypeClass* const, pWeapon, EBX); GET_BASE(const int, weaponIndex, 0xC); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); const auto pTypeExt = pExt->TypeExtData; // Interceptor. @@ -839,12 +839,12 @@ DEFINE_HOOK(0x6FF660, TechnoClass_FireAt_LateLogic, 0x6) { if (const auto pTargetBullet = abstract_cast(pTarget)) { - const auto pTargetExt = BulletExt::ExtMap.Find(pTargetBullet); + const auto pTargetExt = BulletExt::Fetch(pTargetBullet); if (!pTargetExt->TypeExtData->Armor.isset()) pTargetExt->InterceptedStatus |= InterceptedStatus::Locked; - const auto pBulletExt = BulletExt::ExtMap.Find(pBullet); + const auto pBulletExt = BulletExt::Fetch(pBullet); pBulletExt->InterceptorTechnoType = pTypeExt; pBulletExt->InterceptedStatus |= InterceptedStatus::Targeted; @@ -896,7 +896,7 @@ DEFINE_HOOK(0x6FF2BE, TechnoClass_FireAt_BurstOffsetFix, 0x6) static inline void SetChargeTurretDelay(TechnoClass* pThis, int rearmDelay, WeaponTypeClass* pWeapon) { pThis->ChargeTurretDelay = rearmDelay; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->ChargeTurret_Delays.size() > 0) { @@ -908,7 +908,7 @@ static inline void SetChargeTurretDelay(TechnoClass* pThis, int rearmDelay, Weap return; pThis->ChargeTurretDelay = delay; - TechnoExt::ExtMap.Find(pThis)->ChargeTurretTimer.Start(delay); + TechnoExt::Fetch(pThis)->ChargeTurretTimer.Start(delay); } } @@ -945,7 +945,7 @@ DEFINE_HOOK(0x70FDF5, TechnoClass_DrawDrainAnimation_Custom, 0x6) enum { SkipGameCode = 0x70FDFB }; GET(TechnoClass*, pThis, ESI); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; R->EDX(pTypeExt->DrainAnimationType.Get(RulesClass::Instance->DrainAnimationType)); return SkipGameCode; @@ -969,7 +969,7 @@ CoordStruct* GetFLHTemp::UnitClassFake::_GetFLH(CoordStruct* outBuffer, int weap { const auto pTransporter = pThis->Transporter; - if (pThis->InOpenToppedTransport && pTransporter && TechnoExt::ExtMap.Find(pTransporter)->TypeExtData->AlternateFLH_ApplyVehicle) + if (pThis->InOpenToppedTransport && pTransporter && TechnoExt::Fetch(pTransporter)->TypeExtData->AlternateFLH_ApplyVehicle) { if (const int idx = pTransporter->Passengers.IndexOf(pThis)) { @@ -1021,7 +1021,7 @@ DEFINE_HOOK(0x6F3AEB, TechnoClass_GetFLH, 0x6) else { const int index = -weaponIndex - 1; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (index < static_cast(pTypeExt->AlternateFLHs.size())) flh = pTypeExt->AlternateFLHs[index]; @@ -1055,7 +1055,7 @@ DEFINE_HOOK(0x6FCFE0, TechnoClass_RearmDelay_CanCloakDuringRearm, 0x6) GET(TechnoClass*, pThis, ESI); GET(WeaponTypeClass*, pWeapon, EDI); - TechnoExt::ExtMap.Find(pThis)->CanCloakDuringRearm = !pWeapon->DecloakToFire; + TechnoExt::Fetch(pThis)->CanCloakDuringRearm = !pWeapon->DecloakToFire; return 0; } @@ -1067,8 +1067,8 @@ DEFINE_HOOK(0x6FD0B5, TechnoClass_RearmDelay_ROF, 0x6) GET(TechnoClass*, pThis, ESI); GET(WeaponTypeClass*, pWeapon, EDI); - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); + auto const pExt = TechnoExt::Fetch(pThis); auto const range = pWeaponExt->ROF_RandomDelay.Get(RulesExt::Global()->ROF_RandomDelay); const double rof = pWeapon->ROF * pExt->AE.ROFMultiplier; pExt->LastRearmWasFullDelay = true; @@ -1085,7 +1085,7 @@ DEFINE_HOOK(0x6FD054, TechnoClass_RearmDelay_ForceFullDelay, 0x6) GET(TechnoClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); pExt->LastRearmWasFullDelay = false; if (pExt->ForceFullRearmDelay) @@ -1102,7 +1102,7 @@ DEFINE_HOOK(0x6FD05E, TechnoClass_RearmDelay_BurstDelays, 0x7) GET(WeaponTypeClass*, pWeapon, EDI); GET(const int, idxCurrentBurst, ECX); - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const int burstDelay = pWeaponExt->GetBurstDelay(pThis->CurrentBurstIndex); if (burstDelay >= 0) @@ -1153,7 +1153,7 @@ DEFINE_HOOK(0x708AD0, TechnoClass_ShouldRetaliate_IronCurtain, 0x6) if (pWeapon) { - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->AutoTarget_IronCurtained.isset()) { @@ -1179,7 +1179,7 @@ DEFINE_HOOK(0x6F755A, TechnoClass_IsCloseEnough_CylinderRangefinding, 0x7) GET_BASE(WeaponTypeClass* const, pWeaponType, 0x10); GET(CoordStruct* const, pCoord, ESI); GET(TechnoClass* const, pThis, EDI); - const bool cylinder = WeaponTypeExt::ExtMap.Find(pWeaponType)->CylinderRangefinding.Get(RulesExt::Global()->CylinderRangefinding); + const bool cylinder = WeaponTypeExt::Fetch(pWeaponType)->CylinderRangefinding.Get(RulesExt::Global()->CylinderRangefinding); R->EAX(pCoord->X); return (cylinder || pThis->WhatAmI() == AbstractType::Aircraft) ? 0x6F75B2 : 0x6F7568; } diff --git a/src/Ext/Techno/Hooks.Misc.cpp b/src/Ext/Techno/Hooks.Misc.cpp index ce18ec3346..bbc749e44f 100644 --- a/src/Ext/Techno/Hooks.Misc.cpp +++ b/src/Ext/Techno/Hooks.Misc.cpp @@ -14,7 +14,7 @@ DEFINE_HOOK(0x6B0C2C, SlaveManagerClass_FreeSlaves_SlavesFreeSound, 0x5) { GET(TechnoClass*, pSlave, EDI); - const auto pTypeExt = TechnoExt::ExtMap.Find(pSlave)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pSlave)->TypeExtData; const int sound = pTypeExt->SlavesFreeSound.Get(RulesClass::Instance->SlavesFreeSound); if (sound != -1) VocClass::PlayAt(sound, pSlave->Location); @@ -28,7 +28,7 @@ DEFINE_HOOK(0x6B0B9C, SlaveManagerClass_Killed_DecideOwner, 0x6) GET(InfantryClass*, pSlave, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pSlave->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pSlave->Type); switch (pTypeExt->Slaved_OwnerWhenMasterKilled.Get()) { @@ -70,7 +70,7 @@ DEFINE_HOOK(0x6B7265, SpawnManagerClass_AI_UpdateTimer, 0x6) if (pOwner && pThis->Status == SpawnManagerStatus::Launching && pThis->CountDockedSpawns() != 0) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pOwner)->TypeExtData; if (pTypeExt->Spawner_DelayFrames.isset()) R->EAX(std::min(pTypeExt->Spawner_DelayFrames.Get(), 10)); @@ -102,7 +102,7 @@ DEFINE_HOOK(0x6B73AD, SpawnManagerClass_AI_SpawnTimer, 0x5) if (auto const pOwner = pThis->Owner) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pOwner)->TypeExtData; if (pTypeExt->Spawner_DelayFrames.isset()) R->ECX(pTypeExt->Spawner_DelayFrames.Get()); @@ -120,7 +120,7 @@ DEFINE_HOOK(0x6B7600, SpawnManagerClass_AI_InitDestination, 0x6) GET(AircraftClass* const, pSpawnee, EDI); auto const pOwner = pThis->Owner; - auto const pTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pOwner)->TypeExtData; if (pTypeExt->Spawner_AttackImmediately) { @@ -145,7 +145,7 @@ DEFINE_HOOK(0x6B6D44, SpawnManagerClass_Init_Spawns, 0x5) GET(SpawnManagerClass*, pThis, ESI); GET_STACK(const size_t, i, STACK_OFFSET(0x1C, 0x4)); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Owner->GetTechnoType()); + auto const pTypeExt = TechnoTypeExt::Fetch(pThis->Owner->GetTechnoType()); if ((int) i >= pTypeExt->InitialSpawnsNumber.Get(pThis->SpawnCount)) { @@ -169,7 +169,7 @@ DEFINE_HOOK(0x6B78D3, SpawnManagerClass_Update_Spawns, 0x6) GET(SpawnManagerClass*, pThis, ESI); auto const pOwner = pThis->Owner; - auto const pTypeExt = TechnoExt::ExtMap.Find(pOwner)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pOwner)->TypeExtData; if (pTypeExt->Spawns_Queue.empty()) return 0; @@ -197,7 +197,7 @@ DEFINE_HOOK(0x6B7282, SpawnManagerClass_AI_PromoteSpawns, 0x5) { GET(SpawnManagerClass*, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis->Owner)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis->Owner)->TypeExtData; if (pTypeExt->Promote_IncludeSpawns) { @@ -226,7 +226,7 @@ DEFINE_HOOK(0x6B77B4, SpawnManagerClass_Update_RecycleSpawned, 0x7) GET(CellStruct* const, pCarrierMapCrd, EBP); auto const pCarrier = pThis->Owner; - auto const pCarrierTypeExt = TechnoExt::ExtMap.Find(pCarrier)->TypeExtData; + auto const pCarrierTypeExt = TechnoExt::Fetch(pCarrier)->TypeExtData; auto const spawnerCrd = pSpawner->GetCoords(); auto shouldRecycleSpawned = [&]() @@ -270,7 +270,7 @@ DEFINE_HOOK(0x4D962B, FootClass_SetDestination_RecycleFLH, 0x5) if (pCarrier && pCarrier == pDest) // This is a spawner returning to its carrier. { - auto const pCarrierTypeExt = TechnoExt::ExtMap.Find(pCarrier)->TypeExtData; + auto const pCarrierTypeExt = TechnoExt::Fetch(pCarrier)->TypeExtData; auto const& FLH = pCarrierTypeExt->Spawner_RecycleCoord; if (FLH != CoordStruct::Empty) @@ -298,7 +298,7 @@ DEFINE_HOOK(0x6B74F0, SpawnManagerClass_AI_UseTurretFacing, 0x5) auto const pTechno = pThis->Owner; - if (pTechno->HasTurret() && TechnoExt::ExtMap.Find(pTechno)->TypeExtData->Spawner_UseTurretFacing) + if (pTechno->HasTurret() && TechnoExt::Fetch(pTechno)->TypeExtData->Spawner_UseTurretFacing) R->EAX(pTechno->SecondaryFacing.Current().Raw); return 0; @@ -331,7 +331,7 @@ DEFINE_HOOK(0x514AB4, Locomotion_Process_Wake, 0x6) // Hover { GET(ILocomotion* const, iloco, ESI); __assume(iloco != nullptr); - const auto pTypeExt = TechnoExt::ExtMap.Find(static_cast(iloco)->LinkedTo)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(static_cast(iloco)->LinkedTo)->TypeExtData; R->EDX(pTypeExt->Wake.Get(RulesClass::Instance->Wake)); return R->Origin() + 0xC; @@ -342,7 +342,7 @@ DEFINE_HOOK(0x4B079D, DriveLocomotionClass_Process_MakesWake, 0x5) enum { NoWake = 0x4B0828 }; GET(ILocomotion* const, pThis, ESI); const auto pLinkedTo = static_cast(pThis)->LinkedTo; - return TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->DriveLocomotorMakesWake) ? 0 : NoWake; + return TechnoExt::Fetch(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->DriveLocomotorMakesWake) ? 0 : NoWake; } DEFINE_HOOK(0x514A32, HoverLocomotionClass_Process_MakesWake, 0x5) @@ -350,7 +350,7 @@ DEFINE_HOOK(0x514A32, HoverLocomotionClass_Process_MakesWake, 0x5) enum { NoWake = 0x514AC8 }; GET(ILocomotion* const, pThis, ESI); const auto pLinkedTo = static_cast(pThis)->LinkedTo; - return TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->HoverLocomotorMakesWake) ? 0 : NoWake; + return TechnoExt::Fetch(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->HoverLocomotorMakesWake) ? 0 : NoWake; } DEFINE_HOOK(0x69FE4A, ShipLocomotionClass_Process_MakesWake, 0x6) @@ -358,7 +358,7 @@ DEFINE_HOOK(0x69FE4A, ShipLocomotionClass_Process_MakesWake, 0x6) enum { NoWake = 0x69FEF0 }; GET(ILocomotion* const, pThis, ESI); const auto pLinkedTo = static_cast(pThis)->LinkedTo; - return TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->ShipLocomotorMakesWake) ? 0 : NoWake; + return TechnoExt::Fetch(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->ShipLocomotorMakesWake) ? 0 : NoWake; } namespace GrappleUpdateTemp @@ -376,7 +376,7 @@ DEFINE_HOOK(0x629E9B, ParasiteClass_GrappleUpdate_MakeWake_SetContext, 0x5) DEFINE_HOOK(0x629FA3, ParasiteClass_GrappleUpdate_MakeWake, 0x6) { - const auto pTypeExt = TechnoExt::ExtMap.Find(GrappleUpdateTemp::pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(GrappleUpdateTemp::pThis)->TypeExtData; R->EDX(pTypeExt->Wake_Grapple.Get(pTypeExt->Wake.Get(RulesClass::Instance->Wake))); return 0x629FA9; @@ -386,7 +386,7 @@ DEFINE_HOOK(0x7365AD, UnitClass_Update_SinkingWake, 0x6) { GET(UnitClass* const, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->ECX(pTypeExt->Wake_Sinking.Get(pTypeExt->Wake.Get(RulesClass::Instance->Wake))); return 0x7365B3; @@ -396,7 +396,7 @@ DEFINE_HOOK(0x737F05, UnitClass_ReceiveDamage_SinkingWake, 0x6) { GET(UnitClass* const, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->ECX(pTypeExt->Wake_Sinking.Get(pTypeExt->Wake.Get(RulesClass::Instance->Wake))); return 0x737F0B; @@ -407,12 +407,12 @@ DEFINE_HOOK(0x75AC93, WalkLocomotionClass_Process_Wake, 0x6) GET(ILocomotion* const, pThis, ESI); const auto pLinkedTo = static_cast(pThis)->LinkedTo; - if (!TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->WalkLocomotorMakesWake)) + if (!TechnoExt::Fetch(pLinkedTo)->TypeExtData->MakesWake.Get(RulesExt::Global()->WalkLocomotorMakesWake)) return 0; if (pThis->Is_Really_Moving_Now() && !(Unsorted::CurrentFrame % 10) && !pLinkedTo->OnBridge && pLinkedTo->GetCell()->LandType == LandType::Water) { - const auto pAnimType = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData->Wake.Get(RulesClass::Instance->Wake); + const auto pAnimType = TechnoExt::Fetch(pLinkedTo)->TypeExtData->Wake.Get(RulesClass::Instance->Wake); auto location = pLinkedTo->GetCoords(); GameCreate(pAnimType, location, 0, 1, 0x600u, false); } @@ -429,7 +429,7 @@ DEFINE_HOOK(0x728F74, TunnelLocomotionClass_Process_KillAnims, 0x5) GET(ILocomotion*, pThis, ESI); const auto pLoco = static_cast(pThis); - const auto pExt = TechnoExt::ExtMap.Find(pLoco->LinkedTo); + const auto pExt = TechnoExt::Fetch(pLoco->LinkedTo); pExt->IsBurrowed = true; if (const auto pShieldData = pExt->Shield.get()) @@ -451,7 +451,7 @@ DEFINE_HOOK(0x728E5F, TunnelLocomotionClass_Process_RestoreAnims, 0x7) if (pLoco->State == TunnelLocomotionClass::State::PreDigOut) { - const auto pExt = TechnoExt::ExtMap.Find(pLoco->LinkedTo); + const auto pExt = TechnoExt::Fetch(pLoco->LinkedTo); pExt->IsBurrowed = false; if (const auto pShieldData = pExt->Shield.get()) @@ -473,7 +473,7 @@ DEFINE_HOOK(0x728F89, TunnelLocomotionClass_Process_SubterraneanHeight1, 0x5) GET(TechnoClass*, pLinkedTo, ECX); GET(const int, height, EAX); - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; if (height == pTypeExt->SubterraneanHeight.Get(RulesExt::Global()->SubterraneanHeight)) return Continue; @@ -488,7 +488,7 @@ DEFINE_HOOK(0x728FC6, TunnelLocomotionClass_Process_SubterraneanHeight2, 0x5) GET(TechnoClass*, pLinkedTo, ECX); GET(const int, height, EAX); - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; if (height <= pTypeExt->SubterraneanHeight.Get(RulesExt::Global()->SubterraneanHeight)) return Continue; @@ -504,7 +504,7 @@ DEFINE_HOOK(0x728FF2, TunnelLocomotionClass_Process_SubterraneanHeight3, 0x6) GET(const int, heightOffset, EAX); REF_STACK(int, height, 0x14); - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; const int subtHeight = pTypeExt->SubterraneanHeight.Get(RulesExt::Global()->SubterraneanHeight); height -= heightOffset; @@ -521,7 +521,7 @@ DEFINE_HOOK(0x7295E2, TunnelLocomotionClass_ProcessStateDigging_SubterraneanHeig GET(TechnoClass*, pLinkedTo, EAX); REF_STACK(int, height, STACK_OFFSET(0x44, -0x8)); - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; height = pTypeExt->SubterraneanHeight.Get(RulesExt::Global()->SubterraneanHeight); return SkipGameCode; @@ -534,7 +534,7 @@ DEFINE_HOOK(0x7295E2, TunnelLocomotionClass_ProcessStateDigging_SubterraneanHeig DEFINE_HOOK(0x522790, InfantryClass_ClearDisguise_DefaultDisguise, 0x6) { GET(InfantryClass*, pThis, ECX); - auto const pExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto const pExt = TechnoTypeExt::Fetch(pThis->Type); if (pExt->DefaultDisguise) { @@ -554,7 +554,7 @@ DEFINE_HOOK(0x746A30, UnitClass_UpdateDisguise_DefaultMirageDisguises, 0x5) enum { Apply = 0x746A6C }; GET(UnitClass*, pThis, ESI); - const auto& disguises = TechnoTypeExt::ExtMap.Find(pThis->Type)->DefaultMirageDisguises.GetElements(RulesClass::Instance->DefaultMirageDisguises); + const auto& disguises = TechnoTypeExt::Fetch(pThis->Type)->DefaultMirageDisguises.GetElements(RulesClass::Instance->DefaultMirageDisguises); TerrainTypeClass* pDisguiseAs = nullptr; if (const int size = static_cast(disguises.size())) @@ -595,7 +595,7 @@ static bool __fastcall CanAttackMindControlled(TechnoClass* pControlled, TechnoC if (!pManager || !pRetaliator->Owner->IsAlliedWith(pManager->GetOriginalOwner(pControlled))) return true; - return TechnoExt::ExtMap.Find(pControlled)->BeControlledThreatFrame <= Unsorted::CurrentFrame; + return TechnoExt::Fetch(pControlled)->BeControlledThreatFrame <= Unsorted::CurrentFrame; } DEFINE_HOOK(0x7089E8, TechnoClass_AllowedToRetaliate_AttackMindControlledDelay, 0x6) @@ -635,7 +635,7 @@ DEFINE_HOOK(0x70DE40, TechnoClass_GattlingValueRateDown_GattlingRateDownDelay, 0 GET(BuildingClass* const, pThis, ECX); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto pTypeExt = pExt->TypeExtData; if (pTypeExt->RateDown_Delay < 0) @@ -673,7 +673,7 @@ DEFINE_HOOK(0x70DE70, TechnoClass_GattlingRateUp_GattlingRateDownReset, 0x5) { GET(TechnoClass* const, pThis, ECX); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pExt->AccumulatedGattlingValue = 0; pExt->ShouldUpdateGattlingValue = false; @@ -686,7 +686,7 @@ DEFINE_HOOK(0x70E01E, TechnoClass_GattlingRateDown_GattlingRateDownDelay, 0x6) GET(TechnoClass* const, pThis, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto pTypeExt = pExt->TypeExtData; if (pTypeExt->RateDown_Delay < 0) @@ -743,7 +743,7 @@ DEFINE_HOOK(0x7410BB, UnitClass_GetFireError_CheckFacingError, 0x8) const auto pType = pThis->Type; - if (!TechnoTypeExt::ExtMap.Find(pType)->NoTurret_TrackTarget.Get(RulesExt::Global()->NoTurret_TrackTarget)) + if (!TechnoTypeExt::Fetch(pType)->NoTurret_TrackTarget.Get(RulesExt::Global()->NoTurret_TrackTarget)) return NoNeedToCheck; return (fireError == FireError::REARM && !pType->Turret && !pThis->IsWarpingIn()) ? ContinueCheck : NoNeedToCheck; @@ -800,7 +800,7 @@ DEFINE_HOOK(0x51B20E, InfantryClass_AssignTarget_FireOnce, 0x6) if (!pTarget) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->SkipTargetChangeResetSequence) { @@ -837,7 +837,7 @@ DEFINE_HOOK(0x51D7E0, InfantryClass_DoAction_Water, 0x5) R->EBP(0); // Restore overridden instructions. - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); if (pTypeExt->OnlyUseLandSequences) return SkipWaterSequences; @@ -876,7 +876,7 @@ DEFINE_HOOK(0x70FB73, FootClass_IsBunkerableNow_Dehardcode, 0x6) if (!LocomotorCheckForBunkerable(pType)) return NoEnter; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); return pTypeExt->BunkerableAnyway ? CanEnter : 0; } @@ -890,7 +890,7 @@ DEFINE_HOOK(0x730D0F, ProcessDeployCommand_LowDeployPriority, 0x6) { GET(TechnoClass* const, pTechno, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pTechno); + auto const pExt = TechnoExt::Fetch(pTechno); if (pExt->TypeExtData->LowDeployPriority) { @@ -898,7 +898,7 @@ DEFINE_HOOK(0x730D0F, ProcessDeployCommand_LowDeployPriority, 0x6) { if ((pObject->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { - if (!TechnoExt::ExtMap.Find(static_cast(pObject))->TypeExtData->LowDeployPriority) + if (!TechnoExt::Fetch(static_cast(pObject))->TypeExtData->LowDeployPriority) return SkipDeploy; } } @@ -941,7 +941,7 @@ DEFINE_HOOK(0x4C7512, EventClass_Execute_StopCommand, 0x6) } // Explicit stop command should reset subterranean harvester state machine. - auto const pExt = TechnoExt::ExtMap.Find(pUnit); + auto const pExt = TechnoExt::Fetch(pUnit); pExt->SubterraneanHarvStatus = 0; pExt->SubterraneanHarvRallyPoint = nullptr; } @@ -960,7 +960,7 @@ DEFINE_HOOK(0x4C7462, EventClass_Execute_MegaMission_MoveCommand, 0x5) GET(EventClass*, pThis, ESI); auto const mission = static_cast(pThis->MegaMission.Mission); - auto const pExt = TechnoExt::ExtMap.Find(pTechno); + auto const pExt = TechnoExt::Fetch(pTechno); if (mission == Mission::Move) { diff --git a/src/Ext/Techno/Hooks.Pips.cpp b/src/Ext/Techno/Hooks.Pips.cpp index 075299f870..89590a091d 100644 --- a/src/Ext/Techno/Hooks.Pips.cpp +++ b/src/Ext/Techno/Hooks.Pips.cpp @@ -7,7 +7,7 @@ DEFINE_HOOK(0x6D9076, TacticalClass_RenderLayers_DrawBefore, 0x5)// FootClass if (pTechno->IsSelected && Phobos::Config::EnableSelectBox) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; if (!pTypeExt->HealthBar_Hide && !pTypeExt->HideSelectBox) { @@ -25,7 +25,7 @@ DEFINE_HOOK(0x6F5E37, TechnoClass_DrawExtras_DrawHealthBar, 0x6) GET(TechnoClass*, pThis, EBP); - if (pThis && (pThis->IsMouseHovering || TechnoExt::ExtMap.Find(pThis)->TypeExtData->HealthBar_Permanent) + if (pThis && (pThis->IsMouseHovering || TechnoExt::Fetch(pThis)->TypeExtData->HealthBar_Permanent) && !MapClass::Instance.IsLocationShrouded(pThis->GetCoords())) { return Permanent; @@ -40,7 +40,7 @@ DEFINE_HOOK(0x6F64A9, TechnoClass_DrawHealthBar_Hide, 0x5) GET(TechnoClass*, pThis, ECX); - const auto pTypeData = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeData = TechnoExt::Fetch(pThis)->TypeExtData; if (pTypeData->HealthBar_Hide) return SkipDraw; @@ -54,7 +54,7 @@ DEFINE_HOOK(0x6F6637, TechnoClass_DrawHealthBar_HideBuildingsPips, 0x5) GET(TechnoClass*, pThis, ESI); - const bool hidePips = TechnoExt::ExtMap.Find(pThis)->TypeExtData->HealthBar_HidePips; + const bool hidePips = TechnoExt::Fetch(pThis)->TypeExtData->HealthBar_HidePips; return hidePips ? SkipDrawPips : 0; } @@ -66,7 +66,7 @@ DEFINE_HOOK(0x6F67E8, TechnoClass_DrawHealthBar_PermanentPipScale, 0xA) // Dra GET(TechnoClass*, pThis, ESI); - const bool showPipScale = TechnoExt::ExtMap.Find(pThis)->TypeExtData->HealthBar_Permanent_PipScale; + const bool showPipScale = TechnoExt::Fetch(pThis)->TypeExtData->HealthBar_Permanent_PipScale; return !showPipScale && !pThis->IsMouseHovering && !pThis->IsSelected ? Permanent : 0; } @@ -77,7 +77,7 @@ DEFINE_HOOK(0x6F65D1, TechnoClass_DrawHealthBar_Buildings, 0x6) GET(const int, length, EBX); GET_STACK(RectangleStruct*, pBound, STACK_OFFSET(0x4C, 0x8)); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (pThis->IsSelected && Phobos::Config::EnableSelectBox && !pExt->TypeExtData->HideSelectBox) { @@ -105,7 +105,7 @@ DEFINE_HOOK(0x6F683C, TechnoClass_DrawHealthBar_Units, 0x7) GET_STACK(Point2D*, pLocation, STACK_OFFSET(0x4C, 0x4)); GET_STACK(RectangleStruct*, pBound, STACK_OFFSET(0x4C, 0x8)); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (pThis->IsSelected && Phobos::Config::EnableSelectBox && !pExt->TypeExtData->HideSelectBox) { @@ -168,7 +168,7 @@ DEFINE_HOOK(0x709B2E, TechnoClass_DrawPips_Sizes, 0x5) else size = RulesExt::Global()->Pips_Ammo_Size; - size = TechnoTypeExt::ExtMap.Find(pType)->AmmoPipSize.Get(size); + size = TechnoTypeExt::Fetch(pType)->AmmoPipSize.Get(size); } else { @@ -189,7 +189,7 @@ DEFINE_HOOK(0x709B8B, TechnoClass_DrawPips_Spawns, 0x5) enum { SkipGameDrawing = 0x709C27 }; GET(TechnoClass*, pThis, ECX); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (!pTypeExt->ShowSpawnsPips) return SkipGameDrawing; @@ -236,7 +236,7 @@ DEFINE_HOOK(0x70A36E, TechnoClass_DrawPips_Ammo, 0x6) GET_STACK(const int, maxPips, STACK_OFFSET(0x74, -0x60)); GET(const int, yOffset, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const pipOffset = pTypeExt->AmmoPipOffset.Get(); const int offsetWidth = offset->Width; Point2D position = { offset->X + pipOffset.X, offset->Y + pipOffset.Y }; diff --git a/src/Ext/Techno/Hooks.ReceiveDamage.cpp b/src/Ext/Techno/Hooks.ReceiveDamage.cpp index 50cf38262a..be8712b851 100644 --- a/src/Ext/Techno/Hooks.ReceiveDamage.cpp +++ b/src/Ext/Techno/Hooks.ReceiveDamage.cpp @@ -17,7 +17,7 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) GET(TechnoClass*, pThis, ECX); LEA_STACK(args_ReceiveDamage*, args, 0x4); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(args->WH); + const auto pWHExt = WarheadTypeExt::Fetch(args->WH); int& damage = *args->Damage; // AffectsAbove/BelowPercent & AffectsNeutral can ignore IgnoreDefenses like AffectsAllies/Enmies/Owner @@ -31,7 +31,7 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) return 0; } - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto pSourceHouse = args->SourceHouse; const auto pTargetHouse = pThis->Owner; @@ -89,7 +89,7 @@ DEFINE_HOOK(0x701900, TechnoClass_ReceiveDamage_Shield, 0x6) if (!pTargetHouse->IsControlledByCurrentPlayer() || (RulesExt::Global()->CombatAlert_SuppressIfAllyDamage && pTargetHouse->IsAlliedWith(pSourceHouse))) return; - const auto pHouseExt = HouseExt::ExtMap.Find(pTargetHouse); + const auto pHouseExt = HouseExt::Fetch(pTargetHouse); if (pHouseExt->CombatAlertTimer.HasTimeLeft() || pWHExt->CombatAlert_Suppress.Get(!pWHExt->Malicious || pWHExt->Nonprovocative)) return; @@ -202,7 +202,7 @@ DEFINE_HOOK(0x702819, TechnoClass_ReceiveDamage_Decloak, 0xA) GET(TechnoClass* const, pThis, ESI); GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0xC4, 0xC)); - if (auto const pExt = WarheadTypeExt::ExtMap.TryFind(pWarhead)) + if (auto const pExt = WarheadTypeExt::TryFetch(pWarhead)) { if (pExt->DecloakDamagedTargets) pThis->Uncloak(false); @@ -220,7 +220,7 @@ DEFINE_HOOK(0x701DFF, TechnoClass_ReceiveDamage_FlyingStrings, 0x7) GET(int* const, pDamage, EBX); if (*pDamage) - GeneralUtils::DisplayDamageNumberString(*pDamage, DamageDisplayType::Regular, pThis->GetRenderCoords(), TechnoExt::ExtMap.Find(pThis)->DamageNumberOffset); + GeneralUtils::DisplayDamageNumberString(*pDamage, DamageDisplayType::Regular, pThis->GetRenderCoords(), TechnoExt::Fetch(pThis)->DamageNumberOffset); return 0; } @@ -231,7 +231,7 @@ DEFINE_HOOK(0x702603, TechnoClass_ReceiveDamage_Explodes, 0x6) GET(TechnoClass*, pThis, ESI); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pThis->WhatAmI() == AbstractType::Building) { @@ -288,14 +288,14 @@ DEFINE_HOOK(0x518505, InfantryClass_ReceiveDamage_NotHuman, 0x4) constexpr auto Die = [](int x) { return x + 10; }; int resultSequence = Die(1); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pTypeExt->NotHuman_RandomDeathSequence.Get()) resultSequence = ScenarioClass::Instance->Random.RandomRanged(Die(1), Die(5)); if (receiveDamageArgs.WH) { - auto const pWarheadExt = WarheadTypeExt::ExtMap.Find(receiveDamageArgs.WH); + auto const pWarheadExt = WarheadTypeExt::Fetch(receiveDamageArgs.WH); const int whSequence = pWarheadExt->NotHuman_DeathSequence.Get(); if (whSequence > 0) @@ -311,7 +311,7 @@ DEFINE_HOOK(0x702050, TechnoClass_ReceiveDamage_AttachEffectExpireWeapon, 0x6) { GET(TechnoClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); std::set cumulativeTypes; std::vector> expireWeapons; @@ -361,12 +361,12 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7) if (*pDamage <= 0) return 0; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pWHExt = WarheadTypeExt::Fetch(pWarhead); if (pWHExt->Reflected) return 0; - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto& random = ScenarioClass::Instance->Random; const auto& suppressType = pWHExt->SuppressReflectDamage_Types; const auto& suppressGroup = pWHExt->SuppressReflectDamage_Groups; @@ -407,7 +407,7 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7) if (pInvoker && EnumFunctions::CanTargetHouse(pType->ReflectDamage_AffectsHouse, pInvoker->Owner, pSourceHouse)) { - auto const pWHExtRef = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExtRef = WarheadTypeExt::Fetch(pWH); pWHExtRef->Reflected = true; if (pType->ReflectDamage_Warhead_Detonate) @@ -420,7 +420,7 @@ DEFINE_HOOK(0x701E18, TechnoClass_ReceiveDamage_ReflectDamage, 0x7) } else if (EnumFunctions::CanTargetHouse(pType->ReflectDamage_AffectsHouse, pThis->Owner, pSourceHouse)) { - auto const pWHExtRef = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExtRef = WarheadTypeExt::Fetch(pWH); pWHExtRef->Reflected = true; if (pType->ReflectDamage_Warhead_Detonate) @@ -446,7 +446,7 @@ DEFINE_HOOK(0x5F5480, ObjectClass_ReceiveDamage_FlashDuration, 0x6) int nFlashDuration = 7; if (auto const pWH = receiveDamageArgs.WH) - nFlashDuration = WarheadTypeExt::ExtMap.Find(pWH)->Flash_Duration.Get(nFlashDuration); + nFlashDuration = WarheadTypeExt::Fetch(pWH)->Flash_Duration.Get(nFlashDuration); if (nFlashDuration > 0) pThis->Flash(nFlashDuration); diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index 4c73b176f7..ee259270f3 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -9,7 +9,7 @@ DEFINE_HOOK(0x7098B9, TechnoClass_TargetSomethingNearby_AutoFire, 0x6) { GET(TechnoClass* const, pThis, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pExt->AutoTargetOwnPosition) { @@ -44,7 +44,7 @@ DEFINE_HOOK(0x6F9C67, TechnoClass_GreatestThreat_MapZoneSetContext, 0x5) { GET(TechnoClass*, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; MapZoneTemp::zoneScanType = pTypeExt->TargetZoneScanType; return 0; @@ -180,7 +180,7 @@ DEFINE_HOOK(0x4DF3A0, FootClass_UpdateAttackMove_SelectNewTarget, 0x6) { GET(FootClass* const, pThis, ECX); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (pExt->TypeExtData->AttackMove_UpdateTarget.Get(RulesExt::Global()->AttackMove_UpdateTarget) && CheckAttackMoveCanResetTarget(pThis)) @@ -207,7 +207,7 @@ DEFINE_HOOK(0x6F85AB, TechnoClass_CanAutoTargetObject_AggressiveAttackMove, 0x6) if (!pThis->MegaMissionIsAttackMove()) return ContinueCheck; - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); return pExt->TypeExtData->AttackMove_Aggressive.Get(RulesExt::Global()->AttackMove_Aggressive) ? CanTarget : ContinueCheck; } @@ -238,7 +238,7 @@ static double __fastcall HealthRatio_Wrapper(TechnoClass* pTechno) if (result >= 1.0) { - const auto pExt = TechnoExt::ExtMap.Find(pTechno); + const auto pExt = TechnoExt::Fetch(pTechno); if (const auto pShieldData = pExt->Shield.get()) { @@ -277,7 +277,7 @@ class AresScheme if (const auto pTechno = abstract_cast(pObj)) { - const auto pExt = TechnoExt::ExtMap.Find(pTechno); + const auto pExt = TechnoExt::Fetch(pTechno); if (const auto pShieldData = pExt->Shield.get()) { @@ -382,7 +382,7 @@ static inline bool IsAThreatToMe(TechnoClass* const pTechno, AbstractClass* cons { if (const auto pTechnoTarget = abstract_cast(pTarget)) { - const auto pTypeExt = TechnoExt::ExtMap.Find(pTechnoTarget)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pTechnoTarget)->TypeExtData; if (pTypeExt->AlwaysConsideredThreat) return true; @@ -432,7 +432,7 @@ DEFINE_HOOK(0x70CF87, TechnoClass_ThreatCoefficient_CanAttackMeThreatBonus, 0x9) GET(TechnoClass* const, pTarget, ESI); REF_STACK(double, totalThreat, STACK_OFFSET(0x58, -0x48)); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto pTypeExt = pExt->TypeExtData; if (!pTypeExt->ExtraThreat_Enabled) diff --git a/src/Ext/Techno/Hooks.Targeting.cpp b/src/Ext/Techno/Hooks.Targeting.cpp index 3f9162dd64..2550a55622 100644 --- a/src/Ext/Techno/Hooks.Targeting.cpp +++ b/src/Ext/Techno/Hooks.Targeting.cpp @@ -18,7 +18,7 @@ DEFINE_HOOK(0x70982C, TechnoClass_TargetAndEstimateDamage_TargetingDelay, 0x8) pThis->unknown_4FC = frame; int delay = ScenarioClass::Instance->Random.RandomRanged(0, 2); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pThis->MegaMissionIsAttackMove()) { @@ -58,7 +58,7 @@ DEFINE_HOOK(0x6F7CE2, TechnoClass_CanAutoTargetObject_IronCurtain, 0x6) if (pWeapon) { - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->AutoTarget_IronCurtained.isset()) return pWeaponExt->AutoTarget_IronCurtained.Get() ? 0 : ReturnFalse; diff --git a/src/Ext/Techno/Hooks.Tint.cpp b/src/Ext/Techno/Hooks.Tint.cpp index 32cd0ecaa5..87dfead41a 100644 --- a/src/Ext/Techno/Hooks.Tint.cpp +++ b/src/Ext/Techno/Hooks.Tint.cpp @@ -58,7 +58,7 @@ static int GetDeployingAnimIntensity(FootClass* pThis) if (pThis->IsIronCurtained()) intensity = pThis->GetInvulnerabilityTintIntensity(intensity); - if (TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe) + if (TechnoExt::Fetch(pThis)->AirstrikeTargetingMe) intensity = pThis->GetAirstrikeTintIntensity(intensity); return intensity; @@ -105,7 +105,7 @@ DEFINE_HOOK(0x73BF95, UnitClass_DrawAsVoxel_Tint, 0x7) if (pThis->IsIronCurtained()) intensity = pThis->GetInvulnerabilityTintIntensity(intensity); - if (TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe) + if (TechnoExt::Fetch(pThis)->AirstrikeTargetingMe) intensity = pThis->GetAirstrikeTintIntensity(intensity); int color = TechnoExt::GetTintColor(pThis, true, true, true); @@ -137,7 +137,7 @@ DEFINE_HOOK(0x51946D, InfantryClass_Draw_TintIntensity, 0x6) if (pThis->IsIronCurtained()) intensity = pThis->GetInvulnerabilityTintIntensity(intensity); - if (TechnoExt::ExtMap.Find(pThis)->AirstrikeTargetingMe) + if (TechnoExt::Fetch(pThis)->AirstrikeTargetingMe) intensity = pThis->GetAirstrikeTintIntensity(intensity); intensity += TechnoExt::GetCustomTintIntensity(pThis); @@ -169,7 +169,7 @@ DEFINE_HOOK(0x423420, AnimClass_Draw_TintColor, 0x6) } else if (!pTechno) { - pTechno = AnimExt::ExtMap.Find(pThis)->ParentBuilding; + pTechno = AnimExt::Fetch(pThis)->ParentBuilding; } if (pTechno) @@ -206,7 +206,7 @@ DEFINE_HOOK(0x70632E, TechnoClass_DrawShape_GetTintIntensity, 0x6) if (pThis->IsIronCurtained()) intensity = pThis->GetInvulnerabilityTintIntensity(intensity); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (pExt->AirstrikeTargetingMe) intensity = pThis->GetAirstrikeTintIntensity(intensity); @@ -274,7 +274,7 @@ DEFINE_HOOK(0x706786, TechnoClass_DrawVoxel_TintColor, 0x5) if (pThis->IsIronCurtained()) intensity = pThis->GetInvulnerabilityTintIntensity(intensity); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (pExt->AirstrikeTargetingMe) intensity = pThis->GetAirstrikeTintIntensity(intensity); diff --git a/src/Ext/Techno/Hooks.Transport.cpp b/src/Ext/Techno/Hooks.Transport.cpp index e07de41ed3..dec517f1b4 100644 --- a/src/Ext/Techno/Hooks.Transport.cpp +++ b/src/Ext/Techno/Hooks.Transport.cpp @@ -35,7 +35,7 @@ DEFINE_HOOK(0x6F8FD7, TechnoClass_ThreatEvals_OpenToppedOwner, 0x5) // Tec if (auto const pTransport = pThis->Transporter) { - if (TechnoExt::ExtMap.Find(pTransport)->TypeExtData->Passengers_SyncOwner) + if (TechnoExt::Fetch(pTransport)->TypeExtData->Passengers_SyncOwner) return returnAddress; } @@ -48,7 +48,7 @@ DEFINE_HOOK(0x701881, TechnoClass_ChangeHouse_Passenger_SyncOwner, 0x5) if (auto pPassenger = pThis->Passengers.GetFirstPassenger()) { - if (TechnoExt::ExtMap.Find(pThis)->TypeExtData->Passengers_SyncOwner) + if (TechnoExt::Fetch(pThis)->TypeExtData->Passengers_SyncOwner) { const auto pOwner = pThis->Owner; @@ -72,9 +72,9 @@ DEFINE_HOOK(0x71067B, TechnoClass_EnterTransport, 0x7) if (pPassenger) { auto const pType = pPassenger->GetTechnoType(); - auto const pExt = TechnoExt::ExtMap.Find(pPassenger); + auto const pExt = TechnoExt::Fetch(pPassenger); auto const whatAmI = pPassenger->WhatAmI(); - auto const pTransTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTransTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pTransTypeExt->Passengers_SyncOwner && pTransTypeExt->Passengers_SyncOwner_RevertOnExit) pExt->OriginalPassengerOwner = pPassenger->Owner; @@ -97,9 +97,9 @@ DEFINE_HOOK(0x4DE722, FootClass_LeaveTransport, 0x6) if (pPassenger) { auto const pType = pPassenger->GetTechnoType(); - auto const pExt = TechnoExt::ExtMap.Find(pPassenger); + auto const pExt = TechnoExt::Fetch(pPassenger); auto const whatAmI = pPassenger->WhatAmI(); - auto const pTransTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTransTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; // Remove from transport reloader list before switching house if (whatAmI != AbstractType::Aircraft && whatAmI != AbstractType::Building @@ -127,13 +127,13 @@ DEFINE_HOOK(0x737F80, UnitClass_ReceiveDamage_Cargo_SyncOwner, 0x6) if (auto pPassenger = pThis->Passengers.GetFirstPassenger()) { - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TechnoTypeExt::Fetch(pThis->Type); if (pTypeExt->Passengers_SyncOwner && pTypeExt->Passengers_SyncOwner_RevertOnExit) { do { - auto const pExt = TechnoExt::ExtMap.Find(pPassenger); + auto const pExt = TechnoExt::Fetch(pPassenger); if (pExt->OriginalPassengerOwner) pPassenger->SetOwningHouse(pExt->OriginalPassengerOwner, false); @@ -154,7 +154,7 @@ DEFINE_HOOK(0x51DF82, InfantryClass_FireAt_ReloadInTransport, 0x6) if (pThis->Transporter) { auto const pType = pThis->Type; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (pTypeExt->ReloadInTransport && pType->Ammo > 0 && pThis->Ammo < pType->Ammo) pThis->StartReloading(); @@ -171,10 +171,10 @@ DEFINE_HOOK(0x6F72D2, TechnoClass_IsCloseEnoughToTarget_OpenTopped_RangeBonus, 0 if (auto const pTransport = pThis->Transporter) { - auto const pExt = TechnoExt::ExtMap.Find(pTransport)->TypeExtData; + auto const pExt = TechnoExt::Fetch(pTransport)->TypeExtData; const int rangeBonus = pExt->OpenTopped_RangeBonus.Get(RulesClass::Instance->OpenToppedRangeBonus); - R->EAX(rangeBonus + TechnoExt::ExtMap.Find(pThis)->TypeExtData->OpenTransport_RangeBonus); + R->EAX(rangeBonus + TechnoExt::Fetch(pThis)->TypeExtData->OpenTransport_RangeBonus); return 0x6F72DE; } @@ -187,7 +187,7 @@ DEFINE_HOOK(0x71A82C, TemporalClass_AI_Opentopped_WarpDistance, 0xC) if (auto const pTransport = pThis->Owner->Transporter) { - auto const pExt = TechnoExt::ExtMap.Find(pTransport)->TypeExtData; + auto const pExt = TechnoExt::Fetch(pTransport)->TypeExtData; R->EDX(pExt->OpenTopped_WarpDistance.Get(RulesClass::Instance->OpenToppedWarpDistance)); return 0x71A838; } @@ -204,7 +204,7 @@ DEFINE_HOOK(0x710552, TechnoClass_SetOpenTransportCargoTarget_ShareTarget, 0x6) if (pTarget) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (!pTypeExt->OpenTopped_ShareTransportTarget) return ReturnFromFunction; @@ -242,7 +242,7 @@ static inline bool CanEnterNow(UnitClass* pTransport, FootClass* pPassenger) return false; const auto pTransportType = pTransport->Type; - const auto pTransportTypeExt = TechnoTypeExt::ExtMap.Find(pTransportType); + const auto pTransportTypeExt = TechnoTypeExt::Fetch(pTransportType); // Added to fit with AmphibiousEnter if (pTransport->GetCell()->LandType == LandType::Water && !pTransportTypeExt->AmphibiousEnter.Get(RulesExt::Global()->AmphibiousEnter)) @@ -277,7 +277,7 @@ static inline bool CanEnterNow(UnitClass* pTransport, FootClass* pPassenger) } // Rewrite from 0x51A21B/0x73A6D1 -static inline void DoEnterNow(UnitClass* pTransport, FootClass* pPassenger, TechnoExt::ExtData* pExt) +static inline void DoEnterNow(UnitClass* pTransport, FootClass* pPassenger, TechnoExt* pExt) { // Vanilla only for infantry, but why if (const auto pTag = pTransport->AttachedTag) @@ -317,7 +317,7 @@ DEFINE_HOOK(0x4DA8A0, FootClass_Update_AfterLocomotorProcess, 0x6) GET(FootClass* const, pThis, ESI); // Update laser trails after locomotor process, to ensure that the updated position is not the previous frame's position - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pExt->UpdateLaserTrails(); // The core part of the fast enter action @@ -325,7 +325,7 @@ DEFINE_HOOK(0x4DA8A0, FootClass_Update_AfterLocomotorProcess, 0x6) { const auto pType = pDest->Type; - if (pType->Passengers > 0 && TechnoTypeExt::ExtMap.Find(pType)->NoQueueUpToEnter.Get(RulesExt::Global()->NoQueueUpToEnter)) + if (pType->Passengers > 0 && TechnoTypeExt::Fetch(pType)->NoQueueUpToEnter.Get(RulesExt::Global()->NoQueueUpToEnter)) { if (IsCloseEnoughToEnter(pDest, pThis)) { @@ -370,7 +370,7 @@ DEFINE_HOOK(0x4DA8A0, FootClass_Update_AfterLocomotorProcess, 0x6) // Rewrite from 0x4835D5/0x74004B, replace check pThis->GetCell()->LandType != LandType::Water static inline bool CanUnloadNow(UnitClass* pTransport, FootClass* pPassenger) { - if (TechnoTypeExt::ExtMap.Find(pTransport->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) + if (TechnoTypeExt::Fetch(pTransport->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) return GroundType::Array[static_cast(pTransport->GetCell()->LandType)].Cost[static_cast(pPassenger->GetTechnoType()->SpeedType)] != 0.0; return pTransport->GetCell()->LandType != LandType::Water; @@ -412,7 +412,7 @@ DEFINE_HOOK(0x73DC9C, UnitClass_Mission_Unload_NoQueueUpToUnloadBreak, 0xA) TransportUnloadTemp::ShouldPlaySound = false; const auto pType = pThis->Type; - if (TechnoTypeExt::ExtMap.Find(pType)->NoQueueUpToUnload.Get(RulesExt::Global()->NoQueueUpToUnload)) + if (TechnoTypeExt::Fetch(pType)->NoQueueUpToUnload.Get(RulesExt::Global()->NoQueueUpToUnload)) VoxClass::PlayAtPos(pType->LeaveTransportSound, &pThis->Location); } @@ -429,7 +429,7 @@ DEFINE_HOOK(0x73DC1E, UnitClass_Mission_Unload_NoQueueUpToUnloadLoop, 0xA) const auto pType = pThis->Type; const auto pPassenger = pThis->Passengers.GetFirstPassenger(); - if (TechnoTypeExt::ExtMap.Find(pType)->NoQueueUpToUnload.Get(RulesExt::Global()->NoQueueUpToUnload)) + if (TechnoTypeExt::Fetch(pType)->NoQueueUpToUnload.Get(RulesExt::Global()->NoQueueUpToUnload)) { if (!pPassenger || pThis->Passengers.NumPassengers <= pThis->NonPassengerCount) { @@ -608,7 +608,7 @@ DEFINE_HOOK(0x73796B, UnitClass_ReceiveCommand_AmphibiousEnter, 0x7) if (pThis->OnBridge) return MoveToPassenger; - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->AmphibiousEnter.Get(RulesExt::Global()->AmphibiousEnter)) + if (TechnoTypeExt::Fetch(pThis->Type)->AmphibiousEnter.Get(RulesExt::Global()->AmphibiousEnter)) return ContinueCheck; GET(CellClass* const, pCell, EBP); @@ -629,7 +629,7 @@ DEFINE_HOOK(0x7400B5, UnitClass_MouseOverObject_AmphibiousUnload, 0x7) GET(UnitClass* const, pThis, ESI); - return TechnoTypeExt::ExtMap.Find(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload) ? ContinueCheck : CannotUnload; + return TechnoTypeExt::Fetch(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload) ? ContinueCheck : CannotUnload; } DEFINE_HOOK(0x70107A, TechnoClass_CanDeploySlashUnload_AmphibiousUnload, 0x7) @@ -643,7 +643,7 @@ DEFINE_HOOK(0x70107A, TechnoClass_CanDeploySlashUnload_AmphibiousUnload, 0x7) GET(UnitClass* const, pThis, ESI); - return TechnoTypeExt::ExtMap.Find(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload) ? ContinueCheck : CannotUnload; + return TechnoTypeExt::Fetch(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload) ? ContinueCheck : CannotUnload; } DEFINE_HOOK(0x73D769, UnitClass_Mission_Unload_AmphibiousUnload, 0x7) @@ -661,7 +661,7 @@ DEFINE_HOOK(0x73D7AB, UnitClass_Mission_Unload_FindUnloadPosition, 0x5) { GET(UnitClass* const, pThis, ESI); - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) + if (TechnoTypeExt::Fetch(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) { if (const auto pPassenger = pThis->Passengers.GetFirstPassenger()) { @@ -690,7 +690,7 @@ DEFINE_HOOK(0x740C9C, UnitClass_GetUnloadDirection_CheckUnloadPosition, 0x7) { GET(UnitClass* const, pThis, EDI); - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) + if (TechnoTypeExt::Fetch(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) { if (const auto pPassenger = pThis->Passengers.GetFirstPassenger()) { @@ -706,7 +706,7 @@ DEFINE_HOOK(0x73DAD8, UnitClass_Mission_Unload_PassengerLeavePosition, 0x5) { GET(UnitClass* const, pThis, ESI); - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) + if (TechnoTypeExt::Fetch(pThis->Type)->AmphibiousUnload.Get(RulesExt::Global()->AmphibiousUnload)) { GET(FootClass* const, pPassenger, EDI); REF_STACK(MovementZone, movementZone, STACK_OFFSET(0xBC, -0xAC)); @@ -731,7 +731,7 @@ DEFINE_HOOK(0x51EE36, InfantryClass_MouseOvetObject_NoQueueUpToEnter, 0x5) const auto pType = static_cast(pObject)->Type; if (pType->InfantryAbsorb - && TechnoTypeExt::ExtMap.Find(pType)->NoQueueUpToEnter.Get( + && TechnoTypeExt::Fetch(pType)->NoQueueUpToEnter.Get( pRulesExt->NoQueueUpToEnter_Buildings.Get(pRulesExt->NoQueueUpToEnter))) { R->EBP(Action::Repair); @@ -753,7 +753,7 @@ DEFINE_HOOK(0x740375, UnitClass_MouseOvetObject_NoQueueUpToEnter, 0x5) const auto pType = static_cast(pObject)->Type; if (pType->UnitAbsorb - && TechnoTypeExt::ExtMap.Find(pType)->NoQueueUpToEnter.Get( + && TechnoTypeExt::Fetch(pType)->NoQueueUpToEnter.Get( pRulesExt->NoQueueUpToEnter_Buildings.Get(pRulesExt->NoQueueUpToEnter))) { R->EBX(Action::Repair); @@ -773,7 +773,7 @@ DEFINE_HOOK(0x73F63F, UnitClass_IsCellOccupied_NoQueueUpToEnter, 0x6) const auto pType = pThis->Type; if (pType->UnitAbsorb - && TechnoTypeExt::ExtMap.Find(pType)->NoQueueUpToEnter.Get( + && TechnoTypeExt::Fetch(pType)->NoQueueUpToEnter.Get( pRulesExt->NoQueueUpToEnter_Buildings.Get(pRulesExt->NoQueueUpToEnter))) { return SkipGameCode; @@ -789,7 +789,7 @@ DEFINE_HOOK(0x4DFC83, FootClass_EnterBioReactor_NoQueueUpToUnload, 0x6) enum { SkipGameCode = 0x4DFC91 }; const auto RulesExt = RulesExt::Global(); - const Mission mission = TechnoTypeExt::ExtMap.Find(pBuilding->Type)->NoQueueUpToEnter.Get( + const Mission mission = TechnoTypeExt::Fetch(pBuilding->Type)->NoQueueUpToEnter.Get( RulesExt->NoQueueUpToEnter_Buildings.Get(RulesExt->NoQueueUpToEnter)) ? Mission::Eaten : Mission::Enter; @@ -803,7 +803,7 @@ DEFINE_HOOK(0x44DCB1, BuildingClass_Mi_Unload_NoQueueUpToUnload, 0x7) const auto pRulesExt = RulesExt::Global(); - if (TechnoTypeExt::ExtMap.Find(pThis->Type)->NoQueueUpToUnload.Get( + if (TechnoTypeExt::Fetch(pThis->Type)->NoQueueUpToUnload.Get( pRulesExt->NoQueueUpToUnload_Buildings.Get(pRulesExt->NoQueueUpToUnload))) { R->EAX(0); @@ -865,7 +865,7 @@ DEFINE_HOOK(0x739FA2, UnitClass_UpdatePosition_NoQueueUpToEnter, 0x5) pTag->RaiseEvent(TriggerEvent::EnteredBy, pThis, CellStruct::Empty); // This might fix a bug where hover vehicles enter tunnels. - TechnoExt::ExtMap.Find(pThis)->ResetLocomotor = true; + TechnoExt::Fetch(pThis)->ResetLocomotor = true; if (pTunnel) { @@ -903,12 +903,12 @@ DEFINE_HOOK(0x4D9510, FootClass_SetDestination_OpenToppedFireWhileMoving, 0x6) if (pType->OpenTopped && pUnit->Passengers.NumPassengers > 0) { - const bool fireWhileMoving = TechnoTypeExt::ExtMap.Find(pType)->OpenTopped_FireWhileMoving.Get(RulesExt::Global()->OpenTopped_FireWhileMoving); + const bool fireWhileMoving = TechnoTypeExt::Fetch(pType)->OpenTopped_FireWhileMoving.Get(RulesExt::Global()->OpenTopped_FireWhileMoving); auto pPassenger = pUnit->Passengers.GetFirstPassenger(); while (pPassenger) { - const bool canFire = fireWhileMoving && TechnoExt::ExtMap.Find(pPassenger)->TypeExtData->OpenTransport_FireWhileMoving.Get(RulesExt::Global()->OpenTransport_FireWhileMoving); + const bool canFire = fireWhileMoving && TechnoExt::Fetch(pPassenger)->TypeExtData->OpenTransport_FireWhileMoving.Get(RulesExt::Global()->OpenTransport_FireWhileMoving); // Technically these 2 can't exist in the same time, but just in case if (auto const pLocoTarget = pPassenger->LocomotorTarget) diff --git a/src/Ext/Techno/Hooks.WeaponEffects.cpp b/src/Ext/Techno/Hooks.WeaponEffects.cpp index b0ddf15de5..e174d3485d 100644 --- a/src/Ext/Techno/Hooks.WeaponEffects.cpp +++ b/src/Ext/Techno/Hooks.WeaponEffects.cpp @@ -37,7 +37,7 @@ DEFINE_HOOK(0x6FF15F, TechnoClass_FireAt_ObstacleCellSet, 0x6) // This is set to a temp variable as well, as accessing it everywhere needed from TechnoExt would be more complicated. FireAtTemp::pObstacleCell = TrajectoryHelper::FindFirstObstacle(*pSourceCoords, coords, pWeapon->Projectile, pThis->Owner); - TechnoExt::ExtMap.Find(pThis)->FiringObstacleCell = FireAtTemp::pObstacleCell; + TechnoExt::Fetch(pThis)->FiringObstacleCell = FireAtTemp::pObstacleCell; return 0; } @@ -64,7 +64,7 @@ DEFINE_HOOK(0x62FA41, ParticleSystemClass_FireAI_TargetCoords, 0x6) GET(ParticleSystemClass*, pThis, ESI); - auto const pTypeExt = ParticleSystemTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = ParticleSystemTypeExt::Fetch(pThis->Type); if (!pTypeExt->AdjustTargetCoordsOnRotation) return SkipGameCode; @@ -139,7 +139,7 @@ DEFINE_HOOK(0x70C862, TechnoClass_Railgun_AmbientDamageIgnoreTarget1, 0x5) GET_BASE(WeaponTypeClass*, pWeapon, 0x14); - if (WeaponTypeExt::ExtMap.Find(pWeapon)->AmbientDamage_IgnoreTarget) + if (WeaponTypeExt::Fetch(pWeapon)->AmbientDamage_IgnoreTarget) return IgnoreTarget; return 0; @@ -152,7 +152,7 @@ DEFINE_HOOK(0x70CA8B, TechnoClass_Railgun_AmbientDamageIgnoreTarget2, 0x6) GET_BASE(WeaponTypeClass*, pWeapon, 0x14); REF_STACK(DynamicVectorClass, objects, STACK_OFFSET(0xC0, -0xAC)); - if (WeaponTypeExt::ExtMap.Find(pWeapon)->AmbientDamage_IgnoreTarget) + if (WeaponTypeExt::Fetch(pWeapon)->AmbientDamage_IgnoreTarget) { R->EAX(objects.Count); return IgnoreTarget; @@ -167,7 +167,7 @@ DEFINE_HOOK(0x70CBDA, TechnoClass_Railgun_AmbientDamageWarhead, 0x6) GET(WeaponTypeClass*, pWeapon, EDI); - R->EDX(WeaponTypeExt::ExtMap.Find(pWeapon)->AmbientDamage_Warhead.Get(pWeapon->Warhead)); + R->EDX(WeaponTypeExt::Fetch(pWeapon)->AmbientDamage_Warhead.Get(pWeapon->Warhead)); return SkipGameCode; } @@ -178,7 +178,7 @@ DEFINE_HOOK(0x75F39D, WaveClass_DamageAI_AmbientDamageWarhead, 0x6) GET(WeaponTypeClass*, pWeapon, EBX); - auto const pTypeExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pTypeExt = WeaponTypeExt::Fetch(pWeapon); FireAtTemp::IgnoreTargetForWaveAmbientDamage = pTypeExt->AmbientDamage_IgnoreTarget; R->EAX(pTypeExt->AmbientDamage_Warhead.Get(pWeapon->Warhead)); @@ -226,7 +226,7 @@ DEFINE_HOOK(0x6FD38D, TechnoClass_DrawSth_DrawToInvisoFlakScatterLocation, 0x7) if (const auto pBullet = FireAtTemp::FireBullet) { // The weapon may not have been set up - const auto pWeaponExt = WeaponTypeExt::ExtMap.TryFind(pBullet->WeaponType); + const auto pWeaponExt = WeaponTypeExt::TryFetch(pBullet->WeaponType); if (pWeaponExt && pWeaponExt->VisualScatter) { @@ -292,7 +292,7 @@ DEFINE_HOOK(0x6FD446, TechnoClass_LaserZap_IsSingleColor, 0x7) GET(WeaponTypeClass* const, pWeapon, ECX); GET(LaserDrawClass* const, pLaser, EAX); - if (auto const pWeaponExt = WeaponTypeExt::ExtMap.TryFind(pWeapon)) + if (auto const pWeaponExt = WeaponTypeExt::TryFetch(pWeapon)) { if (!pLaser->IsHouseColor && pWeaponExt->Laser_IsSingleColor) pLaser->IsHouseColor = true; @@ -313,7 +313,7 @@ DEFINE_HOOK(0x762AFF, WaveClass_AI_TargetSet, 0x6) { if (auto const pOwner = pThis->Owner) { - auto const pObstacleCell = TechnoExt::ExtMap.Find(pOwner)->FiringObstacleCell; + auto const pObstacleCell = TechnoExt::Fetch(pOwner)->FiringObstacleCell; if (pObstacleCell == pThis->Target && pOwner->Target) { diff --git a/src/Ext/Techno/Hooks.WeaponRange.cpp b/src/Ext/Techno/Hooks.WeaponRange.cpp index 80ac21abb0..8fc337f17a 100644 --- a/src/Ext/Techno/Hooks.WeaponRange.cpp +++ b/src/Ext/Techno/Hooks.WeaponRange.cpp @@ -17,7 +17,7 @@ DEFINE_HOOK(0x7012C2, TechnoClass_WeaponRange, 0x8) if (pWeapon) { result = WeaponTypeExt::GetRangeWithModifiers(pWeapon, pThis); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (!pTypeExt->OpenTopped_IgnoreRangefinding && pTypeExt->OwnerObject()->OpenTopped) { @@ -75,13 +75,13 @@ static bool IsMovingFire(TechnoClass* pThis) static bool IsPrefiring(TechnoClass* pThis, WeaponTypeClass* pWeapon) { - const auto pTypeExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pTypeExt = WeaponTypeExt::Fetch(pWeapon); const int currentBurst = pThis->CurrentBurstIndex % pWeapon->Burst; if (pTypeExt->ExtraRange_Prefiring_IncludeBurst.Get(RulesExt::Global()->ExtraRange_Prefiring_IncludeBurst) && currentBurst != 0) return true; - const auto pTechnoExt = TechnoExt::ExtMap.Find(pThis); + const auto pTechnoExt = TechnoExt::Fetch(pThis); if (pTechnoExt->DelayedFireTimer.InProgress()) return true; @@ -117,7 +117,7 @@ static bool IsPrefiring(TechnoClass* pThis, WeaponTypeClass* pWeapon) case AbstractType::Building: { const auto pBuilding = static_cast(pThis); - const auto pExt = BuildingExt::ExtMap.Find(pBuilding); + const auto pExt = BuildingExt::Fetch(pBuilding); return pBuilding->DelayBeforeFiring || pExt->IsFiringNow; } case AbstractType::Infantry: @@ -150,7 +150,7 @@ DEFINE_HOOK(0x6F7248, TechnoClass_InRange_WeaponRange, 0x6) if (range != -512) { - const auto pExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pExt = WeaponTypeExt::Fetch(pWeapon); const auto prefiringExtraRange = pExt->ExtraRange_Prefiring.Get(RulesExt::Global()->ExtraRange_Prefiring); if (prefiringExtraRange && IsPrefiring(pThis, pWeapon)) diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 3635cb89da..493b1d6ebe 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -31,7 +31,7 @@ DEFINE_HOOK(0x6F9E50, TechnoClass_AI, 0x5) { GET(TechnoClass*, pThis, ECX); - TechnoExt::ExtMap.Find(pThis)->OnEarlyUpdate(); + TechnoExt::Fetch(pThis)->OnEarlyUpdate(); return 0; } @@ -41,7 +41,7 @@ DEFINE_HOOK(0x4DA54E, FootClass_AI, 0x6) { GET(FootClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->PreviousType) pExt->UpdateTypeData_Foot(); @@ -61,7 +61,7 @@ DEFINE_HOOK(0x736480, UnitClass_AI, 0x6) { GET(UnitClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); pExt->UpdateKeepTargetOnMove(); pExt->DepletedAmmoActions(); pExt->UpdateSubterraneanHarvester(); @@ -93,7 +93,7 @@ DEFINE_HOOK(0x71A88D, TemporalClass_AI, 0x8) if (pThis->WarpRemaining > initialWarpRemaining) pThis->WarpRemaining = initialWarpRemaining; - const auto pExt = TechnoExt::ExtMap.Find(pTarget); + const auto pExt = TechnoExt::Fetch(pTarget); pExt->UpdateTemporal(); } @@ -106,7 +106,7 @@ DEFINE_HOOK(0x735A26, FootClass_TunnelAI_Enter, 0x6) // UnitClass_TunnelAI { GET(FootClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); pExt->UpdateOnTunnelEnter(); const auto pType = pThis->GetTechnoType(); @@ -136,7 +136,7 @@ DEFINE_HOOK(0x736005, FootClass_TunnelAI_Exit, 0x6) // UnitClass_TunnelAI { GET(FootClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); pExt->UpdateOnTunnelExit(); return 0; @@ -174,7 +174,7 @@ DEFINE_HOOK(0x6FA167, TechnoClass_AI_DrainMoney, 0x5) GET(TechnoClass*, pThis, ESI); const auto pSource = pThis->DrainingMe; - const auto pTypeExt = TechnoExt::ExtMap.Find(pSource)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pSource)->TypeExtData; if (Unsorted::CurrentFrame % pTypeExt->DrainMoneyFrameDelay.Get(RulesClass::Instance->DrainMoneyFrameDelay)) return SkipGameCode; @@ -211,7 +211,7 @@ DEFINE_HOOK(0x6FA167, TechnoClass_AI_DrainMoney, 0x5) } else if (const auto pBld = abstract_cast(pThis)) { - const auto pBldTypeExt = BuildingTypeExt::ExtMap.Find(pBld->Type); + const auto pBldTypeExt = BuildingTypeExt::Fetch(pBld->Type); const auto displayTo = pBldTypeExt->DisplayIncome_Houses.Get(RulesExt::Global()->DisplayIncome_Houses); // use target for owner check FlyingStrings::AddMoneyString(-amount, pThis, pThis->Owner, displayTo, pThis->GetRenderCoords(), pBldTypeExt->DisplayIncome_Offset); @@ -234,7 +234,7 @@ DEFINE_HOOK(0x6FA540, TechnoClass_AI_ChargeTurret, 0x6) } auto const pType = pThis->GetTechnoType(); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); int timeLeft = pThis->RearmTimer.GetTimeLeft(); if (pExt->ChargeTurretTimer.HasStarted()) @@ -266,8 +266,8 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) if (!pType) // Critical sanity check in s/l return 0; - auto const pExt = TechnoExt::ExtMap.Find(pThis); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pExt = TechnoExt::Fetch(pThis); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); pExt->TypeExtData = pTypeExt; auto const pShieldType = pTypeExt->ShieldType && pTypeExt->ShieldType->Strength > 0 ? pTypeExt->ShieldType : nullptr; @@ -284,7 +284,7 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) pExt->UpdateTintValues(); if (pTypeExt->Harvester_Counted) - HouseExt::ExtMap.Find(pThis->Owner)->OwnedCountedHarvesters.push_back(pThis); + HouseExt::Fetch(pThis->Owner)->OwnedCountedHarvesters.push_back(pThis); if (!(pThis->Owner->IsControlledByHuman() && RulesExt::Global()->DistributeTargetingFrame_AIOnly) && pTypeExt->DistributeTargetingFrame.Get(RulesExt::Global()->DistributeTargetingFrame)) @@ -302,7 +302,7 @@ DEFINE_HOOK(0x6F421C, TechnoClass_Init_DefaultDisguise, 0x6) { GET(TechnoClass*, pThis, ESI); - auto const pExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + auto const pExt = TechnoTypeExt::Fetch(pThis->GetTechnoType()); // mirage is not here yet if (pThis->WhatAmI() == AbstractType::Infantry && pExt->DefaultDisguise) @@ -325,7 +325,7 @@ DEFINE_HOOK(0x414057, TechnoClass_Init_InitialStrength, 0x6) // AircraftCl { GET(TechnoClass*, pThis, ESI); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + auto const pTypeExt = TechnoTypeExt::Fetch(pThis->GetTechnoType()); if (R->Origin() != 0x517D69) { @@ -352,7 +352,7 @@ DEFINE_HOOK(0x6F6AC4, TechnoClass_Limbo, 0x5) { GET(TechnoClass*, pThis, ECX); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->Shield) pExt->Shield->KillAnim(); @@ -369,7 +369,7 @@ static bool __fastcall TechnoClass_Limbo_Wrapper(TechnoClass* pThis) return pThis->TechnoClass::Limbo(); } - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); bool markForRedraw = false; bool altered = false; std::vector>::iterator it; @@ -438,7 +438,7 @@ DEFINE_HOOK(0x708AEB, TechnoClass_ReplaceArmorWithShields, 0x6) //TechnoClass_Sh else pTarget = R->EBP(); - if (const auto pExt = TechnoExt::ExtMap.TryFind(abstract_cast(pTarget))) + if (const auto pExt = TechnoExt::TryFetch(abstract_cast(pTarget))) { if (const auto pShieldData = pExt->Shield.get()) { @@ -463,7 +463,7 @@ DEFINE_HOOK(0x4DB218, FootClass_GetMovementSpeed_SpeedMultiplier, 0x6) GET(FootClass*, pThis, ESI); GET(int, speed, EAX); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); speed = static_cast(speed * pExt->AE.SpeedMultiplier); R->EAX(speed); @@ -472,7 +472,7 @@ DEFINE_HOOK(0x4DB218, FootClass_GetMovementSpeed_SpeedMultiplier, 0x6) double TechnoExt::CalculateArmorMultipliers(TechnoClass* pThis, WarheadTypeClass* pWarhead) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); double mult = pExt->AE.ArmorMultiplier; if (pExt->AE.HasRestrictedArmorMultipliers) @@ -532,7 +532,7 @@ DEFINE_HOOK(0x6FE352, TechnoClass_FirepowerMultiplier, 0x8) // TechnoClass GET(TechnoClass*, pThis, ESI); GET(int, damage, EAX); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); damage = static_cast(damage * pExt->AE.FirepowerMultiplier); R->EAX(damage); @@ -607,7 +607,7 @@ DEFINE_HOOK(0x71067B, TechnoClass_EnterTransport_LaserTrails, 0x7) { GET(TechnoClass*, pTechno, EDI); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTechnoExt = TechnoExt::Fetch(pTechno); for (const auto& pTrail : pTechnoExt->LaserTrails) { @@ -623,7 +623,7 @@ DEFINE_HOOK(0x4D7221, FootClass_Unlimbo_LaserTrails, 0x6) { GET(FootClass*, pTechno, ESI); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTechnoExt = TechnoExt::Fetch(pTechno); for (const auto& pTrail : pTechnoExt->LaserTrails) { @@ -646,7 +646,7 @@ DEFINE_HOOK(0x4DEAEE, FootClass_IronCurtain_Organics, 0x6) if (!pType->Organic && pThis->WhatAmI() != AbstractType::Infantry) return MakeInvulnerable; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); const IronCurtainEffect icEffect = !isForceShield ? pTypeExt->IronCurtain_Effect.Get(RulesExt::Global()->IronCurtain_EffectOnOrganics) : pTypeExt->ForceShield_Effect.Get(RulesExt::Global()->ForceShield_EffectOnOrganics); @@ -682,21 +682,21 @@ DEFINE_HOOK(0x700C58, TechnoClass_CanPlayerMove_NoManualMove, 0x6) { GET(TechnoClass*, pThis, ESI); - return TechnoExt::ExtMap.Find(pThis)->TypeExtData->NoManualMove ? 0x700C62 : 0; + return TechnoExt::Fetch(pThis)->TypeExtData->NoManualMove ? 0x700C62 : 0; } DEFINE_HOOK(0x4437B3, BuildingClass_CellClickedAction_NoManualMove, 0x6) { GET(BuildingTypeClass*, pType, EDX); - return TechnoTypeExt::ExtMap.Find(pType)->NoManualMove ? 0x44384E : 0; + return TechnoTypeExt::Fetch(pType)->NoManualMove ? 0x44384E : 0; } DEFINE_HOOK(0x44F62B, BuildingClass_CanPlayerMove_NoManualMove, 0x6) { GET(BuildingTypeClass*, pType, EDX); - R->ECX(TechnoTypeExt::ExtMap.Find(pType)->NoManualMove ? 0 : pType->UndeploysInto); + R->ECX(TechnoTypeExt::Fetch(pType)->NoManualMove ? 0 : pType->UndeploysInto); return 0x44F631; } @@ -709,7 +709,7 @@ DEFINE_HOOK(0x70EFE0, TechnoClass_GetMaxSpeed, 0x6) GET(TechnoClass*, pThis, ECX); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const pThisType = pTypeExt->OwnerObject(); int maxSpeed = pThisType->Speed; @@ -729,7 +729,7 @@ DEFINE_HOOK(0x73B4DA, UnitClass_DrawVXL_WaterType_Extra, 0x6) GET(UnitClass*, pThis, EBP); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); if (pTypeExt->NeedDamagedImage && pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer) && !pThis->Deployed) { @@ -747,7 +747,7 @@ DEFINE_HOOK(0x73C602, UnitClass_DrawSHP_WaterType_Extra, 0x6) GET(UnitClass*, pThis, EBP); const auto pType = pThis->Type; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); if (pTypeExt->NeedDamagedImage && pThis->IsClearlyVisibleTo(HouseClass::CurrentPlayer) && !pThis->Deployed) { @@ -790,7 +790,7 @@ DEFINE_HOOK(0x521D94, InfantryClass_CurrentSpeed_ProneSpeed, 0x6) GET(int, currentSpeed, ECX); const auto pType = pThis->Type; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); const double multiplier = pTypeExt->ProneSpeed.Get(pType->Crawls ? RulesExt::Global()->ProneSpeed_Crawls : RulesExt::Global()->ProneSpeed_NoCrawls); currentSpeed = static_cast(static_cast(currentSpeed) * multiplier); R->ECX(currentSpeed); @@ -801,7 +801,7 @@ DEFINE_HOOK_AGAIN(0x6A343F, LocomotionClass_Process_DamagedSpeedMultiplier, 0x6) DEFINE_HOOK(0x4B3DF0, LocomotionClass_Process_DamagedSpeedMultiplier, 0x6)// Drive { GET(FootClass*, pLinkedTo, ECX); - const auto pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; const double multiplier = pTypeExt->DamagedSpeed.Get(RulesExt::Global()->DamagedSpeed); __asm fmul multiplier; @@ -815,7 +815,7 @@ DEFINE_HOOK(0x62A0AA, ParasiteClass_AI_CullingTarget, 0x5) GET(ParasiteClass*, pThis, ESI); GET(WarheadTypeClass*, pWarhead, EBP); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWarhead); return EnumFunctions::IsTechnoEligible(pThis->Victim, pWHExt->Parasite_CullingTarget) ? ExecuteCulling : CannotCulling; } @@ -826,7 +826,7 @@ DEFINE_HOOK(0x62A0D3, ParasiteClass_AI_ParticleSystem, 0x5) //GET(ParasiteClass*, pThis, ESI); GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x4C, -0x2C)); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWarhead); if (pWHExt->Parasite_DisableParticleSystem) return SkipGameCode; @@ -845,7 +845,7 @@ DEFINE_HOOK(0x6298CC, ParasiteClass_AI_GrippleAnim, 0x5) enum { SkipGameCode = 0x6298D6 }; GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x68, -0x4C)); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWarhead); R->EAX(pWHExt->Parasite_GrappleAnim.Get(RulesExt::Global()->Parasite_GrappleAnim.isset() ? RulesExt::Global()->Parasite_GrappleAnim.Get() : AnimTypeClass::FindIndex("SQDG"))); return SkipGameCode; @@ -857,7 +857,7 @@ DEFINE_HOOK(0x62AB69, ParasiteClass_CanExistOnVictimCell_AllowWaterExit, 0x6) GET(TechnoTypeClass*, pType, EAX); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); const auto& globalVal = RulesExt::Global()->Parasite_AllowWaterExit; if (pTypeExt->Parasite_AllowWaterExit.isset()) @@ -880,7 +880,7 @@ DEFINE_HOOK(0x655DDD, RadarClass_ProcessPoint_RadarInvisible, 0x6) if (isInShrouded && !pTechno->Owner->IsControlledByCurrentPlayer()) return Invisible; - auto const pTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; if (pTypeExt->OwnerObject()->RadarInvisible && EnumFunctions::CanTargetHouse(pTypeExt->RadarInvisibleToHouse.Get(AffectedHouse::Enemies), pTechno->Owner, HouseClass::CurrentPlayer)) @@ -903,7 +903,7 @@ DEFINE_HOOK(0x514C07, HoverLocomotionClass_Process_HoverShutdown, 0x5) const auto pTechno = pThis->Owner; pTechno->DropAsBomb(); - TechnoExt::ExtMap.Find(pTechno)->HoverShutdown = true; + TechnoExt::Fetch(pTechno)->HoverShutdown = true; return SkipGameCode; } @@ -918,7 +918,7 @@ DEFINE_HOOK(0x5F4021, ObjectClass_Update_FallingDown_ToDead, 0x6) if (const auto pTechno = abstract_cast(pThis)) { - const auto pExt = TechnoExt::ExtMap.Find(pTechno); + const auto pExt = TechnoExt::Fetch(pTechno); const bool onParachuted = pExt->OnParachuted; pExt->OnParachuted = false; @@ -958,7 +958,7 @@ DEFINE_HOOK(0x5F4021, ObjectClass_Update_FallingDown_ToDead, 0x6) if (!onParachuted) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); if (!pTypeExt->FallingDownDamage_AllowEMP && pTechno->EMPLockRemaining > 0) { @@ -1038,7 +1038,7 @@ DEFINE_HOOK(0x4DF4A5, FootClass_UpdateAttackMove_SetTarget, 0x6) { GET(FootClass*, pThis, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (R->Origin() != 0x4DF4A5) { @@ -1068,7 +1068,7 @@ DEFINE_HOOK(0x6FCF3E, TechnoClass_SetTarget_After, 0x6) if (pThis->LocomotorTarget != pTarget) pThis->ReleaseLocomotor(true); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pExt->UpdateGattlingRateDownReset(); if (!pTarget) @@ -1099,7 +1099,7 @@ DEFINE_HOOK(0x6FABC4, TechnoClass_AI_AnimationPaused, 0x6) GET(TechnoClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); if (pExt->DelayedFireSequencePaused) return SkipGameCode; @@ -1118,8 +1118,8 @@ DEFINE_HOOK(0x519FEC, InfantryClass_UpdatePosition_EngineerRepair, 0xA) pTarget->Mark(MarkType::Change); const auto pTargetType = pTarget->Type; - const int repairBuilding = TechnoTypeExt::ExtMap.Find(pTargetType)->EngineerRepairAmount; - const int repairEngineer = TechnoTypeExt::ExtMap.Find(pThis->Type)->EngineerRepairAmount; + const int repairBuilding = TechnoTypeExt::Fetch(pTargetType)->EngineerRepairAmount; + const int repairEngineer = TechnoTypeExt::Fetch(pThis->Type)->EngineerRepairAmount; const int strength = pTargetType->Strength; auto repair = [strength, pTarget](int repair) @@ -1151,7 +1151,7 @@ DEFINE_HOOK(0x519FEC, InfantryClass_UpdatePosition_EngineerRepair, 0xA) pTarget->DamageParticleSystem->UnInit(); } - VocClass::PlayAt(BuildingTypeExt::ExtMap.Find(pTargetType)->BuildingRepairedSound.Get(RulesClass::Instance->BuildingRepairedSound), pTarget->GetCoords()); + VocClass::PlayAt(BuildingTypeExt::Fetch(pTargetType)->BuildingRepairedSound.Get(RulesClass::Instance->BuildingRepairedSound), pTarget->GetCoords()); return SkipGameCode; } @@ -1161,7 +1161,7 @@ DEFINE_HOOK(0x4DF410, FootClass_UpdateAttackMove_TargetAcquired, 0x6) { GET(FootClass* const, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pThis->IsCloseEnoughToAttack(pThis->Target) && pTypeExt->AttackMove_StopWhenTargetAcquired.Get(RulesExt::Global()->AttackMove_StopWhenTargetAcquired.Get(!pTypeExt->OwnerObject()->OpportunityFire))) @@ -1195,7 +1195,7 @@ DEFINE_HOOK(0x4DF4DB, TechnoClass_RefreshMegaMission_CheckMissionFix, 0xA) GET(FootClass* const, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const mission = pThis->GetCurrentMission(); const bool stopWhenTargetAcquired = pTypeExt->AttackMove_StopWhenTargetAcquired.Get(RulesExt::Global()->AttackMove_StopWhenTargetAcquired.Get(!pTypeExt->OwnerObject()->OpportunityFire)); bool clearMegaMission = mission != Mission::Guard; @@ -1242,7 +1242,7 @@ DEFINE_HOOK(0x4DF3A6, FootClass_UpdateAttackMove_Follow, 0x6) pThis->ContinueMegaMission(); } - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pTypeExt->AttackMove_Follow || (pTypeExt->AttackMove_Follow_IfMindControlIsFull && pThis->CaptureManager && pThis->CaptureManager->CannotControlAnyMore())) { @@ -1260,7 +1260,7 @@ DEFINE_HOOK(0x4DF3A6, FootClass_UpdateAttackMove_Follow, 0x6) && pTechno != pThis && pTechno->Owner == pOwner && pTechno->MegaMissionIsAttackMove()) { - auto const pTargetExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTargetExt = TechnoExt::Fetch(pTechno); // Check this to prevent the followed techno from being surrounded if (pTargetExt->AttackMoveFollowerTempCount >= 6) @@ -1283,7 +1283,7 @@ DEFINE_HOOK(0x4DF3A6, FootClass_UpdateAttackMove_Follow, 0x6) if (pClosestTarget) { - auto const pTargetExt = TechnoExt::ExtMap.Find(pClosestTarget); + auto const pTargetExt = TechnoExt::Fetch(pClosestTarget); pTargetExt->AttackMoveFollowerTempCount += pThis->WhatAmI() == AbstractType::Infantry ? 1 : 3; pThis->SetDestination(pClosestTarget, false); pThis->SetArchiveTarget(pClosestTarget); @@ -1324,7 +1324,7 @@ DEFINE_HOOK(0x708FC0, TechnoClass_ResponseMove_Pickup, 0x5) if (pType->Carryall && pAircraft->HasAnyLink() && generic_cast(pAircraft->Destination)) { - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (pTypeExt->VoicePickup.isset()) { @@ -1379,7 +1379,7 @@ DEFINE_HOOK(0x71A8BD, TemporalClass_Update_WarpAwayAnim, 0x5) // Target must exist here auto const pTarget = pThis->Target; - auto const pExt = TechnoExt::ExtMap.Find(pTarget)->TypeExtData; + auto const pExt = TechnoExt::Fetch(pTarget)->TypeExtData; if (pExt->WarpAway.size() > 0) { @@ -1406,7 +1406,7 @@ DEFINE_HOOK(0x70023B, TechnoClass_MouseOverObject_AttackUnderGround, 0x5) auto const pWeapon = pThis->GetWeapon(wpIdx)->WeaponType; - return (!pWeapon || !BulletTypeExt::ExtMap.Find(pWeapon->Projectile)->AU) ? FireIsNotOK : FireIsOK; + return (!pWeapon || !BulletTypeExt::Fetch(pWeapon->Projectile)->AU) ? FireIsNotOK : FireIsOK; } DEFINE_HOOK_AGAIN(0x729029, TunnelLocomotionClass_Process_Track, 0x7); @@ -1418,7 +1418,7 @@ DEFINE_HOOK(0x728F9A, TunnelLocomotionClass_Process_Track, 0x7) const auto pLoco = static_cast(pThis); const auto pTechno = pLoco->LinkedTo; ScenarioExt::Global()->UndergroundTracker.AddUnique(pTechno); - TechnoExt::ExtMap.Find(pTechno)->UndergroundTracked = true; + TechnoExt::Fetch(pTechno)->UndergroundTracked = true; return 0; } @@ -1428,7 +1428,7 @@ DEFINE_HOOK(0x7297F6, TunnelLocomotionClass_ProcessDigging_Track, 0x7) GET(FootClass*, pTechno, ECX); ScenarioExt::Global()->UndergroundTracker.Remove(pTechno); - TechnoExt::ExtMap.Find(pTechno)->UndergroundTracked = false; + TechnoExt::Fetch(pTechno)->UndergroundTracked = false; return 0; } @@ -1438,7 +1438,7 @@ DEFINE_HOOK(0x772AB3, WeaponTypeClass_AllowedThreats_AU, 0x5) GET(BulletTypeClass* const, pType, ECX); GET(const ThreatType, flags, EAX); - if (BulletTypeExt::ExtMap.Find(pType)->AU) + if (BulletTypeExt::Fetch(pType)->AU) R->EAX(static_cast(flags) | 0x20000u); return 0; @@ -1545,7 +1545,7 @@ DEFINE_HOOK(0x6F7E1E, TechnoClass_CanAutoTargetObject_AU, 0x6) GET(TechnoClass*, pTarget, ESI); GET(const int, height, EAX); - return height >= -20 || SelectAutoTarget_Context::AU || TechnoExt::ExtMap.Find(pTarget)->SpecialTracked ? Continue : ReturnFalse; + return height >= -20 || SelectAutoTarget_Context::AU || TechnoExt::Fetch(pTarget)->SpecialTracked ? Continue : ReturnFalse; } #pragma endregion @@ -1562,7 +1562,7 @@ DEFINE_HOOK(0x5F4160, ObjectClass_DropAsBomb_Track, 0x6) if ((pThis->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { ScenarioExt::Global()->FallingDownTracker.AddUnique(pThis); - TechnoExt::ExtMap.Find(pThis)->FallingDownTracked = true; + TechnoExt::Fetch(pThis)->FallingDownTracked = true; } return 0; @@ -1578,7 +1578,7 @@ DEFINE_HOOK(0x5F5965, ObjectClass_SpawnParachuted_Track, 0x7) if ((pThis->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { ScenarioExt::Global()->FallingDownTracker.AddUnique(pThis); - TechnoExt::ExtMap.Find(pThis)->FallingDownTracked = true; + TechnoExt::Fetch(pThis)->FallingDownTracked = true; } return 0; @@ -1594,7 +1594,7 @@ DEFINE_HOOK(0x5F3F86, ObjectClass_Update_Track, 0x7) if ((pThis->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { ScenarioExt::Global()->FallingDownTracker.Remove(pThis); - TechnoExt::ExtMap.Find(pThis)->FallingDownTracked = false; + TechnoExt::Fetch(pThis)->FallingDownTracked = false; } return 0; @@ -1664,7 +1664,7 @@ DEFINE_HOOK(0x6FBFA3, TechnoClass_Select_SkipLimboDelivery, 0x6) if (auto const pBuilding = abstract_cast(pThis)) { - const auto& limboDelivereds = HouseExt::ExtMap.Find(pBuilding->Owner)->OwnedLimboDeliveredBuildings; + const auto& limboDelivereds = HouseExt::Fetch(pBuilding->Owner)->OwnedLimboDeliveredBuildings; const auto vectorEnd = limboDelivereds.end(); if (std::find(limboDelivereds.begin(), vectorEnd, pBuilding) != vectorEnd) @@ -1687,7 +1687,7 @@ DEFINE_HOOK(0x700358, TechnoClass_MouseOverObject_AttackFriendlies, 0x6) GET_STACK(const bool, IvanBomb, STACK_OFFSET(0x1C, -0xC)); const auto pType = pThis->GetTechnoType(); - const auto pWeaponTypeExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponTypeExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponTypeExt->AttackFriendlies.Get(pType->AttackFriendlies) || (pWeaponTypeExt->AttackCursorOnFriendlies.Get(pType->AttackCursorOnFriendlies) && !IvanBomb)) @@ -1706,7 +1706,7 @@ DEFINE_HOOK(0x6F8A92, TechnoClass_CheckAutoTarget_AttackFriendlies, 0xA) { GET(TechnoClass*, pThis, ESI); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; R->CL(pThis->Veterancy.IsElite() ? pTypeExt->AttackFriendlies.Y : pTypeExt->AttackFriendlies.X); return R->Origin() + 0x10; @@ -1714,8 +1714,8 @@ DEFINE_HOOK(0x6F8A92, TechnoClass_CheckAutoTarget_AttackFriendlies, 0xA) namespace CanAutoTargetTemp { - TechnoTypeExt::ExtData* TypeExtData = nullptr; - WeaponTypeExt::ExtData* WeaponExt = nullptr; + TechnoTypeExt* TypeExtData = nullptr; + WeaponTypeExt* WeaponExt = nullptr; } DEFINE_HOOK(0x6F7E30, TechnoClass_CanAutoTarget_SetContent, 0x6) @@ -1723,8 +1723,8 @@ DEFINE_HOOK(0x6F7E30, TechnoClass_CanAutoTarget_SetContent, 0x6) GET(TechnoClass*, pThis, EDI); GET(WeaponTypeClass*, pWeapon, EBP); - CanAutoTargetTemp::TypeExtData = TechnoExt::ExtMap.Find(pThis)->TypeExtData; - CanAutoTargetTemp::WeaponExt = WeaponTypeExt::ExtMap.TryFind(pWeapon); + CanAutoTargetTemp::TypeExtData = TechnoExt::Fetch(pThis)->TypeExtData; + CanAutoTargetTemp::WeaponExt = WeaponTypeExt::TryFetch(pWeapon); return 0; } @@ -1772,7 +1772,7 @@ static bool __fastcall FootClass_Paradrop(FootClass* pThis, void*, const CoordSt if (!pThis->ObjectClass::SpawnParachuted(coords)) return false; - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; Mission mission; @@ -1798,7 +1798,7 @@ DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5D58, FootClass_Paradrop) // Replace ObjectClas #pragma region GuardRange -static int GetMultiWeaponRange(TechnoClass* pThis, TechnoTypeExt::ExtData* pTypeExt) +static int GetMultiWeaponRange(TechnoClass* pThis, TechnoTypeExt* pTypeExt) { int range = -1; @@ -1824,7 +1824,7 @@ static int GetGuardRange(TechnoClass* pThis, int control) if (control == -1) return -1; - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const pType = pTypeExt->OwnerObject(); int range = pType->GuardRange; @@ -1912,7 +1912,7 @@ DEFINE_HOOK(0x6F90DE, TechnoClass_GreatestThreat_MultiWeapon, 0x6) GET(TechnoClass*, pThis, ESI); - if (int result = GetMultiWeaponRange(pThis, TechnoExt::ExtMap.Find(pThis)->TypeExtData); result != -1) + if (int result = GetMultiWeaponRange(pThis, TechnoExt::Fetch(pThis)->TypeExtData); result != -1) { R->EAX(result); return SkipGameCode; @@ -2024,7 +2024,7 @@ DEFINE_HOOK(0x4DA230, FootClass_CanBeRecruited, 0x5) DEFINE_HOOK(0x41AE00, AircraftClass_See_DynamicSight, 0x6) { GET(TechnoClass*, pThis, ESI); - R->EBX(TechnoExt::ExtMap.Find(pThis)->GetSight()); + R->EBX(TechnoExt::Fetch(pThis)->GetSight()); return R->Origin() + 0x6; } @@ -2032,21 +2032,21 @@ DEFINE_HOOK_AGAIN(0x440819, BuildingClass_Unlimbo_DynamicSight, 0x6); DEFINE_HOOK(0x440842, BuildingClass_Unlimbo_DynamicSight, 0x6) { GET(TechnoClass*, pThis, ESI); - R->EDX(TechnoExt::ExtMap.Find(pThis)->GetSight()); + R->EDX(TechnoExt::Fetch(pThis)->GetSight()); return R->Origin() + 0x6; } DEFINE_HOOK(0x51E0E5, InfantryClass_Unlimbo_DynamicSight, 0x6) { GET(TechnoClass*, pThis, EDI); - R->EAX(TechnoExt::ExtMap.Find(pThis)->GetSight()); + R->EAX(TechnoExt::Fetch(pThis)->GetSight()); return R->Origin() + 0x6; } DEFINE_HOOK(0x702B14, TechnoClass_ReceiveDamage_DynamicSight, 0x6) { GET(TechnoClass*, pThis, ESI); - const int sight = TechnoExt::ExtMap.Find(pThis)->GetSight(); + const int sight = TechnoExt::Fetch(pThis)->GetSight(); __asm fild sight; return 0x702B1A; } @@ -2054,21 +2054,21 @@ DEFINE_HOOK(0x702B14, TechnoClass_ReceiveDamage_DynamicSight, 0x6) DEFINE_HOOK(0x70AE48, TechnoClass_See_DynamicSight, 0x6) { GET(TechnoClass*, pThis, ESI); - R->EAX((int)(TechnoExt::ExtMap.Find(pThis)->GetSight() * (pThis->SightIncrease * 0.01 + 1.0))); + R->EAX((int)(TechnoExt::Fetch(pThis)->GetSight() * (pThis->SightIncrease * 0.01 + 1.0))); return 0x70AE69; } DEFINE_HOOK(0x70AF87, TechnoClass_UpdateSight_DynamicSight1, 0x6) { GET(TechnoClass*, pThis, ESI); - R->ECX(TechnoExt::ExtMap.Find(pThis)->GetSight()); + R->ECX(TechnoExt::Fetch(pThis)->GetSight()); return R->Origin() + 0x6; } DEFINE_HOOK(0x70AFEF, TechnoClass_UpdateSight_DynamicSight2, 0x6) { GET(TechnoClass*, pThis, ESI); - R->EAX((int)(TechnoExt::ExtMap.Find(pThis)->GetSight() * (pThis->SightIncrease * 0.01 + 1.0))); + R->EAX((int)(TechnoExt::Fetch(pThis)->GetSight() * (pThis->SightIncrease * 0.01 + 1.0))); return 0x70B010; } @@ -2077,7 +2077,7 @@ DEFINE_HOOK(0x70AFEF, TechnoClass_UpdateSight_DynamicSight2, 0x6) static AnimTypeClass* GetLandingAnim(TechnoClass* pTechno) { auto const pType = pTechno->GetTechnoType(); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (pTypeExt->LandingAnim.isset()) return pTypeExt->LandingAnim.Get(); @@ -2104,7 +2104,7 @@ DEFINE_HOOK(0x4CEB59, FlyLocomotionClass_ProcessLanding_ForceDropship, 0x6) { GET(FlyLocomotionClass*, pLoco, ESI); auto const pType = pLoco->LinkedTo->GetTechnoType(); - const bool force = TechnoTypeExt::ExtMap.Find(pType)->LandingAnim.isset() || RulesExt::Global()->DefaultLandingAnim != nullptr; + const bool force = TechnoTypeExt::Fetch(pType)->LandingAnim.isset() || RulesExt::Global()->DefaultLandingAnim != nullptr; R->CL(force || pType->IsDropship); return 0x4CEB5F; @@ -2131,7 +2131,7 @@ DEFINE_HOOK(0x4CF8B1, FlyLocomotionClass_Draw_Point_NoWobbles, 0x6) enum { Continue = 0x4CF8B7 }; GET(TechnoTypeClass*, pType, EAX); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); R->CL(pTypeExt->FlyNoWobbles.Get(RulesExt::Global()->FlyNoWobbles.Get(pType->IsDropship))); return Continue; @@ -2148,7 +2148,7 @@ DEFINE_HOOK(0x662354, RocketLocomotionClass_Process_CruiseMissileCheck, 0x6) if (!pLinkedTo) return 0; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pLinkedTo->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pLinkedTo->Type); if (pTypeExt->Missile_Cruise) return 0x662369; @@ -2164,7 +2164,7 @@ DEFINE_HOOK(0x6623FC, RocketLocomotionClass_Process_CustomSmokeInterval, 0x5) if (!pLinkedTo) return 0; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pLinkedTo->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pLinkedTo->Type); R->ECX(pTypeExt->Missile_TakeOffSeparation); return 0x662401; @@ -2184,7 +2184,7 @@ DEFINE_HOOK(0x6624FB, RocketLocomotionClass_Process_CustomMissileTakeoff, 0x5) if (pLoco->TrailerTimer.HasTimeLeft()) return SkipAnimation; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pLinkedTo->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pLinkedTo->Type); if (pTypeExt->Missile_TakeOffAnim) { @@ -2205,7 +2205,7 @@ DEFINE_HOOK(0x662720, RocketLocomotionClass_Process_CruiseMissileRaise, 0x6) if (!pLinkedTo) return 0; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pLinkedTo->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pLinkedTo->Type); if (pTypeExt->Missile_Cruise) return 0x6624C8; @@ -2254,7 +2254,7 @@ int WarpPerStep::TemporalClassFake::_GetWarpPerStep(int helperCount) if (const auto pWarhead = pWeapon->Warhead) { - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWarhead); const bool applyMultiplier = pWHExt->Temporal_ApplyMultiplier.Get(RulesExt::Global()->Temporal_ApplyMultiplier); if (applyMultiplier) diff --git a/src/Ext/Techno/WeaponHelpers.cpp b/src/Ext/Techno/WeaponHelpers.cpp index 64ea5740ae..017e784313 100644 --- a/src/Ext/Techno/WeaponHelpers.cpp +++ b/src/Ext/Techno/WeaponHelpers.cpp @@ -17,7 +17,7 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A return weaponIndexTwo; auto const pWeaponTwo = pWeaponStructTwo->WeaponType; - auto const pSecondExt = WeaponTypeExt::ExtMap.Find(pWeaponTwo); + auto const pSecondExt = WeaponTypeExt::Fetch(pWeaponTwo); CellClass* pTargetCell = nullptr; @@ -49,7 +49,7 @@ int TechnoExt::PickWeaponIndex(TechnoClass* pThis, TechnoClass* pTargetTechno, A } const bool secondIsAA = pTargetTechno && pTargetTechno->IsInAir() && pWeaponTwo->Projectile->AA; - auto const pFirstExt = WeaponTypeExt::ExtMap.Find(pWeaponStructOne->WeaponType); + auto const pFirstExt = WeaponTypeExt::Fetch(pWeaponStructOne->WeaponType); const bool skipPrimaryPicking = pFirstExt->SkipWeaponPicking; const bool firstAllowedAE = skipPrimaryPicking || pFirstExt->HasRequiredAttachedEffects(pTargetTechno, pThis); @@ -108,7 +108,7 @@ bool TechnoExt::CanFireNoAmmoWeapon(TechnoClass* pThis, TechnoTypeClass* pType, { if (pType->Ammo > 0) { - auto const pExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pExt = TechnoTypeExt::Fetch(pType); if (pThis->Ammo <= pExt->NoAmmoAmount && (pExt->NoAmmoWeapon == weaponIndex || pExt->NoAmmoWeapon == -1)) return true; @@ -128,7 +128,7 @@ WeaponTypeClass* TechnoExt::GetDeployFireWeapon(TechnoClass* pThis, TechnoTypeCl if (pThis->WhatAmI() == AbstractType::Unit) { - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); // Only apply DeployFireWeapon on vehicles if explicitly set. if (!pTypeExt->DeployFireWeapon.isset()) @@ -195,7 +195,7 @@ WeaponTypeClass* TechnoExt::GetCurrentWeapon(TechnoClass* pThis, TechnoTypeClass // Gets weapon index for a weapon to use against wall overlay. int TechnoExt::GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* pWallOverlayType) { - auto const pTechnoTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTechnoTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto const pTechnoType = pTechnoTypeExt->OwnerObject(); int weaponIndex = -1; auto pWeapon = TechnoExt::GetCurrentWeapon(pThis, pTechnoType, weaponIndex); @@ -205,14 +205,14 @@ int TechnoExt::GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* p else if (weaponIndex == -1) return 0; - auto pWeaponExt = WeaponTypeExt::ExtMap.TryFind(pWeapon); + auto pWeaponExt = WeaponTypeExt::TryFetch(pWeapon); const bool aeForbidsPrimary = pWeaponExt && !pWeaponExt->SkipWeaponPicking && pWeaponExt->AttachEffect_CheckOnFirer && !pWeaponExt->HasRequiredAttachedEffects(pThis, pThis); if (!pWeapon || (!pWeapon->Warhead->Wall && (!pWeapon->Warhead->Wood || pWallOverlayType->Armor != Armor::Wood)) || TechnoExt::CanFireNoAmmoWeapon(pThis, 1) || aeForbidsPrimary) { int weaponIndexSec = -1; pWeapon = TechnoExt::GetCurrentWeapon(pThis, pTechnoType, weaponIndexSec, true); - pWeaponExt = WeaponTypeExt::ExtMap.TryFind(pWeapon); + pWeaponExt = WeaponTypeExt::TryFetch(pWeapon); const bool aeForbidsSecondary = pWeaponExt && !pWeaponExt->SkipWeaponPicking && pWeaponExt->AttachEffect_CheckOnFirer && !pWeaponExt->HasRequiredAttachedEffects(pThis, pThis); if (pWeapon && (pWeapon->Warhead->Wall || (pWeapon->Warhead->Wood && pWallOverlayType->Armor == Armor::Wood)) @@ -229,8 +229,8 @@ int TechnoExt::GetWeaponIndexAgainstWall(TechnoClass* pThis, OverlayTypeClass* p void TechnoExt::ApplyKillWeapon(TechnoClass* pThis, TechnoClass* pSource, WarheadTypeClass* pWH) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; + auto const pWHExt = WarheadTypeExt::Fetch(pWH); const bool hasFilters = pTypeExt->SuppressKillWeapons_Types.size() > 0; // KillWeapon can be triggered without the source @@ -252,9 +252,9 @@ void TechnoExt::ApplyKillWeapon(TechnoClass* pThis, TechnoClass* pSource, Warhea void TechnoExt::ApplyRevengeWeapon(TechnoClass* pThis, TechnoClass* pSource, WarheadTypeClass* pWH) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto const pTypeExt = pExt->TypeExtData; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); auto const pThisOwner = pThis->Owner; auto const pSourceOwner = pSource->Owner; auto const& suppressType = pWHExt->SuppressRevengeWeapons_Types; @@ -294,7 +294,7 @@ void TechnoExt::ApplyRevengeWeapon(TechnoClass* pThis, TechnoClass* pSource, War } } -int TechnoExt::ExtData::ApplyForceWeaponInRange(AbstractClass* pTarget) +int TechnoExt::ApplyForceWeaponInRange(AbstractClass* pTarget) { int forceWeaponIndex = -1; auto const pThis = this->OwnerObject(); @@ -380,7 +380,7 @@ bool TechnoExt::MultiWeaponCanFire(TechnoClass* const pThis, AbstractClass* cons } else { - if (BulletTypeExt::ExtMap.Find(pBulletType)->AAOnly.Get()) + if (BulletTypeExt::Fetch(pBulletType)->AAOnly.Get()) { return false; } @@ -410,7 +410,7 @@ bool TechnoExt::MultiWeaponCanFire(TechnoClass* const pThis, AbstractClass* cons pTargetCell = pCell; } - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeaponType); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeaponType); if (!pWeaponExt->SkipWeaponPicking) { @@ -459,10 +459,10 @@ bool TechnoExt::MultiWeaponCanFire(TechnoClass* const pThis, AbstractClass* cons if (pWH->Airstrike) { - if (!EnumFunctions::IsTechnoEligible(pTechno, WarheadTypeExt::ExtMap.Find(pWH)->AirstrikeTargets)) + if (!EnumFunctions::IsTechnoEligible(pTechno, WarheadTypeExt::Fetch(pWH)->AirstrikeTargets)) return false; - const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + const auto pTechnoTypeExt = TechnoTypeExt::Fetch(pTechnoType); if (pTechno->AbstractFlags & AbstractFlags::Foot) { diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index d868c25021..d87cd6ac9c 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -17,7 +17,7 @@ TechnoTypeExt::ExtContainer TechnoTypeExt::ExtMap; bool TechnoTypeExt::SelectWeaponMutex = false; -void TechnoTypeExt::ExtData::Initialize() +void TechnoTypeExt::Initialize() { auto pThis = this->OwnerObject(); @@ -25,7 +25,7 @@ void TechnoTypeExt::ExtData::Initialize() this->Missile_TakeOffAnim = AnimTypeClass::Find("V3TAKOFF"); } -void TechnoTypeExt::ExtData::ApplyTurretOffset(Matrix3D* mtx, double factor) +void TechnoTypeExt::ApplyTurretOffset(Matrix3D* mtx, double factor) { // Does not verify if the offset actually has all values parsed as it makes no difference, it will be 0 for the unparsed ones either way. const auto offset = this->TurretOffset.GetEx(); @@ -36,7 +36,7 @@ void TechnoTypeExt::ExtData::ApplyTurretOffset(Matrix3D* mtx, double factor) mtx->Translate(x, y, z); } -int TechnoTypeExt::ExtData::SelectForceWeapon(TechnoClass* pThis, AbstractClass* pTarget) const +int TechnoTypeExt::SelectForceWeapon(TechnoClass* pThis, AbstractClass* pTarget) const { if (TechnoTypeExt::SelectWeaponMutex || !this->ForceWeapon_Check || !pTarget) // In theory, pTarget must exist return -1; @@ -78,7 +78,7 @@ int TechnoTypeExt::ExtData::SelectForceWeapon(TechnoClass* pThis, AbstractClass* && (!this->ForceWeapon_InRange.empty() || !this->ForceAAWeapon_InRange.empty())) { TechnoTypeExt::SelectWeaponMutex = true; - forceWeaponIndex = TechnoExt::ExtMap.Find(pThis)->ApplyForceWeaponInRange(pTarget); + forceWeaponIndex = TechnoExt::Fetch(pThis)->ApplyForceWeaponInRange(pTarget); TechnoTypeExt::SelectWeaponMutex = false; } @@ -128,7 +128,7 @@ int TechnoTypeExt::ExtData::SelectForceWeapon(TechnoClass* pThis, AbstractClass* return forceWeaponIndex; } -bool TechnoTypeExt::ExtData::IsSecondary(int nWeaponIndex) const +bool TechnoTypeExt::IsSecondary(int nWeaponIndex) const { const auto pThis = this->OwnerObject(); @@ -144,7 +144,7 @@ bool TechnoTypeExt::ExtData::IsSecondary(int nWeaponIndex) const return nWeaponIndex != 0; } -int TechnoTypeExt::ExtData::SelectMultiWeapon(TechnoClass* const pThis, AbstractClass* const pTarget) const +int TechnoTypeExt::SelectMultiWeapon(TechnoClass* const pThis, AbstractClass* const pTarget) const { if (!pTarget || !this->MultiWeapon) return -1; @@ -263,7 +263,7 @@ int TechnoTypeExt::ExtData::SelectMultiWeapon(TechnoClass* const pThis, Abstract } // Ares 0.A source -bool TechnoTypeExt::ExtData::CameoIsVeteran(HouseClass* pHouse) const +bool TechnoTypeExt::CameoIsVeteran(HouseClass* pHouse) const { const auto pThis = this->OwnerObject();; @@ -332,14 +332,14 @@ bool TechnoTypeExt::ExtData::CameoIsVeteran(HouseClass* pHouse) const return false; } -const char* TechnoTypeExt::ExtData::GetSelectionGroupID() const +const char* TechnoTypeExt::GetSelectionGroupID() const { return GeneralUtils::IsValidString(this->GroupAs) ? this->GroupAs : this->OwnerObject()->ID; } const char* TechnoTypeExt::GetSelectionGroupID(ObjectTypeClass* pType) { - if (const auto pExt = TechnoTypeExt::ExtMap.TryFind(static_cast(pType))) + if (const auto pExt = TechnoTypeExt::TryFetch(static_cast(pType))) return pExt->GetSelectionGroupID(); return pType->ID; @@ -352,7 +352,7 @@ bool TechnoTypeExt::HasSelectionGroupID(ObjectTypeClass* pType, const char* pID) return (_strcmpi(id, pID) == 0); } -void TechnoTypeExt::ExtData::ParseBurstFLHs(INI_EX& exArtINI, const char* pArtSection, +void TechnoTypeExt::ParseBurstFLHs(INI_EX& exArtINI, const char* pArtSection, std::vector>& nFLH, std::vector>& nEFlh, const char* pPrefixTag) { char tempBuffer[32]; @@ -390,7 +390,7 @@ void TechnoTypeExt::ExtData::ParseBurstFLHs(INI_EX& exArtINI, const char* pArtSe } } -void TechnoTypeExt::ExtData::ParseVoiceWeaponAttacks(INI_EX& exINI, const char* pSection, ValueableVector& voice, ValueableVector& voiceElite) +void TechnoTypeExt::ParseVoiceWeaponAttacks(INI_EX& exINI, const char* pSection, ValueableVector& voice, ValueableVector& voiceElite) { if (!this->ReadMultiWeapon) { @@ -434,7 +434,7 @@ void TechnoTypeExt::ExtData::ParseVoiceWeaponAttacks(INI_EX& exINI, const char* } } -void TechnoTypeExt::ExtData::UpdateAdditionalAttributes() +void TechnoTypeExt::UpdateAdditionalAttributes() { int num = 0; int eliteNum = 0; @@ -468,7 +468,7 @@ void TechnoTypeExt::ExtData::UpdateAdditionalAttributes() eliteNum++; if (!this->AttackFriendlies.Y - && WeaponTypeExt::ExtMap.Find(pWeapon)->AttackFriendlies.Get(false)) + && WeaponTypeExt::Fetch(pWeapon)->AttackFriendlies.Get(false)) { this->AttackFriendlies.Y = true; } @@ -482,7 +482,7 @@ void TechnoTypeExt::ExtData::UpdateAdditionalAttributes() num++; if (!this->AttackFriendlies.X - && WeaponTypeExt::ExtMap.Find(pWeapon)->AttackFriendlies.Get(false)) + && WeaponTypeExt::Fetch(pWeapon)->AttackFriendlies.Get(false)) { this->AttackFriendlies.X = true; } @@ -508,7 +508,7 @@ void TechnoTypeExt::ExtData::UpdateAdditionalAttributes() this->CombatDamages.Y /= eliteNum; } -void TechnoTypeExt::ExtData::CalculateSpawnerRange() +void TechnoTypeExt::CalculateSpawnerRange() { const auto pThis = this->OwnerObject(); const int weaponRangeExtra = this->Spawner_ExtraLimitRange * Unsorted::LeptonsPerCell; @@ -663,7 +663,7 @@ TechnoClass* TechnoTypeExt::CreateUnit(CreateUnitTypeClass* pCreateUnit, DirType // Order BalloonHover jumpjets to ascend. pJJLoco->IsMoving = true; pJJLoco->DestinationCoords = pTechno->GetCoords(); - TechnoExt::ExtMap.Find(pTechno)->JumpjetStraightAscend = true; + TechnoExt::Fetch(pTechno)->JumpjetStraightAscend = true; } else if (inAir) { @@ -674,7 +674,7 @@ TechnoClass* TechnoTypeExt::CreateUnit(CreateUnitTypeClass* pCreateUnit, DirType else if (inAir && !parachuted) { pTechno->IsFallingDown = true; - TechnoExt::ExtMap.Find(pTechno)->OnParachuted = true; + TechnoExt::Fetch(pTechno)->OnParachuted = true; } } @@ -722,7 +722,7 @@ WeaponTypeClass* TechnoTypeExt::GetWeaponType(TechnoTypeClass* pThis, int weapon // ============================= // load / save -void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void TechnoTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -1473,7 +1473,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->ParseVoiceWeaponAttacks(exINI, pSection, this->VoiceWeaponAttacks, this->VoiceEliteWeaponAttacks); } -void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection) +void TechnoTypeExt::LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection) { AbstractType abs = this->OwnerObject()->WhatAmI(); @@ -1497,7 +1497,7 @@ void TechnoTypeExt::ExtData::LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSe } template -void TechnoTypeExt::ExtData::Serialize(T& Stm) +void TechnoTypeExt::Serialize(T& Stm) { Stm .Process(this->HealthBar_Hide) @@ -1988,15 +1988,15 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->Missile_TakeOffSeparation) ; } -void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TechnoTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TechnoTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TechnoTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -2006,18 +2006,18 @@ void TechnoTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) TechnoTypeExt::ExtContainer::ExtContainer() : Container("TechnoTypeClass") { } TechnoTypeExt::ExtContainer::~ExtContainer() = default; -TechnoTypeExt::ExtData* TechnoTypeExt::ExtContainer::CreateExtData(AbstractType tag, TechnoTypeClass* pOwner) const +TechnoTypeExt* TechnoTypeExt::ExtContainer::CreateExtData(AbstractType tag, TechnoTypeClass* pOwner) const { switch (tag) { case AbstractType::UnitType: - return new UnitTypeClassExtension(static_cast(pOwner)); + return new UnitTypeExt(static_cast(pOwner)); case AbstractType::InfantryType: - return new InfantryTypeClassExtension(static_cast(pOwner)); + return new InfantryTypeExt(static_cast(pOwner)); case AbstractType::AircraftType: - return new AircraftTypeClassExtension(static_cast(pOwner)); + return new AircraftTypeExt(static_cast(pOwner)); case AbstractType::BuildingType: - return new BuildingTypeExt::ExtData(static_cast(pOwner)); + return new BuildingTypeExt(static_cast(pOwner)); default: Debug::FatalErrorAndExit("TechnoTypeExt - unexpected extension tag %d in the save stream!\n", static_cast(tag)); return nullptr; @@ -2076,7 +2076,7 @@ DEFINE_HOOK(0x747E90, UnitTypeClass_LoadFromINI, 0x5) { GET(UnitTypeClass*, pItem, ESI); - if (auto pTypeExt = TechnoTypeExt::ExtMap.Find(pItem)) + if (auto pTypeExt = TechnoTypeExt::Fetch(pItem)) { if (!pTypeExt->Harvester_Counted.isset() && pItem->Harvester) pTypeExt->Harvester_Counted = true; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 67f7b4f7cb..e7147fe1c7 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -16,1039 +16,1038 @@ class Matrix3D; class ParticleSystemTypeClass; -class TechnoTypeExt +class TechnoTypeExt : public ObjectTypeExt { public: using base_type = TechnoTypeClass; + using ExtData = TechnoTypeExt; static constexpr DWORD Canary = 0x11111111; static constexpr size_t ExtPointerOffset = 0xDF4; - class ExtData : public ObjectTypeClassExtension +public: + // typed owner accessor (the base chain stores the owner as ObjectTypeClass*) + TechnoTypeClass* OwnerObject() const { - public: - // typed owner accessor (the base chain stores the owner as ObjectTypeClass*) - TechnoTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - Valueable HealthBar_Hide; - Valueable HealthBar_HidePips; - Valueable HealthBar_Permanent; - Valueable HealthBar_Permanent_PipScale; - Valueable UIDescription; - Valueable LowSelectionPriority; - Valueable LowDeployPriority; - PhobosFixedString<0x20> GroupAs; - std::vector> WeaponGroupAs; - Valueable RadarJamRadius; - Valueable RadarJamHouses; - Valueable RadarJamDelay; - ValueableVector RadarJamAffect; - ValueableVector RadarJamIgnore; - Nullable InhibitorRange; - Nullable DesignatorRange; - Valueable FactoryPlant_Multiplier; - Valueable MindControlRangeLimit; - Valueable MindControl_IgnoreSize; - Valueable MindControlSize; - Valueable MindControlLink_VisibleToHouse; - - std::unique_ptr InterceptorType; - - Valueable> TurretOffset; - Nullable TurretShadow; - Valueable ShadowIndex_Frame; - std::map ShadowIndices; - Valueable Spawner_LimitRange; - Valueable Spawner_ExtraLimitRange; - int SpawnerRange; - int EliteSpawnerRange; - Nullable Spawner_DelayFrames; - Valueable Spawner_AttackImmediately; - Valueable Spawner_UseTurretFacing; - Nullable Harvester_Counted; - Valueable Promote_IncludeSpawns; - Valueable ImmuneToCrit; - Valueable MultiMindControl_ReleaseVictim; - Valueable CameoPriority; - PhobosPCXFile AltCameoPCX; - Valueable NoManualMove; - Nullable InitialStrength; - Valueable ReloadInTransport; - Valueable ForbidParallelAIQueues; - Valueable IgnoreForBaseCenter; - - int TintColorAirstrike; - Nullable LaserTargetColor; - Nullable AirstrikeLineColor; - - Valueable ShieldType; - std::unique_ptr PassengerDeletionType; - std::unique_ptr DroppodType; - std::unique_ptr TiberiumEaterType; - - Nullable HarvesterDumpAmount; - - Valueable Ammo_AddOnDeploy; - Valueable Ammo_AutoDeployMinimumAmount; - Valueable Ammo_AutoDeployMaximumAmount; - Valueable Ammo_DeployUnlockMinimumAmount; - Valueable Ammo_DeployUnlockMaximumAmount; - - Nullable AutoDeath_Behavior; - ValueableVector AutoDeath_VanishAnimation; - Valueable AutoDeath_OnAmmoDepletion; - Valueable AutoDeath_OnOwnerChange; - Nullable AutoDeath_OnOwnerChange_HumanToComputer; - Nullable AutoDeath_OnOwnerChange_ComputerToHuman; - Valueable AutoDeath_AfterDelay; - ValueableVector AutoDeath_TechnosDontExist; - Valueable AutoDeath_TechnosDontExist_Any; - Valueable AutoDeath_TechnosDontExist_AllowLimboed; - Valueable AutoDeath_TechnosDontExist_Houses; - ValueableVector AutoDeath_TechnosExist; - Valueable AutoDeath_TechnosExist_Any; - Valueable AutoDeath_TechnosExist_AllowLimboed; - Valueable AutoDeath_TechnosExist_Houses; - - Valueable Slaved_OwnerWhenMasterKilled; - NullableIdx SlavesFreeSound; - NullableIdx SellSound; - NullableIdx EVA_Sold; - - Nullable CombatAlert; - Nullable CombatAlert_NotBuilding; - Nullable CombatAlert_UseFeedbackVoice; - Nullable CombatAlert_UseAttackVoice; - Nullable CombatAlert_UseEVA; - NullableIdx CombatAlert_EVA; - - NullableIdx VoiceCreated; - NullableIdx VoicePickup; // Used by carryalls instead of VoiceMove if set. - - ValueableVector WarpOut; - ValueableVector WarpIn; - ValueableVector Chronoshift_WarpOut; - ValueableVector Chronoshift_WarpIn; - ValueableVector WarpAway; - Nullable ChronoTrigger; - Nullable ChronoDistanceFactor; - Nullable ChronoMinimumDelay; - Nullable ChronoRangeMinimum; - Nullable ChronoDelay; - Nullable ChronoSpherePreDelay; - Nullable ChronoSphereDelay; - - Valueable WarpInWeapon; - Nullable WarpInMinRangeWeapon; - Valueable WarpOutWeapon; - Valueable WarpInWeapon_UseDistanceAsDamage; - - int SubterraneanSpeed; - Nullable SubterraneanHeight; - - ValueableVector OreGathering_Anims; - ValueableVector OreGathering_Tiberiums; - ValueableVector OreGathering_FramesPerDir; - - std::vector> WeaponBurstFLHs; - std::vector> EliteWeaponBurstFLHs; - std::vector AlternateFLHs; - Valueable AlternateFLH_OnTurret; - Valueable AlternateFLH_ApplyVehicle; - - Valueable DestroyAnim_Random; - Valueable NotHuman_RandomDeathSequence; - - Valueable DefaultDisguise; - NullableVector DefaultMirageDisguises; - Valueable UseDisguiseMovementSpeed; - - Nullable OpenTopped_RangeBonus; - Nullable OpenTopped_DamageMultiplier; - Nullable OpenTopped_WarpDistance; - Valueable OpenTopped_IgnoreRangefinding; - Valueable OpenTopped_AllowFiringIfDeactivated; - Nullable OpenTopped_AllowFiringIfAttackedByLocomotor; - Valueable OpenTopped_ShareTransportTarget; - Valueable OpenTopped_UseTransportRangeModifiers; - Valueable OpenTopped_CheckTransportDisableWeapons; - Nullable OpenTopped_DecloakToFire; - Nullable OpenTopped_FireWhileMoving; - Valueable OpenTransport_RangeBonus; - Valueable OpenTransport_DamageMultiplier; - Nullable OpenTransport_FireWhileMoving; - - Valueable AutoTargetOwnPosition; - Valueable AutoTargetOwnPosition_Self; - - Valueable NoSecondaryWeaponFallback; - Valueable NoSecondaryWeaponFallback_AllowAA; - Nullable AllowWeaponSelectAgainstWalls; - - Valueable NoAmmoWeapon; - Valueable NoAmmoAmount; - - Valueable JumpjetRotateOnCrash; - Nullable ShadowSizeCharacteristicHeight; - - Valueable IsSimpleDeployer_ConsiderPathfinding; - Nullable IsSimpleDeployer_DisallowedLandTypes; - Nullable DeployDir; - ValueableVector DeployingAnims; - Valueable DeployingAnim_KeepUnitVisible; - Valueable DeployingAnim_ReverseForUndeploy; - Valueable DeployingAnim_UseUnitDrawer; - - Valueable EnemyUIName; - - bool ForceWeapon_Check; - Valueable ForceWeapon_Naval_Decloaked; - Valueable ForceWeapon_Cloaked; - Valueable ForceWeapon_Disguised; - Valueable ForceWeapon_UnderEMP; - Valueable ForceWeapon_InRange_TechnoOnly; - ValueableVector ForceWeapon_InRange; - ValueableVector ForceWeapon_InRange_Overrides; - Valueable ForceWeapon_InRange_ApplyRangeModifiers; - ValueableVector ForceAAWeapon_InRange; - ValueableVector ForceAAWeapon_InRange_Overrides; - Valueable ForceAAWeapon_InRange_ApplyRangeModifiers; - Valueable ForceWeapon_Buildings; - Valueable ForceWeapon_Defenses; - Valueable ForceWeapon_Infantry; - Valueable ForceWeapon_Naval_Units; - Valueable ForceWeapon_Units; - Valueable ForceWeapon_Aircraft; - Valueable ForceAAWeapon_Infantry; - Valueable ForceAAWeapon_Units; - Valueable ForceAAWeapon_Aircraft; - - Valueable Ammo_Shared; - Valueable Ammo_Shared_Group; - - Nullable SelfHealGainType; - Valueable Passengers_SyncOwner; - Valueable Passengers_SyncOwner_RevertOnExit; - - Nullable IronCurtain_KeptOnDeploy; - Nullable IronCurtain_Effect; - Nullable IronCurtain_KillWarhead; - Nullable ForceShield_KeptOnDeploy; - Nullable ForceShield_Effect; - Nullable ForceShield_KillWarhead; - Valueable Explodes_KillPassengers; - Valueable Explodes_DuringBuildup; - Valueable DriverKilled_KeptPassengers; - Nullable DriverKilled_KillPassengers; - Nullable DeployFireWeapon; - Valueable TargetZoneScanType; - - Nullable AreaGuardRange; - Valueable MaxGuardRange; - - Promotable Insignia; - Valueable> InsigniaFrames; - Promotable InsigniaFrame; - Nullable Insignia_ShowEnemy; - std::vector> Insignia_Weapon; - std::vector> InsigniaFrame_Weapon; - std::vector>> InsigniaFrames_Weapon; - std::vector> Insignia_Passengers; - std::vector> InsigniaFrame_Passengers; - std::vector>> InsigniaFrames_Passengers; - - Valueable JumpjetTilt; - Valueable JumpjetTilt_ForwardAccelFactor; - Valueable JumpjetTilt_ForwardSpeedFactor; - Valueable JumpjetTilt_SidewaysRotationFactor; - Valueable JumpjetTilt_SidewaysSpeedFactor; - - Nullable TiltsWhenCrushes_Vehicles; - Nullable TiltsWhenCrushes_Overlays; - Nullable CrushForwardTiltPerFrame; - Valueable CrushOverlayExtraForwardTilt; - Valueable CrushSlowdownMultiplier; - Valueable SkipCrushSlowdown; - - Valueable DigitalDisplay_Disable; - ValueableVector DigitalDisplayTypes; - - Nullable SelectBox; - Valueable HideSelectBox; - - Valueable AmmoPipFrame; - Valueable EmptyAmmoPipFrame; - Valueable AmmoPipWrapStartFrame; - Nullable AmmoPipSize; - Valueable AmmoPipOffset; - - Valueable ShowSpawnsPips; - Valueable SpawnsPipFrame; - Valueable EmptySpawnsPipFrame; - Nullable SpawnsPipSize; - Valueable SpawnsPipOffset; - - Valueable SpawnFromEdge; - Valueable RetreatToEdge; - Nullable SpawnDistanceFromTarget; - Nullable SpawnHeight; - Nullable LandingDir; - - Nullable CurleyShuffle; - - Valueable Convert_Deploy; // Ares - Valueable Convert_Undeploy; - Valueable Convert_HumanToComputer; - Valueable Convert_ComputerToHuman; - Valueable Convert_ResetMindControl; - - Valueable CrateGoodie_RerollChance; - - Nullable Tint_Color; - Valueable Tint_Intensity; - Valueable Tint_VisibleToHouses; - - Valueable RevengeWeapon; - Valueable RevengeWeapon_AffectsHouse; - - AEAttachInfoTypeClass AttachEffects; - - Nullable RecountBurst; - - ValueableVector BuildLimitGroup_Types; - ValueableVector BuildLimitGroup_Nums; - Valueable BuildLimitGroup_Factor; - Valueable BuildLimitGroup_ContentIfAnyMatch; - Valueable BuildLimitGroup_NotBuildableIfQueueMatch; - ValueableVector BuildLimitGroup_ExtraLimit_Types; - ValueableVector BuildLimitGroup_ExtraLimit_Nums; - ValueableVector BuildLimitGroup_ExtraLimit_MaxCount; - Valueable BuildLimitGroup_ExtraLimit_MaxNum; - - Nullable AmphibiousEnter; - Nullable AmphibiousUnload; - Nullable NoQueueUpToEnter; - Nullable NoQueueUpToUnload; - Valueable Passengers_BySize; - - Valueable RateDown_Delay; - Valueable RateDown_Reset; - Valueable RateDown_Cover_Value; - Valueable RateDown_Cover_AmmoBelow; - - Nullable NoRearm_UnderEMP; - Nullable NoRearm_Temporal; - Nullable NoReload_UnderEMP; - Nullable NoReload_Temporal; - Nullable NoTurret_TrackTarget; - - Nullable Wake; - Nullable Wake_Grapple; - Nullable Wake_Sinking; - Nullable MakesWake; - - Nullable AINormalTargetingDelay; - Nullable PlayerNormalTargetingDelay; - Nullable AIGuardAreaTargetingDelay; - Nullable PlayerGuardAreaTargetingDelay; - Nullable AIAttackMoveTargetingDelay; - Nullable PlayerAttackMoveTargetingDelay; - Nullable DistributeTargetingFrame; - - Nullable AttackMove_Aggressive; - Nullable AttackMove_UpdateTarget; - - Valueable BunkerableAnyway; - Valueable KeepTargetOnMove; - Valueable KeepTargetOnMove_Weapon; - Valueable KeepTargetOnMove_NoMorePursuit; - Valueable KeepTargetOnMove_ExtraDistance; - - Valueable Power; - - Nullable AllowAirstrike; - - Nullable Image_ConditionYellow; - Nullable Image_ConditionRed; - Nullable WaterImage_ConditionYellow; - Nullable WaterImage_ConditionRed; - bool NeedDamagedImage; - - Nullable InitialSpawnsNumber; - ValueableVector Spawns_Queue; - - Valueable Spawner_RecycleRange; - ValueableVector Spawner_RecycleAnim; - Valueable Spawner_RecycleCoord; - Valueable Spawner_RecycleOnTurret; - - Nullable Sinkable; - Valueable Sinkable_SquidGrab; - Valueable SinkSpeed; - - Nullable ProneSpeed; - Nullable DamagedSpeed; - - ValueableVector Promote_VeteranAnimation; - ValueableVector Promote_EliteAnimation; - - Nullable RadarInvisibleToHouse; - - struct LaserTrailDataEntry - { - ValueableIdx idxType; - Valueable FLH; - Valueable IsOnTurret; - LaserTrailTypeClass* GetType() const { return LaserTrailTypeClass::Array[idxType].get(); } - }; - - std::vector LaserTrailData; - Valueable OnlyUseLandSequences; - Nullable SecondaryFireSequenceLandOnly; - Nullable PronePrimaryFireFLH; - Nullable ProneSecondaryFireFLH; - Nullable DeployedPrimaryFireFLH; - Nullable DeployedSecondaryFireFLH; - std::vector> CrouchedWeaponBurstFLHs; - std::vector> EliteCrouchedWeaponBurstFLHs; - std::vector> DeployedWeaponBurstFLHs; - std::vector> EliteDeployedWeaponBurstFLHs; - - Valueable SuppressKillWeapons; - ValueableVector SuppressKillWeapons_Types; - - Valueable DigitalDisplay_Health_FakeAtDisguise; - - NullableVector Overload_Count; - NullableVector Overload_Damage; - NullableVector Overload_Frames; - NullableIdx Overload_DeathSound; - Nullable Overload_ParticleSys; - Valueable Overload_ParticleSysCount; - - Valueable Harvester_CanGuardArea; - Valueable Harvester_CanGuardArea_RequireTarget; - Nullable HarvesterScanAfterUnload; - - Nullable ExtendedAircraftMissions; - Nullable ExtendedAircraftMissions_SmoothMoving; - Nullable ExtendedAircraftMissions_EarlyDescend; - Nullable ExtendedAircraftMissions_RearApproach; - Nullable ExtendedAircraftMissions_FastScramble; - Nullable ExtendedAircraftMissions_UnlandDamage; - - Valueable FallingDownDamage; - Nullable FallingDownDamage_Water; - Valueable FallingDownDamage_AllowEMP; - - Valueable Ammo_AutoConvertMinimumAmount; - Valueable Ammo_AutoConvertMaximumAmount; - Nullable Ammo_AutoConvertType; - - Valueable FiringForceScatter; - - Valueable FireUp; - Valueable FireUp_ResetInRetarget; - //Nullable SecondaryFire; - - Nullable DebrisTypes_Limit; - ValueableVector DebrisMinimums; - - Valueable EngineerRepairAmount; - - Valueable AttackMove_Follow; - Valueable AttackMove_Follow_IncludeAir; - Valueable AttackMove_Follow_IfMindControlIsFull; - Nullable AttackMove_StopWhenTargetAcquired; - Valueable AttackMove_PursuitTarget; - - Valueable MultiWeapon; - ValueableVector MultiWeapon_IsSecondary; - Valueable MultiWeapon_SelectCount; - bool ReadMultiWeapon; - Vector2D ThreatTypes; - Vector2D CombatDamages; - - ValueableIdx VoiceIFVRepair; - ValueableVector VoiceWeaponAttacks; - ValueableVector VoiceEliteWeaponAttacks; - - Nullable InfantryAutoDeploy; - - ValueableVector TeamMember_ConsideredAs; - - Nullable TurretResponse; - - Vector2D AttackFriendlies; - - Valueable Deploy_SkipPassengerUnload; - Valueable Deploy_NoPassenger; - Valueable Deploy_NoTiberium; - - Nullable DrainMoneyFrameDelay; - Nullable DrainMoneyAmount; - Nullable DrainAnimationType; - Nullable DrainMoneyDisplay; - Nullable DrainMoneyDisplay_Houses; - Valueable DrainMoneyDisplay_Offset; - Nullable DrainMoneyDisplay_OnTarget; - Nullable DrainMoneyDisplay_OnTarget_UseDisplayIncome; - - Nullable ParadropMission; - Nullable AIParadropMission; - Nullable ParadropDelay; - Nullable ParadropEndDelay; - - Nullable PenetratesTransport_Level; - Valueable PenetratesTransport_PassThroughMultiplier; - Valueable PenetratesTransport_FatalRateMultiplier; - Valueable PenetratesTransport_DamageMultiplier; - - Nullable JumpjetClimbIgnoreBuilding; - - Valueable HoverDrownable; - bool ExtraThreat_Enabled; - Nullable ExtraThreat_IsThreat; - Valueable AlwaysConsideredThreat; - Nullable ExtraThreat_InRange; - Nullable ExtraThreatCoefficient_InRangeDistance; - Nullable ExtraThreatCoefficient_Facing; - Nullable ExtraThreatCoefficient_DistanceToLastTarget; - - Nullable Unsellable; // Ares 3.0 - - SHPStruct* TurretShape; - - Nullable HarvesterLoadRate; - Nullable HarvesterDumpRate; - - Nullable Parasite_AllowWaterExit; - - Nullable FlyNoWobbles; - - Nullable LandingAnim; - - Valueable Missile_Cruise; - Valueable Missile_TakeOffAnim; - Valueable Missile_TakeOffSeparation; - - ExtData(TechnoTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , HealthBar_Hide { false } - , HealthBar_HidePips { false } - , HealthBar_Permanent { false } - , HealthBar_Permanent_PipScale { false } - , UIDescription {} - , LowSelectionPriority { false } - , LowDeployPriority { false } - , GroupAs { NONE_STR } - , WeaponGroupAs {} - , RadarJamRadius { 0 } - , RadarJamHouses { AffectedHouse::Enemies } - , RadarJamDelay { 30 } - , RadarJamAffect {} - , RadarJamIgnore {} - , InhibitorRange {} - , DesignatorRange { } - , FactoryPlant_Multiplier { 1.0f } - , MindControlRangeLimit {} - , MindControl_IgnoreSize { true } - , MindControlSize { 1 } - , MindControlLink_VisibleToHouse{ AffectedHouse::All } - - , InterceptorType { nullptr } - - , TurretOffset { { 0, 0, 0 } } - , TurretShadow { } - , ShadowIndices { } - , ShadowIndex_Frame { 0 } - , Spawner_LimitRange { false } - , Spawner_ExtraLimitRange { 0 } - , SpawnerRange { 0 } - , EliteSpawnerRange { 0 } - , Spawner_DelayFrames {} - , Spawner_AttackImmediately { false } - , Spawner_UseTurretFacing { false } - , Harvester_Counted {} - , Promote_IncludeSpawns { false } - , ImmuneToCrit { false } - , MultiMindControl_ReleaseVictim { false } - , CameoPriority { 0 } - , AltCameoPCX {} - , NoManualMove { false } - , InitialStrength {} - , ReloadInTransport { false } - , ForbidParallelAIQueues { false } - , IgnoreForBaseCenter { false } - , TintColorAirstrike { 0 } - , LaserTargetColor {} - , AirstrikeLineColor {} - , ShieldType { nullptr } - , PassengerDeletionType { nullptr } - - , WarpOut {} - , WarpIn {} - , Chronoshift_WarpOut {} - , Chronoshift_WarpIn {} - , WarpAway {} - , ChronoTrigger {} - , ChronoDistanceFactor {} - , ChronoMinimumDelay {} - , ChronoRangeMinimum {} - , ChronoDelay {} - , ChronoSpherePreDelay {} - , ChronoSphereDelay {} - , WarpInWeapon {} - , WarpInMinRangeWeapon {} - , WarpOutWeapon {} - , WarpInWeapon_UseDistanceAsDamage { false } - - , SubterraneanSpeed { -1 } - , SubterraneanHeight {} - - , OreGathering_Anims {} - , OreGathering_Tiberiums {} - , OreGathering_FramesPerDir {} - , LaserTrailData {} - , AlternateFLH_OnTurret { true } - , AlternateFLH_ApplyVehicle { false } - , DestroyAnim_Random { true } - , NotHuman_RandomDeathSequence { false } - - , DefaultDisguise {} - , UseDisguiseMovementSpeed {} - - , OpenTopped_RangeBonus {} - , OpenTopped_DamageMultiplier {} - , OpenTopped_WarpDistance {} - , OpenTopped_IgnoreRangefinding { false } - , OpenTopped_AllowFiringIfAttackedByLocomotor {} - , OpenTopped_AllowFiringIfDeactivated { true } - , OpenTopped_ShareTransportTarget { true } - , OpenTopped_UseTransportRangeModifiers { false } - , OpenTopped_CheckTransportDisableWeapons { false } - , OpenTopped_DecloakToFire {} - , OpenTopped_FireWhileMoving {} - , OpenTransport_RangeBonus { 0 } - , OpenTransport_DamageMultiplier { 1.0f } - , OpenTransport_FireWhileMoving {} - - , AutoTargetOwnPosition { false } - , AutoTargetOwnPosition_Self { false } - , NoSecondaryWeaponFallback { false } - , NoSecondaryWeaponFallback_AllowAA { false } - , AllowWeaponSelectAgainstWalls {} - , NoAmmoWeapon { -1 } - , NoAmmoAmount { 0 } - , JumpjetRotateOnCrash { true } - , ShadowSizeCharacteristicHeight { } - - , IsSimpleDeployer_ConsiderPathfinding { false } - , IsSimpleDeployer_DisallowedLandTypes {} - , DeployDir {} - , DeployingAnims {} - , DeployingAnim_KeepUnitVisible { false } - , DeployingAnim_ReverseForUndeploy { true } - , DeployingAnim_UseUnitDrawer { true } - - , HarvesterDumpAmount {} - - , Ammo_AddOnDeploy { 0 } - , Ammo_AutoDeployMinimumAmount { -1 } - , Ammo_AutoDeployMaximumAmount { -1 } - , Ammo_DeployUnlockMinimumAmount { -1 } - , Ammo_DeployUnlockMaximumAmount { -1 } - - , AutoDeath_Behavior { } - , AutoDeath_VanishAnimation {} - , AutoDeath_OnAmmoDepletion { false } - , AutoDeath_OnOwnerChange { false } - , AutoDeath_OnOwnerChange_HumanToComputer {} - , AutoDeath_OnOwnerChange_ComputerToHuman {} - , AutoDeath_AfterDelay { 0 } - , AutoDeath_TechnosDontExist {} - , AutoDeath_TechnosDontExist_Any { false } - , AutoDeath_TechnosDontExist_AllowLimboed { false } - , AutoDeath_TechnosDontExist_Houses { AffectedHouse::Owner } - , AutoDeath_TechnosExist {} - , AutoDeath_TechnosExist_Any { true } - , AutoDeath_TechnosExist_AllowLimboed { true } - , AutoDeath_TechnosExist_Houses { AffectedHouse::Owner } - - , Slaved_OwnerWhenMasterKilled { SlaveChangeOwnerType::Killer } - , SlavesFreeSound {} - , SellSound {} - , EVA_Sold {} - - , CombatAlert {} - , CombatAlert_NotBuilding {} - , CombatAlert_UseFeedbackVoice {} - , CombatAlert_UseAttackVoice {} - , CombatAlert_UseEVA {} - , CombatAlert_EVA {} - - , EnemyUIName {} - - , VoiceCreated {} - , VoicePickup {} - - , ForceWeapon_Check { false } - , ForceWeapon_Naval_Decloaked { -1 } - , ForceWeapon_Cloaked { -1 } - , ForceWeapon_Disguised { -1 } - , ForceWeapon_UnderEMP { -1 } - , ForceWeapon_InRange_TechnoOnly { true } - , ForceWeapon_InRange {} - , ForceWeapon_InRange_Overrides {} - , ForceWeapon_InRange_ApplyRangeModifiers { false } - , ForceAAWeapon_InRange {} - , ForceAAWeapon_InRange_Overrides {} - , ForceAAWeapon_InRange_ApplyRangeModifiers { false } - , ForceWeapon_Buildings { -1 } - , ForceWeapon_Defenses { -1 } - , ForceWeapon_Infantry { -1 } - , ForceWeapon_Naval_Units { -1 } - , ForceWeapon_Units { -1 } - , ForceWeapon_Aircraft { -1 } - , ForceAAWeapon_Infantry { -1 } - , ForceAAWeapon_Units { -1 } - , ForceAAWeapon_Aircraft { -1 } - - , Ammo_Shared { false } - , Ammo_Shared_Group { -1 } - - , SelfHealGainType {} - , Passengers_SyncOwner { false } - , Passengers_SyncOwner_RevertOnExit { true } - - , OnlyUseLandSequences { false } - , SecondaryFireSequenceLandOnly {} - - , PronePrimaryFireFLH {} - , ProneSecondaryFireFLH {} - , DeployedPrimaryFireFLH {} - , DeployedSecondaryFireFLH {} - - , IronCurtain_KeptOnDeploy {} - , IronCurtain_Effect {} - , IronCurtain_KillWarhead {} - , ForceShield_KeptOnDeploy {} - , ForceShield_Effect {} - , ForceShield_KillWarhead {} - - , Explodes_KillPassengers { true } - , Explodes_DuringBuildup { true } - , DriverKilled_KeptPassengers { false } - , DriverKilled_KillPassengers {} - , DeployFireWeapon {} - , TargetZoneScanType { TargetZoneScanType::Same } - - , AreaGuardRange {} - , MaxGuardRange { Leptons(4096) } - - , Insignia {} - , InsigniaFrames { { -1, -1, -1 } } - , InsigniaFrame { -1 } - , Insignia_ShowEnemy {} - , Insignia_Weapon {} - , InsigniaFrame_Weapon {} - , InsigniaFrames_Weapon {} - , Insignia_Passengers {} - , InsigniaFrame_Passengers {} - , InsigniaFrames_Passengers {} - - , JumpjetTilt { false } - , JumpjetTilt_ForwardAccelFactor { 1.0 } - , JumpjetTilt_ForwardSpeedFactor { 1.0 } - , JumpjetTilt_SidewaysRotationFactor { 1.0 } - , JumpjetTilt_SidewaysSpeedFactor { 1.0 } - - , TiltsWhenCrushes_Vehicles {} - , TiltsWhenCrushes_Overlays {} - , CrushSlowdownMultiplier { 0.2 } - , CrushForwardTiltPerFrame {} - , CrushOverlayExtraForwardTilt { 0.02 } - , SkipCrushSlowdown { false } - - , DigitalDisplay_Disable { false } - , DigitalDisplayTypes {} - - , SelectBox {} - , HideSelectBox { false } - - , AmmoPipFrame { 13 } - , EmptyAmmoPipFrame { -1 } - , AmmoPipWrapStartFrame { 14 } - , AmmoPipSize {} - , AmmoPipOffset { { 0,0 } } - - , ShowSpawnsPips { true } - , SpawnsPipFrame { 1 } - , EmptySpawnsPipFrame { 0 } - , SpawnsPipSize {} - , SpawnsPipOffset { { 0,0 } } - - , SpawnFromEdge { EdgeType::Owner } - , RetreatToEdge { EdgeType::Owner } - , SpawnDistanceFromTarget {} - , SpawnHeight {} - , LandingDir {} - , DroppodType {} - , TiberiumEaterType {} - - , CurleyShuffle {} - - , Convert_Deploy { } - , Convert_Undeploy { } - , Convert_HumanToComputer { } - , Convert_ComputerToHuman { } - , Convert_ResetMindControl { false } - - , CrateGoodie_RerollChance { 0.0 } - - , Tint_Color {} - , Tint_Intensity { 0.0 } - , Tint_VisibleToHouses { AffectedHouse::All } - - , RevengeWeapon {} - , RevengeWeapon_AffectsHouse { AffectedHouse::All } - - , AttachEffects {} - - , RecountBurst {} - - , BuildLimitGroup_Types {} - , BuildLimitGroup_Nums {} - , BuildLimitGroup_Factor { 1 } - , BuildLimitGroup_ContentIfAnyMatch { false } - , BuildLimitGroup_NotBuildableIfQueueMatch { false } - , BuildLimitGroup_ExtraLimit_Types {} - , BuildLimitGroup_ExtraLimit_Nums {} - , BuildLimitGroup_ExtraLimit_MaxCount {} - , BuildLimitGroup_ExtraLimit_MaxNum { 0 } - - , AmphibiousEnter {} - , AmphibiousUnload {} - , NoQueueUpToEnter {} - , NoQueueUpToUnload {} - , Passengers_BySize { true } - - , RateDown_Delay { 0 } - , RateDown_Reset { false } - , RateDown_Cover_Value { 0 } - , RateDown_Cover_AmmoBelow { -2 } - - , NoRearm_UnderEMP {} - , NoRearm_Temporal {} - , NoReload_UnderEMP {} - , NoReload_Temporal {} - , NoTurret_TrackTarget {} - - , Wake { } - , Wake_Grapple { } - , Wake_Sinking { } - , MakesWake { } - - , AINormalTargetingDelay {} - , PlayerNormalTargetingDelay {} - , AIGuardAreaTargetingDelay {} - , PlayerGuardAreaTargetingDelay {} - , AIAttackMoveTargetingDelay {} - , PlayerAttackMoveTargetingDelay {} - , DistributeTargetingFrame {} - - , DigitalDisplay_Health_FakeAtDisguise { true } - - , AttackMove_Aggressive {} - , AttackMove_UpdateTarget {} - - , BunkerableAnyway { false } - , KeepTargetOnMove { false } - , KeepTargetOnMove_Weapon { -1 } - , KeepTargetOnMove_NoMorePursuit { true } - , KeepTargetOnMove_ExtraDistance { Leptons(0) } - - , Power { } - - , AllowAirstrike { } - - , Image_ConditionYellow { } - , Image_ConditionRed { } - , WaterImage_ConditionYellow { } - , WaterImage_ConditionRed { } - , NeedDamagedImage { false } - - , InitialSpawnsNumber { } - , Spawns_Queue { } - - , Spawner_RecycleRange { Leptons(-1) } - , Spawner_RecycleAnim { } - , Spawner_RecycleCoord { {0,0,0} } - , Spawner_RecycleOnTurret { false } - - , Sinkable { } - , Sinkable_SquidGrab { true } - , SinkSpeed { 5 } - - , ProneSpeed { } - , DamagedSpeed { } + return static_cast(this->GetAttachedObject()); + } + + Valueable HealthBar_Hide; + Valueable HealthBar_HidePips; + Valueable HealthBar_Permanent; + Valueable HealthBar_Permanent_PipScale; + Valueable UIDescription; + Valueable LowSelectionPriority; + Valueable LowDeployPriority; + PhobosFixedString<0x20> GroupAs; + std::vector> WeaponGroupAs; + Valueable RadarJamRadius; + Valueable RadarJamHouses; + Valueable RadarJamDelay; + ValueableVector RadarJamAffect; + ValueableVector RadarJamIgnore; + Nullable InhibitorRange; + Nullable DesignatorRange; + Valueable FactoryPlant_Multiplier; + Valueable MindControlRangeLimit; + Valueable MindControl_IgnoreSize; + Valueable MindControlSize; + Valueable MindControlLink_VisibleToHouse; + + std::unique_ptr InterceptorType; + + Valueable> TurretOffset; + Nullable TurretShadow; + Valueable ShadowIndex_Frame; + std::map ShadowIndices; + Valueable Spawner_LimitRange; + Valueable Spawner_ExtraLimitRange; + int SpawnerRange; + int EliteSpawnerRange; + Nullable Spawner_DelayFrames; + Valueable Spawner_AttackImmediately; + Valueable Spawner_UseTurretFacing; + Nullable Harvester_Counted; + Valueable Promote_IncludeSpawns; + Valueable ImmuneToCrit; + Valueable MultiMindControl_ReleaseVictim; + Valueable CameoPriority; + PhobosPCXFile AltCameoPCX; + Valueable NoManualMove; + Nullable InitialStrength; + Valueable ReloadInTransport; + Valueable ForbidParallelAIQueues; + Valueable IgnoreForBaseCenter; + + int TintColorAirstrike; + Nullable LaserTargetColor; + Nullable AirstrikeLineColor; + + Valueable ShieldType; + std::unique_ptr PassengerDeletionType; + std::unique_ptr DroppodType; + std::unique_ptr TiberiumEaterType; + + Nullable HarvesterDumpAmount; + + Valueable Ammo_AddOnDeploy; + Valueable Ammo_AutoDeployMinimumAmount; + Valueable Ammo_AutoDeployMaximumAmount; + Valueable Ammo_DeployUnlockMinimumAmount; + Valueable Ammo_DeployUnlockMaximumAmount; + + Nullable AutoDeath_Behavior; + ValueableVector AutoDeath_VanishAnimation; + Valueable AutoDeath_OnAmmoDepletion; + Valueable AutoDeath_OnOwnerChange; + Nullable AutoDeath_OnOwnerChange_HumanToComputer; + Nullable AutoDeath_OnOwnerChange_ComputerToHuman; + Valueable AutoDeath_AfterDelay; + ValueableVector AutoDeath_TechnosDontExist; + Valueable AutoDeath_TechnosDontExist_Any; + Valueable AutoDeath_TechnosDontExist_AllowLimboed; + Valueable AutoDeath_TechnosDontExist_Houses; + ValueableVector AutoDeath_TechnosExist; + Valueable AutoDeath_TechnosExist_Any; + Valueable AutoDeath_TechnosExist_AllowLimboed; + Valueable AutoDeath_TechnosExist_Houses; + + Valueable Slaved_OwnerWhenMasterKilled; + NullableIdx SlavesFreeSound; + NullableIdx SellSound; + NullableIdx EVA_Sold; + + Nullable CombatAlert; + Nullable CombatAlert_NotBuilding; + Nullable CombatAlert_UseFeedbackVoice; + Nullable CombatAlert_UseAttackVoice; + Nullable CombatAlert_UseEVA; + NullableIdx CombatAlert_EVA; + + NullableIdx VoiceCreated; + NullableIdx VoicePickup; // Used by carryalls instead of VoiceMove if set. + + ValueableVector WarpOut; + ValueableVector WarpIn; + ValueableVector Chronoshift_WarpOut; + ValueableVector Chronoshift_WarpIn; + ValueableVector WarpAway; + Nullable ChronoTrigger; + Nullable ChronoDistanceFactor; + Nullable ChronoMinimumDelay; + Nullable ChronoRangeMinimum; + Nullable ChronoDelay; + Nullable ChronoSpherePreDelay; + Nullable ChronoSphereDelay; + + Valueable WarpInWeapon; + Nullable WarpInMinRangeWeapon; + Valueable WarpOutWeapon; + Valueable WarpInWeapon_UseDistanceAsDamage; + + int SubterraneanSpeed; + Nullable SubterraneanHeight; + + ValueableVector OreGathering_Anims; + ValueableVector OreGathering_Tiberiums; + ValueableVector OreGathering_FramesPerDir; + + std::vector> WeaponBurstFLHs; + std::vector> EliteWeaponBurstFLHs; + std::vector AlternateFLHs; + Valueable AlternateFLH_OnTurret; + Valueable AlternateFLH_ApplyVehicle; + + Valueable DestroyAnim_Random; + Valueable NotHuman_RandomDeathSequence; + + Valueable DefaultDisguise; + NullableVector DefaultMirageDisguises; + Valueable UseDisguiseMovementSpeed; + + Nullable OpenTopped_RangeBonus; + Nullable OpenTopped_DamageMultiplier; + Nullable OpenTopped_WarpDistance; + Valueable OpenTopped_IgnoreRangefinding; + Valueable OpenTopped_AllowFiringIfDeactivated; + Nullable OpenTopped_AllowFiringIfAttackedByLocomotor; + Valueable OpenTopped_ShareTransportTarget; + Valueable OpenTopped_UseTransportRangeModifiers; + Valueable OpenTopped_CheckTransportDisableWeapons; + Nullable OpenTopped_DecloakToFire; + Nullable OpenTopped_FireWhileMoving; + Valueable OpenTransport_RangeBonus; + Valueable OpenTransport_DamageMultiplier; + Nullable OpenTransport_FireWhileMoving; + + Valueable AutoTargetOwnPosition; + Valueable AutoTargetOwnPosition_Self; + + Valueable NoSecondaryWeaponFallback; + Valueable NoSecondaryWeaponFallback_AllowAA; + Nullable AllowWeaponSelectAgainstWalls; + + Valueable NoAmmoWeapon; + Valueable NoAmmoAmount; + + Valueable JumpjetRotateOnCrash; + Nullable ShadowSizeCharacteristicHeight; + + Valueable IsSimpleDeployer_ConsiderPathfinding; + Nullable IsSimpleDeployer_DisallowedLandTypes; + Nullable DeployDir; + ValueableVector DeployingAnims; + Valueable DeployingAnim_KeepUnitVisible; + Valueable DeployingAnim_ReverseForUndeploy; + Valueable DeployingAnim_UseUnitDrawer; + + Valueable EnemyUIName; + + bool ForceWeapon_Check; + Valueable ForceWeapon_Naval_Decloaked; + Valueable ForceWeapon_Cloaked; + Valueable ForceWeapon_Disguised; + Valueable ForceWeapon_UnderEMP; + Valueable ForceWeapon_InRange_TechnoOnly; + ValueableVector ForceWeapon_InRange; + ValueableVector ForceWeapon_InRange_Overrides; + Valueable ForceWeapon_InRange_ApplyRangeModifiers; + ValueableVector ForceAAWeapon_InRange; + ValueableVector ForceAAWeapon_InRange_Overrides; + Valueable ForceAAWeapon_InRange_ApplyRangeModifiers; + Valueable ForceWeapon_Buildings; + Valueable ForceWeapon_Defenses; + Valueable ForceWeapon_Infantry; + Valueable ForceWeapon_Naval_Units; + Valueable ForceWeapon_Units; + Valueable ForceWeapon_Aircraft; + Valueable ForceAAWeapon_Infantry; + Valueable ForceAAWeapon_Units; + Valueable ForceAAWeapon_Aircraft; + + Valueable Ammo_Shared; + Valueable Ammo_Shared_Group; + + Nullable SelfHealGainType; + Valueable Passengers_SyncOwner; + Valueable Passengers_SyncOwner_RevertOnExit; + + Nullable IronCurtain_KeptOnDeploy; + Nullable IronCurtain_Effect; + Nullable IronCurtain_KillWarhead; + Nullable ForceShield_KeptOnDeploy; + Nullable ForceShield_Effect; + Nullable ForceShield_KillWarhead; + Valueable Explodes_KillPassengers; + Valueable Explodes_DuringBuildup; + Valueable DriverKilled_KeptPassengers; + Nullable DriverKilled_KillPassengers; + Nullable DeployFireWeapon; + Valueable TargetZoneScanType; + + Nullable AreaGuardRange; + Valueable MaxGuardRange; + + Promotable Insignia; + Valueable> InsigniaFrames; + Promotable InsigniaFrame; + Nullable Insignia_ShowEnemy; + std::vector> Insignia_Weapon; + std::vector> InsigniaFrame_Weapon; + std::vector>> InsigniaFrames_Weapon; + std::vector> Insignia_Passengers; + std::vector> InsigniaFrame_Passengers; + std::vector>> InsigniaFrames_Passengers; + + Valueable JumpjetTilt; + Valueable JumpjetTilt_ForwardAccelFactor; + Valueable JumpjetTilt_ForwardSpeedFactor; + Valueable JumpjetTilt_SidewaysRotationFactor; + Valueable JumpjetTilt_SidewaysSpeedFactor; + + Nullable TiltsWhenCrushes_Vehicles; + Nullable TiltsWhenCrushes_Overlays; + Nullable CrushForwardTiltPerFrame; + Valueable CrushOverlayExtraForwardTilt; + Valueable CrushSlowdownMultiplier; + Valueable SkipCrushSlowdown; + + Valueable DigitalDisplay_Disable; + ValueableVector DigitalDisplayTypes; + + Nullable SelectBox; + Valueable HideSelectBox; + + Valueable AmmoPipFrame; + Valueable EmptyAmmoPipFrame; + Valueable AmmoPipWrapStartFrame; + Nullable AmmoPipSize; + Valueable AmmoPipOffset; + + Valueable ShowSpawnsPips; + Valueable SpawnsPipFrame; + Valueable EmptySpawnsPipFrame; + Nullable SpawnsPipSize; + Valueable SpawnsPipOffset; + + Valueable SpawnFromEdge; + Valueable RetreatToEdge; + Nullable SpawnDistanceFromTarget; + Nullable SpawnHeight; + Nullable LandingDir; + + Nullable CurleyShuffle; + + Valueable Convert_Deploy; // Ares + Valueable Convert_Undeploy; + Valueable Convert_HumanToComputer; + Valueable Convert_ComputerToHuman; + Valueable Convert_ResetMindControl; + + Valueable CrateGoodie_RerollChance; + + Nullable Tint_Color; + Valueable Tint_Intensity; + Valueable Tint_VisibleToHouses; + + Valueable RevengeWeapon; + Valueable RevengeWeapon_AffectsHouse; + + AEAttachInfoTypeClass AttachEffects; + + Nullable RecountBurst; + + ValueableVector BuildLimitGroup_Types; + ValueableVector BuildLimitGroup_Nums; + Valueable BuildLimitGroup_Factor; + Valueable BuildLimitGroup_ContentIfAnyMatch; + Valueable BuildLimitGroup_NotBuildableIfQueueMatch; + ValueableVector BuildLimitGroup_ExtraLimit_Types; + ValueableVector BuildLimitGroup_ExtraLimit_Nums; + ValueableVector BuildLimitGroup_ExtraLimit_MaxCount; + Valueable BuildLimitGroup_ExtraLimit_MaxNum; + + Nullable AmphibiousEnter; + Nullable AmphibiousUnload; + Nullable NoQueueUpToEnter; + Nullable NoQueueUpToUnload; + Valueable Passengers_BySize; + + Valueable RateDown_Delay; + Valueable RateDown_Reset; + Valueable RateDown_Cover_Value; + Valueable RateDown_Cover_AmmoBelow; + + Nullable NoRearm_UnderEMP; + Nullable NoRearm_Temporal; + Nullable NoReload_UnderEMP; + Nullable NoReload_Temporal; + Nullable NoTurret_TrackTarget; + + Nullable Wake; + Nullable Wake_Grapple; + Nullable Wake_Sinking; + Nullable MakesWake; + + Nullable AINormalTargetingDelay; + Nullable PlayerNormalTargetingDelay; + Nullable AIGuardAreaTargetingDelay; + Nullable PlayerGuardAreaTargetingDelay; + Nullable AIAttackMoveTargetingDelay; + Nullable PlayerAttackMoveTargetingDelay; + Nullable DistributeTargetingFrame; + + Nullable AttackMove_Aggressive; + Nullable AttackMove_UpdateTarget; + + Valueable BunkerableAnyway; + Valueable KeepTargetOnMove; + Valueable KeepTargetOnMove_Weapon; + Valueable KeepTargetOnMove_NoMorePursuit; + Valueable KeepTargetOnMove_ExtraDistance; + + Valueable Power; + + Nullable AllowAirstrike; + + Nullable Image_ConditionYellow; + Nullable Image_ConditionRed; + Nullable WaterImage_ConditionYellow; + Nullable WaterImage_ConditionRed; + bool NeedDamagedImage; + + Nullable InitialSpawnsNumber; + ValueableVector Spawns_Queue; + + Valueable Spawner_RecycleRange; + ValueableVector Spawner_RecycleAnim; + Valueable Spawner_RecycleCoord; + Valueable Spawner_RecycleOnTurret; + + Nullable Sinkable; + Valueable Sinkable_SquidGrab; + Valueable SinkSpeed; + + Nullable ProneSpeed; + Nullable DamagedSpeed; + + ValueableVector Promote_VeteranAnimation; + ValueableVector Promote_EliteAnimation; + + Nullable RadarInvisibleToHouse; + + struct LaserTrailDataEntry + { + ValueableIdx idxType; + Valueable FLH; + Valueable IsOnTurret; + LaserTrailTypeClass* GetType() const { return LaserTrailTypeClass::Array[idxType].get(); } + }; - , SuppressKillWeapons { false } - , SuppressKillWeapons_Types { } + std::vector LaserTrailData; + Valueable OnlyUseLandSequences; + Nullable SecondaryFireSequenceLandOnly; + Nullable PronePrimaryFireFLH; + Nullable ProneSecondaryFireFLH; + Nullable DeployedPrimaryFireFLH; + Nullable DeployedSecondaryFireFLH; + std::vector> CrouchedWeaponBurstFLHs; + std::vector> EliteCrouchedWeaponBurstFLHs; + std::vector> DeployedWeaponBurstFLHs; + std::vector> EliteDeployedWeaponBurstFLHs; + + Valueable SuppressKillWeapons; + ValueableVector SuppressKillWeapons_Types; + + Valueable DigitalDisplay_Health_FakeAtDisguise; + + NullableVector Overload_Count; + NullableVector Overload_Damage; + NullableVector Overload_Frames; + NullableIdx Overload_DeathSound; + Nullable Overload_ParticleSys; + Valueable Overload_ParticleSysCount; - , Promote_VeteranAnimation { } - , Promote_EliteAnimation { } - - , RadarInvisibleToHouse {} - - , Overload_Count {} - , Overload_Damage {} - , Overload_Frames {} - , Overload_DeathSound {} - , Overload_ParticleSys {} - , Overload_ParticleSysCount { 5 } - - , Harvester_CanGuardArea { false } - , Harvester_CanGuardArea_RequireTarget { false } - , HarvesterScanAfterUnload {} - - , ExtendedAircraftMissions {} - , ExtendedAircraftMissions_SmoothMoving {} - , ExtendedAircraftMissions_EarlyDescend {} - , ExtendedAircraftMissions_RearApproach {} - , ExtendedAircraftMissions_FastScramble {} - , ExtendedAircraftMissions_UnlandDamage {} - - , FallingDownDamage { 1.0 } - , FallingDownDamage_Water {} - , FallingDownDamage_AllowEMP { true } - - , Ammo_AutoConvertMinimumAmount { -1 } - , Ammo_AutoConvertMaximumAmount { -1 } - , Ammo_AutoConvertType { nullptr } + Valueable Harvester_CanGuardArea; + Valueable Harvester_CanGuardArea_RequireTarget; + Nullable HarvesterScanAfterUnload; - , FiringForceScatter { true } + Nullable ExtendedAircraftMissions; + Nullable ExtendedAircraftMissions_SmoothMoving; + Nullable ExtendedAircraftMissions_EarlyDescend; + Nullable ExtendedAircraftMissions_RearApproach; + Nullable ExtendedAircraftMissions_FastScramble; + Nullable ExtendedAircraftMissions_UnlandDamage; + + Valueable FallingDownDamage; + Nullable FallingDownDamage_Water; + Valueable FallingDownDamage_AllowEMP; + + Valueable Ammo_AutoConvertMinimumAmount; + Valueable Ammo_AutoConvertMaximumAmount; + Nullable Ammo_AutoConvertType; + + Valueable FiringForceScatter; + + Valueable FireUp; + Valueable FireUp_ResetInRetarget; + //Nullable SecondaryFire; + + Nullable DebrisTypes_Limit; + ValueableVector DebrisMinimums; + + Valueable EngineerRepairAmount; + + Valueable AttackMove_Follow; + Valueable AttackMove_Follow_IncludeAir; + Valueable AttackMove_Follow_IfMindControlIsFull; + Nullable AttackMove_StopWhenTargetAcquired; + Valueable AttackMove_PursuitTarget; + + Valueable MultiWeapon; + ValueableVector MultiWeapon_IsSecondary; + Valueable MultiWeapon_SelectCount; + bool ReadMultiWeapon; + Vector2D ThreatTypes; + Vector2D CombatDamages; + + ValueableIdx VoiceIFVRepair; + ValueableVector VoiceWeaponAttacks; + ValueableVector VoiceEliteWeaponAttacks; + + Nullable InfantryAutoDeploy; + + ValueableVector TeamMember_ConsideredAs; + + Nullable TurretResponse; + + Vector2D AttackFriendlies; + + Valueable Deploy_SkipPassengerUnload; + Valueable Deploy_NoPassenger; + Valueable Deploy_NoTiberium; + + Nullable DrainMoneyFrameDelay; + Nullable DrainMoneyAmount; + Nullable DrainAnimationType; + Nullable DrainMoneyDisplay; + Nullable DrainMoneyDisplay_Houses; + Valueable DrainMoneyDisplay_Offset; + Nullable DrainMoneyDisplay_OnTarget; + Nullable DrainMoneyDisplay_OnTarget_UseDisplayIncome; + + Nullable ParadropMission; + Nullable AIParadropMission; + Nullable ParadropDelay; + Nullable ParadropEndDelay; + + Nullable PenetratesTransport_Level; + Valueable PenetratesTransport_PassThroughMultiplier; + Valueable PenetratesTransport_FatalRateMultiplier; + Valueable PenetratesTransport_DamageMultiplier; + + Nullable JumpjetClimbIgnoreBuilding; + + Valueable HoverDrownable; + bool ExtraThreat_Enabled; + Nullable ExtraThreat_IsThreat; + Valueable AlwaysConsideredThreat; + Nullable ExtraThreat_InRange; + Nullable ExtraThreatCoefficient_InRangeDistance; + Nullable ExtraThreatCoefficient_Facing; + Nullable ExtraThreatCoefficient_DistanceToLastTarget; + + Nullable Unsellable; // Ares 3.0 + + SHPStruct* TurretShape; + + Nullable HarvesterLoadRate; + Nullable HarvesterDumpRate; + + Nullable Parasite_AllowWaterExit; + + Nullable FlyNoWobbles; + + Nullable LandingAnim; + + Valueable Missile_Cruise; + Valueable Missile_TakeOffAnim; + Valueable Missile_TakeOffSeparation; + + TechnoTypeExt(TechnoTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , HealthBar_Hide { false } + , HealthBar_HidePips { false } + , HealthBar_Permanent { false } + , HealthBar_Permanent_PipScale { false } + , UIDescription {} + , LowSelectionPriority { false } + , LowDeployPriority { false } + , GroupAs { NONE_STR } + , WeaponGroupAs {} + , RadarJamRadius { 0 } + , RadarJamHouses { AffectedHouse::Enemies } + , RadarJamDelay { 30 } + , RadarJamAffect {} + , RadarJamIgnore {} + , InhibitorRange {} + , DesignatorRange { } + , FactoryPlant_Multiplier { 1.0f } + , MindControlRangeLimit {} + , MindControl_IgnoreSize { true } + , MindControlSize { 1 } + , MindControlLink_VisibleToHouse{ AffectedHouse::All } + + , InterceptorType { nullptr } + + , TurretOffset { { 0, 0, 0 } } + , TurretShadow { } + , ShadowIndices { } + , ShadowIndex_Frame { 0 } + , Spawner_LimitRange { false } + , Spawner_ExtraLimitRange { 0 } + , SpawnerRange { 0 } + , EliteSpawnerRange { 0 } + , Spawner_DelayFrames {} + , Spawner_AttackImmediately { false } + , Spawner_UseTurretFacing { false } + , Harvester_Counted {} + , Promote_IncludeSpawns { false } + , ImmuneToCrit { false } + , MultiMindControl_ReleaseVictim { false } + , CameoPriority { 0 } + , AltCameoPCX {} + , NoManualMove { false } + , InitialStrength {} + , ReloadInTransport { false } + , ForbidParallelAIQueues { false } + , IgnoreForBaseCenter { false } + , TintColorAirstrike { 0 } + , LaserTargetColor {} + , AirstrikeLineColor {} + , ShieldType { nullptr } + , PassengerDeletionType { nullptr } + + , WarpOut {} + , WarpIn {} + , Chronoshift_WarpOut {} + , Chronoshift_WarpIn {} + , WarpAway {} + , ChronoTrigger {} + , ChronoDistanceFactor {} + , ChronoMinimumDelay {} + , ChronoRangeMinimum {} + , ChronoDelay {} + , ChronoSpherePreDelay {} + , ChronoSphereDelay {} + , WarpInWeapon {} + , WarpInMinRangeWeapon {} + , WarpOutWeapon {} + , WarpInWeapon_UseDistanceAsDamage { false } + + , SubterraneanSpeed { -1 } + , SubterraneanHeight {} + + , OreGathering_Anims {} + , OreGathering_Tiberiums {} + , OreGathering_FramesPerDir {} + , LaserTrailData {} + , AlternateFLH_OnTurret { true } + , AlternateFLH_ApplyVehicle { false } + , DestroyAnim_Random { true } + , NotHuman_RandomDeathSequence { false } + + , DefaultDisguise {} + , UseDisguiseMovementSpeed {} + + , OpenTopped_RangeBonus {} + , OpenTopped_DamageMultiplier {} + , OpenTopped_WarpDistance {} + , OpenTopped_IgnoreRangefinding { false } + , OpenTopped_AllowFiringIfAttackedByLocomotor {} + , OpenTopped_AllowFiringIfDeactivated { true } + , OpenTopped_ShareTransportTarget { true } + , OpenTopped_UseTransportRangeModifiers { false } + , OpenTopped_CheckTransportDisableWeapons { false } + , OpenTopped_DecloakToFire {} + , OpenTopped_FireWhileMoving {} + , OpenTransport_RangeBonus { 0 } + , OpenTransport_DamageMultiplier { 1.0f } + , OpenTransport_FireWhileMoving {} + + , AutoTargetOwnPosition { false } + , AutoTargetOwnPosition_Self { false } + , NoSecondaryWeaponFallback { false } + , NoSecondaryWeaponFallback_AllowAA { false } + , AllowWeaponSelectAgainstWalls {} + , NoAmmoWeapon { -1 } + , NoAmmoAmount { 0 } + , JumpjetRotateOnCrash { true } + , ShadowSizeCharacteristicHeight { } + + , IsSimpleDeployer_ConsiderPathfinding { false } + , IsSimpleDeployer_DisallowedLandTypes {} + , DeployDir {} + , DeployingAnims {} + , DeployingAnim_KeepUnitVisible { false } + , DeployingAnim_ReverseForUndeploy { true } + , DeployingAnim_UseUnitDrawer { true } + + , HarvesterDumpAmount {} + + , Ammo_AddOnDeploy { 0 } + , Ammo_AutoDeployMinimumAmount { -1 } + , Ammo_AutoDeployMaximumAmount { -1 } + , Ammo_DeployUnlockMinimumAmount { -1 } + , Ammo_DeployUnlockMaximumAmount { -1 } + + , AutoDeath_Behavior { } + , AutoDeath_VanishAnimation {} + , AutoDeath_OnAmmoDepletion { false } + , AutoDeath_OnOwnerChange { false } + , AutoDeath_OnOwnerChange_HumanToComputer {} + , AutoDeath_OnOwnerChange_ComputerToHuman {} + , AutoDeath_AfterDelay { 0 } + , AutoDeath_TechnosDontExist {} + , AutoDeath_TechnosDontExist_Any { false } + , AutoDeath_TechnosDontExist_AllowLimboed { false } + , AutoDeath_TechnosDontExist_Houses { AffectedHouse::Owner } + , AutoDeath_TechnosExist {} + , AutoDeath_TechnosExist_Any { true } + , AutoDeath_TechnosExist_AllowLimboed { true } + , AutoDeath_TechnosExist_Houses { AffectedHouse::Owner } + + , Slaved_OwnerWhenMasterKilled { SlaveChangeOwnerType::Killer } + , SlavesFreeSound {} + , SellSound {} + , EVA_Sold {} + + , CombatAlert {} + , CombatAlert_NotBuilding {} + , CombatAlert_UseFeedbackVoice {} + , CombatAlert_UseAttackVoice {} + , CombatAlert_UseEVA {} + , CombatAlert_EVA {} + + , EnemyUIName {} + + , VoiceCreated {} + , VoicePickup {} + + , ForceWeapon_Check { false } + , ForceWeapon_Naval_Decloaked { -1 } + , ForceWeapon_Cloaked { -1 } + , ForceWeapon_Disguised { -1 } + , ForceWeapon_UnderEMP { -1 } + , ForceWeapon_InRange_TechnoOnly { true } + , ForceWeapon_InRange {} + , ForceWeapon_InRange_Overrides {} + , ForceWeapon_InRange_ApplyRangeModifiers { false } + , ForceAAWeapon_InRange {} + , ForceAAWeapon_InRange_Overrides {} + , ForceAAWeapon_InRange_ApplyRangeModifiers { false } + , ForceWeapon_Buildings { -1 } + , ForceWeapon_Defenses { -1 } + , ForceWeapon_Infantry { -1 } + , ForceWeapon_Naval_Units { -1 } + , ForceWeapon_Units { -1 } + , ForceWeapon_Aircraft { -1 } + , ForceAAWeapon_Infantry { -1 } + , ForceAAWeapon_Units { -1 } + , ForceAAWeapon_Aircraft { -1 } + + , Ammo_Shared { false } + , Ammo_Shared_Group { -1 } + + , SelfHealGainType {} + , Passengers_SyncOwner { false } + , Passengers_SyncOwner_RevertOnExit { true } + + , OnlyUseLandSequences { false } + , SecondaryFireSequenceLandOnly {} + + , PronePrimaryFireFLH {} + , ProneSecondaryFireFLH {} + , DeployedPrimaryFireFLH {} + , DeployedSecondaryFireFLH {} + + , IronCurtain_KeptOnDeploy {} + , IronCurtain_Effect {} + , IronCurtain_KillWarhead {} + , ForceShield_KeptOnDeploy {} + , ForceShield_Effect {} + , ForceShield_KillWarhead {} + + , Explodes_KillPassengers { true } + , Explodes_DuringBuildup { true } + , DriverKilled_KeptPassengers { false } + , DriverKilled_KillPassengers {} + , DeployFireWeapon {} + , TargetZoneScanType { TargetZoneScanType::Same } + + , AreaGuardRange {} + , MaxGuardRange { Leptons(4096) } + + , Insignia {} + , InsigniaFrames { { -1, -1, -1 } } + , InsigniaFrame { -1 } + , Insignia_ShowEnemy {} + , Insignia_Weapon {} + , InsigniaFrame_Weapon {} + , InsigniaFrames_Weapon {} + , Insignia_Passengers {} + , InsigniaFrame_Passengers {} + , InsigniaFrames_Passengers {} + + , JumpjetTilt { false } + , JumpjetTilt_ForwardAccelFactor { 1.0 } + , JumpjetTilt_ForwardSpeedFactor { 1.0 } + , JumpjetTilt_SidewaysRotationFactor { 1.0 } + , JumpjetTilt_SidewaysSpeedFactor { 1.0 } + + , TiltsWhenCrushes_Vehicles {} + , TiltsWhenCrushes_Overlays {} + , CrushSlowdownMultiplier { 0.2 } + , CrushForwardTiltPerFrame {} + , CrushOverlayExtraForwardTilt { 0.02 } + , SkipCrushSlowdown { false } + + , DigitalDisplay_Disable { false } + , DigitalDisplayTypes {} + + , SelectBox {} + , HideSelectBox { false } + + , AmmoPipFrame { 13 } + , EmptyAmmoPipFrame { -1 } + , AmmoPipWrapStartFrame { 14 } + , AmmoPipSize {} + , AmmoPipOffset { { 0,0 } } + + , ShowSpawnsPips { true } + , SpawnsPipFrame { 1 } + , EmptySpawnsPipFrame { 0 } + , SpawnsPipSize {} + , SpawnsPipOffset { { 0,0 } } + + , SpawnFromEdge { EdgeType::Owner } + , RetreatToEdge { EdgeType::Owner } + , SpawnDistanceFromTarget {} + , SpawnHeight {} + , LandingDir {} + , DroppodType {} + , TiberiumEaterType {} + + , CurleyShuffle {} + + , Convert_Deploy { } + , Convert_Undeploy { } + , Convert_HumanToComputer { } + , Convert_ComputerToHuman { } + , Convert_ResetMindControl { false } + + , CrateGoodie_RerollChance { 0.0 } + + , Tint_Color {} + , Tint_Intensity { 0.0 } + , Tint_VisibleToHouses { AffectedHouse::All } + + , RevengeWeapon {} + , RevengeWeapon_AffectsHouse { AffectedHouse::All } + + , AttachEffects {} + + , RecountBurst {} + + , BuildLimitGroup_Types {} + , BuildLimitGroup_Nums {} + , BuildLimitGroup_Factor { 1 } + , BuildLimitGroup_ContentIfAnyMatch { false } + , BuildLimitGroup_NotBuildableIfQueueMatch { false } + , BuildLimitGroup_ExtraLimit_Types {} + , BuildLimitGroup_ExtraLimit_Nums {} + , BuildLimitGroup_ExtraLimit_MaxCount {} + , BuildLimitGroup_ExtraLimit_MaxNum { 0 } + + , AmphibiousEnter {} + , AmphibiousUnload {} + , NoQueueUpToEnter {} + , NoQueueUpToUnload {} + , Passengers_BySize { true } + + , RateDown_Delay { 0 } + , RateDown_Reset { false } + , RateDown_Cover_Value { 0 } + , RateDown_Cover_AmmoBelow { -2 } + + , NoRearm_UnderEMP {} + , NoRearm_Temporal {} + , NoReload_UnderEMP {} + , NoReload_Temporal {} + , NoTurret_TrackTarget {} + + , Wake { } + , Wake_Grapple { } + , Wake_Sinking { } + , MakesWake { } + + , AINormalTargetingDelay {} + , PlayerNormalTargetingDelay {} + , AIGuardAreaTargetingDelay {} + , PlayerGuardAreaTargetingDelay {} + , AIAttackMoveTargetingDelay {} + , PlayerAttackMoveTargetingDelay {} + , DistributeTargetingFrame {} + + , DigitalDisplay_Health_FakeAtDisguise { true } + + , AttackMove_Aggressive {} + , AttackMove_UpdateTarget {} + + , BunkerableAnyway { false } + , KeepTargetOnMove { false } + , KeepTargetOnMove_Weapon { -1 } + , KeepTargetOnMove_NoMorePursuit { true } + , KeepTargetOnMove_ExtraDistance { Leptons(0) } + + , Power { } + + , AllowAirstrike { } + + , Image_ConditionYellow { } + , Image_ConditionRed { } + , WaterImage_ConditionYellow { } + , WaterImage_ConditionRed { } + , NeedDamagedImage { false } + + , InitialSpawnsNumber { } + , Spawns_Queue { } + + , Spawner_RecycleRange { Leptons(-1) } + , Spawner_RecycleAnim { } + , Spawner_RecycleCoord { {0,0,0} } + , Spawner_RecycleOnTurret { false } + + , Sinkable { } + , Sinkable_SquidGrab { true } + , SinkSpeed { 5 } + + , ProneSpeed { } + , DamagedSpeed { } - , FireUp { -1 } - , FireUp_ResetInRetarget { true } - //, SecondaryFire {} + , SuppressKillWeapons { false } + , SuppressKillWeapons_Types { } - , DebrisTypes_Limit {} - , DebrisMinimums {} + , Promote_VeteranAnimation { } + , Promote_EliteAnimation { } + + , RadarInvisibleToHouse {} + + , Overload_Count {} + , Overload_Damage {} + , Overload_Frames {} + , Overload_DeathSound {} + , Overload_ParticleSys {} + , Overload_ParticleSysCount { 5 } + + , Harvester_CanGuardArea { false } + , Harvester_CanGuardArea_RequireTarget { false } + , HarvesterScanAfterUnload {} + + , ExtendedAircraftMissions {} + , ExtendedAircraftMissions_SmoothMoving {} + , ExtendedAircraftMissions_EarlyDescend {} + , ExtendedAircraftMissions_RearApproach {} + , ExtendedAircraftMissions_FastScramble {} + , ExtendedAircraftMissions_UnlandDamage {} + + , FallingDownDamage { 1.0 } + , FallingDownDamage_Water {} + , FallingDownDamage_AllowEMP { true } + + , Ammo_AutoConvertMinimumAmount { -1 } + , Ammo_AutoConvertMaximumAmount { -1 } + , Ammo_AutoConvertType { nullptr } - , EngineerRepairAmount { 0 } + , FiringForceScatter { true } - , AttackMove_Follow { false } - , AttackMove_Follow_IncludeAir { false } - , AttackMove_Follow_IfMindControlIsFull { false } - , AttackMove_StopWhenTargetAcquired { } - , AttackMove_PursuitTarget { false } + , FireUp { -1 } + , FireUp_ResetInRetarget { true } + //, SecondaryFire {} - , MultiWeapon { false } - , MultiWeapon_IsSecondary {} - , MultiWeapon_SelectCount { 2 } - , ReadMultiWeapon { false } - , ThreatTypes { ThreatType::Normal,ThreatType::Normal } - , CombatDamages { 0,0 } + , DebrisTypes_Limit {} + , DebrisMinimums {} - , VoiceIFVRepair { -1 } - , VoiceWeaponAttacks {} - , VoiceEliteWeaponAttacks {} + , EngineerRepairAmount { 0 } - , InfantryAutoDeploy {} + , AttackMove_Follow { false } + , AttackMove_Follow_IncludeAir { false } + , AttackMove_Follow_IfMindControlIsFull { false } + , AttackMove_StopWhenTargetAcquired { } + , AttackMove_PursuitTarget { false } - , TeamMember_ConsideredAs {} + , MultiWeapon { false } + , MultiWeapon_IsSecondary {} + , MultiWeapon_SelectCount { 2 } + , ReadMultiWeapon { false } + , ThreatTypes { ThreatType::Normal,ThreatType::Normal } + , CombatDamages { 0,0 } - , TurretResponse {} + , VoiceIFVRepair { -1 } + , VoiceWeaponAttacks {} + , VoiceEliteWeaponAttacks {} - , AttackFriendlies { false,false } + , InfantryAutoDeploy {} - , Deploy_SkipPassengerUnload { false } - , Deploy_NoPassenger { false } - , Deploy_NoTiberium { false } + , TeamMember_ConsideredAs {} - , DrainMoneyFrameDelay {} - , DrainMoneyAmount {} - , DrainAnimationType {} - , DrainMoneyDisplay {} - , DrainMoneyDisplay_Houses {} - , DrainMoneyDisplay_Offset { Point2D::Empty } - , DrainMoneyDisplay_OnTarget {} - , DrainMoneyDisplay_OnTarget_UseDisplayIncome {} + , TurretResponse {} - , ParadropMission {} - , AIParadropMission {} - , ParadropDelay {} - , ParadropEndDelay {} + , AttackFriendlies { false,false } - , PenetratesTransport_Level {} - , PenetratesTransport_PassThroughMultiplier { 1.0 } - , PenetratesTransport_FatalRateMultiplier { 1.0 } - , PenetratesTransport_DamageMultiplier { 1.0 } + , Deploy_SkipPassengerUnload { false } + , Deploy_NoPassenger { false } + , Deploy_NoTiberium { false } - , JumpjetClimbIgnoreBuilding {} + , DrainMoneyFrameDelay {} + , DrainMoneyAmount {} + , DrainAnimationType {} + , DrainMoneyDisplay {} + , DrainMoneyDisplay_Houses {} + , DrainMoneyDisplay_Offset { Point2D::Empty } + , DrainMoneyDisplay_OnTarget {} + , DrainMoneyDisplay_OnTarget_UseDisplayIncome {} - , HoverDrownable { true } + , ParadropMission {} + , AIParadropMission {} + , ParadropDelay {} + , ParadropEndDelay {} - , Unsellable {} + , PenetratesTransport_Level {} + , PenetratesTransport_PassThroughMultiplier { 1.0 } + , PenetratesTransport_FatalRateMultiplier { 1.0 } + , PenetratesTransport_DamageMultiplier { 1.0 } - , TurretShape { nullptr } - , ExtraThreat_Enabled { false } - , ExtraThreat_IsThreat {} - , AlwaysConsideredThreat { false } - , ExtraThreat_InRange {} - , ExtraThreatCoefficient_InRangeDistance {} - , ExtraThreatCoefficient_Facing {} - , ExtraThreatCoefficient_DistanceToLastTarget {} + , JumpjetClimbIgnoreBuilding {} - , HarvesterLoadRate {} - , HarvesterDumpRate {} - - , Parasite_AllowWaterExit {} + , HoverDrownable { true } - , FlyNoWobbles {} + , Unsellable {} - , LandingAnim {} + , TurretShape { nullptr } + , ExtraThreat_Enabled { false } + , ExtraThreat_IsThreat {} + , AlwaysConsideredThreat { false } + , ExtraThreat_InRange {} + , ExtraThreatCoefficient_InRangeDistance {} + , ExtraThreatCoefficient_Facing {} + , ExtraThreatCoefficient_DistanceToLastTarget {} - , Missile_Cruise { false } - , Missile_TakeOffAnim { nullptr } - , Missile_TakeOffSeparation { 24 } - { } + , HarvesterLoadRate {} + , HarvesterDumpRate {} + + , Parasite_AllowWaterExit {} - virtual ~ExtData() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void Initialize() override; + , FlyNoWobbles {} - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + , LandingAnim {} - void LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection); + , Missile_Cruise { false } + , Missile_TakeOffAnim { nullptr } + , Missile_TakeOffSeparation { 24 } + { } - void ApplyTurretOffset(Matrix3D* mtx, double factor = 1.0); - void CalculateSpawnerRange(); - bool IsSecondary(int nWeaponIndex) const; + virtual ~TechnoTypeExt() = default; + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; - int SelectForceWeapon(TechnoClass* pThis, AbstractClass* pTarget) const; - int SelectMultiWeapon(TechnoClass* const pThis, AbstractClass* const pTarget) const; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - void UpdateAdditionalAttributes(); + void LoadFromINIByWhatAmI(INI_EX& exINI, const char* pSection, INI_EX& exArtINI, const char* pArtSection); - // Ares 0.2 - bool CameoIsVeteran(HouseClass* pHouse) const; + void ApplyTurretOffset(Matrix3D* mtx, double factor = 1.0); + void CalculateSpawnerRange(); + bool IsSecondary(int nWeaponIndex) const; - // Ares 0.A - const char* GetSelectionGroupID() const; + int SelectForceWeapon(TechnoClass* pThis, AbstractClass* pTarget) const; + int SelectMultiWeapon(TechnoClass* const pThis, AbstractClass* const pTarget) const; - private: - template - void Serialize(T& Stm); + void UpdateAdditionalAttributes(); - void ParseBurstFLHs(INI_EX& exArtINI, const char* pArtSection, std::vector>& nFLH, std::vector>& nEFlh, const char* pPrefixTag); - void ParseVoiceWeaponAttacks(INI_EX& exINI, const char* pSection, ValueableVector& n, ValueableVector& nE); - }; + // Ares 0.2 + bool CameoIsVeteran(HouseClass* pHouse) const; + + // Ares 0.A + const char* GetSelectionGroupID() const; +private: + template + void Serialize(T& Stm); + + void ParseBurstFLHs(INI_EX& exArtINI, const char* pArtSection, std::vector>& nFLH, std::vector>& nEFlh, const char* pPrefixTag); + void ParseVoiceWeaponAttacks(INI_EX& exINI, const char* pSection, ValueableVector& n, ValueableVector& nE); + +public: class ExtContainer final : public Container { public: @@ -1060,6 +1059,16 @@ class TechnoTypeExt }; static ExtContainer ExtMap; + + static TechnoTypeExt* Fetch(const TechnoTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TechnoTypeExt* TryFetch(const TechnoTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } static bool SelectWeaponMutex; static void ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor = 1.0); @@ -1075,5 +1084,3 @@ class TechnoTypeExt static bool HasSelectionGroupID(ObjectTypeClass* pType, const char* pID); }; -// top-level name for the TechnoTypeClass extension (the base of the techno-type extension hierarchy) -using TechnoTypeClassExtension = TechnoTypeExt::ExtData; diff --git a/src/Ext/TechnoType/Hooks.MatrixOp.cpp b/src/Ext/TechnoType/Hooks.MatrixOp.cpp index 688925a401..89e025e719 100644 --- a/src/Ext/TechnoType/Hooks.MatrixOp.cpp +++ b/src/Ext/TechnoType/Hooks.MatrixOp.cpp @@ -11,7 +11,7 @@ DEFINE_REFERENCE(double, Pixel_Per_Lepton, 0xB1D008) void TechnoTypeExt::ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor) { - TechnoTypeExt::ExtMap.Find(pType)->ApplyTurretOffset(mtx, factor); + TechnoTypeExt::Fetch(pType)->ApplyTurretOffset(mtx, factor); } DEFINE_HOOK(0x6F3E6E, TechnoClass_ActionLines_TurretMultiOffset, 0x0) @@ -28,7 +28,7 @@ DEFINE_HOOK(0x73B780, UnitClass_DrawVXL_TurretMultiOffset, 0x0) { GET(TechnoTypeClass*, technoType, EAX); - auto const pTypeData = TechnoTypeExt::ExtMap.Find(technoType); + auto const pTypeData = TechnoTypeExt::Fetch(technoType); if (*pTypeData->TurretOffset.GetEx() == CoordStruct { 0, 0, 0 }) return 0x73B78A; @@ -78,7 +78,7 @@ DEFINE_HOOK(0x73BA12, UnitClass_DrawAsVXL_RewriteTurretDrawing, 0x6) // base matrix const auto mtx = Matrix3D::VoxelDefaultMatrix * drawMatrix; - const auto pDrawTypeExt = TechnoTypeExt::ExtMap.Find(pDrawType); + const auto pDrawTypeExt = TechnoTypeExt::Fetch(pDrawType); const bool notChargeTurret = pThis->Type->TurretCount <= 0 || pThis->Type->IsGattling; auto getTurretVoxel = [pDrawType, notChargeTurret, currentTurretNumber]() -> VoxelStruct* @@ -344,7 +344,7 @@ static Matrix3D* __stdcall JumpjetLocomotionClass_Draw_Matrix(ILocomotion* iloco } else { - const auto pTypeExt = TechnoExt::ExtMap.Find(linked)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(linked)->TypeExtData; if (pTypeExt->JumpjetTilt && !onGround @@ -567,7 +567,7 @@ DEFINE_HOOK(0x73C47A, UnitClass_DrawAsVXL_Shadow, 0x5) // This is not necessarily pThis->Type : UnloadingClass or WaterImage // This is the very reason I need to do this here, there's no less hacky way to get this Type from those inner calls - const auto pDrawTypeExt = TechnoTypeExt::ExtMap.Find(pDrawType); + const auto pDrawTypeExt = TechnoTypeExt::Fetch(pDrawType); const auto jjloco = locomotion_cast(loco); const auto height = pThis->GetHeight(); const double baseScale_log = RulesExt::Global()->AirShadowBaseScale_log; @@ -849,7 +849,7 @@ DEFINE_HOOK(0x4147F9, AircraftClass_Draw_Shadow, 0x6) pAircraftType = TechnoExt::GetAircraftTypeExtra(pThis); auto shadow_mtx = loco->Shadow_Matrix(&key); - const auto aTypeExt = TechnoTypeExt::ExtMap.Find(pAircraftType); + const auto aTypeExt = TechnoTypeExt::Fetch(pAircraftType); if (auto const flyLoco = locomotion_cast(loco)) { @@ -950,7 +950,7 @@ DEFINE_HOOK(0x7072A1, cyka707280_WhichMatrix, 0x6) REF_STACK(Matrix3D, matRet, STACK_OFFSET(0xE8, -0x60)); - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; auto pType = pTypeExt->OwnerObject(); const auto hva = pVXL->HVA; diff --git a/src/Ext/TechnoType/Hooks.MultiWeapon.cpp b/src/Ext/TechnoType/Hooks.MultiWeapon.cpp index 6fcda76ce2..cba27cb737 100644 --- a/src/Ext/TechnoType/Hooks.MultiWeapon.cpp +++ b/src/Ext/TechnoType/Hooks.MultiWeapon.cpp @@ -11,7 +11,7 @@ DEFINE_HOOK(0x7128B2, TechnoTypeClass_ReadINI_MultiWeapon, 0x6) INI_EX exINI(pINI); const char* pSection = pThis->ID; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis); pTypeExt->MultiWeapon.Read(exINI, pSection, "MultiWeapon"); const bool multiWeapon = pThis->HasMultipleTurrets() || pTypeExt->MultiWeapon.Get(); @@ -72,7 +72,7 @@ DEFINE_HOOK(0x715B10, TechnoTypeClass_ReadINI_MultiWeapon2, 0x7) GET(TechnoTypeClass*, pThis, EBP); enum { ReadWeaponX = 0x715B1F, Continue = 0x715B17 }; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis); if (pTypeExt->ReadMultiWeapon) return ReadWeaponX; @@ -83,7 +83,7 @@ DEFINE_HOOK(0x715B10, TechnoTypeClass_ReadINI_MultiWeapon2, 0x7) static inline int GetVoiceAttack(TechnoTypeClass* pType, int weaponIndex, bool isElite, WeaponTypeClass* pWeaponType) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); int voiceAttack = -1; if (pWeaponType && pWeaponType->Damage < 0) @@ -143,7 +143,7 @@ DEFINE_HOOK(0x7090A0, TechnoClass_VoiceAttack, 0x7) return 0x7091C7; } -static __forceinline ThreatType GetThreatType(TechnoClass* pThis, TechnoTypeExt::ExtData* pTypeExt, ThreatType result) +static __forceinline ThreatType GetThreatType(TechnoClass* pThis, TechnoTypeExt* pTypeExt, ThreatType result) { const ThreatType flags = pThis->Veterancy.IsElite() ? pTypeExt->ThreatTypes.Y : pTypeExt->ThreatTypes.X; return result | flags; @@ -158,7 +158,7 @@ DEFINE_HOOK(0x7431C9, FootClass_SelectAutoTarget_MultiWeapon, 0x7) // UnitClas GET(const ThreatType, result, EDI); const bool isUnit = R->Origin() == 0x7431C9; - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; const auto pType = pTypeExt->OwnerObject(); if (isUnit @@ -185,7 +185,7 @@ DEFINE_HOOK(0x445F04, BuildingClass_SelectAutoTarget_MultiWeapon, 0xA) return Continue; } - R->EDI(GetThreatType(pThis, TechnoTypeExt::ExtMap.Find(pThis->Type), result)); + R->EDI(GetThreatType(pThis, TechnoTypeExt::Fetch(pThis->Type), result)); return ReturnThreatType; } @@ -205,7 +205,7 @@ DEFINE_HOOK(0x6F398E, TechnoClass_CombatDamage_MultiWeapon, 0x7) return Continue; } - const auto pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; const auto pType = pTypeExt->OwnerObject(); if (rtti == AbstractType::Unit diff --git a/src/Ext/TechnoType/Hooks.Teleport.cpp b/src/Ext/TechnoType/Hooks.Teleport.cpp index 66ce588bc9..2b5092fbfc 100644 --- a/src/Ext/TechnoType/Hooks.Teleport.cpp +++ b/src/Ext/TechnoType/Hooks.Teleport.cpp @@ -10,7 +10,7 @@ TeleportLocomotionClass *pLocomotor = static_cast(Loco); \ FootClass* pLinked = pLocomotor->LinkedTo;\ TechnoTypeClass const*pType = pLinked->GetTechnoType(); \ - TechnoTypeExt::ExtData const*pExt = TechnoTypeExt::ExtMap.Find(pType); + TechnoTypeExt const*pExt = TechnoTypeExt::Fetch(pType); DEFINE_HOOK(0x7193F6, TeleportLocomotionClass_ILocomotion_Process_WarpoutAnim, 0x6) { @@ -30,7 +30,7 @@ DEFINE_HOOK(0x7193F6, TeleportLocomotionClass_ILocomotion_Process_WarpoutAnim, 0 WeaponTypeExt::DetonateAt(pExt->WarpOutWeapon, pLinked, pLinked); const int distance = (int)Math::sqrt(pLinked->Location.DistanceFromSquared(pLocomotor->LastCoords)); - const auto linkedExt = TechnoExt::ExtMap.Find(pLinked); + const auto linkedExt = TechnoExt::Fetch(pLinked); linkedExt->LastWarpDistance = distance; if (const auto pImage = pType->AlphaImage) @@ -87,7 +87,7 @@ DEFINE_HOOK(0x719742, TeleportLocomotionClass_ILocomotion_Process_WarpInAnim, 0x AnimExt::SetAnimOwnerHouseKind(pAnim, pLinked->Owner, nullptr, false, true); } - auto const lastWarpDistance = TechnoExt::ExtMap.Find(pLinked)->LastWarpDistance; + auto const lastWarpDistance = TechnoExt::Fetch(pLinked)->LastWarpDistance; const bool isInMinRange = lastWarpDistance < pExt->ChronoRangeMinimum.Get(RulesClass::Instance->ChronoRangeMinimum); if (auto const weaponType = isInMinRange ? pExt->WarpInMinRangeWeapon.Get(pExt->WarpInWeapon) : pExt->WarpInWeapon) @@ -157,7 +157,7 @@ DEFINE_HOOK(0x7197E4, TeleportLocomotionClass_Process_ChronospherePreDelay, 0x6) { GET(TeleportLocomotionClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis->Owner); + auto const pExt = TechnoExt::Fetch(pThis->Owner); pExt->IsBeingChronoSphered = true; R->ECX(pExt->TypeExtData->ChronoSpherePreDelay.Get(RulesExt::Global()->ChronoSpherePreDelay)); @@ -168,7 +168,7 @@ DEFINE_HOOK(0x719BD9, TeleportLocomotionClass_Process_ChronosphereDelay2, 0x6) { GET(TeleportLocomotionClass*, pThis, ESI); - auto const pExt = TechnoExt::ExtMap.Find(pThis->Owner); + auto const pExt = TechnoExt::Fetch(pThis->Owner); if (!pExt->IsBeingChronoSphered) return 0; diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index c342f7077a..0eada12f2e 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -10,7 +10,7 @@ DEFINE_HOOK(0x73D223, UnitClass_DrawIt_OreGath, 0x6) LEA_STACK(Point2D*, pLocation, STACK_OFFSET(0x50, -0x18)); GET_STACK(const int, nBrightness, STACK_OFFSET(0x50, 0x4)); - auto const pData = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto const pData = TechnoTypeExt::Fetch(pThis->Type); ConvertClass* pDrawer = FileSystem::ANIM_PAL; SHPStruct* pSHP = FileSystem::OREGATH_SHP; @@ -22,7 +22,7 @@ DEFINE_HOOK(0x73D223, UnitClass_DrawIt_OreGath, 0x6) { auto const pAnimType = pData->OreGathering_Anims.size() > 0 ? pData->OreGathering_Anims[idxArray] : nullptr; auto const nFramesPerFacing = pData->OreGathering_FramesPerDir.size() > 0 ? pData->OreGathering_FramesPerDir[idxArray] : 15; - auto const pAnimExt = AnimTypeExt::ExtMap.TryFind(pAnimType); + auto const pAnimExt = AnimTypeExt::TryFetch(pAnimType); if (pAnimType) { pSHP = pAnimType->GetImage(); @@ -70,7 +70,7 @@ DEFINE_HOOK(0x4AE670, DisplayClass_GetToolTip_EnemyUIName, 0x8) if (!IsAlly && !IsCivilian && !IsObserver) { - const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + const auto pTechnoTypeExt = TechnoTypeExt::Fetch(pTechnoType); if (const auto pEnemyUIName = pTechnoTypeExt->EnemyUIName.Get().Text) { @@ -89,7 +89,7 @@ DEFINE_HOOK(0x711F39, TechnoTypeClass_CostOf_FactoryPlant, 0x8) GET(HouseClass*, pHouse, EDI); REF_STACK(float, mult, STACK_OFFSET(0x10, -0x8)); - auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto const pHouseExt = HouseExt::Fetch(pHouse); if (pHouseExt->RestrictedFactoryPlants.size() > 0) mult *= pHouseExt->GetRestrictedFactoryPlantMult(pThis); @@ -103,7 +103,7 @@ DEFINE_HOOK(0x711FDF, TechnoTypeClass_RefundAmount_FactoryPlant, 0x8) GET(HouseClass*, pHouse, EDI); REF_STACK(float, mult, STACK_OFFSET(0x10, -0x4)); - auto const pHouseExt = HouseExt::ExtMap.Find(pHouse); + auto const pHouseExt = HouseExt::Fetch(pHouse); if (pHouseExt->RestrictedFactoryPlants.size() > 0) mult *= pHouseExt->GetRestrictedFactoryPlantMult(pThis); @@ -148,7 +148,7 @@ DEFINE_HOOK(0x747A2E, UnitTypeClass_ReadINI_TurretShape, 0x6) } if (const auto pShape = FileSystem::LoadSHPFile(Buffer)) - TechnoTypeExt::ExtMap.Find(pType)->TurretShape = pShape; + TechnoTypeExt::Fetch(pType)->TurretShape = pShape; } return 0; diff --git a/src/Ext/TerrainType/Body.cpp b/src/Ext/TerrainType/Body.cpp index 254b5751d2..8ea2eeaabf 100644 --- a/src/Ext/TerrainType/Body.cpp +++ b/src/Ext/TerrainType/Body.cpp @@ -4,17 +4,17 @@ TerrainTypeExt::ExtContainer TerrainTypeExt::ExtMap; -int TerrainTypeExt::ExtData::GetTiberiumGrowthStage() +int TerrainTypeExt::GetTiberiumGrowthStage() { return GeneralUtils::GetRangedRandomOrSingleValue(this->SpawnsTiberium_GrowthStage.Get()); } -int TerrainTypeExt::ExtData::GetCellsPerAnim() +int TerrainTypeExt::GetCellsPerAnim() { return GeneralUtils::GetRangedRandomOrSingleValue(this->SpawnsTiberium_CellsPerAnim.Get()); } -void TerrainTypeExt::ExtData::PlayDestroyEffects(const CoordStruct& coords) +void TerrainTypeExt::PlayDestroyEffects(const CoordStruct& coords) { VocClass::PlayIndexAtPos(this->DestroySound, coords); AnimExt::CreateRandomAnim(this->DestroyAnim, coords); @@ -36,7 +36,7 @@ void TerrainTypeExt::Remove(TerrainClass* pTerrain) // load / save template -void TerrainTypeExt::ExtData::Serialize(T& Stm) +void TerrainTypeExt::Serialize(T& Stm) { Stm .Process(this->SpawnsTiberium_Type) @@ -57,7 +57,7 @@ void TerrainTypeExt::ExtData::Serialize(T& Stm) ; } -void TerrainTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void TerrainTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -92,16 +92,16 @@ void TerrainTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) Debug::Log("[Developer warning] [%s] has Palette=%s set but no palette file was loaded (missing file or wrong filename). Missing palettes cause issues with lighting recalculations.\n", pThis->ImageFile, this->PaletteFile.data()); } -void TerrainTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TerrainTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); this->Palette = GeneralUtils::BuildPalette(this->PaletteFile); } -void TerrainTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TerrainTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index 497e5566eb..a81f58488d 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -5,76 +5,75 @@ #include #include -class TerrainTypeExt +class TerrainTypeExt final : public ObjectTypeExt { public: using base_type = TerrainTypeClass; + using ExtData = TerrainTypeExt; static constexpr DWORD Canary = 0xBEE78007; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + TerrainTypeClass* OwnerObject() const { - public: - // typed owner accessor - TerrainTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - Valueable SpawnsTiberium_Type; - Valueable SpawnsTiberium_Range; - Valueable> SpawnsTiberium_GrowthStage; - Valueable> SpawnsTiberium_CellsPerAnim; - ValueableIdx SpawnsTiberium_Particle; - ValueableVector DestroyAnim; - ValueableIdx DestroySound; - Nullable MinimapColor; - Valueable IsPassable; - Valueable CanBeBuiltOn; - Valueable HasDamagedFrames; - Valueable HasCrumblingFrames; - ValueableIdx CrumblingSound; - Nullable AnimationLength; - - PhobosFixedString<32u> PaletteFile; - DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. - - ExtData(TerrainTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , SpawnsTiberium_Type { 0 } - , SpawnsTiberium_Range { 1 } - , SpawnsTiberium_GrowthStage { { 3 } } - , SpawnsTiberium_CellsPerAnim { { 1 } } - , SpawnsTiberium_Particle { -1 } - , DestroyAnim {} - , DestroySound {} - , MinimapColor {} - , IsPassable { false } - , CanBeBuiltOn { false } - , HasDamagedFrames { false } - , HasCrumblingFrames { false } - , CrumblingSound {} - , AnimationLength {} - , PaletteFile {} - , Palette {} - { } - - virtual ~ExtData() = default; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - int GetTiberiumGrowthStage(); - int GetCellsPerAnim(); - void PlayDestroyEffects(const CoordStruct& coords); - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + Valueable SpawnsTiberium_Type; + Valueable SpawnsTiberium_Range; + Valueable> SpawnsTiberium_GrowthStage; + Valueable> SpawnsTiberium_CellsPerAnim; + ValueableIdx SpawnsTiberium_Particle; + ValueableVector DestroyAnim; + ValueableIdx DestroySound; + Nullable MinimapColor; + Valueable IsPassable; + Valueable CanBeBuiltOn; + Valueable HasDamagedFrames; + Valueable HasCrumblingFrames; + ValueableIdx CrumblingSound; + Nullable AnimationLength; + + PhobosFixedString<32u> PaletteFile; + DynamicVectorClass* Palette; // Intentionally not serialized - rebuilt from the palette file on load. + + TerrainTypeExt(TerrainTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , SpawnsTiberium_Type { 0 } + , SpawnsTiberium_Range { 1 } + , SpawnsTiberium_GrowthStage { { 3 } } + , SpawnsTiberium_CellsPerAnim { { 1 } } + , SpawnsTiberium_Particle { -1 } + , DestroyAnim {} + , DestroySound {} + , MinimapColor {} + , IsPassable { false } + , CanBeBuiltOn { false } + , HasDamagedFrames { false } + , HasCrumblingFrames { false } + , CrumblingSound {} + , AnimationLength {} + , PaletteFile {} + , Palette {} + { } + + virtual ~TerrainTypeExt() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + + int GetTiberiumGrowthStage(); + int GetCellsPerAnim(); + void PlayDestroyEffects(const CoordStruct& coords); + +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -84,11 +83,19 @@ class TerrainTypeExt static ExtContainer ExtMap; + static TerrainTypeExt* Fetch(const TerrainTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TerrainTypeExt* TryFetch(const TerrainTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); static void Remove(TerrainClass* pTerrain); }; -// top-level name for the TerrainTypeExt extension -using TerrainTypeClassExtension = TerrainTypeExt::ExtData; diff --git a/src/Ext/TerrainType/Hooks.Passable.cpp b/src/Ext/TerrainType/Hooks.Passable.cpp index 6dbb4dfeb1..5574a55c2e 100644 --- a/src/Ext/TerrainType/Hooks.Passable.cpp +++ b/src/Ext/TerrainType/Hooks.Passable.cpp @@ -8,7 +8,7 @@ DEFINE_HOOK(0x71C110, TerrainClass_SetOccupyBit_PassableTerrain, 0x6) GET(TerrainClass*, pThis, ECX); - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TerrainTypeExt::Fetch(pThis->Type); if (pTypeExt->IsPassable) return Skip; @@ -30,7 +30,7 @@ DEFINE_HOOK(0x7002E9, TechnoClass_WhatAction_PassableTerrain, 0x5) if (const auto pTerrain = abstract_cast(pTarget)) { - if (!isForceFire && TerrainTypeExt::ExtMap.Find(pTerrain->Type)->IsPassable) + if (!isForceFire && TerrainTypeExt::Fetch(pTerrain->Type)->IsPassable) { R->EBP(Action::Move); return ReturnAction; @@ -48,7 +48,7 @@ DEFINE_HOOK(0x483DDF, CellClass_CheckPassability_PassableTerrain, 0x6) GET(CellClass*, pThis, EDI); GET(TerrainClass*, pTerrain, ESI); - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pTerrain->Type); + auto const pTypeExt = TerrainTypeExt::Fetch(pTerrain->Type); if (pTypeExt->IsPassable) { @@ -68,7 +68,7 @@ DEFINE_HOOK(0x73FB71, UnitClass_CanEnterCell_PassableTerrain, 0x6) if (auto const pTerrain = abstract_cast(pTarget)) { - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pTerrain->Type); + auto const pTypeExt = TerrainTypeExt::Fetch(pTerrain->Type); if (pTypeExt->IsPassable) return SkipTerrainChecks; @@ -88,7 +88,7 @@ DEFINE_HOOK(0x6D57C1, TacticalClass_DrawLaserFencePlacement_BuildableTerrain, 0x GET(CellClass*, pCell, ESI); if (auto const pTerrain = pCell->GetTerrain(false)) - return TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn ? ContinueChecks : DontDraw; + return TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn ? ContinueChecks : DontDraw; return ContinueChecks; } @@ -103,7 +103,7 @@ DEFINE_HOOK(0x5684B1, MapClass_PlaceDown_BuildableTerrain, 0x6) { if (auto const pTerrain = pCell->GetTerrain(false)) { - if (TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) { pCell->RemoveContent(pTerrain, false); TerrainTypeExt::Remove(pTerrain); @@ -126,7 +126,7 @@ DEFINE_HOOK(0x5FD2B6, OverlayClass_Unlimbo_SkipTerrainCheck, 0x9) if (auto const pTerrain = pCell->GetTerrain(false)) { - if (!TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (!TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) return NoUnlimbo; pCell->RemoveContent(pTerrain, false); @@ -145,7 +145,7 @@ DEFINE_HOOK(0x45EF3A, BuildingTypeClass_FlushForPlacement_BuildableTerrain, 0x7) if (auto const pTerrain = abstract_cast(pObject)) { - if (!TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (!TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) return Disallow; } @@ -191,7 +191,7 @@ DEFINE_HOOK(0x586780, MapClass_IsAreaFree, 0x7) if (pTerrain) { - if (!FindBuildLocationTemp::EvaluatingBuildLocation || !TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (!FindBuildLocationTemp::EvaluatingBuildLocation || !TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) { R->EAX(false); return ReturnFromFunction; diff --git a/src/Ext/TerrainType/Hooks.cpp b/src/Ext/TerrainType/Hooks.cpp index b991db16d2..4412887e54 100644 --- a/src/Ext/TerrainType/Hooks.cpp +++ b/src/Ext/TerrainType/Hooks.cpp @@ -5,7 +5,7 @@ namespace TerrainTypeTemp { TerrainTypeClass* pCurrentType = nullptr; - TerrainTypeExt::ExtData* pCurrentExt = nullptr; + TerrainTypeExt* pCurrentExt = nullptr; double PriorHealthRatio = 0.0; } @@ -19,7 +19,7 @@ DEFINE_HOOK(0x71C84D, TerrainClass_AI_Animated, 0x6) if (pType->IsAnimated) { - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TerrainTypeExt::Fetch(pType); if (pThis->Animation.Value == (pTypeExt->AnimationLength.isset() ? pTypeExt->AnimationLength.Get() : (pType->GetImage()->Frames / (2 * (pTypeExt->HasDamagedFrames + 1))))) { @@ -64,7 +64,7 @@ DEFINE_HOOK(0x71C812, TerrainClass_AI_Crumbling, 0x6) GET(TerrainClass*, pThis, ESI); auto const pType = pThis->Type; - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TerrainTypeExt::Fetch(pType); if (pTypeExt->HasDamagedFrames && pThis->Health > 0) { @@ -97,7 +97,7 @@ DEFINE_HOOK(0x71C1FE, TerrainClass_Draw_PickFrame, 0x6) GET(TerrainClass*, pThis, ESI); auto const pType = pThis->Type; - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TerrainTypeExt::Fetch(pType); const bool isDamaged = pTypeExt->HasDamagedFrames && pThis->GetHealthPercentage() <= RulesExt::Global()->ConditionYellow_Terrain.Get(RulesClass::Instance->ConditionYellow); if (pType->IsAnimated) @@ -132,7 +132,7 @@ DEFINE_HOOK(0x71C2BC, TerrainClass_Draw_Palette, 0x6) if (wallOwnerIndex >= 0) colorSchemeIndex = HouseClass::Array[wallOwnerIndex]->ColorSchemeIndex; - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TerrainTypeExt::Fetch(pThis->Type); if (pTypeExt->Palette) { @@ -220,7 +220,7 @@ DEFINE_HOOK(0x71C6EE, TerrainClass_FireOut_Crumbling, 0x6) GET(TerrainClass*, pThis, ESI); - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TerrainTypeExt::Fetch(pThis->Type); if (!pThis->IsCrumbling && pTypeExt->HasCrumblingFrames) { @@ -248,7 +248,7 @@ DEFINE_HOOK(0x71B98B, TerrainClass_TakeDamage_RefreshDamageFrame, 0x7) GET(TerrainClass*, pThis, ESI); auto const pType = pThis->Type; - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TerrainTypeExt::Fetch(pType); const double condYellow = RulesExt::Global()->ConditionYellow_Terrain.Get(RulesClass::Instance->ConditionYellow); if (!pType->IsAnimated && pTypeExt->HasDamagedFrames && TerrainTypeTemp::PriorHealthRatio > condYellow && pThis->GetHealthPercentage() <= condYellow) @@ -267,7 +267,7 @@ DEFINE_HOOK(0x71BB2C, TerrainClass_TakeDamage_NowDead_Add, 0x6) //saved for later usage ! //REF_STACK(args_ReceiveDamage const, ReceiveDamageArgs, STACK_OFFSET(0x3C, 0x4)); - auto const pTypeExt = TerrainTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TerrainTypeExt::Fetch(pThis->Type); // Skip over the removal of the tree as well as destroy sound/anim (for now) if the tree has crumble animation. if (pThis->IsCrumbling && pTypeExt->HasCrumblingFrames) @@ -307,7 +307,7 @@ DEFINE_HOOK(0x47C065, CellClass_CellColor_TerrainRadarColor, 0x6) } else { - auto const pTerrainExt = TerrainTypeExt::ExtMap.Find(pType); + auto const pTerrainExt = TerrainTypeExt::Fetch(pType); if (pTerrainExt->MinimapColor.isset()) { diff --git a/src/Ext/Tiberium/Body.cpp b/src/Ext/Tiberium/Body.cpp index d11359ca3f..95d8f4629e 100644 --- a/src/Ext/Tiberium/Body.cpp +++ b/src/Ext/Tiberium/Body.cpp @@ -6,14 +6,14 @@ TiberiumExt::ExtContainer TiberiumExt::ExtMap; // load / save template -void TiberiumExt::ExtData::Serialize(T& Stm) +void TiberiumExt::Serialize(T& Stm) { Stm .Process(this->MinimapColor) ; } -void TiberiumExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void TiberiumExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -22,15 +22,15 @@ void TiberiumExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->MinimapColor.Read(exINI, pSection, "MinimapColor"); } -void TiberiumExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void TiberiumExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void TiberiumExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void TiberiumExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index 9c7fdb69c0..8d36910a7c 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -5,41 +5,40 @@ #include #include -class TiberiumExt +class TiberiumExt final : public AbstractTypeExt { public: using base_type = TiberiumClass; + using ExtData = TiberiumExt; static constexpr DWORD Canary = 0xAABBCCDD; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + TiberiumClass* OwnerObject() const { - public: - // typed owner accessor - TiberiumClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - Nullable MinimapColor; + Nullable MinimapColor; - ExtData(TiberiumClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , MinimapColor {} - { } + TiberiumExt(TiberiumClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , MinimapColor {} + { } - virtual ~ExtData() = default; + virtual ~TiberiumExt() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -49,9 +48,17 @@ class TiberiumExt static ExtContainer ExtMap; + static TiberiumExt* Fetch(const TiberiumClass* pThis) + { + return ExtMap.Find(pThis); + } + + static TiberiumExt* TryFetch(const TiberiumClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the TiberiumExt extension -using TiberiumClassExtension = TiberiumExt::ExtData; diff --git a/src/Ext/Tiberium/Hooks.cpp b/src/Ext/Tiberium/Hooks.cpp index 35a67a64b9..b18195b83f 100644 --- a/src/Ext/Tiberium/Hooks.cpp +++ b/src/Ext/Tiberium/Hooks.cpp @@ -17,7 +17,7 @@ DEFINE_HOOK(0x47C210, CellClass_CellColor_TiberiumRadarColor, 0x6) const auto pTiberium = TiberiumClass::Array.GetItem(tiberiumType); - if (const auto pTiberiumExt = TiberiumExt::ExtMap.TryFind(pTiberium)) + if (const auto pTiberiumExt = TiberiumExt::TryFetch(pTiberium)) { if (pTiberiumExt->MinimapColor.isset()) { diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 97525245f9..3879d597e7 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -1,11 +1,11 @@ #include "Body.h" -// A unit's extension is a concrete UnitClassExtension leaf, owned by the TechnoClass container. +// A unit's extension is a concrete UnitExt leaf, owned by the TechnoClass container. DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) { GET(UnitClass*, pItem, ESI); - TechnoExt::ExtMap.Adopt(new UnitClassExtension(pItem)); + TechnoExt::ExtMap.Adopt(new UnitExt(pItem)); return 0; } diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h index 1b91312016..d0688c4f0f 100644 --- a/src/Ext/Unit/Body.h +++ b/src/Ext/Unit/Body.h @@ -4,12 +4,12 @@ #include // Concrete leaf extension for UnitClass. Empty for now: all techno-level data lives -// in TechnoClassExtension; this leaf only exists so a unit's extension has its own -// concrete type (TechnoClassExtension itself is never instantiated). -class UnitClassExtension : public FootClassExtension +// in TechnoExt; this leaf only exists so a unit's extension has its own +// concrete type (TechnoExt itself is never instantiated). +class UnitExt : public FootExt { public: - explicit UnitClassExtension(UnitClass* const OwnerObject) : FootClassExtension(OwnerObject) + explicit UnitExt(UnitClass* const OwnerObject) : FootExt(OwnerObject) { } UnitClass* OwnerObject() const diff --git a/src/Ext/Unit/Hooks.Crushing.cpp b/src/Ext/Unit/Hooks.Crushing.cpp index 0ebf775979..695d851d82 100644 --- a/src/Ext/Unit/Hooks.Crushing.cpp +++ b/src/Ext/Unit/Hooks.Crushing.cpp @@ -10,7 +10,7 @@ DEFINE_HOOK(0x73B05B, UnitClass_PerCellProcess_TiltWhenCrushes, 0x6) GET(UnitClass*, pThis, EBP); auto const pType = pThis->Type; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (!pTypeExt->TiltsWhenCrushes_Overlays.Get(pType->TiltsWhenCrushes)) return SkipGameCode; @@ -27,7 +27,7 @@ DEFINE_HOOK(0x741941, UnitClass_OverrunSquare_TiltWhenCrushes, 0x6) GET(UnitClass*, pThis, EDI); auto const pType = pThis->Type; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); if (!pTypeExt->TiltsWhenCrushes_Vehicles.Get(pType->TiltsWhenCrushes)) return SkipGameCode; @@ -43,7 +43,7 @@ DEFINE_HOOK(0x4B1150, DriveLocomotionClass_WhileMoving_CrushSlowdown, 0x9) GET(DriveLocomotionClass*, pThis, EBP); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis->LinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis->LinkedTo)->TypeExtData; auto slowdownCoefficient = pThis->movementspeed_50; if (slowdownCoefficient > pTypeExt->CrushSlowdownMultiplier) @@ -63,7 +63,7 @@ DEFINE_HOOK(0x4B19F7, DriveLocomotionClass_WhileMoving_CrushTilt, 0xD) GET(DriveLocomotionClass*, pThis, EBP); auto const pLinkedTo = pThis->LinkedTo; - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; pLinkedTo->RockingForwardsPerFrame = static_cast(pTypeExt->CrushForwardTiltPerFrame.Get(-0.050000001)); return R->Origin() == 0x4B19F7 ? SkipGameCode1 : SkipGameCode2; @@ -75,7 +75,7 @@ DEFINE_HOOK(0x6A0813, ShipLocomotionClass_WhileMoving_CrushSlowdown, 0xB) GET(ShipLocomotionClass*, pThis, EBP); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis->LinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis->LinkedTo)->TypeExtData; auto slowdownCoefficient = pThis->movementspeed_50; if (slowdownCoefficient > pTypeExt->CrushSlowdownMultiplier) @@ -93,7 +93,7 @@ DEFINE_HOOK(0x6A108D, ShipLocomotionClass_WhileMoving_CrushTilt, 0xD) GET(ShipLocomotionClass*, pThis, EBP); auto const pLinkedTo = pThis->LinkedTo; - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; pLinkedTo->RockingForwardsPerFrame = static_cast(pTypeExt->CrushForwardTiltPerFrame.Get(-0.02)); return SkipGameCode; @@ -103,5 +103,5 @@ DEFINE_HOOK_AGAIN(0x6A0809, SomeLocomotionClass_WhileMoving_SkipCrushSlowDown, 0 DEFINE_HOOK(0x4B1146, SomeLocomotionClass_WhileMoving_SkipCrushSlowDown, 0x6) // Drive { GET(FootClass*, pLinkedTo, ECX); - return TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData->SkipCrushSlowdown ? R->Origin() + 0x3C : 0; + return TechnoExt::Fetch(pLinkedTo)->TypeExtData->SkipCrushSlowdown ? R->Origin() + 0x3C : 0; } diff --git a/src/Ext/Unit/Hooks.DeployFire.cpp b/src/Ext/Unit/Hooks.DeployFire.cpp index 8bf795c497..dc18205360 100644 --- a/src/Ext/Unit/Hooks.DeployFire.cpp +++ b/src/Ext/Unit/Hooks.DeployFire.cpp @@ -20,7 +20,7 @@ DEFINE_HOOK(0x4C77E4, EventClass_Execute_DeployCommand, 0x6) if (pWeapon && pWeapon->FireOnce) { - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); if (pExt->DeployFireTimer.HasTimeLeft()) return DoNotExecute; @@ -57,7 +57,7 @@ DEFINE_HOOK(0x73DCEF, UnitClass_Mission_Unload_DeployFire, 0x6) { pThis->SetTarget(nullptr); pThis->QueueMission(Mission::Guard, true); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto UnloadControl = &MissionControlClass::Array[(int)Mission::Unload]; const int delay = static_cast(UnloadControl->Rate * 900) + ScenarioClass::Instance->Random(0, 2); pExt->DeployFireTimer.Start(Math::min(pWeapon->ROF, delay)); diff --git a/src/Ext/Unit/Hooks.DeploysInto.cpp b/src/Ext/Unit/Hooks.DeploysInto.cpp index 98815fdf2f..cb11c79812 100644 --- a/src/Ext/Unit/Hooks.DeploysInto.cpp +++ b/src/Ext/Unit/Hooks.DeploysInto.cpp @@ -20,7 +20,7 @@ static inline void TransferMindControlOnDeploy(TechnoClass* pTechnoFrom, TechnoC { const auto pAnimType = pTechnoFrom->MindControlRingAnim ? pTechnoFrom->MindControlRingAnim->Type - : TechnoExt::ExtMap.Find(pTechnoFrom)->MindControlRingAnimType; + : TechnoExt::Fetch(pTechnoFrom)->MindControlRingAnimType; if (const auto Controller = pTechnoFrom->MindControlledBy) { @@ -120,7 +120,7 @@ DEFINE_HOOK(0x449E2E, BuildingClass_Mi_Selling_CreateUnit, 0x6) // Remember MC ring animation. if (pStructure->IsMindControlled()) { - auto const pTechnoExt = TechnoExt::ExtMap.Find(pStructure); + auto const pTechnoExt = TechnoExt::Fetch(pStructure); pTechnoExt->UpdateMindControlAnim(); } @@ -178,7 +178,7 @@ DEFINE_HOOK(0x47C640, CellClass_CanThisExistHere_IgnoreSomething, 0x6) } else if (const auto pTerrain = abstract_cast(pObject)) { - if (!TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (!TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) return CanNotExistHere; } } @@ -192,7 +192,7 @@ DEFINE_HOOK(0x47C640, CellClass_CanThisExistHere_IgnoreSomething, 0x6) { if (const auto pTerrain = abstract_cast(pObject)) { - if (!TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (!TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) return CanNotExistHere; builtOnCanBeBuiltOn = true; @@ -248,7 +248,7 @@ DEFINE_HOOK(0x47C640, CellClass_CanThisExistHere_IgnoreSomething, 0x6) } else if (const auto pTerrain = abstract_cast(pObject)) { - if (!TerrainTypeExt::ExtMap.Find(pTerrain->Type)->CanBeBuiltOn) + if (!TerrainTypeExt::Fetch(pTerrain->Type)->CanBeBuiltOn) return CanNotExistHere; builtOnCanBeBuiltOn = true; @@ -271,7 +271,7 @@ DEFINE_HOOK(0x7396D2, UnitClass_TryToDeploy_Transfer, 0x5) if (pUnit->Type->DeployToFire && pUnit->Target) pStructure->LastTarget = pUnit->Target; - const auto pStructureExt = BuildingExt::ExtMap.Find(pStructure); + const auto pStructureExt = BuildingExt::Fetch(pStructure); pStructureExt->DeployedTechno = true; return 0; diff --git a/src/Ext/Unit/Hooks.DisallowMoving.cpp b/src/Ext/Unit/Hooks.DisallowMoving.cpp index 42f629e90b..e39177b914 100644 --- a/src/Ext/Unit/Hooks.DisallowMoving.cpp +++ b/src/Ext/Unit/Hooks.DisallowMoving.cpp @@ -70,7 +70,7 @@ DEFINE_HOOK(0x736B60, UnitClass_Rotation_AI_DisallowMoving, 0x6) { GET(UnitClass*, pThis, ESI); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); return (pTypeExt->TurretResponse.isset() ? !pTypeExt->TurretResponse.Get() : TechnoExt::CannotMove(pThis)) ? 0x736AFB : 0; } diff --git a/src/Ext/Unit/Hooks.DrawIt.cpp b/src/Ext/Unit/Hooks.DrawIt.cpp index a82e118823..8666167cf9 100644 --- a/src/Ext/Unit/Hooks.DrawIt.cpp +++ b/src/Ext/Unit/Hooks.DrawIt.cpp @@ -26,7 +26,7 @@ DEFINE_HOOK(0x73C7AC, UnitClass_DrawAsSHP_DrawTurret_TintFix, 0x6) pThis->Draw_A_SHP(pShape, bodyFrameIdx, &location, &bounds, 0, 256, zAdjust, zGradient, 0, extraLight, 0, 0, 0, 0, 0, 0); - const auto pTurretShape = TechnoTypeExt::ExtMap.Find(pType)->TurretShape; + const auto pTurretShape = TechnoTypeExt::Fetch(pType)->TurretShape; const int StartFrame = pTurretShape ? 0 : (pType->WalkFrames * pType->Facings); if (pTurretShape) @@ -61,7 +61,7 @@ DEFINE_HOOK(0x73CCF4, UnitClass_DrawSHP_FacingsB_TurretShape, 0xA) GET(UnitClass*, pThis, EBP); GET(UnitTypeClass*, pType, ECX); - const auto pTurretShape = TechnoTypeExt::ExtMap.Find(pType)->TurretShape; + const auto pTurretShape = TechnoTypeExt::Fetch(pType)->TurretShape; const int StartFrame = pTurretShape ? 0 : (pType->WalkFrames * pType->Facings); const int frameIdx = pThis->SecondaryFacing.Current().GetFacing<32>(4) + StartFrame; diff --git a/src/Ext/Unit/Hooks.Firing.cpp b/src/Ext/Unit/Hooks.Firing.cpp index 853ec461f1..b6d7bb86cc 100644 --- a/src/Ext/Unit/Hooks.Firing.cpp +++ b/src/Ext/Unit/Hooks.Firing.cpp @@ -15,7 +15,7 @@ DEFINE_HOOK(0x736F61, UnitClass_UpdateFiring_FireUp, 0x6) if (pType->Turret || pType->Voxel || pThis->InLimbo) return 0; - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto pTypeExt = pExt->TypeExtData; // SHP vehicles have no secondary action frames, so it does not need SecondaryFire. @@ -58,7 +58,7 @@ DEFINE_HOOK(0x736F61, UnitClass_UpdateFiring_FireUp, 0x6) { int cumulativeDelay = 0; int projectedDelay = 0; - auto const pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + auto const pWeaponExt = WeaponTypeExt::Fetch(pWeapon); const bool allowBurst = pWeaponExt->Burst_FireWithinSequence; const int currentBurstIndex = pThis->CurrentBurstIndex; auto& random = ScenarioClass::Instance->Random; @@ -119,7 +119,7 @@ DEFINE_HOOK(0x736F67, UnitClass_UpdateFiring_BurstNoDelay, 0x6) { if (pWeapon->Burst > 1) { - const auto pWeaponExt = WeaponTypeExt::ExtMap.Find(pWeapon); + const auto pWeaponExt = WeaponTypeExt::Fetch(pWeapon); if (pWeaponExt->Burst_NoDelay && (!pWeaponExt->DelayedFire_Duration.isset() || pWeaponExt->DelayedFire_OnlyOnInitialBurst)) { diff --git a/src/Ext/Unit/Hooks.Harvester.cpp b/src/Ext/Unit/Hooks.Harvester.cpp index 8a56e08896..f954090ceb 100644 --- a/src/Ext/Unit/Hooks.Harvester.cpp +++ b/src/Ext/Unit/Hooks.Harvester.cpp @@ -74,7 +74,7 @@ DEFINE_HOOK(0x4D6D34, FootClass_MissionAreaGuard_Miner, 0x5) GET(FootClass*, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; if (pTypeExt->Harvester_CanGuardArea && pThis->Owner->IsControlledByHuman()) { @@ -89,7 +89,7 @@ DEFINE_HOOK_AGAIN(0x73D515, UnitClass_Harvesting_HarvesterLoadRate, 6) DEFINE_HOOK(0x73D5D5, UnitClass_Harvesting_HarvesterLoadRate, 6) { GET(UnitClass* const, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; R->EAX(pTypeExt->HarvesterLoadRate.Get(RulesClass::Instance->HarvesterLoadRate)); @@ -99,7 +99,7 @@ DEFINE_HOOK(0x73D5D5, UnitClass_Harvesting_HarvesterLoadRate, 6) DEFINE_HOOK(0x73E361, UnitClass_Harvesting_HarvesterDumpRate, 6) { GET(UnitClass* const, pThis, ESI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; double dumpRate = pTypeExt->HarvesterDumpRate.Get(RulesClass::Instance->HarvesterDumpRate); @@ -114,7 +114,7 @@ DEFINE_HOOK(0x73E411, UnitClass_Mission_Unload_DumpAmount, 0x7) GET(UnitClass*, pThis, ESI); GET(const int, tiberiumIdx, EBP); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); const float totalAmount = pThis->Tiberium.GetAmount(tiberiumIdx); float dumpAmount = pTypeExt->HarvesterDumpAmount.Get(RulesExt::Global()->HarvesterDumpAmount); @@ -129,7 +129,7 @@ DEFINE_HOOK(0x73E411, UnitClass_Mission_Unload_DumpAmount, 0x7) DEFINE_HOOK(0x73E951, UnitClass_Harvest_HarvesterLoadRate, 6) { GET(UnitClass* const, pThis, EBP); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis)->TypeExtData; R->EAX(pTypeExt->HarvesterLoadRate.Get(RulesClass::Instance->HarvesterLoadRate)); @@ -145,7 +145,7 @@ DEFINE_HOOK(0x73E730, UnitClass_MissionHarvest_HarvesterScanAfterUnload, 0x5) const auto pType = pThis->Type; // Focus is set when the harvester is fully loaded and go home. - if (pFocus && !pType->Weeder && TechnoTypeExt::ExtMap.Find(pType)->HarvesterScanAfterUnload.Get(RulesExt::Global()->HarvesterScanAfterUnload)) + if (pFocus && !pType->Weeder && TechnoTypeExt::Fetch(pType)->HarvesterScanAfterUnload.Get(RulesExt::Global()->HarvesterScanAfterUnload)) { auto cellBuffer = CellStruct::Empty; const auto pCellStru = pThis->ScanForTiberium(&cellBuffer, RulesClass::Instance->TiberiumLongScan / Unsorted::LeptonsPerCell, 0); @@ -221,7 +221,7 @@ DEFINE_HOOK(0x44459A, BuildingClass_ExitObject_SubterraneanHarvester, 0x5) if ((pType->Harvester || pType->Weeder) && pType->MovementZone == MovementZone::Subterrannean) { - auto const pExt = TechnoExt::ExtMap.Find(pUnit); + auto const pExt = TechnoExt::Fetch(pUnit); pExt->SubterraneanHarvStatus = 1; pExt->SubterraneanHarvRallyPoint = pThis->ArchiveTarget; pThis->ArchiveTarget = nullptr; diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index 0263ff82ae..260103d404 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -68,14 +68,14 @@ DEFINE_HOOK(0x736E6E, UnitClass_UpdateFiring_OmniFireTurnToTarget, 0x9) if ((pType->DeployFire || pType->DeployFireWeapon == wpIdx) && pThis->CurrentMission == Mission::Unload) return 0; - if (err == FireError::REARM && !TechnoTypeExt::ExtMap.Find(pType)->NoTurret_TrackTarget.Get(RulesExt::Global()->NoTurret_TrackTarget)) + if (err == FireError::REARM && !TechnoTypeExt::Fetch(pType)->NoTurret_TrackTarget.Get(RulesExt::Global()->NoTurret_TrackTarget)) return 0; auto const pWpn = pThis->GetWeapon(wpIdx)->WeaponType; if (pWpn->OmniFire) { - if (WeaponTypeExt::ExtMap.Find(pWpn)->OmniFire_TurnToTarget.Get() && !pThis->Locomotor->Is_Moving_Now()) + if (WeaponTypeExt::Fetch(pWpn)->OmniFire_TurnToTarget.Get() && !pThis->Locomotor->Is_Moving_Now()) { CoordStruct& source = pThis->Location; const CoordStruct target = pThis->Target->GetCoords(); @@ -152,7 +152,7 @@ DEFINE_HOOK(0x736BA3, UnitClass_UpdateRotation_TurretFacing_Jumpjet, 0x6) DEFINE_HOOK(0x54CB0E, JumpjetLocomotionClass_State5_CrashSpin, 0x7) { GET(JumpjetLocomotionClass*, pThis, EDI); - auto const pTypeExt = TechnoExt::ExtMap.Find(pThis->LinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pThis->LinkedTo)->TypeExtData; return pTypeExt->JumpjetRotateOnCrash ? 0 : 0x54CB3E; } @@ -182,7 +182,7 @@ DEFINE_HOOK(0x54DAC4, JumpjetLocomotionClass_EndPiggyback_Blyat, 0x6) { GET(FootClass*, pLinkedTo, EAX); const auto pType = pLinkedTo->GetTechnoType(); - const auto pExt = TechnoExt::ExtMap.Find(pLinkedTo); + const auto pExt = TechnoExt::Fetch(pLinkedTo); pExt->JumpjetSpeed = pType->JumpjetSpeed; pLinkedTo->PrimaryFacing.SetROT(pType->ROT); @@ -254,7 +254,7 @@ int JumpjetRushHelpers::JumpjetLocomotionPredictHeight(JumpjetLocomotionClass* p { const auto pFoot = pThis->LinkedTo; const auto pLocation = &pFoot->Location; - const bool ignoreOccupy = TechnoExt::ExtMap.Find(pFoot)->TypeExtData->JumpjetClimbIgnoreBuilding.Get(RulesExt::Global()->JumpjetClimbIgnoreBuilding); + const bool ignoreOccupy = TechnoExt::Fetch(pFoot)->TypeExtData->JumpjetClimbIgnoreBuilding.Get(RulesExt::Global()->JumpjetClimbIgnoreBuilding); constexpr int shift = 8; // >> shift -> / Unsorted::LeptonsPerCell constexpr auto point2Cell = [](const Point2D& point) -> CellStruct @@ -388,7 +388,7 @@ DEFINE_HOOK(0x54BBD0, JumpjetLocomotionClass_Ascending_JumpjetStraightAscend, 0x GET(JumpjetLocomotionClass*, pThis, ESI); - auto const pTechnoExt = TechnoExt::ExtMap.Find(pThis->LinkedTo); + auto const pTechnoExt = TechnoExt::Fetch(pThis->LinkedTo); if (pTechnoExt->JumpjetStraightAscend) return SkipGameCode; @@ -405,7 +405,7 @@ DEFINE_HOOK(0x54D600, JumpjetLocomotionClass_MovementAI_JumpjetStraightAscend, 0 GET(JumpjetLocomotionClass*, pThis, ESI); auto const pLinkedTo = pThis->LinkedTo; - auto const pTechnoExt = TechnoExt::ExtMap.Find(pLinkedTo); + auto const pTechnoExt = TechnoExt::Fetch(pLinkedTo); if (pTechnoExt->JumpjetStraightAscend) { @@ -438,7 +438,7 @@ namespace JumpjetClimbIgnoreBuilding DEFINE_HOOK(0x54D820, JumpjetLocomotionClass_GetFloorZ_SetContext, 0x6) { GET(JumpjetLocomotionClass*, pThis, ESI); - JumpjetClimbIgnoreBuilding::Ignore = TechnoExt::ExtMap.Find(pThis->LinkedTo)->TypeExtData->JumpjetClimbIgnoreBuilding.Get(RulesExt::Global()->JumpjetClimbIgnoreBuilding); + JumpjetClimbIgnoreBuilding::Ignore = TechnoExt::Fetch(pThis->LinkedTo)->TypeExtData->JumpjetClimbIgnoreBuilding.Get(RulesExt::Global()->JumpjetClimbIgnoreBuilding); if (JumpjetClimbIgnoreBuilding::Ignore) JumpjetClimbIgnoreBuilding::Z = MapClass::Instance.GetCellFloorHeight(pThis->LinkedTo->Location); @@ -464,12 +464,12 @@ DEFINE_HOOK(0x54AD41, JumpjetLocomotionClass_Link_To_Object_LocomotorWarhead, 0x GET(ILocomotion*, pThis, EBP); GET(FootClass*, pLinkedTo, EBX); const auto pLoco = static_cast(pThis); - const auto pLinkedToExt = TechnoExt::ExtMap.Find(pLinkedTo); + const auto pLinkedToExt = TechnoExt::Fetch(pLinkedTo); const auto pType = pLinkedTo->GetTechnoType(); if (const auto pLocomotorWarhead = WarheadTypeExt::LocomotorWarhead) { - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pLocomotorWarhead); + const auto pWHExt = WarheadTypeExt::Fetch(pLocomotorWarhead); pLoco->TurnRate = pWHExt->JumpjetTurnRate.Get(pType->JumpjetTurnRate); pLoco->Speed = pLinkedToExt->JumpjetSpeed = pWHExt->JumpjetSpeed.Get(pType->JumpjetSpeed); pLoco->Climb = pWHExt->JumpjetClimb.Get(pType->JumpjetClimb); diff --git a/src/Ext/Unit/Hooks.SimpleDeployer.cpp b/src/Ext/Unit/Hooks.SimpleDeployer.cpp index a15829ec24..a0335e2552 100644 --- a/src/Ext/Unit/Hooks.SimpleDeployer.cpp +++ b/src/Ext/Unit/Hooks.SimpleDeployer.cpp @@ -7,7 +7,7 @@ static __forceinline bool HasDeployingAnim(TechnoTypeClass* pType) { - return pType->DeployingAnim || TechnoTypeExt::ExtMap.Find(pType)->DeployingAnims.size() > 0; + return pType->DeployingAnim || TechnoTypeExt::Fetch(pType)->DeployingAnims.size() > 0; } static inline bool CheckRestrictions(FootClass* pUnit, bool isDeploying) @@ -33,7 +33,7 @@ static inline bool CheckRestrictions(FootClass* pUnit, bool isDeploying) } // Facing restrictions. - auto const pTypeExt = TechnoExt::ExtMap.Find(pUnit)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pUnit)->TypeExtData; auto const defaultFacing = (FacingType)(RulesClass::Instance->DeployDir >> 5); auto const facing = pTypeExt->DeployDir.Get(defaultFacing); @@ -70,7 +70,7 @@ static inline void CreateDeployingAnim(UnitClass* pUnit, bool isDeploying) if (!pUnit->DeployAnim) { auto const pType = pUnit->Type; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); auto pAnimType = pType->DeployingAnim; if (pTypeExt->DeployingAnims.size() > 0) @@ -82,8 +82,8 @@ static inline void CreateDeployingAnim(UnitClass* pUnit, bool isDeploying) pUnit->DeployAnim = pAnim; pAnim->SetOwnerObject(pUnit); AnimExt::SetAnimOwnerHouseKind(pAnim, pUnit->Owner, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pUnit); - auto const pExt = TechnoExt::ExtMap.Find(pUnit); + AnimExt::Fetch(pAnim)->SetInvoker(pUnit); + auto const pExt = TechnoExt::Fetch(pUnit); if (pTypeExt->DeployingAnim_UseUnitDrawer) { @@ -127,7 +127,7 @@ DEFINE_HOOK(0x739AC0, UnitClass_SimpleDeployer_Deploy, 0x6) if (pThis->Deploying && pThis->DeployAnim) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto& timer = pExt->SimpleDeployerAnimationTimer; if (timer.Completed()) @@ -158,7 +158,7 @@ DEFINE_HOOK(0x739AC0, UnitClass_SimpleDeployer_Deploy, 0x6) if (pThis->Deployed) { int maxAmmo = -1; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TechnoTypeExt::Fetch(pThis->Type); if (AresFunctions::ConvertTypeTo && pTypeExt->Convert_Deploy) maxAmmo = pTypeExt->Convert_Deploy->Ammo; @@ -188,7 +188,7 @@ DEFINE_HOOK(0x739CD0, UnitClass_SimpleDeployer_Undeploy, 0x6) { if (pThis->Undeploying && pThis->DeployAnim) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pExt = TechnoExt::Fetch(pThis); auto& timer = pExt->SimpleDeployerAnimationTimer; if (timer.Completed()) @@ -383,7 +383,7 @@ DEFINE_HOOK(0x73CF46, UnitClass_Draw_It_KeepUnitVisible, 0x6) if (pThis->Deploying || pThis->Undeploying) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = TechnoTypeExt::Fetch(pThis->Type); if (pTypeExt->DeployingAnim_KeepUnitVisible || (pThis->Deploying && !pThis->DeployAnim)) return Continue; diff --git a/src/Ext/Unit/Hooks.Sinking.cpp b/src/Ext/Unit/Hooks.Sinking.cpp index 023fa66466..e44fb42a4f 100644 --- a/src/Ext/Unit/Hooks.Sinking.cpp +++ b/src/Ext/Unit/Hooks.Sinking.cpp @@ -5,7 +5,7 @@ DEFINE_HOOK(0x7364DC, UnitClass_Update_SinkSpeed, 0x7) GET(UnitClass* const, pThis, ESI); GET(const int, CoordZ, EDX); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type); + auto const pTypeExt = TechnoTypeExt::Fetch(pThis->Type); R->EDX(CoordZ - (pTypeExt->SinkSpeed - 5)); return 0; } @@ -16,7 +16,7 @@ DEFINE_HOOK(0x737DE2, UnitClass_ReceiveDamage_Sinkable, 0x6) GET(UnitTypeClass*, pType, EAX); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); const bool shouldSink = pType->Weight > RulesClass::Instance->ShipSinkingWeight && pType->Naval && !pType->Underwater && !pType->Organic; return pTypeExt->Sinkable.Get(shouldSink) ? GoOtherChecks : NoSink; @@ -29,7 +29,7 @@ DEFINE_HOOK(0x629C67, ParasiteClass_UpdateSquid_SinkableBySquid, 0x9) GET(ParasiteClass*, pThis, ESI); GET(FootClass*, pVictim, EDI); - const auto pVictimTypeExt = TechnoExt::ExtMap.Find(pVictim)->TypeExtData; + const auto pVictimTypeExt = TechnoExt::Fetch(pVictim)->TypeExtData; const auto pOwner = pThis->Owner; if (pVictimTypeExt->Sinkable_SquidGrab || pVictim->WhatAmI() != AbstractType::Unit) diff --git a/src/Ext/Unit/Hooks.Unload.cpp b/src/Ext/Unit/Hooks.Unload.cpp index 75480d4762..becd72acc3 100644 --- a/src/Ext/Unit/Hooks.Unload.cpp +++ b/src/Ext/Unit/Hooks.Unload.cpp @@ -5,7 +5,7 @@ namespace UnitUnloadTemp { - TechnoTypeExt::ExtData* TypeExtData = nullptr; + TechnoTypeExt* TypeExtData = nullptr; } // Prevent subterranean units from deploying while underground. @@ -22,7 +22,7 @@ DEFINE_HOOK(0x73D63B, UnitClass_Mi_Unload_Subterranean, 0x6) } auto const pType = pThis->Type; - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); UnitUnloadTemp::TypeExtData = pTypeExt; // It should be the highest priority. @@ -81,7 +81,7 @@ DEFINE_HOOK(0x740015, UnitClass_MouseOverObject_SkipPassengers, 0x6) GET(UnitClass* const, pThis, ESI); GET(UnitTypeClass* const, pType, EAX); - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + auto const pTypeExt = TechnoTypeExt::Fetch(pType); return pTypeExt->Deploy_SkipPassengerUnload || (pTypeExt->Deploy_NoPassenger && pThis->Passengers.NumPassengers <= 0) diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index e2c8b1bad1..dcec40ba6a 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -4,7 +4,7 @@ DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) { GET(UnitTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.Adopt(new UnitTypeClassExtension(pItem)); + TechnoTypeExt::ExtMap.Adopt(new UnitTypeExt(pItem)); return 0; } diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h index 22f49da7d2..e8d8733851 100644 --- a/src/Ext/UnitType/Body.h +++ b/src/Ext/UnitType/Body.h @@ -3,11 +3,11 @@ #include #include -// Concrete leaf extension for UnitTypeClass (empty; techno-type data lives in TechnoTypeClassExtension). -class UnitTypeClassExtension : public TechnoTypeClassExtension +// Concrete leaf extension for UnitTypeClass (empty; techno-type data lives in TechnoTypeExt). +class UnitTypeExt : public TechnoTypeExt { public: - explicit UnitTypeClassExtension(UnitTypeClass* const OwnerObject) : TechnoTypeClassExtension(OwnerObject) + explicit UnitTypeExt(UnitTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) { } UnitTypeClass* OwnerObject() const diff --git a/src/Ext/VoxelAnim/Body.cpp b/src/Ext/VoxelAnim/Body.cpp index bceae1b6d1..aa30687687 100644 --- a/src/Ext/VoxelAnim/Body.cpp +++ b/src/Ext/VoxelAnim/Body.cpp @@ -4,12 +4,12 @@ VoxelAnimExt::ExtContainer VoxelAnimExt::ExtMap; void VoxelAnimExt::InitializeLaserTrails(VoxelAnimClass* pThis) { - const auto pThisExt = VoxelAnimExt::ExtMap.Find(pThis); + const auto pThisExt = VoxelAnimExt::Fetch(pThis); if (pThisExt->LaserTrails.size()) return; - const auto pTypeExt = VoxelAnimTypeExt::ExtMap.Find(pThis->Type); + const auto pTypeExt = VoxelAnimTypeExt::Fetch(pThis->Type); const auto pOwner = pThis->OwnerHouse; pThisExt->LaserTrails.reserve(pTypeExt->LaserTrail_Types.size()); @@ -17,12 +17,12 @@ void VoxelAnimExt::InitializeLaserTrails(VoxelAnimClass* pThis) pThisExt->LaserTrails.emplace_back(std::make_unique(LaserTrailTypeClass::Array[idxTrail].get(), pOwner)); } -void VoxelAnimExt::ExtData::Initialize() { } +void VoxelAnimExt::Initialize() { } // ============================= // load / save template -void VoxelAnimExt::ExtData::Serialize(T& Stm) +void VoxelAnimExt::Serialize(T& Stm) { Stm .Process(this->LaserTrails) @@ -30,15 +30,15 @@ void VoxelAnimExt::ExtData::Serialize(T& Stm) ; } -void VoxelAnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void VoxelAnimExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectClassExtension::LoadFromStream(Stm); + ObjectExt::LoadFromStream(Stm); this->Serialize(Stm); } -void VoxelAnimExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void VoxelAnimExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectClassExtension::SaveToStream(Stm); + ObjectExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index 70c567ac76..d9bcca7c65 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -6,42 +6,41 @@ #include #include -class VoxelAnimExt +class VoxelAnimExt final : public ObjectExt { public: using base_type = VoxelAnimClass; + using ExtData = VoxelAnimExt; static constexpr DWORD Canary = 0xAAAAAACC; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectClassExtension +public: + // typed owner accessor + VoxelAnimClass* OwnerObject() const { - public: - // typed owner accessor - VoxelAnimClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } + return static_cast(this->GetAttachedObject()); + } - std::vector> LaserTrails; - CDTimerClass TrailerSpawnTimer; + std::vector> LaserTrails; + CDTimerClass TrailerSpawnTimer; - ExtData(VoxelAnimClass* OwnerObject) : ObjectClassExtension(OwnerObject) - , LaserTrails() - , TrailerSpawnTimer() - { } + VoxelAnimExt(VoxelAnimClass* OwnerObject) : ObjectExt(OwnerObject) + , LaserTrails() + , TrailerSpawnTimer() + { } - virtual ~ExtData() = default; - virtual void LoadFromStream(PhobosStreamReader& Stm)override; - virtual void SaveToStream(PhobosStreamWriter& Stm)override; - virtual void Initialize() override; + virtual ~VoxelAnimExt() = default; + virtual void LoadFromStream(PhobosStreamReader& Stm)override; + virtual void SaveToStream(PhobosStreamWriter& Stm)override; + virtual void Initialize() override; - private: - template - void Serialize(T& Stm); - }; +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -50,10 +49,18 @@ class VoxelAnimExt }; static ExtContainer ExtMap; + + static VoxelAnimExt* Fetch(const VoxelAnimClass* pThis) + { + return ExtMap.Find(pThis); + } + + static VoxelAnimExt* TryFetch(const VoxelAnimClass* pThis) + { + return ExtMap.TryFind(pThis); + } static void InitializeLaserTrails(VoxelAnimClass* pThis); static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the VoxelAnimExt extension -using VoxelAnimClassExtension = VoxelAnimExt::ExtData; diff --git a/src/Ext/VoxelAnim/Hooks.cpp b/src/Ext/VoxelAnim/Hooks.cpp index 498680292a..44777aae49 100644 --- a/src/Ext/VoxelAnim/Hooks.cpp +++ b/src/Ext/VoxelAnim/Hooks.cpp @@ -6,8 +6,8 @@ DEFINE_HOOK(0x74A70E, VoxelAnimClass_AI_Additional, 0xC) { GET(VoxelAnimClass* const, pThis, EBX); - //auto pTypeExt = VoxelAnimTypeExt::ExtMap.Find(pThis->Type); - const auto pThisExt = VoxelAnimExt::ExtMap.Find(pThis); + //auto pTypeExt = VoxelAnimTypeExt::Fetch(pThis->Type); + const auto pThisExt = VoxelAnimExt::Fetch(pThis); if (!pThisExt->LaserTrails.empty()) { @@ -35,7 +35,7 @@ DEFINE_HOOK(0x74A027, VoxelAnimClass_AI_Expired, 0x6) const bool heightFlag = flag & 0xFF; auto const pType = pThis->Type; - auto const pTypeExt = VoxelAnimTypeExt::ExtMap.Find(pType); + auto const pTypeExt = VoxelAnimTypeExt::Fetch(pType); auto const splashAnims = pTypeExt->SplashAnims.GetElements(RulesClass::Instance->SplashList); AnimExt::HandleDebrisImpact(pType->ExpireAnim, pTypeExt->WakeAnim, splashAnims, pThis->OwnerHouse, pType->Warhead, pType->Damage, @@ -54,11 +54,11 @@ DEFINE_HOOK(0x74A70E, VoxelAnimClass_AI_Trailer, 0x6) if (const auto pAnimType = pType->TrailerAnim) { - const auto pExt = VoxelAnimExt::ExtMap.Find(pThis); + const auto pExt = VoxelAnimExt::Fetch(pThis); if (pExt->TrailerSpawnTimer.Expired()) { - pExt->TrailerSpawnTimer.Start(VoxelAnimTypeExt::ExtMap.Find(pType)->Trailer_SpawnDelay.Get()); + pExt->TrailerSpawnTimer.Start(VoxelAnimTypeExt::Fetch(pType)->Trailer_SpawnDelay.Get()); auto const pTrailerAnim = GameCreate(pAnimType, pThis->Location, 1, 1); AnimExt::SetAnimOwnerHouseKind(pTrailerAnim, pThis->OwnerHouse, nullptr, false, true); } diff --git a/src/Ext/VoxelAnimType/Body.cpp b/src/Ext/VoxelAnimType/Body.cpp index 9f7fc518fa..792e5df808 100644 --- a/src/Ext/VoxelAnimType/Body.cpp +++ b/src/Ext/VoxelAnimType/Body.cpp @@ -2,9 +2,9 @@ VoxelAnimTypeExt::ExtContainer VoxelAnimTypeExt::ExtMap; -void VoxelAnimTypeExt::ExtData::Initialize() { } +void VoxelAnimTypeExt::Initialize() { } -void VoxelAnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI) +void VoxelAnimTypeExt::LoadFromINIFile(CCINIClass* pINI) { const char* pID = this->OwnerObject()->ID; INI_EX exINI(pINI); @@ -21,7 +21,7 @@ void VoxelAnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI) // ============================= // load / save template -void VoxelAnimTypeExt::ExtData::Serialize(T& Stm) +void VoxelAnimTypeExt::Serialize(T& Stm) { Stm .Process(LaserTrail_Types) @@ -34,15 +34,15 @@ void VoxelAnimTypeExt::ExtData::Serialize(T& Stm) ; } -void VoxelAnimTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void VoxelAnimTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - ObjectTypeClassExtension::LoadFromStream(Stm); + ObjectTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void VoxelAnimTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void VoxelAnimTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - ObjectTypeClassExtension::SaveToStream(Stm); + ObjectTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 2b60491e99..4477ee3c2a 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -7,54 +7,53 @@ #include -class VoxelAnimTypeExt +class VoxelAnimTypeExt final : public ObjectTypeExt { public: using base_type = VoxelAnimTypeClass; + using ExtData = VoxelAnimTypeExt; static constexpr DWORD Canary = 0xAAAEEEEE; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public ObjectTypeClassExtension +public: + // typed owner accessor + VoxelAnimTypeClass* OwnerObject() const { - public: - // typed owner accessor - VoxelAnimTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - - ValueableIdxVector LaserTrail_Types; - Valueable ExplodeOnWater; - Valueable Warhead_Detonate; - ValueableVector WakeAnim; - NullableVector SplashAnims; - Valueable SplashAnims_PickRandom; - Valueable Trailer_SpawnDelay; - - ExtData(VoxelAnimTypeClass* OwnerObject) : ObjectTypeClassExtension(OwnerObject) - , LaserTrail_Types() - , ExplodeOnWater { false } - , Warhead_Detonate { false } - , WakeAnim {} - , SplashAnims {} - , SplashAnims_PickRandom { false } - , Trailer_SpawnDelay { 2 } - { } - - virtual ~ExtData() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; - - virtual void Initialize() override; - virtual void LoadFromStream(PhobosStreamReader& Stm)override; - virtual void SaveToStream(PhobosStreamWriter& Stm)override; - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + + ValueableIdxVector LaserTrail_Types; + Valueable ExplodeOnWater; + Valueable Warhead_Detonate; + ValueableVector WakeAnim; + NullableVector SplashAnims; + Valueable SplashAnims_PickRandom; + Valueable Trailer_SpawnDelay; + + VoxelAnimTypeExt(VoxelAnimTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) + , LaserTrail_Types() + , ExplodeOnWater { false } + , Warhead_Detonate { false } + , WakeAnim {} + , SplashAnims {} + , SplashAnims_PickRandom { false } + , Trailer_SpawnDelay { 2 } + { } + + virtual ~VoxelAnimTypeExt() = default; + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; + virtual void LoadFromStream(PhobosStreamReader& Stm)override; + virtual void SaveToStream(PhobosStreamWriter& Stm)override; + +private: + template + void Serialize(T& Stm); + +public: class ExtContainer final : public Container { public: @@ -64,9 +63,17 @@ class VoxelAnimTypeExt static ExtContainer ExtMap; + static VoxelAnimTypeExt* Fetch(const VoxelAnimTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static VoxelAnimTypeExt* TryFetch(const VoxelAnimTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); }; -// top-level name for the VoxelAnimTypeExt extension -using VoxelAnimTypeClassExtension = VoxelAnimTypeExt::ExtData; diff --git a/src/Ext/WarheadType/Body.cpp b/src/Ext/WarheadType/Body.cpp index a8d94cf97c..74823e16bb 100644 --- a/src/Ext/WarheadType/Body.cpp +++ b/src/Ext/WarheadType/Body.cpp @@ -6,7 +6,7 @@ WarheadTypeExt::ExtContainer WarheadTypeExt::ExtMap; WarheadTypeClass* WarheadTypeExt::LocomotorWarhead = nullptr; -bool WarheadTypeExt::ExtData::CanTargetHouse(HouseClass* pHouse, TechnoClass* pTarget) const +bool WarheadTypeExt::CanTargetHouse(HouseClass* pHouse, TechnoClass* pTarget) const { if (pHouse && pTarget) { @@ -34,7 +34,7 @@ bool WarheadTypeExt::ExtData::CanTargetHouse(HouseClass* pHouse, TechnoClass* pT return true; } -bool WarheadTypeExt::ExtData::CanAffectTarget(TechnoClass* pTarget) const +bool WarheadTypeExt::CanAffectTarget(TechnoClass* pTarget) const { if (!IsHealthInThreshold(pTarget)) return false; @@ -56,7 +56,7 @@ bool WarheadTypeExt::ExtData::CanAffectTarget(TechnoClass* pTarget) const return GeneralUtils::GetWarheadVersusArmor(this->OwnerObject(), pTarget, pTarget->GetTechnoType()) != 0.0; } -bool WarheadTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const +bool WarheadTypeExt::IsHealthInThreshold(TechnoClass* pTarget) const { if (!this->HealthCheck) return true; @@ -64,7 +64,7 @@ bool WarheadTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const return TechnoExt::IsHealthInThreshold(pTarget, this->AffectsAbovePercent, this->AffectsBelowPercent); } -bool WarheadTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const +bool WarheadTypeExt::IsVeterancyInThreshold(TechnoClass* pTarget) const { if (!this->VeterancyCheck) return true; @@ -72,7 +72,7 @@ bool WarheadTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const return EnumFunctions::CanTargetVeterancy(this->AffectsVeterancy, pTarget); } -bool WarheadTypeExt::ExtData::IsInvokerAllowed(TechnoClass* pTarget, TechnoClass* pInvoker) const +bool WarheadTypeExt::IsInvokerAllowed(TechnoClass* pTarget, TechnoClass* pInvoker) const { if (!this->AffectsInvokerOnly) return true; @@ -91,7 +91,7 @@ bool WarheadTypeExt::ExtData::IsInvokerAllowed(TechnoClass* pTarget, TechnoClass } // Checks if Warhead can affect target that might or might be currently invulnerable. -bool WarheadTypeExt::ExtData::CanAffectInvulnerable(TechnoClass* pTarget) const +bool WarheadTypeExt::CanAffectInvulnerable(TechnoClass* pTarget) const { if (!pTarget->IsIronCurtained()) return true; @@ -109,7 +109,7 @@ void WarheadTypeExt::DetonateAt(WarheadTypeClass* pThis, const CoordStruct& coor BulletExt::Detonate(coords, pOwner, damage, pFiringHouse, pTarget, pThis->Bright, nullptr, pThis); } -bool WarheadTypeExt::ExtData::EligibleForFullMapDetonation(TechnoClass* pTechno, TechnoTypeClass* pType, HouseClass* pOwner) const +bool WarheadTypeExt::EligibleForFullMapDetonation(TechnoClass* pTechno, TechnoTypeClass* pType, HouseClass* pOwner) const { if (!pTechno || !pTechno->IsOnMap || !pTechno->IsAlive || pTechno->InLimbo || pTechno->IsSinking) return false; @@ -132,10 +132,10 @@ bool WarheadTypeExt::ExtData::EligibleForFullMapDetonation(TechnoClass* pTechno, return true; } -// Wrapper for MapClass::DamageArea() that sets a pointer in WarheadTypeExt::ExtData that is used to figure 'intended' target of the Warhead detonation, if set and there's no CellSpread. -DamageAreaResult WarheadTypeExt::ExtData::DamageAreaWithTarget(const CoordStruct& coords, int damage, TechnoClass* pSource, WarheadTypeClass* pWH, bool affectsTiberium, HouseClass* pSourceHouse, TechnoClass* pTarget) +// Wrapper for MapClass::DamageArea() that sets a pointer in WarheadTypeExt that is used to figure 'intended' target of the Warhead detonation, if set and there's no CellSpread. +DamageAreaResult WarheadTypeExt::DamageAreaWithTarget(const CoordStruct& coords, int damage, TechnoClass* pSource, WarheadTypeClass* pWH, bool affectsTiberium, HouseClass* pSourceHouse, TechnoClass* pTarget) { - auto const pWarheadTypeExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWarheadTypeExt = WarheadTypeExt::Fetch(pWH); pWarheadTypeExt->DamageAreaTarget = pTarget; pWarheadTypeExt->DamageAreaInvoker = pSource; auto const result = MapClass::DamageArea(coords, damage, pSource, pWH, affectsTiberium, pSourceHouse); @@ -147,7 +147,7 @@ DamageAreaResult WarheadTypeExt::ExtData::DamageAreaWithTarget(const CoordStruct // ============================= // load / save -void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void WarheadTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -478,7 +478,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->Damage_Deployed.Read(exINI, pSection, "Damage.Deployed"); // List all Warheads here that respect CellSpread - // Used in WarheadTypeExt::ExtData::Detonate + // Used in WarheadTypeExt::Detonate this->PossibleCellSpreadDetonate = ( this->RemoveDisguise || this->RemoveMindControl @@ -542,7 +542,7 @@ void WarheadTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) } template -void WarheadTypeExt::ExtData::Serialize(T& Stm) +void WarheadTypeExt::Serialize(T& Stm) { Stm .Process(this->Reveal) @@ -794,15 +794,15 @@ void WarheadTypeExt::ExtData::Serialize(T& Stm) ; } -void WarheadTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void WarheadTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void WarheadTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void WarheadTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 1f0c0ca6bb..47bb7864b3 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -7,585 +7,584 @@ #include #include -class WarheadTypeExt +class WarheadTypeExt final : public AbstractTypeExt { public: using base_type = WarheadTypeClass; + using ExtData = WarheadTypeExt; static constexpr DWORD Canary = 0x22222222; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + WarheadTypeClass* OwnerObject() const { - public: - // typed owner accessor - WarheadTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - - Valueable Reveal; - Valueable CreateGap; - Valueable TransactMoney; - Valueable TransactMoney_Display; - Valueable TransactMoney_Display_Houses; - Valueable TransactMoney_Display_AtFirer; - Valueable TransactMoney_Display_Offset; - NullableVector SplashList; - Valueable SplashList_PickRandom; - Valueable SplashList_CreateAll; - Valueable SplashList_CreationInterval; - Valueable SplashList_ScatterMin; - Valueable SplashList_ScatterMax; - Valueable AnimList_PickRandom; - Valueable AnimList_CreateAll; - Valueable AnimList_CreationInterval; - Valueable AnimList_ScatterMin; - Valueable AnimList_ScatterMax; - Valueable CreateAnimsOnZeroDamage; - Valueable Conventional_IgnoreUnits; - Valueable RemoveDisguise; - Valueable RemoveMindControl; - Nullable RemoveMindControl_Silent; - Nullable RemoveParasite; - ValueableVector RemoveParasite_Allow; - ValueableVector RemoveParasite_Disallow; - Valueable DecloakDamagedTargets; - Valueable ShakeIsLocal; - Valueable ApplyModifiersOnNegativeDamage; - Valueable PenetratesIronCurtain; - Nullable PenetratesForceShield; - Valueable Rocker_AmplitudeMultiplier; - Nullable Rocker_AmplitudeOverride; - Nullable Temporal_ApplyVersus; - Nullable Temporal_ApplyMultiplier; - - Valueable Crit_Chance; - Valueable Crit_ApplyChancePerTarget; - Valueable Crit_ExtraDamage; - Valueable Crit_ExtraDamage_ApplyFirepowerMult; - Valueable Crit_Warhead; - Valueable Crit_Warhead_FullDetonation; - Valueable Crit_AffectsTarget; - Valueable Crit_AffectsHouse; - ValueableVector Crit_AnimList; - Nullable Crit_AnimList_PickRandom; - Nullable Crit_AnimList_CreateAll; - ValueableVector Crit_ActiveChanceAnims; - Valueable Crit_AnimOnAffectedTargets; - Valueable Crit_AffectsBelowPercent; - Valueable Crit_AffectsAbovePercent; - Valueable Crit_SuppressWhenIntercepted; - - Valueable ReturnWarhead; - Valueable ReturnWarhead_Damage; - Valueable ReturnWarhead_Chance; - Valueable ReturnWarhead_ApplyChancePerTarget; - Valueable ReturnWarhead_FullDetonation; - Valueable ReturnWarhead_AffectsTarget; - Valueable ReturnWarhead_AffectsHouse; - - Nullable MindControl_Anim; - Nullable MindControl_ThreatDelay; - - Valueable Shield_Penetrate; - Valueable Shield_Break; - ValueableVector Shield_BreakAnim; - ValueableVector Shield_HitAnim; - Valueable Shield_SkipHitAnim; - Valueable Shield_HitFlash; - Nullable Shield_BreakWeapon; - - Nullable Shield_AbsorbPercent; - Nullable Shield_PassPercent; - Nullable Shield_ReceivedDamage_Minimum; - Nullable Shield_ReceivedDamage_Maximum; - Valueable Shield_ReceivedDamage_MinMultiplier; - Valueable Shield_ReceivedDamage_MaxMultiplier; - - 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; - Nullable Shield_SelfHealing_RestartInCombat; - Valueable Shield_SelfHealing_RestartInCombatDelay; - Valueable Shield_SelfHealing_RestartTimer; - - std::vector SpawnsCrate_Types; - std::vector SpawnsCrate_Weights; - - ValueableVector Shield_AttachTypes; - ValueableVector Shield_RemoveTypes; - Valueable Shield_RemoveAll; - Valueable Shield_ReplaceOnly; - Valueable Shield_ReplaceNonRespawning; - Valueable Shield_InheritStateOnReplace; - Valueable Shield_MinimumReplaceDelay; - ValueableVector Shield_AffectTypes; - NullableVector Shield_Penetrate_Types; - NullableVector Shield_Break_Types; - NullableVector Shield_Respawn_Types; - NullableVector Shield_SelfHealing_Types; - - Valueable NotHuman_DeathSequence; - ValueableIdxVector LaunchSW; - Valueable LaunchSW_RealLaunch; - Valueable LaunchSW_IgnoreInhibitors; - Valueable LaunchSW_IgnoreDesignators; - Valueable LaunchSW_DisplayMoney; - Valueable LaunchSW_DisplayMoney_Houses; - Valueable LaunchSW_DisplayMoney_Offset; - - Valueable AllowDamageOnSelf; - NullableVector DebrisAnims; - Valueable Debris_Conventional; - Nullable DebrisTypes_Limit; - ValueableVector DebrisMinimums; - - Valueable DetonateOnAllMapObjects; - Valueable DetonateOnAllMapObjects_Full; - Valueable DetonateOnAllMapObjects_RequireVerses; - Valueable DetonateOnAllMapObjects_AffectsTarget; - Valueable DetonateOnAllMapObjects_AffectsHouse; - ValueableVector DetonateOnAllMapObjects_AffectTypes; - ValueableVector DetonateOnAllMapObjects_IgnoreTypes; - - std::vector Convert_Pairs; - AEAttachInfoTypeClass AttachEffects; - - Valueable InflictLocomotor; - Valueable RemoveInflictedLocomotor; - - Nullable Parasite_ParticleSystem; - Valueable Parasite_DisableParticleSystem; - Valueable Parasite_CullingTarget; - NullableIdx Parasite_GrappleAnim; - - Nullable JumpjetTurnRate; - Nullable JumpjetSpeed; - Nullable JumpjetClimb; - Nullable JumpjetCrash; - Nullable JumpjetHeight; - Nullable JumpjetAccel; - Nullable JumpjetWobbles; - Nullable JumpjetNoWobbles; - Nullable JumpjetDeviation; - - Valueable Nonprovocative; - - Nullable MergeBuildingDamage; - - Nullable CombatLightDetailLevel; - Nullable CombatLightDetailLevel_CheckColored; - Valueable CombatLightChance; - Valueable CLIsBlack; - Nullable Particle_AlphaImageIsLightFlash; - - Nullable DamageOwnerMultiplier; - Nullable DamageAlliesMultiplier; - Nullable DamageEnemiesMultiplier; - Nullable DamageOwnerMultiplier_Berzerk; - Nullable DamageAlliesMultiplier_Berzerk; - Nullable DamageEnemiesMultiplier_Berzerk; - Valueable DamageSourceHealthMultiplier; - Valueable DamageTargetHealthMultiplier; - - Valueable SuppressRevengeWeapons; - ValueableVector SuppressRevengeWeapons_Types; - Valueable SuppressReflectDamage; - ValueableVector SuppressReflectDamage_Types; - std::vector SuppressReflectDamage_Groups; - - Valueable BuildingSell; - Valueable BuildingSell_IgnoreUnsellable; - Valueable BuildingUndeploy; - Valueable BuildingUndeploy_Leave; - - Nullable CombatAlert_Suppress; - - Valueable KillWeapon; - Valueable KillWeapon_OnFirer; - Valueable KillWeapon_AffectsHouse; - Valueable KillWeapon_OnFirer_AffectsHouse; - Valueable KillWeapon_AffectsTarget; - Valueable KillWeapon_OnFirer_AffectsTarget; - - Valueable ElectricAssaultLevel; - - Valueable AirstrikeTargets; - - Valueable AffectsBelowPercent; - Valueable AffectsAbovePercent; - Valueable AffectsVeterancy; - - Valueable AffectsNeutral; - Valueable AffectsGround; - Valueable AffectsAir; - Valueable CellSpread_Cylinder; - Valueable AffectsInvokerOnly; - Valueable AffectsInvokerOnly_Reverse; - Nullable AffectsInvokerOnly_IgnoreInvokerState; - - Valueable ReverseEngineer; - - Valueable CanKill; - - Valueable UnlimboDetonate; - Valueable UnlimboDetonate_ForceLocation; - Valueable UnlimboDetonate_KeepTarget; - Valueable UnlimboDetonate_KeepSelected; - - Valueable AffectsUnderground; - Valueable PlayAnimUnderground; - Valueable PlayAnimAboveSurface; - - Nullable AnimZAdjust; - - Nullable ApplyPerTargetEffectsOnDetonate; - - Valueable PenetratesTransport_Level; - Valueable PenetratesTransport_PassThrough; - Valueable PenetratesTransport_FatalRate; - Valueable PenetratesTransport_DamageMultiplier; - Valueable PenetratesTransport_DamageAll; - ValueableIdx PenetratesTransport_CleanSound; - - Valueable Taunt; - - // Ares tags - // http://ares-developers.github.io/Ares-docs/new/warheads/general.html - Valueable AffectsEnemies; - Nullable AffectsOwner; - Valueable EffectsRequireVerses; - Valueable Malicious; - Nullable Flash_Duration; - Valueable Damage_Deployed { 1.0 }; - - double Crit_RandomBuffer; - double Crit_CurrentChance; - bool Crit_Active; - double ReturnWarhead_RandomBuffer; - bool InDamageArea; - bool WasDetonatedOnAllMapObjects; - bool Splashed; - bool Reflected; - int RemainingAnimCreationInterval; - bool PossibleCellSpreadDetonate; - bool HealthCheck; - bool VeterancyCheck; - TechnoClass* DamageAreaTarget; - mutable TechnoClass* DamageAreaInvoker; - - private: - Valueable Shield_Respawn_Rate_InMinutes; - Valueable Shield_SelfHealing_Rate_InMinutes; + return static_cast(this->GetAttachedObject()); + } + + + Valueable Reveal; + Valueable CreateGap; + Valueable TransactMoney; + Valueable TransactMoney_Display; + Valueable TransactMoney_Display_Houses; + Valueable TransactMoney_Display_AtFirer; + Valueable TransactMoney_Display_Offset; + NullableVector SplashList; + Valueable SplashList_PickRandom; + Valueable SplashList_CreateAll; + Valueable SplashList_CreationInterval; + Valueable SplashList_ScatterMin; + Valueable SplashList_ScatterMax; + Valueable AnimList_PickRandom; + Valueable AnimList_CreateAll; + Valueable AnimList_CreationInterval; + Valueable AnimList_ScatterMin; + Valueable AnimList_ScatterMax; + Valueable CreateAnimsOnZeroDamage; + Valueable Conventional_IgnoreUnits; + Valueable RemoveDisguise; + Valueable RemoveMindControl; + Nullable RemoveMindControl_Silent; + Nullable RemoveParasite; + ValueableVector RemoveParasite_Allow; + ValueableVector RemoveParasite_Disallow; + Valueable DecloakDamagedTargets; + Valueable ShakeIsLocal; + Valueable ApplyModifiersOnNegativeDamage; + Valueable PenetratesIronCurtain; + Nullable PenetratesForceShield; + Valueable Rocker_AmplitudeMultiplier; + Nullable Rocker_AmplitudeOverride; + Nullable Temporal_ApplyVersus; + Nullable Temporal_ApplyMultiplier; + + Valueable Crit_Chance; + Valueable Crit_ApplyChancePerTarget; + Valueable Crit_ExtraDamage; + Valueable Crit_ExtraDamage_ApplyFirepowerMult; + Valueable Crit_Warhead; + Valueable Crit_Warhead_FullDetonation; + Valueable Crit_AffectsTarget; + Valueable Crit_AffectsHouse; + ValueableVector Crit_AnimList; + Nullable Crit_AnimList_PickRandom; + Nullable Crit_AnimList_CreateAll; + ValueableVector Crit_ActiveChanceAnims; + Valueable Crit_AnimOnAffectedTargets; + Valueable Crit_AffectsBelowPercent; + Valueable Crit_AffectsAbovePercent; + Valueable Crit_SuppressWhenIntercepted; + + Valueable ReturnWarhead; + Valueable ReturnWarhead_Damage; + Valueable ReturnWarhead_Chance; + Valueable ReturnWarhead_ApplyChancePerTarget; + Valueable ReturnWarhead_FullDetonation; + Valueable ReturnWarhead_AffectsTarget; + Valueable ReturnWarhead_AffectsHouse; + + Nullable MindControl_Anim; + Nullable MindControl_ThreatDelay; + + Valueable Shield_Penetrate; + Valueable Shield_Break; + ValueableVector Shield_BreakAnim; + ValueableVector Shield_HitAnim; + Valueable Shield_SkipHitAnim; + Valueable Shield_HitFlash; + Nullable Shield_BreakWeapon; + + Nullable Shield_AbsorbPercent; + Nullable Shield_PassPercent; + Nullable Shield_ReceivedDamage_Minimum; + Nullable Shield_ReceivedDamage_Maximum; + Valueable Shield_ReceivedDamage_MinMultiplier; + Valueable Shield_ReceivedDamage_MaxMultiplier; + + 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; + Nullable Shield_SelfHealing_RestartInCombat; + Valueable Shield_SelfHealing_RestartInCombatDelay; + Valueable Shield_SelfHealing_RestartTimer; + + std::vector SpawnsCrate_Types; + std::vector SpawnsCrate_Weights; + + ValueableVector Shield_AttachTypes; + ValueableVector Shield_RemoveTypes; + Valueable Shield_RemoveAll; + Valueable Shield_ReplaceOnly; + Valueable Shield_ReplaceNonRespawning; + Valueable Shield_InheritStateOnReplace; + Valueable Shield_MinimumReplaceDelay; + ValueableVector Shield_AffectTypes; + NullableVector Shield_Penetrate_Types; + NullableVector Shield_Break_Types; + NullableVector Shield_Respawn_Types; + NullableVector Shield_SelfHealing_Types; + + Valueable NotHuman_DeathSequence; + ValueableIdxVector LaunchSW; + Valueable LaunchSW_RealLaunch; + Valueable LaunchSW_IgnoreInhibitors; + Valueable LaunchSW_IgnoreDesignators; + Valueable LaunchSW_DisplayMoney; + Valueable LaunchSW_DisplayMoney_Houses; + Valueable LaunchSW_DisplayMoney_Offset; + + Valueable AllowDamageOnSelf; + NullableVector DebrisAnims; + Valueable Debris_Conventional; + Nullable DebrisTypes_Limit; + ValueableVector DebrisMinimums; + + Valueable DetonateOnAllMapObjects; + Valueable DetonateOnAllMapObjects_Full; + Valueable DetonateOnAllMapObjects_RequireVerses; + Valueable DetonateOnAllMapObjects_AffectsTarget; + Valueable DetonateOnAllMapObjects_AffectsHouse; + ValueableVector DetonateOnAllMapObjects_AffectTypes; + ValueableVector DetonateOnAllMapObjects_IgnoreTypes; + + std::vector Convert_Pairs; + AEAttachInfoTypeClass AttachEffects; + + Valueable InflictLocomotor; + Valueable RemoveInflictedLocomotor; + + Nullable Parasite_ParticleSystem; + Valueable Parasite_DisableParticleSystem; + Valueable Parasite_CullingTarget; + NullableIdx Parasite_GrappleAnim; + + Nullable JumpjetTurnRate; + Nullable JumpjetSpeed; + Nullable JumpjetClimb; + Nullable JumpjetCrash; + Nullable JumpjetHeight; + Nullable JumpjetAccel; + Nullable JumpjetWobbles; + Nullable JumpjetNoWobbles; + Nullable JumpjetDeviation; + + Valueable Nonprovocative; + + Nullable MergeBuildingDamage; + + Nullable CombatLightDetailLevel; + Nullable CombatLightDetailLevel_CheckColored; + Valueable CombatLightChance; + Valueable CLIsBlack; + Nullable Particle_AlphaImageIsLightFlash; + + Nullable DamageOwnerMultiplier; + Nullable DamageAlliesMultiplier; + Nullable DamageEnemiesMultiplier; + Nullable DamageOwnerMultiplier_Berzerk; + Nullable DamageAlliesMultiplier_Berzerk; + Nullable DamageEnemiesMultiplier_Berzerk; + Valueable DamageSourceHealthMultiplier; + Valueable DamageTargetHealthMultiplier; + + Valueable SuppressRevengeWeapons; + ValueableVector SuppressRevengeWeapons_Types; + Valueable SuppressReflectDamage; + ValueableVector SuppressReflectDamage_Types; + std::vector SuppressReflectDamage_Groups; + + Valueable BuildingSell; + Valueable BuildingSell_IgnoreUnsellable; + Valueable BuildingUndeploy; + Valueable BuildingUndeploy_Leave; + + Nullable CombatAlert_Suppress; + + Valueable KillWeapon; + Valueable KillWeapon_OnFirer; + Valueable KillWeapon_AffectsHouse; + Valueable KillWeapon_OnFirer_AffectsHouse; + Valueable KillWeapon_AffectsTarget; + Valueable KillWeapon_OnFirer_AffectsTarget; + + Valueable ElectricAssaultLevel; + + Valueable AirstrikeTargets; + + Valueable AffectsBelowPercent; + Valueable AffectsAbovePercent; + Valueable AffectsVeterancy; + + Valueable AffectsNeutral; + Valueable AffectsGround; + Valueable AffectsAir; + Valueable CellSpread_Cylinder; + Valueable AffectsInvokerOnly; + Valueable AffectsInvokerOnly_Reverse; + Nullable AffectsInvokerOnly_IgnoreInvokerState; + + Valueable ReverseEngineer; + + Valueable CanKill; + + Valueable UnlimboDetonate; + Valueable UnlimboDetonate_ForceLocation; + Valueable UnlimboDetonate_KeepTarget; + Valueable UnlimboDetonate_KeepSelected; + + Valueable AffectsUnderground; + Valueable PlayAnimUnderground; + Valueable PlayAnimAboveSurface; + + Nullable AnimZAdjust; + + Nullable ApplyPerTargetEffectsOnDetonate; + + Valueable PenetratesTransport_Level; + Valueable PenetratesTransport_PassThrough; + Valueable PenetratesTransport_FatalRate; + Valueable PenetratesTransport_DamageMultiplier; + Valueable PenetratesTransport_DamageAll; + ValueableIdx PenetratesTransport_CleanSound; + + Valueable Taunt; + + // Ares tags + // http://ares-developers.github.io/Ares-docs/new/warheads/general.html + Valueable AffectsEnemies; + Nullable AffectsOwner; + Valueable EffectsRequireVerses; + Valueable Malicious; + Nullable Flash_Duration; + Valueable Damage_Deployed { 1.0 }; + + double Crit_RandomBuffer; + double Crit_CurrentChance; + bool Crit_Active; + double ReturnWarhead_RandomBuffer; + bool InDamageArea; + bool WasDetonatedOnAllMapObjects; + bool Splashed; + bool Reflected; + int RemainingAnimCreationInterval; + bool PossibleCellSpreadDetonate; + bool HealthCheck; + bool VeterancyCheck; + TechnoClass* DamageAreaTarget; + mutable TechnoClass* DamageAreaInvoker; + +private: + Valueable Shield_Respawn_Rate_InMinutes; + Valueable Shield_SelfHealing_Rate_InMinutes; - public: - ExtData(WarheadTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , Reveal { 0 } - , CreateGap { 0 } - , TransactMoney { 0 } - , TransactMoney_Display { false } - , TransactMoney_Display_Houses { AffectedHouse::All } - , TransactMoney_Display_AtFirer { false } - , TransactMoney_Display_Offset { { 0, 0 } } - , SplashList {} - , SplashList_PickRandom { false } - , SplashList_CreateAll { false } - , SplashList_CreationInterval { 0 } - , SplashList_ScatterMin { Leptons(-1) } - , SplashList_ScatterMax { Leptons(-1) } - , AnimList_PickRandom { false } - , AnimList_CreateAll { false } - , AnimList_CreationInterval { 0 } - , AnimList_ScatterMin { Leptons(-1) } - , AnimList_ScatterMax { Leptons(-1) } - , CreateAnimsOnZeroDamage { false } - , Conventional_IgnoreUnits { false } - , RemoveDisguise { false } - , RemoveMindControl { false } - , RemoveMindControl_Silent {} - , RemoveParasite {} - , RemoveParasite_Allow {} - , RemoveParasite_Disallow {} - , DecloakDamagedTargets { true } - , ShakeIsLocal { false } - , ApplyModifiersOnNegativeDamage { false } - , PenetratesIronCurtain { false } - , PenetratesForceShield {} - , Rocker_AmplitudeMultiplier { 1.0 } - , Rocker_AmplitudeOverride {} - , Temporal_ApplyVersus {} - , Temporal_ApplyMultiplier {} - - , Crit_Chance { 0.0 } - , Crit_ApplyChancePerTarget { false } - , Crit_ExtraDamage { 0 } - , Crit_ExtraDamage_ApplyFirepowerMult { false } - , Crit_Warhead {} - , Crit_Warhead_FullDetonation { true } - , Crit_AffectsTarget { AffectedTarget::All } - , Crit_AffectsHouse { AffectedHouse::All } - , Crit_AnimList {} - , Crit_AnimList_PickRandom {} - , Crit_AnimList_CreateAll {} - , Crit_ActiveChanceAnims {} - , Crit_AnimOnAffectedTargets { false } - , Crit_AffectsBelowPercent { 1.0 } - , Crit_AffectsAbovePercent { 0.0 } - , Crit_SuppressWhenIntercepted { false } - - , ReturnWarhead {} - , ReturnWarhead_Damage { 0 } - , ReturnWarhead_Chance { 1.0 } - , ReturnWarhead_ApplyChancePerTarget { false } - , ReturnWarhead_FullDetonation { true } - , ReturnWarhead_AffectsTarget { AffectedTarget::All } - , ReturnWarhead_AffectsHouse { AffectedHouse::All } - - , MindControl_Anim {} - , MindControl_ThreatDelay {} - - , Shield_Penetrate { false } - , Shield_Break { false } - , Shield_BreakAnim {} - , Shield_HitAnim {} - , Shield_SkipHitAnim { false } - , Shield_HitFlash { true } - , Shield_BreakWeapon {} - , Shield_AbsorbPercent {} - , Shield_PassPercent {} - , Shield_ReceivedDamage_Minimum {} - , Shield_ReceivedDamage_Maximum {} - , Shield_ReceivedDamage_MinMultiplier { 1.0 } - , Shield_ReceivedDamage_MaxMultiplier { 1.0 } - - , Shield_Respawn_Duration { 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 } - , Shield_SelfHealing_Rate_InMinutes { -1.0 } - , Shield_SelfHealing_RestartInCombat {} - , Shield_SelfHealing_RestartInCombatDelay { -1 } - , Shield_SelfHealing_RestartTimer { false } - , Shield_AttachTypes {} - , Shield_RemoveTypes {} - , Shield_RemoveAll { false } - , Shield_ReplaceOnly { false } - , Shield_ReplaceNonRespawning { false } - , Shield_InheritStateOnReplace { false } - , Shield_MinimumReplaceDelay { 0 } - , Shield_AffectTypes {} - , Shield_Penetrate_Types {} - , Shield_Break_Types {} - , Shield_Respawn_Types {} - , Shield_SelfHealing_Types {} - - , SpawnsCrate_Types {} - , SpawnsCrate_Weights {} - - , NotHuman_DeathSequence { -1 } - , LaunchSW {} - , LaunchSW_RealLaunch { true } - , LaunchSW_IgnoreInhibitors { false } - , LaunchSW_IgnoreDesignators { true } - , LaunchSW_DisplayMoney { false } - , LaunchSW_DisplayMoney_Houses { AffectedHouse::All } - , LaunchSW_DisplayMoney_Offset { { 0, 0 } } - - , AllowDamageOnSelf { false } - , DebrisAnims {} - , Debris_Conventional { false } - , DebrisTypes_Limit {} - , DebrisMinimums {} - - , DetonateOnAllMapObjects { false } - , DetonateOnAllMapObjects_Full { true } - , DetonateOnAllMapObjects_RequireVerses { false } - , DetonateOnAllMapObjects_AffectsTarget { AffectedTarget::None } - , DetonateOnAllMapObjects_AffectsHouse { AffectedHouse::None } - , DetonateOnAllMapObjects_AffectTypes {} - , DetonateOnAllMapObjects_IgnoreTypes {} - - , Convert_Pairs {} - , AttachEffects {} - - , InflictLocomotor { false } - , RemoveInflictedLocomotor { false } - - , Parasite_ParticleSystem {} - , Parasite_DisableParticleSystem { false } - , Parasite_CullingTarget { AffectedTarget::Infantry } - , Parasite_GrappleAnim {} - - , JumpjetTurnRate {} - , JumpjetSpeed {} - , JumpjetClimb {} - , JumpjetCrash {} - , JumpjetHeight {} - , JumpjetAccel {} - , JumpjetWobbles {} - , JumpjetNoWobbles {} - , JumpjetDeviation {} - - , Nonprovocative { false } - - , MergeBuildingDamage {} - - , CombatLightDetailLevel {} - , CombatLightDetailLevel_CheckColored {} - , CombatLightChance { 1.0 } - , CLIsBlack { false } - , Particle_AlphaImageIsLightFlash {} - - , DamageOwnerMultiplier {} - , DamageAlliesMultiplier {} - , DamageEnemiesMultiplier {} - , DamageOwnerMultiplier_Berzerk {} - , DamageAlliesMultiplier_Berzerk {} - , DamageEnemiesMultiplier_Berzerk {} - , DamageSourceHealthMultiplier { 0.0 } - , DamageTargetHealthMultiplier { 0.0 } - - , SuppressRevengeWeapons { false } - , SuppressRevengeWeapons_Types {} - , SuppressReflectDamage { false } - , SuppressReflectDamage_Types {} - , SuppressReflectDamage_Groups {} - - , BuildingSell { false } - , BuildingSell_IgnoreUnsellable { false } - , BuildingUndeploy { false } - , BuildingUndeploy_Leave { false } - - , CombatAlert_Suppress {} - - , ElectricAssaultLevel { 1 } - - , AirstrikeTargets { AffectedTarget::Building } - - , AffectsBelowPercent { 1.0 } - , AffectsAbovePercent { 0.0 } - , AffectsVeterancy { AffectedVeterancy::All } - , AffectsNeutral { true } - , AffectsGround { true } - , AffectsAir { true } - , CellSpread_Cylinder { false } - , AffectsInvokerOnly { false } - , AffectsInvokerOnly_Reverse { false } - , AffectsInvokerOnly_IgnoreInvokerState {} - - , PenetratesTransport_Level { 0 } - , PenetratesTransport_PassThrough { 1.0 } - , PenetratesTransport_FatalRate { 0.0 } - , PenetratesTransport_DamageMultiplier { 1.0 } - , PenetratesTransport_DamageAll { false } - , PenetratesTransport_CleanSound { -1 } - - , AffectsEnemies { true } - , AffectsOwner {} - , EffectsRequireVerses { true } - , Malicious { true } - , Flash_Duration {} - - , Crit_RandomBuffer { 0.0 } - , Crit_CurrentChance { 0.0 } - , Crit_Active { false } - , ReturnWarhead_RandomBuffer { 0.0 } - , InDamageArea { true } - , WasDetonatedOnAllMapObjects { false } - , Splashed { false } - , Reflected { false } - , RemainingAnimCreationInterval { 0 } - , PossibleCellSpreadDetonate { false } - , HealthCheck { false } - , VeterancyCheck { false } - , DamageAreaTarget {} - , DamageAreaInvoker {} - - , CanKill { true } - - , KillWeapon {} - , KillWeapon_OnFirer {} - , KillWeapon_AffectsHouse { AffectedHouse::All } - , KillWeapon_OnFirer_AffectsHouse { AffectedHouse::All } - , KillWeapon_AffectsTarget { AffectedTarget::All } - , KillWeapon_OnFirer_AffectsTarget { AffectedTarget::All } - - , ReverseEngineer { false } - - , UnlimboDetonate { false } - , UnlimboDetonate_ForceLocation { false } - , UnlimboDetonate_KeepTarget { true } - , UnlimboDetonate_KeepSelected { true } - - , AffectsUnderground { false } - , PlayAnimUnderground { true } - , PlayAnimAboveSurface { false } - - , AnimZAdjust {} - - , ApplyPerTargetEffectsOnDetonate {} - - , Taunt { false } - { } - - void ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget); - void ApplyLocomotorInfliction(TechnoClass* pTarget); - void ApplyLocomotorInflictionReset(TechnoClass* pTarget); - public: - bool CanTargetHouse(HouseClass* pHouse, TechnoClass* pTechno) const; - bool CanAffectTarget(TechnoClass* pTarget) const; - bool CanAffectInvulnerable(TechnoClass* pTarget) const; - bool EligibleForFullMapDetonation(TechnoClass* pTechno, TechnoTypeClass* pType, HouseClass* pOwner) const; - bool IsHealthInThreshold(TechnoClass* pTarget) const; - bool IsVeterancyInThreshold(TechnoClass* pTarget) const; - bool IsInvokerAllowed(TechnoClass* pTarget, TechnoClass* pInvoker) const; - - virtual ~ExtData() = default; - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); +public: + WarheadTypeExt(WarheadTypeClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , Reveal { 0 } + , CreateGap { 0 } + , TransactMoney { 0 } + , TransactMoney_Display { false } + , TransactMoney_Display_Houses { AffectedHouse::All } + , TransactMoney_Display_AtFirer { false } + , TransactMoney_Display_Offset { { 0, 0 } } + , SplashList {} + , SplashList_PickRandom { false } + , SplashList_CreateAll { false } + , SplashList_CreationInterval { 0 } + , SplashList_ScatterMin { Leptons(-1) } + , SplashList_ScatterMax { Leptons(-1) } + , AnimList_PickRandom { false } + , AnimList_CreateAll { false } + , AnimList_CreationInterval { 0 } + , AnimList_ScatterMin { Leptons(-1) } + , AnimList_ScatterMax { Leptons(-1) } + , CreateAnimsOnZeroDamage { false } + , Conventional_IgnoreUnits { false } + , RemoveDisguise { false } + , RemoveMindControl { false } + , RemoveMindControl_Silent {} + , RemoveParasite {} + , RemoveParasite_Allow {} + , RemoveParasite_Disallow {} + , DecloakDamagedTargets { true } + , ShakeIsLocal { false } + , ApplyModifiersOnNegativeDamage { false } + , PenetratesIronCurtain { false } + , PenetratesForceShield {} + , Rocker_AmplitudeMultiplier { 1.0 } + , Rocker_AmplitudeOverride {} + , Temporal_ApplyVersus {} + , Temporal_ApplyMultiplier {} + + , Crit_Chance { 0.0 } + , Crit_ApplyChancePerTarget { false } + , Crit_ExtraDamage { 0 } + , Crit_ExtraDamage_ApplyFirepowerMult { false } + , Crit_Warhead {} + , Crit_Warhead_FullDetonation { true } + , Crit_AffectsTarget { AffectedTarget::All } + , Crit_AffectsHouse { AffectedHouse::All } + , Crit_AnimList {} + , Crit_AnimList_PickRandom {} + , Crit_AnimList_CreateAll {} + , Crit_ActiveChanceAnims {} + , Crit_AnimOnAffectedTargets { false } + , Crit_AffectsBelowPercent { 1.0 } + , Crit_AffectsAbovePercent { 0.0 } + , Crit_SuppressWhenIntercepted { false } + + , ReturnWarhead {} + , ReturnWarhead_Damage { 0 } + , ReturnWarhead_Chance { 1.0 } + , ReturnWarhead_ApplyChancePerTarget { false } + , ReturnWarhead_FullDetonation { true } + , ReturnWarhead_AffectsTarget { AffectedTarget::All } + , ReturnWarhead_AffectsHouse { AffectedHouse::All } + + , MindControl_Anim {} + , MindControl_ThreatDelay {} + + , Shield_Penetrate { false } + , Shield_Break { false } + , Shield_BreakAnim {} + , Shield_HitAnim {} + , Shield_SkipHitAnim { false } + , Shield_HitFlash { true } + , Shield_BreakWeapon {} + , Shield_AbsorbPercent {} + , Shield_PassPercent {} + , Shield_ReceivedDamage_Minimum {} + , Shield_ReceivedDamage_Maximum {} + , Shield_ReceivedDamage_MinMultiplier { 1.0 } + , Shield_ReceivedDamage_MaxMultiplier { 1.0 } + + , Shield_Respawn_Duration { 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 } + , Shield_SelfHealing_Rate_InMinutes { -1.0 } + , Shield_SelfHealing_RestartInCombat {} + , Shield_SelfHealing_RestartInCombatDelay { -1 } + , Shield_SelfHealing_RestartTimer { false } + , Shield_AttachTypes {} + , Shield_RemoveTypes {} + , Shield_RemoveAll { false } + , Shield_ReplaceOnly { false } + , Shield_ReplaceNonRespawning { false } + , Shield_InheritStateOnReplace { false } + , Shield_MinimumReplaceDelay { 0 } + , Shield_AffectTypes {} + , Shield_Penetrate_Types {} + , Shield_Break_Types {} + , Shield_Respawn_Types {} + , Shield_SelfHealing_Types {} + + , SpawnsCrate_Types {} + , SpawnsCrate_Weights {} + + , NotHuman_DeathSequence { -1 } + , LaunchSW {} + , LaunchSW_RealLaunch { true } + , LaunchSW_IgnoreInhibitors { false } + , LaunchSW_IgnoreDesignators { true } + , LaunchSW_DisplayMoney { false } + , LaunchSW_DisplayMoney_Houses { AffectedHouse::All } + , LaunchSW_DisplayMoney_Offset { { 0, 0 } } + + , AllowDamageOnSelf { false } + , DebrisAnims {} + , Debris_Conventional { false } + , DebrisTypes_Limit {} + , DebrisMinimums {} + + , DetonateOnAllMapObjects { false } + , DetonateOnAllMapObjects_Full { true } + , DetonateOnAllMapObjects_RequireVerses { false } + , DetonateOnAllMapObjects_AffectsTarget { AffectedTarget::None } + , DetonateOnAllMapObjects_AffectsHouse { AffectedHouse::None } + , DetonateOnAllMapObjects_AffectTypes {} + , DetonateOnAllMapObjects_IgnoreTypes {} + + , Convert_Pairs {} + , AttachEffects {} + + , InflictLocomotor { false } + , RemoveInflictedLocomotor { false } + + , Parasite_ParticleSystem {} + , Parasite_DisableParticleSystem { false } + , Parasite_CullingTarget { AffectedTarget::Infantry } + , Parasite_GrappleAnim {} + + , JumpjetTurnRate {} + , JumpjetSpeed {} + , JumpjetClimb {} + , JumpjetCrash {} + , JumpjetHeight {} + , JumpjetAccel {} + , JumpjetWobbles {} + , JumpjetNoWobbles {} + , JumpjetDeviation {} + + , Nonprovocative { false } + + , MergeBuildingDamage {} + + , CombatLightDetailLevel {} + , CombatLightDetailLevel_CheckColored {} + , CombatLightChance { 1.0 } + , CLIsBlack { false } + , Particle_AlphaImageIsLightFlash {} + + , DamageOwnerMultiplier {} + , DamageAlliesMultiplier {} + , DamageEnemiesMultiplier {} + , DamageOwnerMultiplier_Berzerk {} + , DamageAlliesMultiplier_Berzerk {} + , DamageEnemiesMultiplier_Berzerk {} + , DamageSourceHealthMultiplier { 0.0 } + , DamageTargetHealthMultiplier { 0.0 } + + , SuppressRevengeWeapons { false } + , SuppressRevengeWeapons_Types {} + , SuppressReflectDamage { false } + , SuppressReflectDamage_Types {} + , SuppressReflectDamage_Groups {} + + , BuildingSell { false } + , BuildingSell_IgnoreUnsellable { false } + , BuildingUndeploy { false } + , BuildingUndeploy_Leave { false } + + , CombatAlert_Suppress {} + + , ElectricAssaultLevel { 1 } + + , AirstrikeTargets { AffectedTarget::Building } + + , AffectsBelowPercent { 1.0 } + , AffectsAbovePercent { 0.0 } + , AffectsVeterancy { AffectedVeterancy::All } + , AffectsNeutral { true } + , AffectsGround { true } + , AffectsAir { true } + , CellSpread_Cylinder { false } + , AffectsInvokerOnly { false } + , AffectsInvokerOnly_Reverse { false } + , AffectsInvokerOnly_IgnoreInvokerState {} + + , PenetratesTransport_Level { 0 } + , PenetratesTransport_PassThrough { 1.0 } + , PenetratesTransport_FatalRate { 0.0 } + , PenetratesTransport_DamageMultiplier { 1.0 } + , PenetratesTransport_DamageAll { false } + , PenetratesTransport_CleanSound { -1 } + + , AffectsEnemies { true } + , AffectsOwner {} + , EffectsRequireVerses { true } + , Malicious { true } + , Flash_Duration {} + + , Crit_RandomBuffer { 0.0 } + , Crit_CurrentChance { 0.0 } + , Crit_Active { false } + , ReturnWarhead_RandomBuffer { 0.0 } + , InDamageArea { true } + , WasDetonatedOnAllMapObjects { false } + , Splashed { false } + , Reflected { false } + , RemainingAnimCreationInterval { 0 } + , PossibleCellSpreadDetonate { false } + , HealthCheck { false } + , VeterancyCheck { false } + , DamageAreaTarget {} + , DamageAreaInvoker {} + + , CanKill { true } + + , KillWeapon {} + , KillWeapon_OnFirer {} + , KillWeapon_AffectsHouse { AffectedHouse::All } + , KillWeapon_OnFirer_AffectsHouse { AffectedHouse::All } + , KillWeapon_AffectsTarget { AffectedTarget::All } + , KillWeapon_OnFirer_AffectsTarget { AffectedTarget::All } + + , ReverseEngineer { false } + + , UnlimboDetonate { false } + , UnlimboDetonate_ForceLocation { false } + , UnlimboDetonate_KeepTarget { true } + , UnlimboDetonate_KeepSelected { true } + + , AffectsUnderground { false } + , PlayAnimUnderground { true } + , PlayAnimAboveSurface { false } + + , AnimZAdjust {} + + , ApplyPerTargetEffectsOnDetonate {} + + , Taunt { false } + { } + + void ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget); + void ApplyLocomotorInfliction(TechnoClass* pTarget); + void ApplyLocomotorInflictionReset(TechnoClass* pTarget); +public: + bool CanTargetHouse(HouseClass* pHouse, TechnoClass* pTechno) const; + bool CanAffectTarget(TechnoClass* pTarget) const; + bool CanAffectInvulnerable(TechnoClass* pTarget) const; + bool EligibleForFullMapDetonation(TechnoClass* pTechno, TechnoTypeClass* pType, HouseClass* pOwner) const; + bool IsHealthInThreshold(TechnoClass* pTarget) const; + bool IsVeterancyInThreshold(TechnoClass* pTarget) const; + bool IsInvokerAllowed(TechnoClass* pTarget, TechnoClass* pInvoker) const; + + virtual ~WarheadTypeExt() = default; + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); - public: - // Detonate.cpp - void Detonate(TechnoClass* pOwner, HouseClass* pHouse, BulletExt::ExtData* pBullet, CoordStruct coords); - void DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, const CoordStruct& coords, int damage, TechnoClass* pOwner, BulletExt::ExtData* pBulletExt, bool bulletWasIntercepted = false, int distance = -1); - void InterceptBullets(TechnoClass* pOwner, BulletClass* pInterceptor, const CoordStruct& coords); - DamageAreaResult DamageAreaWithTarget(const CoordStruct& coords, int damage, TechnoClass* pSource, WarheadTypeClass* pWH, bool affectsTiberium, HouseClass* pSourceHouse, TechnoClass* pTarget); - private: - void ApplyRemoveDisguise(TechnoClass* pTarget); - HouseClass* ApplyRemoveMindControl(HouseClass* pHouse, TechnoClass* pTarget); - void ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* Owner, BulletExt::ExtData* pBulletExt); - void ApplyShieldModifiers(TechnoClass* pTarget); - void ApplyAttachEffects(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker); - void ApplyBuildingUndeploy(TechnoClass* pTarget); - void ApplyReverseEngineer(HouseClass* pHouse, TechnoClass* pTarget); - void ApplyReturnWarhead(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* Owner); - void ApplyPenetratesTransport(TechnoClass* pTarget, TechnoClass* pInvoker, HouseClass* pInvokerHouse, const CoordStruct& coords, int damage, int distance); - double GetCritChance(TechnoClass* pFirer) const; - }; +public: + // Detonate.cpp + void Detonate(TechnoClass* pOwner, HouseClass* pHouse, BulletExt* pBullet, CoordStruct coords); + void DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, const CoordStruct& coords, int damage, TechnoClass* pOwner, BulletExt* pBulletExt, bool bulletWasIntercepted = false, int distance = -1); + void InterceptBullets(TechnoClass* pOwner, BulletClass* pInterceptor, const CoordStruct& coords); + DamageAreaResult DamageAreaWithTarget(const CoordStruct& coords, int damage, TechnoClass* pSource, WarheadTypeClass* pWH, bool affectsTiberium, HouseClass* pSourceHouse, TechnoClass* pTarget); +private: + void ApplyRemoveDisguise(TechnoClass* pTarget); + HouseClass* ApplyRemoveMindControl(HouseClass* pHouse, TechnoClass* pTarget); + void ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* Owner, BulletExt* pBulletExt); + void ApplyShieldModifiers(TechnoClass* pTarget); + void ApplyAttachEffects(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker); + void ApplyBuildingUndeploy(TechnoClass* pTarget); + void ApplyReverseEngineer(HouseClass* pHouse, TechnoClass* pTarget); + void ApplyReturnWarhead(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* Owner); + void ApplyPenetratesTransport(TechnoClass* pTarget, TechnoClass* pInvoker, HouseClass* pInvokerHouse, const CoordStruct& coords, int damage, int distance); + double GetCritChance(TechnoClass* pFirer) const; +public: class ExtContainer final : public Container { public: @@ -594,6 +593,16 @@ class WarheadTypeExt }; static ExtContainer ExtMap; + + static WarheadTypeExt* Fetch(const WarheadTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static WarheadTypeExt* TryFetch(const WarheadTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -603,5 +612,3 @@ class WarheadTypeExt static void DetonateAt(WarheadTypeClass* pThis, const CoordStruct& coords, TechnoClass* pOwner, int damage, HouseClass* pFiringHouse = nullptr, AbstractClass* pTarget = nullptr); }; -// top-level name for the WarheadTypeExt extension -using WarheadTypeClassExtension = WarheadTypeExt::ExtData; diff --git a/src/Ext/WarheadType/Detonate.cpp b/src/Ext/WarheadType/Detonate.cpp index 47599cbc24..9ebaf5284e 100644 --- a/src/Ext/WarheadType/Detonate.cpp +++ b/src/Ext/WarheadType/Detonate.cpp @@ -23,10 +23,10 @@ static void __stdcall Sub_4ADCD0(char a1, DWORD a2) struct InvokerGuard { - WarheadTypeExt::ExtData* pExt; + WarheadTypeExt* pExt; TechnoClass* pOldInvoker; - InvokerGuard(WarheadTypeExt::ExtData* pExt, TechnoClass* pInvoker) + InvokerGuard(WarheadTypeExt* pExt, TechnoClass* pInvoker) : pExt(pExt), pOldInvoker(pExt->DamageAreaInvoker) { pExt->DamageAreaInvoker = pInvoker; @@ -38,7 +38,7 @@ struct InvokerGuard } }; -void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse, BulletExt::ExtData* pBulletExt, CoordStruct coords) +void WarheadTypeExt::Detonate(TechnoClass* pOwner, HouseClass* pHouse, BulletExt* pBulletExt, CoordStruct coords) { InvokerGuard guard(this, pOwner); auto const pBullet = pBulletExt ? pBulletExt->OwnerObject() : nullptr; @@ -122,7 +122,7 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse, { if (const auto pSuper = pHouse->Supers.GetItem(swIdx)) { - const auto pSWExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pSWExt = SWTypeExt::Fetch(pSuper->Type); const auto cell = CellClass::Coord2Cell(coords); if (pHouse->CanTransactMoney(pSWExt->Money_Amount) && (!this->LaunchSW_RealLaunch || (pSuper->IsPresent && pSuper->IsReady && !pSuper->IsSuspended))) @@ -195,7 +195,7 @@ void WarheadTypeExt::ExtData::Detonate(TechnoClass* pOwner, HouseClass* pHouse, } } -void WarheadTypeExt::ExtData::DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, const CoordStruct& coords, int damage, TechnoClass* pOwner, BulletExt::ExtData* pBulletExt, bool bulletWasIntercepted, int distance) +void WarheadTypeExt::DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* pTarget, const CoordStruct& coords, int damage, TechnoClass* pOwner, BulletExt* pBulletExt, bool bulletWasIntercepted, int distance) { if (!pTarget || pTarget->InLimbo || !pTarget->IsAlive || !pTarget->Health || pTarget->IsSinking || pTarget->BeingWarpedOut) return; @@ -251,13 +251,13 @@ void WarheadTypeExt::ExtData::DetonateOnOneUnit(HouseClass* pHouse, TechnoClass* } -void WarheadTypeExt::ExtData::ApplyReverseEngineer(HouseClass* pHouse, TechnoClass* pTarget) +void WarheadTypeExt::ApplyReverseEngineer(HouseClass* pHouse, TechnoClass* pTarget) { if (pHouse && !pHouse->Type->MultiplayPassive && AresFunctions::ReverseEngineer) AresFunctions::ReverseEngineer(reinterpret_cast(pHouse->unknown_16084), pTarget->GetTechnoType()); } -void WarheadTypeExt::ExtData::ApplyBuildingUndeploy(TechnoClass* pTarget) +void WarheadTypeExt::ApplyBuildingUndeploy(TechnoClass* pTarget) { const auto pBuilding = abstract_cast(pTarget); @@ -359,9 +359,9 @@ void WarheadTypeExt::ExtData::ApplyBuildingUndeploy(TechnoClass* pTarget) pBuilding->Sell(1); } -void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget) +void WarheadTypeExt::ApplyShieldModifiers(TechnoClass* pTarget) { - auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); + auto const pTargetExt = TechnoExt::Fetch(pTarget); auto& pShield = pTargetExt->Shield; int shieldIndex = -1; double ratio = 1.0; @@ -463,7 +463,7 @@ void WarheadTypeExt::ExtData::ApplyShieldModifiers(TechnoClass* pTarget) } } -void WarheadTypeExt::ExtData::ApplyRemoveDisguise(TechnoClass* pTarget) +void WarheadTypeExt::ApplyRemoveDisguise(TechnoClass* pTarget) { if (pTarget->IsDisguised()) { @@ -474,7 +474,7 @@ void WarheadTypeExt::ExtData::ApplyRemoveDisguise(TechnoClass* pTarget) } } -HouseClass* WarheadTypeExt::ExtData::ApplyRemoveMindControl(HouseClass* pHouse, TechnoClass* pTarget) +HouseClass* WarheadTypeExt::ApplyRemoveMindControl(HouseClass* pHouse, TechnoClass* pTarget) { if (const auto pController = pTarget->MindControlledBy) { @@ -485,14 +485,14 @@ HouseClass* WarheadTypeExt::ExtData::ApplyRemoveMindControl(HouseClass* pHouse, return pHouse; } -void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner, BulletExt::ExtData* pBulletExt) +void WarheadTypeExt::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner, BulletExt* pBulletExt) { const double dice = this->Crit_ApplyChancePerTarget || !this->ApplyPerTargetEffectsOnDetonate.Get(RulesExt::Global()->ApplyPerTargetEffectsOnDetonate) ? ScenarioClass::Instance->Random.RandomDouble() : this->Crit_RandomBuffer; if (this->Crit_CurrentChance < dice) return; - auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); + auto const pTargetExt = TechnoExt::Fetch(pTarget); if (pTargetExt->TypeExtData->ImmuneToCrit) return; @@ -525,7 +525,7 @@ void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget auto const pAnim = GameCreate(this->Crit_AnimList[idx], pTarget->Location); AnimExt::SetAnimOwnerHouseKind(pAnim, pHouse, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pOwner, pHouse); + AnimExt::Fetch(pAnim)->SetInvoker(pOwner, pHouse); } else { @@ -533,7 +533,7 @@ void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget { auto const pAnim = GameCreate(pType, pTarget->Location); AnimExt::SetAnimOwnerHouseKind(pAnim, pHouse, nullptr, false, true); - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pOwner, pHouse); + AnimExt::Fetch(pAnim)->SetInvoker(pOwner, pHouse); } } } @@ -559,7 +559,7 @@ void WarheadTypeExt::ExtData::ApplyCrit(HouseClass* pHouse, TechnoClass* pTarget pTarget->ReceiveDamage(&damage, 0, this->OwnerObject(), pOwner, false, false, pHouse); } -void WarheadTypeExt::ExtData::ApplyReturnWarhead(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner) +void WarheadTypeExt::ApplyReturnWarhead(HouseClass* pHouse, TechnoClass* pTarget, TechnoClass* pOwner) { const double dice = this->ReturnWarhead_ApplyChancePerTarget || !this->ApplyPerTargetEffectsOnDetonate.Get(RulesExt::Global()->ApplyPerTargetEffectsOnDetonate) ? ScenarioClass::Instance->Random.RandomDouble() : this->ReturnWarhead_RandomBuffer; @@ -581,7 +581,7 @@ void WarheadTypeExt::ExtData::ApplyReturnWarhead(HouseClass* pHouse, TechnoClass pOwner->ReceiveDamage(&this->ReturnWarhead_Damage, 0, this->ReturnWarhead, pTarget, false, false, pTarget->Owner); } -void WarheadTypeExt::ExtData::InterceptBullets(TechnoClass* pOwner, BulletClass* pInterceptor, const CoordStruct& coords) +void WarheadTypeExt::InterceptBullets(TechnoClass* pOwner, BulletClass* pInterceptor, const CoordStruct& coords) { const float cellSpread = this->OwnerObject()->CellSpread; @@ -589,7 +589,7 @@ void WarheadTypeExt::ExtData::InterceptBullets(TechnoClass* pOwner, BulletClass* { if (const auto pBullet = abstract_cast(pInterceptor->Target)) { - const auto pBulletExt = BulletExt::ExtMap.Find(pBullet); + const auto pBulletExt = BulletExt::Fetch(pBullet); if (!pBulletExt->TypeExtData->Interceptable) return; @@ -605,7 +605,7 @@ void WarheadTypeExt::ExtData::InterceptBullets(TechnoClass* pOwner, BulletClass* for (const auto& pBullet : BulletClass::Array) { - const auto pBulletExt = BulletExt::ExtMap.Find(pBullet); + const auto pBulletExt = BulletExt::Fetch(pBullet); // Cells don't know about bullets that may or may not be located on them so it has to be this way. if (!pBulletExt->TypeExtData->Interceptable || pBullet->SpawnNextAnim) @@ -617,7 +617,7 @@ void WarheadTypeExt::ExtData::InterceptBullets(TechnoClass* pOwner, BulletClass* } } -void WarheadTypeExt::ExtData::ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget) +void WarheadTypeExt::ApplyConvert(HouseClass* pHouse, TechnoClass* pTarget) { const auto pTargetFoot = abstract_cast(pTarget); @@ -627,7 +627,7 @@ void WarheadTypeExt::ExtData::ApplyConvert(HouseClass* pHouse, TechnoClass* pTar TypeConvertGroup::Convert(pTargetFoot, this->Convert_Pairs, pHouse); } -void WarheadTypeExt::ExtData::ApplyLocomotorInfliction(TechnoClass* pTarget) +void WarheadTypeExt::ApplyLocomotorInfliction(TechnoClass* pTarget) { auto pTargetFoot = abstract_cast(pTarget); @@ -649,7 +649,7 @@ void WarheadTypeExt::ExtData::ApplyLocomotorInfliction(TechnoClass* pTarget) LocomotionClass::ChangeLocomotorTo(pTargetFoot, inflictCLSID); } -void WarheadTypeExt::ExtData::ApplyLocomotorInflictionReset(TechnoClass* pTarget) +void WarheadTypeExt::ApplyLocomotorInflictionReset(TechnoClass* pTarget) { auto pTargetFoot = abstract_cast(pTarget); @@ -674,7 +674,7 @@ void WarheadTypeExt::ExtData::ApplyLocomotorInflictionReset(TechnoClass* pTarget LocomotionClass::End_Piggyback(pTargetFoot->Locomotor); } -void WarheadTypeExt::ExtData::ApplyAttachEffects(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker) +void WarheadTypeExt::ApplyAttachEffects(TechnoClass* pTarget, HouseClass* pInvokerHouse, TechnoClass* pInvoker) { std::vector dummy = std::vector(); auto const& info = this->AttachEffects; @@ -683,14 +683,14 @@ void WarheadTypeExt::ExtData::ApplyAttachEffects(TechnoClass* pTarget, HouseClas AttachEffectClass::DetachByGroups(pTarget, info); } -double WarheadTypeExt::ExtData::GetCritChance(TechnoClass* pFirer) const +double WarheadTypeExt::GetCritChance(TechnoClass* pFirer) const { double critChance = this->Crit_Chance; if (!pFirer) return critChance; - auto const pExt = TechnoExt::ExtMap.Find(pFirer); + auto const pExt = TechnoExt::Fetch(pFirer); if (!pExt->AE.HasCritModifiers) return critChance; @@ -725,7 +725,7 @@ double WarheadTypeExt::ExtData::GetCritChance(TechnoClass* pFirer) const return critChance + extraChance; } -void WarheadTypeExt::ExtData::ApplyPenetratesTransport(TechnoClass* pTarget, TechnoClass* pInvoker, HouseClass* pInvokerHouse, const CoordStruct& coords, int damage, int distance) +void WarheadTypeExt::ApplyPenetratesTransport(TechnoClass* pTarget, TechnoClass* pInvoker, HouseClass* pInvokerHouse, const CoordStruct& coords, int damage, int distance) { auto& passengers = pTarget->Passengers; auto passenger = passengers.GetFirstPassenger(); @@ -733,7 +733,7 @@ void WarheadTypeExt::ExtData::ApplyPenetratesTransport(TechnoClass* pTarget, Tec if (!passenger) return; - const auto pTargetTypeExt = TechnoExt::ExtMap.Find(pTarget)->TypeExtData; + const auto pTargetTypeExt = TechnoExt::Fetch(pTarget)->TypeExtData; const auto pTargetType = pTargetTypeExt->OwnerObject(); if (this->PenetratesTransport_Level <= pTargetTypeExt->PenetratesTransport_Level.Get(RulesExt::Global()->PenetratesTransport_Level)) @@ -763,7 +763,7 @@ void WarheadTypeExt::ExtData::ApplyPenetratesTransport(TechnoClass* pTarget, Tec while (passenger) { const auto nextPassenger = abstract_cast(passenger->NextObject); - const auto pPassengerTypeExt = TechnoExt::ExtMap.Find(passenger)->TypeExtData; + const auto pPassengerTypeExt = TechnoExt::Fetch(passenger)->TypeExtData; const auto pPassengerType = pPassengerTypeExt->OwnerObject(); if (this->PenetratesTransport_Level > pPassengerTypeExt->PenetratesTransport_Level.Get(RulesExt::Global()->PenetratesTransport_Level)) @@ -791,7 +791,7 @@ void WarheadTypeExt::ExtData::ApplyPenetratesTransport(TechnoClass* pTarget, Tec { const auto nextPassenger = abstract_cast(passenger->NextObject); - if (this->PenetratesTransport_Level > TechnoExt::ExtMap.Find(passenger)->TypeExtData->PenetratesTransport_Level.Get(RulesExt::Global()->PenetratesTransport_Level)) + if (this->PenetratesTransport_Level > TechnoExt::Fetch(passenger)->TypeExtData->PenetratesTransport_Level.Get(RulesExt::Global()->PenetratesTransport_Level)) { passenger->SetLocation(transporterCoords); int applyDamage = adjustedDamage; @@ -820,7 +820,7 @@ void WarheadTypeExt::ExtData::ApplyPenetratesTransport(TechnoClass* pTarget, Tec --poorBastardIdx; } - const auto pPassengerTypeExt = TechnoExt::ExtMap.Find(passenger)->TypeExtData; + const auto pPassengerTypeExt = TechnoExt::Fetch(passenger)->TypeExtData; const auto pPassengerType = pPassengerTypeExt->OwnerObject(); if (this->PenetratesTransport_Level <= pPassengerTypeExt->PenetratesTransport_Level.Get(RulesExt::Global()->PenetratesTransport_Level)) diff --git a/src/Ext/WarheadType/Hooks.cpp b/src/Ext/WarheadType/Hooks.cpp index 2b59d709ac..32cce805ab 100644 --- a/src/Ext/WarheadType/Hooks.cpp +++ b/src/Ext/WarheadType/Hooks.cpp @@ -10,8 +10,8 @@ DEFINE_HOOK(0x46920B, BulletClass_Detonate, 0x6) GET(BulletClass* const, pBullet, ESI); GET_BASE(const CoordStruct*, pCoords, 0x8); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pBullet->WH); - auto const pBulletExt = BulletExt::ExtMap.Find(pBullet); + auto const pWHExt = WarheadTypeExt::Fetch(pBullet->WH); + auto const pBulletExt = BulletExt::Fetch(pBullet); auto const pOwner = pBullet->Owner; auto const pHouse = pOwner ? pOwner->Owner : nullptr; auto const pDecidedHouse = pHouse ? pHouse : pBulletExt->FirerHouse; @@ -39,7 +39,7 @@ DEFINE_HOOK(0x4696CE, BulletClass_Detonate_ImbueLocomotor, 0x6) DEFINE_HOOK(0x489286, MapClass_DamageArea, 0x6) { GET_BASE(const WarheadTypeClass*, pWH, 0xC); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->InDamageArea) { @@ -61,7 +61,7 @@ DEFINE_HOOK(0x489430, MapClass_DamageArea_Cylinder_1, 0x7) GET_BASE(WarheadTypeClass* const, pWH, 0xC); GET_STACK(const int, nVictimCrdZ, STACK_OFFSET(0xE0, -0x5C)); - const auto pWHExt = WarheadTypeExt::ExtMap.TryFind(pWH); + const auto pWHExt = WarheadTypeExt::TryFetch(pWH); if (pWHExt && pWHExt->CellSpread_Cylinder) R->EDX(nVictimCrdZ); @@ -75,7 +75,7 @@ DEFINE_HOOK(0x4894C1, MapClass_DamageArea_Cylinder_2, 0x5) GET_BASE(WarheadTypeClass* const, pWH, 0xC); GET(const int, nVictimCrdZ, ESI); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->CellSpread_Cylinder) R->EDX(nVictimCrdZ); @@ -89,7 +89,7 @@ DEFINE_HOOK(0x48979C, MapClass_DamageArea_Cylinder_3, 0x8) GET_BASE(WarheadTypeClass* const, pWH, 0xC); GET(const int, nVictimCrdZ, EDX); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->CellSpread_Cylinder) R->ECX(nVictimCrdZ); @@ -103,7 +103,7 @@ DEFINE_HOOK(0x4897C3, MapClass_DamageArea_Cylinder_4, 0x5) GET_BASE(WarheadTypeClass* const, pWH, 0xC); GET(const int, nVictimCrdZ, EDX); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->CellSpread_Cylinder) R->ECX(nVictimCrdZ); @@ -117,7 +117,7 @@ DEFINE_HOOK(0x48985A, MapClass_DamageArea_Cylinder_5, 0x5) GET_BASE(WarheadTypeClass* const, pWH, 0xC); GET(const int, nVictimCrdZ, EDX); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->CellSpread_Cylinder) R->ECX(nVictimCrdZ); @@ -131,7 +131,7 @@ DEFINE_HOOK(0x4898BF, MapClass_DamageArea_Cylinder_6, 0x5) GET_BASE(WarheadTypeClass* const, pWH, 0xC); GET(const int, nVictimCrdZ, ECX); - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->CellSpread_Cylinder) R->EDX(nVictimCrdZ); @@ -150,7 +150,7 @@ DEFINE_HOOK(0x489416, MapClass_DamageArea_CheckHeight_1, 0x6) if (!pObject) return 0; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->AffectsAir && pWHExt->AffectsGround) return 0; @@ -171,7 +171,7 @@ DEFINE_HOOK(0x489710, MapClass_DamageArea_CheckHeight_2, 0x7) if (!pObject) return 0; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->AffectsAir && pWHExt->AffectsGround) return 0; @@ -189,7 +189,7 @@ DEFINE_HOOK(0x48A551, WarheadTypeClass_AnimList_SplashList, 0x6) GET(WarheadTypeClass* const, pThis, ESI); GET(const int, nDamage, EDI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis); + auto const pWHExt = WarheadTypeExt::Fetch(pThis); auto const animTypes = pWHExt->SplashList.GetElements(RulesClass::Instance->SplashList); pWHExt->Splashed = true; @@ -204,7 +204,7 @@ DEFINE_HOOK(0x48A551, WarheadTypeClass_AnimList_SplashList, 0x6) DEFINE_HOOK(0x48A5BD, SelectDamageAnimation_PickRandom, 0x6) { GET(WarheadTypeClass* const, pThis, ESI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis); + auto const pWHExt = WarheadTypeExt::Fetch(pThis); return pWHExt->AnimList_PickRandom ? 0x48A5C7 : 0; } @@ -214,7 +214,7 @@ DEFINE_HOOK(0x48A5B3, SelectDamageAnimation_CritAnim, 0x6) GET(WarheadTypeClass* const, pThis, ESI); GET(const int, nDamage, EDI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pThis); + auto const pWHExt = WarheadTypeExt::Fetch(pThis); if (pWHExt->Crit_Active && pWHExt->Crit_AnimList.size() && !pWHExt->Crit_AnimOnAffectedTargets) { @@ -235,7 +235,7 @@ DEFINE_HOOK(0x4896EC, Explosion_Damage_DamageSelf, 0x6) GET_BASE(WarheadTypeClass*, pWarhead, 0xC); - if (auto const pWHExt = WarheadTypeExt::ExtMap.TryFind(pWarhead)) + if (auto const pWHExt = WarheadTypeExt::TryFetch(pWarhead)) { if (pWHExt->AllowDamageOnSelf) return SkipCheck; @@ -250,7 +250,7 @@ DEFINE_HOOK(0x44224F, BuildingClass_ReceiveDamage_DamageSelf, 0x5) REF_STACK(args_ReceiveDamage const, receiveDamageArgs, STACK_OFFSET(0x9C, 0x4)); - if (auto const pWHExt = WarheadTypeExt::ExtMap.TryFind(receiveDamageArgs.WH)) + if (auto const pWHExt = WarheadTypeExt::TryFetch(receiveDamageArgs.WH)) { if (pWHExt->AllowDamageOnSelf) return SkipCheck; @@ -301,7 +301,7 @@ DEFINE_HOOK(0x48A4F3, SelectDamageAnimation_NegativeZeroDamage, 0x6) if (!warhead) return NoAnim; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(warhead); + auto const pWHExt = WarheadTypeExt::Fetch(warhead); pWHExt->Splashed = false; @@ -329,7 +329,7 @@ DEFINE_HOOK(0x4891AF, GetTotalDamage_NegativeDamageModifiers1, 0x6) GET(WarheadTypeClass* const, pWarhead, EDI); GET(const int, damage, ESI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pWHExt = WarheadTypeExt::Fetch(pWarhead); if (damage < 0 && pWHExt->ApplyModifiersOnNegativeDamage) { @@ -368,7 +368,7 @@ DEFINE_HOOK(0x701A54, TechnoClass_ReceiveDamage_PenetratesIronCurtain, 0x6) GET(TechnoClass*, pThis, ESI); GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0xC4, 0xC)); - if (WarheadTypeExt::ExtMap.Find(pWarhead)->CanAffectInvulnerable(pThis)) + if (WarheadTypeExt::Fetch(pWarhead)->CanAffectInvulnerable(pThis)) return AllowDamage; return 0; @@ -380,7 +380,7 @@ DEFINE_HOOK(0x489968, Explosion_Damage_PenetratesIronCurtain, 0x5) GET_BASE(WarheadTypeClass*, pWarhead, 0xC); - if (WarheadTypeExt::ExtMap.Find(pWarhead)->PenetratesIronCurtain) + if (WarheadTypeExt::Fetch(pWarhead)->PenetratesIronCurtain) return BypassInvulnerability; return 0; @@ -391,7 +391,7 @@ DEFINE_HOOK(0x489B49, MapClass_DamageArea_Rocker, 0xA) GET_BASE(WarheadTypeClass*, pWH, 0xC); GET_STACK(const int, damage, STACK_OFFSET(0xE0, -0xBC)); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); double rocker = pWHExt->Rocker_AmplitudeOverride.Get(damage); rocker *= 0.01 * pWHExt->Rocker_AmplitudeMultiplier; @@ -406,7 +406,7 @@ DEFINE_HOOK(0x4899DA, DamageArea_DamageBuilding_CauseMergeBuildingDamage, 0x7) { GET_BASE(WarheadTypeClass* const, pWH, 0x0C); - if (!WarheadTypeExt::ExtMap.Find(pWH)->MergeBuildingDamage.Get(RulesExt::Global()->MergeBuildingDamage)) + if (!WarheadTypeExt::Fetch(pWH)->MergeBuildingDamage.Get(RulesExt::Global()->MergeBuildingDamage)) return 0; struct DamageGroup @@ -475,7 +475,7 @@ DEFINE_HOOK(0x489A1B, DamageArea_DamageBuilding_SkipVanillaBuildingDamage, 0x6) GET_BASE(WarheadTypeClass* const, pWH, 0x0C); - return WarheadTypeExt::ExtMap.Find(pWH)->MergeBuildingDamage.Get(RulesExt::Global()->MergeBuildingDamage) ? SkipGameCode : 0; + return WarheadTypeExt::Fetch(pWH)->MergeBuildingDamage.Get(RulesExt::Global()->MergeBuildingDamage) ? SkipGameCode : 0; } #pragma endregion @@ -489,7 +489,7 @@ DEFINE_HOOK(0x708B0B, TechnoClass_AllowedToRetaliate_Nonprovocative, 0x5) GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x18, 0x8)); - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); return pTypeExt->Nonprovocative ? SkipEvents : 0; } @@ -500,7 +500,7 @@ DEFINE_HOOK(0x5F57CF, ObjectClass_ReceiveDamage_Nonprovocative, 0x6) GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x24, 0xC)); - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); return pTypeExt->Nonprovocative ? SkipEvents : 0; } @@ -513,7 +513,7 @@ DEFINE_HOOK(0x7027E6, TechnoClass_ReceiveDamage_Nonprovocative, 0x8) GET(TechnoClass*, pSource, EAX); GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0xC4, 0xC)); - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); if (!pTypeExt->Nonprovocative) { @@ -536,7 +536,7 @@ DEFINE_HOOK(0x4D7493, FootClass_ReceiveDamage_Nonprovocative, 0x5) if (!pSource) return SkipChecks; - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); return pTypeExt->Nonprovocative ? SkipEvents : 0; } @@ -547,7 +547,7 @@ DEFINE_HOOK(0x7384BD, UnitClass_ReceiveDamage_Nonprovocative, 0x6) GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x44, 0xC)); - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); return pTypeExt->Nonprovocative ? SkipEvents : 0; } @@ -558,7 +558,7 @@ DEFINE_HOOK(0x442290, BuildingClass_ReceiveDamage_Nonprovocative1, 0x6) GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x9C, 0xC)); - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); return pTypeExt->Nonprovocative ? SkipEvents : 0; } @@ -569,7 +569,7 @@ DEFINE_HOOK(0x442956, BuildingClass_ReceiveDamage_Nonprovocative2, 0x6) GET_STACK(WarheadTypeClass*, pWarhead, STACK_OFFSET(0x9C, 0xC)); - auto const pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + auto const pTypeExt = WarheadTypeExt::Fetch(pWarhead); return pTypeExt->Nonprovocative ? SkipEvents : 0; } @@ -582,7 +582,7 @@ DEFINE_HOOK(0x4D73DE, FootClass_ReceiveDamage_RemoveParasite, 0x5) GET(WarheadTypeClass*, pWarhead, EBP); GET(const int*, damage, EDI); - const auto pTypeExt = WarheadTypeExt::ExtMap.Find(pWarhead); + const auto pTypeExt = WarheadTypeExt::Fetch(pWarhead); if (!pTypeExt->RemoveParasite.Get(*damage < 0)) return Skip; @@ -624,14 +624,14 @@ DEFINE_HOOK(0x6FF7FF, TechnoClass_Fire_UnlimboDetonate, 0x6) GET(WarheadTypeClass* const, pWH, EAX); const auto pBullet = UnlimboDetonateFireTemp::Bullet; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + const auto pWHExt = WarheadTypeExt::Fetch(pWH); if (pThis->IsAlive && pThis->Health > 0 && pBullet && !UnlimboDetonateFireTemp::InLimbo && !pWH->Parasite && pWHExt->UnlimboDetonate) { if (pWHExt->UnlimboDetonate_KeepSelected) { - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pExt->IsSelected = UnlimboDetonateFireTemp::InSelected; auto& vec = ScenarioExt::Global()->LimboLaunchers; diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index 9cdbf853fc..ffc9032c50 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -4,7 +4,7 @@ WeaponTypeExt::ExtContainer WeaponTypeExt::ExtMap; -bool WeaponTypeExt::ExtData::HasRequiredAttachedEffects(TechnoClass* pTarget, TechnoClass* pFirer) const +bool WeaponTypeExt::HasRequiredAttachedEffects(TechnoClass* pTarget, TechnoClass* pFirer) const { const bool hasRequiredTypes = this->AttachEffect_RequiredTypes.size() > 0; const bool hasDisallowedTypes = this->AttachEffect_DisallowedTypes.size() > 0; @@ -21,7 +21,7 @@ bool WeaponTypeExt::ExtData::HasRequiredAttachedEffects(TechnoClass* pTarget, Te if (!pTechno) return true; - auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTechnoExt = TechnoExt::Fetch(pTechno); auto const pWH = this->OwnerObject()->Warhead; if (hasDisallowedTypes && pTechnoExt->HasAttachedEffects(this->AttachEffect_DisallowedTypes, false, this->AttachEffect_IgnoreFromSameSource, pFirer, pWH, &this->AttachEffect_DisallowedMinCounts, &this->AttachEffect_DisallowedMaxCounts)) @@ -40,22 +40,22 @@ bool WeaponTypeExt::ExtData::HasRequiredAttachedEffects(TechnoClass* pTarget, Te return true; } -bool WeaponTypeExt::ExtData::IsHealthInThreshold(TechnoClass* pTarget) const +bool WeaponTypeExt::IsHealthInThreshold(TechnoClass* pTarget) const { return TechnoExt::IsHealthInThreshold(pTarget, this->CanTarget_MinHealth, this->CanTarget_MaxHealth); } -bool WeaponTypeExt::ExtData::IsVeterancyInThreshold(TechnoClass* pTarget) const +bool WeaponTypeExt::IsVeterancyInThreshold(TechnoClass* pTarget) const { return EnumFunctions::CanTargetVeterancy(this->CanTargetVeterancy, pTarget); } -void WeaponTypeExt::ExtData::Initialize() +void WeaponTypeExt::Initialize() { this->RadType = RadTypeClass::FindOrAllocate(GameStrings::Radiation); } -int WeaponTypeExt::ExtData::GetBurstDelay(int burstIndex) const +int WeaponTypeExt::GetBurstDelay(int burstIndex) const { int burstDelay = -1; @@ -72,7 +72,7 @@ int WeaponTypeExt::ExtData::GetBurstDelay(int burstIndex) const // ============================= // load / save -void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) +void WeaponTypeExt::LoadFromINIFile(CCINIClass* const pINI) { auto pThis = this->OwnerObject(); const char* pSection = pThis->ID; @@ -213,7 +213,7 @@ void WeaponTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) } template -void WeaponTypeExt::ExtData::Serialize(T& Stm) +void WeaponTypeExt::Serialize(T& Stm) { Stm .Process(this->DiskLaser_Radius) @@ -305,16 +305,16 @@ void WeaponTypeExt::ExtData::Serialize(T& Stm) ; }; -void WeaponTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +void WeaponTypeExt::LoadFromStream(PhobosStreamReader& Stm) { - AbstractTypeClassExtension::LoadFromStream(Stm); + AbstractTypeExt::LoadFromStream(Stm); this->Serialize(Stm); } -void WeaponTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +void WeaponTypeExt::SaveToStream(PhobosStreamWriter& Stm) { - AbstractTypeClassExtension::SaveToStream(Stm); + AbstractTypeExt::SaveToStream(Stm); this->Serialize(Stm); } @@ -377,13 +377,13 @@ int WeaponTypeExt::GetRangeWithModifiers(WeaponTypeClass* pThis, TechnoClass* pF if (auto const pTransport = pTechno->Transporter) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pTransport)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pTransport)->TypeExtData; if (pTypeExt->OpenTopped_UseTransportRangeModifiers && pTypeExt->OwnerObject()->OpenTopped) pTechno = pTransport; } - auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTechnoExt = TechnoExt::Fetch(pTechno); if (!pTechnoExt->AE.HasRangeModifier) return range; @@ -420,7 +420,7 @@ int WeaponTypeExt::GetTechnoKeepRange(WeaponTypeClass* pThis, TechnoClass* pFire if (!pThis) return 0; - const auto pExt = WeaponTypeExt::ExtMap.Find(pThis); + const auto pExt = WeaponTypeExt::Fetch(pThis); const auto keepRange = pExt->KeepRange.Get(); if (!keepRange || !pFirer || pFirer->Transporter) diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 7901945b50..64e815a7e8 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -6,220 +6,219 @@ #include #include -class WeaponTypeExt +class WeaponTypeExt final : public AbstractTypeExt { public: using base_type = WeaponTypeClass; + using ExtData = WeaponTypeExt; static constexpr DWORD Canary = 0x22222222; static constexpr size_t ExtPointerOffset = 0x18; - class ExtData final : public AbstractTypeClassExtension +public: + // typed owner accessor + WeaponTypeClass* OwnerObject() const { - public: - // typed owner accessor - WeaponTypeClass* OwnerObject() const - { - return static_cast(this->GetAttachedObject()); - } - - - Valueable DiskLaser_Radius; - Valueable ProjectileRange; - Valueable RadType; - Nullable Bolt_Color[3]; - Valueable Bolt_Disable[3]; - Nullable Bolt_ParticleSystem; - Valueable Bolt_Arcs; - Valueable Bolt_Duration; - Nullable Bolt_FollowFLH; - Nullable Strafing; - Nullable Strafing_Shots; - Valueable Strafing_SimulateBurst; - Valueable Strafing_UseAmmoPerShot; - Valueable Strafing_TargetCell; - Nullable Strafing_EndDelay; - Valueable CanTarget; - Valueable CanTargetHouses; - Valueable CanTarget_MaxHealth; - Valueable CanTarget_MinHealth; - Valueable CanTargetVeterancy; - Nullable CanTarget_IronCurtained; - Nullable AutoTarget_IronCurtained; - ValueableVector Burst_Delays; - Valueable Burst_FireWithinSequence; - Valueable Burst_NoDelay; - Valueable AreaFire_Target; - Valueable FeedbackWeapon; - Valueable Laser_IsSingleColor; - Nullable LaserZAdjust; - Nullable EBoltZAdjust; - Nullable EBoltZAdjust_ClampInitialDepthForBuilding; - Valueable VisualScatter; - Nullable> ROF_RandomDelay; - ValueableVector ChargeTurret_Delays; - Valueable OmniFire_TurnToTarget; - Valueable FireOnce_ResetSequence; - Valueable TurretRecoil_Suppress; - ValueableVector ExtraWarheads; - ValueableVector ExtraWarheads_DamageOverrides; - ValueableVector ExtraWarheads_DetonationChances; - ValueableVector ExtraWarheads_RollChances; - std::vector> ExtraWarheads_WeightsData; - ValueableVector ExtraWarheads_FullDetonation; - Nullable AmbientDamage_Warhead; - Valueable AmbientDamage_IgnoreTarget; - ValueableVector AttachEffect_RequiredTypes; - ValueableVector AttachEffect_DisallowedTypes; - std::vector AttachEffect_RequiredGroups; - std::vector AttachEffect_DisallowedGroups; - ValueableVector AttachEffect_RequiredMinCounts; - ValueableVector AttachEffect_RequiredMaxCounts; - ValueableVector AttachEffect_DisallowedMinCounts; - ValueableVector AttachEffect_DisallowedMaxCounts; - Valueable AttachEffect_CheckOnFirer; - Valueable AttachEffect_IgnoreFromSameSource; - Valueable KeepRange; - Valueable KeepRange_AllowAI; - Valueable KeepRange_AllowPlayer; - Valueable KeepRange_EarlyStopFrame; - Valueable KickOutPassengers; - Nullable Beam_Color; - Valueable Beam_Duration; - Valueable Beam_Amplitude; - Valueable Beam_IsHouseColor; - Valueable LaserThickness; - Nullable> DelayedFire_Duration; - Valueable DelayedFire_SkipInTransport; - Valueable DelayedFire_Animation; - Nullable DelayedFire_OpenToppedAnimation; - Valueable DelayedFire_AnimIsAttached; - Valueable DelayedFire_CenterAnimOnFirer; - Valueable DelayedFire_RemoveAnimOnNoDelay; - Valueable DelayedFire_PauseFiringSequence; - Valueable DelayedFire_OnlyOnInitialBurst; - Nullable DelayedFire_AnimOffset; - Valueable DelayedFire_AnimOnTurret; - Nullable ExtraRange_TargetMoving; - Nullable ExtraRange_FirerMoving; - Nullable ExtraRange_Prefiring; - Nullable ExtraRange_Prefiring_IncludeBurst; - Nullable AttackFriendlies; - Nullable AttackCursorOnFriendlies; - Nullable AttackNoThreatBuildings; - - Nullable Anim_Update; - - bool SkipWeaponPicking; - - Nullable CylinderRangefinding; - - ExtData(WeaponTypeClass* OwnerObject) : AbstractTypeClassExtension(OwnerObject) - , DiskLaser_Radius { DiskLaserClass::Radius } - , ProjectileRange { Leptons(100000) } - , RadType {} - , Bolt_Color {} - , Bolt_Disable { Valueable(false) } - , Bolt_ParticleSystem {} - , Bolt_Arcs { 8 } - , Bolt_Duration { 17 } - , Bolt_FollowFLH {} - , Strafing { } - , Strafing_Shots {} - , Strafing_SimulateBurst { false } - , Strafing_UseAmmoPerShot { false } - , Strafing_TargetCell { false } - , Strafing_EndDelay {} - , CanTarget { AffectedTarget::All } - , CanTargetHouses { AffectedHouse::All } - , CanTarget_MaxHealth { 1.0 } - , CanTarget_MinHealth { 0.0 } - , CanTargetVeterancy { AffectedVeterancy::All } - , CanTarget_IronCurtained {} - , AutoTarget_IronCurtained {} - , Burst_Delays {} - , Burst_FireWithinSequence { false } - , Burst_NoDelay { false } - , AreaFire_Target { AreaFireTarget::Base } - , FeedbackWeapon {} - , Laser_IsSingleColor { false } - , LaserZAdjust {} - , EBoltZAdjust {} - , EBoltZAdjust_ClampInitialDepthForBuilding {} - , VisualScatter { false } - , ROF_RandomDelay {} - , ChargeTurret_Delays {} - , OmniFire_TurnToTarget { false } - , FireOnce_ResetSequence { true } - , TurretRecoil_Suppress { false } - , ExtraWarheads {} - , ExtraWarheads_DamageOverrides {} - , ExtraWarheads_DetonationChances {} - , ExtraWarheads_RollChances {} - , ExtraWarheads_WeightsData {} - , ExtraWarheads_FullDetonation {} - , AmbientDamage_Warhead {} - , AmbientDamage_IgnoreTarget { false } - , AttachEffect_RequiredTypes {} - , AttachEffect_DisallowedTypes {} - , AttachEffect_RequiredGroups {} - , AttachEffect_DisallowedGroups {} - , AttachEffect_RequiredMinCounts {} - , AttachEffect_RequiredMaxCounts {} - , AttachEffect_DisallowedMinCounts {} - , AttachEffect_DisallowedMaxCounts {} - , AttachEffect_CheckOnFirer { false } - , AttachEffect_IgnoreFromSameSource { false } - , KeepRange { Leptons(0) } - , KeepRange_AllowAI { false } - , KeepRange_AllowPlayer { false } - , KeepRange_EarlyStopFrame { 0 } - , KickOutPassengers { true } - , Beam_Color {} - , Beam_Duration { 15 } - , Beam_Amplitude { 40.0 } - , Beam_IsHouseColor { false } - , LaserThickness { 3 } - , SkipWeaponPicking { true } - , DelayedFire_Duration {} - , DelayedFire_SkipInTransport { false } - , DelayedFire_Animation {} - , DelayedFire_OpenToppedAnimation {} - , DelayedFire_AnimIsAttached { true } - , DelayedFire_CenterAnimOnFirer { false } - , DelayedFire_RemoveAnimOnNoDelay { false } - , DelayedFire_PauseFiringSequence { false } - , DelayedFire_OnlyOnInitialBurst { false } - , DelayedFire_AnimOffset {} - , DelayedFire_AnimOnTurret { true } - , ExtraRange_TargetMoving {} - , ExtraRange_FirerMoving {} - , ExtraRange_Prefiring {} - , ExtraRange_Prefiring_IncludeBurst {} - , AttackFriendlies {} - , AttackCursorOnFriendlies {} - , AttackNoThreatBuildings {} - , CylinderRangefinding {} - , Anim_Update {} - { } - - int GetBurstDelay(int burstIndex) const; - bool HasRequiredAttachedEffects(TechnoClass* pTechno, TechnoClass* pFirer) const; - bool IsHealthInThreshold(TechnoClass* pTarget) const; - bool IsVeterancyInThreshold(TechnoClass* pTarget) const; - - virtual ~ExtData() = default; - - virtual void LoadFromINIFile(CCINIClass* pINI) override; - virtual void Initialize() override; - virtual void LoadFromStream(PhobosStreamReader& Stm) override; - virtual void SaveToStream(PhobosStreamWriter& Stm) override; - - private: - template - void Serialize(T& Stm); - }; + return static_cast(this->GetAttachedObject()); + } + + + Valueable DiskLaser_Radius; + Valueable ProjectileRange; + Valueable RadType; + Nullable Bolt_Color[3]; + Valueable Bolt_Disable[3]; + Nullable Bolt_ParticleSystem; + Valueable Bolt_Arcs; + Valueable Bolt_Duration; + Nullable Bolt_FollowFLH; + Nullable Strafing; + Nullable Strafing_Shots; + Valueable Strafing_SimulateBurst; + Valueable Strafing_UseAmmoPerShot; + Valueable Strafing_TargetCell; + Nullable Strafing_EndDelay; + Valueable CanTarget; + Valueable CanTargetHouses; + Valueable CanTarget_MaxHealth; + Valueable CanTarget_MinHealth; + Valueable CanTargetVeterancy; + Nullable CanTarget_IronCurtained; + Nullable AutoTarget_IronCurtained; + ValueableVector Burst_Delays; + Valueable Burst_FireWithinSequence; + Valueable Burst_NoDelay; + Valueable AreaFire_Target; + Valueable FeedbackWeapon; + Valueable Laser_IsSingleColor; + Nullable LaserZAdjust; + Nullable EBoltZAdjust; + Nullable EBoltZAdjust_ClampInitialDepthForBuilding; + Valueable VisualScatter; + Nullable> ROF_RandomDelay; + ValueableVector ChargeTurret_Delays; + Valueable OmniFire_TurnToTarget; + Valueable FireOnce_ResetSequence; + Valueable TurretRecoil_Suppress; + ValueableVector ExtraWarheads; + ValueableVector ExtraWarheads_DamageOverrides; + ValueableVector ExtraWarheads_DetonationChances; + ValueableVector ExtraWarheads_RollChances; + std::vector> ExtraWarheads_WeightsData; + ValueableVector ExtraWarheads_FullDetonation; + Nullable AmbientDamage_Warhead; + Valueable AmbientDamage_IgnoreTarget; + ValueableVector AttachEffect_RequiredTypes; + ValueableVector AttachEffect_DisallowedTypes; + std::vector AttachEffect_RequiredGroups; + std::vector AttachEffect_DisallowedGroups; + ValueableVector AttachEffect_RequiredMinCounts; + ValueableVector AttachEffect_RequiredMaxCounts; + ValueableVector AttachEffect_DisallowedMinCounts; + ValueableVector AttachEffect_DisallowedMaxCounts; + Valueable AttachEffect_CheckOnFirer; + Valueable AttachEffect_IgnoreFromSameSource; + Valueable KeepRange; + Valueable KeepRange_AllowAI; + Valueable KeepRange_AllowPlayer; + Valueable KeepRange_EarlyStopFrame; + Valueable KickOutPassengers; + Nullable Beam_Color; + Valueable Beam_Duration; + Valueable Beam_Amplitude; + Valueable Beam_IsHouseColor; + Valueable LaserThickness; + Nullable> DelayedFire_Duration; + Valueable DelayedFire_SkipInTransport; + Valueable DelayedFire_Animation; + Nullable DelayedFire_OpenToppedAnimation; + Valueable DelayedFire_AnimIsAttached; + Valueable DelayedFire_CenterAnimOnFirer; + Valueable DelayedFire_RemoveAnimOnNoDelay; + Valueable DelayedFire_PauseFiringSequence; + Valueable DelayedFire_OnlyOnInitialBurst; + Nullable DelayedFire_AnimOffset; + Valueable DelayedFire_AnimOnTurret; + Nullable ExtraRange_TargetMoving; + Nullable ExtraRange_FirerMoving; + Nullable ExtraRange_Prefiring; + Nullable ExtraRange_Prefiring_IncludeBurst; + Nullable AttackFriendlies; + Nullable AttackCursorOnFriendlies; + Nullable AttackNoThreatBuildings; + + Nullable Anim_Update; + + bool SkipWeaponPicking; + + Nullable CylinderRangefinding; + + WeaponTypeExt(WeaponTypeClass* OwnerObject) : AbstractTypeExt(OwnerObject) + , DiskLaser_Radius { DiskLaserClass::Radius } + , ProjectileRange { Leptons(100000) } + , RadType {} + , Bolt_Color {} + , Bolt_Disable { Valueable(false) } + , Bolt_ParticleSystem {} + , Bolt_Arcs { 8 } + , Bolt_Duration { 17 } + , Bolt_FollowFLH {} + , Strafing { } + , Strafing_Shots {} + , Strafing_SimulateBurst { false } + , Strafing_UseAmmoPerShot { false } + , Strafing_TargetCell { false } + , Strafing_EndDelay {} + , CanTarget { AffectedTarget::All } + , CanTargetHouses { AffectedHouse::All } + , CanTarget_MaxHealth { 1.0 } + , CanTarget_MinHealth { 0.0 } + , CanTargetVeterancy { AffectedVeterancy::All } + , CanTarget_IronCurtained {} + , AutoTarget_IronCurtained {} + , Burst_Delays {} + , Burst_FireWithinSequence { false } + , Burst_NoDelay { false } + , AreaFire_Target { AreaFireTarget::Base } + , FeedbackWeapon {} + , Laser_IsSingleColor { false } + , LaserZAdjust {} + , EBoltZAdjust {} + , EBoltZAdjust_ClampInitialDepthForBuilding {} + , VisualScatter { false } + , ROF_RandomDelay {} + , ChargeTurret_Delays {} + , OmniFire_TurnToTarget { false } + , FireOnce_ResetSequence { true } + , TurretRecoil_Suppress { false } + , ExtraWarheads {} + , ExtraWarheads_DamageOverrides {} + , ExtraWarheads_DetonationChances {} + , ExtraWarheads_RollChances {} + , ExtraWarheads_WeightsData {} + , ExtraWarheads_FullDetonation {} + , AmbientDamage_Warhead {} + , AmbientDamage_IgnoreTarget { false } + , AttachEffect_RequiredTypes {} + , AttachEffect_DisallowedTypes {} + , AttachEffect_RequiredGroups {} + , AttachEffect_DisallowedGroups {} + , AttachEffect_RequiredMinCounts {} + , AttachEffect_RequiredMaxCounts {} + , AttachEffect_DisallowedMinCounts {} + , AttachEffect_DisallowedMaxCounts {} + , AttachEffect_CheckOnFirer { false } + , AttachEffect_IgnoreFromSameSource { false } + , KeepRange { Leptons(0) } + , KeepRange_AllowAI { false } + , KeepRange_AllowPlayer { false } + , KeepRange_EarlyStopFrame { 0 } + , KickOutPassengers { true } + , Beam_Color {} + , Beam_Duration { 15 } + , Beam_Amplitude { 40.0 } + , Beam_IsHouseColor { false } + , LaserThickness { 3 } + , SkipWeaponPicking { true } + , DelayedFire_Duration {} + , DelayedFire_SkipInTransport { false } + , DelayedFire_Animation {} + , DelayedFire_OpenToppedAnimation {} + , DelayedFire_AnimIsAttached { true } + , DelayedFire_CenterAnimOnFirer { false } + , DelayedFire_RemoveAnimOnNoDelay { false } + , DelayedFire_PauseFiringSequence { false } + , DelayedFire_OnlyOnInitialBurst { false } + , DelayedFire_AnimOffset {} + , DelayedFire_AnimOnTurret { true } + , ExtraRange_TargetMoving {} + , ExtraRange_FirerMoving {} + , ExtraRange_Prefiring {} + , ExtraRange_Prefiring_IncludeBurst {} + , AttackFriendlies {} + , AttackCursorOnFriendlies {} + , AttackNoThreatBuildings {} + , CylinderRangefinding {} + , Anim_Update {} + { } + + int GetBurstDelay(int burstIndex) const; + bool HasRequiredAttachedEffects(TechnoClass* pTechno, TechnoClass* pFirer) const; + bool IsHealthInThreshold(TechnoClass* pTarget) const; + bool IsVeterancyInThreshold(TechnoClass* pTarget) const; + + virtual ~WeaponTypeExt() = default; + + virtual void LoadFromINIFile(CCINIClass* pINI) override; + virtual void Initialize() override; + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + +private: + template + void Serialize(T& Stm); +public: class ExtContainer final : public Container { public: @@ -229,6 +228,16 @@ class WeaponTypeExt static ExtContainer ExtMap; + static WeaponTypeExt* Fetch(const WeaponTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static WeaponTypeExt* TryFetch(const WeaponTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } + static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -243,5 +252,3 @@ class WeaponTypeExt static int GetTechnoKeepRange(WeaponTypeClass* pThis, TechnoClass* pFirer, bool isMinimum); }; -// top-level name for the WeaponTypeExt extension -using WeaponTypeClassExtension = WeaponTypeExt::ExtData; diff --git a/src/Ext/WeaponType/Hooks.DiskLaserRadius.cpp b/src/Ext/WeaponType/Hooks.DiskLaserRadius.cpp index cee65ae8c0..845316c278 100644 --- a/src/Ext/WeaponType/Hooks.DiskLaserRadius.cpp +++ b/src/Ext/WeaponType/Hooks.DiskLaserRadius.cpp @@ -24,7 +24,7 @@ DEFINE_HOOK(0x4A757B, DiskLaser_Circle, 0x6) { GET(WeaponTypeClass*, pWeapon, EDX); - auto const pTypeData = WeaponTypeExt::ExtMap.TryFind(pWeapon); + auto const pTypeData = WeaponTypeExt::TryFetch(pWeapon); if (pTypeData && WeaponTypeExt::OldRadius != pTypeData->DiskLaser_Radius) { diff --git a/src/Ext/WeaponType/Hooks.cpp b/src/Ext/WeaponType/Hooks.cpp index 457a303a8c..0906c4e6ee 100644 --- a/src/Ext/WeaponType/Hooks.cpp +++ b/src/Ext/WeaponType/Hooks.cpp @@ -26,7 +26,7 @@ DEFINE_HOOK(0x772AA2, WeaponTypeClass_AllowedThreats_AAOnly, 0x5) { GET(BulletTypeClass* const, pType, ECX); - if (BulletTypeExt::ExtMap.Find(pType)->AAOnly) + if (BulletTypeExt::Fetch(pType)->AAOnly) { R->EAX(4); return 0x772AB3; diff --git a/src/Interop/BulletExt.cpp b/src/Interop/BulletExt.cpp index 9611e55f6a..c445be22bb 100644 --- a/src/Interop/BulletExt.cpp +++ b/src/Interop/BulletExt.cpp @@ -6,7 +6,7 @@ DEFINE_EXPORT(HRESULT, Bullet_SetFirerOwner, BulletClass* pBullet, HouseClass* p if (!pBullet) return E_POINTER; - const auto pBulletExt = BulletExt::ExtMap.TryFind(pBullet); + const auto pBulletExt = BulletExt::TryFetch(pBullet); if (!pBulletExt) return E_UNEXPECTED; diff --git a/src/Misc/Hooks.AlphaImage.cpp b/src/Misc/Hooks.AlphaImage.cpp index 03088fcdc7..a635933bac 100644 --- a/src/Misc/Hooks.AlphaImage.cpp +++ b/src/Misc/Hooks.AlphaImage.cpp @@ -73,7 +73,7 @@ static void __fastcall UpdateAlphaShape(ObjectClass* pSource) const auto pBuilding = abstract_cast(pSource); if (pBuilding && !inactive && pBuilding->GetCurrentMission() != Mission::Construction) - inactive |= !pBuilding->IsPowerOnline() || BuildingExt::ExtMap.Find(pBuilding)->LimboID != -1; + inactive |= !pBuilding->IsPowerOnline() || BuildingExt::Fetch(pBuilding)->LimboID != -1; auto& alphaExt = *AresFunctions::AlphaExtMap; diff --git a/src/Misc/Hooks.Ares.cpp b/src/Misc/Hooks.Ares.cpp index 177818a83c..f34e624175 100644 --- a/src/Misc/Hooks.Ares.cpp +++ b/src/Misc/Hooks.Ares.cpp @@ -68,12 +68,12 @@ static EBolt* __stdcall CreateEBolt2(WeaponTypeClass* pWeapon) static bool __fastcall CameoIsVeteran(TechnoTypeClass** pTypeExt_Ares, void*, HouseClass* pHouse) { - return TechnoTypeExt::ExtMap.Find(*pTypeExt_Ares)->CameoIsVeteran(pHouse); + return TechnoTypeExt::Fetch(*pTypeExt_Ares)->CameoIsVeteran(pHouse); } static bool __fastcall SW_IsAvailable(SuperWeaponTypeClass** pExt_Ares, void*, HouseClass* pHouse) { - return SWTypeExt::ExtMap.Find(*pExt_Ares)->IsAvailable(pHouse); + return SWTypeExt::Fetch(*pExt_Ares)->IsAvailable(pHouse); } namespace PermaMCTemp @@ -93,7 +93,7 @@ static bool __fastcall ApplyPermaMC_Wrapper(WarheadTypeClass** pExt_Ares, void*, static bool __fastcall PermaMC_FreeUnit_SetContext(CaptureManagerClass* pManager, void*, TechnoClass* pTechno) { PermaMCTemp::Selected = pTechno->IsSelected; - return CaptureManagerExt::FreeUnit(pManager, pTechno, WarheadTypeExt::ExtMap.Find(PermaMCTemp::Warhead)->RemoveMindControl_Silent.Get(RulesExt::Global()->MindControl_Permanent_ReplaceSilent)); + return CaptureManagerExt::FreeUnit(pManager, pTechno, WarheadTypeExt::Fetch(PermaMCTemp::Warhead)->RemoveMindControl_Silent.Get(RulesExt::Global()->MindControl_Permanent_ReplaceSilent)); } static bool __fastcall PermaMC_SetOwningHouse_Select(TechnoClass* pTechno, void*, HouseClass* pHouse, bool announce) @@ -224,10 +224,10 @@ void Apply_Ares3_0_Patches() // Redirect Ares's function to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x112D0, &BuildingExt::KickOutClone); - // Redirect Ares's TechnoTypeExt::ExtData::CameoIsElite() to our implementation: + // Redirect Ares's TechnoTypeExt::CameoIsElite() to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x3D800, &CameoIsVeteran); - // Redirect Ares's SWTypeExt::ExtData::IsAvailable to our implementation: + // Redirect Ares's SWTypeExt::IsAvailable to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x32BE0, &SW_IsAvailable); Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x329E0, &SWTypeExt::IsSuperAvailable); @@ -323,10 +323,10 @@ void Apply_Ares3_0p1_Patches() // Redirect Ares's function to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x11860, &BuildingExt::KickOutClone); - // Redirect Ares's TechnoTypeExt::ExtData::CameoIsElite() to our implementation: + // Redirect Ares's TechnoTypeExt::CameoIsElite() to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x3E210, &CameoIsVeteran); - // Redirect Ares's SWTypeExt::ExtData::IsAvailable to our implementation: + // Redirect Ares's SWTypeExt::IsAvailable to our implementation: Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x335E0, &SW_IsAvailable); Patch::Apply_LJMP(AresHelper::AresBaseAddress + 0x333E0, &SWTypeExt::IsSuperAvailable); diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index a8054dd5a1..b8ec75ca91 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -162,7 +162,7 @@ DEFINE_HOOK(0x702299, TechnoClass_ReceiveDamage_Debris, 0xA) // without continuously looping until it exceeds totalSpawnAmount if (count > 0) { - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pTypeExt = TechnoTypeExt::Fetch(pType); const auto& debrisMinimums = pTypeExt->DebrisMinimums; const bool limit = pTypeExt->DebrisTypes_Limit.Get(count > 1); const int minimumsMaxIndex = static_cast(debrisMinimums.size()) - 1; @@ -443,7 +443,7 @@ DEFINE_HOOK(0x54D138, JumpjetLocomotionClass_Movement_AI_SpeedModifiers, 0x6) GET(JumpjetLocomotionClass*, pThis, ESI); const double multiplier = TechnoExt::GetCurrentSpeedMultiplier(pThis->LinkedTo); - pThis->Speed = static_cast(TechnoExt::ExtMap.Find(pThis->LinkedTo)->JumpjetSpeed * multiplier); + pThis->Speed = static_cast(TechnoExt::Fetch(pThis->LinkedTo)->JumpjetSpeed * multiplier); return 0; } @@ -569,7 +569,7 @@ static DamageAreaResult __fastcall _BombClass_Detonate_DamageArea { auto const pThisBomb = FetchBomb::pThisBomb; auto const nCoord = *pCoord; - auto const nDamageAreaResult = WarheadTypeExt::ExtMap.Find(pWarhead)->DamageAreaWithTarget + auto const nDamageAreaResult = WarheadTypeExt::Fetch(pWarhead)->DamageAreaWithTarget (nCoord, nDamage, pSource, pWarhead, pWarhead->Tiberium, pThisBomb->OwnerHouse, abstract_cast(pThisBomb->Target)); auto const nLandType = MapClass::Instance.GetCellAt(nCoord)->LandType; @@ -583,7 +583,7 @@ static DamageAreaResult __fastcall _BombClass_Detonate_DamageArea if (!pAnim->Owner) pAnim->Owner = pThisBomb->OwnerHouse; - AnimExt::ExtMap.Find(pAnim)->SetInvoker(pThisBomb->Owner); + AnimExt::Fetch(pAnim)->SetInvoker(pThisBomb->Owner); } return nDamageAreaResult; @@ -695,7 +695,7 @@ DEFINE_HOOK(0x44643E, BuildingClass_Place_SuperAnim, 0x6) GET(BuildingClass*, pThis, EBP); GET(SuperClass*, pSuper, EAX); - if (pSuper->RechargeTimer.StartTime == 0 && pSuper->RechargeTimer.TimeLeft == 0 && !SWTypeExt::ExtMap.Find(pSuper->Type)->SW_InitialReady) + if (pSuper->RechargeTimer.StartTime == 0 && pSuper->RechargeTimer.TimeLeft == 0 && !SWTypeExt::Fetch(pSuper->Type)->SW_InitialReady) { R->ECX(pThis); return UseSuperAnimOne; @@ -711,7 +711,7 @@ DEFINE_HOOK(0x451033, BuildingClass_AnimationAI_SuperAnim, 0x6) GET(SuperClass*, pSuper, EAX); - if (pSuper->RechargeTimer.StartTime == 0 && pSuper->RechargeTimer.TimeLeft == 0 && !SWTypeExt::ExtMap.Find(pSuper->Type)->SW_InitialReady) + if (pSuper->RechargeTimer.StartTime == 0 && pSuper->RechargeTimer.TimeLeft == 0 && !SWTypeExt::Fetch(pSuper->Type)->SW_InitialReady) return SkipSuperAnimCode; return 0; @@ -994,7 +994,7 @@ DEFINE_HOOK(0x72958E, TunnelLocomotionClass_ProcessDigging_SlowdownDistance, 0x8 // Nov 27, 2024 - Starkku: The movement speed was actually also hardcoded here to 19, so the distance check made sense // It can now be customized globally or per TechnoType however - auto const pTypeExt = TechnoExt::ExtMap.Find(pLinkedTo)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pLinkedTo)->TypeExtData; auto const pType = pTypeExt->OwnerObject(); int speed = pTypeExt->SubterraneanSpeed >= 0 ? pTypeExt->SubterraneanSpeed : RulesExt::Global()->SubterraneanSpeed; @@ -1521,7 +1521,7 @@ DEFINE_HOOK(0x4DE839, FootClass_AddSensorsAt_Record, 0x6) { GET(FootClass*, pThis, ESI); LEA_STACK(CellStruct*, cell, STACK_OFFSET(0x34, 0x4)); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pExt->LastSensorsMapCoords = *cell; return 0; @@ -1532,7 +1532,7 @@ DEFINE_HOOK(0x4D8606, FootClass_UpdatePosition_Sensors, 0x6) enum { SkipGameCode = 0x4D8627 }; GET(FootClass*, pThis, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); const auto currentCell = pThis->GetMapCoords(); if (pExt->LastSensorsMapCoords != currentCell) @@ -1549,7 +1549,7 @@ DEFINE_HOOK(0x4DB36C, FootClass_Limbo_RemoveSensors, 0x5) enum { SkipGameCode = 0x4DB37C }; GET(FootClass*, pThis, EDI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pThis->RemoveSensorsAt(pExt->LastSensorsMapCoords); @@ -1561,7 +1561,7 @@ DEFINE_HOOK(0x4DBEE7, FootClass_SetOwningHouse_RemoveSensors, 0x6) enum { SkipGameCode = 0x4DBF01 }; GET(FootClass*, pThis, ESI); - const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pExt = TechnoExt::Fetch(pThis); pThis->RemoveSensorsAt(pExt->LastSensorsMapCoords); @@ -1577,7 +1577,7 @@ DEFINE_HOOK(0x54C036, JumpjetLocomotionClass_State3_UpdateSensors, 0x7) // Copied from FootClass::UpdatePosition if (pLinkedTo->GetTechnoType()->SensorsSight) { - const auto pExt = TechnoExt::ExtMap.Find(pLinkedTo); + const auto pExt = TechnoExt::Fetch(pLinkedTo); CellStruct const lastCell = pExt->LastSensorsMapCoords; if (lastCell != currentCell) @@ -1597,7 +1597,7 @@ DEFINE_HOOK(0x54D06F, JumpjetLocomotionClass_ProcessCrashing_RemoveSensors, 0x5) if (pLinkedTo->GetTechnoType()->SensorsSight) { - const auto pExt = TechnoExt::ExtMap.Find(pLinkedTo); + const auto pExt = TechnoExt::Fetch(pLinkedTo); pLinkedTo->RemoveSensorsAt(pExt->LastSensorsMapCoords); } @@ -2203,7 +2203,7 @@ DEFINE_HOOK(0x489416, MapClass_DamageArea_AirDamageSelfFix, 0x6) GET_BASE(WarheadTypeClass*, pWarhead, 0xC); - if (WarheadTypeExt::ExtMap.Find(pWarhead)->AllowDamageOnSelf) + if (WarheadTypeExt::Fetch(pWarhead)->AllowDamageOnSelf) return 0; return NextTechno; @@ -2402,7 +2402,7 @@ DEFINE_HOOK(0x415F25, AircraftClass_FireAt_Vertical, 0x6) GET(BulletClass*, pBullet, ESI); - if (pBullet->HasParachute || (pBullet->Type->Vertical && BulletTypeExt::ExtMap.Find(pBullet->Type)->Vertical_AircraftFix)) + if (pBullet->HasParachute || (pBullet->Type->Vertical && BulletTypeExt::Fetch(pBullet->Type)->Vertical_AircraftFix)) return SkipGameCode; return 0; @@ -2414,7 +2414,7 @@ DEFINE_HOOK(0x6FED2F, TechnoClass_FireAt_VerticalInitialFacing, 0x6) GET(BulletTypeClass*, pBulletType, EAX); - if (BulletTypeExt::ExtMap.Find(pBulletType)->VerticalInitialFacing.Get(pBulletType->Voxel || pBulletType->Vertical)) + if (BulletTypeExt::Fetch(pBulletType)->VerticalInitialFacing.Get(pBulletType->Voxel || pBulletType->Vertical)) return Continue; return SkipGameCode; @@ -2747,7 +2747,7 @@ DEFINE_HOOK(0x74431F, UnitClass_ReadyToNextMission_HuntCheck, 0x6) DEFINE_HOOK(0x52182A, InfantryClass_MarkAllOccupationBits_SetOwnerIdx, 0x6) { GET(CellClass*, pCell, ESI); - CellExt::ExtMap.Find(pCell)->InfantryCount++; + CellExt::Fetch(pCell)->InfantryCount++; return 0; } @@ -2759,7 +2759,7 @@ DEFINE_HOOK(0x5218C2, InfantryClass_UnmarkAllOccupationBits_ResetOwnerIdx, 0x6) GET(const DWORD, newFlag, ECX); pCell->OccupationFlags = newFlag; - const auto pExt = CellExt::ExtMap.Find(pCell); + const auto pExt = CellExt::Fetch(pCell); pExt->InfantryCount--; // Vanilla check only the flag to decide if the InfantryOwnerIndex should be reset. @@ -3064,7 +3064,7 @@ DEFINE_HOOK(0x4440B0, BuildingClass_KickOutUnit_CloningFacility, 0x6) GET(BuildingTypeClass*, pFactoryType, EAX); - if (!pFactoryType->WeaponsFactory || BuildingTypeExt::ExtMap.Find(pFactoryType)->CloningFacility) + if (!pFactoryType->WeaponsFactory || BuildingTypeExt::Fetch(pFactoryType)->CloningFacility) return CheckFreeLinks; return ContinueIn; @@ -3109,7 +3109,7 @@ static bool inline CanBeSold(TechnoClass* pTechno, AbstractType rtti) if (rtti == AbstractType::Unit || rtti == AbstractType::Aircraft) { - auto const pTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + auto const pTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; if (pTypeExt->Unsellable.Get(RulesExt::Global()->UnitsUnsellable)) return false; @@ -3120,7 +3120,7 @@ static bool inline CanBeSold(TechnoClass* pTechno, AbstractType rtti) { auto const pType = pBuilding->Type; - if (BuildingTypeExt::ExtMap.Find(pType)->UnitSell.Get(pType->UnitRepair)) + if (BuildingTypeExt::Fetch(pType)->UnitSell.Get(pType->UnitRepair)) return true; } } diff --git a/src/Misc/Hooks.Crates.cpp b/src/Misc/Hooks.Crates.cpp index 4c546a9850..970ed954d3 100644 --- a/src/Misc/Hooks.Crates.cpp +++ b/src/Misc/Hooks.Crates.cpp @@ -82,7 +82,7 @@ DEFINE_HOOK(0x4821BD, CellClass_GoodieCheck_CrateGoodie, 0x6) if (crateGoodie) { - auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pUnitType); + auto const pTypeExt = TechnoTypeExt::Fetch(pUnitType); if (pTypeExt->CrateGoodie_RerollChance > 0.0) crateGoodie = pTypeExt->CrateGoodie_RerollChance < ScenarioClass::Instance->Random.RandomDouble(); diff --git a/src/Misc/Hooks.LaserDraw.cpp b/src/Misc/Hooks.LaserDraw.cpp index 221502db44..51a717bc79 100644 --- a/src/Misc/Hooks.LaserDraw.cpp +++ b/src/Misc/Hooks.LaserDraw.cpp @@ -48,7 +48,7 @@ DEFINE_HOOK(0x6FD3FD, TechnoClass_LaserZap_ZAdjust, 0x5) GET_STACK(WeaponTypeClass*, pWeapon, STACK_OFFSET(0x6C, 0xC)); GET(int, zAdjust, EAX); - zAdjust += WeaponTypeExt::ExtMap.Find(pWeapon)->LaserZAdjust.Get(RulesExt::Global()->LaserZAdjust); + zAdjust += WeaponTypeExt::Fetch(pWeapon)->LaserZAdjust.Get(RulesExt::Global()->LaserZAdjust); R->EAX(zAdjust); return 0; diff --git a/src/Misc/Hooks.LightEffects.cpp b/src/Misc/Hooks.LightEffects.cpp index 646673cab8..eb443a78a4 100644 --- a/src/Misc/Hooks.LightEffects.cpp +++ b/src/Misc/Hooks.LightEffects.cpp @@ -13,7 +13,7 @@ DEFINE_HOOK(0x48A444, AreaDamage_Particle_LightFlashSet, 0x5) { GET(WarheadTypeClass*, pWH, EDI); - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->Particle_AlphaImageIsLightFlash.Get(RulesExt::Global()->WarheadParticleAlphaImageIsLightFlash)) LightEffectsTemp::AlphaIsLightFlash = true; @@ -68,7 +68,7 @@ DEFINE_HOOK(0x48A62E, DoFlash_CombatLightOptions, 0x6) if (pWH) { - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); if (pWHExt->CombatLightChance < Randomizer::Global.RandomDouble()) return SkipFlash; diff --git a/src/Misc/Hooks.UI.cpp b/src/Misc/Hooks.UI.cpp index c9743698a4..0daf859cc3 100644 --- a/src/Misc/Hooks.UI.cpp +++ b/src/Misc/Hooks.UI.cpp @@ -108,7 +108,7 @@ DEFINE_HOOK(0x4A25E0, CreditsClass_GraphicLogic_HarvesterCounter, 0x7) if (Phobos::UI::HarvesterCounter_Show && Phobos::Config::ShowHarvesterCounter) { - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.GetItem(pPlayer->SideIndex)); + const auto pSideExt = SideExt::Fetch(SideClass::Array.GetItem(pPlayer->SideIndex)); wchar_t counter[0x20]; const int nActive = HouseExt::ActiveHarvesterCount(pPlayer); const int nTotal = HouseExt::TotalHarvesterCount(pPlayer); @@ -136,7 +136,7 @@ DEFINE_HOOK(0x4A25E0, CreditsClass_GraphicLogic_HarvesterCounter, 0x7) if (Phobos::UI::PowerDelta_Show && Phobos::Config::ShowPowerDelta && pPlayer->Buildings.Count) { - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.GetItem(pPlayer->SideIndex)); + const auto pSideExt = SideExt::Fetch(SideClass::Array.GetItem(pPlayer->SideIndex)); wchar_t counter[0x20]; ColorStruct clrToolTip; @@ -174,7 +174,7 @@ DEFINE_HOOK(0x4A25E0, CreditsClass_GraphicLogic_HarvesterCounter, 0x7) if (Phobos::UI::WeedsCounter_Show && Phobos::Config::ShowWeedsCounter) { - const auto pSideExt = SideExt::ExtMap.Find(SideClass::Array.GetItem(pPlayer->SideIndex)); + const auto pSideExt = SideExt::Fetch(SideClass::Array.GetItem(pPlayer->SideIndex)); wchar_t counter[0x20]; const ColorStruct clrToolTip = pSideExt->Sidebar_WeedsCounter_Color.Get(Drawing::TooltipColor); @@ -222,12 +222,12 @@ DEFINE_HOOK(0x6A8463, StripClass_OperatorLessThan_CameoPriority, 0x5) GET_STACK(const int, idxRight, STACK_OFFSET(0x1C, 0x10)); GET_STACK(const AbstractType, rttiLeft, STACK_OFFSET(0x1C, 0x4)); GET_STACK(const AbstractType, rttiRight, STACK_OFFSET(0x1C, 0xC)); - const auto pLeftTechnoExt = TechnoTypeExt::ExtMap.TryFind(pLeft); - const auto pRightTechnoExt = TechnoTypeExt::ExtMap.TryFind(pRight); + const auto pLeftTechnoExt = TechnoTypeExt::TryFetch(pLeft); + const auto pRightTechnoExt = TechnoTypeExt::TryFetch(pRight); const auto pLeftSWExt = (rttiLeft == AbstractType::Special || rttiLeft == AbstractType::Super || rttiLeft == AbstractType::SuperWeaponType) - ? SWTypeExt::ExtMap.TryFind(SuperWeaponTypeClass::Array.GetItem(idxLeft)) : nullptr; + ? SWTypeExt::TryFetch(SuperWeaponTypeClass::Array.GetItem(idxLeft)) : nullptr; const auto pRightSWExt = (rttiRight == AbstractType::Special || rttiRight == AbstractType::Super || rttiRight == AbstractType::SuperWeaponType) - ? SWTypeExt::ExtMap.TryFind(SuperWeaponTypeClass::Array.GetItem(idxRight)) : nullptr; + ? SWTypeExt::TryFetch(SuperWeaponTypeClass::Array.GetItem(idxRight)) : nullptr; if ((pLeftTechnoExt || pLeftSWExt) && (pRightTechnoExt || pRightSWExt)) { @@ -360,7 +360,7 @@ DEFINE_HOOK(0x683E41, ScenarioClass_Start_ShowBriefing, 0x6) { const SideClass* pSide = SideClass::Array.GetItemOrDefault(ScenarioClass::Instance->PlayerSideIndex); - if (const auto pSideExt = SideExt::ExtMap.TryFind(pSide)) + if (const auto pSideExt = SideExt::TryFetch(pSide)) theme = pSideExt->BriefingTheme; } @@ -563,7 +563,7 @@ DEFINE_HOOK(0x6D4A10, TacticalClass_Render_DrawSuperTimer_PercentageTimer, 0x6) DrawTimerTemp::IsPercentage = false; const int timeLeft = pSuper->RechargeTimer.GetTimeLeft(); - const auto pSWTypeExt = SWTypeExt::ExtMap.Find(pSuper->Type); + const auto pSWTypeExt = SWTypeExt::Fetch(pSuper->Type); if (pSWTypeExt->ShowTimer_Percentage.Get(RulesExt::Global()->SuperWeaponTimer_Percentage)) { @@ -642,7 +642,7 @@ DEFINE_HOOK(0x6DBEA3, TacticalClass_DrawRadialIndicator_Building_Extras, 0x7) if (Phobos::Config::ShowPowerPlantEnhancerRange && RulesExt::Global()->ShowPowerPlantEnhancerRange) { - const auto pCurrentExt = HouseExt::ExtMap.Find(HouseClass::CurrentPlayer); + const auto pCurrentExt = HouseExt::Fetch(HouseClass::CurrentPlayer); const auto center = DisplayClass::Instance.CurrentFoundation_CenterCell; for (const auto pEnhancer : pCurrentExt->PowerPlantEnhancers) @@ -650,7 +650,7 @@ DEFINE_HOOK(0x6DBEA3, TacticalClass_DrawRadialIndicator_Building_Extras, 0x7) if (!TechnoExt::IsActive(pEnhancer) || pEnhancer->InLimbo || !pEnhancer->HasPower) continue; - const auto pEnhancerTypeExt = BuildingTypeExt::ExtMap.Find(pEnhancer->Type); + const auto pEnhancerTypeExt = BuildingTypeExt::Fetch(pEnhancer->Type); const int range = pEnhancerTypeExt->PowerPlantEnhancer_Range.Get() / Unsorted::LeptonsPerCell; if (range <= 0 || !pEnhancerTypeExt->PowerPlantEnhancer_Buildings.Contains(pCurrentBuilding->Type)) diff --git a/src/Misc/MessageColumn.cpp b/src/Misc/MessageColumn.cpp index 9e2c587351..9831513648 100644 --- a/src/Misc/MessageColumn.cpp +++ b/src/Misc/MessageColumn.cpp @@ -527,7 +527,7 @@ void MessageColumnClass::InitIO() this->Scroll_Board = pButton; } - const int color = SideExt::ExtMap.Find(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])->MessageTextColor; + const int color = SideExt::Fetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])->MessageTextColor; // 0x72A4C5 if (const auto pScheme = ColorScheme::Array.Items[(color < 0 || color >= ColorScheme::Array.Count) ? 0 : color]) diff --git a/src/Misc/PhobosToolTip.cpp b/src/Misc/PhobosToolTip.cpp index 8e7da08d1d..eaed57198b 100644 --- a/src/Misc/PhobosToolTip.cpp +++ b/src/Misc/PhobosToolTip.cpp @@ -19,14 +19,14 @@ inline bool PhobosToolTip::IsEnabled() const return Phobos::UI::ExtendedToolTips; } -inline const wchar_t* PhobosToolTip::GetUIDescription(TechnoTypeExt::ExtData* pData) const +inline const wchar_t* PhobosToolTip::GetUIDescription(TechnoTypeExt* pData) const { return Phobos::Config::ToolTipDescriptions && !pData->UIDescription.Get().empty() ? pData->UIDescription.Get().Text : nullptr; } -inline const wchar_t* PhobosToolTip::GetUIDescription(SWTypeExt::ExtData* pData) const +inline const wchar_t* PhobosToolTip::GetUIDescription(SWTypeExt* pData) const { return Phobos::Config::ToolTipDescriptions && !pData->UIDescription.Get().empty() ? pData->UIDescription.Get().Text @@ -76,7 +76,7 @@ inline int PhobosToolTip::GetPower(TechnoTypeClass* pType) const if (!Phobos::Config::UnitPowerDrain) return 0; - const auto pExt = TechnoTypeExt::ExtMap.Find(pType); + const auto pExt = TechnoTypeExt::Fetch(pType); return pExt->Power; } case AbstractType::BuildingType: @@ -123,7 +123,7 @@ void PhobosToolTip::HelpText_Techno(TechnoTypeClass* pType) if (!pType) return; - auto const pData = TechnoTypeExt::ExtMap.Find(pType); + auto const pData = TechnoTypeExt::Fetch(pType); const int nBuildTime = TickTimeToSeconds(this->GetBuildTime(pType)); const int nSec = nBuildTime % 60; @@ -157,7 +157,7 @@ void PhobosToolTip::HelpText_Super(int swidx) { auto const pSuper = HouseClass::CurrentPlayer->Supers.Items[swidx]; auto const pType = pSuper->Type; - auto const pData = SWTypeExt::ExtMap.Find(pType); + auto const pData = SWTypeExt::Fetch(pType); std::wostringstream oss; oss << pType->UIName; @@ -189,7 +189,7 @@ void PhobosToolTip::HelpText_Super(int swidx) showSth = true; } - auto const& sw_ext = HouseExt::ExtMap.Find(HouseClass::CurrentPlayer)->SuperExts[swidx]; + auto const& sw_ext = HouseExt::Fetch(HouseClass::CurrentPlayer)->SuperExts[swidx]; const int sw_shots = pData->SW_Shots; const int remain_shots = pData->SW_Shots - sw_ext.ShotCount; if (sw_shots > 0) @@ -425,7 +425,7 @@ DEFINE_HOOK(0x478FDC, CCToolTip_Draw2_FillRect, 0x5) const int nPlayerSideIndex = ScenarioClass::Instance->PlayerSideIndex; if (auto const pSide = SideClass::Array.GetItemOrDefault(nPlayerSideIndex)) { - if (auto const pData = SideExt::ExtMap.TryFind(pSide)) + if (auto const pData = SideExt::TryFetch(pSide)) { // Could this flag be lazy? if (isCameo) diff --git a/src/Misc/PhobosToolTip.h b/src/Misc/PhobosToolTip.h index 31c1a92852..73407eec3d 100644 --- a/src/Misc/PhobosToolTip.h +++ b/src/Misc/PhobosToolTip.h @@ -11,8 +11,8 @@ class PhobosToolTip static PhobosToolTip Instance; private: - inline const wchar_t* GetUIDescription(TechnoTypeExt::ExtData* pData) const; - inline const wchar_t* GetUIDescription(SWTypeExt::ExtData* pData) const; + inline const wchar_t* GetUIDescription(TechnoTypeExt* pData) const; + inline const wchar_t* GetUIDescription(SWTypeExt* pData) const; inline int GetBuildTime(TechnoTypeClass* pType) const; inline int GetPower(TechnoTypeClass* pType) const; diff --git a/src/Misc/Selection.cpp b/src/Misc/Selection.cpp index 3018260c0c..2bdfbb5ae7 100644 --- a/src/Misc/Selection.cpp +++ b/src/Misc/Selection.cpp @@ -63,7 +63,7 @@ class ExtSelection { if ((selected.Object->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { - if (!TechnoExt::ExtMap.Find(static_cast(selected.Object))->TypeExtData->LowSelectionPriority) + if (!TechnoExt::Fetch(static_cast(selected.Object))->TypeExtData->LowSelectionPriority) return true; } } @@ -87,7 +87,7 @@ class ExtSelection const auto pObject = selected.Object; const auto pTechnoType = pObject->GetTechnoType(); // Returns nullptr on non techno objects - if (auto const pTypeExt = TechnoTypeExt::ExtMap.TryFind(pTechnoType)) // If pTechnoType is nullptr so will be pTypeExt + if (auto const pTypeExt = TechnoTypeExt::TryFetch(pTechnoType)) // If pTechnoType is nullptr so will be pTypeExt { if (bPriorityFiltering && pTypeExt->LowSelectionPriority) continue; @@ -127,7 +127,7 @@ class ExtSelection do { const auto pTechnoType = pTechno->GetTechnoType(); - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + const auto pTypeExt = TechnoTypeExt::Fetch(pTechnoType); const char* id = pTypeExt->GetSelectionGroupID(); if (std::ranges::none_of(names, [id](const char* pID) { return !_stricmp(pID, id); })) @@ -202,7 +202,7 @@ DEFINE_HOOK(0x73298D, TypeSelectExecute_UseIFVMode, 0x5) if (!pTechnoType->Gunner) continue; - const auto pTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + const auto pTypeExt = TechnoTypeExt::Fetch(pTechnoType); char* gunnerID = pTypeExt->WeaponGroupAs[pTechno->CurrentWeaponNumber]; if (!GeneralUtils::IsValidString(gunnerID)) diff --git a/src/Misc/SyncLogging.cpp b/src/Misc/SyncLogging.cpp index 4668609d22..881e99cb5d 100644 --- a/src/Misc/SyncLogging.cpp +++ b/src/Misc/SyncLogging.cpp @@ -570,7 +570,7 @@ bool ObjectFake::_IsCRCHashable() { // If animation type has logic that affects game simulation, don't ignore. if (pType->Damage != 0.0 || pType->Bouncer || pType->IsMeteor || pType->IsTiberium || pType->TiberiumChainReaction - || pType->IsAnimatedTiberium || pType->MakeInfantry != -1 || AnimTypeExt::ExtMap.Find(pType)->CreateUnitType.get()) + || pType->IsAnimatedTiberium || pType->MakeInfantry != -1 || AnimTypeExt::Fetch(pType)->CreateUnitType.get()) { return true; } diff --git a/src/New/Entity/Ares/RadarJammerClass.cpp b/src/New/Entity/Ares/RadarJammerClass.cpp index 01e006316e..aa1d58381a 100644 --- a/src/New/Entity/Ares/RadarJammerClass.cpp +++ b/src/New/Entity/Ares/RadarJammerClass.cpp @@ -5,7 +5,7 @@ void RadarJammerClass::Update() { const auto pTechno = this->Techno; - const auto pTypeExt = TechnoExt::ExtMap.Find(pTechno)->TypeExtData; + const auto pTypeExt = TechnoExt::Fetch(pTechno)->TypeExtData; if (Unsorted::CurrentFrame - this->LastScan < pTypeExt->RadarJamDelay) return; diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index 9461004260..b5e246b026 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -58,7 +58,7 @@ AttachEffectClass::AttachEffectClass(AttachEffectTypeClass* pType, TechnoClass* if (pType->Duration_ApplyFirepowerMult && duration > 0 && pInvoker) duration = Math::max(static_cast(duration * TechnoExt::GetCurrentFirepowerMultiplier(pInvoker)), 0); - const auto pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + const auto pTechnoExt = TechnoExt::Fetch(pTechno); if (pType->Duration_ApplyArmorMultOnTarget && duration > 0) // count its own ArmorMultiplier as well { @@ -75,7 +75,7 @@ AttachEffectClass::AttachEffectClass(AttachEffectTypeClass* pType, TechnoClass* } if (pInvoker) - TechnoExt::ExtMap.Find(pInvoker)->AttachedEffectInvokerCount++; + TechnoExt::Fetch(pInvoker)->AttachedEffectInvokerCount++; AttachEffectClass::Array.emplace_back(this); } @@ -84,7 +84,7 @@ AttachEffectClass::~AttachEffectClass() { if (const auto& pTrail = this->LaserTrail) { - const auto pTechnoExt = TechnoExt::ExtMap.Find(this->Techno); + const auto pTechnoExt = TechnoExt::Fetch(this->Techno); const auto it = std::find_if(pTechnoExt->LaserTrails.cbegin(), pTechnoExt->LaserTrails.cend(), [pTrail](std::unique_ptr const& item) { return item.get() == pTrail; }); if (it != pTechnoExt->LaserTrails.cend()) @@ -101,7 +101,7 @@ AttachEffectClass::~AttachEffectClass() this->KillAnim(); if (this->Invoker) - TechnoExt::ExtMap.Find(this->Invoker)->AttachedEffectInvokerCount--; + TechnoExt::Fetch(this->Invoker)->AttachedEffectInvokerCount--; } void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) @@ -113,7 +113,7 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) if (auto const pAnim = abstract_cast(abs)) { - if (auto const pAnimExt = AnimExt::ExtMap.Find(pAnim)) + if (auto const pAnimExt = AnimExt::Fetch(pAnim)) { if (pAnimExt->IsAttachedEffectAnim) { @@ -132,7 +132,7 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) { auto const pTechno = abstract_cast(abs); - if (int count = TechnoExt::ExtMap.Find(pTechno)->AttachedEffectInvokerCount) + if (int count = TechnoExt::Fetch(pTechno)->AttachedEffectInvokerCount) { for (auto const pEffect : AttachEffectClass::Array) { @@ -166,7 +166,7 @@ void AttachEffectClass::AI() } auto const pType = this->Type; - auto const pExt = TechnoExt::ExtMap.Find(pTechno); + auto const pExt = TechnoExt::Fetch(pTechno); if (!this->HasInitialized && this->InitialDelay == 0) { @@ -304,7 +304,7 @@ void AttachEffectClass::AnimCheck() { if (this->Type->Animation_HideIfAttachedWith.size() > 0) { - auto const pTechnoExt = TechnoExt::ExtMap.Find(this->Techno); + auto const pTechnoExt = TechnoExt::Fetch(this->Techno); if (pTechnoExt->HasAttachedEffects(this->Type->Animation_HideIfAttachedWith, false, false, nullptr, nullptr, nullptr, nullptr)) { @@ -338,7 +338,7 @@ void AttachEffectClass::OnlineCheck() if (isActive != this->LastActiveStat) { - auto const pExt = TechnoExt::ExtMap.Find(pTechno); + auto const pExt = TechnoExt::Fetch(pTechno); if (pExt->RecalculateStatMultipliers(this) && pTechno->CloakState == CloakState::Cloaked) pTechno->Uncloak(true); @@ -389,7 +389,7 @@ void AttachEffectClass::CloakCheck() const auto cloakState = this->Techno->CloakState; this->IsCloaked = cloakState == CloakState::Cloaked || cloakState == CloakState::Cloaking; - if (this->IsCloaked && this->Animation && AnimTypeExt::ExtMap.Find(this->Animation->Type)->DetachOnCloak) + if (this->IsCloaked && this->Animation && AnimTypeExt::Fetch(this->Animation->Type)->DetachOnCloak) this->KillAnim(); } @@ -404,7 +404,7 @@ void AttachEffectClass::CreateAnim() if (!this->HasCumulativeAnim) return; - const int count = TechnoExt::ExtMap.Find(pTechno)->GetAttachedEffectCumulativeCount(pType); + const int count = TechnoExt::Fetch(pTechno)->GetAttachedEffectCumulativeCount(pType); pAnimType = pType->GetCumulativeAnimation(count); } else @@ -414,7 +414,7 @@ void AttachEffectClass::CreateAnim() if (pAnimType) { - if (this->IsCloaked && AnimTypeExt::ExtMap.Find(pAnimType)->DetachOnCloak) + if (this->IsCloaked && AnimTypeExt::Fetch(pAnimType)->DetachOnCloak) return; auto const pAnim = GameCreate(pAnimType, pTechno->Location); @@ -422,7 +422,7 @@ void AttachEffectClass::CreateAnim() pAnim->SetOwnerObject(pTechno); pAnim->Owner = pType->Animation_UseInvokerAsOwner ? this->InvokerHouse : pTechno->Owner; - auto const pAnimExt = AnimExt::ExtMap.Find(pAnim); + auto const pAnimExt = AnimExt::Fetch(pAnim); pAnimExt->IsAttachedEffectAnim = true; if (pType->Animation_UseInvokerAsOwner) @@ -455,7 +455,7 @@ void AttachEffectClass::UpdateCumulativeAnim() return; const auto pType = this->Type; - const int count = TechnoExt::ExtMap.Find(this->Techno)->GetAttachedEffectCumulativeCount(pType); + const int count = TechnoExt::Fetch(this->Techno)->GetAttachedEffectCumulativeCount(pType); if (count < 1) { @@ -504,7 +504,7 @@ void AttachEffectClass::RefreshDuration(int durationOverride) if (pType->Duration_ApplyArmorMultOnTarget && duration > 0) // no need to count its own effect again { - const auto pTechnoExt = TechnoExt::ExtMap.Find(this->Techno); + const auto pTechnoExt = TechnoExt::Fetch(this->Techno); const double armorMultiplier = TechnoExt::GetCurrentArmorMultiplier(this->Techno, pTechnoExt->TypeExtData->OwnerObject()); duration = Math::max(static_cast(duration / armorMultiplier), 0); } @@ -687,7 +687,7 @@ int AttachEffectClass::Attach(TechnoClass* pTarget, HouseClass* pInvokerHouse, T if (types.size() < 1 || !pTarget) return false; - auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); + auto const pTargetExt = TechnoExt::Fetch(pTarget); auto const pTargetType = pTargetExt->TypeExtData->OwnerObject(); int attachedCount = 0; bool markForRedraw = false; @@ -878,7 +878,7 @@ int AttachEffectClass::DetachByGroups(TechnoClass* pTarget, AEAttachInfoTypeClas if (groups.size() < 1 || !pTarget) return 0; - auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); + auto const pTargetExt = TechnoExt::Fetch(pTarget); std::vector types; types.reserve(pTargetExt->AttachedEffects.size()); @@ -933,7 +933,7 @@ int AttachEffectClass::DetachTypes(TechnoClass* pTarget, AEAttachInfoTypeClass c if (detachedCount > 0) { - const auto pExt = TechnoExt::ExtMap.Find(pTarget); + const auto pExt = TechnoExt::Fetch(pTarget); if (altered) pExt->RecalculateStatMultipliers(); @@ -961,7 +961,7 @@ int AttachEffectClass::RemoveAllOfType(AttachEffectTypeClass* pType, TechnoClass if (!pType || !pTarget) return 0; - auto const pTargetExt = TechnoExt::ExtMap.Find(pTarget); + auto const pTargetExt = TechnoExt::Fetch(pTarget); int detachedCount = 0; int stackCount = -1; @@ -1046,8 +1046,8 @@ void AttachEffectClass::TransferAttachedEffects(TechnoClass* pSource, TechnoClas bool markForRedraw = false; bool altered = false; int transferCount = 0; - const auto pSourceExt = TechnoExt::ExtMap.Find(pSource); - const auto pTargetExt = TechnoExt::ExtMap.Find(pTarget); + const auto pSourceExt = TechnoExt::Fetch(pSource); + const auto pTargetExt = TechnoExt::Fetch(pTarget); const auto pTargetType = pTarget->GetTechnoType(); std::vector>::iterator it; @@ -1122,7 +1122,7 @@ void AttachEffectClass::TransferAttachedEffects(TechnoClass* pSource, TechnoClas if (transferCount > 0) { - const auto pExt = TechnoExt::ExtMap.Find(pTarget); + const auto pExt = TechnoExt::Fetch(pTarget); if (altered) pExt->RecalculateStatMultipliers(); diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index c7867500d9..656519e0f2 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -29,7 +29,7 @@ ShieldClass::ShieldClass(TechnoClass* pTechno, bool isAttached) , Respawn_Rate_Warhead { -1 } , IsSelfHealingEnabled { true } { - auto const pType = TechnoExt::ExtMap.Find(pTechno)->CurrentShieldType; + auto const pType = TechnoExt::Fetch(pTechno)->CurrentShieldType; this->Type = pType; this->SetHP(pType->InitialStrength.Get(pType->Strength)); this->TechnoID = pTechno->GetTechnoType(); @@ -53,7 +53,7 @@ void ShieldClass::PointerGotInvalid(void* ptr, bool removed) if (auto const pAnim = abstract_cast(abs)) { - if (auto const pAnimExt = AnimExt::ExtMap.Find(pAnim)) + if (auto const pAnimExt = AnimExt::Fetch(pAnim)) { if (pAnimExt->IsShieldIdleAnim) { @@ -125,8 +125,8 @@ bool ShieldClass::Save(PhobosStreamWriter& Stm) const // Is used for DeploysInto/UndeploysInto void ShieldClass::SyncShieldToAnother(TechnoClass* pFrom, TechnoClass* pTo) { - const auto pFromExt = TechnoExt::ExtMap.Find(pFrom); - const auto pToExt = TechnoExt::ExtMap.Find(pTo); + const auto pFromExt = TechnoExt::Fetch(pFrom); + const auto pToExt = TechnoExt::Fetch(pTo); if (pFromExt->Shield) { @@ -151,7 +151,7 @@ bool ShieldClass::ShieldIsBrokenTEvent(ObjectClass* pAttached) { if (auto const pTechno = abstract_cast(pAttached)) { - auto const pShield = TechnoExt::ExtMap.Find(pTechno)->Shield.get(); + auto const pShield = TechnoExt::Fetch(pTechno)->Shield.get(); return !pShield || pShield->HP <= 0; } @@ -183,7 +183,7 @@ int ShieldClass::ReceiveDamage(args_ReceiveDamage* args) } auto const pWH = args->WH; - auto const pWHExt = WarheadTypeExt::ExtMap.Find(pWH); + auto const pWHExt = WarheadTypeExt::Fetch(pWH); const bool IC = pWHExt->CanAffectInvulnerable(pTechno); if (!IC || this->CanBePenetrated(pWH) || TechnoExt::IsTypeImmune(pTechno, args->Attacker)) @@ -236,7 +236,7 @@ int ShieldClass::ReceiveDamage(args_ReceiveDamage* args) shieldDamage = Math::clamp(shieldDamage, minDmg, maxDmg); if (Phobos::DisplayDamageNumbers && shieldDamage != 0) - GeneralUtils::DisplayDamageNumberString(shieldDamage, DamageDisplayType::Shield, pTechno->GetRenderCoords(), TechnoExt::ExtMap.Find(pTechno)->DamageNumberOffset); + GeneralUtils::DisplayDamageNumberString(shieldDamage, DamageDisplayType::Shield, pTechno->GetRenderCoords(), TechnoExt::Fetch(pTechno)->DamageNumberOffset); if (shieldDamage > 0) { @@ -392,7 +392,7 @@ bool ShieldClass::CanBePenetrated(WarheadTypeClass* pWarhead) const if (!pWarhead) return false; - const auto pWHExt = WarheadTypeExt::ExtMap.Find(pWarhead); + const auto pWHExt = WarheadTypeExt::Fetch(pWarhead); const auto affectedTypes = pWHExt->Shield_Penetrate_Types.GetElements(pWHExt->Shield_AffectTypes); @@ -448,7 +448,7 @@ void ShieldClass::AI() if (!pTechno || pTechno->InLimbo || pTechno->IsImmobilized || pTechno->Transporter) return; - auto const pTechnoExt = TechnoExt::ExtMap.Find(pTechno); + auto const pTechnoExt = TechnoExt::Fetch(pTechno); if (pTechno->Health <= 0 || !pTechno->IsAlive || pTechno->IsSinking) { @@ -503,7 +503,7 @@ void ShieldClass::CloakCheck() const auto cloakState = this->Techno->CloakState; this->Cloak = cloakState == CloakState::Cloaked || cloakState == CloakState::Cloaking; - if (this->Cloak && this->IdleAnim && AnimTypeExt::ExtMap.Find(this->IdleAnim->Type)->DetachOnCloak) + if (this->Cloak && this->IdleAnim && AnimTypeExt::Fetch(this->IdleAnim->Type)->DetachOnCloak) this->KillAnim(); } @@ -622,8 +622,8 @@ void ShieldClass::TemporalCheck() // Is used for DeploysInto/UndeploysInto and Type conversion void ShieldClass::ConvertCheck(TechnoTypeClass* pTechnoType) { - const auto pTechnoExt = TechnoExt::ExtMap.Find(this->Techno); - const auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + const auto pTechnoExt = TechnoExt::Fetch(this->Techno); + const auto pTechnoTypeExt = TechnoTypeExt::Fetch(pTechnoType); const auto pOldType = this->Type; const bool allowTransfer = pOldType->AllowTransfer.Get(Attached); @@ -919,7 +919,7 @@ void ShieldClass::CreateAnim(ShieldTypeClass* pType, AnimTypeClass* idleAnimType if (idleAnimType) { - if (this->Cloak && AnimTypeExt::ExtMap.Find(idleAnimType)->DetachOnCloak) + if (this->Cloak && AnimTypeExt::Fetch(idleAnimType)->DetachOnCloak) return; auto const pTechno = this->Techno; @@ -928,7 +928,7 @@ void ShieldClass::CreateAnim(ShieldTypeClass* pType, AnimTypeClass* idleAnimType pAnim->SetOwnerObject(pTechno); pAnim->Owner = pTechno->Owner; - auto const pAnimExt = AnimExt::ExtMap.Find(pAnim); + auto const pAnimExt = AnimExt::Fetch(pAnim); pAnimExt->SetInvoker(pTechno); pAnimExt->IsShieldIdleAnim = true; @@ -974,7 +974,7 @@ void ShieldClass::UpdateTint() if (this->Type->HasTint()) { auto const pTechno = this->Techno; - TechnoExt::ExtMap.Find(pTechno)->UpdateTintValues(); + TechnoExt::Fetch(pTechno)->UpdateTintValues(); pTechno->MarkForRedraw(); } } diff --git a/src/New/Type/Affiliated/DroppodTypeClass.cpp b/src/New/Type/Affiliated/DroppodTypeClass.cpp index 23524d991b..c7b26a03f0 100644 --- a/src/New/Type/Affiliated/DroppodTypeClass.cpp +++ b/src/New/Type/Affiliated/DroppodTypeClass.cpp @@ -70,7 +70,7 @@ DEFINE_HOOK(0x4B5B70, DroppodLocomotionClass_ILoco_Process, 0x5) __assume(iloco != nullptr); auto const lThis = static_cast(iloco); auto const pLinked = lThis->LinkedTo; - auto const linkedExt = TechnoExt::ExtMap.Find(pLinked); + auto const linkedExt = TechnoExt::Fetch(pLinked); const auto podType = linkedExt->TypeExtData->DroppodType.get(); if (!podType) @@ -187,7 +187,7 @@ DEFINE_HOOK(0x4B607D, DroppodLocomotionClass_ILoco_MoveTo, 0x8) auto const lThis = static_cast(iloco); auto const pLinked = lThis->LinkedTo; - const auto podType = TechnoExt::ExtMap.Find(pLinked)->TypeExtData->DroppodType.get(); + const auto podType = TechnoExt::Fetch(pLinked)->TypeExtData->DroppodType.get(); if (!podType) return 0; @@ -222,7 +222,7 @@ 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(); + auto const podType = TechnoTypeExt::Fetch(pThis->Type)->DroppodType.get(); shp = podType->AirImage.Get(RulesExt::Global()->PodImage); return 0x519176; diff --git a/src/Utilities/AresAddressInit.cpp b/src/Utilities/AresAddressInit.cpp index bf6d4d6c4b..0e788a5dea 100644 --- a/src/Utilities/AresAddressInit.cpp +++ b/src/Utilities/AresAddressInit.cpp @@ -62,7 +62,7 @@ void AresFunctions::InitAres3_0() NOTE_ARES_FUN(AlphaExtMap, 0xC1924); - // BuildingTypeExt::ExtData + // BuildingTypeExt NOTE_ARES_FUN(AresFunctions::GetTunnel, 0x0D740); NOTE_ARES_FUN(AresFunctions::AddPassengerFromTunnel, 0x09000); @@ -109,7 +109,7 @@ void AresFunctions::InitAres3_0p1() NOTE_ARES_FUN(AlphaExtMap, 0xC2988); - // BuildingTypeExt::ExtData + // BuildingTypeExt NOTE_ARES_FUN(AresFunctions::GetTunnel, 0x0DA30); NOTE_ARES_FUN(AresFunctions::AddPassengerFromTunnel, 0x09040); diff --git a/src/Utilities/AresFunctions.h b/src/Utilities/AresFunctions.h index 000ec07dd9..b6c2e6c625 100644 --- a/src/Utilities/AresFunctions.h +++ b/src/Utilities/AresFunctions.h @@ -39,7 +39,7 @@ class AresFunctions static void(__thiscall* UnitDeliveryStateMachine_Update)(void*); - // WarheadTypeExt::ExtData + // WarheadTypeExt static bool(__thiscall* ApplyPermaMC)(void*, HouseClass* pSourceHouse, AbstractClass* pTarget); static bool (*DetailsCurrentlyEnabled)(); @@ -50,7 +50,7 @@ class AresFunctions static PhobosMap* AlphaExtMap; - // BuildingTypeExt::ExtData + // BuildingTypeExt static void* (__thiscall* GetTunnel)(void*, HouseClass*); static void(__thiscall* AddPassengerFromTunnel)(void*, BuildingClass*, FootClass*); diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index e08c3cc33e..6fde2bfa25 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -11,6 +11,8 @@ #include "Swizzle.h" #include "Phobos.h" +class AbstractClass; + enum class InitState { Blank = 0x0, // CTOR'd @@ -54,12 +56,12 @@ enum class InitState // it owns the back-pointer and the staged init state, so all extensions share a common base. class AbstractExt { - void* AttachedToObject; + AbstractClass* AttachedToObject; InitState Initialized; public: - explicit AbstractExt(void* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } + explicit AbstractExt(AbstractClass* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } { } AbstractExt(const AbstractExt& other) = delete; @@ -116,14 +118,14 @@ class AbstractExt // this queues it for remapping when the swizzle manager resolves pointers. void RegisterOwnerForChange() { - PhobosSwizzle::RegisterForChange(&this->AttachedToObject); + PhobosSwizzle::RegisterPointerForChange(this->AttachedToObject); } // called after loading once all pointers (including the owner) have been remapped virtual void PostLoad() { } protected: - void* GetAttachedObject() const + AbstractClass* GetAttachedObject() const { return this->AttachedToObject; } @@ -144,20 +146,91 @@ class AbstractExt virtual void LoadFromINIFile(CCINIClass* pINI) { } }; -// the typed layer over AbstractExt: gives a strongly-typed owner accessor. +// legacy standalone base for extensions whose owners are not AbstractClass-derived +// (the Rules/Scenario/Sidebar singletons and EBolt); AbstractClass-derived owners use +// the AbstractExt hierarchy instead. template -class Extension : public AbstractExt +class Extension { + T* AttachedToObject; + InitState Initialized; + public: - explicit Extension(T* const OwnerObject) : AbstractExt(OwnerObject) + explicit Extension(T* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } { } + Extension(const Extension& other) = delete; + + void operator=(const Extension& RHS) = delete; + + virtual ~Extension() = default; + // the object this Extension expands T* OwnerObject() const { - return static_cast(this->GetAttachedObject()); + return this->AttachedToObject; + } + + void EnsureConstanted() + { + if (this->Initialized < InitState::Constanted) + { + this->InitializeConstants(); + this->Initialized = InitState::Constanted; + } + } + + void LoadFromINI(CCINIClass* pINI) + { + if (!pINI) + return; + + switch (this->Initialized) + { + case InitState::Blank: + this->EnsureConstanted(); + case InitState::Constanted: + this->InitializeRuled(); + this->Initialized = InitState::Ruled; + case InitState::Ruled: + this->Initialize(); + this->Initialized = InitState::Inited; + case InitState::Inited: + case InitState::Completed: + if (pINI == CCINIClass::INI_Rules) + this->LoadFromRulesFile(pINI); + + this->LoadFromINIFile(pINI); + this->Initialized = InitState::Completed; + } + } + + virtual inline void SaveToStream(PhobosStreamWriter& Stm) + { + Stm.Save(this->Initialized); } + + virtual inline void LoadFromStream(PhobosStreamReader& Stm) + { + Stm.Load(this->Initialized); + } + +protected: + // right after construction. only basic initialization tasks possible; + // owner object is only partially constructed! do not use global state! + virtual void InitializeConstants() { } + + virtual void InitializeRuled() { } + + // called before the first ini file is read + virtual void Initialize() { } + + // for things that only logically work in rules - countries, sides, etc + virtual void LoadFromRulesFile(CCINIClass* pINI) { } + + // load any ini file: rules, game mode, scenario or map + virtual void LoadFromINIFile(CCINIClass* pINI) { } }; // a non-virtual base class for a pointer to pointer map. @@ -423,22 +496,6 @@ class Container return this->MappedItems.find(key); } - // Only used on loading, does not check if key is nullptr. - extension_type_ptr FindOrAllocate(base_type_ptr key) - { - extension_type_ptr value = nullptr; - - if constexpr (HasOffset) - value = GetExtensionPointer(key); - else - value = this->MappedItems.find(key); - - if (!value) - value = Allocate(key); - - return value; - } - void Remove(base_type_ptr key) { if (auto Item = Find(key)) diff --git a/src/Utilities/GeneralUtils.cpp b/src/Utilities/GeneralUtils.cpp index ffdbf9f554..7e7d79ba0d 100644 --- a/src/Utilities/GeneralUtils.cpp +++ b/src/Utilities/GeneralUtils.cpp @@ -94,7 +94,7 @@ const double GeneralUtils::GetWarheadVersusArmor(WarheadTypeClass* pWH, Armor ar const double GeneralUtils::GetWarheadVersusArmor(WarheadTypeClass* pWH, TechnoClass* pThis, TechnoTypeClass* pType) { auto armorType = pType->Armor; - auto const pShield = TechnoExt::ExtMap.Find(pThis)->Shield.get(); + auto const pShield = TechnoExt::Fetch(pThis)->Shield.get(); if (pShield && pShield->IsActive() && !pShield->CanBePenetrated(pWH)) armorType = pShield->GetArmorType(pType); From ca8555043926d2afde0aa99b50630547c0f38f83 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:03:36 +0300 Subject: [PATCH 16/47] Update project structure documentation for the new extension system --- docs/Project-guidelines-and-policies.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index a08a3e5a53..0159378fc1 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -75,12 +75,13 @@ Assuming you've successfully cloned and built the project before getting here, y - `New/` - source code for new ingame classes. - `Type/` - new enumerated types (types that are declared with a list section in an INI, for example, radiation types) implemented in the project. Every enumerated type class inherits `Enumerable` (where `T` is an enum. type class) class that is defined in `Enumerable.h`. - `Entity/` - classes that represent ingame entities are located here. - - `Ext/` - source code for vanilla engine class extensions. Each class extension is kept in a separate folder named after vanilla engine class name and contains the following: - - `Body.h` and `Body.cpp` contain class and method definitions/declarations and common extension hooks. Each extension class must contain the following to work correctly: - - `ExtData` - extension data class definition which inherits `Extension` from `Container.h` (where `T` is the class that is being extended), which is the actual class that contains new data for vanilla classes; - - `ExtContainer` - a definition of a special map class to store and look up `ExtData` instances for base class instances which inherits `Container` from `Container.h` (where `T` is the extension data class); - - `ExtMap` - a static instance of `ExtContainer` map; - - constructor, destructor, serialization, deserialization and (for appropriate classes) INI reading hooks. + - `Ext/` - source code for vanilla engine class extensions. Extension classes form a parallel inheritance hierarchy mirroring the game's own class tree (`AbstractExt` from `Container.h` is the root; e.g. `BuildingExt : TechnoExt : RadioExt : MissionExt : ObjectExt : AbstractExt`), and every game object carries exactly one extension instance of the most derived matching type, cached inside the object at the unified `AbstractClass` `0x18` slot. Each class extension is kept in a separate folder named after vanilla engine class name and contains the following: + - `Body.h` and `Body.cpp` contain class and method definitions/declarations and common extension hooks. Each extension class contains the following to work correctly: + - new data members and (for appropriate classes) `LoadFromINIFile`/`SaveToStream`/`LoadFromStream` overrides, plus static helper methods; + - `ExtContainer`/`ExtMap` - the per-hierarchy container (inherits `Container` from `Container.h`) that tracks all live instances of the extension family for bulk operations (centralized savegame streaming, post-load relinking, scenario clearing); leaves of a family (e.g. `BuildingExt`) share their family root's container; + - `Fetch`/`TryFetch` statics - the O(1) lookup used at call sites (`TechnoExt::Fetch(pThis)`); + - constructor/destructor and (for appropriate classes) INI reading hooks. Serialization is centralized in `Phobos.Ext.cpp` - there are no per-class savegame hooks. + - Extensions subscribe to pointer invalidation by inheriting `Detach::Listener` from `Utilities/Detach.h` and overriding `OnDetach` (see `HouseExt` for an example). - `Hooks.cpp` and `Hooks.*.cpp` contain non-common hooks to correctly patch in new custom logics. - `ExtraHeaders/` - extra header files to interact with / describe types included in game binary that are not included in YRpp yet. - `Misc/` - uncategorized source code, including hooks that don't belong to an extension class. @@ -197,7 +198,7 @@ if (SomeCondition()) - A space must be put between braces of empty curly brace blocks. - To have less Git merge conflicts member initializer lists and other list-like syntax structures used in frequently modified places should be split per-item with item separation characters (commas, for example) placed *after newline character*: ```cpp -ExtData(TerrainTypeClass* OwnerObject) : Extension(OwnerObject) +TerrainTypeExt(TerrainTypeClass* OwnerObject) : ObjectTypeExt(OwnerObject) , SpawnsTiberium_Type(0) , SpawnsTiberium_Range(1) , SpawnsTiberium_GrowthStage({ 3, 0 }) From f63544405b81d9bccb8c4af168596ed920368237 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:17:39 +0300 Subject: [PATCH 17/47] Track extensions in one container per concrete leaf class --- src/Ext/Aircraft/Body.cpp | 7 +++++- src/Ext/Aircraft/Body.h | 31 +++++++++++++++++++++++++- src/Ext/AircraftType/Body.cpp | 7 +++++- src/Ext/AircraftType/Body.h | 31 +++++++++++++++++++++++++- src/Ext/Building/Body.cpp | 8 +++++-- src/Ext/Building/Body.h | 18 ++++++++++++--- src/Ext/BuildingType/Body.cpp | 8 +++++-- src/Ext/BuildingType/Body.h | 18 ++++++++++++--- src/Ext/Infantry/Body.cpp | 7 +++++- src/Ext/Infantry/Body.h | 31 +++++++++++++++++++++++++- src/Ext/InfantryType/Body.cpp | 7 +++++- src/Ext/InfantryType/Body.h | 31 +++++++++++++++++++++++++- src/Ext/Techno/Body.cpp | 29 +----------------------- src/Ext/Techno/Body.h | 30 +++++++++++++------------ src/Ext/TechnoType/Body.cpp | 30 +++---------------------- src/Ext/TechnoType/Body.h | 32 +++++++++++++------------- src/Ext/Unit/Body.cpp | 7 +++++- src/Ext/Unit/Body.h | 31 +++++++++++++++++++++++++- src/Ext/UnitType/Body.cpp | 7 +++++- src/Ext/UnitType/Body.h | 31 +++++++++++++++++++++++++- src/Phobos.Ext.cpp | 11 +++++++++ src/Utilities/Container.h | 42 +++++++++-------------------------- 22 files changed, 317 insertions(+), 137 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index c6771b95d1..2602c011dd 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -1,5 +1,10 @@ #include "Body.h" +AircraftExt::ExtContainer AircraftExt::ExtMap; + +AircraftExt::ExtContainer::ExtContainer() : Container("AircraftClass") { } +AircraftExt::ExtContainer::~ExtContainer() = default; + #include #include @@ -8,7 +13,7 @@ DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) { GET(AircraftClass*, pItem, ESI); - TechnoExt::ExtMap.Adopt(new AircraftExt(pItem)); + AircraftExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/Aircraft/Body.h b/src/Ext/Aircraft/Body.h index 008396badd..6a65ee5d98 100644 --- a/src/Ext/Aircraft/Body.h +++ b/src/Ext/Aircraft/Body.h @@ -4,9 +4,14 @@ #include // Concrete leaf extension for AircraftClass (empty; techno data lives in TechnoExt). -class AircraftExt : public FootExt +class AircraftExt final : public FootExt { public: + using base_type = AircraftClass; + using ExtData = AircraftExt; + + static constexpr DWORD Canary = 0xA1A2A3A4; + explicit AircraftExt(AircraftClass* const OwnerObject) : FootExt(OwnerObject) { } @@ -19,4 +24,28 @@ class AircraftExt : public FootExt static bool PlaceReinforcementAircraft(AircraftClass* pThis, CoordStruct edgeCoords); static CellStruct PickEdgeCellForPlane(AircraftTypeClass* pPlaneType, CellStruct destCell, Edge edge, bool isOnRetreat = false); static DirType GetLandingDir(AircraftClass* pThis, BuildingClass* pDock = nullptr); + + virtual ~AircraftExt() override + { + ExtMap.Unregister(this); + } + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static AircraftExt* Fetch(const AircraftClass* pThis) + { + return ExtMap.Find(pThis); + } + + static AircraftExt* TryFetch(const AircraftClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index f9a1ac1afe..cee0558ae1 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -1,10 +1,15 @@ #include "Body.h" +AircraftTypeExt::ExtContainer AircraftTypeExt::ExtMap; + +AircraftTypeExt::ExtContainer::ExtContainer() : Container("AircraftTypeClass") { } +AircraftTypeExt::ExtContainer::~ExtContainer() = default; + DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) { GET(AircraftTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.Adopt(new AircraftTypeExt(pItem)); + AircraftTypeExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/AircraftType/Body.h b/src/Ext/AircraftType/Body.h index 49c22b8eb4..4fa30dc519 100644 --- a/src/Ext/AircraftType/Body.h +++ b/src/Ext/AircraftType/Body.h @@ -4,9 +4,14 @@ #include // Concrete leaf extension for AircraftTypeClass (empty). -class AircraftTypeExt : public TechnoTypeExt +class AircraftTypeExt final : public TechnoTypeExt { public: + using base_type = AircraftTypeClass; + using ExtData = AircraftTypeExt; + + static constexpr DWORD Canary = 0xA5A6A7A8; + explicit AircraftTypeExt(AircraftTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) { } @@ -14,4 +19,28 @@ class AircraftTypeExt : public TechnoTypeExt { return static_cast(this->GetAttachedObject()); } + + virtual ~AircraftTypeExt() override + { + ExtMap.Unregister(this); + } + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static AircraftTypeExt* Fetch(const AircraftTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static AircraftTypeExt* TryFetch(const AircraftTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 4fde518f02..8026f40cfc 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -4,6 +4,11 @@ #include #include +BuildingExt::ExtContainer BuildingExt::ExtMap; + +BuildingExt::ExtContainer::ExtContainer() : Container("BuildingClass") { } +BuildingExt::ExtContainer::~ExtContainer() = default; + void BuildingExt::DisplayIncomeString() { @@ -602,8 +607,7 @@ DEFINE_HOOK(0x43BCBD, BuildingClass_CTOR, 0x6) { GET(BuildingClass*, pItem, ESI); - // A building's extension is a concrete BuildingExt leaf, owned by the TechnoClass container. - auto const pExt = static_cast(TechnoExt::ExtMap.Adopt(new BuildingExt(pItem))); + auto const pExt = BuildingExt::ExtMap.Allocate(pItem); if (pExt) pExt->TypeExtData = BuildingTypeExt::Fetch(pItem->Type); diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 349f3068ab..e468488381 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -55,7 +55,10 @@ class BuildingExt final : public TechnoExt, public Detach::Listener + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + static BuildingExt* Fetch(const BuildingClass* pThis) { - return static_cast(TechnoExt::Fetch(pThis)); + return ExtMap.Find(pThis); } static BuildingExt* TryFetch(const BuildingClass* pThis) { - return static_cast(TechnoExt::TryFetch(pThis)); + return ExtMap.TryFind(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index b4158c888d..618cf4a6f1 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -3,6 +3,11 @@ #include #include +BuildingTypeExt::ExtContainer BuildingTypeExt::ExtMap; + +BuildingTypeExt::ExtContainer::ExtContainer() : Container("BuildingTypeClass") { } +BuildingTypeExt::ExtContainer::~ExtContainer() = default; + // Assuming SuperWeapon & SuperWeapon2 are used (for the moment) int BuildingTypeExt::GetSuperWeaponCount() const @@ -468,8 +473,7 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) { GET(BuildingTypeClass*, pItem, EAX); - // A building type's extension is a concrete BuildingTypeExt leaf, owned by the TechnoTypeClass container. - TechnoTypeExt::ExtMap.Adopt(new BuildingTypeExt(pItem)); + BuildingTypeExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 8d9024a9ae..27083fde54 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -226,7 +226,10 @@ class BuildingTypeExt final : public TechnoTypeExt int GetSuperWeaponIndex(int index, HouseClass* pHouse) const; int GetSuperWeaponIndex(int index) const; - virtual ~BuildingTypeExt() = default; + virtual ~BuildingTypeExt() override + { + ExtMap.Unregister(this); + } virtual void LoadFromINIFile(CCINIClass* pINI) override; virtual void Initialize() override; @@ -241,14 +244,23 @@ class BuildingTypeExt final : public TechnoTypeExt public: + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + static BuildingTypeExt* Fetch(const BuildingTypeClass* pThis) { - return static_cast(TechnoTypeExt::Fetch(pThis)); + return ExtMap.Find(pThis); } static BuildingTypeExt* TryFetch(const BuildingTypeClass* pThis) { - return static_cast(TechnoTypeExt::TryFetch(pThis)); + return ExtMap.TryFind(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp index f327ead400..ee17cd559e 100644 --- a/src/Ext/Infantry/Body.cpp +++ b/src/Ext/Infantry/Body.cpp @@ -1,10 +1,15 @@ #include "Body.h" +InfantryExt::ExtContainer InfantryExt::ExtMap; + +InfantryExt::ExtContainer::ExtContainer() : Container("InfantryClass") { } +InfantryExt::ExtContainer::~ExtContainer() = default; + DEFINE_HOOK(0x517A60, InfantryClass_CTOR, 0xE) { GET(InfantryClass*, pItem, ESI); - TechnoExt::ExtMap.Adopt(new InfantryExt(pItem)); + InfantryExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/Infantry/Body.h b/src/Ext/Infantry/Body.h index 368d6e6536..0bd0cf6313 100644 --- a/src/Ext/Infantry/Body.h +++ b/src/Ext/Infantry/Body.h @@ -4,9 +4,14 @@ #include // Concrete leaf extension for InfantryClass (empty; techno data lives in TechnoExt). -class InfantryExt : public FootExt +class InfantryExt final : public FootExt { public: + using base_type = InfantryClass; + using ExtData = InfantryExt; + + static constexpr DWORD Canary = 0xF1F2F3F4; + explicit InfantryExt(InfantryClass* const OwnerObject) : FootExt(OwnerObject) { } @@ -14,4 +19,28 @@ class InfantryExt : public FootExt { return static_cast(this->GetAttachedObject()); } + + virtual ~InfantryExt() override + { + ExtMap.Unregister(this); + } + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static InfantryExt* Fetch(const InfantryClass* pThis) + { + return ExtMap.Find(pThis); + } + + static InfantryExt* TryFetch(const InfantryClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index d3710d9b46..21ab6a4091 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -1,10 +1,15 @@ #include "Body.h" +InfantryTypeExt::ExtContainer InfantryTypeExt::ExtMap; + +InfantryTypeExt::ExtContainer::ExtContainer() : Container("InfantryTypeClass") { } +InfantryTypeExt::ExtContainer::~ExtContainer() = default; + DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) { GET(InfantryTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.Adopt(new InfantryTypeExt(pItem)); + InfantryTypeExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/InfantryType/Body.h b/src/Ext/InfantryType/Body.h index 5191931a40..2b0a45a84e 100644 --- a/src/Ext/InfantryType/Body.h +++ b/src/Ext/InfantryType/Body.h @@ -4,9 +4,14 @@ #include // Concrete leaf extension for InfantryTypeClass (empty). -class InfantryTypeExt : public TechnoTypeExt +class InfantryTypeExt final : public TechnoTypeExt { public: + using base_type = InfantryTypeClass; + using ExtData = InfantryTypeExt; + + static constexpr DWORD Canary = 0xF5F6F7F8; + explicit InfantryTypeExt(InfantryTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) { } @@ -14,4 +19,28 @@ class InfantryTypeExt : public TechnoTypeExt { return static_cast(this->GetAttachedObject()); } + + virtual ~InfantryTypeExt() override + { + ExtMap.Unregister(this); + } + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static InfantryTypeExt* Fetch(const InfantryTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static InfantryTypeExt* TryFetch(const InfantryTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index c716c63ea0..ee017d1807 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -15,7 +15,6 @@ #include #include -TechnoExt::ExtContainer TechnoExt::ExtMap; UnitClass* TechnoExt::Deployer = nullptr; TechnoExt::~TechnoExt() @@ -1379,32 +1378,6 @@ bool TechnoExt::SaveGlobals(PhobosStreamWriter& Stm) .Success(); } -// ============================= -// container - -TechnoExt::ExtContainer::ExtContainer() : Container("TechnoClass") { } - -TechnoExt::ExtContainer::~ExtContainer() = default; - -TechnoExt* TechnoExt::ExtContainer::CreateExtData(AbstractType tag, TechnoClass* pOwner) const -{ - switch (tag) - { - case AbstractType::Unit: - return new UnitExt(static_cast(pOwner)); - case AbstractType::Infantry: - return new InfantryExt(static_cast(pOwner)); - case AbstractType::Aircraft: - return new AircraftExt(static_cast(pOwner)); - case AbstractType::Building: - return new BuildingExt(static_cast(pOwner)); - default: - Debug::FatalErrorAndExit("TechnoExt - unexpected extension tag %d in the save stream!\n", static_cast(tag)); - return nullptr; - } -} - - // ============================= // container hooks @@ -1418,7 +1391,7 @@ DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5) if (pItem->AbstractFlags & AbstractFlags::Foot) pItem->Owner->RecheckTechTree = true; // for SW.AuxTechons and SW.NegTechnos - TechnoExt::ExtMap.Remove(pItem); + TechnoExt::Destroy(pItem); return 0; } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 50cb96ed03..2c624c6131 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -242,26 +242,28 @@ class TechnoExt : public RadioExt, public Detach::Listener void Serialize(T& Stm); public: - class ExtContainer final : public Container - { - public: - ExtContainer(); - ~ExtContainer(); - - protected: - virtual ExtData* CreateExtData(AbstractType tag, TechnoClass* pOwner) const override; - }; - - static ExtContainer ExtMap; - + // TechnoExt is never instantiated and has no container of its own: instances are + // concrete leaves (UnitExt/InfantryExt/AircraftExt/BuildingExt) tracked by their + // own containers. The polymorphic fetch reads the inline slot directly. static TechnoExt* Fetch(const TechnoClass* pThis) { - return ExtMap.Find(pThis); + return *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset); } static TechnoExt* TryFetch(const TechnoClass* pThis) { - return ExtMap.TryFind(pThis); + return pThis ? Fetch(pThis) : nullptr; + } + + // deletes the leaf extension of a dying object; the leaf destructor + // unregisters itself from its own container + static void Destroy(TechnoClass* pThis) + { + if (auto const pExt = Fetch(pThis)) + { + delete pExt; + *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset) = 0; + } } static UnitClass* Deployer; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index d87cd6ac9c..50b4ed2672 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -14,7 +14,6 @@ #include -TechnoTypeExt::ExtContainer TechnoTypeExt::ExtMap; bool TechnoTypeExt::SelectWeaponMutex = false; void TechnoTypeExt::Initialize() @@ -2000,30 +1999,6 @@ void TechnoTypeExt::SaveToStream(PhobosStreamWriter& Stm) this->Serialize(Stm); } -// ============================= -// container - -TechnoTypeExt::ExtContainer::ExtContainer() : Container("TechnoTypeClass") { } -TechnoTypeExt::ExtContainer::~ExtContainer() = default; - -TechnoTypeExt* TechnoTypeExt::ExtContainer::CreateExtData(AbstractType tag, TechnoTypeClass* pOwner) const -{ - switch (tag) - { - case AbstractType::UnitType: - return new UnitTypeExt(static_cast(pOwner)); - case AbstractType::InfantryType: - return new InfantryTypeExt(static_cast(pOwner)); - case AbstractType::AircraftType: - return new AircraftTypeExt(static_cast(pOwner)); - case AbstractType::BuildingType: - return new BuildingTypeExt(static_cast(pOwner)); - default: - Debug::FatalErrorAndExit("TechnoTypeExt - unexpected extension tag %d in the save stream!\n", static_cast(tag)); - return nullptr; - } -} - // ============================= // container hooks @@ -2042,7 +2017,7 @@ DEFINE_HOOK(0x711AE0, TechnoTypeClass_DTOR, 0x5) { GET(TechnoTypeClass*, pItem, ECX); - TechnoTypeExt::ExtMap.Remove(pItem); + TechnoTypeExt::Destroy(pItem); return 0; } @@ -2053,7 +2028,8 @@ DEFINE_HOOK(0x716123, TechnoTypeClass_LoadFromINI, 0x5) GET(TechnoTypeClass*, pItem, EBP); GET_STACK(CCINIClass*, pINI, 0x380); - TechnoTypeExt::ExtMap.LoadFromINI(pItem, pINI); + if (auto const pExt = TechnoTypeExt::TryFetch(pItem)) + pExt->LoadFromINI(pINI); return 0; } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index e7147fe1c7..0947522425 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -23,7 +23,7 @@ class TechnoTypeExt : public ObjectTypeExt using ExtData = TechnoTypeExt; static constexpr DWORD Canary = 0x11111111; - static constexpr size_t ExtPointerOffset = 0xDF4; + static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor (the base chain stores the owner as ObjectTypeClass*) @@ -1048,26 +1048,28 @@ class TechnoTypeExt : public ObjectTypeExt void ParseVoiceWeaponAttacks(INI_EX& exINI, const char* pSection, ValueableVector& n, ValueableVector& nE); public: - class ExtContainer final : public Container - { - public: - ExtContainer(); - ~ExtContainer(); - - protected: - virtual ExtData* CreateExtData(AbstractType tag, TechnoTypeClass* pOwner) const override; - }; - - static ExtContainer ExtMap; - + // TechnoTypeExt is never instantiated and has no container of its own: instances are + // concrete leaves (UnitTypeExt/InfantryTypeExt/AircraftTypeExt/BuildingTypeExt) + // tracked by their own containers. The polymorphic fetch reads the inline slot directly. static TechnoTypeExt* Fetch(const TechnoTypeClass* pThis) { - return ExtMap.Find(pThis); + return *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset); } static TechnoTypeExt* TryFetch(const TechnoTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return pThis ? Fetch(pThis) : nullptr; + } + + // deletes the leaf extension of a dying type object; the leaf destructor + // unregisters itself from its own container + static void Destroy(TechnoTypeClass* pThis) + { + if (auto const pExt = Fetch(pThis)) + { + delete pExt; + *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset) = 0; + } } static bool SelectWeaponMutex; diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 3879d597e7..3f5d91225e 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -1,11 +1,16 @@ #include "Body.h" +UnitExt::ExtContainer UnitExt::ExtMap; + +UnitExt::ExtContainer::ExtContainer() : Container("UnitClass") { } +UnitExt::ExtContainer::~ExtContainer() = default; + // A unit's extension is a concrete UnitExt leaf, owned by the TechnoClass container. DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) { GET(UnitClass*, pItem, ESI); - TechnoExt::ExtMap.Adopt(new UnitExt(pItem)); + UnitExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h index d0688c4f0f..97614e4c19 100644 --- a/src/Ext/Unit/Body.h +++ b/src/Ext/Unit/Body.h @@ -6,9 +6,14 @@ // Concrete leaf extension for UnitClass. Empty for now: all techno-level data lives // in TechnoExt; this leaf only exists so a unit's extension has its own // concrete type (TechnoExt itself is never instantiated). -class UnitExt : public FootExt +class UnitExt final : public FootExt { public: + using base_type = UnitClass; + using ExtData = UnitExt; + + static constexpr DWORD Canary = 0xE1E2E3E4; + explicit UnitExt(UnitClass* const OwnerObject) : FootExt(OwnerObject) { } @@ -16,4 +21,28 @@ class UnitExt : public FootExt { return static_cast(this->GetAttachedObject()); } + + virtual ~UnitExt() override + { + ExtMap.Unregister(this); + } + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static UnitExt* Fetch(const UnitClass* pThis) + { + return ExtMap.Find(pThis); + } + + static UnitExt* TryFetch(const UnitClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index dcec40ba6a..d905282af1 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -1,10 +1,15 @@ #include "Body.h" +UnitTypeExt::ExtContainer UnitTypeExt::ExtMap; + +UnitTypeExt::ExtContainer::ExtContainer() : Container("UnitTypeClass") { } +UnitTypeExt::ExtContainer::~ExtContainer() = default; + DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) { GET(UnitTypeClass*, pItem, ESI); - TechnoTypeExt::ExtMap.Adopt(new UnitTypeExt(pItem)); + UnitTypeExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h index e8d8733851..42da9bbf94 100644 --- a/src/Ext/UnitType/Body.h +++ b/src/Ext/UnitType/Body.h @@ -4,9 +4,14 @@ #include // Concrete leaf extension for UnitTypeClass (empty; techno-type data lives in TechnoTypeExt). -class UnitTypeExt : public TechnoTypeExt +class UnitTypeExt final : public TechnoTypeExt { public: + using base_type = UnitTypeClass; + using ExtData = UnitTypeExt; + + static constexpr DWORD Canary = 0xE5E6E7E8; + explicit UnitTypeExt(UnitTypeClass* const OwnerObject) : TechnoTypeExt(OwnerObject) { } @@ -14,4 +19,28 @@ class UnitTypeExt : public TechnoTypeExt { return static_cast(this->GetAttachedObject()); } + + virtual ~UnitTypeExt() override + { + ExtMap.Unregister(this); + } + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; + + static UnitTypeExt* Fetch(const UnitTypeClass* pThis) + { + return ExtMap.Find(pThis); + } + + static UnitTypeExt* TryFetch(const UnitTypeClass* pThis) + { + return ExtMap.TryFind(pThis); + } }; diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index aa5af50558..27abf7c92f 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -3,8 +3,13 @@ #include #include +#include #include #include +#include +#include +#include +#include #include #include #include @@ -254,9 +259,11 @@ struct TypeRegistry #pragma endregion // Add more class names as you like +// NOTE: the order of extension classes with containers is the savegame stream order - append-only! using PhobosTypeRegistry = TypeRegistry < // Ext classes AircraftExt, + AircraftTypeExt, AnimTypeExt, AnimExt, BuildingExt, @@ -267,6 +274,8 @@ using PhobosTypeRegistry = TypeRegistry < EBoltExt, HouseExt, HouseTypeExt, + InfantryExt, + InfantryTypeExt, OverlayTypeExt, ParticleSystemTypeExt, RadSiteExt, @@ -282,6 +291,8 @@ using PhobosTypeRegistry = TypeRegistry < TechnoTypeExt, TerrainTypeExt, TiberiumExt, + UnitExt, + UnitTypeExt, VoxelAnimExt, VoxelAnimTypeExt, WarheadTypeExt, diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 6fde2bfa25..035e156101 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -432,27 +432,18 @@ class Container return val; } - // Registers an already-constructed extension (possibly of a derived leaf type) - // into this container: stores the inline pointer and tracks it for iteration. - extension_type_ptr Adopt(extension_type_ptr val) + // Removes a dying extension from the tracking list without touching the owner or + // deleting it; leaf extension destructors call this on their own container. + void Unregister(extension_type_ptr val) { - // during savegame load extensions are restored from the stream instead - if (Phobos::IsLoadingSaveGame) + auto& vec = this->Items; + auto it = std::find(vec.begin(), vec.end(), val); + + if (it != vec.end()) { - delete val; - return nullptr; + *it = vec.back(); + vec.pop_back(); } - - val->EnsureConstanted(); - - if constexpr (HasOffset) - SetExtensionPointer(val->OwnerObject(), val); - else - this->MappedItems.insert(val->OwnerObject(), val); - - Items.emplace_back(val); - - return val; } extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) @@ -576,8 +567,6 @@ class Container // the extension's own save-time address, so pointers to it can be remapped writer.RegisterChange(item); - // which concrete leaf to construct on load - writer.Save(item->OwnerObject()->WhatAmI()); // the save-time owner address, remapped to the loaded owner by the swizzle manager writer.Save(static_cast(item->OwnerObject())); @@ -636,16 +625,15 @@ class Container PhobosStreamReader reader(loader); void* oldPtr = nullptr; - AbstractType tag = AbstractType::None; void* oldOwner = nullptr; - if (!reader.Load(oldPtr) || !reader.Load(tag) || !reader.Load(oldOwner)) + if (!reader.Load(oldPtr) || !reader.Load(oldOwner)) { Debug::Log("LoadAllFromStream - Invalid item header in '%s'.\n", this->Name); return false; } - auto const buffer = this->CreateExtData(tag, static_cast(oldOwner)); + auto const buffer = new extension_type(static_cast(oldOwner)); buffer->RegisterOwnerForChange(); PhobosSwizzle::RegisterChange(oldPtr, buffer); this->Items.emplace_back(buffer); @@ -689,14 +677,6 @@ class Container return this->Items.size(); } -protected: - // constructs the concrete extension on load; containers whose base class has - // multiple concrete leaves override this to pick the leaf matching the tag - virtual extension_type_ptr CreateExtData(AbstractType tag, base_type_ptr pOwner) const - { - return new extension_type(pOwner); - } - private: Container(const Container&) = delete; Container& operator = (const Container&) = delete; From d5fd64e8314dfe969ac35833513703a94f1ec337 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:29:17 +0300 Subject: [PATCH 18/47] Destroy extensions from concrete leaf destructor hooks --- src/Ext/Aircraft/Body.cpp | 9 +++++++++ src/Ext/Aircraft/Body.h | 5 ----- src/Ext/AircraftType/Body.cpp | 9 +++++++++ src/Ext/AircraftType/Body.h | 5 ----- src/Ext/Building/Body.cpp | 9 +++++++++ src/Ext/Building/Body.h | 5 +---- src/Ext/BuildingType/Body.cpp | 9 +++++++++ src/Ext/BuildingType/Body.h | 5 +---- src/Ext/Infantry/Body.cpp | 9 +++++++++ src/Ext/Infantry/Body.h | 5 ----- src/Ext/InfantryType/Body.cpp | 9 +++++++++ src/Ext/InfantryType/Body.h | 5 ----- src/Ext/Techno/Body.cpp | 4 ++-- src/Ext/Techno/Body.h | 11 ----------- src/Ext/TechnoType/Body.cpp | 8 -------- src/Ext/TechnoType/Body.h | 11 ----------- src/Ext/Unit/Body.cpp | 9 +++++++++ src/Ext/Unit/Body.h | 5 ----- src/Ext/UnitType/Body.cpp | 9 +++++++++ src/Ext/UnitType/Body.h | 5 ----- src/Utilities/Container.h | 13 ------------- 21 files changed, 76 insertions(+), 83 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index 2602c011dd..a298bea531 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -181,3 +181,12 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) return static_cast(std::clamp(landingDir, 0, 255)); } + +DEFINE_HOOK(0x414080, AircraftClass_DTOR, 0x5) +{ + GET(AircraftClass*, pItem, ECX); + + AircraftExt::ExtMap.Remove(pItem); + + return 0; +} diff --git a/src/Ext/Aircraft/Body.h b/src/Ext/Aircraft/Body.h index 6a65ee5d98..7ffc3c8292 100644 --- a/src/Ext/Aircraft/Body.h +++ b/src/Ext/Aircraft/Body.h @@ -25,11 +25,6 @@ class AircraftExt final : public FootExt static CellStruct PickEdgeCellForPlane(AircraftTypeClass* pPlaneType, CellStruct destCell, Edge edge, bool isOnRetreat = false); static DirType GetLandingDir(AircraftClass* pThis, BuildingClass* pDock = nullptr); - virtual ~AircraftExt() override - { - ExtMap.Unregister(this); - } - class ExtContainer final : public Container { public: diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index cee0558ae1..30772361e9 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -13,3 +13,12 @@ DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) return 0; } + +DEFINE_HOOK(0x41CA20, AircraftTypeClass_DTOR, 0x6) +{ + GET(AircraftTypeClass*, pItem, ECX); + + AircraftTypeExt::ExtMap.Remove(pItem); + + return 0; +} diff --git a/src/Ext/AircraftType/Body.h b/src/Ext/AircraftType/Body.h index 4fa30dc519..ab04ff13ec 100644 --- a/src/Ext/AircraftType/Body.h +++ b/src/Ext/AircraftType/Body.h @@ -20,11 +20,6 @@ class AircraftTypeExt final : public TechnoTypeExt return static_cast(this->GetAttachedObject()); } - virtual ~AircraftTypeExt() override - { - ExtMap.Unregister(this); - } - class ExtContainer final : public Container { public: diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 8026f40cfc..6d05a51378 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -637,3 +637,12 @@ static void __fastcall BuildingClass_InfiltratedBy_Wrapper(BuildingClass* pThis, } DEFINE_FUNCTION_JUMP(CALL, 0x51A00B, BuildingClass_InfiltratedBy_Wrapper); + +DEFINE_HOOK(0x43C022, BuildingClass_DTOR, 0x6) +{ + GET(BuildingClass*, pItem, ESI); + + BuildingExt::ExtMap.Remove(pItem); + + return 0; +} diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index e468488381..182c642fe3 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -55,10 +55,7 @@ class BuildingExt final : public TechnoExt, public Detach::Listener(this->GetAttachedObject()); } - virtual ~InfantryExt() override - { - ExtMap.Unregister(this); - } - class ExtContainer final : public Container { public: diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index 21ab6a4091..254165de7f 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -13,3 +13,12 @@ DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) return 0; } + +DEFINE_HOOK(0x5239D0, InfantryTypeClass_DTOR, 0x5) +{ + GET(InfantryTypeClass*, pItem, ECX); + + InfantryTypeExt::ExtMap.Remove(pItem); + + return 0; +} diff --git a/src/Ext/InfantryType/Body.h b/src/Ext/InfantryType/Body.h index 2b0a45a84e..f1fdc78e7e 100644 --- a/src/Ext/InfantryType/Body.h +++ b/src/Ext/InfantryType/Body.h @@ -20,11 +20,6 @@ class InfantryTypeExt final : public TechnoTypeExt return static_cast(this->GetAttachedObject()); } - virtual ~InfantryTypeExt() override - { - ExtMap.Unregister(this); - } - class ExtContainer final : public Container { public: diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index ee017d1807..90d1af8680 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1384,6 +1384,8 @@ bool TechnoExt::SaveGlobals(PhobosStreamWriter& Stm) // The extension is allocated by the concrete leaf constructors (UnitClass/InfantryClass/ // BuildingClass/AircraftClass), not here at the abstract TechnoClass level. +// The extension is removed by the concrete leaf destructor hooks; this only +// keeps the tech tree recheck side effect at the shared base destructor. DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5) { GET(TechnoClass*, pItem, ECX); @@ -1391,8 +1393,6 @@ DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5) if (pItem->AbstractFlags & AbstractFlags::Foot) pItem->Owner->RecheckTechTree = true; // for SW.AuxTechons and SW.NegTechnos - TechnoExt::Destroy(pItem); - return 0; } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 2c624c6131..0cb3fcdcb3 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -255,17 +255,6 @@ class TechnoExt : public RadioExt, public Detach::Listener return pThis ? Fetch(pThis) : nullptr; } - // deletes the leaf extension of a dying object; the leaf destructor - // unregisters itself from its own container - static void Destroy(TechnoClass* pThis) - { - if (auto const pExt = Fetch(pThis)) - { - delete pExt; - *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset) = 0; - } - } - static UnitClass* Deployer; static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 50b4ed2672..528be191a1 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -2013,14 +2013,6 @@ DEFINE_HOOK(0x711835, TechnoTypeClass_CTOR, 0x5) return 0; } -DEFINE_HOOK(0x711AE0, TechnoTypeClass_DTOR, 0x5) -{ - GET(TechnoTypeClass*, pItem, ECX); - - TechnoTypeExt::Destroy(pItem); - - return 0; -} //DEFINE_HOOK_AGAIN(0x716132, TechnoTypeClass_LoadFromINI, 0x5)// Section dont exist! DEFINE_HOOK(0x716123, TechnoTypeClass_LoadFromINI, 0x5) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 0947522425..22f3b0410a 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1060,17 +1060,6 @@ class TechnoTypeExt : public ObjectTypeExt { return pThis ? Fetch(pThis) : nullptr; } - - // deletes the leaf extension of a dying type object; the leaf destructor - // unregisters itself from its own container - static void Destroy(TechnoTypeClass* pThis) - { - if (auto const pExt = Fetch(pThis)) - { - delete pExt; - *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset) = 0; - } - } static bool SelectWeaponMutex; static void ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor = 1.0); diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 3f5d91225e..5a6974cb05 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -14,3 +14,12 @@ DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) return 0; } + +DEFINE_HOOK(0x735780, UnitClass_DTOR, 0x6) +{ + GET(UnitClass*, pItem, ECX); + + UnitExt::ExtMap.Remove(pItem); + + return 0; +} diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h index 97614e4c19..06399be23a 100644 --- a/src/Ext/Unit/Body.h +++ b/src/Ext/Unit/Body.h @@ -22,11 +22,6 @@ class UnitExt final : public FootExt return static_cast(this->GetAttachedObject()); } - virtual ~UnitExt() override - { - ExtMap.Unregister(this); - } - class ExtContainer final : public Container { public: diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index d905282af1..0772d86465 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -13,3 +13,12 @@ DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) return 0; } + +DEFINE_HOOK(0x7472F0, UnitTypeClass_DTOR, 0x6) +{ + GET(UnitTypeClass*, pItem, ECX); + + UnitTypeExt::ExtMap.Remove(pItem); + + return 0; +} diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h index 42da9bbf94..591d97e700 100644 --- a/src/Ext/UnitType/Body.h +++ b/src/Ext/UnitType/Body.h @@ -20,11 +20,6 @@ class UnitTypeExt final : public TechnoTypeExt return static_cast(this->GetAttachedObject()); } - virtual ~UnitTypeExt() override - { - ExtMap.Unregister(this); - } - class ExtContainer final : public Container { public: diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 035e156101..3de1b3bf4f 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -432,19 +432,6 @@ class Container return val; } - // Removes a dying extension from the tracking list without touching the owner or - // deleting it; leaf extension destructors call this on their own container. - void Unregister(extension_type_ptr val) - { - auto& vec = this->Items; - auto it = std::find(vec.begin(), vec.end(), val); - - if (it != vec.end()) - { - *it = vec.back(); - vec.pop_back(); - } - } extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) { From e5b82d53e09f176fc1362b60ecb6675c2708e338 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:37:57 +0300 Subject: [PATCH 19/47] Expose slot-read Fetch on every hierarchy level and unify the slot constant --- src/Ext/AbstractType/Body.h | 10 ++++++++++ src/Ext/Anim/Body.h | 1 - src/Ext/AnimType/Body.h | 1 - src/Ext/BuildingType/Body.h | 1 - src/Ext/Bullet/Body.h | 1 - src/Ext/BulletType/Body.h | 1 - src/Ext/Cell/Body.h | 1 - src/Ext/Foot/Body.h | 10 ++++++++++ src/Ext/House/Body.h | 1 - src/Ext/HouseType/Body.h | 1 - src/Ext/Mission/Body.h | 10 ++++++++++ src/Ext/Object/Body.h | 10 ++++++++++ src/Ext/ObjectType/Body.h | 10 ++++++++++ src/Ext/OverlayType/Body.h | 1 - src/Ext/ParticleSystemType/Body.h | 1 - src/Ext/ParticleType/Body.h | 1 - src/Ext/RadSite/Body.h | 1 - src/Ext/Radio/Body.h | 10 ++++++++++ src/Ext/SWType/Body.h | 1 - src/Ext/Script/Body.h | 1 - src/Ext/Side/Body.h | 1 - src/Ext/TAction/Body.h | 1 - src/Ext/TEvent/Body.h | 1 - src/Ext/Team/Body.h | 1 - src/Ext/TeamType/Body.h | 1 - src/Ext/Techno/Body.h | 3 +-- src/Ext/TechnoType/Body.h | 3 +-- src/Ext/TerrainType/Body.h | 1 - src/Ext/Tiberium/Body.h | 1 - src/Ext/VoxelAnim/Body.h | 1 - src/Ext/VoxelAnimType/Body.h | 1 - src/Ext/WarheadType/Body.h | 1 - src/Ext/WeaponType/Body.h | 1 - src/Utilities/Container.h | 14 ++++++++++++++ 34 files changed, 76 insertions(+), 29 deletions(-) diff --git a/src/Ext/AbstractType/Body.h b/src/Ext/AbstractType/Body.h index bef9f1a589..10f3556cff 100644 --- a/src/Ext/AbstractType/Body.h +++ b/src/Ext/AbstractType/Body.h @@ -14,4 +14,14 @@ class AbstractTypeExt : public AbstractExt { return static_cast(this->GetAttachedObject()); } + + static AbstractTypeExt* Fetch(const AbstractTypeClass* pThis) + { + return static_cast(AbstractExt::Fetch(pThis)); + } + + static AbstractTypeExt* TryFetch(const AbstractTypeClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } }; diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 8d3d399086..88f863391f 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -11,7 +11,6 @@ class AnimExt final : public ObjectExt using ExtData = AnimExt; static constexpr DWORD Canary = 0xAAAAAAAA; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index abb755a304..f2e66ee9e9 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -22,7 +22,6 @@ class AnimTypeExt final : public ObjectTypeExt using ExtData = AnimTypeExt; static constexpr DWORD Canary = 0xEEEEEEEE; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 0df61c9bbd..39cb064b34 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -10,7 +10,6 @@ class BuildingTypeExt final : public TechnoTypeExt using ExtData = BuildingTypeExt; static constexpr DWORD Canary = 0x11111111; - static constexpr size_t ExtPointerOffset = 0x18; public: Valueable PowersUp_Owner; diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index 26a0b77eb0..7c342eda66 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -20,7 +20,6 @@ class BulletExt final : public ObjectExt using ExtData = BulletExt; static constexpr DWORD Canary = 0x2A2A2A2A; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index fdadda4c5c..7392842ad0 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -16,7 +16,6 @@ class BulletTypeExt final : public ObjectTypeExt using ExtData = BulletTypeExt; static constexpr DWORD Canary = 0xF00DF00D; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 4b77fb5b50..da1a4ad7a5 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -11,7 +11,6 @@ class CellExt final : public AbstractExt using ExtData = CellExt; static constexpr DWORD Canary = 0x13371337; - static constexpr size_t ExtPointerOffset = 0x18; struct RadLevel { diff --git a/src/Ext/Foot/Body.h b/src/Ext/Foot/Body.h index 6187d94437..dcd77796d4 100644 --- a/src/Ext/Foot/Body.h +++ b/src/Ext/Foot/Body.h @@ -15,4 +15,14 @@ class FootExt : public TechnoExt { return static_cast(this->GetAttachedObject()); } + + static FootExt* Fetch(const FootClass* pThis) + { + return static_cast(AbstractExt::Fetch(pThis)); + } + + static FootExt* TryFetch(const FootClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } }; diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index 2367e48c25..d536538674 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -14,7 +14,6 @@ class HouseExt final : public AbstractExt, public Detach::Listener(this->GetAttachedObject()); } + + static MissionExt* Fetch(const MissionClass* pThis) + { + return static_cast(AbstractExt::Fetch(pThis)); + } + + static MissionExt* TryFetch(const MissionClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } }; diff --git a/src/Ext/Object/Body.h b/src/Ext/Object/Body.h index 7d20c01717..2c51444162 100644 --- a/src/Ext/Object/Body.h +++ b/src/Ext/Object/Body.h @@ -16,4 +16,14 @@ class ObjectExt : public AbstractExt { return static_cast(this->GetAttachedObject()); } + + static ObjectExt* Fetch(const ObjectClass* pThis) + { + return static_cast(AbstractExt::Fetch(pThis)); + } + + static ObjectExt* TryFetch(const ObjectClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } }; diff --git a/src/Ext/ObjectType/Body.h b/src/Ext/ObjectType/Body.h index 83e11816ef..a056432cc7 100644 --- a/src/Ext/ObjectType/Body.h +++ b/src/Ext/ObjectType/Body.h @@ -14,4 +14,14 @@ class ObjectTypeExt : public AbstractTypeExt { return static_cast(this->GetAttachedObject()); } + + static ObjectTypeExt* Fetch(const ObjectTypeClass* pThis) + { + return static_cast(AbstractExt::Fetch(pThis)); + } + + static ObjectTypeExt* TryFetch(const ObjectTypeClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } }; diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index 020da8c79f..457b257c88 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -12,7 +12,6 @@ class OverlayTypeExt final : public ObjectTypeExt using ExtData = OverlayTypeExt; static constexpr DWORD Canary = 0xADF48498; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/ParticleSystemType/Body.h b/src/Ext/ParticleSystemType/Body.h index c263d1036a..5c8a065e67 100644 --- a/src/Ext/ParticleSystemType/Body.h +++ b/src/Ext/ParticleSystemType/Body.h @@ -12,7 +12,6 @@ class ParticleSystemTypeExt final : public ObjectTypeExt using ExtData = ParticleSystemTypeExt; static constexpr DWORD Canary = 0xF9984EFE; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h index 81a86a96c0..567d3d7402 100644 --- a/src/Ext/ParticleType/Body.h +++ b/src/Ext/ParticleType/Body.h @@ -13,7 +13,6 @@ class ParticleTypeExt final : public ObjectTypeExt using ExtData = ParticleTypeExt; static constexpr DWORD Canary = 0xEAFEEAFE; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/RadSite/Body.h b/src/Ext/RadSite/Body.h index f94a95dc1d..789d85509c 100644 --- a/src/Ext/RadSite/Body.h +++ b/src/Ext/RadSite/Body.h @@ -17,7 +17,6 @@ class RadSiteExt final : public AbstractExt, public Detach::Listener(this->GetAttachedObject()); } + + static RadioExt* Fetch(const RadioClass* pThis) + { + return static_cast(AbstractExt::Fetch(pThis)); + } + + static RadioExt* TryFetch(const RadioClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } }; diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 0d46c69841..166fb993cd 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -12,7 +12,6 @@ class SWTypeExt final : public AbstractTypeExt using ExtData = SWTypeExt; static constexpr DWORD Canary = 0x11111111; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 45cd73002b..0694e60fb9 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -153,7 +153,6 @@ class ScriptExt final : public AbstractExt using ExtData = ScriptExt; static constexpr DWORD Canary = 0x3B3B3B3B; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index 2b49b9c046..a0c5640b24 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -12,7 +12,6 @@ class SideExt final : public AbstractTypeExt using ExtData = SideExt; static constexpr DWORD Canary = 0x05B10501; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index ff2f0160e6..d5e6479b68 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -38,7 +38,6 @@ class TActionExt final : public AbstractExt using ExtData = TActionExt; static constexpr DWORD Canary = 0x91919191; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index c19a48cefd..080be1a298 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -63,7 +63,6 @@ class TEventExt final : public AbstractExt using ExtData = TEventExt; static constexpr DWORD Canary = 0x91919191; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/Team/Body.h b/src/Ext/Team/Body.h index cab4ae1cd2..66e91fdb7d 100644 --- a/src/Ext/Team/Body.h +++ b/src/Ext/Team/Body.h @@ -12,7 +12,6 @@ class TeamExt final : public AbstractExt, public Detach::Listener using ExtData = TeamExt; static constexpr DWORD Canary = 0x414B4B41; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 2962bd38e0..5770745307 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -12,7 +12,6 @@ class TeamTypeExt final : public AbstractTypeExt using ExtData = TeamTypeExt; static constexpr DWORD Canary = 0xABCDEF01; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 0cb3fcdcb3..71603a44df 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -19,7 +19,6 @@ class TechnoExt : public RadioExt, public Detach::Listener using ExtData = TechnoExt; static constexpr DWORD Canary = 0x55555555; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor (the base chain stores the owner as RadioClass*) @@ -247,7 +246,7 @@ class TechnoExt : public RadioExt, public Detach::Listener // own containers. The polymorphic fetch reads the inline slot directly. static TechnoExt* Fetch(const TechnoClass* pThis) { - return *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset); + return static_cast(AbstractExt::Fetch(pThis)); } static TechnoExt* TryFetch(const TechnoClass* pThis) diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 22f3b0410a..37c1c907bb 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -23,7 +23,6 @@ class TechnoTypeExt : public ObjectTypeExt using ExtData = TechnoTypeExt; static constexpr DWORD Canary = 0x11111111; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor (the base chain stores the owner as ObjectTypeClass*) @@ -1053,7 +1052,7 @@ class TechnoTypeExt : public ObjectTypeExt // tracked by their own containers. The polymorphic fetch reads the inline slot directly. static TechnoTypeExt* Fetch(const TechnoTypeClass* pThis) { - return *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset); + return static_cast(AbstractExt::Fetch(pThis)); } static TechnoTypeExt* TryFetch(const TechnoTypeClass* pThis) diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index a81f58488d..3433392902 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -12,7 +12,6 @@ class TerrainTypeExt final : public ObjectTypeExt using ExtData = TerrainTypeExt; static constexpr DWORD Canary = 0xBEE78007; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index 8d36910a7c..e8f117710f 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -12,7 +12,6 @@ class TiberiumExt final : public AbstractTypeExt using ExtData = TiberiumExt; static constexpr DWORD Canary = 0xAABBCCDD; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index d9bcca7c65..bd2f3252cc 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -13,7 +13,6 @@ class VoxelAnimExt final : public ObjectExt using ExtData = VoxelAnimExt; static constexpr DWORD Canary = 0xAAAAAACC; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 4477ee3c2a..429cb3c8f8 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -14,7 +14,6 @@ class VoxelAnimTypeExt final : public ObjectTypeExt using ExtData = VoxelAnimTypeExt; static constexpr DWORD Canary = 0xAAAEEEEE; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 47bb7864b3..a25b653187 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -14,7 +14,6 @@ class WarheadTypeExt final : public AbstractTypeExt using ExtData = WarheadTypeExt; static constexpr DWORD Canary = 0x22222222; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 64e815a7e8..da387dd1cd 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -13,7 +13,6 @@ class WeaponTypeExt final : public AbstractTypeExt using ExtData = WeaponTypeExt; static constexpr DWORD Canary = 0x22222222; - static constexpr size_t ExtPointerOffset = 0x18; public: // typed owner accessor diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 3de1b3bf4f..b54e416658 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -60,6 +60,20 @@ class AbstractExt InitState Initialized; public: + // every extension pointer lives in the unused AbstractClass::unknown_18 field + static constexpr size_t ExtPointerOffset = 0x18; + + // reads the inline extension slot of any AbstractClass-derived object; returns + // null for objects of unextended types (the slot is zeroed by the game's ctor) + static AbstractExt* Fetch(const AbstractClass* pThis) + { + return *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset); + } + + static AbstractExt* TryFetch(const AbstractClass* pThis) + { + return pThis ? Fetch(pThis) : nullptr; + } explicit AbstractExt(AbstractClass* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } { } From b26649546fd7171f4c9ac6a0a7240be6dfc6689d Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:37:57 +0300 Subject: [PATCH 20/47] Hook the twin type destructor bodies the vtables point at --- src/Ext/AircraftType/Body.cpp | 2 ++ src/Ext/InfantryType/Body.cpp | 2 ++ src/Ext/UnitType/Body.cpp | 2 ++ 3 files changed, 6 insertions(+) diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index 30772361e9..ed9af1b0d4 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -14,6 +14,8 @@ DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) return 0; } +// the second address is the twin destructor body the vtable actually points at +DEFINE_HOOK_AGAIN(0x41CFE0, AircraftTypeClass_DTOR, 0x6) DEFINE_HOOK(0x41CA20, AircraftTypeClass_DTOR, 0x6) { GET(AircraftTypeClass*, pItem, ECX); diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index 254165de7f..2eaa7dd845 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -14,6 +14,8 @@ DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) return 0; } +// the second address is the twin destructor body the vtable actually points at +DEFINE_HOOK_AGAIN(0x524D70, InfantryTypeClass_DTOR, 0x6) DEFINE_HOOK(0x5239D0, InfantryTypeClass_DTOR, 0x5) { GET(InfantryTypeClass*, pItem, ECX); diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index 0772d86465..f2a017791f 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -14,6 +14,8 @@ DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) return 0; } +// the second address is the twin destructor body the vtable actually points at +DEFINE_HOOK_AGAIN(0x748190, UnitTypeClass_DTOR, 0x6) DEFINE_HOOK(0x7472F0, UnitTypeClass_DTOR, 0x6) { GET(UnitTypeClass*, pItem, ECX); From bf483a4c2d27cbafe410f01024fdde8f79dfd60c Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:42:52 +0300 Subject: [PATCH 21/47] Enable /MP --- Phobos.vcxproj | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index a6d155fb7c..cea5e31f02 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -3,6 +3,21 @@ true + + + true + + + + + true + + + + + true + + Phobos Phobos @@ -442,4 +457,4 @@ - + \ No newline at end of file From 3d7e46eeb606d8cc93eb483c69bf04406a05e65f Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 13:59:17 +0300 Subject: [PATCH 22/47] Allocate the building extension before the in-constructor Init call --- src/Ext/Building/Body.cpp | 10 +++++----- src/Ext/Techno/Hooks.cpp | 6 +++++- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 6d05a51378..a89e50e70b 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -603,14 +603,14 @@ bool BuildingExt::SaveGlobals(PhobosStreamWriter& Stm) // ============================= // container hooks -DEFINE_HOOK(0x43BCBD, BuildingClass_CTOR, 0x6) +// Right after the TechnoClass base constructor returns: the extension must exist +// before the Init call further down this constructor (0x43BB29), which fills in +// TypeExtData and the rest of the per-object state. +DEFINE_HOOK(0x43B750, BuildingClass_CTOR, 0x6) { GET(BuildingClass*, pItem, ESI); - auto const pExt = BuildingExt::ExtMap.Allocate(pItem); - - if (pExt) - pExt->TypeExtData = BuildingTypeExt::Fetch(pItem->Type); + BuildingExt::ExtMap.Allocate(pItem); return 0; } diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 493b1d6ebe..aaebbf32d9 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -266,7 +266,11 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) if (!pType) // Critical sanity check in s/l return 0; - auto const pExt = TechnoExt::Fetch(pThis); + auto const pExt = TechnoExt::TryFetch(pThis); + + if (!pExt) // constructed during savegame load; state comes from the stream instead + return 0; + auto const pTypeExt = TechnoTypeExt::Fetch(pType); pExt->TypeExtData = pTypeExt; From 5df72cff70c7aba2fb7c48cba891969325537cd5 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 14:12:35 +0300 Subject: [PATCH 23/47] Replace the building TypeExtData shadow member with a typed accessor --- src/Ext/Building/Body.cpp | 13 ++++++------- src/Ext/Building/Body.h | 9 ++++++--- src/Ext/Building/Hooks.cpp | 2 +- src/Ext/BuildingType/Hooks.Upgrade.cpp | 2 +- src/Ext/SWType/FireSuperWeapon.cpp | 6 +++--- 5 files changed, 17 insertions(+), 15 deletions(-) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index a89e50e70b..e89249d6e6 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -14,11 +14,11 @@ void BuildingExt::DisplayIncomeString() { if (this->AccumulatedIncome) { - int const delay = this->TypeExtData->DisplayIncome_Delay.Get(RulesExt::Global()->DisplayIncome_Delay.Get()); + int const delay = this->GetTypeExtData()->DisplayIncome_Delay.Get(RulesExt::Global()->DisplayIncome_Delay.Get()); if (Unsorted::CurrentFrame % delay == 0) { auto const pThis = this->OwnerObject(); - auto const pTypeExt = this->TypeExtData; + auto const pTypeExt = this->GetTypeExtData(); if ((RulesExt::Global()->DisplayIncome_AllowAI || pThis->Owner->IsControlledByHuman()) && pTypeExt->DisplayIncome.Get(RulesExt::Global()->DisplayIncome)) @@ -267,7 +267,7 @@ bool BuildingExt::DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechn { if (auto const pExt = BuildingExt::TryFetch(pBuilding)) { - auto const pTypeExt = pExt->TypeExtData; + auto const pTypeExt = pExt->GetTypeExtData(); pExt->AccumulatedIncome += refund; pExt->GrindingWeapon_AccumulatedCredits += refund; @@ -295,7 +295,7 @@ bool BuildingExt::DoGrindingExtras(BuildingClass* pBuilding, TechnoClass* pTechn void BuildingExt::ApplyPoweredKillSpawns() { auto const pThis = this->OwnerObject(); - auto const pTypeExt = this->TypeExtData; + auto const pTypeExt = this->GetTypeExtData(); if (pTypeExt->Powered_KillSpawns && !pThis->IsPowerOnline()) { @@ -319,7 +319,7 @@ bool BuildingExt::HandleInfiltrate(HouseClass* pInfiltratorHouse, int moneybefor { const auto pThis = this->OwnerObject(); const auto pVictimHouse = pThis->Owner; - const auto pTypeExt = this->TypeExtData; + const auto pTypeExt = this->GetTypeExtData(); this->AccumulatedIncome += pVictimHouse->Available_Money() - moneybefore; if (!pVictimHouse->IsControlledByHuman() && !RulesExt::Global()->DisplayIncome_AllowAI) @@ -468,7 +468,7 @@ void BuildingExt::KickOutClone(std::pair& info, v int BuildingExt::GetTurretFrame(BuildingClass* pThis) { auto const pExt = BuildingExt::Fetch(pThis); - auto const pTypeExt = pExt->TypeExtData; + auto const pTypeExt = pExt->GetTypeExtData(); const int facing = pThis->PrimaryFacing.Current().GetValue<5>(); const int shapeFacing = ObjectClass::BodyShape[facing]; @@ -555,7 +555,6 @@ template void BuildingExt::Serialize(T& Stm) { Stm - .Process(this->TypeExtData) .Process(this->DeployedTechno) .Process(this->IsCreatedFromMapFile) .Process(this->LimboID) diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 182c642fe3..5d2a8ff60d 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -6,11 +6,9 @@ class BuildingExt final : public TechnoExt, public Detach::Listener(this->TechnoExt::OwnerObject()); } + // a building's type extension is always the BuildingTypeExt leaf + BuildingTypeExt* GetTypeExtData() const + { + return static_cast(this->TypeExtData); + } + void DisplayIncomeString(); void ApplyPoweredKillSpawns(); bool HasSuperWeapon(int index) const; diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 84fd2fcc2a..95fe36eda4 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -1156,7 +1156,7 @@ DEFINE_HOOK(0x44B6C7, BuildingClass_Mission_Attack_TurretAnim, 0x6) if (auto const pAnim = pThis->Anims[(int)BuildingAnimSlot::Turret]) { auto const pExt = BuildingExt::Fetch(pThis); - auto const pTypeExt = pExt->TypeExtData; + auto const pTypeExt = pExt->GetTypeExtData(); const bool isLowPower = !pThis->StuffEnabled || !pThis->IsPowerOnline(); const int firingFrames = isLowPower ? pTypeExt->TurretAnim_LowPowerFiringFrames : pTypeExt->TurretAnim_FiringFrames; diff --git a/src/Ext/BuildingType/Hooks.Upgrade.cpp b/src/Ext/BuildingType/Hooks.Upgrade.cpp index e29728f668..5a75ad704b 100644 --- a/src/Ext/BuildingType/Hooks.Upgrade.cpp +++ b/src/Ext/BuildingType/Hooks.Upgrade.cpp @@ -214,7 +214,7 @@ DEFINE_HOOK(0x440988, BuildingClass_Unlimbo_UpgradeAnims, 0x7) auto const animData = &pTarget->Type->BuildingAnim[animIndex]; // Only copy image name to BuildingType anim struct if theres no explicit PowersUpAnim for this level. - if (!pTargetExt->TypeExtData->HasPowerUpAnim[animIndex]) + if (!pTargetExt->GetTypeExtData()->HasPowerUpAnim[animIndex]) strncpy(animData->Anim, pType->ImageFile, 16u); return SkipGameCode; diff --git a/src/Ext/SWType/FireSuperWeapon.cpp b/src/Ext/SWType/FireSuperWeapon.cpp index b6acdac4d1..eae199788a 100644 --- a/src/Ext/SWType/FireSuperWeapon.cpp +++ b/src/Ext/SWType/FireSuperWeapon.cpp @@ -126,13 +126,13 @@ static inline void LimboCreate(BuildingTypeClass* pType, HouseClass* pOwner, int auto const pBuildingExt = BuildingExt::Fetch(pBuilding); auto const pOwnerExt = HouseExt::Fetch(pOwner); - if (!pBuildingExt->TypeExtData->PowerPlantEnhancer_Buildings.empty() - && (pBuildingExt->TypeExtData->PowerPlantEnhancer_Amount != 0 || pBuildingExt->TypeExtData->PowerPlantEnhancer_Factor != 1.0f)) + if (!pBuildingExt->GetTypeExtData()->PowerPlantEnhancer_Buildings.empty() + && (pBuildingExt->GetTypeExtData()->PowerPlantEnhancer_Amount != 0 || pBuildingExt->GetTypeExtData()->PowerPlantEnhancer_Factor != 1.0f)) pOwnerExt->PowerPlantEnhancers.push_back(pBuilding); if (pType->FactoryPlant) { - if (pBuildingExt->TypeExtData->FactoryPlant_AllowTypes.size() > 0 || pBuildingExt->TypeExtData->FactoryPlant_DisallowTypes.size() > 0) + if (pBuildingExt->GetTypeExtData()->FactoryPlant_AllowTypes.size() > 0 || pBuildingExt->GetTypeExtData()->FactoryPlant_DisallowTypes.size() > 0) { pOwnerExt->RestrictedFactoryPlants.push_back(pBuilding); } From 4b9685b4344ade68a1f81591a6c6e297f62a1e80 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 14:12:35 +0300 Subject: [PATCH 24/47] Resolve extension data classes in the container instead of per-class aliases --- src/Ext/Aircraft/Body.h | 1 - src/Ext/AircraftType/Body.h | 1 - src/Ext/Anim/Body.h | 1 - src/Ext/AnimType/Body.h | 1 - src/Ext/BuildingType/Body.h | 1 - src/Ext/Bullet/Body.h | 1 - src/Ext/BulletType/Body.h | 1 - src/Ext/Cell/Body.h | 1 - src/Ext/House/Body.h | 1 - src/Ext/HouseType/Body.h | 1 - src/Ext/Infantry/Body.h | 1 - src/Ext/InfantryType/Body.h | 1 - src/Ext/OverlayType/Body.h | 1 - src/Ext/ParticleSystemType/Body.h | 1 - src/Ext/ParticleType/Body.h | 1 - src/Ext/RadSite/Body.h | 1 - src/Ext/SWType/Body.h | 1 - src/Ext/Script/Body.h | 1 - src/Ext/Side/Body.h | 1 - src/Ext/TAction/Body.h | 1 - src/Ext/TEvent/Body.h | 1 - src/Ext/Team/Body.h | 1 - src/Ext/TeamType/Body.h | 1 - src/Ext/Techno/Body.h | 1 - src/Ext/TechnoType/Body.h | 1 - src/Ext/TerrainType/Body.h | 1 - src/Ext/Tiberium/Body.h | 1 - src/Ext/Unit/Body.h | 1 - src/Ext/UnitType/Body.h | 1 - src/Ext/VoxelAnim/Body.h | 1 - src/Ext/VoxelAnimType/Body.h | 1 - src/Ext/WarheadType/Body.h | 1 - src/Ext/WeaponType/Body.h | 1 - src/Utilities/Container.h | 10 +++++++++- 34 files changed, 9 insertions(+), 34 deletions(-) diff --git a/src/Ext/Aircraft/Body.h b/src/Ext/Aircraft/Body.h index 7ffc3c8292..0c12604f89 100644 --- a/src/Ext/Aircraft/Body.h +++ b/src/Ext/Aircraft/Body.h @@ -8,7 +8,6 @@ class AircraftExt final : public FootExt { public: using base_type = AircraftClass; - using ExtData = AircraftExt; static constexpr DWORD Canary = 0xA1A2A3A4; diff --git a/src/Ext/AircraftType/Body.h b/src/Ext/AircraftType/Body.h index ab04ff13ec..bcce17d0f0 100644 --- a/src/Ext/AircraftType/Body.h +++ b/src/Ext/AircraftType/Body.h @@ -8,7 +8,6 @@ class AircraftTypeExt final : public TechnoTypeExt { public: using base_type = AircraftTypeClass; - using ExtData = AircraftTypeExt; static constexpr DWORD Canary = 0xA5A6A7A8; diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 88f863391f..94bdc4c691 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -8,7 +8,6 @@ class AnimExt final : public ObjectExt { public: using base_type = AnimClass; - using ExtData = AnimExt; static constexpr DWORD Canary = 0xAAAAAAAA; diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index f2e66ee9e9..ea91ad4bc1 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -19,7 +19,6 @@ class AnimTypeExt final : public ObjectTypeExt { public: using base_type = AnimTypeClass; - using ExtData = AnimTypeExt; static constexpr DWORD Canary = 0xEEEEEEEE; diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 39cb064b34..861d91d8f7 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -7,7 +7,6 @@ class BuildingTypeExt final : public TechnoTypeExt { public: using base_type = BuildingTypeClass; - using ExtData = BuildingTypeExt; static constexpr DWORD Canary = 0x11111111; diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index 7c342eda66..f307dfd8d3 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -17,7 +17,6 @@ class BulletExt final : public ObjectExt { public: using base_type = BulletClass; - using ExtData = BulletExt; static constexpr DWORD Canary = 0x2A2A2A2A; diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index 7392842ad0..d55bcf2f3d 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -13,7 +13,6 @@ class BulletTypeExt final : public ObjectTypeExt { public: using base_type = BulletTypeClass; - using ExtData = BulletTypeExt; static constexpr DWORD Canary = 0xF00DF00D; diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index da1a4ad7a5..a07a21c92a 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -8,7 +8,6 @@ class CellExt final : public AbstractExt { public: using base_type = CellClass; - using ExtData = CellExt; static constexpr DWORD Canary = 0x13371337; diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index d536538674..3607d9ad6d 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -11,7 +11,6 @@ class HouseExt final : public AbstractExt, public Detach::Listener { public: using base_type = TeamClass; - using ExtData = TeamExt; static constexpr DWORD Canary = 0x414B4B41; diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 5770745307..c149858f47 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -9,7 +9,6 @@ class TeamTypeExt final : public AbstractTypeExt { public: using base_type = TeamTypeClass; - using ExtData = TeamTypeExt; static constexpr DWORD Canary = 0xABCDEF01; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 71603a44df..35861310bc 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -16,7 +16,6 @@ class TechnoExt : public RadioExt, public Detach::Listener { public: using base_type = TechnoClass; - using ExtData = TechnoExt; static constexpr DWORD Canary = 0x55555555; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 37c1c907bb..f3d45fd5fc 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -20,7 +20,6 @@ class TechnoTypeExt : public ObjectTypeExt { public: using base_type = TechnoTypeClass; - using ExtData = TechnoTypeExt; static constexpr DWORD Canary = 0x11111111; diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index 3433392902..7070f74cc9 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -9,7 +9,6 @@ class TerrainTypeExt final : public ObjectTypeExt { public: using base_type = TerrainTypeClass; - using ExtData = TerrainTypeExt; static constexpr DWORD Canary = 0xBEE78007; diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index e8f117710f..69aac4a6b8 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -9,7 +9,6 @@ class TiberiumExt final : public AbstractTypeExt { public: using base_type = TiberiumClass; - using ExtData = TiberiumExt; static constexpr DWORD Canary = 0xAABBCCDD; diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h index 06399be23a..6f2b93429f 100644 --- a/src/Ext/Unit/Body.h +++ b/src/Ext/Unit/Body.h @@ -10,7 +10,6 @@ class UnitExt final : public FootExt { public: using base_type = UnitClass; - using ExtData = UnitExt; static constexpr DWORD Canary = 0xE1E2E3E4; diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h index 591d97e700..51ba2f27cb 100644 --- a/src/Ext/UnitType/Body.h +++ b/src/Ext/UnitType/Body.h @@ -8,7 +8,6 @@ class UnitTypeExt final : public TechnoTypeExt { public: using base_type = UnitTypeClass; - using ExtData = UnitTypeExt; static constexpr DWORD Canary = 0xE5E6E7E8; diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index bd2f3252cc..3a1392641b 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -10,7 +10,6 @@ class VoxelAnimExt final : public ObjectExt { public: using base_type = VoxelAnimClass; - using ExtData = VoxelAnimExt; static constexpr DWORD Canary = 0xAAAAAACC; diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 429cb3c8f8..317f136fcd 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -11,7 +11,6 @@ class VoxelAnimTypeExt final : public ObjectTypeExt { public: using base_type = VoxelAnimTypeClass; - using ExtData = VoxelAnimTypeExt; static constexpr DWORD Canary = 0xAAAEEEEE; diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index a25b653187..618ab41d6a 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -11,7 +11,6 @@ class WarheadTypeExt final : public AbstractTypeExt { public: using base_type = WarheadTypeClass; - using ExtData = WarheadTypeExt; static constexpr DWORD Canary = 0x22222222; diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index da387dd1cd..a2c65969ab 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -10,7 +10,6 @@ class WeaponTypeExt final : public AbstractTypeExt { public: using base_type = WeaponTypeClass; - using ExtData = WeaponTypeExt; static constexpr DWORD Canary = 0x22222222; diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index b54e416658..de97c05017 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -381,12 +381,20 @@ class ContainerMap final template concept HasOffset = requires(T) { T::ExtPointerOffset; }; +// resolves the data class of an extension: the extension class itself for the +// flattened hierarchy, or the nested ExtData class of the legacy shells (EBolt). +template +struct ExtensionDataType { using type = T; }; + +template requires requires { typename T::ExtData; } +struct ExtensionDataType { using type = typename T::ExtData; }; + template class Container { private: using base_type = typename T::base_type; - using extension_type = typename T::ExtData; + using extension_type = typename ExtensionDataType::type; using base_type_ptr = base_type*; using const_base_type_ptr = const base_type*; using extension_type_ptr = extension_type*; From b7ad2d4923abb433a783b5d3feb5bb0940c0e0c5 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 14:31:09 +0300 Subject: [PATCH 25/47] Remove extensions late in the leaf destructor bodies before the base destructor call --- src/Ext/Aircraft/Body.cpp | 7 +++++-- src/Ext/AircraftType/Body.cpp | 9 +++++---- src/Ext/Building/Body.cpp | 6 +++--- src/Ext/BuildingType/Body.cpp | 6 +++--- src/Ext/Infantry/Body.cpp | 6 ++++-- src/Ext/InfantryType/Body.cpp | 9 +++++---- src/Ext/Techno/Body.cpp | 4 ++-- src/Ext/Unit/Body.cpp | 7 +++++-- src/Ext/UnitType/Body.cpp | 9 +++++---- 9 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index a298bea531..71fb3d8b20 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -182,9 +182,12 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) return static_cast(std::clamp(landingDir, 0, 255)); } -DEFINE_HOOK(0x414080, AircraftClass_DTOR, 0x5) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK_AGAIN(0x41426D, AircraftClass_DTOR, 0x2) +DEFINE_HOOK(0x4141FA, AircraftClass_DTOR, 0x2) { - GET(AircraftClass*, pItem, ECX); + GET(AircraftClass*, pItem, EDI); AircraftExt::ExtMap.Remove(pItem); diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index ed9af1b0d4..fc1d94d3a5 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -14,11 +14,12 @@ DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) return 0; } -// the second address is the twin destructor body the vtable actually points at -DEFINE_HOOK_AGAIN(0x41CFE0, AircraftTypeClass_DTOR, 0x6) -DEFINE_HOOK(0x41CA20, AircraftTypeClass_DTOR, 0x6) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK_AGAIN(0x41D04F, AircraftTypeClass_DTOR, 0x2) +DEFINE_HOOK(0x41CA8F, AircraftTypeClass_DTOR, 0x2) { - GET(AircraftTypeClass*, pItem, ECX); + GET(AircraftTypeClass*, pItem, ESI); AircraftTypeExt::ExtMap.Remove(pItem); diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index e89249d6e6..f9948e271b 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -9,7 +9,6 @@ BuildingExt::ExtContainer BuildingExt::ExtMap; BuildingExt::ExtContainer::ExtContainer() : Container("BuildingClass") { } BuildingExt::ExtContainer::~ExtContainer() = default; - void BuildingExt::DisplayIncomeString() { if (this->AccumulatedIncome) @@ -259,7 +258,6 @@ bool BuildingExt::CanGrindTechno(BuildingClass* pBuilding, TechnoClass* pTechno) if (disallowTypes.size() > 0 && disallowTypes.Contains(pType)) return false; - return true; } @@ -637,7 +635,9 @@ static void __fastcall BuildingClass_InfiltratedBy_Wrapper(BuildingClass* pThis, DEFINE_FUNCTION_JUMP(CALL, 0x51A00B, BuildingClass_InfiltratedBy_Wrapper); -DEFINE_HOOK(0x43C022, BuildingClass_DTOR, 0x6) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK(0x43C0B6, BuildingClass_DTOR, 0x2) { GET(BuildingClass*, pItem, ESI); diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 8439a71baf..466c9771ad 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -8,7 +8,6 @@ BuildingTypeExt::ExtContainer BuildingTypeExt::ExtMap; BuildingTypeExt::ExtContainer::ExtContainer() : Container("BuildingTypeClass") { } BuildingTypeExt::ExtContainer::~ExtContainer() = default; - // Assuming SuperWeapon & SuperWeapon2 are used (for the moment) int BuildingTypeExt::GetSuperWeaponCount() const { @@ -150,7 +149,6 @@ int BuildingTypeExt::GetUpgradesAmount(BuildingTypeClass* pBuilding, HouseClass* return isUpgrade ? result : -1; } - void BuildingTypeExt::Initialize() { TechnoTypeExt::Initialize(); @@ -481,7 +479,9 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) // BuildingTypeClass destruction, save, load and INI parsing of the extension are handled by // the TechnoTypeClass container hooks now that a building type has a single extension at 0x18. -DEFINE_HOOK(0x45E707, BuildingTypeClass_DTOR, 0x6) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK(0x45E732, BuildingTypeClass_DTOR, 0x2) { GET(BuildingTypeClass*, pItem, ESI); diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp index 1cd5f837d7..6a1f7d4df2 100644 --- a/src/Ext/Infantry/Body.cpp +++ b/src/Ext/Infantry/Body.cpp @@ -14,9 +14,11 @@ DEFINE_HOOK(0x517A60, InfantryClass_CTOR, 0xE) return 0; } -DEFINE_HOOK(0x517D90, InfantryClass_DTOR, 0x5) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK(0x517F81, InfantryClass_DTOR, 0x2) { - GET(InfantryClass*, pItem, ECX); + GET(InfantryClass*, pItem, ESI); InfantryExt::ExtMap.Remove(pItem); diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index 2eaa7dd845..865ba0f3dd 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -14,11 +14,12 @@ DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) return 0; } -// the second address is the twin destructor body the vtable actually points at -DEFINE_HOOK_AGAIN(0x524D70, InfantryTypeClass_DTOR, 0x6) -DEFINE_HOOK(0x5239D0, InfantryTypeClass_DTOR, 0x5) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK_AGAIN(0x524E90, InfantryTypeClass_DTOR, 0x2) +DEFINE_HOOK(0x523AF0, InfantryTypeClass_DTOR, 0x2) { - GET(InfantryTypeClass*, pItem, ECX); + GET(InfantryTypeClass*, pItem, ESI); InfantryTypeExt::ExtMap.Remove(pItem); diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 90d1af8680..1f54b375e4 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1384,8 +1384,8 @@ bool TechnoExt::SaveGlobals(PhobosStreamWriter& Stm) // The extension is allocated by the concrete leaf constructors (UnitClass/InfantryClass/ // BuildingClass/AircraftClass), not here at the abstract TechnoClass level. -// The extension is removed by the concrete leaf destructor hooks; this only -// keeps the tech tree recheck side effect at the shared base destructor. +// The extension is removed by the leaf destructor hooks; this only keeps the +// tech tree recheck side effect at the shared base destructor. DEFINE_HOOK(0x6F4500, TechnoClass_DTOR, 0x5) { GET(TechnoClass*, pItem, ECX); diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 5a6974cb05..4c27a27640 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -15,9 +15,12 @@ DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) return 0; } -DEFINE_HOOK(0x735780, UnitClass_DTOR, 0x6) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK_AGAIN(0x7359DA, UnitClass_DTOR, 0x2) +DEFINE_HOOK(0x735967, UnitClass_DTOR, 0x2) { - GET(UnitClass*, pItem, ECX); + GET(UnitClass*, pItem, ESI); UnitExt::ExtMap.Remove(pItem); diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index f2a017791f..0d7b0c4430 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -14,11 +14,12 @@ DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) return 0; } -// the second address is the twin destructor body the vtable actually points at -DEFINE_HOOK_AGAIN(0x748190, UnitTypeClass_DTOR, 0x6) -DEFINE_HOOK(0x7472F0, UnitTypeClass_DTOR, 0x6) +// Late in every destructor body of the class, right before it chains into the +// base destructor: the last point where the extension is no longer used. +DEFINE_HOOK_AGAIN(0x7481FF, UnitTypeClass_DTOR, 0x2) +DEFINE_HOOK(0x74735F, UnitTypeClass_DTOR, 0x2) { - GET(UnitTypeClass*, pItem, ECX); + GET(UnitTypeClass*, pItem, ESI); UnitTypeExt::ExtMap.Remove(pItem); From a99acc16af42520c8f64757c79bab3aa5d9e443b Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 15:05:22 +0300 Subject: [PATCH 26/47] Size the destructor hooks to cover whole instructions --- src/Ext/Aircraft/Body.cpp | 4 ++-- src/Ext/AircraftType/Body.cpp | 7 +++---- src/Ext/Building/Body.cpp | 2 +- src/Ext/BuildingType/Body.cpp | 2 +- src/Ext/Infantry/Body.cpp | 2 +- src/Ext/InfantryType/Body.cpp | 4 ++-- src/Ext/Unit/Body.cpp | 4 ++-- src/Ext/UnitType/Body.cpp | 7 +++---- 8 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index 71fb3d8b20..c98b73289a 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -184,8 +184,8 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. -DEFINE_HOOK_AGAIN(0x41426D, AircraftClass_DTOR, 0x2) -DEFINE_HOOK(0x4141FA, AircraftClass_DTOR, 0x2) +DEFINE_HOOK_AGAIN(0x41426D, AircraftClass_DTOR, 0x9) +DEFINE_HOOK(0x4141FA, AircraftClass_DTOR, 0x9) { GET(AircraftClass*, pItem, EDI); diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index fc1d94d3a5..67fe27935e 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -14,10 +14,9 @@ DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) return 0; } -// Late in every destructor body of the class, right before it chains into the -// base destructor: the last point where the extension is no longer used. -DEFINE_HOOK_AGAIN(0x41D04F, AircraftTypeClass_DTOR, 0x2) -DEFINE_HOOK(0x41CA8F, AircraftTypeClass_DTOR, 0x2) +// Hooked after the base destructor call in the twin destructor body the vtable +// points at (the standalone destructor body has no callers and no hookable window). +DEFINE_HOOK(0x41D056, AircraftTypeClass_DTOR, 0x5) { GET(AircraftTypeClass*, pItem, ESI); diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index f9948e271b..03df069406 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -637,7 +637,7 @@ DEFINE_FUNCTION_JUMP(CALL, 0x51A00B, BuildingClass_InfiltratedBy_Wrapper); // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. -DEFINE_HOOK(0x43C0B6, BuildingClass_DTOR, 0x2) +DEFINE_HOOK(0x43C0B6, BuildingClass_DTOR, 0x9) { GET(BuildingClass*, pItem, ESI); diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index 466c9771ad..c7cb54d706 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -481,7 +481,7 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. -DEFINE_HOOK(0x45E732, BuildingTypeClass_DTOR, 0x2) +DEFINE_HOOK(0x45E732, BuildingTypeClass_DTOR, 0xE) { GET(BuildingTypeClass*, pItem, ESI); diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp index 6a1f7d4df2..9059dbaa74 100644 --- a/src/Ext/Infantry/Body.cpp +++ b/src/Ext/Infantry/Body.cpp @@ -16,7 +16,7 @@ DEFINE_HOOK(0x517A60, InfantryClass_CTOR, 0xE) // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. -DEFINE_HOOK(0x517F81, InfantryClass_DTOR, 0x2) +DEFINE_HOOK(0x517F81, InfantryClass_DTOR, 0x8) { GET(InfantryClass*, pItem, ESI); diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index 865ba0f3dd..c51996d53f 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -16,8 +16,8 @@ DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. -DEFINE_HOOK_AGAIN(0x524E90, InfantryTypeClass_DTOR, 0x2) -DEFINE_HOOK(0x523AF0, InfantryTypeClass_DTOR, 0x2) +DEFINE_HOOK_AGAIN(0x524E90, InfantryTypeClass_DTOR, 0xE) +DEFINE_HOOK(0x523AF0, InfantryTypeClass_DTOR, 0xE) { GET(InfantryTypeClass*, pItem, ESI); diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 4c27a27640..1be2c5d326 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -17,8 +17,8 @@ DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. -DEFINE_HOOK_AGAIN(0x7359DA, UnitClass_DTOR, 0x2) -DEFINE_HOOK(0x735967, UnitClass_DTOR, 0x2) +DEFINE_HOOK_AGAIN(0x7359DA, UnitClass_DTOR, 0x9) +DEFINE_HOOK(0x735967, UnitClass_DTOR, 0x9) { GET(UnitClass*, pItem, ESI); diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index 0d7b0c4430..07ea250f60 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -14,10 +14,9 @@ DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) return 0; } -// Late in every destructor body of the class, right before it chains into the -// base destructor: the last point where the extension is no longer used. -DEFINE_HOOK_AGAIN(0x7481FF, UnitTypeClass_DTOR, 0x2) -DEFINE_HOOK(0x74735F, UnitTypeClass_DTOR, 0x2) +// Hooked after the base destructor call in the twin destructor body the vtable +// points at (the standalone destructor body has no callers and no hookable window). +DEFINE_HOOK(0x748206, UnitTypeClass_DTOR, 0x5) { GET(UnitTypeClass*, pItem, ESI); From 66fa4f1a7e96869df7585f07014b93aa16a3bc9c Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 15:09:41 +0300 Subject: [PATCH 27/47] Cover the standalone type destructor tails via the alignment padding --- src/Ext/AircraftType/Body.cpp | 6 ++++-- src/Ext/UnitType/Body.cpp | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index 67fe27935e..f03dd02b99 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -14,8 +14,10 @@ DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) return 0; } -// Hooked after the base destructor call in the twin destructor body the vtable -// points at (the standalone destructor body has no callers and no hookable window). +// Hooked after the base destructor call in both destructor bodies; the second site +// is the tail of the standalone body (pop/pop/retn, safe to steal - the bytes after +// it are alignment padding that is never executed). +DEFINE_HOOK_AGAIN(0x41CA96, AircraftTypeClass_DTOR, 0x3) DEFINE_HOOK(0x41D056, AircraftTypeClass_DTOR, 0x5) { GET(AircraftTypeClass*, pItem, ESI); diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index 07ea250f60..872f3b0cd1 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -14,8 +14,10 @@ DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) return 0; } -// Hooked after the base destructor call in the twin destructor body the vtable -// points at (the standalone destructor body has no callers and no hookable window). +// Hooked after the base destructor call in both destructor bodies; the second site +// is the tail of the standalone body (pop/pop/retn, safe to steal - the bytes after +// it are alignment padding that is never executed). +DEFINE_HOOK_AGAIN(0x747366, UnitTypeClass_DTOR, 0x3) DEFINE_HOOK(0x748206, UnitTypeClass_DTOR, 0x5) { GET(UnitTypeClass*, pItem, ESI); From d0ba272e8310b410f1bcf6551d4cae9785e07ec5 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 15:48:42 +0300 Subject: [PATCH 28/47] Add the missing ParticleTypeClass destructor hook --- src/Ext/ParticleType/Body.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Ext/ParticleType/Body.cpp b/src/Ext/ParticleType/Body.cpp index fecb563bf6..92f1257940 100644 --- a/src/Ext/ParticleType/Body.cpp +++ b/src/Ext/ParticleType/Body.cpp @@ -71,6 +71,16 @@ DEFINE_HOOK(0x644DBB, ParticleTypeClass_CTOR, 0x5) return 0; } +// Late in the destructor body, right before it chains into the base destructor. +DEFINE_HOOK(0x645A39, ParticleTypeClass_DTOR, 0x13) +{ + GET(ParticleTypeClass*, pItem, ESI); + + ParticleTypeExt::ExtMap.Remove(pItem); + + return 0; +} + DEFINE_HOOK(0x6453FF, ParticleTypeClass_LoadFromINI, 0x6) { GET(ParticleTypeClass*, pItem, ESI); From 4d3ec3c64b6700dadb2a26ced6d701c31c92b250 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 15:48:42 +0300 Subject: [PATCH 29/47] Clean up extension system comments --- src/Ext/Aircraft/Body.cpp | 1 - src/Ext/Building/Body.cpp | 3 --- src/Ext/BuildingType/Body.cpp | 3 --- src/Ext/Techno/Body.h | 2 +- src/Ext/TechnoType/Body.cpp | 2 +- src/Ext/TechnoType/Body.h | 2 +- src/Ext/Unit/Body.cpp | 1 - src/Utilities/Detach.h | 2 +- 8 files changed, 4 insertions(+), 12 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index c98b73289a..bad2080d6d 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -8,7 +8,6 @@ AircraftExt::ExtContainer::~ExtContainer() = default; #include #include -// An aircraft's extension is a concrete AircraftExt leaf, owned by the TechnoClass container. DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) { GET(AircraftClass*, pItem, ESI); diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 03df069406..9ec6c115cd 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -612,9 +612,6 @@ DEFINE_HOOK(0x43B750, BuildingClass_CTOR, 0x6) return 0; } -// BuildingClass destruction, save and load of the extension are handled by the TechnoClass -// container hooks now that a building has a single TechnoClass-derived extension at 0x18. - DEFINE_HOOK(0x454174, BuildingClass_Load_LightSource, 0xA) { GET(BuildingClass*, pThis, EDI); diff --git a/src/Ext/BuildingType/Body.cpp b/src/Ext/BuildingType/Body.cpp index c7cb54d706..18eb268401 100644 --- a/src/Ext/BuildingType/Body.cpp +++ b/src/Ext/BuildingType/Body.cpp @@ -476,9 +476,6 @@ DEFINE_HOOK(0x45E50C, BuildingTypeClass_CTOR, 0x6) return 0; } -// BuildingTypeClass destruction, save, load and INI parsing of the extension are handled by -// the TechnoTypeClass container hooks now that a building type has a single extension at 0x18. - // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. DEFINE_HOOK(0x45E732, BuildingTypeClass_DTOR, 0xE) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 35861310bc..8c1a9322a6 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -20,7 +20,7 @@ class TechnoExt : public RadioExt, public Detach::Listener static constexpr DWORD Canary = 0x55555555; public: - // typed owner accessor (the base chain stores the owner as RadioClass*) + // typed owner accessor TechnoClass* OwnerObject() const { return static_cast(this->GetAttachedObject()); diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 528be191a1..6f6934f882 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -2007,7 +2007,7 @@ DEFINE_HOOK(0x711835, TechnoTypeClass_CTOR, 0x5) GET(TechnoTypeClass*, pItem, ESI); // The extension is allocated by the concrete type leaf constructors - // (UnitType/InfantryType/Building/AircraftType), not here at the TechnoType level. + // (UnitType/InfantryType/BuildingType/AircraftType), not here at the TechnoTypeClass level. pItem->DefaultToGuardArea = RulesExt::Global()->DefaultToGuardArea; return 0; diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index f3d45fd5fc..f4235c4322 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -24,7 +24,7 @@ class TechnoTypeExt : public ObjectTypeExt static constexpr DWORD Canary = 0x11111111; public: - // typed owner accessor (the base chain stores the owner as ObjectTypeClass*) + // typed owner accessor TechnoTypeClass* OwnerObject() const { return static_cast(this->GetAttachedObject()); diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 1be2c5d326..997640f5a0 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -5,7 +5,6 @@ UnitExt::ExtContainer UnitExt::ExtMap; UnitExt::ExtContainer::ExtContainer() : Container("UnitClass") { } UnitExt::ExtContainer::~ExtContainer() = default; -// A unit's extension is a concrete UnitExt leaf, owned by the TechnoClass container. DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) { GET(UnitClass*, pItem, ESI); diff --git a/src/Utilities/Detach.h b/src/Utilities/Detach.h index 276cc629a0..eecec33ae0 100644 --- a/src/Utilities/Detach.h +++ b/src/Utilities/Detach.h @@ -4,7 +4,7 @@ class AbstractClass; -// Typed pointer-invalidation registry, mirroring Vinifera's detach listener system. +// Typed pointer-invalidation registry. // An extension (or a static aggregate for high-volume types) subscribes to deletions // of a game type T by inheriting Detach::Listener and overriding OnDetach. // The game's Detach_This_From_All funnel (hook at 0x7258D0) dispatches the dying From 3c4511c406b01e3cbc6c62c4f8a2334d4f490fe1 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 15:48:42 +0300 Subject: [PATCH 30/47] Handle in-place cell reconstruction and the static working cell --- src/Ext/Cell/Body.cpp | 52 ++++++++++++++++++- src/Utilities/Container.h | 104 +++++++++++++++++++++++++------------- 2 files changed, 120 insertions(+), 36 deletions(-) diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index a65ce39e82..22618da0be 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -1,5 +1,9 @@ #include "Body.h" +#include + +#include + CellExt::ExtContainer CellExt::ExtMap; // ============================= @@ -55,11 +59,28 @@ CellExt::ExtContainer::~ExtContainer() = default; // ============================= // container hooks +// The static working cell (MapClass::InvalidCell) is re-initialized in place at the +// end of every MapClass::SetMapDimensions call and never enters the game's savegame +// stream (only cells installed in the map array do). It keeps a single untracked, +// process-lifetime extension instead of a container-managed one. +static std::unique_ptr InvalidCellExt; + DEFINE_HOOK(0x47BDA1, CellClass_CTOR, 0x5) { GET(CellClass*, pItem, ESI); - CellExt::ExtMap.Allocate(pItem); + if (pItem == &MapClass::InvalidCell) + { + // recreated even during savegame load: this extension is never in the + // stream, the live one is the real thing + InvalidCellExt = std::make_unique(pItem); + InvalidCellExt->EnsureConstanted(); + AbstractExt::Attach(pItem, InvalidCellExt.get()); + } + else + { + CellExt::ExtMap.Allocate(pItem); + } return 0; } @@ -68,8 +89,35 @@ DEFINE_HOOK(0x47BB60, CellClass_DTOR, 0x6) { GET(CellClass*, pItem, ECX); - CellExt::ExtMap.Remove(pItem); + if (pItem == &MapClass::InvalidCell) + InvalidCellExt.reset(); + else + CellExt::ExtMap.Remove(pItem); + + return 0; +} + +// MapClass::InitCells re-runs the cell constructor in place on every allocated cell +// (the AbstractClass constructor zeroes the extension slot in the process); drop the +// tracked extensions up front so the constructor hook can allocate fresh ones. +DEFINE_HOOK(0x565BC0, MapClass_InitCells, 0x6) +{ + CellExt::ExtMap.RemoveAll(); return 0; } +// MapClass::SetMapDimensions re-runs the cell constructor in place on cells that +// already exist at the target map slot; drop the extension first, then make the +// displaced constructor call ourselves. +DEFINE_HOOK(0x5663FC, MapClass_SetMapDimensions_ReinitCell, 0x5) +{ + GET(CellClass*, pItem, ECX); + + CellExt::ExtMap.Remove(pItem); + + R->EAX(reinterpret_cast(0x47BBF0)(pItem)); + + return 0x566401; +} + diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index de97c05017..670fe832b9 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -1,5 +1,6 @@ #pragma once +#include #include #include @@ -23,42 +24,33 @@ enum class InitState }; /* - * ========================== - * It's a kind of magic - * ========================== - - * These two templates are the basis of the new class extension standard. - - * ========================== - - * Extension is the parent class for the data you want to link with this instance of T - ( for example, [Warhead]MindControl.Permanent= should be stored in WarheadClassExt::ExtData - which itself should be a derivate of Extension ) - - * ========================== - - Container is the storage for all the Extension which share the same T, - where TX is the containing class of the relevant derivate of Extension. // complex, huh? - ( for example, there is Container - which contains all the custom data for all WarheadTypeClass instances, - and WarheadTypeExt itself contains just statics like the Container itself ) - - Requires: - using base_type = T; - const DWORD Extension::Canary = (any dword value easily identifiable in a byte stream) - class TX::ExtData : public Extension { custom_data; } - - Complex? Yes. That's partially why you should be happy these are premade for you. + * Extension classes form an inheritance hierarchy that mirrors the game's own class + * tree, rooted at AbstractExt. Every game object of an extended type carries exactly + * one extension instance of the most derived matching type, cached inside the object + * at AbstractExt::ExtPointerOffset and fetched through the static Fetch/TryFetch + * accessors of any level of the hierarchy. * + * Container tracks all live extension instances of one concrete class for the + * bulk operations: allocation, removal, centralized savegame streaming, post-load + * relinking and scenario clearing. TX must provide: + * using base_type = T; (the extended game class) + * static constexpr DWORD Canary = (any dword value easily identifiable in a byte stream) */ -// the non-template root of every extension, mirroring Vinifera's AbstractClassExtension. -// it owns the back-pointer and the staged init state, so all extensions share a common base. +// The non-template root of every extension of an AbstractClass-derived game class. +// It owns the back-pointer and the staged init state, so all extensions share a common base. class AbstractExt { + template + friend class Container; + AbstractClass* AttachedToObject; InitState Initialized; + // position within the owning container's item list, kept up to date on removal + // so single removals don't have to search the list; never serialized + size_t ContainerIndex { SIZE_MAX }; + public: // every extension pointer lives in the unused AbstractClass::unknown_18 field static constexpr size_t ExtPointerOffset = 0x18; @@ -75,6 +67,13 @@ class AbstractExt return pThis ? Fetch(pThis) : nullptr; } + // writes the inline extension slot directly, for extensions that are not + // managed by a container (the static working cell) + static void Attach(AbstractClass* pThis, AbstractExt* pExt) + { + *reinterpret_cast(reinterpret_cast(pThis) + ExtPointerOffset) = pExt; + } + explicit AbstractExt(AbstractClass* const OwnerObject) : AttachedToObject { OwnerObject }, Initialized { InitState::Blank } { } @@ -445,9 +444,14 @@ class Container val->EnsureConstanted(); if constexpr (HasOffset) + { SetExtensionPointer(key, val); + val->ContainerIndex = Items.size(); + } else + { this->MappedItems.insert(key, val); + } Items.emplace_back(val); @@ -500,21 +504,52 @@ class Container { if (auto Item = Find(key)) { + auto& vec = this->Items; + if constexpr (HasOffset) + { ResetExtensionPointer(key); + + // untracked extensions (index out of range) only detach from the owner + const size_t index = Item->ContainerIndex; + + if (index >= vec.size() || vec[index] != Item) + return; + + vec[index] = vec.back(); + vec[index]->ContainerIndex = index; + vec.pop_back(); + } else + { this->MappedItems.remove(key); + auto it = std::find(vec.begin(), vec.end(), Item); - auto& vec = this->Items; - auto it = std::find(vec.begin(), vec.end(), Item); + if (it != vec.end()) + { + *it = vec.back(); + vec.pop_back(); + } + } + + delete Item; + } + } - if (it != vec.end()) + // Deletes every tracked extension and detaches it from its owner. Unlike Clear, + // this is regular teardown for owners the game re-initializes in place without + // destroying them (cells). + void RemoveAll() + { + if constexpr (HasOffset) + { + for (const auto& item : this->Items) { - *it = vec.back(); - vec.pop_back(); + ResetExtensionPointer(item->OwnerObject()); + delete item; } - delete Item; + this->Items.clear(); } } @@ -645,6 +680,7 @@ class Container auto const buffer = new extension_type(static_cast(oldOwner)); buffer->RegisterOwnerForChange(); PhobosSwizzle::RegisterChange(oldPtr, buffer); + buffer->ContainerIndex = this->Items.size(); this->Items.emplace_back(buffer); buffer->LoadFromStream(reader); From 04a1c92105afb8b0d8ec0ec7b6b25ecb2cd1e0d2 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 16 Jul 2026 16:11:13 +0300 Subject: [PATCH 31/47] Tolerate cells absent from the savegame on save and load --- src/Ext/Cell/Body.cpp | 28 ++++++++++++++++++++ src/Ext/Cell/Body.h | 2 ++ src/Utilities/Container.h | 56 ++++++++++++++++++++++++++++++++++----- 3 files changed, 80 insertions(+), 6 deletions(-) diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 22618da0be..3ce86b36d9 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -56,6 +56,34 @@ bool CellExt::RadLevel::Serialize(T& stm) CellExt::ExtContainer::ExtContainer() : Container("CellClass") { } CellExt::ExtContainer::~ExtContainer() = default; +// The game only saves cells that its cell iterator reaches inside the map array; +// any other live cell is recreated as a default-initialized one on load. Mirror +// that: drop stream extensions whose owner could not be remapped, and allocate +// fresh extensions for cells the load recreated outside the stream. +void CellExt::ExtContainer::RelinkExtensionPointers() +{ + if (const size_t orphans = this->RemoveNullOwnerItems()) + Debug::Log("CellClass - dropped %u extensions of cells absent from the savegame.\n", orphans); + + this->Container::RelinkExtensionPointers(); + + size_t added = 0; + + for (int i = 0; i < MapClass::MaxCells; ++i) + { + auto const pCell = MapClass::Instance.Cells[i]; + + if (pCell && !AbstractExt::Fetch(pCell)) + { + this->AllocateUnchecked(pCell); + ++added; + } + } + + if (added) + Debug::Log("CellClass - allocated %u extensions for cells absent from the savegame.\n", added); +} + // ============================= // container hooks diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index a07a21c92a..f5270b3e8d 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -58,6 +58,8 @@ class CellExt final : public AbstractExt ExtContainer(); ~ExtContainer(); + // cells the game omits from the savegame need special handling on load + void RelinkExtensionPointers(); }; static ExtContainer ExtMap; diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 670fe832b9..6b91abd139 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -429,13 +429,11 @@ class Container (*(uintptr_t*)((char*)key + T::ExtPointerOffset)) = 0; } -public: - extension_type_ptr Allocate(base_type_ptr key) +protected: + // the unguarded allocation core, also used by load-time fixups that run while + // Phobos::IsLoadingSaveGame is still set + extension_type_ptr AllocateUnchecked(base_type_ptr key) { - // during savegame load extensions are restored from the stream instead - if (Phobos::IsLoadingSaveGame) - return nullptr; - if constexpr (HasOffset) ResetExtensionPointer(key); @@ -458,6 +456,49 @@ class Container return val; } + // deletes every tracked extension whose owner pointer is null and returns how + // many were dropped; used by load-time fixups for owners the game omits from + // the savegame (their owner cannot be remapped by the swizzle manager) + size_t RemoveNullOwnerItems() + { + if constexpr (HasOffset) + { + size_t removed = 0; + auto& vec = this->Items; + + for (size_t i = vec.size(); i-- > 0;) + { + if (!vec[i]->OwnerObject()) + { + delete vec[i]; + vec[i] = vec.back(); + vec.pop_back(); + + if (i < vec.size()) + vec[i]->ContainerIndex = i; + + ++removed; + } + } + + return removed; + } + else + { + return 0; + } + } + +public: + extension_type_ptr Allocate(base_type_ptr key) + { + // during savegame load extensions are restored from the stream instead + if (Phobos::IsLoadingSaveGame) + return nullptr; + + return this->AllocateUnchecked(key); + } + extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) { @@ -606,6 +647,9 @@ class Container for (const auto& item : this->Items) { + if (!item->OwnerObject()) + Debug::FatalErrorAndExit("SaveAllToStream - '%s' extension has no owner!\n", this->Name); + PhobosByteStream saver(sizeof(*item)); PhobosStreamWriter writer(saver); From 8036a6807d9447cd7a2b8437282d4e602ceea06f Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 03:05:50 +0300 Subject: [PATCH 32/47] Rebuild cell extension slots after SetMapDimensions copies cells around --- src/Ext/Cell/Body.cpp | 25 +++++++++++++++++++++---- src/Utilities/Container.h | 13 +++++++++++++ 2 files changed, 34 insertions(+), 4 deletions(-) diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 3ce86b36d9..358407cf90 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -87,10 +87,11 @@ void CellExt::ExtContainer::RelinkExtensionPointers() // ============================= // container hooks -// The static working cell (MapClass::InvalidCell) is re-initialized in place at the -// end of every MapClass::SetMapDimensions call and never enters the game's savegame -// stream (only cells installed in the map array do). It keeps a single untracked, -// process-lifetime extension instead of a container-managed one. +// The static working cell (MapClass::InvalidCell) is re-initialized in place by +// MapClass::ReadBinary and at the end of every MapClass::SetMapDimensions call, and +// never enters the game's savegame stream (only cells installed in the map array +// do). It keeps a single untracked, process-lifetime extension instead of a +// container-managed one. static std::unique_ptr InvalidCellExt; DEFINE_HOOK(0x47BDA1, CellClass_CTOR, 0x5) @@ -149,3 +150,19 @@ DEFINE_HOOK(0x5663FC, MapClass_SetMapDimensions_ReinitCell, 0x5) return 0x566401; } +// MapClass::SetMapDimensions value-copies whole cells around: it snapshots the old +// map's cells into a temporary buffer, re-initializes the cells of the new map rect, +// then copies the snapshot back into the shifted cells (or into the working cell) via +// AbstractClass::operator=, which copies the extension slot too, leaving the slots +// pointing at stale extensions. The container is the source of truth and the slots +// are only a cache: rebuild them right after the copy-back, before the boundary cells +// are deleted and their destructors read the slots again. +DEFINE_HOOK(0x566AB7, MapClass_SetMapDimensions_PostRestore, 0x6) +{ + CellExt::ExtMap.ReattachAll(); + + AbstractExt::Attach(&MapClass::InvalidCell, InvalidCellExt.get()); + + return 0; +} + diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 6b91abd139..a9fd70dbe0 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -577,6 +577,19 @@ class Container } } + // Rewrites every tracked extension into its owner's inline slot. For owners the + // game value-copies or re-initializes in place (cells), the container is the + // source of truth and the slots are only a cache that has to be rebuilt after + // the game shuffles the objects around. + void ReattachAll() + { + if constexpr (HasOffset) + { + for (const auto& item : this->Items) + SetExtensionPointer(item->OwnerObject(), item); + } + } + // Deletes every tracked extension and detaches it from its owner. Unlike Clear, // this is regular teardown for owners the game re-initializes in place without // destroying them (cells). From e1fde196898ed9b5995279403b0d5a04cd21ba7c Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 04:08:28 +0300 Subject: [PATCH 33/47] Clear the extension slot when an object is loaded from a savegame --- src/Phobos.Ext.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 27abf7c92f..708a734fc3 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -353,6 +353,19 @@ DEFINE_HOOK(0x67E826, LoadGame_Phobos, 0x6) return 0; } +// AbstractClass::Load restores the object image from the savegame as-is, including the +// save-time extension pointer in the inline slot. Clear the slot right after the image +// is read, so anything fetching an extension during the load window (before the +// post-swizzle relink below) sees "no extension" instead of a stale pointer. +DEFINE_HOOK(0x4103D0, AbstractClass_Load_ClearExtensionSlot, 0x5) +{ + GET(AbstractClass*, pThis, ESI); + + AbstractExt::Attach(pThis, nullptr); + + return 0; +} + // First instruction after SwizzleManagerClass::Process has remapped every registered // pointer: extension owners are valid again, restore the owners' inline ext pointers // (their loaded bytes still hold the stale save-time values). From c1fa6857d84945341e106154ec9699f4a34d6e99 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 16:27:22 +0300 Subject: [PATCH 34/47] Persist cell extensions inline with their cells --- src/Ext/Cell/Body.cpp | 91 +++++++++++++++++++++++++++++++++++---- src/Ext/Cell/Body.h | 11 ++++- src/Utilities/Container.h | 33 -------------- 3 files changed, 93 insertions(+), 42 deletions(-) diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 358407cf90..6a614a8d49 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -56,17 +56,60 @@ bool CellExt::RadLevel::Serialize(T& stm) CellExt::ExtContainer::ExtContainer() : Container("CellClass") { } CellExt::ExtContainer::~ExtContainer() = default; -// The game only saves cells that its cell iterator reaches inside the map array; -// any other live cell is recreated as a default-initialized one on load. Mirror -// that: drop stream extensions whose owner could not be remapped, and allocate -// fresh extensions for cells the load recreated outside the stream. -void CellExt::ExtContainer::RelinkExtensionPointers() +// Writes a cell's extension into the cell's own savegame block, right after the +// game's data. The block is length-prefixed and carries the extension's save-time +// address, so pointers to it could be remapped like any other. +void CellExt::ExtContainer::SaveInline(CellClass* pCell, IStream* pStm) +{ + auto const pExt = CellExt::TryFetch(pCell); + + if (!pExt) + Debug::FatalErrorAndExit("SaveInline - saved cell has no extension!\n"); + + PhobosByteStream saver(sizeof(CellExt)); + PhobosStreamWriter writer(saver); + + writer.Save(CellExt::Canary); + writer.RegisterChange(pExt); + + pExt->SaveToStream(writer); + + if (!saver.WriteBlockToStream(pStm)) + Debug::FatalErrorAndExit("SaveInline - failed to save a cell extension!\n"); +} + +// Recreates a cell's extension from the cell's own savegame block. Unlike the +// centralized stream, the owner is the live cell being loaded, so no owner +// remapping is needed. +void CellExt::ExtContainer::LoadInline(CellClass* pCell, IStream* pStm) { - if (const size_t orphans = this->RemoveNullOwnerItems()) - Debug::Log("CellClass - dropped %u extensions of cells absent from the savegame.\n", orphans); + PhobosByteStream loader(0); - this->Container::RelinkExtensionPointers(); + if (!loader.ReadBlockFromStream(pStm)) + Debug::FatalErrorAndExit("LoadInline - failed to read a cell extension block!\n"); + PhobosStreamReader reader(loader); + void* oldPtr = nullptr; + + if (!reader.Expect(CellExt::Canary) || !reader.Load(oldPtr)) + Debug::FatalErrorAndExit("LoadInline - invalid cell extension block!\n"); + + // the loaded cell carries no extension: its constructor ran with allocation + // suppressed and the loaded image's slot was cleared + auto const pExt = this->AllocateUnchecked(pCell); + PhobosSwizzle::RegisterChange(oldPtr, pExt); + + pExt->LoadFromStream(reader); + + if (!reader.ExpectEndOfBlock()) + Debug::FatalErrorAndExit("LoadInline - cell extension block size mismatch!\n"); +} + +// The game only saves cells that its cell iterator reaches inside the map array; +// any other live cell is recreated as a default placeholder on load, while +// extension allocation is suppressed. Give those cells extensions now. +void CellExt::ExtContainer::RelinkExtensionPointers() +{ size_t added = 0; for (int i = 0; i < MapClass::MaxCells; ++i) @@ -126,6 +169,38 @@ DEFINE_HOOK(0x47BB60, CellClass_DTOR, 0x6) return 0; } +// ============================= +// inline save/load hooks + +static CellClass* PersistCell = nullptr; +static IStream* PersistStream = nullptr; + +DEFINE_HOOK_AGAIN(0x483C10, CellClass_SaveLoad_Prefix, 0x5) // Save +DEFINE_HOOK(0x4839F0, CellClass_SaveLoad_Prefix, 0x7) // Load +{ + GET_STACK(CellClass*, pItem, 0x4); + GET_STACK(IStream*, pStm, 0x8); + + PersistCell = pItem; + PersistStream = pStm; + + return 0; +} + +DEFINE_HOOK(0x483C00, CellClass_Load_Suffix, 0x5) +{ + CellExt::ExtMap.LoadInline(PersistCell, PersistStream); + + return 0; +} + +DEFINE_HOOK(0x483C79, CellClass_Save_Suffix, 0x6) +{ + CellExt::ExtMap.SaveInline(PersistCell, PersistStream); + + return 0; +} + // MapClass::InitCells re-runs the cell constructor in place on every allocated cell // (the AbstractClass constructor zeroes the extension slot in the process); drop the // tracked extensions up front so the constructor hook can allocate fresh ones. diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index f5270b3e8d..a717a7f5ae 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -58,7 +58,16 @@ class CellExt final : public AbstractExt ExtContainer(); ~ExtContainer(); - // cells the game omits from the savegame need special handling on load + // cell extension data is persisted inline within each cell's own savegame + // block instead of the centralized extension stream: the owner is known at + // load time, so no owner remapping is needed + bool SaveAllToStream(IStream*) { return true; } + bool LoadAllFromStream(IStream*) { return true; } + + void SaveInline(CellClass* pCell, IStream* pStm); + void LoadInline(CellClass* pCell, IStream* pStm); + + // cells the game omits from the savegame need extensions allocated on load void RelinkExtensionPointers(); }; diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index a9fd70dbe0..7f93cef4fe 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -456,39 +456,6 @@ class Container return val; } - // deletes every tracked extension whose owner pointer is null and returns how - // many were dropped; used by load-time fixups for owners the game omits from - // the savegame (their owner cannot be remapped by the swizzle manager) - size_t RemoveNullOwnerItems() - { - if constexpr (HasOffset) - { - size_t removed = 0; - auto& vec = this->Items; - - for (size_t i = vec.size(); i-- > 0;) - { - if (!vec[i]->OwnerObject()) - { - delete vec[i]; - vec[i] = vec.back(); - vec.pop_back(); - - if (i < vec.size()) - vec[i]->ContainerIndex = i; - - ++removed; - } - } - - return removed; - } - else - { - return 0; - } - } - public: extension_type_ptr Allocate(base_type_ptr key) { From f6c12cc73c0e7e6d2818e3ed7fba960b508ed0e3 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 16:52:55 +0300 Subject: [PATCH 35/47] Polish comments and documentation for the extension rework --- docs/Project-guidelines-and-policies.md | 4 ++-- src/Phobos.Ext.cpp | 4 ++-- src/Utilities/Container.h | 4 +--- 3 files changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index 0159378fc1..8a2fba931a 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -78,9 +78,9 @@ Assuming you've successfully cloned and built the project before getting here, y - `Ext/` - source code for vanilla engine class extensions. Extension classes form a parallel inheritance hierarchy mirroring the game's own class tree (`AbstractExt` from `Container.h` is the root; e.g. `BuildingExt : TechnoExt : RadioExt : MissionExt : ObjectExt : AbstractExt`), and every game object carries exactly one extension instance of the most derived matching type, cached inside the object at the unified `AbstractClass` `0x18` slot. Each class extension is kept in a separate folder named after vanilla engine class name and contains the following: - `Body.h` and `Body.cpp` contain class and method definitions/declarations and common extension hooks. Each extension class contains the following to work correctly: - new data members and (for appropriate classes) `LoadFromINIFile`/`SaveToStream`/`LoadFromStream` overrides, plus static helper methods; - - `ExtContainer`/`ExtMap` - the per-hierarchy container (inherits `Container` from `Container.h`) that tracks all live instances of the extension family for bulk operations (centralized savegame streaming, post-load relinking, scenario clearing); leaves of a family (e.g. `BuildingExt`) share their family root's container; + - `ExtContainer`/`ExtMap` - the per-class container (inherits `Container` from `Container.h`) that tracks all live instances of one concrete extension class for bulk operations (centralized savegame streaming, post-load relinking, scenario clearing); only concrete leaf classes (e.g. `BuildingExt`) have containers, while every level of the hierarchy provides typed `Fetch`/`TryFetch`; - `Fetch`/`TryFetch` statics - the O(1) lookup used at call sites (`TechnoExt::Fetch(pThis)`); - - constructor/destructor and (for appropriate classes) INI reading hooks. Serialization is centralized in `Phobos.Ext.cpp` - there are no per-class savegame hooks. + - constructor/destructor and (for appropriate classes) INI reading hooks. Serialization is centralized in `Phobos.Ext.cpp` - there are no per-class savegame hooks. The one exception is `CellExt`: the game treats cells as value objects with unstable identity (it re-initializes them in place and copies them around wholesale), so cell extensions are persisted inline within each cell's own savegame block instead (see `Ext/Cell/Body.cpp`). - Extensions subscribe to pointer invalidation by inheriting `Detach::Listener` from `Utilities/Detach.h` and overriding `OnDetach` (see `HouseExt` for an example). - `Hooks.cpp` and `Hooks.*.cpp` contain non-common hooks to correctly patch in new custom logics. - `ExtraHeaders/` - extra header files to interact with / describe types included in game binary that are not included in YRpp yet. diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 708a734fc3..8e50db65fc 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -367,8 +367,8 @@ DEFINE_HOOK(0x4103D0, AbstractClass_Load_ClearExtensionSlot, 0x5) } // First instruction after SwizzleManagerClass::Process has remapped every registered -// pointer: extension owners are valid again, restore the owners' inline ext pointers -// (their loaded bytes still hold the stale save-time values). +// pointer: extension owners are valid again, write the extensions back into their +// owners' inline slots (cleared when the owners were loaded). DEFINE_HOOK(0x67E685, LoadGame_PostSwizzle_Phobos, 0x5) { PhobosTypeRegistry::RelinkExtensions(); diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 7f93cef4fe..4ea85f75d6 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -466,7 +466,6 @@ class Container return this->AllocateUnchecked(key); } - extension_type_ptr TryAllocate(base_type_ptr key, bool bCond, const std::string_view& nMessage) { if (!key || (!bCond && !nMessage.empty())) @@ -718,8 +717,7 @@ class Container } // After the swizzle manager has remapped all pointers, write each extension back - // into its owner's inline slot (the owner's loaded bytes still hold the stale - // save-time value). + // into its owner's inline slot (cleared when the owner was loaded). void RelinkExtensionPointers() { if constexpr (HasOffset) From 471967f42f7fb20365d0d0f0ac92032a2e2f64c0 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 16:55:43 +0300 Subject: [PATCH 36/47] Add changelog and credits entries --- CREDITS.md | 1 + docs/Whats-New.md | 2 ++ 2 files changed, 3 insertions(+) diff --git a/CREDITS.md b/CREDITS.md index f1fda770e9..c605107935 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -586,6 +586,7 @@ This page lists all the individual contributions to the project by their author. - Replace `BLOWFISH.DLL` using Red Alert source code - Adjust the dehardcoding of the 255 `OverlayType` limit to a different format - Voxel drawing invisible sections skip + - Extension system rework - **CrimRecya**: - Fix `LimboKill` not working reliably - Allow using waypoints, area guard and attack move with aircraft diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 47787d405d..89b055fc4d 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -29,6 +29,7 @@ This serves as a changelog for when you just need to drop the new version in wit ### Version TBD (develop branch nightly builds) +- The extension system has been reworked to follow the game's own class model. Savegames made with earlier Phobos builds are incompatible with this version. - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. @@ -385,6 +386,7 @@ HideShakeEffects=false ; boolean :open: #### New: +- Reworked the extension system internals to form a class hierarchy mirroring the game's own, with centralized savegame serialization (by ZivDero) - [Allow using waypoints, area guard and attack move with aircraft](Fixed-or-Improved-Logics.md#extended-aircraft-missions) (by CrimRecya) - [Enhanced Straight trajectory](New-or-Enhanced-Logics.md#straight-trajectory) (by CrimRecya) - [Enable building production queue](User-Interface.md#building-production-queue) (by CrimRecya) From 830f6aed10475b4444b731159e12f6c6025ebbf7 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 16:56:39 +0300 Subject: [PATCH 37/47] Move changelog entries to the end of the lists --- docs/Whats-New.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/Whats-New.md b/docs/Whats-New.md index 89b055fc4d..b817c64b97 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -29,7 +29,6 @@ This serves as a changelog for when you just need to drop the new version in wit ### Version TBD (develop branch nightly builds) -- The extension system has been reworked to follow the game's own class model. Savegames made with earlier Phobos builds are incompatible with this version. - `Splits.TargetCellRange` < 0 now applies special behaviour where the projectile does not consider nearby cells as additional targets if there are not enough techno targets to match `Cluster` count at all. - Combat light customizations introduced a bug that removed vanilla behaviour of ignoring detail level / framerate checks for colored combat light. This bug has been fixed but the previous behaviour can be restored by setting `CombatLightDetailLevel.CheckColored` on Warhead or globally under `[AudioVisual]`. - `[TechnoType] -> WarpAway=` has now been changed to set the animation when units are erased to maintain semantic consistency with `[General] -> WarpAway=`. The animation that was originally controlled by `[TechnoType] -> WarpAway=`, which played instead of `[General] -> WarpOut=` when a Techno is chronowarped by chronosphere, now needs to be specified using `[TechnoType] -> Chronoshift.WarpOut=`, which defaults to the value of `[TechnoType] -> WarpOut=`. @@ -53,6 +52,7 @@ This serves as a changelog for when you just need to drop the new version in wit - `[WarheadType] -> KillWeapon.OnFirer.AffectsHouses` -> `[WarheadType] -> KillWeapon.OnFirer.AffectsHouse` - `[WarheadType/SuperWeaponType] -> Convert(N).AffectedHouses` -> `[WarheadType/SuperWeaponType] -> Convert(N).AffectsHouse` - `[SuperWeaponType] -> LimboKill.Affected` -> `[SuperWeaponType] -> LimboKill.AffectsHouse` +- The extension system has been reworked to follow the game's own class model. Savegames made with earlier Phobos builds are incompatible with this version. ```{note} - If it is detected that you are using the old INI flags, a warning will be outputted to `debug.log`. @@ -386,7 +386,6 @@ HideShakeEffects=false ; boolean :open: #### New: -- Reworked the extension system internals to form a class hierarchy mirroring the game's own, with centralized savegame serialization (by ZivDero) - [Allow using waypoints, area guard and attack move with aircraft](Fixed-or-Improved-Logics.md#extended-aircraft-missions) (by CrimRecya) - [Enhanced Straight trajectory](New-or-Enhanced-Logics.md#straight-trajectory) (by CrimRecya) - [Enable building production queue](User-Interface.md#building-production-queue) (by CrimRecya) @@ -613,6 +612,7 @@ HideShakeEffects=false ; boolean - [`ZAdjust` for Projectiles](Fixed-or-Improved-Logics.md#zadjust-for-projectiles) (by Noble_Fish) - [Adjust recruitable status on team member discharge](AI-Scripting-and-Mapping.md#adjust-recruitable-status-on-team-member-discharge) (by TaranDahl) - Customize whether or not passenger can fire out when the transport is moving (by Ollerus) +- Reworked the extension system internals to form a class hierarchy mirroring the game's own, with centralized savegame serialization (by ZivDero) #### Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) From d1f2524505ea53f53bfa8ba8c71338389bdad75f Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 23:30:51 +0300 Subject: [PATCH 38/47] Add typed checked Fetch/TryFetch accessors, deprecate container lookups --- src/Utilities/Container.h | 97 ++++++++++++++++++++++++++++++++------- 1 file changed, 80 insertions(+), 17 deletions(-) diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 4ea85f75d6..22b31b21ff 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -27,12 +27,17 @@ enum class InitState * Extension classes form an inheritance hierarchy that mirrors the game's own class * tree, rooted at AbstractExt. Every game object of an extended type carries exactly * one extension instance of the most derived matching type, cached inside the object - * at AbstractExt::ExtPointerOffset and fetched through the static Fetch/TryFetch - * accessors of any level of the hierarchy. + * at AbstractExt::ExtPointerOffset. + * + * The static Fetch/TryFetch accessors of any level of the hierarchy are the one + * lookup API: Fetch fatals when the object has no extension attached, TryFetch is + * the null-tolerant form (also for objects that legitimately have none, e.g. during + * the savegame load window). * * Container tracks all live extension instances of one concrete class for the * bulk operations: allocation, removal, centralized savegame streaming, post-load - * relinking and scenario clearing. TX must provide: + * relinking and scenario clearing. Its Find/TryFind lookups are deprecated in favor + * of the accessors above. TX must provide: * using base_type = T; (the extended game class) * static constexpr DWORD Canary = (any dword value easily identifiable in a byte stream) */ @@ -67,6 +72,25 @@ class AbstractExt return pThis ? Fetch(pThis) : nullptr; } + // the typed accessors behind every extension class's own Fetch/TryFetch; + // select them with an explicit template argument + template + static TExt* TryFetch(const AbstractClass* pThis) + { + return static_cast(TryFetch(pThis)); + } + + template + static TExt* Fetch(const AbstractClass* pThis) + { + auto const pExt = TryFetch(pThis); + + if (!pExt) + Debug::FatalErrorAndExit("%s - object %p has no extension attached!\n", typeid(TExt).name(), pThis); + + return pExt; + } + // writes the inline extension slot directly, for extensions that are not // managed by a container (the static working cell) static void Attach(AbstractClass* pThis, AbstractExt* pExt) @@ -159,6 +183,26 @@ class AbstractExt virtual void LoadFromINIFile(CCINIClass* pINI) { } }; +// compatibility stand-in for classes whose single container was split into +// per-leaf containers by the extension rework (TechnoExt, TechnoTypeExt): +// lookup-only, forwards the old ExtMap calls to the Fetch/TryFetch accessors. +// TBase is passed explicitly because TExt is incomplete at the declaration site. +template +struct CompatExtMap +{ + [[deprecated("use the extension class's Fetch instead")]] + auto Find(const TBase* pThis) const + { + return TExt::Fetch(pThis); + } + + [[deprecated("use the extension class's TryFetch instead")]] + auto TryFind(const TBase* pThis) const + { + return TExt::TryFetch(pThis); + } +}; + // legacy standalone base for extensions whose owners are not AbstractClass-derived // (the Rules/Scenario/Sidebar singletons and EBolt); AbstractClass-derived owners use // the AbstractExt hierarchy instead. @@ -429,6 +473,19 @@ class Container (*(uintptr_t*)((char*)key + T::ExtPointerOffset)) = 0; } + extension_type_ptr FindRaw(const_base_type_ptr key) const + { + if constexpr (HasOffset) + return GetExtensionPointer(key); + else + return this->MappedItems.find(key); + } + + extension_type_ptr TryFindRaw(const_base_type_ptr key) const + { + return key ? FindRaw(key) : nullptr; + } + protected: // the unguarded allocation core, also used by load-time fixups that run while // Phobos::IsLoadingSaveGame is still set @@ -488,28 +545,34 @@ class Container return Allocate(key); } - extension_type_ptr TryFind(const_base_type_ptr key) const + // lookups belong to the extension classes' own Fetch/TryFetch accessors; the + // container lookups remain only so pre-rework code keeps compiling. Map-mode + // containers (EBolt) have no accessors and keep using these legitimately. + [[deprecated("use the extension class's TryFetch instead")]] + extension_type_ptr TryFind(const_base_type_ptr key) const requires HasOffset { - if (!key) - return nullptr; + return TryFindRaw(key); + } - if constexpr (HasOffset) - return GetExtensionPointer(key); - else - return this->MappedItems.find(key); + extension_type_ptr TryFind(const_base_type_ptr key) const requires (!HasOffset) + { + return TryFindRaw(key); } - extension_type_ptr Find(const_base_type_ptr key) const + [[deprecated("use the extension class's Fetch instead")]] + extension_type_ptr Find(const_base_type_ptr key) const requires HasOffset { - if constexpr (HasOffset) - return GetExtensionPointer(key); - else - return this->MappedItems.find(key); + return FindRaw(key); + } + + extension_type_ptr Find(const_base_type_ptr key) const requires (!HasOffset) + { + return FindRaw(key); } void Remove(base_type_ptr key) { - if (auto Item = Find(key)) + if (auto Item = FindRaw(key)) { auto& vec = this->Items; @@ -597,7 +660,7 @@ class Container void LoadFromINI(const_base_type_ptr key, CCINIClass* pINI) { - if (auto ptr = this->Find(key)) + if (auto ptr = this->FindRaw(key)) ptr->LoadFromINI(pINI); } From 802bf9e851845cf6391df1bbd90d6c43714fb7b6 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 23:30:58 +0300 Subject: [PATCH 39/47] Route all extension lookups through Fetch/TryFetch, shim TechnoExt/TechnoTypeExt ExtMap --- src/Ext/AbstractType/Body.h | 4 ++-- src/Ext/Aircraft/Body.h | 4 ++-- src/Ext/AircraftType/Body.h | 4 ++-- src/Ext/Anim/Body.h | 4 ++-- src/Ext/AnimType/Body.h | 4 ++-- src/Ext/Building/Body.h | 4 ++-- src/Ext/BuildingType/Body.h | 4 ++-- src/Ext/Bullet/Body.h | 4 ++-- src/Ext/BulletType/Body.h | 4 ++-- src/Ext/Cell/Body.cpp | 5 +---- src/Ext/Cell/Body.h | 4 ++-- src/Ext/Foot/Body.h | 4 ++-- src/Ext/House/Body.h | 4 ++-- src/Ext/HouseType/Body.h | 4 ++-- src/Ext/Infantry/Body.h | 4 ++-- src/Ext/InfantryType/Body.h | 4 ++-- src/Ext/Mission/Body.h | 4 ++-- src/Ext/Object/Body.h | 4 ++-- src/Ext/ObjectType/Body.h | 4 ++-- src/Ext/OverlayType/Body.h | 4 ++-- src/Ext/ParticleSystemType/Body.h | 4 ++-- src/Ext/ParticleType/Body.h | 4 ++-- src/Ext/RadSite/Body.h | 4 ++-- src/Ext/Radio/Body.h | 4 ++-- src/Ext/SWType/Body.h | 4 ++-- src/Ext/Script/Body.h | 4 ++-- src/Ext/Side/Body.h | 4 ++-- src/Ext/TAction/Body.h | 4 ++-- src/Ext/TEvent/Body.h | 4 ++-- src/Ext/Team/Body.h | 4 ++-- src/Ext/TeamType/Body.h | 4 ++-- src/Ext/Techno/Body.h | 7 +++++-- src/Ext/TechnoType/Body.h | 7 +++++-- src/Ext/TerrainType/Body.h | 4 ++-- src/Ext/Tiberium/Body.h | 4 ++-- src/Ext/Unit/Body.h | 4 ++-- src/Ext/UnitType/Body.h | 4 ++-- src/Ext/VoxelAnim/Body.h | 4 ++-- src/Ext/VoxelAnimType/Body.h | 4 ++-- src/Ext/WarheadType/Body.h | 4 ++-- src/Ext/WeaponType/Body.h | 4 ++-- 41 files changed, 87 insertions(+), 84 deletions(-) diff --git a/src/Ext/AbstractType/Body.h b/src/Ext/AbstractType/Body.h index 10f3556cff..0ed581cd02 100644 --- a/src/Ext/AbstractType/Body.h +++ b/src/Ext/AbstractType/Body.h @@ -17,11 +17,11 @@ class AbstractTypeExt : public AbstractExt static AbstractTypeExt* Fetch(const AbstractTypeClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static AbstractTypeExt* TryFetch(const AbstractTypeClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Aircraft/Body.h b/src/Ext/Aircraft/Body.h index 0c12604f89..a66771ba69 100644 --- a/src/Ext/Aircraft/Body.h +++ b/src/Ext/Aircraft/Body.h @@ -35,11 +35,11 @@ class AircraftExt final : public FootExt static AircraftExt* Fetch(const AircraftClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static AircraftExt* TryFetch(const AircraftClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/AircraftType/Body.h b/src/Ext/AircraftType/Body.h index bcce17d0f0..788a03830e 100644 --- a/src/Ext/AircraftType/Body.h +++ b/src/Ext/AircraftType/Body.h @@ -30,11 +30,11 @@ class AircraftTypeExt final : public TechnoTypeExt static AircraftTypeExt* Fetch(const AircraftTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static AircraftTypeExt* TryFetch(const AircraftTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index 94bdc4c691..a428a7ec15 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -95,12 +95,12 @@ class AnimExt final : public ObjectExt static AnimExt* Fetch(const AnimClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static AnimExt* TryFetch(const AnimClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner = false, bool defaultToInvokerOwner = false); diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index ea91ad4bc1..53a90ecaac 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -140,12 +140,12 @@ class AnimTypeExt final : public ObjectTypeExt static AnimTypeExt* Fetch(const AnimTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static AnimTypeExt* TryFetch(const AnimTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static void ProcessDestroyAnims(UnitClass* pThis, HouseClass* pKiller = nullptr); diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index 5d2a8ff60d..b0435e7a7b 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -87,12 +87,12 @@ class BuildingExt final : public TechnoExt, public Detach::Listener(pThis); } static BuildingExt* TryFetch(const BuildingClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/BuildingType/Body.h b/src/Ext/BuildingType/Body.h index 861d91d8f7..480438560f 100644 --- a/src/Ext/BuildingType/Body.h +++ b/src/Ext/BuildingType/Body.h @@ -250,12 +250,12 @@ class BuildingTypeExt final : public TechnoTypeExt static BuildingTypeExt* Fetch(const BuildingTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static BuildingTypeExt* TryFetch(const BuildingTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/Bullet/Body.h b/src/Ext/Bullet/Body.h index f8daf671cd..ecc8b7fb2f 100644 --- a/src/Ext/Bullet/Body.h +++ b/src/Ext/Bullet/Body.h @@ -85,12 +85,12 @@ class BulletExt final : public ObjectExt static BulletExt* Fetch(const BulletClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static BulletExt* TryFetch(const BulletClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static void Detonate(const CoordStruct& coords, TechnoClass* pOwner, int damage, HouseClass* pFiringHouse, AbstractClass* pTarget, bool isBright, WeaponTypeClass* pWeapon, WarheadTypeClass* pWarhead); diff --git a/src/Ext/BulletType/Body.h b/src/Ext/BulletType/Body.h index d55bcf2f3d..70c6a2cc1c 100644 --- a/src/Ext/BulletType/Body.h +++ b/src/Ext/BulletType/Body.h @@ -173,12 +173,12 @@ class BulletTypeExt final : public ObjectTypeExt static BulletTypeExt* Fetch(const BulletTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static BulletTypeExt* TryFetch(const BulletTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static double GetAdjustedGravity(BulletTypeClass* pType); diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 6a614a8d49..ce482c9e46 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -61,10 +61,7 @@ CellExt::ExtContainer::~ExtContainer() = default; // address, so pointers to it could be remapped like any other. void CellExt::ExtContainer::SaveInline(CellClass* pCell, IStream* pStm) { - auto const pExt = CellExt::TryFetch(pCell); - - if (!pExt) - Debug::FatalErrorAndExit("SaveInline - saved cell has no extension!\n"); + auto const pExt = CellExt::Fetch(pCell); PhobosByteStream saver(sizeof(CellExt)); PhobosStreamWriter writer(saver); diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index a717a7f5ae..eaedfa6e63 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -75,12 +75,12 @@ class CellExt final : public AbstractExt static CellExt* Fetch(const CellClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static CellExt* TryFetch(const CellClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Foot/Body.h b/src/Ext/Foot/Body.h index dcd77796d4..bc9f9223bc 100644 --- a/src/Ext/Foot/Body.h +++ b/src/Ext/Foot/Body.h @@ -18,11 +18,11 @@ class FootExt : public TechnoExt static FootExt* Fetch(const FootClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static FootExt* TryFetch(const FootClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/House/Body.h b/src/Ext/House/Body.h index 3607d9ad6d..39715798d3 100644 --- a/src/Ext/House/Body.h +++ b/src/Ext/House/Body.h @@ -152,12 +152,12 @@ class HouseExt final : public AbstractExt, public Detach::Listener(pThis); } static HouseExt* TryFetch(const HouseClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/HouseType/Body.h b/src/Ext/HouseType/Body.h index 9f1515a9b1..6f3c913ebd 100644 --- a/src/Ext/HouseType/Body.h +++ b/src/Ext/HouseType/Body.h @@ -54,12 +54,12 @@ class HouseTypeExt final : public AbstractTypeExt static HouseTypeExt* Fetch(const HouseTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static HouseTypeExt* TryFetch(const HouseTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Infantry/Body.h b/src/Ext/Infantry/Body.h index a2d828d4ab..d4ed7443b9 100644 --- a/src/Ext/Infantry/Body.h +++ b/src/Ext/Infantry/Body.h @@ -30,11 +30,11 @@ class InfantryExt final : public FootExt static InfantryExt* Fetch(const InfantryClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static InfantryExt* TryFetch(const InfantryClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/InfantryType/Body.h b/src/Ext/InfantryType/Body.h index 7b61a15d31..3cca3115da 100644 --- a/src/Ext/InfantryType/Body.h +++ b/src/Ext/InfantryType/Body.h @@ -30,11 +30,11 @@ class InfantryTypeExt final : public TechnoTypeExt static InfantryTypeExt* Fetch(const InfantryTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static InfantryTypeExt* TryFetch(const InfantryTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Mission/Body.h b/src/Ext/Mission/Body.h index a738bf26de..12e2464dfc 100644 --- a/src/Ext/Mission/Body.h +++ b/src/Ext/Mission/Body.h @@ -17,11 +17,11 @@ class MissionExt : public ObjectExt static MissionExt* Fetch(const MissionClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static MissionExt* TryFetch(const MissionClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Object/Body.h b/src/Ext/Object/Body.h index 2c51444162..568162b0a9 100644 --- a/src/Ext/Object/Body.h +++ b/src/Ext/Object/Body.h @@ -19,11 +19,11 @@ class ObjectExt : public AbstractExt static ObjectExt* Fetch(const ObjectClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static ObjectExt* TryFetch(const ObjectClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/ObjectType/Body.h b/src/Ext/ObjectType/Body.h index a056432cc7..aa12ddd0b4 100644 --- a/src/Ext/ObjectType/Body.h +++ b/src/Ext/ObjectType/Body.h @@ -17,11 +17,11 @@ class ObjectTypeExt : public AbstractTypeExt static ObjectTypeExt* Fetch(const ObjectTypeClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static ObjectTypeExt* TryFetch(const ObjectTypeClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/OverlayType/Body.h b/src/Ext/OverlayType/Body.h index c56abe885d..9ad446b005 100644 --- a/src/Ext/OverlayType/Body.h +++ b/src/Ext/OverlayType/Body.h @@ -52,12 +52,12 @@ class OverlayTypeExt final : public ObjectTypeExt static OverlayTypeExt* Fetch(const OverlayTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static OverlayTypeExt* TryFetch(const OverlayTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/ParticleSystemType/Body.h b/src/Ext/ParticleSystemType/Body.h index 7cd5dda79a..006d9f6c4f 100644 --- a/src/Ext/ParticleSystemType/Body.h +++ b/src/Ext/ParticleSystemType/Body.h @@ -48,12 +48,12 @@ class ParticleSystemTypeExt final : public ObjectTypeExt static ParticleSystemTypeExt* Fetch(const ParticleSystemTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static ParticleSystemTypeExt* TryFetch(const ParticleSystemTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/ParticleType/Body.h b/src/Ext/ParticleType/Body.h index 168b819f0a..2ff8b83d32 100644 --- a/src/Ext/ParticleType/Body.h +++ b/src/Ext/ParticleType/Body.h @@ -49,12 +49,12 @@ class ParticleTypeExt final : public ObjectTypeExt static ParticleTypeExt* Fetch(const ParticleTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static ParticleTypeExt* TryFetch(const ParticleTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/RadSite/Body.h b/src/Ext/RadSite/Body.h index 52a82c7c47..f223bb4661 100644 --- a/src/Ext/RadSite/Body.h +++ b/src/Ext/RadSite/Body.h @@ -75,12 +75,12 @@ class RadSiteExt final : public AbstractExt, public Detach::Listener(pThis); } static RadSiteExt* TryFetch(const RadSiteClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Radio/Body.h b/src/Ext/Radio/Body.h index 066867f05d..25c800294d 100644 --- a/src/Ext/Radio/Body.h +++ b/src/Ext/Radio/Body.h @@ -17,11 +17,11 @@ class RadioExt : public MissionExt static RadioExt* Fetch(const RadioClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static RadioExt* TryFetch(const RadioClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/SWType/Body.h b/src/Ext/SWType/Body.h index 33ff94eeed..1d25d25ce0 100644 --- a/src/Ext/SWType/Body.h +++ b/src/Ext/SWType/Body.h @@ -268,12 +268,12 @@ class SWTypeExt final : public AbstractTypeExt static SWTypeExt* Fetch(const SuperWeaponTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static SWTypeExt* TryFetch(const SuperWeaponTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/Script/Body.h b/src/Ext/Script/Body.h index 3e9cb81eb9..c7fbe314fe 100644 --- a/src/Ext/Script/Body.h +++ b/src/Ext/Script/Body.h @@ -184,12 +184,12 @@ class ScriptExt final : public AbstractExt static ScriptExt* Fetch(const ScriptClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static ScriptExt* TryFetch(const ScriptClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static void ProcessAction(TeamClass* pTeam); diff --git a/src/Ext/Side/Body.h b/src/Ext/Side/Body.h index c9d2948adf..0e6b9ce7e6 100644 --- a/src/Ext/Side/Body.h +++ b/src/Ext/Side/Body.h @@ -104,12 +104,12 @@ class SideExt final : public AbstractTypeExt static SideExt* Fetch(const SideClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static SideExt* TryFetch(const SideClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/TAction/Body.h b/src/Ext/TAction/Body.h index 3d13742f5a..fbf83f0344 100644 --- a/src/Ext/TAction/Body.h +++ b/src/Ext/TAction/Body.h @@ -106,12 +106,12 @@ class TActionExt final : public AbstractExt static TActionExt* Fetch(const TActionClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static TActionExt* TryFetch(const TActionClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/TEvent/Body.h b/src/Ext/TEvent/Body.h index 945ab57fd1..d449db5a5f 100644 --- a/src/Ext/TEvent/Body.h +++ b/src/Ext/TEvent/Body.h @@ -115,12 +115,12 @@ class TEventExt final : public AbstractExt static TEventExt* Fetch(const TEventClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static TEventExt* TryFetch(const TEventClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Team/Body.h b/src/Ext/Team/Body.h index ba634aaf5a..4b34306352 100644 --- a/src/Ext/Team/Body.h +++ b/src/Ext/Team/Body.h @@ -72,12 +72,12 @@ class TeamExt final : public AbstractExt, public Detach::Listener static TeamExt* Fetch(const TeamClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static TeamExt* TryFetch(const TeamClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index c149858f47..53d4f44876 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -49,12 +49,12 @@ class TeamTypeExt final : public AbstractTypeExt static TeamTypeExt* Fetch(const TeamTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static TeamTypeExt* TryFetch(const TeamTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a391450909..a62df24366 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -252,14 +252,17 @@ class TechnoExt : public RadioExt, public Detach::Listener // own containers. The polymorphic fetch reads the inline slot directly. static TechnoExt* Fetch(const TechnoClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static TechnoExt* TryFetch(const TechnoClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } + // deprecated stand-in for the pre-rework container of all TechnoClass extensions + static inline CompatExtMap ExtMap {}; + static UnitClass* Deployer; static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 575bd93b90..e592ada926 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -1065,13 +1065,16 @@ class TechnoTypeExt : public ObjectTypeExt // tracked by their own containers. The polymorphic fetch reads the inline slot directly. static TechnoTypeExt* Fetch(const TechnoTypeClass* pThis) { - return static_cast(AbstractExt::Fetch(pThis)); + return AbstractExt::Fetch(pThis); } static TechnoTypeExt* TryFetch(const TechnoTypeClass* pThis) { - return pThis ? Fetch(pThis) : nullptr; + return AbstractExt::TryFetch(pThis); } + + // deprecated stand-in for the pre-rework container of all TechnoTypeClass extensions + static inline CompatExtMap ExtMap {}; static bool SelectWeaponMutex; static void ApplyTurretOffset(TechnoTypeClass* pType, Matrix3D* mtx, double factor = 1.0, int turIdx = -1); diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index 7070f74cc9..e0dc01b4c0 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -83,12 +83,12 @@ class TerrainTypeExt final : public ObjectTypeExt static TerrainTypeExt* Fetch(const TerrainTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static TerrainTypeExt* TryFetch(const TerrainTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index 69aac4a6b8..ac22b5d72d 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -48,12 +48,12 @@ class TiberiumExt final : public AbstractTypeExt static TiberiumExt* Fetch(const TiberiumClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static TiberiumExt* TryFetch(const TiberiumClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/Unit/Body.h b/src/Ext/Unit/Body.h index 6f2b93429f..c2b4b13a0d 100644 --- a/src/Ext/Unit/Body.h +++ b/src/Ext/Unit/Body.h @@ -32,11 +32,11 @@ class UnitExt final : public FootExt static UnitExt* Fetch(const UnitClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static UnitExt* TryFetch(const UnitClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/UnitType/Body.h b/src/Ext/UnitType/Body.h index 51ba2f27cb..ab941dc646 100644 --- a/src/Ext/UnitType/Body.h +++ b/src/Ext/UnitType/Body.h @@ -30,11 +30,11 @@ class UnitTypeExt final : public TechnoTypeExt static UnitTypeExt* Fetch(const UnitTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static UnitTypeExt* TryFetch(const UnitTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } }; diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index 3a1392641b..63686a010e 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -50,12 +50,12 @@ class VoxelAnimExt final : public ObjectExt static VoxelAnimExt* Fetch(const VoxelAnimClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static VoxelAnimExt* TryFetch(const VoxelAnimClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static void InitializeLaserTrails(VoxelAnimClass* pThis); static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index 317f136fcd..a01c02bbc5 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -63,12 +63,12 @@ class VoxelAnimTypeExt final : public ObjectTypeExt static VoxelAnimTypeExt* Fetch(const VoxelAnimTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static VoxelAnimTypeExt* TryFetch(const VoxelAnimTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index 618ab41d6a..a2fb9bbbf5 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -594,12 +594,12 @@ class WarheadTypeExt final : public AbstractTypeExt static WarheadTypeExt* Fetch(const WarheadTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static WarheadTypeExt* TryFetch(const WarheadTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 351bafbc5d..9a83f57d25 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -232,12 +232,12 @@ class WeaponTypeExt final : public AbstractTypeExt static WeaponTypeExt* Fetch(const WeaponTypeClass* pThis) { - return ExtMap.Find(pThis); + return AbstractExt::Fetch(pThis); } static WeaponTypeExt* TryFetch(const WeaponTypeClass* pThis) { - return ExtMap.TryFind(pThis); + return AbstractExt::TryFetch(pThis); } static bool LoadGlobals(PhobosStreamReader& Stm); From 48af0a3c18aaafeeff3c0a774d1de4b4e17f2b91 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 17 Jul 2026 23:31:05 +0300 Subject: [PATCH 40/47] Document the extension lookup API --- .github/copilot-instructions.md | 4 +++- docs/Project-guidelines-and-policies.md | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 40e8c55910..ab46e3b885 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -92,10 +92,12 @@ scripts/ Build and setup scripts ### Extending a vanilla game class Each extension lives in `src/Ext//` with: -- **`Body.h`**: Declares `Ext` with `ExtData` (inherits `Extension`) and `ExtContainer`/`ExtMap`. +- **`Body.h`**: Declares `Ext`, part of an inheritance hierarchy mirroring the game's own class tree rooted at `AbstractExt` (`Container.h`); concrete leaf classes also declare an `ExtContainer`/`ExtMap` (inherits `Container`). - **`Body.cpp`**: Implements constructor, `LoadFromINIFile`, serialization (`Serialize`), and common hooks. - **`Hooks.cpp` / `Hooks.*.cpp`**: `DEFINE_HOOK(address, Name, size)` macros for Syringe code injection. +Look extensions up only through the typed static accessors available at every level of the hierarchy: `Ext::Fetch(pThis)` (fatals if the object has no extension attached) or `Ext::TryFetch(pThis)` (returns null instead — also the right choice while a savegame is loading). The container lookups `ExtMap.Find`/`ExtMap.TryFind` are deprecated compatibility forwards; `ExtMap`'s remaining role is lifecycle and persistence machinery (`Allocate`/`Remove`/`LoadFromINI` plus the centralized streaming driven from `Phobos.Ext.cpp`). + After creating a new extension class, **always register it** in `src/Phobos.Ext.cpp` inside the `PhobosTypeRegistry` alias (the `using PhobosTypeRegistry = TypeRegistry<...>` declaration). ### Adding a new enumerable type diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index 8a2fba931a..4c3ad0a554 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -78,8 +78,8 @@ Assuming you've successfully cloned and built the project before getting here, y - `Ext/` - source code for vanilla engine class extensions. Extension classes form a parallel inheritance hierarchy mirroring the game's own class tree (`AbstractExt` from `Container.h` is the root; e.g. `BuildingExt : TechnoExt : RadioExt : MissionExt : ObjectExt : AbstractExt`), and every game object carries exactly one extension instance of the most derived matching type, cached inside the object at the unified `AbstractClass` `0x18` slot. Each class extension is kept in a separate folder named after vanilla engine class name and contains the following: - `Body.h` and `Body.cpp` contain class and method definitions/declarations and common extension hooks. Each extension class contains the following to work correctly: - new data members and (for appropriate classes) `LoadFromINIFile`/`SaveToStream`/`LoadFromStream` overrides, plus static helper methods; - - `ExtContainer`/`ExtMap` - the per-class container (inherits `Container` from `Container.h`) that tracks all live instances of one concrete extension class for bulk operations (centralized savegame streaming, post-load relinking, scenario clearing); only concrete leaf classes (e.g. `BuildingExt`) have containers, while every level of the hierarchy provides typed `Fetch`/`TryFetch`; - - `Fetch`/`TryFetch` statics - the O(1) lookup used at call sites (`TechnoExt::Fetch(pThis)`); + - `ExtContainer`/`ExtMap` - the per-class container (inherits `Container` from `Container.h`) that tracks all live instances of one concrete extension class for bulk operations (allocation/removal, centralized savegame streaming, post-load relinking, scenario clearing); only concrete leaf classes (e.g. `BuildingExt`) have containers, while every level of the hierarchy provides typed `Fetch`/`TryFetch`. The container's own `Find`/`TryFind` lookups are deprecated compatibility forwards (`TechnoExt`/`TechnoTypeExt`, whose containers were split into per-leaf ones, keep a lookup-only `ExtMap` stand-in for the same reason); + - `Fetch`/`TryFetch` statics - the O(1) lookup used at call sites (`TechnoExt::Fetch(pThis)`); `Fetch` fatals if the object carries no extension, `TryFetch` returns null instead (also the right choice while a savegame is loading); - constructor/destructor and (for appropriate classes) INI reading hooks. Serialization is centralized in `Phobos.Ext.cpp` - there are no per-class savegame hooks. The one exception is `CellExt`: the game treats cells as value objects with unstable identity (it re-initializes them in place and copies them around wholesale), so cell extensions are persisted inline within each cell's own savegame block instead (see `Ext/Cell/Body.cpp`). - Extensions subscribe to pointer invalidation by inheriting `Detach::Listener` from `Utilities/Detach.h` and overriding `OnDetach` (see `HouseExt` for an example). - `Hooks.cpp` and `Hooks.*.cpp` contain non-common hooks to correctly patch in new custom logics. From cfb95b89c736e3294a51a40f2bc73b9e8fdfa329 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 16:10:16 +0300 Subject: [PATCH 41/47] Move container hooks to the end of extension Body.cpp files --- src/Ext/Aircraft/Body.cpp | 24 ++++++------ src/Ext/AnimType/Body.cpp | 64 +++++++++++++++++-------------- src/Ext/House/Body.cpp | 79 ++++++++++++++++++++------------------- src/Ext/Script/Body.cpp | 16 ++++---- src/Ext/TAction/Body.cpp | 3 -- src/Ext/TEvent/Body.cpp | 3 -- 6 files changed, 95 insertions(+), 94 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index bad2080d6d..a43a27b6fa 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -1,21 +1,12 @@ #include "Body.h" -AircraftExt::ExtContainer AircraftExt::ExtMap; - -AircraftExt::ExtContainer::ExtContainer() : Container("AircraftClass") { } -AircraftExt::ExtContainer::~ExtContainer() = default; - #include #include -DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) -{ - GET(AircraftClass*, pItem, ESI); - - AircraftExt::ExtMap.Allocate(pItem); +AircraftExt::ExtContainer AircraftExt::ExtMap; - return 0; -} +AircraftExt::ExtContainer::ExtContainer() : Container("AircraftClass") { } +AircraftExt::ExtContainer::~ExtContainer() = default; // TODO: Implement proper extended AircraftClass. @@ -181,6 +172,15 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) return static_cast(std::clamp(landingDir, 0, 255)); } +DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) +{ + GET(AircraftClass*, pItem, ESI); + + AircraftExt::ExtMap.Allocate(pItem); + + return 0; +} + // Late in every destructor body of the class, right before it chains into the // base destructor: the last point where the extension is no longer used. DEFINE_HOOK_AGAIN(0x41426D, AircraftClass_DTOR, 0x9) diff --git a/src/Ext/AnimType/Body.cpp b/src/Ext/AnimType/Body.cpp index 7d717ce922..ae0206aa49 100644 --- a/src/Ext/AnimType/Body.cpp +++ b/src/Ext/AnimType/Body.cpp @@ -198,35 +198,6 @@ void AnimTypeExt::SaveToStream(PhobosStreamWriter& Stm) this->Serialize(Stm); } -AnimTypeExt::ExtContainer::ExtContainer() : Container("AnimTypeClass") { } -AnimTypeExt::ExtContainer::~ExtContainer() = default; - -DEFINE_HOOK(0x42784B, AnimTypeClass_CTOR, 0x5) -{ - GET(AnimTypeClass*, pItem, EAX); - - AnimTypeExt::ExtMap.TryAllocate(pItem); - return 0; -} - -DEFINE_HOOK(0x428EA8, AnimTypeClass_SDDTOR, 0x5) -{ - GET(AnimTypeClass*, pItem, ECX); - - AnimTypeExt::ExtMap.Remove(pItem); - return 0; -} - -//DEFINE_HOOK_AGAIN(0x4287E9, AnimTypeClass_LoadFromINI, 0xA)// Section dont exist! -DEFINE_HOOK(0x4287DC, AnimTypeClass_LoadFromINI, 0xA) -{ - GET(AnimTypeClass*, pItem, ESI); - GET_STACK(CCINIClass*, pINI, 0xBC); - - AnimTypeExt::ExtMap.LoadFromINI(pItem, pINI); - return 0; -} - namespace detail { template <> @@ -257,3 +228,38 @@ namespace detail return false; } } + +// ============================= +// container + +AnimTypeExt::ExtContainer::ExtContainer() : Container("AnimTypeClass") { } +AnimTypeExt::ExtContainer::~ExtContainer() = default; + +// ============================= +// container hooks + +DEFINE_HOOK(0x42784B, AnimTypeClass_CTOR, 0x5) +{ + GET(AnimTypeClass*, pItem, EAX); + + AnimTypeExt::ExtMap.TryAllocate(pItem); + return 0; +} + +DEFINE_HOOK(0x428EA8, AnimTypeClass_SDDTOR, 0x5) +{ + GET(AnimTypeClass*, pItem, ECX); + + AnimTypeExt::ExtMap.Remove(pItem); + return 0; +} + +//DEFINE_HOOK_AGAIN(0x4287E9, AnimTypeClass_LoadFromINI, 0xA)// Section dont exist! +DEFINE_HOOK(0x4287DC, AnimTypeClass_LoadFromINI, 0xA) +{ + GET(AnimTypeClass*, pItem, ESI); + GET_STACK(CCINIClass*, pINI, 0xBC); + + AnimTypeExt::ExtMap.LoadFromINI(pItem, pINI); + return 0; +} diff --git a/src/Ext/House/Body.cpp b/src/Ext/House/Body.cpp index 893c657b8b..939a5476e0 100644 --- a/src/Ext/House/Body.cpp +++ b/src/Ext/House/Body.cpp @@ -770,45 +770,6 @@ void HouseExt::OnDetach(BuildingClass* pTarget, bool removed) } } -// ============================= -// container - -HouseExt::ExtContainer::ExtContainer() : Container("HouseClass") -{ } - -HouseExt::ExtContainer::~ExtContainer() = default; - -// ============================= -// container hooks - -DEFINE_HOOK(0x4F6532, HouseClass_CTOR, 0x5) -{ - GET(HouseClass*, pItem, EAX); - - HouseExt::ExtMap.TryAllocate(pItem); - HouseExt::CalculatePowerSurplus(pItem); - - return 0; -} - -DEFINE_HOOK(0x4F7371, HouseClass_DTOR, 0x6) -{ - GET(HouseClass*, pItem, ESI); - - HouseExt::ExtMap.Remove(pItem); - - return 0; -} - -DEFINE_HOOK(0x50114D, HouseClass_InitFromINI, 0x5) -{ - GET(HouseClass* const, pThis, EBX); - GET(CCINIClass* const, pINI, ESI); - - HouseExt::ExtMap.LoadFromINI(pThis, pINI); - - return 0; -} #pragma region BuildLimitGroup static int CountOwnedIncludeDeploy(const HouseClass* pThis, const TechnoTypeClass* pItem) { @@ -1139,3 +1100,43 @@ bool HouseExt::ReachedBuildLimit(const HouseClass* pHouse, const TechnoTypeClass return false; } #pragma endregion + +// ============================= +// container + +HouseExt::ExtContainer::ExtContainer() : Container("HouseClass") +{ } + +HouseExt::ExtContainer::~ExtContainer() = default; + +// ============================= +// container hooks + +DEFINE_HOOK(0x4F6532, HouseClass_CTOR, 0x5) +{ + GET(HouseClass*, pItem, EAX); + + HouseExt::ExtMap.TryAllocate(pItem); + HouseExt::CalculatePowerSurplus(pItem); + + return 0; +} + +DEFINE_HOOK(0x4F7371, HouseClass_DTOR, 0x6) +{ + GET(HouseClass*, pItem, ESI); + + HouseExt::ExtMap.Remove(pItem); + + return 0; +} + +DEFINE_HOOK(0x50114D, HouseClass_InitFromINI, 0x5) +{ + GET(HouseClass* const, pThis, EBX); + GET(CCINIClass* const, pINI, ESI); + + HouseExt::ExtMap.LoadFromINI(pThis, pINI); + + return 0; +} diff --git a/src/Ext/Script/Body.cpp b/src/Ext/Script/Body.cpp index b5022b42ed..47d5efc069 100644 --- a/src/Ext/Script/Body.cpp +++ b/src/Ext/Script/Body.cpp @@ -17,14 +17,6 @@ void ScriptExt::SaveToStream(PhobosStreamWriter& Stm) // Nothing yet } -// ============================= -// container - -ScriptExt::ExtContainer::ExtContainer() : Container("ScriptClass") -{ } - -ScriptExt::ExtContainer::~ExtContainer() = default; - void ScriptExt::ProcessAction(TeamClass* pTeam) { const int action = pTeam->CurrentScript->Type->ScriptActions[pTeam->CurrentScript->CurrentMission].Action; @@ -1259,3 +1251,11 @@ void ScriptExt::Log(const char* pFormat, ...) Debug::LogWithVArgs(pFormat, args); va_end(args); } + +// ============================= +// container + +ScriptExt::ExtContainer::ExtContainer() : Container("ScriptClass") +{ } + +ScriptExt::ExtContainer::~ExtContainer() = default; diff --git a/src/Ext/TAction/Body.cpp b/src/Ext/TAction/Body.cpp index 87b825137b..41ff5504ea 100644 --- a/src/Ext/TAction/Body.cpp +++ b/src/Ext/TAction/Body.cpp @@ -783,6 +783,3 @@ TActionExt::ExtContainer::ExtContainer() : Container("TActionClass") { } TActionExt::ExtContainer::~ExtContainer() = default; -// ============================= -// container hooks - diff --git a/src/Ext/TEvent/Body.cpp b/src/Ext/TEvent/Body.cpp index 80f4033c84..c7cd3cee94 100644 --- a/src/Ext/TEvent/Body.cpp +++ b/src/Ext/TEvent/Body.cpp @@ -346,6 +346,3 @@ TEventExt::ExtContainer::ExtContainer() : Container("TEventClass") { } TEventExt::ExtContainer::~ExtContainer() = default; -// ============================= -// container hooks - From f4d16952a8884e33e1cd0936cc27665872f319f9 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 16:12:42 +0300 Subject: [PATCH 42/47] Standardize container sections in the new leaf extension files --- src/Ext/Aircraft/Body.cpp | 12 +++++++++--- src/Ext/AircraftType/Body.cpp | 6 ++++++ src/Ext/Infantry/Body.cpp | 6 ++++++ src/Ext/InfantryType/Body.cpp | 6 ++++++ src/Ext/Unit/Body.cpp | 6 ++++++ src/Ext/UnitType/Body.cpp | 6 ++++++ 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index a43a27b6fa..d6f05d633f 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -5,9 +5,6 @@ AircraftExt::ExtContainer AircraftExt::ExtMap; -AircraftExt::ExtContainer::ExtContainer() : Container("AircraftClass") { } -AircraftExt::ExtContainer::~ExtContainer() = default; - // TODO: Implement proper extended AircraftClass. void AircraftExt::FireWeapon(AircraftClass* pThis, AbstractClass* pTarget) @@ -172,6 +169,15 @@ DirType AircraftExt::GetLandingDir(AircraftClass* pThis, BuildingClass* pDock) return static_cast(std::clamp(landingDir, 0, 255)); } +// ============================= +// container + +AircraftExt::ExtContainer::ExtContainer() : Container("AircraftClass") { } +AircraftExt::ExtContainer::~ExtContainer() = default; + +// ============================= +// container hooks + DEFINE_HOOK(0x413D30, AircraftClass_CTOR, 0x7) { GET(AircraftClass*, pItem, ESI); diff --git a/src/Ext/AircraftType/Body.cpp b/src/Ext/AircraftType/Body.cpp index f03dd02b99..6b7e64eff6 100644 --- a/src/Ext/AircraftType/Body.cpp +++ b/src/Ext/AircraftType/Body.cpp @@ -2,9 +2,15 @@ AircraftTypeExt::ExtContainer AircraftTypeExt::ExtMap; +// ============================= +// container + AircraftTypeExt::ExtContainer::ExtContainer() : Container("AircraftTypeClass") { } AircraftTypeExt::ExtContainer::~ExtContainer() = default; +// ============================= +// container hooks + DEFINE_HOOK(0x41C8C0, AircraftTypeClass_CTOR, 0x5) { GET(AircraftTypeClass*, pItem, ESI); diff --git a/src/Ext/Infantry/Body.cpp b/src/Ext/Infantry/Body.cpp index 9059dbaa74..acd969ae3b 100644 --- a/src/Ext/Infantry/Body.cpp +++ b/src/Ext/Infantry/Body.cpp @@ -2,9 +2,15 @@ InfantryExt::ExtContainer InfantryExt::ExtMap; +// ============================= +// container + InfantryExt::ExtContainer::ExtContainer() : Container("InfantryClass") { } InfantryExt::ExtContainer::~ExtContainer() = default; +// ============================= +// container hooks + DEFINE_HOOK(0x517A60, InfantryClass_CTOR, 0xE) { GET(InfantryClass*, pItem, ESI); diff --git a/src/Ext/InfantryType/Body.cpp b/src/Ext/InfantryType/Body.cpp index c51996d53f..3c47864418 100644 --- a/src/Ext/InfantryType/Body.cpp +++ b/src/Ext/InfantryType/Body.cpp @@ -2,9 +2,15 @@ InfantryTypeExt::ExtContainer InfantryTypeExt::ExtMap; +// ============================= +// container + InfantryTypeExt::ExtContainer::ExtContainer() : Container("InfantryTypeClass") { } InfantryTypeExt::ExtContainer::~ExtContainer() = default; +// ============================= +// container hooks + DEFINE_HOOK(0x5236B3, InfantryTypeClass_CTOR, 0xA) { GET(InfantryTypeClass*, pItem, ESI); diff --git a/src/Ext/Unit/Body.cpp b/src/Ext/Unit/Body.cpp index 997640f5a0..9c6e61ed02 100644 --- a/src/Ext/Unit/Body.cpp +++ b/src/Ext/Unit/Body.cpp @@ -2,9 +2,15 @@ UnitExt::ExtContainer UnitExt::ExtMap; +// ============================= +// container + UnitExt::ExtContainer::ExtContainer() : Container("UnitClass") { } UnitExt::ExtContainer::~ExtContainer() = default; +// ============================= +// container hooks + DEFINE_HOOK(0x7353D3, UnitClass_CTOR, 0x7) { GET(UnitClass*, pItem, ESI); diff --git a/src/Ext/UnitType/Body.cpp b/src/Ext/UnitType/Body.cpp index 872f3b0cd1..a2bb0ae04d 100644 --- a/src/Ext/UnitType/Body.cpp +++ b/src/Ext/UnitType/Body.cpp @@ -2,9 +2,15 @@ UnitTypeExt::ExtContainer UnitTypeExt::ExtMap; +// ============================= +// container + UnitTypeExt::ExtContainer::ExtContainer() : Container("UnitTypeClass") { } UnitTypeExt::ExtContainer::~ExtContainer() = default; +// ============================= +// container hooks + DEFINE_HOOK(0x7470E3, UnitTypeClass_CTOR, 0x6) { GET(UnitTypeClass*, pItem, ESI); From 57d93d30611acc8f4ae9394a89680a18daede5ca Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 16:27:28 +0300 Subject: [PATCH 43/47] Remove old TODO --- src/Ext/Aircraft/Body.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Ext/Aircraft/Body.cpp b/src/Ext/Aircraft/Body.cpp index d6f05d633f..0973ce92ac 100644 --- a/src/Ext/Aircraft/Body.cpp +++ b/src/Ext/Aircraft/Body.cpp @@ -5,8 +5,6 @@ AircraftExt::ExtContainer AircraftExt::ExtMap; -// TODO: Implement proper extended AircraftClass. - void AircraftExt::FireWeapon(AircraftClass* pThis, AbstractClass* pTarget) { auto const pExt = TechnoExt::Fetch(pThis); From e691df7152c3cd3dabe03effc82cf61abba31828 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 16:43:57 +0300 Subject: [PATCH 44/47] Add deprecated ExtData self-aliases for source compatibility --- .github/copilot-instructions.md | 2 +- docs/Project-guidelines-and-policies.md | 2 +- src/Ext/Anim/Body.h | 3 +++ src/Ext/AnimType/Body.h | 3 +++ src/Ext/Building/Body.h | 3 +++ src/Ext/BuildingType/Body.h | 3 +++ src/Ext/Bullet/Body.h | 3 +++ src/Ext/BulletType/Body.h | 3 +++ src/Ext/Cell/Body.h | 3 +++ src/Ext/House/Body.h | 3 +++ src/Ext/HouseType/Body.h | 3 +++ src/Ext/OverlayType/Body.h | 3 +++ src/Ext/ParticleSystemType/Body.h | 3 +++ src/Ext/ParticleType/Body.h | 3 +++ src/Ext/RadSite/Body.h | 3 +++ src/Ext/SWType/Body.h | 3 +++ src/Ext/Script/Body.h | 3 +++ src/Ext/Side/Body.h | 3 +++ src/Ext/TAction/Body.h | 3 +++ src/Ext/TEvent/Body.h | 3 +++ src/Ext/Team/Body.h | 3 +++ src/Ext/TeamType/Body.h | 3 +++ src/Ext/Techno/Body.h | 3 +++ src/Ext/TechnoType/Body.h | 3 +++ src/Ext/TerrainType/Body.h | 3 +++ src/Ext/Tiberium/Body.h | 3 +++ src/Ext/VoxelAnim/Body.h | 3 +++ src/Ext/VoxelAnimType/Body.h | 3 +++ src/Ext/WarheadType/Body.h | 3 +++ src/Ext/WeaponType/Body.h | 3 +++ src/Utilities/Container.h | 6 +++++- 31 files changed, 91 insertions(+), 3 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index ab46e3b885..0b0be3a3ad 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -96,7 +96,7 @@ Each extension lives in `src/Ext//` with: - **`Body.cpp`**: Implements constructor, `LoadFromINIFile`, serialization (`Serialize`), and common hooks. - **`Hooks.cpp` / `Hooks.*.cpp`**: `DEFINE_HOOK(address, Name, size)` macros for Syringe code injection. -Look extensions up only through the typed static accessors available at every level of the hierarchy: `Ext::Fetch(pThis)` (fatals if the object has no extension attached) or `Ext::TryFetch(pThis)` (returns null instead — also the right choice while a savegame is loading). The container lookups `ExtMap.Find`/`ExtMap.TryFind` are deprecated compatibility forwards; `ExtMap`'s remaining role is lifecycle and persistence machinery (`Allocate`/`Remove`/`LoadFromINI` plus the centralized streaming driven from `Phobos.Ext.cpp`). +Look extensions up only through the typed static accessors available at every level of the hierarchy: `Ext::Fetch(pThis)` (fatals if the object has no extension attached) or `Ext::TryFetch(pThis)` (returns null instead — also the right choice while a savegame is loading). The container lookups `ExtMap.Find`/`ExtMap.TryFind` are deprecated compatibility forwards, as is each pre-rework class's `ExtData` alias (the extension class itself replaced the old nested data class); `ExtMap`'s remaining role is lifecycle and persistence machinery (`Allocate`/`Remove`/`LoadFromINI` plus the centralized streaming driven from `Phobos.Ext.cpp`). After creating a new extension class, **always register it** in `src/Phobos.Ext.cpp` inside the `PhobosTypeRegistry` alias (the `using PhobosTypeRegistry = TypeRegistry<...>` declaration). diff --git a/docs/Project-guidelines-and-policies.md b/docs/Project-guidelines-and-policies.md index 4c3ad0a554..687cf288b4 100644 --- a/docs/Project-guidelines-and-policies.md +++ b/docs/Project-guidelines-and-policies.md @@ -78,7 +78,7 @@ Assuming you've successfully cloned and built the project before getting here, y - `Ext/` - source code for vanilla engine class extensions. Extension classes form a parallel inheritance hierarchy mirroring the game's own class tree (`AbstractExt` from `Container.h` is the root; e.g. `BuildingExt : TechnoExt : RadioExt : MissionExt : ObjectExt : AbstractExt`), and every game object carries exactly one extension instance of the most derived matching type, cached inside the object at the unified `AbstractClass` `0x18` slot. Each class extension is kept in a separate folder named after vanilla engine class name and contains the following: - `Body.h` and `Body.cpp` contain class and method definitions/declarations and common extension hooks. Each extension class contains the following to work correctly: - new data members and (for appropriate classes) `LoadFromINIFile`/`SaveToStream`/`LoadFromStream` overrides, plus static helper methods; - - `ExtContainer`/`ExtMap` - the per-class container (inherits `Container` from `Container.h`) that tracks all live instances of one concrete extension class for bulk operations (allocation/removal, centralized savegame streaming, post-load relinking, scenario clearing); only concrete leaf classes (e.g. `BuildingExt`) have containers, while every level of the hierarchy provides typed `Fetch`/`TryFetch`. The container's own `Find`/`TryFind` lookups are deprecated compatibility forwards (`TechnoExt`/`TechnoTypeExt`, whose containers were split into per-leaf ones, keep a lookup-only `ExtMap` stand-in for the same reason); + - `ExtContainer`/`ExtMap` - the per-class container (inherits `Container` from `Container.h`) that tracks all live instances of one concrete extension class for bulk operations (allocation/removal, centralized savegame streaming, post-load relinking, scenario clearing); only concrete leaf classes (e.g. `BuildingExt`) have containers, while every level of the hierarchy provides typed `Fetch`/`TryFetch`. The container's own `Find`/`TryFind` lookups are deprecated compatibility forwards (`TechnoExt`/`TechnoTypeExt`, whose containers were split into per-leaf ones, keep a lookup-only `ExtMap` stand-in for the same reason), and every pre-rework class carries a deprecated `ExtData` alias of itself so code written against the old nested data classes keeps compiling; - `Fetch`/`TryFetch` statics - the O(1) lookup used at call sites (`TechnoExt::Fetch(pThis)`); `Fetch` fatals if the object carries no extension, `TryFetch` returns null instead (also the right choice while a savegame is loading); - constructor/destructor and (for appropriate classes) INI reading hooks. Serialization is centralized in `Phobos.Ext.cpp` - there are no per-class savegame hooks. The one exception is `CellExt`: the game treats cells as value objects with unstable identity (it re-initializes them in place and copies them around wholesale), so cell extensions are persisted inline within each cell's own savegame block instead (see `Ext/Cell/Body.cpp`). - Extensions subscribe to pointer invalidation by inheriting `Detach::Listener` from `Utilities/Detach.h` and overriding `OnDetach` (see `HouseExt` for an example). diff --git a/src/Ext/Anim/Body.h b/src/Ext/Anim/Body.h index a428a7ec15..2142d95386 100644 --- a/src/Ext/Anim/Body.h +++ b/src/Ext/Anim/Body.h @@ -9,6 +9,9 @@ class AnimExt final : public ObjectExt public: using base_type = AnimClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = AnimExt; + static constexpr DWORD Canary = 0xAAAAAAAA; public: diff --git a/src/Ext/AnimType/Body.h b/src/Ext/AnimType/Body.h index 53a90ecaac..245ca97617 100644 --- a/src/Ext/AnimType/Body.h +++ b/src/Ext/AnimType/Body.h @@ -20,6 +20,9 @@ class AnimTypeExt final : public ObjectTypeExt public: using base_type = AnimTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = AnimTypeExt; + static constexpr DWORD Canary = 0xEEEEEEEE; public: diff --git a/src/Ext/Building/Body.h b/src/Ext/Building/Body.h index b0435e7a7b..72a168f6c8 100644 --- a/src/Ext/Building/Body.h +++ b/src/Ext/Building/Body.h @@ -7,6 +7,9 @@ class BuildingExt final : public TechnoExt, public Detach::Listener public: using base_type = TeamClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = TeamExt; + static constexpr DWORD Canary = 0x414B4B41; public: diff --git a/src/Ext/TeamType/Body.h b/src/Ext/TeamType/Body.h index 53d4f44876..9cddc1f2dd 100644 --- a/src/Ext/TeamType/Body.h +++ b/src/Ext/TeamType/Body.h @@ -10,6 +10,9 @@ class TeamTypeExt final : public AbstractTypeExt public: using base_type = TeamTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = TeamTypeExt; + static constexpr DWORD Canary = 0xABCDEF01; public: diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 9458dd90a5..67db70e917 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -17,6 +17,9 @@ class TechnoExt : public RadioExt, public Detach::Listener public: using base_type = TechnoClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = TechnoExt; + static constexpr DWORD Canary = 0x55555555; public: diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index a5a5e09eb9..0ac25498df 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -21,6 +21,9 @@ class TechnoTypeExt : public ObjectTypeExt public: using base_type = TechnoTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = TechnoTypeExt; + static constexpr DWORD Canary = 0x11111111; public: diff --git a/src/Ext/TerrainType/Body.h b/src/Ext/TerrainType/Body.h index e0dc01b4c0..e6f06cbc8c 100644 --- a/src/Ext/TerrainType/Body.h +++ b/src/Ext/TerrainType/Body.h @@ -10,6 +10,9 @@ class TerrainTypeExt final : public ObjectTypeExt public: using base_type = TerrainTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = TerrainTypeExt; + static constexpr DWORD Canary = 0xBEE78007; public: diff --git a/src/Ext/Tiberium/Body.h b/src/Ext/Tiberium/Body.h index ac22b5d72d..4db057f521 100644 --- a/src/Ext/Tiberium/Body.h +++ b/src/Ext/Tiberium/Body.h @@ -10,6 +10,9 @@ class TiberiumExt final : public AbstractTypeExt public: using base_type = TiberiumClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = TiberiumExt; + static constexpr DWORD Canary = 0xAABBCCDD; public: diff --git a/src/Ext/VoxelAnim/Body.h b/src/Ext/VoxelAnim/Body.h index 63686a010e..110fdcf5b6 100644 --- a/src/Ext/VoxelAnim/Body.h +++ b/src/Ext/VoxelAnim/Body.h @@ -11,6 +11,9 @@ class VoxelAnimExt final : public ObjectExt public: using base_type = VoxelAnimClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = VoxelAnimExt; + static constexpr DWORD Canary = 0xAAAAAACC; public: diff --git a/src/Ext/VoxelAnimType/Body.h b/src/Ext/VoxelAnimType/Body.h index a01c02bbc5..113fc1133b 100644 --- a/src/Ext/VoxelAnimType/Body.h +++ b/src/Ext/VoxelAnimType/Body.h @@ -12,6 +12,9 @@ class VoxelAnimTypeExt final : public ObjectTypeExt public: using base_type = VoxelAnimTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = VoxelAnimTypeExt; + static constexpr DWORD Canary = 0xAAAEEEEE; public: diff --git a/src/Ext/WarheadType/Body.h b/src/Ext/WarheadType/Body.h index a2fb9bbbf5..bd8fbba2b9 100644 --- a/src/Ext/WarheadType/Body.h +++ b/src/Ext/WarheadType/Body.h @@ -12,6 +12,9 @@ class WarheadTypeExt final : public AbstractTypeExt public: using base_type = WarheadTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = WarheadTypeExt; + static constexpr DWORD Canary = 0x22222222; public: diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 9a83f57d25..9e443bb85a 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -11,6 +11,9 @@ class WeaponTypeExt final : public AbstractTypeExt public: using base_type = WeaponTypeClass; + // deprecated: the pre-rework nested data class is now the extension class itself + using ExtData [[deprecated("use the extension class itself instead")]] = WeaponTypeExt; + static constexpr DWORD Canary = 0x22222222; public: diff --git a/src/Utilities/Container.h b/src/Utilities/Container.h index 22b31b21ff..0cbf0264e9 100644 --- a/src/Utilities/Container.h +++ b/src/Utilities/Container.h @@ -426,10 +426,14 @@ concept HasOffset = requires(T) { T::ExtPointerOffset; }; // resolves the data class of an extension: the extension class itself for the // flattened hierarchy, or the nested ExtData class of the legacy shells (EBolt). +// slot-mode extensions are their own data class and are resolved directly: their +// ExtData is only a deprecated, class-local compatibility alias of themselves and +// must never be consulted here (it would warn, and a derived class that does not +// redeclare it would inherit its parent's alias). template struct ExtensionDataType { using type = T; }; -template requires requires { typename T::ExtData; } +template requires (!HasOffset && requires { typename T::ExtData; }) struct ExtensionDataType { using type = typename T::ExtData; }; template From 4d5aeeef1d73d3bc64dde03efb5b6e12c04dbdf7 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 18:14:12 +0300 Subject: [PATCH 45/47] Use TryFetch where extension lookups are null-checked --- src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp | 4 ++-- src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp | 2 +- src/Ext/TechnoType/Body.cpp | 2 +- src/New/Entity/AttachEffectClass.cpp | 5 +++-- src/New/Entity/ShieldClass.cpp | 2 +- 5 files changed, 8 insertions(+), 7 deletions(-) diff --git a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp index b99a33b5c9..4c6af4f69a 100644 --- a/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp +++ b/src/Ext/Bullet/Trajectories/PhobosTrajectory.cpp @@ -455,7 +455,7 @@ DEFINE_HOOK(0x4666F7, BulletClass_AI_Trajectories, 0x6) DEFINE_HOOK(0x46703E, BulletClass_AI_SkipBridgeCheck1, 0x6) { GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::Fetch(pThis); + auto const pExt = BulletExt::TryFetch(pThis); if (pExt && pExt->Trajectory) return 0x467B7A; return 0; @@ -465,7 +465,7 @@ DEFINE_HOOK(0x46703E, BulletClass_AI_SkipBridgeCheck1, 0x6) DEFINE_HOOK(0x4674D4, BulletClass_AI_SkipBridgeCheck2, 0x6) { GET(BulletClass*, pThis, EBP); - auto const pExt = BulletExt::Fetch(pThis); + auto const pExt = BulletExt::TryFetch(pThis); if (pExt && pExt->Trajectory && pExt->Trajectory->ShouldSkipBridgeCheck()) return 0x467519; return 0; diff --git a/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp b/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp index 95812e5b60..72fc4943b9 100644 --- a/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp +++ b/src/Ext/Sidebar/SWSidebar/SWSidebarClass.cpp @@ -193,7 +193,7 @@ void SWSidebarClass::SortButtons() return BuildType::SortsBefore(AbstractType::Special, a->SuperIndex, AbstractType::Special, b->SuperIndex); }); - const auto pTopPCX = SideExt::TryFetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])->SuperWeaponSidebar_TopPCX.GetSurface(); + const auto pTopPCX = SideExt::Fetch(SideClass::Array.Items[ScenarioClass::Instance->PlayerSideIndex])->SuperWeaponSidebar_TopPCX.GetSurface(); const int buttonCount = static_cast(vec_Buttons.size()); const int cameoWidth = 60, cameoHeight = 48; const int firstColumn = Phobos::UI::SuperWeaponSidebar_Max; diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index 8c6d792435..3e703bd9ed 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -2091,7 +2091,7 @@ DEFINE_HOOK(0x747E90, UnitTypeClass_LoadFromINI, 0x5) { GET(UnitTypeClass*, pItem, ESI); - if (auto pTypeExt = TechnoTypeExt::Fetch(pItem)) + if (auto pTypeExt = TechnoTypeExt::TryFetch(pItem)) { if (!pTypeExt->Harvester_Counted.isset() && pItem->Harvester) pTypeExt->Harvester_Counted = true; diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index b5e246b026..46c8cf4c21 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -113,7 +113,7 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) if (auto const pAnim = abstract_cast(abs)) { - if (auto const pAnimExt = AnimExt::Fetch(pAnim)) + if (auto const pAnimExt = AnimExt::TryFetch(pAnim)) { if (pAnimExt->IsAttachedEffectAnim) { @@ -131,8 +131,9 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) else if ((abs->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { auto const pTechno = abstract_cast(abs); + auto const pTechnoExt = TechnoExt::TryFetch(pTechno); - if (int count = TechnoExt::Fetch(pTechno)->AttachedEffectInvokerCount) + if (int count = pTechnoExt ? pTechnoExt->AttachedEffectInvokerCount : 0) { for (auto const pEffect : AttachEffectClass::Array) { diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index 656519e0f2..b3496186b3 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -53,7 +53,7 @@ void ShieldClass::PointerGotInvalid(void* ptr, bool removed) if (auto const pAnim = abstract_cast(abs)) { - if (auto const pAnimExt = AnimExt::Fetch(pAnim)) + if (auto const pAnimExt = AnimExt::TryFetch(pAnim)) { if (pAnimExt->IsShieldIdleAnim) { From 1753d26575a48144eb99233a91c7314ce0330b0d Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 18:32:04 +0300 Subject: [PATCH 46/47] Make pointer invalidation handlers drop references even without the extension --- src/New/Entity/AttachEffectClass.cpp | 32 +++++++++++++++++----------- src/New/Entity/ShieldClass.cpp | 17 ++++++++------- 2 files changed, 29 insertions(+), 20 deletions(-) diff --git a/src/New/Entity/AttachEffectClass.cpp b/src/New/Entity/AttachEffectClass.cpp index 46c8cf4c21..24af9e9fc4 100644 --- a/src/New/Entity/AttachEffectClass.cpp +++ b/src/New/Entity/AttachEffectClass.cpp @@ -100,8 +100,12 @@ AttachEffectClass::~AttachEffectClass() this->KillAnim(); + // the invoker may be mid-destruction with its extension already removed if (this->Invoker) - TechnoExt::Fetch(this->Invoker)->AttachedEffectInvokerCount--; + { + if (auto const pInvokerExt = TechnoExt::TryFetch(this->Invoker)) + pInvokerExt->AttachedEffectInvokerCount--; + } } void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) @@ -113,17 +117,18 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) if (auto const pAnim = abstract_cast(abs)) { - if (auto const pAnimExt = AnimExt::TryFetch(pAnim)) + auto const pAnimExt = AnimExt::TryFetch(pAnim); + + // the flag is only a fast-path gate: during scenario teardown the anim's + // extension is already gone, and the references must still be dropped + if (!pAnimExt || pAnimExt->IsAttachedEffectAnim) { - if (pAnimExt->IsAttachedEffectAnim) + for (auto const pEffect : AttachEffectClass::Array) { - for (auto const pEffect : AttachEffectClass::Array) + if (pAnim == pEffect->Animation) { - if (pAnim == pEffect->Animation) - { - pEffect->Animation = nullptr; - break; // one anim must be used by less than one AE - } + pEffect->Animation = nullptr; + break; // one anim must be used by less than one AE } } } @@ -133,16 +138,19 @@ void AttachEffectClass::PointerGotInvalid(void* ptr, bool removed) auto const pTechno = abstract_cast(abs); auto const pTechnoExt = TechnoExt::TryFetch(pTechno); - if (int count = pTechnoExt ? pTechnoExt->AttachedEffectInvokerCount : 0) + // the counter is only a fast-path gate: during scenario teardown the techno's + // extension is already gone, and the invoker references must still be dropped + int count = pTechnoExt ? pTechnoExt->AttachedEffectInvokerCount : -1; + + if (count != 0) { for (auto const pEffect : AttachEffectClass::Array) { if (pTechno == pEffect->Invoker) { AnnounceInvalidPointer(pEffect->Invoker, ptr); - count--; - if (count <= 0) + if (--count == 0) break; } } diff --git a/src/New/Entity/ShieldClass.cpp b/src/New/Entity/ShieldClass.cpp index b3496186b3..f58409a1d4 100644 --- a/src/New/Entity/ShieldClass.cpp +++ b/src/New/Entity/ShieldClass.cpp @@ -53,17 +53,18 @@ void ShieldClass::PointerGotInvalid(void* ptr, bool removed) if (auto const pAnim = abstract_cast(abs)) { - if (auto const pAnimExt = AnimExt::TryFetch(pAnim)) + auto const pAnimExt = AnimExt::TryFetch(pAnim); + + // the flag is only a fast-path gate: during scenario teardown the anim's + // extension is already gone, and the references must still be dropped + if (!pAnimExt || pAnimExt->IsShieldIdleAnim) { - if (pAnimExt->IsShieldIdleAnim) + for (auto const pShield : ShieldClass::Array) { - for (auto const pShield : ShieldClass::Array) + if (pAnim == pShield->IdleAnim) { - if (pAnim == pShield->IdleAnim) - { - pShield->IdleAnim = nullptr; - break; // one anim must be used by less than one shield - } + pShield->IdleAnim = nullptr; + break; // one anim must be used by less than one shield } } } From 9c3ffc6ef019f5b39ca0105b499a8d6111279449 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sat, 18 Jul 2026 18:39:28 +0300 Subject: [PATCH 47/47] Stack the ParticleTypeClass destructor hook on Ares' address to avoid patch overlap --- src/Ext/ParticleType/Body.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/Ext/ParticleType/Body.cpp b/src/Ext/ParticleType/Body.cpp index 92f1257940..7cdae8fe37 100644 --- a/src/Ext/ParticleType/Body.cpp +++ b/src/Ext/ParticleType/Body.cpp @@ -72,7 +72,9 @@ DEFINE_HOOK(0x644DBB, ParticleTypeClass_CTOR, 0x5) } // Late in the destructor body, right before it chains into the base destructor. -DEFINE_HOOK(0x645A39, ParticleTypeClass_DTOR, 0x13) +// Deliberately stacked on the exact address and size of Ares' own hook: any other +// placement in this stretch would overlap its 5-byte JMP and corrupt the patch. +DEFINE_HOOK(0x645A3B, ParticleTypeClass_DTOR, 0x7) { GET(ParticleTypeClass*, pItem, ESI);