From f6a876bc3d201baa52de4bc2dc76fe12e63b4fcf Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 29 Apr 2014 15:27:49 +0200 Subject: [PATCH 01/29] added actor ID --- apps/openmw/mwbase/world.hpp | 3 +++ apps/openmw/mwmechanics/creaturestats.cpp | 18 +++++++++++++- apps/openmw/mwmechanics/creaturestats.hpp | 9 +++++++ apps/openmw/mwworld/cellstore.cpp | 29 +++++++++++++++++++++++ apps/openmw/mwworld/cellstore.hpp | 3 +++ apps/openmw/mwworld/scene.cpp | 10 ++++++++ apps/openmw/mwworld/scene.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 7 +++++- apps/openmw/mwworld/worldimp.hpp | 3 +++ 9 files changed, 82 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 44c8c96be..c397b190f 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -200,6 +200,9 @@ namespace MWBase virtual MWWorld::Ptr searchPtrViaHandle (const std::string& handle) = 0; ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found + virtual MWWorld::Ptr searchPtrViaActorId (int actorId) = 0; + ///< Search is limited to the active cells. + /// \todo enable reference in the OGRE scene virtual void enable (const MWWorld::Ptr& ptr) = 0; diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 40ac92251..8490226cb 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -11,6 +11,8 @@ namespace MWMechanics { + int CreatureStats::sActorId = 0; + CreatureStats::CreatureStats() : mLevel (0), mDead (false), mDied (false), mFriendlyHits (0), mTalkedTo (false), mAlarmed (false), @@ -18,7 +20,8 @@ namespace MWMechanics mAttackingOrSpell(false), mIsWerewolf(false), mFallHeight(0), mRecalcDynamicStats(false), mKnockdown(false), mKnockdownOneFrame(false), mKnockdownOverOneFrame(false), mHitRecovery(false), mBlock(false), - mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f) + mMovementFlags(0), mDrawState (DrawState_Nothing), mAttackStrength(0.f), + mActorId (-1) { for (int i=0; i<4; ++i) mAiSettings[i] = 0; @@ -516,4 +519,17 @@ namespace MWMechanics { return mGoldPool; } + + int CreatureStats::getActorId() + { + if (mActorId==-1) + mActorId = sActorId++; + + return mActorId; + } + + bool CreatureStats::matchesActorId (int id) const + { + return mActorId!=-1 && id==mActorId; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 633dc285c..23d9303c0 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -24,6 +24,7 @@ namespace MWMechanics /// class CreatureStats { + static int sActorId; DrawState_ mDrawState; AttributeValue mAttributes[8]; DynamicStat mDynamic[3]; // health, magicka, fatigue @@ -60,6 +61,7 @@ namespace MWMechanics MWWorld::TimeStamp mTradeTime; // Relates to NPC gold reset delay int mGoldPool; // the pool of merchant gold not in inventory + int mActorId; protected: bool mIsWerewolf; @@ -239,6 +241,13 @@ namespace MWMechanics void setGoldPool(int pool); int getGoldPool() const; + + int getActorId(); + ///< Will generate an actor ID, if the actor does not have one yet. + + bool matchesActorId (int id) const; + ///< Check if \a id matches the actor ID of *this (if the actor does not have an ID + /// assigned this function will return false). }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index e5f0c4b88..7a85623cf 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -15,6 +15,8 @@ #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "ptr.hpp" #include "esmstore.hpp" #include "class.hpp" @@ -40,6 +42,22 @@ namespace return MWWorld::Ptr(); } + template + MWWorld::Ptr searchViaActorId (MWWorld::CellRefList& actorList, int actorId, + MWWorld::CellStore *cell) + { + for (typename MWWorld::CellRefList::List::iterator iter (actorList.mList.begin()); + iter!=actorList.mList.end(); ++iter) + { + MWWorld::Ptr actor (&*iter, cell); + + if (MWWorld::Class::get (actor).getCreatureStats (actor).matchesActorId (actorId)) + return actor; + } + + return MWWorld::Ptr(); + } + template void writeReferenceCollection (ESM::ESMWriter& writer, const MWWorld::CellRefList& collection) @@ -319,6 +337,17 @@ namespace MWWorld return Ptr(); } + Ptr CellStore::searchViaActorId (int id) + { + if (Ptr ptr = ::searchViaActorId (mNpcs, id, this)) + return ptr; + + if (Ptr ptr = ::searchViaActorId (mCreatures, id, this)) + return ptr; + + return Ptr(); + } + float CellStore::getWaterLevel() const { return mWaterLevel; diff --git a/apps/openmw/mwworld/cellstore.hpp b/apps/openmw/mwworld/cellstore.hpp index 88b49ed1c..69d1a53a3 100644 --- a/apps/openmw/mwworld/cellstore.hpp +++ b/apps/openmw/mwworld/cellstore.hpp @@ -80,6 +80,9 @@ namespace MWWorld Ptr searchViaHandle (const std::string& handle); ///< Will return an empty Ptr if cell is not loaded. + Ptr searchViaActorId (int id); + ///< Will return an empty Ptr if cell is not loaded. + float getWaterLevel() const; void setWaterLevel (float level); diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 3d4413a35..9d456e236 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -496,4 +496,14 @@ namespace MWWorld } return false; } + + Ptr Scene::searchPtrViaActorId (int actorId) + { + for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); + iter!=mActiveCells.end(); ++iter) + if (Ptr ptr = (*iter)->searchViaActorId (actorId)) + return ptr; + + return Ptr(); + } } diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index 16d4877a9..b85a3d7ce 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -102,6 +102,8 @@ namespace MWWorld ///< Remove an object from the scene, but not from the world model. bool isCellActive(const CellStore &cell); + + Ptr searchPtrViaActorId (int actorId); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index de9c8f04a..79c8b38f3 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -549,6 +549,11 @@ namespace MWWorld return MWWorld::Ptr(); } + Ptr World::searchPtrViaActorId (int actorId) + { + return mWorldScene->searchPtrViaActorId (actorId); + } + void World::addContainerScripts(const Ptr& reference, CellStore * cell) { if( reference.getTypeName()==typeid (ESM::Container).name() || @@ -1909,7 +1914,7 @@ namespace MWWorld out.push_back(searchPtrViaHandle(*it)); } } - + bool World::getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) { if (!targetNpc.getRefData().isEnabled() || !npc.getRefData().isEnabled()) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 645330683..2328e67a8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -287,6 +287,9 @@ namespace MWWorld virtual Ptr searchPtrViaHandle (const std::string& handle); ///< Return a pointer to a liveCellRef with the given Ogre handle or Ptr() if not found + virtual Ptr searchPtrViaActorId (int actorId); + ///< Search is limited to the active cells. + virtual void adjustPosition (const Ptr& ptr); ///< Adjust position after load to be on ground. Must be called after model load. From 9a1b5dc1c64d54f64694eba5f9c21cf0e7dfcd25 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 29 Apr 2014 15:32:00 +0200 Subject: [PATCH 02/29] a bit of cleanup --- apps/openmw/mwworld/scene.cpp | 10 ++++++++++ apps/openmw/mwworld/scene.hpp | 2 ++ apps/openmw/mwworld/worldimp.cpp | 11 +---------- 3 files changed, 13 insertions(+), 10 deletions(-) diff --git a/apps/openmw/mwworld/scene.cpp b/apps/openmw/mwworld/scene.cpp index 9d456e236..b277b2494 100644 --- a/apps/openmw/mwworld/scene.cpp +++ b/apps/openmw/mwworld/scene.cpp @@ -497,6 +497,16 @@ namespace MWWorld return false; } + Ptr Scene::searchPtrViaHandle (const std::string& handle) + { + for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); + iter!=mActiveCells.end(); ++iter) + if (Ptr ptr = (*iter)->searchViaHandle (handle)) + return ptr; + + return Ptr(); + } + Ptr Scene::searchPtrViaActorId (int actorId) { for (CellStoreCollection::const_iterator iter (mActiveCells.begin()); diff --git a/apps/openmw/mwworld/scene.hpp b/apps/openmw/mwworld/scene.hpp index b85a3d7ce..a26ad99de 100644 --- a/apps/openmw/mwworld/scene.hpp +++ b/apps/openmw/mwworld/scene.hpp @@ -103,6 +103,8 @@ namespace MWWorld bool isCellActive(const CellStore &cell); + Ptr searchPtrViaHandle (const std::string& handle); + Ptr searchPtrViaActorId (int actorId); }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 79c8b38f3..6fa182894 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -536,17 +536,8 @@ namespace MWWorld { if (mPlayer->getPlayer().getRefData().getHandle()==handle) return mPlayer->getPlayer(); - for (Scene::CellStoreCollection::const_iterator iter (mWorldScene->getActiveCells().begin()); - iter!=mWorldScene->getActiveCells().end(); ++iter) - { - CellStore* cellstore = *iter; - Ptr ptr = cellstore->searchViaHandle (handle); - if (!ptr.isEmpty()) - return ptr; - } - - return MWWorld::Ptr(); + return mWorldScene->searchPtrViaHandle (handle); } Ptr World::searchPtrViaActorId (int actorId) From 0c2843b0f7e081bfe64c9362f4101462f1223a2d Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 29 Apr 2014 19:56:33 +0200 Subject: [PATCH 03/29] some missing cleanup --- apps/openmw/mwmechanics/creaturestats.cpp | 5 +++++ apps/openmw/mwmechanics/creaturestats.hpp | 2 ++ apps/openmw/mwstate/statemanagerimp.cpp | 3 +++ 3 files changed, 10 insertions(+) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 8490226cb..910760685 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -532,4 +532,9 @@ namespace MWMechanics { return mActorId!=-1 && id==mActorId; } + + void CreatureStats::cleanup() + { + sActorId = 0; + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 23d9303c0..aee11a358 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -248,6 +248,8 @@ namespace MWMechanics bool matchesActorId (int id) const; ///< Check if \a id matches the actor ID of *this (if the actor does not have an ID /// assigned this function will return false). + + static void cleanup(); }; } diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index 33f2dce7c..6dcfd9076 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -28,6 +28,7 @@ #include "../mwworld/inventorystore.hpp" #include "../mwmechanics/npcstats.hpp" +#include "../mwmechanics/creaturestats.hpp" #include "../mwscript/globalscripts.hpp" @@ -46,6 +47,8 @@ void MWState::StateManager::cleanup (bool force) mState = State_NoGame; mCharacterManager.clearCurrentCharacter(); mTimePlayed = 0; + + MWMechanics::CreatureStats::cleanup(); } } From e591d23880cc81c5f086c50a152ef81b5073da44 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 May 2014 19:01:02 +0200 Subject: [PATCH 04/29] Fixes #894: Make sure the player's CharacterController is updated when the player is rebuilt. Necessary if the race is changed while we are still in a GUI (e.g. in the review dialog), and an update normally wouldn't occur. --- apps/openmw/mwmechanics/actors.cpp | 4 +++- apps/openmw/mwmechanics/actors.hpp | 2 +- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b935284d3..91ef48c09 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -781,7 +781,7 @@ namespace MWMechanics } } - void Actors::addActor (const MWWorld::Ptr& ptr) + void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately) { // erase previous death events since we are currently only tracking them while in an active cell MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied(); @@ -790,6 +790,8 @@ namespace MWMechanics MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); + if (updateImmediately) + mActors[ptr]->update(0); } void Actors::removeActor (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index f7dff1058..e42672bad 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -58,7 +58,7 @@ namespace MWMechanics /// paused we may want to do it manually (after equipping permanent enchantment) void updateMagicEffects (const MWWorld::Ptr& ptr) { adjustMagicEffects(ptr); } - void addActor (const MWWorld::Ptr& ptr); + void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false); ///< Register an actor for stats management /// /// \note Dead actors are ignored. diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 2b1f82b92..5c2ee30e1 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -385,7 +385,7 @@ namespace MWMechanics // have been made for them. Make sure they're properly updated. MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getPlayerPtr(); mActors.removeActor(ptr); - mActors.addActor(ptr); + mActors.addActor(ptr, true); } mActors.update(duration, paused); From f01c3e0eacfa0d42bd367892723c2be4df1441cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 May 2014 19:38:28 +0200 Subject: [PATCH 05/29] Fixes #1037: Change default footstep volume to match vanilla --- files/settings-default.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/files/settings-default.cfg b/files/settings-default.cfg index 2b44d0f4e..9eed2c7d9 100644 --- a/files/settings-default.cfg +++ b/files/settings-default.cfg @@ -150,7 +150,7 @@ device = master volume = 1.0 sfx volume = 1.0 music volume = 0.4 -footsteps volume = 0.6 +footsteps volume = 0.15 voice volume = 1.0 From 365ca6c7e1b6208fc323f1fc29db8c4747bb6ee4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Tue, 13 May 2014 21:42:27 +0200 Subject: [PATCH 06/29] Fixes #1331: Manually disable movement state for dead actors. For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually. --- apps/openmw/mwmechanics/character.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 280639f71..2b8b6eccb 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -431,6 +431,11 @@ void CharacterController::playRandomDeath(float startpoint) mDeathState = static_cast(CharState_Death1 + (selected-1)); } + // For dead actors, refreshCurrentAnims is no longer called, so we need to disable the movement state manually. + mMovementState = CharState_None; + mAnimation->disable(mCurrentMovement); + mCurrentMovement = ""; + mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, false, 1.0f, "start", "stop", startpoint, 0); } From 6c7b3074f5447a60d124b42e8ab6fe9114e87a72 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 05:31:19 +0200 Subject: [PATCH 07/29] Fix self-defense for creatures when attacked with a spell --- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index e6342e661..0f5649af2 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -408,9 +408,9 @@ namespace MWMechanics target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, mSourceName, caster.getRefData().getHandle()); - if (anyHarmfulEffect && target.getClass().isActor() && target != caster - && target.getClass().getCreatureStats(target).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) - MWBase::Environment::get().getMechanicsManager()->commitCrime(caster, target, MWBase::MechanicsManager::OT_Assault); + // Notify the target actor they've been hit + if (anyHarmfulEffect && target.getClass().isActor() && target != caster) + target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); } void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const MWMechanics::EffectKey& effect, float magnitude) From 99b4bc721b5ec17ce4cf3f1e2638337b5d1440ac Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 05:33:18 +0200 Subject: [PATCH 08/29] Don't attempt to inflict spells on dead actors --- apps/openmw/mwmechanics/spellcasting.cpp | 3 +++ apps/openmw/mwmechanics/spellcasting.hpp | 1 + 2 files changed, 4 insertions(+) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 0f5649af2..d510373ff 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -189,6 +189,9 @@ namespace MWMechanics void CastSpell::inflict(const MWWorld::Ptr &target, const MWWorld::Ptr &caster, const ESM::EffectList &effects, ESM::RangeType range, bool reflected, bool exploded) { + if (target.getClass().isActor() && target.getClass().getCreatureStats(target).isDead()) + return; + // If none of the effects need to apply, we can early-out bool found = false; for (std::vector::const_iterator iter (effects.mList.begin()); diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index 44402fe7b..a930442fb 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -61,6 +61,7 @@ namespace MWMechanics /// @note Auto detects if spell, ingredient or potion bool cast (const std::string& id); + /// @note \a target can be any type of object, not just actors. void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); From 7697ab37e02890d6a5bdc84dc7cfcd91be87dc9c Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 06:37:31 +0200 Subject: [PATCH 09/29] Fixes #1129: Change summoned creatures to use ActorId Gracefully handles summoned creatures that are left behind in inactive cells. --- apps/openmw/mwmechanics/actors.cpp | 46 +++++++++++++++++------ apps/openmw/mwmechanics/creaturestats.hpp | 12 ++++-- apps/openmw/mwworld/actionteleport.cpp | 1 - 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 91ef48c09..22d033f40 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -38,9 +38,12 @@ void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& a { if (bound) { - MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor); - MWWorld::ActionEquip action(newPtr); - action.execute(actor); + if (actor.getClass().getContainerStore(actor).count(item) == 0) + { + MWWorld::Ptr newPtr = *actor.getClass().getContainerStore(actor).add(item, 1, actor); + MWWorld::ActionEquip action(newPtr); + action.execute(actor); + } } else { @@ -534,28 +537,49 @@ namespace MWMechanics MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1); ref.getPtr().getCellRef().mPos = ipos; - // TODO: Add AI to follow player and fight for him + MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); + + // Make the summoned creature follow its master and help in fights AiFollow package(ptr.getRefData().getHandle()); - MWWorld::Class::get (ref.getPtr()).getCreatureStats (ref.getPtr()).getAiSequence().stack(package, ptr); + summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); + int creatureActorId = summonedCreatureStats.getActorId(); + + MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos); + // TODO: VFX_SummonStart, VFX_SummonEnd - creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos).getRefData().getHandle())); + creatureStats.mSummonedCreatures.insert(std::make_pair(it->first, creatureActorId)); } } else { - std::string handle = creatureStats.mSummonedCreatures[it->first]; - // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation - // plays though, which is a rather lame exploit in vanilla. - MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaHandle(handle); + // Summon lifetime has expired. Try to delete the creature. + int actorId = creatureStats.mSummonedCreatures[it->first]; + creatureStats.mSummonedCreatures.erase(it->first); + + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(actorId); if (!ptr.isEmpty()) { + // TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation + // plays though, which is a rather lame exploit in vanilla. MWBase::Environment::get().getWorld()->deleteObject(ptr); creatureStats.mSummonedCreatures.erase(it->first); } + else + { + // We didn't find the creature. It's probably in an inactive cell. + // Add to graveyard so we can delete it when the cell becomes active. + creatureStats.mSummonGraveyard.push_back(actorId); + } } } } + + for (std::vector::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); ++it) + { + MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); + if (!ptr.isEmpty()) + MWBase::Environment::get().getWorld()->deleteObject(ptr); + } } void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 7cec26fb8..79686bb97 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -223,10 +223,16 @@ namespace MWMechanics void setLastHitObject(const std::string &objectid); const std::string &getLastHitObject() const; - // Note, this is just a cache to avoid checking the whole container store every frame TODO: Put it somewhere else? + // Note, this is just a cache to avoid checking the whole container store every frame. We don't need to store it in saves. + // TODO: Put it somewhere else? std::set mBoundItems; - // Same as above - std::map mSummonedCreatures; + + // TODO: store in savegame + // TODO: encapsulate? + // + std::map mSummonedCreatures; + // Contains summoned creatures with an expired lifetime that have not been deleted yet. + std::vector mSummonGraveyard; void writeState (ESM::CreatureStats& state) const; diff --git a/apps/openmw/mwworld/actionteleport.cpp b/apps/openmw/mwworld/actionteleport.cpp index 627c05251..4378e179d 100644 --- a/apps/openmw/mwworld/actionteleport.cpp +++ b/apps/openmw/mwworld/actionteleport.cpp @@ -22,7 +22,6 @@ namespace MWWorld std::list followers = MWBase::Environment::get().getMechanicsManager()->getActorsFollowing(actor); for(std::list::iterator it = followers.begin();it != followers.end();++it) { - std::cout << "teleporting someone!" << (*it).getCellRef().mRefID; executeImp(*it); } From 67e942e733fc1410b967f7be045e2e892397e0c5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 07:13:12 +0200 Subject: [PATCH 10/29] Bug #869: Some fixes to the previous fix --- apps/openmw/mwclass/creature.cpp | 4 ++++ apps/openmw/mwclass/npc.cpp | 2 ++ libs/openengine/bullet/physic.cpp | 8 +++++--- libs/openengine/bullet/physic.hpp | 1 + 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 4c6ab1c75..dc23b63f3 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -164,7 +164,11 @@ namespace MWClass { const std::string model = getModel(ptr); if(!model.empty()) + { physics.addActor(ptr); + if (getCreatureStats(ptr).isDead()) + MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false); + } MWBase::Environment::get().getMechanicsManager()->add(ptr); } diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 4c9b683a4..214e51009 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -388,6 +388,8 @@ namespace MWClass { physics.addActor(ptr); MWBase::Environment::get().getMechanicsManager()->add(ptr); + if (getCreatureStats(ptr).isDead()) + MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false); } bool Npc::isPersistent(const MWWorld::Ptr &actor) const diff --git a/libs/openengine/bullet/physic.cpp b/libs/openengine/bullet/physic.cpp index c10892e52..6646ce273 100644 --- a/libs/openengine/bullet/physic.cpp +++ b/libs/openengine/bullet/physic.cpp @@ -18,6 +18,7 @@ namespace Physic PhysicActor::PhysicActor(const std::string &name, const std::string &mesh, PhysicEngine *engine, const Ogre::Vector3 &position, const Ogre::Quaternion &rotation, float scale) : mName(name), mEngine(engine), mMesh(mesh), mBoxScaledTranslation(0,0,0), mBoxRotationInverse(0,0,0,0) , mBody(0), mRaycastingBody(0), mOnGround(false), mCollisionMode(true), mBoxRotation(0,0,0,0) + , mCollisionBody(true) , mForce(0.0f) { mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, position, rotation, &mBoxScaledTranslation, &mBoxRotation); @@ -49,8 +50,9 @@ namespace Physic void PhysicActor::enableCollisionBody(bool collision) { assert(mBody); - if(collision && !mCollisionMode) enableCollisionBody(); - if(!collision && mCollisionMode) disableCollisionBody(); + if(collision && !mCollisionBody) enableCollisionBody(); + if(!collision && mCollisionBody) disableCollisionBody(); + mCollisionBody = collision; } void PhysicActor::setPosition(const Ogre::Vector3 &pos) @@ -108,7 +110,7 @@ namespace Physic //Create the newly scaled rigid body mBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot); mRaycastingBody = mEngine->createAndAdjustRigidBody(mMesh, mName, scale, pos, rot, 0, 0, true); - mEngine->addRigidBody(mBody, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map + mEngine->addRigidBody(mCollisionBody ? mBody : 0, false, mRaycastingBody,true); //Add rigid body to dynamics world, but do not add to object map } Ogre::Vector3 PhysicActor::getHalfExtents() const diff --git a/libs/openengine/bullet/physic.hpp b/libs/openengine/bullet/physic.hpp index 4e035446e..16eb45306 100644 --- a/libs/openengine/bullet/physic.hpp +++ b/libs/openengine/bullet/physic.hpp @@ -175,6 +175,7 @@ namespace Physic Ogre::Vector3 mForce; bool mOnGround; bool mCollisionMode; + bool mCollisionBody; std::string mMesh; std::string mName; From dfacf8c0448710c0e64dfc83789cdd86b9fa22f4 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 07:14:08 +0200 Subject: [PATCH 11/29] Change ActiveSpells to use ActorId --- apps/openmw/mwgui/spellicons.cpp | 2 +- apps/openmw/mwgui/spellicons.hpp | 2 +- apps/openmw/mwmechanics/activespells.cpp | 10 +++++----- apps/openmw/mwmechanics/activespells.hpp | 11 +++++------ apps/openmw/mwmechanics/actors.cpp | 19 +++++++++---------- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/character.hpp | 2 ++ apps/openmw/mwmechanics/magiceffects.hpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 6 +++--- apps/openmw/mwmechanics/spells.cpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 2 +- 11 files changed, 30 insertions(+), 30 deletions(-) diff --git a/apps/openmw/mwgui/spellicons.cpp b/apps/openmw/mwgui/spellicons.cpp index 0cd665a87..d5ece298b 100644 --- a/apps/openmw/mwgui/spellicons.cpp +++ b/apps/openmw/mwgui/spellicons.cpp @@ -21,7 +21,7 @@ namespace MWGui { void EffectSourceVisitor::visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& casterHandle, + const std::string& sourceName, int casterActorId, float magnitude, float remainingTime) { MagicEffectInfo newEffectSource; diff --git a/apps/openmw/mwgui/spellicons.hpp b/apps/openmw/mwgui/spellicons.hpp index 1bb80f3d4..6aed8db71 100644 --- a/apps/openmw/mwgui/spellicons.hpp +++ b/apps/openmw/mwgui/spellicons.hpp @@ -43,7 +43,7 @@ namespace MWGui std::map > mEffectSources; virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& casterHandle, + const std::string& sourceName, int casterActorId, float magnitude, float remainingTime = -1); }; diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 86db207a4..95d455edb 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -133,7 +133,7 @@ namespace MWMechanics } void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, - const std::string &displayName, const std::string& casterHandle) + const std::string &displayName, int casterActorId) { bool exists = false; for (TContainer::const_iterator it = begin(); it != end(); ++it) @@ -146,7 +146,7 @@ namespace MWMechanics params.mTimeStamp = MWBase::Environment::get().getWorld()->getTimeStamp(); params.mEffects = effects; params.mDisplayName = displayName; - params.mCasterHandle = casterHandle; + params.mCasterActorId = casterActorId; if (!exists || stack) mSpells.insert (std::make_pair(id, params)); @@ -178,7 +178,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(effectIt->mKey, name, it->second.mCasterHandle, magnitude, remainingTime); + visitor.visit(effectIt->mKey, name, it->second.mCasterActorId, magnitude, remainingTime); } } } @@ -212,7 +212,7 @@ namespace MWMechanics mSpellsChanged = true; } - void ActiveSpells::purge(const std::string &actorHandle) + void ActiveSpells::purge(int casterActorId) { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { @@ -221,7 +221,7 @@ namespace MWMechanics { const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mKey.mId); if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked - && it->second.mCasterHandle == actorHandle) + && it->second.mCasterActorId == casterActorId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 7a40afb4c..5f36cea2c 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -37,8 +37,8 @@ namespace MWMechanics MWWorld::TimeStamp mTimeStamp; std::string mDisplayName; - // Handle to the caster that that inflicted this spell on us - std::string mCasterHandle; + // The caster that inflicted this spell on us + int mCasterActorId; }; typedef std::multimap TContainer; @@ -76,10 +76,9 @@ namespace MWMechanics /// \param stack If false, the spell is not added if one with the same ID exists already. /// \param effects /// \param displayName Name for display in magic menu. - /// \param casterHandle /// void addSpell (const std::string& id, bool stack, std::vector effects, - const std::string& displayName, const std::string& casterHandle); + const std::string& displayName, int casterActorId); /// Removes the active effects from this spell/potion/.. with \a id void removeEffects (const std::string& id); @@ -90,8 +89,8 @@ namespace MWMechanics /// Remove all active effects, if roll succeeds (for each effect) void purgeAll (float chance); - /// Remove all effects with CASTER_LINKED flag that were cast by \a actorHandle - void purge (const std::string& actorHandle); + /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId + void purge (int casterActorId); bool isSpellActive (std::string id) const; ///< case insensitive diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 22d033f40..e0db094d1 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -119,7 +119,7 @@ namespace MWMechanics : mCreature(trappedCreature) {} virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& casterHandle, + const std::string& sourceName, int casterActorId, float magnitude, float remainingTime = -1) { if (key.mId != ESM::MagicEffect::Soultrap) @@ -129,7 +129,7 @@ namespace MWMechanics MWBase::World* world = MWBase::Environment::get().getWorld(); - MWWorld::Ptr caster = world->searchPtrViaHandle(casterHandle); + MWWorld::Ptr caster = world->searchPtrViaActorId(casterActorId); if (caster.isEmpty() || !caster.getClass().isActor()) return; @@ -938,18 +938,17 @@ namespace MWMechanics continue; } - // Make sure spell effects with CasterLinked flag are removed - // TODO: would be nice not to do this all the time... - for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) - { - MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); - spells.purge(iter->first.getRefData().getHandle()); - } - if (iter->second->kill()) { ++mDeathCount[cls.getId(iter->first)]; + // Make sure spell effects with CasterLinked flag are removed + for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2) + { + MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells(); + spells.purge(stats.getActorId()); + } + // Apply soultrap if (iter->first.getTypeName() == typeid(ESM::Creature).name()) { diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 2b8b6eccb..bf8ce8bdc 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -1372,9 +1372,9 @@ bool CharacterController::kill() { if( isDead() ) { - //player's death animation is over if( mPtr.getRefData().getHandle()=="player" && !isAnimPlaying(mCurrentDeath) ) { + //player's death animation is over MWBase::Environment::get().getStateManager()->askLoadRecent(); } return false; diff --git a/apps/openmw/mwmechanics/character.hpp b/apps/openmw/mwmechanics/character.hpp index 5aea0210f..1b7caf34d 100644 --- a/apps/openmw/mwmechanics/character.hpp +++ b/apps/openmw/mwmechanics/character.hpp @@ -202,7 +202,9 @@ public: void skipAnim(); bool isAnimPlaying(const std::string &groupName); + /// @return false if the character has already been killed before bool kill(); + void resurrect(); bool isDead() const { return mDeathState != CharState_None; } diff --git a/apps/openmw/mwmechanics/magiceffects.hpp b/apps/openmw/mwmechanics/magiceffects.hpp index 45abda21d..4fd5e159a 100644 --- a/apps/openmw/mwmechanics/magiceffects.hpp +++ b/apps/openmw/mwmechanics/magiceffects.hpp @@ -56,7 +56,7 @@ namespace MWMechanics struct EffectSourceVisitor { virtual void visit (MWMechanics::EffectKey key, - const std::string& sourceName, const std::string& casterHandle, + const std::string& sourceName, int casterActorId, float magnitude, float remainingTime = -1) = 0; }; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index d510373ff..cb02d4655 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -349,9 +349,9 @@ namespace MWMechanics ActiveSpells::Effect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); - // Also make sure to set casterHandle = target, so that the effect on the caster gets purged when the target dies + // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - effects, mSourceName, target.getRefData().getHandle()); + effects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); } } } @@ -409,7 +409,7 @@ namespace MWMechanics if (!appliedLastingEffects.empty()) target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, - mSourceName, caster.getRefData().getHandle()); + mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); // Notify the target actor they've been hit if (anyHarmfulEffect && target.getClass().isActor() && target != caster) diff --git a/apps/openmw/mwmechanics/spells.cpp b/apps/openmw/mwmechanics/spells.cpp index a326b7a6d..dee1a1b05 100644 --- a/apps/openmw/mwmechanics/spells.cpp +++ b/apps/openmw/mwmechanics/spells.cpp @@ -211,7 +211,7 @@ namespace MWMechanics random = it->second.at(i); float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; - visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, "", magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), spell->mName, -1, magnitude); } } } diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index b18bda5e3..7127c92f3 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -599,7 +599,7 @@ void MWWorld::InventoryStore::visitEffectSources(MWMechanics::EffectSourceVisito const EffectParams& params = mPermanentMagicEffectMagnitudes[(**iter).getCellRef().mRefID][i]; float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * params.mRandom; magnitude *= params.mMultiplier; - visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), "", magnitude); + visitor.visit(MWMechanics::EffectKey(*effectIt), (**iter).getClass().getName(**iter), -1, magnitude); ++i; } From 51020fdb5fb2a95ec9339a331e68d04bc831f858 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 09:17:30 +0200 Subject: [PATCH 12/29] Fix a bug for NpcStats::mUsedIds in savegames, and added a compile-time guard to prevent similar errors in the future. --- components/esm/esmwriter.hpp | 12 ++++++++++++ components/esm/npcstats.cpp | 2 +- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/components/esm/esmwriter.hpp b/components/esm/esmwriter.hpp index 57faefdf4..ca4f42217 100644 --- a/components/esm/esmwriter.hpp +++ b/components/esm/esmwriter.hpp @@ -74,6 +74,18 @@ class ESMWriter endRecord(name); } +private: + // Prevent using writeHNT with strings. This already happened by accident and results in + // state being discarded without any error on writing or reading it. :( + // writeHNString and friends must be used instead. + void writeHNT(const std::string &name, std::string data) + { + } + void writeT(const std::string& data) + { + } +public: + template void writeHNT(const std::string& name, const T& data, int size) { diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index 21f573bc7..3fa954182 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -140,7 +140,7 @@ void ESM::NpcStats::save (ESMWriter &esm) const for (std::vector::const_iterator iter (mUsedIds.begin()); iter!=mUsedIds.end(); ++iter) - esm.writeHNT ("USED", *iter); + esm.writeHNString ("USED", *iter); if (mTimeToStartDrowning) esm.writeHNT ("DRTI", mTimeToStartDrowning); From 61187c2fef662a9fe0217609e6e81d3b0cfd1373 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 09:47:49 +0200 Subject: [PATCH 13/29] Savegame: store actorIds --- apps/openmw/mwmechanics/creaturestats.cpp | 14 ++++++++++++++ apps/openmw/mwmechanics/creaturestats.hpp | 3 +++ apps/openmw/mwstate/statemanagerimp.cpp | 1 + apps/openmw/mwworld/worldimp.cpp | 12 +++++++++++- components/esm/creaturestats.cpp | 6 ++++++ components/esm/creaturestats.hpp | 1 + components/esm/defs.hpp | 1 + 7 files changed, 37 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 9bfea41f3..80f81ffd1 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -501,6 +501,7 @@ namespace MWMechanics state.mRecalcDynamicStats = mRecalcDynamicStats; state.mDrawState = mDrawState; state.mLevel = mLevel; + state.mActorId = mActorId; mSpells.writeState(state.mSpells); } @@ -538,6 +539,7 @@ namespace MWMechanics mRecalcDynamicStats = state.mRecalcDynamicStats; mDrawState = DrawState_(state.mDrawState); mLevel = state.mLevel; + mActorId = state.mActorId; mSpells.readState(state.mSpells); } @@ -579,4 +581,16 @@ namespace MWMechanics { sActorId = 0; } + + void CreatureStats::writeActorIdCounter (ESM::ESMWriter& esm) + { + esm.startRecord(ESM::REC_ACTC); + esm.writeHNT("COUN", sActorId); + esm.endRecord(ESM::REC_ACTC); + } + + void CreatureStats::readActorIdCounter (ESM::ESMReader& esm) + { + esm.getHNT(sActorId, "COUN"); + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 79686bb97..7d2dc1a62 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -238,6 +238,9 @@ namespace MWMechanics void readState (const ESM::CreatureStats& state); + static void writeActorIdCounter (ESM::ESMWriter& esm); + static void readActorIdCounter (ESM::ESMReader& esm); + // Relates to NPC gold reset delay void setTradeTime(MWWorld::TimeStamp tradeTime); MWWorld::TimeStamp getTradeTime() const; diff --git a/apps/openmw/mwstate/statemanagerimp.cpp b/apps/openmw/mwstate/statemanagerimp.cpp index d261fe0c0..f2cda5a01 100644 --- a/apps/openmw/mwstate/statemanagerimp.cpp +++ b/apps/openmw/mwstate/statemanagerimp.cpp @@ -318,6 +318,7 @@ void MWState::StateManager::loadGame (const Character *character, const Slot *sl case ESM::REC_CSTA: case ESM::REC_WTHR: case ESM::REC_DYNA: + case ESM::REC_ACTC: MWBase::Environment::get().getWorld()->readRecord (reader, n.val, contentFileMap); break; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e07ee9df8..e223435dd 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -274,7 +274,8 @@ namespace MWWorld +mStore.countSavedGameRecords() +mGlobalVariables.countSavedGameRecords() +1 // player record - +1; // weather record + +1 // weather record + +1; // actorId counter } void World::write (ESM::ESMWriter& writer, Loading::Listener& progress) const @@ -287,6 +288,9 @@ namespace MWWorld mRendering->writeFog(cellstore); } + MWMechanics::CreatureStats::writeActorIdCounter(writer); + progress.increaseProgress(); + mCells.write (writer, progress); mStore.write (writer, progress); mGlobalVariables.write (writer, progress); @@ -297,6 +301,12 @@ namespace MWWorld void World::readRecord (ESM::ESMReader& reader, int32_t type, const std::map& contentFileMap) { + if (type == ESM::REC_ACTC) + { + MWMechanics::CreatureStats::readActorIdCounter(reader); + return; + } + if (!mStore.readRecord (reader, type) && !mGlobalVariables.readRecord (reader, type) && !mPlayer->readRecord (reader, type) && diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index 5bb6de662..e512ca5f8 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -72,6 +72,9 @@ void ESM::CreatureStats::load (ESMReader &esm) mLevel = 1; esm.getHNOT (mLevel, "LEVL"); + mActorId = -1; + esm.getHNOT (mActorId, "ACID"); + mSpells.load(esm); } @@ -146,5 +149,8 @@ void ESM::CreatureStats::save (ESMWriter &esm) const if (mLevel != 1) esm.writeHNT ("LEVL", mLevel); + if (mActorId != -1) + esm.writeHNT ("ACID", mActorId); + mSpells.save(esm); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 2cf2c5b4f..4c4c44b3f 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -24,6 +24,7 @@ namespace ESM ESM::TimeStamp mTradeTime; int mGoldPool; + int mActorId; bool mDead; bool mDied; diff --git a/components/esm/defs.hpp b/components/esm/defs.hpp index 9ef4dabd2..6c388de08 100644 --- a/components/esm/defs.hpp +++ b/components/esm/defs.hpp @@ -108,6 +108,7 @@ enum RecNameInts REC_KEYS = FourCC<'K','E','Y','S'>::value, REC_DYNA = FourCC<'D','Y','N','A'>::value, REC_ASPL = FourCC<'A','S','P','L'>::value, + REC_ACTC = FourCC<'A','C','T','C'>::value, // format 1 REC_FILT = 0x544C4946 From 1141c1f3f279dea2ab446e5cc3f757d1f7ca4fe1 Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 09:48:16 +0200 Subject: [PATCH 14/29] Change projectiles to use actorIds --- apps/openmw/mwworld/worldimp.cpp | 10 +++++----- apps/openmw/mwworld/worldimp.hpp | 4 ++-- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index e223435dd..947de0e4d 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2223,7 +2223,7 @@ namespace MWWorld const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) { ProjectileState state; - state.mActorHandle = actor.getRefData().getHandle(); + state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); state.mBow = bow; state.mVelocity = orient.yAxis() * speed; @@ -2310,7 +2310,7 @@ namespace MWWorld MagicBoltState state; state.mSourceName = sourceName; state.mId = id; - state.mActorHandle = actor.getRefData().getHandle(); + state.mActorId = actor.getClass().getCreatureStats(actor).getActorId(); state.mSpeed = speed; state.mStack = stack; @@ -2374,7 +2374,7 @@ namespace MWWorld if (obstacle == ptr) continue; - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); // Arrow intersects with player immediately after shooting :/ if (obstacle == caster) @@ -2460,7 +2460,7 @@ namespace MWWorld if (obstacle == ptr) continue; - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + MWWorld::Ptr caster = searchPtrViaActorId(it->second.mActorId); if (caster.isEmpty()) caster = obstacle; @@ -2483,7 +2483,7 @@ namespace MWWorld if (explode) { - MWWorld::Ptr caster = searchPtrViaHandle(it->second.mActorHandle); + 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); diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 2328e67a8..953045fa2 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -96,7 +96,7 @@ namespace MWWorld std::string mId; // Actor who casted this projectile - std::string mActorHandle; + int mActorId; // Name of item to display as effect source in magic menu (in case we casted an enchantment) std::string mSourceName; @@ -111,7 +111,7 @@ namespace MWWorld struct ProjectileState { // Actor who shot this projectile - std::string mActorHandle; + int mActorId; MWWorld::Ptr mBow; // bow or crossbow the projectile was fired from From 9052cc4a57c3bddefb2a8692d149c0a2947eaeed Mon Sep 17 00:00:00 2001 From: scrawl Date: Wed, 14 May 2014 22:16:39 +0200 Subject: [PATCH 15/29] Savegame: store ActiveSpells --- apps/openmw/mwclass/npc.cpp | 3 +- apps/openmw/mwmechanics/activespells.cpp | 61 ++++++++++++++++++----- apps/openmw/mwmechanics/activespells.hpp | 21 ++++---- apps/openmw/mwmechanics/actors.cpp | 1 + apps/openmw/mwmechanics/creaturestats.cpp | 4 +- apps/openmw/mwmechanics/spellcasting.cpp | 12 +++-- components/CMakeLists.txt | 2 +- components/esm/activespells.cpp | 56 +++++++++++++++++++++ components/esm/activespells.hpp | 45 +++++++++++++++++ components/esm/creaturestats.cpp | 3 +- components/esm/creaturestats.hpp | 2 + 11 files changed, 178 insertions(+), 32 deletions(-) create mode 100644 components/esm/activespells.cpp create mode 100644 components/esm/activespells.hpp diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 214e51009..845fffbc0 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -620,7 +620,8 @@ namespace MWClass // NOTE: 'object' and/or 'attacker' may be empty. // Attacking peaceful NPCs is a crime - if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30) + // anything below 80 is considered peaceful (see Actors::updateActor) + if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); getCreatureStats(ptr).setAttacked(true); diff --git a/apps/openmw/mwmechanics/activespells.cpp b/apps/openmw/mwmechanics/activespells.cpp index 95d455edb..e64a736c3 100644 --- a/apps/openmw/mwmechanics/activespells.cpp +++ b/apps/openmw/mwmechanics/activespells.cpp @@ -53,9 +53,9 @@ namespace MWMechanics { const MWWorld::TimeStamp& start = iter->second.mTimeStamp; - const std::vector& effects = iter->second.mEffects; + const std::vector& effects = iter->second.mEffects; - for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) + for (std::vector::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt) { int duration = effectIt->mDuration; MWWorld::TimeStamp end = start; @@ -63,7 +63,7 @@ namespace MWMechanics MWBase::Environment::get().getWorld()->getTimeScaleFactor()/(60*60); if (end>now) - mEffects.add(effectIt->mKey, MWMechanics::EffectParam(effectIt->mMagnitude)); + mEffects.add(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), MWMechanics::EffectParam(effectIt->mMagnitude)); } } } @@ -91,11 +91,11 @@ namespace MWMechanics double ActiveSpells::timeToExpire (const TIterator& iterator) const { - const std::vector& effects = iterator->second.mEffects; + const std::vector& effects = iterator->second.mEffects; int duration = 0; - for (std::vector::const_iterator iter (effects.begin()); + for (std::vector::const_iterator iter (effects.begin()); iter!=effects.end(); ++iter) { if (iter->mDuration > duration) @@ -132,7 +132,7 @@ namespace MWMechanics return mSpells; } - void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, + void ActiveSpells::addSpell(const std::string &id, bool stack, std::vector effects, const std::string &displayName, int casterActorId) { bool exists = false; @@ -168,7 +168,7 @@ namespace MWMechanics { float timeScale = MWBase::Environment::get().getWorld()->getTimeScaleFactor(); - for (std::vector::const_iterator effectIt = it->second.mEffects.begin(); + for (std::vector::const_iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end(); ++effectIt) { std::string name = it->second.mDisplayName; @@ -178,7 +178,7 @@ namespace MWMechanics float magnitude = effectIt->mMagnitude; if (magnitude) - visitor.visit(effectIt->mKey, name, it->second.mCasterActorId, magnitude, remainingTime); + visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime); } } } @@ -200,10 +200,10 @@ namespace MWMechanics { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { - for (std::vector::iterator effectIt = it->second.mEffects.begin(); + for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { - if (effectIt->mKey.mId == effectId) + if (effectIt->mEffectId == effectId) effectIt = it->second.mEffects.erase(effectIt); else ++effectIt; @@ -216,10 +216,10 @@ namespace MWMechanics { for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it) { - for (std::vector::iterator effectIt = it->second.mEffects.begin(); + for (std::vector::iterator effectIt = it->second.mEffects.begin(); effectIt != it->second.mEffects.end();) { - const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mKey.mId); + const ESM::MagicEffect* effect = MWBase::Environment::get().getWorld()->getStore().get().find(effectIt->mEffectId); if (effect->mData.mFlags & ESM::MagicEffect::CasterLinked && it->second.mCasterActorId == casterActorId) effectIt = it->second.mEffects.erase(effectIt); @@ -229,4 +229,41 @@ namespace MWMechanics } mSpellsChanged = true; } + + void ActiveSpells::clear() + { + mSpells.clear(); + mSpellsChanged = true; + } + + void ActiveSpells::writeState(ESM::ActiveSpells &state) const + { + for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) + { + // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp + ESM::ActiveSpells::ActiveSpellParams params; + params.mEffects = it->second.mEffects; + params.mCasterActorId = it->second.mCasterActorId; + params.mDisplayName = it->second.mDisplayName; + params.mTimeStamp = it->second.mTimeStamp.toEsm(); + + state.mSpells.insert (std::make_pair(it->first, params)); + } + } + + void ActiveSpells::readState(const ESM::ActiveSpells &state) + { + for (ESM::ActiveSpells::TContainer::const_iterator it = state.mSpells.begin(); it != state.mSpells.end(); ++it) + { + // Stupid copying of almost identical structures. ESM::TimeStamp <-> MWWorld::TimeStamp + ActiveSpellParams params; + params.mEffects = it->second.mEffects; + params.mCasterActorId = it->second.mCasterActorId; + params.mDisplayName = it->second.mDisplayName; + params.mTimeStamp = MWWorld::TimeStamp(it->second.mTimeStamp); + + mSpells.insert (std::make_pair(it->first, params)); + mSpellsChanged = true; + } + } } diff --git a/apps/openmw/mwmechanics/activespells.hpp b/apps/openmw/mwmechanics/activespells.hpp index 5f36cea2c..6c4c93128 100644 --- a/apps/openmw/mwmechanics/activespells.hpp +++ b/apps/openmw/mwmechanics/activespells.hpp @@ -10,6 +10,7 @@ #include "magiceffects.hpp" #include +#include namespace MWMechanics { @@ -21,19 +22,11 @@ namespace MWMechanics { public: - // Parameters of an effect concerning lasting effects. - // Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc. - // It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster. - struct Effect - { - float mMagnitude; - EffectKey mKey; - float mDuration; - }; + typedef ESM::ActiveEffect ActiveEffect; struct ActiveSpellParams { - std::vector mEffects; + std::vector mEffects; MWWorld::TimeStamp mTimeStamp; std::string mDisplayName; @@ -44,6 +37,9 @@ namespace MWMechanics typedef std::multimap TContainer; typedef TContainer::const_iterator TIterator; + void readState (const ESM::ActiveSpells& state); + void writeState (ESM::ActiveSpells& state) const; + private: mutable TContainer mSpells; @@ -77,7 +73,7 @@ namespace MWMechanics /// \param effects /// \param displayName Name for display in magic menu. /// - void addSpell (const std::string& id, bool stack, std::vector effects, + void addSpell (const std::string& id, bool stack, std::vector effects, const std::string& displayName, int casterActorId); /// Removes the active effects from this spell/potion/.. with \a id @@ -92,6 +88,9 @@ namespace MWMechanics /// Remove all effects with CASTER_LINKED flag that were cast by \a casterActorId void purge (int casterActorId); + /// Remove all spells + void clear(); + bool isSpellActive (std::string id) const; ///< case insensitive diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index e0db094d1..79a90c4d3 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -959,6 +959,7 @@ namespace MWMechanics // Reset magic effects and recalculate derived effects // One case where we need this is to make sure bound items are removed upon death stats.setMagicEffects(MWMechanics::MagicEffects()); + stats.getActiveSpells().clear(); calculateCreatureStatModifiers(iter->first, 0); MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, false); diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index 80f81ffd1..57618e6e9 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -496,7 +496,7 @@ namespace MWMechanics state.mBlock = mBlock; state.mMovementFlags = mMovementFlags; state.mAttackStrength = mAttackStrength; - state.mFallHeight = mFallHeight; + state.mFallHeight = mFallHeight; // TODO: vertical velocity (move from PhysicActor to CreatureStats?) state.mLastHitObject = mLastHitObject; state.mRecalcDynamicStats = mRecalcDynamicStats; state.mDrawState = mDrawState; @@ -504,6 +504,7 @@ namespace MWMechanics state.mActorId = mActorId; mSpells.writeState(state.mSpells); + mActiveSpells.writeState(state.mActiveSpells); } void CreatureStats::readState (const ESM::CreatureStats& state) @@ -542,6 +543,7 @@ namespace MWMechanics mActorId = state.mActorId; mSpells.readState(state.mSpells); + mActiveSpells.readState(state.mActiveSpells); } // Relates to NPC gold reset delay diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index cb02d4655..553674dd3 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -222,7 +222,7 @@ namespace MWMechanics } ESM::EffectList reflectedEffects; - std::vector appliedLastingEffects; + std::vector appliedLastingEffects; bool firstAppliedEffect = true; bool anyHarmfulEffect = false; @@ -332,8 +332,9 @@ namespace MWMechanics bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); if (target.getClass().isActor() && hasDuration) { - ActiveSpells::Effect effect; - effect.mKey = MWMechanics::EffectKey(*effectIt); + ActiveSpells::ActiveEffect effect; + effect.mEffectId = effectIt->mEffectID; + effect.mArg = MWMechanics::EffectKey(*effectIt).mArg; effect.mDuration = effectIt->mDuration; effect.mMagnitude = magnitude; @@ -345,8 +346,8 @@ namespace MWMechanics { if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) { - std::vector effects; - ActiveSpells::Effect effect_ = effect; + std::vector effects; + ActiveSpells::ActiveEffect effect_ = effect; effect_.mMagnitude *= -1; effects.push_back(effect_); // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies @@ -392,6 +393,7 @@ namespace MWMechanics else castStatic = MWBase::Environment::get().getWorld()->getStore().get().find ("VFX_DefaultHit"); + // TODO: VFX are no longer active after saving/reloading the game bool loop = magicEffect->mData.mFlags & ESM::MagicEffect::ContinuousVfx; // Note: in case of non actor, a free effect should be fine as well MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(target); diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 533288048..90570b260 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -45,7 +45,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats creaturestats weatherstate quickkeys fogstate spellstate + npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells ) add_component_dir (misc diff --git a/components/esm/activespells.cpp b/components/esm/activespells.cpp new file mode 100644 index 000000000..4f51a619e --- /dev/null +++ b/components/esm/activespells.cpp @@ -0,0 +1,56 @@ +#include "activespells.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + + void ActiveSpells::save(ESMWriter &esm) const + { + for (TContainer::const_iterator it = mSpells.begin(); it != mSpells.end(); ++it) + { + esm.writeHNString ("ID__", it->first); + + const ActiveSpellParams& params = it->second; + + esm.writeHNT ("CAST", params.mCasterActorId); + esm.writeHNString ("DISP", params.mDisplayName); + esm.writeHNT ("TIME", params.mTimeStamp); + + for (std::vector::const_iterator effectIt = params.mEffects.begin(); effectIt != params.mEffects.end(); ++effectIt) + { + esm.writeHNT ("MGEF", effectIt->mEffectId); + if (effectIt->mArg != -1) + esm.writeHNT ("ARG_", effectIt->mArg); + esm.writeHNT ("MAGN", effectIt->mMagnitude); + esm.writeHNT ("DURA", effectIt->mDuration); + } + } + } + + void ActiveSpells::load(ESMReader &esm) + { + while (esm.isNextSub("ID__")) + { + std::string spellId = esm.getHString(); + + ActiveSpellParams params; + esm.getHNT (params.mCasterActorId, "CAST"); + params.mDisplayName = esm.getHNString ("DISP"); + esm.getHNT (params.mTimeStamp, "TIME"); + + while (esm.isNextSub("MGEF")) + { + ActiveEffect effect; + esm.getHT(effect.mEffectId); + effect.mArg = -1; + esm.getHNOT(effect.mArg, "ARG_"); + esm.getHNT (effect.mMagnitude, "MAGN"); + esm.getHNT (effect.mDuration, "DURA"); + params.mEffects.push_back(effect); + } + mSpells.insert(std::make_pair(spellId, params)); + } + } +} diff --git a/components/esm/activespells.hpp b/components/esm/activespells.hpp new file mode 100644 index 000000000..d9e9a8c63 --- /dev/null +++ b/components/esm/activespells.hpp @@ -0,0 +1,45 @@ +#ifndef OPENMW_ESM_ACTIVESPELLS_H +#define OPENMW_ESM_ACTIVESPELLS_H + +#include "effectlist.hpp" +#include "defs.hpp" + +#include +#include + +namespace ESM +{ + class ESMReader; + class ESMWriter; + + // Parameters of an effect concerning lasting effects. + // Note we are not using ENAMstruct since the magnitude may be modified by magic resistance, etc. + // It could also be a negative magnitude, in case of inversing an effect, e.g. Absorb spell causes damage on target, but heals the caster. + struct ActiveEffect + { + int mEffectId; + float mMagnitude; + int mArg; // skill or attribute + float mDuration; + }; + + // format 0, saved games only + struct ActiveSpells + { + struct ActiveSpellParams + { + std::vector mEffects; + ESM::TimeStamp mTimeStamp; + std::string mDisplayName; + int mCasterActorId; + }; + + typedef std::multimap TContainer; + TContainer mSpells; + + void load (ESMReader &esm); + void save (ESMWriter &esm) const; + }; +} + +#endif diff --git a/components/esm/creaturestats.cpp b/components/esm/creaturestats.cpp index e512ca5f8..66d848124 100644 --- a/components/esm/creaturestats.cpp +++ b/components/esm/creaturestats.cpp @@ -1,4 +1,3 @@ - #include "creaturestats.hpp" void ESM::CreatureStats::load (ESMReader &esm) @@ -76,6 +75,7 @@ void ESM::CreatureStats::load (ESMReader &esm) esm.getHNOT (mActorId, "ACID"); mSpells.load(esm); + mActiveSpells.load(esm); } void ESM::CreatureStats::save (ESMWriter &esm) const @@ -153,4 +153,5 @@ void ESM::CreatureStats::save (ESMWriter &esm) const esm.writeHNT ("ACID", mActorId); mSpells.save(esm); + mActiveSpells.save(esm); } diff --git a/components/esm/creaturestats.hpp b/components/esm/creaturestats.hpp index 4c4c44b3f..7814d937a 100644 --- a/components/esm/creaturestats.hpp +++ b/components/esm/creaturestats.hpp @@ -10,6 +10,7 @@ #include "defs.hpp" #include "spellstate.hpp" +#include "activespells.hpp" namespace ESM { @@ -49,6 +50,7 @@ namespace ESM int mLevel; SpellState mSpells; + ActiveSpells mActiveSpells; void load (ESMReader &esm); void save (ESMWriter &esm) const; From 471bbd0021789c3882fc95a5069ed7ed830fcaea Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 00:23:53 +0200 Subject: [PATCH 16/29] Savegame: store levelled creature state and move to actorIds (Closes #1332) --- apps/openmw/mwclass/creaturelevlist.cpp | 59 ++++++++++++++++++------- apps/openmw/mwclass/creaturelevlist.hpp | 8 ++++ apps/openmw/mwworld/cellstore.cpp | 5 ++- components/CMakeLists.txt | 2 +- components/esm/creaturelevliststate.cpp | 25 +++++++++++ components/esm/creaturelevliststate.hpp | 19 ++++++++ 6 files changed, 100 insertions(+), 18 deletions(-) create mode 100644 components/esm/creaturelevliststate.cpp create mode 100644 components/esm/creaturelevliststate.hpp diff --git a/apps/openmw/mwclass/creaturelevlist.cpp b/apps/openmw/mwclass/creaturelevlist.cpp index 732038b2f..be01b848a 100644 --- a/apps/openmw/mwclass/creaturelevlist.cpp +++ b/apps/openmw/mwclass/creaturelevlist.cpp @@ -2,6 +2,7 @@ #include "creaturelevlist.hpp" #include +#include #include "../mwmechanics/levelledlist.hpp" @@ -11,7 +12,9 @@ namespace { struct CreatureLevListCustomData : public MWWorld::CustomData { - // TODO: save the creature we spawned here + // actorId of the creature we spawned + int mSpawnActorId; + virtual MWWorld::CustomData *clone() const; }; @@ -38,6 +41,25 @@ namespace MWClass void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const { ensureCustomData(ptr); + + CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + if (customData.mSpawnActorId != -1) + return; // TODO: handle respawning + + + MWWorld::LiveCellRef *ref = + ptr.get(); + + std::string id = MWMechanics::getLevelledItem(ref->mBase, true); + + if (!id.empty()) + { + const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); + MWWorld::ManualRef ref(store, id); + ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; + MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); + customData.mSpawnActorId = placed.getClass().getCreatureStats(placed).getActorId(); + } } void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const @@ -45,22 +67,29 @@ namespace MWClass if (!ptr.getRefData().getCustomData()) { std::auto_ptr data (new CreatureLevListCustomData); - - MWWorld::LiveCellRef *ref = - ptr.get(); - - std::string id = MWMechanics::getLevelledItem(ref->mBase, true); - - if (!id.empty()) - { - const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); - MWWorld::ManualRef ref(store, id); - ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos; - // TODO: hold on to this for respawn purposes later - MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), ptr.getCell() , ptr.getCellRef().mPos); - } + data->mSpawnActorId = -1; ptr.getRefData().setCustomData(data.release()); } } + + void CreatureLevList::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const + { + const ESM::CreatureLevListState& state2 = dynamic_cast (state); + + ensureCustomData(ptr); + CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + customData.mSpawnActorId = state2.mSpawnActorId; + } + + void CreatureLevList::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const + { + ESM::CreatureLevListState& state2 = dynamic_cast (state); + + ensureCustomData(ptr); + CreatureLevListCustomData& customData = dynamic_cast (*ptr.getRefData().getCustomData()); + state2.mSpawnActorId = customData.mSpawnActorId; + } } diff --git a/apps/openmw/mwclass/creaturelevlist.hpp b/apps/openmw/mwclass/creaturelevlist.hpp index d2c02043e..b67fb5523 100644 --- a/apps/openmw/mwclass/creaturelevlist.hpp +++ b/apps/openmw/mwclass/creaturelevlist.hpp @@ -19,6 +19,14 @@ namespace MWClass virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const; ///< Add reference into a cell for rendering + + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. }; } diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 67ab44b77..3f94a77c4 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -12,6 +12,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -592,7 +593,7 @@ namespace MWWorld writeReferenceCollection (writer, mCreatures); writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); - writeReferenceCollection (writer, mCreatureLists); + writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); writeReferenceCollection (writer, mLights); writeReferenceCollection (writer, mLockpicks); @@ -668,7 +669,7 @@ namespace MWWorld case ESM::REC_LEVC: - readReferenceCollection (reader, mCreatureLists, contentFileMap); + readReferenceCollection (reader, mCreatureLists, contentFileMap); break; case ESM::REC_LEVI: diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 90570b260..0f2ef1301 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -45,7 +45,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells + npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate ) add_component_dir (misc diff --git a/components/esm/creaturelevliststate.cpp b/components/esm/creaturelevliststate.cpp new file mode 100644 index 000000000..164dae96e --- /dev/null +++ b/components/esm/creaturelevliststate.cpp @@ -0,0 +1,25 @@ +#include "creaturelevliststate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + + void CreatureLevListState::load(ESMReader &esm) + { + ObjectState::load(esm); + + mSpawnActorId = -1; + esm.getHNOT (mSpawnActorId, "SPAW"); + } + + void CreatureLevListState::save(ESMWriter &esm, bool inInventory) const + { + ObjectState::save(esm, inInventory); + + if (mSpawnActorId != -1) + esm.writeHNT ("SPAW", mSpawnActorId); + } + +} diff --git a/components/esm/creaturelevliststate.hpp b/components/esm/creaturelevliststate.hpp new file mode 100644 index 000000000..99b5a7fa2 --- /dev/null +++ b/components/esm/creaturelevliststate.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_ESM_CREATURELEVLISTSTATE_H +#define OPENMW_ESM_CREATURELEVLISTSTATE_H + +#include "objectstate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct CreatureLevListState : public ObjectState + { + int mSpawnActorId; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif From 37b9d2fb0cac2960466554021a8f94f0942471cc Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 01:53:52 +0200 Subject: [PATCH 17/29] Fix a wrong use of reference that causes potential crash --- apps/openmw/mwmechanics/aiavoiddoor.hpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/apps/openmw/mwmechanics/aiavoiddoor.hpp b/apps/openmw/mwmechanics/aiavoiddoor.hpp index 85bb09fc8..c1b326198 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.hpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.hpp @@ -11,9 +11,9 @@ namespace MWMechanics { /// \brief AiPackage to have an actor avoid an opening door /** The AI will retreat from the door until it has finished opening, walked far away from it, or one second has passed, in an attempt to avoid it - **/ - class AiAvoidDoor : public AiPackage - { + **/ + class AiAvoidDoor : public AiPackage + { public: /// Avoid door until the door is fully open AiAvoidDoor(const MWWorld::Ptr& doorPtr); @@ -29,10 +29,10 @@ namespace MWMechanics private: float mDuration; - const MWWorld::Ptr& mDoorPtr; + MWWorld::Ptr mDoorPtr; ESM::Position mLastPos; - float mAdjAngle; - }; -} -#endif + float mAdjAngle; + }; +} +#endif From a76e391ad038c99b9e8b72c08949931c9dd3c395 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 01:58:44 +0200 Subject: [PATCH 18/29] Savegame: store door movement state (Closes #747) --- apps/openmw/mwbase/world.hpp | 10 +--- apps/openmw/mwclass/door.cpp | 78 ++++++++++++++++++++++++- apps/openmw/mwclass/door.hpp | 17 +++++- apps/openmw/mwmechanics/aiavoiddoor.cpp | 2 +- apps/openmw/mwworld/cellstore.cpp | 5 +- apps/openmw/mwworld/class.cpp | 10 ++++ apps/openmw/mwworld/class.hpp | 5 ++ apps/openmw/mwworld/worldimp.cpp | 57 +++++++++++------- apps/openmw/mwworld/worldimp.hpp | 10 ++-- components/CMakeLists.txt | 2 +- components/esm/doorstate.cpp | 25 ++++++++ components/esm/doorstate.hpp | 19 ++++++ 12 files changed, 199 insertions(+), 41 deletions(-) create mode 100644 components/esm/doorstate.cpp create mode 100644 components/esm/doorstate.hpp diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 5c8a2d2dd..0459d5341 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -393,14 +393,10 @@ namespace MWBase virtual void setupPlayer() = 0; virtual void renderPlayer() = 0; - /// if activated, should this door be opened or closed? - virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door) = 0; - - /// activate (open or close) an non-teleport door + /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door) = 0; - - /// Is door currently opening/closing? - virtual bool getIsMovingDoor(const MWWorld::Ptr& door) = 0; + /// open or close a non-teleport door as specified + virtual void activateDoor(const MWWorld::Ptr& door, bool open) = 0; virtual bool getPlayerStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object) = 0; ///< @return true if any actor is standing on \a object diff --git a/apps/openmw/mwclass/door.cpp b/apps/openmw/mwclass/door.cpp index 984e21e72..06f0619ce 100644 --- a/apps/openmw/mwclass/door.cpp +++ b/apps/openmw/mwclass/door.cpp @@ -2,6 +2,7 @@ #include "door.hpp" #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -17,12 +18,28 @@ #include "../mwworld/physicssystem.hpp" #include "../mwworld/inventorystore.hpp" #include "../mwworld/actiontrap.hpp" +#include "../mwworld/customdata.hpp" #include "../mwgui/tooltips.hpp" #include "../mwrender/objects.hpp" #include "../mwrender/renderinginterface.hpp" +namespace +{ + struct DoorCustomData : public MWWorld::CustomData + { + int mDoorState; // 0 = nothing, 1 = opening, 2 = closing + + virtual MWWorld::CustomData *clone() const; + }; + + MWWorld::CustomData *DoorCustomData::clone() const + { + return new DoorCustomData (*this); + } +} + namespace MWClass { void Door::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const @@ -38,6 +55,14 @@ namespace MWClass const std::string model = getModel(ptr); if(!model.empty()) physics.addObject(ptr); + + // Resume the door's opening/closing animation if it wasn't finished + ensureCustomData(ptr); + const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + if (customData.mDoorState > 0) + { + MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); + } } std::string Door::getModel(const MWWorld::Ptr &ptr) const @@ -125,7 +150,14 @@ namespace MWClass { // animated door boost::shared_ptr action(new MWWorld::ActionDoor(ptr)); - if (MWBase::Environment::get().getWorld()->getOpenOrCloseDoor(ptr)) + int doorstate = getDoorState(ptr); + bool opening = true; + if (doorstate == 1) + opening = false; + if (doorstate == 0 && ptr.getRefData().getLocalRotation().rot[2] != 0) + opening = false; + + if (opening) { MWBase::Environment::get().getSoundManager()->fadeOutSound3D(ptr, closeSound, 0.5); @@ -262,4 +294,48 @@ namespace MWClass return MWWorld::Ptr(&cell.get().insert(*ref), &cell); } + + void Door::ensureCustomData(const MWWorld::Ptr &ptr) const + { + if (!ptr.getRefData().getCustomData()) + { + std::auto_ptr data(new DoorCustomData); + + data->mDoorState = 0; + ptr.getRefData().setCustomData(data.release()); + } + } + + int Door::getDoorState (const MWWorld::Ptr &ptr) const + { + ensureCustomData(ptr); + const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + return customData.mDoorState; + } + + void Door::setDoorState (const MWWorld::Ptr &ptr, int state) const + { + ensureCustomData(ptr); + DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + customData.mDoorState = state; + } + + void Door::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const + { + ensureCustomData(ptr); + DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + + const ESM::DoorState& state2 = dynamic_cast(state); + customData.mDoorState = state2.mDoorState; + } + + void Door::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) const + { + ensureCustomData(ptr); + const DoorCustomData& customData = dynamic_cast(*ptr.getRefData().getCustomData()); + + ESM::DoorState& state2 = dynamic_cast(state); + state2.mDoorState = customData.mDoorState; + } + } diff --git a/apps/openmw/mwclass/door.hpp b/apps/openmw/mwclass/door.hpp index bddc46728..12b360aa8 100644 --- a/apps/openmw/mwclass/door.hpp +++ b/apps/openmw/mwclass/door.hpp @@ -9,6 +9,8 @@ namespace MWClass { class Door : public MWWorld::Class { + void ensureCustomData (const MWWorld::Ptr& ptr) const; + virtual MWWorld::Ptr copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; @@ -48,7 +50,20 @@ namespace MWClass static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; - private: + + /// 0 = nothing, 1 = opening, 2 = closing + virtual int getDoorState (const MWWorld::Ptr &ptr) const; + /// This does not actually cause the door to move. Use World::activateDoor instead. + virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; + + + virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) + const; + ///< Read additional state from \a state into \a ptr. + + virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state) + const; + ///< Write additional state from \a ptr into \a state. }; } diff --git a/apps/openmw/mwmechanics/aiavoiddoor.cpp b/apps/openmw/mwmechanics/aiavoiddoor.cpp index a206d27ca..44eacb0d1 100644 --- a/apps/openmw/mwmechanics/aiavoiddoor.cpp +++ b/apps/openmw/mwmechanics/aiavoiddoor.cpp @@ -45,7 +45,7 @@ bool MWMechanics::AiAvoidDoor::execute (const MWWorld::Ptr& actor,float duration return true; // We have tried backing up for more than one second, we've probably cleared it } - if(!MWBase::Environment::get().getWorld()->getIsMovingDoor(mDoorPtr)) + if (!mDoorPtr.getClass().getDoorState(mDoorPtr)) return true; //Door is no longer opening ESM::Position tPos = mDoorPtr.getRefData().getPosition(); //Position of the door diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index 3f94a77c4..a5f166374 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -13,6 +13,7 @@ #include #include #include +#include #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" @@ -591,7 +592,7 @@ namespace MWWorld writeReferenceCollection (writer, mClothes); writeReferenceCollection (writer, mContainers); writeReferenceCollection (writer, mCreatures); - writeReferenceCollection (writer, mDoors); + writeReferenceCollection (writer, mDoors); writeReferenceCollection (writer, mIngreds); writeReferenceCollection (writer, mCreatureLists); writeReferenceCollection (writer, mItemLists); @@ -659,7 +660,7 @@ namespace MWWorld case ESM::REC_DOOR: - readReferenceCollection (reader, mDoors, contentFileMap); + readReferenceCollection (reader, mDoors, contentFileMap); break; case ESM::REC_INGR: diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index f20c5f6d2..951ba7865 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -406,4 +406,14 @@ namespace MWWorld { return false; } + + int Class::getDoorState (const MWWorld::Ptr &ptr) const + { + throw std::runtime_error("this is not a door"); + } + + void Class::setDoorState (const MWWorld::Ptr &ptr, int state) const + { + throw std::runtime_error("this is not a door"); + } } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index 27b842348..057bc906e 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -336,6 +336,11 @@ namespace MWWorld virtual int getBaseGold(const MWWorld::Ptr& ptr) const; virtual bool isClass(const MWWorld::Ptr& ptr, const std::string &className) const; + + /// 0 = nothing, 1 = opening, 2 = closing + virtual int getDoorState (const MWWorld::Ptr &ptr) const; + /// This does not actually cause the door to move. Use World::activateDoor instead. + virtual void setDoorState (const MWWorld::Ptr &ptr, int state) const; }; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 4ba3e0009..97b0767fa 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1202,36 +1202,48 @@ namespace MWWorld while (it != mDoorStates.end()) { if (!mWorldScene->isCellActive(*it->first.getCell()) || !it->first.getRefData().getBaseNode()) + { + // The door is no longer in an active cell, or it was disabled. + // Erase from mDoorStates, since we no longer need to move it. + // Once we load the door's cell again (or re-enable the door), Door::insertObject will reinsert to mDoorStates. mDoorStates.erase(it++); + } else { float oldRot = Ogre::Radian(it->first.getRefData().getLocalRotation().rot[2]).valueDegrees(); float diff = duration * 90; - float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second ? 1 : -1)), 90.f); + float targetRot = std::min(std::max(0.f, oldRot + diff * (it->second == 1 ? 1 : -1)), 90.f); localRotateObject(it->first, 0, 0, targetRot); + bool reached = (targetRot == 90.f && it->second) || targetRot == 0.f; + /// \todo should use convexSweepTest here std::vector collisions = mPhysics->getCollisions(it->first); for (std::vector::iterator cit = collisions.begin(); cit != collisions.end(); ++cit) { MWWorld::Ptr ptr = getPtrViaHandle(*cit); - if (MWWorld::Class::get(ptr).isActor()) + if (ptr.getClass().isActor()) { // Collided with actor, ask actor to try to avoid door if(ptr != MWBase::Environment::get().getWorld()->getPlayerPtr() ) { - MWMechanics::AiSequence& seq = MWWorld::Class::get(ptr).getCreatureStats(ptr).getAiSequence(); + MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence(); if(seq.getTypeId() != MWMechanics::AiPackage::TypeIdAvoidDoor) //Only add it once seq.stack(MWMechanics::AiAvoidDoor(it->first),ptr); } // we need to undo the rotation localRotateObject(it->first, 0, 0, oldRot); + reached = false; //break; //Removed in case multiple actors are touching } } - if ((targetRot == 90.f && it->second) || targetRot == 0.f) + if (reached) + { + // Mark as non-moving + it->first.getClass().setDoorState(it->first, 0); mDoorStates.erase(it++); + } else ++it; } @@ -1849,31 +1861,32 @@ namespace MWWorld void World::activateDoor(const MWWorld::Ptr& door) { - if (mDoorStates.find(door) != mDoorStates.end()) - { - // if currently opening, then close, if closing, then open - mDoorStates[door] = !mDoorStates[door]; - } - else + int state = door.getClass().getDoorState(door); + switch (state) { + case 0: if (door.getRefData().getLocalRotation().rot[2] == 0) - mDoorStates[door] = 1; // open + state = 1; // if closed, then open else - mDoorStates[door] = 0; // close + state = 2; // if open, then close + break; + case 2: + state = 1; // if closing, then open + break; + case 1: + default: + state = 2; // if opening, then close + break; } + door.getClass().setDoorState(door, state); + mDoorStates[door] = state; } - bool World::getOpenOrCloseDoor(const Ptr &door) + void World::activateDoor(const Ptr &door, bool open) { - if (mDoorStates.find(door) != mDoorStates.end()) - return !mDoorStates[door]; // if currently opening or closing, then do the opposite - return door.getRefData().getLocalRotation().rot[2] == 0; - } - - bool World::getIsMovingDoor(const Ptr& door) - { - bool result = mDoorStates.find(door) != mDoorStates.end(); - return result; + int state = open ? 1 : 2; + door.getClass().setDoorState(door, state); + mDoorStates[door] = state; } bool World::getPlayerStandingOn (const MWWorld::Ptr& object) diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index eee69e81c..7b85c3ed8 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -88,7 +88,7 @@ namespace MWWorld float mFacedDistance; std::map mDoorStates; - ///< only holds doors that are currently moving. 0 means closing, 1 opening + ///< only holds doors that are currently moving. 1 = opening, 2 = closing struct MagicBoltState { @@ -496,13 +496,11 @@ namespace MWWorld virtual void setupPlayer(); virtual void renderPlayer(); - /// if activated, should this door be opened or closed? - virtual bool getOpenOrCloseDoor(const MWWorld::Ptr& door); - - /// activate (open or close) an non-teleport door + /// open or close a non-teleport door (depending on current state) virtual void activateDoor(const MWWorld::Ptr& door); - virtual bool getIsMovingDoor(const MWWorld::Ptr& door); + /// open or close a non-teleport door as specified + virtual void activateDoor(const MWWorld::Ptr& door, bool open); virtual bool getPlayerStandingOn (const MWWorld::Ptr& object); ///< @return true if the player is standing on \a object virtual bool getActorStandingOn (const MWWorld::Ptr& object); ///< @return true if any actor is standing on \a object diff --git a/components/CMakeLists.txt b/components/CMakeLists.txt index 0f2ef1301..d7bdaf36c 100644 --- a/components/CMakeLists.txt +++ b/components/CMakeLists.txt @@ -45,7 +45,7 @@ add_component_dir (esm loadnpc loadpgrd loadrace loadregn loadscpt loadskil loadsndg loadsoun loadspel loadsscr loadstat loadweap records aipackage effectlist spelllist variant variantimp loadtes3 cellref filter savedgame journalentry queststate locals globalscript player objectstate cellid cellstate globalmap lightstate inventorystate containerstate npcstate creaturestate dialoguestate statstate - npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate + npcstats creaturestats weatherstate quickkeys fogstate spellstate activespells creaturelevliststate doorstate ) add_component_dir (misc diff --git a/components/esm/doorstate.cpp b/components/esm/doorstate.cpp new file mode 100644 index 000000000..0487be7a4 --- /dev/null +++ b/components/esm/doorstate.cpp @@ -0,0 +1,25 @@ +#include "doorstate.hpp" + +#include "esmreader.hpp" +#include "esmwriter.hpp" + +namespace ESM +{ + + void DoorState::load(ESMReader &esm) + { + ObjectState::load(esm); + + mDoorState = 0; + esm.getHNOT (mDoorState, "ANIM"); + } + + void DoorState::save(ESMWriter &esm, bool inInventory) const + { + ObjectState::save(esm, inInventory); + + if (mDoorState != 0) + esm.writeHNT ("ANIM", mDoorState); + } + +} diff --git a/components/esm/doorstate.hpp b/components/esm/doorstate.hpp new file mode 100644 index 000000000..0df30afb0 --- /dev/null +++ b/components/esm/doorstate.hpp @@ -0,0 +1,19 @@ +#ifndef OPENMW_ESM_DOORSTATE_H +#define OPENMW_ESM_DOORSTATE_H + +#include "objectstate.hpp" + +namespace ESM +{ + // format 0, saved games only + + struct DoorState : public ObjectState + { + int mDoorState; + + virtual void load (ESMReader &esm); + virtual void save (ESMWriter &esm, bool inInventory = false) const; + }; +} + +#endif From a609dc56749831b2011f80c56cd8a0d319b89207 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 02:03:42 +0200 Subject: [PATCH 19/29] Fix exception when getting hit by a trap --- apps/openmw/mwmechanics/spellcasting.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 553674dd3..3f6cc4103 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -410,11 +410,16 @@ namespace MWMechanics inflict(caster, target, reflectedEffects, range, true); if (!appliedLastingEffects.empty()) + { + int casterActorId = -1; + if (caster.getClass().isActor()) + casterActorId = caster.getClass().getCreatureStats(caster).getActorId(); target.getClass().getCreatureStats(target).getActiveSpells().addSpell(mId, mStack, appliedLastingEffects, - mSourceName, caster.getClass().getCreatureStats(caster).getActorId()); + mSourceName, casterActorId); + } // Notify the target actor they've been hit - if (anyHarmfulEffect && target.getClass().isActor() && target != caster) + if (anyHarmfulEffect && target.getClass().isActor() && target != caster && caster.getClass().isActor()) target.getClass().onHit(target, 0.f, true, MWWorld::Ptr(), caster, true); } From f2c193ce3d7bcb868249322c99fece14c729ee32 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 02:36:30 +0200 Subject: [PATCH 20/29] Fix searching the player's actorId --- apps/openmw/mwworld/worldimp.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index 97b0767fa..2b1cbe4c7 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -563,6 +563,10 @@ namespace MWWorld Ptr World::searchPtrViaActorId (int actorId) { + // The player is not registered in any CellStore so must be checked manually + if (actorId == getPlayerPtr().getClass().getCreatureStats(getPlayerPtr()).getActorId()) + return getPlayerPtr(); + // Now search cells return mWorldScene->searchPtrViaActorId (actorId); } From 2f13a17a39a1c11d31b9e45dd7c37f6a993564f5 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 02:37:20 +0200 Subject: [PATCH 21/29] Add some more safety checks to spellcasting --- .../mwmechanics/mechanicsmanagerimp.cpp | 2 +- apps/openmw/mwmechanics/spellcasting.cpp | 36 +++++++++++-------- apps/openmw/mwmechanics/spellcasting.hpp | 2 ++ 3 files changed, 24 insertions(+), 16 deletions(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5c2ee30e1..41374c1a3 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -29,7 +29,7 @@ namespace const std::string& faction = item.getCellRef().mFaction; bool isFactionOwned = false; - if (!faction.empty()) + if (!faction.empty() && ptr.getClass().isNpc()) { const std::map& factions = ptr.getClass().getNpcStats(ptr).getFactionRanks(); if (factions.find(Misc::StringUtils::lowerCase(faction)) == factions.end()) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 3f6cc4103..0843e3e54 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -140,7 +140,7 @@ namespace MWMechanics float x = (willpower + 0.1 * luck) * stats.getFatigueTerm(); // This makes spells that are easy to cast harder to resist and vice versa - if (spell != NULL && caster.getClass().isActor()) + if (spell != NULL && !caster.isEmpty() && caster.getClass().isActor()) { float castChance = getSpellSuccessChance(spell, caster); if (castChance > 0) @@ -226,6 +226,8 @@ namespace MWMechanics bool firstAppliedEffect = true; bool anyHarmfulEffect = false; + bool castByPlayer = (!caster.isEmpty() && caster.getRefData().getHandle() == "player"); + for (std::vector::const_iterator effectIt (effects.mList.begin()); effectIt!=effects.mList.end(); ++effectIt) { @@ -238,7 +240,7 @@ namespace MWMechanics if (!MWBase::Environment::get().getWorld()->isLevitationEnabled() && effectIt->mEffectID == ESM::MagicEffect::Levitate) { - if (caster.getRefData().getHandle() == "player") + if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sLevitateDisabled}"); continue; } @@ -249,13 +251,13 @@ namespace MWMechanics effectIt->mEffectID == ESM::MagicEffect::Mark || effectIt->mEffectID == ESM::MagicEffect::Recall)) { - if (caster.getRefData().getHandle() == "player") + if (castByPlayer) MWBase::Environment::get().getWindowManager()->messageBox("#{sTeleportDisabled}"); continue; } // If player is healing someone, show the target's HP bar - if (caster.getRefData().getHandle() == "player" && target != caster + if (castByPlayer && target != caster && effectIt->mEffectID == ESM::MagicEffect::RestoreHealth && target.getClass().isActor()) MWBase::Environment::get().getWindowManager()->setEnemy(target); @@ -266,7 +268,7 @@ namespace MWMechanics anyHarmfulEffect = true; // If player is attempting to cast a harmful spell, show the target's HP bar - if (caster.getRefData().getHandle() == "player" && target != caster) + if (castByPlayer && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); // Try absorbing if it's a spell @@ -342,17 +344,20 @@ namespace MWMechanics // For absorb effects, also apply the effect to the caster - but with a negative // magnitude, since we're transfering stats from the target to the caster - for (int i=0; i<5; ++i) + if (!caster.isEmpty() && caster.getClass().isActor()) { - if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) + for (int i=0; i<5; ++i) { - std::vector effects; - ActiveSpells::ActiveEffect effect_ = effect; - effect_.mMagnitude *= -1; - effects.push_back(effect_); - // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies - caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, - effects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); + if (effectIt->mEffectID == ESM::MagicEffect::AbsorbAttribute+i) + { + std::vector effects; + ActiveSpells::ActiveEffect effect_ = effect; + effect_.mMagnitude *= -1; + effects.push_back(effect_); + // Also make sure to set casterActorId = target, so that the effect on the caster gets purged when the target dies + caster.getClass().getCreatureStats(caster).getActiveSpells().addSpell("", true, + effects, mSourceName, target.getClass().getCreatureStats(target).getActorId()); + } } } } @@ -441,7 +446,8 @@ namespace MWMechanics if (target.getCellRef().mLockLevel > 0) { MWBase::Environment::get().getSoundManager()->playSound3D(target, "Open Lock", 1.f, 1.f); - MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); + if (!caster.isEmpty() && caster.getClass().isActor()) + MWBase::Environment::get().getMechanicsManager()->objectOpened(caster, target); } target.getCellRef().mLockLevel = -abs(target.getCellRef().mLockLevel); //unlocks the door } diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index a930442fb..dce4b792e 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -62,9 +62,11 @@ namespace MWMechanics bool cast (const std::string& id); /// @note \a target can be any type of object, not just actors. + /// @note \a caster can be any type of object, or even an empty object. void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false, bool exploded=false); + /// @note \a caster can be any type of object, or even an empty object. void applyInstantEffect (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const MWMechanics::EffectKey& effect, float magnitude); }; From 2e9985c1a371b0e5970f3791cc6d8298a624380a Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 03:01:48 +0200 Subject: [PATCH 22/29] Change all AI packages (except AiActivate) to use ActorIds More robust in case the target changes cell or there are multiple targets with the same RefId --- apps/openmw/mwmechanics/actors.cpp | 6 +++--- apps/openmw/mwmechanics/aicombat.cpp | 29 +++++++++++++++----------- apps/openmw/mwmechanics/aicombat.hpp | 6 +++--- apps/openmw/mwmechanics/aiescort.cpp | 21 +++++++++++++------ apps/openmw/mwmechanics/aiescort.hpp | 6 +++--- apps/openmw/mwmechanics/aifollow.cpp | 20 +++++++++++------- apps/openmw/mwmechanics/aifollow.hpp | 22 +++++++++---------- apps/openmw/mwmechanics/aipursue.cpp | 11 +++++----- apps/openmw/mwmechanics/aipursue.hpp | 6 +++--- apps/openmw/mwmechanics/aisequence.cpp | 6 ++++-- apps/openmw/mwscript/aiextensions.cpp | 14 ++++++++----- 11 files changed, 86 insertions(+), 61 deletions(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 79a90c4d3..af6cac12f 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -540,7 +540,7 @@ namespace MWMechanics MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr()); // Make the summoned creature follow its master and help in fights - AiFollow package(ptr.getRefData().getHandle()); + AiFollow package(ptr); summonedCreatureStats.getAiSequence().stack(package, ref.getPtr()); int creatureActorId = summonedCreatureStats.getActorId(); @@ -754,7 +754,7 @@ namespace MWMechanics && MWBase::Environment::get().getWorld()->getLOS(ptr, player) && MWBase::Environment::get().getMechanicsManager()->awarenessCheck(player, ptr)) { - creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); + creatureStats.getAiSequence().stack(AiPursue(player), ptr); creatureStats.setAlarmed(true); npcStats.setCrimeId(MWBase::Environment::get().getWorld()->getPlayer().getNewCrimeId()); } @@ -783,7 +783,7 @@ namespace MWMechanics else if (!creatureStats.isHostile()) { if (ptr.getClass().isClass(ptr, "Guard")) - creatureStats.getAiSequence().stack(AiPursue(player.getClass().getId(player)), ptr); + creatureStats.getAiSequence().stack(AiPursue(player), ptr); else { MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, player); diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 8464cc419..72cb8f574 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -81,7 +81,7 @@ namespace MWMechanics // NOTE: MIN_DIST_TO_DOOR_SQUARED is defined in obstacle.hpp AiCombat::AiCombat(const MWWorld::Ptr& actor) : - mTarget(actor), + mTargetActorId(actor.getClass().getCreatureStats(actor).getActorId()), mTimerAttack(0), mTimerReact(0), mTimerCombatMove(0), @@ -153,7 +153,9 @@ namespace MWMechanics || actor.getClass().getCreatureStats(actor).getHealth().getCurrent() <= 0) return true; - if(mTarget.getClass().getCreatureStats(mTarget).isDead()) + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); + + if(target.getClass().getCreatureStats(target).isDead()) return true; //Update every frame @@ -325,7 +327,7 @@ namespace MWMechanics ESM::Position pos = actor.getRefData().getPosition(); Ogre::Vector3 vActorPos(pos.pos); - Ogre::Vector3 vTargetPos(mTarget.getRefData().getPosition().pos); + Ogre::Vector3 vTargetPos(target.getRefData().getPosition().pos); Ogre::Vector3 vDirToTarget = vTargetPos - vActorPos; bool isStuck = false; @@ -396,7 +398,7 @@ namespace MWMechanics else // remote pathfinding { bool preferShortcut = false; - bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, mTarget); + bool inLOS = MWBase::Environment::get().getWorld()->getLOS(actor, target); if(mReadyToAttack) isStuck = false; @@ -432,7 +434,7 @@ namespace MWMechanics mFollowTarget = false; - buildNewPath(actor); //may fail to build a path, check before use + buildNewPath(actor, target); //may fail to build a path, check before use //delete visited path node mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]); @@ -476,9 +478,9 @@ namespace MWMechanics //less than in time of playing weapon anim from 'start' to 'hit' tags (t_swing) //then start attacking float speed1 = actorCls.getSpeed(actor); - float speed2 = mTarget.getClass().getSpeed(mTarget); - if(mTarget.getClass().getMovementSettings(mTarget).mPosition[0] == 0 - && mTarget.getClass().getMovementSettings(mTarget).mPosition[1] == 0) + float speed2 = target.getClass().getSpeed(target); + if(target.getClass().getMovementSettings(target).mPosition[0] == 0 + && target.getClass().getMovementSettings(target).mPosition[1] == 0) speed2 = 0; float s1 = distToTarget - weapRange; @@ -570,9 +572,9 @@ namespace MWMechanics return false; } - void AiCombat::buildNewPath(const MWWorld::Ptr& actor) + void AiCombat::buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target) { - Ogre::Vector3 newPathTarget = Ogre::Vector3(mTarget.getRefData().getPosition().pos); + Ogre::Vector3 newPathTarget = Ogre::Vector3(target.getRefData().getPosition().pos); float dist; @@ -627,9 +629,12 @@ namespace MWMechanics return 1; } - const std::string &AiCombat::getTargetId() const + std::string AiCombat::getTargetId() const { - return mTarget.getRefData().getHandle(); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mTargetActorId); + if (target.isEmpty()) + return ""; + return target.getRefData().getHandle(); } diff --git a/apps/openmw/mwmechanics/aicombat.hpp b/apps/openmw/mwmechanics/aicombat.hpp index 833b0063c..9125e2832 100644 --- a/apps/openmw/mwmechanics/aicombat.hpp +++ b/apps/openmw/mwmechanics/aicombat.hpp @@ -31,7 +31,7 @@ namespace MWMechanics virtual unsigned int getPriority() const; ///Returns target ID - const std::string &getTargetId() const; + std::string getTargetId() const; private: PathFinder mPathFinder; @@ -53,7 +53,7 @@ namespace MWMechanics ESM::Position mLastPos; MWMechanics::Movement mMovement; - MWWorld::Ptr mTarget; + int mTargetActorId; const MWWorld::CellStore* mCell; ObstacleCheck mObstacleCheck; @@ -63,7 +63,7 @@ namespace MWMechanics MWWorld::CellRefList::List::iterator mDoorIter; MWWorld::CellRefList& mDoors; - void buildNewPath(const MWWorld::Ptr& actor); + void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); }; } diff --git a/apps/openmw/mwmechanics/aiescort.cpp b/apps/openmw/mwmechanics/aiescort.cpp index 0430adf06..f3ce7143a 100644 --- a/apps/openmw/mwmechanics/aiescort.cpp +++ b/apps/openmw/mwmechanics/aiescort.cpp @@ -8,6 +8,8 @@ #include "../mwworld/class.hpp" #include "../mwworld/timestamp.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "steering.hpp" #include "movement.hpp" @@ -19,8 +21,8 @@ namespace MWMechanics { - AiEscort::AiEscort(const std::string &actorId, int duration, float x, float y, float z) - : mActorId(actorId), mX(x), mY(y), mZ(z), mDuration(duration) + AiEscort::AiEscort(const MWWorld::Ptr& actor, int duration, float x, float y, float z) + : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mX(x), mY(y), mZ(z), mDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -38,8 +40,8 @@ namespace MWMechanics } } - AiEscort::AiEscort(const std::string &actorId, const std::string &cellId,int duration, float x, float y, float z) - : mActorId(actorId), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) + AiEscort::AiEscort(const MWWorld::Ptr& actor, const std::string &cellId,int duration, float x, float y, float z) + : mActorId(actor.getClass().getCreatureStats(actor).getActorId()), mCellId(cellId), mX(x), mY(y), mZ(z), mDuration(duration) , mCellX(std::numeric_limits::max()) , mCellY(std::numeric_limits::max()) { @@ -75,7 +77,14 @@ namespace MWMechanics return true; } - const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->getPtr(mActorId, false); + const MWWorld::Ptr follower = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); + if (follower.isEmpty()) + { + // The follower disappeared + MWWorld::Class::get(actor).getMovementSettings(actor).mPosition[1] = 0; + return true; + } + const float* const leaderPos = actor.getRefData().getPosition().pos; const float* const followerPos = follower.getRefData().getPosition().pos; double differenceBetween[3]; @@ -89,7 +98,7 @@ namespace MWMechanics if(distanceBetweenResult <= mMaxDist * mMaxDist) { - if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete + if(pathTo(actor,ESM::Pathgrid::Point(mX,mY,mZ),duration)) //Returns true on path complete return true; mMaxDist = 470; } diff --git a/apps/openmw/mwmechanics/aiescort.hpp b/apps/openmw/mwmechanics/aiescort.hpp index 3771417fa..b8dc9d6e0 100644 --- a/apps/openmw/mwmechanics/aiescort.hpp +++ b/apps/openmw/mwmechanics/aiescort.hpp @@ -15,11 +15,11 @@ namespace MWMechanics /// Implementation of AiEscort /** The Actor will escort the specified actor to the world position x, y, z until they reach their position, or they run out of time \implement AiEscort **/ - AiEscort(const std::string &actorId,int duration, float x, float y, float z); + AiEscort(const MWWorld::Ptr& actor,int duration, float x, float y, float z); /// Implementation of AiEscortCell /** The Actor will escort the specified actor to the cell position x, y, z until they reach their position, or they run out of time \implement AiEscortCell **/ - AiEscort(const std::string &actorId,const std::string &cellId,int duration, float x, float y, float z); + AiEscort(const MWWorld::Ptr& actor,const std::string &cellId,int duration, float x, float y, float z); virtual AiEscort *clone() const; @@ -28,7 +28,7 @@ namespace MWMechanics virtual int getTypeId() const; private: - std::string mActorId; + int mActorId; std::string mCellId; float mX; float mY; diff --git a/apps/openmw/mwmechanics/aifollow.cpp b/apps/openmw/mwmechanics/aifollow.cpp index f1296a949..d747781a3 100644 --- a/apps/openmw/mwmechanics/aifollow.cpp +++ b/apps/openmw/mwmechanics/aifollow.cpp @@ -11,23 +11,26 @@ #include "steering.hpp" -MWMechanics::AiFollow::AiFollow(const std::string &actorId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(""), AiPackage() +MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,float duration, float x, float y, float z) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(""), AiPackage() { + mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } -MWMechanics::AiFollow::AiFollow(const std::string &actorId,const std::string &cellId,float duration, float x, float y, float z) -: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mActorId(actorId), mCellId(cellId), AiPackage() +MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor,const std::string &cellId,float duration, float x, float y, float z) +: mAlwaysFollow(false), mDuration(duration), mX(x), mY(y), mZ(z), mCellId(cellId), AiPackage() { + mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } -MWMechanics::AiFollow::AiFollow(const std::string &actorId) -: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mActorId(actorId), mCellId(""), AiPackage() +MWMechanics::AiFollow::AiFollow(const MWWorld::Ptr& actor) +: mAlwaysFollow(true), mDuration(0), mX(0), mY(0), mZ(0), mCellId(""), AiPackage() { + mActorId = actor.getClass().getCreatureStats(actor).getActorId(); } bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) { - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mActorId, false); //The target to follow + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist @@ -75,7 +78,8 @@ bool MWMechanics::AiFollow::execute (const MWWorld::Ptr& actor,float duration) std::string MWMechanics::AiFollow::getFollowedActor() { - return mActorId; + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow + return target.getCellRef().mRefID; } MWMechanics::AiFollow *MWMechanics::AiFollow::clone() const diff --git a/apps/openmw/mwmechanics/aifollow.hpp b/apps/openmw/mwmechanics/aifollow.hpp index 10a381410..91bdbdac2 100644 --- a/apps/openmw/mwmechanics/aifollow.hpp +++ b/apps/openmw/mwmechanics/aifollow.hpp @@ -10,16 +10,16 @@ namespace MWMechanics { /// \brief AiPackage for an actor to follow another actor/the PC /** The AI will follow the target until a condition (time, or position) are set. Both can be disabled to cause the actor to follow the other indefinitely - **/ - class AiFollow : public AiPackage - { + **/ + class AiFollow : public AiPackage + { public: /// Follow Actor for duration or until you arrive at a world position - AiFollow(const std::string &ActorId,float duration, float X, float Y, float Z); + AiFollow(const MWWorld::Ptr& actor,float duration, float X, float Y, float Z); /// Follow Actor for duration or until you arrive at a position in a cell - AiFollow(const std::string &ActorId,const std::string &CellId,float duration, float X, float Y, float Z); + AiFollow(const MWWorld::Ptr& actor,const std::string &CellId,float duration, float X, float Y, float Z); /// Follow Actor indefinitively - AiFollow(const std::string &ActorId); + AiFollow(const MWWorld::Ptr& actor); virtual AiFollow *clone() const; @@ -38,8 +38,8 @@ namespace MWMechanics float mX; float mY; float mZ; - std::string mActorId; - std::string mCellId; - }; -} -#endif + int mActorId; // The actor we should follow + std::string mCellId; + }; +} +#endif diff --git a/apps/openmw/mwmechanics/aipursue.cpp b/apps/openmw/mwmechanics/aipursue.cpp index 6e91ccb71..aa71af465 100644 --- a/apps/openmw/mwmechanics/aipursue.cpp +++ b/apps/openmw/mwmechanics/aipursue.cpp @@ -7,12 +7,14 @@ #include "../mwworld/action.hpp" #include "../mwworld/cellstore.hpp" +#include "../mwmechanics/creaturestats.hpp" + #include "steering.hpp" #include "movement.hpp" #include "creaturestats.hpp" -MWMechanics::AiPursue::AiPursue(const std::string &objectId) - : mObjectId(objectId) +MWMechanics::AiPursue::AiPursue(const MWWorld::Ptr& actor) + : mActorId(actor.getClass().getCreatureStats(actor).getActorId()) { } MWMechanics::AiPursue *MWMechanics::AiPursue::clone() const @@ -23,7 +25,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) { ESM::Position pos = actor.getRefData().getPosition(); //position of the actor - const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtr(mObjectId, false); //The target to follow + const MWWorld::Ptr target = MWBase::Environment::get().getWorld()->searchPtrViaActorId(mActorId); //The target to follow if(target == MWWorld::Ptr()) return true; //Target doesn't exist @@ -33,8 +35,7 @@ bool MWMechanics::AiPursue::execute (const MWWorld::Ptr& actor, float duration) if(distance(dest, pos.pos[0], pos.pos[1], pos.pos[2]) < 100) { //Stop when you get close actor.getClass().getMovementSettings(actor).mPosition[1] = 0; - MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(mObjectId,false); - MWWorld::Class::get(target).activate(target,actor).get()->execute(actor); //Arrest player + target.getClass().activate(target,actor).get()->execute(actor); //Arrest player return true; } else { diff --git a/apps/openmw/mwmechanics/aipursue.hpp b/apps/openmw/mwmechanics/aipursue.hpp index 2eb533d62..90ff8df93 100644 --- a/apps/openmw/mwmechanics/aipursue.hpp +++ b/apps/openmw/mwmechanics/aipursue.hpp @@ -16,14 +16,14 @@ namespace MWMechanics { public: ///Constructor - /** \param objectId Actor to pursue **/ - AiPursue(const std::string &objectId); + /** \param actor Actor to pursue **/ + AiPursue(const MWWorld::Ptr& actor); virtual AiPursue *clone() const; virtual bool execute (const MWWorld::Ptr& actor,float duration); virtual int getTypeId() const; private: - std::string mObjectId; + int mActorId; // The actor to pursue int mCellX; int mCellY; }; diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 0b1da180d..8afe8402f 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -175,7 +175,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else if (it->mType == ESM::AI_Escort) { ESM::AITarget data = it->mTarget; - package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false); + package = new MWMechanics::AiEscort(target, data.mDuration, data.mX, data.mY, data.mZ); } else if (it->mType == ESM::AI_Travel) { @@ -190,7 +191,8 @@ void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list) else //if (it->mType == ESM::AI_Follow) { ESM::AITarget data = it->mTarget; - package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ); + MWWorld::Ptr target = MWBase::Environment::get().getWorld()->getPtr(data.mId.toString(), false); + package = new MWMechanics::AiFollow(target, data.mDuration, data.mX, data.mY, data.mZ); } mPackages.push_back(package); } diff --git a/apps/openmw/mwscript/aiextensions.cpp b/apps/openmw/mwscript/aiextensions.cpp index e53b53e58..8ed19925e 100644 --- a/apps/openmw/mwscript/aiextensions.cpp +++ b/apps/openmw/mwscript/aiextensions.cpp @@ -91,6 +91,7 @@ namespace MWScript std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); + MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); @@ -107,7 +108,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -145,7 +147,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); Interpreter::Type_Float duration = runtime[0].mFloat; runtime.pop(); @@ -297,7 +300,7 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; igetPtr(actorID, true); std::string cellID = runtime.getStringLiteral (runtime[0].mInteger); runtime.pop(); @@ -335,8 +339,8 @@ namespace MWScript // discard additional arguments (reset), because we have no idea what they mean. for (unsigned int i=0; i Date: Thu, 15 May 2014 03:07:25 +0200 Subject: [PATCH 23/29] AiCombat: Removed obsolete door back-off code (now handled in AiAvoidDoor) --- apps/openmw/mwmechanics/aicombat.cpp | 83 +++++----------------------- apps/openmw/mwmechanics/aicombat.hpp | 6 -- 2 files changed, 13 insertions(+), 76 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index 72cb8f574..ff9c0f8c4 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -92,11 +92,7 @@ namespace MWMechanics mMovement(), mForceNoShortcut(false), mShortcutFailPos(), - mBackOffDoor(false), - mCell(NULL), - mDoorIter(actor.getCell()->get().mList.end()), - mDoors(actor.getCell()->get()), - mDoorCheckDuration(0) + mBackOffDoor(false) { } @@ -198,12 +194,6 @@ namespace MWMechanics mTimerReact = 0; - bool cellChange = mCell && (actor.getCell() != mCell); - if(!mCell || cellChange) - { - mCell = actor.getCell(); - } - //actual attacking logic //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f float attacksPeriod = 1.0f; @@ -503,70 +493,23 @@ namespace MWMechanics // coded at 250ms or 1/4 second // // TODO: Add a parameter to vary DURATION_SAME_SPOT? - MWWorld::CellStore *cell = actor.getCell(); if((distToTarget > rangeAttack || mFollowTarget) && mObstacleCheck.check(actor, tReaction)) // check if evasive action needed { - // first check if we're walking into a door - mDoorCheckDuration += 1.0f; // add time taken for obstacle check - if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL && !cell->getCell()->isExterior()) - { - mDoorCheckDuration = 0; - // Check all the doors in this cell - mDoors = cell->get(); // update - mDoorIter = mDoors.mList.begin(); - for (; mDoorIter != mDoors.mList.end(); ++mDoorIter) - { - MWWorld::LiveCellRef& ref = *mDoorIter; - float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility - if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && - ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening - { - //std::cout<<"closed door id \""<getCell()->isExterior() && !mDoors.mList.empty()) - { - MWWorld::LiveCellRef& ref = *mDoorIter; - float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility - // TODO: add reaction to checking open doors - if(mBackOffDoor && - vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr) - { - mMovement.mPosition[1] = -0.2; // back off, but slowly - } - else if(mBackOffDoor && - mDoorIter != mDoors.mList.end() && - ref.mData.getLocalRotation().rot[2] >= 1) - { - mDoorIter = mDoors.mList.end(); - mBackOffDoor = false; - //std::cout<<"open door id \""<::List::iterator mDoorIter; - MWWorld::CellRefList& mDoors; void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); }; From bac4d875df97c0f797ba874101ceeae78a08387c Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 04:20:40 +0200 Subject: [PATCH 24/29] Fix a potential exploit that made it possible to reset the owner of any item --- apps/openmw/mwgui/inventoryitemmodel.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index c0847929b..390c0768c 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -64,7 +64,10 @@ void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel bool setNewOwner = false; // Are you dead? Then you wont need that anymore - if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead()) + if (mActor.getClass().isActor() && mActor.getClass().getCreatureStats(mActor).isDead() + // Make sure that the item is actually owned by the dead actor + // Prevents a potential exploit for resetting the owner of any item, by placing the item in a corpse + && Misc::StringUtils::ciEqual(item.mBase.getCellRef().mOwner, mActor.getCellRef().mRefID)) setNewOwner = true; otherModel->copyItem(item, count, setNewOwner); From eb40032bde675ac3baa7634c2b6fa3cf650bf789 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 04:41:31 +0200 Subject: [PATCH 25/29] Fix item owner not resetting when dragging an item from a corpse to the player avatar --- apps/openmw/mwgui/companionitemmodel.cpp | 12 ++++++------ apps/openmw/mwgui/companionitemmodel.hpp | 2 +- apps/openmw/mwgui/containeritemmodel.cpp | 4 ++-- apps/openmw/mwgui/containeritemmodel.hpp | 2 +- apps/openmw/mwgui/inventoryitemmodel.cpp | 9 +++++---- apps/openmw/mwgui/inventoryitemmodel.hpp | 4 ++-- apps/openmw/mwgui/inventorywindow.cpp | 10 ++-------- apps/openmw/mwgui/itemmodel.cpp | 10 +++++----- apps/openmw/mwgui/itemmodel.hpp | 6 +++--- 9 files changed, 27 insertions(+), 32 deletions(-) diff --git a/apps/openmw/mwgui/companionitemmodel.cpp b/apps/openmw/mwgui/companionitemmodel.cpp index 9c4ea2d29..b8be9dcb8 100644 --- a/apps/openmw/mwgui/companionitemmodel.cpp +++ b/apps/openmw/mwgui/companionitemmodel.cpp @@ -10,23 +10,23 @@ namespace MWGui { } - void CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) + MWWorld::Ptr CompanionItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) { if (mActor.getClass().isNpc()) { - MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); - stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); + MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor); + stats.modifyProfit(item.mBase.getClass().getValue(item.mBase) * count); } - InventoryItemModel::copyItem(item, count, setNewOwner); + return InventoryItemModel::copyItem(item, count, setNewOwner); } void CompanionItemModel::removeItem (const ItemStack& item, size_t count) { if (mActor.getClass().isNpc()) { - MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); - stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); + MWMechanics::NpcStats& stats = mActor.getClass().getNpcStats(mActor); + stats.modifyProfit(-item.mBase.getClass().getValue(item.mBase) * count); } InventoryItemModel::removeItem(item, count); diff --git a/apps/openmw/mwgui/companionitemmodel.hpp b/apps/openmw/mwgui/companionitemmodel.hpp index 49c58c896..172fa9508 100644 --- a/apps/openmw/mwgui/companionitemmodel.hpp +++ b/apps/openmw/mwgui/companionitemmodel.hpp @@ -13,7 +13,7 @@ namespace MWGui public: CompanionItemModel (const MWWorld::Ptr& actor); - virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner); virtual void removeItem (const ItemStack& item, size_t count); }; diff --git a/apps/openmw/mwgui/containeritemmodel.cpp b/apps/openmw/mwgui/containeritemmodel.cpp index e702c073d..b5b498e5e 100644 --- a/apps/openmw/mwgui/containeritemmodel.cpp +++ b/apps/openmw/mwgui/containeritemmodel.cpp @@ -71,12 +71,12 @@ ItemModel::ModelIndex ContainerItemModel::getIndex (ItemStack item) return -1; } -void ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) +MWWorld::Ptr ContainerItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { const MWWorld::Ptr& source = mItemSources[mItemSources.size()-1]; if (item.mBase.getContainerStore() == &source.getClass().getContainerStore(source)) throw std::runtime_error("Item to copy needs to be from a different container!"); - source.getClass().getContainerStore(source).add(item.mBase, count, source); + return *source.getClass().getContainerStore(source).add(item.mBase, count, source); } void ContainerItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/containeritemmodel.hpp b/apps/openmw/mwgui/containeritemmodel.hpp index 4b9b4ff99..7ced6ae34 100644 --- a/apps/openmw/mwgui/containeritemmodel.hpp +++ b/apps/openmw/mwgui/containeritemmodel.hpp @@ -21,7 +21,7 @@ namespace MWGui virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); - virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual void update(); diff --git a/apps/openmw/mwgui/inventoryitemmodel.cpp b/apps/openmw/mwgui/inventoryitemmodel.cpp index 390c0768c..718c6d991 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.cpp +++ b/apps/openmw/mwgui/inventoryitemmodel.cpp @@ -40,11 +40,11 @@ ItemModel::ModelIndex InventoryItemModel::getIndex (ItemStack item) return -1; } -void InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) +MWWorld::Ptr InventoryItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { if (item.mBase.getContainerStore() == &mActor.getClass().getContainerStore(mActor)) throw std::runtime_error("Item to copy needs to be from a different container!"); - mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner); + return *mActor.getClass().getContainerStore(mActor).add(item.mBase, count, mActor, setNewOwner); } @@ -59,7 +59,7 @@ void InventoryItemModel::removeItem (const ItemStack& item, size_t count) throw std::runtime_error("Not enough items in the stack to remove"); } -void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) +MWWorld::Ptr InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) { bool setNewOwner = false; @@ -70,8 +70,9 @@ void InventoryItemModel::moveItem(const ItemStack &item, size_t count, ItemModel && Misc::StringUtils::ciEqual(item.mBase.getCellRef().mOwner, mActor.getCellRef().mRefID)) setNewOwner = true; - otherModel->copyItem(item, count, setNewOwner); + MWWorld::Ptr ret = otherModel->copyItem(item, count, setNewOwner); removeItem(item, count); + return ret; } void InventoryItemModel::update() diff --git a/apps/openmw/mwgui/inventoryitemmodel.hpp b/apps/openmw/mwgui/inventoryitemmodel.hpp index 78dcc9497..f58ee2939 100644 --- a/apps/openmw/mwgui/inventoryitemmodel.hpp +++ b/apps/openmw/mwgui/inventoryitemmodel.hpp @@ -15,11 +15,11 @@ namespace MWGui virtual ModelIndex getIndex (ItemStack item); virtual size_t getItemCount(); - virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); /// Move items from this model to \a otherModel. - virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); + virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); virtual void update(); diff --git a/apps/openmw/mwgui/inventorywindow.cpp b/apps/openmw/mwgui/inventorywindow.cpp index d6da3b30e..4d52d59bb 100644 --- a/apps/openmw/mwgui/inventorywindow.cpp +++ b/apps/openmw/mwgui/inventorywindow.cpp @@ -408,14 +408,8 @@ namespace MWGui if (mDragAndDrop->mSourceModel != mTradeModel) { - // add item to the player's inventory - MWWorld::ContainerStore& invStore = MWWorld::Class::get(mPtr).getContainerStore(mPtr); - MWWorld::ContainerStoreIterator it = invStore.begin(); - - it = invStore.add(ptr, mDragAndDrop->mDraggedCount, mPtr); - - mDragAndDrop->mSourceModel->removeItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount); - ptr = *it; + // Move item to the player's inventory + ptr = mDragAndDrop->mSourceModel->moveItem(mDragAndDrop->mItem, mDragAndDrop->mDraggedCount, mTradeModel); } useItem(ptr); } diff --git a/apps/openmw/mwgui/itemmodel.cpp b/apps/openmw/mwgui/itemmodel.cpp index 7b2ea4d4a..2cdd8a2e6 100644 --- a/apps/openmw/mwgui/itemmodel.cpp +++ b/apps/openmw/mwgui/itemmodel.cpp @@ -71,10 +71,11 @@ namespace MWGui { } - void ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) + MWWorld::Ptr ItemModel::moveItem(const ItemStack &item, size_t count, ItemModel *otherModel) { - otherModel->copyItem(item, count); + MWWorld::Ptr ret = otherModel->copyItem(item, count); removeItem(item, count); + return ret; } @@ -83,10 +84,9 @@ namespace MWGui delete mSourceModel; } - void ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) + MWWorld::Ptr ProxyItemModel::copyItem (const ItemStack& item, size_t count, bool setNewOwner) { - // no need to use mapToSource since itemIndex refers to an index in the sourceModel - mSourceModel->copyItem (item, count, setNewOwner); + return mSourceModel->copyItem (item, count, setNewOwner); } void ProxyItemModel::removeItem (const ItemStack& item, size_t count) diff --git a/apps/openmw/mwgui/itemmodel.hpp b/apps/openmw/mwgui/itemmodel.hpp index 684771b86..21c5477d0 100644 --- a/apps/openmw/mwgui/itemmodel.hpp +++ b/apps/openmw/mwgui/itemmodel.hpp @@ -56,10 +56,10 @@ namespace MWGui virtual void update() = 0; /// Move items from this model to \a otherModel. - virtual void moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); + virtual MWWorld::Ptr moveItem (const ItemStack& item, size_t count, ItemModel* otherModel); /// @param setNewOwner Set the copied item's owner to the actor we are copying to, or keep the original owner? - virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false) = 0; virtual void removeItem (const ItemStack& item, size_t count) = 0; private: @@ -73,7 +73,7 @@ namespace MWGui { public: virtual ~ProxyItemModel(); - virtual void copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); + virtual MWWorld::Ptr copyItem (const ItemStack& item, size_t count, bool setNewOwner=false); virtual void removeItem (const ItemStack& item, size_t count); virtual ModelIndex getIndex (ItemStack item); From b6a7aee42e15e45d1a251a0b02c2ad6bf514f551 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 04:52:35 +0200 Subject: [PATCH 26/29] Fix player not being allowed to use his own items --- apps/openmw/mwmechanics/mechanicsmanagerimp.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 41374c1a3..bf9a11d6e 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -25,7 +25,7 @@ namespace bool isAllowedToUse (const MWWorld::Ptr& ptr, const MWWorld::Ptr& item, MWWorld::Ptr& victim) { const std::string& owner = item.getCellRef().mOwner; - bool isOwned = !owner.empty(); + bool isOwned = !owner.empty() && owner != "player"; const std::string& faction = item.getCellRef().mFaction; bool isFactionOwned = false; From 6de7e165504981fef276a93ea6c7980b3d53ec82 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 06:08:55 +0200 Subject: [PATCH 27/29] Fix searchPtrViaActorId not skipping over deleted references Fixes an issue when an actor has moved cell: searchPtrViaActorId would randomly return the deleted Ptr from the old cell. --- apps/openmw/mwworld/cellstore.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/cellstore.cpp b/apps/openmw/mwworld/cellstore.cpp index a5f166374..5d2d8a517 100644 --- a/apps/openmw/mwworld/cellstore.cpp +++ b/apps/openmw/mwworld/cellstore.cpp @@ -54,7 +54,7 @@ namespace { MWWorld::Ptr actor (&*iter, cell); - if (MWWorld::Class::get (actor).getCreatureStats (actor).matchesActorId (actorId)) + if (actor.getClass().getCreatureStats (actor).matchesActorId (actorId) && actor.getRefData().getCount() > 0) return actor; } From b16d444f0ff03f77c403a657afca58556cd0b950 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 10:05:35 +0200 Subject: [PATCH 28/29] Fix never clearing graveyard. Oops --- apps/openmw/mwmechanics/actors.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index af6cac12f..a0616258c 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -574,11 +574,16 @@ namespace MWMechanics } } - for (std::vector::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); ++it) + for (std::vector::iterator it = creatureStats.mSummonGraveyard.begin(); it != creatureStats.mSummonGraveyard.end(); ) { MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it); if (!ptr.isEmpty()) + { + it = creatureStats.mSummonGraveyard.erase(it); MWBase::Environment::get().getWorld()->deleteObject(ptr); + } + else + ++it; } } From 0bc33fa86a0dc6bb6fc59ce2092d0efdc0e42dc2 Mon Sep 17 00:00:00 2001 From: scrawl Date: Thu, 15 May 2014 10:14:47 +0200 Subject: [PATCH 29/29] Revert "AiCombat: Removed obsolete door back-off code (now handled in AiAvoidDoor)" Apparently not working for AiCombat due to its higher getPriority(). What should we do here? This reverts commit a6e1d7ffd6daf4af4750d1478048733428db4a5b. --- apps/openmw/mwmechanics/aicombat.cpp | 83 +++++++++++++++++++++++----- apps/openmw/mwmechanics/aicombat.hpp | 6 ++ 2 files changed, 76 insertions(+), 13 deletions(-) diff --git a/apps/openmw/mwmechanics/aicombat.cpp b/apps/openmw/mwmechanics/aicombat.cpp index ff9c0f8c4..72cb8f574 100644 --- a/apps/openmw/mwmechanics/aicombat.cpp +++ b/apps/openmw/mwmechanics/aicombat.cpp @@ -92,7 +92,11 @@ namespace MWMechanics mMovement(), mForceNoShortcut(false), mShortcutFailPos(), - mBackOffDoor(false) + mBackOffDoor(false), + mCell(NULL), + mDoorIter(actor.getCell()->get().mList.end()), + mDoors(actor.getCell()->get()), + mDoorCheckDuration(0) { } @@ -194,6 +198,12 @@ namespace MWMechanics mTimerReact = 0; + bool cellChange = mCell && (actor.getCell() != mCell); + if(!mCell || cellChange) + { + mCell = actor.getCell(); + } + //actual attacking logic //TODO: Some skills affect period of strikes.For berserk-like style period ~ 0.25f float attacksPeriod = 1.0f; @@ -493,23 +503,70 @@ namespace MWMechanics // coded at 250ms or 1/4 second // // TODO: Add a parameter to vary DURATION_SAME_SPOT? + MWWorld::CellStore *cell = actor.getCell(); if((distToTarget > rangeAttack || mFollowTarget) && mObstacleCheck.check(actor, tReaction)) // check if evasive action needed { - // probably walking into another NPC TODO: untested in combat situation + // first check if we're walking into a door + mDoorCheckDuration += 1.0f; // add time taken for obstacle check + if(mDoorCheckDuration >= DOOR_CHECK_INTERVAL && !cell->getCell()->isExterior()) + { + mDoorCheckDuration = 0; + // Check all the doors in this cell + mDoors = cell->get(); // update + mDoorIter = mDoors.mList.begin(); + for (; mDoorIter != mDoors.mList.end(); ++mDoorIter) + { + MWWorld::LiveCellRef& ref = *mDoorIter; + float minSqr = 1.3*1.3*MIN_DIST_TO_DOOR_SQUARED; // for legibility + if(vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr && + ref.mData.getLocalRotation().rot[2] < 0.4f) // even small opening + { + //std::cout<<"closed door id \""<getCell()->isExterior() && !mDoors.mList.empty()) + { + MWWorld::LiveCellRef& ref = *mDoorIter; + float minSqr = 1.6 * 1.6 * MIN_DIST_TO_DOOR_SQUARED; // for legibility + // TODO: add reaction to checking open doors + if(mBackOffDoor && + vActorPos.squaredDistance(Ogre::Vector3(ref.mRef.mPos.pos)) < minSqr) + { + mMovement.mPosition[1] = -0.2; // back off, but slowly + } + else if(mBackOffDoor && + mDoorIter != mDoors.mList.end() && + ref.mData.getLocalRotation().rot[2] >= 1) + { + mDoorIter = mDoors.mList.end(); + mBackOffDoor = false; + //std::cout<<"open door id \""<::List::iterator mDoorIter; + MWWorld::CellRefList& mDoors; void buildNewPath(const MWWorld::Ptr& actor, const MWWorld::Ptr& target); };