diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 80f55b8b2d..6a900f9c42 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -38,7 +38,9 @@ + + @@ -66,6 +68,7 @@ + @@ -75,6 +78,7 @@ + @@ -117,7 +121,9 @@ + + diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index 8f2ce0062b..d9f0772673 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -7,6 +7,7 @@ #include #include #include +#include template<> const DWORD Extension::Canary = 0x12341234; std::unique_ptr RulesExt::Data = nullptr; @@ -32,6 +33,7 @@ void RulesExt::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) { RadTypeClass::LoadFromINIList(pINI); ShieldTypeClass::LoadFromINIList(pINI); + AttachmentTypeClass::LoadFromINIList(pINI); Data->LoadBeforeTypeData(pThis, pINI); } diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 43b5de7571..0caccbd2b7 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -10,6 +10,7 @@ #include #include +#include template<> const DWORD Extension::Canary = 0x55555555; TechnoExt::ExtContainer TechnoExt::ExtMap; @@ -214,7 +215,7 @@ void TechnoExt::InitializeLaserTrails(TechnoClass* pThis) { for (auto const& entry: pTypeExt->LaserTrailData) { - if (auto const pLaserType = LaserTrailTypeClass::Array[entry.idxType].get()) + if (auto const pLaserType = LaserTrailTypeClass::Array[entry.Type].get()) { pExt->LaserTrails.push_back(std::make_unique( pLaserType, pThis->Owner, entry.FLH, entry.IsOnTurret)); @@ -371,6 +372,100 @@ void TechnoExt::EatPassengers(TechnoClass* pThis) } } +bool TechnoExt::AttachmentAI(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + // auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); + + if (pExt && pExt->ParentAttachment) + { + pExt->ParentAttachment->AI(); + return true; + } + + return false; +} + +// Attaches this techno in a first available attachment "slot". +// Returns true if the attachment is successful. +bool TechnoExt::AttachTo(TechnoClass* pThis, TechnoClass* pParent) +{ + auto const pParentExt = TechnoExt::ExtMap.Find(pParent); + + for (auto const& pAttachment: pParentExt->ChildAttachments) + { + if (pAttachment->AttachChild(pThis)) + return true; + } + + return false; +} + +bool TechnoExt::DetachFromParent(TechnoClass* pThis, bool isForceDetachment) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + return pExt->ParentAttachment->DetachChild(isForceDetachment); +} + +void TechnoExt::InitializeAttachments(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const pType = pThis->GetTechnoType(); + auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); + + for (auto& entry : pTypeExt->AttachmentData) + { + pExt->ChildAttachments.push_back(std::make_unique(&entry, pThis, nullptr)); + pExt->ChildAttachments.back()->Initialize(); + } +} + +void TechnoExt::HandleHostDestruction(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + for (auto const& pAttachment: pExt->ChildAttachments) + pAttachment->Uninitialize(); +} + +void TechnoExt::UnlimboAttachments(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + for (auto const& pAttachment: pExt->ChildAttachments) + pAttachment->Unlimbo(); +} + +void TechnoExt::LimboAttachments(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + for (auto const& pAttachment: pExt->ChildAttachments) + pAttachment->Limbo(); +} + +bool TechnoExt::IsParentOf(TechnoClass* pThis, TechnoClass* pOtherTechno) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + + if (!pOtherTechno) + return false; + + for (auto const& pAttachment: pExt->ChildAttachments) + { + if (pAttachment->Child && + (pAttachment->Child == pOtherTechno || + TechnoExt::IsParentOf(pAttachment->Child, pOtherTechno))) + { + return true; + } + } + + return false; +} + +void TechnoExt::FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponType) +{ + WeaponTypeExt::DetonateAt(pWeaponType, pThis, pThis); +} + // ============================= // load / save diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a7a6dfcd0d..582e25806f 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -7,6 +7,7 @@ #include #include +#include class BulletClass; @@ -25,6 +26,9 @@ class TechnoExt Valueable LastKillWasTeamTarget; TimerStruct PassengerDeletionTimer; + AttachmentClass* ParentAttachment; + ValueableVector> ChildAttachments; + ExtData(TechnoClass* OwnerObject) : Extension(OwnerObject) , InterceptedBullet(nullptr) , Shield() @@ -32,6 +36,8 @@ class TechnoExt , ReceiveDamage(false) , LastKillWasTeamTarget(false) , PassengerDeletionTimer(-1) + , ParentAttachment() + , ChildAttachments() { } virtual ~ExtData() = default; @@ -73,6 +79,19 @@ class TechnoExt static CoordStruct GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FLHFound); + static bool AttachmentAI(TechnoClass* pThis); + static bool AttachTo(TechnoClass* pThis, TechnoClass* pParent); + static bool DetachFromParent(TechnoClass* pThis, bool force = false); + + static void InitializeAttachments(TechnoClass* pThis); + static void HandleHostDestruction(TechnoClass* pThis); + static void UnlimboAttachments(TechnoClass* pThis); + static void LimboAttachments(TechnoClass* pThis); + + static bool IsParentOf(TechnoClass* pThis, TechnoClass* pOtherTechno); + + static void FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponType); + static void TransferMindControlOnDeploy(TechnoClass* pTechnoFrom, TechnoClass* pTechnoTo); static void ApplyMindControlRangeLimit(TechnoClass* pThis); diff --git a/src/Ext/Techno/Hooks.Shield.cpp b/src/Ext/Techno/Hooks.Shield.cpp index a3df20d188..b762c553c5 100644 --- a/src/Ext/Techno/Hooks.Shield.cpp +++ b/src/Ext/Techno/Hooks.Shield.cpp @@ -125,6 +125,8 @@ DEFINE_HOOK(0x6F9E50, TechnoClass_AI_Shield, 0x5) DEFINE_HOOK(0x71A88D, TemporalClass_AI_Shield, 0x0) { GET(TemporalClass*, pThis, ESI); + + // Shield logic if (auto const pTarget = pThis->Target) { const auto pExt = TechnoExt::ExtMap.Find(pTarget); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp new file mode 100644 index 0000000000..7002b42668 --- /dev/null +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -0,0 +1,102 @@ +#include "Body.h" + +#include + +DEFINE_HOOK(0x4DA86E, FootClass_AI_UpdateAttachedLocomotion, 0x0) +{ + GET(FootClass* const, pThis, ESI); + auto const pExt = TechnoExt::ExtMap.Find(pThis); + + if (!pExt->ParentAttachment) + pThis->Locomotor->Process(); + + return 0x4DA87A; +} + +DEFINE_HOOK(0x710460, TechnoClass_Destroy_HandleAttachments, 0x6) +{ + GET(TechnoClass*, pThis, ECX); + + TechnoExt::HandleHostDestruction(pThis); + + auto const pExt = TechnoExt::ExtMap.Find(pThis); + if (pExt->ParentAttachment) + pExt->ParentAttachment->ChildDestroyed(); + + pExt->ParentAttachment = nullptr; + + return 0; +} + +DEFINE_HOOK(0x6F6F20, TechnoClass_Unlimbo_UnlimboAttachments, 0x6) +{ + GET(TechnoClass*, pThis, ESI); + + TechnoExt::UnlimboAttachments(pThis); + + return 0; +} + +DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) +{ + GET(TechnoClass*, pThis, ESI); + + TechnoExt::LimboAttachments(pThis); + + return 0; +} + +DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) +{ + enum { IgnoreOccupier = 0x73FC10, Continue = 0x73F530 }; + + GET(UnitClass*, pThis, EBX); + GET(TechnoClass*, pOccupier, ESI); + + if (pThis == pOccupier || TechnoExt::IsParentOf(pThis, pOccupier)) + return IgnoreOccupier; + + return Continue; +} + +DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) +{ + enum { IgnoreOccupier = 0x51C70F, Continue = 0x51C259 }; + + GET(InfantryClass*, pThis, EBP); + GET(TechnoClass*, pOccupier, ESI); + + if ((TechnoClass*)pThis == pOccupier || TechnoExt::IsParentOf((TechnoClass*)pThis, pOccupier)) + return IgnoreOccupier; + + return Continue; +} + +DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) +{ + enum { Skip = 0x6CCCCA, Continue = 0 }; + + GET(FootClass* const, pFoot, ESI); + auto const pExt = TechnoExt::ExtMap.Find(pFoot); + + return pExt->ParentAttachment ? Skip : Continue; +} + +// DEFINE_HOOK(0x6CCCCA, SuperClass_Place_ChronoWarp_HandleAttachment, 0x0) +// { +// enum { Loop = 0x6CC742, Break = 0x6CCCD5 }; +// +// GET(FootClass*, pFoot, ESI) +// +// pFoot = abstract_cast(pFoot->NextObject); +// +// return pFoot ? Loop : Break; +// } + +// TODO +// 0x4DEAE0 IC for footclass +// 0x457C90 IC (forceshield) for buildings +// 0x6CCCCA Chrono Warp +// 0x4694BB Temporal warhead +// 0x4696FB Locomotor warhead +// ... \ No newline at end of file diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 9556cf8b29..c9c052c57d 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -27,11 +27,12 @@ DEFINE_HOOK(0x6F9E50, TechnoClass_AI, 0x5) } -DEFINE_HOOK(0x6F42F7, TechnoClass_Init_SetLaserTrails, 0x2) +DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) { GET(TechnoClass*, pThis, ESI); TechnoExt::InitializeLaserTrails(pThis); + TechnoExt::InitializeAttachments(pThis); return 0; } diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index e05c14937c..751ebe6c46 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -78,6 +78,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) if (!pINI->GetSection(pSection)) return; + char tempBuffer[32]; INI_EX exINI(pINI); this->HealthBar_Hide.Read(exINI, pSection, "HealthBar.Hide"); @@ -129,6 +130,35 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->OpenTopped_DamageMultiplier.Read(exINI, pSection, "OpenTopped.DamageMultiplier"); this->OpenTopped_WarpDistance.Read(exINI, pSection, "OpenTopped.WarpDistance"); + // The following loop iterates over size + 1 INI entries so that the + // vector contents can be properly overriden via scenario rules - Kerbiter + for (size_t i = 0; i <= this->AttachmentData.size(); ++i) + { + NullableIdx type; + _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.Type", i); + type.Read(exINI, pSection, tempBuffer); + + if (!type.isset()) + continue; + + NullableIdx technoType; + _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.TechnoType", i); + technoType.Read(exINI, pSection, tempBuffer); + + Valueable flh; + _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.FLH", i); + flh.Read(exINI, pSection, tempBuffer); + + Valueable isOnTurret; + _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.IsOnTurret", i); + isOnTurret.Read(exINI, pSection, tempBuffer); + + if (i == AttachmentData.size()) + this->AttachmentData.push_back({ ValueableIdx(type), technoType, flh, isOnTurret }); + else + this->AttachmentData[i] = { ValueableIdx(type), technoType, flh, isOnTurret }; + } + // Ares 0.A this->GroupAs.Read(pINI, pSection, "GroupAs"); @@ -144,7 +174,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->TurretOffset.Read(exArtINI, pArtSection, "TurretOffset"); - char tempBuffer[32]; for (size_t i = 0; ; ++i) { NullableIdx trail; @@ -236,8 +265,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->OreGathering_Anims) .Process(this->OreGathering_Tiberiums) .Process(this->OreGathering_FramesPerDir) - .Process(this->LaserTrailData) .Process(this->DestroyAnim_Random) + .Process(this->LaserTrailData) .Process(this->NotHuman_RandomDeathSequence) .Process(this->DefaultDisguise) .Process(this->WeaponBurstFLHs) @@ -250,6 +279,7 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->OpenTopped_RangeBonus) .Process(this->OpenTopped_DamageMultiplier) .Process(this->OpenTopped_WarpDistance) + .Process(this->AttachmentData) ; } void TechnoTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) @@ -264,6 +294,8 @@ void TechnoTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) this->Serialize(Stm); } +#pragma region Data entry save/load + bool TechnoTypeExt::ExtData::LaserTrailDataEntry::Load(PhobosStreamReader& stm, bool registerForChange) { return this->Serialize(stm); @@ -278,12 +310,35 @@ template bool TechnoTypeExt::ExtData::LaserTrailDataEntry::Serialize(T& stm) { return stm - .Process(this->idxType) + .Process(this->Type) .Process(this->FLH) .Process(this->IsOnTurret) .Success(); } +bool TechnoTypeExt::ExtData::AttachmentDataEntry::Load(PhobosStreamReader& stm, bool registerForChange) +{ + return this->Serialize(stm); +} + +bool TechnoTypeExt::ExtData::AttachmentDataEntry::Save(PhobosStreamWriter& stm) const +{ + return const_cast(this)->Serialize(stm); +} + +template +bool TechnoTypeExt::ExtData::AttachmentDataEntry::Serialize(T& stm) +{ + return stm + .Process(this->Type) + .Process(this->TechnoType) + .Process(this->FLH) + .Process(this->IsOnTurret) + .Success(); +} + +#pragma endregion + // ============================= // container diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 160ba9da42..90239b19c4 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -7,6 +7,7 @@ #include #include +#include class Matrix3D; @@ -74,9 +75,26 @@ class TechnoTypeExt Nullable OpenTopped_DamageMultiplier; Nullable OpenTopped_WarpDistance; + struct AttachmentDataEntry + { + ValueableIdx Type; + NullableIdx TechnoType; + Valueable FLH; + Valueable IsOnTurret; + + bool Load(PhobosStreamReader& stm, bool registerForChange); + bool Save(PhobosStreamWriter& stm) const; + + private: + template + bool Serialize(T& stm); + }; + + ValueableVector AttachmentData; + struct LaserTrailDataEntry { - ValueableIdx idxType; + ValueableIdx Type; Valueable FLH; Valueable IsOnTurret; @@ -137,7 +155,8 @@ class TechnoTypeExt DefaultDisguise(), OpenTopped_RangeBonus(), OpenTopped_DamageMultiplier(), - OpenTopped_WarpDistance() + OpenTopped_WarpDistance(), + AttachmentData() { } virtual ~ExtData() = default; diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index cdc58bac35..9db52e77ba 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -1,5 +1,8 @@ #include "Body.h" +#include +#include + template<> const DWORD Extension::Canary = 0x22222222; WeaponTypeExt::ExtContainer WeaponTypeExt::ExtMap; @@ -89,6 +92,34 @@ bool WeaponTypeExt::SaveGlobals(PhobosStreamWriter& Stm) .Success(); } +void WeaponTypeExt::DetonateAt(WeaponTypeClass* pThis, ObjectClass* pTarget, TechnoClass* pOwner) +{ + if (BulletClass* pBullet = pThis->Projectile->CreateBullet(pTarget, pOwner, + pThis->Damage, pThis->Warhead, 0, pThis->Bright)) + { + const CoordStruct& coords = pTarget->GetCoords(); + + pBullet->SetWeaponType(pThis); + pBullet->Limbo(); + pBullet->SetLocation(coords); + pBullet->Explode(true); + pBullet->UnInit(); + } +} + +void WeaponTypeExt::DetonateAt(WeaponTypeClass* pThis, const CoordStruct& coords, TechnoClass* pOwner) +{ + if (BulletClass* pBullet = pThis->Projectile->CreateBullet(nullptr, pOwner, + pThis->Damage, pThis->Warhead, 0, pThis->Bright)) + { + pBullet->SetWeaponType(pThis); + pBullet->Limbo(); + pBullet->SetLocation(coords); + pBullet->Explode(true); + pBullet->UnInit(); + } +} + // ============================= // container diff --git a/src/Ext/WeaponType/Body.h b/src/Ext/WeaponType/Body.h index 932c4542d5..6a571ea9c8 100644 --- a/src/Ext/WeaponType/Body.h +++ b/src/Ext/WeaponType/Body.h @@ -71,4 +71,7 @@ class WeaponTypeExt static bool SaveGlobals(PhobosStreamWriter& Stm); static int nOldCircumference; + + static void DetonateAt(WeaponTypeClass* pThis, ObjectClass* pTarget, TechnoClass* pSource = nullptr); + static void DetonateAt(WeaponTypeClass* pThis, const CoordStruct& coords, TechnoClass* pSource = nullptr); }; diff --git a/src/Misc/Hooks.AI.cpp b/src/Misc/Hooks.AI.cpp new file mode 100644 index 0000000000..bd1ed4456d --- /dev/null +++ b/src/Misc/Hooks.AI.cpp @@ -0,0 +1,11 @@ +#include + +#include + +DEFINE_HOOK(0x55B6B3, LogicClass_AI_After, 0x5) +{ + for (auto const& attachment : AttachmentClass::Array) + attachment->AI(); + + return 0; +} \ No newline at end of file diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp new file mode 100644 index 0000000000..44dc955a56 --- /dev/null +++ b/src/New/Entity/AttachmentClass.cpp @@ -0,0 +1,242 @@ +#include "AttachmentClass.h" + +#include +#include +#include +#include + +#include + +#include + +std::vector AttachmentClass::Array; + +AttachmentTypeClass* AttachmentClass::GetType() +{ + return AttachmentTypeClass::Array[this->Data->Type].get(); +} + +TechnoTypeClass* AttachmentClass::GetChildType() +{ + return this->Data->TechnoType.isset() + ? TechnoTypeClass::Array()->GetItem(this->Data->TechnoType) + : nullptr; +} + +void AttachmentClass::Initialize() +{ + if (this->Child) + return; + + if (this->GetType()->RestoreAtCreation) + this->CreateChild(); +} + +void AttachmentClass::CreateChild() +{ + if (auto const pChildType = this->GetChildType()) + { + if (this->Child = static_cast(pChildType->CreateObject(this->Parent->Owner))) + { + auto const pChildExt = TechnoExt::ExtMap.Find(this->Child); + pChildExt->ParentAttachment = this; + } + else + { + Debug::Log("[" __FUNCTION__ "] Failed to create child %s of parent %s!\n", + pChildType->ID, this->Parent->GetTechnoType()->ID); + } + } +} + +void AttachmentClass::AI() +{ + AttachmentTypeClass* pType = this->GetType(); + + if (this->Child) + { + this->Child->SetLocation(TechnoExt::GetFLHAbsoluteCoords( + this->Parent, this->Data->FLH, this->Data->IsOnTurret)); + + this->Child->OnBridge = this->Parent->OnBridge; + + DirStruct childDir = this->Data->IsOnTurret + ? this->Parent->SecondaryFacing.current() : this->Parent->PrimaryFacing.current(); + + this->Child->PrimaryFacing.set(childDir); + + if (pType->InheritTilt) + { + this->Child->AngleRotatedForwards = this->Parent->AngleRotatedForwards; + this->Child->AngleRotatedSideways = this->Parent->AngleRotatedSideways; + + // DriveLocomotionClass doesn't tilt only with angles set, hence why we + // do this monstrosity in order to inherit timer and ramp data - Kerbiter + FootClass* pParentAsFoot = abstract_cast(this->Parent); + FootClass* pChildAsFoot = abstract_cast(this->Child); + if (pParentAsFoot && pChildAsFoot) + { + auto pParentLoco = static_cast(pParentAsFoot->Locomotor.get()); + auto pChildLoco = static_cast(pChildAsFoot->Locomotor.get()); + + CLSID locoCLSID; + if (SUCCEEDED(pParentLoco->GetClassID(&locoCLSID)) && locoCLSID == LocomotionClass::CLSIDs::Drive && + SUCCEEDED(pChildLoco->GetClassID(&locoCLSID)) && locoCLSID == LocomotionClass::CLSIDs::Drive) + { + auto pParentDriveLoco = static_cast(pParentLoco); + auto pChildDriveLoco = static_cast(pChildLoco); + + pChildDriveLoco->SlopeTimer = pParentDriveLoco->SlopeTimer; + pChildDriveLoco->Ramp1 = pParentDriveLoco->Ramp1; + pChildDriveLoco->Ramp2 = pParentDriveLoco->Ramp2; + } + } + } + + if (pType->InheritStateEffects) + { + this->Child->CloakState = this->Parent->CloakState; + // this->Child->BeingWarpedOut = this->Parent->BeingWarpedOut; + this->Child->Deactivated = this->Parent->Deactivated; + // this->Child->Flash(this->Parent->Flashing.DurationRemaining); + + this->Child->IronCurtainTimer = this->Parent->IronCurtainTimer; + this->Child->IdleActionTimer = this->Parent->IdleActionTimer; + this->Child->IronTintTimer = this->Parent->IronTintTimer; + this->Child->CloakDelayTimer = this->Parent->CloakDelayTimer; + // this->Child->ChronoLockRemaining = this->Parent->ChronoLockRemaining; + this->Child->Berzerk = this->Parent->Berzerk; + // this->Child->ChronoWarpedByHouse = this->Parent->ChronoWarpedByHouse; + this->Child->EMPLockRemaining = this->Parent->EMPLockRemaining; + this->Child->ShouldLoseTargetNow = this->Parent->ShouldLoseTargetNow; + } + + if (pType->InheritOwner) + this->Child->SetOwningHouse(this->Parent->GetOwningHouse(), false); + } +} + +// Doesn't call destructor (to be managed by smart pointers) +void AttachmentClass::Uninitialize() +{ + if (this->Child) + { + auto pType = this->GetType(); + if (pType->DestructionWeapon_Child.isset()) + TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); + + if (!this->Child->InLimbo && pType->ParentDestructionMission.isset()) + this->Child->QueueMission(pType->ParentDestructionMission.Get(), false); + + auto pChildExt = TechnoExt::ExtMap.Find(this->Child); + pChildExt->ParentAttachment = nullptr; + this->Child = nullptr; + } +} + +void AttachmentClass::ChildDestroyed() +{ + AttachmentTypeClass* pType = this->GetType(); + if (pType->DestructionWeapon_Parent.isset()) + TechnoExt::FireWeaponAtSelf(this->Parent, pType->DestructionWeapon_Parent); + + this->Child = nullptr; +} + +void AttachmentClass::Unlimbo() +{ + if (this->Child) + { + CoordStruct childCoord = TechnoExt::GetFLHAbsoluteCoords( + this->Parent, this->Data->FLH, this->Data->IsOnTurret); + + Direction::Value childDir = this->Data->IsOnTurret + ? this->Parent->SecondaryFacing.current().value256() : this->Parent->PrimaryFacing.current().value256(); + + ++Unsorted::IKnowWhatImDoing; + this->Child->Unlimbo(childCoord, childDir); + --Unsorted::IKnowWhatImDoing; + } +} + +void AttachmentClass::Limbo() +{ + if (this->Child) + this->Child->Limbo(); +} + +bool AttachmentClass::AttachChild(TechnoClass* pChild) +{ + if (this->Child || this->Data->TechnoType.isset()) + return false; + + this->Child = pChild; + + auto pChildExt = TechnoExt::ExtMap.Find(this->Child); + pChildExt->ParentAttachment = this; + + AttachmentTypeClass* pType = this->GetType(); + + if (pType->InheritOwner) + { + if (auto pController = this->Child->MindControlledBy) + pController->CaptureManager->FreeUnit(this->Child); + } + + return true; +} + +bool AttachmentClass::DetachChild(bool isForceDetachment) +{ + if (this->Child) + { + AttachmentTypeClass* pType = this->GetType(); + + if (isForceDetachment) + { + if (pType->ForceDetachWeapon_Parent.isset()) + TechnoExt::FireWeaponAtSelf(this->Parent, pType->DestructionWeapon_Parent); + + if (pType->ForceDetachWeapon_Child.isset()) + TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); + } + + if (!this->Child->InLimbo && pType->ParentDetachmentMission.isset()) + this->Child->QueueMission(pType->ParentDetachmentMission.Get(), false); + + if (pType->InheritOwner) + this->Child->SetOwningHouse(this->Parent->GetOriginalOwner(), false); + + auto pChildExt = TechnoExt::ExtMap.Find(this->Child); + pChildExt->ParentAttachment = nullptr; + this->Child = nullptr; + + return true; + } + + return false; +} + +#pragma region Save/Load + +template +bool AttachmentClass::Serialize(T& stm) +{ + return stm + .Process(this->Data) + .Process(this->Parent) + .Process(this->Child) + .Success(); +}; + +bool AttachmentClass::Load(PhobosStreamReader& stm, bool RegisterForChange) +{ + return Serialize(stm); +} + +bool AttachmentClass::Save(PhobosStreamWriter& stm) const +{ + return const_cast(this)->Serialize(stm); +} + +#pragma endregion \ No newline at end of file diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h new file mode 100644 index 0000000000..c601ab55fc --- /dev/null +++ b/src/New/Entity/AttachmentClass.h @@ -0,0 +1,64 @@ +#pragma once + +#include + +#include + +#include +#include + +class AttachmentClass +{ +public: + static std::vector Array; + + TechnoTypeExt::ExtData::AttachmentDataEntry* Data; + TechnoClass* Parent; + TechnoClass* Child; + + AttachmentClass(TechnoTypeExt::ExtData::AttachmentDataEntry* data, + TechnoClass* pParent, TechnoClass* pChild = nullptr) : + Data(data), + Parent(pParent), + Child(pChild) + { + Array.push_back(this); + } + + AttachmentClass() : + Data(), + Parent(), + Child() + { + Array.push_back(this); + } + + ~AttachmentClass() + { + auto position = std::find(Array.begin(), Array.end(), this); + if (position != Array.end()) + Array.erase(position); + } + + AttachmentTypeClass* GetType(); + TechnoTypeClass* GetChildType(); + + void Initialize(); + void CreateChild(); + void AI(); + void Uninitialize(); + void ChildDestroyed(); + + void Unlimbo(); + void Limbo(); + + bool AttachChild(TechnoClass* pChild); + bool DetachChild(bool force = false); + + bool Load(PhobosStreamReader& stm, bool registerForChange); + bool Save(PhobosStreamWriter& stm) const; + +private: + template + bool Serialize(T& stm); +}; \ No newline at end of file diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp new file mode 100644 index 0000000000..84ecf29b72 --- /dev/null +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -0,0 +1,73 @@ +#include "AttachmentTypeClass.h" + +#include + +Enumerable::container_t Enumerable::Array; + +const char* Enumerable::GetMainSection() +{ + return "AttachmentTypes"; +} + +void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) +{ + const char* section = this->Name; + + INI_EX exINI(pINI); + + this->RestoreAtCreation.Read(exINI, section, "RestoreAtCreation"); + this->InheritTilt.Read(exINI, section, "InheritTilt"); + this->InheritCommands.Read(exINI, section, "InheritCommands"); + this->InheritOwner.Read(exINI, section, "InheritOwner"); + this->InheritStateEffects.Read(exINI, section, "InheritStateEffects"); + + this->SyncDamage.Read(exINI, section, "SyncDamage"); + this->SyncDamage_IsRelative.Read(exINI, section, "SyncDamage.IsRelative"); + this->SyncExperienceGain.Read(exINI, section, "SyncExperienceGain"); + this->SyncExperienceGain_IsRelative.Read(exINI, section, "SyncExperienceGain.IsRelative"); + + this->CanBeForceDetached.Read(exINI, section, "CanBeForceDetached"); + this->RestoreAtHealth.Read(exINI, section, "RestoreAtHealth"); + + this->ForceDetachWeapon_Child.Read(exINI, section, "ForceDetachWeapon.Child"); + this->ForceDetachWeapon_Parent.Read(exINI, section, "ForceDetachWeapon.Parent"); + this->DestructionWeapon_Child.Read(exINI, section, "DestructionWeapon.Child"); + this->DestructionWeapon_Parent.Read(exINI, section, "DestructionWeapon.Parent"); + + this->ParentDestructionMission.Read(exINI, section, "ParentDestructionMission"); + this->ParentDetachmentMission.Read(exINI, section, "ParentDetachmentMission"); +} + +template +void AttachmentTypeClass::Serialize(T& Stm) +{ + Stm + .Process(this->RestoreAtCreation) + .Process(this->InheritTilt) + .Process(this->InheritCommands) + .Process(this->InheritOwner) + .Process(this->InheritStateEffects) + .Process(this->SyncDamage) + .Process(this->SyncDamage_IsRelative) + .Process(this->SyncExperienceGain) + .Process(this->SyncExperienceGain_IsRelative) + .Process(this->CanBeForceDetached) + .Process(this->RestoreAtHealth) + .Process(this->ForceDetachWeapon_Child) + .Process(this->ForceDetachWeapon_Parent) + .Process(this->DestructionWeapon_Child) + .Process(this->DestructionWeapon_Parent) + .Process(this->ParentDestructionMission) + .Process(this->ParentDetachmentMission) + ; +} + +void AttachmentTypeClass::LoadFromStream(PhobosStreamReader& Stm) +{ + this->Serialize(Stm); +} + +void AttachmentTypeClass::SaveToStream(PhobosStreamWriter& Stm) +{ + this->Serialize(Stm); +} diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h new file mode 100644 index 0000000000..cbf1e620ae --- /dev/null +++ b/src/New/Type/AttachmentTypeClass.h @@ -0,0 +1,67 @@ +#pragma once + +#include +#include + +#include + +class AttachmentTypeClass final : public Enumerable +{ +public: + Valueable RestoreAtCreation; // whether to spawn the attachment initially + // Inherit = propagate from host to attachment + // Sync = propagate changes both ways + Valueable InheritTilt; + Valueable InheritCommands; + Valueable InheritOwner; //aka mind control inheritance + Valueable InheritStateEffects; // phasing out, stealth etc. + // Explanation: 200 damage to 400 HP host with 200 HP part will... + // - ...kill the part (200 - 200 = 0) if the mode is absolute + // - ...leave the part alive (200 - 200*(200/400) = 100) if the mode is relative + Valueable SyncDamage; + Valueable SyncDamage_IsRelative; + Valueable SyncExperienceGain; + Valueable SyncExperienceGain_IsRelative; + Valueable CanBeForceDetached; + Nullable RestoreAtHealth; // if host is healed to that health it's respawned + Nullable ForceDetachWeapon_Child; + Nullable ForceDetachWeapon_Parent; + Nullable DestructionWeapon_Child; + Nullable DestructionWeapon_Parent; + Nullable ParentDestructionMission; + Nullable ParentDetachmentMission; + + // Targeting, verses, attachment health max/initial, immunities, possibility + // to command are to be done on TechnoType itself + + + AttachmentTypeClass(const char* pTitle = NONE_STR) : Enumerable(pTitle) + , RestoreAtCreation(true) + , InheritTilt(true) + , InheritCommands(false) + , InheritOwner(false) + , InheritStateEffects(true) + , SyncDamage(false) + , SyncDamage_IsRelative(false) + , SyncExperienceGain(false) + , SyncExperienceGain_IsRelative(false) + , CanBeForceDetached(false) + , RestoreAtHealth() + , ForceDetachWeapon_Child() + , ForceDetachWeapon_Parent() + , DestructionWeapon_Child() + , DestructionWeapon_Parent() + , ParentDestructionMission() + , ParentDetachmentMission() + { } + + virtual ~AttachmentTypeClass() override = default; + + virtual void LoadFromINI(CCINIClass* pINI) override; + virtual void LoadFromStream(PhobosStreamReader& Stm); + virtual void SaveToStream(PhobosStreamWriter& Stm); + +private: + template + void Serialize(T& Stm); +}; \ No newline at end of file