#include #include #include #include "actor.hpp" #include "collisiontype.hpp" #include "mtphysics.hpp" #include "object.hpp" #include "projectile.hpp" namespace MWPhysics { Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem) : PtrHolder(MWWorld::Ptr(), position) , mHitWater(false) , mActive(true) , mHitTarget(nullptr) , mPhysics(physicssystem) , mTaskScheduler(scheduler) { mShape = std::make_unique(radius); mConvexShape = static_cast(mShape.get()); mCollisionObject = std::make_unique(); mCollisionObject->setCollisionFlags(btCollisionObject::CF_KINEMATIC_OBJECT); mCollisionObject->setActivationState(DISABLE_DEACTIVATION); mCollisionObject->setCollisionShape(mShape.get()); mCollisionObject->setUserPointer(this); mPosition = position; mPreviousPosition = position; mSimulationPosition = position; setCaster(caster); const int collisionMask = CollisionType_World | CollisionType_HeightMap | CollisionType_Actor | CollisionType_Door | CollisionType_Water | CollisionType_Projectile; mTaskScheduler->addCollisionObject(mCollisionObject.get(), CollisionType_Projectile, collisionMask); updateCollisionObjectPosition(); } Projectile::~Projectile() { if (!mActive) mPhysics->reportCollision(mHitPosition, mHitNormal); mTaskScheduler->removeCollisionObject(mCollisionObject.get()); } void Projectile::updateCollisionObjectPosition() { std::scoped_lock lock(mMutex); auto& trans = mCollisionObject->getWorldTransform(); trans.setOrigin(Misc::Convert::toBullet(mPosition)); mCollisionObject->setWorldTransform(trans); } void Projectile::hit(const btCollisionObject* target, btVector3 pos, btVector3 normal) { bool active = true; if (!mActive.compare_exchange_strong(active, false, std::memory_order_relaxed) || !active) return; mHitTarget = target; mHitPosition = pos; mHitNormal = normal; } MWWorld::Ptr Projectile::getTarget() const { assert(!mActive); auto* target = static_cast(mHitTarget->getUserPointer()); return target ? target->getPtr() : MWWorld::Ptr(); } MWWorld::Ptr Projectile::getCaster() const { return mCaster; } void Projectile::setCaster(const MWWorld::Ptr& caster) { mCaster = caster; mCasterColObj = [this, &caster]() -> const btCollisionObject* { const Actor* actor = mPhysics->getActor(caster); if (actor) return actor->getCollisionObject(); const Object* object = mPhysics->getObject(caster); if (object) return object->getCollisionObject(); return nullptr; }(); } void Projectile::setValidTargets(const std::vector& targets) { std::scoped_lock lock(mMutex); mValidTargets.clear(); for (const auto& ptr : targets) { const auto* physicActor = mPhysics->getActor(ptr); if (physicActor) mValidTargets.push_back(physicActor->getCollisionObject()); } } bool Projectile::isValidTarget(const btCollisionObject* target) const { assert(target); std::scoped_lock lock(mMutex); if (mCasterColObj == target) return false; if (mValidTargets.empty()) return true; return std::any_of(mValidTargets.begin(), mValidTargets.end(), [target](const btCollisionObject* actor) { return target == actor; }); } }