forked from teamnwah/openmw-tes3coop
Merge branch 'master' of https://github.com/OpenMW/openmw.git into QtOGre
commit
875daed4ec
@ -0,0 +1,9 @@
|
|||||||
|
|
||||||
|
#include "idtablebase.hpp"
|
||||||
|
|
||||||
|
CSMWorld::IdTableBase::IdTableBase (unsigned int features) : mFeatures (features) {}
|
||||||
|
|
||||||
|
unsigned int CSMWorld::IdTableBase::getFeatures() const
|
||||||
|
{
|
||||||
|
return mFeatures;
|
||||||
|
}
|
@ -0,0 +1,67 @@
|
|||||||
|
#ifndef CSM_WOLRD_IDTABLEBASE_H
|
||||||
|
#define CSM_WOLRD_IDTABLEBASE_H
|
||||||
|
|
||||||
|
#include <QAbstractItemModel>
|
||||||
|
|
||||||
|
#include "columns.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class UniversalId;
|
||||||
|
|
||||||
|
class IdTableBase : public QAbstractItemModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
enum Features
|
||||||
|
{
|
||||||
|
Feature_ReorderWithinTopic = 1,
|
||||||
|
|
||||||
|
/// Use ID column to generate view request (ID is transformed into
|
||||||
|
/// worldspace and original ID is passed as hint with c: prefix).
|
||||||
|
Feature_ViewId = 2,
|
||||||
|
|
||||||
|
/// Use cell column to generate view request (cell ID is transformed
|
||||||
|
/// into worldspace and record ID is passed as hint with r: prefix).
|
||||||
|
Feature_ViewCell = 4,
|
||||||
|
|
||||||
|
Feature_View = Feature_ViewId | Feature_ViewCell,
|
||||||
|
|
||||||
|
Feature_Preview = 8,
|
||||||
|
|
||||||
|
/// Table can not be modified through ordinary means.
|
||||||
|
Feature_Constant = 16
|
||||||
|
};
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
unsigned int mFeatures;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
IdTableBase (unsigned int features);
|
||||||
|
|
||||||
|
virtual QModelIndex getModelIndex (const std::string& id, int column) const = 0;
|
||||||
|
|
||||||
|
/// Return index of column with the given \a id. If no such column exists, -1 is
|
||||||
|
/// returned.
|
||||||
|
virtual int searchColumnIndex (Columns::ColumnId id) const = 0;
|
||||||
|
|
||||||
|
/// Return index of column with the given \a id. If no such column exists, an
|
||||||
|
/// exception is thrown.
|
||||||
|
virtual int findColumnIndex (Columns::ColumnId id) const = 0;
|
||||||
|
|
||||||
|
/// Return the UniversalId and the hint for viewing \a row. If viewing is not
|
||||||
|
/// supported by this table, return (UniversalId::Type_None, "").
|
||||||
|
virtual std::pair<UniversalId, std::string> view (int row) const = 0;
|
||||||
|
|
||||||
|
/// Is \a id flagged as deleted?
|
||||||
|
virtual bool isDeleted (const std::string& id) const = 0;
|
||||||
|
|
||||||
|
unsigned int getFeatures() const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,103 @@
|
|||||||
|
|
||||||
|
#include "resources.hpp"
|
||||||
|
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include <OgreResourceGroupManager.h>
|
||||||
|
|
||||||
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
CSMWorld::Resources::Resources (const std::string& baseDirectory, UniversalId::Type type,
|
||||||
|
const char * const *extensions)
|
||||||
|
: mBaseDirectory (baseDirectory), mType (type)
|
||||||
|
{
|
||||||
|
int baseSize = mBaseDirectory.size();
|
||||||
|
|
||||||
|
Ogre::StringVector resourcesGroups =
|
||||||
|
Ogre::ResourceGroupManager::getSingleton().getResourceGroups();
|
||||||
|
|
||||||
|
for (Ogre::StringVector::iterator iter (resourcesGroups.begin());
|
||||||
|
iter!=resourcesGroups.end(); ++iter)
|
||||||
|
{
|
||||||
|
if (*iter=="General" || *iter=="Internal" || *iter=="Autodetect")
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Ogre::StringVectorPtr resources =
|
||||||
|
Ogre::ResourceGroupManager::getSingleton().listResourceNames (*iter);
|
||||||
|
|
||||||
|
for (Ogre::StringVector::const_iterator iter (resources->begin());
|
||||||
|
iter!=resources->end(); ++iter)
|
||||||
|
{
|
||||||
|
if (static_cast<int> (iter->size())<baseSize+1 ||
|
||||||
|
iter->substr (0, baseSize)!=mBaseDirectory ||
|
||||||
|
((*iter)[baseSize]!='/' && (*iter)[baseSize]!='\\'))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (extensions)
|
||||||
|
{
|
||||||
|
std::string::size_type index = iter->find_last_of ('.');
|
||||||
|
|
||||||
|
if (index==std::string::npos)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
std::string extension = iter->substr (index+1);
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
for (; extensions[i]; ++i)
|
||||||
|
if (extensions[i]==extension)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!extensions[i])
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string file = iter->substr (baseSize+1);
|
||||||
|
mFiles.push_back (file);
|
||||||
|
mIndex.insert (std::make_pair (file, static_cast<int> (mFiles.size())-1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::Resources::getSize() const
|
||||||
|
{
|
||||||
|
return mFiles.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string CSMWorld::Resources::getId (int index) const
|
||||||
|
{
|
||||||
|
return mFiles.at (index);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::Resources::getIndex (const std::string& id) const
|
||||||
|
{
|
||||||
|
int index = searchId (id);
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
{
|
||||||
|
std::ostringstream stream;
|
||||||
|
stream << "Invalid resource: " << mBaseDirectory << '/' << id;
|
||||||
|
|
||||||
|
throw std::runtime_error (stream.str().c_str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::Resources::searchId (const std::string& id) const
|
||||||
|
{
|
||||||
|
std::string id2 = Misc::StringUtils::lowerCase (id);
|
||||||
|
|
||||||
|
std::map<std::string, int>::const_iterator iter = mIndex.find (id2);
|
||||||
|
|
||||||
|
if (iter==mIndex.end())
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMWorld::UniversalId::Type CSMWorld::Resources::getType() const
|
||||||
|
{
|
||||||
|
return mType;
|
||||||
|
}
|
@ -0,0 +1,37 @@
|
|||||||
|
#ifndef CSM_WOLRD_RESOURCES_H
|
||||||
|
#define CSM_WOLRD_RESOURCES_H
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "universalid.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Resources
|
||||||
|
{
|
||||||
|
std::map<std::string, int> mIndex;
|
||||||
|
std::vector<std::string> mFiles;
|
||||||
|
std::string mBaseDirectory;
|
||||||
|
UniversalId::Type mType;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \param type Type of resources in this table.
|
||||||
|
Resources (const std::string& baseDirectory, UniversalId::Type type,
|
||||||
|
const char * const *extensions = 0);
|
||||||
|
|
||||||
|
int getSize() const;
|
||||||
|
|
||||||
|
std::string getId (int index) const;
|
||||||
|
|
||||||
|
int getIndex (const std::string& id) const;
|
||||||
|
|
||||||
|
int searchId (const std::string& id) const;
|
||||||
|
|
||||||
|
UniversalId::Type getType() const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,31 @@
|
|||||||
|
|
||||||
|
#include "resourcesmanager.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
void CSMWorld::ResourcesManager::addResources (const Resources& resources)
|
||||||
|
{
|
||||||
|
mResources.insert (std::make_pair (resources.getType(), resources));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::ResourcesManager::listResources()
|
||||||
|
{
|
||||||
|
static const char * const sMeshTypes[] = { "nif", 0 };
|
||||||
|
|
||||||
|
addResources (Resources ("meshes", UniversalId::Type_Mesh, sMeshTypes));
|
||||||
|
addResources (Resources ("icons", UniversalId::Type_Icon));
|
||||||
|
addResources (Resources ("music", UniversalId::Type_Music));
|
||||||
|
addResources (Resources ("sound", UniversalId::Type_SoundRes));
|
||||||
|
addResources (Resources ("textures", UniversalId::Type_Texture));
|
||||||
|
addResources (Resources ("videos", UniversalId::Type_Video));
|
||||||
|
}
|
||||||
|
|
||||||
|
const CSMWorld::Resources& CSMWorld::ResourcesManager::get (UniversalId::Type type) const
|
||||||
|
{
|
||||||
|
std::map<UniversalId::Type, Resources>::const_iterator iter = mResources.find (type);
|
||||||
|
|
||||||
|
if (iter==mResources.end())
|
||||||
|
throw std::logic_error ("Unknown resource type");
|
||||||
|
|
||||||
|
return iter->second;
|
||||||
|
}
|
@ -0,0 +1,28 @@
|
|||||||
|
#ifndef CSM_WOLRD_RESOURCESMANAGER_H
|
||||||
|
#define CSM_WOLRD_RESOURCESMANAGER_H
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
|
||||||
|
#include "universalid.hpp"
|
||||||
|
#include "resources.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class ResourcesManager
|
||||||
|
{
|
||||||
|
std::map<UniversalId::Type, Resources> mResources;
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
void addResources (const Resources& resources);
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// Ask OGRE for a list of available resources.
|
||||||
|
void listResources();
|
||||||
|
|
||||||
|
const Resources& get (UniversalId::Type type) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -0,0 +1,146 @@
|
|||||||
|
|
||||||
|
#include "resourcetable.hpp"
|
||||||
|
|
||||||
|
#include <stdexcept>
|
||||||
|
|
||||||
|
#include "resources.hpp"
|
||||||
|
#include "columnbase.hpp"
|
||||||
|
#include "universalid.hpp"
|
||||||
|
|
||||||
|
CSMWorld::ResourceTable::ResourceTable (const Resources *resources, unsigned int features)
|
||||||
|
: IdTableBase (features | Feature_Constant), mResources (resources)
|
||||||
|
{}
|
||||||
|
|
||||||
|
CSMWorld::ResourceTable::~ResourceTable() {}
|
||||||
|
|
||||||
|
int CSMWorld::ResourceTable::rowCount (const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return mResources->getSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::ResourceTable::columnCount (const QModelIndex & parent) const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 2; // ID, type
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CSMWorld::ResourceTable::data (const QModelIndex & index, int role) const
|
||||||
|
{
|
||||||
|
if (role!=Qt::DisplayRole)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (index.column()==0)
|
||||||
|
return QString::fromUtf8 (mResources->getId (index.row()).c_str());
|
||||||
|
|
||||||
|
if (index.column()==1)
|
||||||
|
return static_cast<int> (mResources->getType());
|
||||||
|
|
||||||
|
throw std::logic_error ("Invalid column in resource table");
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant CSMWorld::ResourceTable::headerData (int section, Qt::Orientation orientation,
|
||||||
|
int role ) const
|
||||||
|
{
|
||||||
|
if (orientation==Qt::Vertical)
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
if (role==ColumnBase::Role_Flags)
|
||||||
|
return ColumnBase::Flag_Table;
|
||||||
|
|
||||||
|
switch (section)
|
||||||
|
{
|
||||||
|
case 0:
|
||||||
|
|
||||||
|
if (role==Qt::DisplayRole)
|
||||||
|
return Columns::getName (Columns::ColumnId_Id).c_str();
|
||||||
|
|
||||||
|
if (role==ColumnBase::Role_Display)
|
||||||
|
return ColumnBase::Display_String;
|
||||||
|
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 1:
|
||||||
|
|
||||||
|
if (role==Qt::DisplayRole)
|
||||||
|
return Columns::getName (Columns::ColumnId_RecordType).c_str();
|
||||||
|
|
||||||
|
if (role==ColumnBase::Role_Display)
|
||||||
|
return ColumnBase::Display_Integer;
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return QVariant();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ResourceTable::setData ( const QModelIndex &index, const QVariant &value,
|
||||||
|
int role)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Qt::ItemFlags CSMWorld::ResourceTable::flags (const QModelIndex & index) const
|
||||||
|
{
|
||||||
|
return Qt::ItemIsSelectable | Qt::ItemIsEnabled;;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex CSMWorld::ResourceTable::index (int row, int column, const QModelIndex& parent)
|
||||||
|
const
|
||||||
|
{
|
||||||
|
if (parent.isValid())
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
if (row<0 || row>=mResources->getSize())
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
if (column<0 || column>1)
|
||||||
|
return QModelIndex();
|
||||||
|
|
||||||
|
return createIndex (row, column);
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex CSMWorld::ResourceTable::parent (const QModelIndex& index) const
|
||||||
|
{
|
||||||
|
return QModelIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndex CSMWorld::ResourceTable::getModelIndex (const std::string& id, int column) const
|
||||||
|
{
|
||||||
|
return index (mResources->getIndex (id), column);
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::ResourceTable::searchColumnIndex (Columns::ColumnId id) const
|
||||||
|
{
|
||||||
|
if (id==Columns::ColumnId_Id)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (id==Columns::ColumnId_RecordType)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int CSMWorld::ResourceTable::findColumnIndex (Columns::ColumnId id) const
|
||||||
|
{
|
||||||
|
int index = searchColumnIndex (id);
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
throw std::logic_error ("invalid column index");
|
||||||
|
|
||||||
|
return index;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::pair<CSMWorld::UniversalId, std::string> CSMWorld::ResourceTable::view (int row) const
|
||||||
|
{
|
||||||
|
return std::make_pair (UniversalId::Type_None, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CSMWorld::ResourceTable::isDeleted (const std::string& id) const
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
@ -0,0 +1,57 @@
|
|||||||
|
#ifndef CSM_WOLRD_RESOURCETABLE_H
|
||||||
|
#define CSM_WOLRD_RESOURCETABLE_H
|
||||||
|
|
||||||
|
#include "idtablebase.hpp"
|
||||||
|
|
||||||
|
namespace CSMWorld
|
||||||
|
{
|
||||||
|
class Resources;
|
||||||
|
|
||||||
|
class ResourceTable : public IdTableBase
|
||||||
|
{
|
||||||
|
const Resources *mResources;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
/// \note The feature Feature_Constant will be added implicitly.
|
||||||
|
ResourceTable (const Resources *resources, unsigned int features = 0);
|
||||||
|
|
||||||
|
virtual ~ResourceTable();
|
||||||
|
|
||||||
|
virtual int rowCount (const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
virtual int columnCount (const QModelIndex & parent = QModelIndex()) const;
|
||||||
|
|
||||||
|
virtual QVariant data (const QModelIndex & index, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
virtual QVariant headerData (int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const;
|
||||||
|
|
||||||
|
virtual bool setData ( const QModelIndex &index, const QVariant &value, int role = Qt::EditRole);
|
||||||
|
|
||||||
|
virtual Qt::ItemFlags flags (const QModelIndex & index) const;
|
||||||
|
|
||||||
|
virtual QModelIndex index (int row, int column, const QModelIndex& parent = QModelIndex())
|
||||||
|
const;
|
||||||
|
|
||||||
|
virtual QModelIndex parent (const QModelIndex& index) const;
|
||||||
|
|
||||||
|
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||||
|
|
||||||
|
/// Return index of column with the given \a id. If no such column exists, -1 is
|
||||||
|
/// returned.
|
||||||
|
virtual int searchColumnIndex (Columns::ColumnId id) const;
|
||||||
|
|
||||||
|
/// Return index of column with the given \a id. If no such column exists, an
|
||||||
|
/// exception is thrown.
|
||||||
|
virtual int findColumnIndex (Columns::ColumnId id) const;
|
||||||
|
|
||||||
|
/// Return the UniversalId and the hint for viewing \a row. If viewing is not
|
||||||
|
/// supported by this table, return (UniversalId::Type_None, "").
|
||||||
|
virtual std::pair<UniversalId, std::string> view (int row) const;
|
||||||
|
|
||||||
|
/// Is \a id flagged as deleted?
|
||||||
|
virtual bool isDeleted (const std::string& id) const;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
@ -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
|
#ifndef CSV_WIDGET_SCENETOOL_H
|
||||||
#define CSV_WORLD_SCENETOOL_H
|
#define CSV_WIDGET_SCENETOOL_H
|
||||||
|
|
||||||
#include <QPushButton>
|
#include "pushbutton.hpp"
|
||||||
|
|
||||||
namespace CSVWorld
|
namespace CSVWidget
|
||||||
{
|
{
|
||||||
class SceneToolbar;
|
class SceneToolbar;
|
||||||
|
|
||||||
///< \brief Tool base class
|
///< \brief Tool base class
|
||||||
class SceneTool : public QPushButton
|
class SceneTool : public PushButton
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
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
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue