Merge pull request #295 from TES3MP/master

Add master commits up to 20 Sep 2017
new-script-api
David Cernat 7 years ago committed by GitHub
commit 76f1a61538

@ -24,7 +24,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main)
main->addWidget (list); main->addWidget (list);
QFontMetrics metrics (QApplication::font()); QFontMetrics metrics (QApplication::font(list));
int maxWidth = 1; int maxWidth = 1;

@ -235,6 +235,10 @@ namespace MWBase
virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0; virtual bool isReadyToBlock (const MWWorld::Ptr& ptr) const = 0;
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0; virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const = 0;
/// Check if the target actor was detected by an observer
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer) = 0;
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0; virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer) = 0;
/// List the owners that the player has stolen this item from (the owner can be an NPC or a faction). /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).

@ -82,9 +82,6 @@ namespace MWBase
///< Play a soundifle ///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory. /// \param filename name of a sound file in "Music/" in the data directory.
virtual void startRandomTitle() = 0;
///< Starts a random track from the current playlist
virtual bool isMusicPlaying() = 0; virtual bool isMusicPlaying() = 0;
///< Returns true if music is playing ///< Returns true if music is playing

@ -3,6 +3,7 @@
#include <MyGUI_ListBox.h> #include <MyGUI_ListBox.h>
#include <MyGUI_ImageBox.h> #include <MyGUI_ImageBox.h>
#include <MyGUI_Gui.h> #include <MyGUI_Gui.h>
#include <MyGUI_ScrollView.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -244,6 +245,11 @@ namespace MWGui
} }
} }
} }
}
// Canvas size must be expressed with VScroll disabled, otherwise MyGUI would expand the scroll area when the scrollbar is hidden
mSpellArea->setVisibleVScroll(false);
mSpellArea->setCanvasSize(MyGUI::IntSize(mSpellArea->getWidth(), std::max(mSpellArea->getHeight(), coord.top)));
mSpellArea->setVisibleVScroll(true);
mSpellArea->setViewOffset(MyGUI::IntPoint(0, 0));
}
} }

@ -47,7 +47,7 @@ namespace MWGui
void updateSpells(); void updateSpells();
MyGUI::ListBox* mBirthList; MyGUI::ListBox* mBirthList;
MyGUI::Widget* mSpellArea; MyGUI::ScrollView* mSpellArea;
MyGUI::ImageBox* mBirthImage; MyGUI::ImageBox* mBirthImage;
std::vector<MyGUI::Widget*> mSpellItems; std::vector<MyGUI::Widget*> mSpellItems;

@ -750,7 +750,9 @@ namespace MWGui
lastId = item.getCellRef().getRefId(); lastId = item.getCellRef().getRefId();
if (item.getClass().getTypeName() == typeid(ESM::Weapon).name() && isRightHandWeapon(item)) if (item.getClass().getTypeName() == typeid(ESM::Weapon).name() &&
isRightHandWeapon(item) &&
item.getClass().canBeEquipped(item, player).first)
{ {
found = true; found = true;
break; break;

@ -206,7 +206,8 @@ namespace MWGui
int textButtonPadding = 10; // padding between the text-widget und the button-widget int textButtonPadding = 10; // padding between the text-widget und the button-widget
int buttonLeftPadding = 10; // padding between the buttons if horizontal int buttonLeftPadding = 10; // padding between the buttons if horizontal
int buttonTopPadding = 10; // ^-- if vertical int buttonTopPadding = 10; // ^-- if vertical
int buttonPadding = 5; // padding between button label and button itself int buttonLabelLeftPadding = 12; // padding between button label and button itself, from left
int buttonLabelTopPadding = 4; // padding between button label and button itself, from top
int buttonMainPadding = 10; // padding between buttons and bottom of the main widget int buttonMainPadding = 10; // padding between buttons and bottom of the main widget
mMarkedToDelete = false; mMarkedToDelete = false;
@ -245,10 +246,10 @@ namespace MWGui
if (buttonsWidth != 0) if (buttonsWidth != 0)
buttonsWidth += buttonLeftPadding; buttonsWidth += buttonLeftPadding;
int buttonWidth = button->getTextSize().width + 2*buttonPadding; int buttonWidth = button->getTextSize().width + 2*buttonLabelLeftPadding;
buttonsWidth += buttonWidth; buttonsWidth += buttonWidth;
buttonHeight = button->getTextSize().height + 2*buttonPadding; buttonHeight = button->getTextSize().height + 2*buttonLabelTopPadding;
if (buttonsHeight != 0) if (buttonsHeight != 0)
buttonsHeight += buttonTopPadding; buttonsHeight += buttonTopPadding;
@ -295,8 +296,8 @@ namespace MWGui
buttonCord.left = left; buttonCord.left = left;
buttonCord.top = messageWidgetCoord.top + textSize.height + textButtonPadding; buttonCord.top = messageWidgetCoord.top + textSize.height + textButtonPadding;
buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding; buttonSize.width = (*button)->getTextSize().width + 2*buttonLabelLeftPadding;
buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding; buttonSize.height = (*button)->getTextSize().height + 2*buttonLabelTopPadding;
(*button)->setCoord(buttonCord); (*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize); (*button)->setSize(buttonSize);
@ -322,8 +323,8 @@ namespace MWGui
std::vector<MyGUI::Button*>::const_iterator button; std::vector<MyGUI::Button*>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button) for(button = mButtons.begin(); button != mButtons.end(); ++button)
{ {
buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; buttonSize.width = (*button)->getTextSize().width + buttonLabelLeftPadding*2;
buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; buttonSize.height = (*button)->getTextSize().height + buttonLabelTopPadding*2;
buttonCord.top = top; buttonCord.top = top;
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2; buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2;

@ -1175,6 +1175,39 @@ namespace MWMechanics
} }
} }
bool Actors::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
{
if (!actor.getClass().isActor())
return false;
// If an observer is NPC, check if he detected an actor
if (!observer.isEmpty() && observer.getClass().isNpc())
{
return
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
}
// Otherwise check if any actor in AI processing range sees the target actor
std::vector<MWWorld::Ptr> actors;
osg::Vec3f position (actor.getRefData().getPosition().asVec3());
getObjectsInRange(position, aiProcessingDistance, actors);
for(std::vector<MWWorld::Ptr>::iterator it = actors.begin(); it != actors.end(); ++it)
{
if (*it == actor)
continue;
bool result =
MWBase::Environment::get().getWorld()->getLOS(*it, actor) &&
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, *it);
if (result)
return true;
}
return false;
}
void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{ {
PtrActorMap::iterator iter = mActors.find(old); PtrActorMap::iterator iter = mActors.find(old);
@ -1989,6 +2022,7 @@ namespace MWMechanics
std::string id = reader.getHString(); std::string id = reader.getHString();
int count; int count;
reader.getHNT(count, "COUN"); reader.getHNT(count, "COUN");
if (MWBase::Environment::get().getWorld()->getStore().find(id))
mDeathCount[id] = count; mDeathCount[id] = count;
} }
} }

