From 5468fcb29f0636bd82b56b4bb1f9a8a72f059098 Mon Sep 17 00:00:00 2001 From: Andrei Kortunov Date: Sun, 23 Dec 2018 15:18:33 +0400 Subject: [PATCH] Store attributes and skills values as floats (bug #4021) --- CHANGELOG.md | 1 + apps/openmw/mwclass/armor.cpp | 2 +- apps/openmw/mwclass/creature.cpp | 4 +- apps/openmw/mwclass/creature.hpp | 2 +- apps/openmw/mwclass/ingredient.cpp | 2 +- apps/openmw/mwclass/npc.cpp | 10 ++--- apps/openmw/mwclass/npc.hpp | 2 +- apps/openmw/mwgui/jailscreen.cpp | 4 +- apps/openmw/mwgui/levelupdialog.cpp | 2 +- apps/openmw/mwgui/pickpocketitemmodel.cpp | 2 +- apps/openmw/mwgui/statswindow.cpp | 2 +- apps/openmw/mwgui/trainingwindow.cpp | 4 +- apps/openmw/mwmechanics/actors.cpp | 8 ++-- apps/openmw/mwmechanics/alchemy.cpp | 2 +- apps/openmw/mwmechanics/character.cpp | 2 +- apps/openmw/mwmechanics/combat.cpp | 2 +- apps/openmw/mwmechanics/creaturestats.cpp | 10 ++--- apps/openmw/mwmechanics/creaturestats.hpp | 2 +- .../mwmechanics/mechanicsmanagerimp.cpp | 14 +++--- apps/openmw/mwmechanics/npcstats.cpp | 10 ++--- apps/openmw/mwmechanics/pickpocket.cpp | 4 +- apps/openmw/mwmechanics/repair.cpp | 6 +-- apps/openmw/mwmechanics/security.cpp | 4 +- apps/openmw/mwmechanics/spellresistance.cpp | 4 +- apps/openmw/mwmechanics/spellutil.cpp | 4 +- apps/openmw/mwmechanics/stat.cpp | 22 ++++----- apps/openmw/mwmechanics/stat.hpp | 22 ++++----- apps/openmw/mwscript/statsextensions.cpp | 24 +++++----- apps/openmw/mwworld/class.cpp | 2 +- apps/openmw/mwworld/class.hpp | 2 +- apps/openmw/mwworld/inventorystore.cpp | 6 +-- components/compiler/extensions0.cpp | 12 ++--- components/esm/creaturestats.cpp | 3 +- components/esm/creaturestats.hpp | 2 +- components/esm/npcstats.cpp | 13 +++--- components/esm/npcstats.hpp | 2 +- components/esm/player.cpp | 5 ++- components/esm/player.hpp | 4 +- components/esm/savedgame.cpp | 2 +- components/esm/statstate.cpp | 45 ++++++++++++++----- components/esm/statstate.hpp | 2 +- 41 files changed, 153 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c48660848..298220699 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ Bug #2311: Targeted scripts are not properly supported on non-unique RefIDs Bug #3676: NiParticleColorModifier isn't applied properly Bug #3714: Savegame fails to load due to conflict between SpellState and MagicEffects + Bug #4021: Attributes and skills are not stored as floats Bug #4623: Corprus implementation is incorrect Bug #4774: Guards are ignorant of an invisible player that tries to attack them Bug #5108: Savegame bloating due to inefficient fog textures format diff --git a/apps/openmw/mwclass/armor.cpp b/apps/openmw/mwclass/armor.cpp index e649bba12..3f9bfb859 100644 --- a/apps/openmw/mwclass/armor.cpp +++ b/apps/openmw/mwclass/armor.cpp @@ -276,7 +276,7 @@ namespace MWClass const MWWorld::LiveCellRef *ref = ptr.get(); int armorSkillType = getEquipmentSkill(ptr); - int armorSkill = actor.getClass().getSkill(actor, armorSkillType); + float armorSkill = actor.getClass().getSkill(actor, armorSkillType); const MWBase::World *world = MWBase::Environment::get().getWorld(); int iBaseArmorSkill = world->getStore().get().find("iBaseArmorSkill")->mValue.getInteger(); diff --git a/apps/openmw/mwclass/creature.cpp b/apps/openmw/mwclass/creature.cpp index 375b70dde..3feb25ca7 100644 --- a/apps/openmw/mwclass/creature.cpp +++ b/apps/openmw/mwclass/creature.cpp @@ -605,7 +605,7 @@ namespace MWClass float Creature::getCapacity (const MWWorld::Ptr& ptr) const { const MWMechanics::CreatureStats& stats = getCreatureStats (ptr); - return static_cast(stats.getAttribute(ESM::Attribute::Strength).getModified() * 5); + return stats.getAttribute(ESM::Attribute::Strength).getModified() * 5; } int Creature::getServices(const MWWorld::ConstPtr &actor) const @@ -745,7 +745,7 @@ namespace MWClass throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); } - int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const + float Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const { MWWorld::LiveCellRef *ref = ptr.get(); diff --git a/apps/openmw/mwclass/creature.hpp b/apps/openmw/mwclass/creature.hpp index 9be5c4272..3288c0d11 100644 --- a/apps/openmw/mwclass/creature.hpp +++ b/apps/openmw/mwclass/creature.hpp @@ -108,7 +108,7 @@ namespace MWClass virtual bool canSwim (const MWWorld::ConstPtr &ptr) const; virtual bool canWalk (const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr &ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwclass/ingredient.cpp b/apps/openmw/mwclass/ingredient.cpp index bd61131bf..a007ad115 100644 --- a/apps/openmw/mwclass/ingredient.cpp +++ b/apps/openmw/mwclass/ingredient.cpp @@ -125,7 +125,7 @@ namespace MWClass } MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr(); - int alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); + float alchemySkill = player.getClass().getSkill(player, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index df483962a..5efc96ca1 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -127,8 +127,8 @@ namespace } // initial health - int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); - int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); + float strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); + float endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase(); int multiplier = 3; @@ -1011,7 +1011,7 @@ namespace MWClass gmst.fJumpEncumbranceMultiplier->mValue.getFloat() * (1.0f - Npc::getNormalizedEncumbrance(ptr)); - float a = static_cast(getSkill(ptr, ESM::Skill::Acrobatics)); + float a = getSkill(ptr, ESM::Skill::Acrobatics); float b = 0.0f; if(a > 50.0f) { @@ -1136,7 +1136,7 @@ namespace MWClass float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); + float unarmoredSkill = getSkill(ptr, ESM::Skill::Unarmored); float ratings[MWWorld::InventoryStore::Slots]; for(int i = 0;i < MWWorld::InventoryStore::Slots;i++) @@ -1283,7 +1283,7 @@ namespace MWClass return MWWorld::Ptr(cell.insert(ref), &cell); } - int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const { return getNpcStats(ptr).getSkill(skill).getModified(); } diff --git a/apps/openmw/mwclass/npc.hpp b/apps/openmw/mwclass/npc.hpp index 3d63697fb..ae4f32d13 100644 --- a/apps/openmw/mwclass/npc.hpp +++ b/apps/openmw/mwclass/npc.hpp @@ -129,7 +129,7 @@ namespace MWClass virtual std::string getModel(const MWWorld::ConstPtr &ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; /// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini) virtual int getBloodTexture (const MWWorld::ConstPtr& ptr) const; diff --git a/apps/openmw/mwgui/jailscreen.cpp b/apps/openmw/mwgui/jailscreen.cpp index 4a89304b3..67124884f 100644 --- a/apps/openmw/mwgui/jailscreen.cpp +++ b/apps/openmw/mwgui/jailscreen.cpp @@ -95,9 +95,9 @@ namespace MWGui MWMechanics::SkillValue& value = player.getClass().getNpcStats(player).getSkill(skill); if (skill == ESM::Skill::Security || skill == ESM::Skill::Sneak) - value.setBase(std::min(100, value.getBase()+1)); + value.setBase(std::min(100.f, value.getBase()+1)); else - value.setBase(std::max(0, value.getBase()-1)); + value.setBase(std::max(0.f, value.getBase()-1)); } const MWWorld::Store& gmst = MWBase::Environment::get().getWorld()->getStore().get(); diff --git a/apps/openmw/mwgui/levelupdialog.cpp b/apps/openmw/mwgui/levelupdialog.cpp index eb165254a..86f92db6f 100644 --- a/apps/openmw/mwgui/levelupdialog.cpp +++ b/apps/openmw/mwgui/levelupdialog.cpp @@ -157,7 +157,7 @@ namespace MWGui mAttributeValues[i]->setEnabled(true); availableAttributes++; - int mult = pcStats.getLevelupAttributeMultiplier (i); + float mult = pcStats.getLevelupAttributeMultiplier (i); mult = std::min(mult, 100-pcStats.getAttribute(i).getBase()); text->setCaption(mult <= 1 ? "" : "x" + MyGUI::utility::toString(mult)); } diff --git a/apps/openmw/mwgui/pickpocketitemmodel.cpp b/apps/openmw/mwgui/pickpocketitemmodel.cpp index 2a167be2d..b4de5cb50 100644 --- a/apps/openmw/mwgui/pickpocketitemmodel.cpp +++ b/apps/openmw/mwgui/pickpocketitemmodel.cpp @@ -22,7 +22,7 @@ namespace MWGui { MWWorld::Ptr player = MWMechanics::getPlayer(); mSourceModel = sourceModel; - int chance = player.getClass().getSkill(player, ESM::Skill::Sneak); + float chance = player.getClass().getSkill(player, ESM::Skill::Sneak); mSourceModel->update(); diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 8998e3a80..4959396cc 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -159,7 +159,7 @@ namespace MWGui for (int i=0; ids[i]; ++i) if (ids[i]==id) { - setText (id, std::to_string(value.getModified())); + setText (id, std::to_string(static_cast(value.getModified()))); MyGUI::TextBox* box; getWidget(box, id); diff --git a/apps/openmw/mwgui/trainingwindow.cpp b/apps/openmw/mwgui/trainingwindow.cpp index acc2ef72a..e4e4bae5a 100644 --- a/apps/openmw/mwgui/trainingwindow.cpp +++ b/apps/openmw/mwgui/trainingwindow.cpp @@ -74,11 +74,11 @@ namespace MWGui mPlayerGold->setCaptionWithReplacing("#{sGold}: " + MyGUI::utility::toString(playerGold)); // NPC can train you in his best 3 skills - std::vector< std::pair > skills; + std::vector< std::pair > skills; for (int i=0; i& settings = MWBase::Environment::get().getWorld()->getStore().get(); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); health = 0.1f * endurance; float fRestMagicMult = settings.find("fRestMagicMult")->mValue.getFloat (); @@ -765,7 +765,7 @@ namespace MWMechanics { CreatureStats& creatureStats = ptr.getClass().getCreatureStats (ptr); - int intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); + float intelligence = creatureStats.getAttribute(ESM::Attribute::Intelligence).getModified(); float base = 1.f; if (ptr == getPlayer()) @@ -844,7 +844,7 @@ namespace MWMechanics float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); float fEndFatigueMult = settings.find("fEndFatigueMult")->mValue.getFloat (); - int endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); + float endurance = stats.getAttribute (ESM::Attribute::Endurance).getModified (); float normalizedEncumbrance = ptr.getClass().getNormalizedEncumbrance(ptr); if (normalizedEncumbrance > 1) @@ -871,7 +871,7 @@ namespace MWMechanics return; // Restore fatigue - int endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); + float endurance = stats.getAttribute(ESM::Attribute::Endurance).getModified(); const MWWorld::Store& settings = MWBase::Environment::get().getWorld()->getStore().get(); static const float fFatigueReturnBase = settings.find("fFatigueReturnBase")->mValue.getFloat (); static const float fFatigueReturnMult = settings.find("fFatigueReturnMult")->mValue.getFloat (); diff --git a/apps/openmw/mwmechanics/alchemy.cpp b/apps/openmw/mwmechanics/alchemy.cpp index b490db436..116937fcd 100644 --- a/apps/openmw/mwmechanics/alchemy.cpp +++ b/apps/openmw/mwmechanics/alchemy.cpp @@ -468,7 +468,7 @@ MWMechanics::Alchemy::TEffectsIterator MWMechanics::Alchemy::endEffects() const bool MWMechanics::Alchemy::knownEffect(unsigned int potionEffectIndex, const MWWorld::Ptr &npc) { - int alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); + float alchemySkill = npc.getClass().getSkill (npc, ESM::Skill::Alchemy); static const float fWortChanceValue = MWBase::Environment::get().getWorld()->getStore().get().find("fWortChanceValue")->mValue.getFloat(); return (potionEffectIndex <= 1 && alchemySkill >= fWortChanceValue) diff --git a/apps/openmw/mwmechanics/character.cpp b/apps/openmw/mwmechanics/character.cpp index 0a332a10c..e4f870ed0 100644 --- a/apps/openmw/mwmechanics/character.cpp +++ b/apps/openmw/mwmechanics/character.cpp @@ -2128,7 +2128,7 @@ void CharacterController::update(float duration, bool animationOnly) cls.onHit(mPtr, realHealthLost, true, MWWorld::Ptr(), MWWorld::Ptr(), osg::Vec3f(), true); } - const int acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); + const float acrobaticsSkill = cls.getSkill(mPtr, ESM::Skill::Acrobatics); if (healthLost > (acrobaticsSkill * fatigueTerm)) { if (!godmode) diff --git a/apps/openmw/mwmechanics/combat.cpp b/apps/openmw/mwmechanics/combat.cpp index 9698892e4..183845b8c 100644 --- a/apps/openmw/mwmechanics/combat.cpp +++ b/apps/openmw/mwmechanics/combat.cpp @@ -101,7 +101,7 @@ namespace MWMechanics blockerTerm *= gmst.find("fBlockStillBonus")->mValue.getFloat(); blockerTerm *= blockerStats.getFatigueTerm(); - int attackerSkill = 0; + float attackerSkill = 0; if (weapon.isEmpty()) attackerSkill = attacker.getClass().getSkill(attacker, ESM::Skill::HandToHand); else diff --git a/apps/openmw/mwmechanics/creaturestats.cpp b/apps/openmw/mwmechanics/creaturestats.cpp index a64fb087c..0f11b8b2e 100644 --- a/apps/openmw/mwmechanics/creaturestats.cpp +++ b/apps/openmw/mwmechanics/creaturestats.cpp @@ -126,7 +126,7 @@ namespace MWMechanics return mMagicEffects; } - void CreatureStats::setAttribute(int index, int base) + void CreatureStats::setAttribute(int index, float base) { AttributeValue current = getAttribute(index); current.setBase(base); @@ -152,10 +152,10 @@ namespace MWMechanics index == ESM::Attribute::Agility || index == ESM::Attribute::Endurance) { - int strength = getAttribute(ESM::Attribute::Strength).getModified(); - int willpower = getAttribute(ESM::Attribute::Willpower).getModified(); - int agility = getAttribute(ESM::Attribute::Agility).getModified(); - int endurance = getAttribute(ESM::Attribute::Endurance).getModified(); + float strength = getAttribute(ESM::Attribute::Strength).getModified(); + float willpower = getAttribute(ESM::Attribute::Willpower).getModified(); + float agility = getAttribute(ESM::Attribute::Agility).getModified(); + float endurance = getAttribute(ESM::Attribute::Endurance).getModified(); DynamicStat fatigue = getFatigue(); float diff = (strength+willpower+agility+endurance) - fatigue.getBase(); float currentToBaseRatio = fatigue.getBase() > 0 ? (fatigue.getCurrent() / fatigue.getBase()) : 0; diff --git a/apps/openmw/mwmechanics/creaturestats.hpp b/apps/openmw/mwmechanics/creaturestats.hpp index 5a5dd3f12..b35c1e3b6 100644 --- a/apps/openmw/mwmechanics/creaturestats.hpp +++ b/apps/openmw/mwmechanics/creaturestats.hpp @@ -138,7 +138,7 @@ namespace MWMechanics void setAttribute(int index, const AttributeValue &value); // Shortcut to set only the base - void setAttribute(int index, int base); + void setAttribute(int index, float base); void setHealth(const DynamicStat &value); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 5376ec86e..6933907db 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -688,10 +688,10 @@ namespace MWMechanics // I suppose the temporary disposition change (second param to getDerivedDisposition()) _has_ to be considered here, // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = getDerivedDisposition(ptr); - float a = static_cast(std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100)); + float a = std::min(playerPtr.getClass().getSkill(playerPtr, ESM::Skill::Mercantile), 100.f); float b = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d = static_cast(std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100)); + float d = std::min(ptr.getClass().getSkill(ptr, ESM::Skill::Mercantile), 100.f); float e = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); float pcTerm = (clampedDisposition - 50 + a + b + c) * playerStats.getFatigueTerm(); @@ -1621,8 +1621,8 @@ namespace MWMechanics static float fSneakSkillMult = store.find("fSneakSkillMult")->mValue.getFloat(); static float fSneakBootMult = store.find("fSneakBootMult")->mValue.getFloat(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); - int agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); - int luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float bootWeight = 0; if (ptr.getClass().isNpc() && MWBase::Environment::get().getWorld()->isOnGround(ptr)) { @@ -1645,10 +1645,10 @@ namespace MWMechanics float x = sneakTerm * distTerm * stats.getFatigueTerm() + chameleon + invisibility; CreatureStats& observerStats = observer.getClass().getCreatureStats(observer); - int obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); - int obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); + float obsAgility = observerStats.getAttribute(ESM::Attribute::Agility).getModified(); + float obsLuck = observerStats.getAttribute(ESM::Attribute::Luck).getModified(); float obsBlind = observerStats.getMagicEffects().get(ESM::MagicEffect::Blind).getMagnitude(); - int obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); + float obsSneak = observer.getClass().getSkill(observer, ESM::Skill::Sneak); float obsTerm = obsSneak + 0.2f * obsAgility + 0.1f * obsLuck - obsBlind; diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index ee48ea7d5..1e9003a2f 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -226,9 +226,9 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress, bool readBook) { - int base = getSkill (skillIndex).getBase(); + float base = getSkill (skillIndex).getBase(); - if (base >= 100) + if (base >= 100.f) return; base += 1; @@ -299,7 +299,7 @@ void MWMechanics::NpcStats::levelUp() for (int i=0; i(stats.getAttribute(ESM::Attribute::Agility).getModified()); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float agility = stats.getAttribute(ESM::Attribute::Agility).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float sneak = static_cast(ptr.getClass().getSkill(ptr, ESM::Skill::Sneak)); return (add + 0.2f * agility + 0.1f * luck + sneak) * stats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/repair.cpp b/apps/openmw/mwmechanics/repair.cpp index faa0e3b09..389d00d85 100644 --- a/apps/openmw/mwmechanics/repair.cpp +++ b/apps/openmw/mwmechanics/repair.cpp @@ -32,9 +32,9 @@ void Repair::repair(const MWWorld::Ptr &itemToRepair) MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); float fatigueTerm = stats.getFatigueTerm(); - int pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); - int pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); - int armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); + float pcStrength = stats.getAttribute(ESM::Attribute::Strength).getModified(); + float pcLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float armorerSkill = player.getClass().getSkill(player, ESM::Skill::Armorer); float fRepairAmountMult = MWBase::Environment::get().getWorld()->getStore().get() .find("fRepairAmountMult")->mValue.getFloat(); diff --git a/apps/openmw/mwmechanics/security.cpp b/apps/openmw/mwmechanics/security.cpp index ab286cbee..001375feb 100644 --- a/apps/openmw/mwmechanics/security.cpp +++ b/apps/openmw/mwmechanics/security.cpp @@ -19,8 +19,8 @@ namespace MWMechanics : mActor(actor) { CreatureStats& creatureStats = actor.getClass().getCreatureStats(actor); - mAgility = static_cast(creatureStats.getAttribute(ESM::Attribute::Agility).getModified()); - mLuck = static_cast(creatureStats.getAttribute(ESM::Attribute::Luck).getModified()); + mAgility = creatureStats.getAttribute(ESM::Attribute::Agility).getModified(); + mLuck = creatureStats.getAttribute(ESM::Attribute::Luck).getModified(); mSecuritySkill = static_cast(actor.getClass().getSkill(actor, ESM::Skill::Security)); mFatigueTerm = creatureStats.getFatigueTerm(); } diff --git a/apps/openmw/mwmechanics/spellresistance.cpp b/apps/openmw/mwmechanics/spellresistance.cpp index a187600fb..1edf14091 100644 --- a/apps/openmw/mwmechanics/spellresistance.cpp +++ b/apps/openmw/mwmechanics/spellresistance.cpp @@ -40,8 +40,8 @@ namespace MWMechanics float resistance = getEffectResistanceAttribute(effectId, magicEffects); - int willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - float luck = static_cast(stats.getAttribute(ESM::Attribute::Luck).getModified()); + float willpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float luck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float x = (willpower + 0.1f * luck) * stats.getFatigueTerm(); // This makes spells that are easy to cast harder to resist and vice versa diff --git a/apps/openmw/mwmechanics/spellutil.cpp b/apps/openmw/mwmechanics/spellutil.cpp index bb4953e48..8b2f5c46c 100644 --- a/apps/openmw/mwmechanics/spellutil.cpp +++ b/apps/openmw/mwmechanics/spellutil.cpp @@ -94,8 +94,8 @@ namespace MWMechanics CreatureStats& stats = actor.getClass().getCreatureStats(actor); - int actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); - int actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); + float actorWillpower = stats.getAttribute(ESM::Attribute::Willpower).getModified(); + float actorLuck = stats.getAttribute(ESM::Attribute::Luck).getModified(); float castChance = (lowestSkill - spell->mData.mCost + 0.2f * actorWillpower + 0.1f * actorLuck); diff --git a/apps/openmw/mwmechanics/stat.cpp b/apps/openmw/mwmechanics/stat.cpp index 6a559a361..7f71cf9b1 100644 --- a/apps/openmw/mwmechanics/stat.cpp +++ b/apps/openmw/mwmechanics/stat.cpp @@ -227,29 +227,29 @@ namespace MWMechanics } AttributeValue::AttributeValue() : - mBase(0), mModifier(0), mDamage(0) + mBase(0.f), mModifier(0.f), mDamage(0.f) { } - int AttributeValue::getModified() const + float AttributeValue::getModified() const { - return std::max(0, mBase - (int) mDamage + mModifier); + return std::max(0.f, mBase - mDamage + mModifier); } - int AttributeValue::getBase() const + float AttributeValue::getBase() const { return mBase; } - int AttributeValue::getModifier() const + float AttributeValue::getModifier() const { return mModifier; } - void AttributeValue::setBase(int base) + void AttributeValue::setBase(float base) { mBase = base; } - void AttributeValue::setModifier(int mod) + void AttributeValue::setModifier(float mod) { mModifier = mod; } @@ -275,14 +275,14 @@ namespace MWMechanics return mDamage; } - void AttributeValue::writeState (ESM::StatState& state) const + void AttributeValue::writeState (ESM::StatState& state) const { state.mBase = mBase; state.mMod = mModifier; state.mDamage = mDamage; } - void AttributeValue::readState (const ESM::StatState& state) + void AttributeValue::readState (const ESM::StatState& state) { mBase = state.mBase; mModifier = state.mMod; @@ -303,13 +303,13 @@ namespace MWMechanics mProgress = progress; } - void SkillValue::writeState (ESM::StatState& state) const + void SkillValue::writeState (ESM::StatState& state) const { AttributeValue::writeState (state); state.mProgress = mProgress; } - void SkillValue::readState (const ESM::StatState& state) + void SkillValue::readState (const ESM::StatState& state) { AttributeValue::readState (state); mProgress = state.mProgress; diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index 751b80b9c..5f49da48e 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -122,20 +122,20 @@ namespace MWMechanics class AttributeValue { - int mBase; - int mModifier; + float mBase; + float mModifier; float mDamage; // needs to be float to allow continuous damage public: AttributeValue(); - int getModified() const; - int getBase() const; - int getModifier() const; + float getModified() const; + float getBase() const; + float getModifier() const; - void setBase(int base); + void setBase(float base); - void setModifier(int mod); + void setModifier(float mod); // Maximum attribute damage is limited to the modified value. // Note: I think MW applies damage directly to mModified, since you can also @@ -145,8 +145,8 @@ namespace MWMechanics float getDamage() const; - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; class SkillValue : public AttributeValue @@ -157,8 +157,8 @@ namespace MWMechanics float getProgress() const; void setProgress(float progress); - void writeState (ESM::StatState& state) const; - void readState (const ESM::StatState& state); + void writeState (ESM::StatState& state) const; + void readState (const ESM::StatState& state); }; inline bool operator== (const AttributeValue& left, const AttributeValue& right) diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index 8ba0cdcf2..37751c6d4 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -95,7 +95,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = + Interpreter::Type_Float value = ptr.getClass() .getCreatureStats (ptr) .getAttribute(mIndex) @@ -118,7 +118,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); @@ -140,7 +140,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass() @@ -155,9 +155,9 @@ namespace MWScript return; if (value < 0) - attribute.setBase(std::max(0, attribute.getBase() + value)); + attribute.setBase(std::max(0.f, attribute.getBase() + value)); else - attribute.setBase(std::min(100, attribute.getBase() + value)); + attribute.setBase(std::min(100.f, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } @@ -345,7 +345,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = ptr.getClass().getSkill(ptr, mIndex); + Interpreter::Type_Float value = ptr.getClass().getSkill(ptr, mIndex); runtime.push (value); } @@ -364,7 +364,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats (ptr); @@ -386,7 +386,7 @@ namespace MWScript { MWWorld::Ptr ptr = R()(runtime); - Interpreter::Type_Integer value = runtime[0].mInteger; + Interpreter::Type_Float value = runtime[0].mFloat; runtime.pop(); MWMechanics::SkillValue &skill = ptr.getClass() @@ -396,14 +396,14 @@ namespace MWScript if (value == 0) return; - if (((skill.getBase() <= 0) && (value < 0)) - || ((skill.getBase() >= 100) && (value > 0))) + if (((skill.getBase() <= 0.f) && (value < 0.f)) + || ((skill.getBase() >= 100.f) && (value > 0.f))) return; if (value < 0) - skill.setBase(std::max(0, skill.getBase() + value)); + skill.setBase(std::max(0.f, skill.getBase() + value)); else - skill.setBase(std::min(100, skill.getBase() + value)); + skill.setBase(std::min(100.f, skill.getBase() + value)); } }; diff --git a/apps/openmw/mwworld/class.cpp b/apps/openmw/mwworld/class.cpp index 07981bf2a..d7ee59ee2 100644 --- a/apps/openmw/mwworld/class.cpp +++ b/apps/openmw/mwworld/class.cpp @@ -421,7 +421,7 @@ namespace MWWorld return canSwim(ptr) || canWalk(ptr) || canFly(ptr); } - int Class::getSkill(const MWWorld::Ptr& ptr, int skill) const + float Class::getSkill(const MWWorld::Ptr& ptr, int skill) const { throw std::runtime_error("class does not support skills"); } diff --git a/apps/openmw/mwworld/class.hpp b/apps/openmw/mwworld/class.hpp index fb816d810..fd679c43f 100644 --- a/apps/openmw/mwworld/class.hpp +++ b/apps/openmw/mwworld/class.hpp @@ -321,7 +321,7 @@ namespace MWWorld bool isPureLandCreature(const MWWorld::Ptr& ptr) const; bool isMobile(const MWWorld::Ptr& ptr) const; - virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const; + virtual float getSkill(const MWWorld::Ptr& ptr, int skill) const; virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state) const; diff --git a/apps/openmw/mwworld/inventorystore.cpp b/apps/openmw/mwworld/inventorystore.cpp index a2bd84953..ada211470 100644 --- a/apps/openmw/mwworld/inventorystore.cpp +++ b/apps/openmw/mwworld/inventorystore.cpp @@ -284,12 +284,12 @@ void MWWorld::InventoryStore::autoEquipWeapon (const MWWorld::Ptr& actor, TSlots // rate weapon for (int i = 0; i < static_cast(weaponSkillsLength); ++i) { - int max = 0; + float max = 0; int maxWeaponSkill = -1; for (int j = 0; j < static_cast(weaponSkillsLength); ++j) { - int skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); + float skillValue = actor.getClass().getSkill(actor, static_cast(weaponSkills[j])); if (skillValue > max && !weaponSkillVisited[j]) { max = skillValue; @@ -399,7 +399,7 @@ void MWWorld::InventoryStore::autoEquipArmor (const MWWorld::Ptr& actor, TSlots& static float fUnarmoredBase1 = store.find("fUnarmoredBase1")->mValue.getFloat(); static float fUnarmoredBase2 = store.find("fUnarmoredBase2")->mValue.getFloat(); - int unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); + float unarmoredSkill = actor.getClass().getSkill(actor, ESM::Skill::Unarmored); float unarmoredRating = (fUnarmoredBase1 * unarmoredSkill) * (fUnarmoredBase2 * unarmoredSkill); for (ContainerStoreIterator iter (begin(ContainerStore::Type_Clothing | ContainerStore::Type_Armor)); iter!=end(); ++iter) diff --git a/components/compiler/extensions0.cpp b/components/compiler/extensions0.cpp index c67af29c2..60b4a7451 100644 --- a/components/compiler/extensions0.cpp +++ b/components/compiler/extensions0.cpp @@ -423,13 +423,13 @@ namespace Compiler for (int i=0; i mAttributes[Attribute::Length]; + StatState mAttributes[Attribute::Length]; StatState mDynamic[3]; MagicEffects mMagicEffects; diff --git a/components/esm/npcstats.cpp b/components/esm/npcstats.cpp index c5fa2a09e..277335e8c 100644 --- a/components/esm/npcstats.cpp +++ b/components/esm/npcstats.cpp @@ -31,8 +31,9 @@ void ESM::NpcStats::load (ESMReader &esm) mDisposition = 0; esm.getHNOT (mDisposition, "DISP"); + bool intFallback = esm.getFormat() < 11; for (int i=0; i<27; ++i) - mSkills[i].load (esm); + mSkills[i].load (esm, intFallback); mWerewolfDeprecatedData = false; if (esm.getFormat() < 8 && esm.peekNextSub("STBA")) @@ -40,17 +41,17 @@ void ESM::NpcStats::load (ESMReader &esm) // we have deprecated werewolf skills, stored interleaved // Load into one big vector, then remove every 2nd value mWerewolfDeprecatedData = true; - std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); + std::vector > skills(mSkills, mSkills + sizeof(mSkills)/sizeof(mSkills[0])); for (int i=0; i<27; ++i) { - ESM::StatState skill; - skill.load(esm); + ESM::StatState skill; + skill.load(esm, intFallback); skills.push_back(skill); } int i=0; - for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) + for (std::vector >::iterator it = skills.begin(); it != skills.end(); ++i) { if (i%2 == 1) it = skills.erase(it); @@ -68,7 +69,7 @@ void ESM::NpcStats::load (ESMReader &esm) { ESM::StatState dummy; for (int i=0; i<8; ++i) - dummy.load(esm); + dummy.load(esm, intFallback); mWerewolfDeprecatedData = true; } diff --git a/components/esm/npcstats.hpp b/components/esm/npcstats.hpp index 467a099ce..3ad94b543 100644 --- a/components/esm/npcstats.hpp +++ b/components/esm/npcstats.hpp @@ -31,7 +31,7 @@ namespace ESM std::map mFactions; // lower case IDs int mDisposition; - StatState mSkills[27]; + StatState mSkills[27]; int mBounty; int mReputation; int mWerewolfKills; diff --git a/components/esm/player.cpp b/components/esm/player.cpp index 571a10a8c..3850390e9 100644 --- a/components/esm/player.cpp +++ b/components/esm/player.cpp @@ -43,12 +43,13 @@ void ESM::Player::load (ESMReader &esm) checkPrevItems = false; } + bool intFallback = esm.getFormat() < 11; if (esm.hasMoreSubs()) { for (int i=0; i mSaveAttributes[ESM::Attribute::Length]; - StatState mSaveSkills[ESM::Skill::Length]; + StatState mSaveAttributes[ESM::Attribute::Length]; + StatState mSaveSkills[ESM::Skill::Length]; typedef std::map PreviousItems; // previous equipped items, needed for bound spells PreviousItems mPreviousItems; diff --git a/components/esm/savedgame.cpp b/components/esm/savedgame.cpp index cda411314..063ae86ed 100644 --- a/components/esm/savedgame.cpp +++ b/components/esm/savedgame.cpp @@ -5,7 +5,7 @@ #include "defs.hpp" unsigned int ESM::SavedGame::sRecordId = ESM::REC_SAVE; -int ESM::SavedGame::sCurrentFormat = 10; +int ESM::SavedGame::sCurrentFormat = 11; void ESM::SavedGame::load (ESMReader &esm) { diff --git a/components/esm/statstate.cpp b/components/esm/statstate.cpp index c17bedd81..b9ddc3efd 100644 --- a/components/esm/statstate.cpp +++ b/components/esm/statstate.cpp @@ -9,19 +9,44 @@ namespace ESM StatState::StatState() : mBase(0), mMod(0), mCurrent(0), mDamage(0), mProgress(0) {} template - void StatState::load(ESMReader &esm) + void StatState::load(ESMReader &esm, bool intFallback) { - esm.getHNT(mBase, "STBA"); + // We changed stats values from integers to floats; ensure backwards compatibility + if (intFallback) + { + int base = 0; + esm.getHNT(base, "STBA"); + mBase = static_cast(base); - mMod = 0; - esm.getHNOT(mMod, "STMO"); - mCurrent = 0; - esm.getHNOT(mCurrent, "STCU"); + int mod = 0; + esm.getHNOT(mod, "STMO"); + mMod = static_cast(mod); - // mDamage was changed to a float; ensure backwards compatibility - T oldDamage = 0; - esm.getHNOT(oldDamage, "STDA"); - mDamage = static_cast(oldDamage); + int current = 0; + esm.getHNOT(current, "STCU"); + mCurrent = static_cast(current); + + // mDamage was changed to a float; ensure backwards compatibility + int oldDamage = 0; + esm.getHNOT(oldDamage, "STDA"); + mDamage = static_cast(oldDamage); + } + else + { + mBase = 0; + esm.getHNT(mBase, "STBA"); + + mMod = 0; + esm.getHNOT(mMod, "STMO"); + + mCurrent = 0; + esm.getHNOT(mCurrent, "STCU"); + + mDamage = 0; + esm.getHNOT(mDamage, "STDF"); + + mProgress = 0; + } esm.getHNOT(mDamage, "STDF"); diff --git a/components/esm/statstate.hpp b/components/esm/statstate.hpp index 47aeb0331..d81d24a61 100644 --- a/components/esm/statstate.hpp +++ b/components/esm/statstate.hpp @@ -20,7 +20,7 @@ namespace ESM StatState(); - void load (ESMReader &esm); + void load (ESMReader &esm, bool intFallback = false); void save (ESMWriter &esm) const; }; }