From 7414d044a2070077508a4d444433bdecf9fcde95 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 01/11] Implements feature for animations to spawn additional animations. --- src/extensions/anim/animext.cpp | 125 ++++++++++++++++++++++++ src/extensions/anim/animext.h | 10 ++ src/extensions/anim/animext_hooks.cpp | 99 +++++++++++++++++++ src/extensions/animtype/animtypeext.cpp | 101 ++++++++++++++++++- src/extensions/animtype/animtypeext.h | 30 ++++++ 5 files changed, 364 insertions(+), 1 deletion(-) diff --git a/src/extensions/anim/animext.cpp b/src/extensions/anim/animext.cpp index ae8e5a7e2..44837da12 100644 --- a/src/extensions/anim/animext.cpp +++ b/src/extensions/anim/animext.cpp @@ -27,6 +27,9 @@ ******************************************************************************/ #include "animext.h" #include "anim.h" +#include "animtype.h" +#include "animtypeext.h" +#include "tibsun_inline.h" #include "wwcrc.h" #include "extension.h" #include "asserthandler.h" @@ -164,3 +167,125 @@ void AnimClassExtension::Compute_CRC(WWCRCEngine &crc) const { //EXT_DEBUG_TRACE("AnimClassExtension::Compute_CRC - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); } + + +/** + * Processes any start events. + * + * @author: CCHyper + */ +bool AnimClassExtension::Start() +{ + AnimTypeClassExtension *animtypeext = Extension::Fetch(This()->Class); + + /** + * #issue-752 + * + * Spawns the start animations. + */ + Spawn_Animations(This()->Center_Coord(), animtypeext->StartAnims, animtypeext->StartAnimsCount, animtypeext->StartAnimsMinimum, animtypeext->StartAnimsMaximum); + + return true; +} + + +/** + * Processes any middle events. + * + * @author: CCHyper + */ +bool AnimClassExtension::Middle() +{ + AnimTypeClassExtension *animtypeext = Extension::Fetch(This()->Class); + + /** + * #issue-752 + * + * Spawns the middle animations. + */ + Spawn_Animations(This()->Center_Coord(), animtypeext->MiddleAnims, animtypeext->MiddleAnimsCount, animtypeext->MiddleAnimsMinimum, animtypeext->MiddleAnimsMaximum); + + return true; +} + + +/** + * Processes any end events. + * + * @author: CCHyper + */ +bool AnimClassExtension::End() +{ + AnimTypeClassExtension *animtypeext = Extension::Fetch(This()->Class); + + /** + * #issue-752 + * + * Spawns the end animations. + */ + Spawn_Animations(This()->Center_Coord(), animtypeext->EndAnims, animtypeext->EndAnimsCount, animtypeext->EndAnimsMinimum, animtypeext->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; + } + + /** + * Some checks to make sure values are within expected ranges. + */ + if (!countlist.Count()) { + ASSERT(animlist.Count() == minlist.Count()); + ASSERT(animlist.Count() == maxlist.Count()); + } + + /** + * 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 fc991d150..7532aa914 100644 --- a/src/extensions/anim/animext.h +++ b/src/extensions/anim/animext.h @@ -29,6 +29,9 @@ #include "objectext.h" #include "anim.h" +#include "ttimer.h" +#include "ftimer.h" +#include "typelist.h" class AnimClass; @@ -63,5 +66,12 @@ AnimClassExtension final : public ObjectClassExtension virtual const AnimClass *This_Const() const override { return reinterpret_cast(ObjectClassExtension::This_Const()); } virtual RTTIType What_Am_I() const override { return RTTI_ANIM; } + 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 f8be329d7..9ba914357 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" @@ -136,6 +137,82 @@ 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 = Extension::Fetch(this_ptr); + + 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 = Extension::Fetch(this_ptr); + + 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 = Extension::Fetch(this_ptr); + + 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 * @@ -384,6 +461,28 @@ void AnimClassExtension_Hooks() */ AnimClassExtension_Init(); + Patch_Jump(0x00415F38, &_AnimClass_Start_Ext_Patch); + + /** + * This patch removes duplicate return in AnimClass::Middle, so we only + * need to hook one place. + */ + Patch_Jump(0x004162BD, 0x0041637C); + + /** + * 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 491e8d69f..6e33e37dd 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -29,6 +29,7 @@ #include "animtype.h" #include "ccini.h" #include "tibsun_defines.h" +#include "vinifera_saveload.h" #include "wwcrc.h" #include "extension.h" #include "asserthandler.h" @@ -47,7 +48,19 @@ AnimTypeClassExtension::AnimTypeClassExtension(const 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() { //if (this_ptr) EXT_DEBUG_TRACE("AnimTypeClassExtension::AnimTypeClassExtension - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); @@ -113,7 +126,37 @@ 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); + + VINIFERA_SWIZZLE_REQUEST_POINTER_REMAP_LIST(StartAnims, "StartAnims"); + VINIFERA_SWIZZLE_REQUEST_POINTER_REMAP_LIST(MiddleAnims, "MiddleAnims"); + VINIFERA_SWIZZLE_REQUEST_POINTER_REMAP_LIST(EndAnims, "EndAnims"); return hr; } @@ -133,6 +176,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; } @@ -174,6 +230,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()); } @@ -244,6 +312,37 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) ParticleToSpawn = ini.Get_ParticleType(ini_name, "SpawnsParticle", ParticleToSpawn); NumberOfParticles = ini.Get_Int(ini_name, "NumParticles", NumberOfParticles); + 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); + + if (!StartAnimsCount.Count()) { + ASSERT_FATAL(StartAnims.Count() == StartAnimsMinimum.Count()); + ASSERT_FATAL(StartAnims.Count() == StartAnimsMaximum.Count()); + } + + 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); + + if (!MiddleAnimsCount.Count()) { + ASSERT_FATAL(MiddleAnims.Count() == MiddleAnimsMinimum.Count()); + ASSERT_FATAL(MiddleAnims.Count() == MiddleAnimsMaximum.Count()); + } + + 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); + + if (!EndAnimsCount.Count()) { + ASSERT_FATAL(EndAnims.Count() == EndAnimsMinimum.Count()); + ASSERT_FATAL(EndAnims.Count() == EndAnimsMaximum.Count()); + } + + IsInitialized = true; return true; diff --git a/src/extensions/animtype/animtypeext.h b/src/extensions/animtype/animtypeext.h index 3f8ae31d2..6f486996a 100644 --- a/src/extensions/animtype/animtypeext.h +++ b/src/extensions/animtype/animtypeext.h @@ -29,6 +29,12 @@ #include "objecttypeext.h" #include "animtype.h" +#include "typelist.h" + + +class AnimTypeClass; +class CCINIClass; +class ParticleTypeClass; class DECLSPEC_UUID(UUID_ANIMTYPE_EXTENSION) @@ -94,4 +100,28 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension * The number of the particle to spawn. */ unsigned NumberOfParticles; + + /** + * List of animations to spawn at the logical start of this animation. + */ + TypeList StartAnims; + TypeList StartAnimsCount; + TypeList StartAnimsMinimum; + TypeList StartAnimsMaximum; + + /** + * List of animations to spawn at the logical middle of this animation. + */ + TypeList MiddleAnims; + TypeList MiddleAnimsCount; + TypeList MiddleAnimsMinimum; + TypeList MiddleAnimsMaximum; + + /** + * List of animations to spawn at the logical end of this animation. + */ + TypeList EndAnims; + TypeList EndAnimsCount; + TypeList EndAnimsMinimum; + TypeList EndAnimsMaximum; }; From 9d713eb2d2abfff3e203407a48db0a38bf1e7a7a Mon Sep 17 00:00:00 2001 From: CCHyper <73803386+CCHyper@users.noreply.github.com> Date: Fri, 25 Nov 2022 00:20:17 +0000 Subject: [PATCH 02/11] Implements MiddleFrame for AnimTypes. --- src/extensions/animtype/animtypeext.cpp | 55 ++++++++++++++++++++----- src/extensions/animtype/animtypeext.h | 6 +++ 2 files changed, 51 insertions(+), 10 deletions(-) diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index 6e33e37dd..5dbab9db0 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -60,7 +60,9 @@ AnimTypeClassExtension::AnimTypeClassExtension(const AnimTypeClass *this_ptr) : EndAnims(), EndAnimsCount(), EndAnimsMinimum(), - EndAnimsMaximum() + EndAnimsMaximum(), + BiggestFrameWidth(0), + BiggestFrameHeight(0) { //if (this_ptr) EXT_DEBUG_TRACE("AnimTypeClassExtension::AnimTypeClassExtension - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); @@ -313,9 +315,9 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) NumberOfParticles = ini.Get_Int(ini_name, "NumParticles", NumberOfParticles); 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); + StartAnimsCount = ini.Get_Integers(ini_name, "StartAnimsCount", StartAnimsCount); + StartAnimsMinimum = ini.Get_Integers(ini_name, "StartAnimsMinimum", StartAnimsMinimum); + StartAnimsMaximum = ini.Get_Integers(ini_name, "StartAnimsMaximum", StartAnimsMaximum); if (!StartAnimsCount.Count()) { ASSERT_FATAL(StartAnims.Count() == StartAnimsMinimum.Count()); @@ -323,9 +325,9 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) } 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); + MiddleAnimsCount = ini.Get_Integers(ini_name, "MiddleAnimsCount", MiddleAnimsCount); + MiddleAnimsMinimum = ini.Get_Integers(ini_name, "MiddleAnimsMinimum", MiddleAnimsMinimum); + MiddleAnimsMaximum = ini.Get_Integers(ini_name, "MiddleAnimsMaximum", MiddleAnimsMaximum); if (!MiddleAnimsCount.Count()) { ASSERT_FATAL(MiddleAnims.Count() == MiddleAnimsMinimum.Count()); @@ -333,14 +335,47 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) } 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); + EndAnimsCount = ini.Get_Integers(ini_name, "EndAnimsCount", EndAnimsCount); + EndAnimsMinimum = ini.Get_Integers(ini_name, "EndAnimsMinimum", EndAnimsMinimum); + EndAnimsMaximum = ini.Get_Integers(ini_name, "EndAnimsMaximum", EndAnimsMaximum); if (!EndAnimsCount.Count()) { ASSERT_FATAL(EndAnims.Count() == EndAnimsMinimum.Count()); ASSERT_FATAL(EndAnims.Count() == EndAnimsMaximum.Count()); } + + /** + * #issue-883 + * + * The "biggest" frame of a animation is frame which should hide all cosmetic + * changes to the underlaying ground (e.g. craters) that the animation causes, + * so these effects are delayed until this frame is reached. TibSun calculates + * this by scanning the entire shape file to find the largest visible frame, but + * in some cases, this might not be ideal (e.g. the shape has consistent frame + * dimensions). This new value allows the frame in which these effects are + * spawned be set. + * + * A special value of "-1" will set the biggest frame to the actual middle frame + * of the shape file. This behavior was observed in Red Alert 2. + */ + if (This()->Image != nullptr && This()->Image->Get_Frame_Count() > 0) { + ShapeFileStruct *image = const_cast(This()->Image); + + int biggest = ini.Get_Int_Clamp(ini_name, "MiddleFrame", -1, image->Get_Frame_Count()-1, This()->Biggest); + + if (biggest == -1 && image->Get_Frame_Count() >= 2) { + + This()->Biggest = image->Get_Frame_Count() / 2; + BiggestFrameWidth = image->Get_Frame_Data(This()->Biggest)->FrameWidth; + BiggestFrameHeight = image->Get_Frame_Data(This()->Biggest)->FrameHeight; + + } else if (biggest != This()->Biggest) { + + This()->Biggest = biggest; + BiggestFrameWidth = image->Get_Frame_Data(biggest)->FrameWidth; + BiggestFrameHeight = image->Get_Frame_Data(biggest)->FrameHeight; + } + } IsInitialized = true; diff --git a/src/extensions/animtype/animtypeext.h b/src/extensions/animtype/animtypeext.h index 6f486996a..fdd75caac 100644 --- a/src/extensions/animtype/animtypeext.h +++ b/src/extensions/animtype/animtypeext.h @@ -124,4 +124,10 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension TypeList EndAnimsCount; TypeList EndAnimsMinimum; TypeList EndAnimsMaximum; + + /** + * The width and height of the biggest frame, if set by the user. + */ + unsigned BiggestFrameWidth; + unsigned BiggestFrameHeight; }; From 4863449f6531f7b90719848ca42c5478caf4ee72 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Sun, 27 Oct 2024 21:49:10 +0300 Subject: [PATCH 03/11] Fix save/load --- src/extensions/animtype/animtypeext.cpp | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index 5dbab9db0..025bbe75b 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -76,7 +76,19 @@ AnimTypeClassExtension::AnimTypeClassExtension(const AnimTypeClass *this_ptr) : * @author: CCHyper */ AnimTypeClassExtension::AnimTypeClassExtension(const NoInitClass &noinit) : - ObjectTypeClassExtension(noinit) + ObjectTypeClassExtension(noinit), + StartAnims(noinit), + StartAnimsCount(noinit), + StartAnimsMinimum(noinit), + StartAnimsMaximum(noinit), + MiddleAnims(noinit), + MiddleAnimsCount(noinit), + MiddleAnimsMinimum(noinit), + MiddleAnimsMaximum(noinit), + EndAnims(noinit), + EndAnimsCount(noinit), + EndAnimsMinimum(noinit), + EndAnimsMaximum(noinit) { //EXT_DEBUG_TRACE("AnimTypeClassExtension::AnimTypeClassExtension(NoInitClass) - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); } @@ -123,11 +135,6 @@ HRESULT AnimTypeClassExtension::Load(IStream *pStm) { //EXT_DEBUG_TRACE("AnimTypeClassExtension::Load - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); - HRESULT hr = ObjectTypeClassExtension::Load(pStm); - if (FAILED(hr)) { - return E_FAIL; - } - StartAnims.Clear(); StartAnimsCount.Clear(); StartAnimsMinimum.Clear(); @@ -141,6 +148,11 @@ HRESULT AnimTypeClassExtension::Load(IStream *pStm) EndAnimsMinimum.Clear(); EndAnimsMaximum.Clear(); + HRESULT hr = ObjectTypeClassExtension::Load(pStm); + if (FAILED(hr)) { + return E_FAIL; + } + new (this) AnimTypeClassExtension(NoInitClass()); StartAnims.Load(pStm); From 55937ca859e71e1e54e725c94d68e4c97c07771b Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 00:40:46 +0300 Subject: [PATCH 04/11] Fix middle frame --- src/extensions/animtype/animtypeext.cpp | 49 ++++++++++++------- src/extensions/animtype/animtypeext.h | 8 +-- src/extensions/animtype/animtypeext_hooks.cpp | 23 +++++++++ 3 files changed, 59 insertions(+), 21 deletions(-) diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index 025bbe75b..b2dbf42c3 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -61,8 +61,7 @@ AnimTypeClassExtension::AnimTypeClassExtension(const AnimTypeClass *this_ptr) : EndAnimsCount(), EndAnimsMinimum(), EndAnimsMaximum(), - BiggestFrameWidth(0), - BiggestFrameHeight(0) + MiddleFrame(-2) { //if (this_ptr) EXT_DEBUG_TRACE("AnimTypeClassExtension::AnimTypeClassExtension - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); @@ -370,27 +369,41 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) * A special value of "-1" will set the biggest frame to the actual middle frame * of the shape file. This behavior was observed in Red Alert 2. */ - if (This()->Image != nullptr && This()->Image->Get_Frame_Count() > 0) { - ShapeFileStruct *image = const_cast(This()->Image); - - int biggest = ini.Get_Int_Clamp(ini_name, "MiddleFrame", -1, image->Get_Frame_Count()-1, This()->Biggest); + if (This()->Image && This()->Image->Get_Frame_Count() > 0) { + MiddleFrame = ini.Get_Int_Clamp(ini_name, "MiddleFrame", -2, This()->Image->Get_Frame_Count() - 1, MiddleFrame); + } - if (biggest == -1 && image->Get_Frame_Count() >= 2) { + IsInitialized = true; - This()->Biggest = image->Get_Frame_Count() / 2; - BiggestFrameWidth = image->Get_Frame_Data(This()->Biggest)->FrameWidth; - BiggestFrameHeight = image->Get_Frame_Data(This()->Biggest)->FrameHeight; + return true; +} - } else if (biggest != This()->Biggest) { - This()->Biggest = biggest; - BiggestFrameWidth = image->Get_Frame_Data(biggest)->FrameWidth; - BiggestFrameHeight = image->Get_Frame_Data(biggest)->FrameHeight; - } +/** + * Sets the biggest frame of the AnimType with our override. + * + * @author: ZivDero + */ +void AnimTypeClassExtension::Set_Biggest_Frame() +{ + if (MiddleFrame == -1 && This()->Image && This()->Image->Get_Frame_Count() >= 2) { + This()->Biggest = This()->Image->Get_Frame_Count() / 2; } - + else if (MiddleFrame != -2) { + This()->Biggest = MiddleFrame; + } +} - IsInitialized = true; - return true; +/** + * Sets the biggest frame of the all AnimTypes with our override. + * + * @author: ZivDero + */ +void AnimTypeClassExtension::All_Set_Biggest_Frame() +{ + for (int i = 0; i < AnimTypeExtensions.Count(); i++) { + AnimTypeExtensions[i]->Set_Biggest_Frame(); + } } + diff --git a/src/extensions/animtype/animtypeext.h b/src/extensions/animtype/animtypeext.h index fdd75caac..76c1822a8 100644 --- a/src/extensions/animtype/animtypeext.h +++ b/src/extensions/animtype/animtypeext.h @@ -67,6 +67,9 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension virtual bool Read_INI(CCINIClass &ini) override; + void Set_Biggest_Frame(); + static void All_Set_Biggest_Frame(); + public: /** * If the cell in which this animation is placed does not contain @@ -126,8 +129,7 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension TypeList EndAnimsMaximum; /** - * The width and height of the biggest frame, if set by the user. + * The middle (biggest) frame, if set by the user. */ - unsigned BiggestFrameWidth; - unsigned BiggestFrameHeight; + int MiddleFrame; }; diff --git a/src/extensions/animtype/animtypeext_hooks.cpp b/src/extensions/animtype/animtypeext_hooks.cpp index bea98988d..4fc9767dd 100644 --- a/src/extensions/animtype/animtypeext_hooks.cpp +++ b/src/extensions/animtype/animtypeext_hooks.cpp @@ -33,6 +33,7 @@ #include "fatal.h" #include "debughandler.h" #include "asserthandler.h" +#include "extension.h" #include "hooker.h" #include "hooker_macros.h" @@ -112,6 +113,26 @@ DECLARE_PATCH(_AnimTypeClass_Get_Image_Data_Assertion_Patch) } +/** + * Sets the biggest frame with our override after Init because it reloads the image. + * + * @author: ZivDero + */ +DECLARE_PATCH(_AnimTypeClass_Init_MiddleFrame_Patch) +{ + AnimTypeClassExtension::All_Set_Biggest_Frame(); + + // Reconstruct the epilogue + _asm + { + pop esi + pop ebp + add esp, 0x204 + retn + } +} + + /** * Main function for patching the hooks. */ @@ -127,4 +148,6 @@ void AnimTypeClassExtension_Hooks() Patch_Jump(0x00419B40, &AnimTypeClassExt::_Free_Image); Patch_Jump(0x004187DB, &_AnimTypeClass_DTOR_Free_Image_Patch); Patch_Jump(0x00419C0B, &_AnimTypeClass_SDDTOR_Free_Image_Patch); + Patch_Jump(0x004188E6, &_AnimTypeClass_Init_MiddleFrame_Patch); + Patch_Jump(0x004189C9, &_AnimTypeClass_Init_MiddleFrame_Patch); } From f92920aa008cc1ef3c387ecab029ca965c14e67e Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 00:57:51 +0300 Subject: [PATCH 05/11] Add default values --- src/extensions/animtype/animtypeext.cpp | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index b2dbf42c3..3f55ded58 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -258,6 +258,14 @@ void AnimTypeClassExtension::Compute_CRC(WWCRCEngine &crc) const } +/** + * Helper macro to fill a list up to a certain count with a specific value. + */ +#define FILL_TYPELIST(list, count, value) \ + { while (list.Count() < count) \ + list.Add(value); } + + /** * Fetches the extension data from the INI database. * @@ -331,8 +339,8 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) StartAnimsMaximum = ini.Get_Integers(ini_name, "StartAnimsMaximum", StartAnimsMaximum); if (!StartAnimsCount.Count()) { - ASSERT_FATAL(StartAnims.Count() == StartAnimsMinimum.Count()); - ASSERT_FATAL(StartAnims.Count() == StartAnimsMaximum.Count()); + FILL_TYPELIST(StartAnimsMinimum, StartAnims.Count(), 1); + FILL_TYPELIST(StartAnimsMaximum, StartAnims.Count(), 1); } MiddleAnims = ini.Get_Anims(ini_name, "MiddleAnims", MiddleAnims); @@ -341,8 +349,8 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) MiddleAnimsMaximum = ini.Get_Integers(ini_name, "MiddleAnimsMaximum", MiddleAnimsMaximum); if (!MiddleAnimsCount.Count()) { - ASSERT_FATAL(MiddleAnims.Count() == MiddleAnimsMinimum.Count()); - ASSERT_FATAL(MiddleAnims.Count() == MiddleAnimsMaximum.Count()); + FILL_TYPELIST(MiddleAnimsMinimum, MiddleAnims.Count(), 1); + FILL_TYPELIST(MiddleAnimsMaximum, MiddleAnims.Count(), 1); } EndAnims = ini.Get_Anims(ini_name, "EndAnims", EndAnims); @@ -351,8 +359,8 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) EndAnimsMaximum = ini.Get_Integers(ini_name, "EndAnimsMaximum", EndAnimsMaximum); if (!EndAnimsCount.Count()) { - ASSERT_FATAL(EndAnims.Count() == EndAnimsMinimum.Count()); - ASSERT_FATAL(EndAnims.Count() == EndAnimsMaximum.Count()); + FILL_TYPELIST(EndAnimsMinimum, EndAnims.Count(), 1); + FILL_TYPELIST(EndAnimsMaximum, EndAnims.Count(), 1); } /** From 407fd97393b3093f3a223bc2e0a45290bd15baf5 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 01:00:23 +0300 Subject: [PATCH 06/11] Add docs --- CREDITS.md | 2 ++ docs/New-Features-and-Enhancements.md | 26 +++++++++++++++++++++++++- docs/Whats-New.md | 1 + 3 files changed, 28 insertions(+), 1 deletion(-) diff --git a/CREDITS.md b/CREDITS.md index 59f87352c..537673166 100644 --- a/CREDITS.md +++ b/CREDITS.md @@ -130,6 +130,7 @@ This page lists all the individual contributions to the project by their author. - Implement various controls to customise target lasers line. - Implement various controls to show and customise NavCom queue lines. - Implement customizable mouse cursors and actions. + - Implement a feature for animations to spawn additional animations. - **Kerbiter (Metadorius)**: - Initial documentation setup. - **MarkJFox**: @@ -206,4 +207,5 @@ This page lists all the individual contributions to the project by their author. - Implement required and forbidden houses. - Allow turning off "sticky" technologies. - Allow disabling the ActLike check on construction yards to allow for faction-specific MCVs. + - Finalize the feature for animations to spawn additional animations. diff --git a/docs/New-Features-and-Enhancements.md b/docs/New-Features-and-Enhancements.md index aaa75ab3e..0e822ea89 100644 --- a/docs/New-Features-and-Enhancements.md +++ b/docs/New-Features-and-Enhancements.md @@ -22,10 +22,34 @@ In `RULES.INI`: ```ini [SOMEAIRCRAFT] ; AircraftType ReloadRate= ; float, the rate that this aircraft will reload its ammo when docked with a helipad. Defaults to [General]->ReloadRate. -``` + ## Animations +### Additional Animation Spawning + +- Vinifera implements a new system for AnimTypes, allowing them to spawn additional animations at the start, middle and end of their sequence stages. All animations spawned will be from the center coordinate of the animation spawning these additional animations. + +```{note} +The `` keyword used below can be replaced with: `Start`, `Middle`, `End`. +``` +In `RULES.INI`: +```ini +[AnimType] ; AnimType +Anims= ; list of AnimTypes, list of animations to spawn at the designated stage of the animation sequence. +AnimsMinimum= ; list of integers, the minimum number of animations that can spawn when choosing the random amount for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 1 for each entry. +AnimsMaximum= ; list of integers, the maximum number of animations that can spawn when choosing the random amount for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 1 for each entry. +AnimsCount= ; list of integers, the number of animations to spawn for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 1 for each entry, and takes priority over the Minimum and Maximum entries. +``` + +- In addition to this new system, a new key for setting the logical middle frame (the frame in which the craters etc, are spawned) can now be set. + +In `RULES.INI`: +```ini +[AnimType] ; AnimType +MiddleFrame= ; integer, the frame number in which the animation system will spawn various logics (e.g. craters, scorch marks, fires). Defaults to auto-detect based on the largest frame of the shape file. A special value of -1 can be used to tell the animation system to use the exact middle frame of the shape file (shape file has 30 frames, frame 15 will be used). +``` + ### Various Keys Ported from Red Alert 2 - Vinifera implements various AnimType keys from Red Alert 2. diff --git a/docs/Whats-New.md b/docs/Whats-New.md index c1f4d9e33..bf264ce05 100644 --- a/docs/Whats-New.md +++ b/docs/Whats-New.md @@ -166,6 +166,7 @@ New: - Implement required and forbidden houses (by ZivDero) - Allow turning off "sticky" technologies (by ZivDero) - Allow disabling the ActLike check on construction yards to allow for faction-specific MCVs (by ZivDero) +- Implement a feature for animations to spawn additional animations (by CCHyper/tomsons26, ZivDero) Vanilla fixes: From 5cd77568d324068062531432988bf7dbd50ef2f1 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 01:31:22 +0300 Subject: [PATCH 07/11] Fix MiddleFrame resetting on load --- src/vinifera/vinifera_saveload.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/vinifera/vinifera_saveload.cpp b/src/vinifera/vinifera_saveload.cpp index 4c5091f93..6953e265e 100644 --- a/src/vinifera/vinifera_saveload.cpp +++ b/src/vinifera/vinifera_saveload.cpp @@ -127,6 +127,7 @@ #include #include +#include "animtypeext.h" #include "hooker.h" #include "language.h" #include "loadoptions.h" @@ -917,6 +918,7 @@ bool Vinifera_Load_Game(const char* file_name) SwizzleManager.Reset(); Post_Load_Game(); Put_Storage_Pointers(); + AnimTypeClassExtension::All_Set_Biggest_Frame(); Map.Init_IO(); Map.Activate(1); Map.Set_Dimensions(); From 9345cd49023346ae395668bc33496ab75a751ba5 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 01:33:02 +0300 Subject: [PATCH 08/11] Fill XXXAnimsCount list up with 1's if it contains any values to start with --- src/extensions/animtype/animtypeext.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index 3f55ded58..7b4032bcc 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -342,6 +342,9 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) FILL_TYPELIST(StartAnimsMinimum, StartAnims.Count(), 1); FILL_TYPELIST(StartAnimsMaximum, StartAnims.Count(), 1); } + else { + FILL_TYPELIST(StartAnimsCount, StartAnims.Count(), 1); + } MiddleAnims = ini.Get_Anims(ini_name, "MiddleAnims", MiddleAnims); MiddleAnimsCount = ini.Get_Integers(ini_name, "MiddleAnimsCount", MiddleAnimsCount); @@ -352,6 +355,9 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) FILL_TYPELIST(MiddleAnimsMinimum, MiddleAnims.Count(), 1); FILL_TYPELIST(MiddleAnimsMaximum, MiddleAnims.Count(), 1); } + else { + FILL_TYPELIST(MiddleAnimsCount, MiddleAnims.Count(), 1); + } EndAnims = ini.Get_Anims(ini_name, "EndAnims", EndAnims); EndAnimsCount = ini.Get_Integers(ini_name, "EndAnimsCount", EndAnimsCount); @@ -362,6 +368,9 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) FILL_TYPELIST(EndAnimsMinimum, EndAnims.Count(), 1); FILL_TYPELIST(EndAnimsMaximum, EndAnims.Count(), 1); } + else { + FILL_TYPELIST(EndAnimsCount, EndAnims.Count(), 1); + } /** * #issue-883 From 9d988ccb43ced7f3c8b5547218f288854ba5a856 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 01:37:12 +0300 Subject: [PATCH 09/11] Fix docs --- docs/New-Features-and-Enhancements.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/New-Features-and-Enhancements.md b/docs/New-Features-and-Enhancements.md index 0e822ea89..ccff3bcea 100644 --- a/docs/New-Features-and-Enhancements.md +++ b/docs/New-Features-and-Enhancements.md @@ -22,7 +22,7 @@ In `RULES.INI`: ```ini [SOMEAIRCRAFT] ; AircraftType ReloadRate= ; float, the rate that this aircraft will reload its ammo when docked with a helipad. Defaults to [General]->ReloadRate. - +``` ## Animations From 41efa1c69451b83104f576f5a76fa83a393f112f Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 02:24:13 +0300 Subject: [PATCH 10/11] Add notes to docs --- docs/New-Features-and-Enhancements.md | 11 ++++++++++- src/extensions/anim/animext_hooks.cpp | 4 ++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/New-Features-and-Enhancements.md b/docs/New-Features-and-Enhancements.md index ccff3bcea..8ece264e8 100644 --- a/docs/New-Features-and-Enhancements.md +++ b/docs/New-Features-and-Enhancements.md @@ -33,6 +33,11 @@ ReloadRate= ; float, the rate that this aircraft will reload its ammo when d ```{note} The `` keyword used below can be replaced with: `Start`, `Middle`, `End`. ``` + +```{note} +The `Start` and `End` animations are spawned once per an animation's lifetime, not on each loop iteration. +``` + In `RULES.INI`: ```ini [AnimType] ; AnimType @@ -47,7 +52,11 @@ In `RULES.INI`: In `RULES.INI`: ```ini [AnimType] ; AnimType -MiddleFrame= ; integer, the frame number in which the animation system will spawn various logics (e.g. craters, scorch marks, fires). Defaults to auto-detect based on the largest frame of the shape file. A special value of -1 can be used to tell the animation system to use the exact middle frame of the shape file (shape file has 30 frames, frame 15 will be used). +MiddleFrame= ; integer, the frame number in which the animation system will perform various logics (e.g. spawn craters, scorch marks, fires). Defaults to auto-detect based on the largest frame of the shape file. A special value of -1 can be used to tell the animation system to use the exact middle frame of the shape file (if shape file has 30 frames, frame 15 will be used). +``` + +```{note} +`MiddleFrame=0` is reserved and will not cause `MiddleAnims` to be spawned on every loop, but rather once at the start of the animation (like with `StartAnims`). To repeatedly spawn animations at the start of the loop, use `MiddleFrame` values of `1` or higher. ``` ### Various Keys Ported from Red Alert 2 diff --git a/src/extensions/anim/animext_hooks.cpp b/src/extensions/anim/animext_hooks.cpp index 9ba914357..9c0438a0f 100644 --- a/src/extensions/anim/animext_hooks.cpp +++ b/src/extensions/anim/animext_hooks.cpp @@ -138,7 +138,7 @@ static void Anim_Spawn_Particles(AnimClass *this_ptr) /** - * Calls the AnimClass extension middle event processor. + * Calls the AnimClass extension start event processor. * * @author: CCHyper */ @@ -183,7 +183,7 @@ DECLARE_PATCH(_AnimClass_Middle_Ext_Patch) /** - * Calls the AnimClass extension middle event processor. + * Calls the AnimClass extension end event processor. * * @author: CCHyper */ From 3222cac5ee1bf8018abb27bb982e611b359d0e76 Mon Sep 17 00:00:00 2001 From: ZivDero Date: Thu, 28 Nov 2024 02:43:03 +0300 Subject: [PATCH 11/11] Add XXXAnimsDelay --- docs/New-Features-and-Enhancements.md | 5 +++++ src/extensions/anim/animext.cpp | 10 ++++----- src/extensions/anim/animext.h | 2 +- src/extensions/animtype/animtypeext.cpp | 29 ++++++++++++++++++++++++- src/extensions/animtype/animtypeext.h | 3 +++ 5 files changed, 42 insertions(+), 7 deletions(-) diff --git a/docs/New-Features-and-Enhancements.md b/docs/New-Features-and-Enhancements.md index 8ece264e8..eb45f1708 100644 --- a/docs/New-Features-and-Enhancements.md +++ b/docs/New-Features-and-Enhancements.md @@ -45,6 +45,11 @@ In `RULES.INI`: AnimsMinimum= ; list of integers, the minimum number of animations that can spawn when choosing the random amount for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 1 for each entry. AnimsMaximum= ; list of integers, the maximum number of animations that can spawn when choosing the random amount for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 1 for each entry. AnimsCount= ; list of integers, the number of animations to spawn for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 1 for each entry, and takes priority over the Minimum and Maximum entries. +AnimsDelay= ; list of integers, the number of frames before the spawned animation appears for each of the respective entries on the animations list. This list must have the same number of entries as the animations list. Defaults to 0 for each entry. +``` + +```{note} +If the animation moves, delayed animations that it spawns will appear where it was when they were spawned, not when their delay expired. ``` - In addition to this new system, a new key for setting the logical middle frame (the frame in which the craters etc, are spawned) can now be set. diff --git a/src/extensions/anim/animext.cpp b/src/extensions/anim/animext.cpp index 44837da12..360b2d98e 100644 --- a/src/extensions/anim/animext.cpp +++ b/src/extensions/anim/animext.cpp @@ -183,7 +183,7 @@ bool AnimClassExtension::Start() * * Spawns the start animations. */ - Spawn_Animations(This()->Center_Coord(), animtypeext->StartAnims, animtypeext->StartAnimsCount, animtypeext->StartAnimsMinimum, animtypeext->StartAnimsMaximum); + Spawn_Animations(This()->Center_Coord(), animtypeext->StartAnims, animtypeext->StartAnimsCount, animtypeext->StartAnimsMinimum, animtypeext->StartAnimsMaximum, animtypeext->StartAnimsDelay); return true; } @@ -203,7 +203,7 @@ bool AnimClassExtension::Middle() * * Spawns the middle animations. */ - Spawn_Animations(This()->Center_Coord(), animtypeext->MiddleAnims, animtypeext->MiddleAnimsCount, animtypeext->MiddleAnimsMinimum, animtypeext->MiddleAnimsMaximum); + Spawn_Animations(This()->Center_Coord(), animtypeext->MiddleAnims, animtypeext->MiddleAnimsCount, animtypeext->MiddleAnimsMinimum, animtypeext->MiddleAnimsMaximum, animtypeext->MiddleAnimsDelay); return true; } @@ -223,7 +223,7 @@ bool AnimClassExtension::End() * * Spawns the end animations. */ - Spawn_Animations(This()->Center_Coord(), animtypeext->EndAnims, animtypeext->EndAnimsCount, animtypeext->EndAnimsMinimum, animtypeext->EndAnimsMaximum); + Spawn_Animations(This()->Center_Coord(), animtypeext->EndAnims, animtypeext->EndAnimsCount, animtypeext->EndAnimsMinimum, animtypeext->EndAnimsMaximum, animtypeext->EndAnimsDelay); return true; } @@ -236,7 +236,7 @@ bool AnimClassExtension::End() * * @author: CCHyper */ -bool AnimClassExtension::Spawn_Animations(const Coordinate &coord, const TypeList &animlist, const TypeList &countlist, const TypeList &minlist, const TypeList &maxlist) +bool AnimClassExtension::Spawn_Animations(const Coordinate &coord, const TypeList &animlist, const TypeList &countlist, const TypeList &minlist, const TypeList &maxlist, const TypeList& delaylist) { if (!animlist.Count()) { return false; @@ -282,7 +282,7 @@ bool AnimClassExtension::Spawn_Animations(const Coordinate &coord, const TypeLis * Based on the count decided above, spawn the animation type. */ for (int i = 0; i < count; ++i) { - AnimClass *anim = new AnimClass(animtype, (Coordinate &)coord); + AnimClass *anim = new AnimClass(animtype, (Coordinate &)coord, delaylist[index]); ASSERT(anim != nullptr); } } diff --git a/src/extensions/anim/animext.h b/src/extensions/anim/animext.h index 7532aa914..359599d92 100644 --- a/src/extensions/anim/animext.h +++ b/src/extensions/anim/animext.h @@ -71,7 +71,7 @@ AnimClassExtension final : public ObjectClassExtension bool End(); private: - bool Spawn_Animations(const Coordinate &coord, const TypeList &animlist, const TypeList &countlist, const TypeList &minlist, const TypeList &maxlist); + bool Spawn_Animations(const Coordinate &coord, const TypeList &animlist, const TypeList &countlist, const TypeList &minlist, const TypeList &maxlist, const TypeList& delaylist); public: }; diff --git a/src/extensions/animtype/animtypeext.cpp b/src/extensions/animtype/animtypeext.cpp index 7b4032bcc..fa81db72a 100644 --- a/src/extensions/animtype/animtypeext.cpp +++ b/src/extensions/animtype/animtypeext.cpp @@ -53,14 +53,17 @@ AnimTypeClassExtension::AnimTypeClassExtension(const AnimTypeClass *this_ptr) : StartAnimsCount(), StartAnimsMinimum(), StartAnimsMaximum(), + StartAnimsDelay(), MiddleAnims(), MiddleAnimsCount(), MiddleAnimsMinimum(), MiddleAnimsMaximum(), + MiddleAnimsDelay(), EndAnims(), EndAnimsCount(), EndAnimsMinimum(), EndAnimsMaximum(), + EndAnimsDelay(), MiddleFrame(-2) { //if (this_ptr) EXT_DEBUG_TRACE("AnimTypeClassExtension::AnimTypeClassExtension - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); @@ -80,14 +83,17 @@ AnimTypeClassExtension::AnimTypeClassExtension(const NoInitClass &noinit) : StartAnimsCount(noinit), StartAnimsMinimum(noinit), StartAnimsMaximum(noinit), + StartAnimsDelay(noinit), MiddleAnims(noinit), MiddleAnimsCount(noinit), MiddleAnimsMinimum(noinit), MiddleAnimsMaximum(noinit), + MiddleAnimsDelay(noinit), EndAnims(noinit), EndAnimsCount(noinit), EndAnimsMinimum(noinit), - EndAnimsMaximum(noinit) + EndAnimsMaximum(noinit), + EndAnimsDelay(noinit) { //EXT_DEBUG_TRACE("AnimTypeClassExtension::AnimTypeClassExtension(NoInitClass) - Name: %s (0x%08X)\n", Name(), (uintptr_t)(This())); } @@ -138,14 +144,17 @@ HRESULT AnimTypeClassExtension::Load(IStream *pStm) StartAnimsCount.Clear(); StartAnimsMinimum.Clear(); StartAnimsMaximum.Clear(); + StartAnimsDelay.Clear(); MiddleAnims.Clear(); MiddleAnimsCount.Clear(); MiddleAnimsMinimum.Clear(); MiddleAnimsMaximum.Clear(); + MiddleAnimsDelay.Clear(); EndAnims.Clear(); EndAnimsCount.Clear(); EndAnimsMinimum.Clear(); EndAnimsMaximum.Clear(); + EndAnimsDelay.Clear(); HRESULT hr = ObjectTypeClassExtension::Load(pStm); if (FAILED(hr)) { @@ -158,14 +167,17 @@ HRESULT AnimTypeClassExtension::Load(IStream *pStm) StartAnimsCount.Load(pStm); StartAnimsMinimum.Load(pStm); StartAnimsMaximum.Load(pStm); + StartAnimsDelay.Load(pStm); MiddleAnims.Load(pStm); MiddleAnimsCount.Load(pStm); MiddleAnimsMinimum.Load(pStm); MiddleAnimsMaximum.Load(pStm); + MiddleAnimsDelay.Load(pStm); EndAnims.Load(pStm); EndAnimsCount.Load(pStm); EndAnimsMinimum.Load(pStm); EndAnimsMaximum.Load(pStm); + EndAnimsDelay.Load(pStm); VINIFERA_SWIZZLE_REQUEST_POINTER_REMAP_LIST(StartAnims, "StartAnims"); VINIFERA_SWIZZLE_REQUEST_POINTER_REMAP_LIST(MiddleAnims, "MiddleAnims"); @@ -193,14 +205,17 @@ HRESULT AnimTypeClassExtension::Save(IStream *pStm, BOOL fClearDirty) StartAnimsCount.Save(pStm); StartAnimsMinimum.Save(pStm); StartAnimsMaximum.Save(pStm); + StartAnimsDelay.Save(pStm); MiddleAnims.Save(pStm); MiddleAnimsCount.Save(pStm); MiddleAnimsMinimum.Save(pStm); MiddleAnimsMaximum.Save(pStm); + MiddleAnimsDelay.Save(pStm); EndAnims.Save(pStm); EndAnimsCount.Save(pStm); EndAnimsMinimum.Save(pStm); EndAnimsMaximum.Save(pStm); + EndAnimsDelay.Save(pStm); return hr; } @@ -247,14 +262,17 @@ void AnimTypeClassExtension::Compute_CRC(WWCRCEngine &crc) const crc(StartAnimsCount.Count()); crc(StartAnimsMinimum.Count()); crc(StartAnimsMaximum.Count()); + crc(StartAnimsDelay.Count()); crc(MiddleAnims.Count()); crc(MiddleAnimsCount.Count()); crc(MiddleAnimsMinimum.Count()); crc(MiddleAnimsMaximum.Count()); + crc(MiddleAnimsDelay.Count()); crc(EndAnims.Count()); crc(EndAnimsCount.Count()); crc(EndAnimsMinimum.Count()); crc(EndAnimsMaximum.Count()); + crc(EndAnimsDelay.Count()); } @@ -337,6 +355,7 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) StartAnimsCount = ini.Get_Integers(ini_name, "StartAnimsCount", StartAnimsCount); StartAnimsMinimum = ini.Get_Integers(ini_name, "StartAnimsMinimum", StartAnimsMinimum); StartAnimsMaximum = ini.Get_Integers(ini_name, "StartAnimsMaximum", StartAnimsMaximum); + StartAnimsDelay = ini.Get_Integers(ini_name, "StartAnimsDelay", StartAnimsDelay); if (!StartAnimsCount.Count()) { FILL_TYPELIST(StartAnimsMinimum, StartAnims.Count(), 1); @@ -346,10 +365,13 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) FILL_TYPELIST(StartAnimsCount, StartAnims.Count(), 1); } + FILL_TYPELIST(StartAnimsDelay, StartAnims.Count(), 0); + MiddleAnims = ini.Get_Anims(ini_name, "MiddleAnims", MiddleAnims); MiddleAnimsCount = ini.Get_Integers(ini_name, "MiddleAnimsCount", MiddleAnimsCount); MiddleAnimsMinimum = ini.Get_Integers(ini_name, "MiddleAnimsMinimum", MiddleAnimsMinimum); MiddleAnimsMaximum = ini.Get_Integers(ini_name, "MiddleAnimsMaximum", MiddleAnimsMaximum); + MiddleAnimsDelay = ini.Get_Integers(ini_name, "MiddleAnimsDelay", MiddleAnimsDelay); if (!MiddleAnimsCount.Count()) { FILL_TYPELIST(MiddleAnimsMinimum, MiddleAnims.Count(), 1); @@ -359,10 +381,13 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) FILL_TYPELIST(MiddleAnimsCount, MiddleAnims.Count(), 1); } + FILL_TYPELIST(MiddleAnimsDelay, MiddleAnims.Count(), 0); + EndAnims = ini.Get_Anims(ini_name, "EndAnims", EndAnims); EndAnimsCount = ini.Get_Integers(ini_name, "EndAnimsCount", EndAnimsCount); EndAnimsMinimum = ini.Get_Integers(ini_name, "EndAnimsMinimum", EndAnimsMinimum); EndAnimsMaximum = ini.Get_Integers(ini_name, "EndAnimsMaximum", EndAnimsMaximum); + EndAnimsDelay = ini.Get_Integers(ini_name, "EndAnimsDelay", EndAnimsDelay); if (!EndAnimsCount.Count()) { FILL_TYPELIST(EndAnimsMinimum, EndAnims.Count(), 1); @@ -372,6 +397,8 @@ bool AnimTypeClassExtension::Read_INI(CCINIClass &ini) FILL_TYPELIST(EndAnimsCount, EndAnims.Count(), 1); } + FILL_TYPELIST(EndAnimsDelay, EndAnims.Count(), 0); + /** * #issue-883 * diff --git a/src/extensions/animtype/animtypeext.h b/src/extensions/animtype/animtypeext.h index 76c1822a8..62f1f45ca 100644 --- a/src/extensions/animtype/animtypeext.h +++ b/src/extensions/animtype/animtypeext.h @@ -111,6 +111,7 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension TypeList StartAnimsCount; TypeList StartAnimsMinimum; TypeList StartAnimsMaximum; + TypeList StartAnimsDelay; /** * List of animations to spawn at the logical middle of this animation. @@ -119,6 +120,7 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension TypeList MiddleAnimsCount; TypeList MiddleAnimsMinimum; TypeList MiddleAnimsMaximum; + TypeList MiddleAnimsDelay; /** * List of animations to spawn at the logical end of this animation. @@ -127,6 +129,7 @@ AnimTypeClassExtension final : public ObjectTypeClassExtension TypeList EndAnimsCount; TypeList EndAnimsMinimum; TypeList EndAnimsMaximum; + TypeList EndAnimsDelay; /** * The middle (biggest) frame, if set by the user.