@ -57,6 +57,10 @@ namespace MWMechanics
PtrActorMap::const_iterator begin() { return mActors.begin(); } PtrActorMap::const_iterator begin() { return mActors.begin(); }
PtrActorMap::const_iterator end() { return mActors.end(); } PtrActorMap::const_iterator end() { return mActors.end(); }
/// Check if the target actor was detected by an observer
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer);
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment) /// paused we may want to do it manually (after equipping permanent enchantment)
void updateMagicEffects (const MWWorld::Ptr& ptr); void updateMagicEffects (const MWWorld::Ptr& ptr);

@ -1822,20 +1822,11 @@ void CharacterController::update(float duration)
if(sneak || inwater || flying) if(sneak || inwater || flying)
vec.z() = 0.0f; vec.z() = 0.0f;
if (inwater || flying)
cls.getCreatureStats(mPtr).land();
bool inJump = true; bool inJump = true;
if(!onground && !flying && !inwater) if(!onground && !flying && !inwater)
{ {
// In the air (either getting up —ascending part of jump— or falling). // In the air (either getting up —ascending part of jump— or falling).
if (world->isSlowFalling(mPtr))
{
// SlowFalling spell effect is active, do not keep previous fall height
cls.getCreatureStats(mPtr).land();
}
forcestateupdate = (mJumpState != JumpState_InAir); forcestateupdate = (mJumpState != JumpState_InAir);
jumpstate = JumpState_InAir; jumpstate = JumpState_InAir;
@ -1881,7 +1872,7 @@ void CharacterController::update(float duration)
} }
} }
} }
else if(mJumpState == JumpState_InAir) else if(mJumpState == JumpState_InAir && !inwater && !flying)
{ {
forcestateupdate = true; forcestateupdate = true;
jumpstate = JumpState_Landing; jumpstate = JumpState_Landing;
@ -1964,9 +1955,6 @@ void CharacterController::update(float duration)
movestate = mMovementState; movestate = mMovementState;
} }
if (onground)
cls.getCreatureStats(mPtr).land();
if(movestate != CharState_None && movestate != CharState_TurnLeft && movestate != CharState_TurnRight) if(movestate != CharState_None && movestate != CharState_TurnLeft && movestate != CharState_TurnRight)
clearAnimQueue(); clearAnimQueue();
@ -2158,12 +2146,8 @@ void CharacterController::unpersistAnimationState()
bool CharacterController::playGroup(const std::string &groupname, int mode, int count, bool persist) bool CharacterController::playGroup(const std::string &groupname, int mode, int count, bool persist)
{ {
if(!mAnimation || !mAnimation->hasAnimation(groupname)) if(!mAnimation || !mAnimation->hasAnimation(groupname))
{
std::cerr<< "Animation "<<groupname<<" not found for " << mPtr.getCellRef().getRefId() << std::endl;
return false; return false;
}
else
{
// If this animation is a looped animation (has a "loop start" key) that is already playing // If this animation is a looped animation (has a "loop start" key) that is already playing
// and has not yet reached the end of the loop, allow it to continue animating with its existing loop count // and has not yet reached the end of the loop, allow it to continue animating with its existing loop count
// and remove any other animations that were queued. // and remove any other animations that were queued.
@ -2228,7 +2212,6 @@ bool CharacterController::playGroup(const std::string &groupname, int mode, int
mAnimQueue.resize(1); mAnimQueue.resize(1);
mAnimQueue.push_back(entry); mAnimQueue.push_back(entry);
} }
}
return true; return true;
} }

@ -436,6 +436,11 @@ namespace MWMechanics
mObjects.update(duration, paused); mObjects.update(duration, paused);
} }
bool MechanicsManager::isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer)
{
return mActors.isActorDetected(actor, observer);
}
bool MechanicsManager::isAttackPrepairing(const MWWorld::Ptr& ptr) bool MechanicsManager::isAttackPrepairing(const MWWorld::Ptr& ptr)
{ {
return mActors.isAttackPrepairing(ptr); return mActors.isAttackPrepairing(ptr);

@ -201,6 +201,10 @@ namespace MWMechanics
/// Is \a ptr casting spell or using weapon now? /// Is \a ptr casting spell or using weapon now?
virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const; virtual bool isAttackingOrSpell(const MWWorld::Ptr &ptr) const;
/// Check if the target actor was detected by an observer
/// If the observer is a non-NPC, check all actors in AI processing distance as observers
virtual bool isActorDetected(const MWWorld::Ptr& actor, const MWWorld::Ptr& observer);
virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer); virtual void confiscateStolenItems (const MWWorld::Ptr& player, const MWWorld::Ptr& targetContainer);
/// List the owners that the player has stolen this item from (the owner can be an NPC or a faction). /// List the owners that the player has stolen this item from (the owner can be an NPC or a faction).

@ -1404,14 +1404,16 @@ namespace MWPhysics
// Slow fall reduces fall speed by a factor of (effect magnitude / 200) // Slow fall reduces fall speed by a factor of (effect magnitude / 200)
float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f)); float slowFall = 1.f - std::max(0.f, std::min(1.f, effects.get(ESM::MagicEffect::SlowFall).getMagnitude() * 0.005f));
bool flying = world->isFlying(iter->first);
bool wasOnGround = physicActor->getOnGround();
osg::Vec3f position = physicActor->getPosition(); osg::Vec3f position = physicActor->getPosition();
float oldHeight = position.z(); float oldHeight = position.z();
bool positionChanged = false; bool positionChanged = false;
for (int i=0; i<numSteps; ++i) for (int i=0; i<numSteps; ++i)
{ {
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, physicsDt, position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, physicsDt,
world->isFlying(iter->first), flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
if (position != physicActor->getPosition()) if (position != physicActor->getPosition())
positionChanged = true; positionChanged = true;
physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct physicActor->setPosition(position); // always set even if unchanged to make sure interpolation is correct
@ -1424,8 +1426,11 @@ namespace MWPhysics
float heightDiff = position.z() - oldHeight; float heightDiff = position.z() - oldHeight;
if (heightDiff < 0) MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff); if ((wasOnGround && physicActor->getOnGround()) || flying || world->isSwimming(iter->first) || slowFall < 1)
stats.land();
else if (heightDiff < 0)
stats.addToFallHeight(-heightDiff);
mMovementResults.push_back(std::make_pair(iter->first, interpolated)); mMovementResults.push_back(std::make_pair(iter->first, interpolated));
} }

