Merge pull request #359 from OpenMW/master while resolving conflicts

# Conflicts:
#	.travis.yml
0.6.1-openmw-updates
David Cernat 6 years ago
commit ecb0ad0d77

@ -24,7 +24,7 @@ addons:
- llvm-toolchain-precise-3.6 - llvm-toolchain-precise-3.6
packages: [ packages: [
# Dev # Dev
clang-3.6, libunshield-dev, libtinyxml-dev, cmake, clang-3.6, libunshield-dev, libtinyxml-dev,
g++-6, g++-6,
# Tests # Tests
libgtest-dev, google-mock, libgtest-dev, google-mock,

@ -359,6 +359,7 @@ if (NOT WIN32 AND NOT APPLE)
endif() endif()
# CXX Compiler settings # CXX Compiler settings
set(CMAKE_CXX_STANDARD 11)
if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang) if (CMAKE_CXX_COMPILER_ID STREQUAL GNU OR CMAKE_CXX_COMPILER_ID STREQUAL Clang)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++11 -pedantic -Wno-long-long") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -Wundef -Wno-unused-parameter -std=c++11 -pedantic -Wno-long-long")
add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON ) add_definitions( -DBOOST_NO_CXX11_SCOPED_ENUMS=ON )

@ -33,6 +33,7 @@ Furthermore, we advise to:
* Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title. * Feel free to submit incomplete pull requests. Even if the work can not be merged yet, pull requests are a great place to collect early feedback. Just make sure to mark it as *[Incomplete]* or *[Do not merge yet]* in the title.
* If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards). * If you plan on contributing often, please read the [Developer Reference](https://wiki.openmw.org/index.php?title=Developer_Reference) on our wiki, especially the [Policies and Standards](https://wiki.openmw.org/index.php?title=Policies_and_Standards).
* Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway. * Make sure each of your changes has a clear objective. Unnecessary changes may lead to merge conflicts, clutter the commit history and slow down review. Code formatting 'fixes' should be avoided, unless you were already changing that particular line anyway.
* Reference the bug / feature ticket(s) in your commit message (e.g. 'Bug #123') to make it easier to keep track of what we changed for what reason. Our bugtracker will show those commits next to the ticket. If your commit message includes 'Fixes #123', that bug/feature will automatically be set to 'Closed' when your commit is merged.
Guidelines for original engine "fixes" Guidelines for original engine "fixes"
================================= =================================

@ -169,18 +169,25 @@ void CSMPrefs::State::declare()
"list go to the first/last item"); "list go to the first/last item");
declareCategory ("3D Scene Input"); declareCategory ("3D Scene Input");
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareSeparator();
declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0); declareDouble ("p-navi-free-sensitivity", "Free Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false); declareBool ("p-navi-free-invert", "Invert Free Camera Mouse Input", false);
declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble ("s-navi-sensitivity", "Secondary Camera Movement Sensitivity", 50.0).setRange(-1000.0, 1000.0);
declareDouble ("navi-wheel-factor", "Camera Zoom Sensitivity", 8).setRange(-100.0, 100.0);
declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0); declareDouble ("navi-free-lin-speed", "Free Camera Linear Speed", 1000.0).setRange(1.0, 10000.0);
declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28); declareDouble ("navi-free-rot-speed", "Free Camera Rotational Speed", 3.14 / 2).setRange(0.001, 6.28);
declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0); declareDouble ("navi-free-speed-mult", "Free Camera Speed Multiplier (from Modifier)", 8).setRange(0.001, 1000.0);
declareSeparator();
declareDouble ("p-navi-orbit-sensitivity", "Orbit Camera Sensitivity", 1/650.).setPrecision(5).setRange(0.0, 1.0);
declareBool ("p-navi-orbit-invert", "Invert Orbit Camera Mouse Input", false);
declareDouble ("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28); declareDouble ("navi-orbit-rot-speed", "Orbital Camera Rotational Speed", 3.14 / 4).setRange(0.001, 6.28);
declareDouble ("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4).setRange(0.001, 1000.0); declareDouble ("navi-orbit-speed-mult", "Orbital Camera Speed Multiplier (from Modifier)", 4).setRange(0.001, 1000.0);
declareBool ("navi-orbit-const-roll", "Keep camera roll constant for orbital camera", true);
declareSeparator(); declareSeparator();
declareBool ("context-select", "Context Sensitive Selection", false); declareBool ("context-select", "Context Sensitive Selection", false);
declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0). declareDouble ("drag-factor", "Mouse sensitivity during drag operations", 1.0).
setRange (0.001, 100.0); setRange (0.001, 100.0);
@ -191,6 +198,13 @@ void CSMPrefs::State::declare()
setTooltip ("Acceleration factor during drag operations while holding down shift"). setTooltip ("Acceleration factor during drag operations while holding down shift").
setRange (0.001, 100.0); setRange (0.001, 100.0);
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1); declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Rendering");
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
declareBool ("camera-ortho", "Orthographic projection for camera", false);
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).
setTooltip("Size of the orthographic frustum, greater value will allow the camera to see more of the world.").
setRange(10, 10000);
declareDouble ("object-marker-alpha", "Object Marker Transparency", 0.5).setPrecision(2).setRange(0,1); declareDouble ("object-marker-alpha", "Object Marker Transparency", 0.5).setPrecision(2).setRange(0,1);
declareCategory ("Tooltips"); declareCategory ("Tooltips");

