mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:53:51 +00:00
Refactor projectiles to no longer use MW-objects
This commit is contained in:
parent
18852c09d0
commit
e5a21aca53
13 changed files with 528 additions and 373 deletions
|
@ -57,7 +57,7 @@ add_openmw_dir (mwworld
|
|||
cells localscripts customdata weather inventorystore ptr actionopen actionread
|
||||
actionequip timestamp actionalchemy cellstore actionapply actioneat
|
||||
esmstore store recordcmp fallback actionrepair actionsoulgem livecellref actiondoor
|
||||
contentloader esmloader omwloader actiontrap cellreflist
|
||||
contentloader esmloader omwloader actiontrap cellreflist projectilemanager
|
||||
)
|
||||
|
||||
add_openmw_dir (mwclass
|
||||
|
|
|
@ -110,18 +110,25 @@ namespace MWBase
|
|||
///< Play a sound, independently of 3D-position
|
||||
///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
|
||||
|
||||
virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
|
||||
virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
|
||||
float volume, float pitch, PlayType type=Play_TypeSfx,
|
||||
PlayMode mode=Play_Normal, float offset=0) = 0;
|
||||
///< Play a sound from an object
|
||||
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified.
|
||||
///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
|
||||
|
||||
virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId,
|
||||
float volume, float pitch, PlayType type, PlayMode mode, float offset=0) = 0;
|
||||
///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition.
|
||||
|
||||
virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0;
|
||||
///< Stop the given object from playing the given sound,
|
||||
|
||||
virtual void stopSound3D(const MWWorld::Ptr &reference) = 0;
|
||||
///< Stop the given object from playing all sounds.
|
||||
|
||||
virtual void stopSound(MWBase::SoundPtr sound) = 0;
|
||||
///< Stop the given sound handle
|
||||
|
||||
virtual void stopSound(const MWWorld::CellStore *cell) = 0;
|
||||
///< Stop all sounds for the given cell.
|
||||
|
||||
|
|
|
@ -467,7 +467,8 @@ namespace MWBase
|
|||
|
||||
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
|
||||
|
||||
virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
|
||||
|
@ -513,7 +514,7 @@ namespace MWBase
|
|||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
||||
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
|
||||
|
||||
virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects,
|
||||
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -21,6 +21,41 @@
|
|||
#include "magiceffects.hpp"
|
||||
#include "npcstats.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
||||
/// Get projectile properties (model, sound and speed) for a spell with the given effects
|
||||
/// If \a model is empty, the spell has no ranged effects and should not spawn a projectile.
|
||||
void getProjectileInfo (const ESM::EffectList& effects, std::string& model, std::string& sound, float& speed)
|
||||
{
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
|
||||
iter!=effects.mList.end(); ++iter)
|
||||
{
|
||||
if (iter->mRange != ESM::RT_Target)
|
||||
continue;
|
||||
|
||||
const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
|
||||
iter->mEffectID);
|
||||
|
||||
model = magicEffect->mBolt;
|
||||
if (model.empty())
|
||||
model = "VFX_DefaultBolt";
|
||||
|
||||
static const std::string schools[] = {
|
||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||
};
|
||||
if (!magicEffect->mBoltSound.empty())
|
||||
sound = magicEffect->mBoltSound;
|
||||
else
|
||||
sound = schools[magicEffect->mData.mSchool] + " bolt";
|
||||
|
||||
speed = magicEffect->mData.mSpeed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
|
@ -409,7 +444,7 @@ namespace MWMechanics
|
|||
}
|
||||
|
||||
if (!exploded)
|
||||
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, mTarget, effects, caster, mId, mSourceName);
|
||||
MWBase::Environment::get().getWorld()->explodeSpell(mHitPosition, effects, caster, mId, mSourceName);
|
||||
|
||||
if (!reflectedEffects.mList.empty())
|
||||
inflict(caster, target, reflectedEffects, range, true);
|
||||
|
@ -603,7 +638,13 @@ namespace MWMechanics
|
|||
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, enchantment->mEffects, mCaster, mSourceName);
|
||||
std::string projectileModel;
|
||||
std::string sound;
|
||||
float speed = 0;
|
||||
getProjectileInfo(enchantment->mEffects, projectileModel, sound, speed);
|
||||
if (!projectileModel.empty())
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||
false, enchantment->mEffects, mCaster, mSourceName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
@ -682,7 +723,15 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(mId, false, spell->mEffects, mCaster, mSourceName);
|
||||
|
||||
std::string projectileModel;
|
||||
std::string sound;
|
||||
float speed = 0;
|
||||
getProjectileInfo(spell->mEffects, projectileModel, sound, speed);
|
||||
if (!projectileModel.empty())
|
||||
MWBase::Environment::get().getWorld()->launchMagicBolt(projectileModel, sound, mId, speed,
|
||||
false, spell->mEffects, mCaster, mSourceName);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,18 +11,6 @@
|
|||
namespace MWRender
|
||||
{
|
||||
|
||||
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
float mTime;
|
||||
public:
|
||||
EffectAnimationTime() : mTime(0) { }
|
||||
void addTime(float time) { mTime += time; }
|
||||
|
||||
virtual Ogre::Real getValue() const { return mTime; }
|
||||
virtual void setValue(Ogre::Real value) {}
|
||||
};
|
||||
|
||||
EffectManager::EffectManager(Ogre::SceneManager *sceneMgr)
|
||||
: mSceneMgr(sceneMgr)
|
||||
{
|
||||
|
|
|
@ -5,6 +5,20 @@
|
|||
|
||||
namespace MWRender
|
||||
{
|
||||
|
||||
class EffectAnimationTime : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
private:
|
||||
float mTime;
|
||||
public:
|
||||
EffectAnimationTime() : mTime(0) { }
|
||||
void addTime(float time) { mTime += time; }
|
||||
|
||||
virtual Ogre::Real getValue() const { return mTime; }
|
||||
virtual void setValue(Ogre::Real value) {}
|
||||
};
|
||||
|
||||
|
||||
// Note: effects attached to another object should be managed by MWRender::Animation::addEffect.
|
||||
// This class manages "free" effects, i.e. attached to a dedicated scene node in the world.
|
||||
class EffectManager
|
||||
|
|
|
@ -356,6 +356,44 @@ namespace MWSound
|
|||
return sound;
|
||||
}
|
||||
|
||||
MWBase::SoundPtr SoundManager::playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId,
|
||||
float volume, float pitch, PlayType type, PlayMode mode, float offset)
|
||||
{
|
||||
MWBase::SoundPtr sound;
|
||||
if(!mOutput->isInitialized())
|
||||
return sound;
|
||||
try
|
||||
{
|
||||
// Look up the sound in the ESM data
|
||||
float basevol = volumeFromType(type);
|
||||
float min, max;
|
||||
std::string file = lookup(soundId, volume, min, max);
|
||||
|
||||
sound = mOutput->playSound3D(file, initialPos, volume, basevol, pitch, min, max, mode|type, offset);
|
||||
mActiveSounds[sound] = std::make_pair(MWWorld::Ptr(), soundId);
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
//std::cout <<"Sound Error: "<<e.what()<< std::endl;
|
||||
}
|
||||
return sound;
|
||||
}
|
||||
|
||||
void SoundManager::stopSound (MWBase::SoundPtr sound)
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
while(snditer != mActiveSounds.end())
|
||||
{
|
||||
if(snditer->first == sound)
|
||||
{
|
||||
snditer->first->stop();
|
||||
mActiveSounds.erase(snditer++);
|
||||
}
|
||||
else
|
||||
++snditer;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::stopSound3D(const MWWorld::Ptr &ptr, const std::string& soundId)
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
|
|
|
@ -115,6 +115,13 @@ namespace MWSound
|
|||
virtual MWBase::SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
|
||||
float volume, float pitch, PlayType type=Play_TypeSfx,
|
||||
PlayMode mode=Play_Normal, float offset=0);
|
||||
///< Play a 3D sound attached to an MWWorld::Ptr. Will be updated automatically with the Ptr's position, unless Play_NoTrack is specified.
|
||||
///< @param offset Value from [0,1] meaning from which fraction the sound the playback starts.
|
||||
|
||||
virtual MWBase::SoundPtr playManualSound3D(const Ogre::Vector3& initialPos, const std::string& soundId,
|
||||
float volume, float pitch, PlayType type, PlayMode mode, float offset=0);
|
||||
///< Play a 3D sound at \a initialPos. If the sound should be moving, it must be updated manually using Sound::setPosition.
|
||||
|
||||
///< Play a sound from an object
|
||||
///< @param offset value from [0,1], when to start playback. 0 is beginning, 1 is end.
|
||||
|
||||
|
@ -124,6 +131,9 @@ namespace MWSound
|
|||
virtual void stopSound3D(const MWWorld::Ptr &reference);
|
||||
///< Stop the given object from playing all sounds.
|
||||
|
||||
virtual void stopSound(MWBase::SoundPtr sound);
|
||||
///< Stop the given sound handle
|
||||
|
||||
virtual void stopSound(const MWWorld::CellStore *cell);
|
||||
///< Stop all sounds for the given cell.
|
||||
|
||||
|
|
261
apps/openmw/mwworld/projectilemanager.cpp
Normal file
261
apps/openmw/mwworld/projectilemanager.cpp
Normal file
|
@ -0,0 +1,261 @@
|
|||
#include "projectilemanager.hpp"
|
||||
|
||||
#include <OgreSceneManager.h>
|
||||
|
||||
#include <libs/openengine/bullet/physic.hpp>
|
||||
|
||||
#include "../mwworld/manualref.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "../mwmechanics/combat.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/spellcasting.hpp"
|
||||
|
||||
#include "../mwrender/effectmanager.hpp"
|
||||
|
||||
#include "../mwsound/sound.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
ProjectileManager::ProjectileManager(Ogre::SceneManager* sceneMgr, OEngine::Physic::PhysicEngine &engine)
|
||||
: mPhysEngine(engine)
|
||||
, mSceneMgr(sceneMgr)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void ProjectileManager::createModel(State &state, const std::string &model)
|
||||
{
|
||||
state.mObject = NifOgre::Loader::createObjects(state.mNode, model);
|
||||
for(size_t i = 0;i < state.mObject->mControllers.size();i++)
|
||||
{
|
||||
if(state.mObject->mControllers[i].getSource().isNull())
|
||||
state.mObject->mControllers[i].setSource(Ogre::SharedPtr<MWRender::EffectAnimationTime> (new MWRender::EffectAnimationTime()));
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectileManager::update(NifOgre::ObjectScenePtr object, float duration)
|
||||
{
|
||||
for(size_t i = 0; i < object->mControllers.size() ;i++)
|
||||
{
|
||||
MWRender::EffectAnimationTime* value = dynamic_cast<MWRender::EffectAnimationTime*>(object->mControllers[i].getSource().get());
|
||||
if (value)
|
||||
value->addTime(duration);
|
||||
|
||||
object->mControllers[i].update();
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectileManager::launchMagicBolt(const std::string &model, const std::string &sound,
|
||||
const std::string &spellId, float speed, bool stack,
|
||||
const ESM::EffectList &effects, const Ptr &actor, const std::string &sourceName)
|
||||
{
|
||||
// Spawn at 0.75 * ActorHeight
|
||||
float height = mPhysEngine.getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
|
||||
|
||||
Ogre::Vector3 pos(actor.getRefData().getPosition().pos);
|
||||
pos.z += height;
|
||||
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
|
||||
MagicBoltState state;
|
||||
state.mSourceName = sourceName;
|
||||
state.mId = spellId;
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mSpeed = speed;
|
||||
state.mStack = stack;
|
||||
|
||||
// Only interested in "on target" effects
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
|
||||
iter!=effects.mList.end(); ++iter)
|
||||
{
|
||||
if (iter->mRange == ESM::RT_Target)
|
||||
state.mEffects.mList.push_back(*iter);
|
||||
}
|
||||
|
||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), model);
|
||||
MWWorld::Ptr ptr = ref.getPtr();
|
||||
|
||||
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient);
|
||||
createModel(state, ptr.getClass().getModel(ptr));
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
state.mSound = sndMgr->playManualSound3D(pos, sound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop);
|
||||
|
||||
mMagicBolts.push_back(state);
|
||||
}
|
||||
|
||||
void ProjectileManager::launchProjectile(Ptr actor, Ptr projectile, const Ogre::Vector3 &pos,
|
||||
const Ogre::Quaternion &orient, Ptr bow, float speed)
|
||||
{
|
||||
ProjectileState state;
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mBow = bow;
|
||||
state.mVelocity = orient.yAxis() * speed;
|
||||
state.mProjectileId = projectile.getCellRef().mRefID;
|
||||
|
||||
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), projectile.getCellRef().mRefID);
|
||||
MWWorld::Ptr ptr = ref.getPtr();
|
||||
|
||||
state.mNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(pos, orient);
|
||||
createModel(state, ptr.getClass().getModel(ptr));
|
||||
|
||||
mProjectiles.push_back(state);
|
||||
}
|
||||
|
||||
void ProjectileManager::update(float dt)
|
||||
{
|
||||
moveProjectiles(dt);
|
||||
moveMagicBolts(dt);
|
||||
}
|
||||
|
||||
void ProjectileManager::moveMagicBolts(float duration)
|
||||
{
|
||||
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
|
||||
{
|
||||
Ogre::Quaternion orient = it->mNode->getOrientation();
|
||||
static float fTargetSpellMaxSpeed = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||
.find("fTargetSpellMaxSpeed")->getFloat();
|
||||
float speed = fTargetSpellMaxSpeed * it->mSpeed;
|
||||
|
||||
Ogre::Vector3 direction = orient.yAxis();
|
||||
direction.normalise();
|
||||
Ogre::Vector3 pos(it->mNode->getPosition());
|
||||
Ogre::Vector3 newPos = pos + direction * duration * speed;
|
||||
|
||||
it->mSound->setPosition(newPos);
|
||||
|
||||
it->mNode->setPosition(newPos);
|
||||
|
||||
update(it->mObject, duration);
|
||||
|
||||
// Check for impact
|
||||
// TODO: use a proper btRigidBody / btGhostObject?
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
|
||||
bool hit=false;
|
||||
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
|
||||
{
|
||||
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
|
||||
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
if (caster.isEmpty())
|
||||
caster = obstacle;
|
||||
|
||||
if (obstacle.isEmpty())
|
||||
{
|
||||
// Terrain
|
||||
}
|
||||
else
|
||||
{
|
||||
MWMechanics::CastSpell cast(caster, obstacle);
|
||||
cast.mHitPosition = pos;
|
||||
cast.mId = it->mId;
|
||||
cast.mSourceName = it->mSourceName;
|
||||
cast.mStack = it->mStack;
|
||||
cast.inflict(obstacle, caster, it->mEffects, ESM::RT_Target, false, true);
|
||||
}
|
||||
|
||||
hit = true;
|
||||
}
|
||||
if (hit)
|
||||
{
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
MWBase::Environment::get().getWorld()->explodeSpell(pos, it->mEffects, caster, it->mId, it->mSourceName);
|
||||
|
||||
MWBase::Environment::get().getSoundManager()->stopSound(it->mSound);
|
||||
|
||||
mSceneMgr->destroySceneNode(it->mNode);
|
||||
|
||||
it = mMagicBolts.erase(it);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectileManager::moveProjectiles(float duration)
|
||||
{
|
||||
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
|
||||
{
|
||||
// gravity constant - must be way lower than the gravity affecting actors, since we're not
|
||||
// simulating aerodynamics at all
|
||||
it->mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration;
|
||||
|
||||
Ogre::Vector3 pos(it->mNode->getPosition());
|
||||
Ogre::Vector3 newPos = pos + it->mVelocity * duration;
|
||||
|
||||
Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->mVelocity);
|
||||
it->mNode->setOrientation(orient);
|
||||
it->mNode->setPosition(newPos);
|
||||
|
||||
update(it->mObject, duration);
|
||||
|
||||
// Check for impact
|
||||
// TODO: use a proper btRigidBody / btGhostObject?
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine.rayTest2(from, to);
|
||||
bool hit=false;
|
||||
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
|
||||
{
|
||||
MWWorld::Ptr obstacle = MWBase::Environment::get().getWorld()->searchPtrViaHandle(cIt->second);
|
||||
|
||||
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->mActorId);
|
||||
|
||||
// Arrow intersects with player immediately after shooting :/
|
||||
if (obstacle == caster)
|
||||
continue;
|
||||
|
||||
if (caster.isEmpty())
|
||||
caster = obstacle;
|
||||
|
||||
if (obstacle.isEmpty())
|
||||
{
|
||||
// Terrain
|
||||
}
|
||||
else if (obstacle.getClass().isActor())
|
||||
{
|
||||
MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mProjectileId);
|
||||
MWMechanics::projectileHit(caster, obstacle, it->mBow, projectileRef.getPtr(), pos + (newPos - pos) * cIt->first);
|
||||
}
|
||||
hit = true;
|
||||
}
|
||||
if (hit)
|
||||
{
|
||||
mSceneMgr->destroySceneNode(it->mNode);
|
||||
|
||||
it = mProjectiles.erase(it);
|
||||
continue;
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
void ProjectileManager::clear()
|
||||
{
|
||||
for (std::vector<ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end(); ++it)
|
||||
{
|
||||
mSceneMgr->destroySceneNode(it->mNode);
|
||||
}
|
||||
mProjectiles.clear();
|
||||
for (std::vector<MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end(); ++it)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->stopSound(it->mSound);
|
||||
mSceneMgr->destroySceneNode(it->mNode);
|
||||
}
|
||||
mMagicBolts.clear();
|
||||
}
|
||||
|
||||
}
|
104
apps/openmw/mwworld/projectilemanager.hpp
Normal file
104
apps/openmw/mwworld/projectilemanager.hpp
Normal file
|
@ -0,0 +1,104 @@
|
|||
#ifndef OPENMW_MWWORLD_PROJECTILEMANAGER_H
|
||||
#define OPENMW_MWWORLD_PROJECTILEMANAGER_H
|
||||
|
||||
#include <string>
|
||||
|
||||
#include <OgreVector3.h>
|
||||
|
||||
#include <components/esm/effectlist.hpp>
|
||||
#include <components/nifogre/ogrenifloader.hpp>
|
||||
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "ptr.hpp"
|
||||
|
||||
namespace OEngine
|
||||
{
|
||||
namespace Physic
|
||||
{
|
||||
class PhysicEngine;
|
||||
}
|
||||
}
|
||||
|
||||
namespace Ogre
|
||||
{
|
||||
class SceneManager;
|
||||
}
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
|
||||
class ProjectileManager
|
||||
{
|
||||
public:
|
||||
ProjectileManager (Ogre::SceneManager* sceneMgr,
|
||||
OEngine::Physic::PhysicEngine& engine);
|
||||
|
||||
void launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
|
||||
void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& pos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
|
||||
void update(float dt);
|
||||
|
||||
/// Removes all current projectiles. Should be called when switching to a new worldspace.
|
||||
void clear();
|
||||
|
||||
private:
|
||||
OEngine::Physic::PhysicEngine& mPhysEngine;
|
||||
Ogre::SceneManager* mSceneMgr;
|
||||
|
||||
struct State
|
||||
{
|
||||
NifOgre::ObjectScenePtr mObject;
|
||||
Ogre::SceneNode* mNode;
|
||||
};
|
||||
|
||||
struct MagicBoltState : public State
|
||||
{
|
||||
// Id of spell or enchantment to apply when it hits
|
||||
std::string mId;
|
||||
|
||||
// Actor who casted this projectile
|
||||
int mActorId;
|
||||
|
||||
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
|
||||
std::string mSourceName;
|
||||
|
||||
ESM::EffectList mEffects;
|
||||
|
||||
float mSpeed;
|
||||
|
||||
bool mStack;
|
||||
|
||||
MWBase::SoundPtr mSound;
|
||||
};
|
||||
|
||||
struct ProjectileState : public State
|
||||
{
|
||||
// Actor who shot this projectile
|
||||
int mActorId;
|
||||
|
||||
// RefID of the projectile
|
||||
std::string mProjectileId;
|
||||
|
||||
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from (may be empty)
|
||||
|
||||
Ogre::Vector3 mVelocity;
|
||||
};
|
||||
|
||||
std::vector<MagicBoltState> mMagicBolts;
|
||||
std::vector<ProjectileState> mProjectiles;
|
||||
|
||||
void moveProjectiles(float dt);
|
||||
void moveMagicBolts(float dt);
|
||||
|
||||
void createModel (State& state, const std::string& model);
|
||||
void update (NifOgre::ObjectScenePtr object, float duration);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
|
@ -344,8 +344,6 @@ namespace MWWorld
|
|||
// Sky system
|
||||
MWBase::Environment::get().getWorld()->adjustSky();
|
||||
|
||||
mRendering.switchToExterior();
|
||||
|
||||
mCellChanged = true;
|
||||
|
||||
loadingListener->removeWallpaper();
|
||||
|
@ -439,7 +437,6 @@ namespace MWWorld
|
|||
mCurrentCell = cell;
|
||||
|
||||
// adjust fog
|
||||
mRendering.switchToInterior();
|
||||
mRendering.configureFog(*mCurrentCell);
|
||||
|
||||
// adjust player
|
||||
|
|
|
@ -43,6 +43,7 @@
|
|||
#include "containerstore.hpp"
|
||||
#include "inventorystore.hpp"
|
||||
#include "actionteleport.hpp"
|
||||
#include "projectilemanager.hpp"
|
||||
|
||||
#include "contentloader.hpp"
|
||||
#include "esmloader.hpp"
|
||||
|
@ -136,6 +137,8 @@ namespace MWWorld
|
|||
mPhysics = new PhysicsSystem(renderer);
|
||||
mPhysEngine = mPhysics->getEngine();
|
||||
|
||||
mProjectileManager.reset(new ProjectileManager(renderer.getScene(), *mPhysEngine));
|
||||
|
||||
mRendering = new MWRender::RenderingManager(renderer, resDir, cacheDir, mPhysEngine,&mFallback);
|
||||
|
||||
mPhysEngine->setSceneManager(renderer.getScene());
|
||||
|
@ -255,8 +258,6 @@ namespace MWWorld
|
|||
|
||||
mCells.clear();
|
||||
|
||||
mMagicBolts.clear();
|
||||
mProjectiles.clear();
|
||||
mDoorStates.clear();
|
||||
|
||||
mGodMode = false;
|
||||
|
@ -807,6 +808,10 @@ namespace MWWorld
|
|||
|
||||
void World::changeToInteriorCell (const std::string& cellName, const ESM::Position& position)
|
||||
{
|
||||
// changed worldspace
|
||||
mProjectileManager->clear();
|
||||
mRendering->switchToInterior();
|
||||
|
||||
removeContainerScripts(getPlayerPtr());
|
||||
mWorldScene->changeToInteriorCell(cellName, position);
|
||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||
|
@ -814,6 +819,12 @@ namespace MWWorld
|
|||
|
||||
void World::changeToExteriorCell (const ESM::Position& position)
|
||||
{
|
||||
if (!getPlayerPtr().getCell()->getCell()->isExterior())
|
||||
{
|
||||
// changed worldspace
|
||||
mProjectileManager->clear();
|
||||
mRendering->switchToExterior();
|
||||
}
|
||||
removeContainerScripts(getPlayerPtr());
|
||||
mWorldScene->changeToExteriorCell(position);
|
||||
addContainerScripts(getPlayerPtr(), getPlayerPtr().getCell());
|
||||
|
@ -949,8 +960,6 @@ namespace MWWorld
|
|||
MWWorld::Ptr newPtr = MWWorld::Class::get(ptr)
|
||||
.copyToCell(ptr, *newCell);
|
||||
newPtr.getRefData().setBaseNode(0);
|
||||
|
||||
objectLeftActiveCell(ptr, newPtr);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1172,8 +1181,7 @@ namespace MWWorld
|
|||
{
|
||||
processDoors(duration);
|
||||
|
||||
moveMagicBolts(duration);
|
||||
moveProjectiles(duration);
|
||||
mProjectileManager->update(duration);
|
||||
|
||||
const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration);
|
||||
PtrVelocityList::const_iterator player(results.end());
|
||||
|
@ -2254,306 +2262,14 @@ namespace MWWorld
|
|||
void World::launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed)
|
||||
{
|
||||
ProjectileState state;
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mBow = bow;
|
||||
state.mVelocity = orient.yAxis() * speed;
|
||||
|
||||
MWWorld::ManualRef ref(getStore(), projectile.getCellRef().mRefID);
|
||||
|
||||
ESM::Position pos;
|
||||
pos.pos[0] = worldPos.x;
|
||||
pos.pos[1] = worldPos.y;
|
||||
pos.pos[2] = worldPos.z;
|
||||
|
||||
// Do NOT copy actor rotation! actors use a different rotation order, and this will not produce the same facing direction.
|
||||
Ogre::Matrix3 mat;
|
||||
orient.ToRotationMatrix(mat);
|
||||
Ogre::Radian xr,yr,zr;
|
||||
mat.ToEulerAnglesXYZ(xr, yr, zr);
|
||||
pos.rot[0] = -xr.valueRadians();
|
||||
pos.rot[1] = -yr.valueRadians();
|
||||
pos.rot[2] = -zr.valueRadians();
|
||||
|
||||
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), actor.getCell(), pos, false);
|
||||
ptr.getRefData().setCount(1);
|
||||
|
||||
mProjectiles[ptr] = state;
|
||||
mProjectileManager->launchProjectile(actor, projectile, worldPos, orient, bow, speed);
|
||||
}
|
||||
|
||||
void World::launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
void World::launchMagicBolt (const std::string& model, const std::string &sound, const std::string &spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName)
|
||||
{
|
||||
std::string projectileModel;
|
||||
std::string sound;
|
||||
float speed = 0;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
|
||||
iter!=effects.mList.end(); ++iter)
|
||||
{
|
||||
if (iter->mRange != ESM::RT_Target)
|
||||
continue;
|
||||
|
||||
const ESM::MagicEffect *magicEffect = getStore().get<ESM::MagicEffect>().find (
|
||||
iter->mEffectID);
|
||||
|
||||
projectileModel = magicEffect->mBolt;
|
||||
if (projectileModel.empty())
|
||||
projectileModel = "VFX_DefaultBolt";
|
||||
|
||||
static const std::string schools[] = {
|
||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||
};
|
||||
|
||||
if (!magicEffect->mBoltSound.empty())
|
||||
sound = magicEffect->mBoltSound;
|
||||
else
|
||||
sound = schools[magicEffect->mData.mSchool] + " bolt";
|
||||
|
||||
speed = magicEffect->mData.mSpeed;
|
||||
break;
|
||||
}
|
||||
if (projectileModel.empty())
|
||||
return;
|
||||
|
||||
// Spawn at 0.75 * ActorHeight
|
||||
float height = mPhysEngine->getCharacter(actor.getRefData().getHandle())->getHalfExtents().z * 2 * 0.75;
|
||||
|
||||
MWWorld::ManualRef ref(getStore(), projectileModel);
|
||||
ESM::Position pos;
|
||||
pos.pos[0] = actor.getRefData().getPosition().pos[0];
|
||||
pos.pos[1] = actor.getRefData().getPosition().pos[1];
|
||||
pos.pos[2] = actor.getRefData().getPosition().pos[2] + height;
|
||||
|
||||
// Do NOT copy rotation directly! actors use a different rotation order, and this will not produce the same facing direction.
|
||||
Ogre::Quaternion orient = Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
|
||||
Ogre::Quaternion(Ogre::Radian(actor.getRefData().getPosition().rot[0]), Ogre::Vector3::NEGATIVE_UNIT_X);
|
||||
Ogre::Matrix3 mat;
|
||||
orient.ToRotationMatrix(mat);
|
||||
Ogre::Radian xr,yr,zr;
|
||||
mat.ToEulerAnglesXYZ(xr, yr, zr);
|
||||
pos.rot[0] = -xr.valueRadians();
|
||||
pos.rot[1] = -yr.valueRadians();
|
||||
pos.rot[2] = -zr.valueRadians();
|
||||
|
||||
ref.getPtr().getCellRef().mPos = pos;
|
||||
CellStore* cell = actor.getCell();
|
||||
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), cell, pos);
|
||||
|
||||
MagicBoltState state;
|
||||
state.mSourceName = sourceName;
|
||||
state.mId = id;
|
||||
state.mActorId = actor.getClass().getCreatureStats(actor).getActorId();
|
||||
state.mSpeed = speed;
|
||||
state.mStack = stack;
|
||||
|
||||
// Only interested in "on target" effects
|
||||
for (std::vector<ESM::ENAMstruct>::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);
|
||||
|
||||
mMagicBolts[ptr] = state;
|
||||
}
|
||||
|
||||
void World::moveProjectiles(float duration)
|
||||
{
|
||||
std::map<std::string, ProjectileState> moved;
|
||||
for (std::map<MWWorld::Ptr, ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*it->first.getCell()))
|
||||
{
|
||||
deleteObject(it->first);
|
||||
mProjectiles.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
MWWorld::Ptr ptr = it->first;
|
||||
|
||||
// gravity constant - must be way lower than the gravity affecting actors, since we're not
|
||||
// simulating aerodynamics at all
|
||||
it->second.mVelocity -= Ogre::Vector3(0, 0, 627.2f * 0.1f) * duration;
|
||||
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 newPos = pos + it->second.mVelocity * duration;
|
||||
|
||||
Ogre::Quaternion orient = Ogre::Vector3::UNIT_Y.getRotationTo(it->second.mVelocity);
|
||||
Ogre::Matrix3 mat;
|
||||
orient.ToRotationMatrix(mat);
|
||||
Ogre::Radian xr,yr,zr;
|
||||
mat.ToEulerAnglesXYZ(xr, yr, zr);
|
||||
rotateObject(ptr, -xr.valueDegrees(), -yr.valueDegrees(), -zr.valueDegrees());
|
||||
|
||||
// Check for impact
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine->rayTest2(from, to);
|
||||
bool hit=false;
|
||||
|
||||
// HACK: query against the shape as well, since the ray does not take the volume into account
|
||||
// really, this should be a convex cast, but the whole physics system needs a rewrite
|
||||
std::vector<std::string> col2 = mPhysEngine->getCollisions(ptr.getRefData().getHandle());
|
||||
for (std::vector<std::string>::iterator ci = col2.begin(); ci != col2.end(); ++ci)
|
||||
collisions.push_back(std::make_pair(0.f,*ci));
|
||||
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !hit; ++cIt)
|
||||
{
|
||||
MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second);
|
||||
if (obstacle == ptr)
|
||||
continue;
|
||||
|
||||
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
|
||||
|
||||
// Arrow intersects with player immediately after shooting :/
|
||||
if (obstacle == caster)
|
||||
continue;
|
||||
|
||||
if (caster.isEmpty())
|
||||
caster = obstacle;
|
||||
|
||||
if (obstacle.isEmpty())
|
||||
{
|
||||
// Terrain
|
||||
}
|
||||
else if (obstacle.getClass().isActor())
|
||||
{
|
||||
MWMechanics::projectileHit(caster, obstacle, it->second.mBow, ptr, pos + (newPos - pos) * cIt->first);
|
||||
}
|
||||
hit = true;
|
||||
}
|
||||
if (hit)
|
||||
{
|
||||
deleteObject(ptr);
|
||||
mProjectiles.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string handle = ptr.getRefData().getHandle();
|
||||
|
||||
moveObject(ptr, newPos.x, newPos.y, newPos.z);
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
if (!ptr.getRefData().getCount())
|
||||
{
|
||||
moved[handle] = it->second;
|
||||
mProjectiles.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
for (std::map<std::string, ProjectileState>::iterator it = moved.begin(); it != moved.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr newPtr = searchPtrViaHandle(it->first);
|
||||
if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted
|
||||
continue;
|
||||
mProjectiles[getPtrViaHandle(it->first)] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void World::moveMagicBolts(float duration)
|
||||
{
|
||||
std::map<std::string, MagicBoltState> moved;
|
||||
for (std::map<MWWorld::Ptr, MagicBoltState>::iterator it = mMagicBolts.begin(); it != mMagicBolts.end();)
|
||||
{
|
||||
if (!mWorldScene->isCellActive(*it->first.getCell()))
|
||||
{
|
||||
deleteObject(it->first);
|
||||
mMagicBolts.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
MWWorld::Ptr ptr = it->first;
|
||||
|
||||
Ogre::Vector3 rot(ptr.getRefData().getPosition().rot);
|
||||
|
||||
Ogre::Quaternion orient = ptr.getRefData().getBaseNode()->getOrientation();
|
||||
static float fTargetSpellMaxSpeed = getStore().get<ESM::GameSetting>().find("fTargetSpellMaxSpeed")->getFloat();
|
||||
float speed = fTargetSpellMaxSpeed * it->second.mSpeed;
|
||||
|
||||
Ogre::Vector3 direction = orient.yAxis();
|
||||
direction.normalise();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
Ogre::Vector3 newPos = pos + direction * duration * speed;
|
||||
|
||||
// Check for impact
|
||||
btVector3 from(pos.x, pos.y, pos.z);
|
||||
btVector3 to(newPos.x, newPos.y, newPos.z);
|
||||
std::vector<std::pair<float, std::string> > collisions = mPhysEngine->rayTest2(from, to);
|
||||
bool explode = false;
|
||||
for (std::vector<std::pair<float, std::string> >::iterator cIt = collisions.begin(); cIt != collisions.end() && !explode; ++cIt)
|
||||
{
|
||||
MWWorld::Ptr obstacle = searchPtrViaHandle(cIt->second);
|
||||
if (obstacle == ptr)
|
||||
continue;
|
||||
|
||||
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
|
||||
if (caster.isEmpty())
|
||||
caster = obstacle;
|
||||
|
||||
if (obstacle.isEmpty())
|
||||
{
|
||||
// Terrain
|
||||
}
|
||||
else
|
||||
{
|
||||
MWMechanics::CastSpell cast(caster, obstacle);
|
||||
cast.mHitPosition = pos;
|
||||
cast.mId = it->second.mId;
|
||||
cast.mSourceName = it->second.mSourceName;
|
||||
cast.mStack = it->second.mStack;
|
||||
cast.inflict(obstacle, caster, it->second.mEffects, ESM::RT_Target, false, true);
|
||||
}
|
||||
|
||||
explode = true;
|
||||
}
|
||||
|
||||
if (explode)
|
||||
{
|
||||
MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId);
|
||||
explodeSpell(Ogre::Vector3(ptr.getRefData().getPosition().pos), ptr, it->second.mEffects, caster, it->second.mId, it->second.mSourceName);
|
||||
|
||||
deleteObject(ptr);
|
||||
mMagicBolts.erase(it++);
|
||||
continue;
|
||||
}
|
||||
|
||||
std::string handle = ptr.getRefData().getHandle();
|
||||
|
||||
moveObject(ptr, newPos.x, newPos.y, newPos.z);
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
if (!ptr.getRefData().getCount())
|
||||
{
|
||||
moved[handle] = it->second;
|
||||
mMagicBolts.erase(it++);
|
||||
}
|
||||
else
|
||||
++it;
|
||||
}
|
||||
|
||||
// HACK: Re-fetch Ptrs if necessary, since the cell might have changed
|
||||
for (std::map<std::string, MagicBoltState>::iterator it = moved.begin(); it != moved.end(); ++it)
|
||||
{
|
||||
MWWorld::Ptr newPtr = searchPtrViaHandle(it->first);
|
||||
if (newPtr.isEmpty()) // The projectile went into an inactive cell and was deleted
|
||||
continue;
|
||||
mMagicBolts[getPtrViaHandle(it->first)] = it->second;
|
||||
}
|
||||
}
|
||||
|
||||
void World::objectLeftActiveCell(Ptr object, Ptr movedPtr)
|
||||
{
|
||||
// For now, projectiles moved to an inactive cell are just deleted, because there's no reliable way to hold on to the meta information
|
||||
if (mMagicBolts.find(object) != mMagicBolts.end())
|
||||
deleteObject(movedPtr);
|
||||
if (mProjectiles.find(object) != mProjectiles.end())
|
||||
deleteObject(movedPtr);
|
||||
mProjectileManager->launchMagicBolt(model, sound, spellId, speed, stack, effects, actor, sourceName);
|
||||
}
|
||||
|
||||
const std::vector<std::string>& World::getContentFiles() const
|
||||
|
@ -2935,7 +2651,7 @@ namespace MWWorld
|
|||
mRendering->spawnEffect(model, texture, worldPosition);
|
||||
}
|
||||
|
||||
void World::explodeSpell(const Vector3 &origin, const MWWorld::Ptr& object, const ESM::EffectList &effects, const Ptr &caster,
|
||||
void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster,
|
||||
const std::string& id, const std::string& sourceName)
|
||||
{
|
||||
std::map<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;
|
||||
|
@ -2960,12 +2676,13 @@ namespace MWWorld
|
|||
static const std::string schools[] = {
|
||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||
};
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
if(!effect->mAreaSound.empty())
|
||||
sndMgr->playSound3D(object, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
|
||||
sndMgr->playManualSound3D(origin, effect->mAreaSound, 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
|
||||
else
|
||||
sndMgr->playSound3D(object, schools[effect->mData.mSchool]+" area", 1.0f, 1.0f, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_NoTrack);
|
||||
|
||||
sndMgr->playManualSound3D(origin, 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<MWWorld::Ptr> objects;
|
||||
MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "../mwrender/debugging.hpp"
|
||||
|
||||
#include <boost/shared_ptr.hpp>
|
||||
|
||||
#include "ptr.hpp"
|
||||
#include "scene.hpp"
|
||||
#include "esmstore.hpp"
|
||||
|
@ -51,6 +53,7 @@ namespace MWWorld
|
|||
{
|
||||
class WeatherManager;
|
||||
class Player;
|
||||
class ProjectileManager;
|
||||
|
||||
/// \brief The game world and its visual representation
|
||||
|
||||
|
@ -74,6 +77,8 @@ namespace MWWorld
|
|||
|
||||
OEngine::Physic::PhysicEngine* mPhysEngine;
|
||||
|
||||
boost::shared_ptr<ProjectileManager> mProjectileManager;
|
||||
|
||||
bool mGodMode;
|
||||
std::vector<std::string> mContentFiles;
|
||||
|
||||
|
@ -90,37 +95,6 @@ namespace MWWorld
|
|||
std::map<MWWorld::Ptr, int> mDoorStates;
|
||||
///< only holds doors that are currently moving. 1 = opening, 2 = closing
|
||||
|
||||
struct MagicBoltState
|
||||
{
|
||||
// Id of spell or enchantment to apply when it hits
|
||||
std::string mId;
|
||||
|
||||
// Actor who casted this projectile
|
||||
int mActorId;
|
||||
|
||||
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
|
||||
std::string mSourceName;
|
||||
|
||||
ESM::EffectList mEffects;
|
||||
|
||||
float mSpeed;
|
||||
|
||||
bool mStack;
|
||||
};
|
||||
|
||||
struct ProjectileState
|
||||
{
|
||||
// Actor who shot this projectile
|
||||
int mActorId;
|
||||
|
||||
MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from
|
||||
|
||||
Ogre::Vector3 mVelocity;
|
||||
};
|
||||
|
||||
std::map<MWWorld::Ptr, MagicBoltState> mMagicBolts;
|
||||
std::map<MWWorld::Ptr, ProjectileState> mProjectiles;
|
||||
|
||||
std::string mStartCell;
|
||||
|
||||
void updateWeather(float duration);
|
||||
|
@ -148,9 +122,6 @@ namespace MWWorld
|
|||
void processDoors(float duration);
|
||||
///< Run physics simulation and modify \a world accordingly.
|
||||
|
||||
void moveMagicBolts(float duration);
|
||||
void moveProjectiles(float duration);
|
||||
|
||||
void doPhysics(float duration);
|
||||
///< Run physics simulation and modify \a world accordingly.
|
||||
|
||||
|
@ -171,9 +142,6 @@ namespace MWWorld
|
|||
bool mLevitationEnabled;
|
||||
bool mGoToJail;
|
||||
|
||||
/// Called when \a object is moved to an inactive cell
|
||||
void objectLeftActiveCell (MWWorld::Ptr object, MWWorld::Ptr movedPtr);
|
||||
|
||||
float feetToGameUnits(float feet);
|
||||
|
||||
public:
|
||||
|
@ -572,7 +540,8 @@ namespace MWWorld
|
|||
*/
|
||||
virtual void castSpell (const MWWorld::Ptr& actor);
|
||||
|
||||
virtual void launchMagicBolt (const std::string& id, bool stack, const ESM::EffectList& effects,
|
||||
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
|
||||
float speed, bool stack, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& actor, const std::string& sourceName);
|
||||
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
|
||||
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed);
|
||||
|
@ -612,7 +581,7 @@ namespace MWWorld
|
|||
/// Spawn a blood effect for \a ptr at \a worldPosition
|
||||
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition);
|
||||
|
||||
virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects,
|
||||
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
|
||||
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName);
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue