From 9fa8e88366959c880eae78cc16d128fb3d9804f0 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Feb 2017 10:22:11 +0100 Subject: [PATCH 01/14] Revert "Don't create a CharacterController for objects with no animations" This reverts commit cce42b6e9d09331f9d52367b5324f32ebc18c2ff. --- apps/openmw/mwmechanics/objects.cpp | 3 +-- apps/openmw/mwrender/animation.cpp | 5 ----- apps/openmw/mwrender/animation.hpp | 2 -- 3 files changed, 1 insertion(+), 9 deletions(-) diff --git a/apps/openmw/mwmechanics/objects.cpp b/apps/openmw/mwmechanics/objects.cpp index a15b4cba4..139825c21 100644 --- a/apps/openmw/mwmechanics/objects.cpp +++ b/apps/openmw/mwmechanics/objects.cpp @@ -29,8 +29,7 @@ void Objects::addObject(const MWWorld::Ptr& ptr) removeObject(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); - if (anim && anim->hasAnimSources()) - mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); + if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); } void Objects::removeObject(const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwrender/animation.cpp b/apps/openmw/mwrender/animation.cpp index 0865850e2..ec28d4295 100644 --- a/apps/openmw/mwrender/animation.cpp +++ b/apps/openmw/mwrender/animation.cpp @@ -565,11 +565,6 @@ namespace MWRender } } - bool Animation::hasAnimSources() const - { - return !mAnimSources.empty(); - } - void Animation::clearAnimSources() { mStates.clear(); diff --git a/apps/openmw/mwrender/animation.hpp b/apps/openmw/mwrender/animation.hpp index 2ea369ea7..f765a7a40 100644 --- a/apps/openmw/mwrender/animation.hpp +++ b/apps/openmw/mwrender/animation.hpp @@ -341,8 +341,6 @@ public: Animation(const MWWorld::Ptr &ptr, osg::ref_ptr parentNode, Resource::ResourceSystem* resourceSystem); virtual ~Animation(); - bool hasAnimSources() const; - MWWorld::ConstPtr getPtr() const; /// Set active flag on the object skeleton, if one exists. From 6b5354157140891eaa2925d54192f5a300c9da48 Mon Sep 17 00:00:00 2001 From: Allofich Date: Thu, 2 Feb 2017 16:20:34 +0900 Subject: [PATCH 02/14] Prevent AI actors from hitting unintended targets (Fixes #3254) --- apps/openmw/mwbase/world.hpp | 2 +- apps/openmw/mwclass/creature.cpp | 7 ++- apps/openmw/mwclass/npc.cpp | 7 ++- apps/openmw/mwmechanics/aisequence.cpp | 11 ++++ apps/openmw/mwmechanics/aisequence.hpp | 3 ++ apps/openmw/mwphysics/physicssystem.cpp | 65 +++++++++++++++++++---- apps/openmw/mwphysics/physicssystem.hpp | 9 ++-- apps/openmw/mwworld/projectilemanager.cpp | 22 ++++++-- apps/openmw/mwworld/worldimp.cpp | 24 +++++---- apps/openmw/mwworld/worldimp.hpp | 2 +- 10 files changed, 121 insertions(+), 31 deletions(-) diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index bba473834..59f28fcc8 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -255,7 +255,7 @@ namespace MWBase /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis. - virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance) = 0; + virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector &targets) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr, bool force) = 0; ///< Adjust position after load to be on ground. Must be called after model load. diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index f5b62ca4a..0be61ef51 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -249,7 +249,12 @@ namespace MWClass if (!weapon.isEmpty()) dist *= weapon.get()->mBase->mData.mReach; - std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist); + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) + ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); + + std::pair result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist, targetActors); if (result.first.isEmpty()) return; // Didn't hit anything diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 27d4643fa..8b102a487 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -566,8 +566,13 @@ namespace MWClass weapon.get()->mBase->mData.mReach : store.find("fHandToHandReach")->getFloat()); + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!ptr.isEmpty() && ptr.getClass().isActor() && ptr != MWMechanics::getPlayer()) + ptr.getClass().getCreatureStats(ptr).getAiSequence().getCombatTargets(targetActors); + // TODO: Use second to work out the hit angle - std::pair result = world->getHitContact(ptr, dist); + std::pair result = world->getHitContact(ptr, dist, targetActors); MWWorld::Ptr victim = result.first; osg::Vec3f hitPosition (result.second); if(victim.isEmpty()) // Didn't hit anything diff --git a/apps/openmw/mwmechanics/aisequence.cpp b/apps/openmw/mwmechanics/aisequence.cpp index 520bacf5a..84c8eca49 100644 --- a/apps/openmw/mwmechanics/aisequence.cpp +++ b/apps/openmw/mwmechanics/aisequence.cpp @@ -75,6 +75,17 @@ bool AiSequence::getCombatTarget(MWWorld::Ptr &targetActor) const return !targetActor.isEmpty(); } +bool AiSequence::getCombatTargets(std::vector &targetActors) const +{ + for (std::list::const_iterator it = mPackages.begin(); it != mPackages.end(); ++it) + { + if ((*it)->getTypeId() == MWMechanics::AiPackage::TypeIdCombat) + targetActors.push_back((*it)->getTarget()); + } + + return !targetActors.empty(); +} + std::list::const_iterator AiSequence::begin() const { return mPackages.begin(); diff --git a/apps/openmw/mwmechanics/aisequence.hpp b/apps/openmw/mwmechanics/aisequence.hpp index c7c32979e..4f8b542a4 100644 --- a/apps/openmw/mwmechanics/aisequence.hpp +++ b/apps/openmw/mwmechanics/aisequence.hpp @@ -79,6 +79,9 @@ namespace MWMechanics /// Return true and assign target if combat package is currently active, return false otherwise bool getCombatTarget (MWWorld::Ptr &targetActor) const; + /// Return true and assign targets for all combat packages, or return false if there are no combat packages + bool getCombatTargets(std::vector &targetActors) const; + /// Is there any combat package? bool isInCombat () const; diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index c043c861e..de8774cfd 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -798,6 +798,7 @@ namespace MWPhysics class DeepestNotMeContactTestResultCallback : public btCollisionWorld::ContactResultCallback { const btCollisionObject* mMe; + const std::vector mTargets; // Store the real origin, since the shape's origin is its center btVector3 mOrigin; @@ -807,8 +808,8 @@ namespace MWPhysics btVector3 mContactPoint; btScalar mLeastDistSqr; - DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const btVector3 &origin) - : mMe(me), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0), + DeepestNotMeContactTestResultCallback(const btCollisionObject* me, const std::vector targets, const btVector3 &origin) + : mMe(me), mTargets(targets), mOrigin(origin), mObject(NULL), mContactPoint(0,0,0), mLeastDistSqr(std::numeric_limits::max()) { } @@ -819,6 +820,16 @@ namespace MWPhysics const btCollisionObject* collisionObject = col1Wrap->m_collisionObject; if (collisionObject != mMe) { + if (!mTargets.empty()) + { + if ((std::find(mTargets.begin(), mTargets.end(), collisionObject) == mTargets.end())) + { + PtrHolder* holder = static_cast(collisionObject->getUserPointer()); + if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) + return 0.f; + } + } + btScalar distsqr = mOrigin.distance2(cp.getPositionWorldOnA()); if(!mObject || distsqr < mLeastDistSqr) { @@ -835,7 +846,7 @@ namespace MWPhysics std::pair PhysicsSystem::getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orient, - float queryDistance) + float queryDistance, std::vector targets) { const MWWorld::Store &store = MWBase::Environment::get().getWorld()->getStore().get(); @@ -852,11 +863,23 @@ namespace MWPhysics object.setWorldTransform(btTransform(toBullet(orient), toBullet(center))); const btCollisionObject* me = NULL; + std::vector targetCollisionObjects; + const Actor* physactor = getActor(actor); if (physactor) me = physactor->getCollisionObject(); - DeepestNotMeContactTestResultCallback resultCallback(me, toBullet(origin)); + if (!targets.empty()) + { + for (std::vector::const_iterator it = targets.begin(); it != targets.end(); ++it) + { + const Actor* physactor2 = getActor(*it); + if (physactor2) + targetCollisionObjects.push_back(physactor2->getCollisionObject()); + } + } + + DeepestNotMeContactTestResultCallback resultCallback(me, targetCollisionObjects, toBullet(origin)); resultCallback.m_collisionFilterGroup = CollisionType_Actor; resultCallback.m_collisionFilterMask = CollisionType_World | CollisionType_Door | CollisionType_HeightMap | CollisionType_Actor; mCollisionWorld->contactTest(&object, resultCallback); @@ -903,28 +926,40 @@ namespace MWPhysics class ClosestNotMeRayResultCallback : public btCollisionWorld::ClosestRayResultCallback { public: - ClosestNotMeRayResultCallback(const btCollisionObject* me, const btVector3& from, const btVector3& to) + ClosestNotMeRayResultCallback(const btCollisionObject* me, const std::vector targets, const btVector3& from, const btVector3& to) : btCollisionWorld::ClosestRayResultCallback(from, to) - , mMe(me) + , mMe(me), mTargets(targets) { } - virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult,bool normalInWorldSpace) + virtual btScalar addSingleResult(btCollisionWorld::LocalRayResult& rayResult, bool normalInWorldSpace) { if (rayResult.m_collisionObject == mMe) return 1.f; + if (!mTargets.empty()) + { + if ((std::find(mTargets.begin(), mTargets.end(), rayResult.m_collisionObject) == mTargets.end())) + { + PtrHolder* holder = static_cast(rayResult.m_collisionObject->getUserPointer()); + if (holder && !holder->getPtr().isEmpty() && holder->getPtr().getClass().isActor()) + return 1.f; + } + } return btCollisionWorld::ClosestRayResultCallback::addSingleResult(rayResult, normalInWorldSpace); } private: const btCollisionObject* mMe; + const std::vector mTargets; }; - PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore, int mask, int group) const + PhysicsSystem::RayResult PhysicsSystem::castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore, std::vector targets, int mask, int group) const { btVector3 btFrom = toBullet(from); btVector3 btTo = toBullet(to); const btCollisionObject* me = NULL; + std::vector targetCollisionObjects; + if (!ignore.isEmpty()) { const Actor* actor = getActor(ignore); @@ -938,7 +973,17 @@ namespace MWPhysics } } - ClosestNotMeRayResultCallback resultCallback(me, btFrom, btTo); + if (!targets.empty()) + { + for (std::vector::const_iterator it = targets.begin(); it != targets.end(); ++it) + { + const Actor* actor = getActor(*it); + if (actor) + targetCollisionObjects.push_back(actor->getCollisionObject()); + } + } + + ClosestNotMeRayResultCallback resultCallback(me, targetCollisionObjects, btFrom, btTo); resultCallback.m_collisionFilterGroup = group; resultCallback.m_collisionFilterMask = mask; @@ -991,7 +1036,7 @@ namespace MWPhysics osg::Vec3f pos1 (physactor1->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor1->getHalfExtents().z() * 0.9)); // eye level osg::Vec3f pos2 (physactor2->getCollisionObjectPosition() + osg::Vec3f(0,0,physactor2->getHalfExtents().z() * 0.9)); - RayResult result = castRay(pos1, pos2, MWWorld::Ptr(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door); + RayResult result = castRay(pos1, pos2, MWWorld::ConstPtr(), std::vector(), CollisionType_World|CollisionType_HeightMap|CollisionType_Door); return !result.mHit; } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index ec12f7681..9730cda74 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -95,7 +95,7 @@ namespace MWPhysics std::pair getHitContact(const MWWorld::ConstPtr& actor, const osg::Vec3f &origin, const osg::Quat &orientation, - float queryDistance); + float queryDistance, std::vector targets = std::vector()); /// Get distance from \a point to the collision shape of \a target. Uses a raycast to find where the @@ -112,9 +112,10 @@ namespace MWPhysics MWWorld::Ptr mHitObject; }; - /// @param me Optional, a Ptr to ignore in the list of results - RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore = MWWorld::ConstPtr(), int mask = - CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const; + /// @param me Optional, a Ptr to ignore in the list of results. targets are actors to filter for, ignoring all other actors. + RayResult castRay(const osg::Vec3f &from, const osg::Vec3f &to, MWWorld::ConstPtr ignore = MWWorld::ConstPtr(), + std::vector targets = std::vector(), + int mask = CollisionType_World|CollisionType_HeightMap|CollisionType_Actor|CollisionType_Door, int group=0xff) const; RayResult castSphere(const osg::Vec3f& from, const osg::Vec3f& to, float radius); diff --git a/apps/openmw/mwworld/projectilemanager.cpp b/apps/openmw/mwworld/projectilemanager.cpp index 3f0c0e54e..5afdce700 100644 --- a/apps/openmw/mwworld/projectilemanager.cpp +++ b/apps/openmw/mwworld/projectilemanager.cpp @@ -6,8 +6,10 @@ #include #include + #include #include + #include #include #include @@ -25,6 +27,7 @@ #include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/actorutil.hpp" +#include "../mwmechanics/aipackage.hpp" #include "../mwrender/effectmanager.hpp" #include "../mwrender/animation.hpp" @@ -49,8 +52,8 @@ namespace const ESM::MagicEffect *magicEffect = MWBase::Environment::get().getWorld()->getStore().get().find ( iter->mEffectID); - // All the projectiles should use the same speed. From observations in the - // original engine, this seems to be the average of the constituent effects. + // Speed of multi-effect projectiles should be the average of the constituent effects, + // based on observation of the original engine. speed += magicEffect->mData.mSpeed; count++; @@ -337,9 +340,14 @@ namespace MWWorld MWWorld::Ptr caster = it->getCaster(); + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) + caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors); + // Check for impact // TODO: use a proper btRigidBody / btGhostObject? - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile); bool hit = false; if (result.mHit) @@ -404,11 +412,17 @@ namespace MWWorld MWWorld::Ptr caster = it->getCaster(); + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!caster.isEmpty() && caster.getClass().isActor() && caster != MWMechanics::getPlayer()) + caster.getClass().getCreatureStats(caster).getAiSequence().getCombatTargets(targetActors); + // Check for impact // TODO: use a proper btRigidBody / btGhostObject? - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, 0xff, MWPhysics::CollisionType_Projectile); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(pos, newPos, caster, targetActors, 0xff, MWPhysics::CollisionType_Projectile); bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), newPos); + if (result.mHit || underwater) { MWWorld::ManualRef projectileRef(MWBase::Environment::get().getWorld()->getStore(), it->mIdArrow); diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index dcec8ea85..d03fa263c 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -7,10 +7,11 @@ #include #include +#include #include #include -#include + #include #include @@ -1064,7 +1065,7 @@ namespace MWWorld return osg::Matrixf::translate(actor.getRefData().getPosition().asVec3()); } - std::pair World::getHitContact(const MWWorld::ConstPtr &ptr, float distance) + std::pair World::getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector &targets) { const ESM::Position &posdata = ptr.getRefData().getPosition(); @@ -1084,7 +1085,7 @@ namespace MWWorld distance += halfExtents.y(); } - std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance); + std::pair result = mPhysics->getHitContact(ptr, pos, rot, distance, targets); if(result.first.isEmpty()) return std::make_pair(MWWorld::Ptr(), osg::Vec3f()); @@ -1450,7 +1451,7 @@ namespace MWWorld { osg::Vec3f a(x1,y1,z1); osg::Vec3f b(x2,y2,z2); - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector(), MWPhysics::CollisionType_World|MWPhysics::CollisionType_Door); return result.mHit; } @@ -2443,7 +2444,7 @@ namespace MWWorld if (includeWater) { collisionTypes |= MWPhysics::CollisionType_Water; } - MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), collisionTypes); + MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(from, to, MWWorld::Ptr(), std::vector(), collisionTypes); if (!result.mHit) return maxDist; @@ -2664,12 +2665,17 @@ namespace MWWorld osg::Vec3f direction = orient * osg::Vec3f(0,1,0); osg::Vec3f dest = origin + direction * distance; + // For AI actors, get combat targets to use in the ray cast. Only those targets will return a positive hit result. + std::vector targetActors; + if (!actor.isEmpty() && actor != MWMechanics::getPlayer()) + actor.getClass().getCreatureStats(actor).getAiSequence().getCombatTargets(targetActors); + // For actor targets, we want to use bounding boxes (physics raycast). // This is to give a slight tolerance for errors, especially with creatures like the Skeleton that would be very hard to aim at otherwise. // For object targets, we want the detailed shapes (rendering raycast). // If we used the bounding boxes for static objects, then we would not be able to target e.g. objects lying on a shelf. - MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, MWPhysics::CollisionType_Actor); + MWPhysics::PhysicsSystem::RayResult result1 = mPhysics->castRay(origin, dest, actor, targetActors, MWPhysics::CollisionType_Actor); MWRender::RenderingManager::RayResult result2 = mRendering->castRay(origin, dest, true, true); @@ -2681,7 +2687,7 @@ namespace MWWorld if (result2.mHit) dist2 = (origin - result2.mHitPointWorld).length(); - if (dist1 <= dist2 && result1.mHit) + if (result1.mHit) { target = result1.mHitObject; hitPosition = result1.mHitPos; @@ -2692,7 +2698,7 @@ namespace MWWorld { target = result2.mHitObject; hitPosition = result2.mHitPointWorld; - if (dist2 > getMaxActivationDistance() && !target.isEmpty() && (target.getClass().isActor() || !target.getClass().canBeActivated(target))) + if (dist2 > getMaxActivationDistance() && !target.isEmpty() && !target.getClass().canBeActivated(target)) target = NULL; } @@ -2702,7 +2708,7 @@ namespace MWWorld if (!target.isEmpty() && target.getClass().isActor() && target.getClass().getCreatureStats (target).getAiSequence().isInCombat()) { distance = std::min (distance, getStore().get().find("fCombatDistance")->getFloat()); - if (distance < dist1 && distance < dist2) + if (distance < dist1) target = NULL; } diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 4b86ec14f..5d69f2e12 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -354,7 +354,7 @@ namespace MWWorld /// Returns a pointer to the object the provided object would hit (if within the /// specified distance), and the point where the hit occurs. This will attempt to /// use the "Head" node as a basis. - virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance); + virtual std::pair getHitContact(const MWWorld::ConstPtr &ptr, float distance, std::vector &targets); /// @note No-op for items in containers. Use ContainerStore::removeItem instead. virtual void deleteObject (const Ptr& ptr); From 65878801715efd86faf1673f4ca68cbe512f7517 Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 13:03:37 +0100 Subject: [PATCH 03/14] Update refidcollection.cpp Makes Book records display skill IDs instead of attribute IDs for teached skills. Related issue: - Bug #3746: OpenMW-CS: Book records show attribute IDs instead of skill IDs for teached skills entry. --- apps/opencs/model/world/refidcollection.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 74867d626..5674ba327 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -294,8 +294,8 @@ CSMWorld::RefIdCollection::RefIdCollection() mColumns.push_back (RefIdColumn (Columns::ColumnId_Scroll, ColumnBase::Display_Boolean)); const RefIdColumn *scroll = &mColumns.back(); - mColumns.push_back (RefIdColumn (Columns::ColumnId_Attribute, ColumnBase::Display_Attribute)); - const RefIdColumn *attribute = &mColumns.back(); + mColumns.push_back (RefIdColumn (Columns::ColumnId_Skill, ColumnBase::Display_SkillId)); + const RefIdColumn *skill = &mColumns.back(); mColumns.push_back (RefIdColumn (Columns::ColumnId_Text, ColumnBase::Display_LongString)); const RefIdColumn *text = &mColumns.back(); @@ -659,7 +659,7 @@ CSMWorld::RefIdCollection::RefIdCollection() mAdapters.insert (std::make_pair (UniversalId::Type_Armor, new ArmorRefIdAdapter (enchantableColumns, armorType, health, armor, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Book, - new BookRefIdAdapter (enchantableColumns, scroll, attribute, text))); + new BookRefIdAdapter (enchantableColumns, scroll, skill, text))); mAdapters.insert (std::make_pair (UniversalId::Type_Clothing, new ClothingRefIdAdapter (enchantableColumns, clothingType, partRef))); mAdapters.insert (std::make_pair (UniversalId::Type_Container, From c5dcfe0794daeed3ee39b0887cac7d7a265a203c Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 13:50:58 +0100 Subject: [PATCH 04/14] Update refidadapterimp.cpp Code cosmetics: Change member variable "mSkillID" to "mSkillId" for Book records. --- apps/opencs/model/world/refidadapterimp.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/refidadapterimp.cpp b/apps/opencs/model/world/refidadapterimp.cpp index 086e95c34..0da4769a8 100644 --- a/apps/opencs/model/world/refidadapterimp.cpp +++ b/apps/opencs/model/world/refidadapterimp.cpp @@ -316,7 +316,7 @@ QVariant CSMWorld::BookRefIdAdapter::getData (const RefIdColumn *column, return record.get().mData.mIsScroll!=0; if (column==mSkill) - return record.get().mData.mSkillID; + return record.get().mData.mSkillId; if (column==mText) return QString::fromUtf8 (record.get().mText.c_str()); @@ -335,7 +335,7 @@ void CSMWorld::BookRefIdAdapter::setData (const RefIdColumn *column, RefIdData& if (column==mScroll) book.mData.mIsScroll = value.toInt(); else if (column==mSkill) - book.mData.mSkillID = value.toInt(); + book.mData.mSkillId = value.toInt(); else if (column==mText) book.mText = value.toString().toUtf8().data(); else From 347fb1ff4b57aa69bced6094bee6bc0b365ab717 Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 13:51:36 +0100 Subject: [PATCH 05/14] Update converter.hpp Code cosmetics: Change member variable "mSkillID" to "mSkillId" for Book records. --- apps/essimporter/converter.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/essimporter/converter.hpp b/apps/essimporter/converter.hpp index b05337aea..e4985f993 100644 --- a/apps/essimporter/converter.hpp +++ b/apps/essimporter/converter.hpp @@ -202,7 +202,7 @@ public: bool isDeleted = false; book.load(esm, isDeleted); - if (book.mData.mSkillID == -1) + if (book.mData.mSkillId == -1) mContext->mPlayer.mObject.mNpcStats.mUsedIds.push_back(Misc::StringUtils::lowerCase(book.mId)); mRecords[book.mId] = book; From 7bc65ce05b5b5417da6fc11fbe6d8187c3b2eda7 Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 13:52:11 +0100 Subject: [PATCH 06/14] Update actionread.cpp Code cosmetics: Change member variable "mSkillID" to "mSkillId" for Book records. --- apps/openmw/mwworld/actionread.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 90e9a375b..ffab2959c 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -44,7 +44,7 @@ namespace MWWorld MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); // Skill gain from books - if (ref->mBase->mData.mSkillID >= 0 && ref->mBase->mData.mSkillID < ESM::Skill::Length + if (ref->mBase->mData.mSkillId >= 0 && ref->mBase->mData.mSkillID < ESM::Skill::Length && !npcStats.hasBeenUsed (ref->mBase->mId)) { MWWorld::LiveCellRef *playerRef = actor.get(); @@ -54,7 +54,7 @@ namespace MWWorld playerRef->mBase->mClass ); - npcStats.increaseSkill (ref->mBase->mData.mSkillID, *class_, true); + npcStats.increaseSkill (ref->mBase->mData.mSkillId, *class_, true); npcStats.flagAsUsed (ref->mBase->mId); } From 5a58ab1837bdfd5280582e02d8681cc5fad12459 Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 13:52:36 +0100 Subject: [PATCH 07/14] Update loadbook.hpp --- components/esm/loadbook.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/loadbook.hpp b/components/esm/loadbook.hpp index 3d50356ae..bb2d7912f 100644 --- a/components/esm/loadbook.hpp +++ b/components/esm/loadbook.hpp @@ -21,7 +21,7 @@ struct Book struct BKDTstruct { float mWeight; - int mValue, mIsScroll, mSkillID, mEnchant; + int mValue, mIsScroll, mSkillId, mEnchant; }; BKDTstruct mData; From b4ef9e23144431a0a7e33675b7d2d8e7084626cf Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 13:53:10 +0100 Subject: [PATCH 08/14] Update loadbook.cpp --- components/esm/loadbook.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/components/esm/loadbook.cpp b/components/esm/loadbook.cpp index d5283b8eb..ee3c0c4af 100644 --- a/components/esm/loadbook.cpp +++ b/components/esm/loadbook.cpp @@ -84,7 +84,7 @@ namespace ESM mData.mWeight = 0; mData.mValue = 0; mData.mIsScroll = 0; - mData.mSkillID = 0; + mData.mSkillId = 0; mData.mEnchant = 0; mName.clear(); mModel.clear(); From 72c3ee0a3dc352208f20344ea9028b80fc6a542b Mon Sep 17 00:00:00 2001 From: Allofich Date: Sun, 12 Feb 2017 23:14:09 +0900 Subject: [PATCH 09/14] Make AI not go hostile when absorbing/reflecting --- apps/openmw/mwmechanics/spellcasting.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 5dd47d230..230d01e78 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -413,10 +413,6 @@ namespace MWMechanics float magnitudeMult = 1; if (magicEffect->mData.mFlags & ESM::MagicEffect::Harmful && target.getClass().isActor()) { - // Notify the target actor they've been hit - if (target != caster && !caster.isEmpty()) - target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); - if (absorbed) continue; @@ -449,6 +445,10 @@ namespace MWMechanics // If player is attempting to cast a harmful spell, show the target's HP bar if (castByPlayer && target != caster) MWBase::Environment::get().getWindowManager()->setEnemy(target); + + // Notify the target actor they've been hit + if (target != caster && !caster.isEmpty()) + target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); } if (magnitudeMult > 0 && !absorbed) From 892d1b162dcafd347d0dd8de54baf43132036813 Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Feb 2017 10:33:31 +0100 Subject: [PATCH 10/14] nifloader: ignore nodes named 'bounding box' (Fixes #3650) --- components/nifosg/nifloader.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/components/nifosg/nifloader.cpp b/components/nifosg/nifloader.cpp index 6fa8e4e49..a247c62a4 100644 --- a/components/nifosg/nifloader.cpp +++ b/components/nifosg/nifloader.cpp @@ -552,6 +552,9 @@ namespace NifOsg osg::ref_ptr handleNode(const Nif::Node* nifNode, osg::Group* parentNode, Resource::ImageManager* imageManager, std::vector boundTextures, int animflags, bool skipMeshes, TextKeyMap* textKeys, osg::Node* rootNode=NULL) { + if (rootNode != NULL && Misc::StringUtils::ciEqual(nifNode->name, "Bounding Box")) + return NULL; + osg::ref_ptr node = new osg::MatrixTransform(nifNode->trafo.toMatrix()); // Set a default DataVariance (used as hint by optimization routines). From 342c25dffa082dc10a5a2ecd207cb8abda42fafc Mon Sep 17 00:00:00 2001 From: scrawl Date: Sun, 12 Feb 2017 15:44:23 +0100 Subject: [PATCH 11/14] Fix weather sounds playing at full volume before fading in --- apps/openmw/mwworld/weather.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/weather.cpp b/apps/openmw/mwworld/weather.cpp index b7c5736bd..eaf88d45a 100644 --- a/apps/openmw/mwworld/weather.cpp +++ b/apps/openmw/mwworld/weather.cpp @@ -731,7 +731,7 @@ void WeatherManager::update(float duration, bool paused) { stopSounds(); if (!mResult.mAmbientLoopSoundID.empty()) - mAmbientSound = MWBase::Environment::get().getSoundManager()->playSound(mResult.mAmbientLoopSoundID, 1.0, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); + mAmbientSound = MWBase::Environment::get().getSoundManager()->playSound(mResult.mAmbientLoopSoundID, mResult.mAmbientSoundVolume, 1.0, MWBase::SoundManager::Play_TypeSfx, MWBase::SoundManager::Play_Loop); mPlayingSoundID = mResult.mAmbientLoopSoundID; } From c46eadae901df729ba81b17dc132a5ea4313630b Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 17:17:33 +0100 Subject: [PATCH 12/14] Update actionread.cpp *grrr* Forgot to change the latter "mSkillID" in line 47... --- apps/openmw/mwworld/actionread.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index ffab2959c..b3ac772ae 100644 --- a/apps/openmw/mwworld/actionread.cpp +++ b/apps/openmw/mwworld/actionread.cpp @@ -44,7 +44,7 @@ namespace MWWorld MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); // Skill gain from books - if (ref->mBase->mData.mSkillId >= 0 && ref->mBase->mData.mSkillID < ESM::Skill::Length + if (ref->mBase->mData.mSkillId >= 0 && ref->mBase->mData.mSkillId < ESM::Skill::Length && !npcStats.hasBeenUsed (ref->mBase->mId)) { MWWorld::LiveCellRef *playerRef = actor.get(); From 2d1af3ee4a1bbd45a0e38cee1641f9e37bdc0f09 Mon Sep 17 00:00:00 2001 From: MAtahualpa Date: Sun, 12 Feb 2017 20:55:50 +0100 Subject: [PATCH 13/14] Update record.cpp This should be the last instance of "mSkillID". Let's see what Travis says about it... --- apps/esmtool/record.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/esmtool/record.cpp b/apps/esmtool/record.cpp index 2382612a4..3123308ef 100644 --- a/apps/esmtool/record.cpp +++ b/apps/esmtool/record.cpp @@ -494,7 +494,7 @@ void Record::print() std::cout << " Weight: " << mData.mData.mWeight << std::endl; std::cout << " Value: " << mData.mData.mValue << std::endl; std::cout << " IsScroll: " << mData.mData.mIsScroll << std::endl; - std::cout << " SkillID: " << mData.mData.mSkillID << std::endl; + std::cout << " SkillId: " << mData.mData.mSkillId << std::endl; std::cout << " Enchantment Points: " << mData.mData.mEnchant << std::endl; if (mPrintPlain) { From 5c3f914ff6b8fd8aea10b81e53d39ba316936712 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 13 Feb 2017 10:17:26 +0100 Subject: [PATCH 14/14] updated credits file --- AUTHORS.md | 1 + 1 file changed, 1 insertion(+) diff --git a/AUTHORS.md b/AUTHORS.md index 190a2f345..cb1872b37 100644 --- a/AUTHORS.md +++ b/AUTHORS.md @@ -90,6 +90,7 @@ Programmers Mark Siewert (mark76) Marco Melletti (mellotanica) Marco Schulze + MAtahualpa Mateusz Kołaczek (PL_kolek) Mateusz Malisz (malice) megaton