@ -757,8 +757,6 @@ namespace MWRender
break; break;
} }
} }
if(iter == mAnimSources.rend())
std::cerr<< "Failed to find animation "<<groupname<<" for "<<mPtr.getCellRef().getRefId() <<std::endl;
resetActiveGroups(); resetActiveGroups();
} }
@ -795,7 +793,7 @@ namespace MWRender
// We have to ignore extra garbage at the end. // We have to ignore extra garbage at the end.
// The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop". // The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop".
// Why, just why? :( // Why, just why? :(
&& (stopkey->second.size() < stoptag.size() || stopkey->second.substr(0,stoptag.size()) != stoptag)) && (stopkey->second.size() < stoptag.size() || stopkey->second.compare(0,stoptag.size(), stoptag) != 0))
++stopkey; ++stopkey;
if(stopkey == keys.rend()) if(stopkey == keys.rend())
return false; return false;

@ -530,8 +530,10 @@ namespace MWRender
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr) void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
{ {
if(mPlayerAnimation.get()) if(mPlayerAnimation.get())
{
setupPlayer(ptr);
mPlayerAnimation->updatePtr(ptr); mPlayerAnimation->updatePtr(ptr);
}
mCamera->attachTo(ptr); mCamera->attachTo(ptr);
} }
@ -834,6 +836,7 @@ namespace MWRender
player.getRefData().setBaseNode(mPlayerNode); player.getRefData().setBaseNode(mPlayerNode);
mWater->removeEmitter(player);
mWater->addEmitter(player); mWater->addEmitter(player);
} }

@ -372,21 +372,14 @@ namespace MWScript
virtual void execute (Interpreter::Runtime& runtime) virtual void execute (Interpreter::Runtime& runtime)
{ {
MWWorld::Ptr observer = R()(runtime); MWWorld::Ptr observer = R()(runtime, false); // required=false
std::string actorID = runtime.getStringLiteral (runtime[0].mInteger); std::string actorID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop(); runtime.pop();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true); MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
if(!actor.getClass().isActor() || !observer.getClass().isActor()) Interpreter::Type_Integer value = MWBase::Environment::get().getMechanicsManager()->isActorDetected(actor, observer);
{
runtime.push(0);
return;
}
Interpreter::Type_Integer value =
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
runtime.push (value); runtime.push (value);
} }

