mirror of
https://github.com/OpenMW/openmw.git
synced 2025-02-01 16:45:35 +00:00
Optimize actors processing
1. Do not update physics and animations for actors outside processing range (bug #4647) 2. Do not render such actors 3. Add transparency to actors near processing border, so they will not pop up suddenly
This commit is contained in:
parent
6c4116cc8b
commit
e7de6b974a
19 changed files with 258 additions and 73 deletions
|
@ -233,6 +233,10 @@ namespace MWBase
|
|||
|
||||
virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell) = 0;
|
||||
|
||||
virtual void processChangedSettings (const std::set< std::pair<std::string, std::string> >& settings) = 0;
|
||||
|
||||
virtual float getActorsProcessingRange() const = 0;
|
||||
|
||||
/// Check if the target actor was detected by an observer
|
||||
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
|
||||
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;
|
||||
|
|
|
@ -302,6 +302,9 @@ namespace MWBase
|
|||
|
||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
|
||||
|
||||
virtual void setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled) = 0;
|
||||
virtual bool isActorCollisionEnabled(const MWWorld::Ptr& ptr) = 0;
|
||||
|
||||
virtual bool toggleCollisionMode() = 0;
|
||||
///< Toggle collision mode for player. If disabled player object should ignore
|
||||
/// collisions and gravity.
|
||||
|
|
|
@ -20,6 +20,7 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/inputmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
||||
#include "confirmationdialog.hpp"
|
||||
|
@ -437,6 +438,7 @@ namespace MWGui
|
|||
MWBase::Environment::get().getSoundManager()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getInputManager()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getMechanicsManager()->processChangedSettings(changed);
|
||||
}
|
||||
|
||||
void SettingsWindow::onKeyboardSwitchClicked(MyGUI::Widget* _sender)
|
||||
|
|
|
@ -147,9 +147,6 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
|
|||
|
||||
namespace MWMechanics
|
||||
{
|
||||
const float aiProcessingDistance = 7168;
|
||||
const float sqrAiProcessingDistance = aiProcessingDistance*aiProcessingDistance;
|
||||
|
||||
class SoulTrap : public MWMechanics::EffectSourceVisitor
|
||||
{
|
||||
MWWorld::Ptr mCreature;
|
||||
|
@ -364,7 +361,8 @@ namespace MWMechanics
|
|||
const ESM::Position& actor1Pos = actor1.getRefData().getPosition();
|
||||
const ESM::Position& actor2Pos = actor2.getRefData().getPosition();
|
||||
float sqrDist = (actor1Pos.asVec3() - actor2Pos.asVec3()).length2();
|
||||
if (sqrDist > sqrAiProcessingDistance)
|
||||
|
||||
if (sqrDist > mActorsProcessingRange*mActorsProcessingRange)
|
||||
return;
|
||||
|
||||
// No combat for totally static creatures
|
||||
|
@ -1130,8 +1128,11 @@ namespace MWMechanics
|
|||
}
|
||||
}
|
||||
|
||||
Actors::Actors() {
|
||||
Actors::Actors()
|
||||
{
|
||||
mTimerDisposeSummonsCorpses = 0.2f; // We should add a delay between summoned creature death and its corpse despawning
|
||||
|
||||
updateProcessingRange();
|
||||
}
|
||||
|
||||
Actors::~Actors()
|
||||
|
@ -1139,6 +1140,23 @@ namespace MWMechanics
|
|||
clear();
|
||||
}
|
||||
|
||||
float Actors::getProcessingRange() const
|
||||
{
|
||||
return mActorsProcessingRange;
|
||||
}
|
||||
|
||||
void Actors::updateProcessingRange()
|
||||
{
|
||||
// We have to cap it since using high values (larger than 7168) will make some quests harder or impossible to complete (bug #1876)
|
||||
static const float maxProcessingRange = 7168.f;
|
||||
static const float minProcessingRange = maxProcessingRange / 2.f;
|
||||
|
||||
float actorsProcessingRange = Settings::Manager::getFloat("actors processing range", "Game");
|
||||
actorsProcessingRange = std::min(actorsProcessingRange, maxProcessingRange);
|
||||
actorsProcessingRange = std::max(actorsProcessingRange, minProcessingRange);
|
||||
mActorsProcessingRange = actorsProcessingRange;
|
||||
}
|
||||
|
||||
void Actors::addActor (const MWWorld::Ptr& ptr, bool updateImmediately)
|
||||
{
|
||||
removeActor(ptr);
|
||||
|
@ -1184,7 +1202,7 @@ namespace MWMechanics
|
|||
// Otherwise check if any actor in AI processing range sees the target actor
|
||||
std::vector<MWWorld::Ptr> actors;
|
||||
osg::Vec3f position (actor.getRefData().getPosition().asVec3());
|
||||
getObjectsInRange(position, aiProcessingDistance, actors);
|
||||
getObjectsInRange(position, mActorsProcessingRange, actors);
|
||||
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it)
|
||||
{
|
||||
if (*it == actor)
|
||||
|
@ -1242,7 +1260,7 @@ namespace MWMechanics
|
|||
{
|
||||
if (iter->first == player) continue;
|
||||
|
||||
bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= sqrAiProcessingDistance;
|
||||
bool inProcessingRange = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2() <= mActorsProcessingRange*mActorsProcessingRange;
|
||||
if (inProcessingRange)
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||
|
@ -1288,7 +1306,8 @@ namespace MWMechanics
|
|||
if (timerUpdateEquippedLight >= updateEquippedLightInterval) timerUpdateEquippedLight = 0;
|
||||
|
||||
// show torches only when there are darkness and no precipitations
|
||||
bool showTorches = MWBase::Environment::get().getWorld()->useTorches();
|
||||
MWBase::World* world = MWBase::Environment::get().getWorld();
|
||||
bool showTorches = world->useTorches();
|
||||
|
||||
MWWorld::Ptr player = getPlayer();
|
||||
const osg::Vec3f playerPos = player.getRefData().getPosition().asVec3();
|
||||
|
@ -1301,7 +1320,7 @@ namespace MWMechanics
|
|||
int attackedByPlayerId = player.getClass().getCreatureStats(player).getHitAttemptActorId();
|
||||
if (attackedByPlayerId != -1)
|
||||
{
|
||||
const MWWorld::Ptr playerHitAttemptActor = MWBase::Environment::get().getWorld()->searchPtrViaActorId(attackedByPlayerId);
|
||||
const MWWorld::Ptr playerHitAttemptActor = world->searchPtrViaActorId(attackedByPlayerId);
|
||||
|
||||
if (!playerHitAttemptActor.isInCell())
|
||||
player.getClass().getCreatureStats(player).setHitAttemptActorId(-1);
|
||||
|
@ -1314,14 +1333,11 @@ namespace MWMechanics
|
|||
CharacterController* ctrl = iter->second->getCharacterController();
|
||||
|
||||
float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2();
|
||||
// AI processing is only done within distance of 7168 units to the player. Note the "AI distance" slider doesn't affect this
|
||||
// (it only does some throttling for targets beyond the "AI distance", so doesn't give any guarantees as to whether AI will be enabled or not)
|
||||
// This distance could be made configurable later, but the setting must be marked with a big warning:
|
||||
// using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876)
|
||||
bool inProcessingRange = distSqr <= sqrAiProcessingDistance;
|
||||
// AI processing is only done within given distance to the player.
|
||||
bool inProcessingRange = distSqr <= mActorsProcessingRange*mActorsProcessingRange;
|
||||
|
||||
if (isPlayer)
|
||||
ctrl->setAttackingOrSpell(MWBase::Environment::get().getWorld()->getPlayer().getAttackingOrSpell());
|
||||
ctrl->setAttackingOrSpell(world->getPlayer().getAttackingOrSpell());
|
||||
|
||||
// If dead or no longer in combat, no longer store any actors who attempted to hit us. Also remove for the player.
|
||||
if (iter->first != player && (iter->first.getClass().getCreatureStats(iter->first).isDead()
|
||||
|
@ -1335,10 +1351,10 @@ namespace MWMechanics
|
|||
|
||||
if (!iter->first.getClass().getCreatureStats(iter->first).isDead())
|
||||
{
|
||||
bool cellChanged = MWBase::Environment::get().getWorld()->hasCellChanged();
|
||||
bool cellChanged = world->hasCellChanged();
|
||||
MWWorld::Ptr actor = iter->first; // make a copy of the map key to avoid it being invalidated when the player teleports
|
||||
updateActor(actor, duration);
|
||||
if (!cellChanged && MWBase::Environment::get().getWorld()->hasCellChanged())
|
||||
if (!cellChanged && world->hasCellChanged())
|
||||
{
|
||||
return; // for now abort update of the old cell when cell changes by teleportation magic effect
|
||||
// a better solution might be to apply cell changes at the end of the frame
|
||||
|
@ -1363,7 +1379,7 @@ namespace MWMechanics
|
|||
MWWorld::Ptr headTrackTarget;
|
||||
|
||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||
bool firstPersonPlayer = isPlayer && MWBase::Environment::get().getWorld()->isFirstPerson();
|
||||
bool firstPersonPlayer = isPlayer && world->isFirstPerson();
|
||||
|
||||
// 1. Unconsious actor can not track target
|
||||
// 2. Actors in combat and pursue mode do not bother to headtrack
|
||||
|
@ -1423,27 +1439,25 @@ namespace MWMechanics
|
|||
CharacterController* playerCharacter = nullptr;
|
||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
const float animationDistance = aiProcessingDistance + 400; // Slightly larger than AI distance so there is time to switch back to the idle animation.
|
||||
const float distSqr = (playerPos - iter->first.getRefData().getPosition().asVec3()).length2();
|
||||
const float dist = (playerPos - iter->first.getRefData().getPosition().asVec3()).length();
|
||||
bool isPlayer = iter->first == player;
|
||||
bool inAnimationRange = isPlayer || (animationDistance == 0 || distSqr <= animationDistance*animationDistance);
|
||||
bool inRange = isPlayer || dist <= mActorsProcessingRange;
|
||||
int activeFlag = 1; // Can be changed back to '2' to keep updating bounding boxes off screen (more accurate, but slower)
|
||||
if (isPlayer)
|
||||
activeFlag = 2;
|
||||
int active = inAnimationRange ? activeFlag : 0;
|
||||
bool canFly = iter->first.getClass().canFly(iter->first);
|
||||
if (canFly)
|
||||
{
|
||||
// Keep animating flying creatures so they don't just hover in-air
|
||||
inAnimationRange = true;
|
||||
active = std::max(1, active);
|
||||
}
|
||||
int active = inRange ? activeFlag : 0;
|
||||
|
||||
CharacterController* ctrl = iter->second->getCharacterController();
|
||||
ctrl->setActive(active);
|
||||
|
||||
if (!inAnimationRange)
|
||||
if (!inRange)
|
||||
{
|
||||
iter->first.getRefData().getBaseNode()->setNodeMask(0);
|
||||
world->setActorCollisionMode(iter->first, false);
|
||||
continue;
|
||||
}
|
||||
else if (!isPlayer)
|
||||
iter->first.getRefData().getBaseNode()->setNodeMask(1<<3);
|
||||
|
||||
if (iter->first.getClass().getCreatureStats(iter->first).isParalyzed())
|
||||
ctrl->skipAnim();
|
||||
|
@ -1455,11 +1469,28 @@ namespace MWMechanics
|
|||
playerCharacter = ctrl;
|
||||
continue;
|
||||
}
|
||||
|
||||
world->setActorCollisionMode(iter->first, true);
|
||||
ctrl->update(duration);
|
||||
|
||||
// Fade away actors on large distance (>90% of actor's processing distance)
|
||||
float visibilityRatio = 1.0;
|
||||
float fadeStartDistance = mActorsProcessingRange*0.9f;
|
||||
float fadeEndDistance = mActorsProcessingRange;
|
||||
float fadeRatio = (dist - fadeStartDistance)/(fadeEndDistance - fadeStartDistance);
|
||||
if (fadeRatio > 0)
|
||||
visibilityRatio -= std::max(0.f, fadeRatio);
|
||||
|
||||
visibilityRatio = std::min(1.f, visibilityRatio);
|
||||
|
||||
ctrl->setVisibility(visibilityRatio);
|
||||
}
|
||||
|
||||
if (playerCharacter)
|
||||
{
|
||||
playerCharacter->update(duration);
|
||||
playerCharacter->setVisibility(1.f);
|
||||
}
|
||||
|
||||
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
|
||||
{
|
||||
|
@ -1671,7 +1702,7 @@ namespace MWMechanics
|
|||
restoreDynamicStats(iter->first, sleep);
|
||||
|
||||
if ((!iter->first.getRefData().getBaseNode()) ||
|
||||
(playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > sqrAiProcessingDistance)
|
||||
(playerPos - iter->first.getRefData().getPosition().asVec3()).length2() > mActorsProcessingRange*mActorsProcessingRange)
|
||||
continue;
|
||||
|
||||
adjustMagicEffects (iter->first);
|
||||
|
@ -1915,7 +1946,7 @@ namespace MWMechanics
|
|||
std::list<MWWorld::Ptr> list;
|
||||
std::vector<MWWorld::Ptr> neighbors;
|
||||
osg::Vec3f position (actor.getRefData().getPosition().asVec3());
|
||||
getObjectsInRange(position, aiProcessingDistance, neighbors);
|
||||
getObjectsInRange(position, mActorsProcessingRange, neighbors);
|
||||
for(auto neighbor = neighbors.begin(); neighbor != neighbors.end(); ++neighbor)
|
||||
{
|
||||
if (*neighbor == actor)
|
||||
|
@ -1936,7 +1967,7 @@ namespace MWMechanics
|
|||
std::list<MWWorld::Ptr> list;
|
||||
std::vector<MWWorld::Ptr> neighbors;
|
||||
osg::Vec3f position (actor.getRefData().getPosition().asVec3());
|
||||
getObjectsInRange(position, aiProcessingDistance, neighbors);
|
||||
getObjectsInRange(position, mActorsProcessingRange, neighbors);
|
||||
|
||||
std::set<MWWorld::Ptr> followers;
|
||||
getActorsFollowing(actor, followers);
|
||||
|
|
|
@ -65,6 +65,9 @@ namespace MWMechanics
|
|||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||
void updateMagicEffects (const MWWorld::Ptr& ptr);
|
||||
|
||||
void updateProcessingRange();
|
||||
float getProcessingRange() const;
|
||||
|
||||
void addActor (const MWWorld::Ptr& ptr, bool updateImmediately=false);
|
||||
///< Register an actor for stats management
|
||||
///
|
||||
|
@ -168,6 +171,7 @@ namespace MWMechanics
|
|||
private:
|
||||
PtrActorMap mActors;
|
||||
float mTimerDisposeSummonsCorpses;
|
||||
float mActorsProcessingRange;
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -103,7 +103,7 @@ bool MWMechanics::AiPackage::pathTo(const MWWorld::Ptr& actor, const ESM::Pathgr
|
|||
ESM::Position pos = actor.getRefData().getPosition(); //position of the actor
|
||||
|
||||
/// Stops the actor when it gets too close to a unloaded cell
|
||||
//... At current time, this test is unnecessary. AI shuts down when actor is more than 7168
|
||||
//... At current time, this test is unnecessary. AI shuts down when actor is more than "actors processing range" setting value
|
||||
//... units from player, and exterior cells are 8192 units long and wide.
|
||||
//... But AI processing distance may increase in the future.
|
||||
if (isNearInactiveCell(pos))
|
||||
|
@ -354,7 +354,7 @@ bool MWMechanics::AiPackage::isNearInactiveCell(const ESM::Position& actorPos)
|
|||
|
||||
// currently assumes 3 x 3 grid for exterior cells, with player at center cell.
|
||||
// ToDo: (Maybe) use "exterior cell load distance" setting to get count of actual active cells
|
||||
// While AI Process distance is 7168, AI shuts down actors before they reach edges of 3 x 3 grid.
|
||||
// AI shuts down actors before they reach edges of 3 x 3 grid.
|
||||
const float distanceFromEdge = 200.0;
|
||||
float minThreshold = (-1.0f * ESM::Land::REAL_SIZE) + distanceFromEdge;
|
||||
float maxThreshold = (2.0f * ESM::Land::REAL_SIZE) - distanceFromEdge;
|
||||
|
|
|
@ -3,8 +3,9 @@
|
|||
#include <components/esm/aisequence.hpp>
|
||||
#include <components/esm/loadcell.hpp>
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
@ -21,7 +22,8 @@ bool isWithinMaxRange(const osg::Vec3f& pos1, const osg::Vec3f& pos2)
|
|||
// Maximum travel distance for vanilla compatibility.
|
||||
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
|
||||
// We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
|
||||
return (pos1 - pos2).length2() <= 7168*7168;
|
||||
bool aiDistance = MWBase::Environment::get().getMechanicsManager()->getActorsProcessingRange();
|
||||
return (pos1 - pos2).length2() <= aiDistance*aiDistance;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
#include "../mwrender/animation.hpp"
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
|
@ -1849,7 +1850,7 @@ void CharacterController::updateAnimQueue()
|
|||
mAnimation->setLoopingEnabled(mAnimQueue.front().mGroup, mAnimQueue.size() <= 1);
|
||||
}
|
||||
|
||||
void CharacterController::update(float duration)
|
||||
void CharacterController::update(float duration, bool animationOnly)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Class &cls = mPtr.getClass();
|
||||
|
@ -2235,10 +2236,10 @@ void CharacterController::update(float duration)
|
|||
world->rotateObject(mPtr, rot.x(), rot.y(), 0.0f, true);
|
||||
}
|
||||
|
||||
if (!mMovementAnimationControlled)
|
||||
if (!animationOnly && !mMovementAnimationControlled)
|
||||
world->queueMovement(mPtr, vec);
|
||||
}
|
||||
else
|
||||
else if (!animationOnly)
|
||||
// We must always queue movement, even if there is none, to apply gravity.
|
||||
world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f));
|
||||
|
||||
|
@ -2261,7 +2262,8 @@ void CharacterController::update(float duration)
|
|||
playDeath(1.f, mDeathState);
|
||||
}
|
||||
// We must always queue movement, even if there is none, to apply gravity.
|
||||
world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f));
|
||||
if (!animationOnly)
|
||||
world->queueMovement(mPtr, osg::Vec3f(0.f, 0.f, 0.f));
|
||||
}
|
||||
|
||||
bool isPersist = isPersistentAnimPlaying();
|
||||
|
@ -2295,7 +2297,7 @@ void CharacterController::update(float duration)
|
|||
moved.z() = 1.0;
|
||||
|
||||
// Update movement
|
||||
if(mMovementAnimationControlled && mPtr.getClass().isActor())
|
||||
if(!animationOnly && mMovementAnimationControlled && mPtr.getClass().isActor())
|
||||
world->queueMovement(mPtr, moved);
|
||||
|
||||
mSkipAnim = false;
|
||||
|
@ -2544,20 +2546,6 @@ void CharacterController::updateMagicEffects()
|
|||
{
|
||||
if (!mPtr.getClass().isActor())
|
||||
return;
|
||||
float alpha = 1.f;
|
||||
if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getModifier()) // Ignore base magnitude (see bug #3555).
|
||||
{
|
||||
if (mPtr == getPlayer())
|
||||
alpha = 0.4f;
|
||||
else
|
||||
alpha = 0.f;
|
||||
}
|
||||
float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude();
|
||||
if (chameleon)
|
||||
{
|
||||
alpha *= std::max(0.2f, (100.f - chameleon)/100.f);
|
||||
}
|
||||
mAnimation->setAlpha(alpha);
|
||||
|
||||
bool vampire = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Vampirism).getMagnitude() > 0.0f;
|
||||
mAnimation->setVampire(vampire);
|
||||
|
@ -2566,6 +2554,32 @@ void CharacterController::updateMagicEffects()
|
|||
mAnimation->setLightEffect(light);
|
||||
}
|
||||
|
||||
void CharacterController::setVisibility(float visibility)
|
||||
{
|
||||
// We should take actor's invisibility in account
|
||||
if (mPtr.getClass().isActor())
|
||||
{
|
||||
float alpha = 1.f;
|
||||
if (mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Invisibility).getModifier()) // Ignore base magnitude (see bug #3555).
|
||||
{
|
||||
if (mPtr == getPlayer())
|
||||
alpha = 0.4f;
|
||||
else
|
||||
alpha = 0.f;
|
||||
}
|
||||
float chameleon = mPtr.getClass().getCreatureStats(mPtr).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude();
|
||||
if (chameleon)
|
||||
{
|
||||
alpha *= std::max(0.2f, (100.f - chameleon)/100.f);
|
||||
}
|
||||
|
||||
visibility = std::min(visibility, alpha);
|
||||
}
|
||||
|
||||
// TODO: implement a dithering shader rather than just change object transparency.
|
||||
mAnimation->setAlpha(visibility);
|
||||
}
|
||||
|
||||
void CharacterController::setAttackTypeBasedOnMovement()
|
||||
{
|
||||
float *move = mPtr.getClass().getMovementSettings(mPtr).mPosition;
|
||||
|
|
|
@ -257,7 +257,7 @@ public:
|
|||
|
||||
void updatePtr(const MWWorld::Ptr &ptr);
|
||||
|
||||
void update(float duration);
|
||||
void update(float duration, bool animationOnly=false);
|
||||
|
||||
void persistAnimationState();
|
||||
void unpersistAnimationState();
|
||||
|
@ -292,6 +292,7 @@ public:
|
|||
bool isTurning() const;
|
||||
bool isAttackingOrSpell() const;
|
||||
|
||||
void setVisibility(float visibility);
|
||||
void setAttackingOrSpell(bool attackingOrSpell);
|
||||
void castSpell(const std::string spellId, bool manualSpell=false);
|
||||
void setAIAttackType(const std::string& attackType);
|
||||
|
|
|
@ -431,6 +431,25 @@ namespace MWMechanics
|
|||
mObjects.update(duration, paused);
|
||||
}
|
||||
|
||||
void MechanicsManager::processChangedSettings(const Settings::CategorySettingVector &changed)
|
||||
{
|
||||
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); it != changed.end(); ++it)
|
||||
{
|
||||
if (it->first == "Game" && it->second == "actors processing range")
|
||||
{
|
||||
mActors.updateProcessingRange();
|
||||
|
||||
// Update mechanics for new processing range immediately
|
||||
update(0.f, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float MechanicsManager::getActorsProcessingRange() const
|
||||
{
|
||||
return mActors.getProcessingRange();
|
||||
}
|
||||
|
||||
bool MechanicsManager::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
|
||||
{
|
||||
return mActors.isActorDetected(actor, observer);
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifndef GAME_MWMECHANICS_MECHANICSMANAGERIMP_H
|
||||
#define GAME_MWMECHANICS_MECHANICSMANAGERIMP_H
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
@ -206,6 +208,10 @@ namespace MWMechanics
|
|||
|
||||
virtual void castSpell(const MWWorld::Ptr& ptr, const std::string spellId, bool manualSpell=false);
|
||||
|
||||
void processChangedSettings(const Settings::CategorySettingVector& settings) override;
|
||||
|
||||
virtual float getActorsProcessingRange() const;
|
||||
|
||||
/// Check if the target actor was detected by an observer
|
||||
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
|
||||
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer);
|
||||
|
|
|
@ -1366,6 +1366,33 @@ namespace MWPhysics
|
|||
return false;
|
||||
}
|
||||
|
||||
void PhysicsSystem::setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled)
|
||||
{
|
||||
ActorMap::iterator found = mActors.find(ptr);
|
||||
if (found != mActors.end())
|
||||
{
|
||||
bool cmode = found->second->getCollisionMode();
|
||||
if (cmode == enabled)
|
||||
return;
|
||||
|
||||
cmode = enabled;
|
||||
found->second->enableCollisionMode(cmode);
|
||||
found->second->enableCollisionBody(cmode);
|
||||
}
|
||||
}
|
||||
|
||||
bool PhysicsSystem::isActorCollisionEnabled(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
ActorMap::iterator found = mActors.find(ptr);
|
||||
if (found != mActors.end())
|
||||
{
|
||||
bool cmode = found->second->getCollisionMode();
|
||||
return cmode;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void PhysicsSystem::queueObjectMovement(const MWWorld::Ptr &ptr, const osg::Vec3f &movement)
|
||||
{
|
||||
PtrVelocityList::iterator iter = mMovementQueue.begin();
|
||||
|
|
|
@ -86,6 +86,8 @@ namespace MWPhysics
|
|||
void removeHeightField (int x, int y);
|
||||
|
||||
bool toggleCollisionMode();
|
||||
bool isActorCollisionEnabled(const MWWorld::Ptr& ptr);
|
||||
void setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled);
|
||||
|
||||
void stepSimulation(float dt);
|
||||
void debugDraw();
|
||||
|
|
|
@ -1748,21 +1748,31 @@ namespace MWRender
|
|||
|
||||
if (alpha != 1.f)
|
||||
{
|
||||
osg::StateSet* stateset (new osg::StateSet);
|
||||
// If we have an existing material for alpha transparency, just override alpha level
|
||||
osg::StateSet* stateset = mObjectRoot->getOrCreateStateSet();
|
||||
osg::Material* material = static_cast<osg::Material*>(stateset->getAttribute(osg::StateAttribute::MATERIAL));
|
||||
if (material)
|
||||
{
|
||||
material->setAlpha(osg::Material::FRONT_AND_BACK, alpha);
|
||||
}
|
||||
else
|
||||
{
|
||||
osg::StateSet* stateset (new osg::StateSet);
|
||||
|
||||
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
||||
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
osg::BlendFunc* blendfunc (new osg::BlendFunc);
|
||||
stateset->setAttributeAndModes(blendfunc, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
||||
// FIXME: overriding diffuse/ambient/emissive colors
|
||||
osg::Material* material (new osg::Material);
|
||||
material->setColorMode(osg::Material::OFF);
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha));
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
|
||||
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
// FIXME: overriding diffuse/ambient/emissive colors
|
||||
material = new osg::Material;
|
||||
material->setColorMode(osg::Material::OFF);
|
||||
material->setDiffuse(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,alpha));
|
||||
material->setAmbient(osg::Material::FRONT_AND_BACK, osg::Vec4f(1,1,1,1));
|
||||
stateset->setAttributeAndModes(material, osg::StateAttribute::ON|osg::StateAttribute::OVERRIDE);
|
||||
|
||||
mObjectRoot->setStateSet(stateset);
|
||||
mObjectRoot->setStateSet(stateset);
|
||||
|
||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
||||
mResourceSystem->getSceneManager()->recreateShaders(mObjectRoot);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
|
@ -1582,6 +1582,16 @@ namespace MWWorld
|
|||
}
|
||||
}
|
||||
|
||||
void World::setActorCollisionMode(const MWWorld::Ptr& ptr, bool enabled)
|
||||
{
|
||||
mPhysics->setActorCollisionMode(ptr, enabled);
|
||||
}
|
||||
|
||||
bool World::isActorCollisionEnabled(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
return mPhysics->isActorCollisionEnabled(ptr);
|
||||
}
|
||||
|
||||
bool World::toggleCollisionMode()
|
||||
{
|
||||
if (mPhysics->toggleCollisionMode())
|
||||
|
@ -3559,7 +3569,13 @@ namespace MWWorld
|
|||
MWBase::Environment::get().getMechanicsManager()->getObjectsInRange(
|
||||
origin, feetToGameUnits(static_cast<float>(effectIt->mArea)), objects);
|
||||
for (std::vector<MWWorld::Ptr>::iterator affected = objects.begin(); affected != objects.end(); ++affected)
|
||||
{
|
||||
// Ignore actors without collisions here, otherwise it will be possible to hit actors outside processing range.
|
||||
if (affected->getClass().isActor() && !isActorCollisionEnabled(*affected))
|
||||
continue;
|
||||
|
||||
toApply[*affected].push_back(*effectIt);
|
||||
}
|
||||
}
|
||||
|
||||
// Now apply the appropriate effects to each actor in range
|
||||
|
|
|
@ -407,6 +407,9 @@ namespace MWWorld
|
|||
|
||||
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override;
|
||||
|
||||
void setActorCollisionMode(const Ptr& ptr, bool enabled) override;
|
||||
bool isActorCollisionEnabled(const Ptr& ptr) override;
|
||||
|
||||
bool toggleCollisionMode() override;
|
||||
///< Toggle collision mode for player. If disabled player object should ignore
|
||||
/// collisions and gravity.
|
||||
|
|
|
@ -97,8 +97,21 @@ and values above 500 will result in the player inflicting no damage.
|
|||
|
||||
This setting can be controlled in game with the Difficulty slider in the Prefs panel of the Options menu.
|
||||
|
||||
actors processing range
|
||||
-----------------------
|
||||
|
||||
:Type: integer
|
||||
:Range: 3584 to 7168
|
||||
:Default: 7168
|
||||
|
||||
This setting allows to specify a distance from player in game units, in which OpenMW updates actor's state.
|
||||
Actor state update includes AI, animations, and physics processing.
|
||||
Actors near that border start softly fade out instead of just appearing/disapperaing.
|
||||
|
||||
This setting can be controlled in game with the "Actors processing range slider" in the Prefs panel of the Options menu.
|
||||
|
||||
classic reflected absorb spells behavior
|
||||
-----------------------------------------
|
||||
----------------------------------------
|
||||
|
||||
:Type: boolean
|
||||
:Range: True/False
|
||||
|
|
|
@ -73,7 +73,32 @@
|
|||
<Property key="TextAlign" value="Right"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="4 200 260 24">
|
||||
<Widget type="Widget" skin="" position="4 184 352 54" align="Left Top HStretch">
|
||||
<Widget type="TextBox" skin="NormalText" position="0 0 352 16" align="Left Top" name="ActorProcessingText">
|
||||
<Property key="Caption" value="Actors processing range"/>
|
||||
</Widget>
|
||||
<Widget type="MWScrollBar" skin="MW_HScroll" position="0 20 352 14" align="Left Top HStretch">
|
||||
<Property key="Range" value="3584"/>
|
||||
<Property key="Page" value="300"/>
|
||||
<UserString key="SettingType" value="Slider"/>
|
||||
<UserString key="SettingCategory" value="Game"/>
|
||||
<UserString key="SettingName" value="actors processing range"/>
|
||||
<UserString key="SettingValueType" value="Float"/>
|
||||
<UserString key="SettingMin" value="3584"/>
|
||||
<UserString key="SettingMax" value="7168"/>
|
||||
<UserString key="SettingLabelWidget" value="ActorProcessingText"/>
|
||||
<UserString key="SettingLabelCaption" value="Actors processing range"/>
|
||||
</Widget>
|
||||
<Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Left Top">
|
||||
<Property key="Caption" value="#{sLow}"/>
|
||||
<Property key="TextAlign" value="Left"/>
|
||||
</Widget>
|
||||
<Widget type="TextBox" skin="SandText" position="0 38 352 16" align="Right Top">
|
||||
<Property key="Caption" value="#{sHigh}"/>
|
||||
<Property key="TextAlign" value="Right"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="4 250 260 24">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 24 24" align="Left Top">
|
||||
<UserString key="SettingCategory" value="Saves"/>
|
||||
<UserString key="SettingName" value="autosave"/>
|
||||
|
@ -83,7 +108,7 @@
|
|||
<Property key="Caption" value="#{sQuick_Save}"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="4 230 260 24">
|
||||
<Widget type="HBox" skin="" position="4 280 260 24">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 24 24" align="Left Top">
|
||||
<UserString key="SettingCategory" value="Game"/>
|
||||
<UserString key="SettingName" value="best attack"/>
|
||||
|
@ -93,7 +118,7 @@
|
|||
<Property key="Caption" value="#{sBestAttack}"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="4 260 260 24">
|
||||
<Widget type="HBox" skin="" position="4 310 260 24">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 24 24" align="Left Top">
|
||||
<UserString key="SettingCategory" value="GUI"/>
|
||||
<UserString key="SettingName" value="subtitles"/>
|
||||
|
@ -103,7 +128,7 @@
|
|||
<Property key="Caption" value="#{sSubtitles}"/>
|
||||
</Widget>
|
||||
</Widget>
|
||||
<Widget type="HBox" skin="" position="4 290 260 24">
|
||||
<Widget type="HBox" skin="" position="4 340 260 24">
|
||||
<Widget type="AutoSizedButton" skin="MW_Button" position="0 0 24 24" align="Left Top">
|
||||
<UserString key="SettingCategory" value="HUD"/>
|
||||
<UserString key="SettingName" value="crosshair"/>
|
||||
|
|
|
@ -200,6 +200,9 @@ best attack = false
|
|||
# Difficulty. Expressed as damage dealt and received. (e.g. -100 to 100).
|
||||
difficulty = 0
|
||||
|
||||
# The maximum range of actor AI, animations and physics updates.
|
||||
actors processing range = 7168
|
||||
|
||||
# Make reflected Absorb spells have no practical effect, like in Morrowind.
|
||||
classic reflected absorb spells behavior = true
|
||||
|
||||
|
|
Loading…
Reference in a new issue