@ -84,7 +84,6 @@ CSVPrefs::Dialogue::~Dialogue()
void CSVPrefs::Dialogue::closeEvent (QCloseEvent *event) void CSVPrefs::Dialogue::closeEvent (QCloseEvent *event)
{ {
QMainWindow::closeEvent (event); QMainWindow::closeEvent (event);
CSMPrefs::State::get().save(); CSMPrefs::State::get().save();
} }

@ -668,16 +668,25 @@ namespace CSVRender
mInitialized = true; mInitialized = true;
} }
void OrbitCameraController::setConstRoll(bool enabled)
{
mConstRoll = enabled;
}
void OrbitCameraController::rotateHorizontal(double value) void OrbitCameraController::rotateHorizontal(double value)
{ {
osg::Vec3d eye, center, up; osg::Vec3d eye, center, up;
getCamera()->getViewMatrixAsLookAt(eye, center, up); getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d absoluteUp = osg::Vec3(0,0,1);
osg::Quat rotation = osg::Quat(value, up); osg::Quat rotation = osg::Quat(value, mConstRoll ? absoluteUp : up);
osg::Vec3d oldOffset = eye - mCenter; osg::Vec3d oldOffset = eye - mCenter;
osg::Vec3d newOffset = rotation * oldOffset; osg::Vec3d newOffset = rotation * oldOffset;
if (mConstRoll)
up = rotation * up;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up); getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
} }
@ -687,9 +696,14 @@ namespace CSVRender
getCamera()->getViewMatrixAsLookAt(eye, center, up); getCamera()->getViewMatrixAsLookAt(eye, center, up);
osg::Vec3d forward = center - eye; osg::Vec3d forward = center - eye;
osg::Quat rotation = osg::Quat(value, up ^ forward); osg::Vec3d axis = up ^ forward;
osg::Quat rotation = osg::Quat(value,axis);
osg::Vec3d oldOffset = eye - mCenter; osg::Vec3d oldOffset = eye - mCenter;
osg::Vec3d newOffset = rotation * oldOffset; osg::Vec3d newOffset = rotation * oldOffset;
if (mConstRoll)
up = rotation * up;
getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up); getCamera()->setViewMatrixAsLookAt(mCenter + newOffset, mCenter, up);
} }

@ -160,6 +160,8 @@ namespace CSVRender
/// \brief Flag controller to be re-initialized. /// \brief Flag controller to be re-initialized.
void reset(); void reset();
void setConstRoll(bool enable);
private: private:
void initialize(); void initialize();
@ -181,6 +183,8 @@ namespace CSVRender
double mOrbitSpeed; double mOrbitSpeed;
double mOrbitSpeedMult; double mOrbitSpeedMult;
bool mConstRoll;
private slots: private slots:
void naviPrimary(bool active); void naviPrimary(bool active);

@ -471,7 +471,7 @@ void CSVRender::Object::setSelected(bool selected)
else else
mRootNode->addChild(mBaseNode); mRootNode->addChild(mBaseNode);
mMarkerTransparency = CSMPrefs::get()["3D Scene Input"]["object-marker-alpha"].toDouble(); mMarkerTransparency = CSMPrefs::get()["Rendering"]["object-marker-alpha"].toDouble();
updateMarker(); updateMarker();
} }

