Add OpenMW 0.47 commits up to 10 Oct 2021

pull/593/head
David Cernat 3 years ago
commit e010c61167

@ -222,6 +222,7 @@ Programmers
Yuri Krupenin Yuri Krupenin
zelurker zelurker
Noah Gooder Noah Gooder
Andrew Appuhamy (andrew-app)
Documentation Documentation
------------- -------------

@ -131,6 +131,7 @@
Bug #6043: Actor can have torch missing when torch animation is played Bug #6043: Actor can have torch missing when torch animation is played
Bug #6047: Mouse bindings can be triggered during save loading Bug #6047: Mouse bindings can be triggered during save loading
Bug #6136: Game freezes when NPCs try to open doors that are about to be closed Bug #6136: Game freezes when NPCs try to open doors that are about to be closed
Bug #6294: Game crashes with empty pathgrid
Feature #390: 3rd person look "over the shoulder" Feature #390: 3rd person look "over the shoulder"
Feature #832: OpenMW-CS: Handle deleted references Feature #832: OpenMW-CS: Handle deleted references
Feature #1536: Show more information about level on menu Feature #1536: Show more information about level on menu
@ -160,6 +161,7 @@
Feature #5519: Code Patch tab in launcher Feature #5519: Code Patch tab in launcher
Feature #5524: Resume failed script execution after reload Feature #5524: Resume failed script execution after reload
Feature #5545: Option to allow stealing from an unconscious NPC during combat Feature #5545: Option to allow stealing from an unconscious NPC during combat
Feature #5551: Do not reboot PC after OpenMW installation on Windows
Feature #5563: Run physics update in background thread Feature #5563: Run physics update in background thread
Feature #5579: MCP SetAngle enhancement Feature #5579: MCP SetAngle enhancement
Feature #5580: Service refusal filtering Feature #5580: Service refusal filtering
@ -174,6 +176,7 @@
Feature #5814: Bsatool should be able to create BSA archives, not only to extract it Feature #5814: Bsatool should be able to create BSA archives, not only to extract it
Feature #5828: Support more than 8 lights Feature #5828: Support more than 8 lights
Feature #5910: Fall back to delta time when physics can't keep up Feature #5910: Fall back to delta time when physics can't keep up
Feature #5980: Support Bullet with double precision instead of one with single precision
Feature #6024: OpenMW-CS: Selecting terrain in "Terrain land editing" should support "Add to selection" and "Remove from selection" modes Feature #6024: OpenMW-CS: Selecting terrain in "Terrain land editing" should support "Add to selection" and "Remove from selection" modes
Feature #6033: Include pathgrid to navigation mesh Feature #6033: Include pathgrid to navigation mesh
Feature #6034: Find path based on area cost depending on NPC stats Feature #6034: Find path based on area cost depending on NPC stats

@ -98,8 +98,8 @@ namespace MWInput
// We keep track of our own mouse position, so that moving the mouse while in // We keep track of our own mouse position, so that moving the mouse while in
// game mode does not move the position of the GUI cursor // game mode does not move the position of the GUI cursor
float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor(); float uiScale = MWBase::Environment::get().getWindowManager()->getScalingFactor();
float xMove = xAxis * dt * 1500.0f / uiScale; float xMove = xAxis * dt * 1500.0f / uiScale * mGamepadCursorSpeed;
float yMove = yAxis * dt * 1500.0f / uiScale; float yMove = yAxis * dt * 1500.0f / uiScale * mGamepadCursorSpeed;
float mouseWheelMove = -zAxis * dt * 1500.0f; float mouseWheelMove = -zAxis * dt * 1500.0f;
if (xMove != 0 || yMove != 0 || mouseWheelMove != 0) if (xMove != 0 || yMove != 0 || mouseWheelMove != 0)

