mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-02-04 03:40:14 +00:00
Merge remote-tracking branch 'refs/remotes/upstream/master'
This commit is contained in:
commit
1e2bf9c447
@ -130,15 +130,20 @@
|
||||
Bug #4633: Sneaking stance affects speed even if the actor is not able to crouch
|
||||
Bug #4641: GetPCJumping is handled incorrectly
|
||||
Bug #4644: %Name should be available for all actors, not just for NPCs
|
||||
Bug #4646: Weapon force-equipment messes up ongoing attack animations
|
||||
Bug #4648: Hud thinks that throwing weapons have condition
|
||||
Bug #4649: Levelup fully restores health
|
||||
Bug #4653: Length of non-ASCII strings is handled incorrectly in ESM reader
|
||||
Bug #4654: Editor: UpdateVisitor does not initialize skeletons for animated objects
|
||||
Bug #4656: Combat AI: back up behaviour is incorrect
|
||||
Bug #4668: Editor: Light source color is displayed as an integer
|
||||
Bug #4669: ToggleCollision should trace the player down after collision being enabled
|
||||
Bug #4671: knownEffect functions should use modified Alchemy skill
|
||||
Bug #4672: Pitch factor is handled incorrectly for crossbow animations
|
||||
Bug #4674: Journal can be opened when settings window is open
|
||||
Bug #4677: Crash in ESM reader when NPC record has DNAM record without DODT one
|
||||
Bug #4678: Crash in ESP parser when SCVR has no variable names
|
||||
Bug #4685: Missing sound causes an exception inside Say command
|
||||
Feature #912: Editor: Add missing icons to UniversalId tables
|
||||
Feature #1221: Editor: Creature/NPC rendering
|
||||
Feature #1617: Editor: Enchantment effect record verifier
|
||||
@ -153,6 +158,7 @@
|
||||
Feature #4012: Editor: Write a log file if OpenCS crashes
|
||||
Feature #4222: 360° screenshots
|
||||
Feature #4256: Implement ToggleBorders (TB) console command
|
||||
Feature #4285: Support soundgen calls for activators
|
||||
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
|
||||
Feature #4345: Add equivalents for the command line commands to Launcher
|
||||
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
|
||||
@ -172,6 +178,7 @@
|
||||
Feature #4632: AI priority: utilize vanilla AI GMSTs for priority rating
|
||||
Feature #4636: Use sTo GMST in spellmaking menu
|
||||
Feature #4642: Batching potion creation
|
||||
Feature #4682: Use the collision box from basic creature mesh if the X one have no collisions
|
||||
Task #2490: Don't open command prompt window on Release-mode builds automatically
|
||||
Task #4545: Enable is_pod string test
|
||||
Task #4605: Optimize skinning
|
||||
|
@ -262,8 +262,8 @@ namespace MWBase
|
||||
///< Adjust position after load to be on ground. Must be called after model load.
|
||||
/// @param force do this even if the ptr is flying
|
||||
|
||||
virtual void fixPosition (const MWWorld::Ptr& actor) = 0;
|
||||
///< Attempt to fix position so that the Ptr is no longer inside collision geometry.
|
||||
virtual void fixPosition () = 0;
|
||||
///< Attempt to fix position so that the player is not stuck inside the geometry.
|
||||
|
||||
/// @note No-op for items in containers. Use ContainerStore::removeItem instead.
|
||||
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
|
||||
@ -297,9 +297,11 @@ namespace MWBase
|
||||
///< Queues movement for \a ptr (in local space), to be applied in the next call to
|
||||
/// doPhysics.
|
||||
|
||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) = 0;
|
||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) = 0;
|
||||
///< cast a Ray and return true if there is an object in the ray path.
|
||||
|
||||
virtual bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) = 0;
|
||||
|
||||
virtual bool toggleCollisionMode() = 0;
|
||||
///< Toggle collision mode for player. If disabled player object should ignore
|
||||
/// collisions and gravity.
|
||||
|
@ -1,6 +1,7 @@
|
||||
#include "activator.hpp"
|
||||
|
||||
#include <components/esm/loadacti.hpp>
|
||||
#include <components/misc/rng.hpp>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
@ -134,4 +135,60 @@ namespace MWClass
|
||||
|
||||
return MWWorld::Ptr(cell.insert(ref), &cell);
|
||||
}
|
||||
|
||||
std::string Activator::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
|
||||
{
|
||||
std::string model = getModel(ptr); // Assume it's not empty, since we wouldn't have gotten the soundgen otherwise
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
std::string creatureId;
|
||||
|
||||
for (const ESM::Creature &iter : store.get<ESM::Creature>())
|
||||
{
|
||||
if (!iter.mModel.empty() && Misc::StringUtils::ciEqual(model, "meshes\\" + iter.mModel))
|
||||
{
|
||||
creatureId = !iter.mOriginal.empty() ? iter.mOriginal : iter.mId;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (creatureId.empty())
|
||||
return std::string();
|
||||
|
||||
int type = getSndGenTypeFromName(name);
|
||||
std::vector<const ESM::SoundGenerator*> sounds;
|
||||
|
||||
for (auto sound = store.get<ESM::SoundGenerator>().begin(); sound != store.get<ESM::SoundGenerator>().end(); ++sound)
|
||||
if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(creatureId, sound->mCreature)))
|
||||
sounds.push_back(&*sound);
|
||||
|
||||
if (!sounds.empty())
|
||||
return sounds[Misc::Rng::rollDice(sounds.size())]->mSound;
|
||||
|
||||
if (type == ESM::SoundGenerator::Land)
|
||||
return "Body Fall Large";
|
||||
|
||||
return std::string();
|
||||
}
|
||||
|
||||
int Activator::getSndGenTypeFromName(const std::string &name)
|
||||
{
|
||||
if (name == "left")
|
||||
return ESM::SoundGenerator::LeftFoot;
|
||||
if (name == "right")
|
||||
return ESM::SoundGenerator::RightFoot;
|
||||
if (name == "swimleft")
|
||||
return ESM::SoundGenerator::SwimLeft;
|
||||
if (name == "swimright")
|
||||
return ESM::SoundGenerator::SwimRight;
|
||||
if (name == "moan")
|
||||
return ESM::SoundGenerator::Moan;
|
||||
if (name == "roar")
|
||||
return ESM::SoundGenerator::Roar;
|
||||
if (name == "scream")
|
||||
return ESM::SoundGenerator::Scream;
|
||||
if (name == "land")
|
||||
return ESM::SoundGenerator::Land;
|
||||
|
||||
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
|
||||
}
|
||||
}
|
||||
|
@ -10,6 +10,8 @@ namespace MWClass
|
||||
|
||||
virtual MWWorld::Ptr copyToCellImpl(const MWWorld::ConstPtr &ptr, MWWorld::CellStore &cell) const;
|
||||
|
||||
static int getSndGenTypeFromName(const std::string &name);
|
||||
|
||||
public:
|
||||
|
||||
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, const std::string& model, MWRender::RenderingInterface& renderingInterface) const;
|
||||
@ -44,6 +46,8 @@ namespace MWClass
|
||||
///< Whether or not to use animated variant of model (default false)
|
||||
|
||||
virtual bool isActivator() const;
|
||||
|
||||
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -688,9 +688,9 @@ namespace MWClass
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return 2;
|
||||
return ESM::SoundGenerator::SwimLeft;
|
||||
if(world->isOnGround(ptr))
|
||||
return 0;
|
||||
return ESM::SoundGenerator::LeftFoot;
|
||||
return -1;
|
||||
}
|
||||
if(name == "right")
|
||||
@ -698,23 +698,23 @@ namespace MWClass
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
|
||||
if(world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return 3;
|
||||
return ESM::SoundGenerator::SwimRight;
|
||||
if(world->isOnGround(ptr))
|
||||
return 1;
|
||||
return ESM::SoundGenerator::RightFoot;
|
||||
return -1;
|
||||
}
|
||||
if(name == "swimleft")
|
||||
return 2;
|
||||
return ESM::SoundGenerator::SwimLeft;
|
||||
if(name == "swimright")
|
||||
return 3;
|
||||
return ESM::SoundGenerator::SwimRight;
|
||||
if(name == "moan")
|
||||
return 4;
|
||||
return ESM::SoundGenerator::Moan;
|
||||
if(name == "roar")
|
||||
return 5;
|
||||
return ESM::SoundGenerator::Roar;
|
||||
if(name == "scream")
|
||||
return 6;
|
||||
return ESM::SoundGenerator::Scream;
|
||||
if(name == "land")
|
||||
return 7;
|
||||
return ESM::SoundGenerator::Land;
|
||||
|
||||
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
|
||||
}
|
||||
|
@ -1242,15 +1242,9 @@ namespace MWClass
|
||||
return "";
|
||||
}
|
||||
|
||||
// Morrowind ignores land soundgen for NPCs
|
||||
if(name == "land")
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
osg::Vec3f pos(ptr.getRefData().getPosition().asVec3());
|
||||
if (world->isUnderwater(ptr.getCell(), pos) || world->isWalkingOnWater(ptr))
|
||||
return "DefaultLandWater";
|
||||
|
||||
return "DefaultLand";
|
||||
}
|
||||
return "";
|
||||
if(name == "swimleft")
|
||||
return "Swim Left";
|
||||
if(name == "swimright")
|
||||
|
@ -506,7 +506,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
|
||||
while (!stream.eof () && !ucsLineBreak (stream.peek ()) && ucsBreakingSpace (stream.peek ()))
|
||||
{
|
||||
MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek());
|
||||
if (info.codePoint >= 0)
|
||||
if (info.charFound)
|
||||
space_width += static_cast<int>(info.advance + info.bearingX);
|
||||
stream.consume ();
|
||||
}
|
||||
@ -516,7 +516,7 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
|
||||
while (!stream.eof () && !ucsLineBreak (stream.peek ()) && !ucsBreakingSpace (stream.peek ()))
|
||||
{
|
||||
MWGui::GlyphInfo info = GlyphInfo(style->mFont, stream.peek());
|
||||
if (info.codePoint >= 0)
|
||||
if (info.charFound)
|
||||
word_width += static_cast<int>(info.advance + info.bearingX);
|
||||
stream.consume ();
|
||||
}
|
||||
@ -765,7 +765,7 @@ namespace
|
||||
{
|
||||
MWGui::GlyphInfo info = GlyphInfo(mFont, ch);
|
||||
|
||||
if (info.codePoint < 0)
|
||||
if (!info.charFound)
|
||||
return;
|
||||
|
||||
MyGUI::FloatRect vr;
|
||||
@ -787,7 +787,7 @@ namespace
|
||||
{
|
||||
MWGui::GlyphInfo info = GlyphInfo(mFont, ch);
|
||||
|
||||
if (info.codePoint >= 0)
|
||||
if (info.charFound)
|
||||
mCursor.left += static_cast<int>(info.bearingX + info.advance);
|
||||
}
|
||||
|
||||
|
@ -43,6 +43,7 @@ namespace MWGui
|
||||
float advance;
|
||||
float bearingX;
|
||||
float bearingY;
|
||||
bool charFound;
|
||||
MyGUI::FloatRect uvRect;
|
||||
|
||||
GlyphInfo(MyGUI::IFont* font, MyGUI::Char ch)
|
||||
@ -61,15 +62,17 @@ namespace MWGui
|
||||
height = (int) gi->height / scale;
|
||||
advance = (int) gi->advance / scale;
|
||||
uvRect = gi->uvRect;
|
||||
charFound = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
codePoint = -1;
|
||||
codePoint = 0;
|
||||
bearingX = 0;
|
||||
bearingY = 0;
|
||||
width = 0;
|
||||
height = 0;
|
||||
advance = 0;
|
||||
charFound = false;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -169,19 +169,18 @@ namespace MWGui
|
||||
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
|
||||
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
|
||||
|
||||
mShowWallpaper = visible && (MWBase::Environment::get().getStateManager()->getState()
|
||||
== MWBase::StateManager::State_NoGame);
|
||||
mVisible = visible;
|
||||
mLoadingBox->setVisible(mVisible);
|
||||
setVisible(true);
|
||||
|
||||
if (!visible)
|
||||
if (!mVisible)
|
||||
{
|
||||
mShowWallpaper = false;
|
||||
draw();
|
||||
return;
|
||||
}
|
||||
|
||||
mVisible = visible;
|
||||
mLoadingBox->setVisible(mVisible);
|
||||
|
||||
setVisible(true);
|
||||
mShowWallpaper = MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame;
|
||||
|
||||
if (mShowWallpaper)
|
||||
{
|
||||
|
@ -4,6 +4,10 @@
|
||||
|
||||
#include <components/esm/aisequence.hpp>
|
||||
|
||||
#include <components/sceneutil/positionattitudetransform.hpp>
|
||||
|
||||
#include "../mwphysics/collisiontype.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
@ -456,7 +460,48 @@ namespace MWMechanics
|
||||
mTimerCombatMove = 0.1f + 0.1f * Misc::Rng::rollClosedProbability();
|
||||
mCombatMove = true;
|
||||
}
|
||||
else if (isDistantCombat)
|
||||
{
|
||||
// Backing up behaviour
|
||||
// Actor backs up slightly further away than opponent's weapon range
|
||||
// (in vanilla - only as far as oponent's weapon range),
|
||||
// or not at all if opponent is using a ranged weapon
|
||||
|
||||
if (targetUsesRanged || distToTarget > rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon
|
||||
return;
|
||||
|
||||
// actor should not back up into water
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f))
|
||||
return;
|
||||
|
||||
int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_HeightMap | MWPhysics::CollisionType_Door;
|
||||
|
||||
// Actor can not back up if there is no free space behind
|
||||
// Currently we take the 35% of actor's height from the ground as vector height.
|
||||
// This approach allows us to detect small obstacles (e.g. crates) and curved walls.
|
||||
osg::Vec3f halfExtents = MWBase::Environment::get().getWorld()->getHalfExtents(actor);
|
||||
osg::Vec3f pos = actor.getRefData().getPosition().asVec3();
|
||||
osg::Vec3f source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z());
|
||||
osg::Vec3f fallbackDirection = actor.getRefData().getBaseNode()->getAttitude() * osg::Vec3f(0,-1,0);
|
||||
osg::Vec3f destination = source + fallbackDirection * (halfExtents.y() + 16);
|
||||
|
||||
bool isObstacleDetected = MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask);
|
||||
if (isObstacleDetected)
|
||||
return;
|
||||
|
||||
// Check if there is nothing behind - probably actor is near cliff.
|
||||
// A current approach: cast ray 1.5-yard ray down in 1.5 yard behind actor from 35% of actor's height.
|
||||
// If we did not hit anything, there is a cliff behind actor.
|
||||
source = pos + osg::Vec3f(0, 0, 0.75f * halfExtents.z()) + fallbackDirection * (halfExtents.y() + 96);
|
||||
destination = source - osg::Vec3f(0, 0, 0.75f * halfExtents.z() + 96);
|
||||
bool isCliffDetected = !MWBase::Environment::get().getWorld()->castRay(source.x(), source.y(), source.z(), destination.x(), destination.y(), destination.z(), mask);
|
||||
if (isCliffDetected)
|
||||
return;
|
||||
|
||||
mMovement.mPosition[1] = -1;
|
||||
}
|
||||
// dodge movements (for NPCs and bipedal creatures)
|
||||
// Note: do not use for ranged combat yet since in couple with back up behaviour can move actor out of cliff
|
||||
else if (actor.getClass().isBipedal(actor))
|
||||
{
|
||||
// apply sideway movement (kind of dodging) with some probability
|
||||
@ -468,20 +513,6 @@ namespace MWMechanics
|
||||
mCombatMove = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Backing up behaviour
|
||||
// Actor backs up slightly further away than opponent's weapon range
|
||||
// (in vanilla - only as far as oponent's weapon range),
|
||||
// or not at all if opponent is using a ranged weapon
|
||||
if (isDistantCombat)
|
||||
{
|
||||
// actor should not back up into water
|
||||
if (MWBase::Environment::get().getWorld()->isUnderwater(MWWorld::ConstPtr(actor), 0.5f))
|
||||
return;
|
||||
|
||||
if (!targetUsesRanged && distToTarget <= rangeAttackOfTarget*1.5) // Don't back up if the target is wielding ranged weapon
|
||||
mMovement.mPosition[1] = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void AiCombatStorage::updateCombatMove(float duration)
|
||||
|
@ -979,17 +979,13 @@ void CharacterController::handleTextKey(const std::string &groupname, const std:
|
||||
}
|
||||
}
|
||||
|
||||
if (soundgen == "land") // Morrowind ignores land soundgen for some reason
|
||||
return;
|
||||
|
||||
std::string sound = mPtr.getClass().getSoundIdFromSndGen(mPtr, soundgen);
|
||||
if(!sound.empty())
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
if(soundgen == "left" || soundgen == "right")
|
||||
// NB: landing sound is not played for NPCs here
|
||||
if(soundgen == "left" || soundgen == "right" || soundgen == "land")
|
||||
{
|
||||
// Don't make foot sounds local for the player, it makes sense to keep them
|
||||
// positioned on the ground.
|
||||
sndMgr->playSound3D(mPtr, sound, volume, pitch, MWSound::Type::Foot,
|
||||
MWSound::PlayMode::NoPlayerLocal);
|
||||
}
|
||||
@ -1282,6 +1278,18 @@ bool CharacterController::updateWeaponState(CharacterState& idle)
|
||||
bool isStillWeapon = weaptype > WeapType_HandToHand && weaptype < WeapType_Spell &&
|
||||
mWeaponType > WeapType_HandToHand && mWeaponType < WeapType_Spell;
|
||||
|
||||
// If the current weapon type was changed in the middle of attack (e.g. by Equip console command or when bound spell expires),
|
||||
// we should force actor to the "weapon equipped" state, interrupt attack and update animations.
|
||||
if (isStillWeapon && mWeaponType != weaptype && mUpperBodyState > UpperCharState_WeapEquiped)
|
||||
{
|
||||
forcestateupdate = true;
|
||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
mAttackingOrSpell = false;
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
if (mPtr == getPlayer())
|
||||
MWBase::Environment::get().getWorld()->getPlayer().setAttackingOrSpell(false);
|
||||
}
|
||||
|
||||
if(!isKnockedOut() && !isKnockedDown() && !isRecovery())
|
||||
{
|
||||
std::string weapgroup;
|
||||
@ -2071,11 +2079,17 @@ void CharacterController::update(float duration)
|
||||
}
|
||||
}
|
||||
|
||||
// Play landing sound
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
std::string sound = cls.getSoundIdFromSndGen(mPtr, "land");
|
||||
if (!sound.empty())
|
||||
// Play landing sound for NPCs
|
||||
if (mPtr.getClass().isNpc())
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
std::string sound = "DefaultLand";
|
||||
osg::Vec3f pos(mPtr.getRefData().getPosition().asVec3());
|
||||
if (world->isUnderwater(mPtr.getCell(), pos) || world->isWalkingOnWater(mPtr))
|
||||
sound = "DefaultLandWater";
|
||||
|
||||
sndMgr->playSound3D(mPtr, sound, 1.f, 1.f, MWSound::Type::Foot, MWSound::PlayMode::NoPlayerLocal);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -5,6 +5,8 @@
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
#include "../mwphysics/collisiontype.hpp"
|
||||
|
||||
#include "../mwworld/cellstore.hpp"
|
||||
|
||||
#include "pathgrid.hpp"
|
||||
@ -246,8 +248,9 @@ namespace MWMechanics
|
||||
converter.toWorld(temp);
|
||||
// Add Z offset since path node can overlap with other objects.
|
||||
// Also ignore doors in raytesting.
|
||||
int mask = MWPhysics::CollisionType_World;
|
||||
bool isPathClear = !MWBase::Environment::get().getWorld()->castRay(
|
||||
startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, true);
|
||||
startPoint.mX, startPoint.mY, startPoint.mZ+16, temp.mX, temp.mY, temp.mZ+16, mask);
|
||||
if (isPathClear)
|
||||
mPath.pop_front();
|
||||
}
|
||||
|
@ -1337,6 +1337,16 @@ namespace MWPhysics
|
||||
if (!shape)
|
||||
return;
|
||||
|
||||
// Try to get shape from basic model as fallback for creatures
|
||||
if (!ptr.getClass().isNpc() && shape->mCollisionBoxHalfExtents.length2() == 0)
|
||||
{
|
||||
const std::string fallbackModel = ptr.getClass().getModel(ptr);
|
||||
if (fallbackModel != mesh)
|
||||
{
|
||||
shape = mShapeManager->getShape(fallbackModel);
|
||||
}
|
||||
}
|
||||
|
||||
Actor* actor = new Actor(ptr, shape, mCollisionWorld);
|
||||
mActors.insert(std::make_pair(ptr, actor));
|
||||
}
|
||||
|
@ -831,7 +831,7 @@ namespace MWRender
|
||||
|
||||
cubeTexture->setFilter(osg::Texture::MIN_FILTER,osg::Texture::NEAREST);
|
||||
cubeTexture->setFilter(osg::Texture::MAG_FILTER,osg::Texture::NEAREST);
|
||||
|
||||
|
||||
cubeTexture->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP_TO_EDGE);
|
||||
cubeTexture->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP_TO_EDGE);
|
||||
|
||||
@ -856,19 +856,14 @@ namespace MWRender
|
||||
stateset->addUniform(new osg::Uniform("cubeMap",0));
|
||||
stateset->addUniform(new osg::Uniform("mapping",screenshotMapping));
|
||||
stateset->setTextureAttributeAndModes(0,cubeTexture,osg::StateAttribute::ON);
|
||||
|
||||
|
||||
quad->setStateSet(stateset);
|
||||
quad->setUpdateCallback(nullptr);
|
||||
|
||||
screenshotCamera->addChild(quad);
|
||||
|
||||
mRootNode->addChild(screenshotCamera);
|
||||
|
||||
renderCameraToImage(screenshotCamera,image,screenshotW,screenshotH);
|
||||
|
||||
screenshotCamera->removeChildren(0,screenshotCamera->getNumChildren());
|
||||
mRootNode->removeChild(screenshotCamera);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -893,6 +888,8 @@ namespace MWRender
|
||||
image->setDataType(GL_UNSIGNED_BYTE);
|
||||
image->setPixelFormat(texture->getInternalFormat());
|
||||
|
||||
mRootNode->addChild(camera);
|
||||
|
||||
// The draw needs to complete before we can copy back our image.
|
||||
osg::ref_ptr<NotifyDrawCompletedCallback> callback (new NotifyDrawCompletedCallback);
|
||||
camera->setFinalDrawCallback(callback);
|
||||
@ -908,32 +905,17 @@ namespace MWRender
|
||||
|
||||
// now that we've "used up" the current frame, get a fresh framenumber for the next frame() following after the screenshot is completed
|
||||
mViewer->advance(mViewer->getFrameStamp()->getSimulationTime());
|
||||
|
||||
camera->removeChildren(0, camera->getNumChildren());
|
||||
mRootNode->removeChild(camera);
|
||||
}
|
||||
|
||||
void RenderingManager::screenshot(osg::Image *image, int w, int h, osg::Matrixd cameraTransform)
|
||||
{
|
||||
osg::ref_ptr<osg::Camera> rttCamera (new osg::Camera);
|
||||
rttCamera->setNodeMask(Mask_RenderToTexture);
|
||||
rttCamera->attach(osg::Camera::COLOR_BUFFER, image);
|
||||
rttCamera->setRenderOrder(osg::Camera::PRE_RENDER);
|
||||
rttCamera->setReferenceFrame(osg::Camera::ABSOLUTE_RF);
|
||||
rttCamera->setRenderTargetImplementation(osg::Camera::FRAME_BUFFER_OBJECT, osg::Camera::PIXEL_BUFFER_RTT);
|
||||
rttCamera->setProjectionMatrixAsPerspective(mFieldOfView, w/float(h), mNearClip, mViewDistance);
|
||||
rttCamera->setViewMatrix(mViewer->getCamera()->getViewMatrix() * cameraTransform);
|
||||
|
||||
rttCamera->setViewport(0, 0, w, h);
|
||||
|
||||
osg::ref_ptr<osg::Texture2D> texture (new osg::Texture2D);
|
||||
texture->setInternalFormat(GL_RGB);
|
||||
texture->setTextureSize(w, h);
|
||||
texture->setResizeNonPowerOfTwoHint(false);
|
||||
texture->setFilter(osg::Texture::MIN_FILTER, osg::Texture::LINEAR);
|
||||
texture->setFilter(osg::Texture::MAG_FILTER, osg::Texture::LINEAR);
|
||||
rttCamera->attach(osg::Camera::COLOR_BUFFER, texture);
|
||||
|
||||
image->setDataType(GL_UNSIGNED_BYTE);
|
||||
image->setPixelFormat(texture->getInternalFormat());
|
||||
|
||||
rttCamera->setUpdateCallback(new NoTraverseCallback);
|
||||
rttCamera->addChild(mSceneRoot);
|
||||
|
||||
@ -942,14 +924,9 @@ namespace MWRender
|
||||
|
||||
rttCamera->setCullMask(mViewer->getCamera()->getCullMask() & (~Mask_GUI));
|
||||
|
||||
mRootNode->addChild(rttCamera);
|
||||
|
||||
rttCamera->setClearMask(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
||||
|
||||
renderCameraToImage(rttCamera.get(),image,w,h);
|
||||
|
||||
rttCamera->removeChildren(0, rttCamera->getNumChildren());
|
||||
mRootNode->removeChild(rttCamera);
|
||||
}
|
||||
|
||||
osg::Vec4f RenderingManager::getScreenBounds(const MWWorld::Ptr& ptr)
|
||||
|
@ -738,8 +738,7 @@ namespace MWScript
|
||||
|
||||
virtual void execute (Interpreter::Runtime& runtime)
|
||||
{
|
||||
const MWWorld::Ptr ptr = MWMechanics::getPlayer();
|
||||
MWBase::Environment::get().getWorld()->fixPosition(ptr);
|
||||
MWBase::Environment::get().getWorld()->fixPosition();
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -245,20 +245,30 @@ namespace MWSound
|
||||
|
||||
DecoderPtr SoundManager::loadVoice(const std::string &voicefile)
|
||||
{
|
||||
DecoderPtr decoder = getDecoder();
|
||||
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
|
||||
if(mVFS->exists(voicefile))
|
||||
decoder->open(voicefile);
|
||||
else
|
||||
try
|
||||
{
|
||||
std::string file = voicefile;
|
||||
std::string::size_type pos = file.rfind('.');
|
||||
if(pos != std::string::npos)
|
||||
file = file.substr(0, pos)+".mp3";
|
||||
decoder->open(file);
|
||||
DecoderPtr decoder = getDecoder();
|
||||
|
||||
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
|
||||
if(mVFS->exists(voicefile))
|
||||
decoder->open(voicefile);
|
||||
else
|
||||
{
|
||||
std::string file = voicefile;
|
||||
std::string::size_type pos = file.rfind('.');
|
||||
if(pos != std::string::npos)
|
||||
file = file.substr(0, pos)+".mp3";
|
||||
decoder->open(file);
|
||||
}
|
||||
|
||||
return decoder;
|
||||
}
|
||||
catch(std::exception &e)
|
||||
{
|
||||
Log(Debug::Error) << "Failed to load audio from " << voicefile << ": " << e.what();
|
||||
}
|
||||
|
||||
return decoder;
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
Sound *SoundManager::getSoundRef()
|
||||
@ -471,6 +481,8 @@ namespace MWSound
|
||||
|
||||
mVFS->normalizeFilename(voicefile);
|
||||
DecoderPtr decoder = loadVoice(voicefile);
|
||||
if (!decoder)
|
||||
return;
|
||||
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const osg::Vec3f pos = world->getActorHeadTransform(ptr).getTrans();
|
||||
@ -503,6 +515,8 @@ namespace MWSound
|
||||
|
||||
mVFS->normalizeFilename(voicefile);
|
||||
DecoderPtr decoder = loadVoice(voicefile);
|
||||
if (!decoder)
|
||||
return;
|
||||
|
||||
stopSay(MWWorld::ConstPtr());
|
||||
Stream *sound = playVoice(decoder, osg::Vec3f(), true);
|
||||
|
@ -117,7 +117,7 @@ namespace MWSound
|
||||
Sound_Buffer *lookupSound(const std::string &soundId) const;
|
||||
Sound_Buffer *loadSound(const std::string &soundId);
|
||||
|
||||
// returns a decoder to start streaming
|
||||
// returns a decoder to start streaming, or nullptr if the sound was not found
|
||||
DecoderPtr loadVoice(const std::string &voicefile);
|
||||
|
||||
Sound *getSoundRef();
|
||||
|
@ -1351,8 +1351,9 @@ namespace MWWorld
|
||||
moveObject(ptr, ptr.getCell(), pos.x(), pos.y(), pos.z());
|
||||
}
|
||||
|
||||
void World::fixPosition(const Ptr &actor)
|
||||
void World::fixPosition()
|
||||
{
|
||||
const MWWorld::Ptr actor = getPlayerPtr();
|
||||
const float distance = 128.f;
|
||||
ESM::Position esmPos = actor.getRefData().getPosition();
|
||||
osg::Quat orientation(esmPos.rot[2], osg::Vec3f(0,0,-1));
|
||||
@ -1382,7 +1383,7 @@ namespace MWWorld
|
||||
esmPos.pos[0] = traced.x();
|
||||
esmPos.pos[1] = traced.y();
|
||||
esmPos.pos[2] = traced.z();
|
||||
MWWorld::ActionTeleport("", esmPos, false).execute(actor);
|
||||
MWWorld::ActionTeleport(actor.getCell()->isExterior() ? "" : actor.getCell()->getCell()->mName, esmPos, false).execute(actor);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1505,15 +1506,18 @@ namespace MWWorld
|
||||
moveObjectImp(player->first, player->second.x(), player->second.y(), player->second.z(), false);
|
||||
}
|
||||
|
||||
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors)
|
||||
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2)
|
||||
{
|
||||
int mask = MWPhysics::CollisionType_World | MWPhysics::CollisionType_Door;
|
||||
bool result = castRay(x1, y1, z1, x2, y2, z2, mask);
|
||||
return result;
|
||||
}
|
||||
|
||||
bool World::castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask)
|
||||
{
|
||||
osg::Vec3f a(x1,y1,z1);
|
||||
osg::Vec3f b(x2,y2,z2);
|
||||
|
||||
int mask = MWPhysics::CollisionType_World;
|
||||
if (!ignoreDoors)
|
||||
mask |= MWPhysics::CollisionType_Door;
|
||||
|
||||
MWPhysics::PhysicsSystem::RayResult result = mPhysics->castRay(a, b, MWWorld::Ptr(), std::vector<MWWorld::Ptr>(), mask);
|
||||
return result.mHit;
|
||||
}
|
||||
|
@ -291,8 +291,8 @@ namespace MWWorld
|
||||
///< Adjust position after load to be on ground. Must be called after model load.
|
||||
/// @param force do this even if the ptr is flying
|
||||
|
||||
void fixPosition (const Ptr& actor) override;
|
||||
///< Attempt to fix position so that the Ptr is no longer inside collision geometry.
|
||||
void fixPosition () override;
|
||||
///< Attempt to fix position so that the player is not stuck inside the geometry.
|
||||
|
||||
void enable (const Ptr& ptr) override;
|
||||
|
||||
@ -402,9 +402,11 @@ namespace MWWorld
|
||||
///< Queues movement for \a ptr (in local space), to be applied in the next call to
|
||||
/// doPhysics.
|
||||
|
||||
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, bool ignoreDoors=false) override;
|
||||
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2, int mask) override;
|
||||
///< cast a Ray and return true if there is an object in the ray path.
|
||||
|
||||
bool castRay (float x1, float y1, float z1, float x2, float y2, float z2) override;
|
||||
|
||||
bool toggleCollisionMode() override;
|
||||
///< Toggle collision mode for player. If disabled player object should ignore
|
||||
/// collisions and gravity.
|
||||
|
@ -8,12 +8,13 @@ namespace Debug
|
||||
{
|
||||
enum Level
|
||||
{
|
||||
NoLevel = 0,
|
||||
Error = 1,
|
||||
Warning = 2,
|
||||
Info = 3,
|
||||
Verbose = 4,
|
||||
Marker = Verbose
|
||||
Marker = Verbose,
|
||||
|
||||
NoLevel = 5 // Do not filter messages in this case
|
||||
};
|
||||
|
||||
extern Level CurrentDebugLevel;
|
||||
@ -30,6 +31,11 @@ public:
|
||||
mLock(sLock),
|
||||
mLevel(level)
|
||||
{
|
||||
// If the app has no logging system enabled, log level is not specified.
|
||||
// Show all messages without marker - we just use the plain cout in this case.
|
||||
if (Debug::CurrentDebugLevel == Debug::NoLevel)
|
||||
return;
|
||||
|
||||
if (mLevel <= Debug::CurrentDebugLevel)
|
||||
std::cout << static_cast<unsigned char>(mLevel);
|
||||
}
|
||||
|
@ -30,6 +30,12 @@ namespace ESM
|
||||
// The tmp buffer is a null-byte separated string list, we
|
||||
// just have to pick out one string at a time.
|
||||
char* str = &tmp[0];
|
||||
if (!str && mVarNames.size() > 0)
|
||||
{
|
||||
Log(Debug::Warning) << "SCVR with no variable names";
|
||||
return;
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < mVarNames.size(); i++)
|
||||
{
|
||||
// Support '\r' terminated strings like vanilla. See Bug #1324.
|
||||
|
@ -1,5 +1,7 @@
|
||||
#include "transport.hpp"
|
||||
|
||||
#include <components/debug/debuglog.hpp>
|
||||
|
||||
#include <components/esm/esmreader.hpp>
|
||||
#include <components/esm/esmwriter.hpp>
|
||||
|
||||
@ -16,7 +18,11 @@ namespace ESM
|
||||
}
|
||||
else if (esm.retSubName().intval == ESM::FourCC<'D','N','A','M'>::value)
|
||||
{
|
||||
mList.back().mCellName = esm.getHString();
|
||||
const std::string name = esm.getHString();
|
||||
if (mList.empty())
|
||||
Log(Debug::Warning) << "Encountered DNAM record without DODT record, skipped.";
|
||||
else
|
||||
mList.back().mCellName = name;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,8 +169,8 @@ def setup(app):
|
||||
# Add any paths that contain custom static files (such as style sheets) here,
|
||||
# relative to this directory. They are copied after the builtin static files,
|
||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||
html_static_path = ['_static',
|
||||
'manuals/openmw-cs/_static'
|
||||
html_static_path = [
|
||||
'_static'
|
||||
]
|
||||
|
||||
# Add any extra paths that contain custom files (such as robots.txt or
|
||||
|
@ -8,10 +8,26 @@ If you are familiar with ``.ini`` tweaks in Morrowind or the other games, this w
|
||||
All settings described in this section are changed in ``settings.cfg``, located in your OpenMW user directory.
|
||||
See :doc:`../paths` for this location.
|
||||
|
||||
Changing Settings
|
||||
#################
|
||||
|
||||
#. Once you have located your ``settings.cfg`` file, open it in a plain text editor.
|
||||
#. Find the setting(s) you wish to change in the following pages.
|
||||
#. If the setting is not already in ``settings.cfg``,
|
||||
add it by copy and pasting the name exactly as written in this guide.
|
||||
#. Set the value of the setting by typing ``= <value>`` after the setting on the same line,
|
||||
using an appropriate value in place of ``<value>``.
|
||||
#. If this is the first setting from it's category that you're adding,
|
||||
be sure to add the heading in square brackets ``[]`` above it using just the setting type,
|
||||
i.e. without the word "Settings".
|
||||
|
||||
For example, to delay tooltips popping up by 1 second, add the line ``tooltip delay = 1.0``.
|
||||
Then to the line above, type ``[GUI]``, as the tooltip delay setting comes from the "GUI Settings" section.
|
||||
|
||||
Although this guide attempts to be comprehensive and up to date,
|
||||
you will always be able to find the full list of settings available and their default values in ``settings-default.cfg``
|
||||
in your main OpenMW installation directory.
|
||||
The ranges I have included with each setting are the physically possible ranges, not recommendations.
|
||||
The ranges included with each setting are the physically possible ranges, not recommendations.
|
||||
|
||||
.. warning::
|
||||
As the title suggests, these are advanced settings.
|
||||
|
@ -1,5 +1,5 @@
|
||||
Shader Settings
|
||||
###############
|
||||
Shaders Settings
|
||||
################
|
||||
|
||||
.. _force-shaders-label:
|
||||
force shaders
|
||||
|
Loading…
x
Reference in New Issue
Block a user