@ -56,6 +56,7 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
traits->vsync = false; traits->vsync = false;
mView = new osgViewer::View; mView = new osgViewer::View;
updateCameraParameters( traits->width / static_cast<double>(traits->height) );
osg::ref_ptr<osgQt::GraphicsWindowQt> window = new osgQt::GraphicsWindowQt(traits.get()); osg::ref_ptr<osgQt::GraphicsWindowQt> window = new osgQt::GraphicsWindowQt(traits.get());
QLayout* layout = new QHBoxLayout(this); QLayout* layout = new QHBoxLayout(this);
@ -66,7 +67,6 @@ RenderWidget::RenderWidget(QWidget *parent, Qt::WindowFlags f)
mView->getCamera()->setGraphicsContext(window); mView->getCamera()->setGraphicsContext(window);
mView->getCamera()->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) ); mView->getCamera()->setClearColor( osg::Vec4(0.2, 0.2, 0.6, 1.0) );
mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) ); mView->getCamera()->setViewport( new osg::Viewport(0, 0, traits->width, traits->height) );
mView->getCamera()->setProjectionMatrixAsPerspective(30.0f, static_cast<double>(traits->width)/static_cast<double>(traits->height), 1.0f, 10000.0f );
SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager; SceneUtil::LightManager* lightMgr = new SceneUtil::LightManager;
lightMgr->setStartLight(1); lightMgr->setStartLight(1);
@ -188,6 +188,8 @@ SceneWidget::SceneWidget(std::shared_ptr<Resource::ResourceSystem> resourceSyste
mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain); mOrbitCamControl->setPickingMask(Mask_Reference | Mask_Terrain);
mOrbitCamControl->setConstRoll( CSMPrefs::get()["3D Scene Input"]["navi-orbit-const-roll"].isTrue() );
// we handle lighting manually // we handle lighting manually
mView->setLightingMode(osgViewer::View::NO_LIGHT); mView->setLightingMode(osgViewer::View::NO_LIGHT);
@ -370,6 +372,40 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
{ {
mOrbitCamControl->setOrbitSpeedMultiplier(setting->toDouble()); mOrbitCamControl->setOrbitSpeedMultiplier(setting->toDouble());
} }
else if (*setting=="3D Scene Input/navi-orbit-const-roll")
{
mOrbitCamControl->setConstRoll(setting->isTrue());
}
else if (*setting=="Rendering/camera-fov" ||
*setting=="Rendering/camera-ortho" ||
*setting=="Rendering/camera-ortho-size")
{
updateCameraParameters();
}
}
void RenderWidget::updateCameraParameters(double overrideAspect)
{
const float nearDist = 1.0;
const float farDist = 1000.0;
if (CSMPrefs::get()["Rendering"]["camera-ortho"].isTrue())
{
const float size = CSMPrefs::get()["Rendering"]["camera-ortho-size"].toInt();
const float aspect = overrideAspect >= 0.0 ? overrideAspect : (width() / static_cast<double>(height()));
const float halfH = size * 10.0;
const float halfW = halfH * aspect;
mView->getCamera()->setProjectionMatrixAsOrtho(
-halfW, halfW, -halfH, halfH, nearDist, farDist);
}
else
{
mView->getCamera()->setProjectionMatrixAsPerspective(
CSMPrefs::get()["Rendering"]["camera-fov"].toInt(),
static_cast<double>(width())/static_cast<double>(height()),
nearDist, farDist);
}
} }
void SceneWidget::selectNavigationMode (const std::string& mode) void SceneWidget::selectNavigationMode (const std::string& mode)

@ -64,6 +64,8 @@ namespace CSVRender
osg::ref_ptr<osgViewer::View> mView; osg::ref_ptr<osgViewer::View> mView;
osg::ref_ptr<osg::Group> mRootNode; osg::ref_ptr<osg::Group> mRootNode;
void updateCameraParameters(double overrideAspect = -1.0);
QTimer mTimer; QTimer mTimer;
protected slots: protected slots:

@ -131,7 +131,7 @@ void CSVRender::WorldspaceWidget::settingChanged (const CSMPrefs::Setting *setti
mDragWheelFactor = setting->toDouble(); mDragWheelFactor = setting->toDouble();
else if (*setting=="3D Scene Input/drag-shift-factor") else if (*setting=="3D Scene Input/drag-shift-factor")
mDragShiftFactor = setting->toDouble(); mDragShiftFactor = setting->toDouble();
else if (*setting=="3D Scene Input/object-marker-alpha" && !mInConstructor) else if (*setting=="Rendering/object-marker-alpha" && !mInConstructor)
{ {
float alpha = setting->toDouble(); float alpha = setting->toDouble();
// getSelection is virtual, thus this can not be called from the constructor // getSelection is virtual, thus this can not be called from the constructor

@ -1265,6 +1265,15 @@ namespace MWClass
else else
{ {
ratings[i] = it->getClass().getEffectiveArmorRating(*it, ptr); ratings[i] = it->getClass().getEffectiveArmorRating(*it, ptr);
// Take in account armor condition
const bool hasHealth = it->getClass().hasItemHealth(*it);
if (hasHealth)
{
int armorHealth = it->getClass().getItemHealth(*it);
int armorMaxHealth = it->getClass().getItemMaxHealth(*it);
ratings[i] *= (float(armorHealth) / armorMaxHealth);
}
} }
} }

@ -184,7 +184,6 @@ namespace MWDialogue
// TODO play sound // TODO play sound
} }
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
callback->addResponse("", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); callback->addResponse("", Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript, mActor); executeScript (info->mResultScript, mActor);
@ -425,7 +424,7 @@ namespace MWDialogue
{ {
Filter filter (mActor, mChoice, mTalkedTo); Filter filter (mActor, mChoice, mTalkedTo);
if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting) if (dialogue->mType == ESM::Dialogue::Topic || dialogue->mType == ESM::Dialogue::Greeting)
{ {
if (const ESM::DialInfo *info = filter.search (*dialogue, true)) if (const ESM::DialInfo *info = filter.search (*dialogue, true))
{ {
@ -439,15 +438,18 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
callback->addResponse("", Interpreter::fixDefinesDialog(text, interpreterContext)); callback->addResponse("", Interpreter::fixDefinesDialog(text, interpreterContext));
// Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group, if (dialogue->mType == ESM::Dialogue::Topic)
// in which case it should not be added to the journal.
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();
iter!=dialogue->mInfo.end(); ++iter)
{ {
if (iter->mId == info->mId) // Make sure the returned DialInfo is from the Dialogue we supplied. If could also be from the Info refusal group,
// in which case it should not be added to the journal
for (ESM::Dialogue::InfoContainer::const_iterator iter = dialogue->mInfo.begin();
iter!=dialogue->mInfo.end(); ++iter)
{ {
MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor); if (iter->mId == info->mId)
break; {
MWBase::Environment::get().getJournal()->addTopic (Misc::StringUtils::lowerCase(mLastTopic), info->mId, mActor);
break;
}
} }
} }

@ -281,6 +281,8 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()
textColours.journalTopicOver, textColours.journalTopicOver,
textColours.journalTopicPressed, first); textColours.journalTopicPressed, first);
ch[1]++;
// Words can not be started with these characters // Words can not be started with these characters
if (i == 26 || i == 28) if (i == 26 || i == 28)
continue; continue;
@ -290,8 +292,6 @@ BookTypesetter::Ptr JournalBooks::createCyrillicJournalIndex ()
typesetter->write (style, to_utf8_span (buffer)); typesetter->write (style, to_utf8_span (buffer));
typesetter->lineBreak (); typesetter->lineBreak ();
ch[1]++;
} }
return typesetter; return typesetter;

@ -1069,7 +1069,8 @@ namespace MWMechanics
if (player.getClass().getNpcStats(player).isWerewolf()) if (player.getClass().getNpcStats(player).isWerewolf())
return; return;
if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()) if (ptr.getClass().isClass(ptr, "Guard") && creatureStats.getAiSequence().getTypeId() != AiPackage::TypeIdPursue && !creatureStats.getAiSequence().isInCombat()
&& creatureStats.getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() == 0)
{ {
const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore& esmStore = MWBase::Environment::get().getWorld()->getStore();
static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt(); static const int cutoff = esmStore.get<ESM::GameSetting>().find("iCrimeThreshold")->getInt();

@ -1710,6 +1710,12 @@ namespace MWMechanics
bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target) bool MechanicsManager::isAggressive(const MWWorld::Ptr &ptr, const MWWorld::Ptr &target)
{ {
// Don't become aggressive if a calm effect is active, since it would cause combat to cycle on/off as
// combat is activated here and then canceled by the calm effect
if ((ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::CalmHumanoid).getMagnitude() > 0)
|| (!ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::CalmCreature).getMagnitude() > 0))
return false;
int disposition = 50; int disposition = 50;
if (ptr.getClass().isNpc()) if (ptr.getClass().isNpc())
disposition = getDerivedDisposition(ptr, true); disposition = getDerivedDisposition(ptr, true);

@ -1026,9 +1026,9 @@ namespace MWMechanics
float y = roll / std::min(x, 100.f); float y = roll / std::min(x, 100.f);
y *= 0.25f * x; y *= 0.25f * x;
if (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) if (magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)
effect.mDuration = static_cast<int>(y);
else
effect.mDuration = 1; effect.mDuration = 1;
else
effect.mDuration = static_cast<int>(y);
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude)) if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoMagnitude))
{ {
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) if (!(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))

@ -625,7 +625,7 @@ namespace MWRender
float Animation::getStartTime(const std::string &groupname) const float Animation::getStartTime(const std::string &groupname) const
{ {
for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
{ {
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();
@ -638,7 +638,7 @@ namespace MWRender
float Animation::getTextKeyTime(const std::string &textKey) const float Animation::getTextKeyTime(const std::string &textKey) const
{ {
for(AnimSourceList::const_iterator iter(mAnimSources.begin()); iter != mAnimSources.end(); ++iter) for(AnimSourceList::const_reverse_iterator iter(mAnimSources.rbegin()); iter != mAnimSources.rend(); ++iter)
{ {
const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys(); const NifOsg::TextKeyMap &keys = (*iter)->getTextKeys();

@ -232,11 +232,6 @@ namespace MWScript
if (!ptr.isInCell()) if (!ptr.isInCell())
return; return;
if (ptr == MWMechanics::getPlayer())
{
MWBase::Environment::get().getWorld()->getPlayer().setTeleported(true);
}
std::string axis = runtime.getStringLiteral (runtime[0].mInteger); std::string axis = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
Interpreter::Type_Float pos = runtime[0].mFloat; Interpreter::Type_Float pos = runtime[0].mFloat;
@ -246,6 +241,8 @@ namespace MWScript
float ay = ptr.getRefData().getPosition().pos[1]; float ay = ptr.getRefData().getPosition().pos[1];
float az = ptr.getRefData().getPosition().pos[2]; float az = ptr.getRefData().getPosition().pos[2];
// Note: SetPos does not skip weather transitions in vanilla engine, so we do not call setTeleported(true) here.
MWWorld::Ptr updated = ptr; MWWorld::Ptr updated = ptr;
if(axis == "x") if(axis == "x")
{ {
@ -257,6 +254,17 @@ namespace MWScript
} }
else if(axis == "z") else if(axis == "z")
{ {
// We should not place actors under ground
if (ptr.getClass().isActor())
{
float terrainHeight = -std::numeric_limits<float>::max();
if (ptr.getCell()->isExterior())
terrainHeight = MWBase::Environment::get().getWorld()->getTerrainHeightAt(osg::Vec3f(ax, ay, az));
if (pos < terrainHeight)
pos = terrainHeight;
}
updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos); updated = MWBase::Environment::get().getWorld()->moveObject(ptr,ax,ay,pos);
} }
else else

@ -3,7 +3,7 @@
#include <memory> #include <memory>
#include <stdexcept> #include <stdexcept>
#include <sstream> #include <iostream>
#include <algorithm> #include <algorithm>
#include <components/vfs/manager.hpp> #include <components/vfs/manager.hpp>
@ -11,11 +11,6 @@
namespace MWSound namespace MWSound
{ {
void FFmpeg_Decoder::fail(const std::string &msg)
{
throw std::runtime_error("FFmpeg exception: "+msg);
}
int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size) int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
{ {
try try
@ -33,7 +28,8 @@ int FFmpeg_Decoder::readPacket(void *user_data, uint8_t *buf, int buf_size)
int FFmpeg_Decoder::writePacket(void *, uint8_t *, int) int FFmpeg_Decoder::writePacket(void *, uint8_t *, int)
{ {
throw std::runtime_error("can't write to read-only stream"); std::cerr<< "can't write to read-only stream" <<std::endl;
return -1;
} }
int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence) int64_t FFmpeg_Decoder::seek(void *user_data, int64_t offset, int whence)
@ -186,34 +182,34 @@ void FFmpeg_Decoder::open(const std::string &fname)
mDataStream = mResourceMgr->get(fname); mDataStream = mResourceMgr->get(fname);
if((mFormatCtx=avformat_alloc_context()) == NULL) if((mFormatCtx=avformat_alloc_context()) == NULL)
fail("Failed to allocate context"); throw std::runtime_error("Failed to allocate context");
mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek); try
if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
{ {
// "Note that a user-supplied AVFormatContext will be freed on failure". mFormatCtx->pb = avio_alloc_context(NULL, 0, 0, this, readPacket, writePacket, seek);
if (mFormatCtx) if(!mFormatCtx->pb || avformat_open_input(&mFormatCtx, fname.c_str(), NULL, NULL) != 0)
{ {
if (mFormatCtx->pb != NULL) // "Note that a user-supplied AVFormatContext will be freed on failure".
if (mFormatCtx)
{ {
if (mFormatCtx->pb->buffer != NULL) if (mFormatCtx->pb != NULL)
{ {
av_free(mFormatCtx->pb->buffer); if (mFormatCtx->pb->buffer != NULL)
mFormatCtx->pb->buffer = NULL; {
av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL;
}
av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL;
} }
av_free(mFormatCtx->pb); avformat_free_context(mFormatCtx);
mFormatCtx->pb = NULL;
} }
avformat_free_context(mFormatCtx); mFormatCtx = NULL;
throw std::runtime_error("Failed to allocate input stream");
} }
mFormatCtx = NULL;
fail("Failed to allocate input stream");
}
try
{
if(avformat_find_stream_info(mFormatCtx, NULL) < 0) if(avformat_find_stream_info(mFormatCtx, NULL) < 0)
fail("Failed to find stream info in "+fname); throw std::runtime_error("Failed to find stream info in "+fname);
for(size_t j = 0;j < mFormatCtx->nb_streams;j++) for(size_t j = 0;j < mFormatCtx->nb_streams;j++)
{ {
@ -224,32 +220,46 @@ void FFmpeg_Decoder::open(const std::string &fname)
} }
} }
if(!mStream) if(!mStream)
fail("No audio streams in "+fname); throw std::runtime_error("No audio streams in "+fname);
(*mStream)->codec->request_sample_fmt = (*mStream)->codec->sample_fmt; (*mStream)->codec->request_sample_fmt = (*mStream)->codec->sample_fmt;
AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id); AVCodec *codec = avcodec_find_decoder((*mStream)->codec->codec_id);
if(!codec) if(!codec)
{ {
std::stringstream ss("No codec found for id "); std::string ss = "No codec found for id " +
ss << (*mStream)->codec->codec_id; std::to_string((*mStream)->codec->codec_id);
fail(ss.str()); throw std::runtime_error(ss);
} }
if(avcodec_open2((*mStream)->codec, codec, NULL) < 0) if(avcodec_open2((*mStream)->codec, codec, NULL) < 0)
fail("Failed to open audio codec " + std::string(codec->long_name)); throw std::runtime_error(std::string("Failed to open audio codec ") +
codec->long_name);
mFrame = av_frame_alloc(); mFrame = av_frame_alloc();
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT ||
(*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
mOutputChannelLayout = (*mStream)->codec->channel_layout;
if(mOutputChannelLayout == 0)
mOutputChannelLayout = av_get_default_channel_layout((*mStream)->codec->channels);
} }
catch(std::exception&) catch(...) {
{
if(mStream) if(mStream)
avcodec_close((*mStream)->codec); avcodec_close((*mStream)->codec);
mStream = NULL; mStream = NULL;
if (mFormatCtx->pb->buffer != NULL) if (mFormatCtx->pb->buffer != NULL)
{ {
av_free(mFormatCtx->pb->buffer); av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL; mFormatCtx->pb->buffer = NULL;
} }
av_free(mFormatCtx->pb); av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL; mFormatCtx->pb = NULL;
@ -274,18 +284,18 @@ void FFmpeg_Decoder::close()
{ {
if (mFormatCtx->pb != NULL) if (mFormatCtx->pb != NULL)
{ {
// mFormatCtx->pb->buffer must be freed by hand, // mFormatCtx->pb->buffer must be freed by hand,
// if not, valgrind will show memleak, see: // if not, valgrind will show memleak, see:
// //
// https://trac.ffmpeg.org/ticket/1357 // https://trac.ffmpeg.org/ticket/1357
// //
if (mFormatCtx->pb->buffer != NULL) if (mFormatCtx->pb->buffer != NULL)
{ {
av_free(mFormatCtx->pb->buffer); av_free(mFormatCtx->pb->buffer);
mFormatCtx->pb->buffer = NULL; mFormatCtx->pb->buffer = NULL;
} }
av_free(mFormatCtx->pb); av_free(mFormatCtx->pb);
mFormatCtx->pb = NULL; mFormatCtx->pb = NULL;
} }
avformat_close_input(&mFormatCtx); avformat_close_input(&mFormatCtx);
} }
@ -301,16 +311,7 @@ std::string FFmpeg_Decoder::getName()
void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *type)
{ {
if(!mStream) if(!mStream)
fail("No audio stream info"); throw std::runtime_error("No audio stream info");
if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLT || (*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_FLTP)
mOutputSampleFormat = AV_SAMPLE_FMT_S16; // FIXME: Check for AL_EXT_FLOAT32 support
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_U8P)
mOutputSampleFormat = AV_SAMPLE_FMT_U8;
else if((*mStream)->codec->sample_fmt == AV_SAMPLE_FMT_S16P)
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
else
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
if(mOutputSampleFormat == AV_SAMPLE_FMT_U8) if(mOutputSampleFormat == AV_SAMPLE_FMT_U8)
*type = SampleType_UInt8; *type = SampleType_UInt8;
@ -318,19 +319,11 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
*type = SampleType_Int16; *type = SampleType_Int16;
else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT) else if(mOutputSampleFormat == AV_SAMPLE_FMT_FLT)
*type = SampleType_Float32; *type = SampleType_Float32;
else
int64_t ch_layout = (*mStream)->codec->channel_layout; {
mOutputSampleFormat = AV_SAMPLE_FMT_S16;
if(ch_layout == 0) *type = SampleType_Int16;
ch_layout = av_get_default_channel_layout((*mStream)->codec->channels); }
mOutputChannelLayout = ch_layout;
if (ch_layout == AV_CH_LAYOUT_5POINT1 || ch_layout == AV_CH_LAYOUT_7POINT1
|| ch_layout == AV_CH_LAYOUT_QUAD) // FIXME: check for AL_EXT_MCFORMATS support
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
else if (ch_layout != AV_CH_LAYOUT_MONO
&& ch_layout != AV_CH_LAYOUT_STEREO)
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
if(mOutputChannelLayout == AV_CH_LAYOUT_MONO) if(mOutputChannelLayout == AV_CH_LAYOUT_MONO)
*chans = ChannelConfig_Mono; *chans = ChannelConfig_Mono;
@ -347,13 +340,27 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
char str[1024]; char str[1024];
av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels, av_get_channel_layout_string(str, sizeof(str), (*mStream)->codec->channels,
(*mStream)->codec->channel_layout); (*mStream)->codec->channel_layout);
fail(std::string("Unsupported channel layout: ")+str); std::cerr<< "Unsupported channel layout: "<<str <<std::endl;
if((*mStream)->codec->channels == 1)
{
mOutputChannelLayout = AV_CH_LAYOUT_MONO;
*chans = ChannelConfig_Mono;
}
else
{
mOutputChannelLayout = AV_CH_LAYOUT_STEREO;
*chans = ChannelConfig_Stereo;
}
} }
*samplerate = (*mStream)->codec->sample_rate; *samplerate = (*mStream)->codec->sample_rate;
int64_t ch_layout = (*mStream)->codec->channel_layout;
if(ch_layout == 0)
ch_layout = av_get_default_channel_layout((*mStream)->codec->channels);
if(mOutputSampleFormat != (*mStream)->codec->sample_fmt if(mOutputSampleFormat != (*mStream)->codec->sample_fmt ||
|| mOutputChannelLayout != ch_layout) mOutputChannelLayout != ch_layout)
{ {
mSwr = swr_alloc_set_opts(mSwr, // SwrContext mSwr = swr_alloc_set_opts(mSwr, // SwrContext
mOutputChannelLayout, // output ch layout mOutputChannelLayout, // output ch layout
@ -365,24 +372,29 @@ void FFmpeg_Decoder::getInfo(int *samplerate, ChannelConfig *chans, SampleType *
0, // logging level offset 0, // logging level offset
NULL); // log context NULL); // log context
if(!mSwr) if(!mSwr)
fail(std::string("Couldn't allocate SwrContext")); throw std::runtime_error("Couldn't allocate SwrContext");
if(swr_init(mSwr) < 0) if(swr_init(mSwr) < 0)
fail(std::string("Couldn't initialize SwrContext")); throw std::runtime_error("Couldn't initialize SwrContext");
} }
} }
size_t FFmpeg_Decoder::read(char *buffer, size_t bytes) size_t FFmpeg_Decoder::read(char *buffer, size_t bytes)
{ {
if(!mStream) if(!mStream)
fail("No audio stream"); {
std::cerr<< "No audio stream" <<std::endl;
return 0;
}
return readAVAudioData(buffer, bytes); return readAVAudioData(buffer, bytes);
} }
void FFmpeg_Decoder::readAll(std::vector<char> &output) void FFmpeg_Decoder::readAll(std::vector<char> &output)
{ {
if(!mStream) if(!mStream)
fail("No audio stream"); {
std::cerr<< "No audio stream" <<std::endl;
return;
}
while(getAVAudioData()) while(getAVAudioData())
{ {

@ -35,7 +35,7 @@ extern "C"
namespace MWSound namespace MWSound
{ {
class FFmpeg_Decoder : public Sound_Decoder class FFmpeg_Decoder final : public Sound_Decoder
{ {
AVFormatContext *mFormatCtx; AVFormatContext *mFormatCtx;
AVStream **mStream; AVStream **mStream;
@ -66,17 +66,15 @@ namespace MWSound
bool getAVAudioData(); bool getAVAudioData();
size_t readAVAudioData(void *data, size_t length); size_t readAVAudioData(void *data, size_t length);
virtual void open(const std::string &fname); void open(const std::string &fname) override;
virtual void close(); void close() override;
virtual std::string getName(); std::string getName() override;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
virtual size_t read(char *buffer, size_t bytes); size_t read(char *buffer, size_t bytes) override;
virtual void readAll(std::vector<char> &output); void readAll(std::vector<char> &output) override;
virtual size_t getSampleOffset(); size_t getSampleOffset() override;
void fail(const std::string &msg);
FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs); FFmpeg_Decoder& operator=(const FFmpeg_Decoder &rhs);
FFmpeg_Decoder(const FFmpeg_Decoder &rhs); FFmpeg_Decoder(const FFmpeg_Decoder &rhs);

@ -1,5 +1,7 @@
#include "movieaudiofactory.hpp" #include "movieaudiofactory.hpp"
#include <iostream>
#include <extern/osg-ffmpeg-videoplayer/audiodecoder.hpp> #include <extern/osg-ffmpeg-videoplayer/audiodecoder.hpp>
#include <extern/osg-ffmpeg-videoplayer/videostate.hpp> #include <extern/osg-ffmpeg-videoplayer/videostate.hpp>
@ -13,7 +15,7 @@ namespace MWSound
{ {
class MovieAudioDecoder; class MovieAudioDecoder;
class MWSoundDecoderBridge : public Sound_Decoder class MWSoundDecoderBridge final : public Sound_Decoder
{ {
public: public:
MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder) MWSoundDecoderBridge(MWSound::MovieAudioDecoder* decoder)
@ -25,12 +27,12 @@ namespace MWSound
private: private:
MWSound::MovieAudioDecoder* mDecoder; MWSound::MovieAudioDecoder* mDecoder;
virtual void open(const std::string &fname); void open(const std::string &fname) override;
virtual void close(); void close() override;
virtual std::string getName(); std::string getName() override;
virtual void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type); void getInfo(int *samplerate, ChannelConfig *chans, SampleType *type) override;
virtual size_t read(char *buffer, size_t bytes); size_t read(char *buffer, size_t bytes) override;
virtual size_t getSampleOffset(); size_t getSampleOffset() override;
}; };
class MovieAudioDecoder : public Video::MovieAudioDecoder class MovieAudioDecoder : public Video::MovieAudioDecoder
@ -98,7 +100,7 @@ namespace MWSound
void MWSoundDecoderBridge::open(const std::string &fname) void MWSoundDecoderBridge::open(const std::string &fname)
{ {
throw std::runtime_error("unimplemented"); throw std::runtime_error("Method not implemented");
} }
void MWSoundDecoderBridge::close() {} void MWSoundDecoderBridge::close() {}
@ -123,11 +125,8 @@ namespace MWSound
else if (outputChannelLayout == AV_CH_LAYOUT_QUAD) else if (outputChannelLayout == AV_CH_LAYOUT_QUAD)
*chans = ChannelConfig_Quad; *chans = ChannelConfig_Quad;
else else
{ throw std::runtime_error("Unsupported channel layout: "+
std::stringstream error; std::to_string(outputChannelLayout));
error << "Unsupported channel layout: " << outputChannelLayout;
throw std::runtime_error(error.str());
}
AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat(); AVSampleFormat outputSampleFormat = mDecoder->getOutputSampleFormat();
if (outputSampleFormat == AV_SAMPLE_FMT_U8) if (outputSampleFormat == AV_SAMPLE_FMT_U8)
@ -140,7 +139,7 @@ namespace MWSound
{ {
char str[1024]; char str[1024];
av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat); av_get_sample_fmt_string(str, sizeof(str), outputSampleFormat);
throw std::runtime_error(std::string("Unsupported sample format: ") + str); throw std::runtime_error(std::string("Unsupported sample format: ")+str);
} }
} }

@ -411,9 +411,14 @@ bool OpenAL_SoundStream::init(bool getLoudnessData)
ChannelConfig chans; ChannelConfig chans;
SampleType type; SampleType type;
mDecoder->getInfo(&mSampleRate, &chans, &type); try {
mFormat = getALFormat(chans, type); mDecoder->getInfo(&mSampleRate, &chans, &type);
if(!mFormat) return false; mFormat = getALFormat(chans, type);
}
catch(std::exception &e) {
std::cerr<< "Failed to get stream info: "<<e.what() <<std::endl;
return false;
}
switch(type) switch(type)
{ {
@ -945,31 +950,41 @@ std::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname
{ {
getALError(); getALError();
DecoderPtr decoder = mManager.getDecoder();
// Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
if(decoder->mResourceMgr->exists(fname))
decoder->open(fname);
else
{
std::string file = fname;
std::string::size_type pos = file.rfind('.');
if(pos != std::string::npos)
file = file.substr(0, pos)+".mp3";
decoder->open(file);
}
std::vector<char> data; std::vector<char> data;
ChannelConfig chans;
SampleType type;
ALenum format; ALenum format;
int srate; int srate;
decoder->getInfo(&srate, &chans, &type); try {
format = getALFormat(chans, type); DecoderPtr decoder = mManager.getDecoder();
if(!format) return std::make_pair(nullptr, 0); // Workaround: Bethesda at some point converted some of the files to mp3, but the references were kept as .wav.
if(decoder->mResourceMgr->exists(fname))
decoder->open(fname);
else
{
std::string file = fname;
std::string::size_type pos = file.rfind('.');
if(pos != std::string::npos)
file = file.substr(0, pos)+".mp3";
decoder->open(file);
}
ChannelConfig chans;
SampleType type;
decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type);
if(format) decoder->readAll(data);
}
catch(std::exception &e) {
std::cerr<< "Failed to load audio from "<<fname<<": "<<e.what() <<std::endl;
}
decoder->readAll(data); if(data.empty())
decoder->close(); {
// If we failed to get any usable audio, substitute with silence.
format = AL_FORMAT_MONO8;
srate = 8000;
data.assign(8000, -128);
}
ALint size; ALint size;
ALuint buf = 0; ALuint buf = 0;

@ -2607,6 +2607,10 @@ namespace MWWorld
if (ptr.getRefData().isDeleted()) if (ptr.getRefData().isDeleted())
return true; return true;
// vanilla Morrowind does not allow to sell items from containers with zero capacity
if (ptr.getClass().getCapacity(ptr) <= 0.f)
return true;
if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId())) if (Misc::StringUtils::ciEqual(ptr.getCellRef().getOwner(), mOwner.getCellRef().getRefId()))
mOut.push_back(ptr); mOut.push_back(ptr);

Loading…
Cancel
Save