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 81ef9ee79d..2d923b63df 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 480bcf9cf4..e363749419 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 f5cb12e058..f82fbda2bd 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 a3cab2c93a..c40742a603 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 7924310387..7cf9eaa643 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 a178dc621b..8188946e10 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 fb273c7c13..0d720ecacf 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 1369d783c6..777fdbb9a4 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 7aa347d6ef..cdcede5076 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 50c1278567..69ba1c5cb8 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 75fca0685a..44ad575e40 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 f396157a40..7703470b4c 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 6d8caad219..cb2e00e1f5 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 16b9f2c205..9faa931c61 100644 --- a/files/mygui/openmw_hud.layout +++ b/files/mygui/openmw_hud.layout @@ -32,7 +32,21 @@ - + + + + + + + + + + + + + + +