Added support for enhanced animations for items.
Thanks to @conde2, @BenDol
This commit is contained in:
		
							parent
							
								
									4c4e0b9d07
								
							
						
					
					
						commit
						74af47f4d6
					
				|  | @ -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 | ||||
|  |  | |||
|  | @ -0,0 +1,199 @@ | |||
| /*
 | ||||
| * Copyright (c) 2010-2014 OTClient <https://github.com/edubart/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 <framework/core/clock.h> | ||||
| #include <framework/core/filestream.h> | ||||
| 
 | ||||
| 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<int, int> 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<int>(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<int, int> 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; | ||||
| } | ||||
|  | @ -0,0 +1,83 @@ | |||
| /*
 | ||||
| * Copyright (c) 2010-2014 OTClient <https://github.com/edubart/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 <framework/core/declarations.h> | ||||
| 
 | ||||
| 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<int, int> > m_phaseDurations; | ||||
| 
 | ||||
|     int m_currentDuration; | ||||
|     AnimationDirection m_currentDirection; | ||||
|     int m_currentLoop; | ||||
| 
 | ||||
|     ticks_t m_lastPhaseTicks; | ||||
|     bool m_isComplete; | ||||
| 
 | ||||
|     int m_phase; | ||||
| }; | ||||
| 
 | ||||
| #endif | ||||
|  | @ -457,12 +457,6 @@ namespace Otc | |||
|         LastSpeedFormula | ||||
|     }; | ||||
| 
 | ||||
|     enum AnimationPhase { | ||||
|         PhaseAutomatic = 0, | ||||
|         PhaseRandom = 254, | ||||
|         PhaseAsync = 255 | ||||
|     }; | ||||
| 
 | ||||
|     enum Blessings { | ||||
|         BlessingNone = 0, | ||||
|         BlessingAdventurer = 1, | ||||
|  |  | |||
|  | @ -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<Effect> EffectPtr; | |||
| typedef stdext::shared_object_ptr<Missile> MissilePtr; | ||||
| typedef stdext::shared_object_ptr<AnimatedText> AnimatedTextPtr; | ||||
| typedef stdext::shared_object_ptr<StaticText> StaticTextPtr; | ||||
| typedef stdext::shared_object_ptr<Animator> AnimatorPtr; | ||||
| typedef stdext::shared_object_ptr<ThingType> ThingTypePtr; | ||||
| typedef stdext::shared_object_ptr<ItemType> ItemTypePtr; | ||||
| typedef stdext::shared_object_ptr<House> HousePtr; | ||||
|  |  | |||
|  | @ -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 { | ||||
|  |  | |||
|  | @ -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(); } | ||||
|  |  | |||
|  | @ -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<int, int> 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; | ||||
|  |  | |||
|  | @ -24,6 +24,7 @@ | |||
| #define THINGTYPE_H | ||||
| 
 | ||||
| #include "declarations.h" | ||||
| #include "animator.h" | ||||
| 
 | ||||
| #include <framework/core/declarations.h> | ||||
| #include <framework/otml/declarations.h> | ||||
|  | @ -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<std::tuple<int, int> > frames; | ||||
| 
 | ||||
|     float duration(uint8 frame) { | ||||
|         assert(frames.size() <= frame); | ||||
|         std::tuple<int, int> 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; | ||||
|  |  | |||
|  | @ -161,6 +161,7 @@ | |||
|   </ItemDefinitionGroup> | ||||
|   <ItemGroup> | ||||
|     <ClCompile Include="..\src\client\animatedtext.cpp" /> | ||||
|     <ClCompile Include="..\src\client\animator.cpp" /> | ||||
|     <ClCompile Include="..\src\client\client.cpp" /> | ||||
|     <ClCompile Include="..\src\client\container.cpp" /> | ||||
|     <ClCompile Include="..\src\client\creature.cpp" /> | ||||
|  | @ -315,6 +316,7 @@ | |||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="..\src\client\animatedtext.h" /> | ||||
|     <ClInclude Include="..\src\client\animator.h" /> | ||||
|     <ClInclude Include="..\src\client\client.h" /> | ||||
|     <ClInclude Include="..\src\client\const.h" /> | ||||
|     <ClInclude Include="..\src\client\container.h" /> | ||||
|  |  | |||
|  | @ -528,6 +528,9 @@ | |||
|     <ClCompile Include="..\src\framework\core\config.cpp"> | ||||
|       <Filter>Source Files\framework\core</Filter> | ||||
|     </ClCompile> | ||||
|     <ClCompile Include="..\src\client\animator.cpp"> | ||||
|       <Filter>Source Files\client</Filter> | ||||
|     </ClCompile> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ClInclude Include="..\src\framework\const.h"> | ||||
|  | @ -1049,6 +1052,9 @@ | |||
|     <ClInclude Include="..\src\framework\core\config.h"> | ||||
|       <Filter>Header Files\framework\core</Filter> | ||||
|     </ClInclude> | ||||
|     <ClInclude Include="..\src\client\animator.h"> | ||||
|       <Filter>Header Files\client</Filter> | ||||
|     </ClInclude> | ||||
|   </ItemGroup> | ||||
|   <ItemGroup> | ||||
|     <ResourceCompile Include="..\src\otcicon.rc"> | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue
	
	 Nailson
						Nailson