From 8f4506f5b6e5d63350697157f6229a213fdec21b Mon Sep 17 00:00:00 2001 From: PLkolek Date: Wed, 7 Aug 2013 15:34:11 +0200 Subject: [PATCH] Implemented drowning. Currently no visual effects on losing health, the breathing sound doesn't change (we don't have one), the breath bar doesn't turn red when no breath left and it doesn't pulse from black to red. --- apps/openmw/mwbase/windowmanager.hpp | 7 ++++ apps/openmw/mwbase/world.hpp | 2 ++ apps/openmw/mwgui/hud.cpp | 17 ++++++++++ apps/openmw/mwgui/hud.hpp | 8 ++++- apps/openmw/mwgui/windowmanagerimp.cpp | 10 ++++++ apps/openmw/mwgui/windowmanagerimp.hpp | 7 ++++ apps/openmw/mwmechanics/actors.cpp | 34 +++++++++++++++++++ apps/openmw/mwmechanics/actors.hpp | 1 + .../mwmechanics/mechanicsmanagerimp.cpp | 14 ++++++++ apps/openmw/mwmechanics/npcstats.cpp | 22 ++++++++++++ apps/openmw/mwmechanics/npcstats.hpp | 15 ++++++++ apps/openmw/mwworld/worldimp.cpp | 11 ++++++ apps/openmw/mwworld/worldimp.hpp | 2 ++ files/mygui/openmw_hud.layout | 16 ++++++++- 14 files changed, 164 insertions(+), 2 deletions(-) diff --git a/apps/openmw/mwbase/windowmanager.hpp b/apps/openmw/mwbase/windowmanager.hpp index 81ef9ee79..2d923b63d 100644 --- a/apps/openmw/mwbase/windowmanager.hpp +++ b/apps/openmw/mwbase/windowmanager.hpp @@ -138,6 +138,10 @@ namespace MWBase virtual void setValue (const std::string& id, const std::string& value) = 0; virtual void setValue (const std::string& id, int value) = 0; + /// Set time left for the player to start drowning (update the drowning bar) + /// @param time value from [0,20] + virtual void setDrowningTimeLeft (float time) =0; + virtual void setPlayerClass (const ESM::Class &class_) = 0; ///< set current class of player @@ -181,6 +185,9 @@ namespace MWBase virtual void setInteriorMapTexture(const int x, const int y) = 0; ///< set the index of the map texture that should be used (for interiors) + /// sets the visibility of the drowning bar + virtual void setDrowningBarVisibility(bool visible) = 0; + /// sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible) = 0; diff --git a/apps/openmw/mwbase/world.hpp b/apps/openmw/mwbase/world.hpp index 480bcf9cf..e36374941 100644 --- a/apps/openmw/mwbase/world.hpp +++ b/apps/openmw/mwbase/world.hpp @@ -323,6 +323,8 @@ namespace MWBase virtual bool isFlying(const MWWorld::Ptr &ptr) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; + ///Is the head of the creature underwater? + virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; diff --git a/apps/openmw/mwgui/hud.cpp b/apps/openmw/mwgui/hud.cpp index f5cb12e05..f82fbda2b 100644 --- a/apps/openmw/mwgui/hud.cpp +++ b/apps/openmw/mwgui/hud.cpp @@ -25,6 +25,8 @@ namespace MWGui , mHealth(NULL) , mMagicka(NULL) , mStamina(NULL) + , mDrowning(NULL) + , mDrowningFrame(NULL) , mWeapImage(NULL) , mSpellImage(NULL) , mWeapStatus(NULL) @@ -69,6 +71,11 @@ namespace MWGui magickaFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); fatigueFrame->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onHMSClicked); + //Drowning bar + getWidget(mDrowningFrame, "DrowningFrame"); + getWidget(mDrowning, "Drowning"); + mDrowning->setProgressRange(200); + const MyGUI::IntSize& viewSize = MyGUI::RenderManager::getInstance().getViewSize(); // Item and spell images and status bars @@ -197,6 +204,16 @@ namespace MWGui } } + void HUD::setDrowningTimeLeft(float time) + { + mDrowning->setProgressPosition(time/20.0*200.0); + } + + void HUD::setDrowningBarVisible(bool visible) + { + mDrowningFrame->setVisible(visible); + } + void HUD::onWorldClicked(MyGUI::Widget* _sender) { if (!MWBase::Environment::get().getWindowManager ()->isGuiMode ()) diff --git a/apps/openmw/mwgui/hud.hpp b/apps/openmw/mwgui/hud.hpp index a3cab2c93..c40742a60 100644 --- a/apps/openmw/mwgui/hud.hpp +++ b/apps/openmw/mwgui/hud.hpp @@ -18,6 +18,11 @@ namespace MWGui void setTriangleCount(unsigned int count); void setBatchCount(unsigned int count); + /// Set time left for the player to start drowning + /// @param time value from [0,20] + void setDrowningTimeLeft(float time); + void setDrowningBarVisible(bool visible); + void setHmsVisible(bool visible); void setWeapVisible(bool visible); void setSpellVisible(bool visible); @@ -50,7 +55,7 @@ namespace MWGui void setEnemy(const MWWorld::Ptr& enemy); private: - MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth; + MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::Widget* mHealthFrame; MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox; MyGUI::ImageBox *mWeapImage, *mSpellImage; @@ -62,6 +67,7 @@ namespace MWGui MyGUI::ImageBox* mCrosshair; MyGUI::TextBox* mCellNameBox; MyGUI::TextBox* mWeaponSpellBox; + MyGUI::Widget* mDrowningFrame; MyGUI::Widget* mDummy; diff --git a/apps/openmw/mwgui/windowmanagerimp.cpp b/apps/openmw/mwgui/windowmanagerimp.cpp index 792431038..7cf9eaa64 100644 --- a/apps/openmw/mwgui/windowmanagerimp.cpp +++ b/apps/openmw/mwgui/windowmanagerimp.cpp @@ -599,6 +599,11 @@ namespace MWGui mStatsWindow->setValue (id, value); } + void WindowManager::setDrowningTimeLeft (float time) + { + mHud->setDrowningTimeLeft(time); + } + void WindowManager::setPlayerClass (const ESM::Class &class_) { mStatsWindow->setValue("class", class_.mName); @@ -787,6 +792,11 @@ namespace MWGui mHud->setPlayerDir(x,y); } + void WindowManager::setDrowningBarVisibility(bool visible) + { + mHud->setDrowningBarVisible(visible); + } + void WindowManager::setHMSVisibility(bool visible) { mHud->setHmsVisible (visible); diff --git a/apps/openmw/mwgui/windowmanagerimp.hpp b/apps/openmw/mwgui/windowmanagerimp.hpp index a178dc621..8188946e1 100644 --- a/apps/openmw/mwgui/windowmanagerimp.hpp +++ b/apps/openmw/mwgui/windowmanagerimp.hpp @@ -148,6 +148,10 @@ namespace MWGui virtual void setValue (const std::string& id, const std::string& value); virtual void setValue (const std::string& id, int value); + /// Set time left for the player to start drowning (update the drowning bar) + /// @param time value from [0,20] + virtual void setDrowningTimeLeft (float time); + virtual void setPlayerClass (const ESM::Class &class_); ///< set current class of player virtual void configureSkills (const SkillList& major, const SkillList& minor); ///< configure skill groups, each set contains the skill ID for that group. virtual void setReputation (int reputation); ///< set the current reputation value @@ -173,6 +177,9 @@ namespace MWGui virtual void setInteriorMapTexture(const int x, const int y); ///< set the index of the map texture that should be used (for interiors) + /// sets the visibility of the drowning bar + virtual void setDrowningBarVisibility(bool visible); + // sets the visibility of the hud health/magicka/stamina bars virtual void setHMSVisibility(bool visible); // sets the visibility of the hud minimap diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index fb273c7c1..0d720ecac 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -16,6 +16,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/windowmanager.hpp" +#include "npcstats.hpp" #include "creaturestats.hpp" #include "movement.hpp" @@ -40,6 +41,8 @@ namespace MWMechanics { if (!paused && ptr.getRefData().getHandle()!="player") MWWorld::Class::get (ptr).getInventoryStore (ptr).autoEquip (ptr); + if(!paused) + updateDrowning(ptr,duration); } void Actors::adjustMagicEffects (const MWWorld::Ptr& creature) @@ -159,6 +162,37 @@ namespace MWMechanics } } + void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration) + { + Ogre::Vector3 pos(ptr.getRefData().getPosition().pos); + CreatureStats& creatureStats=MWWorld::Class::get(ptr).getCreatureStats(ptr); + NpcStats& stats=MWWorld::Class::get(ptr).getNpcStats(ptr); + bool waterBreathing=creatureStats.getMagicEffects().get(ESM::MagicEffect::WaterBreathing).mMagnitude>0; + if(MWBase::Environment::get().getWorld()->isSubmerged(ptr) && !waterBreathing) + { + if(creatureStats.getFatigue().getCurrent()==0) + stats.setTimeToStartDrowning(0); + float timeLeft=stats.getTimeToStartDrowning()-duration; + if(timeLeft<0) + timeLeft=0; + stats.setTimeToStartDrowning(timeLeft); + if(timeLeft==0) + stats.setLastDrowningHitTime(stats.getLastDrowningHitTime()+duration); + } + else + { + stats.setTimeToStartDrowning(20); + stats.setLastDrowningHitTime(0); + } + //if npc is drowning and it's time to hit, then hit + while(stats.getTimeToStartDrowning()==0.0 && stats.getLastDrowningHitTime()>0.33) + { + stats.setLastDrowningHitTime(stats.getLastDrowningHitTime()-0.33); + //fixme: replace it with something different once screen hit effects are implemented (blood on screen) + MWWorld::Class::get(ptr).setActorHealth(ptr, creatureStats.getHealth().getCurrent()-1.0); + } + } + Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 1369d783c..777fdbb9a 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -44,6 +44,7 @@ namespace MWMechanics void calculateRestoration (const MWWorld::Ptr& ptr, float duration); + void updateDrowning (const MWWorld::Ptr& ptr, float duration); public: diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7aa347d6e..cdcede507 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -254,6 +254,20 @@ namespace MWMechanics MWBase::Environment::get().getWindowManager()->setValue(dynamicNames[2], stats.getFatigue()); } + if(npcStats.getTimeToStartDrowning() != mWatchedNpc.getTimeToStartDrowning()) + { + mWatchedNpc.setTimeToStartDrowning(npcStats.getTimeToStartDrowning()); + if(npcStats.getTimeToStartDrowning()>=20.0) + { + MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(false); + } + else + { + MWBase::Environment::get().getWindowManager()->setDrowningBarVisibility(true); + MWBase::Environment::get().getWindowManager()->setDrowningTimeLeft(npcStats.getTimeToStartDrowning()); + } + } + bool update = false; //Loop over ESM::Skill::SkillEnum diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index 50c127856..69ba1c5cb 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -32,6 +32,8 @@ MWMechanics::NpcStats::NpcStats() , mWerewolfKills (0) , mProfit(0) , mAttackStrength(0.0f) +, mTimeToStartDrowning(20.0) +, mLastDrowningHit(0) { mSkillIncreases.resize (ESM::Attribute::Length); for (int i=0; i=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 75fca0685..44ad575e4 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -62,6 +62,11 @@ namespace MWMechanics std::set mUsedIds; + /// Countdown to getting damage while underwater + float mTimeToStartDrowning; + /// time since last hit from drowning + float mLastDrowningHit; + public: NpcStats(); @@ -142,6 +147,16 @@ namespace MWMechanics void setWerewolf (bool set); int getWerewolfKills() const; + + float getTimeToStartDrowning(); + /// 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/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index f396157a4..7703470b4 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -1566,6 +1566,17 @@ namespace MWWorld return false; } + bool World::isSubmerged(const MWWorld::Ptr &object) const + { + float *fpos = object.getRefData().getPosition().pos; + Ogre::Vector3 pos(fpos[0], fpos[1], fpos[2]); + + const OEngine::Physic::PhysicActor *actor = mPhysEngine->getCharacter(object.getRefData().getHandle()); + if(actor) pos.z += 1.85*actor->getHalfExtents().z; + + return isUnderwater(object.getCell(), pos); + } + bool World::isSwimming(const MWWorld::Ptr &object) const { diff --git a/apps/openmw/mwworld/worldimp.hpp b/apps/openmw/mwworld/worldimp.hpp index 6d8caad21..cb2e00e1f 100644 --- a/apps/openmw/mwworld/worldimp.hpp +++ b/apps/openmw/mwworld/worldimp.hpp @@ -348,6 +348,8 @@ namespace MWWorld virtual void processChangedSettings(const Settings::CategorySettingVector& settings); virtual bool isFlying(const MWWorld::Ptr &ptr) const; + ///Is the head of the creature underwater? + virtual bool isSubmerged(const MWWorld::Ptr &object) const; virtual bool isSwimming(const MWWorld::Ptr &object) const; virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const; virtual bool isOnGround(const MWWorld::Ptr &ptr) const; diff --git a/files/mygui/openmw_hud.layout b/files/mygui/openmw_hud.layout index 16b9f2c20..9faa931c6 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -32,7 +32,21 @@ - + + + + + + + + + + + + + + +