Projectile models are now spawned (no movement or impact yet). Refactored trap activation to apply range types properly. Handle ContinuousVFX for magic effects (note they aren't stopped yet when the effect ends)

actorid
scrawl 11 years ago
parent 0627e23d3c
commit 9b0e82a37f

@ -58,7 +58,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
contentloader esmloader omwloader actiontrap
)
add_openmw_dir (mwclass

@ -415,6 +415,9 @@ namespace MWBase
virtual void castSpell (const MWWorld::Ptr& actor) = 0;
virtual void updateAnimParts(const MWWorld::Ptr& ptr) = 0;
virtual void launchProjectile (const std::string& id, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
};
}

@ -13,8 +13,8 @@
#include "../mwworld/containerstore.hpp"
#include "../mwworld/customdata.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/actionapply.hpp"
#include "../mwworld/actionopen.hpp"
#include "../mwworld/actiontrap.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/inventorystore.hpp"
@ -147,11 +147,9 @@ namespace MWClass
}
else
{
// Trap activation goes here
std::cout << "Activated trap: " << ptr.getCellRef().mTrap << std::endl;
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionApply(actor, ptr.getCellRef().mTrap));
// Activate trap
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTrap(actor, ptr.getCellRef().mTrap, ptr));
action->setSound(trapActivationSound);
ptr.getCellRef().mTrap = "";
return action;
}
}

@ -12,12 +12,12 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/failedaction.hpp"
#include "../mwworld/actionapply.hpp"
#include "../mwworld/actionteleport.hpp"
#include "../mwworld/actiondoor.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/actiontrap.hpp"
#include "../mwgui/tooltips.hpp"
@ -109,12 +109,8 @@ namespace MWClass
if(!ptr.getCellRef().mTrap.empty())
{
// Trap activation
std::cout << "Activated trap: " << ptr.getCellRef().mTrap << std::endl;
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionApply(actor, ptr.getCellRef().mTrap));
boost::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTrap(actor, ptr.getCellRef().mTrap, ptr));
action->setSound(trapActivationSound);
ptr.getCellRef().mTrap = "";
return action;
}

@ -467,9 +467,12 @@ namespace MWClass
else
{
weapon.getCellRef().mEnchantmentCharge -= castCost;
// Touch
othercls.getCreatureStats(victim).getActiveSpells().addSpell(enchantmentName, victim, ESM::RT_Touch, weapon.getClass().getName(weapon));
// Self
getCreatureStats(ptr).getActiveSpells().addSpell(enchantmentName, ptr, ESM::RT_Self, weapon.getClass().getName(weapon));
// TODO: RT_Target
// Target
MWBase::Environment::get().getWorld()->launchProjectile(enchantmentName, enchantment->mEffects, ptr, weapon.getClass().getName(weapon));
}
}
}

@ -39,6 +39,7 @@ namespace MWMechanics
if (!timeToExpire (iter))
{
mSpells.erase (iter++);
//onSpellExpired
rebuild = true;
}
else
@ -227,6 +228,8 @@ namespace MWMechanics
if (iter->mRange != range)
continue;
// TODO: For Area effects, launch a growing particle effect that applies the effect to more actors as it hits them. Best managed in World.
const ESM::MagicEffect *magicEffect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find (
iter->mEffectID);
@ -248,7 +251,8 @@ namespace MWMechanics
if (!magicEffect->mHit.empty())
{
const ESM::Static* castStatic = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>().find (magicEffect->mHit);
MWBase::Environment::get().getWorld()->getAnimation(actor)->addEffect("meshes\\" + castStatic->mModel, "");
bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx;
MWBase::Environment::get().getWorld()->getAnimation(actor)->addEffect("meshes\\" + castStatic->mModel, loop, "");
}
first = false;

@ -513,7 +513,7 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun
effect = store.get<ESM::MagicEffect>().find(effectentry.mEffectID);
const ESM::Static* castStatic = store.get<ESM::Static>().find (effect->mCasting);
mAnimation->addEffect("meshes\\" + castStatic->mModel, "");
mAnimation->addEffect("meshes\\" + castStatic->mModel);
switch(effectentry.mRange)
{

@ -16,6 +16,8 @@ namespace MWMechanics
int mId;
int mArg; // skill or ability
// TODO: Add caster here for Absorb effects?
EffectKey();
EffectKey (int id, int arg = -1) : mId (id), mArg (arg) {}

@ -87,8 +87,8 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node)
Animation::~Animation()
{
for (std::vector<NifOgre::ObjectList>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
destroyObjectList(mInsert->getCreator(), *it);
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
destroyObjectList(mInsert->getCreator(), it->mObjects);
mAnimSources.clear();
@ -929,22 +929,35 @@ Ogre::Vector3 Animation::runAnimation(float duration)
}
for (std::vector<NifOgre::ObjectList>::iterator it = mEffects.begin(); it != mEffects.end(); )
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); )
{
for(size_t i = 0; i < it->mControllers.size() ;i++)
NifOgre::ObjectList& objects = it->mObjects;
for(size_t i = 0; i < objects.mControllers.size() ;i++)
{
static_cast<EffectAnimationValue*> (it->mControllers[i].getSource().get())->addTime(duration);
static_cast<EffectAnimationValue*> (objects.mControllers[i].getSource().get())->addTime(duration);
it->mControllers[i].update();
objects.mControllers[i].update();
}
if (it->mControllers[0].getSource()->getValue() >= it->mMaxControllerLength)
if (objects.mControllers[0].getSource()->getValue() >= objects.mMaxControllerLength)
{
destroyObjectList(mInsert->getCreator(), *it);
it = mEffects.erase(it);
if (it->mLoop)
{
// Start from the beginning again; carry over the remainder
float remainder = objects.mControllers[0].getSource()->getValue() - objects.mMaxControllerLength;
for(size_t i = 0; i < objects.mControllers.size() ;i++)
{
static_cast<EffectAnimationValue*> (objects.mControllers[i].getSource().get())->resetTime(remainder);
}
}
else
{
destroyObjectList(mInsert->getCreator(), it->mObjects);
it = mEffects.erase(it);
continue;
}
}
else
++it;
++it;
}
return movement;
@ -1011,15 +1024,37 @@ void Animation::detachObjectFromBone(Ogre::MovableObject *obj)
mSkelBase->detachObjectFromBone(obj);
}
void Animation::addEffect(const std::string &model, const std::string &bonename)
void Animation::addEffect(const std::string &model, bool loop, const std::string &bonename)
{
NifOgre::ObjectList list = NifOgre::Loader::createObjects(mInsert, model);
for(size_t i = 0;i < list.mControllers.size();i++)
// Early out if we already have this effect
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
if (it->mModelName == model)
return;
EffectParams params;
params.mModelName = model;
params.mObjects = NifOgre::Loader::createObjects(mInsert, model);
params.mLoop = loop;
for(size_t i = 0;i < params.mObjects.mControllers.size();i++)
{
if(list.mControllers[i].getSource().isNull())
list.mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationValue> (new EffectAnimationValue()));
if(params.mObjects.mControllers[i].getSource().isNull())
params.mObjects.mControllers[i].setSource(Ogre::SharedPtr<EffectAnimationValue> (new EffectAnimationValue()));
}
mEffects.push_back(params);
}
void Animation::removeEffect(const std::string &model)
{
for (std::vector<EffectParams>::iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
if (it->mModelName == model)
{
destroyObjectList(mInsert->getCreator(), it->mObjects);
mEffects.erase(it);
return;
}
}
mEffects.push_back(list);
}

@ -59,6 +59,7 @@ protected:
public:
EffectAnimationValue() : mTime(0) { }
void addTime(float time) { mTime += time; }
void resetTime(float value) { mTime = value; }
virtual Ogre::Real getValue() const;
virtual void setValue(Ogre::Real value);
@ -108,7 +109,14 @@ protected:
typedef std::map<Ogre::MovableObject*,std::string> ObjectAttachMap;
std::vector<NifOgre::ObjectList> mEffects;
struct EffectParams
{
std::string mModelName; // Just here so we don't add the same effect twice
NifOgre::ObjectList mObjects;
bool mLoop;
};
std::vector<EffectParams> mEffects;
MWWorld::Ptr mPtr;
Camera *mCamera;
@ -189,7 +197,17 @@ public:
Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node);
virtual ~Animation();
void addEffect (const std::string& model, const std::string& bonename = "");
/**
* @brief Add an effect mesh attached to a bone or the insert scene node
* @param model
* @param loop Loop the effect. If false, it is removed automatically after it finishes playing. If true,
* you need to remove it manually using removeEffect when the effect should end.
* @param bonename Bone to attach to, or empty string to use the scene node instead
* @note Will not add an effect twice.
*/
void addEffect (const std::string& model, bool loop = false, const std::string& bonename = "");
void removeEffect (const std::string& model);
void updatePtr(const MWWorld::Ptr &ptr);

@ -1,4 +1,3 @@
#ifndef GAME_MWWORLD_ACTIONAPPLY_H
#define GAME_MWWORLD_ACTIONAPPLY_H

