From f070d9966db366d5c4364d0dc95f102cae20d3d5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 15 Jan 2014 16:30:16 +0100 Subject: [PATCH 01/17] Implement movement speed formula for creatures. Still moving a bit too slow. --- apps/openmw/mwclass/creature.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index a18df6d9f6..f2a4f9ccde 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -301,7 +301,7 @@ namespace MWClass const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); - const float normalizedEncumbrance = 0; //getEncumbrance(ptr) / getCapacity(ptr); + const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); From fe66012bcda70a3d1f61bdf726f9b6abc5133ee4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Jan 2014 06:57:50 +0100 Subject: [PATCH 02/17] Closes #1115: Fix a bug causing number of AI packages to grow exponentially when adding an AI package. Not sure if adding the same package twice should even be allowed. --- apps/openmw/mwmechanics/aisequence.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 79a953e385..73caa6ca76 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -109,7 +109,10 @@ void MWMechanics::AiSequence::stack (const AiPackage& package) for(std::list::iterator it = mPackages.begin(); it != mPackages.end(); it++) { if(mPackages.front()->getPriority() <= package.getPriority()) + { mPackages.insert(it,package.clone()); + return; + } } if(mPackages.empty()) From 2836c7c927ede12d6d993831fae3410ba07c7449 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Jan 2014 07:11:34 +0100 Subject: [PATCH 03/17] Do not set owner when adding items to a container, as opposed to NPC/creatures --- apps/openmw/mwworld/containerstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/containerstore.cpp b/apps/openmw/mwworld/containerstore.cpp index 154fa1999d..0c4226f9bf 100644 --- a/apps/openmw/mwworld/containerstore.cpp +++ b/apps/openmw/mwworld/containerstore.cpp @@ -149,7 +149,7 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& itemPtr item.getCellRef().mPos.pos[1] = 0; item.getCellRef().mPos.pos[2] = 0; - if (setOwner) + if (setOwner && actorPtr.getClass().isActor()) item.getCellRef().mOwner = actorPtr.getCellRef().mRefID; std::string script = MWWorld::Class::get(item).getScript(item); From e410eb527310b8ca13a121248e1f09e3f77a5081 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 16 Jan 2014 13:49:26 +0100 Subject: [PATCH 04/17] Play 'Idle' voiced dialogue entries in AIWander. Tweak voice max distance. --- apps/openmw/mwmechanics/aiwander.cpp | 9 +++++++++ apps/openmw/mwsound/soundmanagerimp.cpp | 3 +-- 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/aiwander.cpp b/apps/openmw/mwmechanics/aiwander.cpp index bf6c1dc38e..853d0ff7b7 100644 --- a/apps/openmw/mwmechanics/aiwander.cpp +++ b/apps/openmw/mwmechanics/aiwander.cpp @@ -6,6 +6,7 @@ #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/dialoguemanager.hpp" #include "../mwmechanics/npcstats.hpp" #include @@ -185,6 +186,14 @@ namespace MWMechanics playIdle(actor, mPlayedIdle); mChooseAction = false; mIdleNow = true; + + // Play idle voiced dialogue entries randomly + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + float chance = store.get().find("fVoiceIdleOdds")->getFloat(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + // TODO: do not show subtitle messagebox if player is too far away? or do not say at all? + if (roll < chance) + MWBase::Environment::get().getDialogueManager()->say(actor, "idle"); } } diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index a7ee968314..bdd03a8b46 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -248,14 +248,13 @@ namespace MWSound return; try { - // The range values are not tested float basevol = volumeFromType(Play_TypeVoice); std::string filePath = "Sound/"+filename; const ESM::Position &pos = ptr.getRefData().getPosition(); const Ogre::Vector3 objpos(pos.pos); MWBase::SoundPtr sound = mOutput->playSound3D(filePath, objpos, 1.0f, basevol, 1.0f, - 20.0f, 12750.0f, Play_Normal|Play_TypeVoice, 0); + 20.0f, 1500.0f, Play_Normal|Play_TypeVoice, 0); mActiveSounds[sound] = std::make_pair(ptr, std::string("_say_sound")); } catch(std::exception &e) From ddc432c7ef8c49c6d380ce7b229ab27c098f1c53 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 06:18:37 +0100 Subject: [PATCH 05/17] Fix stealing bug --- apps/openmw/mwclass/npc.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 8d51fd1cf9..57de523386 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -775,10 +775,10 @@ namespace MWClass } if(getCreatureStats(ptr).isDead()) return boost::shared_ptr(new MWWorld::ActionOpen(ptr, true)); - if(getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak)) - return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing if(get(ptr).getCreatureStats(ptr).isHostile()) return boost::shared_ptr(new MWWorld::FailedAction("#{sActorInCombat}")); + if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak)) + return boost::shared_ptr(new MWWorld::ActionOpen(ptr)); // stealing return boost::shared_ptr(new MWWorld::ActionTalk(ptr)); } From 11394d83c5006efe11c1a43484f532dec25938ee Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 08:54:24 +0100 Subject: [PATCH 06/17] Feature #1086: Import blood models/textures in MWIniImporter --- apps/mwiniimporter/importer.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/apps/mwiniimporter/importer.cpp b/apps/mwiniimporter/importer.cpp index cf15891144..648ab3ebee 100644 --- a/apps/mwiniimporter/importer.cpp +++ b/apps/mwiniimporter/importer.cpp @@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter() "Moons:Masser Fade Out Finish", "Moons:Script Color", + // blood + "Blood:Model 0", + "Blood:Model 1", + "Blood:Model 2", + "Blood:Texture 0", + "Blood:Texture 1", + "Blood:Texture 2", + "Blood:Texture Name 0", + "Blood:Texture Name 1", + "Blood:Texture Name 2", + 0 }; From 240d96a0f139d77a74e54635586600b20be2356a Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 09:41:23 +0100 Subject: [PATCH 07/17] Renamed AnimationValue to AnimationTime --- apps/openmw/mwrender/animation.cpp | 34 +++++++++++++-------------- apps/openmw/mwrender/animation.hpp | 14 +++++------ apps/openmw/mwrender/npcanimation.cpp | 9 +++---- apps/openmw/mwrender/npcanimation.hpp | 6 ++--- 4 files changed, 32 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index d4a3533a19..f0650fd82f 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -30,7 +30,7 @@ namespace MWRender { -Ogre::Real Animation::AnimationValue::getValue() const +Ogre::Real Animation::AnimationTime::getValue() const { AnimStateMap::const_iterator iter = mAnimation->mStates.find(mAnimationName); if(iter != mAnimation->mStates.end()) @@ -38,16 +38,16 @@ Ogre::Real Animation::AnimationValue::getValue() const return 0.0f; } -void Animation::AnimationValue::setValue(Ogre::Real) +void Animation::AnimationTime::setValue(Ogre::Real) { } -Ogre::Real Animation::EffectAnimationValue::getValue() const +Ogre::Real Animation::EffectAnimationTime::getValue() const { return mTime; } -void Animation::EffectAnimationValue::setValue(Ogre::Real) +void Animation::EffectAnimationTime::setValue(Ogre::Real) { } @@ -60,10 +60,10 @@ Animation::Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node) , mNonAccumRoot(NULL) , mNonAccumCtrl(NULL) , mAccumulate(0.0f) - , mNullAnimationValuePtr(OGRE_NEW NullAnimationValue) + , mNullAnimationTimePtr(OGRE_NEW NullAnimationTime) { for(size_t i = 0;i < sNumGroups;i++) - mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); + mAnimationTimePtr[i].bind(OGRE_NEW AnimationTime(this)); } Animation::~Animation() @@ -139,7 +139,7 @@ void Animation::setObjectRoot(const std::string &model, bool baseonly) for(size_t i = 0;i < mObjectRoot->mControllers.size();i++) { if(mObjectRoot->mControllers[i].getSource().isNull()) - mObjectRoot->mControllers[i].setSource(mAnimationValuePtr[0]); + mObjectRoot->mControllers[i].setSource(mAnimationTimePtr[0]); } } @@ -286,7 +286,7 @@ void Animation::addAnimSource(const std::string &model) } } - ctrls[i].setSource(mAnimationValuePtr[grp]); + ctrls[i].setSource(mAnimationTimePtr[grp]); grpctrls[grp].push_back(ctrls[i]); } } @@ -296,7 +296,7 @@ void Animation::clearAnimSources() mStates.clear(); for(size_t i = 0;i < sNumGroups;i++) - mAnimationValuePtr[i]->setAnimName(std::string()); + mAnimationTimePtr[i]->setAnimName(std::string()); mNonAccumCtrl = NULL; @@ -789,7 +789,7 @@ void Animation::resetActiveGroups() active = state; } - mAnimationValuePtr[grp]->setAnimName((active == mStates.end()) ? + mAnimationTimePtr[grp]->setAnimName((active == mStates.end()) ? std::string() : active->first); } mNonAccumCtrl = NULL; @@ -797,7 +797,7 @@ void Animation::resetActiveGroups() if(!mNonAccumRoot || mAccumulate == Ogre::Vector3(0.0f)) return; - AnimStateMap::const_iterator state = mStates.find(mAnimationValuePtr[0]->getAnimName()); + AnimStateMap::const_iterator state = mStates.find(mAnimationTimePtr[0]->getAnimName()); if(state == mStates.end()) return; @@ -869,13 +869,13 @@ Ogre::Vector3 Animation::runAnimation(float duration) targetTime = state.mTime + timepassed; if(textkey == textkeys.end() || textkey->first > targetTime) { - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) updatePosition(state.mTime, targetTime, movement); state.mTime = std::min(targetTime, state.mStopTime); } else { - if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) + if(mNonAccumCtrl && stateiter->first == mAnimationTimePtr[0]->getAnimName()) updatePosition(state.mTime, textkey->first, movement); state.mTime = textkey->first; } @@ -926,7 +926,7 @@ Ogre::Vector3 Animation::runAnimation(float duration) // Apply group controllers for(size_t grp = 0;grp < sNumGroups;grp++) { - const std::string &name = mAnimationValuePtr[grp]->getAnimName(); + const std::string &name = mAnimationTimePtr[grp]->getAnimName(); if(!name.empty() && (stateiter=mStates.find(name)) != mStates.end()) { const Ogre::SharedPtr &src = stateiter->second.mSource; @@ -1052,7 +1052,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con for(size_t i = 0;i < params.mObjects->mControllers.size();i++) { if(params.mObjects->mControllers[i].getSource().isNull()) - params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationValue())); + params.mObjects->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); } if (!texture.empty()) @@ -1110,7 +1110,7 @@ void Animation::updateEffects(float duration) NifOgre::ObjectScenePtr objects = it->mObjects; for(size_t i = 0; i < objects->mControllers.size() ;i++) { - EffectAnimationValue* value = dynamic_cast(objects->mControllers[i].getSource().get()); + EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); if (value) value->addTime(duration); @@ -1125,7 +1125,7 @@ void Animation::updateEffects(float duration) float remainder = objects->mControllers[0].getSource()->getValue() - objects->mMaxControllerLength; for(size_t i = 0; i < objects->mControllers.size() ;i++) { - EffectAnimationValue* value = dynamic_cast(objects->mControllers[i].getSource().get()); + EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); if (value) value->resetTime(remainder); } diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 1400cb9a28..22f2e5df94 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -32,14 +32,14 @@ protected: /* This is the number of *discrete* groups. */ static const size_t sNumGroups = 4; - class AnimationValue : public Ogre::ControllerValue + class AnimationTime : public Ogre::ControllerValue { private: Animation *mAnimation; std::string mAnimationName; public: - AnimationValue(Animation *anim) + AnimationTime(Animation *anim) : mAnimation(anim) { } @@ -52,12 +52,12 @@ protected: virtual void setValue(Ogre::Real value); }; - class EffectAnimationValue : public Ogre::ControllerValue + class EffectAnimationTime : public Ogre::ControllerValue { private: float mTime; public: - EffectAnimationValue() : mTime(0) { } + EffectAnimationTime() : mTime(0) { } void addTime(float time) { mTime += time; } void resetTime(float value) { mTime = value; } @@ -67,7 +67,7 @@ protected: - class NullAnimationValue : public Ogre::ControllerValue + class NullAnimationTime : public Ogre::ControllerValue { public: virtual Ogre::Real getValue() const @@ -134,8 +134,8 @@ protected: AnimStateMap mStates; - Ogre::SharedPtr mAnimationValuePtr[sNumGroups]; - Ogre::SharedPtr mNullAnimationValuePtr; + Ogre::SharedPtr mAnimationTimePtr[sNumGroups]; + Ogre::SharedPtr mNullAnimationTimePtr; ObjectAttachMap mAttachedObjects; diff --git a/apps/openmw/mwrender/npcanimation.cpp b/apps/openmw/mwrender/npcanimation.cpp index 497279bd8f..bcb6a374cb 100644 --- a/apps/openmw/mwrender/npcanimation.cpp +++ b/apps/openmw/mwrender/npcanimation.cpp @@ -61,8 +61,9 @@ std::string getVampireHead(const std::string& race, bool female) namespace MWRender { -float SayAnimationValue::getValue() const +float HeadAnimationTime::getValue() const { + // TODO: Handle eye blinking (time is in the text keys) if (MWBase::Environment::get().getSoundManager()->sayDone(mReference)) return 0; else @@ -124,7 +125,7 @@ NpcAnimation::NpcAnimation(const MWWorld::Ptr& ptr, Ogre::SceneNode* node, int v { mNpc = mPtr.get()->mBase; - mSayAnimationValue = Ogre::SharedPtr(new SayAnimationValue(mPtr)); + mHeadAnimationTime = Ogre::SharedPtr(new HeadAnimationTime(mPtr)); for(size_t i = 0;i < ESM::PRT_Count;i++) { @@ -595,10 +596,10 @@ bool NpcAnimation::addOrReplaceIndividualPart(ESM::PartReferenceType type, int g { if(ctrl->getSource().isNull()) { - ctrl->setSource(mNullAnimationValuePtr); + ctrl->setSource(mNullAnimationTimePtr); if (type == ESM::PRT_Head) - ctrl->setSource(mSayAnimationValue); + ctrl->setSource(mHeadAnimationTime); } } diff --git a/apps/openmw/mwrender/npcanimation.hpp b/apps/openmw/mwrender/npcanimation.hpp index 6f0403b9d4..e86ec7d4e1 100644 --- a/apps/openmw/mwrender/npcanimation.hpp +++ b/apps/openmw/mwrender/npcanimation.hpp @@ -13,12 +13,12 @@ namespace ESM namespace MWRender { -class SayAnimationValue : public Ogre::ControllerValue +class HeadAnimationTime : public Ogre::ControllerValue { private: MWWorld::Ptr mReference; public: - SayAnimationValue(MWWorld::Ptr reference) : mReference(reference) {} + HeadAnimationTime(MWWorld::Ptr reference) : mReference(reference) {} virtual Ogre::Real getValue() const; virtual void setValue(Ogre::Real value) @@ -70,7 +70,7 @@ private: Ogre::Vector3 mFirstPersonOffset; - Ogre::SharedPtr mSayAnimationValue; + Ogre::SharedPtr mHeadAnimationTime; float mAlpha; From 805843d7ff8cb15dbe1fb039037e6ae79d34e3d9 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 10:52:44 +0100 Subject: [PATCH 08/17] Closes #1086: Implement blood effects --- apps/esmtool/labels.cpp | 4 +- apps/opencs/model/world/refidcollection.cpp | 2 +- apps/openmw/CMakeLists.txt | 2 +- apps/openmw/mwbase/world.hpp | 3 + apps/openmw/mwclass/creature.cpp | 11 ++ apps/openmw/mwclass/creature.hpp | 3 + apps/openmw/mwclass/npc.cpp | 17 ++- apps/openmw/mwclass/npc.hpp | 3 + apps/openmw/mwrender/animation.cpp | 1 + apps/openmw/mwrender/animation.hpp | 10 +- apps/openmw/mwrender/creatureanimation.cpp | 2 +- apps/openmw/mwrender/effectmanager.cpp | 117 ++++++++++++++++++++ apps/openmw/mwrender/effectmanager.hpp | 31 ++++++ apps/openmw/mwrender/renderingmanager.cpp | 19 +++- apps/openmw/mwrender/renderingmanager.hpp | 8 +- apps/openmw/mwworld/class.cpp | 4 + apps/openmw/mwworld/class.hpp | 3 + apps/openmw/mwworld/worldimp.cpp | 27 +++++ apps/openmw/mwworld/worldimp.hpp | 3 + components/esm/loadcrea.hpp | 16 ++- 20 files changed, 263 insertions(+), 23 deletions(-) create mode 100644 apps/openmw/mwrender/effectmanager.cpp create mode 100644 apps/openmw/mwrender/effectmanager.hpp diff --git a/apps/esmtool/labels.cpp b/apps/esmtool/labels.cpp index d270a9534c..7a42e6900f 100644 --- a/apps/esmtool/labels.cpp +++ b/apps/esmtool/labels.cpp @@ -680,7 +680,7 @@ std::string creatureFlags(int flags) if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Flies) properties += "Flies "; - if (flags & ESM::Creature::Biped) properties += "Biped "; + if (flags & ESM::Creature::Bipedal) properties += "Bipedal "; if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Weapon) properties += "Weapon "; if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; @@ -691,7 +691,7 @@ std::string creatureFlags(int flags) ESM::Creature::Walks| ESM::Creature::Swims| ESM::Creature::Flies| - ESM::Creature::Biped| + ESM::Creature::Bipedal| ESM::Creature::Respawn| ESM::Creature::Weapon| ESM::Creature::Skeleton| diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 9ed5268183..176a19f2f0 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -181,7 +181,7 @@ CSMWorld::RefIdCollection::RefIdCollection() unsigned int mFlag; } sCreatureFlagTable[] = { - { Columns::ColumnId_Biped, ESM::Creature::Biped }, + { Columns::ColumnId_Biped, ESM::Creature::Bipedal }, { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, { Columns::ColumnId_NoMovement, ESM::Creature::None }, { Columns::ColumnId_Swims, ESM::Creature::Swims }, diff --git a/apps/openmw/CMakeLists.txt b/apps/openmw/CMakeLists.txt index 4da5e29970..3b533b4168 100644 --- a/apps/openmw/CMakeLists.txt +++ b/apps/openmw/CMakeLists.txt @@ -20,7 +20,7 @@ add_openmw_dir (mwrender renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation actors objects renderinginterface localmap occlusionquery water shadows characterpreview globalmap videoplayer ripplesimulation refraction - terrainstorage renderconst + terrainstorage renderconst effectmanager ) add_openmw_dir (mwinput diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 83fcba87cc..3ad716b72f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -463,6 +463,9 @@ namespace MWBase /// Spawn a random creature from a levelled list next to the player virtual void spawnRandomCreature(const std::string& creatureList) = 0; + + /// Spawn a blood effect for \a ptr at \a worldPosition + virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index f2a4f9ccde..0fc26950d5 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -530,6 +530,17 @@ namespace MWClass } } + int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::Creature::Skeleton) + return 1; + if (ref->mBase->mFlags & ESM::Creature::Metal) + return 2; + return 0; + } + const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting *Creature::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 7d2f95fae0..708999ef04 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -111,6 +111,9 @@ namespace MWClass virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; + + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) + virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; }; } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 57de523386..93a7ddcf6e 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -455,7 +455,9 @@ namespace MWClass weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); // TODO: Use second to work out the hit angle and where to spawn the blood effect - MWWorld::Ptr victim = world->getHitContact(ptr, dist).first; + std::pair result = world->getHitContact(ptr, dist); + MWWorld::Ptr victim = result.first; + Ogre::Vector3 hitPosition = result.second; if(victim.isEmpty()) // Didn't hit anything return; @@ -602,6 +604,8 @@ namespace MWClass } } + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } @@ -1216,6 +1220,17 @@ namespace MWClass return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified(); } + int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const + { + MWWorld::LiveCellRef *ref = ptr.get(); + + if (ref->mBase->mFlags & ESM::NPC::Skeleton) + return 1; + if (ref->mBase->mFlags & ESM::NPC::Metal) + return 2; + return 0; + } + const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fEncumberedMoveEffect; diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index b729d01514..497d0ced85 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -142,6 +142,9 @@ namespace MWClass virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) + virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual bool isActor() const { return true; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index f0650fd82f..de89b0207d 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -1042,6 +1042,7 @@ void Animation::addEffect(const std::string &model, int effectId, bool loop, con else params.mObjects = NifOgre::Loader::createObjects(mSkelBase, bonename, mInsert, model); + // TODO: turn off shadow casting setRenderProperties(params.mObjects, RV_Misc, RQG_Main, RQG_Alpha, 0.f, false, NULL); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 22f2e5df94..da1c1628cd 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -189,16 +189,18 @@ protected: /** Adds an additional light to the given object list using the specified ESM record. */ void addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScenePtr objlist, const ESM::Light *light); - static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, - Ogre::uint8 transqueue, Ogre::Real dist=0.0f, - bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); - void clearAnimSources(); // TODO: Should not be here Ogre::Vector3 getEnchantmentColor(MWWorld::Ptr item); public: + // FIXME: Move outside of this class + static void setRenderProperties(NifOgre::ObjectScenePtr objlist, Ogre::uint32 visflags, Ogre::uint8 solidqueue, + Ogre::uint8 transqueue, Ogre::Real dist=0.0f, + bool enchantedGlow=false, Ogre::Vector3* glowColor=NULL); + + Animation(const MWWorld::Ptr &ptr, Ogre::SceneNode *node); virtual ~Animation(); diff --git a/apps/openmw/mwrender/creatureanimation.cpp b/apps/openmw/mwrender/creatureanimation.cpp index c3ad512ddd..20e5ff8efc 100644 --- a/apps/openmw/mwrender/creatureanimation.cpp +++ b/apps/openmw/mwrender/creatureanimation.cpp @@ -24,7 +24,7 @@ CreatureAnimation::CreatureAnimation(const MWWorld::Ptr &ptr) setObjectRoot(model, false); setRenderProperties(mObjectRoot, RV_Actors, RQG_Main, RQG_Alpha); - if((ref->mBase->mFlags&ESM::Creature::Biped)) + if((ref->mBase->mFlags&ESM::Creature::Bipedal)) addAnimSource("meshes\\base_anim.nif"); addAnimSource(model); } diff --git a/apps/openmw/mwrender/effectmanager.cpp b/apps/openmw/mwrender/effectmanager.cpp new file mode 100644 index 0000000000..eb4525a4fc --- /dev/null +++ b/apps/openmw/mwrender/effectmanager.cpp @@ -0,0 +1,117 @@ +#include "effectmanager.hpp" + +#include +#include + +#include "animation.hpp" +#include "renderconst.hpp" + +namespace MWRender +{ + +class EffectAnimationTime : public Ogre::ControllerValue +{ +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) +{ +} + +void EffectManager::addEffect(const std::string &model, std::string textureOverride, const Ogre::Vector3 &worldPosition) +{ + Ogre::SceneNode* sceneNode = mSceneMgr->getRootSceneNode()->createChildSceneNode(worldPosition); + + // fix texture extension to .dds + if (textureOverride.size() > 4) + { + textureOverride[textureOverride.size()-3] = 'd'; + textureOverride[textureOverride.size()-2] = 'd'; + textureOverride[textureOverride.size()-1] = 's'; + } + + + NifOgre::ObjectScenePtr scene = NifOgre::Loader::createObjects(sceneNode, model); + + // TODO: turn off shadow casting + MWRender::Animation::setRenderProperties(scene, RV_Misc, + RQG_Main, RQG_Alpha, 0.f, false, NULL); + + for(size_t i = 0;i < scene->mControllers.size();i++) + { + if(scene->mControllers[i].getSource().isNull()) + scene->mControllers[i].setSource(Ogre::SharedPtr (new EffectAnimationTime())); + } + + if (!textureOverride.empty()) + { + for(size_t i = 0;i < scene->mParticles.size(); ++i) + { + Ogre::ParticleSystem* partSys = scene->mParticles[i]; + + Ogre::MaterialPtr mat = scene->mMaterialControllerMgr.getWritableMaterial(partSys); + + for (int t=0; tgetNumTechniques(); ++t) + { + Ogre::Technique* tech = mat->getTechnique(t); + for (int p=0; pgetNumPasses(); ++p) + { + Ogre::Pass* pass = tech->getPass(p); + for (int tex=0; texgetNumTextureUnitStates(); ++tex) + { + Ogre::TextureUnitState* tus = pass->getTextureUnitState(tex); + tus->setTextureName("textures\\" + textureOverride); + } + } + } + } + } + + mEffects.push_back(std::make_pair(sceneNode, scene)); +} + +void EffectManager::update(float dt) +{ + for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) + { + NifOgre::ObjectScenePtr objects = it->second; + for(size_t i = 0; i < objects->mControllers.size() ;i++) + { + EffectAnimationTime* value = dynamic_cast(objects->mControllers[i].getSource().get()); + if (value) + value->addTime(dt); + + objects->mControllers[i].update(); + } + + // Finished playing? + if (objects->mControllers[0].getSource()->getValue() >= objects->mMaxControllerLength) + { + Ogre::SceneNode* node = it->first; + it = mEffects.erase(it); + mSceneMgr->destroySceneNode(node); + continue; + } + ++it; + } +} + +void EffectManager::clear() +{ + for (std::vector >::iterator it = mEffects.begin(); it != mEffects.end(); ) + { + Ogre::SceneNode* node = it->first; + it = mEffects.erase(it); + mSceneMgr->destroySceneNode(node); + } +} + +} diff --git a/apps/openmw/mwrender/effectmanager.hpp b/apps/openmw/mwrender/effectmanager.hpp new file mode 100644 index 0000000000..0c8bc3857c --- /dev/null +++ b/apps/openmw/mwrender/effectmanager.hpp @@ -0,0 +1,31 @@ +#ifndef OPENMW_MWRENDER_EFFECTMANAGER_H +#define OPENMW_MWRENDER_EFFECTMANAGER_H + +#include + +namespace MWRender +{ + // 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 + { + public: + EffectManager(Ogre::SceneManager* sceneMgr); + ~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 update(float dt); + + /// Remove all effects + void clear(); + + private: + std::vector > mEffects; + Ogre::SceneManager* mSceneMgr; + }; + +} + +#endif diff --git a/apps/openmw/mwrender/renderingmanager.cpp b/apps/openmw/mwrender/renderingmanager.cpp index 47cf3ee41b..8a2ab1c7a1 100644 --- a/apps/openmw/mwrender/renderingmanager.cpp +++ b/apps/openmw/mwrender/renderingmanager.cpp @@ -41,6 +41,7 @@ #include "globalmap.hpp" #include "videoplayer.hpp" #include "terrainstorage.hpp" +#include "effectmanager.hpp" using namespace MWRender; using namespace Ogre; @@ -57,9 +58,11 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b , mSunEnabled(0) , mPhysicsEngine(engine) , mTerrain(NULL) + , mEffectManager(NULL) { mActors = new MWRender::Actors(mRendering, this); mObjects = new MWRender::Objects(mRendering); + mEffectManager = new EffectManager(mRendering.getScene()); // select best shader mode bool openGL = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL") != std::string::npos); bool glES = (Ogre::Root::getSingleton ().getRenderSystem ()->getName().find("OpenGL ES") != std::string::npos); @@ -193,6 +196,7 @@ RenderingManager::~RenderingManager () delete mVideoPlayer; delete mActors; delete mObjects; + delete mEffectManager; delete mFactory; } @@ -374,6 +378,8 @@ void RenderingManager::update (float duration, bool paused) if(paused) return; + mEffectManager->update(duration); + mActors->update (mRendering.getCamera()); mPlayerAnimation->preRender(mRendering.getCamera()); mObjects->update (duration, mRendering.getCamera()); @@ -675,14 +681,14 @@ Shadows* RenderingManager::getShadows() void RenderingManager::switchToInterior() { - // causes light flicker in opengl when moving.. - //mRendering.getScene()->setCameraRelativeRendering(false); + // TODO: also do this when switching worldspace + mEffectManager->clear(); } void RenderingManager::switchToExterior() { - // causes light flicker in opengl when moving.. - //mRendering.getScene()->setCameraRelativeRendering(true); + // TODO: also do this when switching worldspace + mEffectManager->clear(); } Ogre::Vector4 RenderingManager::boundingBoxToScreen(Ogre::AxisAlignedBox bounds) @@ -1020,4 +1026,9 @@ float RenderingManager::getCameraDistance() const return mCamera->getCameraDistance(); } +void RenderingManager::spawnEffect(const std::string &model, const std::string &texture, const Vector3 &worldPosition) +{ + mEffectManager->addEffect(model, texture, worldPosition); +} + } // namespace diff --git a/apps/openmw/mwrender/renderingmanager.hpp b/apps/openmw/mwrender/renderingmanager.hpp index 9f77f0a3c9..b6379bee44 100644 --- a/apps/openmw/mwrender/renderingmanager.hpp +++ b/apps/openmw/mwrender/renderingmanager.hpp @@ -21,10 +21,7 @@ namespace Ogre { - class SceneManager; class SceneNode; - class Quaternion; - class Vector3; } namespace MWWorld @@ -51,6 +48,7 @@ namespace MWRender class GlobalMap; class VideoPlayer; class Animation; + class EffectManager; class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener { @@ -209,6 +207,8 @@ public: void stopVideo(); void frameStarted(float dt, bool paused); + void spawnEffect (const std::string& model, const std::string& texture, const Ogre::Vector3& worldPosition); + protected: virtual void windowResized(int x, int y); @@ -239,6 +239,8 @@ private: MWRender::Objects* mObjects; MWRender::Actors* mActors; + MWRender::EffectManager* mEffectManager; + MWRender::NpcAnimation *mPlayerAnimation; // 0 normal, 1 more bright, 2 max diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f3128780b8..6c00b949cf 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -363,4 +363,8 @@ namespace MWWorld throw std::runtime_error("class does not support skills"); } + int Class::getBloodTexture (const MWWorld::Ptr& ptr) const + { + throw std::runtime_error("class does not support gore"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index bbc74323cb..ec22d03066 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -278,6 +278,9 @@ namespace MWWorld virtual bool isKey (const MWWorld::Ptr& ptr) const { return false; } + /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) + virtual int getBloodTexture (const MWWorld::Ptr& ptr) const; + virtual Ptr copyToCell(const Ptr &ptr, CellStore &cell) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index ebc1044bc4..a40f20696d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2610,4 +2610,31 @@ namespace MWWorld safePlaceObject(ref.getPtr(),*cell,ipos); } } + + void World::spawnBloodEffect(const Ptr &ptr, const Vector3 &worldPosition) + { + int type = ptr.getClass().getBloodTexture(ptr); + std::string texture; + switch (type) + { + case 2: + texture = getFallback()->getFallbackString("Blood_Texture_2"); + break; + case 1: + texture = getFallback()->getFallbackString("Blood_Texture_1"); + break; + case 0: + default: + texture = getFallback()->getFallbackString("Blood_Texture_0"); + break; + } + + std::stringstream modelName; + modelName << "Blood_Model_"; + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 3; // [0, 2] + modelName << roll; + std::string model = "meshes\\" + getFallback()->getFallbackString(modelName.str()); + + mRendering->spawnEffect(model, texture, worldPosition); + } } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 92975400a4..38766e74f1 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -549,6 +549,9 @@ namespace MWWorld /// Spawn a random creature from a levelled list next to the player virtual void spawnRandomCreature(const std::string& creatureList); + + /// Spawn a blood effect for \a ptr at \a worldPosition + virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition); }; } diff --git a/components/esm/loadcrea.hpp b/components/esm/loadcrea.hpp index c0523025bd..817c0e43c5 100644 --- a/components/esm/loadcrea.hpp +++ b/components/esm/loadcrea.hpp @@ -25,16 +25,20 @@ struct Creature // Default is 0x48? enum Flags { - Biped = 0x001, - Respawn = 0x002, - Weapon = 0x004, // Has weapon and shield - None = 0x008, // ?? + // Movement types + Bipedal = 0x001, Swims = 0x010, Flies = 0x020, // Don't know what happens if several Walks = 0x040, // of these are set + + Respawn = 0x002, + Weapon = 0x004, // Has weapon and shield + None = 0x008, // ?? Essential = 0x080, - Skeleton = 0x400, // Does not have normal blood - Metal = 0x800 // Has 'golden' blood + + // Blood types + Skeleton = 0x400, + Metal = 0x800 }; enum Type From c548dcee13e6262a32148a4892178d9429456dc8 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 13:13:58 +0100 Subject: [PATCH 09/17] Quick keys menu: make sure selected spell still exists --- apps/openmw/mwgui/quickkeysmenu.cpp | 6 ++++++ apps/openmw/mwmechanics/character.cpp | 6 ++++-- apps/openmw/mwmechanics/spells.hpp | 2 ++ 3 files changed, 12 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwgui/quickkeysmenu.cpp b/apps/openmw/mwgui/quickkeysmenu.cpp index 61e414fc4e..e1d430307d 100644 --- a/apps/openmw/mwgui/quickkeysmenu.cpp +++ b/apps/openmw/mwgui/quickkeysmenu.cpp @@ -301,6 +301,12 @@ namespace MWGui if (type == Type_Magic) { std::string spellId = button->getChildAt(0)->getUserString("Spell"); + + // Make sure the player still has this spell + MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); + MWMechanics::Spells& spells = stats.getSpells(); + if (!spells.hasSpell(spellId)) + return; store.setSelectedEnchantItem(store.end()); MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player))); } diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 5104a0f57f..755dbc0932 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -583,8 +583,10 @@ bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrun // This has to be done at the start of the casting animation, // *not* when selecting a spell in the GUI (otherwise you could change the spell mid-animation) if (mPtr.getRefData().getHandle() == "player") - stats.getSpells().setSelectedSpell(MWBase::Environment::get().getWindowManager()->getSelectedSpell()); - + { + std::string selectedSpell = MWBase::Environment::get().getWindowManager()->getSelectedSpell(); + stats.getSpells().setSelectedSpell(selectedSpell); + } std::string spellid = stats.getSpells().getSelectedSpell(); if(!spellid.empty() && MWBase::Environment::get().getWorld()->startSpellCast(mPtr)) diff --git a/apps/openmw/mwmechanics/spells.hpp b/apps/openmw/mwmechanics/spells.hpp index cf9b660915..facf02da84 100644 --- a/apps/openmw/mwmechanics/spells.hpp +++ b/apps/openmw/mwmechanics/spells.hpp @@ -44,6 +44,8 @@ namespace MWMechanics TIterator end() const; + bool hasSpell(const std::string& spell) { return mSpells.find(Misc::StringUtils::lowerCase(spell)) != mSpells.end(); } + void add (const std::string& spell); ///< Adding a spell that is already listed in *this is a no-op. From 659d790048adf5643d15419fd8a250d7a5663f64 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 13:14:50 +0100 Subject: [PATCH 10/17] uuid is not used --- CMakeLists.txt | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b365e162f5..fd5de4c394 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -186,8 +186,6 @@ if (WIN32) add_definitions(-DSDL_MAIN_HANDLED) else (WIN32) set(PLATFORM_INCLUDE_DIR "") - find_path (UUID_INCLUDE_DIR uuid/uuid.h) - include_directories(${UUID_INCLUDE_DIR}) endif (WIN32) if (MSVC10) set(PLATFORM_INCLUDE_DIR "") @@ -239,7 +237,6 @@ include_directories("." ${MYGUI_INCLUDE_DIRS} ${MYGUI_PLATFORM_INCLUDE_DIRS} ${OPENAL_INCLUDE_DIR} - ${UUID_INCLUDE_DIR} ${LIBDIR} ) @@ -434,7 +431,7 @@ IF(NOT WIN32 AND NOT APPLE) SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)") + SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libqtgui4 (>= 4.7.0)") SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") From 5fc38e7ac480d2b9b922391126d31f17adecee44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 13:20:41 +0100 Subject: [PATCH 11/17] Don't use blood effects for fatigue damage --- apps/openmw/mwclass/npc.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 93a7ddcf6e..cfd981088d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -604,7 +604,8 @@ namespace MWClass } } - MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + if (healthdmg) + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); } From 37a59a37c6d2c6c227b7b9fd029d0d1a618d8028 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 14:11:46 +0100 Subject: [PATCH 12/17] Remove cpack (no longer used, according to BrotherBrick) --- CMakeLists.txt | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd5de4c394..8eb8b44c28 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -406,46 +406,6 @@ IF(NOT WIN32 AND NOT APPLE) # Install resources INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") - - IF (DPKG_PROGRAM) - ## Debian Specific - IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git") - EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION ) - STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}") - EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME ) - EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL) - SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>") - ELSE() - SET(VERSION_STRING "${OPENMW_VERSION}") - SET(PACKAGE_MAINTAINER "unknown") - ENDIF() - - SET(CPACK_GENERATOR "DEB") - SET(CPACK_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org") - SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional") - SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}") - SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind - OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind. - Data files from the original game is required to run it.") - SET(CPACK_DEBIAN_PACKAGE_NAME "openmw") - SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}") - SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter") - SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libqtgui4 (>= 4.7.0)") - - SET(CPACK_DEBIAN_PACKAGE_SECTION "Games") - - STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE) - EXECUTE_PROCESS( - COMMAND ${DPKG_PROGRAM} --print-architecture - OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE - OUTPUT_STRIP_TRAILING_WHITESPACE - ) - SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}") - - - INCLUDE(CPack) - ENDIF(DPKG_PROGRAM) ENDIF(NOT WIN32 AND NOT APPLE) if(WIN32) From 27d0d9c592a22448c64a129824f9bbcc268ee4b4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 15:27:59 +0100 Subject: [PATCH 13/17] Fix exception when clicking on statics when in the inventory window --- apps/openmw/mwgui/hud.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index 7cd9e22569..a6ad43ce58 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -267,7 +267,8 @@ namespace MWGui else if ((mode == GM_Container) || (mode == GM_Inventory)) { // pick up object - MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); + if (!object.isEmpty()) + MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); } } } From c76a0448a3f6e9ad9954cc879b6164aba2f54e8f Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 15:47:34 +0100 Subject: [PATCH 14/17] Closes #1123: Implement SlowFall magic effect --- apps/openmw/mwworld/physicssystem.cpp | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwworld/physicssystem.cpp b/apps/openmw/mwworld/physicssystem.cpp index 2a7f5948e4..a7103b9911 100644 --- a/apps/openmw/mwworld/physicssystem.cpp +++ b/apps/openmw/mwworld/physicssystem.cpp @@ -108,7 +108,7 @@ namespace MWWorld } static Ogre::Vector3 move(const MWWorld::Ptr &ptr, const Ogre::Vector3 &movement, float time, - bool isFlying, float waterlevel, OEngine::Physic::PhysicEngine *engine) + bool isFlying, float waterlevel, float slowFall, OEngine::Physic::PhysicEngine *engine) { const ESM::Position &refpos = ptr.getRefData().getPosition(); Ogre::Vector3 position(refpos.pos); @@ -229,7 +229,10 @@ namespace MWWorld physicActor->setInertialForce(Ogre::Vector3(0.0f)); else { - inertia.z += time*-627.2f; + float diff = time*-627.2f; + if (inertia.z < 0) + diff *= slowFall; + inertia.z += diff; physicActor->setInertialForce(inertia); } physicActor->setOnGround(isOnGround); @@ -577,9 +580,10 @@ namespace MWWorld float oldHeight = iter->first.getRefData().getPosition().pos[2]; + const MWMechanics::MagicEffects& effects = iter->first.getClass().getCreatureStats(iter->first).getMagicEffects(); + bool waterCollision = false; - if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects() - .get(ESM::MagicEffect::WaterWalking).mMagnitude + if (effects.get(ESM::MagicEffect::WaterWalking).mMagnitude && cell->hasWater() && !world->isUnderwater(iter->first.getCell(), Ogre::Vector3(iter->first.getRefData().getPosition().pos))) @@ -592,9 +596,12 @@ namespace MWWorld if (waterCollision) mEngine->dynamicsWorld->addCollisionObject(&object); + // 100 points of slowfall reduce gravity by 90% (this is just a guess) + float slowFall = 1-std::min(std::max(0.f, (effects.get(ESM::MagicEffect::SlowFall).mMagnitude / 100.f) * 0.9f), 0.9f); + Ogre::Vector3 newpos = MovementSolver::move(iter->first, iter->second, mTimeAccum, world->isFlying(iter->first), - waterlevel, mEngine); + waterlevel, slowFall, mEngine); if (waterCollision) mEngine->dynamicsWorld->removeCollisionObject(&object); From 228254c890e6b90f1bcb2070b3e356c854efeeb1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 16:31:27 +0100 Subject: [PATCH 15/17] Handle creature attack animations in character controller --- apps/openmw/mwmechanics/character.cpp | 46 +++++++++++++++++++++++++-- apps/openmw/mwmechanics/character.hpp | 3 +- apps/openmw/mwworld/worldimp.cpp | 3 +- 3 files changed, 47 insertions(+), 5 deletions(-) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 755dbc0932..fe650237ac 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -479,9 +479,47 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr) mPtr = ptr; } -bool CharacterController::updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak) +bool CharacterController::updateCreatureState() { - const MWWorld::Class &cls = MWWorld::Class::get(mPtr); + const MWWorld::Class &cls = mPtr.getClass(); + CreatureStats &stats = cls.getCreatureStats(mPtr); + + if(stats.getAttackingOrSpell()) + { + if(mUpperBodyState == UpperCharState_Nothing && mHitState == CharState_None) + { + MWBase::Environment::get().getWorld()->breakInvisibility(mPtr); + + switch (stats.getAttackType()) + { + case CreatureStats::AT_Chop: + mCurrentWeapon = "attack1"; + break; + case CreatureStats::AT_Thrust: + mCurrentWeapon = "attack2"; + break; + case CreatureStats::AT_Slash: + mCurrentWeapon = "attack3"; + break; + } + + mAnimation->play(mCurrentWeapon, Priority_Weapon, + MWRender::Animation::Group_UpperBody, true, + 1, "start", "stop", + 0.0f, 0); + mUpperBodyState = UpperCharState_StartToMinAttack; + } + } + + bool animPlaying = mAnimation->getInfo(mCurrentWeapon); + if (!animPlaying) + mUpperBodyState = UpperCharState_Nothing; + return false; +} + +bool CharacterController::updateNpcState(bool inwater, bool isrunning) +{ + const MWWorld::Class &cls = MWWorld::Class::get(mPtr); NpcStats &stats = cls.getNpcStats(mPtr); WeaponType weaptype = WeapType_None; MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr); @@ -1091,7 +1129,9 @@ void CharacterController::update(float duration) } if(cls.isNpc()) - forcestateupdate = updateNpcState(onground, inwater, isrunning, sneak) || forcestateupdate; + forcestateupdate = updateNpcState(inwater, isrunning) || forcestateupdate; + else + forcestateupdate = updateCreatureState() || forcestateupdate; refreshCurrentAnims(idlestate, movestate, forcestateupdate); diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 438f542f02..d3d4b44355 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -176,7 +176,8 @@ class CharacterController void clearAnimQueue(); - bool updateNpcState(bool onground, bool inwater, bool isrunning, bool sneak); + bool updateNpcState(bool inwater, bool isrunning); + bool updateCreatureState(); void updateVisibility(); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index a40f20696d..0d78020817 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2282,7 +2282,8 @@ namespace MWWorld void World::breakInvisibility(const Ptr &actor) { actor.getClass().getCreatureStats(actor).getActiveSpells().purgeEffect(ESM::MagicEffect::Invisibility); - actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); + if (actor.getClass().isNpc()) + actor.getClass().getInventoryStore(actor).purgeEffect(ESM::MagicEffect::Invisibility); } bool World::isDark() const From 9b32b1403ba63c63ef5d9261a2c641f78eff7e36 Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 17:19:08 +0100 Subject: [PATCH 16/17] Feature #960: Implement Creature::hit --- apps/openmw/mwclass/creature.cpp | 56 +++++++++++++++++++++++ apps/openmw/mwclass/npc.cpp | 3 +- apps/openmw/mwmechanics/character.cpp | 4 +- apps/openmw/mwmechanics/creaturestats.hpp | 4 +- apps/openmw/mwrender/animation.cpp | 17 +++++-- 5 files changed, 75 insertions(+), 9 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0fc26950d5..e6d2965faf 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -173,6 +173,62 @@ namespace MWClass void Creature::hit(const MWWorld::Ptr& ptr, int type) const { + MWWorld::LiveCellRef *ref = + ptr.get(); + + // TODO: where is the distance defined? + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, 100); + if (result.first.isEmpty()) + return; // Didn't hit anything + + MWWorld::Ptr victim = result.first; + + if (!victim.getClass().isActor()) + return; // Can't hit non-actors + + Ogre::Vector3 hitPosition = result.second; + + MWMechanics::CreatureStats &stats = getCreatureStats(ptr); + MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim); + const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); + float hitchance = ref->mBase->mData.mCombat + + (stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) + + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f); + hitchance *= stats.getFatigueTerm(); + hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude - + mageffects.get(ESM::MagicEffect::Blind).mMagnitude; + hitchance -= otherstats.getEvasion(); + + if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f) + { + victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false); + return; + } + + int min,max; + switch (type) + { + case 0: + min = ref->mBase->mData.mAttack[0]; + max = ref->mBase->mData.mAttack[1]; + break; + case 1: + min = ref->mBase->mData.mAttack[2]; + max = ref->mBase->mData.mAttack[3]; + break; + case 2: + default: + min = ref->mBase->mData.mAttack[4]; + max = ref->mBase->mData.mAttack[5]; + break; + } + + float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0); + + // TODO: do not do this if the attack is blocked + MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); + + victim.getClass().onHit(victim, damage, true, MWWorld::Ptr(), ptr, true); } void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index cfd981088d..f93a3e342d 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -454,7 +454,7 @@ namespace MWClass float dist = 100.0f * (!weapon.isEmpty() ? weapon.get()->mBase->mData.mReach : gmst.find("fHandToHandReach")->getFloat()); - // TODO: Use second to work out the hit angle and where to spawn the blood effect + // TODO: Use second to work out the hit angle std::pair result = world->getHitContact(ptr, dist); MWWorld::Ptr victim = result.first; Ogre::Vector3 hitPosition = result.second; @@ -604,6 +604,7 @@ namespace MWClass } } + // TODO: do not do this if the attack is blocked if (healthdmg) MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition); diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index fe650237ac..013af5b3a1 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -495,10 +495,10 @@ bool CharacterController::updateCreatureState() case CreatureStats::AT_Chop: mCurrentWeapon = "attack1"; break; - case CreatureStats::AT_Thrust: + case CreatureStats::AT_Slash: mCurrentWeapon = "attack2"; break; - case CreatureStats::AT_Slash: + case CreatureStats::AT_Thrust: mCurrentWeapon = "attack3"; break; } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 26856c966d..308883fc53 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -116,9 +116,9 @@ namespace MWMechanics enum AttackType { + AT_Chop, AT_Slash, - AT_Thrust, - AT_Chop + AT_Thrust }; void setAttackType(int attackType) { mAttackType = attackType; } int getAttackType() { return mAttackType; } diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index de89b0207d..a7623efea6 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -660,13 +660,22 @@ void Animation::handleTextKey(AnimState &state, const std::string &groupname, co else if(evt.compare(off, len, "unequip detach") == 0) showWeapons(false); else if(evt.compare(off, len, "chop hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); else if(evt.compare(off, len, "slash hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); else if(evt.compare(off, len, "thrust hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); else if(evt.compare(off, len, "hit") == 0) - MWWorld::Class::get(mPtr).hit(mPtr); + { + if (groupname == "attack1") + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Chop); + else if (groupname == "attack2") + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Slash); + else if (groupname == "attack3") + mPtr.getClass().hit(mPtr, MWMechanics::CreatureStats::AT_Thrust); + else + mPtr.getClass().hit(mPtr); + } else if (groupname == "spellcast" && evt.substr(evt.size()-7, 7) == "release") MWBase::Environment::get().getWorld()->castSpell(mPtr); From 149d77dedf4cb89a46db80248c6f16b73f3a345c Mon Sep 17 00:00:00 2001 From: scrawl Date: Fri, 17 Jan 2014 17:40:58 +0100 Subject: [PATCH 17/17] Feature #960: Add knockdown and hit recovery for creatures --- apps/openmw/mwclass/creature.cpp | 19 +++++++++++++++++++ apps/openmw/mwclass/creature.hpp | 4 ++++ 2 files changed, 23 insertions(+) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index e6d2965faf..a972683181 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -69,6 +69,9 @@ namespace MWClass fMaxFlySpeed = gmst.find("fMaxFlySpeed"); fSwimRunBase = gmst.find("fSwimRunBase"); fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); + fKnockDownMult = gmst.find("fKnockDownMult"); + iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); + iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); inited = true; } @@ -255,6 +258,19 @@ namespace MWClass ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); } + // Check for knockdown + float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); + float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() + * iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); + int roll = std::rand()/ (static_cast (RAND_MAX) + 1) * 100; // [0, 99] + if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) + { + getCreatureStats(ptr).setKnockedDown(true); + + } + else + getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? + if(ishealth) { if(damage > 0.0f) @@ -607,5 +623,8 @@ namespace MWClass const ESM::GameSetting *Creature::fMaxFlySpeed; const ESM::GameSetting *Creature::fSwimRunBase; const ESM::GameSetting *Creature::fSwimRunAthleticsMult; + const ESM::GameSetting *Creature::fKnockDownMult; + const ESM::GameSetting *Creature::iKnockDownOddsMult; + const ESM::GameSetting *Creature::iKnockDownOddsBase; } diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 708999ef04..d518d0056b 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -24,6 +24,10 @@ namespace MWClass static const ESM::GameSetting *fMaxFlySpeed; static const ESM::GameSetting *fSwimRunBase; static const ESM::GameSetting *fSwimRunAthleticsMult; + static const ESM::GameSetting *fKnockDownMult; + static const ESM::GameSetting *iKnockDownOddsMult; + static const ESM::GameSetting *iKnockDownOddsBase; + public: