Add OpenMW commits up to 4 Jul 2020
# Conflicts: # .travis.yml # CI/before_script.linux.sh # apps/openmw/engine.cpp # apps/openmw/mwbase/windowmanager.hpp # apps/openmw/mwgui/charactercreation.cpp # apps/openmw/mwgui/windowmanagerimp.hpp # apps/openmw/mwmechanics/character.cpppull/593/head
commit
5eb7eb8d88
@ -0,0 +1,209 @@
|
|||||||
|
#include "statswatcher.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
|
||||||
|
#include "../mwmechanics/npcstats.hpp"
|
||||||
|
#include "../mwmechanics/spellutil.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/class.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
#include "../mwworld/inventorystore.hpp"
|
||||||
|
|
||||||
|
namespace MWGui
|
||||||
|
{
|
||||||
|
// mWatchedTimeToStartDrowning = -1 for correct drowning state check,
|
||||||
|
// if stats.getTimeToStartDrowning() == 0 already on game start
|
||||||
|
StatsWatcher::StatsWatcher()
|
||||||
|
: mWatchedLevel(-1), mWatchedTimeToStartDrowning(-1), mWatchedStatsEmpty(true)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::watchActor(const MWWorld::Ptr& ptr)
|
||||||
|
{
|
||||||
|
mWatched = ptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::update()
|
||||||
|
{
|
||||||
|
if (mWatched.isEmpty())
|
||||||
|
return;
|
||||||
|
|
||||||
|
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
|
||||||
|
const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched);
|
||||||
|
for (int i = 0;i < ESM::Attribute::Length;++i)
|
||||||
|
{
|
||||||
|
if (stats.getAttribute(i) != mWatchedAttributes[i] || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
std::stringstream attrname;
|
||||||
|
attrname << "AttribVal"<<(i+1);
|
||||||
|
|
||||||
|
mWatchedAttributes[i] = stats.getAttribute(i);
|
||||||
|
setValue(attrname.str(), stats.getAttribute(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.getHealth() != mWatchedHealth || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
static const std::string hbar("HBar");
|
||||||
|
mWatchedHealth = stats.getHealth();
|
||||||
|
setValue(hbar, stats.getHealth());
|
||||||
|
}
|
||||||
|
if (stats.getMagicka() != mWatchedMagicka || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
static const std::string mbar("MBar");
|
||||||
|
mWatchedMagicka = stats.getMagicka();
|
||||||
|
setValue(mbar, stats.getMagicka());
|
||||||
|
}
|
||||||
|
if (stats.getFatigue() != mWatchedFatigue || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
static const std::string fbar("FBar");
|
||||||
|
mWatchedFatigue = stats.getFatigue();
|
||||||
|
setValue(fbar, stats.getFatigue());
|
||||||
|
}
|
||||||
|
|
||||||
|
float timeToDrown = stats.getTimeToStartDrowning();
|
||||||
|
|
||||||
|
if (timeToDrown != mWatchedTimeToStartDrowning)
|
||||||
|
{
|
||||||
|
static const float fHoldBreathTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>()
|
||||||
|
.find("fHoldBreathTime")->mValue.getFloat();
|
||||||
|
|
||||||
|
mWatchedTimeToStartDrowning = timeToDrown;
|
||||||
|
|
||||||
|
if(timeToDrown >= fHoldBreathTime || timeToDrown == -1.0) // -1.0 is a special value during initialization
|
||||||
|
winMgr->setDrowningBarVisibility(false);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
winMgr->setDrowningBarVisibility(true);
|
||||||
|
winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning(), fHoldBreathTime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//Loop over ESM::Skill::SkillEnum
|
||||||
|
for (int i = 0; i < ESM::Skill::Length; ++i)
|
||||||
|
{
|
||||||
|
if(stats.getSkill(i) != mWatchedSkills[i] || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
mWatchedSkills[i] = stats.getSkill(i);
|
||||||
|
setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (stats.getLevel() != mWatchedLevel || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
mWatchedLevel = stats.getLevel();
|
||||||
|
setValue("level", mWatchedLevel);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mWatched.getClass().isNpc())
|
||||||
|
{
|
||||||
|
const ESM::NPC *watchedRecord = mWatched.get<ESM::NPC>()->mBase;
|
||||||
|
|
||||||
|
if (watchedRecord->mName != mWatchedName || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
mWatchedName = watchedRecord->mName;
|
||||||
|
setValue("name", watchedRecord->mName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watchedRecord->mRace != mWatchedRace || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
mWatchedRace = watchedRecord->mRace;
|
||||||
|
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore()
|
||||||
|
.get<ESM::Race>().find(watchedRecord->mRace);
|
||||||
|
setValue("race", race->mName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (watchedRecord->mClass != mWatchedClass || mWatchedStatsEmpty)
|
||||||
|
{
|
||||||
|
mWatchedClass = watchedRecord->mClass;
|
||||||
|
const ESM::Class *cls = MWBase::Environment::get().getWorld()->getStore()
|
||||||
|
.get<ESM::Class>().find(watchedRecord->mClass);
|
||||||
|
setValue("class", cls->mName);
|
||||||
|
|
||||||
|
MWBase::WindowManager::SkillList majorSkills (5);
|
||||||
|
MWBase::WindowManager::SkillList minorSkills (5);
|
||||||
|
|
||||||
|
for (int i=0; i<5; ++i)
|
||||||
|
{
|
||||||
|
minorSkills[i] = cls->mData.mSkills[i][0];
|
||||||
|
majorSkills[i] = cls->mData.mSkills[i][1];
|
||||||
|
}
|
||||||
|
|
||||||
|
configureSkills(majorSkills, minorSkills);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mWatchedStatsEmpty = false;
|
||||||
|
|
||||||
|
// Update the equipped weapon icon
|
||||||
|
MWWorld::InventoryStore& inv = mWatched.getClass().getInventoryStore(mWatched);
|
||||||
|
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||||
|
if (weapon == inv.end())
|
||||||
|
winMgr->unsetSelectedWeapon();
|
||||||
|
else
|
||||||
|
winMgr->setSelectedWeapon(*weapon);
|
||||||
|
|
||||||
|
// Update the selected spell icon
|
||||||
|
MWWorld::ContainerStoreIterator enchantItem = inv.getSelectedEnchantItem();
|
||||||
|
if (enchantItem != inv.end())
|
||||||
|
winMgr->setSelectedEnchantItem(*enchantItem);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
const std::string& spell = winMgr->getSelectedSpell();
|
||||||
|
if (!spell.empty())
|
||||||
|
winMgr->setSelectedSpell(spell, int(MWMechanics::getSpellSuccessChance(spell, mWatched)));
|
||||||
|
else
|
||||||
|
winMgr->unsetSelectedSpell();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::addListener(StatsListener* listener)
|
||||||
|
{
|
||||||
|
mListeners.insert(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::removeListener(StatsListener* listener)
|
||||||
|
{
|
||||||
|
mListeners.erase(listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::setValue(const std::string& id, const MWMechanics::AttributeValue& value)
|
||||||
|
{
|
||||||
|
for (StatsListener* listener : mListeners)
|
||||||
|
listener->setValue(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::setValue(ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value)
|
||||||
|
{
|
||||||
|
/// \todo Don't use the skill enum as a parameter type (we will have to drop it anyway, once we
|
||||||
|
/// allow custom skills.
|
||||||
|
for (StatsListener* listener : mListeners)
|
||||||
|
listener->setValue(parSkill, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value)
|
||||||
|
{
|
||||||
|
for (StatsListener* listener : mListeners)
|
||||||
|
listener->setValue(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::setValue(const std::string& id, const std::string& value)
|
||||||
|
{
|
||||||
|
for (StatsListener* listener : mListeners)
|
||||||
|
listener->setValue(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::setValue(const std::string& id, int value)
|
||||||
|
{
|
||||||
|
for (StatsListener* listener : mListeners)
|
||||||
|
listener->setValue(id, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void StatsWatcher::configureSkills(const std::vector<int>& major, const std::vector<int>& minor)
|
||||||
|
{
|
||||||
|
for (StatsListener* listener : mListeners)
|
||||||
|
listener->configureSkills(major, minor);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
#ifndef MWGUI_STATSWATCHER_H
|
||||||
|
#define MWGUI_STATSWATCHER_H
|
||||||
|
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include <components/esm/attr.hpp>
|
||||||
|
#include <components/esm/loadskil.hpp>
|
||||||
|
|
||||||
|
#include "../mwmechanics/stat.hpp"
|
||||||
|
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
namespace MWGui
|
||||||
|
{
|
||||||
|
class StatsListener
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
/// Set value for the given ID.
|
||||||
|
virtual void setValue(const std::string& id, const MWMechanics::AttributeValue& value) {}
|
||||||
|
virtual void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value) {}
|
||||||
|
virtual void setValue(const std::string& id, const std::string& value) {}
|
||||||
|
virtual void setValue(const std::string& id, int value) {}
|
||||||
|
virtual void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value) {}
|
||||||
|
virtual void configureSkills(const std::vector<int>& major, const std::vector<int>& minor) {}
|
||||||
|
};
|
||||||
|
|
||||||
|
class StatsWatcher
|
||||||
|
{
|
||||||
|
MWWorld::Ptr mWatched;
|
||||||
|
|
||||||
|
MWMechanics::AttributeValue mWatchedAttributes[ESM::Attribute::Length];
|
||||||
|
MWMechanics::SkillValue mWatchedSkills[ESM::Skill::Length];
|
||||||
|
|
||||||
|
MWMechanics::DynamicStat<float> mWatchedHealth;
|
||||||
|
MWMechanics::DynamicStat<float> mWatchedMagicka;
|
||||||
|
MWMechanics::DynamicStat<float> mWatchedFatigue;
|
||||||
|
|
||||||
|
std::string mWatchedName;
|
||||||
|
std::string mWatchedRace;
|
||||||
|
std::string mWatchedClass;
|
||||||
|
|
||||||
|
int mWatchedLevel;
|
||||||
|
|
||||||
|
float mWatchedTimeToStartDrowning;
|
||||||
|
|
||||||
|
bool mWatchedStatsEmpty;
|
||||||
|
|
||||||
|
std::set<StatsListener*> mListeners;
|
||||||
|
|
||||||
|
void setValue(const std::string& id, const MWMechanics::AttributeValue& value);
|
||||||
|
void setValue(const std::string& id, const MWMechanics::DynamicStat<float>& value);
|
||||||
|
void setValue(const std::string& id, const std::string& value);
|
||||||
|
void setValue(const std::string& id, int value);
|
||||||
|
void setValue(const ESM::Skill::SkillEnum parSkill, const MWMechanics::SkillValue& value);
|
||||||
|
void configureSkills(const std::vector<int>& major, const std::vector<int>& minor);
|
||||||
|
|
||||||
|
public:
|
||||||
|
StatsWatcher();
|
||||||
|
|
||||||
|
void update();
|
||||||
|
void addListener(StatsListener* listener);
|
||||||
|
void removeListener(StatsListener* listener);
|
||||||
|
|
||||||
|
void watchActor(const MWWorld::Ptr& ptr);
|
||||||
|
MWWorld::Ptr getWatchedActor() const { return mWatched; }
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,71 @@
|
|||||||
|
#include "regionsoundselector.hpp"
|
||||||
|
|
||||||
|
#include <components/misc/rng.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
#include <numeric>
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwworld/esmstore.hpp"
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
int addChance(int result, const ESM::Region::SoundRef &v)
|
||||||
|
{
|
||||||
|
return result + v.mChance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boost::optional<std::string> RegionSoundSelector::getNextRandom(float duration, const std::string& regionName,
|
||||||
|
const MWBase::World& world)
|
||||||
|
{
|
||||||
|
mTimePassed += duration;
|
||||||
|
|
||||||
|
if (mTimePassed < mTimeToNextEnvSound)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
const float a = Misc::Rng::rollClosedProbability();
|
||||||
|
// NOTE: We should use the "Minimum Time Between Environmental Sounds" and
|
||||||
|
// "Maximum Time Between Environmental Sounds" fallback settings here.
|
||||||
|
mTimeToNextEnvSound = 5.0f * a + 15.0f * (1.0f - a);
|
||||||
|
mTimePassed = 0;
|
||||||
|
|
||||||
|
if (mLastRegionName != regionName)
|
||||||
|
{
|
||||||
|
mLastRegionName = regionName;
|
||||||
|
mSumChance = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ESM::Region* const region = world.getStore().get<ESM::Region>().search(mLastRegionName);
|
||||||
|
|
||||||
|
if (region == nullptr)
|
||||||
|
return {};
|
||||||
|
|
||||||
|
if (mSumChance == 0)
|
||||||
|
{
|
||||||
|
mSumChance = std::accumulate(region->mSoundList.begin(), region->mSoundList.end(), 0, addChance);
|
||||||
|
if (mSumChance == 0)
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
const int r = Misc::Rng::rollDice(mSumChance);
|
||||||
|
int pos = 0;
|
||||||
|
|
||||||
|
const auto isSelected = [&] (const ESM::Region::SoundRef& sound)
|
||||||
|
{
|
||||||
|
if (r - pos < sound.mChance)
|
||||||
|
return true;
|
||||||
|
pos += sound.mChance;
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const auto it = std::find_if(region->mSoundList.begin(), region->mSoundList.end(), isSelected);
|
||||||
|
|
||||||
|
if (it == region->mSoundList.end())
|
||||||
|
return {};
|
||||||
|
|
||||||
|
return it->mSound;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
#ifndef GAME_SOUND_REGIONSOUNDSELECTOR_H
|
||||||
|
#define GAME_SOUND_REGIONSOUNDSELECTOR_H
|
||||||
|
|
||||||
|
#include <boost/optional.hpp>
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace MWBase
|
||||||
|
{
|
||||||
|
class World;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
class RegionSoundSelector
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
boost::optional<std::string> getNextRandom(float duration, const std::string& regionName,
|
||||||
|
const MWBase::World& world);
|
||||||
|
|
||||||
|
private:
|
||||||
|
float mTimeToNextEnvSound = 0.0f;
|
||||||
|
int mSumChance = 0;
|
||||||
|
std::string mLastRegionName;
|
||||||
|
float mTimePassed = 0.0;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,17 @@
|
|||||||
|
#ifndef GAME_SOUND_TYPE_H
|
||||||
|
#define GAME_SOUND_TYPE_H
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
enum class Type
|
||||||
|
{
|
||||||
|
Sfx = 1 << 4, /* Normal SFX sound */
|
||||||
|
Voice = 1 << 5, /* Voice sound */
|
||||||
|
Foot = 1 << 6, /* Footstep sound */
|
||||||
|
Music = 1 << 7, /* Music track */
|
||||||
|
Movie = 1 << 8, /* Movie audio track */
|
||||||
|
Mask = Sfx | Voice | Foot | Music | Movie
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,56 @@
|
|||||||
|
#include "volumesettings.hpp"
|
||||||
|
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
namespace
|
||||||
|
{
|
||||||
|
float clamp(float value)
|
||||||
|
{
|
||||||
|
return std::max(0.0f, std::min(1.0f, value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VolumeSettings::VolumeSettings()
|
||||||
|
: mMasterVolume(clamp(Settings::Manager::getFloat("master volume", "Sound"))),
|
||||||
|
mSFXVolume(clamp(Settings::Manager::getFloat("sfx volume", "Sound"))),
|
||||||
|
mMusicVolume(clamp(Settings::Manager::getFloat("music volume", "Sound"))),
|
||||||
|
mVoiceVolume(clamp(Settings::Manager::getFloat("voice volume", "Sound"))),
|
||||||
|
mFootstepsVolume(clamp(Settings::Manager::getFloat("footsteps volume", "Sound")))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
float VolumeSettings::getVolumeFromType(Type type) const
|
||||||
|
{
|
||||||
|
float volume = mMasterVolume;
|
||||||
|
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case Type::Sfx:
|
||||||
|
volume *= mSFXVolume;
|
||||||
|
break;
|
||||||
|
case Type::Voice:
|
||||||
|
volume *= mVoiceVolume;
|
||||||
|
break;
|
||||||
|
case Type::Foot:
|
||||||
|
volume *= mFootstepsVolume;
|
||||||
|
break;
|
||||||
|
case Type::Music:
|
||||||
|
volume *= mMusicVolume;
|
||||||
|
break;
|
||||||
|
case Type::Movie:
|
||||||
|
case Type::Mask:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return volume;
|
||||||
|
}
|
||||||
|
|
||||||
|
void VolumeSettings::update()
|
||||||
|
{
|
||||||
|
*this = VolumeSettings();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,26 @@
|
|||||||
|
#ifndef GAME_SOUND_VOLUMESETTINGS_H
|
||||||
|
#define GAME_SOUND_VOLUMESETTINGS_H
|
||||||
|
|
||||||
|
#include "type.hpp"
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
class VolumeSettings
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VolumeSettings();
|
||||||
|
|
||||||
|
float getVolumeFromType(Type type) const;
|
||||||
|
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private:
|
||||||
|
float mMasterVolume;
|
||||||
|
float mSFXVolume;
|
||||||
|
float mMusicVolume;
|
||||||
|
float mVoiceVolume;
|
||||||
|
float mFootstepsVolume;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,71 @@
|
|||||||
|
#include "watersoundupdater.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwworld/cellstore.hpp"
|
||||||
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
|
#include <components/esm/loadcell.hpp>
|
||||||
|
|
||||||
|
#include <osg/Vec3f>
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
WaterSoundUpdater::WaterSoundUpdater(const WaterSoundUpdaterSettings& settings)
|
||||||
|
: mSettings(settings)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
WaterSoundUpdate WaterSoundUpdater::update(const MWWorld::ConstPtr& player, const MWBase::World& world) const
|
||||||
|
{
|
||||||
|
WaterSoundUpdate result;
|
||||||
|
|
||||||
|
result.mId = player.getCell()->isExterior() ? mSettings.mNearWaterOutdoorID : mSettings.mNearWaterIndoorID;
|
||||||
|
result.mVolume = std::min(1.0f, getVolume(player, world));
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
float WaterSoundUpdater::getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const
|
||||||
|
{
|
||||||
|
if (mListenerUnderwater)
|
||||||
|
return 1.0f;
|
||||||
|
|
||||||
|
const MWWorld::CellStore& cell = *player.getCell();
|
||||||
|
|
||||||
|
if (!cell.getCell()->hasWater())
|
||||||
|
return 0.0f;
|
||||||
|
|
||||||
|
const osg::Vec3f pos = player.getRefData().getPosition().asVec3();
|
||||||
|
const float dist = std::abs(cell.getWaterLevel() - pos.z());
|
||||||
|
|
||||||
|
if (cell.isExterior() && dist < mSettings.mNearWaterOutdoorTolerance)
|
||||||
|
{
|
||||||
|
if (mSettings.mNearWaterPoints <= 1)
|
||||||
|
return (mSettings.mNearWaterOutdoorTolerance - dist) / mSettings.mNearWaterOutdoorTolerance;
|
||||||
|
|
||||||
|
const float step = mSettings.mNearWaterRadius * 2.0f / (mSettings.mNearWaterPoints - 1);
|
||||||
|
|
||||||
|
int underwaterPoints = 0;
|
||||||
|
|
||||||
|
for (int x = 0; x < mSettings.mNearWaterPoints; x++)
|
||||||
|
{
|
||||||
|
for (int y = 0; y < mSettings.mNearWaterPoints; y++)
|
||||||
|
{
|
||||||
|
const float terrainX = pos.x() - mSettings.mNearWaterRadius + x * step;
|
||||||
|
const float terrainY = pos.y() - mSettings.mNearWaterRadius + y * step;
|
||||||
|
const float height = world.getTerrainHeightAt(osg::Vec3f(terrainX, terrainY, 0.0f));
|
||||||
|
|
||||||
|
if (height < 0)
|
||||||
|
underwaterPoints++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return underwaterPoints * 2.0f / (mSettings.mNearWaterPoints * mSettings.mNearWaterPoints);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!cell.isExterior() && dist < mSettings.mNearWaterIndoorTolerance)
|
||||||
|
return (mSettings.mNearWaterIndoorTolerance - dist) / mSettings.mNearWaterIndoorTolerance;
|
||||||
|
|
||||||
|
return 0.0f;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,54 @@
|
|||||||
|
#ifndef GAME_SOUND_WATERSOUNDUPDATER_H
|
||||||
|
#define GAME_SOUND_WATERSOUNDUPDATER_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace MWBase
|
||||||
|
{
|
||||||
|
class World;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWWorld
|
||||||
|
{
|
||||||
|
class ConstPtr;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace MWSound
|
||||||
|
{
|
||||||
|
struct WaterSoundUpdaterSettings
|
||||||
|
{
|
||||||
|
int mNearWaterRadius;
|
||||||
|
int mNearWaterPoints;
|
||||||
|
float mNearWaterIndoorTolerance;
|
||||||
|
float mNearWaterOutdoorTolerance;
|
||||||
|
std::string mNearWaterIndoorID;
|
||||||
|
std::string mNearWaterOutdoorID;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct WaterSoundUpdate
|
||||||
|
{
|
||||||
|
std::string mId;
|
||||||
|
float mVolume;
|
||||||
|
};
|
||||||
|
|
||||||
|
class WaterSoundUpdater
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
explicit WaterSoundUpdater(const WaterSoundUpdaterSettings& settings);
|
||||||
|
|
||||||
|
WaterSoundUpdate update(const MWWorld::ConstPtr& player, const MWBase::World& world) const;
|
||||||
|
|
||||||
|
void setUnderwater(bool value)
|
||||||
|
{
|
||||||
|
mListenerUnderwater = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
const WaterSoundUpdaterSettings mSettings;
|
||||||
|
bool mListenerUnderwater = false;
|
||||||
|
|
||||||
|
float getVolume(const MWWorld::ConstPtr& player, const MWBase::World& world) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue