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
|
|
|
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
/*
|
|
|
|
Start of tes3mp addition
|
|
|
|
|
|
|
|
Include additional headers for multiplayer purposes
|
|
|
|
*/
|
2019-08-19 18:39:33 +00:00
|
|
|
#include <components/openmw-mp/TimedLog.hpp>
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
#include "../mwmp/Main.hpp"
|
|
|
|
#include "../mwmp/Networking.hpp"
|
2020-04-19 20:17:09 +00:00
|
|
|
#include "../mwmp/LocalPlayer.hpp"
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
#include "../mwmp/Worldstate.hpp"
|
|
|
|
/*
|
|
|
|
End of tes3mp addition
|
|
|
|
*/
|
|
|
|
|
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
|
2013-03-31 21:18:23 +00:00
|
|
|
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
/*
|
|
|
|
Start of tes3mp change (major)
|
|
|
|
|
|
|
|
Send the enchantment's record to the server
|
|
|
|
|
|
|
|
Don't add the new item to the player's inventory and instead expect the server to
|
|
|
|
add it
|
2020-04-19 20:17:09 +00:00
|
|
|
|
|
|
|
Store the quantity used for the enchantment so it can be retrieved in applyEnchantment()
|
|
|
|
when applicable
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
|
2020-04-19 20:17:09 +00:00
|
|
|
The applyEnchantment() method is where the record of the newly enchanted item will be sent
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
to the server, causing the server to send back the player's inventory with the new item
|
|
|
|
included
|
|
|
|
*/
|
|
|
|
mwmp::Main::get().getNetworking()->getWorldstate()->sendEnchantmentRecord(enchantmentPtr);
|
|
|
|
|
2019-12-27 07:21:19 +00:00
|
|
|
store.remove(mOldItemPtr, count, player);
|
2013-04-02 18:46:48 +00:00
|
|
|
|
2018-08-21 07:10:06 +00:00
|
|
|
if(!mSelfEnchanting)
|
2013-04-03 16:02:30 +00:00
|
|
|
payForEnchantment();
|
2013-03-28 16:41:00 +00:00
|
|
|
|
2020-04-19 20:17:09 +00:00
|
|
|
mwmp::Main::get().getLocalPlayer()->storeLastEnchantmentQuantity(count);
|
|
|
|
|
[General] Implement RecordDynamic packet, part 1
Spell, potion, enchantment, creature, NPC, armor, book, clothing, miscellaneous and weapon record data can now be sent in a RecordDynamic packet. Additionally, the packets include data related to associated magical effects (for spells, potions and enchantments), data related to default inventory contents (for creatures and NPCs) and data related to body parts affected (for armor and clothing).
The server now has associated script functions for setting most of the details of the above, with the main exception being individual creature and NPC stats.
Records can either be created entirely from scratch or can use an existing record (set via the baseId variable) as a starting point for their values. In the latter case, only the values that are specifically set override the starting values. Creature and NPC records also have an inventoryBaseId that can be used on top of the baseId to base their inventories on another existing record.
The client's RecordHelper class has been heavily expanded to allow for the above mentioned functionality.
When players create spells, potions and enchantments as part of regular gameplay, they send RecordDynamic packets that provide the server with the complete details of the records that should be created. When they create enchantments, they also provide the server with armor, book, clothing and weapon records corresponding to the items they've enchanted.
This functionality added by this packet was originally supposed to be exclusive to the rewrite, but I've gone ahead and tried to provide it for the pre-rewrite in a way that can mostly be reused for the rewrite.
2018-07-30 07:56:26 +00:00
|
|
|
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
|
|
|
|
/*
|
|
|
|
End of tes3mp change (major)
|
|
|
|
*/
|
|
|
|
|
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
|
|
|
}
|