forked from teamnwah/openmw-tes3coop
parent
3374630e7b
commit
b65f379b7f
25 changed files with 328 additions and 31 deletions
|
@ -1,3 +1,9 @@
|
||||||
|
#include <string>
|
||||||
|
#include <iostream>
|
||||||
|
#include <limits>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
#include "convertacdt.hpp"
|
#include "convertacdt.hpp"
|
||||||
|
|
||||||
namespace ESSImport
|
namespace ESSImport
|
||||||
|
@ -49,4 +55,49 @@ namespace ESSImport
|
||||||
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
|
npcStats.mTimeToStartDrowning = actorData.mACDT.mBreathMeter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void convertANIS (const ANIS& anis, ESM::AnimationState& state)
|
||||||
|
{
|
||||||
|
static const char* animGroups[] =
|
||||||
|
{
|
||||||
|
"Idle", "Idle2", "Idle3", "Idle4", "Idle5", "Idle6", "Idle7", "Idle8", "Idle9", "Idlehh", "Idle1h", "Idle2c",
|
||||||
|
"Idle2w", "IdleSwim", "IdleSpell", "IdleCrossbow", "IdleSneak", "IdleStorm", "Torch", "Hit1", "Hit2", "Hit3",
|
||||||
|
"Hit4", "Hit5", "SwimHit1", "SwimHit2", "SwimHit3", "Death1", "Death2", "Death3", "Death4", "Death5",
|
||||||
|
"DeathKnockDown", "DeathKnockOut", "KnockDown", "KnockOut", "SwimDeath", "SwimDeath2", "SwimDeath3",
|
||||||
|
"SwimDeathKnockDown", "SwimDeathKnockOut", "SwimKnockOut", "SwimKnockDown", "SwimWalkForward",
|
||||||
|
"SwimWalkBack", "SwimWalkLeft", "SwimWalkRight", "SwimRunForward", "SwimRunBack", "SwimRunLeft",
|
||||||
|
"SwimRunRight", "SwimTurnLeft", "SwimTurnRight", "WalkForward", "WalkBack", "WalkLeft", "WalkRight",
|
||||||
|
"TurnLeft", "TurnRight", "RunForward", "RunBack", "RunLeft", "RunRight", "SneakForward", "SneakBack",
|
||||||
|
"SneakLeft", "SneakRight", "Jump", "WalkForwardhh", "WalkBackhh", "WalkLefthh", "WalkRighthh",
|
||||||
|
"TurnLefthh", "TurnRighthh", "RunForwardhh", "RunBackhh", "RunLefthh", "RunRighthh", "SneakForwardhh",
|
||||||
|
"SneakBackhh", "SneakLefthh", "SneakRighthh", "Jumphh", "WalkForward1h", "WalkBack1h", "WalkLeft1h",
|
||||||
|
"WalkRight1h", "TurnLeft1h", "TurnRight1h", "RunForward1h", "RunBack1h", "RunLeft1h", "RunRight1h",
|
||||||
|
"SneakForward1h", "SneakBack1h", "SneakLeft1h", "SneakRight1h", "Jump1h", "WalkForward2c", "WalkBack2c",
|
||||||
|
"WalkLeft2c", "WalkRight2c", "TurnLeft2c", "TurnRight2c", "RunForward2c", "RunBack2c", "RunLeft2c",
|
||||||
|
"RunRight2c", "SneakForward2c", "SneakBack2c", "SneakLeft2c", "SneakRight2c", "Jump2c", "WalkForward2w",
|
||||||
|
"WalkBack2w", "WalkLeft2w", "WalkRight2w", "TurnLeft2w", "TurnRight2w", "RunForward2w", "RunBack2w",
|
||||||
|
"RunLeft2w", "RunRight2w", "SneakForward2w", "SneakBack2w", "SneakLeft2w", "SneakRight2w", "Jump2w",
|
||||||
|
"SpellCast", "SpellTurnLeft", "SpellTurnRight", "Attack1", "Attack2", "Attack3", "SwimAttack1",
|
||||||
|
"SwimAttack2", "SwimAttack3", "HandToHand", "Crossbow", "BowAndArrow", "ThrowWeapon", "WeaponOneHand",
|
||||||
|
"WeaponTwoHand", "WeaponTwoWide", "Shield", "PickProbe", "InventoryHandToHand", "InventoryWeaponOneHand",
|
||||||
|
"InventoryWeaponTwoHand", "InventoryWeaponTwoWide"
|
||||||
|
};
|
||||||
|
|
||||||
|
if (anis.mGroupIndex < (sizeof(animGroups) / sizeof(*animGroups)))
|
||||||
|
{
|
||||||
|
std::string group(animGroups[anis.mGroupIndex]);
|
||||||
|
Misc::StringUtils::lowerCaseInPlace(group);
|
||||||
|
|
||||||
|
ESM::AnimationState::ScriptedAnimation scriptedAnim;
|
||||||
|
scriptedAnim.mGroup = group;
|
||||||
|
scriptedAnim.mTime = anis.mTime;
|
||||||
|
scriptedAnim.mAbsolute = true;
|
||||||
|
// Neither loop count nor queueing seems to be supported by the ess format.
|
||||||
|
scriptedAnim.mLoopCount = std::numeric_limits<size_t>::max();
|
||||||
|
state.mScriptedAnims.push_back(scriptedAnim);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
// TODO: Handle 0xFF index, which seems to be used for finished animations.
|
||||||
|
std::cerr << "unknown animation group index: " << static_cast<unsigned int>(anis.mGroupIndex) << std::endl;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include <components/esm/creaturestats.hpp>
|
#include <components/esm/creaturestats.hpp>
|
||||||
#include <components/esm/npcstats.hpp>
|
#include <components/esm/npcstats.hpp>
|
||||||
#include <components/esm/loadskil.hpp>
|
#include <components/esm/loadskil.hpp>
|
||||||
|
#include <components/esm/animationstate.hpp>
|
||||||
|
|
||||||
#include "importacdt.hpp"
|
#include "importacdt.hpp"
|
||||||
|
|
||||||
|
@ -18,6 +19,8 @@ namespace ESSImport
|
||||||
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
|
void convertACSC (const ACSC& acsc, ESM::CreatureStats& cStats);
|
||||||
|
|
||||||
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
|
void convertNpcData (const ActorData& actorData, ESM::NpcStats& npcStats);
|
||||||
|
|
||||||
|
void convertANIS (const ANIS& anis, ESM::AnimationState& state);
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -34,6 +34,9 @@ namespace
|
||||||
objstate.mCount = 0;
|
objstate.mCount = 0;
|
||||||
convertSCRI(cellref.mSCRI, objstate.mLocals);
|
convertSCRI(cellref.mSCRI, objstate.mLocals);
|
||||||
objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
|
objstate.mHasLocals = !objstate.mLocals.mVariables.empty();
|
||||||
|
|
||||||
|
if (cellref.mHasANIS)
|
||||||
|
convertANIS(cellref.mANIS, objstate.mAnimationState);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isIndexedRefId(const std::string& indexedRefId)
|
bool isIndexedRefId(const std::string& indexedRefId)
|
||||||
|
|
|
@ -123,8 +123,13 @@ namespace ESSImport
|
||||||
|
|
||||||
if (esm.isNextSub("ND3D"))
|
if (esm.isNextSub("ND3D"))
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
|
|
||||||
|
mHasANIS = false;
|
||||||
if (esm.isNextSub("ANIS"))
|
if (esm.isNextSub("ANIS"))
|
||||||
esm.skipHSub();
|
{
|
||||||
|
mHasANIS = true;
|
||||||
|
esm.getHT(mANIS);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -55,6 +55,12 @@ namespace ESSImport
|
||||||
unsigned char mCorpseClearCountdown; // hours?
|
unsigned char mCorpseClearCountdown; // hours?
|
||||||
unsigned char mUnknown3[71];
|
unsigned char mUnknown3[71];
|
||||||
};
|
};
|
||||||
|
struct ANIS
|
||||||
|
{
|
||||||
|
unsigned char mGroupIndex;
|
||||||
|
unsigned char mUnknown[3];
|
||||||
|
float mTime;
|
||||||
|
};
|
||||||
#pragma pack(pop)
|
#pragma pack(pop)
|
||||||
|
|
||||||
struct ActorData : public ESM::CellRef
|
struct ActorData : public ESM::CellRef
|
||||||
|
@ -77,6 +83,9 @@ namespace ESSImport
|
||||||
|
|
||||||
SCRI mSCRI;
|
SCRI mSCRI;
|
||||||
|
|
||||||
|
bool mHasANIS;
|
||||||
|
ANIS mANIS; // scripted animation state
|
||||||
|
|
||||||
void load(ESM::ESMReader& esm);
|
void load(ESM::ESMReader& esm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -159,12 +159,14 @@ namespace MWBase
|
||||||
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
|
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
|
||||||
///< Forces an object to refresh its animation state.
|
///< Forces an object to refresh its animation state.
|
||||||
|
|
||||||
virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
|
virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1, bool persist=false) = 0;
|
||||||
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
///< Run animation for a MW-reference. Calls to this function for references that are currently not
|
||||||
/// in the scene should be ignored.
|
/// in the scene should be ignored.
|
||||||
///
|
///
|
||||||
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
|
/// \param mode 0 normal, 1 immediate start, 2 immediate loop
|
||||||
/// \param count How many times the animation should be run
|
/// \param count How many times the animation should be run
|
||||||
|
/// \param persist Whether the animation state should be stored in saved games
|
||||||
|
/// and persist after cell unload.
|
||||||
/// \return Success or error
|
/// \return Success or error
|
||||||
|
|
||||||
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
|
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
|
||||||
|
@ -173,6 +175,9 @@ namespace MWBase
|
||||||
|
|
||||||
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
|
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
|
||||||
|
|
||||||
|
/// Save the current animation state of managed references to their RefData.
|
||||||
|
virtual void persistAnimationStates() = 0;
|
||||||
|
|
||||||
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
||||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||||
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
|
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
|
||||||
|
|
|
@ -1389,12 +1389,12 @@ namespace MWMechanics
|
||||||
iter->second->getCharacterController()->forceStateUpdate();
|
iter->second->getCharacterController()->forceStateUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
bool Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)
|
||||||
{
|
{
|
||||||
PtrActorMap::iterator iter = mActors.find(ptr);
|
PtrActorMap::iterator iter = mActors.find(ptr);
|
||||||
if(iter != mActors.end())
|
if(iter != mActors.end())
|
||||||
{
|
{
|
||||||
return iter->second->getCharacterController()->playGroup(groupName, mode, number);
|
return iter->second->getCharacterController()->playGroup(groupName, mode, number, persist);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -1417,6 +1417,12 @@ namespace MWMechanics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Actors::persistAnimationStates()
|
||||||
|
{
|
||||||
|
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
|
||||||
|
iter->second->getCharacterController()->persistAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)
|
void Actors::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)
|
||||||
{
|
{
|
||||||
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
|
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
|
||||||
|
|
|
@ -109,9 +109,10 @@ namespace MWMechanics
|
||||||
|
|
||||||
void forceStateUpdate(const MWWorld::Ptr &ptr);
|
void forceStateUpdate(const MWWorld::Ptr &ptr);
|
||||||
|
|
||||||
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);
|
||||||
void skipAnimation(const MWWorld::Ptr& ptr);
|
void skipAnimation(const MWWorld::Ptr& ptr);
|
||||||
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName);
|
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName);
|
||||||
|
void persistAnimationStates();
|
||||||
|
|
||||||
void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);
|
void getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);
|
||||||
|
|
||||||
|
|
|
@ -762,12 +762,17 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
||||||
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
refreshCurrentAnims(mIdleState, mMovementState, mJumpState, true);
|
||||||
|
|
||||||
mAnimation->runAnimation(0.f);
|
mAnimation->runAnimation(0.f);
|
||||||
|
|
||||||
|
unpersistAnimationState();
|
||||||
}
|
}
|
||||||
|
|
||||||
CharacterController::~CharacterController()
|
CharacterController::~CharacterController()
|
||||||
{
|
{
|
||||||
if (mAnimation)
|
if (mAnimation)
|
||||||
|
{
|
||||||
|
persistAnimationState();
|
||||||
mAnimation->setTextKeyListener(NULL);
|
mAnimation->setTextKeyListener(NULL);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void split(const std::string &s, char delim, std::vector<std::string> &elems) {
|
void split(const std::string &s, char delim, std::vector<std::string> &elems) {
|
||||||
|
@ -1557,14 +1562,14 @@ void CharacterController::update(float duration)
|
||||||
{
|
{
|
||||||
if(mAnimQueue.size() > 1)
|
if(mAnimQueue.size() > 1)
|
||||||
{
|
{
|
||||||
if(mAnimation->isPlaying(mAnimQueue.front().first) == false)
|
if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)
|
||||||
{
|
{
|
||||||
mAnimation->disable(mAnimQueue.front().first);
|
mAnimation->disable(mAnimQueue.front().mGroup);
|
||||||
mAnimQueue.pop_front();
|
mAnimQueue.pop_front();
|
||||||
|
|
||||||
mAnimation->play(mAnimQueue.front().first, Priority_Default,
|
mAnimation->play(mAnimQueue.front().mGroup, Priority_Default,
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
1.0f, "start", "stop", 0.0f, mAnimQueue.front().second);
|
1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1837,14 +1842,14 @@ void CharacterController::update(float duration)
|
||||||
}
|
}
|
||||||
else if(mAnimQueue.size() > 1)
|
else if(mAnimQueue.size() > 1)
|
||||||
{
|
{
|
||||||
if(mAnimation->isPlaying(mAnimQueue.front().first) == false)
|
if(mAnimation->isPlaying(mAnimQueue.front().mGroup) == false)
|
||||||
{
|
{
|
||||||
mAnimation->disable(mAnimQueue.front().first);
|
mAnimation->disable(mAnimQueue.front().mGroup);
|
||||||
mAnimQueue.pop_front();
|
mAnimQueue.pop_front();
|
||||||
|
|
||||||
mAnimation->play(mAnimQueue.front().first, Priority_Default,
|
mAnimation->play(mAnimQueue.front().mGroup, Priority_Default,
|
||||||
MWRender::Animation::BlendMask_All, false,
|
MWRender::Animation::BlendMask_All, false,
|
||||||
1.0f, "start", "stop", 0.0f, mAnimQueue.front().second);
|
1.0f, "start", "stop", 0.0f, mAnimQueue.front().mLoopCount);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1951,8 +1956,74 @@ void CharacterController::update(float duration)
|
||||||
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
|
mAnimation->enableHeadAnimation(cls.isActor() && !cls.getCreatureStats(mPtr).isDead());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void CharacterController::persistAnimationState()
|
||||||
|
{
|
||||||
|
ESM::AnimationState& state = mPtr.getRefData().getAnimationState();
|
||||||
|
|
||||||
bool CharacterController::playGroup(const std::string &groupname, int mode, int count)
|
state.mScriptedAnims.clear();
|
||||||
|
for (AnimationQueue::const_iterator iter = mAnimQueue.begin(); iter != mAnimQueue.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (!iter->mPersist)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
ESM::AnimationState::ScriptedAnimation anim;
|
||||||
|
anim.mGroup = iter->mGroup;
|
||||||
|
|
||||||
|
if (iter == mAnimQueue.begin())
|
||||||
|
{
|
||||||
|
anim.mLoopCount = mAnimation->getCurrentLoopCount(anim.mGroup);
|
||||||
|
float complete;
|
||||||
|
mAnimation->getInfo(anim.mGroup, &complete, NULL);
|
||||||
|
anim.mTime = complete;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
anim.mLoopCount = iter->mLoopCount;
|
||||||
|
anim.mTime = 0.f;
|
||||||
|
}
|
||||||
|
|
||||||
|
state.mScriptedAnims.push_back(anim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CharacterController::unpersistAnimationState()
|
||||||
|
{
|
||||||
|
const ESM::AnimationState& state = mPtr.getRefData().getAnimationState();
|
||||||
|
|
||||||
|
if (!state.mScriptedAnims.empty())
|
||||||
|
{
|
||||||
|
clearAnimQueue();
|
||||||
|
for (ESM::AnimationState::ScriptedAnimations::const_iterator iter = state.mScriptedAnims.begin(); iter != state.mScriptedAnims.end(); ++iter)
|
||||||
|
{
|
||||||
|
AnimationQueueEntry entry;
|
||||||
|
entry.mGroup = iter->mGroup;
|
||||||
|
entry.mLoopCount = iter->mLoopCount;
|
||||||
|
entry.mPersist = true;
|
||||||
|
|
||||||
|
mAnimQueue.push_back(entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::AnimationState::ScriptedAnimation& anim = state.mScriptedAnims.front();
|
||||||
|
float complete = anim.mTime;
|
||||||
|
if (anim.mAbsolute)
|
||||||
|
{
|
||||||
|
float start = mAnimation->getTextKeyTime(anim.mGroup+": start");
|
||||||
|
float stop = mAnimation->getTextKeyTime(anim.mGroup+": stop");
|
||||||
|
float time = std::max(start, std::min(stop, anim.mTime));
|
||||||
|
complete = (time - start) / (stop - start);
|
||||||
|
}
|
||||||
|
|
||||||
|
mAnimation->disable(mCurrentIdle);
|
||||||
|
mCurrentIdle.clear();
|
||||||
|
mIdleState = CharState_SpecialIdle;
|
||||||
|
|
||||||
|
mAnimation->play(anim.mGroup,
|
||||||
|
Priority_Default, MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||||
|
"start", "stop", complete, anim.mLoopCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CharacterController::playGroup(const std::string &groupname, int mode, int count, bool persist)
|
||||||
{
|
{
|
||||||
if(!mAnimation || !mAnimation->hasAnimation(groupname))
|
if(!mAnimation || !mAnimation->hasAnimation(groupname))
|
||||||
{
|
{
|
||||||
|
@ -1962,10 +2033,16 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
count = std::max(count, 1);
|
count = std::max(count, 1);
|
||||||
if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().first))
|
|
||||||
|
AnimationQueueEntry entry;
|
||||||
|
entry.mGroup = groupname;
|
||||||
|
entry.mLoopCount = count-1;
|
||||||
|
entry.mPersist = persist;
|
||||||
|
|
||||||
|
if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().mGroup))
|
||||||
{
|
{
|
||||||
clearAnimQueue();
|
clearAnimQueue();
|
||||||
mAnimQueue.push_back(std::make_pair(groupname, count-1));
|
mAnimQueue.push_back(entry);
|
||||||
|
|
||||||
mAnimation->disable(mCurrentIdle);
|
mAnimation->disable(mCurrentIdle);
|
||||||
mCurrentIdle.clear();
|
mCurrentIdle.clear();
|
||||||
|
@ -1978,9 +2055,9 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
|
||||||
else if(mode == 0)
|
else if(mode == 0)
|
||||||
{
|
{
|
||||||
if (!mAnimQueue.empty())
|
if (!mAnimQueue.empty())
|
||||||
mAnimation->stopLooping(mAnimQueue.front().first);
|
mAnimation->stopLooping(mAnimQueue.front().mGroup);
|
||||||
mAnimQueue.resize(1);
|
mAnimQueue.resize(1);
|
||||||
mAnimQueue.push_back(std::make_pair(groupname, count-1));
|
mAnimQueue.push_back(entry);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -2002,7 +2079,7 @@ bool CharacterController::isAnimPlaying(const std::string &groupName)
|
||||||
void CharacterController::clearAnimQueue()
|
void CharacterController::clearAnimQueue()
|
||||||
{
|
{
|
||||||
if(!mAnimQueue.empty())
|
if(!mAnimQueue.empty())
|
||||||
mAnimation->disable(mAnimQueue.front().first);
|
mAnimation->disable(mAnimQueue.front().mGroup);
|
||||||
mAnimQueue.clear();
|
mAnimQueue.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -147,7 +147,13 @@ class CharacterController : public MWRender::Animation::TextKeyListener
|
||||||
MWWorld::Ptr mPtr;
|
MWWorld::Ptr mPtr;
|
||||||
MWRender::Animation *mAnimation;
|
MWRender::Animation *mAnimation;
|
||||||
|
|
||||||
typedef std::deque<std::pair<std::string,size_t> > AnimationQueue;
|
struct AnimationQueueEntry
|
||||||
|
{
|
||||||
|
std::string mGroup;
|
||||||
|
size_t mLoopCount;
|
||||||
|
bool mPersist;
|
||||||
|
};
|
||||||
|
typedef std::deque<AnimationQueueEntry> AnimationQueue;
|
||||||
AnimationQueue mAnimQueue;
|
AnimationQueue mAnimQueue;
|
||||||
|
|
||||||
CharacterState mIdleState;
|
CharacterState mIdleState;
|
||||||
|
@ -236,7 +242,10 @@ public:
|
||||||
|
|
||||||
void update(float duration);
|
void update(float duration);
|
||||||
|
|
||||||
bool playGroup(const std::string &groupname, int mode, int count);
|
void persistAnimationState();
|
||||||
|
void unpersistAnimationState();
|
||||||
|
|
||||||
|
bool playGroup(const std::string &groupname, int mode, int count, bool persist=false);
|
||||||
void skipAnim();
|
void skipAnim();
|
||||||
bool isAnimPlaying(const std::string &groupName);
|
bool isAnimPlaying(const std::string &groupName);
|
||||||
|
|
||||||
|
|
|
@ -842,12 +842,12 @@ namespace MWMechanics
|
||||||
mActors.forceStateUpdate(ptr);
|
mActors.forceStateUpdate(ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
bool MechanicsManager::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)
|
||||||
{
|
{
|
||||||
if(ptr.getClass().isActor())
|
if(ptr.getClass().isActor())
|
||||||
return mActors.playAnimationGroup(ptr, groupName, mode, number);
|
return mActors.playAnimationGroup(ptr, groupName, mode, number, persist);
|
||||||
else
|
else
|
||||||
return mObjects.playAnimationGroup(ptr, groupName, mode, number);
|
return mObjects.playAnimationGroup(ptr, groupName, mode, number, persist);
|
||||||
}
|
}
|
||||||
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
|
void MechanicsManager::skipAnimation(const MWWorld::Ptr& ptr)
|
||||||
{
|
{
|
||||||
|
@ -864,6 +864,12 @@ namespace MWMechanics
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MechanicsManager::persistAnimationStates()
|
||||||
|
{
|
||||||
|
mActors.persistAnimationStates();
|
||||||
|
mObjects.persistAnimationStates();
|
||||||
|
}
|
||||||
|
|
||||||
void MechanicsManager::updateMagicEffects(const MWWorld::Ptr &ptr)
|
void MechanicsManager::updateMagicEffects(const MWWorld::Ptr &ptr)
|
||||||
{
|
{
|
||||||
mActors.updateMagicEffects(ptr);
|
mActors.updateMagicEffects(ptr);
|
||||||
|
|
|
@ -146,9 +146,10 @@ namespace MWMechanics
|
||||||
|
|
||||||
/// Attempt to play an animation group
|
/// Attempt to play an animation group
|
||||||
/// @return Success or error
|
/// @return Success or error
|
||||||
virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
virtual bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);
|
||||||
virtual void skipAnimation(const MWWorld::Ptr& ptr);
|
virtual void skipAnimation(const MWWorld::Ptr& ptr);
|
||||||
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName);
|
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string &groupName);
|
||||||
|
virtual void persistAnimationStates();
|
||||||
|
|
||||||
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
|
||||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||||
|
|
|
@ -79,12 +79,12 @@ void Objects::update(float duration, bool paused)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
|
bool Objects::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist)
|
||||||
{
|
{
|
||||||
PtrControllerMap::iterator iter = mObjects.find(ptr);
|
PtrControllerMap::iterator iter = mObjects.find(ptr);
|
||||||
if(iter != mObjects.end())
|
if(iter != mObjects.end())
|
||||||
{
|
{
|
||||||
return iter->second->playGroup(groupName, mode, number);
|
return iter->second->playGroup(groupName, mode, number, persist);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -99,6 +99,12 @@ void Objects::skipAnimation(const MWWorld::Ptr& ptr)
|
||||||
iter->second->skipAnim();
|
iter->second->skipAnim();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Objects::persistAnimationStates()
|
||||||
|
{
|
||||||
|
for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)
|
||||||
|
iter->second->persistAnimationState();
|
||||||
|
}
|
||||||
|
|
||||||
void Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)
|
void Objects::getObjectsInRange(const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out)
|
||||||
{
|
{
|
||||||
for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)
|
for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter)
|
||||||
|
|
|
@ -38,8 +38,9 @@ namespace MWMechanics
|
||||||
void update(float duration, bool paused);
|
void update(float duration, bool paused);
|
||||||
///< Update object animations
|
///< Update object animations
|
||||||
|
|
||||||
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number);
|
bool playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number, bool persist=false);
|
||||||
void skipAnimation(const MWWorld::Ptr& ptr);
|
void skipAnimation(const MWWorld::Ptr& ptr);
|
||||||
|
void persistAnimationStates();
|
||||||
|
|
||||||
void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);
|
void getObjectsInRange (const osg::Vec3f& position, float radius, std::vector<MWWorld::Ptr>& out);
|
||||||
};
|
};
|
||||||
|
|
|
@ -843,6 +843,15 @@ namespace MWRender
|
||||||
return iter->second.getTime();
|
return iter->second.getTime();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
size_t Animation::getCurrentLoopCount(const std::string& groupname) const
|
||||||
|
{
|
||||||
|
AnimStateMap::const_iterator iter = mStates.find(groupname);
|
||||||
|
if(iter == mStates.end())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return iter->second.mLoopCount;
|
||||||
|
}
|
||||||
|
|
||||||
void Animation::disable(const std::string &groupname)
|
void Animation::disable(const std::string &groupname)
|
||||||
{
|
{
|
||||||
AnimStateMap::iterator iter = mStates.find(groupname);
|
AnimStateMap::iterator iter = mStates.find(groupname);
|
||||||
|
|
|
@ -415,6 +415,8 @@ public:
|
||||||
/// Get the current absolute position in the animation track for the animation that is currently playing from the given group.
|
/// Get the current absolute position in the animation track for the animation that is currently playing from the given group.
|
||||||
float getCurrentTime(const std::string& groupname) const;
|
float getCurrentTime(const std::string& groupname) const;
|
||||||
|
|
||||||
|
size_t getCurrentLoopCount(const std::string& groupname) const;
|
||||||
|
|
||||||
/** Disables the specified animation group;
|
/** Disables the specified animation group;
|
||||||
* \param groupname Animation group to disable.
|
* \param groupname Animation group to disable.
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -56,7 +56,7 @@ namespace MWScript
|
||||||
throw std::runtime_error ("animation mode out of range");
|
throw std::runtime_error ("animation mode out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, std::numeric_limits<int>::max());
|
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, std::numeric_limits<int>::max(), true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -89,7 +89,7 @@ namespace MWScript
|
||||||
throw std::runtime_error ("animation mode out of range");
|
throw std::runtime_error ("animation mode out of range");
|
||||||
}
|
}
|
||||||
|
|
||||||
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops);
|
MWBase::Environment::get().getMechanicsManager()->playAnimationGroup (ptr, group, mode, loops, true);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -219,6 +219,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
|
||||||
else
|
else
|
||||||
slot = character->updateSlot (slot, profile);
|
slot = character->updateSlot (slot, profile);
|
||||||
|
|
||||||
|
// Make sure the animation state held by references is up to date before saving the game.
|
||||||
|
MWBase::Environment::get().getMechanicsManager()->persistAnimationStates();
|
||||||
|
|
||||||
// Write to a memory stream first. If there is an exception during the save process, we don't want to trash the
|
// Write to a memory stream first. If there is an exception during the save process, we don't want to trash the
|
||||||
// existing save file we are overwriting.
|
// existing save file we are overwriting.
|
||||||
std::stringstream stream;
|
std::stringstream stream;
|
||||||
|
|
|
@ -31,6 +31,8 @@ namespace MWWorld
|
||||||
mDeletedByContentFile = refData.mDeletedByContentFile;
|
mDeletedByContentFile = refData.mDeletedByContentFile;
|
||||||
mFlags = refData.mFlags;
|
mFlags = refData.mFlags;
|
||||||
|
|
||||||
|
mAnimationState = refData.mAnimationState;
|
||||||
|
|
||||||
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
|
mCustomData = refData.mCustomData ? refData.mCustomData->clone() : 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -65,6 +67,7 @@ namespace MWWorld
|
||||||
mEnabled (objectState.mEnabled != 0),
|
mEnabled (objectState.mEnabled != 0),
|
||||||
mCount (objectState.mCount),
|
mCount (objectState.mCount),
|
||||||
mPosition (objectState.mPosition),
|
mPosition (objectState.mPosition),
|
||||||
|
mAnimationState(objectState.mAnimationState),
|
||||||
mCustomData (0),
|
mCustomData (0),
|
||||||
mChanged(true), mFlags(objectState.mFlags) // Loading from a savegame -> assume changed
|
mChanged(true), mFlags(objectState.mFlags) // Loading from a savegame -> assume changed
|
||||||
{
|
{
|
||||||
|
@ -96,6 +99,8 @@ namespace MWWorld
|
||||||
objectState.mCount = mCount;
|
objectState.mCount = mCount;
|
||||||
objectState.mPosition = mPosition;
|
objectState.mPosition = mPosition;
|
||||||
objectState.mFlags = mFlags;
|
objectState.mFlags = mFlags;
|
||||||
|
|
||||||
|
objectState.mAnimationState = mAnimationState;
|
||||||
}
|
}
|
||||||
|
|
||||||
RefData& RefData::operator= (const RefData& refData)
|
RefData& RefData::operator= (const RefData& refData)
|
||||||
|
@ -269,4 +274,15 @@ namespace MWWorld
|
||||||
else
|
else
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ESM::AnimationState& RefData::getAnimationState() const
|
||||||
|
{
|
||||||
|
return mAnimationState;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::AnimationState& RefData::getAnimationState()
|
||||||
|
{
|
||||||
|
return mAnimationState;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define GAME_MWWORLD_REFDATA_H
|
#define GAME_MWWORLD_REFDATA_H
|
||||||
|
|
||||||
#include <components/esm/defs.hpp>
|
#include <components/esm/defs.hpp>
|
||||||
|
#include <components/esm/animationstate.hpp>
|
||||||
|
|
||||||
#include "../mwscript/locals.hpp"
|
#include "../mwscript/locals.hpp"
|
||||||
|
|
||||||
|
@ -42,6 +43,8 @@ namespace MWWorld
|
||||||
|
|
||||||
ESM::Position mPosition;
|
ESM::Position mPosition;
|
||||||
|
|
||||||
|
ESM::AnimationState mAnimationState;
|
||||||
|
|
||||||
CustomData *mCustomData;
|
CustomData *mCustomData;
|
||||||
|
|
||||||
void copy (const RefData& refData);
|
void copy (const RefData& refData);
|
||||||
|
@ -132,6 +135,9 @@ namespace MWWorld
|
||||||
|
|
||||||
bool hasChanged() const;
|
bool hasChanged() const;
|
||||||
///< Has this RefData changed since it was originally loaded?
|
///< Has this RefData changed since it was originally loaded?
|
||||||
|
|
||||||
|
const ESM::AnimationState& getAnimationState() const;
|
||||||
|
ESM::AnimationState& getAnimationState();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -77,7 +77,7 @@ add_component_dir (esm
|
||||||
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter
|
||||||
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
|
savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap inventorystate containerstate npcstate creaturestate dialoguestate statstate
|
||||||
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
|
npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate projectilestate debugprofile
|
||||||
aisequence magiceffects util custommarkerstate stolenitems transport
|
aisequence magiceffects util custommarkerstate stolenitems transport animationstate
|
||||||
)
|
)
|
||||||
|
|
||||||
add_component_dir (esmterrain
|
add_component_dir (esmterrain
|
||||||
|
|
37
components/esm/animationstate.cpp
Normal file
37
components/esm/animationstate.cpp
Normal file
|
@ -0,0 +1,37 @@
|
||||||
|
#include "animationstate.hpp"
|
||||||
|
|
||||||
|
#include "esmreader.hpp"
|
||||||
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
void AnimationState::load(ESMReader& esm)
|
||||||
|
{
|
||||||
|
mScriptedAnims.clear();
|
||||||
|
|
||||||
|
while (esm.isNextSub("ANIS"))
|
||||||
|
{
|
||||||
|
ScriptedAnimation anim;
|
||||||
|
|
||||||
|
anim.mGroup = esm.getHString();
|
||||||
|
esm.getHNOT(anim.mTime, "TIME");
|
||||||
|
esm.getHNOT(anim.mAbsolute, "ABST");
|
||||||
|
esm.getHNT(anim.mLoopCount, "COUN");
|
||||||
|
|
||||||
|
mScriptedAnims.push_back(anim);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AnimationState::save(ESMWriter& esm) const
|
||||||
|
{
|
||||||
|
for (ScriptedAnimations::const_iterator iter = mScriptedAnims.begin(); iter != mScriptedAnims.end(); ++iter)
|
||||||
|
{
|
||||||
|
esm.writeHNString("ANIS", iter->mGroup);
|
||||||
|
if (iter->mTime > 0)
|
||||||
|
esm.writeHNT("TIME", iter->mTime);
|
||||||
|
if (iter->mAbsolute)
|
||||||
|
esm.writeHNT("ABST", iter->mAbsolute);
|
||||||
|
esm.writeHNT("COUN", iter->mLoopCount);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
34
components/esm/animationstate.hpp
Normal file
34
components/esm/animationstate.hpp
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
#ifndef OPENMW_ESM_ANIMATIONSTATE_H
|
||||||
|
#define OPENMW_ESM_ANIMATIONSTATE_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
namespace ESM
|
||||||
|
{
|
||||||
|
class ESMReader;
|
||||||
|
class ESMWriter;
|
||||||
|
|
||||||
|
// format 0, saved games only
|
||||||
|
struct AnimationState
|
||||||
|
{
|
||||||
|
struct ScriptedAnimation
|
||||||
|
{
|
||||||
|
ScriptedAnimation()
|
||||||
|
: mTime(0.f), mAbsolute(false), mLoopCount(0) {}
|
||||||
|
|
||||||
|
std::string mGroup;
|
||||||
|
float mTime;
|
||||||
|
bool mAbsolute;
|
||||||
|
size_t mLoopCount;
|
||||||
|
};
|
||||||
|
|
||||||
|
typedef std::vector<ScriptedAnimation> ScriptedAnimations;
|
||||||
|
ScriptedAnimations mScriptedAnims;
|
||||||
|
|
||||||
|
void load(ESMReader& esm);
|
||||||
|
void save(ESMWriter& esm) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -34,6 +34,8 @@ void ESM::ObjectState::load (ESMReader &esm)
|
||||||
int unused;
|
int unused;
|
||||||
esm.getHNOT(unused, "LTIM");
|
esm.getHNOT(unused, "LTIM");
|
||||||
|
|
||||||
|
mAnimationState.load(esm);
|
||||||
|
|
||||||
// FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files
|
// FIXME: assuming "false" as default would make more sense, but also break compatibility with older save files
|
||||||
mHasCustomState = true;
|
mHasCustomState = true;
|
||||||
esm.getHNOT (mHasCustomState, "HCUS");
|
esm.getHNOT (mHasCustomState, "HCUS");
|
||||||
|
@ -61,6 +63,8 @@ void ESM::ObjectState::save (ESMWriter &esm, bool inInventory) const
|
||||||
if (mFlags != 0)
|
if (mFlags != 0)
|
||||||
esm.writeHNT ("FLAG", mFlags);
|
esm.writeHNT ("FLAG", mFlags);
|
||||||
|
|
||||||
|
mAnimationState.save(esm);
|
||||||
|
|
||||||
if (!mHasCustomState)
|
if (!mHasCustomState)
|
||||||
esm.writeHNT ("HCUS", false);
|
esm.writeHNT ("HCUS", false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,7 @@
|
||||||
|
|
||||||
#include "cellref.hpp"
|
#include "cellref.hpp"
|
||||||
#include "locals.hpp"
|
#include "locals.hpp"
|
||||||
|
#include "animationstate.hpp"
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -31,6 +32,8 @@ namespace ESM
|
||||||
|
|
||||||
unsigned int mVersion;
|
unsigned int mVersion;
|
||||||
|
|
||||||
|
ESM::AnimationState mAnimationState;
|
||||||
|
|
||||||
ObjectState() : mHasCustomState(true), mVersion(0)
|
ObjectState() : mHasCustomState(true), mVersion(0)
|
||||||
{}
|
{}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue