diff --git a/CHANGELOG.md b/CHANGELOG.md index a2fbdc6eaf..1aa220717d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -61,6 +61,7 @@ Bug #7034: Misc items defined in one content file are not treated as keys if another content file uses them as such Bug #7042: Weapon follow animations that immediately follow the hit animations cause multiple hits Bug #7044: Changing a class' services does not affect autocalculated NPCs + Bug #7053: Running into objects doesn't trigger GetCollidingPC Bug #7054: Quests aren't sorted by name Bug #7064: NPCs don't report crime if the player is casting offensive spells on them while sneaking Bug #7077: OpenMW fails to load certain particle effects in .osgt format diff --git a/apps/openmw/mwphysics/movementsolver.cpp b/apps/openmw/mwphysics/movementsolver.cpp index c0b5014b31..fe5c1a955e 100644 --- a/apps/openmw/mwphysics/movementsolver.cpp +++ b/apps/openmw/mwphysics/movementsolver.cpp @@ -15,6 +15,7 @@ #include "collisiontype.hpp" #include "constants.hpp" #include "contacttestwrapper.h" +#include "object.hpp" #include "physicssystem.hpp" #include "projectile.hpp" #include "projectileconvexcallback.hpp" @@ -243,11 +244,20 @@ namespace MWPhysics float hitHeight = tracer.mHitPoint.z() - tracer.mEndPos.z() + actor.mHalfExtentsZ; osg::Vec3f oldPosition = newPosition; bool usedStepLogic = false; - if (hitHeight < Constants::sStepSizeUp && !isActor(tracer.mHitObject)) + if (!isActor(tracer.mHitObject)) { - // Try to step up onto it. - // NOTE: this modifies newPosition and velocity on its own if successful - usedStepLogic = stepper.step(newPosition, velocity, remainingTime, seenGround, iterations == 0); + if (hitHeight < Constants::sStepSizeUp) + { + // Try to step up onto it. + // NOTE: this modifies newPosition and velocity on its own if successful + usedStepLogic = stepper.step(newPosition, velocity, remainingTime, seenGround, iterations == 0); + } + auto* ptrHolder = static_cast(tracer.mHitObject->getUserPointer()); + if (Object* hitObject = dynamic_cast(ptrHolder)) + { + hitObject->addCollision( + actor.mIsPlayer ? ScriptedCollisionType_Player : ScriptedCollisionType_Actor); + } } if (usedStepLogic) { diff --git a/apps/openmw/mwphysics/object.cpp b/apps/openmw/mwphysics/object.cpp index 9c97ac7c32..5936083f1d 100644 --- a/apps/openmw/mwphysics/object.cpp +++ b/apps/openmw/mwphysics/object.cpp @@ -23,6 +23,7 @@ namespace MWPhysics , mPosition(ptr.getRefData().getPosition().asVec3()) , mRotation(rotation) , mTaskScheduler(scheduler) + , mCollidedWith(ScriptedCollisionType_None) { mCollisionObject = BulletHelpers::makeCollisionObject(mShapeInstance->mCollisionShape.get(), Misc::Convert::toBullet(mPosition), Misc::Convert::toBullet(rotation)); @@ -166,4 +167,20 @@ namespace MWPhysics } return result; } + + bool Object::collidedWith(ScriptedCollisionType type) const + { + return mCollidedWith & type; + } + + void Object::addCollision(ScriptedCollisionType type) + { + std::unique_lock lock(mPositionMutex); + mCollidedWith |= type; + } + + void Object::resetCollisions() + { + mCollidedWith = ScriptedCollisionType_None; + } } diff --git a/apps/openmw/mwphysics/object.hpp b/apps/openmw/mwphysics/object.hpp index 875f12d879..15b7c46926 100644 --- a/apps/openmw/mwphysics/object.hpp +++ b/apps/openmw/mwphysics/object.hpp @@ -18,6 +18,14 @@ namespace MWPhysics { class PhysicsTaskScheduler; + enum ScriptedCollisionType : char + { + ScriptedCollisionType_None = 0, + ScriptedCollisionType_Actor = 1, + // Note that this isn't 3, colliding with a player doesn't count as colliding with an actor + ScriptedCollisionType_Player = 2 + }; + class Object final : public PtrHolder { public: @@ -38,6 +46,9 @@ namespace MWPhysics /// @brief update object shape /// @return true if shape changed bool animateCollisionShapes(); + bool collidedWith(ScriptedCollisionType type) const; + void addCollision(ScriptedCollisionType type); + void resetCollisions(); private: osg::ref_ptr mShapeInstance; @@ -50,6 +61,7 @@ namespace MWPhysics bool mTransformUpdatePending = false; mutable std::mutex mPositionMutex; PhysicsTaskScheduler* mTaskScheduler; + char mCollidedWith; }; } diff --git a/apps/openmw/mwphysics/physicssystem.cpp b/apps/openmw/mwphysics/physicssystem.cpp index 2196834a50..149113dfb1 100644 --- a/apps/openmw/mwphysics/physicssystem.cpp +++ b/apps/openmw/mwphysics/physicssystem.cpp @@ -673,12 +673,13 @@ namespace MWPhysics // Slow fall reduces fall speed by a factor of (effect magnitude / 200) const float slowFall = 1.f - std::clamp(effects.getOrDefault(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f, 0.f, 1.f); - const bool godmode = ptr == world->getPlayerConstPtr() && world->getGodModeState(); + const bool isPlayer = ptr == world->getPlayerConstPtr(); + const bool godmode = isPlayer && world->getGodModeState(); const bool inert = stats.isDead() || (!godmode && stats.getMagicEffects().getOrDefault(ESM::MagicEffect::Paralyze).getModifier() > 0); simulations.emplace_back(ActorSimulation{ - physicActor, ActorFrameData{ *physicActor, inert, waterCollision, slowFall, waterlevel } }); + physicActor, ActorFrameData{ *physicActor, inert, waterCollision, slowFall, waterlevel, isPlayer } }); // if the simulation will run, a jump request will be fulfilled. Update mechanics accordingly. if (willSimulate) @@ -708,6 +709,8 @@ namespace MWPhysics changed = false; } } + for (auto& [_, object] : mObjects) + object->resetCollisions(); #ifndef BT_NO_PROFILE CProfileManager::Reset(); @@ -782,10 +785,12 @@ namespace MWPhysics } } - bool PhysicsSystem::isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const + bool PhysicsSystem::isObjectCollidingWith(const MWWorld::ConstPtr& object, ScriptedCollisionType type) const { - std::vector collisions = getCollisions(object, CollisionType_World, CollisionType_Actor); - return (std::find(collisions.begin(), collisions.end(), actor) != collisions.end()); + auto found = mObjects.find(object.mRef); + if (found != mObjects.end()) + return found->second->collidedWith(type); + return false; } void PhysicsSystem::getActorsCollidingWith(const MWWorld::ConstPtr& object, std::vector& out) const @@ -890,7 +895,8 @@ namespace MWPhysics mDebugDrawer->addCollision(position, normal); } - ActorFrameData::ActorFrameData(Actor& actor, bool inert, bool waterCollision, float slowFall, float waterlevel) + ActorFrameData::ActorFrameData( + Actor& actor, bool inert, bool waterCollision, float slowFall, float waterlevel, bool isPlayer) : mPosition() , mStandingOn(nullptr) , mIsOnGround(actor.getOnGround()) @@ -917,6 +923,7 @@ namespace MWPhysics , mIsAquatic(actor.getPtr().getClass().isPureWaterCreature(actor.getPtr())) , mWaterCollision(waterCollision) , mSkipCollisionDetection(!actor.getCollisionMode()) + , mIsPlayer(isPlayer) { } diff --git a/apps/openmw/mwphysics/physicssystem.hpp b/apps/openmw/mwphysics/physicssystem.hpp index ad56581eb3..6734682092 100644 --- a/apps/openmw/mwphysics/physicssystem.hpp +++ b/apps/openmw/mwphysics/physicssystem.hpp @@ -56,6 +56,7 @@ namespace MWPhysics class Actor; class PhysicsTaskScheduler; class Projectile; + enum ScriptedCollisionType : char; using ActorMap = std::unordered_map>; @@ -79,7 +80,7 @@ namespace MWPhysics struct ActorFrameData { - ActorFrameData(Actor& actor, bool inert, bool waterCollision, float slowFall, float waterlevel); + ActorFrameData(Actor& actor, bool inert, bool waterCollision, float slowFall, float waterlevel, bool isPlayer); osg::Vec3f mPosition; osg::Vec3f mInertia; const btCollisionObject* mStandingOn; @@ -102,6 +103,7 @@ namespace MWPhysics const bool mIsAquatic; const bool mWaterCollision; const bool mSkipCollisionDetection; + const bool mIsPlayer; }; struct ProjectileFrameData @@ -258,9 +260,8 @@ namespace MWPhysics /// Get the handle of all actors standing on \a object in this frame. void getActorsStandingOn(const MWWorld::ConstPtr& object, std::vector& out) const; - /// Return true if \a actor has collided with \a object in this frame. - /// This will detect running into objects, but will not detect climbing stairs, stepping up a small object, etc. - bool isActorCollidingWith(const MWWorld::Ptr& actor, const MWWorld::ConstPtr& object) const; + /// Return true if an object of the given type has collided with this object + bool isObjectCollidingWith(const MWWorld::ConstPtr& object, ScriptedCollisionType type) const; /// Get the handle of all actors colliding with \a object in this frame. void getActorsCollidingWith(const MWWorld::ConstPtr& object, std::vector& out) const; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f20cbd208f..c6282c6f5a 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2429,15 +2429,12 @@ namespace MWWorld bool World::getPlayerCollidingWith(const MWWorld::ConstPtr& object) { - MWWorld::Ptr player = getPlayerPtr(); - return mPhysics->isActorCollidingWith(player, object); + return mPhysics->isObjectCollidingWith(object, MWPhysics::ScriptedCollisionType_Player); } bool World::getActorCollidingWith(const MWWorld::ConstPtr& object) { - std::vector actors; - mPhysics->getActorsCollidingWith(object, actors); - return !actors.empty(); + return mPhysics->isObjectCollidingWith(object, MWPhysics::ScriptedCollisionType_Actor); } void World::hurtStandingActors(const ConstPtr& object, float healthPerSecond)