1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-30 15:45:31 +00:00

Merge remote-tracking branch 'pakanek/enchanting-mechanics'

This commit is contained in:
Marc Zinnschlag 2013-05-28 10:02:03 +02:00
commit bbec3680c6
4 changed files with 136 additions and 68 deletions

View file

@ -61,30 +61,32 @@ namespace MWGui
void EnchantingDialog::updateLabels() void EnchantingDialog::updateLabels()
{ {
std::stringstream enchantCost; std::stringstream enchantCost;
enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantCost(); enchantCost << std::setprecision(1) << std::fixed << mEnchanting.getEnchantPoints();
mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue())); mEnchantmentPoints->setCaption(enchantCost.str() + " / " + boost::lexical_cast<std::string>(mEnchanting.getMaxEnchantValue()));
mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge())); mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge()));
mCastCost->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantCost())); std::stringstream castCost;
castCost << std::setprecision(1) << std::fixed << mEnchanting.getCastCost();
mCastCost->setCaption(boost::lexical_cast<std::string>(castCost.str()));
mPrice->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantPrice())); mPrice->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantPrice()));
switch(mEnchanting.getEnchantType()) switch(mEnchanting.getCastStyle())
{ {
case 0: case ESM::CastingStyle_CastOnce:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once"));
mAddEffectDialog.constantEffect=false; mAddEffectDialog.constantEffect=false;
break; break;
case 1: case ESM::CastingStyle_WhenStrikes:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes"));
mAddEffectDialog.constantEffect=false; mAddEffectDialog.constantEffect=false;
break; break;
case 2: case ESM::CastingStyle_WhenUsed:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used"));
mAddEffectDialog.constantEffect=false; mAddEffectDialog.constantEffect=false;
break; break;
case 3: case ESM::CastingStyle_ConstantEffect:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant")); mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant"));
mAddEffectDialog.constantEffect=true; mAddEffectDialog.constantEffect=true;
break; break;
@ -169,7 +171,7 @@ namespace MWGui
image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem); image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem);
mEnchanting.setOldItem(item); mEnchanting.setOldItem(item);
mEnchanting.nextEnchantType(); mEnchanting.nextCastStyle();
updateLabels(); updateLabels();
} }
@ -248,7 +250,7 @@ namespace MWGui
void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender) void EnchantingDialog::onTypeButtonClicked(MyGUI::Widget* sender)
{ {
mEnchanting.nextEnchantType(); mEnchanting.nextCastStyle();
updateLabels(); updateLabels();
} }
@ -278,7 +280,7 @@ namespace MWGui
return; return;
} }
if (mEnchanting.getEnchantCost() > mEnchanting.getMaxEnchantValue()) if (mEnchanting.getEnchantPoints() > mEnchanting.getMaxEnchantValue())
{ {
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}"); MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage29}");
return; return;

View file

