From 205e8aa4e95129db49032db48b2a8fc1c7cc34f1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Mon, 20 Jan 2014 13:00:43 +0100 Subject: [PATCH] Feature #957: Implement area magic --- apps/openmw/mwbase/mechanicsmanager.hpp | 2 + apps/openmw/mwmechanics/actors.cpp | 9 +++ apps/openmw/mwmechanics/actors.hpp | 2 + .../mwmechanics/mechanicsmanagerimp.cpp | 6 ++ .../mwmechanics/mechanicsmanagerimp.hpp | 2 + apps/openmw/mwmechanics/objects.cpp | 9 +++ apps/openmw/mwmechanics/objects.hpp | 2 + apps/openmw/mwrender/effectmanager.cpp | 6 +- apps/openmw/mwrender/effectmanager.hpp | 4 +- apps/openmw/mwrender/renderingmanager.cpp | 6 +- apps/openmw/mwrender/renderingmanager.hpp | 2 +- apps/openmw/mwworld/worldimp.cpp | 75 +++++++++++++++---- 12 files changed, 102 insertions(+), 23 deletions(-) diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 726c8cf04..914fbded2 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -157,6 +157,8 @@ namespace MWBase virtual void toggleAI() = 0; virtual bool isAIActive() = 0; + + virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects) = 0; }; } diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index f7d5d3a1b..35783bd9f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -919,4 +919,13 @@ namespace MWMechanics return iter->second->isAnimPlaying(groupName); return false; } + + void Actors::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) + { + for (PtrControllerMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter) + { + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) + out.push_back(iter->first); + } + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index b7544dad4..78aae6861 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -94,6 +94,8 @@ namespace MWMechanics void skipAnimation(const MWWorld::Ptr& ptr); bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName); + void getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out); + private: PtrControllerMap mActors; diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index fff3db8a9..a0183e973 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -950,4 +950,10 @@ namespace MWMechanics return (roll >= target); } + + void MechanicsManager::getObjectsInRange(const Ogre::Vector3 &position, float radius, std::vector &objects) + { + mActors.getObjectsInRange(position, radius, objects); + mObjects.getObjectsInRange(position, radius, objects); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 469123df9..012de2e32 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -135,6 +135,8 @@ namespace MWMechanics virtual void toggleAI(); virtual bool isAIActive(); + + virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& objects); }; } diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index 41d6b4ffa..b09574923 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -92,4 +92,13 @@ void Objects::skipAnimation(const MWWorld::Ptr& ptr) iter->second->skipAnim(); } +void Objects::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector& out) +{ + for (PtrControllerMap::iterator iter = mObjects.begin(); iter != mObjects.end(); ++iter) + { + if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius) + out.push_back(iter->first); + } +} + } diff --git a/apps/openmw/mwmechanics/objects.hpp b/apps/openmw/mwmechanics/objects.hpp index 32432c130..373a2a105 100644 --- a/apps/openmw/mwmechanics/objects.hpp +++ b/apps/openmw/mwmechanics/objects.hpp @@ -40,6 +40,8 @@ namespace MWMechanics void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number); void skipAnimation(const MWWorld::Ptr& ptr); + + void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector& out); }; } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp index eb4525a4f..7d41525b7 100644 --- a/apps/openmw/mwrender/effectmanager.cpp +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -26,9 +26,10 @@ EffectManager::EffectManager(Ogre::SceneManager *sceneMgr) { } -void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition) +void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition, float scale) { Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition); + sceneNode->setScale(scale,scale,scale); // fix texture extension to .dds if (textureOverride.size() > 4) @@ -78,7 +79,7 @@ void EffectManager::addEffect(const std::string &model, std::string textureOverr mEffects.push_back(std::make_pair(sceneNode, scene)); } -void EffectManager::update(float dt) +void EffectManager::update(float dt, Ogre::Camera* camera) { for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) { @@ -91,6 +92,7 @@ void EffectManager::update(float dt) objects->mControllers[i].update(); } + objects->rotateBillboardNodes(camera); // Finished playing? if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp index 0c8bc3857..bc9e68d26 100644 --- a/apps/openmw/mwrender/effectmanager.hpp +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -14,9 +14,9 @@ namespace MWRender ~EffectManager() { clear(); } /// Add an effect. When it's finished playing, it will be removed automatically. - void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition); + void addEffect (const std::string& model, std::string textureOverride, const Ogre::Vector3& worldPosition, float scale); - void update(float dt); + void update(float dt, Ogre::Camera* camera); /// Remove all effects void clear(); diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 572d2abdb..515112668 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -378,7 +378,7 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; - mEffectManager->update(duration); + mEffectManager->update(duration, mRendering.getCamera()); mActors->update (mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera()); @@ -1024,9 +1024,9 @@ float RenderingManager::getCameraDistance() const return mCamera->getCameraDistance(); } -void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition) +void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition, float scale) { - mEffectManager->addEffect(model, texture, worldPosition); + mEffectManager->addEffect(model, "", worldPosition, scale); } } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index b6379bee4..ea9a64120 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -207,7 +207,7 @@ public: void stopVideo(); void frameStarted(float dt, bool paused); - void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition); + void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition, float scale=1.f); protected: virtual void windowResized(int x, int y); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 9be7830cf..ccb16d5fb 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2174,9 +2174,16 @@ namespace MWWorld state.mId = id; state.mActorHandle = actor.getRefData().getHandle(); state.mSpeed = speed; - state.mEffects = effects; state.mStack = stack; + // Only interested in "on target" effects + for (std::vector::const_iterator iter (effects.mList.begin()); + iter!=effects.mList.end(); ++iter) + { + if (iter->mRange == ESM::RT_Target) + state.mEffects.mList.push_back(*iter); + } + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); @@ -2223,30 +2230,68 @@ namespace MWWorld continue; explode = true; + } - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); - if (caster.isEmpty()) - caster = obstacle; - if (obstacle.isEmpty()) + if (explode) + { + std::map > toApply; + for (std::vector::const_iterator effectIt = it->second.mEffects.mList.begin(); + effectIt != it->second.mEffects.mList.end(); ++effectIt) { - // Terrain + const ESM::MagicEffect* effect = getStore().get().find(effectIt->mEffectID); + + // Spawn the explosion orb effect + const ESM::Static* areaStatic; + if (!effect->mCasting.empty()) + areaStatic = getStore().get().find (effect->mArea); + else + areaStatic = getStore().get().find ("VFX_DefaultArea"); + + mRendering->spawnEffect("meshes\\" + areaStatic->mModel, "", Ogre::Vector3(ptr.getRefData().getPosition().pos), effectIt->mArea); + + // Play explosion sound (make sure to use NoTrack, since we will delete the projectile now) + static const std::string schools[] = { + "alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration" + }; + MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); + if(!effect->mAreaSound.empty()) + sndMgr->playSound3D(ptr, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + else + sndMgr->playSound3D(ptr, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack); + + // Get the actors in range of the effect + std::vector objects; + MWBase::Environment::get().getMechanicsManager()->getObjectsInRange( + Ogre::Vector3(ptr.getRefData().getPosition().pos), feetToGameUnits(effectIt->mArea), objects); + + for (std::vector::iterator affected = objects.begin(); affected != objects.end(); ++affected) + toApply[*affected].push_back(*effectIt); } - else + + // Now apply the appropriate effects to each actor in range + for (std::map >::iterator apply = toApply.begin(); apply != toApply.end(); ++apply) { - MWMechanics::CastSpell cast(caster, obstacle); - cast.mStack = it->second.mStack; + MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + + // Vanilla-compatible behaviour of never applying the spell to the caster + // (could be changed by mods later) + if (apply->first == caster) + continue; + + if (caster.isEmpty()) + caster = apply->first; + + MWMechanics::CastSpell cast(caster, apply->first); cast.mId = it->second.mId; cast.mSourceName = it->second.mSourceName; - cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false); + cast.mStack = it->second.mStack; + ESM::EffectList effects; + effects.mList = apply->second; + cast.inflict(apply->first, caster, effects, ESM::RT_Target); } deleteObject(ptr); mProjectiles.erase(it++); - } - - if (explode) - { - // TODO: Explode continue; }