mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-03-03 20:19:40 +00:00
Merge pull request #295 from TES3MP/master
Add master commits up to 20 Sep 2017
This commit is contained in:
commit
76f1a61538
26 changed files with 416 additions and 371 deletions
|
@ -24,7 +24,7 @@ void CSVPrefs::Dialogue::buildCategorySelector (QSplitter *main)
|
|||
|
||||
main->addWidget (list);
|
||||
|
||||
QFontMetrics metrics (QApplication::font());
|
||||
QFontMetrics metrics (QApplication::font(list));
|
||||
|
||||
int maxWidth = 1;
|
||||
|
||||
|
|
|
@ -235,6 +235,10 @@ namespace MWBase
|
|||
virtual bool isReadyToBlock (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;
|
||||
|
||||
/// 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
|
||||
/// \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;
|
||||
///< Returns true if music is playing
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
#include <MyGUI_ListBox.h>
|
||||
#include <MyGUI_ImageBox.h>
|
||||
#include <MyGUI_Gui.h>
|
||||
#include <MyGUI_ScrollView.h>
|
||||
|
||||
#include "../mwbase/environment.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();
|
||||
|
||||
MyGUI::ListBox* mBirthList;
|
||||
MyGUI::Widget* mSpellArea;
|
||||
MyGUI::ScrollView* mSpellArea;
|
||||
MyGUI::ImageBox* mBirthImage;
|
||||
std::vector<MyGUI::Widget*> mSpellItems;
|
||||
|
||||
|
|
|
@ -750,7 +750,9 @@ namespace MWGui
|
|||
|
||||
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;
|
||||
break;
|
||||
|
|
|
@ -206,7 +206,8 @@ namespace MWGui
|
|||
int textButtonPadding = 10; // padding between the text-widget und the button-widget
|
||||
int buttonLeftPadding = 10; // padding between the buttons if horizontal
|
||||
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
|
||||
|
||||
mMarkedToDelete = false;
|
||||
|
@ -245,10 +246,10 @@ namespace MWGui
|
|||
if (buttonsWidth != 0)
|
||||
buttonsWidth += buttonLeftPadding;
|
||||
|
||||
int buttonWidth = button->getTextSize().width + 2*buttonPadding;
|
||||
int buttonWidth = button->getTextSize().width + 2*buttonLabelLeftPadding;
|
||||
buttonsWidth += buttonWidth;
|
||||
|
||||
buttonHeight = button->getTextSize().height + 2*buttonPadding;
|
||||
buttonHeight = button->getTextSize().height + 2*buttonLabelTopPadding;
|
||||
|
||||
if (buttonsHeight != 0)
|
||||
buttonsHeight += buttonTopPadding;
|
||||
|
@ -295,8 +296,8 @@ namespace MWGui
|
|||
buttonCord.left = left;
|
||||
buttonCord.top = messageWidgetCoord.top + textSize.height + textButtonPadding;
|
||||
|
||||
buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding;
|
||||
buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding;
|
||||
buttonSize.width = (*button)->getTextSize().width + 2*buttonLabelLeftPadding;
|
||||
buttonSize.height = (*button)->getTextSize().height + 2*buttonLabelTopPadding;
|
||||
|
||||
(*button)->setCoord(buttonCord);
|
||||
(*button)->setSize(buttonSize);
|
||||
|
@ -322,8 +323,8 @@ namespace MWGui
|
|||
std::vector<MyGUI::Button*>::const_iterator button;
|
||||
for(button = mButtons.begin(); button != mButtons.end(); ++button)
|
||||
{
|
||||
buttonSize.width = (*button)->getTextSize().width + buttonPadding*2;
|
||||
buttonSize.height = (*button)->getTextSize().height + buttonPadding*2;
|
||||
buttonSize.width = (*button)->getTextSize().width + buttonLabelLeftPadding*2;
|
||||
buttonSize.height = (*button)->getTextSize().height + buttonLabelTopPadding*2;
|
||||
|
||||
buttonCord.top = top;
|
||||
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)
|
||||
{
|
||||
PtrActorMap::iterator iter = mActors.find(old);
|
||||
|
@ -1988,8 +2021,9 @@ namespace MWMechanics
|
|||
{
|
||||
std::string id = reader.getHString();
|
||||
int count;
|
||||
reader.getHNT (count, "COUN");
|
||||
mDeathCount[id] = count;
|
||||
reader.getHNT(count, "COUN");
|
||||
if (MWBase::Environment::get().getWorld()->getStore().find(id))
|
||||
mDeathCount[id] = count;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -57,6 +57,10 @@ namespace MWMechanics
|
|||
PtrActorMap::const_iterator begin() { return mActors.begin(); }
|
||||
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
|
||||
/// paused we may want to do it manually (after equipping permanent enchantment)
|
||||
void updateMagicEffects (const MWWorld::Ptr& ptr);
|
||||
|
|
|
@ -1822,20 +1822,11 @@ void CharacterController::update(float duration)
|
|||
if(sneak || inwater || flying)
|
||||
vec.z() = 0.0f;
|
||||
|
||||
if (inwater || flying)
|
||||
cls.getCreatureStats(mPtr).land();
|
||||
|
||||
bool inJump = true;
|
||||
if(!onground && !flying && !inwater)
|
||||
{
|
||||
// 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);
|
||||
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;
|
||||
jumpstate = JumpState_Landing;
|
||||
|
@ -1964,9 +1955,6 @@ void CharacterController::update(float duration)
|
|||
movestate = mMovementState;
|
||||
}
|
||||
|
||||
if (onground)
|
||||
cls.getCreatureStats(mPtr).land();
|
||||
|
||||
if(movestate != CharState_None && movestate != CharState_TurnLeft && movestate != CharState_TurnRight)
|
||||
clearAnimQueue();
|
||||
|
||||
|
@ -2158,77 +2146,72 @@ void CharacterController::unpersistAnimationState()
|
|||
bool CharacterController::playGroup(const std::string &groupname, int mode, int count, bool persist)
|
||||
{
|
||||
if(!mAnimation || !mAnimation->hasAnimation(groupname))
|
||||
{
|
||||
std::cerr<< "Animation "<<groupname<<" not found for " << mPtr.getCellRef().getRefId() << std::endl;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
|
||||
// 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 remove any other animations that were queued.
|
||||
// This emulates observed behavior from the original allows the script "OutsideBanner" to animate banners correctly.
|
||||
if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname &&
|
||||
mAnimation->getTextKeyTime(mAnimQueue.front().mGroup + ": loop start") >= 0 &&
|
||||
mAnimation->isPlaying(groupname))
|
||||
{
|
||||
// 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 remove any other animations that were queued.
|
||||
// This emulates observed behavior from the original allows the script "OutsideBanner" to animate banners correctly.
|
||||
if (!mAnimQueue.empty() && mAnimQueue.front().mGroup == groupname &&
|
||||
mAnimation->getTextKeyTime(mAnimQueue.front().mGroup + ": loop start") >= 0 &&
|
||||
mAnimation->isPlaying(groupname))
|
||||
{
|
||||
float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop stop");
|
||||
float endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": loop stop");
|
||||
|
||||
if (endOfLoop < 0) // if no Loop Stop key was found, use the Stop key
|
||||
endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": stop");
|
||||
if (endOfLoop < 0) // if no Loop Stop key was found, use the Stop key
|
||||
endOfLoop = mAnimation->getTextKeyTime(mAnimQueue.front().mGroup+": stop");
|
||||
|
||||
if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop))
|
||||
{
|
||||
mAnimQueue.resize(1);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
count = std::max(count, 1);
|
||||
|
||||
AnimationQueueEntry entry;
|
||||
entry.mGroup = groupname;
|
||||
entry.mLoopCount = count-1;
|
||||
entry.mPersist = persist;
|
||||
|
||||
if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().mGroup))
|
||||
{
|
||||
clearAnimQueue();
|
||||
mAnimQueue.push_back(entry);
|
||||
|
||||
mAnimation->disable(mCurrentIdle);
|
||||
mCurrentIdle.clear();
|
||||
|
||||
mIdleState = CharState_SpecialIdle;
|
||||
bool loopfallback = (entry.mGroup.compare(0,4,"idle") == 0);
|
||||
mAnimation->play(groupname, Priority_Default,
|
||||
MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||
((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1, loopfallback);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If we are the cell authority over this actor, we need to record this new
|
||||
animation for it
|
||||
*/
|
||||
if (mwmp::Main::get().getCellController()->isLocalActor(mPtr))
|
||||
{
|
||||
mwmp::LocalActor *actor = mwmp::Main::get().getCellController()->getLocalActor(mPtr);
|
||||
actor->animation.groupname = groupname;
|
||||
actor->animation.mode = mode;
|
||||
actor->animation.count = count;
|
||||
actor->animation.persist = persist;
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
else if(mode == 0)
|
||||
if (endOfLoop > 0 && (mAnimation->getCurrentTime(mAnimQueue.front().mGroup) < endOfLoop))
|
||||
{
|
||||
mAnimQueue.resize(1);
|
||||
mAnimQueue.push_back(entry);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
count = std::max(count, 1);
|
||||
|
||||
AnimationQueueEntry entry;
|
||||
entry.mGroup = groupname;
|
||||
entry.mLoopCount = count-1;
|
||||
entry.mPersist = persist;
|
||||
|
||||
if(mode != 0 || mAnimQueue.empty() || !isAnimPlaying(mAnimQueue.front().mGroup))
|
||||
{
|
||||
clearAnimQueue();
|
||||
mAnimQueue.push_back(entry);
|
||||
|
||||
mAnimation->disable(mCurrentIdle);
|
||||
mCurrentIdle.clear();
|
||||
|
||||
mIdleState = CharState_SpecialIdle;
|
||||
bool loopfallback = (entry.mGroup.compare(0,4,"idle") == 0);
|
||||
mAnimation->play(groupname, Priority_Default,
|
||||
MWRender::Animation::BlendMask_All, false, 1.0f,
|
||||
((mode==2) ? "loop start" : "start"), "stop", 0.0f, count-1, loopfallback);
|
||||
|
||||
/*
|
||||
Start of tes3mp addition
|
||||
|
||||
If we are the cell authority over this actor, we need to record this new
|
||||
animation for it
|
||||
*/
|
||||
if (mwmp::Main::get().getCellController()->isLocalActor(mPtr))
|
||||
{
|
||||
mwmp::LocalActor *actor = mwmp::Main::get().getCellController()->getLocalActor(mPtr);
|
||||
actor->animation.groupname = groupname;
|
||||
actor->animation.mode = mode;
|
||||
actor->animation.count = count;
|
||||
actor->animation.persist = persist;
|
||||
}
|
||||
/*
|
||||
End of tes3mp addition
|
||||
*/
|
||||
}
|
||||
else if(mode == 0)
|
||||
{
|
||||
mAnimQueue.resize(1);
|
||||
mAnimQueue.push_back(entry);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -436,6 +436,11 @@ namespace MWMechanics
|
|||
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)
|
||||
{
|
||||
return mActors.isAttackPrepairing(ptr);
|
||||
|
|
|
@ -201,6 +201,10 @@ namespace MWMechanics
|
|||
/// Is \a ptr casting spell or using weapon now?
|
||||
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);
|
||||
|
||||
/// 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)
|
||||
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();
|
||||
float oldHeight = position.z();
|
||||
bool positionChanged = false;
|
||||
for (int i=0; i<numSteps; ++i)
|
||||
{
|
||||
position = MovementSolver::move(position, physicActor->getPtr(), physicActor, iter->second, physicsDt,
|
||||
world->isFlying(iter->first),
|
||||
waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
|
||||
flying, waterlevel, slowFall, mCollisionWorld, mStandingCollisions);
|
||||
if (position != physicActor->getPosition())
|
||||
positionChanged = true;
|
||||
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;
|
||||
|
||||
if (heightDiff < 0)
|
||||
iter->first.getClass().getCreatureStats(iter->first).addToFallHeight(-heightDiff);
|
||||
MWMechanics::CreatureStats& stats = iter->first.getClass().getCreatureStats(iter->first);
|
||||
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));
|
||||
}
|
||||
|
|
|
@ -757,8 +757,6 @@ namespace MWRender
|
|||
break;
|
||||
}
|
||||
}
|
||||
if(iter == mAnimSources.rend())
|
||||
std::cerr<< "Failed to find animation "<<groupname<<" for "<<mPtr.getCellRef().getRefId() <<std::endl;
|
||||
|
||||
resetActiveGroups();
|
||||
}
|
||||
|
@ -795,7 +793,7 @@ namespace MWRender
|
|||
// We have to ignore extra garbage at the end.
|
||||
// The Scrib's idle3 animation has "Idle3: Stop." instead of "Idle3: Stop".
|
||||
// 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;
|
||||
if(stopkey == keys.rend())
|
||||
return false;
|
||||
|
|
|
@ -530,8 +530,10 @@ namespace MWRender
|
|||
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
if(mPlayerAnimation.get())
|
||||
{
|
||||
setupPlayer(ptr);
|
||||
mPlayerAnimation->updatePtr(ptr);
|
||||
|
||||
}
|
||||
mCamera->attachTo(ptr);
|
||||
}
|
||||
|
||||
|
@ -834,6 +836,7 @@ namespace MWRender
|
|||
|
||||
player.getRefData().setBaseNode(mPlayerNode);
|
||||
|
||||
mWater->removeEmitter(player);
|
||||
mWater->addEmitter(player);
|
||||
}
|
||||
|
||||
|
|
|
@ -372,21 +372,14 @@ namespace MWScript
|
|||
|
||||
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);
|
||||
runtime.pop();
|
||||
|
||||
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPtr(actorID, true);
|
||||
|
||||
if(!actor.getClass().isActor() || !observer.getClass().isActor())
|
||||
{
|
||||
runtime.push(0);
|
||||
return;
|
||||
}
|
||||
|
||||
Interpreter::Type_Integer value =
|
||||
MWBase::Environment::get().getWorld()->getLOS(observer, actor) &&
|
||||
MWBase::Environment::get().getMechanicsManager()->awarenessCheck(actor, observer);
|
||||
Interpreter::Type_Integer value = MWBase::Environment::get().getMechanicsManager()->isActorDetected(actor, observer);
|
||||
|
||||
runtime.push (value);
|
||||
}
|
||||
|
|
|
@ -192,12 +192,11 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
|
|||
{ AL_FORMAT_STEREO8, ChannelConfig_Stereo, SampleType_UInt8 },
|
||||
}};
|
||||
|
||||
auto fmt = std::find_if(fmtlist.cbegin(), fmtlist.cend(),
|
||||
[chans,type](const FormatEntry &fmt) -> bool
|
||||
{ return fmt.chans == chans && fmt.type == type; }
|
||||
);
|
||||
if(fmt != fmtlist.cend())
|
||||
return fmt->format;
|
||||
for(auto &fmt : fmtlist)
|
||||
{
|
||||
if(fmt.chans == chans && fmt.type == type)
|
||||
return fmt.format;
|
||||
}
|
||||
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
|
@ -209,18 +208,16 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
|
|||
{ "AL_FORMAT_71CHN16", ChannelConfig_7point1, SampleType_Int16 },
|
||||
{ "AL_FORMAT_71CHN8", ChannelConfig_7point1, SampleType_UInt8 },
|
||||
}};
|
||||
ALenum format = AL_NONE;
|
||||
|
||||
std::find_if(mcfmtlist.cbegin(), mcfmtlist.cend(),
|
||||
[&format,chans,type](const FormatEntryExt &fmt) -> bool
|
||||
for(auto &fmt : mcfmtlist)
|
||||
{
|
||||
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)
|
||||
return format;
|
||||
}
|
||||
);
|
||||
if(format != 0 && format != -1)
|
||||
return format;
|
||||
}
|
||||
}
|
||||
if(alIsExtensionPresent("AL_EXT_FLOAT32"))
|
||||
{
|
||||
|
@ -228,18 +225,16 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
|
|||
{ "AL_FORMAT_MONO_FLOAT32", ChannelConfig_Mono, SampleType_Float32 },
|
||||
{ "AL_FORMAT_STEREO_FLOAT32", ChannelConfig_Stereo, SampleType_Float32 },
|
||||
}};
|
||||
ALenum format = AL_NONE;
|
||||
|
||||
std::find_if(fltfmtlist.cbegin(), fltfmtlist.cend(),
|
||||
[&format,chans,type](const FormatEntryExt &fmt) -> bool
|
||||
for(auto &fmt : fltfmtlist)
|
||||
{
|
||||
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)
|
||||
return format;
|
||||
}
|
||||
);
|
||||
if(format != 0 && format != -1)
|
||||
return format;
|
||||
}
|
||||
|
||||
if(alIsExtensionPresent("AL_EXT_MCFORMATS"))
|
||||
{
|
||||
|
@ -249,16 +244,15 @@ static ALenum getALFormat(ChannelConfig chans, SampleType type)
|
|||
{ "AL_FORMAT_71CHN32", ChannelConfig_7point1, SampleType_Float32 },
|
||||
}};
|
||||
|
||||
std::find_if(fltmcfmtlist.cbegin(), fltmcfmtlist.cend(),
|
||||
[&format,chans,type](const FormatEntryExt &fmt) -> bool
|
||||
for(auto &fmt : fltmcfmtlist)
|
||||
{
|
||||
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)
|
||||
return format;
|
||||
}
|
||||
);
|
||||
if(format != 0 && format != -1)
|
||||
return format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -547,11 +541,11 @@ ALint OpenAL_SoundStream::refillQueue()
|
|||
std::vector<char> data(mBufferSize);
|
||||
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())
|
||||
{
|
||||
mIsFinished = true;
|
||||
std::memset(&data[got], mSilence, data.size()-got);
|
||||
std::fill(data.begin()+got, data.end(), mSilence);
|
||||
}
|
||||
if(got > 0)
|
||||
{
|
||||
|
@ -559,7 +553,7 @@ ALint OpenAL_SoundStream::refillQueue()
|
|||
mLoudnessAnalyzer->analyzeLoudness(data);
|
||||
|
||||
ALuint bufid = mBuffers[mCurrentBufIdx];
|
||||
alBufferData(bufid, mFormat, &data[0], data.size(), mSampleRate);
|
||||
alBufferData(bufid, mFormat, data.data(), data.size(), mSampleRate);
|
||||
alSourceQueueBuffers(mSource, 1, &bufid);
|
||||
mCurrentBufIdx = (mCurrentBufIdx+1) % mBuffers.size();
|
||||
}
|
||||
|
@ -594,24 +588,32 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
|
|||
{
|
||||
deinit();
|
||||
|
||||
std::cout<< "Initializing OpenAL..." <<std::endl;
|
||||
|
||||
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(devname.empty())
|
||||
std::cerr<< "Failed to open default audio device" <<std::endl;
|
||||
else
|
||||
std::cerr<< "Failed to open \""<<devname<<"\"" <<std::endl;
|
||||
std::cerr<< "Failed to open default audio device" <<std::endl;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
const ALCchar *name = NULL;
|
||||
if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
|
||||
name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER);
|
||||
if(alcGetError(mDevice) != AL_NO_ERROR || !name)
|
||||
name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER);
|
||||
std::cout << "Opened \""<<name<<"\"" << std::endl;
|
||||
}
|
||||
|
||||
const ALCchar *name = nullptr;
|
||||
if(alcIsExtensionPresent(mDevice, "ALC_ENUMERATE_ALL_EXT"))
|
||||
name = alcGetString(mDevice, ALC_ALL_DEVICES_SPECIFIER);
|
||||
if(alcGetError(mDevice) != AL_NO_ERROR || !name)
|
||||
name = alcGetString(mDevice, ALC_DEVICE_SPECIFIER);
|
||||
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.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)
|
||||
std::cerr<< "Failed to find HRTF name \""<<hrtfname<<"\", using default" <<std::endl;
|
||||
std::cerr<< "Failed to find HRTF \""<<hrtfname<<"\", using default" <<std::endl;
|
||||
else
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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)
|
||||
std::cout<< "HRTF status unavailable" <<std::endl;
|
||||
else
|
||||
|
@ -698,7 +705,7 @@ bool OpenAL_Output::init(const std::string &devname, const std::string &hrtfname
|
|||
{
|
||||
ALuint src = 0;
|
||||
alGenSources(1, &src);
|
||||
if(getALError() != AL_NO_ERROR)
|
||||
if(alGetError() != AL_NO_ERROR)
|
||||
break;
|
||||
mFreeSources.push_back(src);
|
||||
}
|
||||
|
@ -824,8 +831,8 @@ void OpenAL_Output::deinit()
|
|||
{
|
||||
mStreamThread->removeAll();
|
||||
|
||||
for(size_t i = 0;i < mFreeSources.size();i++)
|
||||
alDeleteSources(1, &mFreeSources[i]);
|
||||
for(ALuint source : mFreeSources)
|
||||
alDeleteSources(1, &source);
|
||||
mFreeSources.clear();
|
||||
|
||||
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();
|
||||
|
||||
|
@ -959,27 +966,31 @@ Sound_Handle OpenAL_Output::loadSound(const std::string &fname)
|
|||
|
||||
decoder->getInfo(&srate, &chans, &type);
|
||||
format = getALFormat(chans, type);
|
||||
if(!format) return nullptr;
|
||||
if(!format) return std::make_pair(nullptr, 0);
|
||||
|
||||
decoder->readAll(data);
|
||||
decoder->close();
|
||||
|
||||
ALint size;
|
||||
ALuint buf = 0;
|
||||
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(buf && alIsBuffer(buf))
|
||||
alDeleteBuffers(1, &buf);
|
||||
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);
|
||||
if(!buffer) return 0;
|
||||
|
||||
// Make sure no sources are playing this buffer before unloading it.
|
||||
SoundVec::const_iterator iter = mActiveSounds.begin();
|
||||
for(;iter != mActiveSounds.end();++iter)
|
||||
|
@ -996,19 +1007,11 @@ void OpenAL_Output::unloadSound(Sound_Handle data)
|
|||
alSourcei(source, AL_BUFFER, 0);
|
||||
}
|
||||
}
|
||||
ALint size = 0;
|
||||
alGetBufferi(buffer, AL_SIZE, &size);
|
||||
alDeleteBuffers(1, &buffer);
|
||||
getALError();
|
||||
}
|
||||
|
||||
size_t OpenAL_Output::getSoundDataSize(Sound_Handle data) const
|
||||
{
|
||||
ALuint buffer = GET_PTRID(data);
|
||||
ALint size = 0;
|
||||
|
||||
alGetBufferi(buffer, AL_SIZE, &size);
|
||||
getALError();
|
||||
|
||||
return (ALuint)size;
|
||||
return size;
|
||||
}
|
||||
|
||||
|
||||
|
@ -1368,23 +1371,19 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi
|
|||
if(mWaterFilter)
|
||||
{
|
||||
ALuint filter = (env == Env_Underwater) ? mWaterFilter : AL_FILTER_NULL;
|
||||
std::for_each(mActiveSounds.cbegin(), mActiveSounds.cend(),
|
||||
[filter](const SoundVec::value_type &item) -> void
|
||||
{
|
||||
if(item->getUseEnv())
|
||||
alSourcei(GET_PTRID(item->mHandle), AL_DIRECT_FILTER, filter);
|
||||
}
|
||||
);
|
||||
std::for_each(mActiveStreams.cbegin(), mActiveStreams.cend(),
|
||||
[filter](const StreamVec::value_type &item) -> void
|
||||
{
|
||||
if(item->getUseEnv())
|
||||
alSourcei(
|
||||
reinterpret_cast<OpenAL_SoundStream*>(item->mHandle)->mSource,
|
||||
AL_DIRECT_FILTER, filter
|
||||
);
|
||||
}
|
||||
);
|
||||
for(Sound *sound : mActiveSounds)
|
||||
{
|
||||
if(sound->getUseEnv())
|
||||
alSourcei(GET_PTRID(sound->mHandle), AL_DIRECT_FILTER, filter);
|
||||
}
|
||||
for(Stream *sound : mActiveStreams)
|
||||
{
|
||||
if(sound->getUseEnv())
|
||||
alSourcei(
|
||||
reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle)->mSource,
|
||||
AL_DIRECT_FILTER, filter
|
||||
);
|
||||
}
|
||||
}
|
||||
// Update the environment effect
|
||||
if(mEffectSlot)
|
||||
|
@ -1403,23 +1402,19 @@ void OpenAL_Output::updateListener(const osg::Vec3f &pos, const osg::Vec3f &atdi
|
|||
void OpenAL_Output::pauseSounds(int types)
|
||||
{
|
||||
std::vector<ALuint> sources;
|
||||
std::for_each(mActiveSounds.cbegin(), mActiveSounds.cend(),
|
||||
[types,&sources](const SoundVec::value_type &sound) -> void
|
||||
for(Sound *sound : mActiveSounds)
|
||||
{
|
||||
if((types&sound->getPlayType()))
|
||||
sources.push_back(GET_PTRID(sound->mHandle));
|
||||
}
|
||||
for(Stream *sound : mActiveStreams)
|
||||
{
|
||||
if((types&sound->getPlayType()))
|
||||
{
|
||||
if(sound && sound->mHandle && (types&sound->getPlayType()))
|
||||
sources.push_back(GET_PTRID(sound->mHandle));
|
||||
OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
|
||||
sources.push_back(stream->mSource);
|
||||
}
|
||||
);
|
||||
std::for_each(mActiveStreams.cbegin(), mActiveStreams.cend(),
|
||||
[types,&sources](const StreamVec::value_type &stream) -> void
|
||||
{
|
||||
if(stream && stream->mHandle && (types&stream->getPlayType()))
|
||||
{
|
||||
OpenAL_SoundStream *strm = reinterpret_cast<OpenAL_SoundStream*>(stream->mHandle);
|
||||
sources.push_back(strm->mSource);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
if(!sources.empty())
|
||||
{
|
||||
alSourcePausev(sources.size(), sources.data());
|
||||
|
@ -1430,23 +1425,19 @@ void OpenAL_Output::pauseSounds(int types)
|
|||
void OpenAL_Output::resumeSounds(int types)
|
||||
{
|
||||
std::vector<ALuint> sources;
|
||||
std::for_each(mActiveSounds.cbegin(), mActiveSounds.cend(),
|
||||
[types,&sources](const SoundVec::value_type &sound) -> void
|
||||
for(Sound *sound : mActiveSounds)
|
||||
{
|
||||
if((types&sound->getPlayType()))
|
||||
sources.push_back(GET_PTRID(sound->mHandle));
|
||||
}
|
||||
for(Stream *sound : mActiveStreams)
|
||||
{
|
||||
if((types&sound->getPlayType()))
|
||||
{
|
||||
if(sound && sound->mHandle && (types&sound->getPlayType()))
|
||||
sources.push_back(GET_PTRID(sound->mHandle));
|
||||
OpenAL_SoundStream *stream = reinterpret_cast<OpenAL_SoundStream*>(sound->mHandle);
|
||||
sources.push_back(stream->mSource);
|
||||
}
|
||||
);
|
||||
std::for_each(mActiveStreams.cbegin(), mActiveStreams.cend(),
|
||||
[types,&sources](const StreamVec::value_type &stream) -> void
|
||||
{
|
||||
if(stream && stream->mHandle && (types&stream->getPlayType()))
|
||||
{
|
||||
OpenAL_SoundStream *strm = reinterpret_cast<OpenAL_SoundStream*>(stream->mHandle);
|
||||
sources.push_back(strm->mSource);
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
if(!sources.empty())
|
||||
{
|
||||
alSourcePlayv(sources.size(), sources.data());
|
||||
|
|
|
@ -66,9 +66,8 @@ namespace MWSound
|
|||
virtual std::vector<std::string> enumerateHrtf();
|
||||
virtual void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode);
|
||||
|
||||
virtual Sound_Handle loadSound(const std::string &fname);
|
||||
virtual void unloadSound(Sound_Handle data);
|
||||
virtual size_t getSoundDataSize(Sound_Handle data) const;
|
||||
virtual std::pair<Sound_Handle,size_t> loadSound(const std::string &fname);
|
||||
virtual size_t unloadSound(Sound_Handle data);
|
||||
|
||||
virtual bool playSound(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 void setHrtf(const std::string &hrtfname, HrtfMode hrtfmode) = 0;
|
||||
|
||||
virtual Sound_Handle loadSound(const std::string &fname) = 0;
|
||||
virtual void unloadSound(Sound_Handle data) = 0;
|
||||
virtual size_t getSoundDataSize(Sound_Handle data) const = 0;
|
||||
virtual std::pair<Sound_Handle,size_t> loadSound(const std::string &fname) = 0;
|
||||
virtual size_t unloadSound(Sound_Handle data) = 0;
|
||||
|
||||
virtual bool playSound(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 "openal_output.hpp"
|
||||
#define SOUND_OUT "OpenAL"
|
||||
#include "ffmpeg_decoder.hpp"
|
||||
#ifndef SOUND_IN
|
||||
#define SOUND_IN "FFmpeg"
|
||||
#endif
|
||||
|
||||
|
||||
namespace MWSound
|
||||
|
@ -84,45 +80,35 @@ namespace MWSound
|
|||
mBufferCacheMin = std::min(mBufferCacheMin*1024*1024, mBufferCacheMax);
|
||||
|
||||
if(!useSound)
|
||||
{
|
||||
std::cout<< "Sound disabled." <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
std::string hrtfname = Settings::Manager::getString("hrtf", "Sound");
|
||||
int hrtfstate = Settings::Manager::getInt("hrtf enable", "Sound");
|
||||
HrtfMode hrtfmode = hrtfstate < 0 ? HrtfMode::Auto :
|
||||
hrtfstate > 0 ? HrtfMode::Enable : HrtfMode::Disable;
|
||||
|
||||
std::cout << "Sound output: " << SOUND_OUT << std::endl;
|
||||
std::cout << "Sound decoder: " << SOUND_IN << std::endl;
|
||||
std::string devname = Settings::Manager::getString("device", "Sound");
|
||||
if(!mOutput->init(devname, hrtfname, hrtfmode))
|
||||
{
|
||||
std::cerr<< "Failed to initialize audio output, sound disabled" <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
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"; }
|
||||
);
|
||||
for(const std::string &name : names)
|
||||
std::cout <<" "<<name<<"\n";
|
||||
std::cout.flush();
|
||||
|
||||
std::string devname = Settings::Manager::getString("device", "Sound");
|
||||
bool inited = mOutput->init(devname, hrtfname, hrtfmode);
|
||||
if(!inited && !devname.empty())
|
||||
{
|
||||
std::cerr<< "Failed to initialize device \""<<devname<<"\", trying default" <<std::endl;
|
||||
inited = mOutput->init(std::string(), hrtfname, hrtfmode);
|
||||
}
|
||||
if(!inited)
|
||||
{
|
||||
std::cerr<< "Failed to initialize default audio device, sound disabled" <<std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
names = mOutput->enumerateHrtf();
|
||||
if(!names.empty())
|
||||
{
|
||||
std::cout<< "Enumerated HRTF names:\n";
|
||||
std::for_each(names.cbegin(), names.cend(),
|
||||
[](const std::string &name) -> void
|
||||
{ std::cout<< " "<<name<<"\n"; }
|
||||
);
|
||||
for(const std::string &name : names)
|
||||
std::cout <<" "<<name<<"\n";
|
||||
std::cout.flush();
|
||||
}
|
||||
}
|
||||
|
@ -130,12 +116,11 @@ namespace MWSound
|
|||
SoundManager::~SoundManager()
|
||||
{
|
||||
clear();
|
||||
SoundBufferList::element_type::iterator sfxiter = mSoundBuffers->begin();
|
||||
for(;sfxiter != mSoundBuffers->end();++sfxiter)
|
||||
for(Sound_Buffer &sfx : *mSoundBuffers)
|
||||
{
|
||||
if(sfxiter->mHandle)
|
||||
mOutput->unloadSound(sfxiter->mHandle);
|
||||
sfxiter->mHandle = 0;
|
||||
if(sfx.mHandle)
|
||||
mOutput->unloadSound(sfx.mHandle);
|
||||
sfx.mHandle = 0;
|
||||
}
|
||||
mUnusedBuffers.clear();
|
||||
mOutput.reset();
|
||||
|
@ -200,9 +185,23 @@ namespace MWSound
|
|||
// minRange, and maxRange), and ensure it's ready for use.
|
||||
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;
|
||||
NameBufferMap::const_iterator snd = mBufferNameMap.find(soundId);
|
||||
if(snd != mBufferNameMap.end())
|
||||
if(LIKELY(snd != mBufferNameMap.end()))
|
||||
sfx = snd->second;
|
||||
else
|
||||
{
|
||||
|
@ -211,13 +210,16 @@ namespace MWSound
|
|||
if(!sound) return nullptr;
|
||||
sfx = insertSound(soundId, sound);
|
||||
}
|
||||
#undef LIKELY
|
||||
#undef UNLIKELY
|
||||
|
||||
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;
|
||||
|
||||
mBufferCacheSize += mOutput->getSoundDataSize(sfx->mHandle);
|
||||
mBufferCacheSize += size;
|
||||
if(mBufferCacheSize > mBufferCacheMax)
|
||||
{
|
||||
do {
|
||||
|
@ -228,8 +230,8 @@ namespace MWSound
|
|||
}
|
||||
Sound_Buffer *unused = mUnusedBuffers.back();
|
||||
|
||||
mBufferCacheSize -= mOutput->getSoundDataSize(unused->mHandle);
|
||||
mOutput->unloadSound(unused->mHandle);
|
||||
size = mOutput->unloadSound(unused->mHandle);
|
||||
mBufferCacheSize -= size;
|
||||
unused->mHandle = 0;
|
||||
|
||||
mUnusedBuffers.pop_back();
|
||||
|
@ -389,11 +391,6 @@ namespace MWSound
|
|||
mMusic->setFadeout(0.5f);
|
||||
}
|
||||
|
||||
void SoundManager::streamMusic(const std::string& filename)
|
||||
{
|
||||
advanceMusic("Music/"+filename);
|
||||
}
|
||||
|
||||
void SoundManager::startRandomTitle()
|
||||
{
|
||||
std::vector<std::string> filelist;
|
||||
|
@ -444,6 +441,12 @@ namespace MWSound
|
|||
tracklist.pop_back();
|
||||
}
|
||||
|
||||
|
||||
void SoundManager::streamMusic(const std::string& filename)
|
||||
{
|
||||
advanceMusic("Music/"+filename);
|
||||
}
|
||||
|
||||
bool SoundManager::isMusicPlaying()
|
||||
{
|
||||
return mMusic && mOutput->isStreamPlaying(mMusic);
|
||||
|
@ -678,11 +681,10 @@ namespace MWSound
|
|||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
for(SoundBufferRefPair &snd : snditer->second)
|
||||
{
|
||||
if(sndidx->second == sfx)
|
||||
mOutput->finishSound(sndidx->first);
|
||||
if(snd.second == sfx)
|
||||
mOutput->finishSound(snd.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -692,33 +694,28 @@ namespace MWSound
|
|||
SoundMap::iterator snditer = mActiveSounds.find(ptr);
|
||||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
mOutput->finishSound(sndidx->first);
|
||||
for(SoundBufferRefPair &snd : snditer->second)
|
||||
mOutput->finishSound(snd.first);
|
||||
}
|
||||
SaySoundMap::iterator sayiter = mActiveSaySounds.find(ptr);
|
||||
if(sayiter != mActiveSaySounds.end())
|
||||
mOutput->finishStream(sayiter->second);
|
||||
}
|
||||
|
||||
void SoundManager::stopSound(const MWWorld::CellStore *cell)
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
for(;snditer != mActiveSounds.end();++snditer)
|
||||
for(SoundMap::value_type &snd : mActiveSounds)
|
||||
{
|
||||
if(snditer->first != MWWorld::ConstPtr() &&
|
||||
snditer->first != MWMechanics::getPlayer() &&
|
||||
snditer->first.getCell() == cell)
|
||||
if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)
|
||||
{
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
mOutput->finishSound(sndidx->first);
|
||||
for(SoundBufferRefPair &sndbuf : snd.second)
|
||||
mOutput->finishSound(sndbuf.first);
|
||||
}
|
||||
}
|
||||
SaySoundMap::iterator sayiter = mActiveSaySounds.begin();
|
||||
for(;sayiter != mActiveSaySounds.end();++sayiter)
|
||||
for(SaySoundMap::value_type &snd : mActiveSaySounds)
|
||||
{
|
||||
if(sayiter->first != MWWorld::ConstPtr() &&
|
||||
sayiter->first != MWMechanics::getPlayer() &&
|
||||
sayiter->first.getCell() == cell)
|
||||
mOutput->finishStream(sayiter->second);
|
||||
if(!snd.first.isEmpty() && snd.first != MWMechanics::getPlayer() && snd.first.getCell() == cell)
|
||||
mOutput->finishStream(snd.second);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -728,11 +725,10 @@ namespace MWSound
|
|||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
for(SoundBufferRefPair &sndbuf : snditer->second)
|
||||
{
|
||||
if(sndidx->second == sfx)
|
||||
mOutput->finishSound(sndidx->first);
|
||||
if(sndbuf.second == sfx)
|
||||
mOutput->finishSound(sndbuf.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -744,11 +740,10 @@ namespace MWSound
|
|||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
Sound_Buffer *sfx = loadSound(Misc::StringUtils::lowerCase(soundId));
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
for(SoundBufferRefPair &sndbuf : snditer->second)
|
||||
{
|
||||
if(sndidx->second == sfx)
|
||||
sndidx->first->setFadeout(duration);
|
||||
if(sndbuf.second == sfx)
|
||||
sndbuf.first->setFadeout(duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -759,12 +754,10 @@ namespace MWSound
|
|||
if(snditer != mActiveSounds.end())
|
||||
{
|
||||
Sound_Buffer *sfx = lookupSound(Misc::StringUtils::lowerCase(soundId));
|
||||
SoundBufferRefPairList::const_iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
{
|
||||
if(sndidx->second == sfx && mOutput->isSoundPlaying(sndidx->first))
|
||||
return true;
|
||||
}
|
||||
return std::find_if(snditer->second.cbegin(), snditer->second.cend(),
|
||||
[this,sfx](const SoundBufferRefPair &snd) -> bool
|
||||
{ return snd.second == sfx && mOutput->isSoundPlaying(snd.first); }
|
||||
) != snditer->second.cend();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
@ -823,10 +816,8 @@ namespace MWSound
|
|||
|
||||
if(total == 0)
|
||||
{
|
||||
std::for_each(regn->mSoundList.cbegin(), regn->mSoundList.cend(),
|
||||
[](const ESM::Region::SoundRef &sndref) -> void
|
||||
{ total += (int)sndref.mChance; }
|
||||
);
|
||||
for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
|
||||
total += (int)sndref.mChance;
|
||||
if(total == 0)
|
||||
return;
|
||||
}
|
||||
|
@ -834,20 +825,15 @@ namespace MWSound
|
|||
int r = Misc::Rng::rollDice(total);
|
||||
int pos = 0;
|
||||
|
||||
std::find_if_not(regn->mSoundList.cbegin(), regn->mSoundList.cend(),
|
||||
[&pos, r, this](const ESM::Region::SoundRef &sndref) -> bool
|
||||
for(const ESM::Region::SoundRef &sndref : regn->mSoundList)
|
||||
{
|
||||
if(r - pos < sndref.mChance)
|
||||
{
|
||||
if(r - pos < sndref.mChance)
|
||||
{
|
||||
playSound(sndref.mSound.toString(), 1.0f, 1.0f);
|
||||
// Played this sound, stop iterating
|
||||
return false;
|
||||
}
|
||||
pos += sndref.mChance;
|
||||
// Not this sound, keep iterating
|
||||
return true;
|
||||
playSound(sndref.mSound.toString(), 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
);
|
||||
pos += sndref.mChance;
|
||||
}
|
||||
}
|
||||
|
||||
void SoundManager::updateWaterSound(float /*duration*/)
|
||||
|
@ -1135,28 +1121,23 @@ namespace MWSound
|
|||
if(!mOutput->isInitialized())
|
||||
return;
|
||||
mOutput->startUpdate();
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
for(;snditer != mActiveSounds.end();++snditer)
|
||||
for(SoundMap::value_type &snd : mActiveSounds)
|
||||
{
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
for(SoundBufferRefPair &sndbuf : snd.second)
|
||||
{
|
||||
Sound *sound = sndidx->first;
|
||||
Sound *sound = sndbuf.first;
|
||||
sound->setBaseVolume(volumeFromType(sound->getPlayType()));
|
||||
mOutput->updateSound(sound);
|
||||
}
|
||||
}
|
||||
SaySoundMap::iterator sayiter = mActiveSaySounds.begin();
|
||||
for(;sayiter != mActiveSaySounds.end();++sayiter)
|
||||
for(SaySoundMap::value_type &snd : mActiveSaySounds)
|
||||
{
|
||||
Stream *sound = sayiter->second;
|
||||
Stream *sound = snd.second;
|
||||
sound->setBaseVolume(volumeFromType(sound->getPlayType()));
|
||||
mOutput->updateStream(sound);
|
||||
}
|
||||
TrackList::iterator trkiter = mActiveTracks.begin();
|
||||
for(;trkiter != mActiveTracks.end();++trkiter)
|
||||
for(Stream *sound : mActiveTracks)
|
||||
{
|
||||
Stream *sound = *trkiter;
|
||||
sound->setBaseVolume(volumeFromType(sound->getPlayType()));
|
||||
mOutput->updateStream(sound);
|
||||
}
|
||||
|
@ -1262,36 +1243,35 @@ namespace MWSound
|
|||
|
||||
void SoundManager::clear()
|
||||
{
|
||||
SoundMap::iterator snditer = mActiveSounds.begin();
|
||||
for(;snditer != mActiveSounds.end();++snditer)
|
||||
stopMusic();
|
||||
|
||||
for(SoundMap::value_type &snd : mActiveSounds)
|
||||
{
|
||||
SoundBufferRefPairList::iterator sndidx = snditer->second.begin();
|
||||
for(;sndidx != snditer->second.end();++sndidx)
|
||||
for(SoundBufferRefPair &sndbuf : snd.second)
|
||||
{
|
||||
mOutput->finishSound(sndidx->first);
|
||||
mUnusedSounds.push_back(sndidx->first);
|
||||
Sound_Buffer *sfx = sndidx->second;
|
||||
mOutput->finishSound(sndbuf.first);
|
||||
mUnusedSounds.push_back(sndbuf.first);
|
||||
Sound_Buffer *sfx = sndbuf.second;
|
||||
if(sfx->mUses-- == 1)
|
||||
mUnusedBuffers.push_front(sfx);
|
||||
}
|
||||
}
|
||||
mActiveSounds.clear();
|
||||
SaySoundMap::iterator sayiter = mActiveSaySounds.begin();
|
||||
for(;sayiter != mActiveSaySounds.end();++sayiter)
|
||||
{
|
||||
mOutput->finishStream(sayiter->second);
|
||||
mUnusedStreams.push_back(sayiter->second);
|
||||
}
|
||||
mActiveSaySounds.clear();
|
||||
TrackList::iterator trkiter = mActiveTracks.begin();
|
||||
for(;trkiter != mActiveTracks.end();++trkiter)
|
||||
{
|
||||
mOutput->finishStream(*trkiter);
|
||||
mUnusedStreams.push_back(*trkiter);
|
||||
}
|
||||
mActiveTracks.clear();
|
||||
mUnderwaterSound = nullptr;
|
||||
mNearWaterSound = nullptr;
|
||||
stopMusic();
|
||||
|
||||
for(SaySoundMap::value_type &snd : mActiveSaySounds)
|
||||
{
|
||||
mOutput->finishStream(snd.second);
|
||||
mUnusedStreams.push_back(snd.second);
|
||||
}
|
||||
mActiveSaySounds.clear();
|
||||
|
||||
for(Stream *sound : mActiveTracks)
|
||||
{
|
||||
mOutput->finishStream(sound);
|
||||
mUnusedStreams.push_back(sound);
|
||||
}
|
||||
mActiveTracks.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -127,6 +127,7 @@ namespace MWSound
|
|||
|
||||
void streamMusicFull(const std::string& filename);
|
||||
void advanceMusic(const std::string& filename);
|
||||
void startRandomTitle();
|
||||
|
||||
void updateSounds(float duration);
|
||||
void updateRegionSound(float duration);
|
||||
|
@ -157,9 +158,6 @@ namespace MWSound
|
|||
///< Play a soundifle
|
||||
/// \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();
|
||||
///< Returns true if music is playing
|
||||
|
||||
|
|
|
@ -34,21 +34,19 @@ namespace Misc
|
|||
|
||||
if (i < m.size())
|
||||
{
|
||||
int precision = 0;
|
||||
bool precisionSet = false;
|
||||
int precision = -1;
|
||||
if (m[i] == '.')
|
||||
{
|
||||
precision = 0;
|
||||
while (++i < m.size() && m[i] >= '0' && m[i] <= '9')
|
||||
{
|
||||
precision = precision * 10 + (m[i] - '0');
|
||||
precisionSet = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (i < m.size())
|
||||
{
|
||||
width = (widthSet) ? width : -1;
|
||||
precision = (precisionSet) ? precision : -1;
|
||||
|
||||
if (m[i] == 'S' || m[i] == 's')
|
||||
visitedPlaceholder(StringPlaceholder, pad, width, precision);
|
||||
|
|
|
@ -141,11 +141,11 @@ namespace Resource
|
|||
void setUnRefImageDataAfterApply(bool unref);
|
||||
|
||||
/// @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:
|
||||
|
||||
|
|
|
@ -32,9 +32,9 @@ namespace Terrain
|
|||
|
||||
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;
|
||||
|
||||
|
|
|
@ -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
|
||||
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>
|
||||
|
||||
<!-- 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 -->
|
||||
<Widget type="HBox" position="0 338 511 24">
|
||||
|
|
Loading…
Reference in a new issue