1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-15 20:49:56 +00:00
openmw-tes3mp/apps/openmw/mwmechanics/enchanting.cpp

301 lines
10 KiB
C++
Raw Normal View History

2013-03-28 16:41:00 +00:00
#include "enchanting.hpp"
2015-04-22 15:58:55 +00:00
#include <components/misc/rng.hpp>
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"
#include "spellcasting.hpp"
2015-08-21 09:12:39 +00:00
#include "actorutil.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)
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;
if(!itemEmpty())
{
mObjectType = mOldItemPtr.getTypeName();
}
else
{
mObjectType="";
}
}
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;
}
int Enchanting::getCastStyle() const
2013-03-28 16:41:00 +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;
}
bool Enchanting::create()
2013-03-28 16:41:00 +00:00
{
2015-08-21 09:12:39 +00:00
const MWWorld::Ptr& player = getPlayer();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
ESM::Enchantment enchantment;
enchantment.mData.mCharge = getGemCharge();
enchantment.mData.mAutocalc = 0;
enchantment.mData.mType = mCastStyle;
enchantment.mData.mCost = getBaseCastCost();
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"))
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()))
return false;
2013-03-30 18:08:42 +00:00
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
2013-03-30 18:08:42 +00:00
}
if(mCastStyle==ESM::Enchantment::ConstantEffect)
2013-03-28 16:41:00 +00:00
{
enchantment.mData.mCharge=0;
2013-03-28 16:41:00 +00:00
}
enchantment.mEffects = mEffectList;
2013-03-31 21:18:23 +00:00
// Apply the enchantment
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
2013-03-31 21:18:23 +00:00
// Add the new item to player inventory and remove the old one
store.remove(mOldItemPtr, 1, player);
store.add(newItemId, 1, 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
return true;
2013-03-28 16:41:00 +00:00
}
void Enchanting::nextCastStyle()
2013-03-28 16:41:00 +00:00
{
if (itemEmpty())
{
mCastStyle = ESM::Enchantment::WhenUsed;
2013-03-28 16:41:00 +00:00
return;
}
const bool powerfulSoul = getGemCharge() >= \
2018-08-29 15:38:12 +00:00
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("iSoulAmountForConstantEffect")->mValue.getInteger();
if ((mObjectType == typeid(ESM::Armor).name()) || (mObjectType == typeid(ESM::Clothing).name()))
{ // Armor or Clothing
switch(mCastStyle)
2013-03-28 16:41:00 +00:00
{
case ESM::Enchantment::WhenUsed:
if (powerfulSoul)
mCastStyle = ESM::Enchantment::ConstantEffect;
return;
default: // takes care of Constant effect too
mCastStyle = ESM::Enchantment::WhenUsed;
return;
2013-03-28 16:41:00 +00:00
}
}
else if(mObjectType == typeid(ESM::Weapon).name())
{ // Weapon
switch(mCastStyle)
2013-03-28 16:41:00 +00:00
{
case ESM::Enchantment::WhenStrikes:
mCastStyle = ESM::Enchantment::WhenUsed;
2013-05-27 18:47:53 +00:00
return;
case ESM::Enchantment::WhenUsed:
2013-05-27 18:47:53 +00:00
if (powerfulSoul)
mCastStyle = ESM::Enchantment::ConstantEffect;
2013-05-27 18:47:53 +00:00
else
mCastStyle = ESM::Enchantment::WhenStrikes;
2013-05-27 18:47:53 +00:00
return;
default: // takes care of Constant effect too
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())
{ // Scroll or Book
mCastStyle = ESM::Enchantment::CastOnce;
return;
2013-03-28 16:41:00 +00:00
}
// Fail case
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.
*/
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-03-28 16:41:00 +00:00
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
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
float enchantmentCost = 0.f;
float cost = 0.f;
for (const ESM::ENAMstruct& effect : mEffectList.mList)
2013-03-28 16:41:00 +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);
if (mCastStyle == ESM::Enchantment::ConstantEffect)
duration = fEnchantmentConstantDurationMult;
2013-03-28 16:41:00 +00:00
cost += ((magMin + magMax) * duration + area) * baseCost * fEffectCostMult * 0.05f;
cost = std::max(1.f, cost);
if (effect.mRange == ESM::RT_Target)
cost *= 1.5f;
2015-01-12 22:28:52 +00:00
enchantmentCost += precise ? cost : std::floor(cost);
2013-03-28 16:41:00 +00:00
}
return enchantmentCost;
2013-03-28 16:41:00 +00:00
}
2013-04-02 18:46:48 +00:00
2013-05-27 16:08:12 +00:00
int Enchanting::getBaseCastCost() const
2013-05-27 16:08:12 +00:00
{
if (mCastStyle == ESM::Enchantment::ConstantEffect)
2013-05-27 18:47:53 +00:00
return 0;
return static_cast<int>(getEnchantPoints(false));
}
2013-05-27 16:08:12 +00:00
int Enchanting::getEffectiveCastCost() const
{
int baseCost = getBaseCastCost();
2015-08-21 09:12:39 +00:00
MWWorld::Ptr player = getPlayer();
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();
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true);
2013-04-02 18:46:48 +00:00
return price;
}
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;
if(mSoulGemPtr.getCellRef().getSoul()=="")
2013-03-28 16:41:00 +00:00
return 0;
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
}
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
}
bool Enchanting::soulEmpty() const
2013-03-28 16:41:00 +00:00
{
return mSoulGemPtr.isEmpty();
2013-03-28 16:41:00 +00:00
}
bool Enchanting::itemEmpty() const
2013-03-28 16:41:00 +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;
}
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>();
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
float x = (a - getEnchantPoints()*fEnchantmentChanceMult + 0.2f * b + 0.1f * c) * stats.getFatigueTerm();
if (mCastStyle == ESM::Enchantment::ConstantEffect)
x *= fEnchantmentConstantChanceMult;
2013-03-30 18:08:42 +00:00
return static_cast<int>(x);
2013-03-30 18:08:42 +00:00
}
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();
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
2013-04-02 18:46:48 +00:00
store.remove(MWWorld::ContainerStore::sGoldId, getEnchantPrice(), player);
// 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
}