Skip to content
Merged
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
7 changes: 5 additions & 2 deletions Phobos.vcxproj
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<ClCompile Include="src\Commands\Dummy.h" />
<ClCompile Include="src\Ext\Sidebar\Body.cpp" />
<ClCompile Include="src\Ext\Sidebar\Hooks.cpp" />
<ClCompile Include="src\Ext\AnimType\Body.cpp" />
<ClCompile Include="src\Ext\Anim\Body.cpp" />
<ClCompile Include="src\Ext\TAction\Body.cpp" />
<ClCompile Include="src\Ext\TAction\Hooks.cpp" />
<ClCompile Include="src\Misc\CaptureManager.cpp" />
Expand All @@ -34,6 +34,8 @@
<ClCompile Include="src\New\Entity\LaserTrailClass.cpp" />
<ClCompile Include="src\Ext\Aircraft\Body.cpp" />
<ClCompile Include="src\Ext\Aircraft\Hooks.cpp" />
<ClCompile Include="src\Ext\AnimType\Body.cpp" />
<ClCompile Include="src\Ext\AnimType\Hooks.AnimCreateUnit.cpp" />
<ClCompile Include="src\Ext\Building\Body.cpp" />
<ClCompile Include="src\Ext\Building\Hooks.cpp" />
<ClCompile Include="src\Ext\BulletType\Body.cpp" />
Expand Down Expand Up @@ -95,8 +97,8 @@
<ClInclude Include="src\Commands\NextIdleHarvester.h" />
<ClInclude Include="src\Commands\ObjectInfo.h" />
<ClInclude Include="src\Commands\Commands.h" />
<ClInclude Include="src\Ext\AnimType\Body.h" />
<ClInclude Include="src\Ext\Sidebar\Body.h" />
<ClInclude Include="src\Ext\Anim\Body.h" />
<ClInclude Include="src\Ext\TAction\Body.h" />
<ClInclude Include="src\Misc\CaptureManager.h" />
<ClInclude Include="src\New\Entity\ShieldClass.h" />
Expand All @@ -107,6 +109,7 @@
<ClInclude Include="src\Utilities\Enumerable.h" />
<ClInclude Include="src\ExtraHeaders\ArmorType.h" />
<ClInclude Include="src\Ext\Aircraft\Body.h" />
<ClInclude Include="src\Ext\AnimType\Body.h" />
<ClInclude Include="src\Ext\Building\Body.h" />
<ClInclude Include="src\Ext\BulletType\Body.h" />
<ClInclude Include="src\Ext\Bullet\Body.h" />
Expand Down
2 changes: 1 addition & 1 deletion YRpp
Submodule YRpp updated 2 files
+5 −1 BuildingClass.h
+9 −6 CellClass.h
29 changes: 29 additions & 0 deletions docs/New-or-Enhanced-Logics.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,35 @@ RadColor=0,255,0 ; RGB
RadSiteWarhead=RadSite ; WarheadType
```

## Animations

### Anim-to-Unit

![image](_static/images/animToUnit.gif)

- Animations can now create (or "convert" to) units when they end.
- Because anims usually don't have an owner the unit will be created with civilian owner unless you use `DestroyAnim` which was modified to store owner and facing information from the destroyed unit.

In `rulesmd.ini`:
```ini
[SOMEUNIT] ; UnitType
DestroyAnim.Random=yes ; boolean, whether to randomize DestroyAnim
```

In `art.ini`:
```ini
[SOMEANIM] ; AnimationType
CreateUnit= ; UnitType
CreateUnit.Facing=0 ; unsigned short, `CreateUnit` facings in range of 0-255
CreateUnit.RandomFacing=yes ; boolean, `CreateUnit` use random facings
CreateUnit.InheritFacings=no ; boolean, inherit facing from destroyed unit
CreateUnit.InheritTurretFacings=no ; boolean, inherit facing from destroyed unit
CreateUnit.RemapAnim=no ; boolean, whether to remap anim to owner color
CreateUnit.Mission=Guard ; MissionType
CreateUnit.Owner=Victim ; owner house kind, Invoker/Killer/Victim/Civilian/Special/Neutral/Random
```


## Buildings

### Extended building upgrades logic
Expand Down
Binary file added docs/_static/images/animToUnit.gif
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
118 changes: 118 additions & 0 deletions src/Ext/Anim/Body.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,118 @@
#include "Body.h"

#include <Ext/AnimType/Body.h>
#include <Ext/House/Body.h>

template<> const DWORD Extension<AnimClass>::Canary = 0xAAAAAAAA;
AnimExt::ExtContainer AnimExt::ExtMap;

//Modified from Ares
const bool AnimExt::SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker, HouseClass* pVictim, bool defaultToVictimOwner)
{
auto const pTypeExt = AnimTypeExt::ExtMap.Find(pAnim->Type);
auto newOwner = HouseExt::GetHouseKind(pTypeExt->CreateUnit_Owner.Get(), true, defaultToVictimOwner ? pVictim : nullptr, pInvoker, pVictim);

if (newOwner)
{
pAnim->Owner = newOwner;

if (pTypeExt->CreateUnit_RemapAnim.Get() && !newOwner->Defeated)
pAnim->LightConvert = ColorScheme::Array->Items[newOwner->ColorSchemeIndex]->LightConvert;
}

return newOwner;
}

// =============================
// load / save

template <typename T>
void AnimExt::ExtData::Serialize(T& Stm)
{
Stm
.Process(this->DeathUnitFacing)
.Process(this->FromDeathUnit)
.Process(this->DeathUnitTurretFacing)
.Process(this->DeathUnitHasTurret)
;
}

void AnimExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
{
Extension<AnimClass>::LoadFromStream(Stm);
this->Serialize(Stm);
}

void AnimExt::ExtData::SaveToStream(PhobosStreamWriter& Stm)
{
Extension<AnimClass>::SaveToStream(Stm);
this->Serialize(Stm);
}

// =============================
// container

AnimExt::ExtContainer::ExtContainer() : Container("AnimClass") { }
AnimExt::ExtContainer::~ExtContainer() = default;

// =============================
// container hooks

DEFINE_HOOK_AGAIN(0x422126, AnimClass_CTOR, 0x5)
DEFINE_HOOK_AGAIN(0x422707, AnimClass_CTOR, 0x5)
DEFINE_HOOK(0x4228D2, AnimClass_CTOR, 0x5)
{
GET(AnimClass*, pItem, ESI);

AnimExt::ExtMap.FindOrAllocate(pItem);
return 0;
}

DEFINE_HOOK(0x422967, AnimClass_DTOR, 0x6)
{
GET(AnimClass*, pItem, ESI);

if (AnimExt::ExtMap.Find(pItem))
AnimExt::ExtMap.Remove(pItem);

R->EAX(pItem->Type);

return 0;
}

/*Crash when Anim called with GameDelete()
DEFINE_HOOK(0x426598, AnimClass_SDDTOR, 0x7)
{
GET(AnimClass*, pItem, ESI);

if(AnimExt::ExtMap.Find(pItem))
AnimExt::ExtMap.Remove(pItem);

return 0;
}
*/

DEFINE_HOOK_AGAIN(0x425280, AnimClass_SaveLoad_Prefix, 0x5)
DEFINE_HOOK(0x4253B0, AnimClass_SaveLoad_Prefix, 0x5)
{
GET_STACK(AnimClass*, pItem, 0x4);
GET_STACK(IStream*, pStm, 0x8);

AnimExt::ExtMap.PrepareStream(pItem, pStm);

return 0;
}

DEFINE_HOOK_AGAIN(0x425391, AnimClass_Load_Suffix, 0x7)
DEFINE_HOOK_AGAIN(0x4253A2, AnimClass_Load_Suffix, 0x7)
DEFINE_HOOK(0x425358, AnimClass_Load_Suffix, 0x7)
{
AnimExt::ExtMap.LoadStatic();
return 0;
}

DEFINE_HOOK(0x4253FF, AnimClass_Save_Suffix, 0x5)
{
AnimExt::ExtMap.SaveStatic();
return 0;
}
50 changes: 50 additions & 0 deletions src/Ext/Anim/Body.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#pragma once
#include <AnimClass.h>

#include <Helpers/Macro.h>
#include <Utilities/Container.h>
#include <Utilities/TemplateDef.h>

class AnimExt
{
public:
using base_type = AnimClass;

class ExtData final : public Extension<AnimClass>
{
public:
short DeathUnitFacing;
DirStruct DeathUnitTurretFacing;
bool FromDeathUnit;
bool DeathUnitHasTurret;

ExtData(AnimClass* OwnerObject) : Extension<AnimClass>(OwnerObject)
, DeathUnitFacing(0)
, DeathUnitTurretFacing()
, FromDeathUnit(false)
, DeathUnitHasTurret(false)
{ }

virtual ~ExtData() = default;

virtual void InvalidatePointer(void* ptr, bool bRemoved) override { }

virtual void LoadFromStream(PhobosStreamReader& Stm) override;
virtual void SaveToStream(PhobosStreamWriter& Stm) override;

private:
template <typename T>
void Serialize(T& Stm);
};

class ExtContainer final : public Container<AnimExt>
{
public:
ExtContainer();
~ExtContainer();
};

static ExtContainer ExtMap;

static const bool SetAnimOwnerHouseKind(AnimClass* pAnim, HouseClass* pInvoker , HouseClass* pVictim, bool defaultToVictimOwner = true);
};
101 changes: 84 additions & 17 deletions src/Ext/AnimType/Body.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,13 @@
#include <Phobos.h>
#include <Helpers/Macro.h>
#include <Utilities/TemplateDef.h>
#include <AnimClass.h>
#include <HouseTypeClass.h>
#include <HouseClass.h>
#include <ScenarioClass.h>
#include <UnitClass.h>

#include <Ext/Anim/Body.h>
#include <Ext/TechnoType/Body.h>

template<> const DWORD Extension<AnimTypeClass>::Canary = 0xEEEEEEEE;
AnimTypeExt::ExtContainer AnimTypeExt::ExtMap;
Expand All @@ -17,39 +20,103 @@ void AnimTypeExt::ExtData::LoadFromINIFile(CCINIClass* pINI)
INI_EX exINI(pINI);

this->Palette.LoadFromINI(pINI, pID, "CustomPalette");
this->CreateUnit.Read(exINI, pID, "CreateUnit");
this->CreateUnit_Facing.Read(exINI, pID, "CreateUnit.Facing");
this->CreateUnit_InheritDeathFacings.Read(exINI, pID, "CreateUnit.InheritFacings");
this->CreateUnit_InheritTurretFacings.Read(exINI, pID, "CreateUnit.InheritTurretFacings");
this->CreateUnit_RemapAnim.Read(exINI, pID, "CreateUnit.RemapAnim");
this->CreateUnit_Mission.Read(exINI, pID, "CreateUnit.Mission");
this->CreateUnit_Owner.Read(exINI, pID, "CreateUnit.Owner");
this->CreateUnit_RandomFacing.Read(exINI, pID, "CreateUnit.RandomFacing");
}

// =============================
// container

AnimTypeExt::ExtContainer::ExtContainer() : Container("AnimTypeClass") { }

AnimTypeExt::ExtContainer::~ExtContainer() = default;

// =============================
// load / save
const void AnimTypeExt::ProcessDestroyAnims(UnitClass* pThis, TechnoClass* pKiller)
{
if (!pThis)
return;

HouseClass* pInvoker = pKiller ? pKiller->Owner : nullptr;

if (pThis->Type->DestroyAnim.Count > 0)
{
auto const facing = pThis->PrimaryFacing.current().value256();
auto pAnimType = pThis->Type->DestroyAnim[ScenarioClass::Instance->Random.Random() % pThis->Type->DestroyAnim.Count];
auto const pTypeExt = TechnoTypeExt::ExtMap.Find(pThis->Type);

if (!pTypeExt->DestroyAnim_Random.Get())
{
int idxAnim = 0;

if (pThis->Type->DestroyAnim.Count >= 8)
{
idxAnim = pThis->Type->DestroyAnim.Count;
if (pThis->Type->DestroyAnim.Count % 2 == 0)
idxAnim *= static_cast<int>(facing / 256.0);
}

pAnimType = pThis->Type->DestroyAnim[idxAnim];
}

if (pAnimType)
{
if (auto const pAnim = GameCreate<AnimClass>(pAnimType, pThis->GetCoords()))
{
//auto VictimOwner = pThis->IsMindControlled() && pThis->GetOriginalOwner()
// ? pThis->GetOriginalOwner() : pThis->Owner;

auto const pAnimTypeExt = AnimTypeExt::ExtMap.Find(pAnim->Type);
auto const pAnimExt = AnimExt::ExtMap.Find(pAnim);

AnimExt::SetAnimOwnerHouseKind(pAnim, pInvoker, pThis->Owner);

pAnimExt->FromDeathUnit = true;

if (pAnimTypeExt->CreateUnit_InheritDeathFacings.Get())
pAnimExt->DeathUnitFacing = facing;

if (pAnimTypeExt->CreateUnit_InheritTurretFacings.Get())
{
if (pThis->HasTurret())
{
pAnimExt->DeathUnitHasTurret = true;
pAnimExt->DeathUnitTurretFacing = pThis->SecondaryFacing.current();
}
}
}
}
}
}

template <typename T>
void AnimTypeExt::ExtData::Serialize(T & Stm)
void AnimTypeExt::ExtData::Serialize(T& Stm)
{
Stm
.Process(this->Palette);
.Process(this->Palette)
.Process(this->CreateUnit)
.Process(this->CreateUnit_Facing)
.Process(this->CreateUnit_InheritDeathFacings)
.Process(this->CreateUnit_RemapAnim)
.Process(this->CreateUnit_Mission)
.Process(this->CreateUnit_InheritTurretFacings)
.Process(this->CreateUnit_Owner)
.Process(this->CreateUnit_RandomFacing)
;
}

void AnimTypeExt::ExtData::LoadFromStream(PhobosStreamReader & Stm)
void AnimTypeExt::ExtData::LoadFromStream(PhobosStreamReader& Stm)
{
Extension<AnimTypeClass>::LoadFromStream(Stm);
this->Serialize(Stm);
}

void AnimTypeExt::ExtData::SaveToStream(PhobosStreamWriter & Stm)
void AnimTypeExt::ExtData::SaveToStream(PhobosStreamWriter& Stm)
{
Extension<AnimTypeClass>::SaveToStream(Stm);
this->Serialize(Stm);
}

// =============================
// container hooks
AnimTypeExt::ExtContainer::ExtContainer() : Container("AnimTypeClass") { }
AnimTypeExt::ExtContainer::~ExtContainer() = default;

DEFINE_HOOK(0x42784B, AnimTypeClass_CTOR, 0x5)
{
Expand Down Expand Up @@ -99,4 +166,4 @@ DEFINE_HOOK(0x4287DC, AnimTypeClass_LoadFromINI, 0xA)

AnimTypeExt::ExtMap.LoadFromINI(pItem, pINI);
return 0;
}
}
Loading