Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CREDITS.md
Original file line number Diff line number Diff line change
Expand Up @@ -242,6 +242,8 @@ This page lists all the individual contributions to the project by their author.
- Dump variables to file on scenario end / hotkey
- "House owns TechnoType" and "House doesn't own TechnoType" trigger events
- Help with docs
- **Multfinite**
- Techno Attachment logic: type conversion handling and `Attachment.ForcedLayer` flag
- **ChrisLv_CN** (work relicensed under [following permission](https://github.com/Phobos-developers/Phobos/blob/develop/images/ChrisLv-relicense.png)):
- General assistance
- Interceptor logic prototype
Expand Down
9 changes: 5 additions & 4 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,8 @@
<ClCompile Include="src\Ext\TEvent\Body.cpp" />
<ClCompile Include="src\Ext\Trigger\Hooks.cpp" />
<ClCompile Include="src\Ext\Unit\Hooks.Crushing.cpp" />
<ClCompile Include="src\Locomotion\TestLocomotionClass.cpp"/>
<ClCompile Include="src\Locomotion\AttachmentLocomotionClass.cpp"/>
<ClCompile Include="src\Locomotion\TestLocomotionClass.cpp" />
<ClCompile Include="src\Locomotion\AttachmentLocomotionClass.cpp" />
<ClCompile Include="src\Misc\Hooks.AssignHouses.cpp" />
<ClCompile Include="src\Misc\Hooks.Gamespeed.cpp" />
<ClCompile Include="src\Misc\Hooks.Ares.cpp" />
Expand Down Expand Up @@ -204,8 +204,8 @@
<ClInclude Include="src\Ext\TAction\Body.h" />
<ClInclude Include="src\Ext\Team\Body.h" />
<ClInclude Include="src\Ext\TEvent\Body.h" />
<ClInclude Include="src\Locomotion\TestLocomotionClass.h"/>
<ClInclude Include="src\Locomotion\AttachmentLocomotionClass.h"/>
<ClInclude Include="src\Locomotion\TestLocomotionClass.h" />
<ClInclude Include="src\Locomotion\AttachmentLocomotionClass.h" />
<ClInclude Include="src\Misc\BlittersFix.h" />
<ClInclude Include="src\Ext\CaptureManager\Body.h" />
<ClInclude Include="src\Misc\FlyingStrings.h" />
Expand All @@ -224,6 +224,7 @@
<ClInclude Include="src\New\Entity\ShieldClass.h" />
<ClInclude Include="src\New\Entity\AttachmentClass.h" />
<ClInclude Include="src\Utilities\Anchor.h" />
<ClInclude Include="src\Utilities\Misc.hpp" />
<ClInclude Include="src\Utilities\Enumerable.h" />
<ClInclude Include="src\Ext\Aircraft\Body.h" />
<ClInclude Include="src\Ext\AnimType\Body.h" />
Expand Down
31 changes: 31 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,32 @@ This feature is not final and is under development.
- 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).

```{warning}
This feature has the same limitations as [Ares' Type Conversion](https://ares-developers.github.io/Ares-docs/new/typeconversion.html).
```

There are way to define special rules for attachments during type conversion and it's the following:
- Attachment type and Techno type conversion rules via `AttachmentX.ConversionMode.Instance`
- `AlwaysPresent` which is `Default` forces attach to be always on unit and keep instance as-is
- `Switch` means that new attachment and techno instances will be created during type conversion - the current will be hidden and inactive, the new will be present on the unit
- If next attachment not present then the current just will be hidden and inactive
- Use it when you need to keep different state on objects
- `Convert` means that current instance will be converted and all state will be the same
- If TechnoTypes the same then chlid techno won't convert
- Attachment type will convert too
- `Switch` and `Convert` requires to be linked with attachment on other parent TechnoType via `AttachmentX.ID`. You **MUST** gurantee that each value is **unique**.
- Attachment timers can be affected too using `AttachmentX.ConversionMode.RespawnTimer.Current` and `AttachmentX.ConversionMode.RespawnTimer.Next`
- `Nothing` which is `Default` will do obviosly nothing
- `Freeze` flag if present will pause timer and otherwise will resume timer
- `InheritAbsolute` will works only for `Next` and it will transfer timer value from `Current` itself
- `InheritRelative` will works only for `Next` and it will transfer timer value from `Current` as percent between attachment entries
- `Instant` set left time to zero
- `Reset` set left time to `RespawnDelay`
- `FreezeAndInstant` - both `Freeze` & `Instant` effects
- `FreezeAndReset` - both `Freeze` & `Reset` effects
- `FreezeAndInheritAbsolute` - both `Freeze` & `InheritAbsolute` effects
- `FreezeAndInheritRelative` - both `Freeze` & `InheritRelative` effects

In `rulesmd.ini`:
```ini
[AttachmentTypes]
Expand All @@ -41,14 +67,19 @@ ParentDetachmentMission= ; MissionType, queued to child when it's d

[SOMETECHNO] ; TechnoTypeClass
; used when this techno is attached
Attachment.ForcedLayer= ; Layer None|Underground|Surface|Ground|Air|Top (don't set anything to disable this feature)
AttachmentTopLayerMinHeight= ; integer
AttachmentUndergroundLayerMaxHeight= ; integer
; used for attaching other technos
AttachmentX.ID=-1 ; int, unique ID to link attachments during type conversion. Index must be in range [0; inf+) and any negative value treated as NO ID
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.ConversionMode.Instance=Default ; AttachmentInstanceConversionMode: Default | AlwaysPresent | Switch | Convert
AttachmentX.ConversionMode.RespawnTimer.Current=Default ; AttachmentTimerConversionMode Default | Nothing | Freeze | InheritAbsolute | InheritRelative | Instant | Reset | FreezeAndInstant | FreezeAndReset | FreezeAndInheritAbsolute | FreezeAndInheritRelative
AttachmentX.ConversionMode.RespawnTimer.Next=Default ; AttachmentTimerConversionMode Default | Nothing | Freeze | InheritAbsolute | InheritRelative | Instant | Reset | FreezeAndInstant | FreezeAndReset | FreezeAndInheritAbsolute | FreezeAndInheritRelative

[General]
AttachmentTopLayerMinHeight=500 ; integer,
Expand Down
116 changes: 116 additions & 0 deletions src/Ext/Techno/Body.Update.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -482,6 +482,122 @@ void TechnoExt::ExtData::UpdateTypeData(TechnoTypeClass* pCurrentType)
pPassenger = abstract_cast<FootClass*>(pPassenger->NextObject);
}
}
// Techno attachment behavior during type conversion
if (
!this->TypeExtData->AttachmentData.empty() ||
!pOldTypeExt->AttachmentData.empty() ||
!this->ChildAttachments.empty() ||
!this->ChildAttachmentsPerType.empty()
) {
// Save attachment sequence for previous (it should be executed once and only for initial type in theory)
if (!this->ChildAttachmentsPerType.contains(pOldTypeExt))
{
auto& vector = this->ChildAttachmentsPerType[pOldTypeExt];
for (auto& a : this->ChildAttachments)
vector.push_back(a);
}
// Create or copy attachments for new type
// NOTE:
// Converting attachments are the same instances
// Switching attachments are new instances
if (!this->ChildAttachmentsPerType.contains(this->TypeExtData))
{
auto& vector = this->ChildAttachmentsPerType[this->TypeExtData];
for (auto& ae : this->TypeExtData->AttachmentData)
{
auto a = this->FindAttachmentForTypeByID(pOldTypeExt, ae.ID);
// do not make new attachment instance for jumping between types attachments
// i think that we should make inactive chlid & attachment which is off-field (i.e. disable updating it in-time)
if (!a || a->Data->ConversionMode_Instance.Get() == AttachmentInstanceConversionMode::Switch)
{
a = std::make_shared<AttachmentClass>(&ae, pThis, nullptr);
a->Initialize();
a->Limbo();
}
vector.push_back(a);
}
}
auto& vectorNew = this->ChildAttachmentsPerType[this->TypeExtData];

// Iterate over attachments of old type and do conversions if it needed
for (size_t i = 0; i < this->ChildAttachments.size(); i++)
{
auto& attachment = this->ChildAttachments.at(i);
auto const attachmentEntry = pOldTypeExt->AttachmentData.at(i);
auto const attachmentType = attachment->GetType();
auto const timeLeftCurrent = attachment->RespawnTimer.TimeLeft;

// Linked by ID attachment on new type
// You should know that in this case with switching attachment and attachmentNew are same instance if it were linked properly
auto attachmentNew = this->FindAttachmentForTypeByID(this->TypeExtData, attachmentEntry.ID);

// Apply conversion for chlid
switch (attachmentEntry.ConversionMode_Instance.Get())
{
case(AttachmentInstanceConversionMode::Convert):
{
// Type to convert is not define, so need just hide this as switching does.
// Exact same as switching wtthout linked by ID attachment
if (!attachmentNew)
{
attachment->Limbo();
attachment->UpdateRespawnTimerAtConversion(attachmentEntry.ConversionMode_RespawnTimer_Current);
}

auto const attachmentEntryNew = this->TypeExtData->GetAttachmentEntryByID(attachmentEntry.ID);
auto const attachmentTypeNew = AttachmentTypeClass::Array[attachmentEntryNew->Type].get();

auto pTechnoTypeNew = TechnoTypeClass::Array()->GetItem(attachmentEntryNew->TechnoType);
if (attachment->GetChildType() != attachmentNew->GetChildType() && !AresFunctions::ConvertTypeTo(attachment->Child, pTechnoTypeNew))
{
auto pTechnoType = TechnoTypeClass::Array()->GetItem(attachmentEntry.TechnoType);
Debug::Log("[Developer warning] Error during attachment {%s } techno type conversion { %s } -> { %s }\n", attachmentType->Name, pTechnoType->Name, pTechnoTypeNew->Name);
}
attachment->Data = attachmentEntryNew;

// It is the same instance of attachment, so it is single call
attachment->UpdateRespawnTimerAtConversion(
attachmentEntry.ConversionMode_RespawnTimer_Next,
timeLeftCurrent, attachmentType
);
} break;
case(AttachmentInstanceConversionMode::Switch):
{
attachment->Limbo();
attachment->UpdateRespawnTimerAtConversion(attachmentEntry.ConversionMode_RespawnTimer_Current);
if (attachmentNew)
{
attachmentNew->Unlimbo();
attachmentNew->UpdateRespawnTimerAtConversion(
attachmentEntry.ConversionMode_RespawnTimer_Next,
timeLeftCurrent, attachmentType
);
}
} break;
case(AttachmentInstanceConversionMode::AlwaysPresent):
default:
{
if (attachmentNew != nullptr && attachment == attachmentNew)
{
Debug::Log("[Developer warning] Always presented attachment { %s } of type { %s } has linked by ID { %u } other attachment { %s } of type { %s }\n",
attachmentType->Name, pOldType->Name,
attachmentEntry.ID,
attachmentNew->GetType()->Name, this->TypeExtData->OwnerObject()->Name
);
}

attachment->Unlimbo();
attachment->IsMigrating = true;
vectorNew.AddUnique(attachment);
} break;
}
}

// DO MAGIC
this->ChildAttachments.clear();
this->ChildAttachments.assign(vectorNew.begin(), vectorNew.end());
}

}

void TechnoExt::ExtData::UpdateLaserTrails()
Expand Down
55 changes: 53 additions & 2 deletions src/Ext/Techno/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,26 @@ bool TechnoExt::AllowedTargetByZone(TechnoClass* pThis, TechnoClass* pTarget, Ta
return true;
}

std::shared_ptr<AttachmentClass> TechnoExt::ExtData::FindAttachmentForTypeByID(TechnoTypeExt::ExtData* pTypeExt, int entryId)
{
if (entryId < 0) return nullptr;
auto& vector = ChildAttachmentsPerType[pTypeExt];
auto iter = std::find_if(vector.begin(), vector.end(), [&entryId](std::shared_ptr<AttachmentClass>& item) -> bool
{
return item->Data->ID.Get() == entryId;
});
return iter != vector.end() ? *iter : nullptr;
}

void TechnoExt::ExtData::RemoveAttachmentFromPerTypeLists(AttachmentClass* pWhat)
{
for (auto& vector : ChildAttachmentsPerType)
std::remove_if(vector.second.begin(), vector.second.end(), [&pWhat](const AttachmentClassPtr& item) -> bool
{
return item.get() == pWhat && item;
});
}

// Feature for common usage : TechnoType conversion -- Trsdy
// BTW, who said it was merely a Type pointer replacement and he could make a better one than Ares?
bool TechnoExt::ConvertToType(FootClass* pThis, TechnoTypeClass* pToType)
Expand Down Expand Up @@ -393,7 +413,7 @@ void TechnoExt::InitializeAttachments(TechnoClass* pThis)

for (auto& entry : pTypeExt->AttachmentData)
{
pExt->ChildAttachments.push_back(std::make_unique<AttachmentClass>(&entry, pThis, nullptr));
pExt->ChildAttachments.push_back(std::make_shared<AttachmentClass>(&entry, pThis, nullptr));
pExt->ChildAttachments.back()->Initialize();
}
}
Expand Down Expand Up @@ -439,15 +459,43 @@ void TechnoExt::TransferAttachments(TechnoClass* pThis, TechnoClass* pThat)
auto const pThisExt = TechnoExt::ExtMap.Find(pThis);
auto const pThatExt = TechnoExt::ExtMap.Find(pThat);

auto* pVectorOfThat = pThatExt->ChildAttachmentsPerType.contains(pThisExt->TypeExtData) ? &(pThatExt->ChildAttachmentsPerType.at(pThisExt->TypeExtData)) : nullptr;

for (auto& pAttachment : pThisExt->ChildAttachments)
{
pAttachment->Parent = pThat;
pThatExt->ChildAttachments.push_back(std::move(pAttachment));
pThisExt->RemoveAttachmentFromPerTypeLists(pAttachment.get());

pThatExt->ChildAttachments.push_back(pAttachment);
if (pVectorOfThat)
pVectorOfThat->push_back(pAttachment); // Non-safe for unique entry ID !
}

pThisExt->ChildAttachments.clear();
}

void TechnoExt::TransferAttachment(TechnoClass* pThis, TechnoClass* pThat, AttachmentClass* pWhat)
{
auto const pThisExt = TechnoExt::ExtMap.Find(pThis);
auto const pThatExt = TechnoExt::ExtMap.Find(pThat);

auto srcIter = std::find_if(pThisExt->ChildAttachments.begin(), pThisExt->ChildAttachments.end(), [&pWhat](const AttachmentClassPtr& item) -> bool
{
return item.get() == pWhat;
});
if (srcIter == pThisExt->ChildAttachments.end())
return;

(*srcIter)->Parent = pThat;

pThatExt->ChildAttachments.push_back(*srcIter);
if (pThatExt->ChildAttachmentsPerType.contains(pThisExt->TypeExtData))
pThatExt->ChildAttachmentsPerType.at(pThisExt->TypeExtData).push_back(*srcIter); // Non-safe for unique entry ID !

pThisExt->RemoveAttachmentFromPerTypeLists(pWhat);
pThisExt->ChildAttachments.erase(srcIter);
}

bool TechnoExt::IsAttached(TechnoClass* pThis)
{
auto const& pExt = TechnoExt::ExtMap.Find(pThis);
Expand Down Expand Up @@ -518,6 +566,9 @@ void TechnoExt::ExtData::Serialize(T& Stm)
.Process(this->DeployFireTimer)
.Process(this->ForceFullRearmDelay)
.Process(this->WHAnimRemainingCreationInterval)
// In theory it should be serialized too
//.Process(this->ChildAttachments)
//.Process(this->ChildAttachmentsPerType)
;
}

Expand Down
11 changes: 10 additions & 1 deletion src/Ext/Techno/Body.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ class TechnoExt
static constexpr DWORD Canary = 0x55555555;
static constexpr size_t ExtPointerOffset = 0x34C;

using AttachmentClassPtr = std::shared_ptr<AttachmentClass>;
using AttachmentVector = ValueableVector<AttachmentClassPtr>;

class ExtData final : public Extension<TechnoClass>
{
public:
Expand All @@ -46,7 +49,8 @@ class TechnoExt
HouseClass* OriginalPassengerOwner;

AttachmentClass* ParentAttachment;
ValueableVector<std::unique_ptr<AttachmentClass>> ChildAttachments;
AttachmentVector ChildAttachments;
std::map<TechnoTypeExt::ExtData*, AttachmentVector> ChildAttachmentsPerType;

ExtData(TechnoClass* OwnerObject) : Extension<TechnoClass>(OwnerObject)
, TypeExtData { nullptr }
Expand All @@ -69,6 +73,7 @@ class TechnoExt
, CanCurrentlyDeployIntoBuilding { false }
, ParentAttachment {}
, ChildAttachments {}
, ChildAttachmentsPerType { }
{ }

void OnEarlyUpdate();
Expand All @@ -85,6 +90,9 @@ class TechnoExt
void InitializeLaserTrails();
void UpdateMindControlAnim();

std::shared_ptr<AttachmentClass> FindAttachmentForTypeByID(TechnoTypeExt::ExtData* pTypeExt, int entryId);
void RemoveAttachmentFromPerTypeLists(AttachmentClass* pWhat);

virtual ~ExtData() override;

virtual void InvalidatePointer(void* ptr, bool bRemoved) override
Expand Down Expand Up @@ -152,6 +160,7 @@ class TechnoExt
static void UnlimboAttachments(TechnoClass* pThis);
static void LimboAttachments(TechnoClass* pThis);
static void TransferAttachments(TechnoClass* pThis, TechnoClass* pThat);
static void TransferAttachment(TechnoClass* pThis, TechnoClass* pThat, AttachmentClass* pWhat);

static bool IsAttached(TechnoClass* pThis);
static bool HasAttachmentLoco(FootClass* pThis); // FIXME shouldn't be here
Expand Down
Loading