@ -0,0 +1,28 @@
#include "actiontrap.hpp"
#include "../mwworld/class.hpp"
#include "../mwmechanics/activespells.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
namespace MWWorld
{
void ActionTrap::executeImp(const Ptr &actor)
{
// TODO: Apply RT_Self effects on the door / container that triggered the trap. Not terribly useful, but you could
// make it lock itself when activated for example.
actor.getClass().getCreatureStats(actor).getActiveSpells().addSpell(mSpellId, actor, ESM::RT_Touch);
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(mSpellId);
MWBase::Environment::get().getWorld()->launchProjectile(mSpellId, spell->mEffects, mTrapSource, spell->mName);
mTrapSource.getCellRef().mTrap = "";
}
}

@ -0,0 +1,29 @@
#ifndef GAME_MWWORLD_ACTIONTRAP_H
#define GAME_MWWORLD_ACTIONTRAP_H
#include <string>
#include "action.hpp"
#include "ptr.hpp"
namespace MWWorld
{
class ActionTrap : public Action
{
std::string mSpellId;
MWWorld::Ptr mTrapSource;
virtual void executeImp (const Ptr& actor);
public:
/// @param spellId
/// @param actor Actor that activated the trap
/// @param trapSource
ActionTrap (const Ptr& actor, const std::string& spellId, const Ptr& trapSource)
: Action(false, actor), mSpellId(spellId), mTrapSource(trapSource) {}
};
}
#endif

@ -254,6 +254,7 @@ void MWWorld::InventoryStore::autoEquip (const MWWorld::Ptr& npc)
const MWMechanics::MagicEffects& MWWorld::InventoryStore::getMagicEffects()
{
// TODO: Add VFX; update when needed instead of update on request
if (!mMagicEffectsUpToDate)
{
mMagicEffects = MWMechanics::MagicEffects();

@ -1122,6 +1122,8 @@ namespace MWWorld
processDoors(duration);
moveProjectiles(duration);
const PtrVelocityList &results = mPhysics->applyQueuedMovement(duration);
PtrVelocityList::const_iterator player(results.end());
for(PtrVelocityList::const_iterator iter(results.begin());iter != results.end();iter++)
@ -2163,8 +2165,78 @@ namespace MWWorld
}
}
launchProjectile(selectedSpell, effects, actor, sourceName);
}
void World::launchProjectile (const std::string& id, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName)
{
std::string projectileModel;
std::string sound;
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;
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";
break;
}
if (projectileModel.empty())
return;
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];
pos.rot[0] = actor.getRefData().getPosition().rot[0];
pos.rot[1] = actor.getRefData().getPosition().rot[1];
pos.rot[2] = actor.getRefData().getPosition().rot[2];
ref.getPtr().getCellRef().mPos = pos;
MWWorld::Ptr ptr = copyObjectToCell(ref.getPtr(), *actor.getCell(), pos);
ProjectileState state;
state.mSourceName = sourceName;
state.mId = id;
state.mActorHandle = actor.getRefData().getHandle();
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(ptr, sound, 1.0f, 1.0f);
mProjectiles[ptr] = state;
}
void World::moveProjectiles(float duration)
{
for (std::map<MWWorld::Ptr, ProjectileState>::iterator it = mProjectiles.begin(); it != mProjectiles.end();)
{
if (!mWorldScene->isCellActive(*it->first.getCell()))
{
mProjectiles.erase(it++);
continue;
}
// TODO: Move
//moveObject(it->first, newPos.x, newPos.y, newPos.z);
++it;
}
}
void World::updateAnimParts(const Ptr& actor)
{
mRendering->updateAnimParts(actor);

@ -87,6 +87,20 @@ namespace MWWorld
std::map<MWWorld::Ptr, int> mDoorStates;
///< only holds doors that are currently moving. 0 means closing, 1 opening
struct ProjectileState
{
// Id of spell or enchantment to apply when it hits
std::string mId;
// Actor who casted this projectile
std::string mActorHandle;
// Name of item to display as effect source in magic menu (in case we casted an enchantment)
std::string mSourceName;
};
std::map<MWWorld::Ptr, ProjectileState> mProjectiles;
int getDaysPerMonth (int month) const;
void rotateObjectImp (const Ptr& ptr, Ogre::Vector3 rot, bool adjust);
@ -112,6 +126,8 @@ namespace MWWorld
void processDoors(float duration);
///< Run physics simulation and modify \a world accordingly.
void moveProjectiles(float duration);
void doPhysics(float duration);
///< Run physics simulation and modify \a world accordingly.
@ -475,6 +491,9 @@ namespace MWWorld
virtual void castSpell (const MWWorld::Ptr& actor);
virtual void updateAnimParts(const MWWorld::Ptr& ptr);
virtual void launchProjectile (const std::string& id, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName);
};
}

Loading…
Cancel
Save