mirror of
				https://github.com/TES3MP/openmw-tes3mp.git
				synced 2025-10-31 20:56:42 +00:00 
			
		
		
		
	Merge pull request #292 from OpenMW/master
Add OpenMW commits up to 18 Sep 2017
This commit is contained in:
		
						commit
						4d4eb1bb9f
					
				
					 15 changed files with 284 additions and 272 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; | ||||
| 
 | ||||
|  |  | |||
|  | @ -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; | ||||
|  |  | |||
|  | @ -1988,8 +1988,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; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  |  | |||
|  | @ -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(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -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)); | ||||
|         } | ||||
|  |  | |||
|  | @ -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); | ||||
|     } | ||||
| 
 | ||||
|  |  | |||
|  | @ -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(); | ||||
|  | @ -678,11 +680,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 +693,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 +724,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 +739,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 +753,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 +815,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 +824,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 +1120,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 +1242,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(); | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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); | ||||
|  |  | |||
|  | @ -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