From 74af47f4d6fb03c51a02c68f9812b99f9961615f Mon Sep 17 00:00:00 2001 From: Nailson Date: Fri, 13 Feb 2015 08:19:45 -0300 Subject: [PATCH] Added support for enhanced animations for items. Thanks to @conde2, @BenDol --- src/client/CMakeLists.txt | 2 + src/client/animator.cpp | 199 ++++++++++++++++++++++++++++++++++ src/client/animator.h | 83 ++++++++++++++ src/client/const.h | 6 - src/client/declarations.h | 2 + src/client/item.cpp | 3 + src/client/thing.h | 2 +- src/client/thingtype.cpp | 34 ++---- src/client/thingtype.h | 20 +--- vc12/otclient.vcxproj | 2 + vc12/otclient.vcxproj.filters | 6 + 11 files changed, 310 insertions(+), 49 deletions(-) create mode 100644 src/client/animator.cpp create mode 100644 src/client/animator.h diff --git a/src/client/CMakeLists.txt b/src/client/CMakeLists.txt index 6e012ef2..d6fb4f6b 100644 --- a/src/client/CMakeLists.txt +++ b/src/client/CMakeLists.txt @@ -24,6 +24,8 @@ set(client_SOURCES ${client_SOURCES} # core ${CMAKE_CURRENT_LIST_DIR}/animatedtext.cpp ${CMAKE_CURRENT_LIST_DIR}/animatedtext.h + ${CMAKE_CURRENT_LIST_DIR}/animator.h + ${CMAKE_CURRENT_LIST_DIR}/animator.cpp ${CMAKE_CURRENT_LIST_DIR}/container.cpp ${CMAKE_CURRENT_LIST_DIR}/container.h ${CMAKE_CURRENT_LIST_DIR}/creature.cpp diff --git a/src/client/animator.cpp b/src/client/animator.cpp new file mode 100644 index 00000000..a285c072 --- /dev/null +++ b/src/client/animator.cpp @@ -0,0 +1,199 @@ +/* +* Copyright (c) 2010-2014 OTClient +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +#include "declarations.h" +#include "animator.h" + +#include +#include + +Animator::Animator() +{ + m_animationPhases = 0; + m_startPhase = 0; + m_loopCount = 0; + m_async = false; + m_currentDuration = 0; + m_currentDirection = AnimDirForward; + m_currentLoop = 0; + m_lastPhaseTicks = 0; + m_isComplete = false; + m_phase = 0; +} + +void Animator::unserialize(int animationPhases, const FileStreamPtr& fin) +{ + m_animationPhases = animationPhases; + m_async = fin->getU8() == 0; + m_loopCount = fin->get32(); + m_startPhase = fin->get8(); + + for(int i = 0; i < m_animationPhases; ++i) { + int minimum = fin->getU32(); + int maximum = fin->getU32(); + m_phaseDurations.push_back(std::make_tuple(minimum, maximum)); + } + + m_phase = getStartPhase(); + + assert(m_animationPhases == m_phaseDurations.size()); + assert(m_startPhase >= -1 && m_startPhase < m_animationPhases); +} + +void Animator::serialize(const FileStreamPtr& fin) +{ + fin->addU8(m_async ? 0 : 1); + fin->add32(m_loopCount); + fin->add8(m_startPhase); + + for(std::tuple phase : m_phaseDurations) { + fin->addU32(std::get<0>(phase)); + fin->addU32(std::get<1>(phase)); + } +} + +void Animator::setPhase(int phase) +{ + if(m_phase == phase) return; + + if(m_async) { + if(phase == AnimPhaseAsync) + m_phase = 0; + else if(phase == AnimPhaseRandom) + m_phase = (int)stdext::random_range(0, (long)m_animationPhases); + else if(phase >= 0 && phase < m_animationPhases) + m_phase = phase; + else + m_phase = getStartPhase(); + + m_isComplete = false; + m_lastPhaseTicks = g_clock.millis(); + m_currentDuration = getPhaseDuration(phase); + m_currentLoop = 0; + } else + calculateSynchronous(); +} + +int Animator::getPhase() +{ + ticks_t ticks = g_clock.millis(); + if(ticks != m_lastPhaseTicks && !m_isComplete) { + int elapsedTicks = (int)(ticks - m_lastPhaseTicks); + if(elapsedTicks >= m_currentDuration) { + int phase = 0; + if(m_loopCount < 0) + phase = getPingPongPhase(); + else + phase = getLoopPhase(); + + if(m_phase != phase) { + int duration = getPhaseDuration(phase) - (elapsedTicks - m_currentDuration); + if(duration < 0 && !m_async) { + calculateSynchronous(); + } else { + m_phase = phase; + m_currentDuration = std::max(0, duration); + } + } else + m_isComplete = true; + } else + m_currentDuration -= elapsedTicks; + + m_lastPhaseTicks = ticks; + } + return m_phase; +} + +int Animator::getStartPhase() +{ + if(m_startPhase > -1) + return m_startPhase; + return (int)stdext::random_range(0, (long)m_animationPhases); +} + +void Animator::resetAnimation() +{ + m_isComplete = false; + m_currentDirection = AnimDirForward; + m_currentLoop = 0; + setPhase(AnimPhaseAutomatic); +} + +int Animator::getPingPongPhase() +{ + int count = m_currentDirection == AnimDirForward ? 1 : -1; + int nextPhase = m_phase + count; + if(nextPhase < 0 || nextPhase >= m_animationPhases) { + m_currentDirection = m_currentDirection == AnimDirForward ? AnimDirBackward : AnimDirForward; + count *= -1; + } + return m_phase + count; +} + +int Animator::getLoopPhase() +{ + int nextPhase = m_phase + 1; + if(nextPhase < m_animationPhases) + return nextPhase; + + if(m_loopCount == 0) + return 0; + + if(m_currentLoop < (m_loopCount - 1)) { + m_currentLoop++; + return 0; + } + + return m_phase; +} + +int Animator::getPhaseDuration(int phase) +{ + assert(phase < (int)m_phaseDurations.size()); + + std::tuple data = m_phaseDurations.at(phase); + int min = std::get<0>(data); + int max = std::get<1>(data); + if(min == max) return min; + return (int)stdext::random_range((long)min, (long)max); +} + +void Animator::calculateSynchronous() +{ + int totalDuration = 0; + for(int i = 0; i < m_animationPhases; i++) + totalDuration += getPhaseDuration(i); + + ticks_t ticks = g_clock.millis(); + int elapsedTicks = (int)(ticks % totalDuration); + int totalTime = 0; + for(int i = 0; i < m_animationPhases; i++) { + int duration = getPhaseDuration(i); + if(elapsedTicks >= totalTime && elapsedTicks < totalTime + duration) { + m_phase = i; + m_currentDuration = duration - (elapsedTicks - totalTime); + break; + } + totalTime += duration; + } + m_lastPhaseTicks = ticks; +} diff --git a/src/client/animator.h b/src/client/animator.h new file mode 100644 index 00000000..68ed60d5 --- /dev/null +++ b/src/client/animator.h @@ -0,0 +1,83 @@ +/* +* Copyright (c) 2010-2014 OTClient +* +* Permission is hereby granted, free of charge, to any person obtaining a copy +* of this software and associated documentation files (the "Software"), to deal +* in the Software without restriction, including without limitation the rights +* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +* copies of the Software, and to permit persons to whom the Software is +* furnished to do so, subject to the following conditions: +* +* The above copyright notice and this permission notice shall be included in +* all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +* THE SOFTWARE. +*/ + +#ifndef ANIMATOR_H +#define ANIMATOR_H + +#include "declarations.h" + +#include + +enum AnimationPhase : int16 +{ + AnimPhaseAutomatic = -1, + AnimPhaseRandom = 254, + AnimPhaseAsync = 255, +}; + +enum AnimationDirection : uint8 +{ + AnimDirForward = 0, + AnimDirBackward = 1 +}; + +class Animator : public stdext::shared_object +{ +public: + Animator(); + + void unserialize(int animationPhases, const FileStreamPtr& fin); + void serialize(const FileStreamPtr& fin); + + void setPhase(int phase); + int getPhase(); + + int getStartPhase(); + int getAnimationPhases() { return m_animationPhases; } + bool isAsync() { return m_async; } + bool isComplete() { return m_isComplete; } + + void resetAnimation(); + +private: + int getPingPongPhase(); + int getLoopPhase(); + int getPhaseDuration(int phase); + void calculateSynchronous(); + + int m_animationPhases; + int m_startPhase; + int m_loopCount; + bool m_async; + std::vector< std::tuple > m_phaseDurations; + + int m_currentDuration; + AnimationDirection m_currentDirection; + int m_currentLoop; + + ticks_t m_lastPhaseTicks; + bool m_isComplete; + + int m_phase; +}; + +#endif diff --git a/src/client/const.h b/src/client/const.h index 9ce1dc37..e654e5c9 100644 --- a/src/client/const.h +++ b/src/client/const.h @@ -457,12 +457,6 @@ namespace Otc LastSpeedFormula }; - enum AnimationPhase { - PhaseAutomatic = 0, - PhaseRandom = 254, - PhaseAsync = 255 - }; - enum Blessings { BlessingNone = 0, BlessingAdventurer = 1, diff --git a/src/client/declarations.h b/src/client/declarations.h index 95c8a783..a6f4a612 100644 --- a/src/client/declarations.h +++ b/src/client/declarations.h @@ -45,6 +45,7 @@ class Effect; class Missile; class AnimatedText; class StaticText; +class Animator; class ThingType; class ItemType; class House; @@ -68,6 +69,7 @@ typedef stdext::shared_object_ptr EffectPtr; typedef stdext::shared_object_ptr MissilePtr; typedef stdext::shared_object_ptr AnimatedTextPtr; typedef stdext::shared_object_ptr StaticTextPtr; +typedef stdext::shared_object_ptr AnimatorPtr; typedef stdext::shared_object_ptr ThingTypePtr; typedef stdext::shared_object_ptr ItemTypePtr; typedef stdext::shared_object_ptr HousePtr; diff --git a/src/client/item.cpp b/src/client/item.cpp index 0e4695ed..90bd18a6 100644 --- a/src/client/item.cpp +++ b/src/client/item.cpp @@ -382,6 +382,9 @@ int Item::calculateAnimationPhase(bool animate) { if(getAnimationPhases() > 1) { if(animate) { + if(getAnimator() != nullptr) + return getAnimator()->getPhase(); + if(m_async) return (g_clock.millis() % (Otc::ITEM_TICKS_PER_FRAME * getAnimationPhases())) / Otc::ITEM_TICKS_PER_FRAME; else { diff --git a/src/client/thing.h b/src/client/thing.h index c8913bdb..9da464eb 100644 --- a/src/client/thing.h +++ b/src/client/thing.h @@ -74,7 +74,7 @@ public: int getNumPatternY() { return rawGetThingType()->getNumPatternY(); } int getNumPatternZ() { return rawGetThingType()->getNumPatternZ(); } int getAnimationPhases() { return rawGetThingType()->getAnimationPhases(); } - Animation getAnimation() { return rawGetThingType()->getAnimation(); } + AnimatorPtr getAnimator() { return rawGetThingType()->getAnimator(); } int getGroundSpeed() { return rawGetThingType()->getGroundSpeed(); } int getMaxTextLength() { return rawGetThingType()->getMaxTextLength(); } Light getLight() { return rawGetThingType()->getLight(); } diff --git a/src/client/thingtype.cpp b/src/client/thingtype.cpp index b19e50d3..1c9d20e1 100644 --- a/src/client/thingtype.cpp +++ b/src/client/thingtype.cpp @@ -39,6 +39,7 @@ ThingType::ThingType() m_null = true; m_exactSize = 0; m_realSize = 0; + m_animator = nullptr; m_numPatternX = m_numPatternY = m_numPatternZ = 0; m_animationPhases = 0; m_layers = 0; @@ -117,15 +118,8 @@ void ThingType::serialize(const FileStreamPtr& fin) fin->addU8(m_animationPhases); if(g_game.getFeature(Otc::GameEnhancedAnimations)) { - if(m_animationPhases > 1) { - fin->addU8(m_animation.async ? 0 : 1); - fin->add32(m_animation.loopCount); - fin->addU8(m_animation.startIndex); - - for(std::tuple frame : m_animation.frames) { - fin->addU32(std::get<0>(frame)); - fin->addU32(std::get<1>(frame)); - } + if(m_animationPhases > 1 && m_animator != nullptr) { + m_animator->serialize(fin); } } @@ -274,11 +268,11 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS stdext::throw_exception(stdext::format("corrupt data (id: %d, category: %d, count: %d, lastAttr: %d)", m_id, m_category, count, attr)); - uint8 frames = 1; + uint8 groupCount = 1; if(category == ThingCategoryCreature && g_game.getClientVersion() >= 1057) - frames = fin->getU8(); + groupCount = fin->getU8(); - for(int i = 0; i < frames; ++i) { + for(int i = 0; i < groupCount; ++i) { uint8 frameGroup = FrameGroupDefault; if(category == ThingCategoryCreature && g_game.getClientVersion() >= 1057) { frameGroup = fin->getU8(); @@ -303,19 +297,9 @@ void ThingType::unserialize(uint16 clientId, ThingCategory category, const FileS m_numPatternZ = 1; m_animationPhases = fin->getU8(); - if(g_game.getFeature(Otc::GameEnhancedAnimations)) { - if(m_animationPhases > 1) { - m_animation.async = fin->getU8() == 0; - m_animation.loopCount = fin->get32(); - m_animation.startIndex = fin->getU8(); - - for (int i = 0; i < m_animationPhases; i++) { - int minDuration = fin->getU32(); - int maxDuration = fin->getU32(); - - m_animation.frames.push_back(std::make_tuple(minDuration, maxDuration)); - } - } + if(m_animationPhases > 1 && g_game.getFeature(Otc::GameEnhancedAnimations)) { + m_animator = AnimatorPtr(new Animator); + m_animator->unserialize(m_animationPhases, fin); } int totalSprites = m_size.area() * m_layers * m_numPatternX * m_numPatternY * m_numPatternZ * m_animationPhases; diff --git a/src/client/thingtype.h b/src/client/thingtype.h index 11a85f20..bcc64d45 100644 --- a/src/client/thingtype.h +++ b/src/client/thingtype.h @@ -24,6 +24,7 @@ #define THINGTYPE_H #include "declarations.h" +#include "animator.h" #include #include @@ -116,21 +117,6 @@ struct Light { uint8 color; }; -struct Animation { - Animation() { startIndex = 0; loopCount = 0; async = false; } - - int startIndex; - int loopCount; - bool async; - std::vector > frames; - - float duration(uint8 frame) { - assert(frames.size() <= frame); - std::tuple data = frames.at(frame); - return stdext::random_range((long)std::get<0>(data), (long)std::get<1>(data)); - } -}; - class ThingType : public LuaObject { public: @@ -159,7 +145,7 @@ public: int getNumPatternY() { return m_numPatternY; } int getNumPatternZ() { return m_numPatternZ; } int getAnimationPhases() { return m_animationPhases; } - Animation getAnimation() { return m_animation; } + AnimatorPtr getAnimator() { return m_animator; } Point getDisplacement() { return m_displacement; } int getDisplacementX() { return getDisplacement().x; } int getDisplacementY() { return getDisplacement().y; } @@ -226,7 +212,7 @@ private: Size m_size; Point m_displacement; - Animation m_animation; + AnimatorPtr m_animator; int m_animationPhases; int m_exactSize; int m_realSize; diff --git a/vc12/otclient.vcxproj b/vc12/otclient.vcxproj index 88fee3ed..0cc33c3d 100644 --- a/vc12/otclient.vcxproj +++ b/vc12/otclient.vcxproj @@ -161,6 +161,7 @@ + @@ -315,6 +316,7 @@ + diff --git a/vc12/otclient.vcxproj.filters b/vc12/otclient.vcxproj.filters index 870bd4e5..7056e0b8 100644 --- a/vc12/otclient.vcxproj.filters +++ b/vc12/otclient.vcxproj.filters @@ -528,6 +528,9 @@ Source Files\framework\core + + Source Files\client + @@ -1049,6 +1052,9 @@ Header Files\framework\core + + Header Files\client +