@ -12,7 +12,7 @@
namespace MWMechanics namespace MWMechanics
{ {
Enchanting::Enchanting(): Enchanting::Enchanting():
mEnchantType(0) mCastStyle(ESM::CastingStyle_CastOnce)
{} {}
void Enchanting::setOldItem(MWWorld::Ptr oldItem) void Enchanting::setOldItem(MWWorld::Ptr oldItem)
@ -41,9 +41,9 @@ namespace MWMechanics
mEffectList=effectList; mEffectList=effectList;
} }
int Enchanting::getEnchantType() const int Enchanting::getCastStyle() const
{ {
return mEnchantType; return mCastStyle;
} }
void Enchanting::setSoulGem(MWWorld::Ptr soulGem) void Enchanting::setSoulGem(MWWorld::Ptr soulGem)
@ -74,12 +74,12 @@ namespace MWMechanics
MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1); MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1);
} }
if(mEnchantType==3) if(mCastStyle==ESM::CastingStyle_ConstantEffect)
{ {
enchantment.mData.mCharge=0; enchantment.mData.mCharge=0;
} }
enchantment.mData.mType = mEnchantType; enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getEnchantCost(); enchantment.mData.mCost = getEnchantPoints();
enchantment.mEffects = mEffectList; enchantment.mEffects = mEffectList;
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment); const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
@ -98,87 +98,142 @@ namespace MWMechanics
return true; return true;
} }
void Enchanting::nextEnchantType() void Enchanting::nextCastStyle()
{ {
mEnchantType++;
if (itemEmpty()) if (itemEmpty())
{ {
mEnchantType = 0; mCastStyle = ESM::CastingStyle_WhenUsed;
return; return;
} }
if ((mObjectType == typeid(ESM::Armor).name())||(mObjectType == typeid(ESM::Clothing).name()))
{ const bool powerfulSoul = getGemCharge() >= \
int soulConstAmount = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt(); MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt();
switch(mEnchantType) if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))
{ // Armor or Clothing
switch(mCastStyle)
{ {
case 1: case ESM::CastingStyle_WhenUsed:
mEnchantType = 2; if (powerfulSoul)
case 3: mCastStyle = ESM::CastingStyle_ConstantEffect;
if(getGemCharge()<soulConstAmount) return;
mEnchantType=2; default: // takes care of Constant effect too
case 4: mCastStyle = ESM::CastingStyle_WhenUsed;
mEnchantType = 2; return;
} }
} }
else if(mObjectType == typeid(ESM::Weapon).name()) else if(mObjectType == typeid(ESM::Weapon).name())
{ { // Weapon
switch(mEnchantType) switch(mCastStyle)
{ {
case 3: case ESM::CastingStyle_WhenStrikes:
mEnchantType = 1; mCastStyle = ESM::CastingStyle_WhenUsed;
return;
case ESM::CastingStyle_WhenUsed:
if (powerfulSoul)
mCastStyle = ESM::CastingStyle_ConstantEffect;
else
mCastStyle = ESM::CastingStyle_WhenStrikes;
return;
default: // takes care of Constant effect too
mCastStyle = ESM::CastingStyle_WhenStrikes;
return;
} }
} }
else if(mObjectType == typeid(ESM::Book).name()) else if(mObjectType == typeid(ESM::Book).name())
{ { // Scroll or Book
mEnchantType=0; mCastStyle = ESM::CastingStyle_CastOnce;
return;
} }
// Fail case
mCastStyle = ESM::CastingStyle_CastOnce;
} }
float Enchanting::getEnchantCost() const /*
* Vanilla enchant cost formula:
*
* Touch/Self: (min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025
* Target: 1.5 * ((min + max) * baseCost * 0.025 * duration + area * baseCost * 0.025)
* Constant eff: (min + max) * baseCost * 2.5 + area * baseCost * 0.025
*
* For multiple effects - cost of each effect is multiplied by number of effects that follows +1.
*
* Note: Minimal value inside formula for 'min' and 'max' is 1. So in vanilla:
* (0 + 0) == (1 + 0) == (1 + 1) => 2 or (2 + 0) == (1 + 2) => 3
*
* Formula on UESPWiki is not entirely correct.
*/
float Enchanting::getEnchantPoints() const
{ {
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); if (mEffectList.mList.empty())
float cost = 0; // No effects added, cost = 0
std::vector<ESM::ENAMstruct> mEffects = mEffectList.mList;
int i=mEffects.size();
if(i<=0)
return 0; return 0;
/* const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
Formula from http://www.uesp.net/wiki/Morrowind:Enchant std::vector<ESM::ENAMstruct> mEffects = mEffectList.mList;
*/
float enchantmentCost = 0;
int effectsLeftCnt = mEffects.size();
float baseCost, magnitudeCost, areaCost;
int magMin, magMax, area;
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it) for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{ {
const ESM::MagicEffect* effect = store.get<ESM::MagicEffect>().find(it->mEffectID); baseCost = (store.get<ESM::MagicEffect>().find(it->mEffectID))->mData.mBaseCost;
// To reflect vanilla behavior
magMin = (it->mMagnMin == 0) ? 1 : it->mMagnMin;
magMax = (it->mMagnMax == 0) ? 1 : it->mMagnMax;
area = (it->mArea == 0) ? 1 : it->mArea;
float cost1 = ((it->mMagnMin + it->mMagnMax)*it->mDuration*effect->mData.mBaseCost*0.025); if (mCastStyle == ESM::CastingStyle_ConstantEffect)
float cost2 = (std::max(1, it->mArea)*0.125*effect->mData.mBaseCost);
if(mEnchantType==3)
{ {
int constDurationMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantDurationMult")->getFloat(); magnitudeCost = (magMin + magMax) * baseCost * 2.5;
cost1 *= constDurationMultipler; }
cost2 *= 2; else
{
magnitudeCost = (magMin + magMax) * it->mDuration * baseCost * 0.025;
if(it->mRange == ESM::RT_Target)
magnitudeCost *= 1.5;
} }
if(effect->mData.mFlags & ESM::MagicEffect::CastTarget)
cost1 *= 1.5;
float fullcost = cost1+cost2; areaCost = area * 0.025 * baseCost;
fullcost*= i; if (it->mRange == ESM::RT_Target)
i--; areaCost *= 1.5;
cost+=fullcost; enchantmentCost += (magnitudeCost + areaCost) * effectsLeftCnt;
--effectsLeftCnt;
} }
return cost;
return enchantmentCost;
} }
float Enchanting::getCastCost() const
{
if (mCastStyle == ESM::CastingStyle_ConstantEffect)
return 0;
const float enchantCost = getEnchantPoints();
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::NpcStats &stats = MWWorld::Class::get(player).getNpcStats(player);
int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified();
/*
* Each point of enchant skill above/under 10 subtracts/adds
* one percent of enchantment cost while minimum is 1.
*/
const float castCost = enchantCost - (enchantCost / 100) * (eSkill - 10);
return (castCost < 1) ? 1 : castCost;
}
int Enchanting::getEnchantPrice() const int Enchanting::getEnchantPrice() const
{ {
if(mEnchanter.isEmpty()) if(mEnchanter.isEmpty())
return 0; return 0;
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->getFloat(); float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->getFloat();
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantCost() * priceMultipler), true); int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantPoints() * priceMultipler), true);
return price; return price;
} }
@ -235,8 +290,8 @@ namespace MWMechanics
(0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified()) (0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified())
+ (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified())); + (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()));
float chance2 = 2.5 * getEnchantCost(); float chance2 = 2.5 * getEnchantPoints();
if(mEnchantType==3) if(mCastStyle==ESM::CastingStyle_ConstantEffect)
{ {
float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat(); float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat();
chance2 /= constantChance; chance2 /= constantChance;

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include <components/esm/effectlist.hpp> #include <components/esm/effectlist.hpp>
#include <components/esm/defs.hpp>
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
namespace MWMechanics namespace MWMechanics
@ -13,7 +14,7 @@ namespace MWMechanics
MWWorld::Ptr mSoulGemPtr; MWWorld::Ptr mSoulGemPtr;
MWWorld::Ptr mEnchanter; MWWorld::Ptr mEnchanter;
int mEnchantType; int mCastStyle;
bool mSelfEnchanting; bool mSelfEnchanting;
@ -33,9 +34,10 @@ namespace MWMechanics
void setEffect(ESM::EffectList effectList); void setEffect(ESM::EffectList effectList);
void setSoulGem(MWWorld::Ptr soulGem); void setSoulGem(MWWorld::Ptr soulGem);
bool create(); //Return true if created, false if failed. bool create(); //Return true if created, false if failed.
void nextEnchantType(); //Set enchant type to next possible type (for mOldItemPtr object) void nextCastStyle(); //Set enchant type to next possible type (for mOldItemPtr object)
int getEnchantType() const; int getCastStyle() const;
float getEnchantCost() const; float getEnchantPoints() const;
float getCastCost() const;
int getEnchantPrice() const; int getEnchantPrice() const;
float getMaxEnchantValue() const; float getMaxEnchantValue() const;
int getGemCharge() const; int getGemCharge() const;

View file

@ -23,6 +23,15 @@ enum RangeType
RT_Target = 2 RT_Target = 2
}; };
// Casting style (in enchanting)
enum CastingStyle
{
CastingStyle_CastOnce = 0,
CastingStyle_WhenStrikes = 1,
CastingStyle_WhenUsed = 2,
CastingStyle_ConstantEffect = 3
};
#pragma pack(push) #pragma pack(push)
#pragma pack(1) #pragma pack(1)