@ -44,14 +44,14 @@ namespace MWMechanics
} }
}; };
bool absorbSpell (const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target) int getAbsorbChance(const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
{ {
if (spellId.empty() || target.isEmpty() || caster == target || !target.getClass().isActor()) if(target.isEmpty() || caster == target || !target.getClass().isActor())
return false; return 0;
CreatureStats& stats = target.getClass().getCreatureStats(target); CreatureStats& stats = target.getClass().getCreatureStats(target);
if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() <= 0.f) if (stats.getMagicEffects().get(ESM::MagicEffect::SpellAbsorption).getMagnitude() <= 0.f)
return false; return 0;
GetAbsorptionProbability check; GetAbsorptionProbability check;
stats.getActiveSpells().visitEffectSources(check); stats.getActiveSpells().visitEffectSources(check);
@ -59,9 +59,12 @@ namespace MWMechanics
if (target.getClass().hasInventoryStore(target)) if (target.getClass().hasInventoryStore(target))
target.getClass().getInventoryStore(target).visitEffectSources(check); target.getClass().getInventoryStore(target).visitEffectSources(check);
int chance = check.mProbability * 100; return check.mProbability * 100;
if (Misc::Rng::roll0to99() >= chance) }
return false;
void absorbSpell (const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target)
{
CreatureStats& stats = target.getClass().getCreatureStats(target);
const auto& esmStore = MWBase::Environment::get().getWorld()->getStore(); const auto& esmStore = MWBase::Environment::get().getWorld()->getStore();
const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find("VFX_Absorb"); const ESM::Static* absorbStatic = esmStore.get<ESM::Static>().find("VFX_Absorb");
@ -85,7 +88,6 @@ namespace MWMechanics
DynamicStat<float> magicka = stats.getMagicka(); DynamicStat<float> magicka = stats.getMagicka();
magicka.setCurrent(magicka.getCurrent() + spellCost); magicka.setCurrent(magicka.getCurrent() + spellCost);
stats.setMagicka(magicka); stats.setMagicka(magicka);
return true;
} }
} }

@ -10,8 +10,9 @@ namespace MWWorld
namespace MWMechanics namespace MWMechanics
{ {
// Try to absorb a spell based on the magnitude of every Spell Absorption effect source on the target. void absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
bool absorbSpell(const std::string& spellId, const MWWorld::Ptr& caster, const MWWorld::Ptr& target); // Calculate the chance to absorb a spell based on the magnitude of every Spell Absorption effect source on the target.
int getAbsorbChance(const MWWorld::Ptr& caster, const MWWorld::Ptr& target);
} }
#endif #endif

@ -136,8 +136,7 @@ namespace MWMechanics
// throughout the iteration of this spell's // throughout the iteration of this spell's
// effects, we display a "can't re-cast" message // effects, we display a "can't re-cast" message
// Try absorbing the spell. Some handling must still happen for absorbed effects. int absorbChance = getAbsorbChance(caster, target);
bool absorbed = absorbSpell(mId, caster, target);
int currentEffectIndex = 0; int currentEffectIndex = 0;
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator effectIt (effects.mList.begin());
@ -159,6 +158,13 @@ namespace MWMechanics
} }
canCastAnEffect = true; canCastAnEffect = true;
// Try absorbing the effect
if(absorbChance && Misc::Rng::roll0to99() < absorbChance)
{
absorbSpell(mId, caster, target);
continue;
}
if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer)) if (!checkEffectTarget(effectIt->mEffectID, target, caster, castByPlayer))
continue; continue;
@ -172,10 +178,6 @@ namespace MWMechanics
if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful) if (target.getClass().isActor() && target != caster && !caster.isEmpty() && isHarmful)
target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true); target.getClass().onHit(target, 0.0f, true, MWWorld::Ptr(), caster, osg::Vec3f(), true);
// Avoid proceeding further for absorbed spells.
if (absorbed)
continue;
// Reflect harmful effects // Reflect harmful effects
if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects)) if (!reflected && reflectEffect(*effectIt, magicEffect, caster, target, reflectedEffects))
continue; continue;