@ -192,12 +192,11 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
{ AL_FORMAT_STEREO8, ChannelConfig_Stereo, SampleType_UInt8 }, { AL_FORMAT_STEREO8, ChannelConfig_Stereo, SampleType_UInt8 },
}}; }};
auto fmt = std::find_if(fmtlist.cbegin(), fmtlist.cend(), for(auto &fmt : fmtlist)
[chans,type](const FormatEntry &fmt) -> bool {
{ return fmt.chans == chans && fmt.type == type; } if(fmt.chans == chans && fmt.type == type)
); return fmt.format;
if(fmt != fmtlist.cend()) }
return fmt->format;
if(alIsExtensionPresent("AL_EXT_MCFORMATS")) if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{ {
@ -209,37 +208,33 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
{ "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 }, { "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 },
{ "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 }, { "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 },
}}; }};
ALenum format = AL_NONE;
std::find_if(mcfmtlist.cbegin(), mcfmtlist.cend(), for(auto &fmt : mcfmtlist)
[&format,chans,type](const FormatEntryExt &fmt) -> bool
{ {
if(fmt.chans == chans && fmt.type == type) if(fmt.chans == chans && fmt.type == type)
format = alGetEnumValue(fmt.name); {
return format != 0 && format != -1; ALenum format = alGetEnumValue(fmt.name);
}
);
if(format != 0 && format != -1) if(format != 0 && format != -1)
return format; return format;
} }
}
}
if(alIsExtensionPresent("AL_EXT_FLOAT32")) if(alIsExtensionPresent("AL_EXT_FLOAT32"))
{ {
static const std::array<FormatEntryExt,2> fltfmtlist{{ static const std::array<FormatEntryExt,2> fltfmtlist{{
{ "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 }, { "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 },
{ "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 }, { "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 },
}}; }};
ALenum format = AL_NONE;
std::find_if(fltfmtlist.cbegin(), fltfmtlist.cend(), for(auto &fmt : fltfmtlist)
[&format,chans,type](const FormatEntryExt &fmt) -> bool
{ {
if(fmt.chans == chans && fmt.type == type) if(fmt.chans == chans && fmt.type == type)
format = alGetEnumValue(fmt.name); {
return format != 0 && format != -1; ALenum format = alGetEnumValue(fmt.name);
}
);
if(format != 0 && format != -1) if(format != 0 && format != -1)
return format; return format;
}
}
if(alIsExtensionPresent("AL_EXT_MCFORMATS")) if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
{ {
@ -249,18 +244,17 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
{ "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 }, { "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 },
}}; }};
std::find_if(fltmcfmtlist.cbegin(), fltmcfmtlist.cend(), for(auto &fmt : fltmcfmtlist)
[&format,chans,type](const FormatEntryExt &fmt) -> bool
{ {
if(fmt.chans == chans && fmt.type == type) if(fmt.chans == chans && fmt.type == type)
format = alGetEnumValue(fmt.name); {
return format != 0 && format != -1; ALenum format = alGetEnumValue(fmt.name);
}
);
if(format != 0 && format != -1) if(format != 0 && format != -1)
return format; return format;
} }
} }
}
}
std::cerr<< "Unsupported sound format ("<<getChannelConfigName(chans)<<", "<< std::cerr<< "Unsupported sound format ("<<getChannelConfigName(chans)<<", "<<
getSampleTypeName(type)<<")" <<std::endl; getSampleTypeName(type)<<")" <<std::endl;
@ -547,11 +541,11 @@ ALint OpenAL_SoundStream::refillQueue()
std::vector<char> data(mBufferSize); std::vector<char> data(mBufferSize);
for(;!mIsFinished && (ALuint)queued < mBuffers.size();++queued) for(;!mIsFinished && (ALuint)queued < mBuffers.size();++queued)
{ {
size_t got = mDecoder->read(&data[0], data.size()); size_t got = mDecoder->read(data.data(), data.size());
if(got < data.size()) if(got < data.size())
{ {
mIsFinished = true; mIsFinished = true;
std::memset(&data[got], mSilence, data.size()-got); std::fill(data.begin()+got, data.end(), mSilence);
} }
if(got > 0) if(got > 0)
{ {
@ -559,7 +553,7 @@ ALint OpenAL_SoundStream::refillQueue()
mLoudnessAnalyzer->analyzeLoudness(data); mLoudnessAnalyzer->analyzeLoudness(data);
ALuint bufid = mBuffers[mCurrentBufIdx]; ALuint bufid = mBuffers[mCurrentBufIdx];
alBufferData(bufid, mFormat, &data[0], data.size(), mSampleRate); alBufferData(bufid, mFormat, data.data(), data.size(), mSampleRate);
alSourceQueueBuffers(mSource, 1, &bufid); alSourceQueueBuffers(mSource, 1, &bufid);
mCurrentBufIdx = (mCurrentBufIdx+1) % mBuffers.size(); mCurrentBufIdx = (mCurrentBufIdx+1) % mBuffers.size();
} }
@ -594,24 +588,32 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
{ {
deinit(); deinit();
std::cout<< "Initializing OpenAL..." <<std::endl;
mDevice = alcOpenDevice(devname.c_str()); mDevice = alcOpenDevice(devname.c_str());
if(!mDevice && !devname.empty())
{
std::cerr<< "Failed to open \""<<devname<<"\", trying default" <<std::endl;
mDevice = alcOpenDevice(nullptr);
}
if(!mDevice) if(!mDevice)
{ {
if(devname.empty())
std::cerr<< "Failed to open default audio device" <<std::endl; std::cerr<< "Failed to open default audio device" <<std::endl;
else
std::cerr<< "Failed to open \""<<devname<<"\"" <<std::endl;
return false; return false;
} }
else
{ const ALCchar *name = nullptr;
const ALCchar *name = NULL;
if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT")) if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER); name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER);
if(alcGetError(mDevice) != AL_NO_ERROR || !name) if(alcGetError(mDevice) != AL_NO_ERROR || !name)
name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER); name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER);
std::cout<< "Opened \""<<name<<"\"" <<std::endl; std::cout<< "Opened \""<<name<<"\"" <<std::endl;
}
ALCint major=0, minor=0;
alcGetIntegerv(mDevice, ALC_MAJOR_VERSION, 1, &major);
alcGetIntegerv(mDevice, ALC_MINOR_VERSION, 1, &minor);
std::cout<< " ALC Version: "<<major<<"."<<minor<<"\n"<<
" ALC Extensions: "<<alcGetString(mDevice, ALC_EXTENSIONS) <<std::endl;
ALC.EXT_EFX = alcIsExtensionPresent(mDevice, "ALC_EXT_EFX"); ALC.EXT_EFX = alcIsExtensionPresent(mDevice, "ALC_EXT_EFX");
ALC.SOFT_HRTF = alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF"); ALC.SOFT_HRTF = alcIsExtensionPresent(mDevice, "ALC_SOFT_HRTF");
@ -643,7 +645,7 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
} }
if(index < 0) if(index < 0)
std::cerr<< "Failed to find HRTF name \""<<hrtfname<<"\", using default" <<std::endl; std::cerr<< "Failed to find HRTF \""<<hrtfname<<"\", using default" <<std::endl;
else else
{ {
attrs.push_back(ALC_HRTF_ID_SOFT); attrs.push_back(ALC_HRTF_ID_SOFT);
@ -665,6 +667,11 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
return false; return false;
} }
std::cout<< " Vendor: "<<alGetString(AL_VENDOR)<<"\n"<<
" Renderer: "<<alGetString(AL_RENDERER)<<"\n"<<
" Version: "<<alGetString(AL_VERSION)<<"\n"<<
" Extensions: "<<alGetString(AL_EXTENSIONS)<<std::endl;
if(!ALC.SOFT_HRTF) if(!ALC.SOFT_HRTF)
std::cout<< "HRTF status unavailable" <<std::endl; std::cout<< "HRTF status unavailable" <<std::endl;
else else
@ -698,7 +705,7 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
{ {
ALuint src = 0; ALuint src = 0;
alGenSources(1, &src); alGenSources(1, &src);
if(getALError() != AL_NO_ERROR) if(alGetError() != AL_NO_ERROR)
break; break;
mFreeSources.push_back(src); mFreeSources.push_back(src);
} }
@ -824,8 +831,8 @@ void OpenAL_Output::deinit()
{ {
mStreamThread->removeAll(); mStreamThread->removeAll();
for(size_t i = 0;i < mFreeSources.size();i++) for(ALuint source : mFreeSources)
alDeleteSources(1, &mFreeSources[i]); alDeleteSources(1, &source);
mFreeSources.clear(); mFreeSources.clear();
if(mEffectSlot) if(mEffectSlot)
@ -934,7 +941,7 @@ void OpenAL_Output::setHrtf(const std::string &hrtfname, HrtfMode hrtfmode)
} }
Sound_Handle OpenAL_Output::loadSound(const std::string &fname) std::pair<Sound_Handle,size_t> OpenAL_Output::loadSound(const std::string &fname)
{ {
getALError(); getALError();
@ -959,27 +966,31 @@ Sound_Handle OpenAL_Output::loadSound(const std::string &fname)
decoder->getInfo(&srate, &chans, &type); decoder->getInfo(&srate, &chans, &type);
format = getALFormat(chans, type); format = getALFormat(chans, type);
if(!format) return nullptr; if(!format) return std::make_pair(nullptr, 0);
decoder->readAll(data); decoder->readAll(data);
decoder->close(); decoder->close();
ALint size;
ALuint buf = 0; ALuint buf = 0;
alGenBuffers(1, &buf); alGenBuffers(1, &buf);
alBufferData(buf, format, &data[0], data.size(), srate); alBufferData(buf, format, data.data(), data.size(), srate);
alGetBufferi(buf, AL_SIZE, &size);
if(getALError() != AL_NO_ERROR) if(getALError() != AL_NO_ERROR)
{ {
if(buf && alIsBuffer(buf)) if(buf && alIsBuffer(buf))
alDeleteBuffers(1, &buf); alDeleteBuffers(1, &buf);
getALError(); getALError();
return nullptr; return std::make_pair(nullptr, 0);
} }
return MAKE_PTRID(buf); return std::make_pair(MAKE_PTRID(buf), size);
} }
void OpenAL_Output::unloadSound(Sound_Handle data) size_t OpenAL_Output::unloadSound(Sound_Handle data)
{ {
ALuint buffer = GET_PTRID(data); ALuint buffer = GET_PTRID(data);
if(!buffer) return 0;
// Make sure no sources are playing this buffer before unloading it. // Make sure no sources are playing this buffer before unloading it.
SoundVec::const_iterator iter = mActiveSounds.begin(); SoundVec::const_iterator iter = mActiveSounds.begin();
for(;iter != mActiveSounds.end();++iter) for(;iter != mActiveSounds.end();++iter)
@ -996,19 +1007,11 @@ void OpenAL_Output::unloadSound(Sound_Handle data)
alSourcei(source, AL_BUFFER, 0); alSourcei(source, AL_BUFFER, 0);
} }
} }
alDeleteBuffers(1, &buffer);
getALError();
}
size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const
{
ALuint buffer = GET_PTRID(data);
ALint size = 0; ALint size = 0;
alGetBufferi(buffer, AL_SIZE, &size); alGetBufferi(buffer, AL_SIZE, &size);
alDeleteBuffers(1, &buffer);
getALError(); getALError();
return size;
return (ALuint)size;
} }
@ -1368,23 +1371,19 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi
if(mWaterFilter) if(mWaterFilter)
{ {
ALuint filter = (env == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL; ALuint filter = (env == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL;
std::for_each(mActiveSounds.cbegin(), mActiveSounds.cend(), for(Sound *sound : mActiveSounds)
[filter](const SoundVec::value_type &item) -> void
{ {
if(item->getUseEnv()) if(sound->getUseEnv())
alSourcei(GET_PTRID(item->mHandle), AL_DIRECT_FILTER, filter); alSourcei(GET_PTRID(sound->mHandle), AL_DIRECT_FILTER, filter);
} }
); for(Stream *sound : mActiveStreams)
std::for_each(mActiveStreams.cbegin(), mActiveStreams.cend(),
[filter](const StreamVec::value_type &item) -> void
{ {
if(item->getUseEnv()) if(sound->getUseEnv())
alSourcei( alSourcei(
reinterpret_cast<OpenAL_SoundStream*>(item->mHandle)->mSource, reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle)->mSource,
AL_DIRECT_FILTER, filter AL_DIRECT_FILTER, filter
); );
} }
);
} }
// Update the environment effect // Update the environment effect
if(mEffectSlot) if(mEffectSlot)
@ -1403,23 +1402,19 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi
void OpenAL_Output::pauseSounds(int types) void OpenAL_Output::pauseSounds(int types)
{ {
std::vector<ALuint> sources; std::vector<ALuint> sources;
std::for_each(mActiveSounds.cbegin(), mActiveSounds.cend(), for(Sound *sound : mActiveSounds)
[types,&sources](const SoundVec::value_type &sound) -> void
{ {
if(sound && sound->mHandle && (types&sound->getPlayType())) if((types&sound->getPlayType()))
sources.push_back(GET_PTRID(sound->mHandle)); sources.push_back(GET_PTRID(sound->mHandle));
} }
); for(Stream *sound : mActiveStreams)
std::for_each(mActiveStreams.cbegin(), mActiveStreams.cend(),
[types,&sources](const StreamVec::value_type &stream) -> void
{ {
if(stream && stream->mHandle && (types&stream->getPlayType())) if((types&sound->getPlayType()))
{ {
OpenAL_SoundStream *strm = reinterpret_cast<OpenAL_SoundStream*>(stream->mHandle); OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
sources.push_back(strm->mSource); sources.push_back(stream->mSource);
} }
} }
);
if(!sources.empty()) if(!sources.empty())
{ {
alSourcePausev(sources.size(), sources.data()); alSourcePausev(sources.size(), sources.data());
@ -1430,23 +1425,19 @@ void OpenAL_Output::pauseSounds(int types)
void OpenAL_Output::resumeSounds(int types) void OpenAL_Output::resumeSounds(int types)
{ {
std::vector<ALuint> sources; std::vector<ALuint> sources;
std::for_each(mActiveSounds.cbegin(), mActiveSounds.cend(), for(Sound *sound : mActiveSounds)
[types,&sources](const SoundVec::value_type &sound) -> void
{ {
if(sound && sound->mHandle && (types&sound->getPlayType())) if((types&sound->getPlayType()))
sources.push_back(GET_PTRID(sound->mHandle)); sources.push_back(GET_PTRID(sound->mHandle));
} }
); for(Stream *sound : mActiveStreams)
std::for_each(mActiveStreams.cbegin(), mActiveStreams.cend(),
[types,&sources](const StreamVec::value_type &stream) -> void
{ {
if(stream && stream->mHandle && (types&stream->getPlayType())) if((types&sound->getPlayType()))
{ {
OpenAL_SoundStream *strm = reinterpret_cast<OpenAL_SoundStream*>(stream->mHandle); OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
sources.push_back(strm->mSource); sources.push_back(stream->mSource);
} }
} }
);
if(!sources.empty()) if(!sources.empty())
{ {
alSourcePlayv(sources.size(), sources.data()); alSourcePlayv(sources.size(), sources.data());

@ -66,9 +66,8 @@ namespace MWSound
virtual std::vector<std::string> enumerateHrtf(); virtual std::vector<std::string> enumerateHrtf();
virtual void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode); virtual void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode);
virtual Sound_Handle loadSound(const std::string &fname); virtual std::pair<Sound_Handle,size_t> loadSound(const std::string &fname);
virtual void unloadSound(Sound_Handle data); virtual size_t unloadSound(Sound_Handle data);
virtual size_t getSoundDataSize(Sound_Handle data) const;
virtual bool playSound(Sound *sound, Sound_Handle data, float offset); virtual bool playSound(Sound *sound, Sound_Handle data, float offset);
virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset); virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset);

@ -36,9 +36,8 @@ namespace MWSound
virtual std::vector<std::string> enumerateHrtf() = 0; virtual std::vector<std::string> enumerateHrtf() = 0;
virtual void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode) = 0; virtual void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode) = 0;
virtual Sound_Handle loadSound(const std::string &fname) = 0; virtual std::pair<Sound_Handle,size_t> loadSound(const std::string &fname) = 0;
virtual void unloadSound(Sound_Handle data) = 0; virtual size_t unloadSound(Sound_Handle data) = 0;
virtual size_t getSoundDataSize(Sound_Handle data) const = 0;
virtual bool playSound(Sound *sound, Sound_Handle data, float offset) = 0; virtual bool playSound(Sound *sound, Sound_Handle data, float offset) = 0;
virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset) = 0; virtual bool playSound3D(Sound *sound, Sound_Handle data, float offset) = 0;

@ -26,11 +26,7 @@
#include "sound.hpp" #include "sound.hpp"
#include "openal_output.hpp" #include "openal_output.hpp"
#define SOUND_OUT "OpenAL"
#include "ffmpeg_decoder.hpp" #include "ffmpeg_decoder.hpp"
#ifndef SOUND_IN
#define SOUND_IN "FFmpeg"
#endif
namespace MWSound namespace MWSound
@ -84,45 +80,35 @@ namespace MWSound
mBufferCacheMin = std::min(mBufferCacheMin*1024*1024, mBufferCacheMax); mBufferCacheMin = std::min(mBufferCacheMin*1024*1024, mBufferCacheMax);
if(!useSound) if(!useSound)
{
std::cout<< "Sound disabled." <<std::endl;
return; return;
}
std::string hrtfname = Settings::Manager::getString("hrtf", "Sound"); std::string hrtfname = Settings::Manager::getString("hrtf", "Sound");
int hrtfstate = Settings::Manager::getInt("hrtf enable", "Sound"); int hrtfstate = Settings::Manager::getInt("hrtf enable", "Sound");
HrtfMode hrtfmode = hrtfstate < 0 ? HrtfMode::Auto : HrtfMode hrtfmode = hrtfstate < 0 ? HrtfMode::Auto :
hrtfstate > 0 ? HrtfMode::Enable : HrtfMode::Disable; hrtfstate > 0 ? HrtfMode::Enable : HrtfMode::Disable;
std::cout << "Sound output: " << SOUND_OUT << std::endl;
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
std::vector<std::string> names = mOutput->enumerate();
std::cout <<"Enumerated output devices:\n";
std::for_each(names.cbegin(), names.cend(),
[](const std::string &name) -> void
{ std::cout <<" "<<name<<"\n"; }
);
std::cout.flush();
std::string devname = Settings::Manager::getString("device", "Sound"); std::string devname = Settings::Manager::getString("device", "Sound");
bool inited = mOutput->init(devname, hrtfname, hrtfmode); if(!mOutput->init(devname, hrtfname, hrtfmode))
if(!inited && !devname.empty())
{ {
std::cerr<< "Failed to initialize device \""<<devname<<"\", trying default" <<std::endl; std::cerr<< "Failed to initialize audio output, sound disabled" <<std::endl;
inited = mOutput->init(std::string(), hrtfname, hrtfmode);
}
if(!inited)
{
std::cerr<< "Failed to initialize default audio device, sound disabled" <<std::endl;
return; return;
} }
std::vector<std::string> names = mOutput->enumerate();
std::cout <<"Enumerated output devices:\n";
for(const std::string &name : names)
std::cout <<" "<<name<<"\n";
std::cout.flush();
names = mOutput->enumerateHrtf(); names = mOutput->enumerateHrtf();
if(!names.empty()) if(!names.empty())
{ {
std::cout<< "Enumerated HRTF names:\n"; std::cout<< "Enumerated HRTF names:\n";
std::for_each(names.cbegin(), names.cend(), for(const std::string &name : names)
[](const std::string &name) -> void std::cout <<" "<<name<<"\n";
{ std::cout<< " "<<name<<"\n"; }
);
std::cout.flush(); std::cout.flush();
} }
} }
@ -130,12 +116,11 @@ namespace MWSound
SoundManager::~SoundManager() SoundManager::~SoundManager()
{ {
clear(); clear();
SoundBufferList::element_type::iterator sfxiter = mSoundBuffers->begin(); for(Sound_Buffer &sfx : *mSoundBuffers)
for(;sfxiter != mSoundBuffers->end();++sfxiter)
{ {
if(sfxiter->mHandle) if(sfx.mHandle)
mOutput->unloadSound(sfxiter->mHandle); mOutput->unloadSound(sfx.mHandle);
sfxiter->mHandle = 0; sfx.mHandle = 0;
} }
mUnusedBuffers.clear(); mUnusedBuffers.clear();
mOutput.reset(); mOutput.reset();
@ -200,9 +185,23 @@ namespace MWSound
// minRange, and maxRange), and ensure it's ready for use. // minRange, and maxRange), and ensure it's ready for use.
Sound_Buffer *SoundManager::loadSound(const std::string &soundId) Sound_Buffer *SoundManager::loadSound(const std::string &soundId)
{ {
#ifdef __GNUC__
#define LIKELY(x) __builtin_expect((bool)(x), true)
#define UNLIKELY(x) __builtin_expect((bool)(x), false)
#else
#define LIKELY(x) (bool)(x)
#define UNLIKELY(x) (bool)(x)
#endif
if(UNLIKELY(mBufferNameMap.empty()))
{
MWBase::World *world = MWBase::Environment::get().getWorld();
for(const ESM::Sound &sound : world->getStore().get<ESM::Sound>())
insertSound(Misc::StringUtils::lowerCase(sound.mId), &sound);
}
Sound_Buffer *sfx; Sound_Buffer *sfx;
NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId); NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId);
if(snd != mBufferNameMap.end()) if(LIKELY(snd != mBufferNameMap.end()))
sfx = snd->second; sfx = snd->second;
else else
{ {
@ -211,13 +210,16 @@ namespace MWSound
if(!sound) return nullptr; if(!sound) return nullptr;
sfx = insertSound(soundId, sound); sfx = insertSound(soundId, sound);
} }
#undef LIKELY
#undef UNLIKELY
if(!sfx->mHandle) if(!sfx->mHandle)
{ {
sfx->mHandle = mOutput->loadSound(sfx->mResourceName); size_t size;
std::tie(sfx->mHandle, size) = mOutput->loadSound(sfx->mResourceName);
if(!sfx->mHandle) return nullptr; if(!sfx->mHandle) return nullptr;
mBufferCacheSize += mOutput->getSoundDataSize(sfx->mHandle); mBufferCacheSize += size;
if(mBufferCacheSize > mBufferCacheMax) if(mBufferCacheSize > mBufferCacheMax)
{ {
do { do {
@ -228,8 +230,8 @@ namespace MWSound
} }
Sound_Buffer *unused = mUnusedBuffers.back(); Sound_Buffer *unused = mUnusedBuffers.back();
mBufferCacheSize -= mOutput->getSoundDataSize(unused->mHandle); size = mOutput->unloadSound(unused->mHandle);
mOutput->unloadSound(unused->mHandle); mBufferCacheSize -= size;
unused->mHandle = 0; unused->mHandle = 0;
mUnusedBuffers.pop_back(); mUnusedBuffers.pop_back();
@ -389,11 +391,6 @@ namespace MWSound
mMusic->setFadeout(0.5f); mMusic->setFadeout(0.5f);
} }
void SoundManager::streamMusic(const std::string& filename)
{
advanceMusic("Music/"+filename);
}
void SoundManager::startRandomTitle() void SoundManager::startRandomTitle()
{ {
std::vector<std::string> filelist; std::vector<std::string> filelist;
@ -444,6 +441,12 @@ namespace MWSound
tracklist.pop_back(); tracklist.pop_back();
} }
void SoundManager::streamMusic(const std::string& filename)
{
advanceMusic("Music/"+filename);
}
bool SoundManager::isMusicPlaying() bool SoundManager::isMusicPlaying()
{ {
return mMusic && mOutput->isStreamPlaying(mMusic); return mMusic && mOutput->isStreamPlaying(mMusic);
@ -678,11 +681,10 @@ namespace MWSound
if(snditer != mActiveSounds.end()) if(snditer != mActiveSounds.end())
{ {
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &snd : snditer->second)
for(;sndidx != snditer->second.end();++sndidx)
{ {
if(sndidx->second == sfx) if(snd.second == sfx)
mOutput->finishSound(sndidx->first); mOutput->finishSound(snd.first);
} }
} }
} }
@ -692,33 +694,28 @@ namespace MWSound
SoundMap::iterator snditer = mActiveSounds.find(ptr); SoundMap::iterator snditer = mActiveSounds.find(ptr);
if(snditer != mActiveSounds.end()) if(snditer != mActiveSounds.end())
{ {
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &snd : snditer->second)
for(;sndidx != snditer->second.end();++sndidx) mOutput->finishSound(snd.first);
mOutput->finishSound(sndidx->first);
} }
SaySoundMap::iterator sayiter = mActiveSaySounds.find(ptr);
if(sayiter != mActiveSaySounds.end())
mOutput->finishStream(sayiter->second);
} }
void SoundManager::stopSound(const MWWorld::CellStore *cell) void SoundManager::stopSound(const MWWorld::CellStore *cell)
{ {
SoundMap::iterator snditer = mActiveSounds.begin(); for(SoundMap::value_type &snd : mActiveSounds)
for(;snditer != mActiveSounds.end();++snditer)
{ {
if(snditer->first != MWWorld::ConstPtr() && if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)
snditer->first != MWMechanics::getPlayer() &&
snditer->first.getCell() == cell)
{ {
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &sndbuf : snd.second)
for(;sndidx != snditer->second.end();++sndidx) mOutput->finishSound(sndbuf.first);
mOutput->finishSound(sndidx->first);
} }
} }
SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); for(SaySoundMap::value_type &snd : mActiveSaySounds)
for(;sayiter != mActiveSaySounds.end();++sayiter)
{ {
if(sayiter->first != MWWorld::ConstPtr() && if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)
sayiter->first != MWMechanics::getPlayer() && mOutput->finishStream(snd.second);
sayiter->first.getCell() == cell)
mOutput->finishStream(sayiter->second);
} }
} }
@ -728,11 +725,10 @@ namespace MWSound
if(snditer != mActiveSounds.end()) if(snditer != mActiveSounds.end())
{ {
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &sndbuf : snditer->second)
for(;sndidx != snditer->second.end();++sndidx)
{ {
if(sndidx->second == sfx) if(sndbuf.second == sfx)
mOutput->finishSound(sndidx->first); mOutput->finishSound(sndbuf.first);
} }
} }
} }
@ -744,11 +740,10 @@ namespace MWSound
if(snditer != mActiveSounds.end()) if(snditer != mActiveSounds.end())
{ {
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId)); Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &sndbuf : snditer->second)
for(;sndidx != snditer->second.end();++sndidx)
{ {
if(sndidx->second == sfx) if(sndbuf.second == sfx)
sndidx->first->setFadeout(duration); sndbuf.first->setFadeout(duration);
} }
} }
} }
@ -759,12 +754,10 @@ namespace MWSound
if(snditer != mActiveSounds.end()) if(snditer != mActiveSounds.end())
{ {
Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId)); Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
SoundBufferRefPairList::const_iterator sndidx = snditer->second.begin(); return std::find_if(snditer->second.cbegin(), snditer->second.cend(),
for(;sndidx != snditer->second.end();++sndidx) [this,sfx](const SoundBufferRefPair &snd) -> bool
{ { return snd.second == sfx && mOutput->isSoundPlaying(snd.first); }
if(sndidx->second == sfx && mOutput->isSoundPlaying(sndidx->first)) ) != snditer->second.cend();
return true;
}
} }
return false; return false;
} }
@ -823,10 +816,8 @@ namespace MWSound
if(total == 0) if(total == 0)
{ {
std::for_each(regn->mSoundList.cbegin(), regn->mSoundList.cend(), for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
[](const ESM::Region::SoundRef &sndref) -> void total += (int)sndref.mChance;
{ total += (int)sndref.mChance; }
);
if(total == 0) if(total == 0)
return; return;
} }
@ -834,20 +825,15 @@ namespace MWSound
int r = Misc::Rng::rollDice(total); int r = Misc::Rng::rollDice(total);
int pos = 0; int pos = 0;
std::find_if_not(regn->mSoundList.cbegin(), regn->mSoundList.cend(), for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
[&pos, r, this](const ESM::Region::SoundRef &sndref) -> bool
{ {
if(r - pos < sndref.mChance) if(r - pos < sndref.mChance)
{ {
playSound(sndref.mSound.toString(), 1.0f, 1.0f); playSound(sndref.mSound.toString(), 1.0f, 1.0f);
// Played this sound, stop iterating break;
return false;
} }
pos += sndref.mChance; pos += sndref.mChance;
// Not this sound, keep iterating
return true;
} }
);
} }
void SoundManager::updateWaterSound(float /*duration*/) void SoundManager::updateWaterSound(float /*duration*/)
@ -1135,28 +1121,23 @@ namespace MWSound
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return; return;
mOutput->startUpdate(); mOutput->startUpdate();
SoundMap::iterator snditer = mActiveSounds.begin(); for(SoundMap::value_type &snd : mActiveSounds)
for(;snditer != mActiveSounds.end();++snditer)
{ {
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &sndbuf : snd.second)
for(;sndidx != snditer->second.end();++sndidx)
{ {
Sound *sound = sndidx->first; Sound *sound = sndbuf.first;
sound->setBaseVolume(volumeFromType(sound->getPlayType())); sound->setBaseVolume(volumeFromType(sound->getPlayType()));
mOutput->updateSound(sound); mOutput->updateSound(sound);
} }
} }
SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); for(SaySoundMap::value_type &snd : mActiveSaySounds)
for(;sayiter != mActiveSaySounds.end();++sayiter)
{ {
Stream *sound = sayiter->second; Stream *sound = snd.second;
sound->setBaseVolume(volumeFromType(sound->getPlayType())); sound->setBaseVolume(volumeFromType(sound->getPlayType()));
mOutput->updateStream(sound); mOutput->updateStream(sound);
} }
TrackList::iterator trkiter = mActiveTracks.begin(); for(Stream *sound : mActiveTracks)
for(;trkiter != mActiveTracks.end();++trkiter)
{ {
Stream *sound = *trkiter;
sound->setBaseVolume(volumeFromType(sound->getPlayType())); sound->setBaseVolume(volumeFromType(sound->getPlayType()));
mOutput->updateStream(sound); mOutput->updateStream(sound);
} }
@ -1262,36 +1243,35 @@ namespace MWSound
void SoundManager::clear() void SoundManager::clear()
{ {
SoundMap::iterator snditer = mActiveSounds.begin(); stopMusic();
for(;snditer != mActiveSounds.end();++snditer)
for(SoundMap::value_type &snd : mActiveSounds)
{ {
SoundBufferRefPairList::iterator sndidx = snditer->second.begin(); for(SoundBufferRefPair &sndbuf : snd.second)
for(;sndidx != snditer->second.end();++sndidx)
{ {
mOutput->finishSound(sndidx->first); mOutput->finishSound(sndbuf.first);
mUnusedSounds.push_back(sndidx->first); mUnusedSounds.push_back(sndbuf.first);
Sound_Buffer *sfx = sndidx->second; Sound_Buffer *sfx = sndbuf.second;
if(sfx->mUses-- == 1) if(sfx->mUses-- == 1)
mUnusedBuffers.push_front(sfx); mUnusedBuffers.push_front(sfx);
} }
} }
mActiveSounds.clear(); mActiveSounds.clear();
SaySoundMap::iterator sayiter = mActiveSaySounds.begin(); mUnderwaterSound = nullptr;
for(;sayiter != mActiveSaySounds.end();++sayiter) mNearWaterSound = nullptr;
for(SaySoundMap::value_type &snd : mActiveSaySounds)
{ {
mOutput->finishStream(sayiter->second); mOutput->finishStream(snd.second);
mUnusedStreams.push_back(sayiter->second); mUnusedStreams.push_back(snd.second);
} }
mActiveSaySounds.clear(); mActiveSaySounds.clear();
TrackList::iterator trkiter = mActiveTracks.begin();
for(;trkiter != mActiveTracks.end();++trkiter) for(Stream *sound : mActiveTracks)
{ {
mOutput->finishStream(*trkiter); mOutput->finishStream(sound);
mUnusedStreams.push_back(*trkiter); mUnusedStreams.push_back(sound);
} }
mActiveTracks.clear(); mActiveTracks.clear();
mUnderwaterSound = nullptr;
mNearWaterSound = nullptr;
stopMusic();
} }
} }

