diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5769138fa..a89a06091 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -648,6 +648,8 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim if(!mAnimation) return; + mAnimation->setTextKeyListener(this); + const MWWorld::Class &cls = mPtr.getClass(); if(cls.isActor()) { @@ -698,8 +700,147 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim CharacterController::~CharacterController() { + if (mAnimation) + mAnimation->setTextKeyListener(NULL); } +void split(const std::string &s, char delim, std::vector &elems) { + std::stringstream ss(s); + std::string item; + while (std::getline(ss, item, delim)) { + elems.push_back(item); + } +} + +void CharacterController::handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, const std::multimap &map) +{ + const std::string &evt = key->second; + + if(evt.compare(0, 7, "sound: ") == 0) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); + return; + } + if(evt.compare(0, 10, "soundgen: ") == 0) + { + std::string soundgen = evt.substr(10); + + // The event can optionally contain volume and pitch modifiers + float volume=1.f, pitch=1.f; + if (soundgen.find(" ") != std::string::npos) + { + std::vector tokens; + split(soundgen, ' ', tokens); + soundgen = tokens[0]; + if (tokens.size() >= 2) + volume = Ogre::StringConverter::parseReal(tokens[1]); + if (tokens.size() >= 3) + pitch = Ogre::StringConverter::parseReal(tokens[2]); + } + + std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen); + if(!sound.empty()) + { + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx; + if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0 || evt.compare(10, evt.size()-10, "land") == 0) + type = MWBase::SoundManager::Play_TypeFoot; + sndMgr->playSound3D(mPtr, sound, volume, pitch, type); + } + return; + } + + if(evt.compare(0, groupname.size(), groupname) != 0 || + evt.compare(groupname.size(), 2, ": ") != 0) + { + // Not ours, skip it + return; + } + size_t off = groupname.size()+2; + size_t len = evt.size() - off; + + if(evt.compare(off, len, "equip attach") == 0) + mAnimation->showWeapons(true); + else if(evt.compare(off, len, "unequip detach") == 0) + mAnimation->showWeapons(false); + else if(evt.compare(off, len, "chop hit") == 0) + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); + else if(evt.compare(off, len, "slash hit") == 0) + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); + else if(evt.compare(off, len, "thrust hit") == 0) + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); + else if(evt.compare(off, len, "hit") == 0) + { + if (groupname == "attack1") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); + else if (groupname == "attack2") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); + else if (groupname == "attack3") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); + else + mPtr.getClass().hit(mPtr); + } + else if (!groupname.empty() && groupname.compare(0, groupname.size()-1, "attack") == 0 + && evt.compare(off, len, "start") == 0) + { + std::multimap::const_iterator hitKey = key; + + // Not all animations have a hit key defined. If there is none, the hit happens with the start key. + bool hasHitKey = false; + while (hitKey != map.end()) + { + if (hitKey->second == groupname + ": hit") + { + hasHitKey = true; + break; + } + if (hitKey->second == groupname + ": stop") + break; + ++hitKey; + } + if (!hasHitKey) + { + if (groupname == "attack1") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Chop); + else if (groupname == "attack2") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Slash); + else if (groupname == "attack3") + mPtr.getClass().hit(mPtr, ESM::Weapon::AT_Thrust); + } + } + else if (evt.compare(off, len, "shoot attach") == 0) + mAnimation->attachArrow(); + else if (evt.compare(off, len, "shoot release") == 0) + {;}//mAnimation->releaseArrow(); + else if (evt.compare(off, len, "shoot follow attach") == 0) + mAnimation->attachArrow(); + + else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") + { + // Make sure this key is actually for the RangeType we are casting. The flame atronach has + // the same animation for all range types, so there are 3 "release" keys on the same time, one for each range type. + // FIXME: compare with mCurrentWeapon instead + const std::string& spellid = mPtr.getClass().getCreatureStats(mPtr).getSpells().getSelectedSpell(); + const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get().find(spellid); + const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0); + int range = 0; + if (evt.compare(off, len, "self release") == 0) + range = 0; + else if (evt.compare(off, len, "touch release") == 0) + range = 1; + else if (evt.compare(off, len, "target release") == 0) + range = 2; + if (effectentry.mRange == range) + { + MWBase::Environment::get().getWorld()->castSpell(mPtr); + } + std::cout << "current attack: " << mCurrentWeapon << std::endl; + } + + else if (groupname == "shield" && evt.compare(off, len, "block hit") == 0) + mPtr.getClass().block(mPtr); +} void CharacterController::updatePtr(const MWWorld::Ptr &ptr) { diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 987a0d29a..2b2131061 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -7,6 +7,8 @@ #include "../mwworld/ptr.hpp" +#include "../mwrender/animation.hpp" + namespace MWWorld { class ContainerStoreIterator; @@ -134,7 +136,7 @@ enum JumpingState { JumpState_Landing }; -class CharacterController +class CharacterController : public MWRender::Animation::TextKeyListener { MWWorld::Ptr mPtr; MWRender::Animation *mAnimation; @@ -205,6 +207,9 @@ public: CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); virtual ~CharacterController(); + virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, + const std::multimap& map); + // Be careful when to call this, see comment in Actors void updateContinuousVfx(); diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 416f66cec..e8d6bbcc9 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -238,6 +238,7 @@ namespace MWRender , mInsert(parentNode) , mResourceSystem(resourceSystem) , mAccumulate(1.f, 1.f, 0.f) + , mTextKeyListener(NULL) { for(size_t i = 0;i < sNumGroups;i++) mAnimationTimePtr[i].reset(new AnimationTime(this)); @@ -426,7 +427,8 @@ namespace MWRender else if(evt.compare(off, len, "loop stop") == 0) state.mLoopStopTime = key->first; - // TODO: forward to listener? + if (mTextKeyListener) + mTextKeyListener->handleTextKey(groupname, key, map); } void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, @@ -590,6 +592,11 @@ namespace MWRender return true; } + void Animation::setTextKeyListener(Animation::TextKeyListener *listener) + { + mTextKeyListener = listener; + } + void Animation::resetActiveGroups() { // remove all previous external controllers from the scene graph diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 82b54bdea..20fbbed5c 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -54,6 +54,15 @@ public: Group_All = Group_LowerBody | Group_UpperBody }; + class TextKeyListener + { + public: + virtual void handleTextKey(const std::string &groupname, const std::multimap::const_iterator &key, + const std::multimap& map) = 0; + }; + + void setTextKeyListener(TextKeyListener* listener); + protected: /* This is the number of *discrete* groups. */ static const size_t sNumGroups = 4; @@ -203,6 +212,8 @@ protected: std::vector mEffects; + TextKeyListener* mTextKeyListener; + /* Sets the appropriate animations on the bone groups based on priority. */ void resetActiveGroups(); diff --git a/apps/openmw/mwrender/objects.cpp b/apps/openmw/mwrender/objects.cpp index 5face96a6..6c5709b5d 100644 --- a/apps/openmw/mwrender/objects.cpp +++ b/apps/openmw/mwrender/objects.cpp @@ -158,7 +158,7 @@ void Objects::insertNPC(const MWWorld::Ptr &ptr) mObjects.insert(std::make_pair(ptr, anim.release())); } -bool Objects::deleteObject (const MWWorld::Ptr& ptr) +bool Objects::removeObject (const MWWorld::Ptr& ptr) { if(!ptr.getRefData().getBaseNode()) return true; diff --git a/apps/openmw/mwrender/objects.hpp b/apps/openmw/mwrender/objects.hpp index 3e1af6087..298dc97bb 100644 --- a/apps/openmw/mwrender/objects.hpp +++ b/apps/openmw/mwrender/objects.hpp @@ -66,7 +66,7 @@ public: //Ogre::AxisAlignedBox getDimensions(MWWorld::CellStore*); ///< get a bounding box that encloses all objects in the specified cell - bool deleteObject (const MWWorld::Ptr& ptr); + bool removeObject (const MWWorld::Ptr& ptr); ///< \return found? void removeCell(const MWWorld::CellStore* store); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index bbff16a10..9b796a359 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -285,6 +285,11 @@ namespace MWRender ptr.getRefData().getBaseNode()->setScale(scale); } + void RenderingManager::removeObject(const MWWorld::Ptr &ptr) + { + mObjects->removeObject(ptr); + } + void RenderingManager::updatePtr(const MWWorld::Ptr &old, const MWWorld::Ptr &updated) { mObjects->updatePtr(old, updated); diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index bb04d04e1..8a81aacd7 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -71,6 +71,8 @@ namespace MWRender void moveObject(const MWWorld::Ptr& ptr, const osg::Vec3f& pos); void scaleObject(const MWWorld::Ptr& ptr, const osg::Vec3f& scale); + void removeObject(const MWWorld::Ptr& ptr); + void setSkyEnabled(bool enabled); bool toggleRenderMode(RenderMode mode); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index fd63146f2..d210ff162 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -210,12 +210,12 @@ namespace MWWorld mPhysics->removeHeightField ((*iter)->getCell()->getGridX(), (*iter)->getCell()->getGridY()); } + MWBase::Environment::get().getMechanicsManager()->drop (*iter); + mRendering.removeCell(*iter); MWBase::Environment::get().getWorld()->getLocalScripts().clearCell (*iter); - MWBase::Environment::get().getMechanicsManager()->drop (*iter); - MWBase::Environment::get().getSoundManager()->stopSound (*iter); mActiveCells.erase(*iter); } @@ -563,7 +563,7 @@ namespace MWWorld MWBase::Environment::get().getMechanicsManager()->remove (ptr); MWBase::Environment::get().getSoundManager()->stopSound3D (ptr); mPhysics->remove(ptr); - //mRendering.removeObject (ptr); + mRendering.removeObject (ptr); } bool Scene::isCellActive(const CellStore &cell)