diff --git a/apps/openmw/mwbase/soundmanager.hpp b/apps/openmw/mwbase/soundmanager.hpp index b75f6753d8..4d764597cf 100644 --- a/apps/openmw/mwbase/soundmanager.hpp +++ b/apps/openmw/mwbase/soundmanager.hpp @@ -39,9 +39,11 @@ namespace MWBase Play_Normal = 0, /* tracked, non-looping, multi-instance, environment */ Play_Loop = 1<<0, /* Sound will continually loop until explicitly stopped */ Play_NoEnv = 1<<1, /* Do not apply environment effects (eg, underwater filters) */ - Play_NoTrack = 1<<2 /* (3D only) Play the sound at the given object's position + Play_NoTrack = 1<<2, /* (3D only) Play the sound at the given object's position * but do not keep it updated (the sound will not move with * the object and will not stop when the object is deleted. */ + + Play_LoopNoEnv = Play_Loop | Play_NoEnv }; enum PlayType { Play_TypeSfx = 1<<3, /* Normal SFX sound */ diff --git a/apps/openmw/mwclass/activator.cpp b/apps/openmw/mwclass/activator.cpp index 3a60d9c397..583cb08d39 100644 --- a/apps/openmw/mwclass/activator.cpp +++ b/apps/openmw/mwclass/activator.cpp @@ -6,19 +6,26 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/mechanicsmanager.hpp" +#include "../mwbase/world.hpp" #include "../mwworld//cellstore.hpp" #include "../mwworld/ptr.hpp" #include "../mwworld/physicssystem.hpp" +#include "../mwworld/action.hpp" +#include "../mwworld/failedaction.hpp" +#include "../mwworld/nullaction.hpp" #include "../mwrender/actors.hpp" #include "../mwrender/renderinginterface.hpp" #include "../mwgui/tooltips.hpp" +#include "../mwmechanics/npcstats.hpp" + + namespace MWClass { - void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const + void Activator::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const { const std::string model = getModel(ptr); if (!model.empty()) { @@ -94,7 +101,23 @@ namespace MWClass return info; } - + + boost::shared_ptr Activator::activate(const MWWorld::Ptr &ptr, const MWWorld::Ptr &actor) const + { + if(get(actor).isNpc() && get(actor).getNpcStats(actor).isWerewolf()) + { + const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); + const ESM::Sound *sound = store.get().searchRandom("WolfActivator"); + + boost::shared_ptr action(new MWWorld::FailedAction("#{sWerewolfRefusal}")); + if(sound) action->setSound(sound->mId); + + return action; + } + return boost::shared_ptr(new MWWorld::NullAction); + } + + MWWorld::Ptr Activator::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const { diff --git a/apps/openmw/mwclass/activator.hpp b/apps/openmw/mwclass/activator.hpp index 4165fbc080..1e772ef4f2 100644 --- a/apps/openmw/mwclass/activator.hpp +++ b/apps/openmw/mwclass/activator.hpp @@ -31,6 +31,9 @@ namespace MWClass virtual std::string getScript (const MWWorld::Ptr& ptr) const; ///< Return name of the script attached to ptr + virtual boost::shared_ptr activate (const MWWorld::Ptr& ptr, const MWWorld::Ptr& actor) const; + ///< Generate action for activation + static void registerSelf(); virtual std::string getModel(const MWWorld::Ptr &ptr) const; diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index b8d0a87454..566d4bc505 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -11,10 +11,12 @@ #include "../mwworld/class.hpp" #include "../mwworld/inventorystore.hpp" +#include "../mwworld/player.hpp" #include "../mwbase/world.hpp" #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "../mwbase/soundmanager.hpp" #include "npcstats.hpp" #include "creaturestats.hpp" @@ -68,22 +70,24 @@ namespace MWMechanics { CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - int strength = creatureStats.getAttribute(0).getBase(); - int intelligence = creatureStats.getAttribute(1).getBase(); - int willpower = creatureStats.getAttribute(2).getBase(); - int agility = creatureStats.getAttribute(3).getBase(); - int endurance = creatureStats.getAttribute(5).getBase(); + int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getBase(); + int willpower = creatureStats.getAttribute(ESM::Attribute::Willpower).getBase(); + int agility = creatureStats.getAttribute(ESM::Attribute::Agility).getBase(); + int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); double magickaFactor = creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5; DynamicStat magicka = creatureStats.getMagicka(); - magicka.setBase (static_cast (intelligence + magickaFactor * intelligence)); - creatureStats.setMagicka (magicka); + float diff = (static_cast(intelligence + magickaFactor*intelligence)) - magicka.getBase(); + magicka.modify(diff); + creatureStats.setMagicka(magicka); DynamicStat fatigue = creatureStats.getFatigue(); - fatigue.setBase (strength+willpower+agility+endurance); - creatureStats.setFatigue (fatigue); + diff = (strength+willpower+agility+endurance) - fatigue.getBase(); + fatigue.modify(diff); + creatureStats.setFatigue(fatigue); } void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) @@ -133,62 +137,63 @@ namespace MWMechanics void Actors::calculateCreatureStatModifiers (const MWWorld::Ptr& ptr) { - CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); + CreatureStats &creatureStats = MWWorld::Class::get(ptr).getCreatureStats(ptr); + const MagicEffects &effects = creatureStats.getMagicEffects(); // attributes - for (int i=0; i<8; ++i) + for(int i = 0;i < ESM::Attribute::Length;++i) { - int modifier = - creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyAttribute, i)).mMagnitude; + Stat stat = creatureStats.getAttribute(i); + stat.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifyAttribute, i)).mMagnitude - + effects.get(EffectKey(ESM::MagicEffect::DrainAttribute, i)).mMagnitude); - modifier -= creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::DrainAttribute, i)).mMagnitude; - - creatureStats.getAttribute(i).setModifier (modifier); + creatureStats.setAttribute(i, stat); } // dynamic stats - MagicEffects effects = creatureStats.getMagicEffects(); - - for (int i=0; i<3; ++i) + for(int i = 0;i < 3;++i) { - DynamicStat stat = creatureStats.getDynamic (i); + DynamicStat stat = creatureStats.getDynamic(i); + stat.setModifier(effects.get(EffectKey(80+i)).mMagnitude - + effects.get(EffectKey(18+i)).mMagnitude); - stat.setModifier ( - effects.get (EffectKey(80+i)).mMagnitude - effects.get (EffectKey(18+i)).mMagnitude); - - creatureStats.setDynamic (i, stat); + creatureStats.setDynamic(i, stat); } } void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) { - NpcStats &stats = MWWorld::Class::get(ptr).getNpcStats(ptr); - if(MWBase::Environment::get().getWorld()->isSubmerged(ptr) && + MWBase::World *world = MWBase::Environment::get().getWorld(); + NpcStats &stats = ptr.getClass().getNpcStats(ptr); + if(world->isSubmerged(ptr) && stats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).mMagnitude == 0) { + float timeLeft = 0.0f; if(stats.getFatigue().getCurrent() == 0) stats.setTimeToStartDrowning(0); - - float timeLeft = stats.getTimeToStartDrowning()-duration; - if(timeLeft < 0.0f) - timeLeft = 0.0f; - - stats.setTimeToStartDrowning(timeLeft); + else + { + timeLeft = stats.getTimeToStartDrowning() - duration; + if(timeLeft < 0.0f) + timeLeft = 0.0f; + stats.setTimeToStartDrowning(timeLeft); + } if(timeLeft == 0.0f) - stats.setLastDrowningHitTime(stats.getLastDrowningHitTime()+duration); + { + // If drowning, apply 3 points of damage per second + ptr.getClass().setActorHealth(ptr, stats.getHealth().getCurrent() - 3.0f*duration); + + // Play a drowning sound as necessary for the player + if(ptr == world->getPlayer().getPlayer()) + { + MWBase::SoundManager *sndmgr = MWBase::Environment::get().getSoundManager(); + if(!sndmgr->getSoundPlaying(MWWorld::Ptr(), "drown")) + sndmgr->playSound("drown", 1.0f, 1.0f); + } + } } else - { stats.setTimeToStartDrowning(20); - stats.setLastDrowningHitTime(0); - } - //if npc is drowning and it's time to hit, then hit - while(stats.getTimeToStartDrowning() == 0.0f && stats.getLastDrowningHitTime() > 0.33f) - { - stats.setLastDrowningHitTime(stats.getLastDrowningHitTime()-0.33f); - //fixme: replace it with something different once screen hit effects are implemented (blood on screen) - MWWorld::Class::get(ptr).setActorHealth(ptr, stats.getHealth().getCurrent()-1.0f); - } } Actors::Actors() : mDuration (0) {} diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index c559e4445f..8c13db7900 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -8,7 +8,6 @@ #include "../mwbase/world.hpp" #include "../mwbase/windowmanager.hpp" #include "../mwbase/dialoguemanager.hpp" -#include "../mwbase/soundmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -189,6 +188,9 @@ namespace MWMechanics void MechanicsManager::updateCell(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr) { + if(old == mWatched) + mWatched = ptr; + if(MWWorld::Class::get(ptr).isActor()) mActors.updateActor(old, ptr); else @@ -213,98 +215,76 @@ namespace MWMechanics void MechanicsManager::update(float duration, bool paused) { - if (!mWatched.isEmpty()) + if(!mWatched.isEmpty()) { - MWMechanics::CreatureStats& stats = - MWWorld::Class::get (mWatched).getCreatureStats (mWatched); - - MWMechanics::NpcStats& npcStats = - MWWorld::Class::get (mWatched).getNpcStats (mWatched); - - static const char *attributeNames[8] = + MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); + const MWMechanics::NpcStats &stats = mWatched.getClass().getNpcStats(mWatched); + for(int i = 0;i < ESM::Attribute::Length;++i) { - "AttribVal1", "AttribVal2", "AttribVal3", "AttribVal4", "AttribVal5", - "AttribVal6", "AttribVal7", "AttribVal8" - }; - - static const char *dynamicNames[3] = - { - "HBar", "MBar", "FBar" - }; - - for (int i=0; i<8; ++i) - { - if (stats.getAttribute(i)!=mWatchedCreature.getAttribute(i)) + if(stats.getAttribute(i) != mWatchedStats.getAttribute(i)) { - mWatchedCreature.setAttribute(i, stats.getAttribute(i)); + std::stringstream attrname; + attrname << "AttribVal"<<(i+1); - MWBase::Environment::get().getWindowManager()->setValue (attributeNames[i], stats.getAttribute(i)); + mWatchedStats.setAttribute(i, stats.getAttribute(i)); + winMgr->setValue(attrname.str(), stats.getAttribute(i)); } } - if (stats.getHealth() != mWatchedCreature.getHealth()) { - mWatchedCreature.setHealth(stats.getHealth()); - MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[0], stats.getHealth()); + if(stats.getHealth() != mWatchedStats.getHealth()) + { + static const std::string hbar("HBar"); + mWatchedStats.setHealth(stats.getHealth()); + winMgr->setValue(hbar, stats.getHealth()); } - if (stats.getMagicka() != mWatchedCreature.getMagicka()) { - mWatchedCreature.setMagicka(stats.getMagicka()); - MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[1], stats.getMagicka()); + if(stats.getMagicka() != mWatchedStats.getMagicka()) + { + static const std::string mbar("MBar"); + mWatchedStats.setMagicka(stats.getMagicka()); + winMgr->setValue(mbar, stats.getMagicka()); } - if (stats.getFatigue() != mWatchedCreature.getFatigue()) { - mWatchedCreature.setFatigue(stats.getFatigue()); - MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[2], stats.getFatigue()); + if(stats.getFatigue() != mWatchedStats.getFatigue()) + { + static const std::string fbar("FBar"); + mWatchedStats.setFatigue(stats.getFatigue()); + winMgr->setValue(fbar, stats.getFatigue()); } - if(npcStats.getTimeToStartDrowning() != mWatchedNpc.getTimeToStartDrowning()) + if(stats.getTimeToStartDrowning() != mWatchedStats.getTimeToStartDrowning()) { - mWatchedNpc.setTimeToStartDrowning(npcStats.getTimeToStartDrowning()); - if(npcStats.getTimeToStartDrowning()>=20.0) - { - MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(false); - } + mWatchedStats.setTimeToStartDrowning(stats.getTimeToStartDrowning()); + if(stats.getTimeToStartDrowning() >= 20.0f) + winMgr->setDrowningBarVisibility(false); else { - MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(true); - MWBase::Environment::get().getWindowManager()->setDrowningTimeLeft(npcStats.getTimeToStartDrowning()); + winMgr->setDrowningBarVisibility(true); + winMgr->setDrowningTimeLeft(stats.getTimeToStartDrowning()); } } bool update = false; //Loop over ESM::Skill::SkillEnum - for(int i = 0; i < 27; ++i) + for(int i = 0; i < ESM::Skill::Length; ++i) { - if(npcStats.getSkill (i) != mWatchedNpc.getSkill (i)) + if(stats.getSkill(i) != mWatchedStats.getSkill(i)) { update = true; - mWatchedNpc.getSkill (i) = npcStats.getSkill (i); - MWBase::Environment::get().getWindowManager()->setValue((ESM::Skill::SkillEnum)i, npcStats.getSkill (i)); + mWatchedStats.getSkill(i) = stats.getSkill(i); + winMgr->setValue((ESM::Skill::SkillEnum)i, stats.getSkill(i)); } } - if (update) - MWBase::Environment::get().getWindowManager()->updateSkillArea(); + if(update) + winMgr->updateSkillArea(); - MWBase::Environment::get().getWindowManager()->setValue ("level", stats.getLevel()); - } - - //update drowning sound - MWBase::World *world = MWBase::Environment::get().getWorld(); - MWBase::SoundManager * sndmgr = MWBase::Environment::get().getSoundManager(); - MWWorld::Ptr playerPtr = world->getPlayer().getPlayer(); - NpcStats& playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - if(!sndmgr->getSoundPlaying(MWWorld::Ptr(), "drown") && playerStats.getTimeToStartDrowning()==0.0) - { - sndmgr->playSound("drown",1.0,1.0,MWBase::SoundManager::Play_TypeSfx,MWBase::SoundManager::Play_Loop); - } - if(playerStats.getTimeToStartDrowning()>0.0) - { - //no need to check if it's playing, stop sound does nothing in that case - sndmgr->stopSound("drown"); + winMgr->setValue("level", stats.getLevel()); } if (mUpdatePlayer) { + MWBase::World *world = MWBase::Environment::get().getWorld(); + // basic player profile; should not change anymore after the creation phase is finished. MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 95f760d113..ad07562c7b 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -25,8 +25,7 @@ namespace MWMechanics class MechanicsManager : public MWBase::MechanicsManager { MWWorld::Ptr mWatched; - CreatureStats mWatchedCreature; - NpcStats mWatchedNpc; + NpcStats mWatchedStats; bool mUpdatePlayer; bool mClassSelected; bool mRaceSelected; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 6a5e5a98f4..0b36982890 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -422,7 +422,7 @@ void MWMechanics::NpcStats::modifyProfit(int diff) mProfit += diff; } -float MWMechanics::NpcStats::getTimeToStartDrowning() +float MWMechanics::NpcStats::getTimeToStartDrowning() const { return mTimeToStartDrowning; } @@ -431,13 +431,3 @@ void MWMechanics::NpcStats::setTimeToStartDrowning(float time) assert(time>=0 && time<=20); mTimeToStartDrowning=time; } - -float MWMechanics::NpcStats::getLastDrowningHitTime() -{ - return mLastDrowningHit; -} - -void MWMechanics::NpcStats::setLastDrowningHitTime(float time) -{ - mLastDrowningHit=time; -} diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index d3fe61829e..6b7efa5b72 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -147,15 +147,10 @@ namespace MWMechanics int getWerewolfKills() const; - float getTimeToStartDrowning(); + float getTimeToStartDrowning() const; /// Sets time left for the creature to drown if it stays underwater. /// @param time value from [0,20] void setTimeToStartDrowning(float time); - - float getLastDrowningHitTime(); - /// Sets time since last hit caused by drowning. - /// @param time value from [0,0.33] - void setLastDrowningHitTime(float time); }; } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 606671389a..65d47c9c08 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -42,6 +42,18 @@ namespace MWMechanics mBase = mModified = value; } + void modify(const T& diff) + { + mBase += diff; + if(mBase >= static_cast(0)) + mModified += diff; + else + { + mModified += diff - mBase; + mBase = static_cast(0); + } + } + /// Set base and adjust modified accordingly. void setBase (const T& value) { diff --git a/apps/openmw/mwsound/soundmanagerimp.cpp b/apps/openmw/mwsound/soundmanagerimp.cpp index 03e2ce6239..372be83938 100644 --- a/apps/openmw/mwsound/soundmanagerimp.cpp +++ b/apps/openmw/mwsound/soundmanagerimp.cpp @@ -4,11 +4,10 @@ #include #include -#include "../mwworld/esmstore.hpp" - #include "../mwbase/environment.hpp" #include "../mwbase/world.hpp" +#include "../mwworld/esmstore.hpp" #include "../mwworld/player.hpp" #include "sound_output.hpp" @@ -103,6 +102,7 @@ namespace MWSound SoundManager::~SoundManager() { + mUnderwaterSound.reset(); mActiveSounds.clear(); mMusic.reset(); mOutput.reset(); @@ -474,27 +474,32 @@ namespace MWSound void SoundManager::updateRegionSound(float duration) { - MWWorld::Ptr::CellStore *current = MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell(); + static float sTimeToNextEnvSound = 0.0f; static int total = 0; static std::string regionName = ""; - static float timePassed = 0.0; + static float sTimePassed = 0.0; + MWBase::World *world = MWBase::Environment::get().getWorld(); + const MWWorld::Ptr player = world->getPlayer().getPlayer(); + const ESM::Cell *cell = player.getCell()->mCell; - //If the region has changed - timePassed += duration; - if(!current->mCell->isExterior() || timePassed < 10) + sTimePassed += duration; + if(!cell->isExterior() || sTimePassed < sTimeToNextEnvSound) return; - timePassed = 0; - if(regionName != current->mCell->mRegion) + float a = std::rand() / (double)RAND_MAX; + // NOTE: We should use the "Minimum Time Between Environmental Sounds" and + // "Maximum Time Between Environmental Sounds" fallback settings here. + sTimeToNextEnvSound = 5.0f*a + 15.0f*(1.0f-a); + sTimePassed = 0; + + if(regionName != cell->mRegion) { - regionName = current->mCell->mRegion; + regionName = cell->mRegion; total = 0; } - const ESM::Region *regn = - MWBase::Environment::get().getWorld()->getStore().get().search(regionName); - - if (regn == NULL) + const ESM::Region *regn = world->getStore().get().search(regionName); + if(regn == NULL) return; std::vector::const_iterator soundIter; @@ -550,15 +555,13 @@ namespace MWSound { env = Env_Underwater; //play underwater sound - //HACK: this sound is always played underwater, so set volume and pitch higher (it's then lowered) - //Currently not possible to play looping sound with no environment - if(!getSoundPlaying(MWWorld::Ptr(), "Underwater")) - playSound("Underwater", 1.11, 1.42 ,Play_TypeSfx, Play_Loop ); + if(!(mUnderwaterSound && mUnderwaterSound->isPlaying())) + mUnderwaterSound = playSound("Underwater", 1.0f, 1.0f, Play_TypeSfx, Play_LoopNoEnv); } - else + else if(mUnderwaterSound) { - //no need to check if it's playing, stop sound does nothing in that case - stopSound("Underwater"); + mUnderwaterSound->stop(); + mUnderwaterSound.reset(); } mOutput->updateListener( diff --git a/apps/openmw/mwsound/soundmanagerimp.hpp b/apps/openmw/mwsound/soundmanagerimp.hpp index 2450ba5c37..f62e62d503 100644 --- a/apps/openmw/mwsound/soundmanagerimp.hpp +++ b/apps/openmw/mwsound/soundmanagerimp.hpp @@ -44,6 +44,8 @@ namespace MWSound typedef std::map SoundMap; SoundMap mActiveSounds; + MWBase::SoundPtr mUnderwaterSound; + Ogre::Vector3 mListenerPos; Ogre::Vector3 mListenerDir; Ogre::Vector3 mListenerUp;