diff --git a/apps/openmw/mwrender/characterpreview.cpp b/apps/openmw/mwrender/characterpreview.cpp index a0ba01b37..643225515 100644 --- a/apps/openmw/mwrender/characterpreview.cpp +++ b/apps/openmw/mwrender/characterpreview.cpp @@ -140,8 +140,7 @@ namespace MWRender void InventoryPreview::update(int sizeX, int sizeY) { - // TODO: can we avoid this. Vampire state needs to be updated. - mAnimation->rebuild(); + mAnimation->updateParts(); MWWorld::InventoryStore &inv = MWWorld::Class::get(mCharacter).getInventoryStore(mCharacter); MWWorld::ContainerStoreIterator iter = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight); @@ -177,12 +176,8 @@ namespace MWRender groupname = "inventoryhandtohand"; } - // TODO see above - //if(groupname != mCurrentAnimGroup) - //{ - mCurrentAnimGroup = groupname; - mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); - //} + mCurrentAnimGroup = groupname; + mAnimation->play(mCurrentAnimGroup, 1, Animation::Group_All, false, 1.0f, "start", "stop", 0.0f, 0); MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft); if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name()) @@ -194,7 +189,6 @@ namespace MWRender else if(mAnimation->getInfo("torch")) mAnimation->disable("torch"); - mAnimation->updateParts(); mAnimation->runAnimation(0.0f); mViewport->setDimensions (0, 0, std::min(1.f, float(sizeX) / float(512)), std::min(1.f, float(sizeY) / float(1024))); diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index b1455f0dc..dbb5a0868 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -119,7 +119,8 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v mShowWeapons(false), mShowCarriedLeft(true), mFirstPersonOffset(0.f, 0.f, 0.f), - mAlpha(1.f) + mAlpha(1.f), + mNpcType(Type_Normal) { mNpc = mPtr.get()->mBase; @@ -157,8 +158,8 @@ void NpcAnimation::updateNpcBase() const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const ESM::Race *race = store.get().find(mNpc->mRace); - bool isWerewolf = mPtr.getClass().getNpcStats(mPtr).isWerewolf(); - bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).mMagnitude; + bool isWerewolf = (mNpcType == Type_Werewolf); + bool isVampire = (mNpcType == Type_Vampire); if (isWerewolf) { @@ -167,7 +168,7 @@ void NpcAnimation::updateNpcBase() } else { - if (vampire) + if (isVampire) mHeadModel = getVampireHead(mNpc->mRace, mNpc->mFlags & ESM::NPC::Female); else mHeadModel = "meshes\\" + store.get().find(mNpc->mHead)->mModel; @@ -221,11 +222,24 @@ void NpcAnimation::updateNpcBase() } void NpcAnimation::updateParts() -{ +{ mAlpha = 1.f; const MWWorld::Class &cls = MWWorld::Class::get(mPtr); MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); + NpcType curType = Type_Normal; + if (cls.getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).mMagnitude > 0) + curType = Type_Vampire; + if (cls.getNpcStats(mPtr).isWerewolf()) + curType = Type_Werewolf; + + if (curType != mNpcType) + { + mNpcType = curType; + rebuild(); + return; + } + static const struct { int mSlot; int mBasePriority; @@ -329,7 +343,7 @@ void NpcAnimation::updateParts() static const int Flag_Female = 1<<0; static const int Flag_FirstPerson = 1<<1; - bool isWerewolf = cls.getNpcStats(mPtr).isWerewolf(); + bool isWerewolf = (mNpcType == Type_Werewolf); int flags = (isWerewolf ? -1 : 0); if(!mNpc->isMale()) flags |= Flag_Female; diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 28bb81ddd..8edcc04be 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -55,6 +55,14 @@ private: bool mShowWeapons; bool mShowCarriedLeft; + enum NpcType + { + Type_Normal, + Type_Werewolf, + Type_Vampire + }; + NpcType mNpcType; + int mVisibilityFlags; int mPartslots[ESM::PRT_Count]; //Each part slot is taken by clothing, armor, or is empty diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index 509c34afb..2944f00d4 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -139,8 +139,13 @@ void MWWorld::InventoryStore::equip (int slot, const ContainerStoreIterator& ite void MWWorld::InventoryStore::unequipAll(const MWWorld::Ptr& actor) { + // Only *one* change event should be fired + mUpdatesEnabled = false; for (int slot=0; slot < MWWorld::InventoryStore::Slots; ++slot) unequipSlot(slot, actor); + mUpdatesEnabled = true; + fireEquipmentChangedEvent(); + updateMagicEffects(actor); } MWWorld::ContainerStoreIterator MWWorld::InventoryStore::getSlot (int slot) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index c23c63e2e..621cd75ea 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1960,6 +1960,10 @@ namespace MWWorld npcStats.setWerewolf(werewolf); + // This is a bit dangerous. Equipped items other than WerewolfRobe may reference + // bones that do not even exist with the werewolf object root. + // Therefore, make sure to unequip everything at once, and only fire the change event + // (which will rebuild the animation parts) afterwards. unequipAll will do this for us. MWWorld::InventoryStore& invStore = MWWorld::Class::get(actor).getInventoryStore(actor); invStore.unequipAll(actor); @@ -1974,6 +1978,10 @@ namespace MWWorld actor.getClass().getContainerStore(actor).remove("werewolfrobe", 1, actor); } + // NpcAnimation::updateParts will already rebuild the animation when it detects change of Npc type. + // the following is just for reattaching the camera properly. + mRendering->rebuildPtr(actor); + if(actor.getRefData().getHandle() == "player") { // Update the GUI only when called on the player @@ -1991,8 +1999,6 @@ namespace MWWorld windowManager->unsetForceHide(MWGui::GW_Magic); } } - - mRendering->rebuildPtr(actor); } void World::applyWerewolfAcrobatics(const Ptr &actor)