@ -127,6 +127,7 @@ namespace MWSound
void streamMusicFull(const std::string& filename); void streamMusicFull(const std::string& filename);
void advanceMusic(const std::string& filename); void advanceMusic(const std::string& filename);
void startRandomTitle();
void updateSounds(float duration); void updateSounds(float duration);
void updateRegionSound(float duration); void updateRegionSound(float duration);
@ -157,9 +158,6 @@ namespace MWSound
///< Play a soundifle ///< Play a soundifle
/// \param filename name of a sound file in "Music/" in the data directory. /// \param filename name of a sound file in "Music/" in the data directory.
virtual void startRandomTitle();
///< Starts a random track from the current playlist
virtual bool isMusicPlaying(); virtual bool isMusicPlaying();
///< Returns true if music is playing ///< Returns true if music is playing

@ -34,21 +34,19 @@ namespace Misc
if (i < m.size()) if (i < m.size())
{ {
int precision = 0; int precision = -1;
bool precisionSet = false;
if (m[i] == '.') if (m[i] == '.')
{ {
precision = 0;
while (++i < m.size() && m[i] >= '0' && m[i] <= '9') while (++i < m.size() && m[i] >= '0' && m[i] <= '9')
{ {
precision = precision * 10 + (m[i] - '0'); precision = precision * 10 + (m[i] - '0');
precisionSet = true;
} }
} }
if (i < m.size()) if (i < m.size())
{ {
width = (widthSet) ? width : -1; width = (widthSet) ? width : -1;
precision = (precisionSet) ? precision : -1;
if (m[i] == 'S' || m[i] == 's') if (m[i] == 'S' || m[i] == 's')
visitedPlaceholder(StringPlaceholder, pad, width, precision); visitedPlaceholder(StringPlaceholder, pad, width, precision);

@ -141,11 +141,11 @@ namespace Resource
void setUnRefImageDataAfterApply(bool unref); void setUnRefImageDataAfterApply(bool unref);
/// @see ResourceManager::updateCache /// @see ResourceManager::updateCache
virtual void updateCache(double referenceTime); void updateCache(double referenceTime) override;
virtual void clearCache(); void clearCache() override;
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
private: private:

@ -32,9 +32,9 @@ namespace Terrain
osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags); osg::ref_ptr<osg::Node> getChunk(float size, const osg::Vec2f& center, int lod, unsigned int lodFlags);
virtual void reportStats(unsigned int frameNumber, osg::Stats* stats) const; void reportStats(unsigned int frameNumber, osg::Stats* stats) const override;
virtual void clearCache(); void clearCache() override;
void releaseGLObjects(osg::State* state) override; void releaseGLObjects(osg::State* state) override;

@ -304,3 +304,42 @@ already checked. Load a game and make your way to Seyda Neen - or start a new ga
Check whether Arrille has one (or more) for sale, and whether Fargoth give you one Check whether Arrille has one (or more) for sale, and whether Fargoth give you one
when you return his healing ring. when you return his healing ring.
Placing in a chest
******************
For this example we will use the small chest intended for lockpick practice,
located in the Census and Excise Office in Seyda Neen.
First we need the ID of the chest - this can be obtained either by clicking on it in the console
in the game, or by applying a similar process in the CS -
World/Cells
Select "Seyda Neen, Census and Excise Office"
Right-click and select "View"
Use mouse wheel to zoom in/out, and mouse plus WASD keys to navigate
Click on the small chest
Either way, you should find the ID, which is "chest_small_02_lockprac".
Open the Objects table (World/Objects) and scroll down to find this item.
Alternatively use the Edit/Search facility, selecting ID rather than text,
enter "lockprac" (without the quotes) into the search box, press "Search",
which should return two rows, then select the "Container" one rather than the "Instance"
Right-click and "Edit Record".
Right-click the "Content" section and select "Add a row"
Set the Item ID of the new row to be your new ring - simplest way is probably to open the Objects
table if it's not already open, sort on the "Modified" column which should bring the ring,
with its status of "Added" to the top, then drag and drop to the chest row.
Increase the Count to 1.
Save the addon, then test to ensure it works - e.g. start a new game and lockpick the chest.

@ -11,7 +11,9 @@
</Widget> </Widget>
<!-- Spell list --> <!-- Spell list -->
<Widget type="Widget" skin="" position="8 160 519 178" align="Left Top" name="SpellArea"/> <Widget type="ScrollView" skin="MW_ScrollView" position="8 160 507 170" align="Left Top" name="SpellArea">
<Property key="CanvasAlign" value="Left"/>
</Widget>>
<!-- Dialog buttons --> <!-- Dialog buttons -->
<Widget type="HBox" position="0 338 511 24"> <Widget type="HBox" position="0 338 511 24">

Loading…
Cancel
Save