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 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) { 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; 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 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, diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 27f59f437..e21d7f480 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 65e5298bf..83af2c8cc 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -574,8 +574,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/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/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 796719448..8fa850019 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -417,10 +417,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; @@ -453,6 +449,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) 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/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. diff --git a/apps/openmw/mwworld/actionread.cpp b/apps/openmw/mwworld/actionread.cpp index 90e9a375b..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(); @@ -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); } 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/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; } diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b409f2b5d..f60694613 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -13,10 +13,11 @@ #include #include +#include #include #include -#include + #include #include @@ -1079,7 +1080,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(); @@ -1099,7 +1100,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()); @@ -1476,7 +1477,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; } @@ -2526,7 +2527,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; @@ -2747,12 +2748,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); @@ -2764,7 +2770,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; @@ -2775,7 +2781,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; } @@ -2785,7 +2791,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 c220932a7..800c8e110 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); 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(); 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; 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).