Commit b42240be authored by scrawl's avatar scrawl

Implement Damage/restore skill/attribute effects. Use dedicated classes for...

Implement Damage/restore skill/attribute effects. Use dedicated classes for skill and attribute values (instead of Stat<T>) since there are some important differences.
parent 32ff3b53
......@@ -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<ESM::Skill>().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<int>((level-1) * (majorMultiplier + specMultiplier)), 100.0f));
+ static_cast<int>((level-1) * (majorMultiplier + specMultiplier)), 100));
}
}
}
......
......@@ -65,7 +65,7 @@ namespace MWGui
getWidget(attribute, std::string("Attribute") + boost::lexical_cast<std::string>(idx));
mAttributeWidgets.insert(std::make_pair(static_cast<int>(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)
......
......@@ -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<int>(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<ESM::Skill>().find(skillId);
assert(skill);
std::string icon = "icons\\k\\" + ESM::Skill::sIconNames[skillId];
const ESM::Attribute* attr =
esmStore.get<ESM::Attribute>().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<ESM::Attribute>().find(faction->mData.mAttribute[0]);
const ESM::Attribute* attr2 = store.get<ESM::Attribute>().find(faction->mData.mAttribute[1]);
assert(attr1 && attr2);
text += "\n#BF9959#{" + attr1->mName + "}: " + boost::lexical_cast<std::string>(rankData.mAttribute1)
+ ", #{" + attr2->mName + "}: " + boost::lexical_cast<std::string>(rankData.mAttribute2);
......
......@@ -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);
......
......@@ -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<MyGUI::TextBox*>(mAttributeValueWidget)->setCaption(boost::lexical_cast<std::string>(modified));
if (modified > base)
mAttributeValueWidget->_setWidgetState("increased");
......
......@@ -523,7 +523,7 @@ namespace MWMechanics
// skills
for(int i = 0;i < ESM::Skill::Length;++i)
{
Stat<float>& 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);
......
......@@ -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);
......
......@@ -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<int> (base);
value.setProgress(value.getProgress() + getSkillGain (skillIndex, class_, usageType));
base += getSkillGain (skillIndex, class_, usageType);
if (static_cast<int> (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<int> (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;
......
......@@ -45,8 +45,8 @@ namespace MWMechanics
DrawState_ mDrawState;
int mDisposition;
unsigned int mMovementFlags;
Stat<float> mSkill[27];
Stat<float> mWerewolfSkill[27];
SkillValue mSkill[27];
SkillValue mWerewolfSkill[27];
int mBounty;
std::set<std::string> mExpelled;
std::map<std::string, int> mFactionReputation;
......
......@@ -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)
......
......@@ -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);
};
}
......
......@@ -206,8 +206,45 @@ namespace MWMechanics
return !(left==right);
}
typedef Stat<int> AttributeValue;
typedef Stat<float> 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
......@@ -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<ESM::Class>().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<int> (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));
}
};
......
......@@ -2000,7 +2000,7 @@ namespace MWWorld
const Store<ESM::GameSetting> &gmst = getStore().get<ESM::GameSetting>();
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()
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment