diff --git a/apps/openmw/mwbase/mechanicsmanager.hpp b/apps/openmw/mwbase/mechanicsmanager.hpp index 39d7e6e1a..750fc2fff 100644 --- a/apps/openmw/mwbase/mechanicsmanager.hpp +++ b/apps/openmw/mwbase/mechanicsmanager.hpp @@ -39,6 +39,8 @@ namespace MWBase virtual void addActor (const MWWorld::Ptr& ptr) = 0; ///< Register an actor for stats management + /// + /// \note Dead actors are ignored. virtual void removeActor (const MWWorld::Ptr& ptr) = 0; ///< Deregister an actor for stats management @@ -74,6 +76,9 @@ namespace MWBase virtual void restoreDynamicStats() = 0; ///< If the player is sleeping, this should be called every hour. + + virtual int countDeaths (const std::string& id) const = 0; + ///< Return the number of deaths for actors with the given ID. }; } diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 0cf348b14..0fb9daf19 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -55,9 +55,9 @@ namespace MWClass data->mCreatureStats.getAttribute(5).set (ref->base->mData.mEndurance); data->mCreatureStats.getAttribute(6).set (ref->base->mData.mPersonality); data->mCreatureStats.getAttribute(7).set (ref->base->mData.mLuck); - data->mCreatureStats.getHealth().set (ref->base->mData.mHealth); - data->mCreatureStats.getMagicka().set (ref->base->mData.mMana); - data->mCreatureStats.getFatigue().set (ref->base->mData.mFatigue); + data->mCreatureStats.setHealth (ref->base->mData.mHealth); + data->mCreatureStats.setMagicka (ref->base->mData.mMana); + data->mCreatureStats.setFatigue (ref->base->mData.mFatigue); data->mCreatureStats.setLevel(ref->base->mData.mLevel); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index 03b5e56aa..bcde31b26 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -89,15 +89,22 @@ namespace MWClass data->mCreatureStats.getAttribute(5).set (ref->base->mNpdt52.mEndurance); data->mCreatureStats.getAttribute(6).set (ref->base->mNpdt52.mPersonality); data->mCreatureStats.getAttribute(7).set (ref->base->mNpdt52.mLuck); - data->mCreatureStats.getHealth().set (ref->base->mNpdt52.mHealth); - data->mCreatureStats.getMagicka().set (ref->base->mNpdt52.mMana); - data->mCreatureStats.getFatigue().set (ref->base->mNpdt52.mFatigue); + data->mCreatureStats.setHealth (ref->base->mNpdt52.mHealth); + data->mCreatureStats.setMagicka (ref->base->mNpdt52.mMana); + data->mCreatureStats.setFatigue (ref->base->mNpdt52.mFatigue); data->mCreatureStats.setLevel(ref->base->mNpdt52.mLevel); } else { /// \todo do something with mNpdt12 maybe:p + for (int i=0; i<8; ++i) + data->mCreatureStats.getAttribute (i).set (10); + + for (int i=0; i<3; ++i) + data->mCreatureStats.setDynamic (i, 10); + + data->mCreatureStats.setLevel (1); } data->mCreatureStats.setHello(ref->base->mAiData.mHello); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index ab332eb96..1c9fefa16 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -24,8 +24,8 @@ namespace MWMechanics { // magic effects adjustMagicEffects (ptr); - calculateCreatureStatModifiers (ptr); calculateDynamicStats (ptr); + calculateCreatureStatModifiers (ptr); // AI CreatureStats& creatureStats = MWWorld::Class::get (ptr).getCreatureStats (ptr); @@ -73,13 +73,17 @@ namespace MWMechanics double magickaFactor = creatureStats.getMagicEffects().get (EffectKey (84)).mMagnitude * 0.1 + 0.5; - creatureStats.getHealth().setBase( - static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); + DynamicStat health = creatureStats.getHealth(); + health.setBase (static_cast (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ()); + creatureStats.setHealth (health); - creatureStats.getMagicka().setBase( - static_cast (intelligence + magickaFactor * intelligence)); - - creatureStats.getFatigue().setBase(strength+willpower+agility+endurance); + DynamicStat magicka = creatureStats.getMagicka(); + magicka.setBase (static_cast (intelligence + magickaFactor * intelligence)); + creatureStats.setMagicka (magicka); + + DynamicStat fatigue = creatureStats.getFatigue(); + fatigue.setBase (strength+willpower+agility+endurance); + creatureStats.setFatigue (fatigue); } void Actors::calculateRestoration (const MWWorld::Ptr& ptr, float duration) @@ -92,8 +96,10 @@ namespace MWMechanics bool stunted = stats.getMagicEffects ().get(MWMechanics::EffectKey(136)).mMagnitude > 0; int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); - stats.getHealth().setCurrent(stats.getHealth ().getCurrent () - + 0.1 * endurance); + + DynamicStat health = stats.getHealth(); + health.setCurrent (health.getCurrent() + 0.1 * endurance); + stats.setHealth (health); const ESMS::ESMStore& store = MWBase::Environment::get().getWorld()->getStore(); @@ -109,13 +115,19 @@ namespace MWMechanics float x = fFatigueReturnBase + fFatigueReturnMult * (1 - normalizedEncumbrance); x *= fEndFatigueMult * endurance; - stats.getFatigue ().setCurrent (stats.getFatigue ().getCurrent () + 3600 * x); - + + DynamicStat fatigue = stats.getFatigue(); + fatigue.setCurrent (fatigue.getCurrent() + 3600 * x); + stats.setFatigue (fatigue); + if (!stunted) { float fRestMagicMult = store.gameSettings.find("fRestMagicMult")->getFloat (); - stats.getMagicka().setCurrent (stats.getMagicka ().getCurrent () - + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified ()); + + DynamicStat magicka = stats.getMagicka(); + magicka.setCurrent (magicka.getCurrent() + + fRestMagicMult * stats.getAttribute(ESM::Attribute::Intelligence).getModified()); + stats.setMagicka (magicka); } } } @@ -137,21 +149,26 @@ namespace MWMechanics // dynamic stats MagicEffects effects = creatureStats.getMagicEffects(); - creatureStats.getHealth().setModifier( - effects.get(EffectKey(80)).mMagnitude - effects.get(EffectKey(18)).mMagnitude); - - creatureStats.getMagicka().setModifier( - effects.get(EffectKey(81)).mMagnitude - effects.get(EffectKey(19)).mMagnitude); - - creatureStats.getFatigue().setModifier( - effects.get(EffectKey(82)).mMagnitude - effects.get(EffectKey(20)).mMagnitude); + + for (int i=0; i<3; ++i) + { + DynamicStat stat = creatureStats.getDynamic (i); + + stat.setModifier ( + effects.get (EffectKey(80+i)).mMagnitude - effects.get (EffectKey(18+i)).mMagnitude); + + creatureStats.setDynamic (i, stat); + } } Actors::Actors() : mDuration (0) {} void Actors::addActor (const MWWorld::Ptr& ptr) { - mActors.insert (ptr); + if (!MWWorld::Class::get (ptr).getCreatureStats (ptr).isDead()) + mActors.insert (ptr); + else + MWBase::Environment::get().getWorld()->playAnimationGroup (ptr, "death1", 2); } void Actors::removeActor (const MWWorld::Ptr& ptr) @@ -184,13 +201,47 @@ namespace MWMechanics { float totalDuration = mDuration; mDuration = 0; + + std::set::iterator iter (mActors.begin()); - for (std::set::iterator iter (mActors.begin()); iter!=mActors.end(); ++iter) + while (iter!=mActors.end()) { - updateActor (*iter, totalDuration); + if (!MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) + { + updateActor (*iter, totalDuration); - if (iter->getTypeName()==typeid (ESM::NPC).name()) - updateNpc (*iter, totalDuration, paused); + if (iter->getTypeName()==typeid (ESM::NPC).name()) + updateNpc (*iter, totalDuration, paused); + } + + if (MWWorld::Class::get (*iter).getCreatureStats (*iter).isDead()) + { + // workaround: always keep player alive for now + // \todo remove workaround, once player death can be handled + if (iter->getRefData().getHandle()=="player") + { + MWMechanics::DynamicStat stat ( + MWWorld::Class::get (*iter).getCreatureStats (*iter).getHealth()); + + if (stat.getModified()<1) + { + stat.setModified (1, 0); + MWWorld::Class::get (*iter).getCreatureStats (*iter).setHealth (stat); + } + + MWWorld::Class::get (*iter).getCreatureStats (*iter).resurrect(); + ++iter; + continue; + } + + ++mDeathCount[MWWorld::Class::get (*iter).getId (*iter)]; + + MWBase::Environment::get().getWorld()->playAnimationGroup (*iter, "death1", 0); + + mActors.erase (iter++); + } + else + ++iter; } } @@ -211,4 +262,14 @@ namespace MWMechanics calculateRestoration (*iter, 3600); } } + + int Actors::countDeaths (const std::string& id) const + { + std::map::const_iterator iter = mDeathCount.find (id); + + if (iter!=mDeathCount.end()) + return iter->second; + + return 0; + } } diff --git a/apps/openmw/mwmechanics/actors.hpp b/apps/openmw/mwmechanics/actors.hpp index 7e5a0ac86..79ae16fc3 100644 --- a/apps/openmw/mwmechanics/actors.hpp +++ b/apps/openmw/mwmechanics/actors.hpp @@ -4,6 +4,7 @@ #include #include #include +#include namespace Ogre { @@ -22,6 +23,7 @@ namespace MWMechanics { std::set mActors; float mDuration; + std::map mDeathCount; void updateNpc (const MWWorld::Ptr& ptr, float duration, bool paused); @@ -40,6 +42,8 @@ namespace MWMechanics void addActor (const MWWorld::Ptr& ptr); ///< Register an actor for stats management + /// + /// \note Dead actors are ignored. void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management @@ -59,6 +63,9 @@ namespace MWMechanics void restoreDynamicStats(); ///< If the player is sleeping, this should be called every hour. + + int countDeaths (const std::string& id) const; + ///< Return the number of deaths for actors with the given ID. }; } diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index fc0523141..1e57ba313 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -10,7 +10,7 @@ namespace MWMechanics { CreatureStats::CreatureStats() - : mLevelHealthBonus(0.f) + : mLevel (0), mHello (0), mFight (0), mFlee (0), mAlarm (0), mLevelHealthBonus(0.f), mDead (false) { } @@ -118,22 +118,7 @@ namespace MWMechanics return mAttributes[index]; } - DynamicStat &CreatureStats::getHealth() - { - return mDynamic[0]; - } - - DynamicStat &CreatureStats::getMagicka() - { - return mDynamic[1]; - } - - DynamicStat &CreatureStats::getFatigue() - { - return mDynamic[2]; - } - - DynamicStat &CreatureStats::getDynamic(int index) + const DynamicStat &CreatureStats::getDynamic(int index) const { if (index < 0 || index > 2) { throw std::runtime_error("dynamic stat index is out of range"); @@ -171,19 +156,30 @@ namespace MWMechanics void CreatureStats::setHealth(const DynamicStat &value) { - mDynamic[0] = value; + setDynamic (0, value); } void CreatureStats::setMagicka(const DynamicStat &value) { - mDynamic[1] = value; + setDynamic (1, value); } void CreatureStats::setFatigue(const DynamicStat &value) { - mDynamic[2] = value; + setDynamic (2, value); } + void CreatureStats::setDynamic (int index, const DynamicStat &value) + { + if (index < 0 || index > 2) + throw std::runtime_error("dynamic stat index is out of range"); + + mDynamic[index] = value; + + if (index==0 && mDynamic[index].getCurrent()<1) + mDead = true; + } + void CreatureStats::setLevel(int level) { mLevel = level; @@ -218,4 +214,21 @@ namespace MWMechanics { mAlarm = value; } + + bool CreatureStats::isDead() const + { + return mDead; + } + + void CreatureStats::resurrect() + { + if (mDead) + { + if (mDynamic[0].getCurrent()<1) + mDynamic[0].setCurrent (1); + + if (mDynamic[0].getCurrent()>=1) + mDead = false; + } + } } diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 7a1e46f56..671dcd439 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -29,8 +29,8 @@ namespace MWMechanics int mFlee; int mAlarm; AiSequence mAiSequence; - float mLevelHealthBonus; + bool mDead; public: CreatureStats(); @@ -43,6 +43,8 @@ namespace MWMechanics const DynamicStat & getFatigue() const; + const DynamicStat & getDynamic (int index) const; + const Spells & getSpells() const; const ActiveSpells & getActiveSpells() const; @@ -59,24 +61,14 @@ namespace MWMechanics int getAlarm() const; - Stat & getAttribute(int index); - DynamicStat & getHealth(); - - DynamicStat & getMagicka(); - - DynamicStat & getFatigue(); - - DynamicStat & getDynamic(int index); - Spells & getSpells(); ActiveSpells & getActiveSpells(); MagicEffects & getMagicEffects(); - void setAttribute(int index, const Stat &value); void setHealth(const DynamicStat &value); @@ -85,6 +77,8 @@ namespace MWMechanics void setFatigue(const DynamicStat &value); + void setDynamic (int index, const DynamicStat &value); + void setSpells(const Spells &spells); void setActiveSpells(const ActiveSpells &active); @@ -111,6 +105,10 @@ namespace MWMechanics // small hack to allow the fact that Health permanently increases by 10% of endurance on each level up void increaseLevelHealthBonus(float value); float getLevelHealthBonus() const; + + bool isDead() const; + + void resurrect(); }; } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 364e65321..873dd9498 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -153,12 +153,14 @@ namespace MWMechanics // forced update and current value adjustments mActors.updateActor (ptr, 0); - creatureStats.getHealth().setCurrent(creatureStats.getHealth().getModified()); - creatureStats.getMagicka().setCurrent(creatureStats.getMagicka().getModified()); - creatureStats.getFatigue().setCurrent(creatureStats.getFatigue().getModified()); + for (int i=0; i<2; ++i) + { + DynamicStat stat = creatureStats.getDynamic (i); + stat.setCurrent (stat.getModified()); + creatureStats.setDynamic (i, stat); + } } - MechanicsManager::MechanicsManager() : mUpdatePlayer (true), mClassSelected (false), mRaceSelected (false) @@ -324,4 +326,9 @@ namespace MWMechanics buildPlayer(); mUpdatePlayer = true; } + + int MechanicsManager::countDeaths (const std::string& id) const + { + return mActors.countDeaths (id); + } } diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp index 3a41e8fa6..38536d3bd 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.hpp @@ -41,6 +41,8 @@ namespace MWMechanics virtual void addActor (const MWWorld::Ptr& ptr); ///< Register an actor for stats management + /// + /// \note Dead actors are ignored. virtual void removeActor (const MWWorld::Ptr& ptr); ///< Deregister an actor for stats management @@ -76,6 +78,10 @@ namespace MWMechanics virtual void restoreDynamicStats(); ///< If the player is sleeping, this should be called every hour. + + virtual int countDeaths (const std::string& id) const; + ///< Return the number of deaths for actors with the given ID. + }; } diff --git a/apps/openmw/mwscript/docs/vmformat.txt b/apps/openmw/mwscript/docs/vmformat.txt index 584a29926..cb984e257 100644 --- a/apps/openmw/mwscript/docs/vmformat.txt +++ b/apps/openmw/mwscript/docs/vmformat.txt @@ -206,5 +206,6 @@ op 0x200019f: GetPcSleep op 0x20001a0: ShowMap op 0x20001a1: FillMap op 0x20001a2: WakeUpPc -opcodes 0x20001a3-0x3ffffff unused +op 0x20001a3: GetDeadCount +opcodes 0x20001a4-0x3ffffff unused diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 66a5acae0..0d69608e1 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -17,6 +17,7 @@ #include "../mwbase/environment.hpp" #include "../mwbase/dialoguemanager.hpp" +#include "../mwbase/mechanicsmanager.hpp" #include "../mwworld/class.hpp" #include "../mwworld/player.hpp" @@ -155,7 +156,7 @@ namespace MWScript virtual void execute (Interpreter::Runtime& runtime) { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value; + Interpreter::Type_Float value; if (mIndex==0 && MWWorld::Class::get (ptr).hasItemHealth (ptr)) { @@ -185,13 +186,15 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); - MWWorld::Class::get(ptr) - .getCreatureStats(ptr) - .getDynamic(mIndex) - .setModified(value, 0); + MWMechanics::DynamicStat stat (MWWorld::Class::get (ptr).getCreatureStats (ptr) + .getDynamic (mIndex)); + + stat.setModified (value, 0); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).setDynamic (mIndex, stat); } }; @@ -208,17 +211,21 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer diff = runtime[0].mInteger; + Interpreter::Type_Float diff = runtime[0].mFloat; runtime.pop(); MWMechanics::CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - Interpreter::Type_Integer current = stats.getDynamic(mIndex).getCurrent(); + Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent(); - stats.getDynamic(mIndex).setModified( - diff + stats.getDynamic(mIndex).getModified(), 0); + MWMechanics::DynamicStat stat (MWWorld::Class::get (ptr).getCreatureStats (ptr) + .getDynamic (mIndex)); - stats.getDynamic(mIndex).setCurrent(diff + current); + stat.setModified (diff + stat.getModified(), 0); + + stat.setCurrent (diff + current); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).setDynamic (mIndex, stat); } }; @@ -235,14 +242,19 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer diff = runtime[0].mInteger; + Interpreter::Type_Float diff = runtime[0].mFloat; runtime.pop(); MWMechanics::CreatureStats& stats = MWWorld::Class::get (ptr).getCreatureStats (ptr); - Interpreter::Type_Integer current = stats.getDynamic(mIndex).getCurrent(); + Interpreter::Type_Float current = stats.getDynamic(mIndex).getCurrent(); - stats.getDynamic(mIndex).setCurrent (diff + current); + MWMechanics::DynamicStat stat (MWWorld::Class::get (ptr).getCreatureStats (ptr) + .getDynamic (mIndex)); + + stat.setCurrent (diff + current); + + MWWorld::Class::get (ptr).getCreatureStats (ptr).setDynamic (mIndex, stat); } }; @@ -580,6 +592,17 @@ namespace MWScript /// \todo modify disposition towards the player } }; + + class OpGetDeadCount : public Interpreter::Opcode0 + { + public: + + virtual void execute (Interpreter::Runtime& runtime) + { + std::string id = runtime.getStringLiteral (runtime[0].mInteger); + runtime[0].mInteger = MWBase::Environment::get().getMechanicsManager()->countDeaths (id); + } + }; const int numberOfAttributes = 8; @@ -632,6 +655,8 @@ namespace MWScript const int opcodeGetLevelExplicit = 0x200018d; const int opcodeSetLevel = 0x200018e; const int opcodeSetLevelExplicit = 0x200018f; + + const int opcodeGetDeadCount = 0x20001a3; void registerExtensions (Compiler::Extensions& extensions) { @@ -676,16 +701,16 @@ namespace MWScript for (int i=0; i); interpreter.installSegment5 (opcodeSetLevelExplicit, new OpSetLevel); + interpreter.installSegment5 (opcodeGetDeadCount, new OpGetDeadCount); } } }