Change projectile behaviour to be like in vanilla wrt. water plane:

- enchanted arrow explode upon hit the water plane
- non enchanted arrow disappear (or more accurately, they hit nothingness)
- enchanted arrow shot underwater explode immediately
- non enchanted arrow disappear immediately

Also, solve a bug that occured previously and could theoritically still happens where we use the last tested collision position for instead of the last registered hit:
Use the hit position as saved inside Projectile::hit() instead of the last position saved inside the callback.
If a projectile collides with several objects (bottom of the sea and water surface for instance), the last collision tested won't necessarily be the impact position as we have no control over the order in which the tests are performed.
pull/593/head
fredzio 3 years ago
parent f0a77a48df
commit 32108adc31

@ -620,7 +620,7 @@ namespace MWPhysics
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback);
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(resultCallback.m_hitPointWorld);
const auto newpos = projectile->isActive() ? position : Misc::Convert::toOsg(projectile->getHitPosition());
projectile->setPosition(newpos);
mTaskScheduler->updateSingleAabb(foundProjectile->second);
}
@ -693,7 +693,7 @@ namespace MWPhysics
mActors.emplace(ptr, std::move(actor));
}
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater)
int PhysicsSystem::addProjectile (const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius)
{
osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
assert(shapeInstance);
@ -701,7 +701,7 @@ namespace MWPhysics
mProjectileId++;
auto projectile = std::make_shared<Projectile>(caster, position, radius, canTraverseWater, mTaskScheduler.get(), this);
auto projectile = std::make_shared<Projectile>(caster, position, radius, mTaskScheduler.get(), this);
mProjectiles.emplace(mProjectileId, std::move(projectile));
return mProjectileId;

@ -125,7 +125,7 @@ namespace MWPhysics
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh);
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius, bool canTraverseWater);
int addProjectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, const std::string& mesh, bool computeRadius);
void setCaster(int projectileId, const MWWorld::Ptr& caster);
void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
void removeProjectile(const int projectileId);

@ -13,12 +13,10 @@
namespace MWPhysics
{
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
: mCanCrossWaterSurface(canCrossWaterSurface)
, mCrossedWaterSurface(false)
Projectile::Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem)
: mHitWater(false)
, mActive(true)
, mCaster(caster)
, mWaterHitPosition(std::nullopt)
, mPhysics(physicssystem)
, mTaskScheduler(scheduler)
{
@ -72,11 +70,6 @@ osg::Vec3f Projectile::getPosition() const
return mPosition;
}
bool Projectile::canTraverseWater() const
{
return mCanCrossWaterSurface;
}
void Projectile::hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal)
{
if (!mActive.load(std::memory_order_acquire))
@ -127,17 +120,4 @@ bool Projectile::isValidTarget(const MWWorld::Ptr& target) const
return validTarget;
}
std::optional<btVector3> Projectile::getWaterHitPosition()
{
return std::exchange(mWaterHitPosition, std::nullopt);
}
void Projectile::setWaterHitPosition(btVector3 pos)
{
if (mCrossedWaterSurface)
return;
mCrossedWaterSurface = true;
mWaterHitPosition = pos;
}
}

@ -4,7 +4,6 @@
#include <atomic>
#include <memory>
#include <mutex>
#include <optional>
#include <LinearMath/btVector3.h>
@ -32,7 +31,7 @@ namespace MWPhysics
class Projectile final : public PtrHolder
{
public:
Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, bool canCrossWaterSurface, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
Projectile(const MWWorld::Ptr& caster, const osg::Vec3f& position, float radius, PhysicsTaskScheduler* scheduler, PhysicsSystem* physicssystem);
~Projectile() override;
btConvexShape* getConvexShape() const { return mConvexShape; }
@ -61,15 +60,25 @@ namespace MWPhysics
MWWorld::Ptr getCaster() const;
void setCaster(MWWorld::Ptr caster);
bool canTraverseWater() const;
void setHitWater()
{
mHitWater = true;
}
bool getHitWater() const
{
return mHitWater;
}
void hit(MWWorld::Ptr target, btVector3 pos, btVector3 normal);
void setValidTargets(const std::vector<MWWorld::Ptr>& targets);
bool isValidTarget(const MWWorld::Ptr& target) const;
std::optional<btVector3> getWaterHitPosition();
void setWaterHitPosition(btVector3 pos);
btVector3 getHitPosition() const
{
return mHitPosition;
}
private:
@ -78,12 +87,10 @@ namespace MWPhysics
std::unique_ptr<btCollisionObject> mCollisionObject;
bool mTransformUpdatePending;
bool mCanCrossWaterSurface;
bool mCrossedWaterSurface;
bool mHitWater;
std::atomic<bool> mActive;
MWWorld::Ptr mCaster;
MWWorld::Ptr mHitTarget;
std::optional<btVector3> mWaterHitPosition;
osg::Vec3f mPosition;
btVector3 mHitPosition;
btVector3 mHitNormal;

@ -47,9 +47,7 @@ namespace MWPhysics
}
case CollisionType_Water:
{
mProjectile->setWaterHitPosition(m_hitPointWorld);
if (mProjectile->canTraverseWater())
return 1.f;
mProjectile->setHitWater();
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
break;
}

@ -320,7 +320,7 @@ namespace MWWorld
// in case there are multiple effects, the model is a dummy without geometry. Use the second effect for physics shape
if (state.mIdMagic.size() > 1)
model = "meshes\\" + MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>().find(state.mIdMagic.at(1))->mModel;
state.mProjectileId = mPhysics->addProjectile(caster, pos, model, true, false);
state.mProjectileId = mPhysics->addProjectile(caster, pos, model, true);
state.mToDelete = false;
mMagicBolts.push_back(state);
}
@ -345,7 +345,7 @@ namespace MWWorld
if (!ptr.getClass().getEnchantment(ptr).empty())
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr));
state.mProjectileId = mPhysics->addProjectile(actor, pos, model, false, true);
state.mProjectileId = mPhysics->addProjectile(actor, pos, model, false);
state.mToDelete = false;
mProjectiles.push_back(state);
}
@ -496,9 +496,6 @@ namespace MWWorld
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
if (const auto hitWaterPos = projectile->getWaterHitPosition())
mRendering->emitWaterRipple(Misc::Convert::toOsg(*hitWaterPos));
const auto pos = projectile->getPosition();
projectileState.mNode->setPosition(pos);
@ -522,6 +519,8 @@ namespace MWWorld
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
bow = *invIt;
}
if (projectile->getHitWater())
mRendering->emitWaterRipple(pos);
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
cleanupProjectile(projectileState);
@ -654,7 +653,7 @@ namespace MWWorld
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown;
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, false, true);
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, false);
}
catch(...)
{
@ -707,7 +706,7 @@ namespace MWWorld
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture);
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, true, false);
state.mProjectileId = mPhysics->addProjectile(state.getCaster(), osg::Vec3f(esm.mPosition), model, true);
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
for (const std::string &soundid : state.mSoundIds)

@ -3165,6 +3165,7 @@ namespace MWWorld
bool underwater = MWBase::Environment::get().getWorld()->isUnderwater(MWMechanics::getPlayer().getCell(), worldPos);
if (underwater)
{
MWMechanics::projectileHit(actor, Ptr(), bow, projectile, worldPos, attackStrength);
mRendering->emitWaterRipple(worldPos);
return;
}

Loading…
Cancel
Save