mirror of
https://gitlab.com/OpenMW/openmw.git
synced 2025-03-30 16:20:21 +00:00
Merge pull request #2646 from akortunov/projectiles
Allow to enchant multiple projectiles at once
This commit is contained in:
commit
5a17d8e5a4
@ -189,6 +189,7 @@
|
|||||||
Feature #2229: Improve pathfinding AI
|
Feature #2229: Improve pathfinding AI
|
||||||
Feature #3025: Analogue gamepad movement controls
|
Feature #3025: Analogue gamepad movement controls
|
||||||
Feature #3442: Default values for fallbacks from ini file
|
Feature #3442: Default values for fallbacks from ini file
|
||||||
|
Feature #3517: Multiple projectiles enchantment
|
||||||
Feature #3610: Option to invert X axis
|
Feature #3610: Option to invert X axis
|
||||||
Feature #3871: Editor: Terrain Selection
|
Feature #3871: Editor: Terrain Selection
|
||||||
Feature #3893: Implicit target for "set" function in console
|
Feature #3893: Implicit target for "set" function in console
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
#include "enchanting.hpp"
|
#include "enchanting.hpp"
|
||||||
|
|
||||||
#include <components/misc/rng.hpp>
|
#include <components/misc/rng.hpp>
|
||||||
|
#include <components/settings/settings.hpp>
|
||||||
|
|
||||||
#include "../mwworld/manualref.hpp"
|
#include "../mwworld/manualref.hpp"
|
||||||
#include "../mwworld/class.hpp"
|
#include "../mwworld/class.hpp"
|
||||||
@ -62,7 +63,6 @@ namespace MWMechanics
|
|||||||
const MWWorld::Ptr& player = getPlayer();
|
const MWWorld::Ptr& player = getPlayer();
|
||||||
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
MWWorld::ContainerStore& store = player.getClass().getContainerStore(player);
|
||||||
ESM::Enchantment enchantment;
|
ESM::Enchantment enchantment;
|
||||||
enchantment.mData.mCharge = getGemCharge();
|
|
||||||
enchantment.mData.mAutocalc = 0;
|
enchantment.mData.mAutocalc = 0;
|
||||||
enchantment.mData.mType = mCastStyle;
|
enchantment.mData.mType = mCastStyle;
|
||||||
enchantment.mData.mCost = getBaseCastCost();
|
enchantment.mData.mCost = getBaseCastCost();
|
||||||
@ -81,19 +81,26 @@ namespace MWMechanics
|
|||||||
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
|
mEnchanter.getClass().skillUsageSucceeded (mEnchanter, ESM::Skill::Enchant, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(mCastStyle==ESM::Enchantment::ConstantEffect)
|
|
||||||
{
|
|
||||||
enchantment.mData.mCharge=0;
|
|
||||||
}
|
|
||||||
enchantment.mEffects = mEffectList;
|
enchantment.mEffects = mEffectList;
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
// Apply the enchantment
|
// Apply the enchantment
|
||||||
const ESM::Enchantment *enchantmentPtr = MWBase::Environment::get().getWorld()->createRecord (enchantment);
|
|
||||||
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
|
std::string newItemId = mOldItemPtr.getClass().applyEnchantment(mOldItemPtr, enchantmentPtr->mId, getGemCharge(), mNewItemName);
|
||||||
|
|
||||||
// Add the new item to player inventory and remove the old one
|
// Add the new item to player inventory and remove the old one
|
||||||
store.remove(mOldItemPtr, 1, player);
|
store.remove(mOldItemPtr, count, player);
|
||||||
store.add(newItemId, 1, player);
|
store.add(newItemId, count, player);
|
||||||
|
|
||||||
if(!mSelfEnchanting)
|
if(!mSelfEnchanting)
|
||||||
payForEnchantment();
|
payForEnchantment();
|
||||||
@ -123,20 +130,22 @@ namespace MWMechanics
|
|||||||
}
|
}
|
||||||
else if (mWeaponType != -1)
|
else if (mWeaponType != -1)
|
||||||
{ // Weapon
|
{ // Weapon
|
||||||
|
ESM::WeaponType::Class weapclass = MWMechanics::getWeaponType(mWeaponType)->mWeaponClass;
|
||||||
switch(mCastStyle)
|
switch(mCastStyle)
|
||||||
{
|
{
|
||||||
case ESM::Enchantment::WhenStrikes:
|
case ESM::Enchantment::WhenStrikes:
|
||||||
mCastStyle = ESM::Enchantment::WhenUsed;
|
if (weapclass == ESM::WeaponType::Melee || weapclass == ESM::WeaponType::Ranged)
|
||||||
|
mCastStyle = ESM::Enchantment::WhenUsed;
|
||||||
return;
|
return;
|
||||||
case ESM::Enchantment::WhenUsed:
|
case ESM::Enchantment::WhenUsed:
|
||||||
if (powerfulSoul)
|
if (powerfulSoul && weapclass != ESM::WeaponType::Ammo && weapclass != ESM::WeaponType::Thrown)
|
||||||
mCastStyle = ESM::Enchantment::ConstantEffect;
|
mCastStyle = ESM::Enchantment::ConstantEffect;
|
||||||
else if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged)
|
else if (weapclass != ESM::WeaponType::Ranged)
|
||||||
mCastStyle = ESM::Enchantment::WhenStrikes;
|
mCastStyle = ESM::Enchantment::WhenStrikes;
|
||||||
return;
|
return;
|
||||||
default: // takes care of Constant effect too
|
default: // takes care of Constant effect too
|
||||||
mCastStyle = ESM::Enchantment::WhenUsed;
|
mCastStyle = ESM::Enchantment::WhenUsed;
|
||||||
if (getWeaponType(mWeaponType)->mWeaponClass != ESM::WeaponType::Ranged)
|
if (weapclass != ESM::WeaponType::Ranged)
|
||||||
mCastStyle = ESM::Enchantment::WhenStrikes;
|
mCastStyle = ESM::Enchantment::WhenStrikes;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -200,6 +209,53 @@ namespace MWMechanics
|
|||||||
return enchantmentCost;
|
return enchantmentCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
if (iter->mData.mAutocalc != toFind.mData.mAutocalc
|
||||||
|
|| 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;
|
||||||
|
}
|
||||||
|
|
||||||
int Enchanting::getBaseCastCost() const
|
int Enchanting::getBaseCastCost() const
|
||||||
{
|
{
|
||||||
@ -224,6 +280,7 @@ namespace MWMechanics
|
|||||||
|
|
||||||
float priceMultipler = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find ("fEnchantmentValueMult")->mValue.getFloat();
|
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);
|
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer(mEnchanter, static_cast<int>(getEnchantPoints() * priceMultipler), true);
|
||||||
|
price *= getEnchantItemsCount() * getTypeMultiplier();
|
||||||
return price;
|
return price;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -282,13 +339,45 @@ namespace MWMechanics
|
|||||||
const float fEnchantmentChanceMult = gmst.find("fEnchantmentChanceMult")->mValue.getFloat();
|
const float fEnchantmentChanceMult = gmst.find("fEnchantmentChanceMult")->mValue.getFloat();
|
||||||
const float fEnchantmentConstantChanceMult = gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat();
|
const float fEnchantmentConstantChanceMult = gmst.find("fEnchantmentConstantChanceMult")->mValue.getFloat();
|
||||||
|
|
||||||
float x = (a - getEnchantPoints()*fEnchantmentChanceMult + 0.2f * b + 0.1f * c) * stats.getFatigueTerm();
|
float x = (a - getEnchantPoints() * fEnchantmentChanceMult * getTypeMultiplier() * getEnchantItemsCount() + 0.2f * b + 0.1f * c) * stats.getFatigueTerm();
|
||||||
if (mCastStyle == ESM::Enchantment::ConstantEffect)
|
if (mCastStyle == ESM::Enchantment::ConstantEffect)
|
||||||
x *= fEnchantmentConstantChanceMult;
|
x *= fEnchantmentConstantChanceMult;
|
||||||
|
|
||||||
return static_cast<int>(x);
|
return static_cast<int>(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void Enchanting::payForEnchantment() const
|
void Enchanting::payForEnchantment() const
|
||||||
{
|
{
|
||||||
const MWWorld::Ptr& player = getPlayer();
|
const MWWorld::Ptr& player = getPlayer();
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include <components/esm/effectlist.hpp>
|
#include <components/esm/effectlist.hpp>
|
||||||
|
#include <components/esm/loadench.hpp>
|
||||||
|
|
||||||
#include "../mwworld/ptr.hpp"
|
#include "../mwworld/ptr.hpp"
|
||||||
|
|
||||||
@ -25,6 +26,8 @@ namespace MWMechanics
|
|||||||
std::string mObjectType;
|
std::string mObjectType;
|
||||||
int mWeaponType;
|
int mWeaponType;
|
||||||
|
|
||||||
|
const ESM::Enchantment* getRecord(const ESM::Enchantment& newEnchantment) const;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Enchanting();
|
Enchanting();
|
||||||
void setEnchanter(const MWWorld::Ptr& enchanter);
|
void setEnchanter(const MWWorld::Ptr& enchanter);
|
||||||
@ -45,6 +48,8 @@ namespace MWMechanics
|
|||||||
int getMaxEnchantValue() const;
|
int getMaxEnchantValue() const;
|
||||||
int getGemCharge() const;
|
int getGemCharge() const;
|
||||||
int getEnchantChance() const;
|
int getEnchantChance() const;
|
||||||
|
int getEnchantItemsCount() const;
|
||||||
|
float getTypeMultiplier() const;
|
||||||
bool soulEmpty() const; //Return true if empty
|
bool soulEmpty() const; //Return true if empty
|
||||||
bool itemEmpty() const; //Return true if empty
|
bool itemEmpty() const; //Return true if empty
|
||||||
void payForEnchantment() const;
|
void payForEnchantment() const;
|
||||||
|
@ -106,6 +106,11 @@ namespace MWWorld
|
|||||||
return iter;
|
return iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
SharedIterator &operator+=(int advance) {
|
||||||
|
mIter += advance;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
SharedIterator &operator--() {
|
SharedIterator &operator--() {
|
||||||
--mIter;
|
--mIter;
|
||||||
return *this;
|
return *this;
|
||||||
|
@ -282,3 +282,19 @@ equivalent to the one introduced by the equivalent Morrowind Code Patch feature.
|
|||||||
This makes the movement speed behavior more fair between different races.
|
This makes the movement speed behavior more fair between different races.
|
||||||
|
|
||||||
This setting can be controlled in Advanced tab of the launcher.
|
This setting can be controlled in Advanced tab of the launcher.
|
||||||
|
|
||||||
|
projectiles enchant multiplier
|
||||||
|
------------------------------
|
||||||
|
|
||||||
|
:Type: floating point
|
||||||
|
:Range: 0.0 to 1.0
|
||||||
|
:Default: 0.0
|
||||||
|
|
||||||
|
The value of this setting determines how many projectiles (thrown weapons, arrows and bolts) you can enchant at once according to the following formula:
|
||||||
|
|
||||||
|
count = (soul gem charge * projectiles enchant multiplier) / enchantment strength
|
||||||
|
|
||||||
|
A value of 0 means that you can only enchant one projectile.
|
||||||
|
If you want to have Morrowind Code Patch-like count of projectiles being enchanted at once, set this value to 0.25 (i.e. 25% of the charge).
|
||||||
|
|
||||||
|
This setting can only be configured by editing the settings configuration file.
|
||||||
|
@ -267,6 +267,11 @@ use magic item animations = false
|
|||||||
# Don't use race weight in NPC movement speed calculations
|
# Don't use race weight in NPC movement speed calculations
|
||||||
normalise race speed = false
|
normalise race speed = false
|
||||||
|
|
||||||
|
# Adjusts the number of projectiles you can enchant at once:
|
||||||
|
# count = (soul gem charge * projectiles enchant multiplier) / enchantment strength
|
||||||
|
# A value of 0 means that you can only enchant one projectile.
|
||||||
|
projectiles enchant multiplier = 0
|
||||||
|
|
||||||
[General]
|
[General]
|
||||||
|
|
||||||
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
# Anisotropy reduces distortion in textures at low angles (e.g. 0 to 16).
|
||||||
|
Loading…
x
Reference in New Issue
Block a user