@ -644,7 +644,7 @@ namespace MWPhysics
mTaskScheduler->convexSweepTest(projectile->getConvexShape(), from_, to_, resultCallback); 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); projectile->setPosition(newpos);
mTaskScheduler->updateSingleAabb(foundProjectile->second); mTaskScheduler->updateSingleAabb(foundProjectile->second);
} }
@ -717,7 +717,7 @@ namespace MWPhysics
mActors.emplace(ptr, std::move(actor)); 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); osg::ref_ptr<Resource::BulletShapeInstance> shapeInstance = mShapeManager->getInstance(mesh);
assert(shapeInstance); assert(shapeInstance);
@ -725,7 +725,7 @@ namespace MWPhysics
mProjectileId++; 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)); mProjectiles.emplace(mProjectileId, std::move(projectile));
return mProjectileId; return mProjectileId;

@ -125,7 +125,7 @@ namespace MWPhysics
void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World); void addObject (const MWWorld::Ptr& ptr, const std::string& mesh, int collisionType = CollisionType_World);
void addActor (const MWWorld::Ptr& ptr, const std::string& mesh); 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 setCaster(int projectileId, const MWWorld::Ptr& caster);
void updateProjectile(const int projectileId, const osg::Vec3f &position) const; void updateProjectile(const int projectileId, const osg::Vec3f &position) const;
void removeProjectile(const int projectileId); void removeProjectile(const int projectileId);

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

@ -47,9 +47,7 @@ namespace MWPhysics
} }
case CollisionType_Water: case CollisionType_Water:
{ {
mProjectile->setWaterHitPosition(m_hitPointWorld); mProjectile->setHitWater();
if (mProjectile->canTraverseWater())
return 1.f;
mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld); mProjectile->hit(MWWorld::Ptr(), m_hitPointWorld, m_hitNormalWorld);
break; 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 // 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) if (state.mIdMagic.size() > 1)
model = "meshes\\" + MWBase::Environment::get().getWorld()->getStore().get<ESM::Weapon>().find(state.mIdMagic.at(1))->mModel; 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; state.mToDelete = false;
mMagicBolts.push_back(state); mMagicBolts.push_back(state);
} }
@ -345,7 +345,7 @@ namespace MWWorld
if (!ptr.getClass().getEnchantment(ptr).empty()) if (!ptr.getClass().getEnchantment(ptr).empty())
SceneUtil::addEnchantedGlow(state.mNode, mResourceSystem, ptr.getClass().getEnchantmentColor(ptr)); 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; state.mToDelete = false;
mProjectiles.push_back(state); mProjectiles.push_back(state);
} }
@ -496,9 +496,6 @@ namespace MWWorld
auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId); auto* projectile = mPhysics->getProjectile(projectileState.mProjectileId);
if (const auto hitWaterPos = projectile->getWaterHitPosition())
mRendering->emitWaterRipple(Misc::Convert::toOsg(*hitWaterPos));
const auto pos = projectile->getPosition(); const auto pos = projectile->getPosition();
projectileState.mNode->setPosition(pos); projectileState.mNode->setPosition(pos);
@ -522,6 +519,8 @@ namespace MWWorld
if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId)) if (invIt != inv.end() && Misc::StringUtils::ciEqual(invIt->getCellRef().getRefId(), projectileState.mBowId))
bow = *invIt; bow = *invIt;
} }
if (projectile->getHitWater())
mRendering->emitWaterRipple(pos);
MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength); MWMechanics::projectileHit(caster, target, bow, projectileRef.getPtr(), pos, projectileState.mAttackStrength);
cleanupProjectile(projectileState); cleanupProjectile(projectileState);
@ -654,7 +653,7 @@ namespace MWWorld
int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType; int weaponType = ptr.get<ESM::Weapon>()->mBase->mData.mType;
state.mThrown = MWMechanics::getWeaponType(weaponType)->mWeaponClass == ESM::WeaponType::Thrown; 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(...) catch(...)
{ {
@ -707,7 +706,7 @@ namespace MWWorld
osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects); osg::Vec4 lightDiffuseColor = getMagicBoltLightDiffuseColor(state.mEffects);
createModel(state, model, osg::Vec3f(esm.mPosition), osg::Quat(esm.mOrientation), true, true, lightDiffuseColor, texture); 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(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
for (const std::string &soundid : state.mSoundIds) for (const std::string &soundid : state.mSoundIds)

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

Loading…
Cancel
Save