Merge remote-tracking branch 'refs/remotes/master/master' into NonTableFields
Conflicts: apps/opencs/CMakeLists.txttest
commit
cdac934315
@ -0,0 +1,83 @@
|
||||
|
||||
#include "pushbutton.hpp"
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QKeyEvent>
|
||||
|
||||
void CSVWidget::PushButton::setExtendedToolTip (const QString& text)
|
||||
{
|
||||
QString tooltip = text;
|
||||
|
||||
if (tooltip.isEmpty())
|
||||
tooltip = "(Tool tip not implemented yet)";
|
||||
|
||||
switch (mType)
|
||||
{
|
||||
case Type_TopMode:
|
||||
|
||||
tooltip +=
|
||||
"<p>(left click to change mode)";
|
||||
|
||||
break;
|
||||
|
||||
case Type_Mode:
|
||||
|
||||
tooltip +=
|
||||
"<p>(left click to activate,"
|
||||
"<br>shift-left click to activate and keep panel open)";
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
setToolTip (tooltip);
|
||||
}
|
||||
|
||||
void CSVWidget::PushButton::keyPressEvent (QKeyEvent *event)
|
||||
{
|
||||
if (event->key()!=Qt::Key_Shift)
|
||||
mKeepOpen = false;
|
||||
|
||||
QPushButton::keyPressEvent (event);
|
||||
}
|
||||
|
||||
void CSVWidget::PushButton::keyReleaseEvent (QKeyEvent *event)
|
||||
{
|
||||
if (event->key()==Qt::Key_Return || event->key()==Qt::Key_Enter)
|
||||
{
|
||||
mKeepOpen = event->modifiers() & Qt::ShiftModifier;
|
||||
emit clicked();
|
||||
}
|
||||
|
||||
QPushButton::keyReleaseEvent (event);
|
||||
}
|
||||
|
||||
void CSVWidget::PushButton::mouseReleaseEvent (QMouseEvent *event)
|
||||
{
|
||||
mKeepOpen = event->button()==Qt::LeftButton && (event->modifiers() & Qt::ShiftModifier);
|
||||
QPushButton::mouseReleaseEvent (event);
|
||||
}
|
||||
|
||||
CSVWidget::PushButton::PushButton (const QIcon& icon, Type type, const QString& tooltip,
|
||||
QWidget *parent)
|
||||
: QPushButton (icon, "", parent), mKeepOpen (false), mType (type), mToolTip (tooltip)
|
||||
{
|
||||
setCheckable (type==Type_Mode);
|
||||
setExtendedToolTip (tooltip);
|
||||
}
|
||||
|
||||
CSVWidget::PushButton::PushButton (Type type, const QString& tooltip, QWidget *parent)
|
||||
: QPushButton (parent), mKeepOpen (false), mType (type), mToolTip (tooltip)
|
||||
{
|
||||
setCheckable (type==Type_Mode);
|
||||
setExtendedToolTip (tooltip);
|
||||
}
|
||||
|
||||
bool CSVWidget::PushButton::hasKeepOpen() const
|
||||
{
|
||||
return mKeepOpen;
|
||||
}
|
||||
|
||||
QString CSVWidget::PushButton::getBaseToolTip() const
|
||||
{
|
||||
return mToolTip;
|
||||
}
|
@ -0,0 +1,55 @@
|
||||
#ifndef CSV_WIDGET_PUSHBUTTON_H
|
||||
#define CSV_WIDGET_PUSHBUTTON_H
|
||||
|
||||
#include <QPushButton>
|
||||
|
||||
namespace CSVWidget
|
||||
{
|
||||
class PushButton : public QPushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
enum Type
|
||||
{
|
||||
Type_TopMode, // top level button for mode selector panel
|
||||
Type_Mode // mode button
|
||||
};
|
||||
|
||||
private:
|
||||
|
||||
bool mKeepOpen;
|
||||
Type mType;
|
||||
QString mToolTip;
|
||||
|
||||
private:
|
||||
|
||||
void setExtendedToolTip (const QString& text);
|
||||
|
||||
protected:
|
||||
|
||||
virtual void keyPressEvent (QKeyEvent *event);
|
||||
|
||||
virtual void keyReleaseEvent (QKeyEvent *event);
|
||||
|
||||
virtual void mouseReleaseEvent (QMouseEvent *event);
|
||||
|
||||
public:
|
||||
|
||||
/// \param push Do not maintain a toggle state
|
||||
PushButton (const QIcon& icon, Type type, const QString& tooltip = "",
|
||||
QWidget *parent = 0);
|
||||
|
||||
/// \param push Do not maintain a toggle state
|
||||
PushButton (Type type, const QString& tooltip = "",
|
||||
QWidget *parent = 0);
|
||||
|
||||
bool hasKeepOpen() const;
|
||||
|
||||
/// Return tooltip used at construction (without any button-specific modifications)
|
||||
QString getBaseToolTip() const;
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
@ -1,14 +1,14 @@
|
||||
#ifndef CSV_WORLD_SCENETOOL_H
|
||||
#define CSV_WORLD_SCENETOOL_H
|
||||
#ifndef CSV_WIDGET_SCENETOOL_H
|
||||
#define CSV_WIDGET_SCENETOOL_H
|
||||
|
||||
#include <QPushButton>
|
||||
#include "pushbutton.hpp"
|
||||
|
||||
namespace CSVWorld
|
||||
namespace CSVWidget
|
||||
{
|
||||
class SceneToolbar;
|
||||
|
||||
///< \brief Tool base class
|
||||
class SceneTool : public QPushButton
|
||||
class SceneTool : public PushButton
|
||||
{
|
||||
Q_OBJECT
|
||||
|
@ -0,0 +1,47 @@
|
||||
|
||||
#include "scenetoolbar.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
#include <QShortcut>
|
||||
|
||||
#include "scenetool.hpp"
|
||||
|
||||
void CSVWidget::SceneToolbar::focusInEvent (QFocusEvent *event)
|
||||
{
|
||||
QWidget::focusInEvent (event);
|
||||
|
||||
if (mLayout->count())
|
||||
dynamic_cast<QWidgetItem&> (*mLayout->itemAt (0)).widget()->setFocus();
|
||||
}
|
||||
|
||||
CSVWidget::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)
|
||||
: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)
|
||||
{
|
||||
setFixedWidth (mButtonSize);
|
||||
|
||||
mLayout = new QVBoxLayout (this);
|
||||
mLayout->setAlignment (Qt::AlignTop);
|
||||
|
||||
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
setLayout (mLayout);
|
||||
|
||||
/// \todo make shortcut configurable
|
||||
QShortcut *focusScene = new QShortcut (Qt::Key_T, this, 0, 0, Qt::WidgetWithChildrenShortcut);
|
||||
connect (focusScene, SIGNAL (activated()), this, SIGNAL (focusSceneRequest()));
|
||||
}
|
||||
|
||||
void CSVWidget::SceneToolbar::addTool (SceneTool *tool)
|
||||
{
|
||||
mLayout->addWidget (tool, 0, Qt::AlignTop);
|
||||
}
|
||||
|
||||
int CSVWidget::SceneToolbar::getButtonSize() const
|
||||
{
|
||||
return mButtonSize;
|
||||
}
|
||||
|
||||
int CSVWidget::SceneToolbar::getIconSize() const
|
||||
{
|
||||
return mIconSize;
|
||||
}
|
@ -0,0 +1,86 @@
|
||||
|
||||
#include "scenetoolmode.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QSignalMapper>
|
||||
|
||||
#include "scenetoolbar.hpp"
|
||||
#include "pushbutton.hpp"
|
||||
|
||||
void CSVWidget::SceneToolMode::adjustToolTip (const PushButton *activeMode)
|
||||
{
|
||||
QString toolTip = mToolTip;
|
||||
|
||||
toolTip += "<p>Currently selected: " + activeMode->getBaseToolTip();
|
||||
|
||||
toolTip += "<p>(left click to change mode)";
|
||||
|
||||
setToolTip (toolTip);
|
||||
}
|
||||
|
||||
CSVWidget::SceneToolMode::SceneToolMode (SceneToolbar *parent, const QString& toolTip)
|
||||
: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize()),
|
||||
mToolTip (toolTip), mFirst (0)
|
||||
{
|
||||
mPanel = new QFrame (this, Qt::Popup);
|
||||
|
||||
mLayout = new QHBoxLayout (mPanel);
|
||||
|
||||
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
mPanel->setLayout (mLayout);
|
||||
}
|
||||
|
||||
void CSVWidget::SceneToolMode::showPanel (const QPoint& position)
|
||||
{
|
||||
mPanel->move (position);
|
||||
mPanel->show();
|
||||
|
||||
if (mFirst)
|
||||
mFirst->setFocus (Qt::OtherFocusReason);
|
||||
}
|
||||
|
||||
void CSVWidget::SceneToolMode::addButton (const std::string& icon, const std::string& id,
|
||||
const QString& tooltip)
|
||||
{
|
||||
PushButton *button = new PushButton (QIcon (QPixmap (icon.c_str())), PushButton::Type_Mode,
|
||||
tooltip, mPanel);
|
||||
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
|
||||
button->setIconSize (QSize (mIconSize, mIconSize));
|
||||
button->setFixedSize (mButtonSize, mButtonSize);
|
||||
|
||||
mLayout->addWidget (button);
|
||||
|
||||
mButtons.insert (std::make_pair (button, id));
|
||||
|
||||
connect (button, SIGNAL (clicked()), this, SLOT (selected()));
|
||||
|
||||
if (mButtons.size()==1)
|
||||
{
|
||||
mFirst = button;
|
||||
setIcon (button->icon());
|
||||
button->setChecked (true);
|
||||
adjustToolTip (button);
|
||||
}
|
||||
}
|
||||
|
||||
void CSVWidget::SceneToolMode::selected()
|
||||
{
|
||||
std::map<PushButton *, std::string>::const_iterator iter =
|
||||
mButtons.find (dynamic_cast<PushButton *> (sender()));
|
||||
|
||||
if (iter!=mButtons.end())
|
||||
{
|
||||
if (!iter->first->hasKeepOpen())
|
||||
mPanel->hide();
|
||||
|
||||
for (std::map<PushButton *, std::string>::const_iterator iter2 = mButtons.begin();
|
||||
iter2!=mButtons.end(); ++iter2)
|
||||
iter2->first->setChecked (iter2==iter);
|
||||
|
||||
setIcon (iter->first->icon());
|
||||
adjustToolTip (iter->first);
|
||||
emit modeChanged (iter->second);
|
||||
}
|
||||
}
|
@ -1,34 +0,0 @@
|
||||
|
||||
#include "scenetoolbar.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "scenetool.hpp"
|
||||
|
||||
CSVWorld::SceneToolbar::SceneToolbar (int buttonSize, QWidget *parent)
|
||||
: QWidget (parent), mButtonSize (buttonSize), mIconSize (buttonSize-6)
|
||||
{
|
||||
setFixedWidth (mButtonSize);
|
||||
|
||||
mLayout = new QVBoxLayout (this);
|
||||
mLayout->setAlignment (Qt::AlignTop);
|
||||
|
||||
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
setLayout (mLayout);
|
||||
}
|
||||
|
||||
void CSVWorld::SceneToolbar::addTool (SceneTool *tool)
|
||||
{
|
||||
mLayout->addWidget (tool, 0, Qt::AlignTop);
|
||||
}
|
||||
|
||||
int CSVWorld::SceneToolbar::getButtonSize() const
|
||||
{
|
||||
return mButtonSize;
|
||||
}
|
||||
|
||||
int CSVWorld::SceneToolbar::getIconSize() const
|
||||
{
|
||||
return mIconSize;
|
||||
}
|
@ -1,57 +0,0 @@
|
||||
|
||||
#include "scenetoolmode.hpp"
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QFrame>
|
||||
#include <QSignalMapper>
|
||||
|
||||
#include "scenetoolbar.hpp"
|
||||
|
||||
CSVWorld::SceneToolMode::SceneToolMode (SceneToolbar *parent)
|
||||
: SceneTool (parent), mButtonSize (parent->getButtonSize()), mIconSize (parent->getIconSize())
|
||||
{
|
||||
mPanel = new QFrame (this, Qt::Popup);
|
||||
|
||||
mLayout = new QHBoxLayout (mPanel);
|
||||
|
||||
mLayout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
mPanel->setLayout (mLayout);
|
||||
}
|
||||
|
||||
void CSVWorld::SceneToolMode::showPanel (const QPoint& position)
|
||||
{
|
||||
mPanel->move (position);
|
||||
mPanel->show();
|
||||
}
|
||||
|
||||
void CSVWorld::SceneToolMode::addButton (const std::string& icon, const std::string& id)
|
||||
{
|
||||
QPushButton *button = new QPushButton (QIcon (QPixmap (icon.c_str())), "", mPanel);
|
||||
button->setSizePolicy (QSizePolicy (QSizePolicy::Fixed, QSizePolicy::Fixed));
|
||||
button->setIconSize (QSize (mIconSize, mIconSize));
|
||||
button->setFixedSize (mButtonSize, mButtonSize);
|
||||
|
||||
mLayout->addWidget (button);
|
||||
|
||||
mButtons.insert (std::make_pair (button, id));
|
||||
|
||||
connect (button, SIGNAL (clicked()), this, SLOT (selected()));
|
||||
|
||||
if (mButtons.size()==1)
|
||||
setIcon (button->icon());
|
||||
}
|
||||
|
||||
void CSVWorld::SceneToolMode::selected()
|
||||
{
|
||||
std::map<QPushButton *, std::string>::const_iterator iter =
|
||||
mButtons.find (dynamic_cast<QPushButton *> (sender()));
|
||||
|
||||
if (iter!=mButtons.end())
|
||||
{
|
||||
mPanel->hide();
|
||||
|
||||
setIcon (iter->first->icon());
|
||||
emit modeChanged (iter->second);
|
||||
}
|
||||
}
|
@ -0,0 +1,232 @@
|
||||
#include "autocalcspell.hpp"
|
||||
|
||||
#include <climits>
|
||||
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
struct SchoolCaps
|
||||
{
|
||||
int mCount;
|
||||
int mLimit;
|
||||
bool mReachedLimit;
|
||||
int mMinCost;
|
||||
std::string mWeakestSpell;
|
||||
};
|
||||
|
||||
std::vector<std::string> autoCalcNpcSpells(const int *actorSkills, const int *actorAttributes, const ESM::Race* race)
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
static const float fNPCbaseMagickaMult = gmst.find("fNPCbaseMagickaMult")->getFloat();
|
||||
float baseMagicka = fNPCbaseMagickaMult * actorAttributes[ESM::Attribute::Intelligence];
|
||||
|
||||
static const std::string schools[] = {
|
||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||
};
|
||||
static int iAutoSpellSchoolMax[6];
|
||||
static bool init = false;
|
||||
if (!init)
|
||||
{
|
||||
for (int i=0; i<6; ++i)
|
||||
{
|
||||
const std::string& gmstName = "iAutoSpell" + schools[i] + "Max";
|
||||
iAutoSpellSchoolMax[i] = gmst.find(gmstName)->getInt();
|
||||
}
|
||||
init = true;
|
||||
}
|
||||
|
||||
std::map<int, SchoolCaps> schoolCaps;
|
||||
for (int i=0; i<6; ++i)
|
||||
{
|
||||
SchoolCaps caps;
|
||||
caps.mCount = 0;
|
||||
caps.mLimit = iAutoSpellSchoolMax[i];
|
||||
caps.mReachedLimit = iAutoSpellSchoolMax[i] <= 0;
|
||||
caps.mMinCost = INT_MAX;
|
||||
caps.mWeakestSpell.clear();
|
||||
schoolCaps[i] = caps;
|
||||
}
|
||||
|
||||
std::vector<std::string> selectedSpells;
|
||||
|
||||
const MWWorld::Store<ESM::Spell> &spells =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>();
|
||||
|
||||
// Note: the algorithm heavily depends on the traversal order of the spells. For vanilla-compatible results the
|
||||
// Store must preserve the record ordering as it was in the content files.
|
||||
for (MWWorld::Store<ESM::Spell>::iterator iter = spells.begin(); iter != spells.end(); ++iter)
|
||||
{
|
||||
const ESM::Spell* spell = &*iter;
|
||||
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
continue;
|
||||
if (!(spell->mData.mFlags & ESM::Spell::F_Autocalc))
|
||||
continue;
|
||||
static const int iAutoSpellTimesCanCast = gmst.find("iAutoSpellTimesCanCast")->getInt();
|
||||
if (baseMagicka < iAutoSpellTimesCanCast * spell->mData.mCost)
|
||||
continue;
|
||||
|
||||
if (race && std::find(race->mPowers.mList.begin(), race->mPowers.mList.end(), spell->mId) != race->mPowers.mList.end())
|
||||
continue;
|
||||
|
||||
if (!attrSkillCheck(spell, actorSkills, actorAttributes))
|
||||
continue;
|
||||
|
||||
int school;
|
||||
float skillTerm;
|
||||
calcWeakestSchool(spell, actorSkills, school, skillTerm);
|
||||
assert(school >= 0 && school < 6);
|
||||
SchoolCaps& cap = schoolCaps[school];
|
||||
|
||||
if (cap.mReachedLimit && spell->mData.mCost <= cap.mMinCost)
|
||||
continue;
|
||||
|
||||
static const float fAutoSpellChance = gmst.find("fAutoSpellChance")->getFloat();
|
||||
if (calcAutoCastChance(spell, actorSkills, actorAttributes, school) < fAutoSpellChance)
|
||||
continue;
|
||||
|
||||
selectedSpells.push_back(spell->mId);
|
||||
|
||||
if (cap.mReachedLimit)
|
||||
{
|
||||
std::vector<std::string>::iterator found = std::find(selectedSpells.begin(), selectedSpells.end(), cap.mWeakestSpell);
|
||||
if (found != selectedSpells.end())
|
||||
selectedSpells.erase(found);
|
||||
|
||||
cap.mMinCost = INT_MAX;
|
||||
for (std::vector<std::string>::iterator weakIt = selectedSpells.begin(); weakIt != selectedSpells.end(); ++weakIt)
|
||||
{
|
||||
const ESM::Spell* testSpell = spells.find(*weakIt);
|
||||
|
||||
//int testSchool;
|
||||
//float dummySkillTerm;
|
||||
//calcWeakestSchool(testSpell, actorSkills, testSchool, dummySkillTerm);
|
||||
|
||||
// Note: if there are multiple spells with the same cost, we pick the first one we found.
|
||||
// So the algorithm depends on the iteration order of the outer loop.
|
||||
if (
|
||||
// There is a huge bug here. It is not checked that weakestSpell is of the correct school.
|
||||
// As result multiple SchoolCaps could have the same mWeakestSpell. Erasing the weakest spell would then fail if another school
|
||||
// already erased it, and so the number of spells would often exceed the sum of limits.
|
||||
// This bug cannot be fixed without significantly changing the results of the spell autocalc, which will not have been playtested.
|
||||
//testSchool == school &&
|
||||
testSpell->mData.mCost < cap.mMinCost)
|
||||
{
|
||||
cap.mMinCost = testSpell->mData.mCost;
|
||||
cap.mWeakestSpell = testSpell->mId;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
cap.mCount += 1;
|
||||
if (cap.mCount == cap.mLimit)
|
||||
cap.mReachedLimit = true;
|
||||
|
||||
if (spell->mData.mCost < cap.mMinCost)
|
||||
{
|
||||
cap.mWeakestSpell = spell->mId;
|
||||
cap.mMinCost = spell->mData.mCost;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return selectedSpells;
|
||||
}
|
||||
|
||||
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes)
|
||||
{
|
||||
const std::vector<ESM::ENAMstruct>& effects = spell->mEffects.mList;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator effectIt = effects.begin(); effectIt != effects.end(); ++effectIt)
|
||||
{
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effectIt->mEffectID);
|
||||
static const int iAutoSpellAttSkillMin = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("iAutoSpellAttSkillMin")->getInt();
|
||||
|
||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetSkill))
|
||||
{
|
||||
assert (effectIt->mSkill >= 0 && effectIt->mSkill < ESM::Skill::Length);
|
||||
if (actorSkills[effectIt->mSkill] < iAutoSpellAttSkillMin)
|
||||
return false;
|
||||
}
|
||||
|
||||
if ((magicEffect->mData.mFlags & ESM::MagicEffect::TargetAttribute))
|
||||
{
|
||||
assert (effectIt->mAttribute >= 0 && effectIt->mAttribute < ESM::Attribute::Length);
|
||||
if (actorAttributes[effectIt->mAttribute] < iAutoSpellAttSkillMin)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
ESM::Skill::SkillEnum mapSchoolToSkill(int school)
|
||||
{
|
||||
std::map<int, ESM::Skill::SkillEnum> schoolSkillMap; // maps spell school to skill id
|
||||
schoolSkillMap[0] = ESM::Skill::Alteration;
|
||||
schoolSkillMap[1] = ESM::Skill::Conjuration;
|
||||
schoolSkillMap[3] = ESM::Skill::Illusion;
|
||||
schoolSkillMap[2] = ESM::Skill::Destruction;
|
||||
schoolSkillMap[4] = ESM::Skill::Mysticism;
|
||||
schoolSkillMap[5] = ESM::Skill::Restoration;
|
||||
assert(schoolSkillMap.find(school) != schoolSkillMap.end());
|
||||
return schoolSkillMap[school];
|
||||
}
|
||||
|
||||
void calcWeakestSchool (const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm)
|
||||
{
|
||||
float minChance = FLT_MAX;
|
||||
|
||||
const ESM::EffectList& effects = spell->mEffects;
|
||||
for (std::vector<ESM::ENAMstruct>::const_iterator it = effects.mList.begin(); it != effects.mList.end(); ++it)
|
||||
{
|
||||
const ESM::ENAMstruct& effect = *it;
|
||||
float x = effect.mDuration;
|
||||
|
||||
const ESM::MagicEffect* magicEffect = MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(effect.mEffectID);
|
||||
if (!(magicEffect->mData.mFlags & ESM::MagicEffect::UncappedDamage))
|
||||
x = std::max(1.f, x);
|
||||
|
||||
x *= 0.1f * magicEffect->mData.mBaseCost;
|
||||
x *= 0.5f * (effect.mMagnMin + effect.mMagnMax);
|
||||
x += effect.mArea * 0.05f * magicEffect->mData.mBaseCost;
|
||||
if (effect.mRange == ESM::RT_Target)
|
||||
x *= 1.5f;
|
||||
|
||||
static const float fEffectCostMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fEffectCostMult")->getFloat();
|
||||
x *= fEffectCostMult;
|
||||
|
||||
float s = 2.f * actorSkills[mapSchoolToSkill(magicEffect->mData.mSchool)];
|
||||
if (s - x < minChance)
|
||||
{
|
||||
minChance = s - x;
|
||||
effectiveSchool = magicEffect->mData.mSchool;
|
||||
skillTerm = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float calcAutoCastChance(const ESM::Spell *spell, const int *actorSkills, const int *actorAttributes, int effectiveSchool)
|
||||
{
|
||||
if (spell->mData.mType != ESM::Spell::ST_Spell)
|
||||
return 100.f;
|
||||
|
||||
if (spell->mData.mFlags & ESM::Spell::F_Always)
|
||||
return 100.f;
|
||||
|
||||
float skillTerm;
|
||||
if (effectiveSchool != -1)
|
||||
skillTerm = 2.f * actorSkills[mapSchoolToSkill(effectiveSchool)];
|
||||
else
|
||||
calcWeakestSchool(spell, actorSkills, effectiveSchool, skillTerm); // Note effectiveSchool is unused after this
|
||||
|
||||
float castChance = skillTerm - spell->mData.mCost + 0.2f * actorAttributes[ESM::Attribute::Willpower] + 0.1f * actorAttributes[ESM::Attribute::Luck];
|
||||
return castChance;
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
#ifndef OPENMW_AUTOCALCSPELL_H
|
||||
#define OPENMW_AUTOCALCSPELL_H
|
||||
|
||||
#include <cfloat>
|
||||
#include <set>
|
||||
|
||||
#include <components/esm/loadspel.hpp>
|
||||
#include <components/esm/loadskil.hpp>
|
||||
#include <components/esm/loadrace.hpp>
|
||||
|
||||
namespace MWMechanics
|
||||
{
|
||||
|
||||
/// Contains algorithm for calculating an NPC's spells based on stats
|
||||
/// @note We might want to move this code to a component later, so the editor can use it for preview purposes
|
||||
|
||||
std::vector<std::string> autoCalcNpcSpells(const int* actorSkills, const int* actorAttributes, const ESM::Race* race);
|
||||
|
||||
// Helpers
|
||||
|
||||
bool attrSkillCheck (const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes);
|
||||
|
||||
ESM::Skill::SkillEnum mapSchoolToSkill(int school);
|
||||
|
||||
void calcWeakestSchool(const ESM::Spell* spell, const int* actorSkills, int& effectiveSchool, float& skillTerm);
|
||||
|
||||
float calcAutoCastChance(const ESM::Spell* spell, const int* actorSkills, const int* actorAttributes, int effectiveSchool);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
@ -0,0 +1,38 @@
|
||||
#include "difficultyscaling.hpp"
|
||||
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwworld/esmstore.hpp"
|
||||
|
||||
#include <components/settings/settings.hpp>
|
||||
|
||||
float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim)
|
||||
{
|
||||
const MWWorld::Ptr& player = MWBase::Environment::get().getWorld()->getPlayerPtr();
|
||||
|
||||
// [-100, 100]
|
||||
int difficultySetting = Settings::Manager::getInt("difficulty", "Game");
|
||||
|
||||
static const float fDifficultyMult = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fDifficultyMult")->getFloat();
|
||||
|
||||
float difficultyTerm = 0.01f * difficultySetting;
|
||||
|
||||
float x = 0;
|
||||
if (victim == player)
|
||||
{
|
||||
if (difficultyTerm > 0)
|
||||
x = fDifficultyMult * difficultyTerm;
|
||||
else
|
||||
x = difficultyTerm / fDifficultyMult;
|
||||
}
|
||||
else if (attacker == player)
|
||||
{
|
||||
if (difficultyTerm > 0)
|
||||
x = -difficultyTerm / fDifficultyMult;
|
||||
else
|
||||
x = fDifficultyMult * (-difficultyTerm);
|
||||
}
|
||||
|
||||
damage *= 1 + x;
|
||||
return damage;
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
#ifndef OPENMW_MWMECHANICS_DIFFICULTYSCALING_H
|
||||
#define OPENMW_MWMECHANICS_DIFFICULTYSCALING_H
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
}
|
||||
|
||||
/// Scales damage dealt to an actor based on difficulty setting
|
||||
float scaleDamage(float damage, const MWWorld::Ptr& attacker, const MWWorld::Ptr& victim);
|
||||
|
||||
#endif
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue