From 16caecd6713bae4ccb0ede82c92a85ae02cda60d Mon Sep 17 00:00:00 2001 From: CCHyper <73803386+CCHyper@users.noreply.github.com> Date: Thu, 24 Mar 2022 14:19:27 +0000 Subject: [PATCH] Implements feature for animations to spawn additional animations. --- src/extensions/anim/animext.cpp | 127 ++++++++++++++++++++++++ src/extensions/anim/animext.h | 8 ++ src/extensions/anim/animext_hooks.cpp | 104 +++++++++++++++++++ src/extensions/animtype/animtypeext.cpp | 102 ++++++++++++++++++- src/extensions/animtype/animtypeext.h | 26 +++++ src/extensions/rules/rulesext_hooks.cpp | 15 +++ 6 files changed, 381 insertions(+), 1 deletion(-) diff --git a/src/extensions/anim/animext.cpp b/src/extensions/anim/animext.cpp index 283a19988..3b481e4fe 100644 --- a/src/extensions/anim/animext.cpp +++ b/src/extensions/anim/animext.cpp @@ -27,7 +27,10 @@ ******************************************************************************/ #include "animext.h" #include "anim.h" +#include "animtype.h" +#include "animtypeext.h" #include "wwcrc.h" +#include "tibsun_inline.h" #include "asserthandler.h" #include "debughandler.h" @@ -156,3 +159,127 @@ void AnimClassExtension::Compute_CRC(WWCRCEngine &crc) const ASSERT(ThisPtr != nullptr); //EXT_DEBUG_TRACE("AnimClassExtension::Compute_CRC - Name: %s (0x%08X)\n", ThisPtr->Name(), (uintptr_t)(ThisPtr)); } + + +/** + * Processes any start events. + * + * @author: CCHyper + */ +bool AnimClassExtension::Start() +{ + AnimTypeClassExtension *animext = AnimTypeClassExtensions.find(ThisPtr->Class); + if (!animext) { + return false; + } + + /** + * #issue-752 + * + * Spawns the start animations. + */ + Spawn_Animations(ThisPtr->Center_Coord(), animext->StartAnims, animext->StartAnimsCount, animext->StartAnimsMinimum, animext->StartAnimsMaximum); + + return true; +} + + +/** + * Processes any middle events. + * + * @author: CCHyper + */ +bool AnimClassExtension::Middle() +{ + AnimTypeClassExtension *animext = AnimTypeClassExtensions.find(ThisPtr->Class); + if (!animext) { + return false; + } + + /** + * #issue-752 + * + * Spawns the middle animations. + */ + Spawn_Animations(ThisPtr->Center_Coord(), animext->MiddleAnims, animext->MiddleAnimsCount, animext->MiddleAnimsMinimum, animext->MiddleAnimsMaximum); + + return true; +} + + +/** + * Processes any end events. + * + * @author: CCHyper + */ +bool AnimClassExtension::End() +{ + AnimTypeClassExtension *animext = AnimTypeClassExtensions.find(ThisPtr->Class); + if (!animext) { + return false; + } + + /** + * #issue-752 + * + * Spawns the end animations. + */ + Spawn_Animations(ThisPtr->Center_Coord(), animext->EndAnims, animext->EndAnimsCount, animext->EndAnimsMinimum, animext->EndAnimsMaximum); + + return true; +} + + +/** + * #issue-752 + * + * Spawns the requested animation from the parsed type lists. + * + * @author: CCHyper + */ +bool AnimClassExtension::Spawn_Animations(const Coordinate &coord, const TypeList &animlist, const TypeList &countlist, const TypeList &minlist, const TypeList &maxlist) +{ + if (!animlist.Count()) { + return false; + } + + /** + * Iterate over all animations set and spawn them. + */ + for (int index = 0; index < animlist.Count(); ++index) { + + const AnimTypeClass *animtype = animlist[index]; + + int count = 1; + + /** + * Pick a random count based on the minimum and maximum values + * defined and spawn the animations. + */ + if (animlist.Count() == countlist.Count()) { + count = countlist[index]; + + } else if (minlist.Count() && maxlist.Count()) { + + int min = minlist[index]; + int max = maxlist[index]; + + if (min != max) { + count = Random_Pick(std::min(min, max), std::max(min, max)); + } else { + count = std::min(min, max); + } + } + + /** + * Based on the count decided above, spawn the animation type. + */ + for (int i = 0; i < count; ++i) { + AnimClass *anim = new AnimClass(animtype, (Coordinate &)coord); + ASSERT(anim != nullptr); + } + } + + return true; +} + diff --git a/src/extensions/anim/animext.h b/src/extensions/anim/animext.h index a0f5d206d..09b8ff4a6 100644 --- a/src/extensions/anim/animext.h +++ b/src/extensions/anim/animext.h @@ -32,6 +32,7 @@ #include "ttimer.h" #include "ftimer.h" +#include "typelist.h" class AnimClass; @@ -52,6 +53,13 @@ class AnimClassExtension final : public Extension virtual void Detach(TARGET target, bool all = true) override; virtual void Compute_CRC(WWCRCEngine &crc) const override; + bool Start(); + bool Middle(); + bool End(); + + private: + bool Spawn_Animations(const Coordinate &coord, const TypeList &animlist, const TypeList &countlist, const TypeList &minlist, const TypeList &maxlist); + public: }; diff --git a/src/extensions/anim/animext_hooks.cpp b/src/extensions/anim/animext_hooks.cpp index 48a8011e4..e4e7fbd9e 100644 --- a/src/extensions/anim/animext_hooks.cpp +++ b/src/extensions/anim/animext_hooks.cpp @@ -29,6 +29,7 @@ #include "tibsun_globals.h" #include "tibsun_inline.h" #include "anim.h" +#include "animext.h" #include "animext_init.h" #include "animtype.h" #include "animtypeext.h" @@ -59,6 +60,8 @@ class AnimClassFake final : public AnimClass { public: LayerType _In_Which_Layer() const; + + }; @@ -137,6 +140,85 @@ static void Anim_Spawn_Particles(AnimClass *this_ptr) } +/** + * Calls the AnimClass extension middle event processor. + * + * @author: CCHyper + */ +DECLARE_PATCH(_AnimClass_Start_Ext_Patch) +{ + GET_REGISTER_STATIC(AnimClass *, this_ptr, esi); + static AnimClassExtension *animext; + + animext = AnimClassExtensions.find(this_ptr); + if (animext) { + animext->Start(); + } + +original_code: + _asm { pop esi } + _asm { pop ebx } + _asm { add esp, 0x24 } + _asm { retn } +} + + +/** + * Calls the AnimClass extension middle event processor. + * + * @author: CCHyper + */ +DECLARE_PATCH(_AnimClass_Middle_Ext_Patch) +{ + GET_REGISTER_STATIC(AnimClass *, this_ptr, esi); + static AnimClassExtension *animext; + + animext = AnimClassExtensions.find(this_ptr); + if (animext) { + animext->Middle(); + } + +original_code: + _asm { pop edi } + _asm { pop esi } + _asm { pop ebp } + _asm { add esp, 0x38 } + _asm { retn } +} + + +/** + * Calls the AnimClass extension middle event processor. + * + * @author: CCHyper + */ +DECLARE_PATCH(_AnimClass_AI_End_Ext_Patch) +{ + GET_REGISTER_STATIC(AnimClass *, this_ptr, esi); + static AnimClassExtension *animext; + + animext = AnimClassExtensions.find(this_ptr); + if (animext) { + animext->End(); + } + +original_code: + /** + * Restore expected register states. + */ + _asm { mov esi, this_ptr } + _asm { xor ebp, ebp} + + /** + * Stolen bytes/code. + */ + _asm { mov edx, [esi+0x64] } // this->Class + _asm { mov ecx, [edx+0x154] } // Class->ChainTo + + JMP_REG(edx, 0x00415B03); +} + + /** * #issue-568 * @@ -394,6 +476,28 @@ void AnimClassExtension_Hooks() */ AnimClassExtension_Init(); + /** + * This patch removes duplicate return in AnimClass::Middle, so we only + * need to hook one place. + */ + Patch_Jump(0x004162BD, 0x0041637C); + + Patch_Jump(0x00415F38, &_AnimClass_Start_Ext_Patch); + + /** + * Unfortunately, this manual patch is required because the code is optimised + * and reuses "this" (ESI), which we need for the ext patch. + */ + Patch_Byte(0x0041636D, 0x8B); // mov esi, [esi+0x68] -> mov ebp, [esi+0x68] + Patch_Byte(0x0041636D+1, 0x6E); // ^ + Patch_Byte(0x0041636D+2, 0x68); // ^ + Patch_Byte(0x00416370, 0x85); // test esi, esi -> test ebp, ebp + Patch_Byte(0x00416370+1, 0xED); // ^ + Patch_Byte(0x00416374, 0x55); // push esi -> push ebp + Patch_Jump(0x0041637C, &_AnimClass_Middle_Ext_Patch); + + Patch_Jump(0x00415AFA, &_AnimClass_AI_End_Ext_Patch); + Patch_Jump(0x00415ADA, &_AnimClass_AI_RandomLoop_Randomiser_BugFix_Patch); //Patch_Jump(0x00413C79, &_AnimClass_Constructor_Init_Class_Values_Patch); // Moved to AnimClassExtension due to patching conflict. Patch_Jump(0x00414E8F, &_AnimClass_AI_Beginning_Patch); diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index d4621f621..2662e0a9d 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -50,7 +50,19 @@ AnimTypeClassExtension::AnimTypeClassExtension(AnimTypeClass *this_ptr) : ZAdjust(0), AttachLayer(LAYER_NONE), ParticleToSpawn(PARTICLE_NONE), - NumberOfParticles(0) + NumberOfParticles(0), + StartAnims(), + StartAnimsCount(), + StartAnimsMinimum(), + StartAnimsMaximum(), + MiddleAnims(), + MiddleAnimsCount(), + MiddleAnimsMinimum(), + MiddleAnimsMaximum(), + EndAnims(), + EndAnimsCount(), + EndAnimsMinimum(), + EndAnimsMaximum() { ASSERT(ThisPtr != nullptr); //EXT_DEBUG_TRACE("AnimTypeClassExtension constructor - Name: %s (0x%08X)\n", ThisPtr->Name(), (uintptr_t)(ThisPtr)); @@ -101,7 +113,33 @@ HRESULT AnimTypeClassExtension::Load(IStream *pStm) return E_FAIL; } + StartAnims.Clear(); + StartAnimsCount.Clear(); + StartAnimsMinimum.Clear(); + StartAnimsMaximum.Clear(); + MiddleAnims.Clear(); + MiddleAnimsCount.Clear(); + MiddleAnimsMinimum.Clear(); + MiddleAnimsMaximum.Clear(); + EndAnims.Clear(); + EndAnimsCount.Clear(); + EndAnimsMinimum.Clear(); + EndAnimsMaximum.Clear(); + new (this) AnimTypeClassExtension(NoInitClass()); + + StartAnims.Load(pStm); + StartAnimsCount.Load(pStm); + StartAnimsMinimum.Load(pStm); + StartAnimsMaximum.Load(pStm); + MiddleAnims.Load(pStm); + MiddleAnimsCount.Load(pStm); + MiddleAnimsMinimum.Load(pStm); + MiddleAnimsMaximum.Load(pStm); + EndAnims.Load(pStm); + EndAnimsCount.Load(pStm); + EndAnimsMinimum.Load(pStm); + EndAnimsMaximum.Load(pStm); return hr; } @@ -122,6 +160,19 @@ HRESULT AnimTypeClassExtension::Save(IStream *pStm, BOOL fClearDirty) return hr; } + StartAnims.Save(pStm); + StartAnimsCount.Save(pStm); + StartAnimsMinimum.Save(pStm); + StartAnimsMaximum.Save(pStm); + MiddleAnims.Save(pStm); + MiddleAnimsCount.Save(pStm); + MiddleAnimsMinimum.Save(pStm); + MiddleAnimsMaximum.Save(pStm); + EndAnims.Save(pStm); + EndAnimsCount.Save(pStm); + EndAnimsMinimum.Save(pStm); + EndAnimsMaximum.Save(pStm); + return hr; } @@ -164,6 +215,18 @@ void AnimTypeClassExtension::Compute_CRC(WWCRCEngine &crc) const crc(AttachLayer); crc(NumberOfParticles); + crc(StartAnims.Count()); + crc(StartAnimsCount.Count()); + crc(StartAnimsMinimum.Count()); + crc(StartAnimsMaximum.Count()); + crc(MiddleAnims.Count()); + crc(MiddleAnimsCount.Count()); + crc(MiddleAnimsMinimum.Count()); + crc(MiddleAnimsMaximum.Count()); + crc(EndAnims.Count()); + crc(EndAnimsCount.Count()); + crc(EndAnimsMinimum.Count()); + crc(EndAnimsMaximum.Count()); } @@ -202,3 +265,40 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) return true; } + + +/** + * Fetches the extension data from the INI database. This function is to be + * called only after the main rules processing has been done. + * + * @author: CCHyper + */ +bool AnimTypeClassExtension::Post_Read_INI(CCINIClass &ini) +{ + ASSERT(ThisPtr != nullptr); + //EXT_DEBUG_TRACE("AnimTypeClassExtension::Post_Read_INI - Name: %s (0x%08X)\n", ThisPtr->Name(), (uintptr_t)(ThisPtr)); + EXT_DEBUG_WARNING("AnimTypeClassExtension::Post_Read_INI - Name: %s (0x%08X)\n", ThisPtr->Name(), (uintptr_t)(ThisPtr)); + + const char *ini_name = ThisPtr->Name(); + + if (!ini.Is_Present(ini_name)) { + return false; + } + + StartAnims = ini.Get_Anims(ini_name, "StartAnims", StartAnims); + StartAnimsCount = ini.Get_Integer_List(ini_name, "StartAnimsCount", StartAnimsCount); + StartAnimsMinimum = ini.Get_Integer_List(ini_name, "StartAnimsMinimum", StartAnimsMinimum); + StartAnimsMaximum = ini.Get_Integer_List(ini_name, "StartAnimsMaximum", StartAnimsMaximum); + + MiddleAnims = ini.Get_Anims(ini_name, "MiddleAnims", MiddleAnims); + MiddleAnimsCount = ini.Get_Integer_List(ini_name, "MiddleAnimsCount", MiddleAnimsCount); + MiddleAnimsMinimum = ini.Get_Integer_List(ini_name, "MiddleAnimsMinimum", MiddleAnimsMinimum); + MiddleAnimsMaximum = ini.Get_Integer_List(ini_name, "MiddleAnimsMaximum", MiddleAnimsMaximum); + + EndAnims = ini.Get_Anims(ini_name, "EndAnims", EndAnims); + EndAnimsCount = ini.Get_Integer_List(ini_name, "EndAnimsCount", EndAnimsCount); + EndAnimsMinimum = ini.Get_Integer_List(ini_name, "EndAnimsMinimum", EndAnimsMinimum); + EndAnimsMaximum = ini.Get_Integer_List(ini_name, "EndAnimsMaximum", EndAnimsMaximum); + + return true; +} diff --git a/src/extensions/animtype/animtypeext.h b/src/extensions/animtype/animtypeext.h index ec72cde17..371381601 100644 --- a/src/extensions/animtype/animtypeext.h +++ b/src/extensions/animtype/animtypeext.h @@ -29,6 +29,7 @@ #include "extension.h" #include "container.h" +#include "typelist.h" class AnimTypeClass; @@ -51,6 +52,7 @@ class AnimTypeClassExtension final : public Extension virtual void Compute_CRC(WWCRCEngine &crc) const override; bool Read_INI(CCINIClass &ini); + bool Post_Read_INI(CCINIClass &ini); public: /** @@ -85,6 +87,30 @@ class AnimTypeClassExtension final : public Extension * The number of the particle to spawn. */ unsigned NumberOfParticles; + + /** + * + */ + TypeList StartAnims; + TypeList StartAnimsCount; + TypeList StartAnimsMinimum; + TypeList StartAnimsMaximum; + + /** + * + */ + TypeList MiddleAnims; + TypeList MiddleAnimsCount; + TypeList MiddleAnimsMinimum; + TypeList MiddleAnimsMaximum; + + /** + * + */ + TypeList EndAnims; + TypeList EndAnimsCount; + TypeList EndAnimsMinimum; + TypeList EndAnimsMaximum; }; diff --git a/src/extensions/rules/rulesext_hooks.cpp b/src/extensions/rules/rulesext_hooks.cpp index 19834b2d1..48b15f892 100644 --- a/src/extensions/rules/rulesext_hooks.cpp +++ b/src/extensions/rules/rulesext_hooks.cpp @@ -29,6 +29,8 @@ #include "rulesext_init.h" #include "rulesext.h" #include "rules.h" +#include "animtype.h" +#include "animtypeext.h" #include "tiberium.h" #include "weapontype.h" #include "tibsun_globals.h" @@ -162,6 +164,19 @@ void RulesClassFake::_Process(CCINIClass &ini) if (RulesExtension) { RulesExtension->Process(ini); } + + /** + * Because of the additions added in #issue-752, we need to do a second + * pass on the AnimTypes. This is becauses it is a recursive operation if + * the animation referes to itself, but it has not yet been allocated. + */ + for (int i = ANIM_FIRST; i < AnimTypes.Count(); ++i) { + AnimTypeClass *animtype = AnimTypes[i]; + AnimTypeClassExtension *animtypeext = AnimTypeClassExtensions.find(animtype); + if (animtypeext) { + animtypeext->Post_Read_INI(ini); + } + } }