From cf33e39e2e2544c62059e9ae96723c4c7db3a987 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 11 Nov 2021 23:28:53 +0200 Subject: [PATCH 001/112] WIP Techno Attachment logic Implement ParentDestructionMission & ParentDetachmentMission "Fixed" chronowarping children Add children skip for ChronoWarp Rewritten and fixed a hook Loop over attachment AI separately from other AI routines --- Phobos.vcxproj | 6 + src/Ext/Rules/Body.cpp | 2 + src/Ext/Techno/Body.cpp | 97 ++++++++- src/Ext/Techno/Body.h | 19 ++ src/Ext/Techno/Hooks.Shield.cpp | 2 + src/Ext/Techno/Hooks.TechnoAttachment.cpp | 102 +++++++++ src/Ext/Techno/Hooks.cpp | 3 +- src/Ext/TechnoType/Body.cpp | 61 +++++- src/Ext/TechnoType/Body.h | 23 +- src/Ext/WeaponType/Body.cpp | 31 +++ src/Ext/WeaponType/Body.h | 3 + src/Misc/Hooks.AI.cpp | 11 + src/New/Entity/AttachmentClass.cpp | 242 ++++++++++++++++++++++ src/New/Entity/AttachmentClass.h | 64 ++++++ src/New/Type/AttachmentTypeClass.cpp | 73 +++++++ src/New/Type/AttachmentTypeClass.h | 67 ++++++ 16 files changed, 799 insertions(+), 7 deletions(-) create mode 100644 src/Ext/Techno/Hooks.TechnoAttachment.cpp create mode 100644 src/Misc/Hooks.AI.cpp create mode 100644 src/New/Entity/AttachmentClass.cpp create mode 100644 src/New/Entity/AttachmentClass.h create mode 100644 src/New/Type/AttachmentTypeClass.cpp create mode 100644 src/New/Type/AttachmentTypeClass.h 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 From 774bfa5902f4ad2ffd74021df097439b70c6b757 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 13 Nov 2021 00:21:44 +0200 Subject: [PATCH 002/112] Restrict commandability of attached technos --- src/Ext/TechnoType/Hooks.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 59389f967c..e07d554790 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -153,11 +153,12 @@ DEFINE_HOOK(0x73D223, UnitClass_DrawIt_OreGath, 0x6) DEFINE_HOOK(0x700C58, TechnoClass_CanPlayerMove_NoManualMove, 0x6) { GET(TechnoClass*, pThis, ESI); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + auto const& pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); - if (auto pExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType())) - return pExt->NoManualMove ? 0x700C62 : 0; + bool noMove = (pExt && pExt->ParentAttachment) || (pTypeExt && pTypeExt->NoManualMove); - return 0; + return noMove ? 0x700C62 : 0; } DEFINE_HOOK_AGAIN(0x522790, TechnoClass_DefaultDisguise, 0x6) // InfantryClass_SetDisguise_DefaultDisguise From b64eb0b7eced5f52072a247b8f102858bfac0e5f Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 13 Nov 2021 00:22:08 +0200 Subject: [PATCH 003/112] Implement InheritsCommands pt. 1 --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 28 +++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 7002b42668..61512eae3b 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -82,6 +82,34 @@ DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) return pExt->ParentAttachment ? Skip : Continue; } +DEFINE_HOOK_AGAIN(0x6FFDEB, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) +DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) +{ + GET(TechnoClass* const, pThis, ESI); + GET_STACK(Mission, mission, STACK_OFFS(0x98, -0x4)); + GET_STACK(ObjectClass* const, pTarget, STACK_OFFS(0x98, -0x8)); + GET_STACK(CellClass* const, pTargetCell, STACK_OFFS(0x98, -0xC)); + GET_STACK(CellClass* const, pCellNearTarget, STACK_OFFS(0x98, -0x10)); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + bool oldFeedback = Unsorted::MoveFeedback; + Unsorted::MoveFeedback = false; + + for (auto const& attachment : pExt->ChildAttachments) + { + // Recursive call, PlayerAssignMission == ClickedMission + if (attachment->Child && attachment->GetType()->InheritCommands) + attachment->Child->ClickedMission(mission, pTarget, pTargetCell, pCellNearTarget); + } + + Unsorted::MoveFeedback = oldFeedback; + + // PlayerAssignMission returns bool which indicates whether the player is in planning mode. + // This can't change when we handle children so we don't adjust the return value - Kerbiter + return 0; +} + + // DEFINE_HOOK(0x6CCCCA, SuperClass_Place_ChronoWarp_HandleAttachment, 0x0) // { // enum { Loop = 0x6CC742, Break = 0x6CCCD5 }; From 01dd1cd7dca773515c661a95a36f127c171f6fcf Mon Sep 17 00:00:00 2001 From: Meta Date: Mon, 15 Nov 2021 22:33:58 +0200 Subject: [PATCH 004/112] Fix command inheritance crashing in waypoint mode --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 61512eae3b..782caffac0 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -82,15 +82,26 @@ DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) return pExt->ParentAttachment ? Skip : Continue; } +namespace TechnoAttachmentTemp +{ + TechnoClass* pParent; +} + +DEFINE_HOOK(0x6FFBE0, TechnoClass_PlayerAssignMission_Context_Set, 0x6) +{ + TechnoAttachmentTemp::pParent = R->ECX(); + + return 0; +} + DEFINE_HOOK_AGAIN(0x6FFDEB, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) { - GET(TechnoClass* const, pThis, ESI); GET_STACK(Mission, mission, STACK_OFFS(0x98, -0x4)); GET_STACK(ObjectClass* const, pTarget, STACK_OFFS(0x98, -0x8)); GET_STACK(CellClass* const, pTargetCell, STACK_OFFS(0x98, -0xC)); GET_STACK(CellClass* const, pCellNearTarget, STACK_OFFS(0x98, -0x10)); - auto const& pExt = TechnoExt::ExtMap.Find(pThis); + auto const& pExt = TechnoExt::ExtMap.Find(TechnoAttachmentTemp::pParent); bool oldFeedback = Unsorted::MoveFeedback; Unsorted::MoveFeedback = false; From 58c7c0ac19216182134e84c5df1d1d613e3a1559 Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 16 Nov 2021 00:08:33 +0200 Subject: [PATCH 005/112] Filter movement commands --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 782caffac0..5a009a15f6 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -98,6 +98,16 @@ DEFINE_HOOK_AGAIN(0x6FFDEB, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) { GET_STACK(Mission, mission, STACK_OFFS(0x98, -0x4)); + + switch (mission) + { + case Mission::Move: + case Mission::AttackMove: + case Mission::Enter: + case Mission::QMove: + return 0; + } + GET_STACK(ObjectClass* const, pTarget, STACK_OFFS(0x98, -0x8)); GET_STACK(CellClass* const, pTargetCell, STACK_OFFS(0x98, -0xC)); GET_STACK(CellClass* const, pCellNearTarget, STACK_OFFS(0x98, -0x10)); From facba8c609c6e03223ecbfb9febf81375105d279 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 20 Nov 2021 19:45:42 +0200 Subject: [PATCH 006/112] Proper implementation of command inheritance --- YRpp | 2 +- src/Ext/Script/Body.cpp | 12 ++-- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 86 +++++++++++++++++++++-- 3 files changed, 89 insertions(+), 11 deletions(-) diff --git a/YRpp b/YRpp index 432f112031..1fd158b862 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 432f112031db061d9a0a56b8b8ff9b3359e980bc +Subproject commit 1fd158b86282d2e260491f41f4e9bcc31382f1be diff --git a/src/Ext/Script/Body.cpp b/src/Ext/Script/Body.cpp index 10afdf288f..2c1271295e 100644 --- a/src/Ext/Script/Body.cpp +++ b/src/Ext/Script/Body.cpp @@ -849,7 +849,7 @@ void ScriptExt::Mission_Attack(TeamClass *pTeam, bool repeatAction = true, int c if (pUnitType->WhatAmI() != AbstractType::AircraftType) { pUnit->QueueMission(Mission::Attack, true); - pUnit->ClickedAction(Action::Attack, selectedTarget, false); + pUnit->ObjectClickedAction(Action::Attack, selectedTarget, false); if (pUnit->GetCurrentMission() != Mission::Attack) pUnit->Mission_Attack(); @@ -869,7 +869,7 @@ void ScriptExt::Mission_Attack(TeamClass *pTeam, bool repeatAction = true, int c else { pUnit->QueueMission(Mission::Attack, true); - pUnit->ClickedAction(Action::Attack, selectedTarget, false); + pUnit->ObjectClickedAction(Action::Attack, selectedTarget, false); pUnit->Mission_Attack(); } } @@ -963,7 +963,7 @@ void ScriptExt::Mission_Attack(TeamClass *pTeam, bool repeatAction = true, int c if (pUnit->Ammo > 0) { pUnit->QueueMission(Mission::Attack, true); - pUnit->ClickedAction(Action::Attack, pFocus, false); + pUnit->ObjectClickedAction(Action::Attack, pFocus, false); pUnit->Mission_Attack(); } else @@ -980,7 +980,7 @@ void ScriptExt::Mission_Attack(TeamClass *pTeam, bool repeatAction = true, int c if (pUnit->Ammo > 0) { pUnit->QueueMission(Mission::Attack, true); - pUnit->ClickedAction(Action::Attack, pFocus, false); + pUnit->ObjectClickedAction(Action::Attack, pFocus, false); pUnit->Mission_Attack(); } else @@ -1009,7 +1009,7 @@ void ScriptExt::Mission_Attack(TeamClass *pTeam, bool repeatAction = true, int c pTeam->Focus = nullptr; pTeam->QueuedFocus = nullptr; - pUnit->ClickedAction(Action::Attack, pFocus, false); + pUnit->ObjectClickedAction(Action::Attack, pFocus, false); pUnit->CurrentTargets.Clear(); pUnit->SetTarget(nullptr); pUnit->SetFocus(nullptr); @@ -2114,7 +2114,7 @@ void ScriptExt::Mission_Move(TeamClass *pTeam, int calcThreatMode = 0, bool pick if (pUnitType->WhatAmI() != AbstractType::AircraftType) { pUnit->QueueMission(Mission::Move, false); - pUnit->ClickedAction(Action::Move, selectedTarget, false); + pUnit->ObjectClickedAction(Action::Move, selectedTarget, false); if (pUnit->GetCurrentMission() != Mission::Move) pUnit->Mission_Move(); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 5a009a15f6..3ec1254923 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -82,6 +82,84 @@ DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) return pExt->ParentAttachment ? Skip : Continue; } +void ParentClickedWaypoint(TechnoClass* pThis, int idxPath, signed char idxWP) +{ + // Rewrite of the original code + pThis->AssignPlanningPath(idxPath, idxWP); + + if ((pThis->AbstractFlags & AbstractFlags::Foot) == AbstractFlags::Foot) + pThis->unknown_bool_430 = false; + + // Children handling + if (auto const& pExt = TechnoExt::ExtMap.Find(pThis)) + { + for (auto const& pAttachment : pExt->ChildAttachments) + { + if (pAttachment->Child && pAttachment->GetType()->InheritCommands) + ParentClickedWaypoint(pAttachment->Child, idxPath, idxWP); + } + } +} + +void ParentClickedAction(TechnoClass* pThis, ObjectClass* pTarget, CellStruct* pCell, CellStruct* pSecondCell) +{ + // Rewrite of the original code + if (pTarget) + { + Action whatAction = pThis->MouseOverObject(pTarget, false); + pThis->ObjectClickedAction(whatAction, pTarget, false); + } + else + { + Action whatAction = pThis->MouseOverCell(pCell, false, false); + pThis->CellClickedAction(whatAction, pCell, pSecondCell, false); + } + + Unsorted::MoveFeedback = false; + + // Children handling + if (auto const& pExt = TechnoExt::ExtMap.Find(pThis)) + { + for (auto const& pAttachment : pExt->ChildAttachments) + { + if (pAttachment->Child && pAttachment->GetType()->InheritCommands) + ParentClickedAction(pAttachment->Child, pTarget, pCell, pSecondCell); + } + } +} + +DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) +{ + GET_STACK(int, idxPath, STACK_OFFS(0x18, +0x8)); + GET_STACK(unsigned char, idxWP, STACK_OFFS(0x18, +0xC)); + + for (auto const& pObject : ObjectClass::CurrentObjects.get()) + { + if (auto pTechno = abstract_cast(pObject)) + ParentClickedWaypoint(pTechno, idxPath, idxWP); + } + + GET_STACK(ObjectClass* const, pTarget, STACK_OFFS(0x18, -0x4)); + LEA_STACK(CellStruct* const, pCell, STACK_OFFS(0x18, -0x8)); + GET_STACK(Action const, action, STACK_OFFS(0x18, -0xC)); + + CellStruct invalidCell { -1, -1 }; + CellStruct* pSecondCell = &invalidCell; + + if (action == Action::Move || action == Action::PatrolWaypoint || action == Action::NoMove) + pSecondCell = pCell; + + for (auto const& pObject : ObjectClass::CurrentObjects.get()) + { + if (auto pTechno = abstract_cast(pObject)) + ParentClickedAction(pTechno, pTarget, pCell, pSecondCell); + } + + Unsorted::MoveFeedback = true; + + return 0x4AE99B; +} +/* namespace TechnoAttachmentTemp { TechnoClass* pParent; @@ -116,11 +194,11 @@ DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) bool oldFeedback = Unsorted::MoveFeedback; Unsorted::MoveFeedback = false; - for (auto const& attachment : pExt->ChildAttachments) + for (auto const& pAttachment : pExt->ChildAttachments) { // Recursive call, PlayerAssignMission == ClickedMission - if (attachment->Child && attachment->GetType()->InheritCommands) - attachment->Child->ClickedMission(mission, pTarget, pTargetCell, pCellNearTarget); + if (pAttachment->Child && pAttachment->GetType()->InheritCommands) + pAttachment->Child->ClickedMission(mission, pTarget, pTargetCell, pCellNearTarget); } Unsorted::MoveFeedback = oldFeedback; @@ -129,7 +207,7 @@ DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) // This can't change when we handle children so we don't adjust the return value - Kerbiter return 0; } - +*/ // DEFINE_HOOK(0x6CCCCA, SuperClass_Place_ChronoWarp_HandleAttachment, 0x0) // { From ac910db5e6bd97de6d35b341a262ffb3465f3aac Mon Sep 17 00:00:00 2001 From: Metadorius Date: Sat, 23 Apr 2022 00:43:07 +0300 Subject: [PATCH 007/112] Init docs --- CREDITS.md | 1 + docs/New-or-Enhanced-Logics.md | 82 ++++++++++++++++++++++++---------- docs/Whats-New.md | 10 +++++ 3 files changed, 70 insertions(+), 23 deletions(-) diff --git a/CREDITS.md b/CREDITS.md index 01119e5496..dbd0fa2222 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -31,6 +31,7 @@ This page lists all the individual contributions to the project by their author. - Zero size map previews fix - LaserTrails port and rework - Laser graphics fixes + - Techno Attachment logic - GitHub Actions setup - Overhauled Unicode font - Official docs diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index a70b664dfe..f8ffd72ae1 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -4,34 +4,40 @@ This page describes all the engine features that are either new and introduced b ## New types / ingame entities -### Custom Radiation Types +### Attachments -![image](_static/images/radtype-01.png) -*Mixing different radiation types* +![Unit Attachment](your image here) +*Attachments used in [mod name](link)* -- Allows to have custom radiation type for any weapon now. More details on radiation [here](https://www.modenc.renegadeprojects.com/Radiation). +```{warning} +This feature is not final and is under development. Currently unit-to-unit attachment with same locomotor is being prioritized. Other types of attachments are WIP and probably have many bugs and are mainly untested. +``` + +- Technos now can be attached one to another in a tree like way. The attached units won't process any locomotion code and act like a part of a parent unit in a configurable. In `rulesmd.ini`: ```ini -[RadiationTypes] -0=SOMERADTYPE - -[SOMEWEAPON] ; WeaponType -RadType=Radiation ; RadType to use instead - ; of default [Radiation] - -[SOMERADTYPE] ; custom RadType name -RadDurationMultiple=1 ; int -RadApplicationDelay=16 ; int -RadApplicationDelay.Building=0 ; int -RadLevelMax=500 ; int -RadLevelDelay=90 ; int -RadLightDelay=90 ; int -RadLevelFactor=0.2 ; double -RadLightFactor=0.1 ; double -RadTintFactor=1.0 ; double -RadColor=0,255,0 ; RGB -RadSiteWarhead=RadSite ; WarheadType +[AttachmentTypes] +0=MNT ; (example) + +[MNT] +RestoreAtCreation=yes ; boolean, whether to spawn the unit when it's created +InheritTilt=yes ; boolean, whether the child tilts with the parent +InheritOwner=no ; boolean, whether the child inherits owner of the parent while it's attached +InheritStateEffects=yes ; boolean (state effects = chaos, iron curtain etc.) +InheritCommands=yes ; boolean +DestructionWeapon.Child= ; WeaponType, detonated on child when parent is destroyed +DestructionWeapon.Parent= ; WeaponType, detonated on parent when child is destroyed +ForceDetachWeapon.Child= ; WeaponType, detonated on child when it is force detached +ForceDetachWeapon.Parent= ; WeaponType, detonated on parent when a child is force detached from it +ParentDestructionMission= ; MissionType, queued to child when parent is destroyed +ParentDetachmentMission= ; MissionType, queued to child when parent is destroyed + +[SUMTECHNO] +AttachmentX.Type=MNT ; AttachmentType (example) +AttachmentX.TechnoType= ; TechnoType that can be attached +AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height +AttachmentX.IsOnTurret=no ``` ### Laser Trails @@ -184,6 +190,36 @@ Shield.InheritStateOnReplace=false ; boolean - `Shield.MinimumReplaceDelay` can be used to control how long after the shield has been broken (in game frames) can it be replaced. If not enough frames have passed, it won't be replaced. - If `Shield.InheritStateOnReplace` is set, shields replaced via `Shield.ReplaceOnly` inherit the current strength (relative to ShieldType `Strength`) of the previous shield and whether or not the shield was currently broken. Self-healing and respawn timers are always reset. +### Radiation Types + +![image](_static/images/radtype-01.png) +*Mixing different radiation types* + +- Allows to have custom radiation type for any weapon now. More details on radiation [here](https://www.modenc.renegadeprojects.com/Radiation). + +In `rulesmd.ini`: +```ini +[RadiationTypes] +0=SOMERADTYPE + +[SOMEWEAPON] ; WeaponType +RadType=Radiation ; RadType to use instead + ; of default [Radiation] + +[SOMERADTYPE] ; custom RadType name +RadDurationMultiple=1 ; int +RadApplicationDelay=16 ; int +RadApplicationDelay.Building=0 ; int +RadLevelMax=500 ; int +RadLevelDelay=90 ; int +RadLightDelay=90 ; int +RadLevelFactor=0.2 ; double +RadLightFactor=0.1 ; double +RadTintFactor=1.0 ; double +RadColor=0,255,0 ; RGB +RadSiteWarhead=RadSite ; WarheadType +``` + ## Animations ### Anim-to-Unit diff --git a/docs/Whats-New.md b/docs/Whats-New.md index b536bd2cbf..66b2ed4cc0 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -217,6 +217,16 @@ This page lists the history of changes across stable Phobos releases and also al ## Changelog +### TBD + +
+ Click to show + +New: +- Techno Attachment logic (by Kerbiter) + +
+ ### 0.3
From 43b679fbf08efb47d636c0765e015e7d3b041c3a Mon Sep 17 00:00:00 2001 From: Metadorius Date: Sat, 23 Apr 2022 00:45:14 +0300 Subject: [PATCH 008/112] Stop command handling --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 47 +++++++++++++++++++++++ src/New/Entity/AttachmentClass.cpp | 8 ++-- src/New/Type/AttachmentTypeClass.cpp | 24 ++++++------ src/New/Type/AttachmentTypeClass.h | 23 ++++++----- 4 files changed, 74 insertions(+), 28 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 3ec1254923..8de7598d6f 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -159,6 +159,53 @@ DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) return 0x4AE99B; } + +namespace TechnoAttachmentTemp +{ + bool stopPressed = false; +} + +DEFINE_HOOK(0x730EA0, StopCommand_Context_Set, 0x5) +{ + TechnoAttachmentTemp::stopPressed = true; + return 0; +} + +namespace TechnoAttachmentTemp +{ + TechnoClass* pParent = nullptr; +} + +DEFINE_HOOK(0x6FFE00, TechnoClass_ClickedEvent_Context_Set, 0x5) +{ + TechnoAttachmentTemp::pParent = R->ECX(); + return 0; +} + +DEFINE_HOOK_AGAIN(0x6FFEB1, TechnoClass_ClickedEvent_HandleChildren, 0x6) +DEFINE_HOOK(0x6FFE4F, TechnoClass_ClickedEvent_HandleChildren, 0x6) +{ + if (TechnoAttachmentTemp::stopPressed && TechnoAttachmentTemp::pParent) + { + if (auto const& pExt = TechnoExt::ExtMap.Find(TechnoAttachmentTemp::pParent)) + { + for (auto const& pAttachment : pExt->ChildAttachments) + { + if (pAttachment->Child && pAttachment->GetType()->InheritCommands) + pAttachment->Child->ClickedEvent(NetworkEvents::Idle); + } + } + } + + return 0; +} + +DEFINE_HOOK(0x730F20, StopCommand_Context_Unset, 0x1) +{ + TechnoAttachmentTemp::stopPressed = false; + return 0; +} + /* namespace TechnoAttachmentTemp { diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 44dc955a56..09eceea72b 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -96,17 +96,17 @@ void AttachmentClass::AI() if (pType->InheritStateEffects) { this->Child->CloakState = this->Parent->CloakState; - // this->Child->BeingWarpedOut = this->Parent->BeingWarpedOut; + this->Child->BeingWarpedOut = this->Parent->BeingWarpedOut; this->Child->Deactivated = this->Parent->Deactivated; - // this->Child->Flash(this->Parent->Flashing.DurationRemaining); + 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->ChronoLockRemaining = this->Parent->ChronoLockRemaining; this->Child->Berzerk = this->Parent->Berzerk; - // this->Child->ChronoWarpedByHouse = this->Parent->ChronoWarpedByHouse; + this->Child->ChronoWarpedByHouse = this->Parent->ChronoWarpedByHouse; this->Child->EMPLockRemaining = this->Parent->EMPLockRemaining; this->Child->ShouldLoseTargetNow = this->Parent->ShouldLoseTargetNow; } diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 84ecf29b72..8fa4266c0b 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -21,13 +21,13 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) 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->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->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"); @@ -47,12 +47,12 @@ void AttachmentTypeClass::Serialize(T& Stm) .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->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) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index cbf1e620ae..1e99437c62 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -18,12 +18,11 @@ class AttachmentTypeClass final : public Enumerable // 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 + // Valueable SyncDamage; + // Valueable SyncDamage_IsRelative; + // Valueable SyncExperienceGain; + // Valueable SyncExperienceGain_IsRelative; + // Valueable CanBeForceDetached; Nullable ForceDetachWeapon_Child; Nullable ForceDetachWeapon_Parent; Nullable DestructionWeapon_Child; @@ -41,12 +40,12 @@ class AttachmentTypeClass final : public Enumerable , InheritCommands(false) , InheritOwner(false) , InheritStateEffects(true) - , SyncDamage(false) - , SyncDamage_IsRelative(false) - , SyncExperienceGain(false) - , SyncExperienceGain_IsRelative(false) - , CanBeForceDetached(false) - , RestoreAtHealth() + // , SyncDamage(false) + // , SyncDamage_IsRelative(false) + // , SyncExperienceGain(false) + // , SyncExperienceGain_IsRelative(false) + // , CanBeForceDetached(false) + // , RestoreAtHealth() , ForceDetachWeapon_Child() , ForceDetachWeapon_Parent() , DestructionWeapon_Child() From 645820a9b5f1d05d2f015a8730258d212e382a14 Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 10 Jul 2022 17:55:53 +0300 Subject: [PATCH 009/112] Fix YRpp --- YRpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YRpp b/YRpp index 4328a696b5..e53b654bfa 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 4328a696b57196e7e57babffee7708d168f9e518 +Subproject commit e53b654bfa718ecbac626452c76b59a4ff01208c From c703dffee6acf56af5d3451ba6ab9bd10dd1c133 Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 12 Jul 2022 17:10:16 +0300 Subject: [PATCH 010/112] Another pass at techno attachment --- YRpp | 2 +- docs/New-or-Enhanced-Logics.md | 8 +- src/Ext/Techno/Body.cpp | 17 ++++- src/Ext/Techno/Body.h | 5 +- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 91 ++++++++++++++++++++--- src/Ext/TechnoType/Hooks.cpp | 4 +- src/Ext/Unit/Hooks.DisallowMoving.cpp | 22 +++++- src/New/Entity/AttachmentClass.cpp | 52 ++++++++++--- src/New/Entity/AttachmentClass.h | 15 ++-- src/New/Type/AttachmentTypeClass.cpp | 16 ++-- src/New/Type/AttachmentTypeClass.h | 9 ++- src/Phobos.Ext.cpp | 4 +- 12 files changed, 194 insertions(+), 51 deletions(-) diff --git a/YRpp b/YRpp index e53b654bfa..0e2d339187 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit e53b654bfa718ecbac626452c76b59a4ff01208c +Subproject commit 0e2d3391875e9c7ee9d8023736fb1e077589a66d diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 4d682b0725..2f8d4e006f 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -14,6 +14,7 @@ This feature is not final and is under development. Currently unit-to-unit attac ``` - Technos now can be attached one to another in a tree like way. The attached units won't process any locomotion code and act like a part of a parent unit in a configurable. + - For now the attached techno may only be a vehicle. In `rulesmd.ini`: ```ini @@ -21,17 +22,22 @@ In `rulesmd.ini`: 0=MNT ; (example) [MNT] + InheritTilt=yes ; boolean, whether the child tilts with the parent -InheritOwner=no ; boolean, whether the child inherits owner of the parent while it's attached +InheritOwner=yes ; boolean, whether the child inherits owner of the parent while it's attached InheritStateEffects=yes ; boolean (state effects = chaos, iron curtain etc.) InheritCommands=yes ; boolean + [SUMTECHNO] AttachmentX.Type=MNT ; AttachmentType (example) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index a9422d8c5c..663f287f74 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -872,11 +872,22 @@ void TechnoExt::InitializeAttachments(TechnoClass* pThis) } } -void TechnoExt::HandleHostDestruction(TechnoClass* pThis) +void TechnoExt::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + for (auto const& pAttachment: pExt->ChildAttachments) - pAttachment->Uninitialize(); + pAttachment->Destroy(pSource); +} + +void TechnoExt::HandleDestructionAsChild(TechnoClass* pThis) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + if (pExt->ParentAttachment) + pExt->ParentAttachment->ChildDestroyed(); + + pExt->ParentAttachment = nullptr; } void TechnoExt::UnlimboAttachments(TechnoClass* pThis) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 956912f207..730db33b9d 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -64,6 +64,8 @@ class TechnoExt { if (auto const pShield = this->Shield.get()) pShield->InvalidatePointer(ptr); + for (auto const& pAttachment : ChildAttachments) + pAttachment->InvalidatePointer(ptr); } virtual void LoadFromStream(PhobosStreamReader& Stm) override; @@ -118,7 +120,8 @@ class TechnoExt static bool DetachFromParent(TechnoClass* pThis, bool force = false); static void InitializeAttachments(TechnoClass* pThis); - static void HandleHostDestruction(TechnoClass* pThis); + static void DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource); + static void HandleDestructionAsChild(TechnoClass* pThis); static void UnlimboAttachments(TechnoClass* pThis); static void LimboAttachments(TechnoClass* pThis); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 8de7598d6f..9119e032fd 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -2,6 +2,8 @@ #include +#include + DEFINE_HOOK(0x4DA86E, FootClass_AI_UpdateAttachedLocomotion, 0x0) { GET(FootClass* const, pThis, ESI); @@ -13,21 +15,18 @@ DEFINE_HOOK(0x4DA86E, FootClass_AI_UpdateAttachedLocomotion, 0x0) return 0x4DA87A; } -DEFINE_HOOK(0x710460, TechnoClass_Destroy_HandleAttachments, 0x6) +DEFINE_HOOK(0x707CB3, TechnoClass_KillCargo_HandleAttachments, 0x6) { - GET(TechnoClass*, pThis, ECX); - - TechnoExt::HandleHostDestruction(pThis); - - auto const pExt = TechnoExt::ExtMap.Find(pThis); - if (pExt->ParentAttachment) - pExt->ParentAttachment->ChildDestroyed(); + GET(TechnoClass*, pThis, EBX); + GET_STACK(TechnoClass*, pSource, STACK_OFFS(0x4, -0x4)); - pExt->ParentAttachment = nullptr; + TechnoExt::DestroyAttachments(pThis, pSource); return 0; } +// TODO HandleDestructionAsChild at 0x5F65F0 possibly + DEFINE_HOOK(0x6F6F20, TechnoClass_Unlimbo_UnlimboAttachments, 0x6) { GET(TechnoClass*, pThis, ESI); @@ -273,4 +272,76 @@ DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) // 0x6CCCCA Chrono Warp // 0x4694BB Temporal warhead // 0x4696FB Locomotor warhead -// ... \ No newline at end of file +// ... + +DEFINE_HOOK(0x6F3280, TechnoClass_CanScatter_CheckIfAttached, 0x5) +{ + enum { Return = 0x6F32C4, ContinueCheck = 0x0 }; + + GET(TechnoClass*, pThis, ECX); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + if (pExt->ParentAttachment) + { + R->EAX(0); + return Return; + } + + return ContinueCheck; +} + +// TODO handle scatter for InfantryClass and possibly AircraftClass + +DEFINE_HOOK(0x736FB6, UnitClass_FiringAI_ForbidAttachmentRotation, 0x6) +{ + enum { SkipBodyRotation = 0x737063, ContinueCheck = 0x0 }; + + GET(UnitClass*, pThis, ESI); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + return pExt->ParentAttachment + ? SkipBodyRotation + : ContinueCheck; +} + +DEFINE_HOOK(0x736A2F, UnitClass_RotationAI_ForbidAttachmentRotation, 0x7) +{ + enum { SkipBodyRotation = 0x736A8E, ContinueCheck = 0x0 }; + + GET(UnitClass*, pThis, ESI); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + return pExt->ParentAttachment + ? SkipBodyRotation + : ContinueCheck; +} + +Action __fastcall UnitClass_MouseOverCell(UnitClass* pThis, void* _, CellStruct const* pCell, bool checkFog, bool ignoreForce) +{ + JMP_THIS(0x7404B0); +} + +// The following two functions represenet +Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, void* _, CellStruct const* pCell, bool checkFog, bool ignoreForce) +{ + Action result = UnitClass_MouseOverCell(pThis, _, pCell, checkFog, ignoreForce); + + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + if (!pExt->ParentAttachment) + return result; + + switch (result) + { + case Action::GuardArea: + case Action::AttackMoveNav: + case Action::PatrolWaypoint: + case Action::Harvest: + case Action::Move: + result = Action::NoMove; + break; + } + + return result; +} + +DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) diff --git a/src/Ext/TechnoType/Hooks.cpp b/src/Ext/TechnoType/Hooks.cpp index 4ea1c895fe..5994c0eeb6 100644 --- a/src/Ext/TechnoType/Hooks.cpp +++ b/src/Ext/TechnoType/Hooks.cpp @@ -155,10 +155,10 @@ DEFINE_HOOK(0x73D223, UnitClass_DrawIt_OreGath, 0x6) DEFINE_HOOK(0x700C58, TechnoClass_CanPlayerMove_NoManualMove, 0x6) { GET(TechnoClass*, pThis, ESI); - auto const& pExt = TechnoExt::ExtMap.Find(pThis); + // auto const& pExt = TechnoExt::ExtMap.Find(pThis); auto const& pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->GetTechnoType()); - bool noMove = (pExt && pExt->ParentAttachment) || (pTypeExt && pTypeExt->NoManualMove); + bool noMove = pTypeExt && pTypeExt->NoManualMove; return noMove ? 0x700C62 : 0; } diff --git a/src/Ext/Unit/Hooks.DisallowMoving.cpp b/src/Ext/Unit/Hooks.DisallowMoving.cpp index 72187c7748..0359e6c696 100644 --- a/src/Ext/Unit/Hooks.DisallowMoving.cpp +++ b/src/Ext/Unit/Hooks.DisallowMoving.cpp @@ -4,27 +4,43 @@ #include "UnitClass.h" #include +#include + #include +#include DEFINE_HOOK(0x740A93, UnitClass_Mission_Move_DisallowMoving, 0x6) { + enum { QueueGuardInstead = 0x740AEF, ContinueCheck = 0x0 }; + GET(UnitClass*, pThis, ESI); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); - return pThis->Type->Speed == 0 ? 0x740AEF : 0; + // skips this->IsHarvesting = 0, may backfire somewhere - Kerbiter + return pThis->Type->Speed == 0 || pExt->ParentAttachment + ? QueueGuardInstead + : ContinueCheck; } DEFINE_HOOK(0x741AA7, UnitClass_Assign_Destination_DisallowMoving, 0x6) { + enum { ClearNavComsAndReturn = 0x743173, ContinueCheck = 0x0 }; + GET(UnitClass*, pThis, EBP); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); - return pThis->Type->Speed == 0 ? 0x743173 : 0; + return pThis->Type->Speed == 0 || pExt->ParentAttachment + ? ClearNavComsAndReturn + : ContinueCheck; } DEFINE_HOOK(0x743B4B, UnitClass_Scatter_DisallowMoving, 0x6) { + enum { ReleaseReturn = 0x74408E, ContinueCheck = 0x0 }; + GET(UnitClass*, pThis, EBP); - return pThis->Type->Speed == 0 ? 0x74408E : 0; + return pThis->Type->Speed == 0 ? ReleaseReturn : ContinueCheck; } DEFINE_HOOK(0x74038F, UnitClass_What_Action_ObjectClass_DisallowMoving_1, 0x6) diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 09eceea72b..d3bf9749b4 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -23,6 +23,20 @@ TechnoTypeClass* AttachmentClass::GetChildType() : nullptr; } +AttachmentClass::~AttachmentClass() +{ + // clear up non-owning references + if (this->Child) + { + auto const& pChildExt = TechnoExt::ExtMap.Find(Child); + pChildExt->ParentAttachment = nullptr; + } + + auto position = std::find(Array.begin(), Array.end(), this); + if (position != Array.end()) + Array.erase(position); +} + void AttachmentClass::Initialize() { if (this->Child) @@ -36,6 +50,9 @@ void AttachmentClass::CreateChild() { if (auto const pChildType = this->GetChildType()) { + if (pChildType->WhatAmI() != AbstractType::UnitType) + return; + if (this->Child = static_cast(pChildType->CreateObject(this->Parent->Owner))) { auto const pChildExt = TechnoExt::ExtMap.Find(this->Child); @@ -116,20 +133,28 @@ void AttachmentClass::AI() } } -// Doesn't call destructor (to be managed by smart pointers) -void AttachmentClass::Uninitialize() +void AttachmentClass::Destroy(TechnoClass* pSource) { if (this->Child) { + auto pChildExt = TechnoExt::ExtMap.Find(this->Child); + pChildExt->ParentAttachment = nullptr; + 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); + // if (pType->DestructionWeapon_Child.isset()) + // TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); + + if (pType->InheritDestruction && this->Child) + { + this->Child->KillPassengers(pSource); + this->Child->RegisterDestruction(pSource); + this->Child->UnInit(); + } + + // 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; } } @@ -217,6 +242,13 @@ bool AttachmentClass::DetachChild(bool isForceDetachment) return false; } + +void AttachmentClass::InvalidatePointer(void* ptr) +{ + AnnounceInvalidPointer(this->Parent, ptr); + AnnounceInvalidPointer(this->Child, ptr); +} + #pragma region Save/Load template @@ -227,7 +259,7 @@ bool AttachmentClass::Serialize(T& stm) .Process(this->Parent) .Process(this->Child) .Success(); -}; +} bool AttachmentClass::Load(PhobosStreamReader& stm, bool RegisterForChange) { @@ -239,4 +271,4 @@ bool AttachmentClass::Save(PhobosStreamWriter& stm) const return const_cast(this)->Serialize(stm); } -#pragma endregion \ No newline at end of file +#pragma endregion diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index c601ab55fc..fb0699e84a 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -7,6 +7,8 @@ #include #include +class TechnoClass; + class AttachmentClass { public: @@ -33,12 +35,7 @@ class AttachmentClass Array.push_back(this); } - ~AttachmentClass() - { - auto position = std::find(Array.begin(), Array.end(), this); - if (position != Array.end()) - Array.erase(position); - } + ~AttachmentClass(); AttachmentTypeClass* GetType(); TechnoTypeClass* GetChildType(); @@ -46,7 +43,7 @@ class AttachmentClass void Initialize(); void CreateChild(); void AI(); - void Uninitialize(); + void Destroy(TechnoClass* pSource); void ChildDestroyed(); void Unlimbo(); @@ -55,10 +52,12 @@ class AttachmentClass bool AttachChild(TechnoClass* pChild); bool DetachChild(bool force = false); + void InvalidatePointer(void* ptr); + 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 index 8fa4266c0b..32dfd3c1cf 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -15,11 +15,12 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) INI_EX exINI(pINI); - this->RestoreAtCreation.Read(exINI, section, "RestoreAtCreation"); + // 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->InheritDestruction.Read(exINI, section, "InheritDestruction"); // this->SyncDamage.Read(exINI, section, "SyncDamage"); // this->SyncDamage_IsRelative.Read(exINI, section, "SyncDamage.IsRelative"); @@ -29,13 +30,13 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) // 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->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"); + // this->ParentDestructionMission.Read(exINI, section, "ParentDestructionMission"); + // this->ParentDetachmentMission.Read(exINI, section, "ParentDetachmentMission"); } template @@ -47,6 +48,7 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->InheritCommands) .Process(this->InheritOwner) .Process(this->InheritStateEffects) + .Process(this->InheritDestruction) // .Process(this->SyncDamage) // .Process(this->SyncDamage_IsRelative) // .Process(this->SyncExperienceGain) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 1e99437c62..6fdaf57736 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -15,6 +15,7 @@ class AttachmentTypeClass final : public Enumerable Valueable InheritCommands; Valueable InheritOwner; //aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. + Valueable InheritDestruction; // 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 @@ -33,13 +34,13 @@ class AttachmentTypeClass final : public Enumerable // 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) + , InheritCommands(true) + , InheritOwner(true) , InheritStateEffects(true) + , InheritDestruction(true) // , SyncDamage(false) // , SyncDamage_IsRelative(false) // , SyncExperienceGain(false) @@ -63,4 +64,4 @@ class AttachmentTypeClass final : public Enumerable private: template void Serialize(T& Stm); -}; \ No newline at end of file +}; diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 33a811678f..e634f4f5d6 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -251,7 +251,9 @@ auto MassActions = MassAction < // New classes ShieldTypeClass, LaserTrailTypeClass, - RadTypeClass + RadTypeClass, + AttachmentClass, + AttachmentTypeClass // other classes > (); From 580d9c896e0dd5f77adb5f1c8b8a541efbb68d7c Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 12 Jul 2022 22:26:36 +0300 Subject: [PATCH 011/112] Fix scatter hook incompatibility with Ares; handle IsLocomotor --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 29 ++++++++++++++--------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 9119e032fd..ed6ad0ab69 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -271,26 +271,33 @@ DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) // 0x457C90 IC (forceshield) for buildings // 0x6CCCCA Chrono Warp // 0x4694BB Temporal warhead -// 0x4696FB Locomotor warhead // ... -DEFINE_HOOK(0x6F3280, TechnoClass_CanScatter_CheckIfAttached, 0x5) +DEFINE_HOOK(0x469672, BulletClass_Logics_Locomotor_CheckIfAttached, 0x6) { - enum { Return = 0x6F32C4, ContinueCheck = 0x0 }; + enum { SkipInfliction = 0x469AA4, ContinueCheck = 0x0 }; - GET(TechnoClass*, pThis, ECX); + GET(FootClass*, pThis, EDI); auto const& pExt = TechnoExt::ExtMap.Find(pThis); - if (pExt->ParentAttachment) - { - R->EAX(0); - return Return; - } + return pExt->ParentAttachment + ? SkipInfliction + : ContinueCheck; +} - return ContinueCheck; +DEFINE_HOOK(0x6F3283, TechnoClass_CanScatter_CheckIfAttached, 0x8) +{ + enum { EpilogFalse = 0x6F32C5, ContinueCheck = 0x0 }; + + GET(TechnoClass*, pThis, ECX); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + return pExt->ParentAttachment + ? EpilogFalse + : ContinueCheck; } -// TODO handle scatter for InfantryClass and possibly AircraftClass +// TODO maybe? handle scatter for InfantryClass and possibly AircraftClass DEFINE_HOOK(0x736FB6, UnitClass_FiringAI_ForbidAttachmentRotation, 0x6) { From cec6eabcbae782cfdf391e5b33e08068fb87811c Mon Sep 17 00:00:00 2001 From: Meta Date: Wed, 13 Jul 2022 23:41:29 +0300 Subject: [PATCH 012/112] Fixed stuff - Fixed destruction crash - Fixed command handling - FireError handling for Locomotor warheads - Implement low priority for technos, refactor low priority - Clean up some stuff --- src/Ext/Techno/Body.cpp | 25 ++--- src/Ext/Techno/Body.h | 3 +- src/Ext/Techno/Hooks.Shield.cpp | 34 ++++++- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 106 ++++++++++++++++++++-- src/Misc/Selection.cpp | 64 ++++++++----- src/New/Type/AttachmentTypeClass.cpp | 13 +-- src/New/Type/AttachmentTypeClass.h | 47 ++++------ 7 files changed, 204 insertions(+), 88 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 6e984e3f24..ae717b7d08 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -923,24 +923,19 @@ void TechnoExt::LimboAttachments(TechnoClass* pThis) pAttachment->Limbo(); } -bool TechnoExt::IsParentOf(TechnoClass* pThis, TechnoClass* pOtherTechno) +bool TechnoExt::IsAttached(TechnoClass* pThis) { - auto const pExt = TechnoExt::ExtMap.Find(pThis); - - if (!pOtherTechno) - return false; + return TechnoExt::ExtMap.Find(pThis)->ParentAttachment; +} - for (auto const& pAttachment: pExt->ChildAttachments) - { - if (pAttachment->Child && - (pAttachment->Child == pOtherTechno || - TechnoExt::IsParentOf(pAttachment->Child, pOtherTechno))) - { - return true; - } - } +bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); - return false; + return pParent + && pThisExt->ParentAttachment + && (pThisExt->ParentAttachment->Parent == pParent + || (deep && TechnoExt::IsChildOf(pThisExt->ParentAttachment->Parent, pParent))); } // ============================= diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 8223b12e1a..f501b9509a 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -124,7 +124,8 @@ class TechnoExt static void UnlimboAttachments(TechnoClass* pThis); static void LimboAttachments(TechnoClass* pThis); - static bool IsParentOf(TechnoClass* pThis, TechnoClass* pOtherTechno); + static bool IsAttached(TechnoClass* pThis); + static bool IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep = true); static void FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponType); diff --git a/src/Ext/Techno/Hooks.Shield.cpp b/src/Ext/Techno/Hooks.Shield.cpp index 46045fd98d..40157c71fc 100644 --- a/src/Ext/Techno/Hooks.Shield.cpp +++ b/src/Ext/Techno/Hooks.Shield.cpp @@ -314,8 +314,37 @@ Action __fastcall UnitClass__WhatAction(UnitClass* pThis, void*_, ObjectClass* p Action __fastcall UnitClass__WhatAction_Wrapper(UnitClass* pThis, void*_, ObjectClass* pObj, bool ignoreForce) { AresScheme::Prefix(pObj); - auto const result = UnitClass__WhatAction(pThis, _, pObj, ignoreForce); + Action result = UnitClass__WhatAction(pThis, _, pObj, ignoreForce); AresScheme::Suffix(); + + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + if (!pExt->ParentAttachment) + return result; + + switch (result) + { + case Action::Repair: + result = Action::NoRepair; + break; + + case Action::Self_Deploy: + if (pThis->Type->DeploysInto) + result = Action::NoDeploy; + break; + + case Action::Sabotage: + case Action::Capture: + case Action::Enter: + result = Action::NoEnter; + break; + + case Action::GuardArea: + case Action::AttackMoveNav: + case Action::Move: + result = Action::NoMove; + break; + } + return result; } DEFINE_JUMP(VTABLE, 0x7F5CE4, GET_OFFSET(UnitClass__WhatAction_Wrapper)) @@ -330,8 +359,9 @@ Action __fastcall InfantryClass__WhatAction(InfantryClass* pThis, void*_, Object Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis, void*_, ObjectClass* pObj, bool ignoreForce) { AresScheme::Prefix(pObj); - auto const result = InfantryClass__WhatAction(pThis, _, pObj, ignoreForce); + Action result = InfantryClass__WhatAction(pThis, _, pObj, ignoreForce); AresScheme::Suffix(); + return result; } DEFINE_JUMP(VTABLE, 0x7EB0CC, GET_OFFSET(InfantryClass__WhatAction_Wrapper)) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index ed6ad0ab69..4e6b01ab00 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -1,5 +1,7 @@ #include "Body.h" +#include + #include #include @@ -25,7 +27,16 @@ DEFINE_HOOK(0x707CB3, TechnoClass_KillCargo_HandleAttachments, 0x6) return 0; } -// TODO HandleDestructionAsChild at 0x5F65F0 possibly +DEFINE_HOOK(0x5F6609, ObjectClass_RemoveThis_TechnoClass, 0x9) +{ + GET(TechnoClass*, pThis, ESI); + + pThis->KillPassengers(nullptr); // restored code + TechnoExt::HandleDestructionAsChild(pThis); + + return 0x5F6612; +} + DEFINE_HOOK(0x6F6F20, TechnoClass_Unlimbo_UnlimboAttachments, 0x6) { @@ -52,7 +63,7 @@ DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) GET(UnitClass*, pThis, EBX); GET(TechnoClass*, pOccupier, ESI); - if (pThis == pOccupier || TechnoExt::IsParentOf(pThis, pOccupier)) + if (pThis == pOccupier || TechnoExt::IsChildOf(pOccupier, pThis)) return IgnoreOccupier; return Continue; @@ -65,7 +76,7 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) GET(InfantryClass*, pThis, EBP); GET(TechnoClass*, pOccupier, ESI); - if ((TechnoClass*)pThis == pOccupier || TechnoExt::IsParentOf((TechnoClass*)pThis, pOccupier)) + if ((TechnoClass*)pThis == pOccupier || TechnoExt::IsChildOf(pOccupier, (TechnoClass*)pThis)) return IgnoreOccupier; return Continue; @@ -285,15 +296,37 @@ DEFINE_HOOK(0x469672, BulletClass_Logics_Locomotor_CheckIfAttached, 0x6) : ContinueCheck; } +DEFINE_HOOK(0x6FC3F4, TechnoClass_CanFire_HandleAttachmentLogics, 0x6) +{ + enum { ReturnFireErrorIllegal = 0x6FC86A, ContinueCheck = 0x0 }; + + GET(TechnoClass*, pThis, ESI); + GET(TechnoClass*, pTarget, EBP); + GET(WeaponTypeClass*, pWeapon, EDI); + + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + auto const& pTargetExt = TechnoExt::ExtMap.Find(pTarget); + + bool illegalParentTargetWarhead = pWeapon->Warhead + && pWeapon->Warhead->IsLocomotor; + + if (illegalParentTargetWarhead && TechnoExt::IsChildOf(pThis, pTarget)) + return ReturnFireErrorIllegal; + + return ContinueCheck; +} + +// TODO WhatWeaponShouldIUse + DEFINE_HOOK(0x6F3283, TechnoClass_CanScatter_CheckIfAttached, 0x8) { - enum { EpilogFalse = 0x6F32C5, ContinueCheck = 0x0 }; + enum { ReturnFalse = 0x6F32C5, ContinueCheck = 0x0 }; GET(TechnoClass*, pThis, ECX); auto const& pExt = TechnoExt::ExtMap.Find(pThis); return pExt->ParentAttachment - ? EpilogFalse + ? ReturnFalse : ContinueCheck; } @@ -328,7 +361,6 @@ Action __fastcall UnitClass_MouseOverCell(UnitClass* pThis, void* _, CellStruct JMP_THIS(0x7404B0); } -// The following two functions represenet Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, void* _, CellStruct const* pCell, bool checkFog, bool ignoreForce) { Action result = UnitClass_MouseOverCell(pThis, _, pCell, checkFog, ignoreForce); @@ -346,9 +378,71 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, void* _, Cel case Action::Move: result = Action::NoMove; break; + case Action::EnterTunnel: + result = Action::NoEnterTunnel; + break; } return result; } DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) + +DEFINE_HOOK(0x4D74EC, FootClass_ObjectClickedAction_HandleAttachment, 0x6) +{ + enum { ReturnFalse = 0x4D77EC, Continue = 0x0 }; + + GET(FootClass*, pThis, ESI); + GET_STACK(Action, action, STACK_OFFS(0x108, -0x4)); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + if (!pExt->ParentAttachment) + return Continue; + + switch (action) + { + case Action::Move: + case Action::AttackMoveNav: + case Action::NoMove: + case Action::Enter: + case Action::NoEnter: + case Action::Capture: + case Action::Repair: + case Action::Sabotage: + case Action::GuardArea: + return ReturnFalse; + default: + return Continue; + } +} + +DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) +{ + enum { ReturnFalse = 0x4D7D62, Continue = 0x0 }; + + GET(FootClass*, pThis, ESI); + GET_STACK(Action, action, STACK_OFFS(0x24, -0x4)); + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + if (!pExt->ParentAttachment) + return Continue; + + switch (action) + { + case Action::Move: + case Action::AttackMoveNav: + case Action::NoMove: + case Action::Enter: + case Action::NoEnter: + case Action::Harvest: + case Action::Capture: + case Action::Sabotage: + case Action::GuardArea: + case Action::EnterTunnel: + case Action::NoEnterTunnel: + case Action::PatrolWaypoint: + return ReturnFalse; + default: + return Continue; + } +} diff --git a/src/Misc/Selection.cpp b/src/Misc/Selection.cpp index 9060d6c5d7..ff2c1d3741 100644 --- a/src/Misc/Selection.cpp +++ b/src/Misc/Selection.cpp @@ -1,6 +1,10 @@ #include "Phobos.h" -#include "Utilities/Macro.h" -#include "Ext/TechnoType/Body.h" +#include + +#include +#include +#include +#include #include #include @@ -47,10 +51,8 @@ class ExtSelection int nLocalX = selectable.X - pThis->TacticalPos.X; int nLocalY = selectable.Y - pThis->TacticalPos.Y; - if ((nLocalX >= pRect->Left && nLocalX < pRect->Right + pRect->Left) && - (nLocalY >= pRect->Top && nLocalY < pRect->Bottom + pRect->Top)) { - return true; - } + return (nLocalX >= pRect->Left && nLocalX < pRect->Right + pRect->Left) && + (nLocalY >= pRect->Top && nLocalY < pRect->Bottom + pRect->Top); } return false; } @@ -58,15 +60,22 @@ class ExtSelection static bool Tactical_IsHighPriorityInRect(TacticalClass* pThis, LTRBStruct* rect) { for (const auto& selected : Array) + { if (Tactical_IsInSelectionRect(pThis, rect, selected) && ObjectClass_IsSelectable(selected.Techno)) - if (!TechnoTypeExt::ExtMap.Find(selected.Techno->GetTechnoType())->LowSelectionPriority) - return true; + { + auto const& pExt = TechnoExt::ExtMap.Find(selected.Techno); + auto const& pTypeExt = TechnoTypeExt::ExtMap.Find(selected.Techno->GetTechnoType()); + + bool isLowPriorityByAttachment = pExt->ParentAttachment && pExt->ParentAttachment->GetType()->LowSelectionPriority; + return !pTypeExt->LowSelectionPriority && !isLowPriorityByAttachment; + } + } return false; } static // Reversed from Tactical::Select - void Tactical_SelectFiltered(TacticalClass* pThis, LTRBStruct* pRect, callback_type check_callback, bool bPriorityFiltering) + void Tactical_SelectFiltered(TacticalClass* pThis, LTRBStruct* pRect, callback_type fpCheckCallback, bool bFilter) { Unsorted::MoveFeedback = true; @@ -74,23 +83,33 @@ class ExtSelection return; for (const auto& selected : Array) + { if (Tactical_IsInSelectionRect(pThis, pRect, selected)) { - const auto pTechno = selected.Techno; - auto pTechnoType = pTechno->GetTechnoType(); - auto TypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + auto const& pTechno = selected.Techno; + auto const& pExt = TechnoExt::ExtMap.Find(pTechno); + auto const& pTechnoType = pTechno->GetTechnoType(); + auto const& pTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType); + + // Attached units shouldn't be selected regardless of the setting + bool isLowPriorityByAttachment = pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->LowSelectionPriority; + bool isLowPriorityByTechno = Phobos::Config::PrioritySelectionFiltering && pTypeExt && pTypeExt->LowSelectionPriority; - if (bPriorityFiltering && TypeExt && TypeExt->LowSelectionPriority) + if (bFilter && (isLowPriorityByAttachment || isLowPriorityByTechno)) continue; - if (TypeExt && Game::IsTypeSelecting()) - Game::UICommands_TypeSelect_7327D0(TypeExt->GetSelectionGroupID()); - else if (check_callback) - (*check_callback)(pTechno); + if (pTypeExt && Game::IsTypeSelecting()) + { + Game::UICommands_TypeSelect_7327D0(pTypeExt->GetSelectionGroupID()); + } + else if (fpCheckCallback) + { + (*fpCheckCallback)(pTechno); + } else { - const auto pBldType = abstract_cast(pTechnoType); - const auto pOwner = pTechno->GetOwningHouse(); + const auto& pBldType = abstract_cast(pTechnoType); + const auto& pOwner = pTechno->GetOwningHouse(); if (pOwner && pOwner->ControlledByPlayer() && pTechno->CanBeSelected() && (!pBldType || (pBldType && pBldType->UndeploysInto && pBldType->IsUndeployable()))) @@ -99,12 +118,13 @@ class ExtSelection } } } + } Unsorted::MoveFeedback = true; } static // Reversed from Tactical::MakeSelection - void __fastcall Tactical_MakeFilteredSelection(TacticalClass* pThis, void*_, callback_type check_callback) + void __fastcall Tactical_MakeFilteredSelection(TacticalClass* pThis, void*_, callback_type fpCheckCallback) { if (pThis->Band.Left || pThis->Band.Top) { int nLeft = pThis->Band.Left; @@ -119,8 +139,8 @@ class ExtSelection LTRBStruct rect { nLeft , nTop, nRight - nLeft + 1, nBottom - nTop + 1 }; - bool bPriorityFiltering = Phobos::Config::PrioritySelectionFiltering && Tactical_IsHighPriorityInRect(pThis, &rect); - Tactical_SelectFiltered(pThis, &rect, check_callback, bPriorityFiltering); + Tactical_SelectFiltered(pThis, &rect, fpCheckCallback, + Tactical_IsHighPriorityInRect(pThis, &rect)); pThis->Band.Left = 0; pThis->Band.Top = 0; diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 32dfd3c1cf..21d34bafcd 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -21,14 +21,9 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->InheritOwner.Read(exINI, section, "InheritOwner"); this->InheritStateEffects.Read(exINI, section, "InheritStateEffects"); // this->InheritDestruction.Read(exINI, section, "InheritDestruction"); - - // 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->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); // 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"); @@ -49,12 +44,8 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->InheritOwner) .Process(this->InheritStateEffects) .Process(this->InheritDestruction) - // .Process(this->SyncDamage) - // .Process(this->SyncDamage_IsRelative) - // .Process(this->SyncExperienceGain) - // .Process(this->SyncExperienceGain_IsRelative) + .Process(this->LowSelectionPriority) // .Process(this->CanBeForceDetached) - // .Process(this->RestoreAtHealth) .Process(this->ForceDetachWeapon_Child) .Process(this->ForceDetachWeapon_Parent) .Process(this->DestructionWeapon_Child) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 6fdaf57736..ecce859df6 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -9,20 +9,12 @@ 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 InheritOwner; // aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. Valueable InheritDestruction; - // 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 LowSelectionPriority; // Valueable CanBeForceDetached; Nullable ForceDetachWeapon_Child; Nullable ForceDetachWeapon_Parent; @@ -31,28 +23,21 @@ class AttachmentTypeClass final : public Enumerable 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(true) - , InheritOwner(true) - , InheritStateEffects(true) - , InheritDestruction(true) - // , SyncDamage(false) - // , SyncDamage_IsRelative(false) - // , SyncExperienceGain(false) - // , SyncExperienceGain_IsRelative(false) - // , CanBeForceDetached(false) - // , RestoreAtHealth() - , ForceDetachWeapon_Child() - , ForceDetachWeapon_Parent() - , DestructionWeapon_Child() - , DestructionWeapon_Parent() - , ParentDestructionMission() - , ParentDetachmentMission() + , RestoreAtCreation { true } + , InheritTilt { true } + , InheritCommands { true } + , InheritOwner { true } + , InheritStateEffects { true } + , InheritDestruction { true } + , LowSelectionPriority { true } + // , CanBeForceDetached { false } + , ForceDetachWeapon_Child { } + , ForceDetachWeapon_Parent { } + , DestructionWeapon_Child { } + , DestructionWeapon_Parent { } + , ParentDestructionMission { } + , ParentDetachmentMission { } { } virtual ~AttachmentTypeClass() override = default; From 39158bae96bd31a16c6b1708c5ae54e3f235a935 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 14 Jul 2022 00:23:58 +0300 Subject: [PATCH 013/112] Add sanity check (why does it even crash?) --- src/Ext/Techno/Body.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index ae717b7d08..d50ce73ea4 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -932,7 +932,7 @@ bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) { auto const pThisExt = TechnoExt::ExtMap.Find(pThis); - return pParent + return pThis && pThisExt && pParent // sanity check, sometimes crashes because ext is null - Kerbiter && pThisExt->ParentAttachment && (pThisExt->ParentAttachment->Parent == pParent || (deep && TechnoExt::IsChildOf(pThisExt->ParentAttachment->Parent, pParent))); From db9f830009b94b64cb33f9b56bd17f403f7f9d39 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 14 Jul 2022 00:42:38 +0300 Subject: [PATCH 014/112] Fix re(tard)factoring --- src/Misc/Selection.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Misc/Selection.cpp b/src/Misc/Selection.cpp index ff2c1d3741..b63856d85e 100644 --- a/src/Misc/Selection.cpp +++ b/src/Misc/Selection.cpp @@ -67,7 +67,8 @@ class ExtSelection auto const& pTypeExt = TechnoTypeExt::ExtMap.Find(selected.Techno->GetTechnoType()); bool isLowPriorityByAttachment = pExt->ParentAttachment && pExt->ParentAttachment->GetType()->LowSelectionPriority; - return !pTypeExt->LowSelectionPriority && !isLowPriorityByAttachment; + if (!pTypeExt->LowSelectionPriority && !isLowPriorityByAttachment) + return true; } } From 7ddae7e73fb3df1f72f42628136ccb03e2e9efb6 Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 19 Jul 2022 15:44:33 +0300 Subject: [PATCH 015/112] Add failsafe check --- src/Ext/Techno/Body.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 177ed12667..39aae5a05b 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -941,7 +941,8 @@ void TechnoExt::LimboAttachments(TechnoClass* pThis) bool TechnoExt::IsAttached(TechnoClass* pThis) { - return TechnoExt::ExtMap.Find(pThis)->ParentAttachment; + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + return pExt && pExt->ParentAttachment; } bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) From 5b44a9ffc9e093d44716aa18ea041032ad1ad3b7 Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 19 Jul 2022 15:45:16 +0300 Subject: [PATCH 016/112] Add ShipLoco support --- YRpp | 2 +- src/New/Entity/AttachmentClass.cpp | 10 ++++++++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/YRpp b/YRpp index 0e2d339187..eaa088ee40 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 0e2d3391875e9c7ee9d8023736fb1e077589a66d +Subproject commit eaa088ee401e03ec354e00a90912c0abf78d0681 diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index d3bf9749b4..1fc8ce55ab 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -97,9 +97,15 @@ void AttachmentClass::AI() 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) + if (SUCCEEDED(pParentLoco->GetClassID(&locoCLSID)) + && (locoCLSID == LocomotionClass::CLSIDs::Drive + || locoCLSID == LocomotionClass::CLSIDs::Ship) && + SUCCEEDED(pChildLoco->GetClassID(&locoCLSID)) + && (locoCLSID == LocomotionClass::CLSIDs::Drive + || locoCLSID == LocomotionClass::CLSIDs::Ship)) { + // shh DriveLocomotionClass almost equates to ShipLocomotionClass + // for this particular case it's OK to cast to it - Kerbiter auto pParentDriveLoco = static_cast(pParentLoco); auto pChildDriveLoco = static_cast(pChildLoco); From 7e74eb4c16b0a6c43b025939291826e344ead5f6 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 17 Sep 2022 23:36:43 +0300 Subject: [PATCH 017/112] Attempt to fix lag --- src/Ext/Techno/Body.cpp | 4 +++- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 19 +++++++++++++++++++ src/New/Entity/AttachmentClass.cpp | 8 ++++++-- src/New/Entity/AttachmentClass.h | 1 + 4 files changed, 29 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 39aae5a05b..75312fc6be 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -17,6 +17,8 @@ #include #include +#include + template<> const DWORD Extension::Canary = 0x55555555; TechnoExt::ExtContainer TechnoExt::ExtMap; @@ -286,7 +288,7 @@ CoordStruct TechnoExt::GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct pCoo // Step 5: apply as an offset to global object coords CoordStruct location = pThis->GetCoords(); - location += { (int)result.X, (int)result.Y, (int)result.Z }; + location += { std::lround(result.X), std::lround(result.Y), std::lround(result.Z) }; return location; } diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 4e6b01ab00..01dbbec241 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -446,3 +446,22 @@ DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) return Continue; } } + +// DEFINE_HOOK(0x41BE02, AbstractClass_RenderCoord_AttachedCoord, 0x7) +// { +// enum { Return = 0x41BE2A, Continue = 0x0 }; + +// GET(AbstractClass* const, pThis, ECX); + +// if (auto const& pThisAsTechno = abstract_cast(pThis)) +// { +// auto const& pExt = TechnoExt::ExtMap.Find(pThisAsTechno); +// if (pExt && pExt->ParentAttachment) +// { +// R->EAX(&pExt->ParentAttachment->GetChildLocation()); +// return Return; +// } +// } + +// return Continue; +// } diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 1fc8ce55ab..6b0dc0afe3 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -23,6 +23,11 @@ TechnoTypeClass* AttachmentClass::GetChildType() : nullptr; } +CoordStruct AttachmentClass::GetChildLocation() +{ + return TechnoExt::GetFLHAbsoluteCoords(this->Parent, this->Data->FLH, this->Data->IsOnTurret); +} + AttachmentClass::~AttachmentClass() { // clear up non-owning references @@ -72,8 +77,7 @@ void AttachmentClass::AI() if (this->Child) { - this->Child->SetLocation(TechnoExt::GetFLHAbsoluteCoords( - this->Parent, this->Data->FLH, this->Data->IsOnTurret)); + this->Child->SetLocation(this->GetChildLocation()); this->Child->OnBridge = this->Parent->OnBridge; diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index fb0699e84a..b156ee7a66 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -39,6 +39,7 @@ class AttachmentClass AttachmentTypeClass* GetType(); TechnoTypeClass* GetChildType(); + CoordStruct GetChildLocation(); void Initialize(); void CreateChild(); From b85f751973b50c25e7eadb66d934de1c120b0c42 Mon Sep 17 00:00:00 2001 From: Meta Date: Mon, 19 Sep 2022 12:16:33 +0300 Subject: [PATCH 018/112] Update YRpp --- YRpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YRpp b/YRpp index eaa088ee40..928b8eb894 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit eaa088ee401e03ec354e00a90912c0abf78d0681 +Subproject commit 928b8eb894bfbf2d6e65a3ef61a3ae8b35b5f1ad From f240be5454ee36c5f4d7b05bb015c620a54d07f1 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 24 Sep 2022 22:00:24 +0300 Subject: [PATCH 019/112] BROKEN attempt at rendering from parent's origin --- YRpp | 2 +- src/Ext/AnimType/Hooks.cpp | 2 +- src/Ext/Building/Hooks.cpp | 2 +- src/Ext/Techno/Body.cpp | 62 +++++++++++++---- src/Ext/Techno/Body.h | 8 ++- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 83 +++++++++++++++++++++++ src/New/Entity/AttachmentClass.cpp | 76 ++++++++++++++------- src/New/Entity/AttachmentClass.h | 13 ++++ 8 files changed, 207 insertions(+), 41 deletions(-) diff --git a/YRpp b/YRpp index 928b8eb894..b36fa814d6 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 928b8eb894bfbf2d6e65a3ef61a3ae8b35b5f1ad +Subproject commit b36fa814d6abe994622df4bfdfabd033f325e064 diff --git a/src/Ext/AnimType/Hooks.cpp b/src/Ext/AnimType/Hooks.cpp index 4dedd81f65..a4f9b4816a 100644 --- a/src/Ext/AnimType/Hooks.cpp +++ b/src/Ext/AnimType/Hooks.cpp @@ -63,7 +63,7 @@ DEFINE_HOOK(0x424C49, AnimClass_AttachTo_BuildingCoords, 0x5) if (pExt->UseCenterCoordsIfAttached) { - pCoords = pObject->GetCenterCoord(pCoords); + pCoords = pObject->GetRenderCoords(pCoords); pCoords->X += 128; pCoords->Y += 128; } diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index a682d6d467..68fc6312b8 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -102,7 +102,7 @@ DEFINE_HOOK(0x43FE73, BuildingClass_AI_FlyingStrings, 0x6) swprintf_s(moneyStr, L"%ls%ls%d", isPositive ? L"+" : L"-", Phobos::UI::CostLabel, std::abs(refundAmount)); auto coords = CoordStruct::Empty; - coords = *pThis->GetCenterCoord(&coords); + coords = *pThis->GetRenderCoords(&coords); int width = 0, height = 0; BitFont::Instance->GetTextDimension(moneyStr, &width, &height, 120); diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 75312fc6be..448452415f 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -253,20 +253,23 @@ void TechnoExt::FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponTyp WeaponTypeExt::DetonateAt(pWeaponType, pThis, pThis); } -// reversed from 6F3D60 -CoordStruct TechnoExt::GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret) +Matrix3D TechnoExt::GetTransform(TechnoClass* pThis, int* pKey) { - auto const pType = pThis->GetTechnoType(); - auto const pFoot = abstract_cast(pThis); Matrix3D mtx; - // Step 1: get body transform matrix - if (pFoot && pFoot->Locomotor) - mtx = pFoot->Locomotor->Draw_Matrix(nullptr); + if ((pThis->AbstractFlags & AbstractFlags::Foot) && ((FootClass*)pThis)->Locomotor) + mtx = ((FootClass*)pThis)->Locomotor->Draw_Matrix(pKey); else // no locomotor means no rotation or transform of any kind (f.ex. buildings) - Kerbiter mtx.MakeIdentity(); - // Steps 2-3: turret offset and rotation + return mtx; +} + +Matrix3D TechnoExt::TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret) +{ + auto const pType = pThis->GetTechnoType(); + + // turret offset and rotation if (isOnTurret && pThis->HasTurret()) { TechnoTypeExt::ApplyTurretOffset(pType, &mtx); @@ -278,15 +281,29 @@ CoordStruct TechnoExt::GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct pCoo mtx.RotateZ(angle); } - // Step 4: apply FLH offset + return mtx; +} + +Matrix3D TechnoExt::GetFLHMatrix(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret) +{ + Matrix3D mtx = TechnoExt::TransformFLHForTurret(pThis, TechnoExt::GetTransform(pThis), isOnTurret); + + // apply FLH offset mtx.Translate((float)pCoord.X, (float)pCoord.Y, (float)pCoord.Z); - Vector3D result = Matrix3D::MatrixMultiply(mtx, Vector3D::Empty); + return mtx; +} + +// reversed from 6F3D60 +CoordStruct TechnoExt::GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret) +{ + Vector3D result = Matrix3D::MatrixMultiply( + TechnoExt::GetFLHMatrix(pThis, pCoord, isOnTurret), Vector3D::Empty); // Resulting coords are mirrored along X axis, so we mirror it back result.Y *= -1; - // Step 5: apply as an offset to global object coords + // apply as an offset to global object coords CoordStruct location = pThis->GetCoords(); location += { std::lround(result.X), std::lround(result.Y), std::lround(result.Z) }; @@ -847,7 +864,7 @@ void TechnoExt::DisplayDamageNumberString(TechnoClass* pThis, int damage, bool i wchar_t damageStr[0x20]; swprintf_s(damageStr, L"%d", damage); auto coords = CoordStruct::Empty; - coords = *pThis->GetCenterCoord(&coords); + coords = *pThis->GetRenderCoords(&coords); int maxOffset = Unsorted::CellWidthInPixels / 2; int width = 0, height = 0; @@ -957,6 +974,27 @@ bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) || (deep && TechnoExt::IsChildOf(pThisExt->ParentAttachment->Parent, pParent))); } +// Returns this if no parent. +TechnoClass* TechnoExt::GetTopLevelParent(TechnoClass* pThis) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + + return pThis && pThisExt // sanity check, sometimes crashes because ext is null - Kerbiter + && pThisExt->ParentAttachment + ? TechnoExt::GetTopLevelParent(pThisExt->ParentAttachment->Parent) + : pThis; +} + +Matrix3D TechnoExt::GetAttachmentTransform(TechnoClass* pThis, int* pKey) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + + if (pThis && pThisExt && pThisExt->ParentAttachment) + return pThisExt->ParentAttachment->GetUpdatedTransform(pKey); + + return TechnoExt::GetTransform(pThis, pKey); +} + // ============================= // load / save diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 44cd1e16a3..0c6292796f 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -109,7 +109,11 @@ class TechnoExt static void InitializeLaserTrails(TechnoClass* pThis); static void InitializeShield(TechnoClass* pThis); - static CoordStruct GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct flh, bool turretFLH = false); + + static Matrix3D GetTransform(TechnoClass* pThis, int* pKey = nullptr); + static Matrix3D TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret = false); + static CoordStruct GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct flh, bool isOnTurret = false); + static Matrix3D GetFLHMatrix(TechnoClass* pThis, CoordStruct flh, bool isOnTurret = false); static CoordStruct GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FLHFound); static CoordStruct GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& FLHFound); @@ -126,6 +130,8 @@ class TechnoExt static bool IsAttached(TechnoClass* pThis); static bool IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep = true); + static TechnoClass* GetTopLevelParent(TechnoClass* pThis); + static Matrix3D GetAttachmentTransform(TechnoClass* pThis, int* pKey = nullptr); static void FireWeaponAtSelf(TechnoClass* pThis, WeaponTypeClass* pWeaponType); static void KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 01dbbec241..d9766f8ce6 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -465,3 +465,86 @@ DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) // return Continue; // } + +CoordStruct __fastcall ObjectClass_GetRenderCoords(ObjectClass* pThis, void* _) +{ + JMP_THIS(0x41BE00); +} + +CoordStruct __fastcall BuildingClass_GetRenderCoords(BuildingClass* pThis, void* _) +{ + JMP_THIS(0x459EF0); +} + +CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis, void* _) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + if (pExt && pExt->ParentAttachment) + { + // The parent origin is our origin, we will offset later in draw function + return pExt->ParentAttachment->Cache.TopLevelParent->GetRenderCoords(); + } + + return ObjectClass_GetRenderCoords(pThis, _); +} + +// TODO hook matrix +// 6F3B88 DONE +// 6F3DA4 DONE +// 73B5B5 DONE +// 73C864 DONE + +DEFINE_HOOK(0x6F3B88, TechnoClass_FireCoord_AttachmentAdjust, 0x6) +{ + enum { Skip = 0x6F3B9E }; + + GET(TechnoClass*, pThis, EBX); + + R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); + return Skip; +} + +DEFINE_HOOK(0x6F3DA4, TechnoClass_firecoord_6F3D60_AttachmentAdjust, 0x6) +{ + enum { Skip = 0x6F3DBA }; + + GET(TechnoClass*, pThis, EBX); + + R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); + return Skip; +} + +DEFINE_HOOK(0x73B5B5, UnitClass_DrawVoxel_AttachmentAdjust, 0x6) +{ + enum { Skip = 0x73B5CE }; + + GET(UnitClass*, pThis, EBP); + LEA_STACK(int*, pKey, STACK_OFFS(0x1C8, 0xC4)); + + R->EAX(&TechnoExt::GetAttachmentTransform(pThis, pKey)); + return Skip; +} + +DEFINE_HOOK(0x73C864, UnitClass_drawcode_AttachmentAdjust, 0x6) +{ + enum { Skip = 0x73C87D }; + + GET(UnitClass*, pThis, EBP); + LEA_STACK(int*, pKey, STACK_OFFS(0x128, 0xC8)); + + R->EAX(&TechnoExt::GetAttachmentTransform(pThis, pKey)); + return Skip; +} +// TODO merge hooks + +// TODO hook shadow matrix + +// BuildingClass::GetRenderCoords already has it's own override, +// should hook it if you ever want to dip into that - Kerbiter + +// FIXME it's probably not a good idea to mix vtable replacements with manual hooks +DEFINE_JUMP(VTABLE, 0x7F4A0C, GET_OFFSET(TechnoClass_GetRenderCoords)) // TechnoClass +DEFINE_JUMP(VTABLE, 0x7E8D40, GET_OFFSET(TechnoClass_GetRenderCoords)) // FootClass +DEFINE_JUMP(VTABLE, 0x7F5D1C, GET_OFFSET(TechnoClass_GetRenderCoords)) // UnitClass +DEFINE_JUMP(VTABLE, 0x7EB104, GET_OFFSET(TechnoClass_GetRenderCoords)) // InfantryClass +DEFINE_JUMP(VTABLE, 0x7E2350, GET_OFFSET(TechnoClass_GetRenderCoords)) // AircraftClass diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 6b0dc0afe3..5bc4ebc67e 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -11,6 +11,30 @@ std::vector AttachmentClass::Array; +void AttachmentClass::InitCacheData() +{ + this->Cache.TopLevelParent = TechnoExt::GetTopLevelParent(this->Parent); +} + +Matrix3D AttachmentClass::GetUpdatedTransform(int* pKey) +{ + if (Unsorted::CurrentFrame != this->Cache.LastUpdateFrame) { + auto const& flh = this->Data->FLH.Get(); + + Matrix3D mtx; + mtx.MakeIdentity(); + mtx = TechnoExt::TransformFLHForTurret(this->Parent, mtx, this->Data->IsOnTurret); + mtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); + + this->Cache.ChildTransform = Matrix3D::MatrixMultiply( + mtx, TechnoExt::GetAttachmentTransform(this->Parent, pKey)); + + this->Cache.LastUpdateFrame = Unsorted::CurrentFrame; + } + + return this->Cache.ChildTransform; +} + AttachmentTypeClass* AttachmentClass::GetType() { return AttachmentTypeClass::Array[this->Data->Type].get(); @@ -93,31 +117,31 @@ void AttachmentClass::AI() // 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 - || locoCLSID == LocomotionClass::CLSIDs::Ship) && - SUCCEEDED(pChildLoco->GetClassID(&locoCLSID)) - && (locoCLSID == LocomotionClass::CLSIDs::Drive - || locoCLSID == LocomotionClass::CLSIDs::Ship)) - { - // shh DriveLocomotionClass almost equates to ShipLocomotionClass - // for this particular case it's OK to cast to it - Kerbiter - auto pParentDriveLoco = static_cast(pParentLoco); - auto pChildDriveLoco = static_cast(pChildLoco); - - pChildDriveLoco->SlopeTimer = pParentDriveLoco->SlopeTimer; - pChildDriveLoco->Ramp1 = pParentDriveLoco->Ramp1; - pChildDriveLoco->Ramp2 = pParentDriveLoco->Ramp2; - } - } + // 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 + // || locoCLSID == LocomotionClass::CLSIDs::Ship) && + // SUCCEEDED(pChildLoco->GetClassID(&locoCLSID)) + // && (locoCLSID == LocomotionClass::CLSIDs::Drive + // || locoCLSID == LocomotionClass::CLSIDs::Ship)) + // { + // // shh DriveLocomotionClass almost equates to ShipLocomotionClass + // // for this particular case it's OK to cast to it - Kerbiter + // 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) @@ -239,6 +263,7 @@ bool AttachmentClass::DetachChild(bool isForceDetachment) if (!this->Child->InLimbo && pType->ParentDetachmentMission.isset()) this->Child->QueueMission(pType->ParentDetachmentMission.Get(), false); + // FIXME this won't work probably if (pType->InheritOwner) this->Child->SetOwningHouse(this->Parent->GetOriginalOwner(), false); @@ -257,6 +282,7 @@ void AttachmentClass::InvalidatePointer(void* ptr) { AnnounceInvalidPointer(this->Parent, ptr); AnnounceInvalidPointer(this->Child, ptr); + AnnounceInvalidPointer(this->Cache.TopLevelParent, ptr); } #pragma region Save/Load diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index b156ee7a66..7641cd817c 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -18,12 +18,22 @@ class AttachmentClass TechnoClass* Parent; TechnoClass* Child; + // volatile, don't serialize + // if you ever change the tree structure, you need to call CacheTreeData() + struct Cache { + TechnoClass* TopLevelParent; + + int LastUpdateFrame; + Matrix3D ChildTransform; + } Cache; + AttachmentClass(TechnoTypeExt::ExtData::AttachmentDataEntry* data, TechnoClass* pParent, TechnoClass* pChild = nullptr) : Data(data), Parent(pParent), Child(pChild) { + this->InitCacheData(); Array.push_back(this); } @@ -37,6 +47,9 @@ class AttachmentClass ~AttachmentClass(); + void InitCacheData(); + Matrix3D GetUpdatedTransform(int* pKey = nullptr); + AttachmentTypeClass* GetType(); TechnoTypeClass* GetChildType(); CoordStruct GetChildLocation(); From fa930127e6ffc51ddff091e708a9b56d636c34fd Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 25 Sep 2022 18:13:02 +0300 Subject: [PATCH 020/112] Fix partially --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 39 ++++++++++++++++------- 1 file changed, 27 insertions(+), 12 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index d9766f8ce6..b1e7015635 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -516,24 +516,28 @@ DEFINE_HOOK(0x6F3DA4, TechnoClass_firecoord_6F3D60_AttachmentAdjust, 0x6) DEFINE_HOOK(0x73B5B5, UnitClass_DrawVoxel_AttachmentAdjust, 0x6) { - enum { Skip = 0x73B5CE }; + enum { Skip = 0x73B5CE }; - GET(UnitClass*, pThis, EBP); - LEA_STACK(int*, pKey, STACK_OFFS(0x1C8, 0xC4)); - - R->EAX(&TechnoExt::GetAttachmentTransform(pThis, pKey)); - return Skip; + GET(UnitClass*, pThis, EBP); + LEA_STACK(int*, pKey, STACK_OFFS(0x1C4, 0x1B0)); + // LEA_STACK(Matrix3D* , pMtx ,STACK_OFFS(0x1C4, 0xC0)); + //Use .get() to skip locomotor empty checking + //since it already done above + R->EAX(&TechnoExt::GetAttachmentTransform(pThis, pKey)); + return Skip; } DEFINE_HOOK(0x73C864, UnitClass_drawcode_AttachmentAdjust, 0x6) { - enum { Skip = 0x73C87D }; - - GET(UnitClass*, pThis, EBP); - LEA_STACK(int*, pKey, STACK_OFFS(0x128, 0xC8)); + enum { Skip = 0x73C87D }; - R->EAX(&TechnoExt::GetAttachmentTransform(pThis, pKey)); - return Skip; + GET(UnitClass*, pThis, EBP); + LEA_STACK(int*, pKey, STACK_OFFS(0x128, 0xC8)); + // LEA_STACK(Matrix3D* , pMtx ,STACK_OFFS(0x128, 0x30)); + //Use .get() to skip locomotor empty checking + //since it already done above + R->EAX(&TechnoExt::GetAttachmentTransform(pThis, pKey)); + return Skip; } // TODO merge hooks @@ -548,3 +552,14 @@ DEFINE_JUMP(VTABLE, 0x7E8D40, GET_OFFSET(TechnoClass_GetRenderCoords)) // FootCl DEFINE_JUMP(VTABLE, 0x7F5D1C, GET_OFFSET(TechnoClass_GetRenderCoords)) // UnitClass DEFINE_JUMP(VTABLE, 0x7EB104, GET_OFFSET(TechnoClass_GetRenderCoords)) // InfantryClass DEFINE_JUMP(VTABLE, 0x7E2350, GET_OFFSET(TechnoClass_GetRenderCoords)) // AircraftClass + + +// // DEFINE_JUMP(VTABLE, 0x7F4A74, GET_OFFSET(TechnoClass_DrawIt)) + +// // skip children before parent drawn +// DEFINE_HOOK(0x73CEC0, UnitClass_DrawIt_HandleChildren , 0x5) +// { +// GET(UnitClass*, pThis, ECX); + +// if (TechnoExt::IsAttached(pThis)) +// } From 799ed8c6b8249d50bdea3c62e25e2669826b1d37 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 1 Oct 2022 15:35:03 +0300 Subject: [PATCH 021/112] Use multiplication, adjust post draw rect calculation still nothing --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 65 ++++++++++++++++------- src/New/Entity/AttachmentClass.cpp | 25 ++++++--- 2 files changed, 64 insertions(+), 26 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index b1e7015635..ef241d4bdf 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -1,6 +1,7 @@ #include "Body.h" #include +#include #include @@ -494,25 +495,25 @@ CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis, void* _) // 73B5B5 DONE // 73C864 DONE -DEFINE_HOOK(0x6F3B88, TechnoClass_FireCoord_AttachmentAdjust, 0x6) -{ - enum { Skip = 0x6F3B9E }; +// DEFINE_HOOK(0x6F3B88, TechnoClass_FireCoord_AttachmentAdjust, 0x6) +// { +// enum { Skip = 0x6F3B9E }; - GET(TechnoClass*, pThis, EBX); +// GET(TechnoClass*, pThis, EBX); - R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); - return Skip; -} +// R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); +// return Skip; +// } -DEFINE_HOOK(0x6F3DA4, TechnoClass_firecoord_6F3D60_AttachmentAdjust, 0x6) -{ - enum { Skip = 0x6F3DBA }; +// DEFINE_HOOK(0x6F3DA4, TechnoClass_firecoord_6F3D60_AttachmentAdjust, 0x6) +// { +// enum { Skip = 0x6F3DBA }; - GET(TechnoClass*, pThis, EBX); +// GET(TechnoClass*, pThis, EBX); - R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); - return Skip; -} +// R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); +// return Skip; +// } DEFINE_HOOK(0x73B5B5, UnitClass_DrawVoxel_AttachmentAdjust, 0x6) { @@ -547,11 +548,11 @@ DEFINE_HOOK(0x73C864, UnitClass_drawcode_AttachmentAdjust, 0x6) // should hook it if you ever want to dip into that - Kerbiter // FIXME it's probably not a good idea to mix vtable replacements with manual hooks -DEFINE_JUMP(VTABLE, 0x7F4A0C, GET_OFFSET(TechnoClass_GetRenderCoords)) // TechnoClass -DEFINE_JUMP(VTABLE, 0x7E8D40, GET_OFFSET(TechnoClass_GetRenderCoords)) // FootClass -DEFINE_JUMP(VTABLE, 0x7F5D1C, GET_OFFSET(TechnoClass_GetRenderCoords)) // UnitClass -DEFINE_JUMP(VTABLE, 0x7EB104, GET_OFFSET(TechnoClass_GetRenderCoords)) // InfantryClass -DEFINE_JUMP(VTABLE, 0x7E2350, GET_OFFSET(TechnoClass_GetRenderCoords)) // AircraftClass +// DEFINE_JUMP(VTABLE, 0x7F4A0C, GET_OFFSET(TechnoClass_GetRenderCoords)) // TechnoClass +// DEFINE_JUMP(VTABLE, 0x7E8D40, GET_OFFSET(TechnoClass_GetRenderCoords)) // FootClass +// DEFINE_JUMP(VTABLE, 0x7F5D1C, GET_OFFSET(TechnoClass_GetRenderCoords)) // UnitClass +// DEFINE_JUMP(VTABLE, 0x7EB104, GET_OFFSET(TechnoClass_GetRenderCoords)) // InfantryClass +// DEFINE_JUMP(VTABLE, 0x7E2350, GET_OFFSET(TechnoClass_GetRenderCoords)) // AircraftClass // // DEFINE_JUMP(VTABLE, 0x7F4A74, GET_OFFSET(TechnoClass_DrawIt)) @@ -563,3 +564,29 @@ DEFINE_JUMP(VTABLE, 0x7E2350, GET_OFFSET(TechnoClass_GetRenderCoords)) // Aircra // if (TechnoExt::IsAttached(pThis)) // } + +namespace TechnoAttachmentTemp +{ + ObjectClass* pThis; +} + +DEFINE_HOOK(0x5F4B13, ObjectClass_Render_Context_Set, 0x5) +{ + GET(ObjectClass*, pThis, ECX); + + TechnoAttachmentTemp::pThis = pThis; + + return 0; +} + +// Swap the render coord after the draw rect is calculated +DEFINE_HOOK(0x5F4CC3, ObjectClass_Render_AttachmentAdjust, 0x6) +{ + LEA_STACK(Point2D*, drawPoint, STACK_OFFS(0x30, 0x18)); + ObjectClass* pThis = TechnoAttachmentTemp::pThis; + + if (pThis->AbstractFlags & AbstractFlags::Techno) + TacticalClass::Instance->CoordsToClient(TechnoExt::GetTopLevelParent((TechnoClass*)pThis)->GetRenderCoords(), drawPoint); + + return 0; +} diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 5bc4ebc67e..69b6c184eb 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -18,21 +18,31 @@ void AttachmentClass::InitCacheData() Matrix3D AttachmentClass::GetUpdatedTransform(int* pKey) { + auto const& flh = this->Data->FLH.Get(); + Matrix3D attachmentMtx; + attachmentMtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); + Matrix3D turretMtx; + turretMtx.MakeIdentity(); + turretMtx = TechnoExt::TransformFLHForTurret(this->Parent, turretMtx, this->Data->IsOnTurret); + + return attachmentMtx * turretMtx * TechnoExt::GetAttachmentTransform(this->Parent, pKey); + /* if (Unsorted::CurrentFrame != this->Cache.LastUpdateFrame) { auto const& flh = this->Data->FLH.Get(); - Matrix3D mtx; - mtx.MakeIdentity(); - mtx = TechnoExt::TransformFLHForTurret(this->Parent, mtx, this->Data->IsOnTurret); - mtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); + Matrix3D attachmentMtx; + attachmentMtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); + Matrix3D turretMtx; + turretMtx.MakeIdentity(); + turretMtx = TechnoExt::TransformFLHForTurret(this->Parent, turretMtx, this->Data->IsOnTurret); - this->Cache.ChildTransform = Matrix3D::MatrixMultiply( - mtx, TechnoExt::GetAttachmentTransform(this->Parent, pKey)); + this->Cache.ChildTransform = attachmentMtx * turretMtx * TechnoExt::GetAttachmentTransform(this->Parent, pKey); this->Cache.LastUpdateFrame = Unsorted::CurrentFrame; } return this->Cache.ChildTransform; + */ } AttachmentTypeClass* AttachmentClass::GetType() @@ -49,7 +59,8 @@ TechnoTypeClass* AttachmentClass::GetChildType() CoordStruct AttachmentClass::GetChildLocation() { - return TechnoExt::GetFLHAbsoluteCoords(this->Parent, this->Data->FLH, this->Data->IsOnTurret); + auto& flh = this->Data->FLH.Get(); + return TechnoExt::GetFLHAbsoluteCoords(this->Parent, flh, this->Data->IsOnTurret); } AttachmentClass::~AttachmentClass() From 289618ebe0fe6d711148505559411054b3542f4f Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 1 Oct 2022 15:49:00 +0300 Subject: [PATCH 022/112] forgot MakeIdentity --- src/New/Entity/AttachmentClass.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 69b6c184eb..7b826c4675 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -20,6 +20,7 @@ Matrix3D AttachmentClass::GetUpdatedTransform(int* pKey) { auto const& flh = this->Data->FLH.Get(); Matrix3D attachmentMtx; + attachmentMtx.MakeIdentity(); attachmentMtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); Matrix3D turretMtx; turretMtx.MakeIdentity(); From 13b65169b9007477674c12713909e51041fac77a Mon Sep 17 00:00:00 2001 From: Meta Date: Fri, 21 Oct 2022 23:00:16 +0300 Subject: [PATCH 023/112] it WORKS! no caching support yet though... --- src/Ext/Techno/Body.cpp | 11 +++++---- src/Ext/Techno/Body.h | 4 ++-- src/New/Entity/AttachmentClass.cpp | 37 ++++++++++++------------------ 3 files changed, 23 insertions(+), 29 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 448452415f..e161d8d7b8 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -265,14 +265,14 @@ Matrix3D TechnoExt::GetTransform(TechnoClass* pThis, int* pKey) return mtx; } -Matrix3D TechnoExt::TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret) +Matrix3D TechnoExt::TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret, double factor) { auto const pType = pThis->GetTechnoType(); // turret offset and rotation if (isOnTurret && pThis->HasTurret()) { - TechnoTypeExt::ApplyTurretOffset(pType, &mtx); + TechnoTypeExt::ApplyTurretOffset(pType, &mtx, factor); double turretRad = (pThis->TurretFacing().value32() - 8) * -(Math::Pi / 16); double bodyRad = (pThis->PrimaryFacing.current().value32() - 8) * -(Math::Pi / 16); @@ -284,12 +284,13 @@ Matrix3D TechnoExt::TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool return mtx; } -Matrix3D TechnoExt::GetFLHMatrix(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret) +Matrix3D TechnoExt::GetFLHMatrix(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret, double factor) { - Matrix3D mtx = TechnoExt::TransformFLHForTurret(pThis, TechnoExt::GetTransform(pThis), isOnTurret); + Matrix3D mtx = TechnoExt::TransformFLHForTurret(pThis, TechnoExt::GetTransform(pThis), isOnTurret, factor); + CoordStruct scaledCoord = pCoord * factor; // apply FLH offset - mtx.Translate((float)pCoord.X, (float)pCoord.Y, (float)pCoord.Z); + mtx.Translate((float)scaledCoord.X, (float)scaledCoord.Y, (float)scaledCoord.Z); return mtx; } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 0c6292796f..9cbb9176aa 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -111,9 +111,9 @@ class TechnoExt static void InitializeShield(TechnoClass* pThis); static Matrix3D GetTransform(TechnoClass* pThis, int* pKey = nullptr); - static Matrix3D TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret = false); + static Matrix3D TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret, double factor = 1.0); static CoordStruct GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct flh, bool isOnTurret = false); - static Matrix3D GetFLHMatrix(TechnoClass* pThis, CoordStruct flh, bool isOnTurret = false); + static Matrix3D GetFLHMatrix(TechnoClass* pThis, CoordStruct flh, bool isOnTurret, double factor = 1.0); static CoordStruct GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FLHFound); static CoordStruct GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& FLHFound); diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 7b826c4675..5f788e6e26 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -18,32 +18,22 @@ void AttachmentClass::InitCacheData() Matrix3D AttachmentClass::GetUpdatedTransform(int* pKey) { - auto const& flh = this->Data->FLH.Get(); - Matrix3D attachmentMtx; - attachmentMtx.MakeIdentity(); - attachmentMtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); - Matrix3D turretMtx; - turretMtx.MakeIdentity(); - turretMtx = TechnoExt::TransformFLHForTurret(this->Parent, turretMtx, this->Data->IsOnTurret); - - return attachmentMtx * turretMtx * TechnoExt::GetAttachmentTransform(this->Parent, pKey); - /* - if (Unsorted::CurrentFrame != this->Cache.LastUpdateFrame) { - auto const& flh = this->Data->FLH.Get(); - - Matrix3D attachmentMtx; - attachmentMtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); - Matrix3D turretMtx; - turretMtx.MakeIdentity(); - turretMtx = TechnoExt::TransformFLHForTurret(this->Parent, turretMtx, this->Data->IsOnTurret); - - this->Cache.ChildTransform = attachmentMtx * turretMtx * TechnoExt::GetAttachmentTransform(this->Parent, pKey); + if (Unsorted::CurrentFrame != this->Cache.LastUpdateFrame) + { + double& factor = *reinterpret_cast(0xB1D008); + auto const flh = this->Data->FLH.Get() * factor; + + Matrix3D mtx = TechnoExt::GetAttachmentTransform(this->Parent, pKey); + mtx = TechnoExt::TransformFLHForTurret(this->Parent, mtx, this->Data->IsOnTurret, factor); + mtx.Translate(flh.X, flh.Y, flh.Z); + + this->Cache.ChildTransform = mtx; this->Cache.LastUpdateFrame = Unsorted::CurrentFrame; } return this->Cache.ChildTransform; - */ + } AttachmentTypeClass* AttachmentClass::GetType() @@ -98,6 +88,9 @@ void AttachmentClass::CreateChild() { auto const pChildExt = TechnoExt::ExtMap.Find(this->Child); pChildExt->ParentAttachment = this; + + this->Child->GetTechnoType()->DisableVoxelCache = true; + this->Child->GetTechnoType()->DisableShadowCache = true; } else { @@ -140,7 +133,7 @@ void AttachmentClass::AI() // if (SUCCEEDED(pParentLoco->GetClassID(&locoCLSID)) // && (locoCLSID == LocomotionClass::CLSIDs::Drive // || locoCLSID == LocomotionClass::CLSIDs::Ship) && - // SUCCEEDED(pChildLoco->GetClassID(&locoCLSID)) + // SUCCEEDED(pChildLoco->GetClassID(&locoCLSID)) // && (locoCLSID == LocomotionClass::CLSIDs::Drive // || locoCLSID == LocomotionClass::CLSIDs::Ship)) // { From 3f7501bfab5331f6fd58afdacc7b9e853d1c1e5a Mon Sep 17 00:00:00 2001 From: Trsdy <914137150@qq.com> Date: Sat, 22 Oct 2022 22:29:55 +0800 Subject: [PATCH 024/112] Move the lasertrail's type-convertion aftermath into TechnoClass_AI --- src/Ext/Techno/Body.cpp | 32 ++++++++++++++++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.cpp | 65 ++++------------------------------------ 3 files changed, 39 insertions(+), 59 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 73235c8b28..59f0f55f9e 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -255,6 +255,38 @@ void TechnoExt::ExtData::ApplySpawnLimitRange() } } +void TechnoExt::ExtData::UpdateTypeAndLaserTrails(const TechnoTypeClass* currentType) +{ + auto const pThis = this->OwnerObject(); + + if (this->LaserTrails.size()) + this->LaserTrails.clear(); + + this->TypeExtData = TechnoTypeExt::ExtMap.Find(currentType); + + for (auto const& entry : this->TypeExtData->LaserTrailData) + { + if (auto const pLaserType = LaserTrailTypeClass::Array[entry.idxType].get()) + { + this->LaserTrails.push_back(std::make_unique( + pLaserType, pThis->Owner, entry.FLH, entry.IsOnTurret)); + } + } + // LaserTrails update routine is in TechnoClass::AI hook because TechnoClass::Draw + // doesn't run when the object is off-screen which leads to visual bugs - Kerbiter + for (auto const& trail : this->LaserTrails) + { + if (pThis->CloakState == CloakState::Cloaked && !trail->Type->CloakVisible) + continue; + + CoordStruct trailLoc = TechnoExt::GetFLHAbsoluteCoords(pThis, trail->FLH, trail->IsOnTurret); + if (pThis->CloakState == CloakState::Uncloaking && !trail->Type->CloakVisible) + trail->LastLocation = trailLoc; + else + trail->Update(trailLoc); + } +} + bool TechnoExt::IsActive(TechnoClass* pThis) { return diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 64c4f7de4a..a6df8c0dc6 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -59,6 +59,7 @@ class TechnoExt void EatPassengers(); void UpdateShield(); void ApplySpawnLimitRange(); + void UpdateTypeAndLaserTrails(const TechnoTypeClass* currentType); virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index 8bf80ce3d2..038141e0d4 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -16,9 +16,13 @@ DEFINE_HOOK(0x6F9E50, TechnoClass_AI, 0x5) auto pExt = TechnoExt::ExtMap.Find(pThis); auto pType = pThis->GetTechnoType(); - // Set only if unset or type has changed - if (!pExt->TypeExtData || pExt->TypeExtData->OwnerObject() != pType) + // Set only if unset + if (!pExt->TypeExtData) pExt->TypeExtData = TechnoTypeExt::ExtMap.Find(pType); + // or type is changed, and if it does + // it should be done by Ares in the same hook here, which is executed right before this one thankfully + else if (pExt->TypeExtData->OwnerObject() != pType) + pExt->UpdateTypeAndLaserTrails(pType); if (pExt->CheckDeathConditions()) return 0; @@ -30,21 +34,6 @@ DEFINE_HOOK(0x6F9E50, TechnoClass_AI, 0x5) TechnoExt::ApplyMindControlRangeLimit(pThis); - // LaserTrails update routine is in TechnoClass::AI hook because TechnoClass::Draw - // doesn't run when the object is off-screen which leads to visual bugs - Kerbiter - for (auto const& trail : pExt->LaserTrails) - { - if (pThis->CloakState == CloakState::Cloaked && !trail->Type->CloakVisible) - continue; - - CoordStruct trailLoc = TechnoExt::GetFLHAbsoluteCoords(pThis, trail->FLH, trail->IsOnTurret); - if (pThis->CloakState == CloakState::Uncloaking && !trail->Type->CloakVisible) - trail->LastLocation = trailLoc; - else - trail->Update(trailLoc); - - } - return 0; } @@ -313,48 +302,6 @@ DEFINE_HOOK(0x702819, TechnoClass_ReceiveDamage_Decloak, 0xA) return 0x702823; } -namespace AresConvert -{ - UnitTypeClass* TypeBeforeDeploy = nullptr; -} - -DEFINE_HOOK(0x73DE78, UnitClass_SimpleDeployer_BeforeDeploy, 0x6) -{ - GET(UnitTypeClass*, type, EAX); - AresConvert::TypeBeforeDeploy = type; - return 0; -} - -DEFINE_HOOK(0x73DE90, UnitClass_SimpleDeployer_TransferLaserTrails, 0x6) -{ - GET(UnitClass*, pUnit, ESI); - - if (pUnit->Type == AresConvert::TypeBeforeDeploy) - return 0; - - auto pTechnoExt = TechnoExt::ExtMap.Find(pUnit); - auto pTechnoTypeExt = TechnoTypeExt::ExtMap.Find(pUnit->Type); - - if (pTechnoExt && pTechnoTypeExt) - { - if (pTechnoExt->LaserTrails.size()) - pTechnoExt->LaserTrails.clear(); - - for (auto const& entry : pTechnoTypeExt->LaserTrailData) - { - if (auto const pLaserType = LaserTrailTypeClass::Array[entry.idxType].get()) - { - pTechnoExt->LaserTrails.push_back(std::make_unique( - pLaserType, pUnit->Owner, entry.FLH, entry.IsOnTurret)); - } - } - } - - pTechnoExt->TypeExtData = pTechnoTypeExt; - - return 0; -} - DEFINE_HOOK(0x71067B, TechnoClass_EnterTransport_LaserTrails, 0x7) { GET(TechnoClass*, pTechno, EDI); From 6d9a0e4a8d90d2beceb9c5a3146be033899f01ec Mon Sep 17 00:00:00 2001 From: Starkku Date: Sat, 22 Oct 2022 17:50:19 +0300 Subject: [PATCH 025/112] Add YSort handling, fix warnings --- YRpp | 2 +- docs/New-or-Enhanced-Logics.md | 12 ++-- src/Ext/Techno/Body.cpp | 11 ++-- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 69 +++++++++++++++++------ src/New/Entity/AttachmentClass.cpp | 5 +- src/New/Entity/AttachmentClass.h | 3 +- src/New/Type/AttachmentTypeClass.cpp | 2 + src/New/Type/AttachmentTypeClass.h | 3 + src/Utilities/Enum.h | 8 +++ src/Utilities/TemplateDef.h | 27 +++++++++ 10 files changed, 110 insertions(+), 32 deletions(-) diff --git a/YRpp b/YRpp index b36fa814d6..8808af52e4 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit b36fa814d6abe994622df4bfdfabd033f325e064 +Subproject commit 8808af52e498a3e47ff8ab0a7fd1f921e42799cf diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 628c47cb29..fb11e87518 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -25,10 +25,12 @@ In `rulesmd.ini`: -InheritTilt=yes ; boolean, whether the child tilts with the parent -InheritOwner=yes ; boolean, whether the child inherits owner of the parent while it's attached -InheritStateEffects=yes ; boolean (state effects = chaos, iron curtain etc.) -InheritCommands=yes ; boolean +InheritTilt=true ; boolean, whether the child tilts with the parent +InheritOwner=true ; boolean, whether the child inherits owner of the parent while it's attached +InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) +InheritCommands=true ; boolean +LowSelectionPriority=true ; boolean +YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent -InheritTilt=true ; boolean, whether the child tilts with the parent InheritOwner=true ; boolean, whether the child inherits owner of the parent while it's attached InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) InheritCommands=true ; boolean -LowSelectionPriority=true ; boolean +LowSelectionPriority=true ; boolean, whether the child is low priority while attached YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent - +ParentDetachmentMission= ; MissionType, queued to child when it's detached from parent -[SUMTECHNO] +[SOMETECHNO] ; TechnoTypeClass AttachmentX.Type=MNT ; AttachmentType (example) -AttachmentX.TechnoType= ; TechnoType that can be attached +AttachmentX.TechnoType= ; TechnoType that can be attached, currently only units are supported AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height -AttachmentX.IsOnTurret=false - +AttachmentX.IsOnTurret=false ; boolean ``` ### Custom Radiation Types diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index db6400ee89..b9fd23aec2 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -186,14 +186,14 @@ void AttachmentClass::Destroy(TechnoClass* pSource) auto pType = this->GetType(); - // if (pType->DestructionWeapon_Child.isset()) - // TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); + if (pType->DestructionWeapon_Child.isset()) + TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); if (pType->InheritDestruction && this->Child) TechnoExt::Kill(this->Child, pSource); - // if (!this->Child->InLimbo && pType->ParentDestructionMission.isset()) - // this->Child->QueueMission(pType->ParentDestructionMission.Get(), false); + if (!this->Child->InLimbo && pType->ParentDestructionMission.isset()) + this->Child->QueueMission(pType->ParentDestructionMission.Get(), false); this->Child = nullptr; } @@ -277,11 +277,11 @@ bool AttachmentClass::DetachChild(bool isForceDetachment) if (isForceDetachment) { - if (pType->ForceDetachWeapon_Parent.isset()) - TechnoExt::FireWeaponAtSelf(this->Parent, pType->DestructionWeapon_Parent); + // 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 (pType->ForceDetachWeapon_Child.isset()) + // TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); } if (!this->Child->InLimbo && pType->ParentDetachmentMission.isset()) diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 8613a643bf..f0fde7133b 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -16,11 +16,10 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) 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->InheritDestruction.Read(exINI, section, "InheritDestruction"); + this->InheritDestruction.Read(exINI, section, "InheritDestruction"); this->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); this->YSortPosition.Read(exINI, section, "YSortPosition"); @@ -28,11 +27,11 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) // 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->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"); + this->ParentDestructionMission.Read(exINI, section, "ParentDestructionMission"); + this->ParentDetachmentMission.Read(exINI, section, "ParentDetachmentMission"); } template @@ -40,7 +39,6 @@ void AttachmentTypeClass::Serialize(T& Stm) { Stm .Process(this->RestoreAtCreation) - .Process(this->InheritTilt) .Process(this->InheritCommands) .Process(this->InheritOwner) .Process(this->InheritStateEffects) @@ -48,8 +46,8 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->LowSelectionPriority) .Process(this->YSortPosition) // .Process(this->CanBeForceDetached) - .Process(this->ForceDetachWeapon_Child) - .Process(this->ForceDetachWeapon_Parent) + // .Process(this->ForceDetachWeapon_Child) + // .Process(this->ForceDetachWeapon_Parent) .Process(this->DestructionWeapon_Child) .Process(this->DestructionWeapon_Parent) .Process(this->ParentDestructionMission) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 4cc270118a..8ebff314e7 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -10,7 +10,6 @@ class AttachmentTypeClass final : public Enumerable { public: Valueable RestoreAtCreation; // whether to spawn the attachment initially - Valueable InheritTilt; Valueable InheritCommands; Valueable InheritOwner; // aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. @@ -18,8 +17,8 @@ class AttachmentTypeClass final : public Enumerable Valueable LowSelectionPriority; Valueable YSortPosition; // Valueable CanBeForceDetached; - Nullable ForceDetachWeapon_Child; - Nullable ForceDetachWeapon_Parent; + // Nullable ForceDetachWeapon_Child; + // Nullable ForceDetachWeapon_Parent; Nullable DestructionWeapon_Child; Nullable DestructionWeapon_Parent; Nullable ParentDestructionMission; @@ -27,7 +26,6 @@ class AttachmentTypeClass final : public Enumerable AttachmentTypeClass(const char* pTitle = NONE_STR) : Enumerable(pTitle) , RestoreAtCreation { true } - , InheritTilt { true } , InheritCommands { true } , InheritOwner { true } , InheritStateEffects { true } @@ -35,8 +33,8 @@ class AttachmentTypeClass final : public Enumerable , LowSelectionPriority { true } , YSortPosition { AttachmentYSortPosition::Default } // , CanBeForceDetached { false } - , ForceDetachWeapon_Child { } - , ForceDetachWeapon_Parent { } + // , ForceDetachWeapon_Child { } + // , ForceDetachWeapon_Parent { } , DestructionWeapon_Child { } , DestructionWeapon_Parent { } , ParentDestructionMission { } From 6f7d8c45f8ca6c694f7b92e7f63fdd8e4b9b375a Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 24 Jun 2023 20:21:29 +0300 Subject: [PATCH 047/112] Fix Kill call signature --- src/Ext/Techno/Body.Update.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 5f3c248351..6520a7d564 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -582,7 +582,7 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, Anim } default: //must be AutoDeathBehavior::Kill - TechnoExt::Kill(pThis, pThis->Owner); + TechnoExt::Kill(pThis, nullptr, pThis->Owner); } } From c6d05fe7030dd014bfe319ec9c440aa55e2b4190 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 24 Jun 2023 20:32:23 +0300 Subject: [PATCH 048/112] Mention loco in the doc --- docs/Miscellanous.md | 31 +++++++++++++++++-------------- docs/New-or-Enhanced-Logics.md | 4 ++-- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/docs/Miscellanous.md b/docs/Miscellanous.md index 69abce2ef1..025d75afc2 100644 --- a/docs/Miscellanous.md +++ b/docs/Miscellanous.md @@ -41,25 +41,28 @@ SkirmishUnlimitedColors=false ; boolean ### Frame Step In - There's a new hotkey to execute the game frame by frame for development usage. - - You can switch to frame by frame mode and then use frame step in command to forward 1, 5, 10, 15, 30 or 60 frames by one hit. + - You can switch to frame by frame mode and then use frame step in command to forward 1, 5, 10, 15, 30 or 60 frames by one hit. ### Semantic locomotor aliases - It's now possible to write locomotor aliases instead of their CLSIDs in the `Locomotor` tag value. Use the table below to find the needed alias for a locomotor. + - The feature is also supported for Phobos locomotors. + +| *Alias* | *CLSID* | +| -------: | :--------------------------------------: | +Drive | `{4A582741-9839-11d1-B709-00A024DDAFD1}` | +Jumpjet | `{92612C46-F71F-11d1-AC9F-006008055BB5}` | +Hover | `{4A582742-9839-11d1-B709-00A024DDAFD1}` | +Rocket | `{B7B49766-E576-11d3-9BD9-00104B972FE8}` | +Tunnel | `{4A582743-9839-11d1-B709-00A024DDAFD1}` | +Walk | `{4A582744-9839-11d1-B709-00A024DDAFD1}` | +DropPod | `{4A582745-9839-11d1-B709-00A024DDAFD1}` | +Fly | `{4A582746-9839-11d1-B709-00A024DDAFD1}` | +Teleport | `{4A582747-9839-11d1-B709-00A024DDAFD1}` | +Mech | `{55D141B8-DB94-11d1-AC98-006008055BB5}` | +Ship | `{2BEA74E1-7CCA-11d3-BE14-00104B62A16C}` | +Attachment | `{C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E}` | -| *Alias*| *CLSID* | -| -----: | :--------------------------------------: | -Drive | `{4A582741-9839-11d1-B709-00A024DDAFD1}` | -Jumpjet | `{92612C46-F71F-11d1-AC9F-006008055BB5}` | -Hover | `{4A582742-9839-11d1-B709-00A024DDAFD1}` | -Rocket | `{B7B49766-E576-11d3-9BD9-00104B972FE8}` | -Tunnel | `{4A582743-9839-11d1-B709-00A024DDAFD1}` | -Walk | `{4A582744-9839-11d1-B709-00A024DDAFD1}` | -DropPod | `{4A582745-9839-11d1-B709-00A024DDAFD1}` | -Fly | `{4A582746-9839-11d1-B709-00A024DDAFD1}` | -Teleport | `{4A582747-9839-11d1-B709-00A024DDAFD1}` | -Mech | `{55D141B8-DB94-11d1-AC98-006008055BB5}` | -Ship | `{2BEA74E1-7CCA-11d3-BE14-00104B62A16C}` | ## Game Speed diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index b7bb4b0c81..97a2618b48 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -14,8 +14,8 @@ This feature is not final and is under development. ``` - Technos now can be attached one to another in a tree like way. The attached units won't process any locomotion code and act like a part of a parent unit in a configurable. - - For now the attached techno may only be a vehicle. - + - Currently the attached techno may only be a vehicle. + - When attached, the special `Attachment` (`{C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E}`) locomotor is automatically casted on a unit. You may also specify it in the child unit types manually if the unit is not intended to move without a parent (f. ex. a turret). In `rulesmd.ini`: ```ini From 81c5bc4018bcbf753335b0be10492629d6d890e2 Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 25 Jun 2023 16:14:02 +0300 Subject: [PATCH 049/112] Fix destruction crashing bugs --- src/Ext/Techno/Body.Update.cpp | 4 ++-- src/New/Entity/AttachmentClass.cpp | 7 +++++-- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 6520a7d564..d6f3b3ff45 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -594,7 +594,7 @@ void TechnoExt::Kill(TechnoClass* pThis, ObjectClass* pAttacker, HouseClass* pAt { case AbstractType::Unit: case AbstractType::Aircraft: - AresFunctions::SpawnSurvivors(static_cast(pThis), nullptr, false, false); + AresFunctions::SpawnSurvivors(abstract_cast(pThis), abstract_cast(pAttacker), false, false); default: break; } } @@ -604,7 +604,7 @@ void TechnoExt::Kill(TechnoClass* pThis, ObjectClass* pAttacker, HouseClass* pAt void TechnoExt::Kill(TechnoClass* pThis, TechnoClass* pAttacker) { - TechnoExt::Kill(pThis, pAttacker, pAttacker->Owner); + TechnoExt::Kill(pThis, pAttacker, pAttacker ? pAttacker->Owner : nullptr); } void TechnoExt::UpdateSharedAmmo(TechnoClass* pThis) diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index b9fd23aec2..b9b102fb2d 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -177,6 +177,7 @@ void AttachmentClass::AI() } } +// Called in Kill_Cargo, handles logics for parent destruction on children void AttachmentClass::Destroy(TechnoClass* pSource) { if (this->Child) @@ -186,13 +187,15 @@ void AttachmentClass::Destroy(TechnoClass* pSource) auto pType = this->GetType(); + if (auto const pChildAsFoot = abstract_cast(this->Child)) + LocomotionClass::End_Piggyback(pChildAsFoot->Locomotor); + if (pType->DestructionWeapon_Child.isset()) TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); if (pType->InheritDestruction && this->Child) TechnoExt::Kill(this->Child, pSource); - - if (!this->Child->InLimbo && pType->ParentDestructionMission.isset()) + else if (!this->Child->InLimbo && pType->ParentDestructionMission.isset()) this->Child->QueueMission(pType->ParentDestructionMission.Get(), false); this->Child = nullptr; From 48702a311b01ab53299f9897942477d605e54a0b Mon Sep 17 00:00:00 2001 From: Starkku Date: Sun, 25 Jun 2023 20:51:14 +0300 Subject: [PATCH 050/112] Tentative fix for waypoint mode command inheritance --- YRpp | 2 +- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 49 +++++++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/YRpp b/YRpp index 7fdb436a5d..f62e0cff8b 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 7fdb436a5d7411b7837b533bce9f782259b9ead5 +Subproject commit f62e0cff8ba2e39efac01b0799cbf3de83b32c5b diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index ce8aa61bed..6e005abe7a 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -1,5 +1,6 @@ #include "Body.h" +#include #include #include @@ -704,3 +705,51 @@ DEFINE_JUMP(VTABLE, 0x7F5D28, GET_OFFSET(TechnoClass_SortY_Wrapper)) // UnitClas DEFINE_JUMP(LJMP, 0x568831, 0x568841); // Skip locomotion layer check in MapClass::PickUp DEFINE_JUMP(LJMP, 0x4D37A2, 0x4D37AE); // Skip locomotion layer check in FootClass::Mark + +// Handle waypoint mode command inheritance +DEFINE_HOOK(0x4C73D1, EventClass_Execute_MegaMission_HandleChildren, 0x6) +{ + GET(EventClass*, pThis, ESI); + GET(TechnoClass*, pParent, EDI); + GET(AbstractClass*, pArchiveTarget, EBP); + GET(Mission, mission, EBX); + + auto const pExt = TechnoExt::ExtMap.Find(pParent); + auto const pTarget = pThis->Data.MegaMission.Target.As_Abstract(); + auto const pDestination = pThis->Data.MegaMission.Destination.As_Abstract(); + bool isGuard = pThis->Data.MegaMission.Mission == static_cast(Mission::Area_Guard) && pParent->AbstractFlags & AbstractFlags::Foot; + + for (auto const& pAttachment : pExt->ChildAttachments) + { + if (!pAttachment->Child || !pAttachment->GetType()->InheritCommands) + continue; + + auto const pChild = pAttachment->Child; + pChild->QueueMission(mission, false); + + if (isGuard) + { + pChild->SetTarget(pChild); + pChild->SetDestination(pTarget, true); + pChild->SetFocus(pTarget); + } + else + { + if (pArchiveTarget) + { + pChild->SetFocus(pArchiveTarget); + + if (auto const pFoot = abstract_cast(pChild)) + pFoot->ClearNavQueue(); + } + + pChild->SetTarget(pTarget); + pChild->SetDestination(pDestination, true); + + if (pTarget && pChild->GetTechnoType()->OpenTopped) + pChild->SetTargetForPassengers(pTarget); + } + } + + return 0; +} From bd185a08df76a3e2f353359798b0a38931454089 Mon Sep 17 00:00:00 2001 From: Meta Date: Mon, 26 Jun 2023 14:23:48 +0300 Subject: [PATCH 051/112] Fix turret drawing & shadow height --- src/Locomotion/AttachmentLocomotionClass.cpp | 60 +++++++++++++++----- src/Locomotion/AttachmentLocomotionClass.h | 2 +- 2 files changed, 46 insertions(+), 16 deletions(-) diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 0941fd4cde..eaa65334dd 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -17,6 +17,7 @@ #include +// TODO maybe some macros for repeated parent function calls? bool AttachmentLocomotionClass::Is_Moving() { @@ -26,18 +27,47 @@ bool AttachmentLocomotionClass::Is_Moving() Matrix3D AttachmentLocomotionClass::Draw_Matrix(VoxelIndexKey* key) { - ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); - return pParentLoco - ? pParentLoco->Draw_Matrix(key) - : LocomotionClass::Draw_Matrix(key); + if (auto const pParentFoot = abstract_cast(this->GetAttachmentParent())) + { + Matrix3D mtx = pParentFoot->Locomotor->Draw_Matrix(key); + + // adjust for the real facing which is the source of truth for hor. rotation + double childRotation = this->LinkedTo->PrimaryFacing.Current().GetRadian<32>(); + double parentRotation = pParentFoot->PrimaryFacing.Current().GetRadian<32>(); + float adjustmentAngle = (float)(childRotation - parentRotation); + + mtx.RotateZ(adjustmentAngle); + + if (key && key->Is_Valid_Key()) + key->MainVoxel.FrameIndex = this->LinkedTo->PrimaryFacing.Current().GetFacing<32>(); + + return mtx; + } + + return LocomotionClass::Draw_Matrix(key); } Matrix3D AttachmentLocomotionClass::Shadow_Matrix(VoxelIndexKey* key) { - ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); - return pParentLoco - ? pParentLoco->Shadow_Matrix(key) - : LocomotionClass::Shadow_Matrix(key); + if (auto const pParentFoot = abstract_cast(this->GetAttachmentParent())) + { + Matrix3D mtx = pParentFoot->Locomotor->Shadow_Matrix(key); + + // adjust for the real facing which is the source of truth for hor. rotation + double childRotation = this->LinkedTo->PrimaryFacing.Current().GetRadian<32>(); + double parentRotation = pParentFoot->PrimaryFacing.Current().GetRadian<32>(); + float adjustmentAngle = (float)(childRotation - parentRotation); + + mtx.RotateZ(adjustmentAngle); + + // should be shadow key in fact but I kerbo is lazy to properly define shadow key + if (key && key->Is_Valid_Key()) + key->MainVoxel.FrameIndex = this->LinkedTo->PrimaryFacing.Current().GetFacing<32>(); + + return mtx; + } + + return LocomotionClass::Shadow_Matrix(key); } Point2D AttachmentLocomotionClass::Draw_Point() @@ -48,13 +78,13 @@ Point2D AttachmentLocomotionClass::Draw_Point() : LocomotionClass::Draw_Point(); } -Point2D AttachmentLocomotionClass::Shadow_Point() -{ - ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); - return pParentLoco - ? pParentLoco->Shadow_Point() - : LocomotionClass::Shadow_Point(); -} +// Point2D AttachmentLocomotionClass::Shadow_Point() +// { +// ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); +// return pParentLoco +// ? pParentLoco->Shadow_Point() +// : LocomotionClass::Shadow_Point(); +// } VisualType AttachmentLocomotionClass::Visual_Character(bool raw) { diff --git a/src/Locomotion/AttachmentLocomotionClass.h b/src/Locomotion/AttachmentLocomotionClass.h index cf21ab1873..9325126911 100644 --- a/src/Locomotion/AttachmentLocomotionClass.h +++ b/src/Locomotion/AttachmentLocomotionClass.h @@ -125,7 +125,7 @@ class __declspec(uuid("C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E")) virtual Matrix3D __stdcall Draw_Matrix(VoxelIndexKey* key) override; virtual Matrix3D __stdcall Shadow_Matrix(VoxelIndexKey* key) override; virtual Point2D __stdcall Draw_Point() override; - virtual Point2D __stdcall Shadow_Point() override; + // virtual Point2D __stdcall Shadow_Point() override; virtual VisualType __stdcall Visual_Character(bool raw) override; virtual int __stdcall Z_Adjust() override; virtual ZGradient __stdcall Z_Gradient() override; From efce95c2e88534239c03984fb58ae840e1b645a4 Mon Sep 17 00:00:00 2001 From: Meta Date: Mon, 26 Jun 2023 15:52:57 +0300 Subject: [PATCH 052/112] Revert to draw shadows as for individual units --- src/Locomotion/AttachmentLocomotionClass.cpp | 43 ++++++++++++-------- src/Locomotion/AttachmentLocomotionClass.h | 2 +- 2 files changed, 27 insertions(+), 18 deletions(-) diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index eaa65334dd..6bc22e81a0 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -47,28 +47,37 @@ Matrix3D AttachmentLocomotionClass::Draw_Matrix(VoxelIndexKey* key) return LocomotionClass::Draw_Matrix(key); } -Matrix3D AttachmentLocomotionClass::Shadow_Matrix(VoxelIndexKey* key) -{ - if (auto const pParentFoot = abstract_cast(this->GetAttachmentParent())) - { - Matrix3D mtx = pParentFoot->Locomotor->Shadow_Matrix(key); +// Shadow drawing works acceptable as is. It draws separate units as normal. +// A possibly better solution would be to actually "merge" the shadows +// and draw them as a single one, but this needs calculating the extension +// of the parent slope plane to calculate the correct offset for Shadow_Point, +// complicated trigonometry that would be a waste of time at this point. - // adjust for the real facing which is the source of truth for hor. rotation - double childRotation = this->LinkedTo->PrimaryFacing.Current().GetRadian<32>(); - double parentRotation = pParentFoot->PrimaryFacing.Current().GetRadian<32>(); - float adjustmentAngle = (float)(childRotation - parentRotation); +// If you want to work on this - Shadow_Matrix should be fine as is, +// Shadow_Point would need calculated height from the ramp extension plane - Kerbiter - mtx.RotateZ(adjustmentAngle); +// Matrix3D AttachmentLocomotionClass::Shadow_Matrix(VoxelIndexKey* key) +// { +// if (auto const pParentFoot = abstract_cast(this->GetAttachmentParent())) +// { +// Matrix3D mtx = pParentFoot->Locomotor->Shadow_Matrix(key); - // should be shadow key in fact but I kerbo is lazy to properly define shadow key - if (key && key->Is_Valid_Key()) - key->MainVoxel.FrameIndex = this->LinkedTo->PrimaryFacing.Current().GetFacing<32>(); +// // adjust for the real facing which is the source of truth for hor. rotation +// double childRotation = this->LinkedTo->PrimaryFacing.Current().GetRadian<32>(); +// double parentRotation = pParentFoot->PrimaryFacing.Current().GetRadian<32>(); +// float adjustmentAngle = (float)(childRotation - parentRotation); - return mtx; - } +// mtx.RotateZ(adjustmentAngle); - return LocomotionClass::Shadow_Matrix(key); -} +// // should be shadow key in fact but I kerbo is lazy to properly define shadow key +// if (key && key->Is_Valid_Key()) +// key->MainVoxel.FrameIndex = this->LinkedTo->PrimaryFacing.Current().GetFacing<32>(); + +// return mtx; +// } + +// return LocomotionClass::Shadow_Matrix(key); +// } Point2D AttachmentLocomotionClass::Draw_Point() { diff --git a/src/Locomotion/AttachmentLocomotionClass.h b/src/Locomotion/AttachmentLocomotionClass.h index 9325126911..60339ff8ca 100644 --- a/src/Locomotion/AttachmentLocomotionClass.h +++ b/src/Locomotion/AttachmentLocomotionClass.h @@ -123,7 +123,7 @@ class __declspec(uuid("C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E")) // virtual Move __stdcall Can_Enter_Cell(CellStruct cell) override; //virtual bool __stdcall Is_To_Have_Shadow() override; virtual Matrix3D __stdcall Draw_Matrix(VoxelIndexKey* key) override; - virtual Matrix3D __stdcall Shadow_Matrix(VoxelIndexKey* key) override; + // virtual Matrix3D __stdcall Shadow_Matrix(VoxelIndexKey* key) override; virtual Point2D __stdcall Draw_Point() override; // virtual Point2D __stdcall Shadow_Point() override; virtual VisualType __stdcall Visual_Character(bool raw) override; From 2436e2a70c38e91285c77accec03f979182dfe10 Mon Sep 17 00:00:00 2001 From: Meta Date: Mon, 26 Jun 2023 23:23:54 +0300 Subject: [PATCH 053/112] Handle airborne attachments --- YRpp | 2 +- src/Locomotion/AttachmentLocomotionClass.cpp | 38 +++++++++++++++++--- src/Locomotion/AttachmentLocomotionClass.h | 5 +++ 3 files changed, 39 insertions(+), 6 deletions(-) diff --git a/YRpp b/YRpp index 621b344e07..33167153a3 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 621b344e0797a14904e67419a1f3de5fb493ab74 +Subproject commit 33167153a3829b1a3ea0126d6a8701ea464bbe4e diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 6bc22e81a0..779ef6888f 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -8,6 +8,7 @@ #include #include +#include #include #include @@ -123,11 +124,38 @@ ZGradient AttachmentLocomotionClass::Z_Gradient() bool AttachmentLocomotionClass::Process() { if (this->LinkedTo->IsAlive) - { - Layer newLayer = this->In_Which_Layer(); - if (this->PreviousLayer != newLayer) - DisplayClass::Instance->Submit(this->LinkedTo); - } + { + Layer newLayer = this->In_Which_Layer(); + Layer oldLayer = this->PreviousLayer; + + bool changedAirborneStatus = false; + + if (oldLayer != newLayer) + { + DisplayClass::Instance->Submit(this->LinkedTo); + + if (oldLayer < Layer::Air && Layer::Air <= newLayer) + { + AircraftTrackerClass::Instance->Add(this->LinkedTo); + changedAirborneStatus = true; + } + else if (newLayer < Layer::Air && Layer::Air <= oldLayer) + { + AircraftTrackerClass::Instance->Remove(this->LinkedTo); + changedAirborneStatus = true; + } + + this->PreviousLayer = newLayer; + } + + CellStruct oldPos = this->PreviousCell; + CellStruct newPos = this->LinkedTo->GetMapCoords(); + + if (Layer::Air <= newLayer && !changedAirborneStatus && oldPos != newPos) + AircraftTrackerClass::Instance->Update(this->LinkedTo, oldPos, newPos); + + this->PreviousCell = newPos; + } return LocomotionClass::Process(); } diff --git a/src/Locomotion/AttachmentLocomotionClass.h b/src/Locomotion/AttachmentLocomotionClass.h index 60339ff8ca..b519a4ddfd 100644 --- a/src/Locomotion/AttachmentLocomotionClass.h +++ b/src/Locomotion/AttachmentLocomotionClass.h @@ -184,6 +184,7 @@ class __declspec(uuid("C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E")) public: inline AttachmentLocomotionClass() : LocomotionClass { } , PreviousLayer { Layer::None } + , PreviousCell { CellStruct::Empty } , Piggybacker { nullptr } { } @@ -197,6 +198,10 @@ class __declspec(uuid("C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E")) // Used for resubmitting the FootClass to another layer. Layer PreviousLayer; + // The cell this locomotor's user was in previously. + // Used for tracking the FootClass while it's in air. + CellStruct PreviousCell; + // The piggybacking locomotor. ILocomotionPtr Piggybacker; }; From fc749fed5cd08949c0e9b3fa741bf0f4f40e57a0 Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 27 Jun 2023 21:19:06 +0300 Subject: [PATCH 054/112] Fix AttachmentLoco destruction crash --- YRpp | 2 +- src/New/Entity/AttachmentClass.cpp | 3 --- 2 files changed, 1 insertion(+), 4 deletions(-) diff --git a/YRpp b/YRpp index 33167153a3..5414a33aa3 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 33167153a3829b1a3ea0126d6a8701ea464bbe4e +Subproject commit 5414a33aa319687e77dc3fdbfe9ab5a389d7ddf3 diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index b9b102fb2d..d13a1bebd1 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -187,9 +187,6 @@ void AttachmentClass::Destroy(TechnoClass* pSource) auto pType = this->GetType(); - if (auto const pChildAsFoot = abstract_cast(this->Child)) - LocomotionClass::End_Piggyback(pChildAsFoot->Locomotor); - if (pType->DestructionWeapon_Child.isset()) TechnoExt::FireWeaponAtSelf(this->Child, pType->DestructionWeapon_Child); From e9dbd24d2fa3dd0d8dad87c4108cf5f1c20941af Mon Sep 17 00:00:00 2001 From: Meta Date: Tue, 27 Jun 2023 23:00:29 +0300 Subject: [PATCH 055/112] Improve death handling for children, fix Crashable=yes death issues --- src/Ext/Techno/Body.cpp | 2 -- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 11 ++++++++++- src/Ext/Unit/Hooks.Jumpjet.cpp | 17 +++++++++++++++++ src/New/Entity/AttachmentClass.cpp | 16 +++++++++++----- 4 files changed, 38 insertions(+), 8 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 278389f97a..079fc88758 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -371,8 +371,6 @@ void TechnoExt::HandleDestructionAsChild(TechnoClass* pThis) if (pExt->ParentAttachment) pExt->ParentAttachment->ChildDestroyed(); - - pExt->ParentAttachment = nullptr; } void TechnoExt::UnlimboAttachments(TechnoClass* pThis) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 6e005abe7a..042b065ff1 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -29,7 +29,7 @@ DEFINE_HOOK(0x707CB3, TechnoClass_KillCargo_HandleAttachments, 0x6) return 0; } -DEFINE_HOOK(0x5F6609, ObjectClass_RemoveThis_TechnoClass, 0x9) +DEFINE_HOOK(0x5F6609, ObjectClass_RemoveThis_TechnoClass_NotifyParent, 0x9) { GET(TechnoClass*, pThis, ESI); @@ -39,6 +39,15 @@ DEFINE_HOOK(0x5F6609, ObjectClass_RemoveThis_TechnoClass, 0x9) return 0x5F6612; } +DEFINE_HOOK(0x4DEBB4, FootClass_OnDestroyed_NotifyParent, 0x8) +{ + GET(FootClass*, pThis, ESI); + + TechnoExt::HandleDestructionAsChild(pThis); + + return 0; +} + DEFINE_HOOK(0x6F6F20, TechnoClass_Unlimbo_UnlimboAttachments, 0x6) { diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index 27822b3c6d..1e28a40238 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -157,3 +157,20 @@ FireError __stdcall JumpjetLocomotionClass_Can_Fire(ILocomotion* pThis) DEFINE_JUMP(VTABLE, 0x7ECDF4, GET_OFFSET(JumpjetLocomotionClass_Can_Fire)); //TODO : Issue #690 #655 + +// This fixes the issue when locomotor is crashing in grounded or +// hovering state and the crash processing code won't be reached. +// Can be observed easily when Crashable=yes jumpjet is attached to +// a unit and then destroyed. +DEFINE_HOOK(0x54AEDC, JumpjetLocomotionClass_Process_CheckCrashing, 0x0) +{ + enum { ProcessMovement = 0x54AEED, Skip = 0x54B16C }; + + GET(ILocomotion*, iLoco, ESI); + auto const pLoco = static_cast(iLoco); + + return pLoco->Is_Moving_Now() // stolen code + || pLoco->LinkedTo->IsCrashing + ? ProcessMovement + : Skip; +} diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index d13a1bebd1..2851aa4d0a 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -182,7 +182,7 @@ void AttachmentClass::Destroy(TechnoClass* pSource) { if (this->Child) { - auto pChildExt = TechnoExt::ExtMap.Find(this->Child); + auto const pChildExt = TechnoExt::ExtMap.Find(this->Child); pChildExt->ParentAttachment = nullptr; auto pType = this->GetType(); @@ -201,11 +201,17 @@ void AttachmentClass::Destroy(TechnoClass* pSource) void AttachmentClass::ChildDestroyed() { - AttachmentTypeClass* pType = this->GetType(); - if (pType->DestructionWeapon_Parent.isset()) - TechnoExt::FireWeaponAtSelf(this->Parent, pType->DestructionWeapon_Parent); + if (this->Child) + { + if (auto const pChildExt = TechnoExt::ExtMap.Find(this->Child)) + pChildExt->ParentAttachment = nullptr; - this->Child = nullptr; + AttachmentTypeClass* pType = this->GetType(); + if (pType->DestructionWeapon_Parent.isset()) + TechnoExt::FireWeaponAtSelf(this->Parent, pType->DestructionWeapon_Parent); + + this->Child = nullptr; + } } void AttachmentClass::Unlimbo() From a06372365369bd145179a0788892ec4403d8d6f8 Mon Sep 17 00:00:00 2001 From: Meta Date: Wed, 28 Jun 2023 01:10:10 +0300 Subject: [PATCH 056/112] Minor fix for crashable units which don't have correct source set --- src/Misc/Hooks.BugFixes.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index df0769cda6..92a6e24a0f 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -706,3 +706,14 @@ DEFINE_HOOK(0x54AE44, JumpjetLocomotionClass_LinkToObject_FixFacing, 0x7) return 0; } + +// WWP for some reason passed nullptr as source to On_Destroyed even though the real source existed +DEFINE_HOOK(0x738467, UnitClass_TakeDamage_FixOnDestroyedSource, 0x0) +{ + enum { Continue = 0x73866E, ForceKill = 0x73847B }; + + GET(UnitClass*, pThis, ESI); + GET_STACK(TechnoClass*, pSource, STACK_OFFSET(0x44, 0x10)); + + return pThis->Crash(pSource) ? Continue : ForceKill; +} From 717845234da403e7cacc14e2b307e3f8c4f7aadc Mon Sep 17 00:00:00 2001 From: Meta Date: Wed, 28 Jun 2023 01:10:56 +0300 Subject: [PATCH 057/112] Allow negative DirType in parsing --- src/Utilities/TemplateDef.h | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index 9650c39fdf..1347f7f758 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -551,14 +551,17 @@ namespace detail if (parser.ReadInteger(pSection, pKey, &buffer)) { - if (buffer <= (int)DirType::Max && buffer >= (int)DirType::North) + unsigned int absValue = abs(buffer); + bool isNegative = buffer < 0; + + if ((int)DirType::North <= absValue && absValue <= (int)DirType::Max) { - value = static_cast(buffer); + value = static_cast(!isNegative ? absValue : (int)DirType::Max - absValue); return true; } else { - Debug::INIParseFailed(pSection, pKey, parser.value(), "Expected a valid DirType (0-255)."); + Debug::INIParseFailed(pSection, pKey, parser.value(), "Expected a valid DirType (0-255 abs. value)."); } } From e33bb19345f585f385de2c0f2554a0abb305079a Mon Sep 17 00:00:00 2001 From: Meta Date: Wed, 28 Jun 2023 01:11:39 +0300 Subject: [PATCH 058/112] Implement RotationAdjust --- docs/New-or-Enhanced-Logics.md | 1 + src/Ext/TechnoType/Body.cpp | 10 ++++++++-- src/Ext/TechnoType/Body.h | 1 + src/New/Entity/AttachmentClass.cpp | 10 +++++++--- 4 files changed, 17 insertions(+), 5 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 97a2618b48..3af85879e5 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -39,6 +39,7 @@ AttachmentX.Type=MNT ; AttachmentType (example) AttachmentX.TechnoType= ; TechnoType that can be attached, currently only units are supported AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height AttachmentX.IsOnTurret=false ; boolean +AttachmentX.RotationAdjust=0 ; rotation in DirType, from -255 to 255 ``` ### Custom Radiation Types diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index d4d4ed606d..cbe3edb367 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -230,10 +230,15 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.IsOnTurret", i); isOnTurret.Read(exINI, pSection, tempBuffer); + Valueable rotationAdjust; + _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.RotationAdjust", i); + rotationAdjust.Read(exINI, pSection, tempBuffer); + + AttachmentDataEntry const entry { ValueableIdx(type), technoType, flh, isOnTurret, rotationAdjust }; if (i == AttachmentData.size()) - this->AttachmentData.push_back({ ValueableIdx(type), technoType, flh, isOnTurret }); + this->AttachmentData.push_back(entry); else - this->AttachmentData[i] = { ValueableIdx(type), technoType, flh, isOnTurret }; + this->AttachmentData[i] = entry; } this->NoSecondaryWeaponFallback.Read(exINI, pSection, "NoSecondaryWeaponFallback"); @@ -596,6 +601,7 @@ bool TechnoTypeExt::ExtData::AttachmentDataEntry::Serialize(T& stm) .Process(this->TechnoType) .Process(this->FLH) .Process(this->IsOnTurret) + .Process(this->RotationAdjust) .Success(); } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 3694721970..171af505e8 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -157,6 +157,7 @@ class TechnoTypeExt NullableIdx TechnoType; Valueable FLH; Valueable IsOnTurret; + Valueable RotationAdjust; bool Load(PhobosStreamReader& stm, bool registerForChange); bool Save(PhobosStreamWriter& stm) const; diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 2851aa4d0a..30dd02a336 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -144,6 +144,8 @@ void AttachmentClass::AI() DirStruct childDir = this->Data->IsOnTurret ? this->Parent->SecondaryFacing.Current() : this->Parent->PrimaryFacing.Current(); + childDir.Raw += DirStruct(this->Data->RotationAdjust).Raw; // overflow = free modulo for rotation + this->Child->PrimaryFacing.SetCurrent(childDir); // TODO handle secondary facing in case the turret is idle @@ -221,11 +223,13 @@ void AttachmentClass::Unlimbo() CoordStruct childCoord = TechnoExt::GetFLHAbsoluteCoords( this->Parent, this->Data->FLH, this->Data->IsOnTurret); - DirType childDir = this->Data->IsOnTurret - ? this->Parent->SecondaryFacing.Current().GetDir() : this->Parent->PrimaryFacing.Current().GetDir(); + DirStruct childDir = this->Data->IsOnTurret + ? this->Parent->SecondaryFacing.Current() : this->Parent->PrimaryFacing.Current(); + + childDir.Raw += DirStruct(this->Data->RotationAdjust).Raw; // overflow = free modulo for rotation ++Unsorted::IKnowWhatImDoing; - this->Child->Unlimbo(childCoord, childDir); + this->Child->Unlimbo(childCoord, childDir.GetDir()); --Unsorted::IKnowWhatImDoing; } } From 2a402feb8a91019b3e229f2715d044c29f0f285d Mon Sep 17 00:00:00 2001 From: Meta Date: Wed, 28 Jun 2023 21:30:47 +0300 Subject: [PATCH 059/112] Improve syncing --- src/New/Entity/AttachmentClass.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 30dd02a336..5d49b55693 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -131,12 +131,19 @@ void AttachmentClass::CreateChild() } } +#define SYNC_CHILD(property) this->Child->property = this->Parent->property + void AttachmentClass::AI() { AttachmentTypeClass* pType = this->GetType(); if (this->Child) { + if (this->Child->InLimbo && !this->Parent->InLimbo) + this->Unlimbo(); + else if (!this->Child->InLimbo && this->Parent->InLimbo) + this->Limbo(); + this->Child->SetLocation(this->GetChildLocation()); this->Child->OnBridge = this->Parent->OnBridge; @@ -158,7 +165,11 @@ void AttachmentClass::AI() if (pType->InheritStateEffects) { + this->Child->IsFallingDown = this->Parent->IsFallingDown; + this->Child->WasFallingDown = this->Parent->WasFallingDown; this->Child->CloakState = this->Parent->CloakState; + this->Child->WarpingOut = this->Parent->WarpingOut; + this->Child->unknown_280 = this->Parent->unknown_280; // sth related to teleport this->Child->BeingWarpedOut = this->Parent->BeingWarpedOut; this->Child->Deactivated = this->Parent->Deactivated; this->Child->Flash(this->Parent->Flashing.DurationRemaining); @@ -169,6 +180,7 @@ void AttachmentClass::AI() this->Child->CloakDelayTimer = this->Parent->CloakDelayTimer; this->Child->ChronoLockRemaining = this->Parent->ChronoLockRemaining; this->Child->Berzerk = this->Parent->Berzerk; + this->Child->BerzerkDurationLeft = this->Parent->BerzerkDurationLeft; this->Child->ChronoWarpedByHouse = this->Parent->ChronoWarpedByHouse; this->Child->EMPLockRemaining = this->Parent->EMPLockRemaining; this->Child->ShouldLoseTargetNow = this->Parent->ShouldLoseTargetNow; @@ -179,6 +191,8 @@ void AttachmentClass::AI() } } +#undef SYNC_CHILD + // Called in Kill_Cargo, handles logics for parent destruction on children void AttachmentClass::Destroy(TechnoClass* pSource) { From e80572acf353ee5de74f318ec1a451845b3a661f Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 6 Jul 2023 17:07:26 +0300 Subject: [PATCH 060/112] WIP OccupiesCell impl. + built techno shouldn't occupy bib with childs anymore --- docs/New-or-Enhanced-Logics.md | 1 + src/Ext/Techno/Body.cpp | 7 ++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.TechnoAttachment.cpp | 110 +++++++++++++++++++++- src/New/Type/AttachmentTypeClass.cpp | 2 + src/New/Type/AttachmentTypeClass.h | 2 + 6 files changed, 121 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 3af85879e5..7f4f19076a 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -29,6 +29,7 @@ InheritCommands=true ; boolean LowSelectionPriority=true ; boolean, whether the child is low priority while attached YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent InheritDestruction=true ; boolean +OccupiesCell=true ; boolean DestructionWeapon.Child= ; WeaponType, detonated on child when parent is destroyed DestructionWeapon.Parent= ; WeaponType, detonated on parent when child is destroyed ParentDestructionMission= ; MissionType, queued to child when parent is destroyed diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 079fc88758..e103167734 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -393,6 +393,13 @@ bool TechnoExt::IsAttached(TechnoClass* pThis) return pExt && pExt->ParentAttachment; } +bool TechnoExt::DoesntOccupyCellAsChild(TechnoClass* pThis) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + return pExt && pExt->ParentAttachment + && !pExt->ParentAttachment->GetType()->OccupiesCell; +} + bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) { auto const pThisExt = TechnoExt::ExtMap.Find(pThis); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a495af27ba..418fce879a 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -132,6 +132,7 @@ class TechnoExt static void LimboAttachments(TechnoClass* pThis); static bool IsAttached(TechnoClass* pThis); + static bool DoesntOccupyCellAsChild(TechnoClass* pThis); static bool IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep = true); static TechnoClass* GetTopLevelParent(TechnoClass* pThis); static Matrix3D GetAttachmentTransform(TechnoClass* pThis, VoxelIndexKey* pKey = nullptr, bool isShadow = false); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 042b065ff1..a2002d74f1 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -67,6 +67,10 @@ DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) return 0; } + +#pragma region Cell occupation handling + + DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) { enum { IgnoreOccupier = 0x73FC10, Continue = 0x73F530 }; @@ -74,8 +78,12 @@ DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) GET(UnitClass*, pThis, EBX); GET(TechnoClass*, pOccupier, ESI); - if (pThis == pOccupier || TechnoExt::IsChildOf(pOccupier, pThis)) + if (pThis == pOccupier + || TechnoExt::DoesntOccupyCellAsChild(pOccupier) + || TechnoExt::IsChildOf(pOccupier, pThis)) + { return IgnoreOccupier; + } return Continue; } @@ -87,12 +95,110 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) GET(InfantryClass*, pThis, EBP); GET(TechnoClass*, pOccupier, ESI); - if ((TechnoClass*)pThis == pOccupier || TechnoExt::IsChildOf(pOccupier, (TechnoClass*)pThis)) + if ((TechnoClass*)pThis == pOccupier + || TechnoExt::DoesntOccupyCellAsChild(pOccupier) + || TechnoExt::IsChildOf(pOccupier, (TechnoClass*)pThis)) + { + return IgnoreOccupier; + } + + return Continue; +} + +// above hook seems not enough + +enum CellTechnoMode +{ + NoAttachments, + NoVirtualOrRelatives, + NoVirtual, + NoRelatives, + All, + + DefaultBehavior = All, +}; + +namespace TechnoAttachmentTemp +{ + CellTechnoMode currentMode = DefaultBehavior; +} + +typedef size_t nothing_t; + +#define DEFINE_CELLTECHNO_WRAPPER(mode) \ +TechnoClass* __fastcall CellTechno_##mode(CellClass* pThis, nothing_t, Point2D *a2, bool check_alt, TechnoClass* techno) \ +{ \ + TechnoAttachmentTemp::currentMode = CellTechnoMode::mode; \ + auto const retval = pThis->FindTechnoNearestTo(*a2, check_alt, techno); \ + TechnoAttachmentTemp::currentMode = CellTechnoMode::DefaultBehavior; \ + return retval; \ +} + +DEFINE_CELLTECHNO_WRAPPER(NoAttachments); +DEFINE_CELLTECHNO_WRAPPER(NoVirtualOrRelatives); +DEFINE_CELLTECHNO_WRAPPER(NoVirtual); +DEFINE_CELLTECHNO_WRAPPER(NoRelatives); +DEFINE_CELLTECHNO_WRAPPER(All); + +#undef DEFINE_CELLTECHNO_WRAPPER + +DEFINE_HOOK(0x47C432, CellClass_CellTechno_HandleAttachments, 0x0) +{ + enum { Continue = 0x47C437, IgnoreOccupier = 0x47C4A7 }; + + GET(TechnoClass*, pOccupier, ESI); + GET_BASE(TechnoClass*, pSelf, 0x10); + + using namespace TechnoAttachmentTemp; + const bool noAttachments = + currentMode == CellTechnoMode::NoAttachments; + const bool noVirtual = + currentMode == CellTechnoMode::NoVirtual || + currentMode == CellTechnoMode::NoVirtualOrRelatives; + const bool noRelatives = + currentMode == CellTechnoMode::NoRelatives || + currentMode == CellTechnoMode::NoVirtualOrRelatives; + + if (pOccupier == pSelf // restored code + || noAttachments && TechnoExt::IsAttached(pOccupier) + || noVirtual && TechnoExt::DoesntOccupyCellAsChild(pOccupier) + || noRelatives && TechnoExt::IsChildOf(pOccupier, (TechnoClass*)pSelf)) + { return IgnoreOccupier; + } return Continue; } +// skip building placement occupation checks for virtuals +DEFINE_JUMP(CALL, 0x47C805, GET_OFFSET(CellTechno_NoVirtual)); +DEFINE_JUMP(CALL, 0x47C738, GET_OFFSET(CellTechno_NoVirtual)); + +// skip building attachments in bib check +DEFINE_JUMP(CALL, 0x4495F2, GET_OFFSET(CellTechno_NoVirtualOrRelatives)); +DEFINE_JUMP(CALL, 0x44964E, GET_OFFSET(CellTechno_NoVirtualOrRelatives)); + +DEFINE_HOOK(0x4495F7, BuildingClass_ClearFactoryBib_SkipCreatedUnitAttachments, 0x0) +{ + enum { BibClear = 0x44969B, NotClear = 0x4495FF }; + + GET(TechnoClass*, pBibTechno, EAX); + + if (!pBibTechno) + return BibClear; + + GET(BuildingClass*, pThis, ESI); + + TechnoClass* pBuiltTechno = pThis->GetNthLink(0); + if (TechnoExt::IsChildOf(pBibTechno, pBuiltTechno)) + return BibClear; + + return NotClear; +} + +#pragma endregion + + DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) { enum { Skip = 0x6CCCCA, Continue = 0 }; diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index f0fde7133b..5a7fcf674a 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -20,6 +20,7 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->InheritOwner.Read(exINI, section, "InheritOwner"); this->InheritStateEffects.Read(exINI, section, "InheritStateEffects"); this->InheritDestruction.Read(exINI, section, "InheritDestruction"); + this->OccupiesCell.Read(exINI, section, "OccupiesCell"); this->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); this->YSortPosition.Read(exINI, section, "YSortPosition"); @@ -43,6 +44,7 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->InheritOwner) .Process(this->InheritStateEffects) .Process(this->InheritDestruction) + .Process(this->OccupiesCell) .Process(this->LowSelectionPriority) .Process(this->YSortPosition) // .Process(this->CanBeForceDetached) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 8ebff314e7..0d8297a404 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -14,6 +14,7 @@ class AttachmentTypeClass final : public Enumerable Valueable InheritOwner; // aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. Valueable InheritDestruction; + Valueable OccupiesCell; Valueable LowSelectionPriority; Valueable YSortPosition; // Valueable CanBeForceDetached; @@ -29,6 +30,7 @@ class AttachmentTypeClass final : public Enumerable , InheritCommands { true } , InheritOwner { true } , InheritStateEffects { true } + , OccupiesCell { true } , InheritDestruction { true } , LowSelectionPriority { true } , YSortPosition { AttachmentYSortPosition::Default } From c2cb8a4b06055924da0dd9a15dffc35b4ce4fb2a Mon Sep 17 00:00:00 2001 From: Meta Date: Fri, 7 Jul 2023 00:26:48 +0300 Subject: [PATCH 061/112] OccupiesCell pt. 2 --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 79 +++++++++++++++++++---- 1 file changed, 66 insertions(+), 13 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index a2002d74f1..3f94315354 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -8,6 +8,8 @@ #include +typedef size_t nothing_t; + // DEFINE_HOOK(0x4DA86E, FootClass_AI_UpdateAttachedLocomotion, 0x0) // { // GET(FootClass* const, pThis, ESI); @@ -67,19 +69,74 @@ DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) return 0; } - #pragma region Cell occupation handling +// DEFINE_HOOK(0x7441B6, UnitClass_SetOccupyBit_SkipVirtual, 0x6) +// { +// enum { Skip = 0x7441F6, Continue = 0 }; + +// GET(UnitClass*, pThis, ECX); + +// if (TechnoExt::DoesntOccupyCellAsChild(pThis)) +// return Skip; + +// return Continue; +// } + +// DEFINE_HOOK(0x744216, UnitClass_ClearOccupyBit_SkipVirtual, 0x6) +// { +// enum { Skip = 0x74425E, Continue = 0 }; + +// GET(UnitClass*, pThis, ECX); + +// if (TechnoExt::DoesntOccupyCellAsChild(pThis)) +// return Skip; + +// return Continue; +// } + +// screw Ares + +void __fastcall UnitClass_SetOccupyBit(UnitClass* pThis, nothing_t, const CoordStruct& coords) +{ + JMP_THIS(0x7441B0); +} + +void __fastcall UnitClass_SetOccupyBit_SkipVirtual(UnitClass* pThis, nothing_t _, const CoordStruct& coords) +{ + if (!TechnoExt::DoesntOccupyCellAsChild(pThis)) + UnitClass_SetOccupyBit(pThis, _, coords); +} + +void __fastcall UnitClass_ClearOccupyBit(UnitClass* pThis, nothing_t, const CoordStruct& coords) +{ + JMP_THIS(0x744210); +} + +void __fastcall UnitClass_ClearOccupyBit_SkipVirtual(UnitClass* pThis, nothing_t _, const CoordStruct& coords) +{ + if (!TechnoExt::DoesntOccupyCellAsChild(pThis)) + UnitClass_ClearOccupyBit(pThis, _, coords); +} + +DEFINE_JUMP(VTABLE, 0x7F5D60, GET_OFFSET(UnitClass_SetOccupyBit_SkipVirtual)) +DEFINE_JUMP(VTABLE, 0x7F5D64, GET_OFFSET(UnitClass_ClearOccupyBit_SkipVirtual)) + +// TODO ^ same for non-UnitClass, cba for now + +// TODO now children block the parent from moving, should not happen DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) { - enum { IgnoreOccupier = 0x73FC10, Continue = 0x73F530 }; + enum { ItsMe = 0x73FC10, IgnoreOccupier = 0x73FA87, Continue = 0x73F530 }; GET(UnitClass*, pThis, EBX); GET(TechnoClass*, pOccupier, ESI); - if (pThis == pOccupier - || TechnoExt::DoesntOccupyCellAsChild(pOccupier) + if (pThis == pOccupier) + return ItsMe; + + if (TechnoExt::DoesntOccupyCellAsChild(pOccupier) || TechnoExt::IsChildOf(pOccupier, pThis)) { return IgnoreOccupier; @@ -105,8 +162,6 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) return Continue; } -// above hook seems not enough - enum CellTechnoMode { NoAttachments, @@ -123,8 +178,6 @@ namespace TechnoAttachmentTemp CellTechnoMode currentMode = DefaultBehavior; } -typedef size_t nothing_t; - #define DEFINE_CELLTECHNO_WRAPPER(mode) \ TechnoClass* __fastcall CellTechno_##mode(CellClass* pThis, nothing_t, Point2D *a2, bool check_alt, TechnoClass* techno) \ { \ @@ -473,12 +526,12 @@ DEFINE_HOOK(0x736A2F, UnitClass_RotationAI_ForbidAttachmentRotation, 0x7) : ContinueCheck; } -Action __fastcall UnitClass_MouseOverCell(UnitClass* pThis, void* _, CellStruct const* pCell, bool checkFog, bool ignoreForce) +Action __fastcall UnitClass_MouseOverCell(UnitClass* pThis, nothing_t _, CellStruct const* pCell, bool checkFog, bool ignoreForce) { JMP_THIS(0x7404B0); } -Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, void* _, CellStruct const* pCell, bool checkFog, bool ignoreForce) +Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, nothing_t _, CellStruct const* pCell, bool checkFog, bool ignoreForce) { Action result = UnitClass_MouseOverCell(pThis, _, pCell, checkFog, ignoreForce); @@ -583,17 +636,17 @@ DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) // return Continue; // } -CoordStruct __fastcall ObjectClass_GetRenderCoords(ObjectClass* pThis, void* _) +CoordStruct __fastcall ObjectClass_GetRenderCoords(ObjectClass* pThis, nothing_t _) { JMP_THIS(0x41BE00); } -CoordStruct __fastcall BuildingClass_GetRenderCoords(BuildingClass* pThis, void* _) +CoordStruct __fastcall BuildingClass_GetRenderCoords(BuildingClass* pThis, nothing_t _) { JMP_THIS(0x459EF0); } -CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis, void* _) +CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis, nothing_t _) { auto const& pExt = TechnoExt::ExtMap.Find(pThis); if (pExt && pExt->ParentAttachment) From c8859340c07ec1eb84bbb03efe70fc5597fa9604 Mon Sep 17 00:00:00 2001 From: Meta Date: Fri, 7 Jul 2023 18:32:24 +0300 Subject: [PATCH 062/112] Finished initial OccupiesCell implementation --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 77 +++++++++++++++++++++-- 1 file changed, 73 insertions(+), 4 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 3f94315354..278714e82c 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -126,25 +126,85 @@ DEFINE_JUMP(VTABLE, 0x7F5D64, GET_OFFSET(UnitClass_ClearOccupyBit_SkipVirtual)) // TODO now children block the parent from moving, should not happen +namespace TechnoAttachmentTemp +{ + // no idea what Ares or w/e else is doing with occupation flags, + // so just to be safe assume it can be null and store it + byte storedVehicleFlag; +} + +// Game assumes cell is occupied by a vehicle by default and if this vehicle +// turns out to be self, then it un-assumes the occupancy. Because with techno +// attachment logic it's possible to have multiple vehicles on the same cell, +// we flip the logic from "passable if special case is found" to "impassable if +// non-special case is found" - Kerbiter + +DEFINE_HOOK(0x73F520, UnitClass_CanEnterCell_AssumeNoVehicleByDefault, 0x0) +{ + enum { Check = 0x73F528, Skip = 0x73FA92 }; + + REF_STACK(byte, occupyFlags, STACK_OFFSET(0x90, -0x7C)); + REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x90, -0x7B)); + + GET(TechnoClass*, pOccupier, ESI); + + if (!pOccupier) // stolen code + return Skip; + + TechnoAttachmentTemp::storedVehicleFlag = occupyFlags & 0x20; + + occupyFlags &= ~0x20; + isVehicleFlagSet = false; + + return Check; +} + DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) { - enum { ItsMe = 0x73FC10, IgnoreOccupier = 0x73FA87, Continue = 0x73F530 }; + enum { IgnoreOccupier = 0x73FA87, Continue = 0x73F530 }; GET(UnitClass*, pThis, EBX); GET(TechnoClass*, pOccupier, ESI); - if (pThis == pOccupier) - return ItsMe; + REF_STACK(byte, occupyFlags, STACK_OFFSET(0x90, -0x7C)); + REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x90, -0x7B)); - if (TechnoExt::DoesntOccupyCellAsChild(pOccupier) + if (pThis == pOccupier + || TechnoExt::DoesntOccupyCellAsChild(pOccupier) || TechnoExt::IsChildOf(pOccupier, pThis)) { return IgnoreOccupier; } + if (abstract_cast(pOccupier)) + { + occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; + isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + } + return Continue; } +DEFINE_HOOK(0x51C249, InfantryClass_CanEnterCell_AssumeNoVehicleByDefault, 0x0) +{ + enum { Check = 0x51C251, Skip = 0x51C78F }; + + REF_STACK(byte, occupyFlags, STACK_OFFSET(0x34, -0x21)); + REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x34, -0x22)); + + GET(TechnoClass*, pOccupier, ESI); + + if (!pOccupier) // stolen code + return Skip; + + TechnoAttachmentTemp::storedVehicleFlag = occupyFlags & 0x20; + + occupyFlags &= ~0x20; + isVehicleFlagSet = false; + + return Check; +} + DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) { enum { IgnoreOccupier = 0x51C70F, Continue = 0x51C259 }; @@ -152,6 +212,9 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) GET(InfantryClass*, pThis, EBP); GET(TechnoClass*, pOccupier, ESI); + REF_STACK(byte, occupyFlags, STACK_OFFSET(0x34, -0x21)); + REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x34, -0x22)); + if ((TechnoClass*)pThis == pOccupier || TechnoExt::DoesntOccupyCellAsChild(pOccupier) || TechnoExt::IsChildOf(pOccupier, (TechnoClass*)pThis)) @@ -159,6 +222,12 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) return IgnoreOccupier; } + if (abstract_cast(pOccupier)) + { + occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; + isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + } + return Continue; } From fa16ca259dc75037dcfebab7986b436c4f583929 Mon Sep 17 00:00:00 2001 From: Meta Date: Mon, 10 Jul 2023 00:45:47 +0300 Subject: [PATCH 063/112] Clean up a bit, add InheritLayerStatus and custom layer calculation, improve AircraftTracker (un)limbo stuff --- YRpp | 2 +- docs/Contributing.md | 2 +- docs/New-or-Enhanced-Logics.md | 7 ++ src/Ext/Rules/Body.cpp | 5 + src/Ext/Rules/Body.h | 5 + src/Ext/Techno/Hooks.Shield.cpp | 16 +-- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 116 +++++++++++-------- src/Ext/TechnoType/Body.cpp | 6 + src/Ext/TechnoType/Body.h | 5 + src/Locomotion/AttachmentLocomotionClass.cpp | 59 +++++++++- src/Locomotion/AttachmentLocomotionClass.h | 9 +- src/Locomotion/TestLocomotionClass.cpp | 2 +- src/Locomotion/TestLocomotionClass.h | 2 +- src/Misc/Hooks.INIInheritance.cpp | 2 +- src/New/Type/AttachmentTypeClass.cpp | 2 + src/New/Type/AttachmentTypeClass.h | 2 + src/Utilities/Macro.h | 8 ++ 17 files changed, 184 insertions(+), 66 deletions(-) diff --git a/YRpp b/YRpp index 484faae30f..2d1a15c891 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 484faae30f6ca32d3e0d603d545ed3e49087f845 +Subproject commit 2d1a15c891dcaaa1bb8c4bc3bfa6f2da04932294 diff --git a/docs/Contributing.md b/docs/Contributing.md index a994803569..f8ddd6c118 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -146,7 +146,7 @@ ExtData(TerrainTypeClass* OwnerObject) : Extension(OwnerObject - Class fields that can be set via INI tags should be named exactly like ini tags with dots replaced with underscores. - Pointer type declarations always have pointer sign `*` attached to the type declaration. - Non-static class extension methods faked by declaring a static method with `pThis` as a first argument are only to be placed in the extension class for the class instance of which `pThis` is. - - If it's crucial to fake `__thiscall` you may use `__fastcall` and use `void*` or `void* _` as a second argument to discard value passed through `EDX` register. Such methods are to be used for call replacement. + - If it's crucial to fake `__thiscall` you may use `__fastcall` and use `discard_t` or `discard_t _` as a second argument to discard value passed through `EDX` register. Such methods are to be used for call replacement. - Hooks have to be named using a following scheme: `HookedFunction_HookPurpose`, or `ClassName_HookedMethod_HookPurpose`. Defined-again hooks are exempt from this scheme due to impossibility to define different names for the same hook. - Return addresses should use anonymous enums to make it clear what address means what, if applicable. The enum has to be placed right at the function start and include all addresses that are used in this hook: ```cpp diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 90d2bff295..07c60b77bb 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -29,6 +29,7 @@ InheritCommands=true ; boolean LowSelectionPriority=true ; boolean, whether the child is low priority while attached YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent InheritDestruction=true ; boolean +InheritHeightStatus=true ; boolean, whether the layer and InAir/OnGround/IsSurfaced inherited from parent OccupiesCell=true ; boolean DestructionWeapon.Child= ; WeaponType, detonated on child when parent is destroyed DestructionWeapon.Parent= ; WeaponType, detonated on parent when child is destroyed @@ -36,11 +37,17 @@ ParentDestructionMission= ; MissionType, queued to child when parent ParentDetachmentMission= ; MissionType, queued to child when it's detached from parent [SOMETECHNO] ; TechnoTypeClass +AttachmentTopLayerMinHeight= ; integer +AttachmentUndergroundLayerMaxHeight= ; integer AttachmentX.Type=MNT ; AttachmentType (example) AttachmentX.TechnoType= ; TechnoType that can be attached, currently only units are supported AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height AttachmentX.IsOnTurret=false ; boolean AttachmentX.RotationAdjust=0 ; rotation in DirType, from -255 to 255 + +[General] +AttachmentTopLayerMinHeight=500 ; integer +AttachmentUndergroundLayerMaxHeight=-256 ; integer ``` ### Custom Radiation Types diff --git a/src/Ext/Rules/Body.cpp b/src/Ext/Rules/Body.cpp index f91a70bc2c..9f083d33cc 100644 --- a/src/Ext/Rules/Body.cpp +++ b/src/Ext/Rules/Body.cpp @@ -118,6 +118,9 @@ void RulesExt::ExtData::LoadBeforeTypeData(RulesClass* pThis, CCINIClass* pINI) this->DisplayIncome_Houses.Read(exINI, GameStrings::AudioVisual, "DisplayIncome.Houses"); this->DisplayIncome_AllowAI.Read(exINI, GameStrings::AudioVisual, "DisplayIncome.AllowAI"); + this->AttachmentTopLayerMinHeight.Read(exINI, GameStrings::General, "AttachmentTopLayerMinHeight"); + this->AttachmentUndergroundLayerMaxHeight.Read(exINI, GameStrings::General, "AttachmentUndergroundLayerMaxHeight"); + // Section AITargetTypes int itemsCount = pINI->GetKeyCount(sectionAITargetTypes); for (int i = 0; i < itemsCount; ++i) @@ -231,6 +234,8 @@ void RulesExt::ExtData::Serialize(T& Stm) .Process(this->CrateOnlyOnLand) .Process(this->RadialIndicatorVisibility) .Process(this->DrawTurretShadow) + .Process(this->AttachmentTopLayerMinHeight) + .Process(this->AttachmentUndergroundLayerMaxHeight) ; } diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 2b38f4fb28..b6fb259a1d 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -81,6 +81,9 @@ class RulesExt Valueable RadialIndicatorVisibility; Valueable DrawTurretShadow; + Valueable AttachmentTopLayerMinHeight; + Valueable AttachmentUndergroundLayerMaxHeight; + ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } , InfantryGainSelfHealCap {} @@ -126,6 +129,8 @@ class RulesExt , CrateOnlyOnLand { false } , RadialIndicatorVisibility { AffectedHouse::Allies } , DrawTurretShadow { false } + , AttachmentTopLayerMinHeight { 500 } + , AttachmentUndergroundLayerMaxHeight { -256 } { } virtual ~ExtData() = default; diff --git a/src/Ext/Techno/Hooks.Shield.cpp b/src/Ext/Techno/Hooks.Shield.cpp index 32d0f3f8ec..c79d6ddf42 100644 --- a/src/Ext/Techno/Hooks.Shield.cpp +++ b/src/Ext/Techno/Hooks.Shield.cpp @@ -320,12 +320,12 @@ class AresScheme #pragma region UnitClass_GetFireError_Heal -FireError __fastcall UnitClass__GetFireError(UnitClass* pThis, void* _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) +FireError __fastcall UnitClass__GetFireError(UnitClass* pThis, discard_t _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) { JMP_THIS(0x740FD0); } -FireError __fastcall UnitClass__GetFireError_Wrapper(UnitClass* pThis, void* _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) +FireError __fastcall UnitClass__GetFireError_Wrapper(UnitClass* pThis, discard_t _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) { AresScheme::Prefix(pThis, pObj, nWeaponIndex, false); auto const result = UnitClass__GetFireError(pThis, _, pObj, nWeaponIndex, ignoreRange); @@ -336,11 +336,11 @@ DEFINE_JUMP(VTABLE, 0x7F6030, GET_OFFSET(UnitClass__GetFireError_Wrapper)) #pragma endregion UnitClass_GetFireError_Heal #pragma region InfantryClass_GetFireError_Heal -FireError __fastcall InfantryClass__GetFireError(InfantryClass* pThis, void* _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) +FireError __fastcall InfantryClass__GetFireError(InfantryClass* pThis, discard_t _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) { JMP_THIS(0x51C8B0); } -FireError __fastcall InfantryClass__GetFireError_Wrapper(InfantryClass* pThis, void* _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) +FireError __fastcall InfantryClass__GetFireError_Wrapper(InfantryClass* pThis, discard_t _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) { AresScheme::Prefix(pThis, pObj, nWeaponIndex, false); auto const result = InfantryClass__GetFireError(pThis, _, pObj, nWeaponIndex, ignoreRange); @@ -351,12 +351,12 @@ DEFINE_JUMP(VTABLE, 0x7EB418, GET_OFFSET(InfantryClass__GetFireError_Wrapper)) #pragma endregion InfantryClass_GetFireError_Heal #pragma region UnitClass__WhatAction -Action __fastcall UnitClass__WhatAction(UnitClass* pThis, void* _, ObjectClass* pObj, bool ignoreForce) +Action __fastcall UnitClass__WhatAction(UnitClass* pThis, discard_t _, ObjectClass* pObj, bool ignoreForce) { JMP_THIS(0x73FD50); } -Action __fastcall UnitClass__WhatAction_Wrapper(UnitClass* pThis, void* _, ObjectClass* pObj, bool ignoreForce) +Action __fastcall UnitClass__WhatAction_Wrapper(UnitClass* pThis, discard_t _, ObjectClass* pObj, bool ignoreForce) { AresScheme::Prefix(pThis, pObj, -1, false); auto result = UnitClass__WhatAction(pThis, _, pObj, ignoreForce); @@ -396,12 +396,12 @@ DEFINE_JUMP(VTABLE, 0x7F5CE4, GET_OFFSET(UnitClass__WhatAction_Wrapper)) #pragma endregion UnitClass__WhatAction #pragma region InfantryClass__WhatAction -Action __fastcall InfantryClass__WhatAction(InfantryClass* pThis, void* _, ObjectClass* pObj, bool ignoreForce) +Action __fastcall InfantryClass__WhatAction(InfantryClass* pThis, discard_t _, ObjectClass* pObj, bool ignoreForce) { JMP_THIS(0x51E3B0); } -Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis, void* _, ObjectClass* pObj, bool ignoreForce) +Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis, discard_t _, ObjectClass* pObj, bool ignoreForce) { AresScheme::Prefix(pThis, pObj, -1, pThis->Type->Engineer); auto const result = InfantryClass__WhatAction(pThis, _, pObj, ignoreForce); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 278714e82c..81df045741 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -8,18 +8,6 @@ #include -typedef size_t nothing_t; - -// 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(0x707CB3, TechnoClass_KillCargo_HandleAttachments, 0x6) { @@ -97,26 +85,16 @@ DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) // screw Ares -void __fastcall UnitClass_SetOccupyBit(UnitClass* pThis, nothing_t, const CoordStruct& coords) -{ - JMP_THIS(0x7441B0); -} - -void __fastcall UnitClass_SetOccupyBit_SkipVirtual(UnitClass* pThis, nothing_t _, const CoordStruct& coords) +void __fastcall UnitClass_SetOccupyBit_SkipVirtual(UnitClass* pThis, discard_t, const CoordStruct& coords) { if (!TechnoExt::DoesntOccupyCellAsChild(pThis)) - UnitClass_SetOccupyBit(pThis, _, coords); -} - -void __fastcall UnitClass_ClearOccupyBit(UnitClass* pThis, nothing_t, const CoordStruct& coords) -{ - JMP_THIS(0x744210); + pThis->UnitClass::MarkAllOccupationBits(coords); } -void __fastcall UnitClass_ClearOccupyBit_SkipVirtual(UnitClass* pThis, nothing_t _, const CoordStruct& coords) +void __fastcall UnitClass_ClearOccupyBit_SkipVirtual(UnitClass* pThis, discard_t, const CoordStruct& coords) { if (!TechnoExt::DoesntOccupyCellAsChild(pThis)) - UnitClass_ClearOccupyBit(pThis, _, coords); + pThis->UnitClass::UnmarkAllOccupationBits(coords); } DEFINE_JUMP(VTABLE, 0x7F5D60, GET_OFFSET(UnitClass_SetOccupyBit_SkipVirtual)) @@ -248,7 +226,7 @@ namespace TechnoAttachmentTemp } #define DEFINE_CELLTECHNO_WRAPPER(mode) \ -TechnoClass* __fastcall CellTechno_##mode(CellClass* pThis, nothing_t, Point2D *a2, bool check_alt, TechnoClass* techno) \ +TechnoClass* __fastcall CellTechno_##mode(CellClass* pThis, discard_t, Point2D *a2, bool check_alt, TechnoClass* techno) \ { \ TechnoAttachmentTemp::currentMode = CellTechnoMode::mode; \ auto const retval = pThis->FindTechnoNearestTo(*a2, check_alt, techno); \ @@ -320,6 +298,63 @@ DEFINE_HOOK(0x4495F7, BuildingClass_ClearFactoryBib_SkipCreatedUnitAttachments, #pragma endregion +#pragma region InAir/OnGround + +bool __fastcall TechnoClass_OnGround(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + + return pExt->ParentAttachment && pExt->ParentAttachment->GetType()->InheritHeightStatus + ? pExt->ParentAttachment->Parent->IsOnFloor() + : pThis->ObjectClass::IsOnFloor(); +} + +bool __fastcall TechnoClass_InAir(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + + return pExt->ParentAttachment && pExt->ParentAttachment->GetType()->InheritHeightStatus + ? pExt->ParentAttachment->Parent->IsInAir() + : pThis->ObjectClass::IsInAir(); +} + +bool __fastcall TechnoClass_IsSurfaced(TechnoClass* pThis) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + + return pExt->ParentAttachment && pExt->ParentAttachment->GetType()->InheritHeightStatus + ? pExt->ParentAttachment->Parent->IsSurfaced() + : pThis->ObjectClass::IsSurfaced(); +} + +// TechnoClass +DEFINE_JUMP(VTABLE, 0x7F49B0, GET_OFFSET(TechnoClass_OnGround)); +DEFINE_JUMP(VTABLE, 0x7F49B4, GET_OFFSET(TechnoClass_InAir)); +DEFINE_JUMP(VTABLE, 0x7F49DC, GET_OFFSET(TechnoClass_IsSurfaced)); + +// BuildingClass +DEFINE_JUMP(VTABLE, 0x7E3F0C, GET_OFFSET(TechnoClass_OnGround)); +DEFINE_JUMP(VTABLE, 0x7E3F10, GET_OFFSET(TechnoClass_InAir)); +DEFINE_JUMP(VTABLE, 0x7E3F38, GET_OFFSET(TechnoClass_IsSurfaced)); + +// FootClass +DEFINE_JUMP(VTABLE, 0x7E8CE4, GET_OFFSET(TechnoClass_OnGround)); +DEFINE_JUMP(VTABLE, 0x7E8CE8, GET_OFFSET(TechnoClass_InAir)); +DEFINE_JUMP(VTABLE, 0x7E8D10, GET_OFFSET(TechnoClass_IsSurfaced)); + +// UnitClass +DEFINE_JUMP(VTABLE, 0x7F5CC0, GET_OFFSET(TechnoClass_OnGround)); +DEFINE_JUMP(VTABLE, 0x7F5CC4, GET_OFFSET(TechnoClass_InAir)); +DEFINE_JUMP(VTABLE, 0x7F5CEC, GET_OFFSET(TechnoClass_IsSurfaced)); + +// InfantryClass +DEFINE_JUMP(VTABLE, 0x7EB0A8, GET_OFFSET(TechnoClass_OnGround)); +DEFINE_JUMP(VTABLE, 0x7EB0AC, GET_OFFSET(TechnoClass_InAir)); +DEFINE_JUMP(VTABLE, 0x7EB0D4, GET_OFFSET(TechnoClass_IsSurfaced)); + +// AircraftClass has it's own logic, who would want to attach aircrafts anyways + +#pragma endregion DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) { @@ -595,14 +630,9 @@ DEFINE_HOOK(0x736A2F, UnitClass_RotationAI_ForbidAttachmentRotation, 0x7) : ContinueCheck; } -Action __fastcall UnitClass_MouseOverCell(UnitClass* pThis, nothing_t _, CellStruct const* pCell, bool checkFog, bool ignoreForce) +Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, CellStruct const* pCell, bool checkFog, bool ignoreForce) { - JMP_THIS(0x7404B0); -} - -Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, nothing_t _, CellStruct const* pCell, bool checkFog, bool ignoreForce) -{ - Action result = UnitClass_MouseOverCell(pThis, _, pCell, checkFog, ignoreForce); + Action result = pThis->UnitClass::MouseOverCell(pCell, checkFog, ignoreForce); auto const& pExt = TechnoExt::ExtMap.Find(pThis); if (!pExt->ParentAttachment) @@ -625,6 +655,8 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, nothing_t _, return result; } +// TODO MouseOverObject for entering bunkers, grinder, buildings etc + DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) DEFINE_HOOK(0x4D74EC, FootClass_ObjectClickedAction_HandleAttachment, 0x6) @@ -705,17 +737,7 @@ DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) // return Continue; // } -CoordStruct __fastcall ObjectClass_GetRenderCoords(ObjectClass* pThis, nothing_t _) -{ - JMP_THIS(0x41BE00); -} - -CoordStruct __fastcall BuildingClass_GetRenderCoords(BuildingClass* pThis, nothing_t _) -{ - JMP_THIS(0x459EF0); -} - -CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis, nothing_t _) +CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis) { auto const& pExt = TechnoExt::ExtMap.Find(pThis); if (pExt && pExt->ParentAttachment) @@ -724,7 +746,7 @@ CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis, nothing_t return pExt->ParentAttachment->Cache.TopLevelParent->GetRenderCoords(); } - return ObjectClass_GetRenderCoords(pThis, _); + return pThis->ObjectClass::GetRenderCoords(); } // TODO hook matrix @@ -932,7 +954,7 @@ int __fastcall TechnoClass_SortY_Wrapper(ObjectClass* pThis) } } - return pThis->Get_YSort(); + return pThis->ObjectClass::GetYSort(); } DEFINE_JUMP(CALL, 0x449413, GET_OFFSET(TechnoClass_SortY_Wrapper)) // BuildingClass diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index cbe3edb367..f4b368fc31 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -207,6 +207,10 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->AutoFire.Read(exINI, pSection, "AutoFire"); this->AutoFire_TargetSelf.Read(exINI, pSection, "AutoFire.TargetSelf"); + + this->AttachmentTopLayerMinHeight.Read(exINI, pSection, "AttachmentTopLayerMinHeight"); + this->AttachmentUndergroundLayerMaxHeight.Read(exINI, pSection, "AttachmentUndergroundLayerMaxHeight"); + // 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) @@ -546,6 +550,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->CrushOverlayExtraForwardTilt) .Process(this->CrushSlowdownMultiplier) + .Process(this->AttachmentTopLayerMinHeight) + .Process(this->AttachmentUndergroundLayerMaxHeight) .Process(this->AttachmentData) ; } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 171af505e8..ae372af14e 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -151,6 +151,9 @@ class TechnoTypeExt Valueable CrushOverlayExtraForwardTilt; Valueable CrushSlowdownMultiplier; + Valueable AttachmentTopLayerMinHeight; + Valueable AttachmentUndergroundLayerMaxHeight; + struct AttachmentDataEntry { ValueableIdx Type; @@ -322,6 +325,8 @@ class TechnoTypeExt , CrushForwardTiltPerFrame {} , CrushOverlayExtraForwardTilt { 0.02 } + , AttachmentTopLayerMinHeight { RulesExt::Global()->AttachmentTopLayerMinHeight } + , AttachmentUndergroundLayerMaxHeight { RulesExt::Global()->AttachmentUndergroundLayerMaxHeight } , AttachmentData {} { } diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 779ef6888f..a22db988fd 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -120,7 +120,6 @@ ZGradient AttachmentLocomotionClass::Z_Gradient() : LocomotionClass::Z_Gradient(); } - bool AttachmentLocomotionClass::Process() { if (this->LinkedTo->IsAlive) @@ -157,6 +156,17 @@ bool AttachmentLocomotionClass::Process() this->PreviousCell = newPos; } + AttachmentClass* pAttachment = this->GetAttachment(); + if (pAttachment && pAttachment->GetType()->InheritHeightStatus) + { + this->LinkedTo->OnBridge = pAttachment->Parent->OnBridge; + } + else + { + this->LinkedTo->OnBridge = false; // GetHeight returns different height depending on this + this->LinkedTo->OnBridge = this->ShouldBeOnBridge(); + } + return LocomotionClass::Process(); } @@ -177,10 +187,14 @@ bool AttachmentLocomotionClass::Is_Ion_Sensitive() Layer AttachmentLocomotionClass::In_Which_Layer() { - ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); - return pParentLoco - ? pParentLoco->In_Which_Layer() - : Layer::Ground; + AttachmentClass* pAttachment = this->GetAttachment(); + if (!pAttachment || !pAttachment->GetType()->InheritHeightStatus) + return this->CalculateLayer(); + + auto const pParentAsFoot = abstract_cast(pAttachment->Parent); + return pParentAsFoot && pParentAsFoot->Locomotor + ? pParentAsFoot->Locomotor->In_Which_Layer() + : this->CalculateLayer(); } bool AttachmentLocomotionClass::Is_Moving_Now() @@ -227,6 +241,13 @@ bool AttachmentLocomotionClass::Is_Really_Moving_Now() return pParentLoco && pParentLoco->Is_Really_Moving_Now(); } +void AttachmentLocomotionClass::Limbo() +{ + this->PreviousLayer = Layer::None; + this->PreviousCell = CellStruct::Empty; + // AircraftTracker is handled by FootClass::Limbo +} + HRESULT AttachmentLocomotionClass::Begin_Piggyback(ILocomotion* pointer) { if (!pointer) @@ -337,3 +358,31 @@ ILocomotionPtr AttachmentLocomotionClass::GetAttachmentParentLoco() return result; } + +Layer AttachmentLocomotionClass::CalculateLayer() +{ + auto const pExt = TechnoTypeExt::ExtMap.Find(this->LinkedTo->GetTechnoType()); + int height = this->LinkedTo->GetHeight(); + + if (this->LinkedTo->IsInAir()) + { + if (!this->LinkedTo->OnBridge && this->ShouldBeOnBridge()) + height -= CellClass::BridgeHeight; + + return height >= pExt->AttachmentTopLayerMinHeight + ? Layer::Top : Layer::Air; + } + else if (this->LinkedTo->IsOnFloor()) + { + return height <= pExt->AttachmentUndergroundLayerMaxHeight + ? Layer::Underground : Layer::Ground; + } + + return Layer::None; +} + +bool AttachmentLocomotionClass::ShouldBeOnBridge() +{ + return MapClass::Instance->GetCellAt(this->LinkedTo->Location)->ContainsBridge() + && this->LinkedTo->GetHeight() >= CellClass::BridgeHeight && !this->LinkedTo->IsFallingDown; +} diff --git a/src/Locomotion/AttachmentLocomotionClass.h b/src/Locomotion/AttachmentLocomotionClass.h index b519a4ddfd..bf1f06dd6b 100644 --- a/src/Locomotion/AttachmentLocomotionClass.h +++ b/src/Locomotion/AttachmentLocomotionClass.h @@ -157,7 +157,7 @@ class __declspec(uuid("C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E")) //virtual bool __stdcall Will_Jump_Tracks() override; virtual bool __stdcall Is_Really_Moving_Now() override; //virtual void __stdcall Stop_Movement_Animation() override; - //virtual void __stdcall Clear_Coords() override; + virtual void __stdcall Limbo() override; //virtual void __stdcall Lock() override; //virtual void __stdcall Unlock() override; //virtual int __stdcall Get_Track_Number() override; @@ -181,6 +181,13 @@ class __declspec(uuid("C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E")) // Shortcut to parent techno of this locomotor's owner. ILocomotionPtr GetAttachmentParentLoco(); + // Non-parent layer calculation. + Layer CalculateLayer(); + + // Should the LinkedTo be on bridge (when it's currently not)? + // (yoinked from JumpjetLocomotionClass::In_Which_Layer) + bool ShouldBeOnBridge(); + public: inline AttachmentLocomotionClass() : LocomotionClass { } , PreviousLayer { Layer::None } diff --git a/src/Locomotion/TestLocomotionClass.cpp b/src/Locomotion/TestLocomotionClass.cpp index 0c514e5e05..d01e896112 100644 --- a/src/Locomotion/TestLocomotionClass.cpp +++ b/src/Locomotion/TestLocomotionClass.cpp @@ -140,7 +140,7 @@ bool TestLocomotionClass::Is_Really_Moving_Now() return IsMoving; } -void TestLocomotionClass::Clear_Coords() +void TestLocomotionClass::Limbo() { this->Stop_Moving(); } diff --git a/src/Locomotion/TestLocomotionClass.h b/src/Locomotion/TestLocomotionClass.h index 92d4cae623..c10295de6b 100644 --- a/src/Locomotion/TestLocomotionClass.h +++ b/src/Locomotion/TestLocomotionClass.h @@ -199,7 +199,7 @@ class __declspec(uuid("74FC2B59-C2D3-47D7-9D10-93436A34EBB9")) // you can use an //virtual bool __stdcall Will_Jump_Tracks() override; virtual bool __stdcall Is_Really_Moving_Now() override; //virtual void __stdcall Stop_Movement_Animation() override; - virtual void __stdcall Clear_Coords() override; + virtual void __stdcall Limbo() override; //virtual void __stdcall Lock() override; //virtual void __stdcall Unlock() override; //virtual int __stdcall Get_Track_Number() override; diff --git a/src/Misc/Hooks.INIInheritance.cpp b/src/Misc/Hooks.INIInheritance.cpp index 74eddeba87..fa2ad23bfb 100644 --- a/src/Misc/Hooks.INIInheritance.cpp +++ b/src/Misc/Hooks.INIInheritance.cpp @@ -297,7 +297,7 @@ DEFINE_HOOK(0x527920, INIClass_ReadGUID_Overwrite, 0x5) // locomotor } // Fix issue with TilesInSet caused by incorrect vanilla INIs and the fixed parser returning correct default value (-1) instead of 0 for existing non-integer values -int __fastcall IsometricTileTypeClass_ReadINI_TilesInSet_Wrapper(INIClass* pThis, void* _, const char* pSection, const char* pKey, int defaultValue) +int __fastcall IsometricTileTypeClass_ReadINI_TilesInSet_Wrapper(INIClass* pThis, discard_t _, const char* pSection, const char* pKey, int defaultValue) { if (pThis->Exists(pSection, pKey)) return pThis->ReadInteger(pSection, pKey, 0); diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 5a7fcf674a..9f8e630062 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -20,6 +20,7 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->InheritOwner.Read(exINI, section, "InheritOwner"); this->InheritStateEffects.Read(exINI, section, "InheritStateEffects"); this->InheritDestruction.Read(exINI, section, "InheritDestruction"); + this->InheritHeightStatus.Read(exINI, section, "InheritHeightStatus"); this->OccupiesCell.Read(exINI, section, "OccupiesCell"); this->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); this->YSortPosition.Read(exINI, section, "YSortPosition"); @@ -44,6 +45,7 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->InheritOwner) .Process(this->InheritStateEffects) .Process(this->InheritDestruction) + .Process(this->InheritHeightStatus) .Process(this->OccupiesCell) .Process(this->LowSelectionPriority) .Process(this->YSortPosition) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 0d8297a404..31d8fcb21e 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -14,6 +14,7 @@ class AttachmentTypeClass final : public Enumerable Valueable InheritOwner; // aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. Valueable InheritDestruction; + Valueable InheritHeightStatus; Valueable OccupiesCell; Valueable LowSelectionPriority; Valueable YSortPosition; @@ -32,6 +33,7 @@ class AttachmentTypeClass final : public Enumerable , InheritStateEffects { true } , OccupiesCell { true } , InheritDestruction { true } + , InheritHeightStatus { true } , LowSelectionPriority { true } , YSortPosition { AttachmentYSortPosition::Default } // , CanBeForceDetached { false } diff --git a/src/Utilities/Macro.h b/src/Utilities/Macro.h index 9a749d05c8..d587f724ea 100644 --- a/src/Utilities/Macro.h +++ b/src/Utilities/Macro.h @@ -3,6 +3,14 @@ #include #include "Patch.h" +// Use when some function argument is unneeded. +// Currently that happens when faking __thiscall functions +// via __fastcall ones (fastcall function accepts args via +// ECX, EDX, then stack, thiscall via ECX for this and stack +// for rest, so second arg in fastcall-faked function would +// need to be discarded). +typedef size_t discard_t; + #define GET_REGISTER_STATIC_TYPE(type, dst, reg) static type dst; _asm { mov dst, reg } template From 10d929fc7f88e0d80c4c59a15a14416cccdeb136 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 15 Jul 2023 00:29:14 +0300 Subject: [PATCH 064/112] Fixed waypointing with InheritCommands, temp. disable actions override --- src/Ext/Techno/Body.cpp | 8 ++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.TechnoAttachment.cpp | 13 +++++---- src/Ext/Unit/Hooks.DisallowMoving.cpp | 32 ++++++++++++++++++----- 4 files changed, 42 insertions(+), 12 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index e103167734..7ff7734734 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -393,6 +393,14 @@ bool TechnoExt::IsAttached(TechnoClass* pThis) return pExt && pExt->ParentAttachment; } +bool TechnoExt::HasAttachmentLoco(FootClass* pThis) +{ + IPersistPtr pPersist = pThis->Locomotor; + CLSID locoCLSID {}; + return pPersist && SUCCEEDED(pPersist->GetClassID(&locoCLSID)) + && locoCLSID == __uuidof(AttachmentLocomotionClass); +} + bool TechnoExt::DoesntOccupyCellAsChild(TechnoClass* pThis) { auto const& pExt = TechnoExt::ExtMap.Find(pThis); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 418fce879a..3b9eda0625 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -132,6 +132,7 @@ class TechnoExt static void LimboAttachments(TechnoClass* pThis); static bool IsAttached(TechnoClass* pThis); + static bool HasAttachmentLoco(FootClass* pThis); // FIXME shouldn't be here static bool DoesntOccupyCellAsChild(TechnoClass* pThis); static bool IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep = true); static TechnoClass* GetTopLevelParent(TechnoClass* pThis); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 81df045741..1c7f49a4bc 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -414,8 +414,8 @@ void ParentClickedAction(TechnoClass* pThis, ObjectClass* pTarget, CellStruct* p DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) { - GET_STACK(int, idxPath, STACK_OFFSET(0x18, -0x8)); - GET_STACK(unsigned char, idxWP, STACK_OFFSET(0x18, -0xC)); + REF_STACK(int, idxPath, STACK_OFFSET(0x18, -0x8)); + REF_STACK(unsigned char, idxWP, STACK_OFFSET(0x18, -0xC)); for (auto const& pObject : ObjectClass::CurrentObjects.get()) { @@ -629,7 +629,7 @@ DEFINE_HOOK(0x736A2F, UnitClass_RotationAI_ForbidAttachmentRotation, 0x7) ? SkipBodyRotation : ContinueCheck; } - +/* Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, CellStruct const* pCell, bool checkFog, bool ignoreForce) { Action result = pThis->UnitClass::MouseOverCell(pCell, checkFog, ignoreForce); @@ -658,7 +658,8 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, C // TODO MouseOverObject for entering bunkers, grinder, buildings etc DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) - +*/ +/* DEFINE_HOOK(0x4D74EC, FootClass_ObjectClickedAction_HandleAttachment, 0x6) { enum { ReturnFalse = 0x4D77EC, Continue = 0x0 }; @@ -717,6 +718,7 @@ DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) return Continue; } } +*/ // DEFINE_HOOK(0x41BE02, AbstractClass_RenderCoord_AttachedCoord, 0x7) // { @@ -964,7 +966,7 @@ DEFINE_JUMP(VTABLE, 0x7F5D28, GET_OFFSET(TechnoClass_SortY_Wrapper)) // UnitClas DEFINE_JUMP(LJMP, 0x568831, 0x568841); // Skip locomotion layer check in MapClass::PickUp DEFINE_JUMP(LJMP, 0x4D37A2, 0x4D37AE); // Skip locomotion layer check in FootClass::Mark - +/* // Handle waypoint mode command inheritance DEFINE_HOOK(0x4C73D1, EventClass_Execute_MegaMission_HandleChildren, 0x6) { @@ -1012,3 +1014,4 @@ DEFINE_HOOK(0x4C73D1, EventClass_Execute_MegaMission_HandleChildren, 0x6) return 0; } +*/ diff --git a/src/Ext/Unit/Hooks.DisallowMoving.cpp b/src/Ext/Unit/Hooks.DisallowMoving.cpp index 0359e6c696..5b14e7d079 100644 --- a/src/Ext/Unit/Hooks.DisallowMoving.cpp +++ b/src/Ext/Unit/Hooks.DisallowMoving.cpp @@ -11,13 +11,29 @@ DEFINE_HOOK(0x740A93, UnitClass_Mission_Move_DisallowMoving, 0x6) { - enum { QueueGuardInstead = 0x740AEF, ContinueCheck = 0x0 }; + enum { QueueGuardInstead = 0x740AEF, ReturnTrue = 0x740AFD, ContinueCheck = 0x0 }; GET(UnitClass*, pThis, ESI); - auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + if (TechnoExt::HasAttachmentLoco(pThis)) + { + auto const pExt = TechnoExt::ExtMap.Find(pThis); + if (pExt && pExt->ParentAttachment) + { + auto const& pParent = pExt->ParentAttachment->Parent; + if (pThis->PlanningToken && pThis->PlanningToken->PlanningNodes.Count + && pParent->PlanningToken && pParent->PlanningToken->PlanningNodes.Count + && pThis->PlanningToken->PlanningNodes[0] == pParent->PlanningToken->PlanningNodes[0]) + { + return ReturnTrue; + } + } + pThis->EnterIdleMode(false, true); + return ReturnTrue; + } // skips this->IsHarvesting = 0, may backfire somewhere - Kerbiter - return pThis->Type->Speed == 0 || pExt->ParentAttachment + return pThis->Type->Speed == 0 ? QueueGuardInstead : ContinueCheck; } @@ -27,9 +43,8 @@ DEFINE_HOOK(0x741AA7, UnitClass_Assign_Destination_DisallowMoving, 0x6) enum { ClearNavComsAndReturn = 0x743173, ContinueCheck = 0x0 }; GET(UnitClass*, pThis, EBP); - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - return pThis->Type->Speed == 0 || pExt->ParentAttachment + return pThis->Type->Speed == 0 || TechnoExt::HasAttachmentLoco(pThis) ? ClearNavComsAndReturn : ContinueCheck; } @@ -40,7 +55,8 @@ DEFINE_HOOK(0x743B4B, UnitClass_Scatter_DisallowMoving, 0x6) GET(UnitClass*, pThis, EBP); - return pThis->Type->Speed == 0 ? ReleaseReturn : ContinueCheck; + return pThis->Type->Speed == 0 || TechnoExt::HasAttachmentLoco(pThis) + ? ReleaseReturn : ContinueCheck; } DEFINE_HOOK(0x74038F, UnitClass_What_Action_ObjectClass_DisallowMoving_1, 0x6) @@ -84,11 +100,13 @@ DEFINE_HOOK(0x740744, UnitClass_What_Action_DisallowMoving_2, 0x6) return 0; } +// Makes the vehicle keep it's current turret heading without snapping back to neutral position DEFINE_HOOK(0x736B60, UnitClass_Rotation_AI_DisallowMoving, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 ? 0x736AFB : 0; + return pThis->Type->Speed == 0 + ? 0x736AFB : 0; } DEFINE_HOOK(0x73891D, UnitClass_Active_Click_With_DisallowMoving, 0x6) From 33327f71a472a0ede350875d07074d476fbb0ab6 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 15 Jul 2023 00:45:54 +0300 Subject: [PATCH 065/112] que? --- src/Ext/Techno/Body.cpp | 2 +- src/Phobos.Ext.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 5c31ebc91d..ad373fd59f 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -141,7 +141,7 @@ CoordStruct TechnoExt::PassengerKickOutLocation(TechnoClass* pThis, FootClass* p extraDistanceX += 1; extraDistanceY += 1; } - while (extraDistanceX < maxAttempts && (pThis->IsCellOccupied(pCell, (int)FacingType::None, -1, nullptr, false) != Move::OK) && pCell->MapCoords != CellStruct::Empty); + while (extraDistanceX < maxAttempts && (pThis->IsCellOccupied(pCell, FacingType::None, -1, nullptr, false) != Move::OK) && pCell->MapCoords != CellStruct::Empty); pCell = MapClass::Instance->TryGetCellAt(placeCoords); if (pCell) diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index f096628d4b..c1de34fd7d 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -218,7 +218,7 @@ using PhobosTypeRegistry = TypeRegistry< LaserTrailTypeClass, RadTypeClass, ShieldClass, - DigitalDisplayTypeClass + DigitalDisplayTypeClass, AttachmentClass, AttachmentTypeClass // other classes From 451a0839916357c39c30a52ec96e05acc0bbb2d8 Mon Sep 17 00:00:00 2001 From: Meta Date: Wed, 19 Jul 2023 23:59:24 +0300 Subject: [PATCH 066/112] Attempt to fix crashes --- src/Ext/Techno/Body.cpp | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 8b3b110cf3..ce2293cc53 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -634,6 +634,12 @@ void TechnoExt::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) for (auto const& pAttachment : pExt->ChildAttachments) pAttachment->Destroy(pSource); + + // TODO I am not sure, without clearing the attachments it sometimes crashes under + // weird circumstances, like if the techno exists but the parent attachment isn't, + // in particular in can enter cell hook, this may be a bandaid fix for something + // way worse like improper occupation clearance or whatever - Kerbiter + pExt->ChildAttachments.clear(); } void TechnoExt::HandleDestructionAsChild(TechnoClass* pThis) From 8f224e5225bf2ef18c78289c1fee4bea3acaa0e3 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 20 Jul 2023 00:03:47 +0300 Subject: [PATCH 067/112] Implement RespawnTimer and TransparentToMouse, re-enable RespawnAtCreation, --- docs/New-or-Enhanced-Logics.md | 6 ++++- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 29 +++++++++++++++++++++++ src/New/Entity/AttachmentClass.cpp | 25 ++++++++++++++++--- src/New/Entity/AttachmentClass.h | 15 +++++++----- src/New/Type/AttachmentTypeClass.cpp | 8 +++++-- src/New/Type/AttachmentTypeClass.h | 8 +++++-- 6 files changed, 77 insertions(+), 14 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index ea32ba1f16..c07e27a131 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -23,6 +23,8 @@ In `rulesmd.ini`: 0=MNT ; (example) [MNT] +RespawnAtCreation=true ; boolean +RespawnDelay=-1 ; integer, non-negative values enable the respawn timer InheritOwner=true ; boolean, whether the child inherits owner of the parent while it's attached InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) InheritCommands=true ; boolean @@ -37,8 +39,10 @@ ParentDestructionMission= ; MissionType, queued to child when parent ParentDetachmentMission= ; MissionType, queued to child when it's detached from parent [SOMETECHNO] ; TechnoTypeClass +; used when this techno is attached AttachmentTopLayerMinHeight= ; integer AttachmentUndergroundLayerMaxHeight= ; integer +; used for attaching other technos AttachmentX.Type=MNT ; AttachmentType (example) AttachmentX.TechnoType= ; TechnoType that can be attached, currently only units are supported AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height @@ -46,7 +50,7 @@ AttachmentX.IsOnTurret=false ; boolean AttachmentX.RotationAdjust=0 ; rotation in DirType, from -255 to 255 [General] -AttachmentTopLayerMinHeight=500 ; integer +AttachmentTopLayerMinHeight=500 ; integer, AttachmentUndergroundLayerMaxHeight=-256 ; integer ``` diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 1c7f49a4bc..95e7901259 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -1015,3 +1015,32 @@ DEFINE_HOOK(0x4C73D1, EventClass_Execute_MegaMission_HandleChildren, 0x6) return 0; } */ + +// TODO review following 2 hooks from Kratos + +DEFINE_HOOK(0x69251A, ScrollClass_ProcessClickCoords_TransparentToMouse, 0x6) +{ + GET(TechnoClass*, pTechno, EAX); + + if (!pTechno) + return 0x0; + + auto const pExt = TechnoExt::ExtMap.Find(pTechno); + if (pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->TransparentToMouse) + R->EAX(nullptr); + + return 0x0; +} + +DEFINE_HOOK(0x6DA3FF, TacticalClass_SelectAt_TransparentToMouse, 0x6) +{ + enum { SkipTechno = 0x6DA440, ContinueCheck = 0x0 }; + + GET(TechnoClass*, pTechno, EAX); + + auto const pExt = TechnoExt::ExtMap.Find(pTechno); + if (pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->TransparentToMouse) + return SkipTechno; + + return ContinueCheck; +} diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 5d49b55693..97b6be445a 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -108,7 +108,7 @@ void AttachmentClass::Initialize() if (this->Child) return; - if (this->GetType()->RestoreAtCreation) + if (this->GetType()->RespawnAtCreation) this->CreateChild(); } @@ -137,6 +137,26 @@ void AttachmentClass::AI() { AttachmentTypeClass* pType = this->GetType(); + if (!this->Child) + { + if (pType->RespawnDelay == 0) + { + this->CreateChild(); + } + else if (pType->RespawnDelay > 0) + { + if (!this->RespawnTimer.HasStarted()) + { + this->RespawnTimer.Start(pType->RespawnDelay); + } + else if (this->RespawnTimer.Completed()) + { + this->CreateChild(); + this->RespawnTimer.Stop(); + } + } + } + if (this->Child) { if (this->Child->InLimbo && !this->Parent->InLimbo) @@ -146,8 +166,6 @@ void AttachmentClass::AI() this->Child->SetLocation(this->GetChildLocation()); - this->Child->OnBridge = this->Parent->OnBridge; - DirStruct childDir = this->Data->IsOnTurret ? this->Parent->SecondaryFacing.Current() : this->Parent->PrimaryFacing.Current(); @@ -346,6 +364,7 @@ bool AttachmentClass::Serialize(T& stm) .Process(this->Data) .Process(this->Parent) .Process(this->Child) + .Process(this->RespawnTimer) .Success(); } diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index 4022400abd..d4b68f8fe1 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -17,6 +17,7 @@ class AttachmentClass TechnoTypeExt::ExtData::AttachmentDataEntry* Data; TechnoClass* Parent; TechnoClass* Child; + CDTimerClass RespawnTimer; // volatile, don't serialize // if you ever change the tree structure, you need to call CacheTreeData() @@ -32,18 +33,20 @@ class AttachmentClass AttachmentClass(TechnoTypeExt::ExtData::AttachmentDataEntry* data, TechnoClass* pParent, TechnoClass* pChild = nullptr) : - Data(data), - Parent(pParent), - Child(pChild) + Data { data }, + Parent { pParent }, + Child { pChild }, + RespawnTimer { } { this->InitCacheData(); Array.push_back(this); } AttachmentClass() : - Data(), - Parent(), - Child() + Data { }, + Parent { }, + Child { }, + RespawnTimer { } { Array.push_back(this); } diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 9f8e630062..d8bf7efa10 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -15,7 +15,8 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) INI_EX exINI(pINI); - // this->RestoreAtCreation.Read(exINI, section, "RestoreAtCreation"); + this->RespawnAtCreation.Read(exINI, section, "RespawnAtCreation"); + this->RespawnDelay.Read(exINI, section, "RespawnDelay"); this->InheritCommands.Read(exINI, section, "InheritCommands"); this->InheritOwner.Read(exINI, section, "InheritOwner"); this->InheritStateEffects.Read(exINI, section, "InheritStateEffects"); @@ -23,6 +24,7 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->InheritHeightStatus.Read(exINI, section, "InheritHeightStatus"); this->OccupiesCell.Read(exINI, section, "OccupiesCell"); this->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); + this->TransparentToMouse.Read(exINI, section, "TransparentToMouse"); this->YSortPosition.Read(exINI, section, "YSortPosition"); // this->CanBeForceDetached.Read(exINI, section, "CanBeForceDetached"); @@ -40,7 +42,8 @@ template void AttachmentTypeClass::Serialize(T& Stm) { Stm - .Process(this->RestoreAtCreation) + .Process(this->RespawnAtCreation) + .Process(this->RespawnDelay) .Process(this->InheritCommands) .Process(this->InheritOwner) .Process(this->InheritStateEffects) @@ -48,6 +51,7 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->InheritHeightStatus) .Process(this->OccupiesCell) .Process(this->LowSelectionPriority) + .Process(this->TransparentToMouse) .Process(this->YSortPosition) // .Process(this->CanBeForceDetached) // .Process(this->ForceDetachWeapon_Child) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 31d8fcb21e..db0b1c65ee 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -9,7 +9,8 @@ class AttachmentTypeClass final : public Enumerable { public: - Valueable RestoreAtCreation; // whether to spawn the attachment initially + Valueable RespawnAtCreation; // whether to spawn the attachment initially + Valueable RespawnDelay; Valueable InheritCommands; Valueable InheritOwner; // aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. @@ -17,6 +18,7 @@ class AttachmentTypeClass final : public Enumerable Valueable InheritHeightStatus; Valueable OccupiesCell; Valueable LowSelectionPriority; + Valueable TransparentToMouse; Valueable YSortPosition; // Valueable CanBeForceDetached; // Nullable ForceDetachWeapon_Child; @@ -27,7 +29,8 @@ class AttachmentTypeClass final : public Enumerable Nullable ParentDetachmentMission; AttachmentTypeClass(const char* pTitle = NONE_STR) : Enumerable(pTitle) - , RestoreAtCreation { true } + , RespawnAtCreation { true } + , RespawnDelay { -1 } , InheritCommands { true } , InheritOwner { true } , InheritStateEffects { true } @@ -35,6 +38,7 @@ class AttachmentTypeClass final : public Enumerable , InheritDestruction { true } , InheritHeightStatus { true } , LowSelectionPriority { true } + , TransparentToMouse { false } , YSortPosition { AttachmentYSortPosition::Default } // , CanBeForceDetached { false } // , ForceDetachWeapon_Child { } From 6a793b44e666a2c9543597e43198b34a33246f20 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 20 Jul 2023 12:48:49 +0300 Subject: [PATCH 068/112] Added missed doc edit for TransparentToMouse --- docs/New-or-Enhanced-Logics.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index c07e27a131..0da976708b 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -29,6 +29,7 @@ InheritOwner=true ; boolean, whether the child inherits owne InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) InheritCommands=true ; boolean LowSelectionPriority=true ; boolean, whether the child is low priority while attached +TransparentToMouse=false ; boolean, can't click on attached techno if set YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent InheritDestruction=true ; boolean InheritHeightStatus=true ; boolean, whether the layer and InAir/OnGround/IsSurfaced inherited from parent From c02f8f9570d118f0edaa21e9eb007adff9ca87f8 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 20 Jul 2023 23:09:24 +0300 Subject: [PATCH 069/112] Try to fix AircraftTracker crash one more time --- YRpp | 2 +- src/Ext/Unit/Hooks.Jumpjet.cpp | 2 +- src/Locomotion/AttachmentLocomotionClass.cpp | 8 ++++++++ 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/YRpp b/YRpp index 2d1a15c891..9f873c9799 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 2d1a15c891dcaaa1bb8c4bc3bfa6f2da04932294 +Subproject commit 9f873c9799692d1defa6a82f99dc3dae31d17e9e diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index e3dabd74b0..79522da454 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -104,7 +104,7 @@ DEFINE_HOOK(0x54C036, JumpjetLocomotionClass_State3_UpdateSensors, 0x7) // Copied from FootClass::UpdatePosition if (pLinkedTo->GetTechnoType()->SensorsSight) { - CellStruct const lastCell = pLinkedTo->LastJumpjetMapCoords; + CellStruct const lastCell = pLinkedTo->LastFlightMapCoords; if (lastCell != currentCell) { pLinkedTo->RemoveSensorsAt(lastCell); diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index a22db988fd..3308c934c1 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -256,6 +256,10 @@ HRESULT AttachmentLocomotionClass::Begin_Piggyback(ILocomotion* pointer) if (this->Piggybacker) return E_FAIL; + // since LinkedTo may've been managed by AircraftTracker before we need to remove the AircraftTracker entry + if (this->LinkedTo && this->LinkedTo->GetLastFlightMapCoords() != CellStruct::Empty) + AircraftTrackerClass::Instance->Remove(this->LinkedTo); + this->Piggybacker = pointer; return S_OK; @@ -269,6 +273,10 @@ HRESULT AttachmentLocomotionClass::End_Piggyback(ILocomotion** pointer) if (!this->Piggybacker) return S_FALSE; + // since LinkedTo may no longer be considered airborne we need to remove the AircraftTracker entry + if (this->LinkedTo && this->LinkedTo->GetLastFlightMapCoords() != CellStruct::Empty) + AircraftTrackerClass::Instance->Remove(this->LinkedTo); + // since pointer is a dumb pointer, we don't need to call Release, // hence we use Detach, otherwise the locomotor gets trashed *pointer = this->Piggybacker.Detach(); From 85e3ea9a80041dd343def39e9e386c5574aa1310 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 20 Jul 2023 23:11:47 +0300 Subject: [PATCH 070/112] Fix TransparentToMouse undesired behavior --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 37 ++++++++++++++--------- 1 file changed, 22 insertions(+), 15 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 95e7901259..ba7bde421d 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -1016,31 +1016,38 @@ DEFINE_HOOK(0x4C73D1, EventClass_Execute_MegaMission_HandleChildren, 0x6) } */ -// TODO review following 2 hooks from Kratos - -DEFINE_HOOK(0x69251A, ScrollClass_ProcessClickCoords_TransparentToMouse, 0x6) +DEFINE_HOOK(0x6DA3FF, TacticalClass_SelectAt_TransparentToMouse_TacticalSelectable, 0x6) { - GET(TechnoClass*, pTechno, EAX); + enum { SkipTechno = 0x6DA440, ContinueCheck = 0x0 }; - if (!pTechno) - return 0x0; + GET(TechnoClass*, pTechno, EAX); auto const pExt = TechnoExt::ExtMap.Find(pTechno); if (pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->TransparentToMouse) - R->EAX(nullptr); + return SkipTechno; - return 0x0; + return ContinueCheck; } -DEFINE_HOOK(0x6DA3FF, TacticalClass_SelectAt_TransparentToMouse, 0x6) +DEFINE_HOOK(0x6DA4FB, TacticalClass_SelectAt_TransparentToMouse_OccupierPtr, 0x6) { - enum { SkipTechno = 0x6DA440, ContinueCheck = 0x0 }; + GET(CellClass*, pCell, EAX); - GET(TechnoClass*, pTechno, EAX); + ObjectClass* pFoundObject = nullptr; + for (ObjectClass* pOccupier = pCell->FirstObject; pOccupier; pOccupier = pOccupier->NextObject) + { + // find first non-transparent to mouse techno and return it + if (auto const pOccupierAsTechno = abstract_cast(pOccupier)) + { + auto const pExt = TechnoExt::ExtMap.Find(pOccupierAsTechno); + if (pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->TransparentToMouse) + continue; + } - auto const pExt = TechnoExt::ExtMap.Find(pTechno); - if (pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->TransparentToMouse) - return SkipTechno; + pFoundObject = pOccupier; + break; + } - return ContinueCheck; + R->EAX(pFoundObject); + return 0x6DA501; } From eb4439bfb979b665a2f6055706181d09ed1ac277 Mon Sep 17 00:00:00 2001 From: Meta Date: Sat, 5 Aug 2023 21:15:10 +0300 Subject: [PATCH 071/112] (hope)Fully fix pathfinding --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 134 ++++++++++++++++------ 1 file changed, 100 insertions(+), 34 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index ba7bde421d..d96c3db889 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -117,6 +117,14 @@ namespace TechnoAttachmentTemp // we flip the logic from "passable if special case is found" to "impassable if // non-special case is found" - Kerbiter +void AssumeNoVehicleByDefault(byte& occupyFlags, bool& isVehicleFlagSet) +{ + TechnoAttachmentTemp::storedVehicleFlag = occupyFlags & 0x20; + + occupyFlags &= ~0x20; + isVehicleFlagSet = false; +} + DEFINE_HOOK(0x73F520, UnitClass_CanEnterCell_AssumeNoVehicleByDefault, 0x0) { enum { Check = 0x73F528, Skip = 0x73FA92 }; @@ -129,38 +137,97 @@ DEFINE_HOOK(0x73F520, UnitClass_CanEnterCell_AssumeNoVehicleByDefault, 0x0) if (!pOccupier) // stolen code return Skip; - TechnoAttachmentTemp::storedVehicleFlag = occupyFlags & 0x20; - - occupyFlags &= ~0x20; - isVehicleFlagSet = false; + AssumeNoVehicleByDefault(occupyFlags, isVehicleFlagSet); return Check; } +bool IsOccupierIgnorable(TechnoClass* pThis, ObjectClass* pOccupier, byte& occupyFlags, bool& isVehicleFlagSet) +{ + if (pThis == pOccupier) + return true; + + auto const pTechno = abstract_cast(pOccupier); + if (pTechno && + (TechnoExt::DoesntOccupyCellAsChild(pTechno) || TechnoExt::IsChildOf(pTechno, pThis))) + { + return true; + } + + if (abstract_cast(pOccupier)) + { + occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; + isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + } + + return false; +} + DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) { enum { IgnoreOccupier = 0x73FA87, Continue = 0x73F530 }; GET(UnitClass*, pThis, EBX); - GET(TechnoClass*, pOccupier, ESI); + GET(ObjectClass*, pOccupier, ESI); REF_STACK(byte, occupyFlags, STACK_OFFSET(0x90, -0x7C)); REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x90, -0x7B)); - if (pThis == pOccupier - || TechnoExt::DoesntOccupyCellAsChild(pOccupier) - || TechnoExt::IsChildOf(pOccupier, pThis)) - { - return IgnoreOccupier; - } + return IsOccupierIgnorable(pThis, pOccupier, occupyFlags, isVehicleFlagSet) + ? IgnoreOccupier : Continue; +} - if (abstract_cast(pOccupier)) +// Also because the occupancy can be marked by something moving into the spot +// we also check the nearby (yes this is an assumption, if you are reviewing +// this voice your opinion in the review) cells for the presence of technos +// that are moving into this spot right now - Kerbiter + +void AccountForMovingInto(CellClass* into, bool isAlt, TechnoClass* pThis, byte& occupyFlags, bool& isVehicleFlagSet) +{ + // see 42B080, A* checks 2 cells around, this is approx. it + CellStruct center = into->MapCoords; + CoordStruct intoCoords = into->GetCellCoords(); + if (isAlt) + intoCoords.Z += CellClass::BridgeHeight; + + for (CellRectEnumerator cell({ center.X - 2, center.Y - 2, center.X + 2, center.Y + 2}); cell; ++cell) { - occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; - isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + if (auto const pCell = MapClass::Instance->TryGetCellAt(*cell)) + { + for (NextObject object(isAlt ? pCell->AltObject : pCell->FirstObject); object; ++object) + { + if (*object == pThis) + continue; + + auto const pUnit = abstract_cast(*object); + if (pUnit && pUnit->Locomotor->Is_Moving_Here(intoCoords) + && !TechnoExt::DoesntOccupyCellAsChild(pUnit) + && !TechnoExt::IsChildOf(pUnit, pThis)) + { + occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; + isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + } + } + } } +} - return Continue; +DEFINE_HOOK(0x73FA92, UnitClass_CanEnterCell_CheckMovingInto, 0x0) +{ + GET_STACK(CellClass*, into, STACK_OFFSET(0x90, 0x4)); + GET_STACK(bool const, isAlt, STACK_OFFSET(0x90, -0x7D)); + GET(UnitClass*, pThis, EBX); + + REF_STACK(byte, occupyFlags, STACK_OFFSET(0x90, -0x7C)); + REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x90, -0x7B)); + + AccountForMovingInto(into, isAlt, pThis, occupyFlags, isVehicleFlagSet); + + // stolen code ahead + if (!isAlt) + return 0x73FA9E; + + return 0x73FC24; } DEFINE_HOOK(0x51C249, InfantryClass_CanEnterCell_AssumeNoVehicleByDefault, 0x0) @@ -175,10 +242,7 @@ DEFINE_HOOK(0x51C249, InfantryClass_CanEnterCell_AssumeNoVehicleByDefault, 0x0) if (!pOccupier) // stolen code return Skip; - TechnoAttachmentTemp::storedVehicleFlag = occupyFlags & 0x20; - - occupyFlags &= ~0x20; - isVehicleFlagSet = false; + AssumeNoVehicleByDefault(occupyFlags, isVehicleFlagSet); return Check; } @@ -188,25 +252,27 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) enum { IgnoreOccupier = 0x51C70F, Continue = 0x51C259 }; GET(InfantryClass*, pThis, EBP); - GET(TechnoClass*, pOccupier, ESI); + GET(ObjectClass*, pOccupier, ESI); REF_STACK(byte, occupyFlags, STACK_OFFSET(0x34, -0x21)); REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x34, -0x22)); - if ((TechnoClass*)pThis == pOccupier - || TechnoExt::DoesntOccupyCellAsChild(pOccupier) - || TechnoExt::IsChildOf(pOccupier, (TechnoClass*)pThis)) - { - return IgnoreOccupier; - } + return IsOccupierIgnorable(pThis, pOccupier, occupyFlags, isVehicleFlagSet) + ? IgnoreOccupier : Continue; +} - if (abstract_cast(pOccupier)) - { - occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; - isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; - } +DEFINE_HOOK(0x51C78F, InfantryClass_CanEnterCell_CheckMovingInto, 0x6) +{ + GET_STACK(CellClass*, into, STACK_OFFSET(0x34, 0x4)); + GET_STACK(bool const, isAlt, STACK_OFFSET(0x34, -0x23)); + GET(InfantryClass*, pThis, EBP); - return Continue; + REF_STACK(byte, occupyFlags, STACK_OFFSET(0x34, -0x21)); + REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x34, -0x22)); + + AccountForMovingInto(into, isAlt, pThis, occupyFlags, isVehicleFlagSet); + + return 0; } enum CellTechnoMode @@ -629,7 +695,7 @@ DEFINE_HOOK(0x736A2F, UnitClass_RotationAI_ForbidAttachmentRotation, 0x7) ? SkipBodyRotation : ContinueCheck; } -/* + Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, CellStruct const* pCell, bool checkFog, bool ignoreForce) { Action result = pThis->UnitClass::MouseOverCell(pCell, checkFog, ignoreForce); @@ -658,7 +724,7 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, C // TODO MouseOverObject for entering bunkers, grinder, buildings etc DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) -*/ + /* DEFINE_HOOK(0x4D74EC, FootClass_ObjectClickedAction_HandleAttachment, 0x6) { From 57483aeee62a778db734e87d4d51c469fb103cf6 Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 6 Aug 2023 00:03:00 +0300 Subject: [PATCH 072/112] Remove experimental/commented out stuff --- src/Ext/Techno/Body.cpp | 29 +- src/Ext/Techno/Body.h | 4 +- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 414 ------------------- src/Locomotion/AttachmentLocomotionClass.cpp | 30 -- src/New/Entity/AttachmentClass.cpp | 75 +--- src/New/Entity/AttachmentClass.h | 18 +- src/New/Type/AttachmentTypeClass.cpp | 9 - src/New/Type/AttachmentTypeClass.h | 6 - 8 files changed, 5 insertions(+), 580 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index ce2293cc53..97e6db02f3 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -579,21 +579,6 @@ void TechnoExt::GetValuesForDisplay(TechnoClass* pThis, DisplayInfoType infoType } } - -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) @@ -609,10 +594,10 @@ bool TechnoExt::AttachTo(TechnoClass* pThis, TechnoClass* pParent) return false; } -bool TechnoExt::DetachFromParent(TechnoClass* pThis, bool isForceDetachment) +bool TechnoExt::DetachFromParent(TechnoClass* pThis) { auto const pExt = TechnoExt::ExtMap.Find(pThis); - return pExt->ParentAttachment->DetachChild(isForceDetachment); + return pExt->ParentAttachment->DetachChild(); } void TechnoExt::InitializeAttachments(TechnoClass* pThis) @@ -706,16 +691,6 @@ TechnoClass* TechnoExt::GetTopLevelParent(TechnoClass* pThis) : pThis; } -Matrix3D TechnoExt::GetAttachmentTransform(TechnoClass* pThis, VoxelIndexKey* pKey, bool isShadow) -{ - auto const pThisExt = TechnoExt::ExtMap.Find(pThis); - - if (pThis && pThisExt && pThisExt->ParentAttachment) - return pThisExt->ParentAttachment->GetUpdatedTransform(pKey, isShadow); - - return TechnoExt::GetTransform(pThis, pKey, isShadow); -} - // ============================= // load / save diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 961ac67423..9d65c07fdc 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -136,9 +136,8 @@ class TechnoExt static CoordStruct GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FLHFound); static CoordStruct GetSimpleFLH(InfantryClass* 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 bool DetachFromParent(TechnoClass* pThis); static void InitializeAttachments(TechnoClass* pThis); static void DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource); @@ -151,7 +150,6 @@ class TechnoExt static bool DoesntOccupyCellAsChild(TechnoClass* pThis); static bool IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep = true); static TechnoClass* GetTopLevelParent(TechnoClass* pThis); - static Matrix3D GetAttachmentTransform(TechnoClass* pThis, VoxelIndexKey* pKey = nullptr, bool isShadow = false); static void ChangeOwnerMissionFix(FootClass* pThis); static void KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, AnimTypeClass* pVanishAnimation); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index d96c3db889..9e4956dcd4 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -59,30 +59,6 @@ DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) #pragma region Cell occupation handling -// DEFINE_HOOK(0x7441B6, UnitClass_SetOccupyBit_SkipVirtual, 0x6) -// { -// enum { Skip = 0x7441F6, Continue = 0 }; - -// GET(UnitClass*, pThis, ECX); - -// if (TechnoExt::DoesntOccupyCellAsChild(pThis)) -// return Skip; - -// return Continue; -// } - -// DEFINE_HOOK(0x744216, UnitClass_ClearOccupyBit_SkipVirtual, 0x6) -// { -// enum { Skip = 0x74425E, Continue = 0 }; - -// GET(UnitClass*, pThis, ECX); - -// if (TechnoExt::DoesntOccupyCellAsChild(pThis)) -// return Skip; - -// return Continue; -// } - // screw Ares void __fastcall UnitClass_SetOccupyBit_SkipVirtual(UnitClass* pThis, discard_t, const CoordStruct& coords) @@ -556,73 +532,6 @@ DEFINE_HOOK(0x730F1C, StopCommand_Context_Unset, 0x5) return 0; } -/* -namespace TechnoAttachmentTemp -{ - TechnoClass* pParent; -} - -DEFINE_HOOK(0x6FFBE0, TechnoClass_PlayerAssignMission_Context_Set, 0x6) -{ - TechnoAttachmentTemp::pParent = R->ECX(); - - return 0; -} - -DEFINE_HOOK_AGAIN(0x6FFDEB, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) -DEFINE_HOOK(0x6FFCAE, TechnoClass_PlayerAssignMission_HandleChildren, 0x5) -{ - GET_STACK(Mission, mission, STACK_OFFSET(0x98, +0x4)); - - switch (mission) - { - case Mission::Move: - case Mission::AttackMove: - case Mission::Enter: - case Mission::QMove: - return 0; - } - - GET_STACK(ObjectClass* const, pTarget, STACK_OFFSET(0x98, +0x8)); - GET_STACK(CellClass* const, pTargetCell, STACK_OFFSET(0x98, +0xC)); - GET_STACK(CellClass* const, pCellNearTarget, STACK_OFFSET(0x98, +0x10)); - auto const& pExt = TechnoExt::ExtMap.Find(TechnoAttachmentTemp::pParent); - - bool oldFeedback = Unsorted::MoveFeedback; - Unsorted::MoveFeedback = false; - - for (auto const& pAttachment : pExt->ChildAttachments) - { - // Recursive call, PlayerAssignMission == ClickedMission - if (pAttachment->Child && pAttachment->GetType()->InheritCommands) - pAttachment->Child->ClickedMission(mission, pTarget, pTargetCell, pCellNearTarget); - } - - Unsorted::MoveFeedback = oldFeedback; - - // PlayerAssignMission returns bool which indicates whether the player is in planning mode. - // This can't change when we handle children so we don't adjust the return value - Kerbiter - return 0; -} -*/ - -// 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 -// ... DEFINE_HOOK(0x469672, BulletClass_Logics_Locomotor_CheckIfAttached, 0x6) { @@ -725,280 +634,6 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, C DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) -/* -DEFINE_HOOK(0x4D74EC, FootClass_ObjectClickedAction_HandleAttachment, 0x6) -{ - enum { ReturnFalse = 0x4D77EC, Continue = 0x0 }; - - GET(FootClass*, pThis, ESI); - GET_STACK(Action, action, STACK_OFFSET(0x108, +0x4)); - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - - if (!pExt->ParentAttachment) - return Continue; - - switch (action) - { - case Action::Move: - case Action::AttackMoveNav: - case Action::NoMove: - case Action::Enter: - case Action::NoEnter: - case Action::Capture: - case Action::Repair: - case Action::Sabotage: - case Action::GuardArea: - return ReturnFalse; - default: - return Continue; - } -} - -DEFINE_HOOK(0x4D7D58, FootClass_CellClickedAction_HandleAttachment, 0x6) -{ - enum { ReturnFalse = 0x4D7D62, Continue = 0x0 }; - - GET(FootClass*, pThis, ESI); - GET_STACK(Action, action, STACK_OFFSET(0x24, +0x4)); - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - - if (!pExt->ParentAttachment) - return Continue; - - switch (action) - { - case Action::Move: - case Action::AttackMoveNav: - case Action::NoMove: - case Action::Enter: - case Action::NoEnter: - case Action::Harvest: - case Action::Capture: - case Action::Sabotage: - case Action::GuardArea: - case Action::EnterTunnel: - case Action::NoEnterTunnel: - case Action::PatrolWaypoint: - return ReturnFalse; - default: - return Continue; - } -} -*/ - -// DEFINE_HOOK(0x41BE02, AbstractClass_RenderCoord_AttachedCoord, 0x7) -// { -// enum { Return = 0x41BE2A, Continue = 0x0 }; - -// GET(AbstractClass* const, pThis, ECX); - -// if (auto const& pThisAsTechno = abstract_cast(pThis)) -// { -// auto const& pExt = TechnoExt::ExtMap.Find(pThisAsTechno); -// if (pExt && pExt->ParentAttachment) -// { -// R->EAX(&pExt->ParentAttachment->GetChildLocation()); -// return Return; -// } -// } - -// return Continue; -// } - -CoordStruct __fastcall TechnoClass_GetRenderCoords(TechnoClass* pThis) -{ - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - if (pExt && pExt->ParentAttachment) - { - // The parent origin is our origin, we will offset later in draw function - return pExt->ParentAttachment->Cache.TopLevelParent->GetRenderCoords(); - } - - return pThis->ObjectClass::GetRenderCoords(); -} - -// TODO hook matrix -// 6F3B88 DONE -// 6F3DA4 DONE -// 73B5B5 DONE -// 73C864 DONE - -// DEFINE_HOOK(0x6F3B88, TechnoClass_FireCoord_AttachmentAdjust, 0x6) -// { -// enum { Skip = 0x6F3B9E }; - -// GET(TechnoClass*, pThis, EBX); - -// R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); -// return Skip; -// } - -// DEFINE_HOOK(0x6F3DA4, TechnoClass_firecoord_6F3D60_AttachmentAdjust, 0x6) -// { -// enum { Skip = 0x6F3DBA }; - -// GET(TechnoClass*, pThis, EBX); - -// R->EAX(&TechnoExt::GetAttachmentTransform(pThis)); -// return Skip; -// } - -// WIP jitterless drawing. requires a lot of work so wasn't finished -/* -DEFINE_HOOK(0x73B5B5, UnitClass_DrawVoxel_AttachmentAdjust, 0x6) -{ - enum { Skip = 0x73B5CE }; - - GET(UnitClass*, pThis, EBP); - LEA_STACK(VoxelIndexKey*, pKey, STACK_OFFSET(0x1C4, -0x1B0)); - - Matrix3D transform = TechnoExt::GetAttachmentTransform(pThis, pKey); - R->EAX(&transform); - - return Skip; -} - -DEFINE_HOOK(0x73C864, UnitClass_drawcode_AttachmentAdjust, 0x6) -{ - enum { Skip = 0x73C87D }; - - GET(UnitClass*, pThis, EBP); - LEA_STACK(VoxelIndexKey*, pKey, STACK_OFFSET(0x128, -0xC8)); - - Matrix3D transform = TechnoExt::GetAttachmentTransform(pThis, pKey); - R->EAX(&transform); - - return Skip; -} - -DEFINE_HOOK(0x73C29F, UnitClass_DrawVoxel_AttachmentAdjustShadow, 0x6) -{ - enum { Skip = 0x73C2B8 }; - - GET(UnitClass*, pThis, EBP); - LEA_STACK(VoxelIndexKey*, pKey, STACK_OFFSET(0x1F0, -0x1DC)); - - Matrix3D transform = TechnoExt::GetAttachmentTransform(pThis, pKey, true); - R->EAX(&transform); - - return Skip; -} - -DEFINE_HOOK(0x414953, AircraftClass_DrawIt_AttachmentAdjust, 0x6) -{ - enum { Skip = 0x41496C }; - - GET(AircraftClass*, pThis, EBP); - LEA_STACK(VoxelIndexKey*, pKey, STACK_OFFSET(0xD8, -0xC8)); - - Matrix3D transform = TechnoExt::GetAttachmentTransform((TechnoClass*)pThis, pKey); - R->EAX(&transform); - - return Skip; -} - -DEFINE_HOOK(0x41480D, AircraftClass_DrawIt_AttachmentAdjustShadow, 0x6) -{ - enum { Skip = 0x414826 }; - - GET(AircraftClass*, pThis, EBP); - LEA_STACK(VoxelIndexKey*, pKey, STACK_OFFSET(0xD4, -0xC4)); - - Matrix3D transform = TechnoExt::GetAttachmentTransform((TechnoClass*)pThis, pKey, true); - R->EAX(&transform); - - return Skip; -} - -// TODO merge hooks - -// TODO aircraft -// normal matrix 0x414969 -// shadow matrix 0x414823 - -// TODO hook shadow matrix - -// BuildingClass::GetRenderCoords already has it's own override, -// should hook it if you ever want to dip into that - Kerbiter - -// FIXME it's probably not a good idea to mix vtable replacements with manual hooks -// DEFINE_JUMP(VTABLE, 0x7F4A0C, GET_OFFSET(TechnoClass_GetRenderCoords)) // TechnoClass -// DEFINE_JUMP(VTABLE, 0x7E8D40, GET_OFFSET(TechnoClass_GetRenderCoords)) // FootClass -// DEFINE_JUMP(VTABLE, 0x7F5D1C, GET_OFFSET(TechnoClass_GetRenderCoords)) // UnitClass -// DEFINE_JUMP(VTABLE, 0x7EB104, GET_OFFSET(TechnoClass_GetRenderCoords)) // InfantryClass -// DEFINE_JUMP(VTABLE, 0x7E2350, GET_OFFSET(TechnoClass_GetRenderCoords)) // AircraftClass - - -// // DEFINE_JUMP(VTABLE, 0x7F4A74, GET_OFFSET(TechnoClass_DrawIt)) - -// // skip children before parent drawn -// DEFINE_HOOK(0x73CEC0, UnitClass_DrawIt_HandleChildren , 0x5) -// { -// GET(UnitClass*, pThis, ECX); - -// if (TechnoExt::IsAttached(pThis)) -// } - -namespace TechnoAttachmentTemp -{ - ObjectClass* pThis; - Point2D realDrawPoint = Point2D::Empty; -} - -DEFINE_HOOK(0x5F4B13, ObjectClass_Render_Context_Set, 0x5) -{ - GET(ObjectClass*, pThis, ECX); - - TechnoAttachmentTemp::pThis = pThis; - - return 0; -} - -// Swap the render coord after the draw rect is calculated -DEFINE_HOOK(0x5F4CC3, ObjectClass_Render_AttachmentAdjust, 0x6) -{ - LEA_STACK(Point2D*, drawPoint, STACK_OFFSET(0x30, -0x18)); - TechnoAttachmentTemp::realDrawPoint = *drawPoint; - - ObjectClass* pThis = TechnoAttachmentTemp::pThis; - if (pThis->AbstractFlags & AbstractFlags::Techno) - TacticalClass::Instance->CoordsToClient(TechnoExt::GetTopLevelParent((TechnoClass*)pThis)->GetRenderCoords(), drawPoint); - - return 0; -} - -DEFINE_HOOK(0x6D9EF0, TacticalClass_AddSelectable_SwapCoord, 0x6) -{ - REF_STACK(TechnoClass*, pTechno, STACK_OFFSET(0x0, 0x4)); - REF_STACK(int, x, STACK_OFFSET(0x0, 0x8)); - REF_STACK(int, y, STACK_OFFSET(0x0, 0xC)); - - if (pTechno == TechnoAttachmentTemp::pThis) - { - x = TechnoAttachmentTemp::realDrawPoint.X; - y = TechnoAttachmentTemp::realDrawPoint.Y; - } - - return 0; -} -*/ - -// offsets the voxel visual, needs additional handling -// DEFINE_HOOK(0x73B140, UnitClass_DrawObject_SwapCoord, 0x5) -// { -// GET(UnitClass*, pThis, ECX); -// REF_STACK(int, x, STACK_OFFSET(0x0, 0x8)); -// REF_STACK(int, y, STACK_OFFSET(0x0, 0xC)); - -// if (pThis == TechnoAttachmentTemp::pThis) -// { -// x = TechnoAttachmentTemp::realDrawPoint.X; -// y = TechnoAttachmentTemp::realDrawPoint.Y; -// } - -// return 0; -// } - // YSort for attachments int __fastcall TechnoClass_SortY_Wrapper(ObjectClass* pThis) { @@ -1032,55 +667,6 @@ DEFINE_JUMP(VTABLE, 0x7F5D28, GET_OFFSET(TechnoClass_SortY_Wrapper)) // UnitClas DEFINE_JUMP(LJMP, 0x568831, 0x568841); // Skip locomotion layer check in MapClass::PickUp DEFINE_JUMP(LJMP, 0x4D37A2, 0x4D37AE); // Skip locomotion layer check in FootClass::Mark -/* -// Handle waypoint mode command inheritance -DEFINE_HOOK(0x4C73D1, EventClass_Execute_MegaMission_HandleChildren, 0x6) -{ - GET(EventClass*, pThis, ESI); - GET(TechnoClass*, pParent, EDI); - GET(AbstractClass*, pArchiveTarget, EBP); - GET(Mission, mission, EBX); - - auto const pExt = TechnoExt::ExtMap.Find(pParent); - auto const pTarget = pThis->Data.MegaMission.Target.As_Abstract(); - auto const pDestination = pThis->Data.MegaMission.Destination.As_Abstract(); - bool isGuard = pThis->Data.MegaMission.Mission == static_cast(Mission::Area_Guard) && pParent->AbstractFlags & AbstractFlags::Foot; - - for (auto const& pAttachment : pExt->ChildAttachments) - { - if (!pAttachment->Child || !pAttachment->GetType()->InheritCommands) - continue; - - auto const pChild = pAttachment->Child; - pChild->QueueMission(mission, false); - - if (isGuard) - { - pChild->SetTarget(pChild); - pChild->SetDestination(pTarget, true); - pChild->SetFocus(pTarget); - } - else - { - if (pArchiveTarget) - { - pChild->SetFocus(pArchiveTarget); - - if (auto const pFoot = abstract_cast(pChild)) - pFoot->ClearNavQueue(); - } - - pChild->SetTarget(pTarget); - pChild->SetDestination(pDestination, true); - - if (pTarget && pChild->GetTechnoType()->OpenTopped) - pChild->SetTargetForPassengers(pTarget); - } - } - - return 0; -} -*/ DEFINE_HOOK(0x6DA3FF, TacticalClass_SelectAt_TransparentToMouse_TacticalSelectable, 0x6) { diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 3308c934c1..7cf3a0ce2e 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -57,28 +57,6 @@ Matrix3D AttachmentLocomotionClass::Draw_Matrix(VoxelIndexKey* key) // If you want to work on this - Shadow_Matrix should be fine as is, // Shadow_Point would need calculated height from the ramp extension plane - Kerbiter -// Matrix3D AttachmentLocomotionClass::Shadow_Matrix(VoxelIndexKey* key) -// { -// if (auto const pParentFoot = abstract_cast(this->GetAttachmentParent())) -// { -// Matrix3D mtx = pParentFoot->Locomotor->Shadow_Matrix(key); - -// // adjust for the real facing which is the source of truth for hor. rotation -// double childRotation = this->LinkedTo->PrimaryFacing.Current().GetRadian<32>(); -// double parentRotation = pParentFoot->PrimaryFacing.Current().GetRadian<32>(); -// float adjustmentAngle = (float)(childRotation - parentRotation); - -// mtx.RotateZ(adjustmentAngle); - -// // should be shadow key in fact but I kerbo is lazy to properly define shadow key -// if (key && key->Is_Valid_Key()) -// key->MainVoxel.FrameIndex = this->LinkedTo->PrimaryFacing.Current().GetFacing<32>(); - -// return mtx; -// } - -// return LocomotionClass::Shadow_Matrix(key); -// } Point2D AttachmentLocomotionClass::Draw_Point() { @@ -88,14 +66,6 @@ Point2D AttachmentLocomotionClass::Draw_Point() : LocomotionClass::Draw_Point(); } -// Point2D AttachmentLocomotionClass::Shadow_Point() -// { -// ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); -// return pParentLoco -// ? pParentLoco->Shadow_Point() -// : LocomotionClass::Shadow_Point(); -// } - VisualType AttachmentLocomotionClass::Visual_Character(bool raw) { ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 97b6be445a..e7d9b956ab 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -12,33 +12,6 @@ std::vector AttachmentClass::Array; -void AttachmentClass::InitCacheData() -{ - this->Cache.TopLevelParent = TechnoExt::GetTopLevelParent(this->Parent); -} - -Matrix3D AttachmentClass::GetUpdatedTransform(VoxelIndexKey* pKey, bool isShadow) -{ - Matrix3D& transform = isShadow ? this->Cache.ChildShadowTransform : this->Cache.ChildTransform; - int& lastUpdateFrame = isShadow ? this->Cache.ShadowLastUpdateFrame : this->Cache.LastUpdateFrame; - - if (Unsorted::CurrentFrame != lastUpdateFrame) - { - double& factor = *reinterpret_cast(0xB1D008); - auto const flh = this->Data->FLH.Get() * factor; - - Matrix3D mtx = TechnoExt::GetAttachmentTransform(this->Parent, pKey, isShadow); - mtx = TechnoExt::TransformFLHForTurret(this->Parent, mtx, this->Data->IsOnTurret, factor); - mtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); - - transform = mtx; - - lastUpdateFrame = Unsorted::CurrentFrame; - } - - return transform; -} - AttachmentTypeClass* AttachmentClass::GetType() { return AttachmentTypeClass::Array[this->Data->Type].get(); @@ -51,42 +24,10 @@ TechnoTypeClass* AttachmentClass::GetChildType() : nullptr; } -Matrix3D AttachmentClass::GetChildTransformForLocation() -{ - auto const flh = this->Data->FLH.Get(); - - auto const pParentExt = TechnoExt::ExtMap.Find(this->Parent); - - Matrix3D mtx; - if (pParentExt && pParentExt->ParentAttachment) - mtx = pParentExt->ParentAttachment->GetChildTransformForLocation(); - else - mtx = TechnoExt::GetTransform(this->Parent); - - mtx = TechnoExt::TransformFLHForTurret(this->Parent, mtx, this->Data->IsOnTurret); - mtx.Translate((float)flh.X, (float)flh.Y, (float)flh.Z); - - return mtx; -} - CoordStruct AttachmentClass::GetChildLocation() { auto& flh = this->Data->FLH.Get(); return TechnoExt::GetFLHAbsoluteCoords(this->Parent, flh, this->Data->IsOnTurret); - - /* - // TODO it doesn't work correctly for some unexplicable reason - auto result = this->GetChildTransformForLocation() * Vector3D::Empty; - - // Resulting coords are mirrored along X axis, so we mirror it back - result.Y *= -1; - - // apply as an offset to global object coords - CoordStruct location = this->Cache.TopLevelParent->GetCoords(); - location += { std::lround(result.X), std::lround(result.Y), std::lround(result.Z) }; - - return location; - */ } AttachmentClass::~AttachmentClass() @@ -131,8 +72,6 @@ void AttachmentClass::CreateChild() } } -#define SYNC_CHILD(property) this->Child->property = this->Parent->property - void AttachmentClass::AI() { AttachmentTypeClass* pType = this->GetType(); @@ -209,8 +148,6 @@ void AttachmentClass::AI() } } -#undef SYNC_CHILD - // Called in Kill_Cargo, handles logics for parent destruction on children void AttachmentClass::Destroy(TechnoClass* pSource) { @@ -311,21 +248,12 @@ bool AttachmentClass::AttachChild(TechnoClass* pChild) return true; } -bool AttachmentClass::DetachChild(bool isForceDetachment) +bool AttachmentClass::DetachChild() { 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); @@ -352,7 +280,6 @@ void AttachmentClass::InvalidatePointer(void* ptr) { AnnounceInvalidPointer(this->Parent, ptr); AnnounceInvalidPointer(this->Child, ptr); - AnnounceInvalidPointer(this->Cache.TopLevelParent, ptr); } #pragma region Save/Load diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index d4b68f8fe1..74af53a8e1 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -19,17 +19,6 @@ class AttachmentClass TechnoClass* Child; CDTimerClass RespawnTimer; - // volatile, don't serialize - // if you ever change the tree structure, you need to call CacheTreeData() - struct Cache - { - TechnoClass* TopLevelParent; - - int LastUpdateFrame; - Matrix3D ChildTransform; - int ShadowLastUpdateFrame; - Matrix3D ChildShadowTransform; - } Cache; AttachmentClass(TechnoTypeExt::ExtData::AttachmentDataEntry* data, TechnoClass* pParent, TechnoClass* pChild = nullptr) : @@ -38,7 +27,6 @@ class AttachmentClass Child { pChild }, RespawnTimer { } { - this->InitCacheData(); Array.push_back(this); } @@ -53,12 +41,8 @@ class AttachmentClass ~AttachmentClass(); - void InitCacheData(); - Matrix3D GetUpdatedTransform(VoxelIndexKey* pKey = nullptr, bool shadow = false); - AttachmentTypeClass* GetType(); TechnoTypeClass* GetChildType(); - Matrix3D GetChildTransformForLocation(); CoordStruct GetChildLocation(); void Initialize(); @@ -71,7 +55,7 @@ class AttachmentClass void Limbo(); bool AttachChild(TechnoClass* pChild); - bool DetachChild(bool force = false); + bool DetachChild(); void InvalidatePointer(void* ptr); diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index d8bf7efa10..3acb54f786 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -26,14 +26,8 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); this->TransparentToMouse.Read(exINI, section, "TransparentToMouse"); this->YSortPosition.Read(exINI, section, "YSortPosition"); - - // this->CanBeForceDetached.Read(exINI, section, "CanBeForceDetached"); - - // 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"); } @@ -53,9 +47,6 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->LowSelectionPriority) .Process(this->TransparentToMouse) .Process(this->YSortPosition) - // .Process(this->CanBeForceDetached) - // .Process(this->ForceDetachWeapon_Child) - // .Process(this->ForceDetachWeapon_Parent) .Process(this->DestructionWeapon_Child) .Process(this->DestructionWeapon_Parent) .Process(this->ParentDestructionMission) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index db0b1c65ee..5032532ed1 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -20,9 +20,6 @@ class AttachmentTypeClass final : public Enumerable Valueable LowSelectionPriority; Valueable TransparentToMouse; Valueable YSortPosition; - // Valueable CanBeForceDetached; - // Nullable ForceDetachWeapon_Child; - // Nullable ForceDetachWeapon_Parent; Nullable DestructionWeapon_Child; Nullable DestructionWeapon_Parent; Nullable ParentDestructionMission; @@ -40,9 +37,6 @@ class AttachmentTypeClass final : public Enumerable , LowSelectionPriority { true } , TransparentToMouse { false } , YSortPosition { AttachmentYSortPosition::Default } - // , CanBeForceDetached { false } - // , ForceDetachWeapon_Child { } - // , ForceDetachWeapon_Parent { } , DestructionWeapon_Child { } , DestructionWeapon_Parent { } , ParentDestructionMission { } From 2391209295485ab5be38d68ffd4bd198c07954c0 Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 6 Aug 2023 00:22:53 +0300 Subject: [PATCH 073/112] Minor cleanups --- src/Ext/Techno/Body.cpp | 6 ++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.TechnoAttachment.cpp | 18 ++++++++++-------- src/Locomotion/AttachmentLocomotionClass.cpp | 9 ++++++--- src/New/Entity/AttachmentClass.cpp | 3 +++ 5 files changed, 26 insertions(+), 11 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 97e6db02f3..29dee20eb5 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -680,6 +680,12 @@ bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) || (deep && TechnoExt::IsChildOf(pThisExt->ParentAttachment->Parent, pParent))); } +bool TechnoExt::AreRelatives(TechnoClass* pThis, TechnoClass* pThat) +{ + return TechnoExt::IsChildOf(pThis, pThat) + || TechnoExt::IsChildOf(pThat, pThis); +} + // Returns this if no parent. TechnoClass* TechnoExt::GetTopLevelParent(TechnoClass* pThis) { diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 9d65c07fdc..0364b19bd3 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -149,6 +149,7 @@ class TechnoExt static bool HasAttachmentLoco(FootClass* pThis); // FIXME shouldn't be here static bool DoesntOccupyCellAsChild(TechnoClass* pThis); static bool IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep = true); + static bool AreRelatives(TechnoClass* pThis, TechnoClass* pThat); static TechnoClass* GetTopLevelParent(TechnoClass* pThis); static void ChangeOwnerMissionFix(FootClass* pThis); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 9e4956dcd4..bc0e789c0f 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -59,6 +59,7 @@ DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) #pragma region Cell occupation handling +// because Ares hooks in the single usable position we need to do a detour instead // screw Ares void __fastcall UnitClass_SetOccupyBit_SkipVirtual(UnitClass* pThis, discard_t, const CoordStruct& coords) @@ -76,14 +77,12 @@ void __fastcall UnitClass_ClearOccupyBit_SkipVirtual(UnitClass* pThis, discard_t DEFINE_JUMP(VTABLE, 0x7F5D60, GET_OFFSET(UnitClass_SetOccupyBit_SkipVirtual)) DEFINE_JUMP(VTABLE, 0x7F5D64, GET_OFFSET(UnitClass_ClearOccupyBit_SkipVirtual)) -// TODO ^ same for non-UnitClass, cba for now - -// TODO now children block the parent from moving, should not happen +// TODO ^ same for non-UnitClass, not needed so cba for now namespace TechnoAttachmentTemp { // no idea what Ares or w/e else is doing with occupation flags, - // so just to be safe assume it can be null and store it + // so just to be safe assume it can be nothing and store it byte storedVehicleFlag; } @@ -256,7 +255,7 @@ enum CellTechnoMode NoAttachments, NoVirtualOrRelatives, NoVirtual, - NoRelatives, + NoRelatives, // misleading name but I think doesn't matter for the use case for now All, DefaultBehavior = All, @@ -403,11 +402,12 @@ 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; + return TechnoExt::IsAttached(pFoot) ? Skip : Continue; } +#pragma region Command inheritance + void ParentClickedWaypoint(TechnoClass* pThis, int idxPath, signed char idxWP) { // Rewrite of the original code @@ -532,6 +532,7 @@ DEFINE_HOOK(0x730F1C, StopCommand_Context_Unset, 0x5) return 0; } +#pragma endregion DEFINE_HOOK(0x469672, BulletClass_Logics_Locomotor_CheckIfAttached, 0x6) { @@ -630,7 +631,8 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, C return result; } -// TODO MouseOverObject for entering bunkers, grinder, buildings etc +// MouseOverObject for entering bunkers, grinder, buildings etc +// is handled along with the shield logics in another file DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 7cf3a0ce2e..09fea05a43 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -54,9 +54,9 @@ Matrix3D AttachmentLocomotionClass::Draw_Matrix(VoxelIndexKey* key) // of the parent slope plane to calculate the correct offset for Shadow_Point, // complicated trigonometry that would be a waste of time at this point. -// If you want to work on this - Shadow_Matrix should be fine as is, -// Shadow_Point would need calculated height from the ramp extension plane - Kerbiter - +// If you want to work on this - Shadow_Matrix should be fine to copy from Draw_Matrix, +// (even the key shenanigans can be left the same), butShadow_Point would need to be +// calculated using height from the ramp extension plane - Kerbiter Point2D AttachmentLocomotionClass::Draw_Point() { @@ -126,6 +126,8 @@ bool AttachmentLocomotionClass::Process() this->PreviousCell = newPos; } + // TODO sensors and sight + AttachmentClass* pAttachment = this->GetAttachment(); if (pAttachment && pAttachment->GetType()->InheritHeightStatus) { @@ -140,6 +142,7 @@ bool AttachmentLocomotionClass::Process() return LocomotionClass::Process(); } +// I am not sure this does anything and could probably be removed bool AttachmentLocomotionClass::Is_Powered() { ILocomotionPtr pParentLoco = this->GetAttachmentParentLoco(); diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index e7d9b956ab..69d1c0cc4c 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -214,6 +214,9 @@ bool AttachmentClass::AttachChild(TechnoClass* pChild) if (this->Child) return false; + if (pChild->WhatAmI() != AbstractType::Unit) + return false; + if (auto const pChildAsFoot = abstract_cast(pChild)) { if (IPersistPtr pLocoPersist = pChildAsFoot->Locomotor) From ff5e9c9ef2e2b81d1b7141622ef7c237335ffc9f Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 7 Sep 2023 23:27:14 +0300 Subject: [PATCH 074/112] Sensors for attachment loco --- src/Locomotion/AttachmentLocomotionClass.cpp | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 09fea05a43..ca68de32ad 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -120,8 +120,17 @@ bool AttachmentLocomotionClass::Process() CellStruct oldPos = this->PreviousCell; CellStruct newPos = this->LinkedTo->GetMapCoords(); - if (Layer::Air <= newLayer && !changedAirborneStatus && oldPos != newPos) - AircraftTrackerClass::Instance->Update(this->LinkedTo, oldPos, newPos); + if (oldPos != newPos) + { + if (Layer::Air <= newLayer && !changedAirborneStatus) + AircraftTrackerClass::Instance->Update(this->LinkedTo, oldPos, newPos); + + if (this->LinkedTo->GetTechnoType()->SensorsSight) + { + this->LinkedTo->RemoveSensorsAt(oldPos); + this->LinkedTo->AddSensorsAt(newPos); + } + } this->PreviousCell = newPos; } From 2a3d47339944d568ec82b1aa49ff04ee273f4453 Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 7 Sep 2023 23:27:44 +0300 Subject: [PATCH 075/112] (Hacky?) sight for attachment loco --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index bc0e789c0f..c06b1d8a9f 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -705,3 +705,17 @@ DEFINE_HOOK(0x6DA4FB, TacticalClass_SelectAt_TransparentToMouse_OccupierPtr, 0x6 R->EAX(pFoundObject); return 0x6DA501; } + +// this is probably not the best way to implement sight since we may be hijacking +// into some undesirable side effects, cause this is intended for air units that +// don't run Per Cell Process function, ergo, don't update their sight - Kerbiter +DEFINE_HOOK(0x4DA6A0, FootClass_AI_CheckLocoForSight, 0x0) +{ + enum { ContinueCheck = 0x4DA6AF, NoSightUpdate = 0x4DA7B0 }; + + GET(FootClass*, pThis, ESI); + + return pThis->IsInAir() || TechnoExt::HasAttachmentLoco(pThis) + ? ContinueCheck + : NoSightUpdate; +} From 00b221cdc3b546103f9c4e2efe59a9b25478e4ce Mon Sep 17 00:00:00 2001 From: Meta Date: Thu, 7 Sep 2023 23:28:10 +0300 Subject: [PATCH 076/112] Handle CellClass::Incoming properly --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 39 ++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index c06b1d8a9f..f8854da5a6 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -580,7 +580,44 @@ DEFINE_HOOK(0x6F3283, TechnoClass_CanScatter_CheckIfAttached, 0x8) : ContinueCheck; } -// TODO maybe? handle scatter for InfantryClass and possibly AircraftClass +DEFINE_HOOK(0x4817A8, CellClass_Incoming_CheckIfTechnoOccupies, 0x6) +{ + enum { ConditionIsTrue = 0x4817C3, ContinueCheck = 0x0 }; + + GET(TechnoClass*, pTechno, ESI); + auto const& pExt = TechnoExt::ExtMap.Find(pTechno); + + return pExt->ParentAttachment && pExt->ParentAttachment->GetType()->OccupiesCell + ? ConditionIsTrue + : ContinueCheck; +} + +DEFINE_HOOK(0x4817C3, CellClass_Incoming_HandleScatterWithAttachments, 0x0) +{ + GET(TechnoClass*, pTechno, ESI); + + GET(CoordStruct*, pThreatCoord, EBP); + GET(bool, isForced, EBX); + GET_STACK(bool, isNoKidding, STACK_OFFSET(0x2C, 0xC)); // direct all complaints to tomsons26 for the variable naming + CoordStruct const& threatCoord = *pThreatCoord; + + // we already checked that this is something that occupies the cell, see the hook above - Kerbiter + TechnoExt::GetTopLevelParent(pTechno)->Scatter(threatCoord, isForced, isNoKidding); + + return 0x4817D9; +} + +DEFINE_HOOK(0x51D0DD, InfantryClass_Scatter_CheckAttachments, 0x6) +{ + enum { Bail = 0x51D6E6, Continue = 0x0 }; + + GET(InfantryClass*, pThis, ESI); + + return TechnoExt::HasAttachmentLoco(pThis) + ? Bail + : Continue; +} + DEFINE_HOOK(0x736FB6, UnitClass_FiringAI_ForbidAttachmentRotation, 0x6) { From 85025e966647680ecdef017650feb58dcdbe1212 Mon Sep 17 00:00:00 2001 From: Meta Date: Fri, 8 Sep 2023 15:53:04 +0300 Subject: [PATCH 077/112] Fix *Layer tags type --- src/Ext/Rules/Body.h | 4 ++-- src/Ext/TechnoType/Body.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Ext/Rules/Body.h b/src/Ext/Rules/Body.h index 08d0d49330..de7050a48a 100644 --- a/src/Ext/Rules/Body.h +++ b/src/Ext/Rules/Body.h @@ -101,8 +101,8 @@ class RulesExt Valueable ShowDesignatorRange; Valueable IsVoiceCreatedGlobal; - Valueable AttachmentTopLayerMinHeight; - Valueable AttachmentUndergroundLayerMaxHeight; + Valueable AttachmentTopLayerMinHeight; + Valueable AttachmentUndergroundLayerMaxHeight; ExtData(RulesClass* OwnerObject) : Extension(OwnerObject) , Storage_TiberiumIndex { -1 } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 66d1a3031e..556788594b 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -179,8 +179,8 @@ class TechnoTypeExt Nullable SpawnsPipSize; Valueable SpawnsPipOffset; - Valueable AttachmentTopLayerMinHeight; - Valueable AttachmentUndergroundLayerMaxHeight; + Valueable AttachmentTopLayerMinHeight; + Valueable AttachmentUndergroundLayerMaxHeight; struct AttachmentDataEntry { From 1def3aad6be1165934820ba459f0d4c62d85237c Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 10 Sep 2023 00:12:19 +0300 Subject: [PATCH 078/112] Implement building upgrades support --- src/Ext/Techno/Body.cpp | 14 ++++++++++++++ src/Ext/Techno/Body.h | 1 + src/Ext/Techno/Hooks.TechnoAttachment.cpp | 10 ++++++++++ 3 files changed, 25 insertions(+) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 3748ac7e7a..e665c64100 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -700,6 +700,20 @@ void TechnoExt::LimboAttachments(TechnoClass* pThis) pAttachment->Limbo(); } +void TechnoExt::TransferAttachments(TechnoClass* pThis, TechnoClass* pThat) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + auto const pThatExt = TechnoExt::ExtMap.Find(pThat); + + for (auto& pAttachment : pThisExt->ChildAttachments) + { + pAttachment->Parent = pThat; + pThatExt->ChildAttachments.push_back(std::move(pAttachment)); + } + + pThisExt->ChildAttachments.clear(); +} + bool TechnoExt::IsAttached(TechnoClass* pThis) { auto const& pExt = TechnoExt::ExtMap.Find(pThis); diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 6ce506b592..aa17da08c6 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -147,6 +147,7 @@ class TechnoExt static void HandleDestructionAsChild(TechnoClass* pThis); static void UnlimboAttachments(TechnoClass* pThis); static void LimboAttachments(TechnoClass* pThis); + static void TransferAttachments(TechnoClass* pThis, TechnoClass* pThat); static bool IsAttached(TechnoClass* pThis); static bool HasAttachmentLoco(FootClass* pThis); // FIXME shouldn't be here diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index f8854da5a6..dcb8a86728 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -756,3 +756,13 @@ DEFINE_HOOK(0x4DA6A0, FootClass_AI_CheckLocoForSight, 0x0) ? ContinueCheck : NoSightUpdate; } + +DEFINE_HOOK(0x440951, BuildingClass_Unlimbo_AttachmentsFromUpgrade, 0x6) +{ + GET(BuildingClass*, pBuilding, EDI); + GET(BuildingClass*, pUpgrade, ESI); + + TechnoExt::TransferAttachments(pUpgrade, pBuilding); + + return 0; +} \ No newline at end of file From a3b084ae552141aae21c13d5e1ed0bc4ea463329 Mon Sep 17 00:00:00 2001 From: Meta Date: Sun, 10 Sep 2023 00:13:21 +0300 Subject: [PATCH 079/112] Fix AreRelatives --- src/Ext/Techno/Body.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index e665c64100..a5f8e4f8b3 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -747,8 +747,8 @@ bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) bool TechnoExt::AreRelatives(TechnoClass* pThis, TechnoClass* pThat) { - return TechnoExt::IsChildOf(pThis, pThat) - || TechnoExt::IsChildOf(pThat, pThis); + return TechnoExt::GetTopLevelParent(pThis) + == TechnoExt::GetTopLevelParent(pThat); } // Returns this if no parent. From 6f25f1db076468c72f9105c12c7bf16973c784c6 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Wed, 17 Apr 2024 13:03:28 +0300 Subject: [PATCH 080/112] Fix passenger loading for multiple technos on cell --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 33 +++++++++++++++++++++++ src/Misc/Hooks.BugFixes.cpp | 2 ++ 2 files changed, 35 insertions(+) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index dcb8a86728..c9f55b5e2e 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -337,6 +337,39 @@ DEFINE_HOOK(0x4495F7, BuildingClass_ClearFactoryBib_SkipCreatedUnitAttachments, return NotClear; } +// original code doesn't account for multiple possible technos on the cell +DEFINE_HOOK(0x73A5EA, UnitClass_PerCellProcess_EntryLoopTechnos, 0x0) +{ + enum { SkipEntry = 0x73A7D2, TryEnterTarget = 0x73A6D1 }; + + GET(UnitClass*, pThis, EBP); + + if (pThis->GetCurrentMission() != Mission::Enter) + return SkipEntry; + + CellClass* pCell = pThis->GetCell(); + ObjectClass*& pFirst = pThis->OnBridge + ? pCell->AltObject : pCell->FirstObject; + + for (ObjectClass* pObject = pFirst; pObject; pObject = pObject->NextObject) + { + auto pEntryTarget = abstract_cast(pObject); + + if (pEntryTarget + && pEntryTarget != pThis + && pEntryTarget->GetMapCoords() == pThis->GetMapCoords() + && pThis->ContainsLink(pEntryTarget) + && pEntryTarget->GetTechnoType()->Passengers > 0) + { + R->ESI(pEntryTarget); + return TryEnterTarget; + } + } + + return SkipEntry; +} + + #pragma endregion #pragma region InAir/OnGround diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index 1a1a1abee0..b6123ffd7b 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -14,6 +14,8 @@ #include #include #include +#include + #include #include #include From 64e3fbe7e78d03f8a811d079e1d4b5e06e23792a Mon Sep 17 00:00:00 2001 From: Metadorius Date: Wed, 17 Apr 2024 13:17:08 +0300 Subject: [PATCH 081/112] Post-merge fix --- src/Utilities/TemplateDef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index ebdc155f4a..3142f81fcc 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -1238,7 +1238,7 @@ if(_strcmpi(parser.value(), #name) == 0){ value = __uuidof(name ## LocomotionCla } template <> - inline bool read(AttachmentYSortPosition& value, INI_EX& parser, const char* pSection, const char* pKey, bool allocate) + inline bool read(AttachmentYSortPosition& value, INI_EX& parser, const char* pSection, const char* pKey) { if (parser.ReadString(pSection, pKey)) { From fd7197a88cc7212fa9568620d56221c0fb05188e Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 6 May 2024 01:25:07 +0300 Subject: [PATCH 082/112] Post-merge fix --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index c9f55b5e2e..3ecd8da16e 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -551,7 +551,7 @@ DEFINE_HOOK(0x6FFE4F, TechnoClass_ClickedEvent_HandleChildren, 0x6) for (auto const& pAttachment : pExt->ChildAttachments) { if (pAttachment->Child && pAttachment->GetType()->InheritCommands) - pAttachment->Child->ClickedEvent(NetworkEvents::Idle); + pAttachment->Child->ClickedEvent(EventType::Idle); } } } From 250b4c3e9e5c1ff62516b70c668880754f76b334 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 6 May 2024 01:25:40 +0300 Subject: [PATCH 083/112] Fix transports not working properly when there's multiple technos on cell --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 41 +++++++++++++++++++++-- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 3ecd8da16e..3ff0cde532 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -250,7 +250,7 @@ DEFINE_HOOK(0x51C78F, InfantryClass_CanEnterCell_CheckMovingInto, 0x6) return 0; } -enum CellTechnoMode +enum class CellTechnoMode { NoAttachments, NoVirtualOrRelatives, @@ -263,7 +263,7 @@ enum CellTechnoMode namespace TechnoAttachmentTemp { - CellTechnoMode currentMode = DefaultBehavior; + CellTechnoMode currentMode = CellTechnoMode::DefaultBehavior; } #define DEFINE_CELLTECHNO_WRAPPER(mode) \ @@ -369,6 +369,41 @@ DEFINE_HOOK(0x73A5EA, UnitClass_PerCellProcess_EntryLoopTechnos, 0x0) return SkipEntry; } +enum class AttachCargoMode +{ + SingleObject, + ObjectChain, + + DefaultBehavior = SingleObject, +}; + +namespace TechnoAttachmentTemp +{ + AttachCargoMode currentAttachMode = AttachCargoMode::DefaultBehavior; +} + +#define DEFINE_ATTACH_WRAPPER(mode) \ +void __fastcall CargoClass_Attach_##mode(PassengersClass* pThis, discard_t, FootClass* pThat) \ +{ \ + TechnoAttachmentTemp::currentAttachMode = AttachCargoMode::mode; \ + pThis->AddPassenger(pThat); \ + TechnoAttachmentTemp::currentAttachMode = AttachCargoMode::DefaultBehavior; \ +} + +DEFINE_ATTACH_WRAPPER(SingleObject); +DEFINE_ATTACH_WRAPPER(ObjectChain); + +DEFINE_JUMP(CALL, 0x65DF88, GET_OFFSET(CargoClass_Attach_ObjectChain)); // Create_Group +DEFINE_JUMP(CALL, 0x65DCF0, GET_OFFSET(CargoClass_Attach_ObjectChain)); // Do_Reinforcements, paradrop loading + +DEFINE_HOOK(0x4733BD, CargoClass_Attach_HandleCurrentAttachMode, 0x6) +{ + enum { SkipAttachingChain = 0x4733FA, Continue = 0x0 }; + + return TechnoAttachmentTemp::currentAttachMode == AttachCargoMode::SingleObject + ? SkipAttachingChain + : Continue; +} #pragma endregion @@ -798,4 +833,4 @@ DEFINE_HOOK(0x440951, BuildingClass_Unlimbo_AttachmentsFromUpgrade, 0x6) TechnoExt::TransferAttachments(pUpgrade, pBuilding); return 0; -} \ No newline at end of file +} From 6448ac3476a1ebfe87e2553ef3f00397e7e3b8b5 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Fri, 20 Sep 2024 00:13:55 +0300 Subject: [PATCH 084/112] InheritCommands.DeployCommand & StopCommand as separate key --- docs/New-or-Enhanced-Logics.md | 2 ++ src/Ext/Techno/Hooks.TechnoAttachment.cpp | 25 +++++++++++++++++++++-- src/New/Type/AttachmentTypeClass.cpp | 4 ++++ src/New/Type/AttachmentTypeClass.h | 4 ++++ 4 files changed, 33 insertions(+), 2 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 92770aed43..158c9cdf64 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -177,6 +177,8 @@ RespawnDelay=-1 ; integer, non-negative values enable the InheritOwner=true ; boolean, whether the child inherits owner of the parent while it's attached InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) InheritCommands=true ; boolean +InheritCommands.StopCommand=true ; boolean +InheritCommands.DeployCommand=true ; boolean LowSelectionPriority=true ; boolean, whether the child is low priority while attached TransparentToMouse=false ; boolean, can't click on attached techno if set YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 3ff0cde532..dadce93067 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -557,6 +557,7 @@ DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) namespace TechnoAttachmentTemp { bool stopPressed = false; + bool deployPressed = false; } DEFINE_HOOK(0x730EA0, StopCommand_Context_Set, 0x5) @@ -565,6 +566,12 @@ DEFINE_HOOK(0x730EA0, StopCommand_Context_Set, 0x5) return 0; } +DEFINE_HOOK(0x730AF0, DeployCommand_Context_Set, 0x8) +{ + TechnoAttachmentTemp::deployPressed = true; + return 0; +} + namespace TechnoAttachmentTemp { TechnoClass* pParent = nullptr; @@ -579,14 +586,21 @@ DEFINE_HOOK(0x6FFE00, TechnoClass_ClickedEvent_Context_Set, 0x5) DEFINE_HOOK_AGAIN(0x6FFEB1, TechnoClass_ClickedEvent_HandleChildren, 0x6) DEFINE_HOOK(0x6FFE4F, TechnoClass_ClickedEvent_HandleChildren, 0x6) { - if (TechnoAttachmentTemp::stopPressed && TechnoAttachmentTemp::pParent) + if ((TechnoAttachmentTemp::stopPressed || TechnoAttachmentTemp::deployPressed) + && TechnoAttachmentTemp::pParent) { if (auto const& pExt = TechnoExt::ExtMap.Find(TechnoAttachmentTemp::pParent)) { for (auto const& pAttachment : pExt->ChildAttachments) { - if (pAttachment->Child && pAttachment->GetType()->InheritCommands) + if (!pAttachment->Child) + continue; + + if (pAttachment->GetType()->InheritCommands_StopCommand) pAttachment->Child->ClickedEvent(EventType::Idle); + + if (pAttachment->GetType()->InheritCommands_DeployCommand) + pAttachment->Child->ClickedEvent(EventType::Deploy); } } } @@ -600,6 +614,13 @@ DEFINE_HOOK(0x730F1C, StopCommand_Context_Unset, 0x5) return 0; } +DEFINE_HOOK(0x730D55, DeployCommand_Context_Unset, 0x7) +{ + TechnoAttachmentTemp::deployPressed = false; + return 0; +} + + #pragma endregion DEFINE_HOOK(0x469672, BulletClass_Logics_Locomotor_CheckIfAttached, 0x6) diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 3acb54f786..2290eb1571 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -18,6 +18,8 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->RespawnAtCreation.Read(exINI, section, "RespawnAtCreation"); this->RespawnDelay.Read(exINI, section, "RespawnDelay"); this->InheritCommands.Read(exINI, section, "InheritCommands"); + this->InheritCommands_StopCommand.Read(exINI, section, "InheritCommands.StopCommand"); + this->InheritCommands_DeployCommand.Read(exINI, section, "InheritCommands.DeployCommand"); this->InheritOwner.Read(exINI, section, "InheritOwner"); this->InheritStateEffects.Read(exINI, section, "InheritStateEffects"); this->InheritDestruction.Read(exINI, section, "InheritDestruction"); @@ -39,6 +41,8 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->RespawnAtCreation) .Process(this->RespawnDelay) .Process(this->InheritCommands) + .Process(this->InheritCommands_StopCommand) + .Process(this->InheritCommands_DeployCommand) .Process(this->InheritOwner) .Process(this->InheritStateEffects) .Process(this->InheritDestruction) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 5032532ed1..b4acf4bbae 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -12,6 +12,8 @@ class AttachmentTypeClass final : public Enumerable Valueable RespawnAtCreation; // whether to spawn the attachment initially Valueable RespawnDelay; Valueable InheritCommands; + Valueable InheritCommands_StopCommand; + Valueable InheritCommands_DeployCommand; Valueable InheritOwner; // aka mind control inheritance Valueable InheritStateEffects; // phasing out, stealth etc. Valueable InheritDestruction; @@ -29,6 +31,8 @@ class AttachmentTypeClass final : public Enumerable , RespawnAtCreation { true } , RespawnDelay { -1 } , InheritCommands { true } + , InheritCommands_StopCommand { true } + , InheritCommands_DeployCommand { true } , InheritOwner { true } , InheritStateEffects { true } , OccupiesCell { true } From 4242a2927d548da7611653081bd545aab35600b9 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Thu, 30 Jan 2025 22:26:38 +0200 Subject: [PATCH 085/112] WIP rewrite of occupancy handling, CellExt crashes --- Phobos.vcxproj | 3 + src/Ext/Cell/Body.cpp | 82 ++++++++++++++++++++ src/Ext/Cell/Body.h | 66 ++++++++++++++++ src/Ext/Cell/Hooks.cpp | 72 +++++++++++++++++ src/Ext/Techno/Hooks.TechnoAttachment.cpp | 44 +++-------- src/Locomotion/AttachmentLocomotionClass.cpp | 2 +- src/Phobos.Ext.cpp | 2 + 7 files changed, 238 insertions(+), 33 deletions(-) create mode 100644 src/Ext/Cell/Body.cpp create mode 100644 src/Ext/Cell/Body.h create mode 100644 src/Ext/Cell/Hooks.cpp diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 1ea8d1aa62..c8691e86ca 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -40,6 +40,8 @@ + + @@ -215,6 +217,7 @@ + diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp new file mode 100644 index 0000000000..69fd539e84 --- /dev/null +++ b/src/Ext/Cell/Body.cpp @@ -0,0 +1,82 @@ +#include "Body.h" + +#include + +CellExt::ExtContainer CellExt::ExtMap; + +void CellExt::ExtData::Initialize() { } + + + +// ============================= +// load / save +template +void CellExt::ExtData::Serialize(T& Stm) +{ + Stm + .Process(IncomingUnit) + .Process(IncomingUnitAlt) + ; +} + +void CellExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) +{ + Extension::LoadFromStream(Stm); + this->Serialize(Stm); +} + +void CellExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) +{ + Extension::SaveToStream(Stm); + this->Serialize(Stm); +} + +// ============================= +// container + +CellExt::ExtContainer::ExtContainer() : Container("CellClass") { } +CellExt::ExtContainer::~ExtContainer() = default; + +// ============================= +// container hooks + +DEFINE_HOOK(0x47BBF0, CellClass_CTOR, 0x6) +{ + GET(CellClass*, pItem, ECX); + + CellExt::ExtMap.TryAllocate(pItem); + + return 0; +} + +DEFINE_HOOK(0x47BB60, CellClass_DTOR, 0x6) +{ + GET(CellClass*, pItem, ECX); + + CellExt::ExtMap.Remove(pItem); + + 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, 0x5) +{ + CellExt::ExtMap.LoadStatic(); + return 0; +} + +DEFINE_HOOK(0x483C79, CellClass_Save_Suffix, 0x6) +{ + CellExt::ExtMap.SaveStatic(); + return 0; +} diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h new file mode 100644 index 0000000000..a4c4de46f0 --- /dev/null +++ b/src/Ext/Cell/Body.h @@ -0,0 +1,66 @@ +#pragma once + +#include + +#include +#include +#include +#include +#include + +#include + +class CellExt +{ +public: + using base_type = CellClass; + + static constexpr DWORD Canary = 0x13371337; + // static constexpr size_t ExtPointerOffset = 0x144; + + class ExtData final : public Extension + { + public: + UnitClass* IncomingUnit; + UnitClass* IncomingUnitAlt; + + ExtData(CellClass* OwnerObject) : Extension(OwnerObject) + , IncomingUnit() + , IncomingUnitAlt() + { } + + virtual ~ExtData() = default; + + virtual void InvalidatePointer(void* ptr, bool bRemoved) override + { + if (ptr == static_cast(this->IncomingUnit)) + { + this->OwnerObject()->OccupationFlags &= ~0x20; + this->IncomingUnit = nullptr; + } + + if (ptr == static_cast(this->IncomingUnitAlt)) + { + this->OwnerObject()->AltOccupationFlags &= ~0x20; + this->IncomingUnitAlt = nullptr; + } + } + + virtual void LoadFromStream(PhobosStreamReader& Stm) override; + virtual void SaveToStream(PhobosStreamWriter& Stm) override; + virtual void Initialize() override; + + private: + template + void Serialize(T& Stm); + }; + + class ExtContainer final : public Container + { + public: + ExtContainer(); + ~ExtContainer(); + }; + + static ExtContainer ExtMap; +}; diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp new file mode 100644 index 0000000000..5d9c2073ca --- /dev/null +++ b/src/Ext/Cell/Hooks.cpp @@ -0,0 +1,72 @@ +#include "Body.h" + +#include + +namespace CellExtTemp +{ + UnitClass* pThis = nullptr; +} + +DEFINE_HOOK(0x7441B0, UnitClass_SetOccupyBit_Prefix, 0x5) +{ + enum { SkipFuncAndReturn = 0x74420B }; + + GET(UnitClass*, pThis, ECX); + + if (TechnoExt::DoesntOccupyCellAsChild(pThis)) + return SkipFuncAndReturn; + + CellExtTemp::pThis = pThis; + + return 0; +} + +DEFINE_HOOK(0x744209, UnitClass_SetOccupyBit_IncomingUnit, 0x5) +{ + GET(CellClass*, pCell, EDI); + + CellExt::ExtMap.Find(pCell)->IncomingUnit = CellExtTemp::pThis; + + return 0; +} + +DEFINE_HOOK(0x7441F6, UnitClass_SetOccupyBit_IncomingUnitAlt, 0x5) +{ + GET(CellClass*, pCell, EDI); + + CellExt::ExtMap.Find(pCell)->IncomingUnitAlt = CellExtTemp::pThis; + + return 0; +} + +// currently the game, at least without other exts, doesn't do any checks if it's clearing a unit's occupy bit + +DEFINE_HOOK(0x744210, UnitClass_ClearOccupyBit_Prefix, 0x5) +{ + enum { SkipFuncAndReturn = 0x74424D }; + + GET(UnitClass*, pThis, ECX); + + if (TechnoExt::DoesntOccupyCellAsChild(pThis)) + return SkipFuncAndReturn; + + return 0; +} + +DEFINE_HOOK(0x74425E, UnitClass_ClearOccupyBit_IncomingUnit, 0x5) +{ + GET(CellClass*, pCell, EDI); + + CellExt::ExtMap.Find(pCell)->IncomingUnit = nullptr; + + return 0; +} + +DEFINE_HOOK(0x74424B, UnitClass_ClearOccupyBit_IncomingUnitAlt, 0x5) +{ + GET(CellClass*, pCell, EDI); + + CellExt::ExtMap.Find(pCell)->IncomingUnitAlt = nullptr; + + return 0; +} diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index dadce93067..9c7448f96e 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -5,6 +5,7 @@ #include #include +#include #include @@ -140,7 +141,7 @@ bool IsOccupierIgnorable(TechnoClass* pThis, ObjectClass* pOccupier, byte& occup DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) { - enum { IgnoreOccupier = 0x73FA87, Continue = 0x73F530 }; + enum { SkipToNextOccupier = 0x73FA87, ContinueCheck = 0x73F530 }; GET(UnitClass*, pThis, EBX); GET(ObjectClass*, pOccupier, ESI); @@ -149,41 +150,20 @@ DEFINE_HOOK(0x73F528, UnitClass_CanEnterCell_SkipChildren, 0x0) REF_STACK(bool, isVehicleFlagSet, STACK_OFFSET(0x90, -0x7B)); return IsOccupierIgnorable(pThis, pOccupier, occupyFlags, isVehicleFlagSet) - ? IgnoreOccupier : Continue; + ? SkipToNextOccupier : ContinueCheck; } -// Also because the occupancy can be marked by something moving into the spot -// we also check the nearby (yes this is an assumption, if you are reviewing -// this voice your opinion in the review) cells for the presence of technos -// that are moving into this spot right now - Kerbiter - void AccountForMovingInto(CellClass* into, bool isAlt, TechnoClass* pThis, byte& occupyFlags, bool& isVehicleFlagSet) { - // see 42B080, A* checks 2 cells around, this is approx. it - CellStruct center = into->MapCoords; - CoordStruct intoCoords = into->GetCellCoords(); - if (isAlt) - intoCoords.Z += CellClass::BridgeHeight; + auto const pCellExt = CellExt::ExtMap.Find(into); + auto const& pIncoming = isAlt ? pCellExt->IncomingUnitAlt : pCellExt->IncomingUnit; - for (CellRectEnumerator cell({ center.X - 2, center.Y - 2, center.X + 2, center.Y + 2}); cell; ++cell) + // Non-occupiers shouldn't be inserted as incoming units anyways so don't check that + if (pIncoming && pIncoming != pThis && + !TechnoExt::IsChildOf(pIncoming, pThis)) { - if (auto const pCell = MapClass::Instance->TryGetCellAt(*cell)) - { - for (NextObject object(isAlt ? pCell->AltObject : pCell->FirstObject); object; ++object) - { - if (*object == pThis) - continue; - - auto const pUnit = abstract_cast(*object); - if (pUnit && pUnit->Locomotor->Is_Moving_Here(intoCoords) - && !TechnoExt::DoesntOccupyCellAsChild(pUnit) - && !TechnoExt::IsChildOf(pUnit, pThis)) - { - occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; - isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; - } - } - } + occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; + isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; } } @@ -235,7 +215,7 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) return IsOccupierIgnorable(pThis, pOccupier, occupyFlags, isVehicleFlagSet) ? IgnoreOccupier : Continue; } - +/* DEFINE_HOOK(0x51C78F, InfantryClass_CanEnterCell_CheckMovingInto, 0x6) { GET_STACK(CellClass*, into, STACK_OFFSET(0x34, 0x4)); @@ -249,7 +229,7 @@ DEFINE_HOOK(0x51C78F, InfantryClass_CanEnterCell_CheckMovingInto, 0x6) return 0; } - +*/ enum class CellTechnoMode { NoAttachments, diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index ca68de32ad..5897e16276 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -135,7 +135,7 @@ bool AttachmentLocomotionClass::Process() this->PreviousCell = newPos; } - // TODO sensors and sight + // sight is handled in FootClass::AI AttachmentClass* pAttachment = this->GetAttachment(); if (pAttachment && pAttachment->GetType()->InheritHeightStatus) diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 98e2f48820..7e69837e2b 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -9,6 +9,7 @@ #include #include #include +#include #include #include #include @@ -203,6 +204,7 @@ using PhobosTypeRegistry = TypeRegistry< BuildingTypeExt, BulletExt, BulletTypeExt, + CellExt, HouseExt, OverlayTypeExt, ParticleSystemTypeExt, From b9cf6a3a3aeeaabe31ec1caaf2d52ddf68295c42 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Sun, 2 Feb 2025 00:18:17 +0200 Subject: [PATCH 086/112] cellext working, collisions? still no --- src/Ext/Cell/Hooks.cpp | 118 +++++++++++----------- src/Ext/Techno/Body.cpp | 1 + src/Ext/Techno/Body.h | 5 + src/Ext/Techno/Hooks.TechnoAttachment.cpp | 20 +--- 4 files changed, 68 insertions(+), 76 deletions(-) diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index 5d9c2073ca..889038df12 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -2,71 +2,75 @@ #include -namespace CellExtTemp -{ - UnitClass* pThis = nullptr; -} - -DEFINE_HOOK(0x7441B0, UnitClass_SetOccupyBit_Prefix, 0x5) -{ - enum { SkipFuncAndReturn = 0x74420B }; - - GET(UnitClass*, pThis, ECX); - - if (TechnoExt::DoesntOccupyCellAsChild(pThis)) - return SkipFuncAndReturn; - CellExtTemp::pThis = pThis; - - return 0; -} - -DEFINE_HOOK(0x744209, UnitClass_SetOccupyBit_IncomingUnit, 0x5) -{ - GET(CellClass*, pCell, EDI); - - CellExt::ExtMap.Find(pCell)->IncomingUnit = CellExtTemp::pThis; - - return 0; -} +// because 🦅💣 takes over, we have to do reimpl even more of the func and replicate Ares code -DEFINE_HOOK(0x7441F6, UnitClass_SetOccupyBit_IncomingUnitAlt, 0x5) +void __fastcall UnitClass_SetOccupyBit_Reimpl(UnitClass* pThis, discard_t, CoordStruct* pCrd) { - GET(CellClass*, pCell, EDI); - - CellExt::ExtMap.Find(pCell)->IncomingUnitAlt = CellExtTemp::pThis; - - return 0; + if (TechnoExt::DoesntOccupyCellAsChild(pThis)) + return; + + CellClass* pCell = MapClass::Instance->GetCellAt(*pCrd); + auto pCellExt = CellExt::ExtMap.Find(pCell); + int height = MapClass::Instance->GetCellFloorHeight(*pCrd) + CellClass::BridgeHeight; + bool alt = (pCrd->Z >= height && pCell->ContainsBridge()); + + // remember which occupation bit we set + auto pExt = TechnoExt::ExtMap.Find(pThis); + pExt->AltOccupation = alt; + + if (alt) + { + pCell->AltOccupationFlags |= 0x20; + // Phobos addition: set incoming unit tracker + pCellExt->IncomingUnitAlt = pThis; + } + else + { + pCell->OccupationFlags |= 0x20; + // Phobos addition: set incoming unit tracker + pCellExt->IncomingUnit = pThis; + } } -// currently the game, at least without other exts, doesn't do any checks if it's clearing a unit's occupy bit +DEFINE_JUMP(VTABLE, 0x7F5D60, GET_OFFSET(UnitClass_SetOccupyBit_Reimpl)); -DEFINE_HOOK(0x744210, UnitClass_ClearOccupyBit_Prefix, 0x5) +void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, discard_t, CoordStruct* pCrd) { - enum { SkipFuncAndReturn = 0x74424D }; - - GET(UnitClass*, pThis, ECX); - if (TechnoExt::DoesntOccupyCellAsChild(pThis)) - return SkipFuncAndReturn; - - return 0; -} - -DEFINE_HOOK(0x74425E, UnitClass_ClearOccupyBit_IncomingUnit, 0x5) -{ - GET(CellClass*, pCell, EDI); - - CellExt::ExtMap.Find(pCell)->IncomingUnit = nullptr; - - return 0; + return; + + enum { obNormal = 1, obAlt = 2 }; + + CellClass* pCell = MapClass::Instance->GetCellAt(*pCrd); + auto pCellExt = CellExt::ExtMap.Find(pCell); + int height = MapClass::Instance->GetCellFloorHeight(*pCrd) + CellClass::BridgeHeight; + int alt = (pCrd->Z >= height) ? obAlt : obNormal; + + // also clear the last occupation bit, if set + auto pExt = TechnoExt::ExtMap.Find(pThis); + if(!pExt->AltOccupation.empty()) + { + int lastAlt = pExt->AltOccupation ? obAlt : obNormal; + alt |= lastAlt; + pExt->AltOccupation.clear(); + } + + if (alt & obAlt) + { + pCell->AltOccupationFlags &= ~0x20; + // Phobos addition: clear incoming unit tracker + pCellExt->IncomingUnitAlt = nullptr; + } + + if (alt & obNormal) + { + pCell->OccupationFlags &= ~0x20; + // Phobos addition: clear incoming unit tracker + pCellExt->IncomingUnit = nullptr; + } } -DEFINE_HOOK(0x74424B, UnitClass_ClearOccupyBit_IncomingUnitAlt, 0x5) -{ - GET(CellClass*, pCell, EDI); - - CellExt::ExtMap.Find(pCell)->IncomingUnitAlt = nullptr; +DEFINE_JUMP(VTABLE, 0x7F5D64, GET_OFFSET(UnitClass_ClearOccupyBit_Reimpl)); - return 0; -} +// TODO ^ same for TA for non-UnitClass, not needed so cba for now diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index c31549ebe5..86b9676ea4 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -632,6 +632,7 @@ void TechnoExt::ExtData::Serialize(T& Stm) .Process(this->HasRemainingWarpInDelay) .Process(this->LastWarpInDelay) .Process(this->IsBeingChronoSphered) + .Process(this->AltOccupation) ; } diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 9fab58ec81..cee21a3390 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -60,6 +60,9 @@ class TechnoExt AttachmentClass* ParentAttachment; ValueableVector> ChildAttachments; + // Ares + OptionalStruct AltOccupation; // if the unit marks cell occupation flags, this is set to whether it uses the "high" occupation members + ExtData(TechnoClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } , Shield {} @@ -92,6 +95,8 @@ class TechnoExt , IsBeingChronoSphered { false} , ParentAttachment {} , ChildAttachments {} + + , AltOccupation { } { } void OnEarlyUpdate(); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 9c7448f96e..d168276f4c 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -60,25 +60,7 @@ DEFINE_HOOK(0x6F6B1C, TechnoClass_Limbo_LimboAttachments, 0x6) #pragma region Cell occupation handling -// because Ares hooks in the single usable position we need to do a detour instead -// screw Ares - -void __fastcall UnitClass_SetOccupyBit_SkipVirtual(UnitClass* pThis, discard_t, const CoordStruct& coords) -{ - if (!TechnoExt::DoesntOccupyCellAsChild(pThis)) - pThis->UnitClass::MarkAllOccupationBits(coords); -} - -void __fastcall UnitClass_ClearOccupyBit_SkipVirtual(UnitClass* pThis, discard_t, const CoordStruct& coords) -{ - if (!TechnoExt::DoesntOccupyCellAsChild(pThis)) - pThis->UnitClass::UnmarkAllOccupationBits(coords); -} - -DEFINE_JUMP(VTABLE, 0x7F5D60, GET_OFFSET(UnitClass_SetOccupyBit_SkipVirtual)) -DEFINE_JUMP(VTABLE, 0x7F5D64, GET_OFFSET(UnitClass_ClearOccupyBit_SkipVirtual)) - -// TODO ^ same for non-UnitClass, not needed so cba for now +// see hooks for CellExt namespace TechnoAttachmentTemp { From 32c90bded6f720ad15594d11a32c5f24e11b0fcd Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Mon, 3 Feb 2025 04:12:50 +0800 Subject: [PATCH 087/112] ")" --- docs/Fixed-or-Improved-Logics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/Fixed-or-Improved-Logics.md b/docs/Fixed-or-Improved-Logics.md index d39932ba2b..5606546923 100644 --- a/docs/Fixed-or-Improved-Logics.md +++ b/docs/Fixed-or-Improved-Logics.md @@ -1124,7 +1124,7 @@ Palette= ; filename - excluding .pal extension and three-character the - Instead of showing at 1 point of HP left, TerrainTypes switch to damaged frames once their health reaches `[AudioVisual]` -> `ConditionYellow.Terrain` percentage of their maximum health. Defaults to `ConditionYellow` if not set. - In addition, TerrainTypes can now show 'crumbling' animation after their health has reached zero and before they are deleted from the map by setting `HasCrumblingFrames` to true. - Crumbling frames start from first frame after both regular & damaged frames and ends at halfway point of the frames in TerrainType's image. - - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes). Exercise caution and ensure there are correct amount of frames to display. + - Note that the number of regular & damage frames considered for this depends on value of `HasDamagedFrames` and for `IsAnimated` TerrainTypes, `AnimationLength` (see [Animated TerrainTypes](#animated-terraintypes)). Exercise caution and ensure there are correct amount of frames to display. - Sound event from `CrumblingSound` (if set) is played when crumbling animation starts playing. - [Destroy animation & sound](New-or-Enhanced-Logics.md#destroy-animation--sound) only play after crumbling animation has finished. From 62a7f428a6dd42b8b407c066489a77ba125e7682 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 3 Feb 2025 00:28:15 +0200 Subject: [PATCH 088/112] COLLISIONS ARE BACK --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index d168276f4c..e1515bc468 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -115,7 +115,7 @@ bool IsOccupierIgnorable(TechnoClass* pThis, ObjectClass* pOccupier, byte& occup if (abstract_cast(pOccupier)) { occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; - isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + isVehicleFlagSet = (occupyFlags & 0x20) != 0; } return false; @@ -145,7 +145,7 @@ void AccountForMovingInto(CellClass* into, bool isAlt, TechnoClass* pThis, byte& !TechnoExt::IsChildOf(pIncoming, pThis)) { occupyFlags |= TechnoAttachmentTemp::storedVehicleFlag; - isVehicleFlagSet = TechnoAttachmentTemp::storedVehicleFlag != 0; + isVehicleFlagSet = (occupyFlags & 0x20) != 0; } } From 51da83c034ae8939b9a4ed1f46ba0bc024ae87d6 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Tue, 4 Feb 2025 23:40:46 +0200 Subject: [PATCH 089/112] Post-merge fixes --- src/Ext/Cell/Hooks.cpp | 4 ++-- src/Ext/Techno/Body.Update.cpp | 2 +- src/Ext/Techno/Body.h | 2 +- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 4 ++-- src/New/Type/AttachmentTypeClass.cpp | 2 -- src/New/Type/AttachmentTypeClass.h | 4 ++-- 6 files changed, 8 insertions(+), 10 deletions(-) diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index 889038df12..6e7dde3986 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -49,11 +49,11 @@ void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, discard_t, Coo // also clear the last occupation bit, if set auto pExt = TechnoExt::ExtMap.Find(pThis); - if(!pExt->AltOccupation.empty()) + if(pExt->AltOccupation.has_value()) { int lastAlt = pExt->AltOccupation ? obAlt : obNormal; alt |= lastAlt; - pExt->AltOccupation.clear(); + pExt->AltOccupation.reset(); } if (alt & obAlt) diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 63d31da3e3..37b7aefe4a 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -751,7 +751,7 @@ void TechnoExt::KillSelf(TechnoClass* pThis, AutoDeathBehavior deathOption, Anim void TechnoExt::Kill(TechnoClass* pThis, ObjectClass* pAttacker, HouseClass* pAttackingHouse) { - if (IS_ARES_FUN_AVAILABLE(SpawnSurvivors)) + if (AresFunctions::SpawnSurvivors) { switch (pThis->WhatAmI()) { diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 37694ee4e1..95590db193 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -65,7 +65,7 @@ class TechnoExt ValueableVector> ChildAttachments; // Ares - OptionalStruct AltOccupation; // if the unit marks cell occupation flags, this is set to whether it uses the "high" occupation members + std::optional AltOccupation; // if the unit marks cell occupation flags, this is set to whether it uses the "high" occupation members ExtData(TechnoClass* OwnerObject) : Extension(OwnerObject) , TypeExtData { nullptr } diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index e1515bc468..8fda17d4ef 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -489,7 +489,7 @@ DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) REF_STACK(int, idxPath, STACK_OFFSET(0x18, -0x8)); REF_STACK(unsigned char, idxWP, STACK_OFFSET(0x18, -0xC)); - for (auto const& pObject : ObjectClass::CurrentObjects.get()) + for (auto const& pObject : ObjectClass::CurrentObjects()) { if (auto pTechno = abstract_cast(pObject)) ParentClickedWaypoint(pTechno, idxPath, idxWP); @@ -505,7 +505,7 @@ DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) if (action == Action::Move || action == Action::PatrolWaypoint || action == Action::NoMove) pSecondCell = pCell; - for (auto const& pObject : ObjectClass::CurrentObjects.get()) + for (auto const& pObject : ObjectClass::CurrentObjects()) { if (auto pTechno = abstract_cast(pObject)) ParentClickedAction(pTechno, pTarget, pCell, pSecondCell); diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 2290eb1571..793a81e361 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -2,8 +2,6 @@ #include -Enumerable::container_t Enumerable::Array; - const char* Enumerable::GetMainSection() { return "AttachmentTypes"; diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index b4acf4bbae..838413530c 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -47,9 +47,9 @@ class AttachmentTypeClass final : public Enumerable , ParentDetachmentMission { } { } - virtual ~AttachmentTypeClass() override = default; + virtual ~AttachmentTypeClass() = default; - virtual void LoadFromINI(CCINIClass* pINI) override; + virtual void LoadFromINI(CCINIClass* pINI); virtual void LoadFromStream(PhobosStreamReader& Stm); virtual void SaveToStream(PhobosStreamWriter& Stm); From 8b0ccdc5e468e7d8aeb003c1df3cfaa8a81faef5 Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Sun, 23 Feb 2025 01:27:08 +0800 Subject: [PATCH 090/112] Delete an unnecessary ',' & changed "TechnoTypeClass" to "TechnoType" --- docs/New-or-Enhanced-Logics.md | 56 +++++++++++++++++----------------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index d414d7d765..e32b9fda2c 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -180,40 +180,40 @@ This feature is not final and is under development. In `rulesmd.ini`: ```ini [AttachmentTypes] -0=MNT ; (example) +0=MNT ; (example) [MNT] -RespawnAtCreation=true ; boolean -RespawnDelay=-1 ; integer, non-negative values enable the respawn timer -InheritOwner=true ; boolean, whether the child inherits owner of the parent while it's attached -InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) -InheritCommands=true ; boolean -InheritCommands.StopCommand=true ; boolean -InheritCommands.DeployCommand=true ; boolean -LowSelectionPriority=true ; boolean, whether the child is low priority while attached -TransparentToMouse=false ; boolean, can't click on attached techno if set -YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent -InheritDestruction=true ; boolean -InheritHeightStatus=true ; boolean, whether the layer and InAir/OnGround/IsSurfaced inherited from parent -OccupiesCell=true ; boolean -DestructionWeapon.Child= ; WeaponType, detonated on child when parent is destroyed -DestructionWeapon.Parent= ; WeaponType, detonated on parent when child is destroyed -ParentDestructionMission= ; MissionType, queued to child when parent is destroyed -ParentDetachmentMission= ; MissionType, queued to child when it's detached from parent - -[SOMETECHNO] ; TechnoTypeClass +RespawnAtCreation=true ; boolean +RespawnDelay=-1 ; integer, non-negative values enable the respawn timer +InheritOwner=true ; boolean, whether the child inherits owner of the parent while it's attached +InheritStateEffects=true ; boolean (state effects = chaos, iron curtain etc.) +InheritCommands=true ; boolean +InheritCommands.StopCommand=true ; boolean +InheritCommands.DeployCommand=true ; boolean +LowSelectionPriority=true ; boolean, whether the child is low priority while attached +TransparentToMouse=false ; boolean, can't click on attached techno if set +YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent +InheritDestruction=true ; boolean +InheritHeightStatus=true ; boolean, whether the layer and InAir/OnGround/IsSurfaced inherited from parent +OccupiesCell=true ; boolean +DestructionWeapon.Child= ; WeaponType, detonated on child when parent is destroyed +DestructionWeapon.Parent= ; WeaponType, detonated on parent when child is destroyed +ParentDestructionMission= ; MissionType, queued to child when parent is destroyed +ParentDetachmentMission= ; MissionType, queued to child when it's detached from parent + +[SOMETECHNO] ; TechnoType ; used when this techno is attached -AttachmentTopLayerMinHeight= ; integer -AttachmentUndergroundLayerMaxHeight= ; integer +AttachmentTopLayerMinHeight= ; integer +AttachmentUndergroundLayerMaxHeight= ; integer ; used for attaching other technos -AttachmentX.Type=MNT ; AttachmentType (example) -AttachmentX.TechnoType= ; TechnoType that can be attached, currently only units are supported -AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height -AttachmentX.IsOnTurret=false ; boolean -AttachmentX.RotationAdjust=0 ; rotation in DirType, from -255 to 255 +AttachmentX.Type=MNT ; AttachmentType (example) +AttachmentX.TechnoType= ; TechnoType that can be attached, currently only units are supported +AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height +AttachmentX.IsOnTurret=false ; boolean +AttachmentX.RotationAdjust=0 ; rotation in DirType, from -255 to 255 [General] -AttachmentTopLayerMinHeight=500 ; integer, +AttachmentTopLayerMinHeight=500 ; integer AttachmentUndergroundLayerMaxHeight=-256 ; integer ``` From 44a6a1c9adff033c210ecfcddd8f6a2301f13041 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 7 Mar 2025 18:29:09 +0300 Subject: [PATCH 091/112] Fix another hook forcing an attachment's facing --- src/Ext/Unit/Hooks.Jumpjet.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/Ext/Unit/Hooks.Jumpjet.cpp b/src/Ext/Unit/Hooks.Jumpjet.cpp index 1be3eb24b4..9e4b16441a 100644 --- a/src/Ext/Unit/Hooks.Jumpjet.cpp +++ b/src/Ext/Unit/Hooks.Jumpjet.cpp @@ -3,6 +3,7 @@ #include #include #include +#include // Misc jumpjet facing, turning, drawing fix -- Author: Trsdy // Jumpjets stuck at FireError::FACING because Jumpjet has its own facing just for JumpjetTurnRate @@ -13,6 +14,9 @@ DEFINE_HOOK(0x736F78, UnitClass_UpdateFiring_FireErrorIsFACING, 0x6) { GET(UnitClass* const, pThis, ESI); + if (TechnoExt::HasAttachmentLoco(pThis)) + return 0; + auto pType = pThis->Type; CoordStruct& source = pThis->Location; CoordStruct target = pThis->Target->GetCoords(); // Target checked so it's not null here From e38b338d3c80379c0bce9433f402146da4145064 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 7 Mar 2025 19:41:40 +0300 Subject: [PATCH 092/112] Fix incorrect usage of std::optional --- src/Ext/Cell/Hooks.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index 6e7dde3986..294222e8f0 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -51,7 +51,7 @@ void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, discard_t, Coo auto pExt = TechnoExt::ExtMap.Find(pThis); if(pExt->AltOccupation.has_value()) { - int lastAlt = pExt->AltOccupation ? obAlt : obNormal; + int lastAlt = pExt->AltOccupation.value() ? obAlt : obNormal; alt |= lastAlt; pExt->AltOccupation.reset(); } From 9b72053ede546f0b08004416efbd047459927ded Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 7 Mar 2025 19:45:06 +0300 Subject: [PATCH 093/112] Update YRpp --- YRpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YRpp b/YRpp index 49a4d22c4a..7d42296f57 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 49a4d22c4a628e37c98ed57ed976810ed528f793 +Subproject commit 7d42296f57d9e7e7ac17ef433626940a95cb7aaa From 4f1e9e692e934f3b45d44f4c10d76217c928d7f4 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 7 Mar 2025 19:48:43 +0300 Subject: [PATCH 094/112] Fix merge error --- src/Ext/TechnoType/Body.cpp | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index a3cbe71b83..795566492e 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -539,8 +539,6 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) this->NoAmmoWeapon.Read(exINI, pSection, "NoAmmoWeapon"); this->NoAmmoAmount.Read(exINI, pSection, "NoAmmoAmount"); - char tempBuffer[32]; - if (this->OwnerObject()->Gunner) { size_t weaponCount = this->OwnerObject()->WeaponCount; From 60edd0dd7bc34f3df972f8ce1c39fd6cdaf43fe2 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Fri, 7 Mar 2025 19:56:23 +0300 Subject: [PATCH 095/112] Revert YRpp --- YRpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/YRpp b/YRpp index 7d42296f57..49a4d22c4a 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 7d42296f57d9e7e7ac17ef433626940a95cb7aaa +Subproject commit 49a4d22c4a628e37c98ed57ed976810ed528f793 From f06ee537ac32c5cb88d2801dc5250d5ffbce37a9 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 10 Mar 2025 00:29:32 +0200 Subject: [PATCH 096/112] Fix inf. entry on cells with multiple units --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 32 +++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 8fda17d4ef..552d475aab 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -331,6 +331,38 @@ DEFINE_HOOK(0x73A5EA, UnitClass_PerCellProcess_EntryLoopTechnos, 0x0) return SkipEntry; } +DEFINE_HOOK(0x51A0DA, InfantryClass_PerCellProcess_EntryLoopTechnos, 0x0) +{ + enum { SkipEntry = 0x51A4BF, TryEnterTarget = 0x51A258 }; + + GET(InfantryClass*, pThis, ESI); + + if (pThis->GetCurrentMission() != Mission::Enter) + return SkipEntry; + + CellClass* pCell = pThis->GetCell(); + ObjectClass*& pFirst = pThis->OnBridge + ? pCell->AltObject : pCell->FirstObject; + + for (ObjectClass* pObject = pFirst; pObject; pObject = pObject->NextObject) + { + auto pEntryTarget = abstract_cast(pObject); + + // TODO additional priority checks (original code gets technos in certain order) because may backfire + + if (pEntryTarget && pEntryTarget != pThis + && (pThis->Target == pEntryTarget || pThis->Destination == pEntryTarget + || pThis->OnBridge && pCell == pEntryTarget->GetCell())) + { + R->EDI(pEntryTarget); + R->EBP(0); + return TryEnterTarget; + } + } + + return SkipEntry; +} + enum class AttachCargoMode { SingleObject, From 359b0832442ebacb3684732fff6e4f1e2498e6fc Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 10 Mar 2025 00:46:02 +0200 Subject: [PATCH 097/112] Switch to new function replacement macro --- src/Ext/Cell/Hooks.cpp | 4 +- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 52 +++++++++++------------ 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index 294222e8f0..e75e8dd534 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -33,7 +33,7 @@ void __fastcall UnitClass_SetOccupyBit_Reimpl(UnitClass* pThis, discard_t, Coord } } -DEFINE_JUMP(VTABLE, 0x7F5D60, GET_OFFSET(UnitClass_SetOccupyBit_Reimpl)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5D60, UnitClass_SetOccupyBit_Reimpl); void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, discard_t, CoordStruct* pCrd) { @@ -71,6 +71,6 @@ void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, discard_t, Coo } } -DEFINE_JUMP(VTABLE, 0x7F5D64, GET_OFFSET(UnitClass_ClearOccupyBit_Reimpl)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5D64, UnitClass_ClearOccupyBit_Reimpl); // TODO ^ same for TA for non-UnitClass, not needed so cba for now diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 552d475aab..6460c9017e 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -274,12 +274,12 @@ DEFINE_HOOK(0x47C432, CellClass_CellTechno_HandleAttachments, 0x0) } // skip building placement occupation checks for virtuals -DEFINE_JUMP(CALL, 0x47C805, GET_OFFSET(CellTechno_NoVirtual)); -DEFINE_JUMP(CALL, 0x47C738, GET_OFFSET(CellTechno_NoVirtual)); +DEFINE_FUNCTION_JUMP(CALL, 0x47C805, CellTechno_NoVirtual); +DEFINE_FUNCTION_JUMP(CALL, 0x47C738, CellTechno_NoVirtual); // skip building attachments in bib check -DEFINE_JUMP(CALL, 0x4495F2, GET_OFFSET(CellTechno_NoVirtualOrRelatives)); -DEFINE_JUMP(CALL, 0x44964E, GET_OFFSET(CellTechno_NoVirtualOrRelatives)); +DEFINE_FUNCTION_JUMP(CALL, 0x4495F2, CellTechno_NoVirtualOrRelatives); +DEFINE_FUNCTION_JUMP(CALL, 0x44964E, CellTechno_NoVirtualOrRelatives); DEFINE_HOOK(0x4495F7, BuildingClass_ClearFactoryBib_SkipCreatedUnitAttachments, 0x0) { @@ -387,8 +387,8 @@ void __fastcall CargoClass_Attach_##mode(PassengersClass* pThis, discard_t, Foot DEFINE_ATTACH_WRAPPER(SingleObject); DEFINE_ATTACH_WRAPPER(ObjectChain); -DEFINE_JUMP(CALL, 0x65DF88, GET_OFFSET(CargoClass_Attach_ObjectChain)); // Create_Group -DEFINE_JUMP(CALL, 0x65DCF0, GET_OFFSET(CargoClass_Attach_ObjectChain)); // Do_Reinforcements, paradrop loading +DEFINE_FUNCTION_JUMP(CALL, 0x65DF88, CargoClass_Attach_ObjectChain); // Create_Group +DEFINE_FUNCTION_JUMP(CALL, 0x65DCF0, CargoClass_Attach_ObjectChain); // Do_Reinforcements, paradrop loading DEFINE_HOOK(0x4733BD, CargoClass_Attach_HandleCurrentAttachMode, 0x6) { @@ -431,29 +431,29 @@ bool __fastcall TechnoClass_IsSurfaced(TechnoClass* pThis) } // TechnoClass -DEFINE_JUMP(VTABLE, 0x7F49B0, GET_OFFSET(TechnoClass_OnGround)); -DEFINE_JUMP(VTABLE, 0x7F49B4, GET_OFFSET(TechnoClass_InAir)); -DEFINE_JUMP(VTABLE, 0x7F49DC, GET_OFFSET(TechnoClass_IsSurfaced)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F49B0, TechnoClass_OnGround); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F49B4, TechnoClass_InAir); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F49DC, TechnoClass_IsSurfaced); // BuildingClass -DEFINE_JUMP(VTABLE, 0x7E3F0C, GET_OFFSET(TechnoClass_OnGround)); -DEFINE_JUMP(VTABLE, 0x7E3F10, GET_OFFSET(TechnoClass_InAir)); -DEFINE_JUMP(VTABLE, 0x7E3F38, GET_OFFSET(TechnoClass_IsSurfaced)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E3F0C, TechnoClass_OnGround); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E3F10, TechnoClass_InAir); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E3F38, TechnoClass_IsSurfaced); // FootClass -DEFINE_JUMP(VTABLE, 0x7E8CE4, GET_OFFSET(TechnoClass_OnGround)); -DEFINE_JUMP(VTABLE, 0x7E8CE8, GET_OFFSET(TechnoClass_InAir)); -DEFINE_JUMP(VTABLE, 0x7E8D10, GET_OFFSET(TechnoClass_IsSurfaced)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E8CE4, TechnoClass_OnGround); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E8CE8, TechnoClass_InAir); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E8D10, TechnoClass_IsSurfaced); // UnitClass -DEFINE_JUMP(VTABLE, 0x7F5CC0, GET_OFFSET(TechnoClass_OnGround)); -DEFINE_JUMP(VTABLE, 0x7F5CC4, GET_OFFSET(TechnoClass_InAir)); -DEFINE_JUMP(VTABLE, 0x7F5CEC, GET_OFFSET(TechnoClass_IsSurfaced)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5CC0, TechnoClass_OnGround); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5CC4, TechnoClass_InAir); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5CEC, TechnoClass_IsSurfaced); // InfantryClass -DEFINE_JUMP(VTABLE, 0x7EB0A8, GET_OFFSET(TechnoClass_OnGround)); -DEFINE_JUMP(VTABLE, 0x7EB0AC, GET_OFFSET(TechnoClass_InAir)); -DEFINE_JUMP(VTABLE, 0x7EB0D4, GET_OFFSET(TechnoClass_IsSurfaced)); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0A8, TechnoClass_OnGround); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0AC, TechnoClass_InAir); +DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0D4, TechnoClass_IsSurfaced); // AircraftClass has it's own logic, who would want to attach aircrafts anyways @@ -754,7 +754,7 @@ Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, C // MouseOverObject for entering bunkers, grinder, buildings etc // is handled along with the shield logics in another file -DEFINE_JUMP(VTABLE, 0x7F5CE0, GET_OFFSET(UnitClass_MouseOverCell_Wrapper)) +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5CE0, UnitClass_MouseOverCell_Wrapper) // YSort for attachments int __fastcall TechnoClass_SortY_Wrapper(ObjectClass* pThis) @@ -782,10 +782,10 @@ int __fastcall TechnoClass_SortY_Wrapper(ObjectClass* pThis) return pThis->ObjectClass::GetYSort(); } -DEFINE_JUMP(CALL, 0x449413, GET_OFFSET(TechnoClass_SortY_Wrapper)) // BuildingClass -DEFINE_JUMP(VTABLE, 0x7E235C, GET_OFFSET(TechnoClass_SortY_Wrapper)) // AircraftClass -DEFINE_JUMP(VTABLE, 0x7EB110, GET_OFFSET(TechnoClass_SortY_Wrapper)) // InfantryClass -DEFINE_JUMP(VTABLE, 0x7F5D28, GET_OFFSET(TechnoClass_SortY_Wrapper)) // UnitClass +DEFINE_FUNCTION_JUMP(CALL, 0x449413, TechnoClass_SortY_Wrapper) // BuildingClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E235C, TechnoClass_SortY_Wrapper) // AircraftClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB110, TechnoClass_SortY_Wrapper) // InfantryClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5D28, TechnoClass_SortY_Wrapper) // UnitClass DEFINE_JUMP(LJMP, 0x568831, 0x568841); // Skip locomotion layer check in MapClass::PickUp DEFINE_JUMP(LJMP, 0x4D37A2, 0x4D37AE); // Skip locomotion layer check in FootClass::Mark From 8ad63c14a75361f9627b40a55a0e28a1448d4506 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 10 Mar 2025 13:33:01 +0200 Subject: [PATCH 098/112] forgor to uncomment CheckMovingInto for inf --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 6460c9017e..fb836f1575 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -197,7 +197,7 @@ DEFINE_HOOK(0x51C251, InfantryClass_CanEnterCell_SkipChildren, 0x0) return IsOccupierIgnorable(pThis, pOccupier, occupyFlags, isVehicleFlagSet) ? IgnoreOccupier : Continue; } -/* + DEFINE_HOOK(0x51C78F, InfantryClass_CanEnterCell_CheckMovingInto, 0x6) { GET_STACK(CellClass*, into, STACK_OFFSET(0x34, 0x4)); @@ -211,7 +211,7 @@ DEFINE_HOOK(0x51C78F, InfantryClass_CanEnterCell_CheckMovingInto, 0x6) return 0; } -*/ + enum class CellTechnoMode { NoAttachments, From 5568d7ac08f2b7edab2e2fe64cc6c2d17c3109ba Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 10 Mar 2025 13:50:30 +0200 Subject: [PATCH 099/112] Fix stop/deploy command inheritance mixing up commands when enabled --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index fb836f1575..a436c141a6 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -590,10 +590,10 @@ DEFINE_HOOK(0x6FFE4F, TechnoClass_ClickedEvent_HandleChildren, 0x6) if (!pAttachment->Child) continue; - if (pAttachment->GetType()->InheritCommands_StopCommand) + if (TechnoAttachmentTemp::stopPressed && pAttachment->GetType()->InheritCommands_StopCommand) pAttachment->Child->ClickedEvent(EventType::Idle); - if (pAttachment->GetType()->InheritCommands_DeployCommand) + if (TechnoAttachmentTemp::deployPressed && pAttachment->GetType()->InheritCommands_DeployCommand) pAttachment->Child->ClickedEvent(EventType::Deploy); } } From 8ee88c8f6951e14f07d9165db3e3adf89a3020e8 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Fri, 25 Jul 2025 14:15:30 +0300 Subject: [PATCH 100/112] remove discard_t as unrelated to branch --- docs/Contributing.md | 2 +- src/Ext/Cell/Hooks.cpp | 4 ++-- src/Ext/Techno/Hooks.TargetEvaluation.cpp | 8 ++++---- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 6 +++--- src/Misc/Hooks.INIInheritance.cpp | 2 +- src/Utilities/Macro.h | 8 -------- 6 files changed, 11 insertions(+), 19 deletions(-) diff --git a/docs/Contributing.md b/docs/Contributing.md index d1cd6c9c2b..d2ace5e068 100644 --- a/docs/Contributing.md +++ b/docs/Contributing.md @@ -195,7 +195,7 @@ ExtData(TerrainTypeClass* OwnerObject) : Extension(OwnerObject - Class fields that can be set via INI tags should be named exactly like ini tags with dots replaced with underscores. - Pointer type declarations always have pointer sign `*` attached to the type declaration. - Non-static class extension methods faked by declaring a static method with `pThis` as a first argument are only to be placed in the extension class for the class instance of which `pThis` is. - - If it's crucial to fake `__thiscall` you may use `__fastcall` and use `discard_t` or `discard_t _` as a second argument to discard value passed through `EDX` register. Such methods are to be used for call replacement. + - If it's crucial to fake `__thiscall` you may use `__fastcall` and use `void*` or `void* _` as a second argument to discard value passed through `EDX` register. Such methods are to be used for call replacement. - Hooks have to be named using a following scheme: `HookedFunction_HookPurpose`, or `ClassName_HookedMethod_HookPurpose`. Defined-again hooks are exempt from this scheme due to impossibility to define different names for the same hook. - Return addresses should use anonymous enums to make it clear what address means what, if applicable. The enum has to be placed right at the function start and include all addresses that are used in this hook: ```cpp diff --git a/src/Ext/Cell/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index e75e8dd534..c7b1e625f5 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -5,7 +5,7 @@ // because 🦅💣 takes over, we have to do reimpl even more of the func and replicate Ares code -void __fastcall UnitClass_SetOccupyBit_Reimpl(UnitClass* pThis, discard_t, CoordStruct* pCrd) +void __fastcall UnitClass_SetOccupyBit_Reimpl(UnitClass* pThis, void*, CoordStruct* pCrd) { if (TechnoExt::DoesntOccupyCellAsChild(pThis)) return; @@ -35,7 +35,7 @@ void __fastcall UnitClass_SetOccupyBit_Reimpl(UnitClass* pThis, discard_t, Coord DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5D60, UnitClass_SetOccupyBit_Reimpl); -void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, discard_t, CoordStruct* pCrd) +void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, void*, CoordStruct* pCrd) { if (TechnoExt::DoesntOccupyCellAsChild(pThis)) return; diff --git a/src/Ext/Techno/Hooks.TargetEvaluation.cpp b/src/Ext/Techno/Hooks.TargetEvaluation.cpp index c1745878e8..b5c141f1f4 100644 --- a/src/Ext/Techno/Hooks.TargetEvaluation.cpp +++ b/src/Ext/Techno/Hooks.TargetEvaluation.cpp @@ -339,7 +339,7 @@ class AresScheme }; -FireError __fastcall UnitClass__GetFireError_Wrapper(UnitClass* pThis, discard_t _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) +FireError __fastcall UnitClass__GetFireError_Wrapper(UnitClass* pThis, void* _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) { AresScheme::Prefix(pThis, pObj, nWeaponIndex, false); auto const result = pThis->UnitClass::GetFireError(pObj, nWeaponIndex, ignoreRange); @@ -348,7 +348,7 @@ FireError __fastcall UnitClass__GetFireError_Wrapper(UnitClass* pThis, discard_t } DEFINE_FUNCTION_JUMP(VTABLE, 0x7F6030, UnitClass__GetFireError_Wrapper) -FireError __fastcall InfantryClass__GetFireError_Wrapper(InfantryClass* pThis, discard_t _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) +FireError __fastcall InfantryClass__GetFireError_Wrapper(InfantryClass* pThis, void* _, ObjectClass* pObj, int nWeaponIndex, bool ignoreRange) { AresScheme::Prefix(pThis, pObj, nWeaponIndex, false); auto const result = pThis->InfantryClass::GetFireError(pObj, nWeaponIndex, ignoreRange); @@ -357,7 +357,7 @@ FireError __fastcall InfantryClass__GetFireError_Wrapper(InfantryClass* pThis, d } DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB418, InfantryClass__GetFireError_Wrapper) -Action __fastcall UnitClass__WhatAction_Wrapper(UnitClass* pThis, discard_t _, ObjectClass* pObj, bool ignoreForce) +Action __fastcall UnitClass__WhatAction_Wrapper(UnitClass* pThis, void* _, ObjectClass* pObj, bool ignoreForce) { AresScheme::Prefix(pThis, pObj, -1, false); auto result = pThis->UnitClass::MouseOverObject(pObj, ignoreForce); @@ -395,7 +395,7 @@ Action __fastcall UnitClass__WhatAction_Wrapper(UnitClass* pThis, discard_t _, O } DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5CE4, UnitClass__WhatAction_Wrapper) -Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis, discard_t _, ObjectClass* pObj, bool ignoreForce) +Action __fastcall InfantryClass__WhatAction_Wrapper(InfantryClass* pThis, void* _, ObjectClass* pObj, bool ignoreForce) { AresScheme::Prefix(pThis, pObj, -1, pThis->Type->Engineer); auto const result = pThis->InfantryClass::MouseOverObject(pObj, ignoreForce); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index a436c141a6..1bc723666f 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -229,7 +229,7 @@ namespace TechnoAttachmentTemp } #define DEFINE_CELLTECHNO_WRAPPER(mode) \ -TechnoClass* __fastcall CellTechno_##mode(CellClass* pThis, discard_t, Point2D *a2, bool check_alt, TechnoClass* techno) \ +TechnoClass* __fastcall CellTechno_##mode(CellClass* pThis, void*, Point2D *a2, bool check_alt, TechnoClass* techno) \ { \ TechnoAttachmentTemp::currentMode = CellTechnoMode::mode; \ auto const retval = pThis->FindTechnoNearestTo(*a2, check_alt, techno); \ @@ -377,7 +377,7 @@ namespace TechnoAttachmentTemp } #define DEFINE_ATTACH_WRAPPER(mode) \ -void __fastcall CargoClass_Attach_##mode(PassengersClass* pThis, discard_t, FootClass* pThat) \ +void __fastcall CargoClass_Attach_##mode(PassengersClass* pThis, void*, FootClass* pThat) \ { \ TechnoAttachmentTemp::currentAttachMode = AttachCargoMode::mode; \ pThis->AddPassenger(pThat); \ @@ -726,7 +726,7 @@ DEFINE_HOOK(0x736A2F, UnitClass_RotationAI_ForbidAttachmentRotation, 0x7) : ContinueCheck; } -Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, discard_t, CellStruct const* pCell, bool checkFog, bool ignoreForce) +Action __fastcall UnitClass_MouseOverCell_Wrapper(UnitClass* pThis, void*, CellStruct const* pCell, bool checkFog, bool ignoreForce) { Action result = pThis->UnitClass::MouseOverCell(pCell, checkFog, ignoreForce); diff --git a/src/Misc/Hooks.INIInheritance.cpp b/src/Misc/Hooks.INIInheritance.cpp index a1cf959747..2acd934bfc 100644 --- a/src/Misc/Hooks.INIInheritance.cpp +++ b/src/Misc/Hooks.INIInheritance.cpp @@ -298,7 +298,7 @@ DEFINE_HOOK(0x527920, INIClass_ReadGUID_Overwrite, 0x5) // locomotor } // Fix issue with TilesInSet caused by incorrect vanilla INIs and the fixed parser returning correct default value (-1) instead of 0 for existing non-integer values -int __fastcall IsometricTileTypeClass_ReadINI_TilesInSet_Wrapper(INIClass* pThis, discard_t _, const char* pSection, const char* pKey, int defaultValue) +int __fastcall IsometricTileTypeClass_ReadINI_TilesInSet_Wrapper(INIClass* pThis, void* _, const char* pSection, const char* pKey, int defaultValue) { if (pThis->Exists(pSection, pKey)) return pThis->ReadInteger(pSection, pKey, 0); diff --git a/src/Utilities/Macro.h b/src/Utilities/Macro.h index abea38a805..a0bdefff07 100644 --- a/src/Utilities/Macro.h +++ b/src/Utilities/Macro.h @@ -3,14 +3,6 @@ #include #include "Patch.h" -// Use when some function argument is unneeded. -// Currently that happens when faking __thiscall functions -// via __fastcall ones (fastcall function accepts args via -// ECX, EDX, then stack, thiscall via ECX for this and stack -// for rest, so second arg in fastcall-faked function would -// need to be discarded). -typedef size_t discard_t; - #define GET_REGISTER_STATIC_TYPE(type, dst, reg) static type dst; _asm { mov dst, reg } template From b9621f8808c636afd8db768762e0af84d3595ece Mon Sep 17 00:00:00 2001 From: Metadorius Date: Fri, 25 Jul 2025 14:22:37 +0300 Subject: [PATCH 101/112] fix table in docs --- docs/Miscellanous.md | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/docs/Miscellanous.md b/docs/Miscellanous.md index bda2ef6c38..44026ebb8c 100644 --- a/docs/Miscellanous.md +++ b/docs/Miscellanous.md @@ -47,19 +47,19 @@ SaveVariablesOnScenarioEnd=false ; boolean - It's now possible to write locomotor aliases instead of their CLSIDs in the `Locomotor` tag value. Use the table below to find the needed alias for a locomotor. - The feature is also supported for Phobos locomotors. -| *Alias* | *CLSID* | -|--------:|:----------------------------------------:| -|Drive | `{4A582741-9839-11d1-B709-00A024DDAFD1}` | -|Hover | `{4A582742-9839-11d1-B709-00A024DDAFD1}` | -|Tunnel | `{4A582743-9839-11d1-B709-00A024DDAFD1}` | -|Walk | `{4A582744-9839-11d1-B709-00A024DDAFD1}` | -|DropPod | `{4A582745-9839-11d1-B709-00A024DDAFD1}` | -|Fly | `{4A582746-9839-11d1-B709-00A024DDAFD1}` | -|Teleport | `{4A582747-9839-11d1-B709-00A024DDAFD1}` | -|Mech | `{55D141B8-DB94-11d1-AC98-006008055BB5}` | -|Ship | `{2BEA74E1-7CCA-11d3-BE14-00104B62A16C}` | -|Jumpjet | `{92612C46-F71F-11d1-AC9F-006008055BB5}` | -|Rocket | `{B7B49766-E576-11d3-9BD9-00104B972FE8}` | +| *Alias* | *CLSID* | +|----------:|:----------------------------------------:| +|Drive | `{4A582741-9839-11d1-B709-00A024DDAFD1}` | +|Hover | `{4A582742-9839-11d1-B709-00A024DDAFD1}` | +|Tunnel | `{4A582743-9839-11d1-B709-00A024DDAFD1}` | +|Walk | `{4A582744-9839-11d1-B709-00A024DDAFD1}` | +|DropPod | `{4A582745-9839-11d1-B709-00A024DDAFD1}` | +|Fly | `{4A582746-9839-11d1-B709-00A024DDAFD1}` | +|Teleport | `{4A582747-9839-11d1-B709-00A024DDAFD1}` | +|Mech | `{55D141B8-DB94-11d1-AC98-006008055BB5}` | +|Ship | `{2BEA74E1-7CCA-11d3-BE14-00104B62A16C}` | +|Jumpjet | `{92612C46-F71F-11d1-AC9F-006008055BB5}` | +|Rocket | `{B7B49766-E576-11d3-9BD9-00104B972FE8}` | |Attachment | `{C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E}` | ```{note} From 4121f3a74ce76995e54c320dbb10dd7c159e5f66 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Fri, 25 Jul 2025 18:39:02 +0300 Subject: [PATCH 102/112] Fix compilation; remove more unneeded changes --- Phobos.vcxproj | 4 +- docs/Whats-New.md | 10 +--- src/Ext/Cell/Body.cpp | 18 +++++-- src/Ext/Cell/Body.h | 15 +----- src/Ext/Cell/Hooks.cpp | 8 +-- src/Ext/Techno/Body.Internal.cpp | 48 ++++++------------ src/Ext/Techno/Body.cpp | 5 +- src/Ext/Techno/Body.h | 12 +---- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 4 +- src/Ext/TechnoType/Body.cpp | 3 +- src/Ext/Unit/Hooks.DisallowMoving.cpp | 4 +- src/Ext/WeaponType/Body.cpp | 3 -- src/Locomotion/AttachmentLocomotionClass.cpp | 14 +++--- src/Misc/Hooks.BugFixes.cpp | 1 - src/Misc/Selection.cpp | 52 +++++++++----------- src/New/Entity/AttachmentClass.cpp | 6 +-- src/Phobos.COM.h | 2 +- src/Phobos.Ext.cpp | 2 +- src/Utilities/Enum.h | 1 - 19 files changed, 83 insertions(+), 129 deletions(-) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index ae0d2c0634..923dde9a5a 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -29,6 +29,7 @@ + @@ -63,8 +64,6 @@ - - @@ -258,7 +257,6 @@ - diff --git a/docs/Whats-New.md b/docs/Whats-New.md index ad93d0650b..85af387370 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -322,15 +322,6 @@ HideLightFlashEffects=false ; boolean ## Changelog -### 0.4 - -
- Click to show - -New: -- Techno Attachment logic (by Kerbiter) -
- ### Version TBD (develop branch nightly builds) ```{dropdown} Click to show @@ -434,6 +425,7 @@ New: - [Damaged aircraft image changes](New-or-Enhanced-Logics.md#damaged-aircraft-image-changes) (by Fryone) - [Additional attached animation position customizations](Fixed-or-Improved-Logics.md#attached-animation-position-customization) (by Starkku) - Use `SkipCrushSlowdown=true` to avoid the bug related to `Accelerates=true` and `MovementZone=CrushAll` (by TaranDahl) +- [Techno attachment system](New-or-Enhanced-Logics.md#attachments) (by Kerbiter) Vanilla fixes: - Fixed sidebar not updating queued unit numbers when adding or removing units when the production is on hold (by CrimRecya) diff --git a/src/Ext/Cell/Body.cpp b/src/Ext/Cell/Body.cpp index 21f9fb5d12..b53771d3dd 100644 --- a/src/Ext/Cell/Body.cpp +++ b/src/Ext/Cell/Body.cpp @@ -15,8 +15,8 @@ void CellExt::ExtData::Serialize(T& Stm) Stm .Process(this->RadSites) .Process(this->RadLevels) - .Process(IncomingUnit) - .Process(IncomingUnitAlt) + .Process(this->IncomingUnit) + .Process(this->IncomingUnitAlt) ; } @@ -33,7 +33,19 @@ void CellExt::ExtData::SaveToStream(PhobosStreamWriter& Stm) } void CellExt::ExtData::InvalidatePointer(void* ptr, bool removed) -{ } +{ + if (ptr == static_cast(this->IncomingUnit)) + { + this->OwnerObject()->OccupationFlags &= ~0x20; + this->IncomingUnit = nullptr; + } + + if (ptr == static_cast(this->IncomingUnitAlt)) + { + this->OwnerObject()->AltOccupationFlags &= ~0x20; + this->IncomingUnitAlt = nullptr; + } +} bool CellExt::RadLevel::Load(PhobosStreamReader& stm, bool registerForChange) { diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 750b5748e5..d3ae147623 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -43,20 +43,7 @@ class CellExt virtual ~ExtData() = default; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override - { - if (ptr == static_cast(this->IncomingUnit)) - { - this->OwnerObject()->OccupationFlags &= ~0x20; - this->IncomingUnit = nullptr; - } - - if (ptr == static_cast(this->IncomingUnitAlt)) - { - this->OwnerObject()->AltOccupationFlags &= ~0x20; - this->IncomingUnitAlt = nullptr; - } - } + 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/Hooks.cpp b/src/Ext/Cell/Hooks.cpp index c7b1e625f5..f13afb818a 100644 --- a/src/Ext/Cell/Hooks.cpp +++ b/src/Ext/Cell/Hooks.cpp @@ -10,9 +10,9 @@ void __fastcall UnitClass_SetOccupyBit_Reimpl(UnitClass* pThis, void*, CoordStru if (TechnoExt::DoesntOccupyCellAsChild(pThis)) return; - CellClass* pCell = MapClass::Instance->GetCellAt(*pCrd); + CellClass* pCell = MapClass::Instance.GetCellAt(*pCrd); auto pCellExt = CellExt::ExtMap.Find(pCell); - int height = MapClass::Instance->GetCellFloorHeight(*pCrd) + CellClass::BridgeHeight; + int height = MapClass::Instance.GetCellFloorHeight(*pCrd) + CellClass::BridgeHeight; bool alt = (pCrd->Z >= height && pCell->ContainsBridge()); // remember which occupation bit we set @@ -42,9 +42,9 @@ void __fastcall UnitClass_ClearOccupyBit_Reimpl(UnitClass* pThis, void*, CoordSt enum { obNormal = 1, obAlt = 2 }; - CellClass* pCell = MapClass::Instance->GetCellAt(*pCrd); + CellClass* pCell = MapClass::Instance.GetCellAt(*pCrd); auto pCellExt = CellExt::ExtMap.Find(pCell); - int height = MapClass::Instance->GetCellFloorHeight(*pCrd) + CellClass::BridgeHeight; + int height = MapClass::Instance.GetCellFloorHeight(*pCrd) + CellClass::BridgeHeight; int alt = (pCrd->Z >= height) ? obAlt : obNormal; // also clear the last occupation bit, if set diff --git a/src/Ext/Techno/Body.Internal.cpp b/src/Ext/Techno/Body.Internal.cpp index 95bacae751..7f3456da7c 100644 --- a/src/Ext/Techno/Body.Internal.cpp +++ b/src/Ext/Techno/Body.Internal.cpp @@ -1,5 +1,7 @@ #include "Body.h" + #include + #include // Unsorted methods @@ -32,26 +34,23 @@ void TechnoExt::ObjectKilledBy(TechnoClass* pVictim, TechnoClass* pKiller) } } -Matrix3D TechnoExt::GetTransform(TechnoClass* pThis, VoxelIndexKey* pKey, bool isShadow) +// reversed from 6F3D60 +CoordStruct TechnoExt::GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret) { + auto const pType = pThis->GetTechnoType(); + auto const pFoot = abstract_cast(pThis); Matrix3D mtx; - if ((pThis->AbstractFlags & AbstractFlags::Foot) && ((FootClass*)pThis)->Locomotor) - mtx = isShadow ? ((FootClass*)pThis)->Locomotor->Shadow_Matrix(pKey) : ((FootClass*)pThis)->Locomotor->Draw_Matrix(pKey); + // Step 1: get body transform matrix + if (pFoot && pFoot->Locomotor) + mtx = pFoot->Locomotor->Draw_Matrix(nullptr); else // no locomotor means no rotation or transform of any kind (f.ex. buildings) - Kerbiter mtx.MakeIdentity(); - return mtx; -} - -Matrix3D TechnoExt::TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret, double factor) -{ - auto const pType = pThis->GetTechnoType(); - - // turret offset and rotation - if (isOnTurret && pThis->HasTurret()) + // Steps 2-3: turret offset and rotation + if (isOnTurret && (pType->Turret || !pFoot)) // If building has no turret, it's TurretFacing is TargetDirection { - TechnoTypeExt::ApplyTurretOffset(pType, &mtx, factor); + TechnoTypeExt::ApplyTurretOffset(pType, &mtx); double turretRad = pThis->TurretFacing().GetRadian<32>(); // For BuildingClass turret facing is equal to primary facing @@ -60,27 +59,12 @@ Matrix3D TechnoExt::TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool mtx.RotateZ(angle); } - return mtx; -} - -Matrix3D TechnoExt::GetFLHMatrix(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret, double factor, bool isShadow) -{ - Matrix3D transform = TechnoExt::GetTransform(pThis, nullptr, isShadow); - Matrix3D mtx = TechnoExt::TransformFLHForTurret(pThis, transform, isOnTurret, factor); + // Step 4: apply FLH offset + mtx.Translate((float)pCoord.X, (float)pCoord.Y, (float)pCoord.Z); - CoordStruct scaledCoord = pCoord * factor; - // apply FLH offset - mtx.Translate((float)scaledCoord.X, (float)scaledCoord.Y, (float)scaledCoord.Z); - - return mtx; -} - -// reversed from 6F3D60 -CoordStruct TechnoExt::GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct pCoord, bool isOnTurret) -{ - auto result = TechnoExt::GetFLHMatrix(pThis, pCoord, isOnTurret).GetTranslation(); + auto result = mtx.GetTranslation(); - // apply as an offset to global object coords + // Step 5: apply as an offset to global object coords // Resulting coords are mirrored along X axis, so we mirror it back auto location = pThis->GetRenderCoords() + CoordStruct { (int)result.X, -(int)result.Y, (int)result.Z }; diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 6597dc2b0a..7437cc4339 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -11,8 +11,6 @@ #include -#include - TechnoExt::ExtContainer TechnoExt::ExtMap; UnitClass* TechnoExt::Deployer = nullptr; @@ -926,6 +924,9 @@ void TechnoExt::ExtData::Serialize(T& Stm) void TechnoExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) { AnnounceInvalidPointer(this->AirstrikeTargetingMe, ptr); + + for (auto const& pAttachment : ChildAttachments) + pAttachment->InvalidatePointer(ptr); } void TechnoExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index de5b961161..5fbdd8d414 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -196,12 +196,6 @@ class TechnoExt virtual ~ExtData() override; virtual void InvalidatePointer(void* ptr, bool bRemoved) override; - virtual void InvalidatePointer(void* ptr, bool bRemoved) override - { - for (auto const& pAttachment : ChildAttachments) - pAttachment->InvalidatePointer(ptr); - } - virtual void LoadFromStream(PhobosStreamReader& Stm) override; virtual void SaveToStream(PhobosStreamWriter& Stm) override; @@ -244,13 +238,9 @@ class TechnoExt static bool HasAvailableDock(TechnoClass* pThis); static bool HasRadioLinkWithDock(TechnoClass* pThis); - static void InitializeLaserTrails(TechnoClass* pThis); + static CoordStruct GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct flh, bool turretFLH = false); static void InitializeShield(TechnoClass* pThis); - static Matrix3D GetTransform(TechnoClass* pThis, VoxelIndexKey* pKey = nullptr, bool isShadow = false); - static Matrix3D GetFLHMatrix(TechnoClass* pThis, CoordStruct flh, bool isOnTurret, double factor = 1.0, bool isShadow = false); - static Matrix3D TransformFLHForTurret(TechnoClass* pThis, Matrix3D mtx, bool isOnTurret, double factor = 1.0); - static CoordStruct GetFLHAbsoluteCoords(TechnoClass* pThis, CoordStruct flh, bool isOnTurret = false); static CoordStruct GetBurstFLH(TechnoClass* pThis, int weaponIndex, bool& FLHFound); static CoordStruct GetSimpleFLH(InfantryClass* pThis, int weaponIndex, bool& FLHFound); diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 1bc723666f..029ced6b65 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -521,7 +521,7 @@ DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) REF_STACK(int, idxPath, STACK_OFFSET(0x18, -0x8)); REF_STACK(unsigned char, idxWP, STACK_OFFSET(0x18, -0xC)); - for (auto const& pObject : ObjectClass::CurrentObjects()) + for (auto const& pObject : ObjectClass::CurrentObjects) { if (auto pTechno = abstract_cast(pObject)) ParentClickedWaypoint(pTechno, idxPath, idxWP); @@ -537,7 +537,7 @@ DEFINE_HOOK(0x4AE7B3, DisplayClass_ActiveClickWith_Iterate, 0x0) if (action == Action::Move || action == Action::PatrolWaypoint || action == Action::NoMove) pSecondCell = pCell; - for (auto const& pObject : ObjectClass::CurrentObjects()) + for (auto const& pObject : ObjectClass::CurrentObjects) { if (auto pTechno = abstract_cast(pObject)) ParentClickedAction(pTechno, pTarget, pCell, pSecondCell); diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index b20f385315..ab301167da 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -665,6 +665,7 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) // vector contents can be properly overriden via scenario rules - Kerbiter for (size_t i = 0; i <= this->AttachmentData.size(); ++i) { + char tempBuffer[32]; NullableIdx type; _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.Type", i); type.Read(exINI, pSection, tempBuffer); @@ -1299,8 +1300,8 @@ void TechnoTypeExt::ExtData::Serialize(T& Stm) .Process(this->OreGathering_Anims) .Process(this->OreGathering_Tiberiums) .Process(this->OreGathering_FramesPerDir) - .Process(this->DestroyAnim_Random) .Process(this->LaserTrailData) + .Process(this->DestroyAnim_Random) .Process(this->NotHuman_RandomDeathSequence) .Process(this->DefaultDisguise) .Process(this->UseDisguiseMovementSpeed) diff --git a/src/Ext/Unit/Hooks.DisallowMoving.cpp b/src/Ext/Unit/Hooks.DisallowMoving.cpp index 5b14e7d079..6f74a0a2f4 100644 --- a/src/Ext/Unit/Hooks.DisallowMoving.cpp +++ b/src/Ext/Unit/Hooks.DisallowMoving.cpp @@ -100,13 +100,11 @@ DEFINE_HOOK(0x740744, UnitClass_What_Action_DisallowMoving_2, 0x6) return 0; } -// Makes the vehicle keep it's current turret heading without snapping back to neutral position DEFINE_HOOK(0x736B60, UnitClass_Rotation_AI_DisallowMoving, 0x6) { GET(UnitClass*, pThis, ESI); - return pThis->Type->Speed == 0 - ? 0x736AFB : 0; + return pThis->Type->Speed == 0 ? 0x736AFB : 0; } DEFINE_HOOK(0x73891D, UnitClass_Active_Click_With_DisallowMoving, 0x6) diff --git a/src/Ext/WeaponType/Body.cpp b/src/Ext/WeaponType/Body.cpp index eb55aa0552..ad52bfbee9 100644 --- a/src/Ext/WeaponType/Body.cpp +++ b/src/Ext/WeaponType/Body.cpp @@ -3,9 +3,6 @@ #include #include -#include -#include - WeaponTypeExt::ExtContainer WeaponTypeExt::ExtMap; bool WeaponTypeExt::ExtData::HasRequiredAttachedEffects(TechnoClass* pTarget, TechnoClass* pFirer) const diff --git a/src/Locomotion/AttachmentLocomotionClass.cpp b/src/Locomotion/AttachmentLocomotionClass.cpp index 5897e16276..4c2a02b8e9 100644 --- a/src/Locomotion/AttachmentLocomotionClass.cpp +++ b/src/Locomotion/AttachmentLocomotionClass.cpp @@ -101,16 +101,16 @@ bool AttachmentLocomotionClass::Process() if (oldLayer != newLayer) { - DisplayClass::Instance->Submit(this->LinkedTo); + DisplayClass::Instance.Submit(this->LinkedTo); if (oldLayer < Layer::Air && Layer::Air <= newLayer) { - AircraftTrackerClass::Instance->Add(this->LinkedTo); + AircraftTrackerClass::Instance.Add(this->LinkedTo); changedAirborneStatus = true; } else if (newLayer < Layer::Air && Layer::Air <= oldLayer) { - AircraftTrackerClass::Instance->Remove(this->LinkedTo); + AircraftTrackerClass::Instance.Remove(this->LinkedTo); changedAirborneStatus = true; } @@ -123,7 +123,7 @@ bool AttachmentLocomotionClass::Process() if (oldPos != newPos) { if (Layer::Air <= newLayer && !changedAirborneStatus) - AircraftTrackerClass::Instance->Update(this->LinkedTo, oldPos, newPos); + AircraftTrackerClass::Instance.Update(this->LinkedTo, oldPos, newPos); if (this->LinkedTo->GetTechnoType()->SensorsSight) { @@ -240,7 +240,7 @@ HRESULT AttachmentLocomotionClass::Begin_Piggyback(ILocomotion* pointer) // since LinkedTo may've been managed by AircraftTracker before we need to remove the AircraftTracker entry if (this->LinkedTo && this->LinkedTo->GetLastFlightMapCoords() != CellStruct::Empty) - AircraftTrackerClass::Instance->Remove(this->LinkedTo); + AircraftTrackerClass::Instance.Remove(this->LinkedTo); this->Piggybacker = pointer; @@ -257,7 +257,7 @@ HRESULT AttachmentLocomotionClass::End_Piggyback(ILocomotion** pointer) // since LinkedTo may no longer be considered airborne we need to remove the AircraftTracker entry if (this->LinkedTo && this->LinkedTo->GetLastFlightMapCoords() != CellStruct::Empty) - AircraftTrackerClass::Instance->Remove(this->LinkedTo); + AircraftTrackerClass::Instance.Remove(this->LinkedTo); // since pointer is a dumb pointer, we don't need to call Release, // hence we use Detach, otherwise the locomotor gets trashed @@ -373,6 +373,6 @@ Layer AttachmentLocomotionClass::CalculateLayer() bool AttachmentLocomotionClass::ShouldBeOnBridge() { - return MapClass::Instance->GetCellAt(this->LinkedTo->Location)->ContainsBridge() + return MapClass::Instance.GetCellAt(this->LinkedTo->Location)->ContainsBridge() && this->LinkedTo->GetHeight() >= CellClass::BridgeHeight && !this->LinkedTo->IsFallingDown; } diff --git a/src/Misc/Hooks.BugFixes.cpp b/src/Misc/Hooks.BugFixes.cpp index b87d50e57a..f07d34d587 100644 --- a/src/Misc/Hooks.BugFixes.cpp +++ b/src/Misc/Hooks.BugFixes.cpp @@ -21,7 +21,6 @@ #include #include #include -#include #include #include diff --git a/src/Misc/Selection.cpp b/src/Misc/Selection.cpp index d02318e084..6b614ada93 100644 --- a/src/Misc/Selection.cpp +++ b/src/Misc/Selection.cpp @@ -1,10 +1,7 @@ #include "Phobos.h" -#include - -#include -#include -#include -#include +#include "Utilities/Macro.h" +#include "Ext/Techno/Body.h" +#include "Ext/TechnoType/Body.h" #include #include @@ -56,8 +53,6 @@ class ExtSelection { return true; } - return (nLocalX >= pRect->Left && nLocalX < pRect->Right + pRect->Left) && - (nLocalY >= pRect->Top && nLocalY < pRect->Bottom + pRect->Top); } return false; } @@ -65,12 +60,11 @@ class ExtSelection static bool Tactical_IsHighPriorityInRect(TacticalClass* pThis, LTRBStruct* rect) { for (const auto& selected : Array) - { if (Tactical_IsInSelectionRect(pThis, rect, selected) && ObjectClass_IsSelectable(selected.Object)) { if ((selected.Object->AbstractFlags & AbstractFlags::Techno) != AbstractFlags::None) { - auto const& pExt = TechnoExt::ExtMap.Find(selected.Object); + auto const& pExt = TechnoExt::ExtMap.Find(static_cast(selected.Object)); auto const& pTypeExt = TechnoTypeExt::ExtMap.Find(selected.Object->GetTechnoType()); bool isLowPriorityByAttachment = pExt->ParentAttachment && pExt->ParentAttachment->GetType()->LowSelectionPriority; @@ -78,13 +72,12 @@ class ExtSelection return true; } } - } return false; } - static // Reversed from Tactical::Select - void Tactical_SelectFiltered(TacticalClass* pThis, LTRBStruct* pRect, callback_type fpCheckCallback, bool bFilter) + // Reversed from Tactical::Select + static void Tactical_SelectFiltered(TacticalClass* pThis, LTRBStruct* pRect, callback_type check_callback, bool bPriorityFiltering) { Unsorted::MoveFeedback = true; @@ -95,29 +88,32 @@ class ExtSelection { if (Tactical_IsInSelectionRect(pThis, pRect, selected)) { - auto const& pObject = selected.Object; + const auto pObject = selected.Object; TechnoTypeClass* pTechnoType = pObject->GetTechnoType(); // Returns nullptr on non techno objects if (auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pTechnoType)) // If pTechnoType is nullptr so will be pTypeExt { - auto const& pExt = TechnoExt::ExtMap.Find(pObject); - - // Attached units shouldn't be selected regardless of the setting - bool isLowPriorityByAttachment = pExt && pExt->ParentAttachment && pExt->ParentAttachment->GetType()->LowSelectionPriority; - bool isLowPriorityByTechno = Phobos::Config::PrioritySelectionFiltering && pTypeExt && pTypeExt->LowSelectionPriority; + if (bPriorityFiltering) + { + auto const& pExt = TechnoExt::ExtMap.Find(static_cast(pObject)); + // Attached units shouldn't be selected regardless of the setting + bool isLowPriorityByAttachment = pExt->ParentAttachment && pExt->ParentAttachment->GetType()->LowSelectionPriority; + bool isLowPriorityByTechno = Phobos::Config::PrioritySelectionFiltering && pTypeExt->LowSelectionPriority; - if (bFilter && (isLowPriorityByAttachment || isLowPriorityByTechno)) - continue; + if (isLowPriorityByAttachment || isLowPriorityByTechno) + continue; + } - if (pTypeExt && Game::IsTypeSelecting()) + if (Game::IsTypeSelecting()) { Game::UICommands_TypeSelect_7327D0(pTypeExt->GetSelectionGroupID()); + continue; } } - else if (fpCheckCallback) + if (check_callback) { - (*fpCheckCallback)(pObject); + (*check_callback)(pObject); } else { @@ -136,8 +132,8 @@ class ExtSelection Unsorted::MoveFeedback = true; } - static // Reversed from Tactical::MakeSelection - void __fastcall Tactical_MakeFilteredSelection(TacticalClass* pThis, void*_, callback_type fpCheckCallback) + // Reversed from Tactical::MakeSelection + static void __fastcall Tactical_MakeFilteredSelection(TacticalClass* pThis, void* _, callback_type check_callback) { if (pThis->Band.Left || pThis->Band.Top) { @@ -153,8 +149,8 @@ class ExtSelection LTRBStruct rect { nLeft , nTop, nRight - nLeft + 1, nBottom - nTop + 1 }; - Tactical_SelectFiltered(pThis, &rect, fpCheckCallback, - Tactical_IsHighPriorityInRect(pThis, &rect)); + bool bPriorityFiltering = Tactical_IsHighPriorityInRect(pThis, &rect); + Tactical_SelectFiltered(pThis, &rect, check_callback, bPriorityFiltering); pThis->Band.Left = 0; pThis->Band.Top = 0; diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 69d1c0cc4c..391cda8d3c 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -20,7 +20,7 @@ AttachmentTypeClass* AttachmentClass::GetType() TechnoTypeClass* AttachmentClass::GetChildType() { return this->Data->TechnoType.isset() - ? TechnoTypeClass::Array()->GetItem(this->Data->TechnoType) + ? TechnoTypeClass::Array[this->Data->TechnoType] : nullptr; } @@ -197,9 +197,9 @@ void AttachmentClass::Unlimbo() childDir.Raw += DirStruct(this->Data->RotationAdjust).Raw; // overflow = free modulo for rotation - ++Unsorted::IKnowWhatImDoing; + ++Unsorted::ScenarioInit; this->Child->Unlimbo(childCoord, childDir.GetDir()); - --Unsorted::IKnowWhatImDoing; + --Unsorted::ScenarioInit; } } diff --git a/src/Phobos.COM.h b/src/Phobos.COM.h index ef98e297fe..6838b4a3cf 100644 --- a/src/Phobos.COM.h +++ b/src/Phobos.COM.h @@ -13,7 +13,7 @@ void RegisterFactoryForClass(IClassFactory* pFactory) else Debug::Log("Class factory for %s registered.\n", typeid(T).name()); - Game::COMClasses->AddItem((ULONG)dwRegister); + Game::COMClasses.AddItem((ULONG)dwRegister); } // Registers an automatically created factory for a class. diff --git a/src/Phobos.Ext.cpp b/src/Phobos.Ext.cpp index 7149dfd998..7d2c54331a 100644 --- a/src/Phobos.Ext.cpp +++ b/src/Phobos.Ext.cpp @@ -242,7 +242,7 @@ using PhobosTypeRegistry = TypeRegistry < AttachEffectTypeClass, AttachEffectClass, NewSWType, - SelectBoxTypeClass + SelectBoxTypeClass, AttachEffectClass, AttachmentClass, AttachmentTypeClass diff --git a/src/Utilities/Enum.h b/src/Utilities/Enum.h index 4e878c7a2f..895b2d4101 100644 --- a/src/Utilities/Enum.h +++ b/src/Utilities/Enum.h @@ -32,7 +32,6 @@ #pragma once -#include #include enum class AttachedAnimFlag From bece7a85a02408619d7318a4edfee7b4d376171d Mon Sep 17 00:00:00 2001 From: Metadorius Date: Fri, 25 Jul 2025 21:18:45 +0300 Subject: [PATCH 103/112] Fix parsed DirType beign rotated by 1 if negative Co-authored-by: CrimRecya <335958461@qq.com> --- src/Utilities/TemplateDef.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Utilities/TemplateDef.h b/src/Utilities/TemplateDef.h index 3fb0b97c4b..525251c942 100644 --- a/src/Utilities/TemplateDef.h +++ b/src/Utilities/TemplateDef.h @@ -526,7 +526,7 @@ namespace detail if ((int)DirType::North <= absValue && absValue <= (int)DirType::Max) { - value = static_cast(!isNegative ? absValue : (int)DirType::Max - absValue); + value = static_cast(!isNegative ? absValue : (int)DirType::Max + 1 - absValue); return true; } else From 959c88b8d89269a43abbc08634c1a740a5665608 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Sun, 27 Jul 2025 00:05:41 +0300 Subject: [PATCH 104/112] Re-add Hooks.AI.cpp to compilation --- Phobos.vcxproj | 1 + 1 file changed, 1 insertion(+) diff --git a/Phobos.vcxproj b/Phobos.vcxproj index 923dde9a5a..08baf4ddaf 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -180,6 +180,7 @@ + From 0799bedd3c377193ee2e6af27b94658c6e543a52 Mon Sep 17 00:00:00 2001 From: ayylmaoRotE Date: Sat, 26 Jul 2025 20:58:00 +1000 Subject: [PATCH 105/112] Fix unit stuck in factory bug --- src/Ext/Building/Body.cpp | 4 ++++ src/Ext/Building/Hooks.cpp | 1 + 2 files changed, 5 insertions(+) diff --git a/src/Ext/Building/Body.cpp b/src/Ext/Building/Body.cpp index 423df95a92..aedca705f6 100644 --- a/src/Ext/Building/Body.cpp +++ b/src/Ext/Building/Body.cpp @@ -379,6 +379,10 @@ void BuildingExt::KickOutStuckUnits(BuildingClass* pThis) if (pThis->Owner != pUnit->Owner || pUnit->IsTether) continue; + // Skip attachments - they shouldn't be treated as stuck units + if (TechnoExt::IsAttached(pUnit)) + continue; + const auto height = pUnit->GetHeight(); if (height < 0 || height > Unsorted::CellHeight) diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 2d3169244c..91b8c60534 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -716,6 +716,7 @@ DEFINE_HOOK(0x44EFD8, BuildingClass_FindExitCell_BarracksExitCell, 0x6) return 0; } + DEFINE_HOOK(0x444B83, BuildingClass_ExitObject_BarracksExitCell, 0x7) { enum { SkipGameCode = 0x444C7C }; From bf49892c66a818eeeaa702a1f3cab5b07a71b4ea Mon Sep 17 00:00:00 2001 From: Metadorius Date: Thu, 4 Dec 2025 00:04:46 +0200 Subject: [PATCH 106/112] Revert to vanilla behavior for CargoClass::Attach, re-patch the usages to fix a number of discrepancies/bugs --- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 029ced6b65..09c7bfea0a 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -368,7 +368,7 @@ enum class AttachCargoMode SingleObject, ObjectChain, - DefaultBehavior = SingleObject, + DefaultBehavior = ObjectChain, }; namespace TechnoAttachmentTemp @@ -387,8 +387,11 @@ void __fastcall CargoClass_Attach_##mode(PassengersClass* pThis, void*, FootClas DEFINE_ATTACH_WRAPPER(SingleObject); DEFINE_ATTACH_WRAPPER(ObjectChain); -DEFINE_FUNCTION_JUMP(CALL, 0x65DF88, CargoClass_Attach_ObjectChain); // Create_Group -DEFINE_FUNCTION_JUMP(CALL, 0x65DCF0, CargoClass_Attach_ObjectChain); // Do_Reinforcements, paradrop loading +DEFINE_FUNCTION_JUMP(CALL, 0x41729E, CargoClass_Attach_SingleObject); // AircraftClass::MissionMoveCarryall +DEFINE_FUNCTION_JUMP(CALL, 0x41A048, CargoClass_Attach_SingleObject); // AircraftClass::MissionEnter +DEFINE_FUNCTION_JUMP(CALL, 0x51A38A, CargoClass_Attach_SingleObject); // InfrantryClass::PerCellProcess +DEFINE_FUNCTION_JUMP(CALL, 0x710682, CargoClass_Attach_SingleObject); // TechnoClass::AttachCargo +DEFINE_FUNCTION_JUMP(CALL, 0x73A2E4, CargoClass_Attach_SingleObject); // UnitClass::PerCellProcess DEFINE_HOOK(0x4733BD, CargoClass_Attach_HandleCurrentAttachMode, 0x6) { From 8a05b928346269ab8fb7a48457e2206b13ad2d66 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Thu, 4 Dec 2025 01:39:59 +0200 Subject: [PATCH 107/112] init incoming unit fields with default values (nullptr) --- src/Ext/Cell/Body.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Ext/Cell/Body.h b/src/Ext/Cell/Body.h index 3547540c16..3b037826da 100644 --- a/src/Ext/Cell/Body.h +++ b/src/Ext/Cell/Body.h @@ -37,8 +37,8 @@ class CellExt std::vector RadSites {}; std::vector RadLevels { }; int InfantryCount{ 0 }; - UnitClass* IncomingUnit; - UnitClass* IncomingUnitAlt; + UnitClass* IncomingUnit {}; + UnitClass* IncomingUnitAlt {}; ExtData(CellClass* OwnerObject) : Extension(OwnerObject) { } From 91c8ed0085ff55fa7c817da2d7f300c8e2f16440 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Thu, 4 Dec 2025 18:17:11 +0200 Subject: [PATCH 108/112] Split techno functions to another file for TA --- Phobos.vcxproj | 1 + src/Ext/Techno/Body.TechnoAttachment.cpp | 135 +++++++++++++++++++++++ src/Ext/Techno/Body.cpp | 132 ---------------------- 3 files changed, 136 insertions(+), 132 deletions(-) create mode 100644 src/Ext/Techno/Body.TechnoAttachment.cpp diff --git a/Phobos.vcxproj b/Phobos.vcxproj index bb7974c4d3..90761a48b2 100644 --- a/Phobos.vcxproj +++ b/Phobos.vcxproj @@ -155,6 +155,7 @@ + diff --git a/src/Ext/Techno/Body.TechnoAttachment.cpp b/src/Ext/Techno/Body.TechnoAttachment.cpp new file mode 100644 index 0000000000..9b4ff6e97a --- /dev/null +++ b/src/Ext/Techno/Body.TechnoAttachment.cpp @@ -0,0 +1,135 @@ +#include "Body.h" + +#include + +// 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) +{ + auto const pExt = TechnoExt::ExtMap.Find(pThis); + return pExt->ParentAttachment->DetachChild(); +} + +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::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + for (auto const& pAttachment : pExt->ChildAttachments) + pAttachment->Destroy(pSource); + + // TODO I am not sure, without clearing the attachments it sometimes crashes under + // weird circumstances, like if the techno exists but the parent attachment isn't, + // in particular in can enter cell hook, this may be a bandaid fix for something + // way worse like improper occupation clearance or whatever - Kerbiter + pExt->ChildAttachments.clear(); +} + +void TechnoExt::HandleDestructionAsChild(TechnoClass* pThis) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + + if (pExt->ParentAttachment) + pExt->ParentAttachment->ChildDestroyed(); +} + +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(); +} + +void TechnoExt::TransferAttachments(TechnoClass* pThis, TechnoClass* pThat) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + auto const pThatExt = TechnoExt::ExtMap.Find(pThat); + + for (auto& pAttachment : pThisExt->ChildAttachments) + { + pAttachment->Parent = pThat; + pThatExt->ChildAttachments.push_back(std::move(pAttachment)); + } + + pThisExt->ChildAttachments.clear(); +} + +bool TechnoExt::IsAttached(TechnoClass* pThis) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + return pExt && pExt->ParentAttachment; +} + +bool TechnoExt::HasAttachmentLoco(FootClass* pThis) +{ + IPersistPtr pPersist = pThis->Locomotor; + CLSID locoCLSID {}; + return pPersist && SUCCEEDED(pPersist->GetClassID(&locoCLSID)) + && locoCLSID == __uuidof(AttachmentLocomotionClass); +} + +bool TechnoExt::DoesntOccupyCellAsChild(TechnoClass* pThis) +{ + auto const& pExt = TechnoExt::ExtMap.Find(pThis); + return pExt && pExt->ParentAttachment + && !pExt->ParentAttachment->GetType()->OccupiesCell; +} + +bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + + return pThis && pThisExt && pParent // sanity check, sometimes crashes because ext is null - Kerbiter + && pThisExt->ParentAttachment + && (pThisExt->ParentAttachment->Parent == pParent + || (deep && TechnoExt::IsChildOf(pThisExt->ParentAttachment->Parent, pParent))); +} + +bool TechnoExt::AreRelatives(TechnoClass* pThis, TechnoClass* pThat) +{ + return TechnoExt::GetTopLevelParent(pThis) + == TechnoExt::GetTopLevelParent(pThat); +} + +// Returns this if no parent. +TechnoClass* TechnoExt::GetTopLevelParent(TechnoClass* pThis) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + + return pThis && pThisExt // sanity check, sometimes crashes because ext is null - Kerbiter + && pThisExt->ParentAttachment + ? TechnoExt::GetTopLevelParent(pThisExt->ParentAttachment->Parent) + : pThis; +} diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 18b53fa86d..e505c3c1be 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -867,138 +867,6 @@ bool TechnoExt::SimpleDeployerAllowedToDeploy(UnitClass* pThis, bool defaultValu return true; } -// 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) -{ - auto const pExt = TechnoExt::ExtMap.Find(pThis); - return pExt->ParentAttachment->DetachChild(); -} - -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::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) -{ - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - - for (auto const& pAttachment : pExt->ChildAttachments) - pAttachment->Destroy(pSource); - - // TODO I am not sure, without clearing the attachments it sometimes crashes under - // weird circumstances, like if the techno exists but the parent attachment isn't, - // in particular in can enter cell hook, this may be a bandaid fix for something - // way worse like improper occupation clearance or whatever - Kerbiter - pExt->ChildAttachments.clear(); -} - -void TechnoExt::HandleDestructionAsChild(TechnoClass* pThis) -{ - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - - if (pExt->ParentAttachment) - pExt->ParentAttachment->ChildDestroyed(); -} - -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(); -} - -void TechnoExt::TransferAttachments(TechnoClass* pThis, TechnoClass* pThat) -{ - auto const pThisExt = TechnoExt::ExtMap.Find(pThis); - auto const pThatExt = TechnoExt::ExtMap.Find(pThat); - - for (auto& pAttachment : pThisExt->ChildAttachments) - { - pAttachment->Parent = pThat; - pThatExt->ChildAttachments.push_back(std::move(pAttachment)); - } - - pThisExt->ChildAttachments.clear(); -} - -bool TechnoExt::IsAttached(TechnoClass* pThis) -{ - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - return pExt && pExt->ParentAttachment; -} - -bool TechnoExt::HasAttachmentLoco(FootClass* pThis) -{ - IPersistPtr pPersist = pThis->Locomotor; - CLSID locoCLSID {}; - return pPersist && SUCCEEDED(pPersist->GetClassID(&locoCLSID)) - && locoCLSID == __uuidof(AttachmentLocomotionClass); -} - -bool TechnoExt::DoesntOccupyCellAsChild(TechnoClass* pThis) -{ - auto const& pExt = TechnoExt::ExtMap.Find(pThis); - return pExt && pExt->ParentAttachment - && !pExt->ParentAttachment->GetType()->OccupiesCell; -} - -bool TechnoExt::IsChildOf(TechnoClass* pThis, TechnoClass* pParent, bool deep) -{ - auto const pThisExt = TechnoExt::ExtMap.Find(pThis); - - return pThis && pThisExt && pParent // sanity check, sometimes crashes because ext is null - Kerbiter - && pThisExt->ParentAttachment - && (pThisExt->ParentAttachment->Parent == pParent - || (deep && TechnoExt::IsChildOf(pThisExt->ParentAttachment->Parent, pParent))); -} - -bool TechnoExt::AreRelatives(TechnoClass* pThis, TechnoClass* pThat) -{ - return TechnoExt::GetTopLevelParent(pThis) - == TechnoExt::GetTopLevelParent(pThat); -} - -// Returns this if no parent. -TechnoClass* TechnoExt::GetTopLevelParent(TechnoClass* pThis) -{ - auto const pThisExt = TechnoExt::ExtMap.Find(pThis); - - return pThis && pThisExt // sanity check, sometimes crashes because ext is null - Kerbiter - && pThisExt->ParentAttachment - ? TechnoExt::GetTopLevelParent(pThisExt->ParentAttachment->Parent) - : pThis; -} - // ============================= // load / save From f71219c4330c5c136ad97376db7137facb3dea5e Mon Sep 17 00:00:00 2001 From: Noble Fish <89088785+DeathFishAtEase@users.noreply.github.com> Date: Tue, 9 Dec 2025 21:05:24 +0800 Subject: [PATCH 109/112] AttachmentUnit improvement - Selection and Flash (#1992) Fixed the abnormal [SelectionFlash](https://phobos.readthedocs.io/en/latest/User-Interface.html#flashing-technos-on-selecting) issue caused by synchronization states and added a flag to pass the selection target to the Parent Unit when a Child Unit is selected. - When `PassSelection=false`, the Child Unit will be selected as usual, but unlike before, it can correctly reference SelectionFlash instead of being terminated after 1 frame. - When `PassSelection=true`, if a Child Unit is selected, it will search upward for the nearest Parent Unit within an AttachmentType where `PassSelection=false` (or the final caller). - This will cause all Child Units along the tree path to flash due to the Parent Unit being selected. Before (in [the current latest version compiled three days ago](https://github.com/Phobos-developers/Phobos/pull/352#issuecomment-3613072028)): ![Before_5ecdf4e023c926f621732d1998a82cceade1fca9](https://github.com/user-attachments/assets/40203a4d-0599-441f-8d7b-0a9938261a2b) Now : ![Now](https://github.com/user-attachments/assets/ab6ee55c-27b5-4572-a1b6-02c553294a79) In `rulesmd.ini`: ```ini [AttachmentTypes] +=PassSelect_False +=PassSelect_True [PassSelect_False] PassSelection=false [APOC] Attachment0.Type=PassSelect_False Attachment0.TechnoType=SREF Attachment0.FLH=-512,0,208 Attachment1.Type=PassSelect_True Attachment1.TechnoType=DLPH Attachment1.FLH=512,0,208 [HTNK] Attachment0.Type=PassSelect_False Attachment0.TechnoType=DLPH Attachment0.FLH=-512,256,208 Attachment1.Type=PassSelect_True Attachment1.TechnoType=DLPH Attachment1.FLH=-512,-256,208 [TNKD] Attachment0.Type=PassSelect_True Attachment0.TechnoType=APOC Attachment0.FLH=256,0,208 [SREF] Attachment0.Type=PassSelect_True Attachment0.TechnoType=HTNK Attachment0.FLH=256,0,208 ``` The line with a red cross mark indicates that the selection status of a Child Unit is not passed to the Parent Unit when the Child Unit is selected, due to `PassSelection=false`. image *Selected State Transmission Relationship Diagram* --------- Co-authored-by: NetsuNegi39 --- YRpp | 2 +- docs/New-or-Enhanced-Logics.md | 1 + src/Ext/Techno/Hooks.Misc.cpp | 16 +++++--- src/Ext/Techno/Hooks.TechnoAttachment.cpp | 47 +++++++++++++++++++++++ src/New/Entity/AttachmentClass.cpp | 2 +- src/New/Type/AttachmentTypeClass.cpp | 2 + src/New/Type/AttachmentTypeClass.h | 2 + 7 files changed, 64 insertions(+), 8 deletions(-) diff --git a/YRpp b/YRpp index 5af96790ce..5cd30376e7 160000 --- a/YRpp +++ b/YRpp @@ -1 +1 @@ -Subproject commit 5af96790ce73e4ea068a390c60c124dccbc220e1 +Subproject commit 5cd30376e723e65be5aa5e8741be8d1e55accad8 diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index fea18811b5..f5b39738b0 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -214,6 +214,7 @@ InheritCommands=true ; boolean InheritCommands.StopCommand=true ; boolean InheritCommands.DeployCommand=true ; boolean LowSelectionPriority=true ; boolean, whether the child is low priority while attached +PassSelection=true ; boolean, whether the child selection propagates to parent TransparentToMouse=false ; boolean, can't click on attached techno if set YSortPosition=default ; Attachment YSort position enumeration - default|underparent|overparent InheritDestruction=true ; boolean diff --git a/src/Ext/Techno/Hooks.Misc.cpp b/src/Ext/Techno/Hooks.Misc.cpp index c4344c0c79..c1c04d92cc 100644 --- a/src/Ext/Techno/Hooks.Misc.cpp +++ b/src/Ext/Techno/Hooks.Misc.cpp @@ -721,16 +721,20 @@ DEFINE_HOOK_AGAIN(0x5F4718, ObjectClass_Select, 0x7) DEFINE_HOOK(0x5F46AE, ObjectClass_Select, 0x7) { GET(ObjectClass*, pThis, ESI); - pThis->IsSelected = true; - if (!Phobos::Config::ShowFlashOnSelecting) - return 0; + if (Phobos::Config::ShowFlashOnSelecting) + { + const int duration = RulesExt::Global()->SelectionFlashDuration; - auto const duration = RulesExt::Global()->SelectionFlashDuration; + if (duration > 0) + { + const auto pFlashTarget = abstract_cast(pThis); - if (duration > 0 && pThis->GetOwningHouse()->IsControlledByCurrentPlayer()) - pThis->Flash(duration); + if (pFlashTarget && pFlashTarget->Owner->IsControlledByCurrentPlayer()) + pFlashTarget->Flash(duration); + } + } return 0; } diff --git a/src/Ext/Techno/Hooks.TechnoAttachment.cpp b/src/Ext/Techno/Hooks.TechnoAttachment.cpp index 09c7bfea0a..36a5248614 100644 --- a/src/Ext/Techno/Hooks.TechnoAttachment.cpp +++ b/src/Ext/Techno/Hooks.TechnoAttachment.cpp @@ -462,6 +462,53 @@ DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB0D4, TechnoClass_IsSurfaced); #pragma endregion +#pragma region Select & Flash + +bool __fastcall TechnoClass_Select(TechnoClass* pThis) +{ + const auto pExt = TechnoExt::ExtMap.Find(pThis); + const auto pAttachment = pExt->ParentAttachment; + return pAttachment && pAttachment->GetType()->PassSelection + ? pAttachment->Parent->Select() + : pThis->TechnoClass::Select(); +} +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5DBC, TechnoClass_Select) // UnitClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB1A4, TechnoClass_Select) // InfantryClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E4008, TechnoClass_Select) // BuildingClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E23F0, TechnoClass_Select) // AircraftClass + +void __fastcall TechnoClass_Flash(TechnoClass* pThis, void*, int duration) +{ + const auto pExt = TechnoExt::ExtMap.Find(pThis); + + for (const auto& pAttachment : pExt->ChildAttachments) + { + if (pAttachment->GetType()->InheritStateEffects && pAttachment->Child) + pAttachment->Child->Flash(duration); + } + + return pThis->TechnoClass::Flash(duration); +} +DEFINE_FUNCTION_JUMP(VTABLE, 0x7F5DB8, TechnoClass_Flash) // UnitClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7EB1A0, TechnoClass_Flash) // InfantryClass +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E23EC, TechnoClass_Flash) // AircraftClass + +void __fastcall BuildingClass_Flash(BuildingClass* pThis, void*, int duration) +{ + const auto pExt = TechnoExt::ExtMap.Find(pThis); + + for (const auto& pAttachment : pExt->ChildAttachments) + { + if (pAttachment->GetType()->InheritStateEffects && pAttachment->Child) + pAttachment->Child->Flash(duration); + } + + return pThis->BuildingClass::Flash(duration); +} +DEFINE_FUNCTION_JUMP(VTABLE, 0x7E4004, BuildingClass_Flash) // BuildingClass + +#pragma endregion + DEFINE_HOOK(0x6CC763, SuperClass_Place_ChronoWarp_SkipChildren, 0x6) { enum { Skip = 0x6CCCCA, Continue = 0 }; diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 391cda8d3c..7bc94f7bd0 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -129,7 +129,7 @@ void AttachmentClass::AI() this->Child->unknown_280 = this->Parent->unknown_280; // sth related to teleport this->Child->BeingWarpedOut = this->Parent->BeingWarpedOut; this->Child->Deactivated = this->Parent->Deactivated; - this->Child->Flash(this->Parent->Flashing.DurationRemaining); + //this->Child->Flash(this->Parent->Flashing.DurationRemaining); this->Child->IronCurtainTimer = this->Parent->IronCurtainTimer; this->Child->IdleActionTimer = this->Parent->IdleActionTimer; diff --git a/src/New/Type/AttachmentTypeClass.cpp b/src/New/Type/AttachmentTypeClass.cpp index 793a81e361..4165f89bb2 100644 --- a/src/New/Type/AttachmentTypeClass.cpp +++ b/src/New/Type/AttachmentTypeClass.cpp @@ -24,6 +24,7 @@ void AttachmentTypeClass::LoadFromINI(CCINIClass* pINI) this->InheritHeightStatus.Read(exINI, section, "InheritHeightStatus"); this->OccupiesCell.Read(exINI, section, "OccupiesCell"); this->LowSelectionPriority.Read(exINI, section, "LowSelectionPriority"); + this->PassSelection.Read(exINI, section, "PassSelection"); this->TransparentToMouse.Read(exINI, section, "TransparentToMouse"); this->YSortPosition.Read(exINI, section, "YSortPosition"); this->DestructionWeapon_Child.Read(exINI, section, "DestructionWeapon.Child"); @@ -47,6 +48,7 @@ void AttachmentTypeClass::Serialize(T& Stm) .Process(this->InheritHeightStatus) .Process(this->OccupiesCell) .Process(this->LowSelectionPriority) + .Process(this->PassSelection) .Process(this->TransparentToMouse) .Process(this->YSortPosition) .Process(this->DestructionWeapon_Child) diff --git a/src/New/Type/AttachmentTypeClass.h b/src/New/Type/AttachmentTypeClass.h index 838413530c..eb519e8d4d 100644 --- a/src/New/Type/AttachmentTypeClass.h +++ b/src/New/Type/AttachmentTypeClass.h @@ -20,6 +20,7 @@ class AttachmentTypeClass final : public Enumerable Valueable InheritHeightStatus; Valueable OccupiesCell; Valueable LowSelectionPriority; + Valueable PassSelection; Valueable TransparentToMouse; Valueable YSortPosition; Nullable DestructionWeapon_Child; @@ -39,6 +40,7 @@ class AttachmentTypeClass final : public Enumerable , InheritDestruction { true } , InheritHeightStatus { true } , LowSelectionPriority { true } + , PassSelection { true } , TransparentToMouse { false } , YSortPosition { AttachmentYSortPosition::Default } , DestructionWeapon_Child { } From b50a29503d420030dba039a086ff5590a49a40ee Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 2 Mar 2026 03:57:31 +0200 Subject: [PATCH 110/112] type conversion support (untested) --- docs/New-or-Enhanced-Logics.md | 13 ++++ src/Ext/Techno/Body.TechnoAttachment.cpp | 86 ++++++++++++++++++++++++ src/Ext/Techno/Body.Update.cpp | 3 + src/Ext/Techno/Body.cpp | 4 ++ src/Ext/Techno/Body.h | 5 ++ src/Ext/TechnoType/Body.cpp | 23 ++++++- src/Ext/TechnoType/Body.h | 1 + src/New/Entity/AttachmentClass.cpp | 24 +++++-- src/New/Entity/AttachmentClass.h | 4 ++ 9 files changed, 155 insertions(+), 8 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 47cf2b1df2..046e8854b8 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -201,6 +201,18 @@ This feature is not final and is under development. - Technos now can be attached one to another in a tree like way. The attached units won't process any locomotion code and act like a part of a parent unit in a configurable. - Currently the attached techno may only be a vehicle. - When attached, the special `Attachment` (`{C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E}`) locomotor is automatically casted on a unit. You may also specify it in the child unit types manually if the unit is not intended to move without a parent (f. ex. a turret). +- Attachment slots can now be assigned an `AttachmentX.ID` string. This enables child techno preservation across type conversions (see below). + +**Behavior during type conversion** (e.g. Ares `UpdateType`): + +Each TechnoType owns its own set of attachment slots. When a techno undergoes type conversion, the following happens: +1. The current type's slots (and any children inside them) are placed in a per-type dormant storage. +2. The new type's slots are set up - restored from dormant storage if the unit previously had this type, or created fresh otherwise. +3. For each new slot that has an `AttachmentX.ID`, the dormant slots from the old type are searched for a matching ID. If found, that child is transferred into the new slot. + - If the new slot specifies an `AttachmentX.TechnoType` that differs from the child's current type, the child is automatically converted to the new slot's TechnoType. + - If the new slot has no `AttachmentX.TechnoType`, or if the old slot has a `AttachmentX.TechnoType` set and contained a child of type that doesn't match the old slot defined type, the child is transferred as-is regardless of its type. + - Slots without an `AttachmentX.ID` are never matched and their children are not transferred. +4. Old dormant slots not matched by any new slot have their children placed in limbo. New slots that ended up empty are unlimboed (unless the parent is currently in limbo). In `rulesmd.ini`: ```ini @@ -237,6 +249,7 @@ AttachmentX.TechnoType= ; TechnoType that can be attached, cur AttachmentX.FLH=0,0,0 ; integer - Forward, Lateral, Height AttachmentX.IsOnTurret=false ; boolean AttachmentX.RotationAdjust=0 ; rotation in DirType, from -255 to 255 +AttachmentX.ID= ; string, max 32 chars - ID for child transfer on type conversion; must be unique per TechnoType [General] AttachmentTopLayerMinHeight=500 ; integer diff --git a/src/Ext/Techno/Body.TechnoAttachment.cpp b/src/Ext/Techno/Body.TechnoAttachment.cpp index 9b4ff6e97a..a73eb02490 100644 --- a/src/Ext/Techno/Body.TechnoAttachment.cpp +++ b/src/Ext/Techno/Body.TechnoAttachment.cpp @@ -86,6 +86,92 @@ void TechnoExt::TransferAttachments(TechnoClass* pThis, TechnoClass* pThat) pThisExt->ChildAttachments.clear(); } +void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* pOldType, TechnoTypeClass* pNewType) +{ + auto const pThisExt = TechnoExt::ExtMap.Find(pThis); + auto const pNewTypeExt = TechnoTypeExt::ExtMap.Find(pNewType); + + const int oldTypeIndex = TechnoTypeClass::Array.FindItemIndex(pOldType); + const int newTypeIndex = TechnoTypeClass::Array.FindItemIndex(pNewType); + + // Helper to resolve a NullableIdx to a TechnoTypeClass pointer + auto resolveChildType = [](const NullableIdx& idx) -> TechnoTypeClass* + { + return idx.isset() ? TechnoTypeClass::Array[idx] : nullptr; + }; + + // Step 1: Store current (old type) mount points as dormant (without limboing yet) + pThisExt->DormantAttachments[oldTypeIndex] = std::move(pThisExt->ChildAttachments); + pThisExt->ChildAttachments.clear(); + + // Step 2: Establish new mount points - restore from dormant or create fresh + auto dormantIt = pThisExt->DormantAttachments.find(newTypeIndex); + if (dormantIt != pThisExt->DormantAttachments.end()) + { + pThisExt->ChildAttachments = std::move(dormantIt->second); + pThisExt->DormantAttachments.erase(dormantIt); + } + else + { + for (auto& entry : pNewTypeExt->AttachmentData) + pThisExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr))->Initialize(); + } + + // Step 3: Match old mount points to new active ones by ID and transfer children + auto& oldMounts = pThisExt->DormantAttachments[oldTypeIndex]; + + // this assumes we only encounter each attachment ID once, which is currently enforced on parsing + for (auto& pNewMount : pThisExt->ChildAttachments) + { + const auto& newID = pNewMount->Data->ID; + if (!newID) + continue; + + for (auto& pOldMount : oldMounts) + { + if (!pOldMount->Child) + continue; // nothing to transfer from this mount point, skip + + const auto& oldID = pOldMount->Data->ID; + if (!oldID || _strcmpi(oldID, newID) != 0) + continue; // IDs don't match or no ID, skip + + // Found a match - transfer child techno from old mount point to new + TechnoClass* pChild = pOldMount->Child; + if (pChild) + { + auto* oldChildType = resolveChildType(pOldMount->Data->TechnoType); + auto* newChildType = resolveChildType(pNewMount->Data->TechnoType); + + pOldMount->DetachChildCore(); + + bool childMatchesType = pChild->GetTechnoType() == oldChildType; + bool typesDiffer = oldChildType != newChildType; + if (childMatchesType && typesDiffer && newChildType) + { + if (auto* pChildAsFoot = abstract_cast(pChild)) + TechnoExt::ConvertToType(pChildAsFoot, newChildType); + } + + pNewMount->AttachChildCore(pChild); + } + + pOldMount = nullptr; + break; + } + } + + // Step 4: Limbo all old mount points and unlimbo new ones without children + for (auto& pOldMount : oldMounts) + pOldMount->Limbo(); + + if (pThis->InLimbo) + return; // if parent is in limbo, leave new attachments in limbo as well and skip unlimboing + + for (auto& pNewMount : pThisExt->ChildAttachments) + pNewMount->Unlimbo(); +} + bool TechnoExt::IsAttached(TechnoClass* pThis) { auto const& pExt = TechnoExt::ExtMap.Find(pThis); diff --git a/src/Ext/Techno/Body.Update.cpp b/src/Ext/Techno/Body.Update.cpp index 98920283a8..d8f1cf2139 100644 --- a/src/Ext/Techno/Body.Update.cpp +++ b/src/Ext/Techno/Body.Update.cpp @@ -695,6 +695,9 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType) auto const pNewTypeExt = TechnoTypeExt::ExtMap.Find(pCurrentType); this->TypeExtData = pNewTypeExt; + // Handle attachment slot matching and conversion + TechnoExt::HandleAttachmentConversion(pThis, pOldType, pCurrentType); + this->UpdateSelfOwnedAttachEffects(); if (auto const pShield = this->Shield.get()) diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 0f7fb2f275..53b18bb64a 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -1067,6 +1067,10 @@ void TechnoExt::ExtData::InvalidatePointer(void* ptr, bool bRemoved) for (auto const& pAttachment : ChildAttachments) pAttachment->InvalidatePointer(ptr); + + for (auto& [key, vec] : DormantAttachments) + for (auto const& pAttachment : vec) + pAttachment->InvalidatePointer(ptr); } void TechnoExt::ExtData::LoadFromStream(PhobosStreamReader& Stm) diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index 55d97c1def..a95a93fa49 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -1,5 +1,7 @@ #pragma once +#include + #include #include #include @@ -103,6 +105,7 @@ class TechnoExt AttachmentClass* ParentAttachment; ValueableVector> ChildAttachments; + std::map>> DormantAttachments; // Ares std::optional AltOccupation; // if the unit marks cell occupation flags, this is set to whether it uses the "high" occupation members @@ -175,6 +178,7 @@ class TechnoExt , JumpjetStraightAscend { false } , ParentAttachment {} , ChildAttachments {} + , DormantAttachments {} , AltOccupation {} { } @@ -278,6 +282,7 @@ class TechnoExt static void UnlimboAttachments(TechnoClass* pThis); static void LimboAttachments(TechnoClass* pThis); static void TransferAttachments(TechnoClass* pThis, TechnoClass* pThat); + static void HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* pOldType, TechnoTypeClass* pNewType); static bool IsAttached(TechnoClass* pThis); static bool HasAttachmentLoco(FootClass* pThis); // FIXME shouldn't be here diff --git a/src/Ext/TechnoType/Body.cpp b/src/Ext/TechnoType/Body.cpp index f34e77fb65..488834f882 100644 --- a/src/Ext/TechnoType/Body.cpp +++ b/src/Ext/TechnoType/Body.cpp @@ -895,13 +895,33 @@ void TechnoTypeExt::ExtData::LoadFromINIFile(CCINIClass* const pINI) _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.RotationAdjust", i); rotationAdjust.Read(exINI, pSection, tempBuffer); - AttachmentDataEntry const entry { ValueableIdx(type), technoType, flh, isOnTurret, rotationAdjust }; + PhobosFixedString<32> id; + _snprintf_s(tempBuffer, sizeof(tempBuffer), "Attachment%d.ID", i); + id.Read(pINI, pSection, tempBuffer); + + AttachmentDataEntry const entry { ValueableIdx(type), technoType, flh, isOnTurret, rotationAdjust, id }; if (i == AttachmentData.size()) this->AttachmentData.push_back(entry); else this->AttachmentData[i] = entry; } + // Validate attachment ID uniqueness + std::set> usedIds; + for (size_t i = 0; i < this->AttachmentData.size(); ++i) + { + const auto& id = this->AttachmentData[i].ID; + + if (!id) + continue; + + if (!usedIds.insert(id).second) + { + Debug::FatalErrorAndExit("[%s] Duplicate Attachment ID '%s'\n", + pSection, id); + } + } + this->NoSecondaryWeaponFallback.Read(exINI, pSection, "NoSecondaryWeaponFallback"); this->NoSecondaryWeaponFallback_AllowAA.Read(exINI, pSection, "NoSecondaryWeaponFallback.AllowAA"); this->AllowWeaponSelectAgainstWalls.Read(exINI, pSection, "AllowWeaponSelectAgainstWalls"); @@ -1953,6 +1973,7 @@ bool TechnoTypeExt::ExtData::AttachmentDataEntry::Serialize(T& stm) .Process(this->FLH) .Process(this->IsOnTurret) .Process(this->RotationAdjust) + .Process(this->ID) .Success(); } diff --git a/src/Ext/TechnoType/Body.h b/src/Ext/TechnoType/Body.h index 61ff211bff..1626777fc2 100644 --- a/src/Ext/TechnoType/Body.h +++ b/src/Ext/TechnoType/Body.h @@ -388,6 +388,7 @@ class TechnoTypeExt Valueable FLH; Valueable IsOnTurret; Valueable RotationAdjust; + PhobosFixedString<32> ID; bool Load(PhobosStreamReader& stm, bool registerForChange); bool Save(PhobosStreamWriter& stm) const; diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 7bc94f7bd0..8a3fcf13d2 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -231,10 +231,7 @@ bool AttachmentClass::AttachChild(TechnoClass* pChild) } } - this->Child = pChild; - - auto pChildExt = TechnoExt::ExtMap.Find(this->Child); - pChildExt->ParentAttachment = this; + this->AttachChildCore(pChild); // bandaid for jitterless drawing. TODO fix properly // this->Child->GetTechnoType()->DisableVoxelCache = true; @@ -268,9 +265,7 @@ bool AttachmentClass::DetachChild() if (auto const pChildAsFoot = abstract_cast(this->Child)) LocomotionClass::End_Piggyback(pChildAsFoot->Locomotor); - auto pChildExt = TechnoExt::ExtMap.Find(this->Child); - pChildExt->ParentAttachment = nullptr; - this->Child = nullptr; + this->DetachChildCore(); return true; } @@ -279,6 +274,21 @@ bool AttachmentClass::DetachChild() } +void AttachmentClass::AttachChildCore(TechnoClass* pChild) +{ + this->Child = pChild; + TechnoExt::ExtMap.Find(pChild)->ParentAttachment = this; +} + +void AttachmentClass::DetachChildCore() +{ + if (this->Child) + { + TechnoExt::ExtMap.Find(this->Child)->ParentAttachment = nullptr; + this->Child = nullptr; + } +} + void AttachmentClass::InvalidatePointer(void* ptr) { AnnounceInvalidPointer(this->Parent, ptr); diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index 74af53a8e1..a7bc65d28e 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -57,6 +57,10 @@ class AttachmentClass bool AttachChild(TechnoClass* pChild); bool DetachChild(); + // Core link/unlink without side effects (locomotor changes, missions, owner resets) + void AttachChildCore(TechnoClass* pChild); + void DetachChildCore(); + void InvalidatePointer(void* ptr); bool Load(PhobosStreamReader& stm, bool registerForChange); From 41d49ecf2f874b2e4485206a9468c1117d89c7b1 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Mon, 16 Mar 2026 02:05:37 +0200 Subject: [PATCH 111/112] feat: attachment conversion on deploy unit <-> build --- docs/New-or-Enhanced-Logics.md | 20 ++--- src/Ext/Techno/Body.TechnoAttachment.cpp | 105 +++++++++++++++++------ src/Ext/Techno/Body.cpp | 1 + src/Ext/Techno/Body.h | 2 + src/Ext/Techno/Hooks.cpp | 8 +- src/Ext/Unit/Hooks.DeploysInto.cpp | 6 ++ 6 files changed, 102 insertions(+), 40 deletions(-) diff --git a/docs/New-or-Enhanced-Logics.md b/docs/New-or-Enhanced-Logics.md index 046e8854b8..347e64348e 100644 --- a/docs/New-or-Enhanced-Logics.md +++ b/docs/New-or-Enhanced-Logics.md @@ -201,18 +201,14 @@ This feature is not final and is under development. - Technos now can be attached one to another in a tree like way. The attached units won't process any locomotion code and act like a part of a parent unit in a configurable. - Currently the attached techno may only be a vehicle. - When attached, the special `Attachment` (`{C5D54B98-8C98-4275-8CE4-EF75CB0CBE3E}`) locomotor is automatically casted on a unit. You may also specify it in the child unit types manually if the unit is not intended to move without a parent (f. ex. a turret). -- Attachment slots can now be assigned an `AttachmentX.ID` string. This enables child techno preservation across type conversions (see below). - -**Behavior during type conversion** (e.g. Ares `UpdateType`): - -Each TechnoType owns its own set of attachment slots. When a techno undergoes type conversion, the following happens: -1. The current type's slots (and any children inside them) are placed in a per-type dormant storage. -2. The new type's slots are set up - restored from dormant storage if the unit previously had this type, or created fresh otherwise. -3. For each new slot that has an `AttachmentX.ID`, the dormant slots from the old type are searched for a matching ID. If found, that child is transferred into the new slot. - - If the new slot specifies an `AttachmentX.TechnoType` that differs from the child's current type, the child is automatically converted to the new slot's TechnoType. - - If the new slot has no `AttachmentX.TechnoType`, or if the old slot has a `AttachmentX.TechnoType` set and contained a child of type that doesn't match the old slot defined type, the child is transferred as-is regardless of its type. - - Slots without an `AttachmentX.ID` are never matched and their children are not transferred. -4. Old dormant slots not matched by any new slot have their children placed in limbo. New slots that ended up empty are unlimboed (unless the parent is currently in limbo). +- Attachment slots can now be assigned an `AttachmentX.ID` string. This enables child techno preservation across type conversions (e.g. Ares `UpdateType`) and deploys between building and unit. Each TechnoType owns its own set of attachment slots, and the following happens when a techno is type-converted: + 1. The current type's slots (and any children inside them) are placed in a per-type dormant storage. + 2. The new type's slots are set up - restored from dormant storage if the unit previously had this type, or created fresh otherwise. + 3. For each new slot that has an `AttachmentX.ID`, the old type's dormant slots are searched for a matching ID. If found, the child and its attachment state (e.g. the respawn timer, scaled proportionally to the new slot's `RespawnDelay`) are transferred into the new slot. + - If the new slot specifies an `AttachmentX.TechnoType` that differs from the child's current type, the child is automatically converted to the new slot's TechnoType. + - If the new slot has no `AttachmentX.TechnoType`, or if the old slot had `AttachmentX.TechnoType` set and the child's type does not match it, the child is transferred as-is regardless of its type. + - Slots without an `AttachmentX.ID` are never matched and their children are not transferred. + 4. Unmatched old dormant slots have their children placed in limbo. New slots that ended up empty are unlimboed (unless the parent is currently in limbo). In `rulesmd.ini`: ```ini diff --git a/src/Ext/Techno/Body.TechnoAttachment.cpp b/src/Ext/Techno/Body.TechnoAttachment.cpp index a73eb02490..516d8a030f 100644 --- a/src/Ext/Techno/Body.TechnoAttachment.cpp +++ b/src/Ext/Techno/Body.TechnoAttachment.cpp @@ -2,6 +2,8 @@ #include +#include + // Attaches this techno in a first available attachment "slot". // Returns true if the attachment is successful. bool TechnoExt::AttachTo(TechnoClass* pThis, TechnoClass* pParent) @@ -25,19 +27,24 @@ bool TechnoExt::DetachFromParent(TechnoClass* pThis) void TechnoExt::InitializeAttachments(TechnoClass* pThis) { + if (TechnoExt::DeployTransferSource) + return; // we handle that as part of the "conversion" + 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(); - } + pExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr))->Initialize(); } void TechnoExt::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) { + // During deploy transfer the source object goes through Remove_This -> KillCargo after + // attachments were moved. The vector is empty so this is normally a no-op, but guard for safety. + if (TechnoExt::DeployTransferSource == pThis) + return; + auto const& pExt = TechnoExt::ExtMap.Find(pThis); for (auto const& pAttachment : pExt->ChildAttachments) @@ -52,6 +59,11 @@ void TechnoExt::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) void TechnoExt::HandleDestructionAsChild(TechnoClass* pThis) { + // During deploy transfer the source goes through Remove_This which would notify the parent + // that the child was destroyed. The source is being replaced, not destroyed. + if (TechnoExt::DeployTransferSource == pThis) + return; + auto const& pExt = TechnoExt::ExtMap.Find(pThis); if (pExt->ParentAttachment) @@ -67,6 +79,11 @@ void TechnoExt::UnlimboAttachments(TechnoClass* pThis) void TechnoExt::LimboAttachments(TechnoClass* pThis) { + // During deploy transfer the source object is Limbo'd before the transfer hook fires + // (building->unit direction). Skip limbo-ing children - they will be moved to the new object. + if (TechnoExt::DeployTransferSource == pThis) + return; + auto const pExt = TechnoExt::ExtMap.Find(pThis); for (auto const& pAttachment : pExt->ChildAttachments) pAttachment->Limbo(); @@ -101,15 +118,13 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* }; // Step 1: Store current (old type) mount points as dormant (without limboing yet) + // We preserve old attachments as is so we can then restore them as is pThisExt->DormantAttachments[oldTypeIndex] = std::move(pThisExt->ChildAttachments); - pThisExt->ChildAttachments.clear(); // Step 2: Establish new mount points - restore from dormant or create fresh - auto dormantIt = pThisExt->DormantAttachments.find(newTypeIndex); - if (dormantIt != pThisExt->DormantAttachments.end()) + if (auto node = pThisExt->DormantAttachments.extract(newTypeIndex)) { - pThisExt->ChildAttachments = std::move(dormantIt->second); - pThisExt->DormantAttachments.erase(dormantIt); + pThisExt->ChildAttachments = std::move(node.mapped()); } else { @@ -117,33 +132,34 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* pThisExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr))->Initialize(); } - // Step 3: Match old mount points to new active ones by ID and transfer children + // Step 3: Match old mount points to new active ones by ID; transfer children and synchronize timers. + // Assumes each attachment ID is unique per TechnoType, which is enforced on parsing. auto& oldMounts = pThisExt->DormantAttachments[oldTypeIndex]; - // this assumes we only encounter each attachment ID once, which is currently enforced on parsing + // Shallow copy of old mount pointers for consumed-entry tracking; originals remain in DormantAttachments. + std::vector oldMountsCopy; + std::ranges::transform(oldMounts, std::back_inserter(oldMountsCopy), [](const auto& p) { return p.get(); }); + for (auto& pNewMount : pThisExt->ChildAttachments) { const auto& newID = pNewMount->Data->ID; if (!newID) continue; - for (auto& pOldMount : oldMounts) + for (auto it = oldMountsCopy.begin(); it != oldMountsCopy.end(); ++it) { - if (!pOldMount->Child) - continue; // nothing to transfer from this mount point, skip - - const auto& oldID = pOldMount->Data->ID; + const auto& oldID = (*it)->Data->ID; if (!oldID || _strcmpi(oldID, newID) != 0) - continue; // IDs don't match or no ID, skip + continue; - // Found a match - transfer child techno from old mount point to new - TechnoClass* pChild = pOldMount->Child; - if (pChild) + // Transfer child techno if present + assert(!pNewMount->Child && "ID-matched new attachment mount already has a child before conversion illegally!"); + if (TechnoClass* pChild = (*it)->Child) { - auto* oldChildType = resolveChildType(pOldMount->Data->TechnoType); + auto* oldChildType = resolveChildType((*it)->Data->TechnoType); auto* newChildType = resolveChildType(pNewMount->Data->TechnoType); - pOldMount->DetachChildCore(); + (*it)->DetachChildCore(); bool childMatchesType = pChild->GetTechnoType() == oldChildType; bool typesDiffer = oldChildType != newChildType; @@ -156,12 +172,25 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* pNewMount->AttachChildCore(pChild); } - pOldMount = nullptr; + // Synchronize respawn timer if both attachment types have respawn enabled. + // Preserves the completion percentage: newRemaining/newDelay == oldRemaining/oldDelay. + int oldDelay = (*it)->GetType()->RespawnDelay; + int newDelay = pNewMount->GetType()->RespawnDelay; + if (oldDelay > 0 && newDelay > 0 && (*it)->RespawnTimer.HasStarted()) + { + int oldRemaining = (*it)->RespawnTimer.GetTimeLeft(); + int newRemaining = (oldRemaining * newDelay) / oldDelay; + pNewMount->RespawnTimer.TimeLeft = newDelay; + pNewMount->RespawnTimer.StartTime = static_cast(Unsorted::CurrentFrame) - (newDelay - newRemaining); + } + + oldMountsCopy.erase(it); break; } } - // Step 4: Limbo all old mount points and unlimbo new ones without children + // Step 4: Limbo all old mount points (matched ones have no child, so Limbo is a no-op for them); + // unlimbo the new active ones for (auto& pOldMount : oldMounts) pOldMount->Limbo(); @@ -172,6 +201,34 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* pNewMount->Unlimbo(); } +void TechnoExt::HandleAttachmentDeployTransfer(TechnoClass* pFrom, TechnoClass* pTo) +{ + auto const pFromExt = TechnoExt::ExtMap.Find(pFrom); + auto const pToExt = TechnoExt::ExtMap.Find(pTo); + + // The flag is consumed here - clear it now that the transfer is happening. + TechnoExt::DeployTransferSource = nullptr; + assert(pToExt->ChildAttachments.empty() && "pTo should have no mounts before deploy transfer"); + + // Move pFrom's active and dormant attachments into pTo so they live on the surviving object. + pToExt->ChildAttachments = std::move(pFromExt->ChildAttachments); + pToExt->DormantAttachments = std::move(pFromExt->DormantAttachments); + + // Re-parent all active mounts to point at the new parent techno. + for (auto& pAttachment : pToExt->ChildAttachments) + pAttachment->Parent = pTo; + + // Re-parent dormant mounts as well, since they may be restored on a future conversion. + for (auto& [typeIdx, mounts] : pToExt->DormantAttachments) + { + for (auto& pAttachment : mounts) + pAttachment->Parent = pTo; + } + + // Now handle conversion from pFrom's type to pTo's type on the new object. + HandleAttachmentConversion(pTo, pFrom->GetTechnoType(), pTo->GetTechnoType()); +} + bool TechnoExt::IsAttached(TechnoClass* pThis) { auto const& pExt = TechnoExt::ExtMap.Find(pThis); diff --git a/src/Ext/Techno/Body.cpp b/src/Ext/Techno/Body.cpp index 53b18bb64a..ba6a0e82c0 100644 --- a/src/Ext/Techno/Body.cpp +++ b/src/Ext/Techno/Body.cpp @@ -12,6 +12,7 @@ TechnoExt::ExtContainer TechnoExt::ExtMap; UnitClass* TechnoExt::Deployer = nullptr; +TechnoClass* TechnoExt::DeployTransferSource = nullptr; TechnoExt::ExtData::~ExtData() { diff --git a/src/Ext/Techno/Body.h b/src/Ext/Techno/Body.h index a95a93fa49..fa546fef16 100644 --- a/src/Ext/Techno/Body.h +++ b/src/Ext/Techno/Body.h @@ -255,6 +255,7 @@ class TechnoExt static ExtContainer ExtMap; static UnitClass* Deployer; + static TechnoClass* DeployTransferSource; // Set before deploy-target construction to skip InitializeAttachments and possibly other things static bool LoadGlobals(PhobosStreamReader& Stm); static bool SaveGlobals(PhobosStreamWriter& Stm); @@ -283,6 +284,7 @@ class TechnoExt static void LimboAttachments(TechnoClass* pThis); static void TransferAttachments(TechnoClass* pThis, TechnoClass* pThat); static void HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* pOldType, TechnoTypeClass* pNewType); + static void HandleAttachmentDeployTransfer(TechnoClass* pFrom, TechnoClass* pTo); static bool IsAttached(TechnoClass* pThis); static bool HasAttachmentLoco(FootClass* pThis); // FIXME shouldn't be here diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index e10d4b288d..cd59b3ca42 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -31,7 +31,7 @@ DEFINE_HOOK(0x7363C9, UnitClass_AI_AnimationPaused, 0x6) enum { SkipGameCode = 0x7363DE }; GET(UnitClass*, pThis, ESI); - + if (TechnoExt::ExtMap.Find(pThis)->DelayedFireSequencePaused) return SkipGameCode; @@ -255,13 +255,13 @@ DEFINE_HOOK(0x6F42F7, TechnoClass_Init, 0x2) { GET(TechnoClass*, pThis, ESI); - TechnoExt::InitializeAttachments(pThis); - auto const pType = pThis->GetTechnoType(); if (!pType) // Critical sanity check in s/l return 0; + TechnoExt::InitializeAttachments(pThis); + auto const pExt = TechnoExt::ExtMap.Find(pThis); auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); pExt->TypeExtData = pTypeExt; @@ -1290,7 +1290,7 @@ DEFINE_HOOK(0x71A8BD, TemporalClass_Update_WarpAwayAnim, 0x5) AnimExt::CreateRandomAnim(pExt->WarpAway, pTarget->Location, nullptr, pTarget->Owner); return 0x71A90E; } - + return 0; } diff --git a/src/Ext/Unit/Hooks.DeploysInto.cpp b/src/Ext/Unit/Hooks.DeploysInto.cpp index 98815fdf2f..2f27b1cc52 100644 --- a/src/Ext/Unit/Hooks.DeploysInto.cpp +++ b/src/Ext/Unit/Hooks.DeploysInto.cpp @@ -92,6 +92,7 @@ DEFINE_HOOK(0x739956, UnitClass_Deploy_Transfer, 0x6) ShieldClass::SyncShieldToAnother(pUnit, pStructure); TechnoExt::SyncInvulnerability(pUnit, pStructure); AttachEffectClass::TransferAttachedEffects(pUnit, pStructure); + TechnoExt::HandleAttachmentDeployTransfer(pUnit, pStructure); return 0; } @@ -105,6 +106,7 @@ DEFINE_HOOK(0x44A03C, BuildingClass_Mi_Selling_Transfer, 0x6) ShieldClass::SyncShieldToAnother(pStructure, pUnit); TechnoExt::SyncInvulnerability(pStructure, pUnit); AttachEffectClass::TransferAttachedEffects(pStructure, pUnit); + TechnoExt::HandleAttachmentDeployTransfer(pStructure, pUnit); // This line will break the bahavior of UnDeploysInto buildings. However, it might serve a purpose that no one knows yet // Comment out the line instead of removing it for now, so we can turn to it if something related goes wrong in the future @@ -117,6 +119,8 @@ DEFINE_HOOK(0x449E2E, BuildingClass_Mi_Selling_CreateUnit, 0x6) GET(BuildingClass*, pStructure, EBP); R->ECX(pStructure->GetOriginalOwner()); + TechnoExt::DeployTransferSource = pStructure; + // Remember MC ring animation. if (pStructure->IsMindControlled()) { @@ -132,6 +136,8 @@ DEFINE_HOOK(0x7396AD, UnitClass_Deploy_CreateBuilding, 0x6) GET(UnitClass*, pUnit, EBP); R->EDX(pUnit->GetOriginalOwner()); + TechnoExt::DeployTransferSource = pUnit; + return 0x7396B3; } From 6d90711b932bb35cc81a78038d1d28020e8ff107 Mon Sep 17 00:00:00 2001 From: Metadorius Date: Sun, 22 Mar 2026 02:58:34 +0200 Subject: [PATCH 112/112] fix: bunch of issues with attachment conversion; works as needed now --- src/Ext/Building/Hooks.cpp | 3 +++ src/Ext/Techno/Body.TechnoAttachment.cpp | 16 ++++++++++++++-- src/Ext/Techno/Hooks.Transport.cpp | 4 ++++ src/Ext/Techno/Hooks.cpp | 2 ++ src/Misc/Hooks.AI.cpp | 12 ++++++------ src/New/Entity/AttachmentClass.cpp | 2 +- src/New/Entity/AttachmentClass.h | 2 +- 7 files changed, 31 insertions(+), 10 deletions(-) diff --git a/src/Ext/Building/Hooks.cpp b/src/Ext/Building/Hooks.cpp index 174376db4b..c5c5bce94e 100644 --- a/src/Ext/Building/Hooks.cpp +++ b/src/Ext/Building/Hooks.cpp @@ -19,6 +19,9 @@ DEFINE_HOOK(0x43FE69, BuildingClass_AI, 0xA) const auto pTechnoExt = pBuildingExt->TechnoExtData; pTechnoExt->UpdateLaserTrails(); // Mainly for on turret trails + for (auto const& attachment : pTechnoExt->ChildAttachments) + attachment->AI(); + // Force airstrike targets to redraw every frame to account for tint intensity fluctuations. if (pTechnoExt->AirstrikeTargetingMe) pThis->Mark(MarkType::Change); diff --git a/src/Ext/Techno/Body.TechnoAttachment.cpp b/src/Ext/Techno/Body.TechnoAttachment.cpp index 516d8a030f..5f50eab273 100644 --- a/src/Ext/Techno/Body.TechnoAttachment.cpp +++ b/src/Ext/Techno/Body.TechnoAttachment.cpp @@ -35,7 +35,7 @@ void TechnoExt::InitializeAttachments(TechnoClass* pThis) auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pType); for (auto& entry : pTypeExt->AttachmentData) - pExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr))->Initialize(); + pExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr))->OnCreated(); } void TechnoExt::DestroyAttachments(TechnoClass* pThis, TechnoClass* pSource) @@ -121,6 +121,7 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* // We preserve old attachments as is so we can then restore them as is pThisExt->DormantAttachments[oldTypeIndex] = std::move(pThisExt->ChildAttachments); + bool areNewAttachments = false; // Step 2: Establish new mount points - restore from dormant or create fresh if (auto node = pThisExt->DormantAttachments.extract(newTypeIndex)) { @@ -128,8 +129,11 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* } else { + // Do NOT call OnCreated() here - we do not consider all of "technically" new attachments as new. for (auto& entry : pNewTypeExt->AttachmentData) - pThisExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr))->Initialize(); + pThisExt->ChildAttachments.emplace_back(std::make_unique(&entry, pThis, nullptr)); + + areNewAttachments = true; } // Step 3: Match old mount points to new active ones by ID; transfer children and synchronize timers. @@ -146,6 +150,7 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* if (!newID) continue; + bool gotConverted = false; for (auto it = oldMountsCopy.begin(); it != oldMountsCopy.end(); ++it) { const auto& oldID = (*it)->Data->ID; @@ -182,11 +187,18 @@ void TechnoExt::HandleAttachmentConversion(TechnoClass* pThis, TechnoTypeClass* int newRemaining = (oldRemaining * newDelay) / oldDelay; pNewMount->RespawnTimer.TimeLeft = newDelay; pNewMount->RespawnTimer.StartTime = static_cast(Unsorted::CurrentFrame) - (newDelay - newRemaining); + + (*it)->RespawnTimer.Stop(); // old must giveth teh state to teh new } oldMountsCopy.erase(it); + gotConverted = true; break; } + + // now that's what I call new - Kerbiter + if (!gotConverted && areNewAttachments) + pNewMount->OnCreated(); } // Step 4: Limbo all old mount points (matched ones have no child, so Limbo is a no-op for them); diff --git a/src/Ext/Techno/Hooks.Transport.cpp b/src/Ext/Techno/Hooks.Transport.cpp index 8416ebf863..2259408683 100644 --- a/src/Ext/Techno/Hooks.Transport.cpp +++ b/src/Ext/Techno/Hooks.Transport.cpp @@ -312,6 +312,7 @@ static inline void DoEnterNow(UnitClass* pTransport, FootClass* pPassenger, Tech } // Update after unit location update +// why is this hook in this file of all places? DEFINE_HOOK(0x4DA8A0, FootClass_Update_AfterLocomotorProcess, 0x6) { GET(FootClass* const, pThis, ESI); @@ -320,6 +321,9 @@ DEFINE_HOOK(0x4DA8A0, FootClass_Update_AfterLocomotorProcess, 0x6) const auto pExt = TechnoExt::ExtMap.Find(pThis); pExt->UpdateLaserTrails(); + for (auto const& attachment : pExt->ChildAttachments) + attachment->AI(); + // The core part of the fast enter action if (const auto pDest = abstract_cast(pThis->CurrentMission == Mission::Enter ? pThis->GetNthLink() : pThis->QueueUpToEnter)) { diff --git a/src/Ext/Techno/Hooks.cpp b/src/Ext/Techno/Hooks.cpp index cd59b3ca42..01ccc81493 100644 --- a/src/Ext/Techno/Hooks.cpp +++ b/src/Ext/Techno/Hooks.cpp @@ -83,6 +83,8 @@ DEFINE_HOOK(0x71A88D, TemporalClass_AI, 0x0) { GET(TemporalClass*, pThis, ESI); + // TODO maybe handle attachments on temporal? + if (auto const pTarget = pThis->Target) { pTarget->IsMouseHovering = false; diff --git a/src/Misc/Hooks.AI.cpp b/src/Misc/Hooks.AI.cpp index aecb10ca8f..7e60e45535 100644 --- a/src/Misc/Hooks.AI.cpp +++ b/src/Misc/Hooks.AI.cpp @@ -2,10 +2,10 @@ #include -DEFINE_HOOK(0x55B6B3, LogicClass_AI_After, 0x5) -{ - for (auto const& attachment : AttachmentClass::Array) - attachment->AI(); +// DEFINE_HOOK(0x55B6B3, LogicClass_AI_After, 0x5) +// { +// for (auto const& attachment : AttachmentClass::Array) +// attachment->AI(); - return 0; -} +// return 0; +// } diff --git a/src/New/Entity/AttachmentClass.cpp b/src/New/Entity/AttachmentClass.cpp index 8a3fcf13d2..2efd4f4457 100644 --- a/src/New/Entity/AttachmentClass.cpp +++ b/src/New/Entity/AttachmentClass.cpp @@ -44,7 +44,7 @@ AttachmentClass::~AttachmentClass() Array.erase(position); } -void AttachmentClass::Initialize() +void AttachmentClass::OnCreated() { if (this->Child) return; diff --git a/src/New/Entity/AttachmentClass.h b/src/New/Entity/AttachmentClass.h index a7bc65d28e..51693f0ea7 100644 --- a/src/New/Entity/AttachmentClass.h +++ b/src/New/Entity/AttachmentClass.h @@ -45,7 +45,7 @@ class AttachmentClass TechnoTypeClass* GetChildType(); CoordStruct GetChildLocation(); - void Initialize(); + void OnCreated(); void CreateChild(); void AI(); void Destroy(TechnoClass* pSource);