From 0966755a0c8393c6276f3217f6a215f7182f72f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 26 May 2014 19:56:32 +0200 Subject: [PATCH 1/4] Store death animation index in CreatureStats --- apps/openmw/mwmechanics/character.cpp | 56 +++++++++++++++-------- apps/openmw/mwmechanics/character.hpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 15 +++++- apps/openmw/mwmechanics/creaturestats.hpp | 6 +++ components/esm/creaturestats.cpp | 6 +++ components/esm/creaturestats.hpp | 1 + 6 files changed, 66 insertions(+), 19 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 60bccb3f7e..c9da912dd2 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -407,29 +407,25 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); } -void CharacterController::playRandomDeath(float startpoint) +void CharacterController::playDeath(float startpoint, CharacterState death) { - if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) + switch (death) { - mDeathState = CharState_SwimDeath; + case CharState_SwimDeath: mCurrentDeath = "swimdeath"; - } - else if (mHitState == CharState_KnockDown) - { - mDeathState = CharState_DeathKnockDown; + break; + case CharState_DeathKnockDown: mCurrentDeath = "deathknockdown"; - } - else if (mHitState == CharState_KnockOut) - { - mDeathState = CharState_DeathKnockOut; + break; + case CharState_DeathKnockOut: mCurrentDeath = "deathknockout"; + break; + default: + mCurrentDeath = "death" + Ogre::StringConverter::toString(death - CharState_Death1 + 1); } - else - { - int selected=0; - mCurrentDeath = chooseRandomGroup("death", &selected); - mDeathState = static_cast(CharState_Death1 + (selected-1)); - } + mDeathState = death; + + mPtr.getClass().getCreatureStats(mPtr).setDeathAnimation(mDeathState - CharState_Death1); // For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually. mMovementState = CharState_None; @@ -440,6 +436,29 @@ void CharacterController::playRandomDeath(float startpoint) false, 1.0f, "start", "stop", startpoint, 0); } +void CharacterController::playRandomDeath(float startpoint) +{ + if(MWBase::Environment::get().getWorld()->isSwimming(mPtr) && mAnimation->hasAnimation("swimdeath")) + { + mDeathState = CharState_SwimDeath; + } + else if (mHitState == CharState_KnockDown) + { + mDeathState = CharState_DeathKnockDown; + } + else if (mHitState == CharState_KnockOut) + { + mDeathState = CharState_DeathKnockOut; + } + else + { + int selected=0; + chooseRandomGroup("death", &selected); + mDeathState = static_cast(CharState_Death1 + (selected-1)); + } + playDeath(startpoint, mDeathState); +} + CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim) : mPtr(ptr) , mAnimation(anim) @@ -497,7 +516,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if(mDeathState != CharState_None) { - playRandomDeath(1.0f); + int deathindex = mPtr.getClass().getCreatureStats(mPtr).getDeathAnimation(); + playDeath(1.0f, CharacterState(CharState_Death1 + deathindex)); } else refreshCurrentAnims(mIdleState, mMovementState, true); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 1b7caf34d7..5cefe13bc8 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -181,6 +181,7 @@ class CharacterController void updateVisibility(); + void playDeath(float startpoint, CharacterState death); void playRandomDeath(float startpoint = 0.0f); /// choose a random animation group with \a prefix and numeric suffix diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 3ef6ff4df9..7fd26c25c9 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -22,7 +22,8 @@ namespace MWMechanics mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f), - mLastRestock(0,0), mGoldPool(0), mActorId(-1) + mLastRestock(0,0), mGoldPool(0), mActorId(-1), + mDeathAnimation(0) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -498,6 +499,7 @@ namespace MWMechanics state.mDrawState = mDrawState; state.mLevel = mLevel; state.mActorId = mActorId; + state.mDeathAnimation = mDeathAnimation; mSpells.writeState(state.mSpells); mActiveSpells.writeState(state.mActiveSpells); @@ -537,6 +539,7 @@ namespace MWMechanics mDrawState = DrawState_(state.mDrawState); mLevel = state.mLevel; mActorId = state.mActorId; + mDeathAnimation = state.mDeathAnimation; mSpells.readState(state.mSpells); mActiveSpells.readState(state.mActiveSpells); @@ -590,4 +593,14 @@ namespace MWMechanics { esm.getHNT(sActorId, "COUN"); } + + unsigned char CreatureStats::getDeathAnimation() const + { + return mDeathAnimation; + } + + void CreatureStats::setDeathAnimation(unsigned char index) + { + mDeathAnimation = index; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 70a86536a0..8b2398dfbd 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -64,6 +64,9 @@ namespace MWMechanics int mActorId; + // The index of the death animation that was played + unsigned char mDeathAnimation; + protected: // These two are only set by NpcStats, but they are declared in CreatureStats to prevent using virtual methods. bool mIsWerewolf; @@ -250,6 +253,9 @@ namespace MWMechanics void setGoldPool(int pool); int getGoldPool() const; + unsigned char getDeathAnimation() const; + void setDeathAnimation(unsigned char index); + int getActorId(); ///< Will generate an actor ID, if the actor does not have one yet. diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 66d8481240..3860e9351f 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -74,6 +74,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mActorId = -1; esm.getHNOT (mActorId, "ACID"); + mDeathAnimation = 0; + esm.getHNOT (mDeathAnimation, "DANM"); + mSpells.load(esm); mActiveSpells.load(esm); } @@ -152,6 +155,9 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mActorId != -1) esm.writeHNT ("ACID", mActorId); + if (mDeathAnimation) + esm.writeHNT ("DANM", mDeathAnimation); + mSpells.save(esm); mActiveSpells.save(esm); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 7814d937ae..5ca3d071fe 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -46,6 +46,7 @@ namespace ESM std::string mLastHitObject; bool mRecalcDynamicStats; int mDrawState; + unsigned char mDeathAnimation; int mLevel; From 5660f283dde88ce71ed9475c3b12a42c45075320 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 26 May 2014 20:33:01 +0200 Subject: [PATCH 2/4] Fix actor models incorrectly being rotated on X/Y axes --- apps/openmw/mwrender/actors.cpp | 13 ++++--------- apps/openmw/mwworld/worldimp.cpp | 14 ++++++++------ 2 files changed, 12 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwrender/actors.cpp b/apps/openmw/mwrender/actors.cpp index a9c9884d58..b7e9f5730c 100644 --- a/apps/openmw/mwrender/actors.cpp +++ b/apps/openmw/mwrender/actors.cpp @@ -54,17 +54,12 @@ void Actors::insertBegin(const MWWorld::Ptr &ptr) // Convert MW rotation to a quaternion: f = ptr.getCellRef().getPosition().rot; - // Rotate around X axis - Ogre::Quaternion xr(Ogre::Radian(-f[0]), Ogre::Vector3::UNIT_X); - - // Rotate around Y axis - Ogre::Quaternion yr(Ogre::Radian(-f[1]), Ogre::Vector3::UNIT_Y); - - // Rotate around Z axis + // For rendering purposes, actors should only rotate around the Z axis. + // X rotation is used for camera rotation (for the player) and for + // ranged magic / ranged weapon aiming. Ogre::Quaternion zr(Ogre::Radian(-f[2]), Ogre::Vector3::UNIT_Z); - // Rotates first around z, then y, then x - insert->setOrientation(xr*yr*zr); + insert->setOrientation(zr); ptr.getRefData().setBaseNode(insert); } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 43153612d7..435ca8b367 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1116,13 +1116,15 @@ namespace MWWorld while(ptr.getRefData().getLocalRotation().rot[2]<=-fullRotateRad) ptr.getRefData().getLocalRotation().rot[2]+=fullRotateRad; - Ogre::Quaternion worldRotQuat(Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* - Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* - Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z)); + Ogre::Quaternion worldRotQuat(Ogre::Radian(ptr.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z); + if (!ptr.getClass().isActor()) + worldRotQuat = Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Radian(ptr.getRefData().getPosition().rot[1]), Ogre::Vector3::NEGATIVE_UNIT_Y)* worldRotQuat; - Ogre::Quaternion rot(Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)* - Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)* - Ogre::Quaternion(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z)); + Ogre::Quaternion rot(Ogre::Degree(z), Ogre::Vector3::NEGATIVE_UNIT_Z); + if (!ptr.getClass().isActor()) + rot = Ogre::Quaternion(Ogre::Degree(x), Ogre::Vector3::NEGATIVE_UNIT_X)* + Ogre::Quaternion(Ogre::Degree(y), Ogre::Vector3::NEGATIVE_UNIT_Y)*rot; ptr.getRefData().getBaseNode()->setOrientation(worldRotQuat*rot); mPhysics->rotateObject(ptr); From 4caa8c5ccaa31324c08b40a045f372e6fc2a5745 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 26 May 2014 20:37:12 +0200 Subject: [PATCH 3/4] Fix offset to accumulation root not being cleared when adding an animation state with startpoint=1.f (observed with death animations) --- apps/openmw/mwrender/animation.cpp | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 9124e89b88..a2f901b26a 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -775,11 +775,11 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo } /* Look in reverse; last-inserted source has priority. */ + AnimState state; AnimSourceList::reverse_iterator iter(mAnimSources.rbegin()); for(;iter != mAnimSources.rend();++iter) { const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys; - AnimState state; if(reset(state, textkeys, groupname, start, stop, startpoint)) { state.mSource = *iter; @@ -821,6 +821,13 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo std::cerr<< "Failed to find animation "<setPosition(-mNonAccumCtrl->getTranslation(state.mTime)*mAccumulate); + } } bool Animation::isPlaying(const std::string &groupname) const From cf68f6da96ad4d3be5137f39e6888f25d253019e Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 27 May 2014 00:06:34 +0200 Subject: [PATCH 4/4] Fix ESX dependencies not being checked except for the first one --- apps/openmw/mwworld/esmstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/esmstore.cpp b/apps/openmw/mwworld/esmstore.cpp index fc2bd4d773..03d928d2a5 100644 --- a/apps/openmw/mwworld/esmstore.cpp +++ b/apps/openmw/mwworld/esmstore.cpp @@ -33,12 +33,12 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener) // Cache parent esX files by tracking their indices in the global list of // all files/readers used by the engine. This will greaty accelerate // refnumber mangling, as required for handling moved references. - int index = ~0; const std::vector &masters = esm.getGameFiles(); std::vector *allPlugins = esm.getGlobalReaderList(); for (size_t j = 0; j < masters.size(); j++) { ESM::Header::MasterData &mast = const_cast(masters[j]); std::string fname = mast.name; + int index = ~0; for (int i = 0; i < esm.getIndex(); i++) { const std::string &candidate = allPlugins->at(i).getContext().filename; std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();