2013-03-28 16:41:00 +00:00
|
|
|
#include "enchanting.hpp"
|
2015-03-15 01:07:47 +00:00
|
|
|
|
2015-04-22 15:58:55 +00:00
|
|
|
#include <components/misc/rng.hpp>
|
2019-12-27 07:21:19 +00:00
|
|
|
#include <components/settings/settings.hpp>
|
2015-03-15 01:07:47 +00:00
|
|
|
|
2013-03-28 16:41:00 +00:00
|
|
|
#include "../mwworld/manualref.hpp"
|
|
|
|
#include "../mwworld/class.hpp"
|
|
|
|
#include "../mwworld/containerstore.hpp"
|
2015-02-09 14:01:49 +00:00
|
|
|
#include "../mwworld/esmstore.hpp"
|
2016-06-17 14:07:16 +00:00
|
|
|
|
2019-02-18 22:10:55 +00:00
|
|
|
#include "../mwbase/world.hpp"
|
|
|
|
#include "../mwbase/environment.hpp"
|
2013-04-02 18:46:48 +00:00
|
|
|
#include "../mwbase/mechanicsmanager.hpp"
|
2013-03-30 18:08:42 +00:00
|
|
|
|
|
|
|
#include "creaturestats.hpp"
|
2020-04-26 17:46:51 +00:00
|
|
|
#include "spellutil.hpp"
|
2015-08-21 09:12:39 +00:00
|
|
|
#include "actorutil.hpp"
|
2019-10-20 10:30:52 +00:00
|
|
|
#include "weapontype.hpp"
|
2013-03-30 18:08:42 +00:00
|
|
|
|
2013-03-28 16:41:00 +00:00
|
|
|
namespace MWMechanics
|
|
|
|
{
|
2013-07-31 16:46:32 +00:00
|
|
|
Enchanting::Enchanting()
|
|
|
|
: mCastStyle(ESM::Enchantment::CastOnce)
|
|
|
|
, mSelfEnchanting(false)
|
2019-11-13 10:47:29 +00:00
|
|
|
, mWeaponType(-1)
|
2013-03-28 16:41:00 +00:00
|
|
|
{}
|
|
|
|
|
2017-04-20 11:36:14 +00:00
|
|
|
void Enchanting::setOldItem(const MWWorld::Ptr& oldItem)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
mOldItemPtr=oldItem;
|
2019-10-20 10:30:52 +00:00
|
|
|
mWeaponType = -1;
|
|
|
|
mObjectType.clear();
|
2013-03-28 16:41:00 +00:00
|
|
|
if(!itemEmpty())
|
|
|
|
{
|
|
|
|
mObjectType = mOldItemPtr.getTypeName();
|
2019-10-20 10:30:52 +00:00
|
|
|
if (mObjectType == typeid(ESM::Weapon).name())
|
|
|
|
mWeaponType = mOldItemPtr.get<ESM::Weapon>()->mBase->mData.mType;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2013-04-01 15:12:47 +00:00
|
|
|
void Enchanting::setNewItemName(const std::string& s)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
mNewItemName=s;
|
|
|
|
}
|
|
|
|
|
2017-04-20 11:36:14 +00:00
|
|
|
void Enchanting::setEffect(const ESM::EffectList& effectList)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
mEffectList=effectList;
|
|
|
|
}
|
|
|
|
|
2013-05-27 12:42:08 +00:00
|
|
|
int Enchanting::getCastStyle() const
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2013-05-27 12:42:08 +00:00
|
|
|
return mCastStyle;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
|
2017-04-20 11:36:14 +00:00
|
|
|
void Enchanting::setSoulGem(const MWWorld::Ptr& soulGem)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
mSoulGemPtr=soulGem;
|
|
|
|
}
|
|
|
|
|
2013-04-01 15:12:47 +00:00
|
|
|
bool Enchanting::create()
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2015-08-21 09:12:39 +00:00
|
|
|
const MWWorld::Ptr& player = getPlayer();
|
2014-05-22 18:37:22 +00:00
|
|
|
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
2013-04-01 15:12:47 +00:00
|
|
|
ESM::Enchantment enchantment;
|
2020-04-10 21:02:12 +00:00
|
|
|
enchantment.mData.mFlags = 0;
|
2014-10-01 22:32:22 +00:00
|
|
|
enchantment.mData.mType = mCastStyle;
|
2015-01-07 03:28:56 +00:00
|
|
|
enchantment.mData.mCost = getBaseCastCost();
|
2013-04-02 20:23:38 +00:00
|
|
|
|
2013-08-12 23:19:33 +00:00
|
|
|
store.remove(mSoulGemPtr, 1, player);
|
2013-04-08 15:53:41 +00:00
|
|
|
|
|
|
|
//Exception for Azura Star, new one will be added after enchanting
|
2014-01-14 08:47:31 +00:00
|
|
|
if(Misc::StringUtils::ciEqual(mSoulGemPtr.get<ESM::Miscellaneous>()->mBase->mId, "Misc_SoulGem_Azura"))
|
2013-11-21 03:11:06 +00:00
|
|
|
store.add("Misc_SoulGem_Azura", 1, player);
|
2013-03-29 11:00:09 +00:00
|
|
|
|
2013-03-30 18:08:42 +00:00
|
|
|
if(mSelfEnchanting)
|
|
|
|
{
|
2015-04-22 15:58:55 +00:00
|
|
|
if(getEnchantChance() <= (Misc::Rng::roll0to99()))
|
2013-04-01 15:12:47 +00:00
|
|
|
return false;
|
2013-03-30 18:08:42 +00:00
|
|
|
|
2014-05-22 18:37:22 +00:00
|
|
|
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
|
2013-03-30 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 15:12:47 +00:00
|
|
|
enchantment.mEffects = mEffectList;
|
2013-03-31 21:18:23 +00:00
|
|
|
|
2019-12-27 07:21:19 +00:00
|
|
|
int count = getEnchantItemsCount();
|
|
|
|
|
|
|
|
if(mCastStyle==ESM::Enchantment::ConstantEffect)
|
|
|
|
enchantment.mData.mCharge = 0;
|
|
|
|
else
|
|
|
|
enchantment.mData.mCharge = getGemCharge() / count;
|
|
|
|
|
|
|
|
// Try to find a dynamic enchantment with the same stats, create a new one if not found.
|
|
|
|
const ESM::Enchantment* enchantmentPtr = getRecord(enchantment);
|
|
|
|
if (enchantmentPtr == nullptr)
|
|
|
|
enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
|
|
|
|
|
2013-08-12 23:19:33 +00:00
|
|
|
// Apply the enchantment
|
2014-05-24 12:48:37 +00:00
|
|
|
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
|
2013-03-31 21:18:23 +00:00
|
|
|
|
2013-08-12 23:19:33 +00:00
|
|
|
// Add the new item to player inventory and remove the old one
|
2019-12-27 07:21:19 +00:00
|
|
|
store.remove(mOldItemPtr, count, player);
|
|
|
|
store.add(newItemId, count, player);
|
2013-04-02 18:46:48 +00:00
|
|
|
|
2013-04-03 16:02:30 +00:00
|
|
|
if(!mSelfEnchanting)
|
|
|
|
payForEnchantment();
|
2013-03-28 16:41:00 +00:00
|
|
|
|
2013-04-01 15:12:47 +00:00
|
|
|
return true;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
|
2013-05-27 12:42:08 +00:00
|
|
|
void Enchanting::nextCastStyle()
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
if (itemEmpty())
|
|
|
|
return;
|
2013-05-27 14:08:58 +00:00
|
|
|
|
|
|
|
const bool powerfulSoul = getGemCharge() >= \
|
2018-08-29 15:38:12 +00:00
|
|
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->mValue.getInteger();
|
2013-05-27 14:08:58 +00:00
|
|
|
if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))
|
|
|
|
{ // Armor or Clothing
|
2013-05-27 12:42:08 +00:00
|
|
|
switch(mCastStyle)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2013-05-28 22:01:18 +00:00
|
|
|
case ESM::Enchantment::WhenUsed:
|
2013-05-27 14:08:58 +00:00
|
|
|
if (powerfulSoul)
|
2013-05-28 22:01:18 +00:00
|
|
|
mCastStyle = ESM::Enchantment::ConstantEffect;
|
2013-05-27 14:08:58 +00:00
|
|
|
return;
|
|
|
|
default: // takes care of Constant effect too
|
2013-05-28 22:01:18 +00:00
|
|
|
mCastStyle = ESM::Enchantment::WhenUsed;
|
2013-05-27 14:08:58 +00:00
|
|
|
return;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
}
|
2019-10-20 10:30:52 +00:00
|
|
|
else if (mWeaponType != -1)
|
2013-05-27 14:08:58 +00:00
|
|
|
{ // Weapon
|
2019-12-27 07:21:19 +00:00
|
|
|
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
|
2013-05-27 12:42:08 +00:00
|
|
|
switch(mCastStyle)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2013-05-28 22:01:18 +00:00
|
|
|
case ESM::Enchantment::WhenStrikes:
|
2019-12-27 07:21:19 +00:00
|
|
|
if (weapclass == ESM::WeaponType::Melee || weapclass == ESM::WeaponType::Ranged)
|
|
|
|
mCastStyle = ESM::Enchantment::WhenUsed;
|
2013-05-27 18:47:53 +00:00
|
|
|
return;
|
2013-05-28 22:01:18 +00:00
|
|
|
case ESM::Enchantment::WhenUsed:
|
2019-12-27 07:21:19 +00:00
|
|
|
if (powerfulSoul && weapclass != ESM::WeaponType::Ammo && weapclass != ESM::WeaponType::Thrown)
|
2013-05-28 22:01:18 +00:00
|
|
|
mCastStyle = ESM::Enchantment::ConstantEffect;
|
2019-12-27 07:21:19 +00:00
|
|
|
else if (weapclass != ESM::WeaponType::Ranged)
|
2013-05-28 22:01:18 +00:00
|
|
|
mCastStyle = ESM::Enchantment::WhenStrikes;
|
2013-05-27 18:47:53 +00:00
|
|
|
return;
|
2013-05-27 14:08:58 +00:00
|
|
|
default: // takes care of Constant effect too
|
2019-10-20 10:30:52 +00:00
|
|
|
mCastStyle = ESM::Enchantment::WhenUsed;
|
2019-12-27 07:21:19 +00:00
|
|
|
if (weapclass != ESM::WeaponType::Ranged)
|
2019-10-20 10:30:52 +00:00
|
|
|
mCastStyle = ESM::Enchantment::WhenStrikes;
|
2013-05-27 18:47:53 +00:00
|
|
|
return;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else if(mObjectType == typeid(ESM::Book).name())
|
2013-05-27 14:08:58 +00:00
|
|
|
{ // Scroll or Book
|
2013-05-28 22:01:18 +00:00
|
|
|
mCastStyle = ESM::Enchantment::CastOnce;
|
2013-05-27 14:08:58 +00:00
|
|
|
return;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
2013-05-27 14:08:58 +00:00
|
|
|
|
|
|
|
// Fail case
|
2013-05-28 22:01:18 +00:00
|
|
|
mCastStyle = ESM::Enchantment::CastOnce;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
|
2013-05-27 18:47:53 +00:00
|
|
|
/*
|
|
|
|
* 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.
|
|
|
|
*/
|
2019-05-13 22:40:04 +00:00
|
|
|
float Enchanting::getEnchantPoints(bool precise) const
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2013-05-27 18:47:53 +00:00
|
|
|
if (mEffectList.mList.empty())
|
|
|
|
// No effects added, cost = 0
|
|
|
|
return 0;
|
2013-05-27 12:42:08 +00:00
|
|
|
|
2013-03-28 16:41:00 +00:00
|
|
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
2019-05-13 22:40:04 +00:00
|
|
|
const float fEffectCostMult = store.get<ESM::GameSetting>().find("fEffectCostMult")->mValue.getFloat();
|
|
|
|
const float fEnchantmentConstantDurationMult = store.get<ESM::GameSetting>().find("fEnchantmentConstantDurationMult")->mValue.getFloat();
|
2013-03-30 18:08:42 +00:00
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
float enchantmentCost = 0.f;
|
|
|
|
float cost = 0.f;
|
|
|
|
for (const ESM::ENAMstruct& effect : mEffectList.mList)
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2019-05-13 22:40:04 +00:00
|
|
|
float baseCost = (store.get<ESM::MagicEffect>().find(effect.mEffectID))->mData.mBaseCost;
|
|
|
|
int magMin = std::max(1, effect.mMagnMin);
|
|
|
|
int magMax = std::max(1, effect.mMagnMax);
|
|
|
|
int area = std::max(1, effect.mArea);
|
|
|
|
float duration = static_cast<float>(effect.mDuration);
|
2013-05-28 22:01:18 +00:00
|
|
|
if (mCastStyle == ESM::Enchantment::ConstantEffect)
|
2019-05-13 22:40:04 +00:00
|
|
|
duration = fEnchantmentConstantDurationMult;
|
2013-03-28 16:41:00 +00:00
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
cost += ((magMin + magMax) * duration + area) * baseCost * fEffectCostMult * 0.05f;
|
2017-05-26 19:42:11 +00:00
|
|
|
|
|
|
|
cost = std::max(1.f, cost);
|
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
if (effect.mRange == ESM::RT_Target)
|
|
|
|
cost *= 1.5f;
|
2015-01-12 22:28:52 +00:00
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
enchantmentCost += precise ? cost : std::floor(cost);
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
2013-05-27 12:42:08 +00:00
|
|
|
|
2017-05-26 19:42:11 +00:00
|
|
|
return enchantmentCost;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
2013-04-02 18:46:48 +00:00
|
|
|
|
2019-12-27 07:21:19 +00:00
|
|
|
const ESM::Enchantment* Enchanting::getRecord(const ESM::Enchantment& toFind) const
|
|
|
|
{
|
|
|
|
const MWWorld::Store<ESM::Enchantment>& enchantments = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>();
|
|
|
|
MWWorld::Store<ESM::Enchantment>::iterator iter (enchantments.begin());
|
|
|
|
iter += (enchantments.getSize() - enchantments.getDynamicSize());
|
|
|
|
for (; iter != enchantments.end(); ++iter)
|
|
|
|
{
|
|
|
|
if (iter->mEffects.mList.size() != toFind.mEffects.mList.size())
|
|
|
|
continue;
|
|
|
|
|
2020-04-10 21:02:12 +00:00
|
|
|
if (iter->mData.mFlags != toFind.mData.mFlags
|
2019-12-27 07:21:19 +00:00
|
|
|
|| iter->mData.mType != toFind.mData.mType
|
|
|
|
|| iter->mData.mCost != toFind.mData.mCost
|
|
|
|
|| iter->mData.mCharge != toFind.mData.mCharge)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// Don't choose an ID that came from the content files, would have unintended side effects
|
|
|
|
if (!enchantments.isDynamic(iter->mId))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
bool mismatch = false;
|
|
|
|
|
|
|
|
for (int i=0; i<static_cast<int> (iter->mEffects.mList.size()); ++i)
|
|
|
|
{
|
|
|
|
const ESM::ENAMstruct& first = iter->mEffects.mList[i];
|
|
|
|
const ESM::ENAMstruct& second = toFind.mEffects.mList[i];
|
|
|
|
|
|
|
|
if (first.mEffectID!=second.mEffectID ||
|
|
|
|
first.mArea!=second.mArea ||
|
|
|
|
first.mRange!=second.mRange ||
|
|
|
|
first.mSkill!=second.mSkill ||
|
|
|
|
first.mAttribute!=second.mAttribute ||
|
|
|
|
first.mMagnMin!=second.mMagnMin ||
|
|
|
|
first.mMagnMax!=second.mMagnMax ||
|
|
|
|
first.mDuration!=second.mDuration)
|
|
|
|
{
|
|
|
|
mismatch = true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!mismatch)
|
|
|
|
return &(*iter);
|
|
|
|
}
|
|
|
|
|
|
|
|
return nullptr;
|
|
|
|
}
|
2013-05-27 16:08:12 +00:00
|
|
|
|
2015-01-07 03:28:56 +00:00
|
|
|
int Enchanting::getBaseCastCost() const
|
2013-05-27 16:08:12 +00:00
|
|
|
{
|
2013-05-28 22:01:18 +00:00
|
|
|
if (mCastStyle == ESM::Enchantment::ConstantEffect)
|
2013-05-27 18:47:53 +00:00
|
|
|
return 0;
|
2013-05-27 18:16:57 +00:00
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
return static_cast<int>(getEnchantPoints(false));
|
2015-01-07 03:28:56 +00:00
|
|
|
}
|
2013-05-27 16:08:12 +00:00
|
|
|
|
2015-01-07 03:28:56 +00:00
|
|
|
int Enchanting::getEffectiveCastCost() const
|
|
|
|
{
|
|
|
|
int baseCost = getBaseCastCost();
|
2015-08-21 09:12:39 +00:00
|
|
|
MWWorld::Ptr player = getPlayer();
|
2015-03-08 04:42:07 +00:00
|
|
|
return getEffectiveEnchantmentCastCost(static_cast<float>(baseCost), player);
|
2013-05-27 16:08:12 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
2013-04-02 18:46:48 +00:00
|
|
|
int Enchanting::getEnchantPrice() const
|
|
|
|
{
|
|
|
|
if(mEnchanter.isEmpty())
|
|
|
|
return 0;
|
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->mValue.getFloat();
|
2015-03-08 04:42:07 +00:00
|
|
|
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true);
|
2019-12-27 07:21:19 +00:00
|
|
|
price *= getEnchantItemsCount() * getTypeMultiplier();
|
2020-09-08 12:55:12 +00:00
|
|
|
return std::max(1, price);
|
2013-04-02 18:46:48 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 15:12:47 +00:00
|
|
|
int Enchanting::getGemCharge() const
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
if(soulEmpty())
|
|
|
|
return 0;
|
2014-05-25 12:13:07 +00:00
|
|
|
if(mSoulGemPtr.getCellRef().getSoul()=="")
|
2013-03-28 16:41:00 +00:00
|
|
|
return 0;
|
2018-03-08 23:38:04 +00:00
|
|
|
const ESM::Creature* soul = store.get<ESM::Creature>().search(mSoulGemPtr.getCellRef().getSoul());
|
|
|
|
if(soul)
|
|
|
|
return soul->mData.mSoul;
|
|
|
|
else
|
|
|
|
return 0;
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
|
2014-12-10 16:30:33 +00:00
|
|
|
int Enchanting::getMaxEnchantValue() const
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
|
|
|
if (itemEmpty())
|
|
|
|
return 0;
|
2014-01-27 00:58:04 +00:00
|
|
|
|
|
|
|
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
|
|
|
|
2018-08-29 15:38:12 +00:00
|
|
|
return static_cast<int>(mOldItemPtr.getClass().getEnchantmentPoints(mOldItemPtr) * store.get<ESM::GameSetting>().find("fEnchantmentMult")->mValue.getFloat());
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
2013-04-01 15:12:47 +00:00
|
|
|
bool Enchanting::soulEmpty() const
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2013-05-28 22:01:18 +00:00
|
|
|
return mSoulGemPtr.isEmpty();
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
|
|
|
|
2013-04-01 15:12:47 +00:00
|
|
|
bool Enchanting::itemEmpty() const
|
2013-03-28 16:41:00 +00:00
|
|
|
{
|
2013-05-28 22:01:18 +00:00
|
|
|
return mOldItemPtr.isEmpty();
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|
2013-03-30 18:08:42 +00:00
|
|
|
|
|
|
|
void Enchanting::setSelfEnchanting(bool selfEnchanting)
|
|
|
|
{
|
|
|
|
mSelfEnchanting = selfEnchanting;
|
|
|
|
}
|
|
|
|
|
2017-04-20 11:36:14 +00:00
|
|
|
void Enchanting::setEnchanter(const MWWorld::Ptr& enchanter)
|
2013-03-30 18:08:42 +00:00
|
|
|
{
|
|
|
|
mEnchanter = enchanter;
|
2019-06-28 12:52:37 +00:00
|
|
|
// Reset cast style
|
|
|
|
mCastStyle = ESM::Enchantment::CastOnce;
|
2013-03-30 18:08:42 +00:00
|
|
|
}
|
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
int Enchanting::getEnchantChance() const
|
2013-03-30 18:08:42 +00:00
|
|
|
{
|
2018-10-08 14:06:30 +00:00
|
|
|
const CreatureStats& stats = mEnchanter.getClass().getCreatureStats(mEnchanter);
|
2014-01-27 00:58:04 +00:00
|
|
|
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
2019-05-13 22:40:04 +00:00
|
|
|
const float a = static_cast<float>(mEnchanter.getClass().getSkill(mEnchanter, ESM::Skill::Enchant));
|
|
|
|
const float b = static_cast<float>(stats.getAttribute (ESM::Attribute::Intelligence).getModified());
|
|
|
|
const float c = static_cast<float>(stats.getAttribute (ESM::Attribute::Luck).getModified());
|
|
|
|
const float fEnchantmentChanceMult = gmst.find("fEnchantmentChanceMult")->mValue.getFloat();
|
|
|
|
const float fEnchantmentConstantChanceMult = gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat();
|
2014-01-27 00:58:04 +00:00
|
|
|
|
2019-12-27 07:21:19 +00:00
|
|
|
float x = (a - getEnchantPoints() * fEnchantmentChanceMult * getTypeMultiplier() * getEnchantItemsCount() + 0.2f * b + 0.1f * c) * stats.getFatigueTerm();
|
2019-05-13 22:40:04 +00:00
|
|
|
if (mCastStyle == ESM::Enchantment::ConstantEffect)
|
|
|
|
x *= fEnchantmentConstantChanceMult;
|
2013-03-30 18:08:42 +00:00
|
|
|
|
2019-05-13 22:40:04 +00:00
|
|
|
return static_cast<int>(x);
|
2013-03-30 18:08:42 +00:00
|
|
|
}
|
2013-04-02 18:46:48 +00:00
|
|
|
|
2019-12-27 07:21:19 +00:00
|
|
|
int Enchanting::getEnchantItemsCount() const
|
|
|
|
{
|
|
|
|
int count = 1;
|
|
|
|
float enchantPoints = getEnchantPoints();
|
|
|
|
if (mWeaponType != -1 && enchantPoints > 0)
|
|
|
|
{
|
|
|
|
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
|
|
|
|
if (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)
|
|
|
|
{
|
|
|
|
static const float multiplier = std::max(0.f, std::min(1.0f, Settings::Manager::getFloat("projectiles enchant multiplier", "Game")));
|
|
|
|
MWWorld::Ptr player = getPlayer();
|
|
|
|
int itemsInInventoryCount = player.getClass().getContainerStore(player).count(mOldItemPtr.getCellRef().getRefId());
|
|
|
|
count = std::min(itemsInInventoryCount, std::max(1, int(getGemCharge() * multiplier / enchantPoints)));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return count;
|
|
|
|
}
|
|
|
|
|
|
|
|
float Enchanting::getTypeMultiplier() const
|
|
|
|
{
|
|
|
|
static const bool useMultiplier = Settings::Manager::getFloat("projectiles enchant multiplier", "Game") > 0;
|
|
|
|
if (useMultiplier && mWeaponType != -1 && getEnchantPoints() > 0)
|
|
|
|
{
|
|
|
|
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
|
|
|
|
if (weapclass == ESM::WeaponType::Thrown || weapclass == ESM::WeaponType::Ammo)
|
|
|
|
return 0.125f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1.f;
|
|
|
|
}
|
|
|
|
|
2013-04-02 18:46:48 +00:00
|
|
|
void Enchanting::payForEnchantment() const
|
|
|
|
{
|
2015-08-21 09:12:39 +00:00
|
|
|
const MWWorld::Ptr& player = getPlayer();
|
2014-05-22 18:37:22 +00:00
|
|
|
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
2013-04-02 18:46:48 +00:00
|
|
|
|
2014-01-08 22:37:46 +00:00
|
|
|
store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player);
|
2014-07-27 22:55:57 +00:00
|
|
|
|
|
|
|
// add gold to NPC trading gold pool
|
|
|
|
CreatureStats& enchanterStats = mEnchanter.getClass().getCreatureStats(mEnchanter);
|
|
|
|
enchanterStats.setGoldPool(enchanterStats.getGoldPool() + getEnchantPrice());
|
2013-04-02 18:46:48 +00:00
|
|
|
}
|
2013-03-28 16:41:00 +00:00
|
|
|
}
|