You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openmw-tes3mp/apps/openmw/mwmechanics/enchanting.cpp

285 lines
9.1 KiB
C++

12 years ago
#include "enchanting.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/manualref.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "creaturestats.hpp"
#include "npcstats.hpp"
#include <boost/algorithm/string.hpp>
12 years ago
namespace MWMechanics
{
Enchanting::Enchanting():
mCastStyle(ESM::CS_CastOnce)
12 years ago
{}
void Enchanting::setOldItem(MWWorld::Ptr oldItem)
{
mOldItemPtr=oldItem;
if(!itemEmpty())
{
mObjectType = mOldItemPtr.getTypeName();
mOldItemId = mOldItemPtr.getCellRef().mRefID;
mOldItemCount = mOldItemPtr.getRefData().getCount();
12 years ago
}
else
{
mObjectType="";
mOldItemId="";
}
}
void Enchanting::setNewItemName(const std::string& s)
12 years ago
{
mNewItemName=s;
}
void Enchanting::setEffect(ESM::EffectList effectList)
{
mEffectList=effectList;
}
int Enchanting::getCastStyle() const
12 years ago
{
return mCastStyle;
12 years ago
}
void Enchanting::setSoulGem(MWWorld::Ptr soulGem)
{
mSoulGemPtr=soulGem;
}
bool Enchanting::create()
12 years ago
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
ESM::Enchantment enchantment;
enchantment.mData.mCharge = getGemCharge();
mSoulGemPtr.getRefData().setCount (mSoulGemPtr.getRefData().getCount()-1);
//Exception for Azura Star, new one will be added after enchanting
if(boost::iequals(mSoulGemPtr.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
{
MWWorld::ManualRef azura (MWBase::Environment::get().getWorld()->getStore(), "Misc_SoulGem_Azura");
MWWorld::Class::get (player).getContainerStore (player).add (azura.getPtr());
}
if(mSelfEnchanting)
{
if(getEnchantChance()<std::rand()/static_cast<double> (RAND_MAX)*100)
return false;
MWWorld::Class::get (mEnchanter).skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 1);
}
if(mCastStyle==ESM::CS_ConstantEffect)
12 years ago
{
enchantment.mData.mCharge=0;
12 years ago
}
enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getEnchantCost();
enchantment.mEffects = mEffectList;
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
12 years ago
MWWorld::Class::get(mOldItemPtr).applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
mOldItemPtr.getRefData().setCount(1);
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), mOldItemId);
ref.getPtr().getRefData().setCount (mOldItemCount-1);
MWWorld::Class::get (player).getContainerStore (player).add (ref.getPtr());
if(!mSelfEnchanting)
payForEnchantment();
12 years ago
return true;
12 years ago
}
void Enchanting::nextCastStyle()
12 years ago
{
mCastStyle++;
12 years ago
if (itemEmpty())
{
mCastStyle = 0;
12 years ago
return;
}
if ((mObjectType == typeid(ESM::Armor).name())||(mObjectType == typeid(ESM::Clothing).name()))
{
int soulConstAmount = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->getInt();
switch(mCastStyle)
12 years ago
{
case 1:
mCastStyle = 2;
12 years ago
case 3:
if(getGemCharge()<soulConstAmount)
mCastStyle=2;
12 years ago
case 4:
mCastStyle = 2;
12 years ago
}
}
else if(mObjectType == typeid(ESM::Weapon).name())
{
switch(mCastStyle)
12 years ago
{
case 3:
mCastStyle = 1;
12 years ago
}
}
else if(mObjectType == typeid(ESM::Book).name())
{
mCastStyle=0;
12 years ago
}
}
/*
* 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::getEnchantCost() const
12 years ago
{
if (mEffectList.mList.empty())
// No effects added, cost = 0
return 0;
12 years ago
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
std::vector<ESM::ENAMstruct> mEffects = mEffectList.mList;
float enchantmentCost = 0;
int effectsLeftCnt = mEffects.size();
float baseCost, magnitudeCost, areaCost;
int magMin, magMax, area;
12 years ago
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
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;
12 years ago
if (mCastStyle == ESM::CS_ConstantEffect)
12 years ago
{
magnitudeCost = (magMin + magMax) * baseCost * 2.5;
}
else
{
magnitudeCost = (magMin + magMax) * it->mDuration * baseCost * 0.025;
if(it->mRange == ESM::RT_Target)
magnitudeCost *= 1.5;
12 years ago
}
areaCost = area * 0.025 * baseCost;
if (it->mRange == ESM::RT_Target)
areaCost *= 1.5;
12 years ago
enchantmentCost += (magnitudeCost + areaCost) * effectsLeftCnt;
--effectsLeftCnt;
12 years ago
}
return enchantmentCost;
12 years ago
}
int Enchanting::getEnchantPrice() const
{
if(mEnchanter.isEmpty())
return 0;
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->getFloat();
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, (getEnchantCost() * priceMultipler), true);
return price;
}
int Enchanting::getGemCharge() const
12 years ago
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
if(soulEmpty())
return 0;
if(mSoulGemPtr.getCellRef().mSoul=="")
return 0;
const ESM::Creature* soul = store.get<ESM::Creature>().find(mSoulGemPtr.getCellRef().mSoul);
return soul->mData.mSoul;
}
float Enchanting::getMaxEnchantValue() const
12 years ago
{
if (itemEmpty())
return 0;
return MWWorld::Class::get(mOldItemPtr).getEnchantmentPoints(mOldItemPtr);
}
bool Enchanting::soulEmpty() const
12 years ago
{
if (mSoulGemPtr.isEmpty())
return true;
return false;
}
bool Enchanting::itemEmpty() const
12 years ago
{
if(mOldItemPtr.isEmpty())
return true;
return false;
}
void Enchanting::setSelfEnchanting(bool selfEnchanting)
{
mSelfEnchanting = selfEnchanting;
}
void Enchanting::setEnchanter(MWWorld::Ptr enchanter)
{
mEnchanter = enchanter;
}
float Enchanting::getEnchantChance() const
{
/*
Formula from http://www.uesp.net/wiki/Morrowind:Enchant
*/
const CreatureStats& creatureStats = MWWorld::Class::get (mEnchanter).getCreatureStats (mEnchanter);
const NpcStats& npcStats = MWWorld::Class::get (mEnchanter).getNpcStats (mEnchanter);
float chance1 = (npcStats.getSkill (ESM::Skill::Enchant).getModified() +
(0.25 * creatureStats.getAttribute (ESM::Attribute::Intelligence).getModified())
+ (0.125 * creatureStats.getAttribute (ESM::Attribute::Luck).getModified()));
float chance2 = 2.5 * getEnchantCost();
if(mCastStyle==ESM::CS_ConstantEffect)
{
float constantChance = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentConstantChanceMult")->getFloat();
chance2 /= constantChance;
}
return (chance1-chance2);
}
void Enchanting::payForEnchantment() const
{
MWWorld::Ptr gold;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWWorld::ContainerStore& store = MWWorld::Class::get(player).getContainerStore(player);
for (MWWorld::ContainerStoreIterator it = store.begin();
it != store.end(); ++it)
{
if (Misc::StringUtils::ciEqual(it->getCellRef().mRefID, "gold_001"))
{
gold = *it;
}
}
gold.getRefData().setCount(gold.getRefData().getCount() - getEnchantPrice());
}
12 years ago
}