Merge remote-tracking branch 'refs/remotes/upstream/master'

pull/1547/head
AnyOldName3 6 years ago
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
{
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)
{
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);
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…
Cancel
Save