diff --git a/apps/openmw/mwclass/npc.cpp b/apps/openmw/mwclass/npc.cpp index f2481195b..307ad3d7c 100644 --- a/apps/openmw/mwclass/npc.cpp +++ b/apps/openmw/mwclass/npc.cpp @@ -179,29 +179,29 @@ namespace for (int raceSkillIndex = 0; raceSkillIndex < 7; ++raceSkillIndex) { - if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) - { - raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; - break; - } + if (race->mData.mBonus[raceSkillIndex].mSkill == skillIndex) + { + raceBonus = race->mData.mBonus[raceSkillIndex].mBonus; + break; + } } for (int k = 0; k < 5; ++k) { - // is this a minor or major skill? - if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) - { - majorMultiplier = 1.0f; - break; - } + // is this a minor or major skill? + if ((class_->mData.mSkills[k][0] == skillIndex) || (class_->mData.mSkills[k][1] == skillIndex)) + { + majorMultiplier = 1.0f; + break; + } } // is this skill in the same Specialization as the class? const ESM::Skill* skill = MWBase::Environment::get().getWorld()->getStore().get().find(skillIndex); if (skill->mData.mSpecialization == class_->mData.mSpecialization) { - specMultiplier = 0.5f; - specBonus = 5; + specMultiplier = 0.5f; + specBonus = 5; } npcStats.getSkill(skillIndex).setBase( @@ -210,7 +210,7 @@ namespace + 5 + raceBonus + specBonus - + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100.0f)); + + static_cast((level-1) * (majorMultiplier + specMultiplier)), 100)); } } } diff --git a/apps/openmw/mwgui/review.cpp b/apps/openmw/mwgui/review.cpp index b1c85e1c6..e27e40ae6 100644 --- a/apps/openmw/mwgui/review.cpp +++ b/apps/openmw/mwgui/review.cpp @@ -65,7 +65,7 @@ namespace MWGui getWidget(attribute, std::string("Attribute") + boost::lexical_cast(idx)); mAttributeWidgets.insert(std::make_pair(static_cast(ESM::Attribute::sAttributeIds[idx]), attribute)); attribute->setAttributeId(ESM::Attribute::sAttributeIds[idx]); - attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue(0, 0)); + attribute->setAttributeValue(Widgets::MWAttribute::AttributeValue()); } // Setup skills @@ -280,8 +280,8 @@ namespace MWGui assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); + int base = stat.getBase(); + int modified = stat.getModified(); std::string state = "normal"; if (modified > base) diff --git a/apps/openmw/mwgui/statswindow.cpp b/apps/openmw/mwgui/statswindow.cpp index 17583b1c0..549cf65ab 100644 --- a/apps/openmw/mwgui/statswindow.cpp +++ b/apps/openmw/mwgui/statswindow.cpp @@ -359,21 +359,19 @@ namespace MWGui assert(skillId >= 0 && skillId < ESM::Skill::Length); const std::string &skillNameId = ESM::Skill::sSkillNameIds[skillId]; const MWMechanics::SkillValue &stat = mSkillValues.find(skillId)->second; - float base = stat.getBase(); - float modified = stat.getModified(); - int progressPercent = (modified - float(static_cast(modified))) * 100; + int base = stat.getBase(); + int modified = stat.getModified(); + int progressPercent = stat.getProgress() * 100; const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore(); const ESM::Skill* skill = esmStore.get().find(skillId); - assert(skill); std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId]; const ESM::Attribute* attr = esmStore.get().find(skill->mData.mAttribute); - assert(attr); std::string state = "normal"; if (modified > base) @@ -484,7 +482,6 @@ namespace MWGui ESM::RankData rankData = faction->mData.mRankData[it->second+1]; const ESM::Attribute* attr1 = store.get().find(faction->mData.mAttribute[0]); const ESM::Attribute* attr2 = store.get().find(faction->mData.mAttribute[1]); - assert(attr1 && attr2); text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast(rankData.mAttribute1) + ", #{" + attr2->mName + "}: " + boost::lexical_cast(rankData.mAttribute2); diff --git a/apps/openmw/mwgui/tradewindow.cpp b/apps/openmw/mwgui/tradewindow.cpp index d544b83cf..e6cbbf968 100644 --- a/apps/openmw/mwgui/tradewindow.cpp +++ b/apps/openmw/mwgui/tradewindow.cpp @@ -296,10 +296,10 @@ namespace MWGui const MWMechanics::NpcStats &sellerStats = MWWorld::Class::get(mPtr).getNpcStats(mPtr); const MWMechanics::NpcStats &playerStats = MWWorld::Class::get(playerPtr).getNpcStats(playerPtr); - float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a1 = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float b1 = std::min(0.1f * playerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float c1 = std::min(0.2f * playerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); - float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d1 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); float e1 = std::min(0.1f * sellerStats.getAttribute(ESM::Attribute::Luck).getModified(), 10.f); float f1 = std::min(0.2f * sellerStats.getAttribute(ESM::Attribute::Personality).getModified(), 10.f); diff --git a/apps/openmw/mwgui/widgets.cpp b/apps/openmw/mwgui/widgets.cpp index 0df6c5477..b30cf2bae 100644 --- a/apps/openmw/mwgui/widgets.cpp +++ b/apps/openmw/mwgui/widgets.cpp @@ -178,7 +178,7 @@ namespace MWGui } if (mAttributeValueWidget) { - AttributeValue::Type modified = mValue.getModified(), base = mValue.getBase(); + int modified = mValue.getModified(), base = mValue.getBase(); static_cast(mAttributeValueWidget)->setCaption(boost::lexical_cast(modified)); if (modified > base) mAttributeValueWidget->_setWidgetState("increased"); diff --git a/apps/openmw/mwmechanics/actors.cpp b/apps/openmw/mwmechanics/actors.cpp index 30ea6aa2c..ec3b86137 100644 --- a/apps/openmw/mwmechanics/actors.cpp +++ b/apps/openmw/mwmechanics/actors.cpp @@ -523,7 +523,7 @@ namespace MWMechanics // skills for(int i = 0;i < ESM::Skill::Length;++i) { - Stat& skill = npcStats.getSkill(i); + SkillValue& skill = npcStats.getSkill(i); skill.setModifier(effects.get(EffectKey(ESM::MagicEffect::FortifySkill, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::DrainSkill, i)).mMagnitude - effects.get(EffectKey(ESM::MagicEffect::AbsorbSkill, i)).mMagnitude); diff --git a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp index 7740240b6..2e18fae63 100644 --- a/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp +++ b/apps/openmw/mwmechanics/mechanicsmanagerimp.cpp @@ -499,10 +499,10 @@ namespace MWMechanics // otherwise one would get different prices when exiting and re-entering the dialogue window... int clampedDisposition = std::max(0, std::min(getDerivedDisposition(ptr) + MWBase::Environment::get().getDialogueManager()->getTemporaryDispositionChange(),100)); - float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float a = std::min(playerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); 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 = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100.f); + float d = std::min(sellerStats.getSkill(ESM::Skill::Mercantile).getModified(), 100); 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); diff --git a/apps/openmw/mwmechanics/npcstats.cpp b/apps/openmw/mwmechanics/npcstats.cpp index c4b8b0b7a..0a0b51270 100644 --- a/apps/openmw/mwmechanics/npcstats.cpp +++ b/apps/openmw/mwmechanics/npcstats.cpp @@ -197,34 +197,25 @@ void MWMechanics::NpcStats::useSkill (int skillIndex, const ESM::Class& class_, if(mIsWerewolf) return; - float base = getSkill (skillIndex).getBase(); + MWMechanics::SkillValue value = getSkill (skillIndex); - int level = static_cast (base); + value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType)); - base += getSkillGain (skillIndex, class_, usageType); - - if (static_cast (base)!=level) + if (value.getProgress()>=1) { // skill leveled up increaseSkill(skillIndex, class_, false); } - else - getSkill (skillIndex).setBase (base); } void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &class_, bool preserveProgress) { - float base = getSkill (skillIndex).getBase(); + int base = getSkill (skillIndex).getBase(); - int level = static_cast (base); - - if (level >= 100) + if (base >= 100) return; - if (preserveProgress) - base += 1; - else - base = level+1; + base += 1; // if this is a major or minor skill of the class, increase level progress bool levelProgress = false; @@ -260,6 +251,8 @@ void MWMechanics::NpcStats::increaseSkill(int skillIndex, const ESM::Class &clas } getSkill (skillIndex).setBase (base); + if (!preserveProgress) + getSkill(skillIndex).setProgress(0); } int MWMechanics::NpcStats::getLevelProgress () const @@ -387,7 +380,7 @@ void MWMechanics::NpcStats::setWerewolf (bool set) // Oh, Bethesda. It's "Intelligence". std::string name = "fWerewolf"+((i==ESM::Attribute::Intelligence) ? std::string("Intellegence") : ESM::Attribute::sAttributeNames[i]); - mWerewolfAttributes[i].setModified(int(gmst.find(name)->getFloat()), 0); + mWerewolfAttributes[i].setBase(int(gmst.find(name)->getFloat())); } for(size_t i = 0;i < ESM::Skill::Length;i++) @@ -401,7 +394,7 @@ void MWMechanics::NpcStats::setWerewolf (bool set) // "Mercantile"! >_< std::string name = "fWerewolf"+((i==ESM::Skill::Mercantile) ? std::string("Merchantile") : ESM::Skill::sSkillNames[i]); - mWerewolfSkill[i].setModified(int(gmst.find(name)->getFloat()), 0); + mWerewolfSkill[i].setBase(int(gmst.find(name)->getFloat())); } } mIsWerewolf = set; diff --git a/apps/openmw/mwmechanics/npcstats.hpp b/apps/openmw/mwmechanics/npcstats.hpp index df0f084ce..552422d84 100644 --- a/apps/openmw/mwmechanics/npcstats.hpp +++ b/apps/openmw/mwmechanics/npcstats.hpp @@ -45,8 +45,8 @@ namespace MWMechanics DrawState_ mDrawState; int mDisposition; unsigned int mMovementFlags; - Stat mSkill[27]; - Stat mWerewolfSkill[27]; + SkillValue mSkill[27]; + SkillValue mWerewolfSkill[27]; int mBounty; std::set mExpelled; std::map mFactionReputation; diff --git a/apps/openmw/mwmechanics/spellcasting.cpp b/apps/openmw/mwmechanics/spellcasting.cpp index 64a824030..1de4e50c4 100644 --- a/apps/openmw/mwmechanics/spellcasting.cpp +++ b/apps/openmw/mwmechanics/spellcasting.cpp @@ -135,7 +135,8 @@ namespace MWMechanics float magnitude = effectIt->mMagnMin + (effectIt->mMagnMax - effectIt->mMagnMin) * random; magnitude *= magnitudeMult; - if (target.getClass().isActor() && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)) + bool hasDuration = !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration); + if (target.getClass().isActor() && hasDuration) { ActiveSpells::Effect effect; effect.mKey = MWMechanics::EffectKey(*effectIt); @@ -160,7 +161,17 @@ namespace MWMechanics } } else - applyInstantEffect(target, effectIt->mEffectID, magnitude); + applyInstantEffect(target, EffectKey(*effectIt), magnitude); + + // HACK: Damage attribute/skill actually has a duration, even though the actual effect is instant and permanent. + // This was probably just done to have the effect visible in the magic menu for a while + // to notify the player they've been damaged? + if (effectIt->mEffectID == ESM::MagicEffect::DamageAttribute + || effectIt->mEffectID == ESM::MagicEffect::DamageSkill + || effectIt->mEffectID == ESM::MagicEffect::RestoreAttribute + || effectIt->mEffectID == ESM::MagicEffect::RestoreSkill + ) + applyInstantEffect(target, EffectKey(*effectIt), magnitude); if (target.getClass().isActor() || magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration) { @@ -203,8 +214,9 @@ namespace MWMechanics mSourceName, caster.getRefData().getHandle()); } - void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, short effectId, float magnitude) + void CastSpell::applyInstantEffect(const MWWorld::Ptr &target, MWMechanics::EffectKey effect, float magnitude) { + short effectId = effect.mId; if (!target.getClass().isActor()) { if (effectId == ESM::MagicEffect::Lock) @@ -227,6 +239,28 @@ namespace MWMechanics } else { + if (effectId == ESM::MagicEffect::DamageAttribute || effectId == ESM::MagicEffect::RestoreAttribute) + { + int attribute = effect.mArg; + AttributeValue value = target.getClass().getCreatureStats(target).getAttribute(attribute); + if (effectId == ESM::MagicEffect::DamageAttribute) + value.damage(magnitude); + else + value.restore(magnitude); + target.getClass().getCreatureStats(target).setAttribute(attribute, value); + } + else if (effectId == ESM::MagicEffect::DamageSkill || effectId == ESM::MagicEffect::RestoreSkill) + { + if (target.getTypeName() != typeid(ESM::NPC).name()) + return; + int skill = effect.mArg; + SkillValue& value = target.getClass().getNpcStats(target).getSkill(skill); + if (effectId == ESM::MagicEffect::DamageSkill) + value.damage(magnitude); + else + value.restore(magnitude); + } + if (effectId == ESM::MagicEffect::CurePoison) target.getClass().getCreatureStats(target).getActiveSpells().purgeEffect(ESM::MagicEffect::Poison); else if (effectId == ESM::MagicEffect::CureParalyzation) diff --git a/apps/openmw/mwmechanics/spellcasting.hpp b/apps/openmw/mwmechanics/spellcasting.hpp index e2efaa140..a55c45fd1 100644 --- a/apps/openmw/mwmechanics/spellcasting.hpp +++ b/apps/openmw/mwmechanics/spellcasting.hpp @@ -203,7 +203,7 @@ namespace MWMechanics void inflict (const MWWorld::Ptr& target, const MWWorld::Ptr& caster, const ESM::EffectList& effects, ESM::RangeType range, bool reflected=false); - void applyInstantEffect (const MWWorld::Ptr& target, short effectId, float magnitude); + void applyInstantEffect (const MWWorld::Ptr& target, MWMechanics::EffectKey effect, float magnitude); }; } diff --git a/apps/openmw/mwmechanics/stat.hpp b/apps/openmw/mwmechanics/stat.hpp index c7778a3d8..77b3f6364 100644 --- a/apps/openmw/mwmechanics/stat.hpp +++ b/apps/openmw/mwmechanics/stat.hpp @@ -206,8 +206,45 @@ namespace MWMechanics return !(left==right); } - typedef Stat AttributeValue; - typedef Stat SkillValue; + class AttributeValue + { + int mBase; + int mModifier; + int mDamage; + + public: + AttributeValue() : mBase(0), mModifier(0), mDamage(0) {} + + int getModified() const { return std::max(0, mBase - mDamage + mModifier); } + int getBase() const { return mBase; } + int getModifier() const { return mModifier; } + + void setBase(int base) { mBase = std::max(0, base); } + void setModifier(int mod) { mModifier = mod; } + + void damage(int damage) { mDamage += damage; } + void restore(int amount) { mDamage -= std::min(mDamage, amount); } + int getDamage() const { return mDamage; } + }; + + class SkillValue : public AttributeValue + { + float mProgress; + public: + float getProgress() const { return mProgress; } + void setProgress(float progress) { mProgress = progress; } + }; + + inline bool operator== (const AttributeValue& left, const AttributeValue& right) + { + return left.getBase() == right.getBase() + && left.getModifier() == right.getModifier() + && left.getDamage() == right.getDamage(); + } + inline bool operator!= (const AttributeValue& left, const AttributeValue& right) + { + return !(left == right); + } } #endif diff --git a/apps/openmw/mwscript/statsextensions.cpp b/apps/openmw/mwscript/statsextensions.cpp index f32d602da..a899b05f0 100644 --- a/apps/openmw/mwscript/statsextensions.cpp +++ b/apps/openmw/mwscript/statsextensions.cpp @@ -125,7 +125,7 @@ namespace MWScript runtime.pop(); MWMechanics::AttributeValue attribute = ptr.getClass().getCreatureStats(ptr).getAttribute(mIndex); - attribute.setModified (value, 0); + attribute.setBase (value - (attribute.getModified() - attribute.getBase())); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -150,11 +150,7 @@ namespace MWScript .getCreatureStats(ptr) .getAttribute(mIndex); - value += - attribute.getModified(); - - attribute - .setModified (value, 0, 100); + attribute.setBase (std::min(100, attribute.getBase() + value)); ptr.getClass().getCreatureStats(ptr).setAttribute(mIndex, attribute); } }; @@ -346,12 +342,10 @@ namespace MWScript const ESM::Class& class_ = *MWBase::Environment::get().getWorld()->getStore().get().find (ref->mBase->mClass); - float level = 0; - float progress = std::modf (stats.getSkill (mIndex).getBase(), &level); - - float modifier = stats.getSkill (mIndex).getModifier(); + float level = stats.getSkill(mIndex).getBase(); + float progress = stats.getSkill(mIndex).getProgress(); - int newLevel = static_cast (value-modifier); + int newLevel = value - (stats.getSkill(mIndex).getModified() - stats.getSkill(mIndex).getBase()); if (newLevel<0) newLevel = 0; @@ -362,8 +356,8 @@ namespace MWScript if (progress>=1) progress = 0.999999999; - stats.getSkill (mIndex).set (newLevel + progress); - stats.getSkill (mIndex).setModifier (modifier); + stats.getSkill (mIndex).setBase (newLevel); + stats.getSkill (mIndex).setProgress(progress); } }; @@ -383,11 +377,10 @@ namespace MWScript Interpreter::Type_Integer value = runtime[0].mInteger; runtime.pop(); - value += MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - getModified(); + MWMechanics::NpcStats& stats = ptr.getClass().getNpcStats(ptr); - MWWorld::Class::get (ptr).getNpcStats (ptr).getSkill (mIndex). - setModified (value, 0, 100); + stats.getSkill(mIndex). + setBase (std::min(100, stats.getSkill(mIndex).getBase() + value)); } }; diff --git a/apps/openmw/mwworld/worldimp.cpp b/apps/openmw/mwworld/worldimp.cpp index b410da2d8..083c1cf0e 100644 --- a/apps/openmw/mwworld/worldimp.cpp +++ b/apps/openmw/mwworld/worldimp.cpp @@ -2000,7 +2000,7 @@ namespace MWWorld const Store &gmst = getStore().get(); MWMechanics::NpcStats &stats = Class::get(actor).getNpcStats(actor); - stats.getSkill(ESM::Skill::Acrobatics).setModified(gmst.find("fWerewolfAcrobatics")->getFloat(), 0); + stats.getSkill(ESM::Skill::Acrobatics).setBase(gmst.find("fWerewolfAcrobatics")->getFloat()); } bool World::getGodModeState()