mirror of
https://github.com/OpenMW/openmw.git
synced 2025-01-21 07:53:53 +00:00
Merge branch 'master' of https://github.com/zinnschlag/openmw.git into MeleeCombat2
This commit is contained in:
commit
b248d85b84
140 changed files with 3690 additions and 1151 deletions
|
@ -19,7 +19,7 @@ include (OpenMWMacros)
|
|||
# Version
|
||||
|
||||
set (OPENMW_VERSION_MAJOR 0)
|
||||
set (OPENMW_VERSION_MINOR 24)
|
||||
set (OPENMW_VERSION_MINOR 25)
|
||||
set (OPENMW_VERSION_RELEASE 0)
|
||||
|
||||
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
|
||||
|
|
|
@ -106,10 +106,10 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
|
|||
{
|
||||
case '0': oper_str = "=="; break;
|
||||
case '1': oper_str = "!="; break;
|
||||
case '2': oper_str = "< "; break;
|
||||
case '3': oper_str = "<="; break;
|
||||
case '4': oper_str = "> "; break;
|
||||
case '5': oper_str = ">="; break;
|
||||
case '2': oper_str = "> "; break;
|
||||
case '3': oper_str = ">="; break;
|
||||
case '4': oper_str = "< "; break;
|
||||
case '5': oper_str = "<="; break;
|
||||
}
|
||||
|
||||
std::ostringstream stream;
|
||||
|
|
|
@ -57,13 +57,14 @@ opencs_hdrs_noqt (view/doc
|
|||
|
||||
|
||||
opencs_units (view/world
|
||||
table tablesubview scriptsubview util regionmapsubview
|
||||
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
|
||||
cellcreator referenceablecreator referencecreator
|
||||
)
|
||||
|
||||
opencs_units_noqt (view/world
|
||||
dialoguesubview subviews
|
||||
enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate
|
||||
scripthighlighter
|
||||
scripthighlighter idvalidator
|
||||
)
|
||||
|
||||
|
||||
|
|
|
@ -34,7 +34,7 @@ namespace CSMSettings
|
|||
bool updateItem (const QString &value);
|
||||
bool updateItem (int valueListIndex);
|
||||
|
||||
/// retroeve list of valid values for setting
|
||||
/// retrieve list of valid values for setting
|
||||
inline QStringList *getValueList() { return mValueList; }
|
||||
|
||||
/// write list of valid values for setting
|
||||
|
|
|
@ -42,6 +42,33 @@ CSMSettings::UserSettings::UserSettings()
|
|||
|
||||
mReadOnlyMessage = QObject::tr("<br><b>Could not open file for reading</b><br><br> \
|
||||
Please make sure you have the right permissions and try again.<br>");
|
||||
|
||||
buildEditorSettingDefaults();
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::buildEditorSettingDefaults()
|
||||
{
|
||||
SettingContainer *windowHeight = new SettingContainer("768", this);
|
||||
SettingContainer *windowWidth = new SettingContainer("1024", this);
|
||||
SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this);
|
||||
SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this);
|
||||
|
||||
windowHeight->setObjectName ("Height");
|
||||
windowWidth->setObjectName ("Width");
|
||||
rsDelegate->setObjectName ("Record Status Display");
|
||||
refIdTypeDelegate->setObjectName ("Referenceable ID Type Display");
|
||||
|
||||
SettingMap *displayFormatMap = new SettingMap;
|
||||
SettingMap *windowSizeMap = new SettingMap;
|
||||
|
||||
displayFormatMap->insert (rsDelegate->objectName(), rsDelegate );
|
||||
displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate);
|
||||
|
||||
windowSizeMap->insert (windowWidth->objectName(), windowWidth );
|
||||
windowSizeMap->insert (windowHeight->objectName(), windowHeight );
|
||||
|
||||
mEditorSettingDefaults.insert ("Display Format", displayFormatMap);
|
||||
mEditorSettingDefaults.insert ("Window Size", windowSizeMap);
|
||||
}
|
||||
|
||||
CSMSettings::UserSettings::~UserSettings()
|
||||
|
@ -104,17 +131,22 @@ bool CSMSettings::UserSettings::writeSettings(QMap<QString, CSMSettings::Setting
|
|||
}
|
||||
|
||||
|
||||
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSettings() const
|
||||
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const
|
||||
{
|
||||
return mSectionSettings;
|
||||
}
|
||||
|
||||
const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString §ionName) const
|
||||
{
|
||||
return getValidSettings(sectionName);
|
||||
}
|
||||
|
||||
bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
|
||||
{
|
||||
if (filePath.isEmpty())
|
||||
return false;
|
||||
|
||||
mSectionSettings.clear();
|
||||
SectionMap loadedSettings;
|
||||
|
||||
QTextStream *stream = openFileStream (filePath, true);
|
||||
|
||||
|
@ -150,7 +182,7 @@ bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
|
|||
{
|
||||
//add the previous section's settings to the member map
|
||||
if (settings)
|
||||
mSectionSettings.insert(section, settings);
|
||||
loadedSettings.insert(section, settings);
|
||||
|
||||
//save new section and create a new list
|
||||
section = sectionRe.cap(1);
|
||||
|
@ -167,18 +199,48 @@ bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
|
|||
|
||||
}
|
||||
|
||||
mSectionSettings.insert(section, settings);
|
||||
loadedSettings.insert(section, settings);
|
||||
|
||||
stream->device()->close();
|
||||
delete stream;
|
||||
stream = 0;
|
||||
}
|
||||
|
||||
mergeMap (loadedSettings);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap §ionSettings)
|
||||
{
|
||||
foreach (QString key, sectionSettings.uniqueKeys())
|
||||
{
|
||||
// insert entire section if it does not already exist in the loaded files
|
||||
if (mSectionSettings.find(key) == mSectionSettings.end())
|
||||
mSectionSettings.insert(key, sectionSettings.value(key));
|
||||
else
|
||||
{
|
||||
SettingMap *passedSettings = sectionSettings.value(key);
|
||||
SettingMap *settings = mSectionSettings.value(key);
|
||||
|
||||
foreach (QString key2, passedSettings->uniqueKeys())
|
||||
{
|
||||
//insert section settings individially if they do not already exist
|
||||
if (settings->find(key2) == settings->end())
|
||||
settings->insert(key2, passedSettings->value(key2));
|
||||
else
|
||||
{
|
||||
settings->value(key2)->update(passedSettings->value(key2)->getValue());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSMSettings::UserSettings::loadSettings (const QString &fileName)
|
||||
{
|
||||
mSectionSettings.clear();
|
||||
|
||||
//global
|
||||
QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName;
|
||||
bool globalOk = loadFromFile(globalFilePath);
|
||||
|
@ -207,10 +269,11 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName)
|
|||
|
||||
void CSMSettings::UserSettings::updateSettings (const QString §ionName, const QString &settingName)
|
||||
{
|
||||
if (mSectionSettings.find(sectionName) == mSectionSettings.end())
|
||||
return;
|
||||
|
||||
SettingMap *settings = mSectionSettings.value(sectionName);
|
||||
SettingMap *settings = getValidSettings(sectionName);
|
||||
|
||||
if (!settings)
|
||||
return;
|
||||
|
||||
if (settingName.isEmpty())
|
||||
{
|
||||
|
@ -229,15 +292,12 @@ void CSMSettings::UserSettings::updateSettings (const QString §ionName, cons
|
|||
|
||||
QString CSMSettings::UserSettings::getSetting (const QString §ion, const QString &setting) const
|
||||
{
|
||||
QString retVal = "";
|
||||
SettingMap *settings = getValidSettings(section);
|
||||
|
||||
if (mSectionSettings.find(section) != mSectionSettings.end())
|
||||
{
|
||||
CSMSettings::SettingMap *settings = mSectionSettings.value(section);
|
||||
QString retVal = "";
|
||||
|
||||
if (settings->find(setting) != settings->end())
|
||||
retVal = settings->value(setting)->getValue();
|
||||
}
|
||||
|
||||
return retVal;
|
||||
}
|
||||
|
@ -263,3 +323,33 @@ void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message,
|
|||
|
||||
msgBox.exec();
|
||||
}
|
||||
|
||||
CSMSettings::SettingMap *
|
||||
CSMSettings::UserSettings::getValidSettings (const QString §ionName) const
|
||||
{
|
||||
SettingMap *settings = 0;
|
||||
|
||||
//copy the default values for the entire section if it's not found
|
||||
if (mSectionSettings.find(sectionName) == mSectionSettings.end())
|
||||
{
|
||||
if (mEditorSettingDefaults.find(sectionName) != mEditorSettingDefaults.end())
|
||||
settings = mEditorSettingDefaults.value (sectionName);
|
||||
}
|
||||
//otherwise, iterate the section's settings, looking for missing values and replacing them with defaults.
|
||||
else
|
||||
{
|
||||
SettingMap *loadedSettings = mSectionSettings[sectionName];
|
||||
SettingMap *defaultSettings = mEditorSettingDefaults[sectionName];
|
||||
|
||||
foreach (QString key, defaultSettings->uniqueKeys())
|
||||
{
|
||||
//write the default value to the loaded settings
|
||||
if (loadedSettings->find((key))==loadedSettings->end())
|
||||
loadedSettings->insert(key, defaultSettings->value(key));
|
||||
}
|
||||
|
||||
settings = mSectionSettings.value (sectionName);
|
||||
}
|
||||
|
||||
return settings;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ namespace CSMSettings {
|
|||
Q_OBJECT
|
||||
|
||||
SectionMap mSectionSettings;
|
||||
SectionMap mEditorSettingDefaults;
|
||||
|
||||
static UserSettings *mUserSettingsInstance;
|
||||
QString mUserFilePath;
|
||||
Files::ConfigurationManager mCfgMgr;
|
||||
|
@ -58,7 +60,9 @@ namespace CSMSettings {
|
|||
void loadSettings (const QString &fileName);
|
||||
|
||||
/// Returns the entire map of settings across all sections
|
||||
const SectionMap &getSettings () const;
|
||||
const SectionMap &getSectionMap () const;
|
||||
|
||||
const SettingMap *getSettings (const QString §ionName) const;
|
||||
|
||||
/// Retrieves the value as a QString of the specified setting in the specified section
|
||||
QString getSetting(const QString §ion, const QString &setting) const;
|
||||
|
@ -72,8 +76,15 @@ namespace CSMSettings {
|
|||
/// Parses a setting file specified in filePath from the provided text stream.
|
||||
bool loadFromFile (const QString &filePath = "");
|
||||
|
||||
/// merge the passed map into mSectionSettings
|
||||
void mergeMap (const SectionMap &);
|
||||
|
||||
void displayFileErrorMessage(const QString &message, bool isReadOnly);
|
||||
|
||||
void buildEditorSettingDefaults();
|
||||
|
||||
SettingMap *getValidSettings (const QString §ionName) const;
|
||||
|
||||
signals:
|
||||
|
||||
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "idtableproxymodel.hpp"
|
||||
#include "idtable.hpp"
|
||||
#include "idtable.hpp"
|
||||
|
||||
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
|
||||
|
@ -25,15 +25,28 @@ void CSMWorld::ModifyCommand::undo()
|
|||
mModel.setData (mIndex, mOld);
|
||||
}
|
||||
|
||||
CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent)
|
||||
: QUndoCommand (parent), mModel (model), mId (id)
|
||||
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
|
||||
: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
|
||||
{
|
||||
setText (("Create record " + id).c_str());
|
||||
}
|
||||
|
||||
void CSMWorld::CreateCommand::addValue (int column, const QVariant& value)
|
||||
{
|
||||
mValues[column] = value;
|
||||
}
|
||||
|
||||
void CSMWorld::CreateCommand::setType (UniversalId::Type type)
|
||||
{
|
||||
mType = type;
|
||||
}
|
||||
|
||||
void CSMWorld::CreateCommand::redo()
|
||||
{
|
||||
mModel.addRecord (mId);
|
||||
mModel.addRecord (mId, mType);
|
||||
|
||||
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
|
||||
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
|
||||
}
|
||||
|
||||
void CSMWorld::CreateCommand::undo()
|
||||
|
|
|
@ -4,17 +4,20 @@
|
|||
#include "record.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
|
||||
#include <QVariant>
|
||||
#include <QUndoCommand>
|
||||
#include <QModelIndex>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
||||
class QModelIndex;
|
||||
class QAbstractItemModel;
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class IdTableProxyModel;
|
||||
class IdTable;
|
||||
class IdTable;
|
||||
class RecordBase;
|
||||
|
||||
|
@ -37,12 +40,18 @@ namespace CSMWorld
|
|||
|
||||
class CreateCommand : public QUndoCommand
|
||||
{
|
||||
IdTableProxyModel& mModel;
|
||||
IdTable& mModel;
|
||||
std::string mId;
|
||||
UniversalId::Type mType;
|
||||
std::map<int, QVariant> mValues;
|
||||
|
||||
public:
|
||||
|
||||
CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0);
|
||||
CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
|
||||
|
||||
void setType (UniversalId::Type type);
|
||||
|
||||
void addValue (int column, const QVariant& value);
|
||||
|
||||
virtual void redo();
|
||||
|
||||
|
|
|
@ -300,6 +300,16 @@ CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables()
|
|||
return mReferenceables;
|
||||
}
|
||||
|
||||
const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const
|
||||
{
|
||||
return mRefs;
|
||||
}
|
||||
|
||||
CSMWorld::RefCollection& CSMWorld::Data::getReferences()
|
||||
{
|
||||
return mRefs;
|
||||
}
|
||||
|
||||
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
|
||||
{
|
||||
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
|
||||
|
@ -395,3 +405,21 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool CSMWorld::Data::hasId (const std::string& id) const
|
||||
{
|
||||
return
|
||||
getGlobals().searchId (id)!=-1 ||
|
||||
getGmsts().searchId (id)!=-1 ||
|
||||
getSkills().searchId (id)!=-1 ||
|
||||
getClasses().searchId (id)!=-1 ||
|
||||
getFactions().searchId (id)!=-1 ||
|
||||
getRaces().searchId (id)!=-1 ||
|
||||
getSounds().searchId (id)!=-1 ||
|
||||
getScripts().searchId (id)!=-1 ||
|
||||
getRegions().searchId (id)!=-1 ||
|
||||
getBirthsigns().searchId (id)!=-1 ||
|
||||
getSpells().searchId (id)!=-1 ||
|
||||
getCells().searchId (id)!=-1 ||
|
||||
getReferenceables().searchId (id)!=-1;
|
||||
}
|
||||
|
|
|
@ -112,6 +112,10 @@ namespace CSMWorld
|
|||
|
||||
RefIdCollection& getReferenceables();
|
||||
|
||||
const RefCollection& getReferences() const;
|
||||
|
||||
RefCollection& getReferences();
|
||||
|
||||
QAbstractItemModel *getTableModel (const UniversalId& id);
|
||||
///< If no table model is available for \a id, an exception is thrown.
|
||||
///
|
||||
|
@ -123,6 +127,8 @@ namespace CSMWorld
|
|||
|
||||
void loadFile (const boost::filesystem::path& path, bool base);
|
||||
///< Merging content of a file into base or modified.
|
||||
|
||||
bool hasId (const std::string& id) const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -116,13 +116,13 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
|
|||
return QModelIndex();
|
||||
}
|
||||
|
||||
void CSMWorld::IdTable::addRecord (const std::string& id)
|
||||
void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)
|
||||
{
|
||||
int index = mIdCollection->getAppendIndex();
|
||||
|
||||
beginInsertRows (QModelIndex(), index, index);
|
||||
|
||||
mIdCollection->appendBlankRecord (id);
|
||||
mIdCollection->appendBlankRecord (id, type);
|
||||
|
||||
endInsertRows();
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <QAbstractItemModel>
|
||||
|
||||
#include "universalid.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CollectionBase;
|
||||
|
@ -44,7 +46,8 @@ namespace CSMWorld
|
|||
|
||||
virtual QModelIndex parent (const QModelIndex& index) const;
|
||||
|
||||
void addRecord (const std::string& id);
|
||||
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
|
||||
///< \param type Will be ignored, unless the collection supports multiple record types
|
||||
|
||||
QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||
|
||||
|
|
|
@ -7,11 +7,6 @@ CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
|
|||
: QSortFilterProxyModel (parent)
|
||||
{}
|
||||
|
||||
void CSMWorld::IdTableProxyModel::addRecord (const std::string& id)
|
||||
{
|
||||
dynamic_cast<IdTable&> (*sourceModel()).addRecord (id);
|
||||
}
|
||||
|
||||
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
|
||||
{
|
||||
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));
|
||||
|
|
|
@ -15,8 +15,6 @@ namespace CSMWorld
|
|||
|
||||
IdTableProxyModel (QObject *parent = 0);
|
||||
|
||||
virtual void addRecord (const std::string& id);
|
||||
|
||||
virtual QModelIndex getModelIndex (const std::string& id, int column) const;
|
||||
};
|
||||
}
|
||||
|
|
|
@ -21,10 +21,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
|||
while (cell2.getNextRef (reader, ref))
|
||||
{
|
||||
/// \todo handle deleted and moved references
|
||||
std::ostringstream stream;
|
||||
stream << "ref#" << mNextId++;
|
||||
|
||||
ref.load (reader, cell2, stream.str());
|
||||
ref.load (reader, cell2, getNewId());
|
||||
|
||||
Record<CellRef> record2;
|
||||
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||
|
@ -35,3 +32,10 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
|
|||
|
||||
mCells.setRecord (cellIndex, cell);
|
||||
}
|
||||
|
||||
std::string CSMWorld::RefCollection::getNewId()
|
||||
{
|
||||
std::ostringstream stream;
|
||||
stream << "ref#" << mNextId++;
|
||||
return stream.str();
|
||||
}
|
|
@ -21,6 +21,8 @@ namespace CSMWorld
|
|||
|
||||
void load (ESM::ESMReader& reader, int cellIndex, bool base);
|
||||
///< Load a sequence of references.
|
||||
|
||||
std::string getNewId();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,81 +12,82 @@ namespace
|
|||
CSMWorld::UniversalId::Class mClass;
|
||||
CSMWorld::UniversalId::Type mType;
|
||||
const char *mName;
|
||||
const char *mIcon;
|
||||
};
|
||||
|
||||
static const TypeData sNoArg[] =
|
||||
{
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" },
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
|
||||
"Referenceables" },
|
||||
"Referenceables", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
|
||||
"References" },
|
||||
"References", 0 },
|
||||
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap,
|
||||
"Region Map" },
|
||||
"Region Map", 0 },
|
||||
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
|
||||
};
|
||||
|
||||
static const TypeData sIdArg[] =
|
||||
{
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList,
|
||||
"Creature Levelled List" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList,
|
||||
"Item Levelled List" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous,
|
||||
"Miscellaneous" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Reference, "Reference" },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", 0 },
|
||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":./container.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,
|
||||
"Creature Levelled List", ":./creature.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList,
|
||||
"Item Levelled List", ":./leveled-item.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous,
|
||||
"Miscellaneous", ":./miscellaneous.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
|
||||
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
|
||||
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 },
|
||||
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
|
||||
};
|
||||
|
||||
static const TypeData sIndexArg[] =
|
||||
{
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results" },
|
||||
{ CSMWorld::UniversalId::Class_Transient, CSMWorld::UniversalId::Type_VerificationResults, "Verification Results", 0 },
|
||||
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
|
||||
};
|
||||
|
||||
static const unsigned int IDARG_SIZE = sizeof (sIdArg) / sizeof (TypeData);
|
||||
|
@ -268,6 +269,28 @@ std::string CSMWorld::UniversalId::toString() const
|
|||
return stream.str();
|
||||
}
|
||||
|
||||
std::string CSMWorld::UniversalId::getIcon() const
|
||||
{
|
||||
const TypeData *typeData = mArgumentType==ArgumentType_None ? sNoArg :
|
||||
(mArgumentType==ArgumentType_Id ? sIdArg : sIndexArg);
|
||||
|
||||
for (int i=0; typeData[i].mName; ++i)
|
||||
if (typeData[i].mType==mType)
|
||||
return typeData[i].mIcon ? typeData[i].mIcon : "";
|
||||
|
||||
throw std::logic_error ("failed to retrieve UniversalId type icon");
|
||||
}
|
||||
|
||||
std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceableTypes()
|
||||
{
|
||||
std::vector<CSMWorld::UniversalId::Type> list;
|
||||
|
||||
for (int i=0; sIdArg[i].mName; ++i)
|
||||
if (sIdArg[i].mClass==Class_RefRecord)
|
||||
list.push_back (sIdArg[i].mType);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
std::pair<int, const char *> CSMWorld::UniversalId::getIdArgPair (unsigned int index)
|
||||
{
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
|
||||
#include <string>
|
||||
#include <iosfwd>
|
||||
#include <vector>
|
||||
|
||||
#include <QMetaType>
|
||||
|
||||
|
@ -16,6 +17,7 @@ namespace CSMWorld
|
|||
{
|
||||
Class_None = 0,
|
||||
Class_Record,
|
||||
Class_RefRecord, // referenceable record
|
||||
Class_SubRecord,
|
||||
Class_RecordList,
|
||||
Class_Collection, // multiple types of records combined
|
||||
|
@ -126,6 +128,11 @@ namespace CSMWorld
|
|||
|
||||
std::string toString() const;
|
||||
|
||||
std::string getIcon() const;
|
||||
///< Will return an empty string, if no icon is available.
|
||||
|
||||
static std::vector<Type> listReferenceableTypes();
|
||||
|
||||
static std::pair<int, const char *> getIdArgPair (unsigned int index);
|
||||
static unsigned int getIdArgSize ();
|
||||
};
|
||||
|
|
|
@ -1,15 +1,10 @@
|
|||
#include "subview.hpp"
|
||||
|
||||
|
||||
CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id)
|
||||
{
|
||||
/// \todo add a button to the title bar that clones this sub view
|
||||
|
||||
setWindowTitle (mUniversalId.toString().c_str());
|
||||
|
||||
/// \todo remove (for testing only)
|
||||
setMinimumWidth (100);
|
||||
setMinimumHeight (60);
|
||||
}
|
||||
|
||||
CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const
|
||||
|
@ -20,3 +15,5 @@ CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const
|
|||
void CSVDoc::SubView::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
{
|
||||
}
|
||||
|
||||
void CSVDoc::SubView::setStatusBar (bool show) {}
|
|
@ -37,6 +37,9 @@ namespace CSVDoc
|
|||
virtual void setEditLock (bool locked) = 0;
|
||||
virtual void updateEditorSetting (const QString &, const QString &);
|
||||
|
||||
virtual void setStatusBar (bool show);
|
||||
///< Default implementation: ignored
|
||||
|
||||
signals:
|
||||
|
||||
void focusId (const CSMWorld::UniversalId& universalId);
|
||||
|
|
|
@ -22,28 +22,20 @@ namespace CSVDoc
|
|||
return new SubViewT (id, document);
|
||||
}
|
||||
|
||||
template<class SubViewT>
|
||||
class SubViewFactoryWithCreateFlag : public SubViewFactoryBase
|
||||
|
||||
template<class SubViewT, class CreatorFactoryT>
|
||||
class SubViewFactoryWithCreator : public SubViewFactoryBase
|
||||
{
|
||||
bool mCreateAndDelete;
|
||||
|
||||
public:
|
||||
|
||||
SubViewFactoryWithCreateFlag (bool createAndDelete);
|
||||
|
||||
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
|
||||
};
|
||||
|
||||
template<class SubViewT>
|
||||
SubViewFactoryWithCreateFlag<SubViewT>::SubViewFactoryWithCreateFlag (bool createAndDelete)
|
||||
: mCreateAndDelete (createAndDelete)
|
||||
{}
|
||||
|
||||
template<class SubViewT>
|
||||
CSVDoc::SubView *SubViewFactoryWithCreateFlag<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,
|
||||
CSMDoc::Document& document)
|
||||
template<class SubViewT, class CreatorFactoryT>
|
||||
CSVDoc::SubView *SubViewFactoryWithCreator<SubViewT, CreatorFactoryT>::makeSubView (
|
||||
const CSMWorld::UniversalId& id, CSMDoc::Document& document)
|
||||
{
|
||||
return new SubViewT (id, document, mCreateAndDelete);
|
||||
return new SubViewT (id, document, CreatorFactoryT());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -78,56 +78,21 @@ void CSVDoc::View::setupViewMenu()
|
|||
QAction *newWindow = new QAction (tr ("&New View"), this);
|
||||
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
|
||||
view->addAction (newWindow);
|
||||
|
||||
mShowStatusBar = new QAction (tr ("Show Status Bar"), this);
|
||||
mShowStatusBar->setCheckable (true);
|
||||
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
|
||||
view->addAction (mShowStatusBar);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupWorldMenu()
|
||||
{
|
||||
QMenu *world = menuBar()->addMenu (tr ("&World"));
|
||||
|
||||
QAction *globals = new QAction (tr ("Globals"), this);
|
||||
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
|
||||
world->addAction (globals);
|
||||
|
||||
QAction *gmsts = new QAction (tr ("Game settings"), this);
|
||||
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
|
||||
world->addAction (gmsts);
|
||||
|
||||
QAction *skills = new QAction (tr ("Skills"), this);
|
||||
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
|
||||
world->addAction (skills);
|
||||
|
||||
QAction *classes = new QAction (tr ("Classes"), this);
|
||||
connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));
|
||||
world->addAction (classes);
|
||||
|
||||
QAction *factions = new QAction (tr ("Factions"), this);
|
||||
connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));
|
||||
world->addAction (factions);
|
||||
|
||||
QAction *races = new QAction (tr ("Races"), this);
|
||||
connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView()));
|
||||
world->addAction (races);
|
||||
|
||||
QAction *sounds = new QAction (tr ("Sounds"), this);
|
||||
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
|
||||
world->addAction (sounds);
|
||||
|
||||
QAction *scripts = new QAction (tr ("Scripts"), this);
|
||||
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
|
||||
world->addAction (scripts);
|
||||
|
||||
QAction *regions = new QAction (tr ("Regions"), this);
|
||||
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
|
||||
world->addAction (regions);
|
||||
|
||||
QAction *birthsigns = new QAction (tr ("Birthsigns"), this);
|
||||
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
|
||||
world->addAction (birthsigns);
|
||||
|
||||
QAction *spells = new QAction (tr ("Spells"), this);
|
||||
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
|
||||
world->addAction (spells);
|
||||
|
||||
QAction *cells = new QAction (tr ("Cells"), this);
|
||||
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
|
||||
world->addAction (cells);
|
||||
|
@ -140,17 +105,71 @@ void CSVDoc::View::setupWorldMenu()
|
|||
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
|
||||
world->addAction (references);
|
||||
|
||||
world->addSeparator(); // items that don't represent single record lists follow here
|
||||
|
||||
QAction *regionMap = new QAction (tr ("Region Map"), this);
|
||||
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
|
||||
world->addAction (regionMap);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupMechanicsMenu()
|
||||
{
|
||||
QMenu *mechanics = menuBar()->addMenu (tr ("&Mechanics"));
|
||||
|
||||
QAction *globals = new QAction (tr ("Globals"), this);
|
||||
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
|
||||
mechanics->addAction (globals);
|
||||
|
||||
QAction *gmsts = new QAction (tr ("Game settings"), this);
|
||||
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
|
||||
mechanics->addAction (gmsts);
|
||||
|
||||
QAction *skills = new QAction (tr ("Skills"), this);
|
||||
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
|
||||
mechanics->addAction (skills);
|
||||
|
||||
QAction *classes = new QAction (tr ("Classes"), this);
|
||||
connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));
|
||||
mechanics->addAction (classes);
|
||||
|
||||
QAction *factions = new QAction (tr ("Factions"), this);
|
||||
connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));
|
||||
mechanics->addAction (factions);
|
||||
|
||||
QAction *races = new QAction (tr ("Races"), this);
|
||||
connect (races, SIGNAL (triggered()), this, SLOT (addRacesSubView()));
|
||||
mechanics->addAction (races);
|
||||
|
||||
QAction *scripts = new QAction (tr ("Scripts"), this);
|
||||
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
|
||||
mechanics->addAction (scripts);
|
||||
|
||||
QAction *birthsigns = new QAction (tr ("Birthsigns"), this);
|
||||
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
|
||||
mechanics->addAction (birthsigns);
|
||||
|
||||
QAction *spells = new QAction (tr ("Spells"), this);
|
||||
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
|
||||
mechanics->addAction (spells);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupAssetsMenu()
|
||||
{
|
||||
QMenu *assets = menuBar()->addMenu (tr ("&Assets"));
|
||||
|
||||
QAction *sounds = new QAction (tr ("Sounds"), this);
|
||||
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
|
||||
assets->addAction (sounds);
|
||||
}
|
||||
|
||||
void CSVDoc::View::setupUi()
|
||||
{
|
||||
setupFileMenu();
|
||||
setupEditMenu();
|
||||
setupViewMenu();
|
||||
setupWorldMenu();
|
||||
setupMechanicsMenu();
|
||||
setupAssetsMenu();
|
||||
}
|
||||
|
||||
void CSVDoc::View::updateTitle()
|
||||
|
@ -189,9 +208,6 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
|
|||
QString width = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Width"));
|
||||
QString height = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Height"));
|
||||
|
||||
if(width==QString() || height==QString())
|
||||
resize(800, 600);
|
||||
else
|
||||
resize (width.toInt(), height.toInt());
|
||||
|
||||
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);
|
||||
|
@ -271,6 +287,9 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id)
|
|||
/// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis)
|
||||
|
||||
SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
|
||||
|
||||
view->setStatusBar (mShowStatusBar->isChecked());
|
||||
|
||||
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
|
||||
|
||||
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this,
|
||||
|
@ -425,3 +444,12 @@ void CSVDoc::View::updateEditorSetting (const QString &settingName, const QStrin
|
|||
else if (settingName == "Height")
|
||||
resizeViewHeight (settingValue.toInt());
|
||||
}
|
||||
|
||||
void CSVDoc::View::toggleShowStatusBar (bool show)
|
||||
{
|
||||
foreach (QObject *view, mSubViewWindow.children())
|
||||
{
|
||||
if (CSVDoc::SubView *subView = dynamic_cast<CSVDoc::SubView *> (view))
|
||||
subView->setStatusBar (show);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,7 @@ namespace CSVDoc
|
|||
QAction *mRedo;
|
||||
QAction *mSave;
|
||||
QAction *mVerify;
|
||||
QAction *mShowStatusBar;
|
||||
std::vector<QAction *> mEditingActions;
|
||||
Operations *mOperations;
|
||||
SubViewFactoryManager mSubViewFactory;
|
||||
|
@ -60,6 +61,10 @@ namespace CSVDoc
|
|||
|
||||
void setupWorldMenu();
|
||||
|
||||
void setupMechanicsMenu();
|
||||
|
||||
void setupAssetsMenu();
|
||||
|
||||
void setupUi();
|
||||
|
||||
void updateTitle();
|
||||
|
@ -154,6 +159,8 @@ namespace CSVDoc
|
|||
void addRegionMapSubView();
|
||||
|
||||
void showUserSettings();
|
||||
|
||||
void toggleShowStatusBar (bool show);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -12,7 +12,7 @@ CSVSettings::GroupBlockDef *CSVSettings::DataDisplayFormatPage::setupDataDisplay
|
|||
{
|
||||
GroupBlockDef *statusBlock = new GroupBlockDef(QString(title));
|
||||
|
||||
SettingsItemDef *statusItem = new SettingsItemDef (statusBlock->title, "Icon and Text");
|
||||
SettingsItemDef *statusItem = new SettingsItemDef (statusBlock->title, "Icon Only");
|
||||
*(statusItem->valueList) << QString("Icon and Text") << QString("Icon Only") << QString("Text Only");
|
||||
|
||||
WidgetDef statusWidget (Widget_RadioButton);
|
||||
|
|
|
@ -15,7 +15,7 @@ namespace CSVSettings
|
|||
|
||||
ItemBlock (QWidget* parent = 0);
|
||||
|
||||
/// pure virtual function not implemneted
|
||||
/// pure virtual function not implemented
|
||||
bool updateSettings (const CSMSettings::SettingMap &settings) { return false; }
|
||||
|
||||
CSMSettings::SettingList *getSettings ();
|
||||
|
|
|
@ -44,7 +44,6 @@ void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event)
|
|||
void CSVSettings::UserSettingsDialog::setWidgetStates ()
|
||||
{
|
||||
CSMSettings::UserSettings::instance().loadSettings("opencs.cfg");
|
||||
const CSMSettings::SectionMap §ionSettings = CSMSettings::UserSettings::instance().getSettings();
|
||||
|
||||
//iterate the tabWidget's pages (sections)
|
||||
for (int i = 0; i < mStackedWidget->count(); i++)
|
||||
|
@ -53,13 +52,10 @@ void CSVSettings::UserSettingsDialog::setWidgetStates ()
|
|||
//and update widget
|
||||
QString pageName = mStackedWidget->widget(i)->objectName();
|
||||
|
||||
if (sectionSettings.find(pageName) != sectionSettings.end())
|
||||
{
|
||||
CSMSettings::SettingMap *settings = sectionSettings.value(pageName);
|
||||
const CSMSettings::SettingMap *settings = CSMSettings::UserSettings::instance().getSettings(pageName);
|
||||
AbstractPage &page = getAbstractPage (i);
|
||||
page.initializeWidgets(*settings);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CSVSettings::UserSettingsDialog::buildPages()
|
||||
|
|
|
@ -19,7 +19,7 @@
|
|||
#include "../../view/settings/abstractblock.hpp"
|
||||
|
||||
CSVSettings::WindowPage::WindowPage(QWidget *parent):
|
||||
AbstractPage("Window", parent)
|
||||
AbstractPage("Window Size", parent)
|
||||
{
|
||||
// Hacks to get the stylesheet look properly
|
||||
#ifdef Q_OS_MAC
|
||||
|
@ -82,7 +82,7 @@ CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildCustomWindowSize()
|
|||
|
||||
CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildWindowSizeToggle()
|
||||
{
|
||||
GroupBlockDef *block = new GroupBlockDef ("Window Size");
|
||||
GroupBlockDef *block = new GroupBlockDef (objectName());
|
||||
|
||||
// window size toggle
|
||||
block->captions << "Pre-Defined" << "Custom";
|
||||
|
|
81
apps/opencs/view/world/cellcreator.cpp
Normal file
81
apps/opencs/view/world/cellcreator.cpp
Normal file
|
@ -0,0 +1,81 @@
|
|||
|
||||
#include "cellcreator.hpp"
|
||||
|
||||
#include <limits>
|
||||
#include <sstream>
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QSpinBox>
|
||||
#include <QLabel>
|
||||
|
||||
std::string CSVWorld::CellCreator::getId() const
|
||||
{
|
||||
if (mType->currentIndex()==0)
|
||||
return GenericCreator::getId();
|
||||
|
||||
std::ostringstream stream;
|
||||
|
||||
stream << "#" << mX->value() << " " << mY->value();
|
||||
|
||||
return stream.str();
|
||||
}
|
||||
|
||||
CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id)
|
||||
: GenericCreator (data, undoStack, id)
|
||||
{
|
||||
mY = new QSpinBox (this);
|
||||
mY->setVisible (false);
|
||||
mY->setMinimum (std::numeric_limits<int>::min());
|
||||
mY->setMaximum (std::numeric_limits<int>::max());
|
||||
connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));
|
||||
insertAtBeginning (mY, true);
|
||||
|
||||
mYLabel = new QLabel ("Y", this);
|
||||
mYLabel->setVisible (false);
|
||||
insertAtBeginning (mYLabel, false);
|
||||
|
||||
mX = new QSpinBox (this);
|
||||
mX->setVisible (false);
|
||||
mX->setMinimum (std::numeric_limits<int>::min());
|
||||
mX->setMaximum (std::numeric_limits<int>::max());
|
||||
connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));
|
||||
insertAtBeginning (mX, true);
|
||||
|
||||
mXLabel = new QLabel ("X", this);
|
||||
mXLabel->setVisible (false);
|
||||
insertAtBeginning (mXLabel, false);
|
||||
|
||||
mType = new QComboBox (this);
|
||||
|
||||
mType->addItem ("Interior Cell");
|
||||
mType->addItem ("Exterior Cell");
|
||||
|
||||
connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int)));
|
||||
|
||||
insertAtBeginning (mType, false);
|
||||
}
|
||||
|
||||
void CSVWorld::CellCreator::reset()
|
||||
{
|
||||
mX->setValue (0);
|
||||
mY->setValue (0);
|
||||
mType->setCurrentIndex (0);
|
||||
GenericCreator::reset();
|
||||
}
|
||||
|
||||
void CSVWorld::CellCreator::setType (int index)
|
||||
{
|
||||
setManualEditing (index==0);
|
||||
mXLabel->setVisible (index==1);
|
||||
mX->setVisible (index==1);
|
||||
mYLabel->setVisible (index==1);
|
||||
mY->setVisible (index==1);
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void CSVWorld::CellCreator::valueChanged (int index)
|
||||
{
|
||||
update();
|
||||
}
|
40
apps/opencs/view/world/cellcreator.hpp
Normal file
40
apps/opencs/view/world/cellcreator.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef CSV_WORLD_CELLCREATOR_H
|
||||
#define CSV_WORLD_CELLCREATOR_H
|
||||
|
||||
class QLabel;
|
||||
class QSpinBox;
|
||||
class QComboBox;
|
||||
|
||||
#include "genericcreator.hpp"
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class CellCreator : public GenericCreator
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QComboBox *mType;
|
||||
QLabel *mXLabel;
|
||||
QSpinBox *mX;
|
||||
QLabel *mYLabel;
|
||||
QSpinBox *mY;
|
||||
|
||||
protected:
|
||||
|
||||
virtual std::string getId() const;
|
||||
|
||||
public:
|
||||
|
||||
CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
|
||||
|
||||
virtual void reset();
|
||||
|
||||
private slots:
|
||||
|
||||
void setType (int index);
|
||||
|
||||
void valueChanged (int index);
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
13
apps/opencs/view/world/creator.cpp
Normal file
13
apps/opencs/view/world/creator.cpp
Normal file
|
@ -0,0 +1,13 @@
|
|||
|
||||
#include "creator.hpp"
|
||||
|
||||
CSVWorld::Creator:: ~Creator() {}
|
||||
|
||||
CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {}
|
||||
|
||||
|
||||
CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data,
|
||||
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
|
||||
{
|
||||
return 0;
|
||||
}
|
86
apps/opencs/view/world/creator.hpp
Normal file
86
apps/opencs/view/world/creator.hpp
Normal file
|
@ -0,0 +1,86 @@
|
|||
#ifndef CSV_WORLD_CREATOR_H
|
||||
#define CSV_WORLD_CREATOR_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QUndoStack;
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
class UniversalId;
|
||||
}
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
/// \brief Record creator UI base class
|
||||
class Creator : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
|
||||
virtual ~Creator();
|
||||
|
||||
virtual void reset() = 0;
|
||||
|
||||
virtual void setEditLock (bool locked) = 0;
|
||||
|
||||
signals:
|
||||
|
||||
void done();
|
||||
|
||||
void requestFocus (const std::string& id);
|
||||
///< Request owner of this creator to focus the just created \a id. The owner may
|
||||
/// ignore this request.
|
||||
};
|
||||
|
||||
/// \brief Base class for Creator factory
|
||||
class CreatorFactoryBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual ~CreatorFactoryBase();
|
||||
|
||||
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id) const = 0;
|
||||
///< The ownership of the returned Creator is transferred to the caller.
|
||||
///
|
||||
/// \note The function can return a 0-pointer, which means no UI for creating/deleting
|
||||
/// records should be provided.
|
||||
};
|
||||
|
||||
/// \brief Creator factory that does not produces any creator
|
||||
class NullCreatorFactory : public CreatorFactoryBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id) const;
|
||||
///< The ownership of the returned Creator is transferred to the caller.
|
||||
///
|
||||
/// \note The function always returns 0.
|
||||
};
|
||||
|
||||
template<class CreatorT>
|
||||
class CreatorFactory : public CreatorFactoryBase
|
||||
{
|
||||
public:
|
||||
|
||||
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id) const;
|
||||
///< The ownership of the returned Creator is transferred to the caller.
|
||||
///
|
||||
/// \note The function can return a 0-pointer, which means no UI for creating/deleting
|
||||
/// records should be provided.
|
||||
};
|
||||
|
||||
template<class CreatorT>
|
||||
Creator *CreatorFactory<CreatorT>::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id) const
|
||||
{
|
||||
return new CreatorT (data, undoStack, id);
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
135
apps/opencs/view/world/genericcreator.cpp
Normal file
135
apps/opencs/view/world/genericcreator.cpp
Normal file
|
@ -0,0 +1,135 @@
|
|||
|
||||
#include "genericcreator.hpp"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QHBoxLayout>
|
||||
#include <QPushButton>
|
||||
#include <QLineEdit>
|
||||
#include <QUndoStack>
|
||||
|
||||
#include "../../model/world/commands.hpp"
|
||||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/idtable.hpp"
|
||||
|
||||
#include "idvalidator.hpp"
|
||||
|
||||
void CSVWorld::GenericCreator::update()
|
||||
{
|
||||
mErrors = getErrors();
|
||||
|
||||
mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str()));
|
||||
mId->setToolTip (QString::fromUtf8 (mErrors.c_str()));
|
||||
|
||||
mCreate->setEnabled (mErrors.empty() && !mLocked);
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::setManualEditing (bool enabled)
|
||||
{
|
||||
mId->setVisible (enabled);
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched)
|
||||
{
|
||||
mLayout->insertWidget (0, widget, stretched ? 1 : 0);
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched)
|
||||
{
|
||||
mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0);
|
||||
}
|
||||
|
||||
std::string CSVWorld::GenericCreator::getId() const
|
||||
{
|
||||
return mId->text().toUtf8().constData();
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {}
|
||||
|
||||
const CSMWorld::Data& CSVWorld::GenericCreator::getData() const
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
CSMWorld::Data& CSVWorld::GenericCreator::getData()
|
||||
{
|
||||
return mData;
|
||||
}
|
||||
|
||||
CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id)
|
||||
: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false)
|
||||
{
|
||||
mLayout = new QHBoxLayout;
|
||||
mLayout->setContentsMargins (0, 0, 0, 0);
|
||||
|
||||
mId = new QLineEdit;
|
||||
mId->setValidator (new IdValidator (this));
|
||||
mLayout->addWidget (mId, 1);
|
||||
|
||||
mCreate = new QPushButton ("Create");
|
||||
mLayout->addWidget (mCreate);
|
||||
|
||||
QPushButton *cancelButton = new QPushButton ("Cancel");
|
||||
mLayout->addWidget (cancelButton);
|
||||
|
||||
setLayout (mLayout);
|
||||
|
||||
connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done()));
|
||||
connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create()));
|
||||
|
||||
connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::setEditLock (bool locked)
|
||||
{
|
||||
mLocked = locked;
|
||||
update();
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::reset()
|
||||
{
|
||||
mId->setText ("");
|
||||
update();
|
||||
}
|
||||
|
||||
std::string CSVWorld::GenericCreator::getErrors() const
|
||||
{
|
||||
std::string errors;
|
||||
|
||||
std::string id = getId();
|
||||
|
||||
if (id.empty())
|
||||
{
|
||||
errors = "Missing ID";
|
||||
}
|
||||
else if (mData.hasId (id))
|
||||
{
|
||||
errors = "ID is already in use";
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::textChanged (const QString& text)
|
||||
{
|
||||
update();
|
||||
}
|
||||
|
||||
void CSVWorld::GenericCreator::create()
|
||||
{
|
||||
if (!mLocked)
|
||||
{
|
||||
std::string id = getId();
|
||||
|
||||
std::auto_ptr<CSMWorld::CreateCommand> command (new CSMWorld::CreateCommand (
|
||||
dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel (mListId)), id));
|
||||
|
||||
configureCreateCommand (*command);
|
||||
|
||||
mUndoStack.push (command.release());
|
||||
|
||||
emit done();
|
||||
emit requestFocus (id);
|
||||
}
|
||||
}
|
73
apps/opencs/view/world/genericcreator.hpp
Normal file
73
apps/opencs/view/world/genericcreator.hpp
Normal file
|
@ -0,0 +1,73 @@
|
|||
#ifndef CSV_WORLD_GENERICCREATOR_H
|
||||
#define CSV_WORLD_GENERICCREATOR_H
|
||||
|
||||
class QPushButton;
|
||||
class QLineEdit;
|
||||
class QHBoxLayout;
|
||||
|
||||
#include "creator.hpp"
|
||||
|
||||
#include "../../model/world/universalid.hpp"
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class CreateCommand;
|
||||
}
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class GenericCreator : public Creator
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
CSMWorld::Data& mData;
|
||||
QUndoStack& mUndoStack;
|
||||
CSMWorld::UniversalId mListId;
|
||||
QPushButton *mCreate;
|
||||
QLineEdit *mId;
|
||||
std::string mErrors;
|
||||
QHBoxLayout *mLayout;
|
||||
bool mLocked;
|
||||
|
||||
protected:
|
||||
|
||||
void update();
|
||||
|
||||
virtual void setManualEditing (bool enabled);
|
||||
///< Enable/disable manual ID editing (enabled by default).
|
||||
|
||||
void insertAtBeginning (QWidget *widget, bool stretched);
|
||||
|
||||
void insertBeforeButtons (QWidget *widget, bool stretched);
|
||||
|
||||
virtual std::string getId() const;
|
||||
|
||||
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
|
||||
|
||||
const CSMWorld::Data& getData() const;
|
||||
|
||||
CSMWorld::Data& getData();
|
||||
|
||||
public:
|
||||
|
||||
GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id);
|
||||
|
||||
virtual void setEditLock (bool locked);
|
||||
|
||||
virtual void reset();
|
||||
|
||||
virtual std::string getErrors() const;
|
||||
///< Return formatted error descriptions for the current state of the creator. if an empty
|
||||
/// string is returned, there is no error.
|
||||
|
||||
|
||||
private slots:
|
||||
|
||||
void textChanged (const QString& text);
|
||||
|
||||
void create();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
26
apps/opencs/view/world/idvalidator.cpp
Normal file
26
apps/opencs/view/world/idvalidator.cpp
Normal file
|
@ -0,0 +1,26 @@
|
|||
|
||||
#include "idvalidator.hpp"
|
||||
|
||||
bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const
|
||||
{
|
||||
if (c.isLetter() || c=='_')
|
||||
return true;
|
||||
|
||||
if (!first && (c.isDigit() || c.isSpace()))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {}
|
||||
|
||||
QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const
|
||||
{
|
||||
bool first = true;
|
||||
|
||||
for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false)
|
||||
if (!isValid (*iter, first))
|
||||
return QValidator::Invalid;
|
||||
|
||||
return QValidator::Acceptable;
|
||||
}
|
23
apps/opencs/view/world/idvalidator.hpp
Normal file
23
apps/opencs/view/world/idvalidator.hpp
Normal file
|
@ -0,0 +1,23 @@
|
|||
#ifndef CSV_WORLD_IDVALIDATOR_H
|
||||
#define CSV_WORLD_IDVALIDATOR_H
|
||||
|
||||
#include <QValidator>
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class IdValidator : public QValidator
|
||||
{
|
||||
private:
|
||||
|
||||
bool isValid (const QChar& c, bool first) const;
|
||||
|
||||
public:
|
||||
|
||||
IdValidator (QObject *parent = 0);
|
||||
|
||||
virtual State validate (QString& input, int& pos) const;
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -16,7 +16,7 @@ CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (
|
|||
return new RecordStatusDelegate (mValues, mIcons, undoStack, parent);
|
||||
}
|
||||
|
||||
void CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
bool CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
{
|
||||
if (settingName == "Record Status Display")
|
||||
{
|
||||
|
@ -28,7 +28,11 @@ void CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &setting
|
|||
|
||||
else if (settingValue == "Text Only")
|
||||
mDisplayMode = Mode_TextOnly;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace CSVWorld
|
|||
const IconList& icons,
|
||||
QUndoStack& undoStack, QObject *parent = 0);
|
||||
|
||||
void updateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
||||
};
|
||||
|
||||
|
|
43
apps/opencs/view/world/referenceablecreator.cpp
Normal file
43
apps/opencs/view/world/referenceablecreator.cpp
Normal file
|
@ -0,0 +1,43 @@
|
|||
|
||||
#include "referenceablecreator.hpp"
|
||||
|
||||
#include <QComboBox>
|
||||
#include <QLabel>
|
||||
|
||||
#include "../../model/world/universalid.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
|
||||
void CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
command.setType (
|
||||
static_cast<CSMWorld::UniversalId::Type> (mType->itemData (mType->currentIndex()).toInt()));
|
||||
}
|
||||
|
||||
CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id)
|
||||
: GenericCreator (data, undoStack, id)
|
||||
{
|
||||
QLabel *label = new QLabel ("Type", this);
|
||||
insertBeforeButtons (label, false);
|
||||
|
||||
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
|
||||
|
||||
mType = new QComboBox (this);
|
||||
|
||||
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
|
||||
iter!=types.end(); ++iter)
|
||||
{
|
||||
CSMWorld::UniversalId id (*iter, "");
|
||||
|
||||
mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(),
|
||||
static_cast<int> (id.getType()));
|
||||
}
|
||||
|
||||
insertBeforeButtons (mType, false);
|
||||
}
|
||||
|
||||
void CSVWorld::ReferenceableCreator::reset()
|
||||
{
|
||||
mType->setCurrentIndex (0);
|
||||
GenericCreator::reset();
|
||||
}
|
30
apps/opencs/view/world/referenceablecreator.hpp
Normal file
30
apps/opencs/view/world/referenceablecreator.hpp
Normal file
|
@ -0,0 +1,30 @@
|
|||
#ifndef CSV_WORLD_REFERENCEABLECREATOR_H
|
||||
#define CSV_WORLD_REFERENCEABLECREATOR_H
|
||||
|
||||
class QComboBox;
|
||||
|
||||
#include "genericcreator.hpp"
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class ReferenceableCreator : public GenericCreator
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QComboBox *mType;
|
||||
|
||||
private:
|
||||
|
||||
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
|
||||
|
||||
public:
|
||||
|
||||
ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id);
|
||||
|
||||
virtual void reset();
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
70
apps/opencs/view/world/referencecreator.cpp
Normal file
70
apps/opencs/view/world/referencecreator.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
|
||||
#include "referencecreator.hpp"
|
||||
|
||||
#include <QLabel>
|
||||
#include <QLineEdit>
|
||||
|
||||
#include "../../model/world/data.hpp"
|
||||
#include "../../model/world/commands.hpp"
|
||||
|
||||
std::string CSVWorld::ReferenceCreator::getId() const
|
||||
{
|
||||
return mId;
|
||||
}
|
||||
|
||||
void CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
|
||||
{
|
||||
/// \todo avoid using hard-coded column numbers
|
||||
command.addValue (2, mCell->text());
|
||||
}
|
||||
|
||||
CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id)
|
||||
: GenericCreator (data, undoStack, id)
|
||||
{
|
||||
QLabel *label = new QLabel ("Cell", this);
|
||||
insertBeforeButtons (label, false);
|
||||
|
||||
mCell = new QLineEdit (this);
|
||||
insertBeforeButtons (mCell, true);
|
||||
|
||||
setManualEditing (false);
|
||||
|
||||
connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged()));
|
||||
}
|
||||
|
||||
void CSVWorld::ReferenceCreator::reset()
|
||||
{
|
||||
mCell->setText ("");
|
||||
mId = getData().getReferences().getNewId();
|
||||
GenericCreator::reset();
|
||||
}
|
||||
|
||||
std::string CSVWorld::ReferenceCreator::getErrors() const
|
||||
{
|
||||
std::string errors = GenericCreator::getErrors();
|
||||
|
||||
std::string cell = mCell->text().toUtf8().constData();
|
||||
|
||||
if (cell.empty())
|
||||
{
|
||||
if (!errors.empty())
|
||||
errors += "<br>";
|
||||
|
||||
errors += "Missing Cell ID";
|
||||
}
|
||||
else if (getData().getCells().searchId (cell)==-1)
|
||||
{
|
||||
if (!errors.empty())
|
||||
errors += "<br>";
|
||||
|
||||
errors += "Invalid Cell ID";
|
||||
}
|
||||
|
||||
return errors;
|
||||
}
|
||||
|
||||
void CSVWorld::ReferenceCreator::cellChanged()
|
||||
{
|
||||
update();
|
||||
}
|
40
apps/opencs/view/world/referencecreator.hpp
Normal file
40
apps/opencs/view/world/referencecreator.hpp
Normal file
|
@ -0,0 +1,40 @@
|
|||
#ifndef CSV_WORLD_REFERENCECREATOR_H
|
||||
#define CSV_WORLD_REFERENCECREATOR_H
|
||||
|
||||
#include "genericcreator.hpp"
|
||||
|
||||
class QLineEdit;
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class ReferenceCreator : public GenericCreator
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
QLineEdit *mCell;
|
||||
std::string mId;
|
||||
|
||||
private:
|
||||
|
||||
virtual std::string getId() const;
|
||||
|
||||
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
|
||||
|
||||
public:
|
||||
|
||||
ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,
|
||||
const CSMWorld::UniversalId& id);
|
||||
|
||||
virtual void reset();
|
||||
|
||||
virtual std::string getErrors() const;
|
||||
///< Return formatted error descriptions for the current state of the creator. if an empty
|
||||
/// string is returned, there is no error.
|
||||
|
||||
private slots:
|
||||
|
||||
void cellChanged();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -27,31 +27,20 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto
|
|||
{
|
||||
UidTypeList list;
|
||||
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Activator, ":./activator.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Potion, ":./potion.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Apparatus, ":./apparatus.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Armor, ":./armor.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Book, ":./book.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Clothing, ":./clothing.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Container, ":./container.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Creature, ":./creature.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Door, ":./door.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Ingredient, ":./ingredient.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_CreatureLevelledList, ":./creature.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_ItemLevelledList, ":./item.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Light, ":./light.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Lockpick, ":./lockpick.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Miscellaneous, ":./misc.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Npc, ":./npc.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Probe, ":./probe.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Repair, ":./repair.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Static, ":./static.png"));
|
||||
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Weapon, ":./weapon.png"));
|
||||
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
|
||||
|
||||
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
|
||||
iter!=types.end(); ++iter)
|
||||
{
|
||||
CSMWorld::UniversalId id (*iter, "");
|
||||
|
||||
list.push_back (std::make_pair (id.getType(), id.getIcon().c_str()));
|
||||
}
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
void CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
bool CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
{
|
||||
if (settingName == "Referenceable ID Type Display")
|
||||
{
|
||||
|
@ -63,5 +52,9 @@ void CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingNam
|
|||
|
||||
else if (settingValue == "Text Only")
|
||||
mDisplayMode = Mode_TextOnly;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -13,7 +13,7 @@ namespace CSVWorld
|
|||
public:
|
||||
RefIdTypeDelegate (const ValueList &mValues, const IconList &icons, QUndoStack& undoStack, QObject *parent);
|
||||
|
||||
void updateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
||||
};
|
||||
|
||||
|
|
|
@ -7,14 +7,20 @@
|
|||
#include "dialoguesubview.hpp"
|
||||
#include "scriptsubview.hpp"
|
||||
#include "regionmapsubview.hpp"
|
||||
#include "genericcreator.hpp"
|
||||
#include "cellcreator.hpp"
|
||||
#include "referenceablecreator.hpp"
|
||||
#include "referencecreator.hpp"
|
||||
|
||||
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
||||
{
|
||||
// Regular record tables (including references which are actually sub-records, but are promoted
|
||||
// to top-level records within the editor)
|
||||
manager.add (CSMWorld::UniversalId::Type_Gmsts,
|
||||
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Skills,
|
||||
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
|
||||
|
||||
static const CSMWorld::UniversalId::Type sTableTypes[] =
|
||||
{
|
||||
|
@ -27,20 +33,26 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
|||
CSMWorld::UniversalId::Type_Regions,
|
||||
CSMWorld::UniversalId::Type_Birthsigns,
|
||||
CSMWorld::UniversalId::Type_Spells,
|
||||
CSMWorld::UniversalId::Type_Cells,
|
||||
CSMWorld::UniversalId::Type_Referenceables,
|
||||
CSMWorld::UniversalId::Type_References,
|
||||
|
||||
CSMWorld::UniversalId::Type_None // end marker
|
||||
};
|
||||
|
||||
for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i)
|
||||
manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (true));
|
||||
manager.add (sTableTypes[i],
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator> >);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Cells,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<CellCreator> >);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_Referenceables,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceableCreator> >);
|
||||
|
||||
manager.add (CSMWorld::UniversalId::Type_References,
|
||||
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >);
|
||||
|
||||
// Subviews for editing/viewing individual records
|
||||
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);
|
||||
|
||||
// Other stuff (combined record tables)
|
||||
manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory<RegionMapSubView>);
|
||||
|
||||
// manager.add (CSMWorld::UniversalId::Type_Global,
|
||||
// new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
|
||||
}
|
|
@ -26,6 +26,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
|
|||
|
||||
if (!mEditLock)
|
||||
{
|
||||
if (selectedRows.size()==1)
|
||||
menu.addAction (mEditAction);
|
||||
|
||||
if (mCreateAction)
|
||||
menu.addAction (mCreateAction);
|
||||
|
||||
|
@ -41,40 +44,62 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
|
|||
|
||||
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
|
||||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
/// \todo Do not use hardcoded column numbers
|
||||
std::vector<std::string> revertableIds;
|
||||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
|
||||
if (mProxyModel->columnCount()>0)
|
||||
{
|
||||
std::string id = mProxyModel->data (*iter).toString().toStdString();
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
|
||||
++iter)
|
||||
{
|
||||
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
|
||||
|
||||
CSMWorld::RecordBase::State state =
|
||||
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
|
||||
static_cast<CSMWorld::RecordBase::State> (
|
||||
mModel->data (mModel->index (index.row(), 1)).toInt());
|
||||
|
||||
if (state!=CSMWorld::RecordBase::State_BaseOnly)
|
||||
{
|
||||
std::string id = mModel->data (mModel->index (index.row(), 0)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
revertableIds.push_back (id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return revertableIds;
|
||||
}
|
||||
|
||||
std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
|
||||
{
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
/// \todo Do not use hardcoded column numbers
|
||||
std::vector<std::string> deletableIds;
|
||||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
|
||||
if (mProxyModel->columnCount()>0)
|
||||
{
|
||||
std::string id = mProxyModel->data (*iter).toString().toStdString();
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
|
||||
++iter)
|
||||
{
|
||||
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
|
||||
|
||||
CSMWorld::RecordBase::State state =
|
||||
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
|
||||
static_cast<CSMWorld::RecordBase::State> (
|
||||
mModel->data (mModel->index (index.row(), 1)).toInt());
|
||||
|
||||
if (state!=CSMWorld::RecordBase::State_Deleted)
|
||||
{
|
||||
std::string id = mModel->data (mModel->index (index.row(), 0)).
|
||||
toString().toUtf8().constData();
|
||||
|
||||
deletableIds.push_back (id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return deletableIds;
|
||||
}
|
||||
|
@ -116,12 +141,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
|
|||
hideColumn (i);
|
||||
}
|
||||
|
||||
/// \todo make initial layout fill the whole width of the table
|
||||
mEditAction = new QAction (tr ("Edit Record"), this);
|
||||
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
|
||||
addAction (mEditAction);
|
||||
|
||||
if (createAndDelete)
|
||||
{
|
||||
mCreateAction = new QAction (tr ("Add Record"), this);
|
||||
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord()));
|
||||
connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
|
||||
addAction (mCreateAction);
|
||||
}
|
||||
|
||||
|
@ -132,6 +159,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
|
|||
mDeleteAction = new QAction (tr ("Delete Record"), this);
|
||||
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
|
||||
addAction (mDeleteAction);
|
||||
|
||||
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
|
||||
this, SLOT (tableSizeUpdate()));
|
||||
|
||||
/// \note This signal could instead be connected to a slot that filters out changes not affecting
|
||||
/// the records status column (for permanence reasons)
|
||||
connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
|
||||
this, SLOT (tableSizeUpdate()));
|
||||
|
||||
connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
|
||||
this, SLOT (selectionSizeUpdate ()));
|
||||
}
|
||||
|
||||
void CSVWorld::Table::setEditLock (bool locked)
|
||||
|
@ -149,22 +187,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
|
|||
mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString());
|
||||
}
|
||||
|
||||
#include <sstream> /// \todo remove
|
||||
void CSVWorld::Table::createRecord()
|
||||
{
|
||||
if (!mEditLock)
|
||||
{
|
||||
/// \todo ask the user for an ID instead.
|
||||
static int index = 0;
|
||||
|
||||
std::ostringstream stream;
|
||||
stream << "id" << index++;
|
||||
|
||||
mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str()));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void CSVWorld::Table::revertRecord()
|
||||
{
|
||||
if (!mEditLock)
|
||||
|
@ -205,21 +227,67 @@ void CSVWorld::Table::deleteRecord()
|
|||
}
|
||||
}
|
||||
|
||||
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
void CSVWorld::Table::editRecord()
|
||||
{
|
||||
if (settingName == "Record Status Display")
|
||||
if (!mEditLock)
|
||||
{
|
||||
RecordStatusDelegate &rsDelegate = dynamic_cast <CSVWorld::RecordStatusDelegate &> (*itemDelegateForColumn(1));
|
||||
QModelIndexList selectedRows = selectionModel()->selectedRows();
|
||||
|
||||
rsDelegate.updateEditorSetting (settingName, settingValue);
|
||||
emit dataChanged(mModel->index(0,1), mModel->index(mModel->rowCount()-1, 1));
|
||||
}
|
||||
|
||||
if (settingName == "Referenceable ID Type Display")
|
||||
{
|
||||
RefIdTypeDelegate &refidDelegate = dynamic_cast <CSVWorld::RefIdTypeDelegate &> (*itemDelegateForColumn(2));
|
||||
|
||||
refidDelegate.updateEditorSetting (settingName, settingValue);
|
||||
emit dataChanged(mModel->index(0,1), mModel->index(mModel->rowCount()-1, 1));
|
||||
if (selectedRows.size()==1)
|
||||
emit editRequest (selectedRows.begin()->row());
|
||||
}
|
||||
}
|
||||
|
||||
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
|
||||
{
|
||||
int columns = mModel->columnCount();
|
||||
|
||||
for (int i=0; i<columns; ++i)
|
||||
if (QAbstractItemDelegate *delegate = itemDelegateForColumn (i))
|
||||
if (dynamic_cast<CommandDelegate&> (*delegate).
|
||||
updateEditorSetting (settingName, settingValue))
|
||||
emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i));
|
||||
}
|
||||
|
||||
void CSVWorld::Table::tableSizeUpdate()
|
||||
{
|
||||
int size = 0;
|
||||
int deleted = 0;
|
||||
int modified = 0;
|
||||
|
||||
if (mModel->columnCount()>0)
|
||||
{
|
||||
int rows = mModel->rowCount();
|
||||
|
||||
for (int i=0; i<rows; ++i)
|
||||
{
|
||||
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));
|
||||
|
||||
/// \todo Do not use hardcoded column numbers
|
||||
int state = mModel->data (mModel->index (index.row(), 1)).toInt();
|
||||
|
||||
switch (state)
|
||||
{
|
||||
case CSMWorld::RecordBase::State_BaseOnly: ++size; break;
|
||||
case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;
|
||||
case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;
|
||||
case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tableSizeChanged (size, deleted, modified);
|
||||
}
|
||||
|
||||
void CSVWorld::Table::selectionSizeUpdate()
|
||||
{
|
||||
selectionSizeChanged (selectionModel()->selectedRows().size());
|
||||
}
|
||||
|
||||
void CSVWorld::Table::requestFocus (const std::string& id)
|
||||
{
|
||||
QModelIndex index = mProxyModel->getModelIndex (id, 0);
|
||||
|
||||
if (index.isValid())
|
||||
scrollTo (index, QAbstractItemView::PositionAtTop);
|
||||
}
|
|
@ -28,6 +28,7 @@ namespace CSVWorld
|
|||
|
||||
std::vector<CommandDelegate *> mDelegates;
|
||||
QUndoStack& mUndoStack;
|
||||
QAction *mEditAction;
|
||||
QAction *mCreateAction;
|
||||
QAction *mRevertAction;
|
||||
QAction *mDeleteAction;
|
||||
|
@ -55,13 +56,35 @@ namespace CSVWorld
|
|||
|
||||
void updateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
|
||||
private slots:
|
||||
signals:
|
||||
|
||||
void createRecord();
|
||||
void editRequest (int row);
|
||||
|
||||
void selectionSizeChanged (int size);
|
||||
|
||||
void tableSizeChanged (int size, int deleted, int modified);
|
||||
///< \param size Number of not deleted records
|
||||
/// \param deleted Number of deleted records
|
||||
/// \param modified Number of added and modified records
|
||||
|
||||
void createRequest();
|
||||
|
||||
private slots:
|
||||
|
||||
void revertRecord();
|
||||
|
||||
void deleteRecord();
|
||||
|
||||
void editRecord();
|
||||
|
||||
public slots:
|
||||
|
||||
void tableSizeUpdate();
|
||||
|
||||
void selectionSizeUpdate();
|
||||
|
||||
void requestFocus (const std::string& id);
|
||||
|
||||
};
|
||||
}
|
||||
|
||||
|
|
156
apps/opencs/view/world/tablebottombox.cpp
Normal file
156
apps/opencs/view/world/tablebottombox.cpp
Normal file
|
@ -0,0 +1,156 @@
|
|||
|
||||
#include "tablebottombox.hpp"
|
||||
|
||||
#include <sstream>
|
||||
|
||||
#include <QStatusBar>
|
||||
#include <QStackedLayout>
|
||||
#include <QLabel>
|
||||
|
||||
#include "creator.hpp"
|
||||
|
||||
void CSVWorld::TableBottomBox::updateStatus()
|
||||
{
|
||||
if (mShowStatusBar)
|
||||
{
|
||||
static const char *sLabels[4] = { "record", "deleted", "touched", "selected" };
|
||||
static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" };
|
||||
|
||||
std::ostringstream stream;
|
||||
|
||||
bool first = true;
|
||||
|
||||
for (int i=0; i<4; ++i)
|
||||
{
|
||||
if (mStatusCount[i]>0)
|
||||
{
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
stream << ", ";
|
||||
|
||||
stream
|
||||
<< mStatusCount[i] << ' '
|
||||
<< (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]);
|
||||
}
|
||||
}
|
||||
|
||||
mStatus->setText (QString::fromUtf8 (stream.str().c_str()));
|
||||
}
|
||||
}
|
||||
|
||||
CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory,
|
||||
CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent)
|
||||
: QWidget (parent), mShowStatusBar (false), mCreating (false)
|
||||
{
|
||||
for (int i=0; i<4; ++i)
|
||||
mStatusCount[i] = 0;
|
||||
|
||||
setVisible (false);
|
||||
|
||||
mLayout = new QStackedLayout;
|
||||
mLayout->setContentsMargins (0, 0, 0, 0);
|
||||
|
||||
mStatus = new QLabel;
|
||||
|
||||
mStatusBar = new QStatusBar;
|
||||
|
||||
mStatusBar->addWidget (mStatus);
|
||||
|
||||
mLayout->addWidget (mStatusBar);
|
||||
|
||||
setLayout (mLayout);
|
||||
|
||||
mCreator = creatorFactory.makeCreator (data, undoStack, id);
|
||||
|
||||
mLayout->addWidget (mCreator);
|
||||
|
||||
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone()));
|
||||
|
||||
connect (mCreator, SIGNAL (requestFocus (const std::string&)),
|
||||
this, SIGNAL (requestFocus (const std::string&)));
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::setEditLock (bool locked)
|
||||
{
|
||||
if (mCreator)
|
||||
mCreator->setEditLock (locked);
|
||||
}
|
||||
|
||||
CSVWorld::TableBottomBox::~TableBottomBox()
|
||||
{
|
||||
delete mCreator;
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::setStatusBar (bool show)
|
||||
{
|
||||
if (show!=mShowStatusBar)
|
||||
{
|
||||
setVisible (show || mCreating);
|
||||
|
||||
mShowStatusBar = show;
|
||||
|
||||
if (show)
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
bool CSVWorld::TableBottomBox::canCreateAndDelete() const
|
||||
{
|
||||
return mCreator;
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::createRequestDone()
|
||||
{
|
||||
if (!mShowStatusBar)
|
||||
setVisible (false);
|
||||
else
|
||||
updateStatus();
|
||||
|
||||
mLayout->setCurrentWidget (mStatusBar);
|
||||
|
||||
mCreating = false;
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::selectionSizeChanged (int size)
|
||||
{
|
||||
if (mStatusCount[3]!=size)
|
||||
{
|
||||
mStatusCount[3] = size;
|
||||
updateStatus();
|
||||
}
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (mStatusCount[0]!=size)
|
||||
{
|
||||
mStatusCount[0] = size;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (mStatusCount[1]!=deleted)
|
||||
{
|
||||
mStatusCount[1] = deleted;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (mStatusCount[2]!=modified)
|
||||
{
|
||||
mStatusCount[2] = modified;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
if (changed)
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void CSVWorld::TableBottomBox::createRequest()
|
||||
{
|
||||
mCreator->reset();
|
||||
mLayout->setCurrentWidget (mCreator);
|
||||
setVisible (true);
|
||||
mCreating = true;
|
||||
}
|
82
apps/opencs/view/world/tablebottombox.hpp
Normal file
82
apps/opencs/view/world/tablebottombox.hpp
Normal file
|
@ -0,0 +1,82 @@
|
|||
#ifndef CSV_WORLD_BOTTOMBOX_H
|
||||
#define CSV_WORLD_BOTTOMBOX_H
|
||||
|
||||
#include <QWidget>
|
||||
|
||||
class QLabel;
|
||||
class QStackedLayout;
|
||||
class QStatusBar;
|
||||
class QUndoStack;
|
||||
|
||||
namespace CSMWorld
|
||||
{
|
||||
class Data;
|
||||
class UniversalId;
|
||||
}
|
||||
|
||||
namespace CSVWorld
|
||||
{
|
||||
class CreatorFactoryBase;
|
||||
class Creator;
|
||||
|
||||
class TableBottomBox : public QWidget
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
bool mShowStatusBar;
|
||||
QLabel *mStatus;
|
||||
QStatusBar *mStatusBar;
|
||||
int mStatusCount[4];
|
||||
Creator *mCreator;
|
||||
bool mCreating;
|
||||
QStackedLayout *mLayout;
|
||||
|
||||
private:
|
||||
|
||||
// not implemented
|
||||
TableBottomBox (const TableBottomBox&);
|
||||
TableBottomBox& operator= (const TableBottomBox&);
|
||||
|
||||
void updateStatus();
|
||||
|
||||
public:
|
||||
|
||||
TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data,
|
||||
QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent = 0);
|
||||
|
||||
virtual ~TableBottomBox();
|
||||
|
||||
void setEditLock (bool locked);
|
||||
|
||||
void setStatusBar (bool show);
|
||||
|
||||
bool canCreateAndDelete() const;
|
||||
///< Is record creation and deletion supported?
|
||||
///
|
||||
/// \note The BotomBox does not partake in the deletion of records.
|
||||
|
||||
signals:
|
||||
|
||||
void requestFocus (const std::string& id);
|
||||
///< Request owner of this box to focus the just created \a id. The owner may
|
||||
/// ignore this request.
|
||||
|
||||
private slots:
|
||||
|
||||
void createRequestDone();
|
||||
///< \note This slot being called does not imply success.
|
||||
|
||||
public slots:
|
||||
|
||||
void selectionSizeChanged (int size);
|
||||
|
||||
void tableSizeChanged (int size, int deleted, int modified);
|
||||
///< \param size Number of not deleted records
|
||||
/// \param deleted Number of deleted records
|
||||
/// \param modified Number of added and modified records
|
||||
|
||||
void createRequest();
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
|
@ -1,32 +1,68 @@
|
|||
|
||||
#include "tablesubview.hpp"
|
||||
|
||||
#include <QVBoxLayout>
|
||||
|
||||
#include "../../model/doc/document.hpp"
|
||||
|
||||
#include "table.hpp"
|
||||
#include "tablebottombox.hpp"
|
||||
#include "creator.hpp"
|
||||
|
||||
CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
|
||||
bool createAndDelete)
|
||||
const CreatorFactoryBase& creatorFactory)
|
||||
: SubView (id)
|
||||
{
|
||||
setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete));
|
||||
QVBoxLayout *layout = new QVBoxLayout;
|
||||
|
||||
connect (mTable, SIGNAL (doubleClicked (const QModelIndex&)), this, SLOT (rowActivated (const QModelIndex&)));
|
||||
layout->setContentsMargins (QMargins (0, 0, 0, 0));
|
||||
|
||||
layout->addWidget (mBottom =
|
||||
new TableBottomBox (creatorFactory, document.getData(), document.getUndoStack(), id, this), 0);
|
||||
|
||||
layout->insertWidget (0, mTable =
|
||||
new Table (id, document.getData(), document.getUndoStack(), mBottom->canCreateAndDelete()), 2);
|
||||
|
||||
QWidget *widget = new QWidget;
|
||||
|
||||
widget->setLayout (layout);
|
||||
|
||||
setWidget (widget);
|
||||
|
||||
connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int)));
|
||||
|
||||
connect (mTable, SIGNAL (selectionSizeChanged (int)),
|
||||
mBottom, SLOT (selectionSizeChanged (int)));
|
||||
connect (mTable, SIGNAL (tableSizeChanged (int, int, int)),
|
||||
mBottom, SLOT (tableSizeChanged (int, int, int)));
|
||||
|
||||
mTable->tableSizeUpdate();
|
||||
mTable->selectionSizeUpdate();
|
||||
|
||||
if (mBottom->canCreateAndDelete())
|
||||
connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest()));
|
||||
|
||||
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
|
||||
mTable, SLOT (requestFocus (const std::string&)));
|
||||
}
|
||||
|
||||
void CSVWorld::TableSubView::setEditLock (bool locked)
|
||||
{
|
||||
mTable->setEditLock (locked);
|
||||
mBottom->setEditLock (locked);
|
||||
}
|
||||
|
||||
void CSVWorld::TableSubView::rowActivated (const QModelIndex& index)
|
||||
void CSVWorld::TableSubView::editRequest (int row)
|
||||
{
|
||||
focusId (mTable->getUniversalId (index.row()));
|
||||
focusId (mTable->getUniversalId (row));
|
||||
}
|
||||
|
||||
void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue)
|
||||
{
|
||||
|
||||
if ( (settingName == "Record Status Display") || settingName == "Referenceable ID Type Display" )
|
||||
mTable->updateEditorSetting(settingName, settingValue);
|
||||
}
|
||||
|
||||
void CSVWorld::TableSubView::setStatusBar (bool show)
|
||||
{
|
||||
mBottom->setStatusBar (show);
|
||||
}
|
|
@ -13,22 +13,30 @@ namespace CSMDoc
|
|||
namespace CSVWorld
|
||||
{
|
||||
class Table;
|
||||
class TableBottomBox;
|
||||
class CreatorFactoryBase;
|
||||
|
||||
class TableSubView : public CSVDoc::SubView
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
Table *mTable;
|
||||
TableBottomBox *mBottom;
|
||||
|
||||
public:
|
||||
|
||||
TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete);
|
||||
TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
|
||||
const CreatorFactoryBase& creatorFactory);
|
||||
|
||||
virtual void setEditLock (bool locked);
|
||||
void updateEditorSetting (const QString &, const QString &);
|
||||
|
||||
virtual void updateEditorSetting (const QString& key, const QString& value);
|
||||
|
||||
virtual void setStatusBar (bool show);
|
||||
|
||||
private slots:
|
||||
|
||||
void rowActivated (const QModelIndex& index);
|
||||
void editRequest (int row);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -135,3 +135,9 @@ bool CSVWorld::CommandDelegate::isEditLocked() const
|
|||
{
|
||||
return mEditLock;
|
||||
}
|
||||
|
||||
bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName,
|
||||
const QString &settingValue)
|
||||
{
|
||||
return false;
|
||||
}
|
|
@ -108,6 +108,9 @@ namespace CSVWorld
|
|||
|
||||
bool isEditLocked() const;
|
||||
|
||||
virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
|
||||
///< \return Does column require update?
|
||||
|
||||
private slots:
|
||||
|
||||
virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {}
|
||||
|
|
|
@ -40,6 +40,8 @@ namespace MWBase
|
|||
virtual MWWorld::Ptr getActor() const = 0;
|
||||
///< Return the actor the player is currently talking to.
|
||||
|
||||
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const = 0;
|
||||
|
||||
//calbacks for the GUI
|
||||
virtual void keywordSelected (const std::string& keyword) = 0;
|
||||
virtual void goodbyeSelected() = 0;
|
||||
|
|
|
@ -46,9 +46,10 @@ namespace MWBase
|
|||
enum PlayType {
|
||||
Play_TypeSfx = 1<<3, /* Normal SFX sound */
|
||||
Play_TypeVoice = 1<<4, /* Voice sound */
|
||||
Play_TypeMusic = 1<<5, /* Music track */
|
||||
Play_TypeMovie = 1<<6, /* Movie audio track */
|
||||
Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie
|
||||
Play_TypeFoot = 1<<5, /* Footstep sound */
|
||||
Play_TypeMusic = 1<<6, /* Music track */
|
||||
Play_TypeMovie = 1<<7, /* Movie audio track */
|
||||
Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeFoot|Play_TypeMusic|Play_TypeMovie
|
||||
};
|
||||
|
||||
private:
|
||||
|
@ -102,11 +103,12 @@ namespace MWBase
|
|||
///< Play a 2D audio track, using a custom decoder
|
||||
|
||||
virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch,
|
||||
PlayMode mode=Play_Normal) = 0;
|
||||
PlayType type=Play_TypeSfx, PlayMode mode=Play_Normal) = 0;
|
||||
///< Play a sound, independently of 3D-position
|
||||
|
||||
virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId,
|
||||
float volume, float pitch, PlayMode mode=Play_Normal) = 0;
|
||||
float volume, float pitch, PlayType type=Play_TypeSfx,
|
||||
PlayMode mode=Play_Normal) = 0;
|
||||
///< Play a sound from an object
|
||||
|
||||
virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0;
|
||||
|
|
|
@ -215,6 +215,7 @@ namespace MWBase
|
|||
virtual void removeStaticMessageBox() = 0;
|
||||
|
||||
virtual void enterPressed () = 0;
|
||||
virtual void activateKeyPressed () = 0;
|
||||
virtual int readPressedButton() = 0;
|
||||
///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
|
||||
|
||||
|
@ -237,6 +238,8 @@ namespace MWBase
|
|||
|
||||
virtual void processChangedSettings(const Settings::CategorySettingVector& changed) = 0;
|
||||
|
||||
virtual void windowResized(int x, int y) = 0;
|
||||
|
||||
virtual void executeInConsole (const std::string& path) = 0;
|
||||
|
||||
virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 0;
|
||||
|
@ -263,6 +266,8 @@ namespace MWBase
|
|||
|
||||
virtual void changePointer (const std::string& name) = 0;
|
||||
|
||||
virtual void setEnemy (const MWWorld::Ptr& enemy) = 0;
|
||||
|
||||
virtual const Translation::Storage& getTranslationDataStorage() const = 0;
|
||||
|
||||
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
|
||||
|
|
|
@ -201,6 +201,8 @@ namespace MWBase
|
|||
|
||||
virtual void setMoonColour (bool red) = 0;
|
||||
|
||||
virtual void modRegion(const std::string ®ionid, const std::vector<char> &chances) = 0;
|
||||
|
||||
virtual float getTimeScaleFactor() const = 0;
|
||||
|
||||
virtual void changeToInteriorCell (const std::string& cellName,
|
||||
|
@ -218,6 +220,10 @@ namespace MWBase
|
|||
virtual MWWorld::Ptr getFacedObject() = 0;
|
||||
///< Return pointer to the object the player is looking at, if it is within activation range
|
||||
|
||||
/// Returns a pointer to the object the provided object is facing (if within the
|
||||
/// specified distance). This will attempt to use the "Bip01 Head" node as a basis.
|
||||
virtual MWWorld::Ptr getFacedObject(const MWWorld::Ptr &ptr, float distance) = 0;
|
||||
|
||||
virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
|
||||
///< Adjust position after load to be on ground. Must be called after model load.
|
||||
|
||||
|
@ -327,6 +333,7 @@ namespace MWBase
|
|||
virtual void togglePlayerLooking(bool enable) = 0;
|
||||
virtual void changeVanityModeScale(float factor) = 0;
|
||||
virtual bool vanityRotateCamera(float * rot) = 0;
|
||||
virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0;
|
||||
|
||||
virtual void setupPlayer() = 0;
|
||||
virtual void renderPlayer() = 0;
|
||||
|
@ -371,6 +378,12 @@ namespace MWBase
|
|||
/// Find default position inside interior cell specified by name
|
||||
/// \return false if interior with given name not exists, true otherwise
|
||||
virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0;
|
||||
|
||||
/// Enables or disables use of teleport spell effects (recall, intervention, etc).
|
||||
virtual void enableTeleporting(bool enable) = 0;
|
||||
|
||||
/// Returns true if teleport spell effects are allowed.
|
||||
virtual bool isTeleportingEnabled() const = 0;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -46,7 +46,11 @@ namespace MWClass
|
|||
{
|
||||
std::auto_ptr<CustomData> data (new CustomData);
|
||||
|
||||
// \todo add initial container content
|
||||
MWWorld::LiveCellRef<ESM::Container> *ref =
|
||||
ptr.get<ESM::Container>();
|
||||
|
||||
data->mContainerStore.fill(
|
||||
ref->mBase->mInventory, ptr.getCellRef().mOwner, MWBase::Environment::get().getWorld()->getStore());
|
||||
|
||||
// store
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
#include "../mwworld/actiontalk.hpp"
|
||||
|
@ -78,6 +79,8 @@ namespace MWClass
|
|||
|
||||
data->mCreatureStats.setLevel(ref->mBase->mData.mLevel);
|
||||
|
||||
data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);
|
||||
|
||||
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
|
||||
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
|
||||
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
|
||||
|
@ -88,6 +91,10 @@ namespace MWClass
|
|||
iter!=ref->mBase->mSpells.mList.end(); ++iter)
|
||||
data->mCreatureStats.getSpells().add (*iter);
|
||||
|
||||
// inventory
|
||||
data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr),
|
||||
MWBase::Environment::get().getWorld()->getStore());
|
||||
|
||||
// store
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
}
|
||||
|
@ -148,6 +155,70 @@ namespace MWClass
|
|||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
|
||||
}
|
||||
|
||||
|
||||
void Creature::hit(const MWWorld::Ptr& ptr, int type) const
|
||||
{
|
||||
}
|
||||
|
||||
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
|
||||
{
|
||||
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||
|
||||
if(!successful)
|
||||
{
|
||||
// TODO: Handle HitAttemptOnMe script function
|
||||
|
||||
// Missed
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!object.isEmpty())
|
||||
getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object));
|
||||
|
||||
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
|
||||
{
|
||||
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
|
||||
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
||||
if(!script.empty())
|
||||
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
|
||||
}
|
||||
|
||||
if(ishealth)
|
||||
{
|
||||
if(damage > 0.0f)
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
|
||||
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
|
||||
setActorHealth(ptr, health, attacker);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
|
||||
fatigue.setCurrent(fatigue.getCurrent() - damage);
|
||||
getCreatureStats(ptr).setFatigue(fatigue);
|
||||
}
|
||||
}
|
||||
|
||||
void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
|
||||
{
|
||||
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
|
||||
bool wasDead = crstats.isDead();
|
||||
|
||||
MWMechanics::DynamicStat<float> stat(crstats.getHealth());
|
||||
stat.setCurrent(health);
|
||||
crstats.setHealth(stat);
|
||||
|
||||
if(!wasDead && crstats.isDead())
|
||||
{
|
||||
// actor was just killed
|
||||
}
|
||||
else if(wasDead && !crstats.isDead())
|
||||
{
|
||||
// actor was just resurrected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor) const
|
||||
{
|
||||
|
@ -291,6 +362,33 @@ namespace MWClass
|
|||
return ref->mBase->mPersistent;
|
||||
}
|
||||
|
||||
std::string Creature::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
|
||||
{
|
||||
const MWWorld::Store<ESM::SoundGenerator> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::SoundGenerator>();
|
||||
|
||||
int type = getSndGenTypeFromName(ptr, name);
|
||||
if(type >= 0)
|
||||
{
|
||||
std::vector<const ESM::SoundGenerator*> sounds;
|
||||
sounds.reserve(8);
|
||||
|
||||
std::string ptrid = Creature::getId(ptr);
|
||||
MWWorld::Store<ESM::SoundGenerator>::iterator sound = store.begin();
|
||||
while(sound != store.end())
|
||||
{
|
||||
if(type == sound->mType && sound->mCreature.size() > 0 &&
|
||||
Misc::StringUtils::ciEqual(ptrid.substr(0, sound->mCreature.size()),
|
||||
sound->mCreature))
|
||||
sounds.push_back(&*sound);
|
||||
sound++;
|
||||
}
|
||||
if(sounds.size() > 0)
|
||||
return sounds[(int)(rand()/(RAND_MAX+1.0)*sounds.size())]->mSound;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
MWWorld::Ptr
|
||||
Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
|
||||
{
|
||||
|
@ -300,6 +398,40 @@ namespace MWClass
|
|||
return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell);
|
||||
}
|
||||
|
||||
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
|
||||
{
|
||||
if(name == "left")
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
return 2;
|
||||
if(world->isOnGround(ptr))
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
if(name == "right")
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
return 3;
|
||||
if(world->isOnGround(ptr))
|
||||
return 1;
|
||||
return -1;
|
||||
}
|
||||
if(name == "moan")
|
||||
return 4;
|
||||
if(name == "roar")
|
||||
return 5;
|
||||
if(name == "scream")
|
||||
return 6;
|
||||
if(name == "land")
|
||||
return 7;
|
||||
|
||||
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
|
||||
}
|
||||
|
||||
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
|
||||
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
|
||||
}
|
||||
|
|
|
@ -12,6 +12,8 @@ namespace MWClass
|
|||
virtual MWWorld::Ptr
|
||||
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
|
||||
|
||||
static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);
|
||||
|
||||
static const ESM::GameSetting *fMinWalkSpeedCreature;
|
||||
static const ESM::GameSetting *fMaxWalkSpeedCreature;
|
||||
|
||||
|
@ -40,6 +42,12 @@ namespace MWClass
|
|||
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
|
||||
///< Return creature stats
|
||||
|
||||
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
|
||||
|
||||
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
|
||||
|
||||
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor) const;
|
||||
///< Generate action for activation
|
||||
|
@ -69,6 +77,8 @@ namespace MWClass
|
|||
|
||||
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
|
||||
|
||||
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
|
||||
///< Return desired movement.
|
||||
|
||||
|
|
|
@ -53,7 +53,9 @@ namespace MWClass
|
|||
physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry);
|
||||
|
||||
if (!ref->mBase->mSound.empty())
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_Loop);
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0,
|
||||
MWBase::SoundManager::Play_TypeSfx,
|
||||
MWBase::SoundManager::Play_Loop);
|
||||
}
|
||||
|
||||
std::string Light::getModel(const MWWorld::Ptr &ptr) const
|
||||
|
|
|
@ -13,6 +13,8 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/dialoguemanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwmechanics/npcstats.hpp"
|
||||
|
@ -109,6 +111,23 @@ namespace
|
|||
creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase()
|
||||
+ static_cast<int>((level-1) * modifierSum+0.5), 100) );
|
||||
}
|
||||
|
||||
// initial health
|
||||
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
|
||||
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
|
||||
int multiplier = 3;
|
||||
|
||||
if (class_->mData.mSpecialization == ESM::Class::Combat)
|
||||
multiplier += 2;
|
||||
else if (class_->mData.mSpecialization == ESM::Class::Stealth)
|
||||
multiplier += 1;
|
||||
|
||||
if (class_->mData.mAttribute[0] == ESM::Attribute::Endurance
|
||||
|| class_->mData.mAttribute[1] == ESM::Attribute::Endurance)
|
||||
multiplier += 1;
|
||||
|
||||
creatureStats.setHealth(static_cast<int> (0.5 * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -196,6 +215,8 @@ namespace MWClass
|
|||
autoCalculateAttributes(ref->mBase, data->mCreatureStats);
|
||||
}
|
||||
|
||||
data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);
|
||||
|
||||
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
|
||||
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
|
||||
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
|
||||
|
@ -206,6 +227,10 @@ namespace MWClass
|
|||
iter!=ref->mBase->mSpells.mList.end(); ++iter)
|
||||
data->mCreatureStats.getSpells().add (*iter);
|
||||
|
||||
// inventory
|
||||
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr),
|
||||
MWBase::Environment::get().getWorld()->getStore());
|
||||
|
||||
// store
|
||||
ptr.getRefData().setCustomData (data.release());
|
||||
}
|
||||
|
@ -283,6 +308,243 @@ namespace MWClass
|
|||
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
|
||||
}
|
||||
|
||||
|
||||
void Npc::hit(const MWWorld::Ptr& ptr, int type) const
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
// Get the weapon used (if hand-to-hand, weapon = inv.end())
|
||||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr());
|
||||
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
|
||||
weapon = MWWorld::Ptr();
|
||||
|
||||
float dist = 100.0f * (!weapon.isEmpty() ?
|
||||
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
|
||||
gmst.find("fHandToHandReach")->getFloat());
|
||||
MWWorld::Ptr victim = world->getFacedObject(ptr, dist);
|
||||
if(victim.isEmpty()) // Didn't hit anything
|
||||
return;
|
||||
|
||||
const MWWorld::Class &othercls = MWWorld::Class::get(victim);
|
||||
if(!othercls.isActor() || othercls.getCreatureStats(victim).isDead())
|
||||
{
|
||||
// Can't hit non-actors, or dead actors
|
||||
return;
|
||||
}
|
||||
|
||||
if(ptr.getRefData().getHandle() == "player")
|
||||
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
|
||||
|
||||
int weapskill = ESM::Skill::HandToHand;
|
||||
if(!weapon.isEmpty())
|
||||
weapskill = MWWorld::Class::get(weapon).getEquipmentSkill(weapon);
|
||||
|
||||
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
|
||||
MWMechanics::NpcStats &npcstats = getNpcStats(ptr);
|
||||
const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects();
|
||||
float hitchance = npcstats.getSkill(weapskill).getModified() +
|
||||
(crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
|
||||
(crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
|
||||
hitchance *= crstats.getFatigueTerm();
|
||||
hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude -
|
||||
mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
|
||||
hitchance -= othercls.getEvasion(victim);
|
||||
|
||||
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
|
||||
{
|
||||
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
|
||||
return;
|
||||
}
|
||||
|
||||
bool healthdmg;
|
||||
float damage = 0.0f;
|
||||
if(!weapon.isEmpty())
|
||||
{
|
||||
const bool weaphashealth = get(weapon).hasItemHealth(weapon);
|
||||
const unsigned char *attack = NULL;
|
||||
if(type == MWMechanics::CreatureStats::AT_Chop)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
|
||||
else if(type == MWMechanics::CreatureStats::AT_Slash)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
|
||||
else if(type == MWMechanics::CreatureStats::AT_Thrust)
|
||||
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
|
||||
if(attack)
|
||||
{
|
||||
damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength());
|
||||
damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
|
||||
if(weaphashealth)
|
||||
{
|
||||
int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth;
|
||||
if(weapon.getCellRef().mCharge == -1)
|
||||
weapon.getCellRef().mCharge = weapmaxhealth;
|
||||
damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
|
||||
}
|
||||
if(!othercls.hasDetected(victim, ptr))
|
||||
{
|
||||
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
||||
}
|
||||
weapon.getCellRef().mCharge -= std::min(std::max(1,
|
||||
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())),
|
||||
weapon.getCellRef().mCharge);
|
||||
}
|
||||
healthdmg = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Note: MCP contains an option to include Strength in hand-to-hand damage
|
||||
// calculations. Some mods recommend using it, so we may want to include am
|
||||
// option for it.
|
||||
float minstrike = gmst.find("fMinHandToHandMult")->getFloat();
|
||||
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
|
||||
damage = npcstats.getSkill(weapskill).getModified();
|
||||
damage *= minstrike + ((maxstrike-minstrike)*npcstats.getAttackStrength());
|
||||
if(!othercls.hasDetected(victim, ptr))
|
||||
{
|
||||
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
|
||||
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
|
||||
}
|
||||
|
||||
healthdmg = (othercls.getCreatureStats(victim).getFatigue().getCurrent() < 1.0f ||
|
||||
npcstats.isWerewolf());
|
||||
if(healthdmg)
|
||||
damage *= gmst.find("fHandtoHandHealthPer")->getFloat();
|
||||
}
|
||||
if(ptr.getRefData().getHandle() == "player")
|
||||
skillUsageSucceeded(ptr, weapskill, 0);
|
||||
|
||||
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
|
||||
}
|
||||
|
||||
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
|
||||
// NOTE: 'object' and/or 'attacker' may be empty.
|
||||
|
||||
if(!successful)
|
||||
{
|
||||
// TODO: Handle HitAttemptOnMe script function
|
||||
|
||||
// Missed
|
||||
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!object.isEmpty())
|
||||
getCreatureStats(ptr).setLastHitObject(get(object).getId(object));
|
||||
|
||||
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
|
||||
{
|
||||
const std::string &script = ptr.get<ESM::NPC>()->mBase->mScript;
|
||||
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
|
||||
if(!script.empty())
|
||||
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
|
||||
}
|
||||
|
||||
if(damage > 0.0f)
|
||||
{
|
||||
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
|
||||
// something, alert the character controller, scripts, etc.
|
||||
|
||||
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
|
||||
|
||||
if(object.isEmpty())
|
||||
{
|
||||
if(ishealth)
|
||||
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
|
||||
sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f);
|
||||
}
|
||||
else if(ishealth)
|
||||
{
|
||||
// Hit percentages:
|
||||
// cuirass = 30%
|
||||
// shield, helmet, greaves, boots, pauldrons = 10% each
|
||||
// guantlets = 5% each
|
||||
static const int hitslots[20] = {
|
||||
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
||||
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
||||
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
|
||||
MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft,
|
||||
MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet,
|
||||
MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves,
|
||||
MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots,
|
||||
MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron,
|
||||
MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron,
|
||||
MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet
|
||||
};
|
||||
int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)];
|
||||
|
||||
float damagediff = damage;
|
||||
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
|
||||
damagediff -= damage;
|
||||
|
||||
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
|
||||
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
|
||||
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
|
||||
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
|
||||
{
|
||||
ESM::CellRef &armorref = armor.getCellRef();
|
||||
if(armorref.mCharge == -1)
|
||||
armorref.mCharge = armor.get<ESM::Armor>()->mBase->mData.mHealth;
|
||||
armorref.mCharge -= std::min(std::max(1, (int)damagediff),
|
||||
armorref.mCharge);
|
||||
switch(get(armor).getEquipmentSkill(armor))
|
||||
{
|
||||
case ESM::Skill::LightArmor:
|
||||
sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
|
||||
break;
|
||||
case ESM::Skill::MediumArmor:
|
||||
sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f);
|
||||
break;
|
||||
case ESM::Skill::HeavyArmor:
|
||||
sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(ishealth)
|
||||
{
|
||||
if(damage > 0.0f)
|
||||
sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
|
||||
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
|
||||
setActorHealth(ptr, health, attacker);
|
||||
}
|
||||
else
|
||||
{
|
||||
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
|
||||
fatigue.setCurrent(fatigue.getCurrent() - damage);
|
||||
getCreatureStats(ptr).setFatigue(fatigue);
|
||||
}
|
||||
}
|
||||
|
||||
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
|
||||
{
|
||||
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
|
||||
bool wasDead = crstats.isDead();
|
||||
|
||||
MWMechanics::DynamicStat<float> stat(crstats.getHealth());
|
||||
stat.setCurrent(health);
|
||||
crstats.setHealth(stat);
|
||||
|
||||
if(!wasDead && crstats.isDead())
|
||||
{
|
||||
// actor was just killed
|
||||
}
|
||||
else if(wasDead && !crstats.isDead())
|
||||
{
|
||||
// actor was just resurrected
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor) const
|
||||
{
|
||||
|
@ -587,18 +849,19 @@ namespace MWClass
|
|||
|
||||
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr);
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
const MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
|
||||
|
||||
int ratings[MWWorld::InventoryStore::Slots];
|
||||
MWMechanics::NpcStats &stats = getNpcStats(ptr);
|
||||
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
|
||||
|
||||
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
|
||||
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
|
||||
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
|
||||
int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified();
|
||||
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
|
||||
|
||||
for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i)
|
||||
int ratings[MWWorld::InventoryStore::Slots];
|
||||
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator it = invStore.getSlot(i);
|
||||
if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())
|
||||
|
@ -608,28 +871,27 @@ namespace MWClass
|
|||
}
|
||||
else
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Armor> *ref =
|
||||
it->get<ESM::Armor>();
|
||||
MWWorld::LiveCellRef<ESM::Armor> *ref = it->get<ESM::Armor>();
|
||||
|
||||
int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it);
|
||||
int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified();
|
||||
int armorSkill = stats.getSkill(armorSkillType).getModified();
|
||||
|
||||
if (ref->mBase->mData.mWeight == 0)
|
||||
if(ref->mBase->mData.mWeight == 0)
|
||||
ratings[i] = ref->mBase->mData.mArmor;
|
||||
else
|
||||
ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill;
|
||||
}
|
||||
}
|
||||
|
||||
float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
|
||||
float shield = getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
|
||||
|
||||
return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3
|
||||
return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f
|
||||
+ (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]
|
||||
+ ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]
|
||||
+ ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]
|
||||
) * 0.1
|
||||
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet)
|
||||
* 0.05
|
||||
) * 0.1f
|
||||
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet])
|
||||
* 0.05f
|
||||
+ shield;
|
||||
}
|
||||
|
||||
|
@ -663,6 +925,74 @@ namespace MWClass
|
|||
return 0;
|
||||
}
|
||||
|
||||
|
||||
std::string Npc::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
|
||||
{
|
||||
if(name == "left")
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
return "FootWaterLeft";
|
||||
if(world->isOnGround(ptr))
|
||||
{
|
||||
MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
|
||||
MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
|
||||
if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())
|
||||
return "FootBareLeft";
|
||||
|
||||
switch(Class::get(*boots).getEquipmentSkill(*boots))
|
||||
{
|
||||
case ESM::Skill::LightArmor:
|
||||
return "FootLightLeft";
|
||||
case ESM::Skill::MediumArmor:
|
||||
return "FootMedLeft";
|
||||
case ESM::Skill::HeavyArmor:
|
||||
return "FootHeavyLeft";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
if(name == "right")
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
|
||||
if(world->isUnderwater(ptr.getCell(), pos))
|
||||
return "FootWaterRight";
|
||||
if(world->isOnGround(ptr))
|
||||
{
|
||||
MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
|
||||
MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
|
||||
if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())
|
||||
return "FootBareRight";
|
||||
|
||||
switch(Class::get(*boots).getEquipmentSkill(*boots))
|
||||
{
|
||||
case ESM::Skill::LightArmor:
|
||||
return "FootLightRight";
|
||||
case ESM::Skill::MediumArmor:
|
||||
return "FootMedRight";
|
||||
case ESM::Skill::HeavyArmor:
|
||||
return "FootHeavyRight";
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
// TODO: I have no idea what these are supposed to do for NPCs since they use
|
||||
// voiced dialog for various conditions like health loss and combat taunts. Maybe
|
||||
// only for biped creatures?
|
||||
if(name == "moan")
|
||||
return "";
|
||||
if(name == "roar")
|
||||
return "";
|
||||
if(name == "scream")
|
||||
return "";
|
||||
if(name == "land")
|
||||
return "";
|
||||
|
||||
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
|
||||
}
|
||||
|
||||
MWWorld::Ptr
|
||||
Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
|
||||
{
|
||||
|
|
|
@ -68,6 +68,12 @@ namespace MWClass
|
|||
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
|
||||
///< Return inventory store
|
||||
|
||||
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
|
||||
|
||||
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
|
||||
|
||||
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
|
||||
|
||||
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
|
||||
const MWWorld::Ptr& actor) const;
|
||||
///< Generate action for activation
|
||||
|
@ -132,6 +138,8 @@ namespace MWClass
|
|||
|
||||
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
|
||||
|
||||
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
|
||||
|
||||
static void registerSelf();
|
||||
|
||||
virtual std::string getModel(const MWWorld::Ptr &ptr) const;
|
||||
|
|
|
@ -22,6 +22,13 @@
|
|||
|
||||
namespace MWClass
|
||||
{
|
||||
std::string Weapon::getId (const MWWorld::Ptr& ptr) const
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
|
||||
|
||||
return ref->mBase->mId;
|
||||
}
|
||||
|
||||
void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
|
||||
{
|
||||
const std::string model = getModel(ptr);
|
||||
|
|
|
@ -12,6 +12,9 @@ namespace MWClass
|
|||
|
||||
public:
|
||||
|
||||
virtual std::string getId (const MWWorld::Ptr& ptr) const;
|
||||
///< Return ID of \a ptr
|
||||
|
||||
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
|
||||
///< Add reference into a cell for rendering
|
||||
|
||||
|
|
|
@ -25,6 +25,7 @@
|
|||
#include "../mwbase/scriptmanager.hpp"
|
||||
#include "../mwbase/windowmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/containerstore.hpp"
|
||||
|
@ -557,6 +558,30 @@ namespace MWDialogue
|
|||
return false;
|
||||
}
|
||||
|
||||
void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) const
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
if(!sndMgr->sayDone(actor))
|
||||
{
|
||||
// Actor is already saying something.
|
||||
return;
|
||||
}
|
||||
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic);
|
||||
|
||||
Filter filter(actor, 0, false);
|
||||
const ESM::DialInfo *info = filter.search(*dial, false);
|
||||
if(info != NULL)
|
||||
{
|
||||
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
|
||||
if(winMgr->getSubtitlesEnabled())
|
||||
winMgr->messageBox(info->mResponse);
|
||||
sndMgr->say(actor, info->mSound);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
|
||||
{
|
||||
std::vector<HyperTextToken> result;
|
||||
|
|
|
@ -68,6 +68,8 @@ namespace MWDialogue
|
|||
|
||||
virtual bool checkServiceRefused ();
|
||||
|
||||
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const;
|
||||
|
||||
//calbacks for the GUI
|
||||
virtual void keywordSelected (const std::string& keyword);
|
||||
virtual void goodbyeSelected();
|
||||
|
|
|
@ -120,7 +120,7 @@ namespace MWGui
|
|||
|
||||
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
|
||||
MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0);
|
||||
|
||||
MWWorld::ActionTake take(mBook);
|
||||
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
|
|
|
@ -10,7 +10,10 @@
|
|||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
#include "../mwbase/mechanicsmanager.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
#include "../mwworld/fallback.hpp"
|
||||
#include "../mwworld/player.hpp"
|
||||
|
||||
namespace
|
||||
{
|
||||
|
@ -41,6 +44,14 @@ namespace
|
|||
// Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz
|
||||
unsigned int points[3];
|
||||
};
|
||||
|
||||
void updatePlayerHealth()
|
||||
{
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
|
||||
creatureStats.updateHealth();
|
||||
}
|
||||
}
|
||||
|
||||
namespace MWGui
|
||||
|
@ -334,6 +345,8 @@ namespace MWGui
|
|||
mCreationStage = CSE_ClassChosen;
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
|
||||
updatePlayerHealth();
|
||||
}
|
||||
|
||||
void CharacterCreation::onPickClassDialogBack()
|
||||
|
@ -461,6 +474,8 @@ namespace MWGui
|
|||
mCreationStage = CSE_RaceChosen;
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
|
||||
updatePlayerHealth();
|
||||
}
|
||||
|
||||
void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow)
|
||||
|
@ -484,6 +499,8 @@ namespace MWGui
|
|||
mCreationStage = CSE_BirthSignChosen;
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
|
||||
updatePlayerHealth();
|
||||
}
|
||||
|
||||
void CharacterCreation::onBirthSignDialogBack()
|
||||
|
@ -547,6 +564,8 @@ namespace MWGui
|
|||
mCreationStage = CSE_ClassChosen;
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
|
||||
updatePlayerHealth();
|
||||
}
|
||||
|
||||
void CharacterCreation::onCreateClassDialogBack()
|
||||
|
@ -715,6 +734,8 @@ namespace MWGui
|
|||
mCreationStage = CSE_ClassChosen;
|
||||
MWBase::Environment::get().getWindowManager()->popGuiMode();
|
||||
}
|
||||
|
||||
updatePlayerHealth();
|
||||
}
|
||||
|
||||
CharacterCreation::~CharacterCreation()
|
||||
|
|
|
@ -49,11 +49,16 @@ namespace MWGui
|
|||
mItemEdit->setCaption(boost::lexical_cast<std::string>(maxCount));
|
||||
}
|
||||
|
||||
void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
void CountDialog::cancel()
|
||||
{
|
||||
setVisible(false);
|
||||
}
|
||||
|
||||
void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
cancel();
|
||||
}
|
||||
|
||||
void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender)
|
||||
{
|
||||
eventOkClicked(NULL, mSlider->getScrollPosition()+1);
|
||||
|
|
|
@ -10,6 +10,7 @@ namespace MWGui
|
|||
public:
|
||||
CountDialog();
|
||||
void open(const std::string& item, const std::string& message, const int maxCount);
|
||||
void cancel();
|
||||
|
||||
typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt;
|
||||
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#include "../mwworld/player.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
|
||||
#include "inventorywindow.hpp"
|
||||
#include "console.hpp"
|
||||
#include "spellicons.hpp"
|
||||
|
@ -47,6 +49,7 @@ namespace MWGui
|
|||
, mWeaponVisible(true)
|
||||
, mSpellVisible(true)
|
||||
, mWorldMouseOver(false)
|
||||
, mEnemyHealthTimer(0)
|
||||
{
|
||||
setCoord(0,0, width, height);
|
||||
|
||||
|
@ -55,6 +58,7 @@ namespace MWGui
|
|||
getWidget(mHealth, "Health");
|
||||
getWidget(mMagicka, "Magicka");
|
||||
getWidget(mStamina, "Stamina");
|
||||
getWidget(mEnemyHealth, "EnemyHealth");
|
||||
mHealthManaStaminaBaseLeft = mHealthFrame->getLeft();
|
||||
|
||||
MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame;
|
||||
|
@ -320,6 +324,13 @@ namespace MWGui
|
|||
mCellNameBox->setVisible(false);
|
||||
if (mWeaponSpellTimer < 0)
|
||||
mWeaponSpellBox->setVisible(false);
|
||||
|
||||
mEnemyHealthTimer -= dt;
|
||||
if (mEnemyHealth->getVisible() && mEnemyHealthTimer < 0)
|
||||
{
|
||||
mEnemyHealth->setVisible(false);
|
||||
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20));
|
||||
}
|
||||
}
|
||||
|
||||
void HUD::onResChange(int width, int height)
|
||||
|
@ -535,6 +546,25 @@ namespace MWGui
|
|||
void HUD::update()
|
||||
{
|
||||
mSpellIcons->updateWidgets(mEffectBox, true);
|
||||
|
||||
if (!mEnemy.isEmpty() && mEnemyHealth->getVisible())
|
||||
{
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
|
||||
mEnemyHealth->setProgressRange(100);
|
||||
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
|
||||
}
|
||||
}
|
||||
|
||||
void HUD::setEnemy(const MWWorld::Ptr &enemy)
|
||||
{
|
||||
mEnemy = enemy;
|
||||
mEnemyHealthTimer = 5;
|
||||
if (!mEnemyHealth->getVisible())
|
||||
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
|
||||
mEnemyHealth->setVisible(true);
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
|
||||
mEnemyHealth->setProgressRange(100);
|
||||
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -46,12 +46,14 @@ namespace MWGui
|
|||
|
||||
void update();
|
||||
|
||||
void setEnemy(const MWWorld::Ptr& enemy);
|
||||
|
||||
private:
|
||||
MyGUI::ProgressPtr mHealth, mMagicka, mStamina;
|
||||
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth;
|
||||
MyGUI::Widget* mHealthFrame;
|
||||
MyGUI::Widget *mWeapBox, *mSpellBox;
|
||||
MyGUI::ImageBox *mWeapImage, *mSpellImage;
|
||||
MyGUI::ProgressPtr mWeapStatus, mSpellStatus;
|
||||
MyGUI::ProgressBar *mWeapStatus, *mSpellStatus;
|
||||
MyGUI::Widget *mEffectBox, *mMinimapBox;
|
||||
MyGUI::Button* mMinimapButton;
|
||||
MyGUI::ScrollView* mMinimap;
|
||||
|
@ -89,6 +91,9 @@ namespace MWGui
|
|||
|
||||
SpellIcons* mSpellIcons;
|
||||
|
||||
MWWorld::Ptr mEnemy;
|
||||
float mEnemyHealthTimer;
|
||||
|
||||
void onWorldClicked(MyGUI::Widget* _sender);
|
||||
void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y);
|
||||
void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new);
|
||||
|
|
|
@ -173,12 +173,7 @@ namespace MWGui
|
|||
attribute.setBase(100);
|
||||
}
|
||||
|
||||
// "When you gain a level, in addition to increasing three primary attributes, your Health
|
||||
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
|
||||
// the Health increase is calculated from the increased Endurance"
|
||||
creatureStats.increaseLevelHealthBonus (creatureStats.getAttribute(ESM::Attribute::Endurance).getBase() * 0.1f);
|
||||
|
||||
creatureStats.setLevel (creatureStats.getLevel()+1);
|
||||
creatureStats.levelUp();
|
||||
pcStats.levelUp ();
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup);
|
||||
|
|
|
@ -146,10 +146,10 @@ namespace MWGui
|
|||
mMessageBoxSpeed = speed;
|
||||
}
|
||||
|
||||
void MessageBoxManager::enterPressed ()
|
||||
void MessageBoxManager::okayPressed ()
|
||||
{
|
||||
if(mInterMessageBoxe != NULL)
|
||||
mInterMessageBoxe->enterPressed();
|
||||
mInterMessageBoxe->okayPressed();
|
||||
}
|
||||
|
||||
int MessageBoxManager::readPressedButton ()
|
||||
|
@ -379,7 +379,7 @@ namespace MWGui
|
|||
}
|
||||
}
|
||||
|
||||
void InteractiveMessageBox::enterPressed()
|
||||
void InteractiveMessageBox::okayPressed()
|
||||
{
|
||||
|
||||
std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));
|
||||
|
|
|
@ -40,7 +40,7 @@ namespace MWGui
|
|||
bool removeMessageBox (MessageBox *msgbox);
|
||||
void setMessageBoxSpeed (int speed);
|
||||
|
||||
void enterPressed();
|
||||
void okayPressed();
|
||||
int readPressedButton ();
|
||||
|
||||
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
|
||||
|
@ -82,7 +82,7 @@ namespace MWGui
|
|||
{
|
||||
public:
|
||||
InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);
|
||||
void enterPressed ();
|
||||
void okayPressed ();
|
||||
void mousePressed (MyGUI::Widget* _widget);
|
||||
int readPressedButton ();
|
||||
|
||||
|
|
|
@ -87,7 +87,7 @@ namespace MWGui
|
|||
|
||||
void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender)
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
|
||||
MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0);
|
||||
|
||||
MWWorld::ActionTake take(mScroll);
|
||||
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <boost/algorithm/string.hpp>
|
||||
#include <boost/math/common_factor_rt.hpp>
|
||||
|
||||
#include <SDL_video.h>
|
||||
|
||||
#include "../mwbase/environment.hpp"
|
||||
#include "../mwbase/world.hpp"
|
||||
#include "../mwbase/soundmanager.hpp"
|
||||
|
@ -172,16 +174,14 @@ namespace MWGui
|
|||
mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
|
||||
|
||||
// fill resolution list
|
||||
Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem();
|
||||
Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues;
|
||||
int screen = Settings::Manager::getInt("screen", "Video");
|
||||
int numDisplayModes = SDL_GetNumDisplayModes(screen);
|
||||
std::vector < std::pair<int, int> > resolutions;
|
||||
for (Ogre::StringVector::const_iterator it=videoModes.begin();
|
||||
it!=videoModes.end(); ++it)
|
||||
for (int i = 0; i < numDisplayModes; i++)
|
||||
{
|
||||
|
||||
int resX, resY;
|
||||
parseResolution (resX, resY, *it);
|
||||
resolutions.push_back(std::make_pair(resX, resY));
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetDisplayMode(screen, i, &mode);
|
||||
resolutions.push_back(std::make_pair(mode.w, mode.h));
|
||||
}
|
||||
std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
|
||||
for (std::vector < std::pair<int, int> >::const_iterator it=resolutions.begin();
|
||||
|
@ -272,15 +272,12 @@ namespace MWGui
|
|||
if (index == MyGUI::ITEM_NONE)
|
||||
return;
|
||||
|
||||
/*
|
||||
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
|
||||
dialog->open("#{sNotifyMessage67}");
|
||||
dialog->eventOkClicked.clear();
|
||||
dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept);
|
||||
dialog->eventCancelClicked.clear();
|
||||
dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel);
|
||||
*/
|
||||
onResolutionAccept();
|
||||
}
|
||||
|
||||
void SettingsWindow::onResolutionAccept()
|
||||
|
@ -293,9 +290,6 @@ namespace MWGui
|
|||
Settings::Manager::setInt("resolution y", "Video", resY);
|
||||
|
||||
apply();
|
||||
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
messageBox("New resolution will be applied after a restart", std::vector<std::string>());
|
||||
}
|
||||
|
||||
void SettingsWindow::onResolutionCancel()
|
||||
|
@ -364,8 +358,6 @@ namespace MWGui
|
|||
{
|
||||
Settings::Manager::setBool("fullscreen", "Video", newState);
|
||||
apply();
|
||||
MWBase::Environment::get().getWindowManager()->
|
||||
messageBox("Fullscreen will be applied after a restart", std::vector<std::string>());
|
||||
}
|
||||
}
|
||||
else if (_sender == mVSyncButton)
|
||||
|
|
|
@ -216,7 +216,13 @@ namespace MWGui
|
|||
|
||||
void WaitDialog::setCanRest (bool canRest)
|
||||
{
|
||||
mUntilHealedButton->setVisible(canRest);
|
||||
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
|
||||
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
|
||||
bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified())
|
||||
&& (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
|
||||
&& (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified());
|
||||
|
||||
mUntilHealedButton->setVisible(canRest && !full);
|
||||
mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}");
|
||||
mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}");
|
||||
|
||||
|
|
|
@ -663,7 +663,13 @@ namespace MWGui
|
|||
|
||||
void WindowManager::enterPressed ()
|
||||
{
|
||||
mMessageBoxManager->enterPressed();
|
||||
mMessageBoxManager->okayPressed();
|
||||
}
|
||||
|
||||
void WindowManager::activateKeyPressed ()
|
||||
{
|
||||
mMessageBoxManager->okayPressed();
|
||||
mCountDialog->cancel();
|
||||
}
|
||||
|
||||
int WindowManager::readPressedButton ()
|
||||
|
@ -877,30 +883,18 @@ namespace MWGui
|
|||
|
||||
setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI"));
|
||||
|
||||
//bool changeRes = false;
|
||||
bool windowRecreated = false;
|
||||
for (Settings::CategorySettingVector::const_iterator it = changed.begin();
|
||||
it != changed.end(); ++it)
|
||||
{
|
||||
/*if (it->first == "Video" && (
|
||||
it->second == "resolution x"
|
||||
|| it->second == "resolution y"))
|
||||
{
|
||||
changeRes = true;
|
||||
}*/
|
||||
if (it->first == "Video" && it->second == "vsync")
|
||||
windowRecreated = true;
|
||||
else if (it->first == "HUD" && it->second == "crosshair")
|
||||
if (it->first == "HUD" && it->second == "crosshair")
|
||||
mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD");
|
||||
else if (it->first == "GUI" && it->second == "subtitles")
|
||||
mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI");
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (changeRes)
|
||||
void WindowManager::windowResized(int x, int y)
|
||||
{
|
||||
int x = Settings::Manager::getInt("resolution x", "Video");
|
||||
int y = Settings::Manager::getInt("resolution y", "Video");
|
||||
mHud->onResChange(x, y);
|
||||
mConsole->onResChange(x, y);
|
||||
mMenu->onResChange(x, y);
|
||||
|
@ -913,13 +907,7 @@ namespace MWGui
|
|||
mLoadingScreen->onResChange (x,y);
|
||||
mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y));
|
||||
mInputBlocker->setSize(MyGUI::IntSize(x,y));
|
||||
}
|
||||
*/
|
||||
if (windowRecreated)
|
||||
{
|
||||
mGuiManager->updateWindow (mRendering->getWindow ());
|
||||
mLoadingScreen->updateWindow (mRendering->getWindow ());
|
||||
}
|
||||
mGuiManager->windowResized();
|
||||
}
|
||||
|
||||
void WindowManager::pushGuiMode(GuiMode mode)
|
||||
|
@ -1318,4 +1306,9 @@ namespace MWGui
|
|||
SDL_StopTextInput();
|
||||
}
|
||||
|
||||
void WindowManager::setEnemy(const MWWorld::Ptr &enemy)
|
||||
{
|
||||
mHud->setEnemy(enemy);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -204,6 +204,7 @@ namespace MWGui
|
|||
virtual void staticMessageBox(const std::string& message);
|
||||
virtual void removeStaticMessageBox();
|
||||
virtual void enterPressed ();
|
||||
virtual void activateKeyPressed ();
|
||||
virtual int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
|
||||
|
||||
virtual void onFrame (float frameDuration);
|
||||
|
@ -225,6 +226,8 @@ namespace MWGui
|
|||
|
||||
virtual void processChangedSettings(const Settings::CategorySettingVector& changed);
|
||||
|
||||
virtual void windowResized(int x, int y);
|
||||
|
||||
virtual void executeInConsole (const std::string& path);
|
||||
|
||||
virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total);
|
||||
|
@ -254,6 +257,8 @@ namespace MWGui
|
|||
|
||||
virtual void changePointer (const std::string& name);
|
||||
|
||||
virtual void setEnemy (const MWWorld::Ptr& enemy);
|
||||
|
||||
virtual const Translation::Storage& getTranslationDataStorage() const;
|
||||
|
||||
void onSoulgemDialogButtonPressed (int button);
|
||||
|
|
|
@ -204,13 +204,15 @@ namespace MWInput
|
|||
case A_Activate:
|
||||
resetIdleTime();
|
||||
|
||||
if (mWindows.getMode() == MWGui::GM_Container) {
|
||||
if (mWindows.isGuiMode())
|
||||
{
|
||||
if (mWindows.getMode() == MWGui::GM_Container)
|
||||
toggleContainer ();
|
||||
} else if (MWBase::Environment::get().getWindowManager()->isGuiMode()) {
|
||||
MWBase::Environment::get().getWindowManager()->enterPressed();
|
||||
} else {
|
||||
activate();
|
||||
else
|
||||
MWBase::Environment::get().getWindowManager()->activateKeyPressed();
|
||||
}
|
||||
else
|
||||
activate();
|
||||
break;
|
||||
case A_Journal:
|
||||
toggleJournal ();
|
||||
|
@ -385,7 +387,7 @@ namespace MWInput
|
|||
if (mControlSwitch["playerviewswitch"]) {
|
||||
|
||||
// work around preview mode toggle when pressing Alt+Tab
|
||||
if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(KMOD_ALT)) {
|
||||
if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) {
|
||||
if (mPreviewPOVDelay <= 0.5 &&
|
||||
(mPreviewPOVDelay += dt) > 0.5)
|
||||
{
|
||||
|
@ -434,13 +436,9 @@ namespace MWInput
|
|||
|
||||
void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed)
|
||||
{
|
||||
bool changeRes = false;
|
||||
for (Settings::CategorySettingVector::const_iterator it = changed.begin();
|
||||
it != changed.end(); ++it)
|
||||
{
|
||||
if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y"))
|
||||
changeRes = true;
|
||||
|
||||
if (it->first == "Input" && it->second == "invert y axis")
|
||||
mInvertY = Settings::Manager::getBool("invert y axis", "Input");
|
||||
|
||||
|
@ -451,9 +449,6 @@ namespace MWInput
|
|||
mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input");
|
||||
|
||||
}
|
||||
|
||||
if (changeRes)
|
||||
adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video"));
|
||||
}
|
||||
|
||||
bool InputManager::getControlSwitch (const std::string& sw)
|
||||
|
@ -608,21 +603,27 @@ namespace MWInput
|
|||
}
|
||||
|
||||
if (arg.zrel)
|
||||
MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputManager::windowFocusChange(bool have_focus)
|
||||
{
|
||||
MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel);
|
||||
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool InputManager::windowVisibilityChange(bool visible)
|
||||
void InputManager::windowFocusChange(bool have_focus)
|
||||
{
|
||||
}
|
||||
|
||||
void InputManager::windowVisibilityChange(bool visible)
|
||||
{
|
||||
//TODO: Pause game?
|
||||
return true;
|
||||
}
|
||||
|
||||
void InputManager::windowResized(int x, int y)
|
||||
{
|
||||
mOgre.windowResized(x,y);
|
||||
}
|
||||
|
||||
void InputManager::toggleMainMenu()
|
||||
|
@ -682,10 +683,8 @@ namespace MWInput
|
|||
if (MyGUI::InputManager::getInstance ().isModalAny())
|
||||
return;
|
||||
|
||||
bool gameMode = !mWindows.isGuiMode();
|
||||
|
||||
// Toggle between game mode and inventory mode
|
||||
if(gameMode)
|
||||
if(!mWindows.isGuiMode())
|
||||
mWindows.pushGuiMode(MWGui::GM_Inventory);
|
||||
else
|
||||
{
|
||||
|
@ -702,9 +701,7 @@ namespace MWInput
|
|||
if (MyGUI::InputManager::getInstance ().isModalAny())
|
||||
return;
|
||||
|
||||
bool gameMode = !mWindows.isGuiMode();
|
||||
|
||||
if(!gameMode)
|
||||
if(mWindows.isGuiMode())
|
||||
{
|
||||
if (mWindows.getMode() == MWGui::GM_Container)
|
||||
mWindows.popGuiMode();
|
||||
|
@ -714,17 +711,14 @@ namespace MWInput
|
|||
|
||||
}
|
||||
|
||||
|
||||
void InputManager::toggleConsole()
|
||||
{
|
||||
if (MyGUI::InputManager::getInstance ().isModalAny())
|
||||
return;
|
||||
|
||||
bool gameMode = !mWindows.isGuiMode();
|
||||
|
||||
// Switch to console mode no matter what mode we are currently
|
||||
// in, except of course if we are already in console mode
|
||||
if (!gameMode)
|
||||
if (mWindows.isGuiMode())
|
||||
{
|
||||
if (mWindows.getMode() == MWGui::GM_Console)
|
||||
mWindows.popGuiMode();
|
||||
|
@ -741,9 +735,7 @@ namespace MWInput
|
|||
return;
|
||||
|
||||
// Toggle between game mode and journal mode
|
||||
bool gameMode = !mWindows.isGuiMode();
|
||||
|
||||
if(gameMode && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
|
||||
if(!mWindows.isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
|
||||
{
|
||||
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
|
||||
mWindows.pushGuiMode(MWGui::GM_Journal);
|
||||
|
|
|
@ -94,8 +94,9 @@ namespace MWInput
|
|||
virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
|
||||
virtual bool mouseMoved( const SFO::MouseMotionEvent &arg );
|
||||
|
||||
virtual bool windowVisibilityChange( bool visible );
|
||||
virtual bool windowFocusChange( bool have_focus );
|
||||
virtual void windowVisibilityChange( bool visible );
|
||||
virtual void windowFocusChange( bool have_focus );
|
||||
virtual void windowResized (int x, int y);
|
||||
|
||||
virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue);
|
||||
|
||||
|
|
|
@ -76,10 +76,6 @@ namespace MWMechanics
|
|||
double magickaFactor =
|
||||
creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5;
|
||||
|
||||
DynamicStat<float> health = creatureStats.getHealth();
|
||||
health.setBase (static_cast<int> (0.5 * (strength + endurance)) + creatureStats.getLevelHealthBonus ());
|
||||
creatureStats.setHealth (health);
|
||||
|
||||
DynamicStat<float> magicka = creatureStats.getMagicka();
|
||||
magicka.setBase (static_cast<int> (intelligence + magickaFactor * intelligence));
|
||||
creatureStats.setMagicka (magicka);
|
||||
|
@ -170,6 +166,8 @@ namespace MWMechanics
|
|||
// erase previous death events since we are currently only tracking them while in an active cell
|
||||
MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied();
|
||||
|
||||
removeActor(ptr);
|
||||
|
||||
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
||||
mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
|
||||
}
|
||||
|
@ -223,7 +221,11 @@ namespace MWMechanics
|
|||
|
||||
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
|
||||
{
|
||||
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead())
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
|
||||
CreatureStats &stats = cls.getCreatureStats(iter->first);
|
||||
|
||||
stats.setLastHitObject(std::string());
|
||||
if(!stats.isDead())
|
||||
{
|
||||
if(iter->second->isDead())
|
||||
iter->second->resurrect();
|
||||
|
@ -232,7 +234,7 @@ namespace MWMechanics
|
|||
if(iter->first.getTypeName() == typeid(ESM::NPC).name())
|
||||
updateNpc(iter->first, totalDuration, paused);
|
||||
|
||||
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead())
|
||||
if(!stats.isDead())
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -240,16 +242,15 @@ namespace MWMechanics
|
|||
// \todo remove workaround, once player death can be handled
|
||||
if(iter->first.getRefData().getHandle()=="player")
|
||||
{
|
||||
MWMechanics::DynamicStat<float> stat (
|
||||
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth());
|
||||
MWMechanics::DynamicStat<float> stat(stats.getHealth());
|
||||
|
||||
if (stat.getModified()<1)
|
||||
if(stat.getModified()<1)
|
||||
{
|
||||
stat.setModified (1, 0);
|
||||
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat);
|
||||
stat.setModified(1, 0);
|
||||
stats.setHealth(stat);
|
||||
}
|
||||
|
||||
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect();
|
||||
stats.resurrect();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -258,11 +259,10 @@ namespace MWMechanics
|
|||
|
||||
iter->second->kill();
|
||||
|
||||
++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)];
|
||||
++mDeathCount[cls.getId(iter->first)];
|
||||
|
||||
if(MWWorld::Class::get(iter->first).isEssential(iter->first))
|
||||
MWBase::Environment::get().getWindowManager()->messageBox(
|
||||
"#{sKilledEssential}");
|
||||
if(cls.isEssential(iter->first))
|
||||
MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,12 @@
|
|||
|
||||
#include "aipackage.hpp"
|
||||
|
||||
#include "aiwander.hpp"
|
||||
#include "aiescort.hpp"
|
||||
#include "aitravel.hpp"
|
||||
#include "aifollow.hpp"
|
||||
#include "aiactivate.hpp"
|
||||
|
||||
void MWMechanics::AiSequence::copy (const AiSequence& sequence)
|
||||
{
|
||||
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin());
|
||||
|
@ -77,3 +83,40 @@ void MWMechanics::AiSequence::queue (const AiPackage& package)
|
|||
{
|
||||
mPackages.push_back (package.clone());
|
||||
}
|
||||
|
||||
void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
|
||||
{
|
||||
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
|
||||
{
|
||||
MWMechanics::AiPackage* package;
|
||||
if (it->mType == ESM::AI_Wander)
|
||||
{
|
||||
ESM::AIWander data = it->mWander;
|
||||
std::vector<int> idles;
|
||||
for (int i=0; i<8; ++i)
|
||||
idles.push_back(data.mIdle[i]);
|
||||
package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mUnk);
|
||||
}
|
||||
else if (it->mType == ESM::AI_Escort)
|
||||
{
|
||||
ESM::AITarget data = it->mTarget;
|
||||
package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
||||
}
|
||||
else if (it->mType == ESM::AI_Travel)
|
||||
{
|
||||
ESM::AITravel data = it->mTravel;
|
||||
package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ);
|
||||
}
|
||||
else if (it->mType == ESM::AI_Activate)
|
||||
{
|
||||
ESM::AIActivate data = it->mActivate;
|
||||
package = new MWMechanics::AiActivate(data.mName.toString());
|
||||
}
|
||||
else //if (it->mType == ESM::AI_Follow)
|
||||
{
|
||||
ESM::AITarget data = it->mTarget;
|
||||
package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
|
||||
}
|
||||
mPackages.push_back(package);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include <list>
|
||||
|
||||
#include <components/esm/loadnpc.hpp>
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class Ptr;
|
||||
|
@ -48,6 +50,8 @@ namespace MWMechanics
|
|||
void queue (const AiPackage& package);
|
||||
///< Add \a package to the end of the sequence (executed after all other packages have been
|
||||
/// completed)
|
||||
|
||||
void fill (const ESM::AIPackageList& list);
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -61,27 +61,15 @@ struct StateInfo {
|
|||
const char groupname[32];
|
||||
};
|
||||
|
||||
static const StateInfo sStateList[] = {
|
||||
{ CharState_Idle, "idle" },
|
||||
{ CharState_Idle2, "idle2" },
|
||||
{ CharState_Idle3, "idle3" },
|
||||
{ CharState_Idle4, "idle4" },
|
||||
{ CharState_Idle5, "idle5" },
|
||||
{ CharState_Idle6, "idle6" },
|
||||
{ CharState_Idle7, "idle7" },
|
||||
{ CharState_Idle8, "idle8" },
|
||||
{ CharState_Idle9, "idle9" },
|
||||
{ CharState_IdleSwim, "idleswim" },
|
||||
{ CharState_IdleSneak, "idlesneak" },
|
||||
|
||||
static const StateInfo sDeathList[] = {
|
||||
{ CharState_Death1, "death1" },
|
||||
{ CharState_Death2, "death2" },
|
||||
{ CharState_Death3, "death3" },
|
||||
{ CharState_Death4, "death4" },
|
||||
{ CharState_Death5, "death5" },
|
||||
{ CharState_SwimDeath, "swimdeath" },
|
||||
};
|
||||
static const StateInfo *sStateListEnd = &sStateList[sizeof(sStateList)/sizeof(sStateList[0])];
|
||||
|
||||
static const StateInfo *sDeathListEnd = &sDeathList[sizeof(sDeathList)/sizeof(sDeathList[0])];
|
||||
|
||||
static const StateInfo sMovementList[] = {
|
||||
{ CharState_WalkForward, "walkforward" },
|
||||
|
@ -169,7 +157,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
idle = "idleswim";
|
||||
else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak"))
|
||||
idle = "idlesneak";
|
||||
else
|
||||
else if(mIdleState != CharState_None)
|
||||
{
|
||||
idle = "idle";
|
||||
if(weap != sWeaponTypeListEnd)
|
||||
|
@ -182,6 +170,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
|
||||
mAnimation->disable(mCurrentIdle);
|
||||
mCurrentIdle = idle;
|
||||
if(!mCurrentIdle.empty())
|
||||
mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false,
|
||||
1.0f, "start", "stop", 0.0f, ~0ul);
|
||||
}
|
||||
|
@ -208,13 +197,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
|
||||
if(!mAnimation->hasAnimation(movement))
|
||||
{
|
||||
std::string::size_type sneakpos = movement.find("sneak");
|
||||
if(sneakpos == std::string::npos)
|
||||
std::string::size_type swimpos = movement.find("swim");
|
||||
if(swimpos == std::string::npos)
|
||||
movement.clear();
|
||||
else
|
||||
{
|
||||
movegroup = MWRender::Animation::Group_LowerBody;
|
||||
movement.erase(sneakpos, 5);
|
||||
movement.erase(swimpos, 4);
|
||||
if(!mAnimation->hasAnimation(movement))
|
||||
movement.clear();
|
||||
}
|
||||
|
@ -225,11 +214,11 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
|
|||
mCurrentMovement = movement;
|
||||
if(!mCurrentMovement.empty())
|
||||
{
|
||||
float vel, speed = 0.0f;
|
||||
float vel, speedmult = 1.0f;
|
||||
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f)
|
||||
speed = mMovementSpeed / vel;
|
||||
speedmult = mMovementSpeed / vel;
|
||||
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
|
||||
speed, "start", "stop", 0.0f, ~0ul);
|
||||
speedmult, "start", "stop", 0.0f, ~0ul);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -243,10 +232,71 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group
|
|||
}
|
||||
|
||||
|
||||
MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype)
|
||||
{
|
||||
if(stats.getDrawState() == DrawState_Spell)
|
||||
{
|
||||
*weaptype = WeapType_Spell;
|
||||
return inv.end();
|
||||
}
|
||||
|
||||
if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
|
||||
{
|
||||
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if(weapon == inv.end())
|
||||
*weaptype = WeapType_HandToHand;
|
||||
else
|
||||
{
|
||||
const std::string &type = weapon->getTypeName();
|
||||
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
|
||||
*weaptype = WeapType_PickProbe;
|
||||
else if(type == typeid(ESM::Weapon).name())
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
|
||||
ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType;
|
||||
switch(type)
|
||||
{
|
||||
case ESM::Weapon::ShortBladeOneHand:
|
||||
case ESM::Weapon::LongBladeOneHand:
|
||||
case ESM::Weapon::BluntOneHand:
|
||||
case ESM::Weapon::AxeOneHand:
|
||||
case ESM::Weapon::Arrow:
|
||||
case ESM::Weapon::Bolt:
|
||||
*weaptype = WeapType_OneHand;
|
||||
break;
|
||||
case ESM::Weapon::LongBladeTwoHand:
|
||||
case ESM::Weapon::BluntTwoClose:
|
||||
case ESM::Weapon::AxeTwoHand:
|
||||
*weaptype = WeapType_TwoHand;
|
||||
break;
|
||||
case ESM::Weapon::BluntTwoWide:
|
||||
case ESM::Weapon::SpearTwoWide:
|
||||
*weaptype = WeapType_TwoWide;
|
||||
break;
|
||||
case ESM::Weapon::MarksmanBow:
|
||||
*weaptype = WeapType_BowAndArrow;
|
||||
break;
|
||||
case ESM::Weapon::MarksmanCrossbow:
|
||||
*weaptype = WeapType_Crossbow;
|
||||
break;
|
||||
case ESM::Weapon::MarksmanThrown:
|
||||
*weaptype = WeapType_ThowWeapon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return weapon;
|
||||
}
|
||||
|
||||
return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
}
|
||||
|
||||
|
||||
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
|
||||
: mPtr(ptr)
|
||||
, mAnimation(anim)
|
||||
, mIdleState(CharState_Idle)
|
||||
, mIdleState(CharState_None)
|
||||
, mMovementState(CharState_None)
|
||||
, mMovementSpeed(0.0f)
|
||||
, mDeathState(CharState_None)
|
||||
|
@ -255,18 +305,30 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
, mSkipAnim(false)
|
||||
, mSecondsOfRunning(0)
|
||||
, mSecondsOfSwimming(0)
|
||||
, mUpdateWeapon(true)
|
||||
{
|
||||
if(!mAnimation)
|
||||
return;
|
||||
|
||||
if(MWWorld::Class::get(mPtr).isActor())
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
|
||||
if(cls.isActor())
|
||||
{
|
||||
/* Accumulate along X/Y only for now, until we can figure out how we should
|
||||
* handle knockout and death which moves the character down. */
|
||||
mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f));
|
||||
|
||||
if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).isDead())
|
||||
if(mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType);
|
||||
if(mWeaponType != WeapType_None)
|
||||
{
|
||||
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
}
|
||||
}
|
||||
|
||||
if(!cls.getCreatureStats(mPtr).isDead())
|
||||
mIdleState = CharState_Idle;
|
||||
else
|
||||
{
|
||||
/* FIXME: Get the actual death state used. */
|
||||
mDeathState = CharState_Death1;
|
||||
|
@ -276,13 +338,15 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
|
|||
{
|
||||
/* Don't accumulate with non-actors. */
|
||||
mAnimation->setAccumulation(Ogre::Vector3(0.0f));
|
||||
|
||||
mIdleState = CharState_Idle;
|
||||
}
|
||||
|
||||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
||||
if(mDeathState != CharState_None)
|
||||
{
|
||||
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState));
|
||||
if(state == sStateListEnd)
|
||||
const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
|
||||
if(state == sDeathListEnd)
|
||||
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
|
||||
|
||||
mCurrentDeath = state->groupname;
|
||||
|
@ -302,6 +366,223 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
|
|||
}
|
||||
|
||||
|
||||
bool CharacterController::updateNpcState()
|
||||
{
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
|
||||
CreatureStats &crstats = cls.getCreatureStats(mPtr);
|
||||
NpcStats &stats = cls.getNpcStats(mPtr);
|
||||
WeaponType weaptype = WeapType_None;
|
||||
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype);
|
||||
|
||||
bool forcestateupdate = false;
|
||||
if(weaptype != mWeaponType)
|
||||
{
|
||||
forcestateupdate = true;
|
||||
|
||||
std::string weapgroup;
|
||||
if(weaptype == WeapType_None)
|
||||
{
|
||||
getWeaponGroup(mWeaponType, weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||
}
|
||||
else
|
||||
{
|
||||
getWeaponGroup(weaptype, weapgroup);
|
||||
mAnimation->showWeapons(false);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
||||
}
|
||||
|
||||
if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell))
|
||||
{
|
||||
std::string soundid = (weaptype == WeapType_None) ?
|
||||
MWWorld::Class::get(*weapon).getDownSoundId(*weapon) :
|
||||
MWWorld::Class::get(*weapon).getUpSoundId(*weapon);
|
||||
if(!soundid.empty())
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
|
||||
mWeaponType = weaptype;
|
||||
getWeaponGroup(mWeaponType, mCurrentWeapon);
|
||||
}
|
||||
|
||||
|
||||
bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name());
|
||||
float weapSpeed = 1.0f;
|
||||
if(isWeapon)
|
||||
weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
|
||||
|
||||
float complete;
|
||||
bool animPlaying;
|
||||
if(crstats.getAttackingOrSpell())
|
||||
{
|
||||
if(mUpperBodyState == UpperCharState_WeapEquiped)
|
||||
{
|
||||
mAttackType.clear();
|
||||
if(mWeaponType == WeapType_Spell)
|
||||
{
|
||||
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
|
||||
|
||||
const std::string spellid = crstats.getSpells().getSelectedSpell();
|
||||
if(!spellid.empty())
|
||||
{
|
||||
static const std::string schools[] = {
|
||||
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
|
||||
};
|
||||
|
||||
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
|
||||
const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0);
|
||||
|
||||
const ESM::MagicEffect *effect;
|
||||
effect = store.get<ESM::MagicEffect>().find(effectentry.mEffectID);
|
||||
|
||||
switch(effectentry.mRange)
|
||||
{
|
||||
case 0: mAttackType = "self"; break;
|
||||
case 1: mAttackType = "touch"; break;
|
||||
case 2: mAttackType = "target"; break;
|
||||
}
|
||||
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
weapSpeed, mAttackType+" start", mAttackType+" stop",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_CastingSpell;
|
||||
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
if(!effect->mCastSound.empty())
|
||||
sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f);
|
||||
else
|
||||
sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
else if(mWeaponType != WeapType_PickProbe)
|
||||
{
|
||||
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow ||
|
||||
mWeaponType == WeapType_ThowWeapon)
|
||||
mAttackType = "shoot";
|
||||
else
|
||||
{
|
||||
int attackType = crstats.getAttackType();
|
||||
if(isWeapon && Settings::Manager::getBool("best attack", "Game"))
|
||||
attackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
|
||||
|
||||
if (attackType == MWMechanics::CreatureStats::AT_Chop)
|
||||
mAttackType = "chop";
|
||||
else if (attackType == MWMechanics::CreatureStats::AT_Slash)
|
||||
mAttackType = "slash";
|
||||
else
|
||||
mAttackType = "thrust";
|
||||
}
|
||||
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" start", mAttackType+" min attack",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
||||
}
|
||||
}
|
||||
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
||||
}
|
||||
else
|
||||
{
|
||||
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
|
||||
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack)
|
||||
{
|
||||
if(mAttackType != "shoot")
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
// NOTE: SwishL, SwishM, SwishS - large, medium, small.
|
||||
// Based on weapon weight, speed, or attack strength?
|
||||
sndMgr->playSound3D(mPtr, "SwishL", 1.0f, 1.0f);
|
||||
}
|
||||
stats.setAttackStrength(complete);
|
||||
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
|
||||
1.0f-complete, 0);
|
||||
mUpperBodyState = UpperCharState_MaxAttackToMinHit;
|
||||
}
|
||||
}
|
||||
|
||||
if(!animPlaying)
|
||||
{
|
||||
if(mUpperBodyState == UpperCharState_EquipingWeap ||
|
||||
mUpperBodyState == UpperCharState_FollowStartToFollowStop ||
|
||||
mUpperBodyState == UpperCharState_CastingSpell)
|
||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
|
||||
mUpperBodyState = UpperCharState_Nothing;
|
||||
}
|
||||
else if(complete >= 1.0f)
|
||||
{
|
||||
if(mUpperBodyState == UpperCharState_StartToMinAttack)
|
||||
{
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" min attack", mAttackType+" max attack",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
|
||||
}
|
||||
else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit)
|
||||
{
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
if(mAttackType == "shoot")
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" min hit", mAttackType+" follow start",
|
||||
0.0f, 0);
|
||||
else
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" min hit", mAttackType+" hit",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_MinHitToHit;
|
||||
}
|
||||
else if(mUpperBodyState == UpperCharState_MinHitToHit)
|
||||
{
|
||||
mAnimation->disable(mCurrentWeapon);
|
||||
if(mAttackType == "shoot")
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
weapSpeed, mAttackType+" follow start", mAttackType+" follow stop",
|
||||
0.0f, 0);
|
||||
else
|
||||
mAnimation->play(mCurrentWeapon, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",
|
||||
0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name())
|
||||
{
|
||||
if(!mAnimation->isPlaying("torch"))
|
||||
mAnimation->play("torch", Priority_Torch,
|
||||
MWRender::Animation::Group_LeftArm, false,
|
||||
1.0f, "start", "stop", 0.0f, (~(size_t)0));
|
||||
}
|
||||
else if(mAnimation->isPlaying("torch"))
|
||||
mAnimation->disable("torch");
|
||||
|
||||
return forcestateupdate;
|
||||
}
|
||||
|
||||
void CharacterController::update(float duration, Movement &movement)
|
||||
{
|
||||
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
|
||||
|
@ -443,206 +724,7 @@ void CharacterController::update(float duration, Movement &movement)
|
|||
movement.mRotation[2] += rot.z;
|
||||
|
||||
if(mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
NpcStats &stats = cls.getNpcStats(mPtr);
|
||||
WeaponType weaptype = WeapType_None;
|
||||
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
|
||||
MWWorld::ContainerStoreIterator weapon = inv.end();
|
||||
|
||||
if(stats.getDrawState() == DrawState_Spell)
|
||||
weaptype = WeapType_Spell;
|
||||
else if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
|
||||
{
|
||||
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
if(weapon == inv.end())
|
||||
weaptype = WeapType_HandToHand;
|
||||
else
|
||||
{
|
||||
const std::string &type = weapon->getTypeName();
|
||||
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
|
||||
weaptype = WeapType_PickProbe;
|
||||
else if(type == typeid(ESM::Weapon).name())
|
||||
{
|
||||
MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
|
||||
ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType;
|
||||
switch(type)
|
||||
{
|
||||
case ESM::Weapon::ShortBladeOneHand:
|
||||
case ESM::Weapon::LongBladeOneHand:
|
||||
case ESM::Weapon::BluntOneHand:
|
||||
case ESM::Weapon::AxeOneHand:
|
||||
case ESM::Weapon::Arrow:
|
||||
case ESM::Weapon::Bolt:
|
||||
weaptype = WeapType_OneHand;
|
||||
break;
|
||||
case ESM::Weapon::LongBladeTwoHand:
|
||||
case ESM::Weapon::BluntTwoClose:
|
||||
case ESM::Weapon::AxeTwoHand:
|
||||
weaptype = WeapType_TwoHand;
|
||||
break;
|
||||
case ESM::Weapon::BluntTwoWide:
|
||||
case ESM::Weapon::SpearTwoWide:
|
||||
weaptype = WeapType_TwoWide;
|
||||
break;
|
||||
case ESM::Weapon::MarksmanBow:
|
||||
weaptype = WeapType_BowAndArrow;
|
||||
break;
|
||||
case ESM::Weapon::MarksmanCrossbow:
|
||||
weaptype = WeapType_Crossbow;
|
||||
break;
|
||||
case ESM::Weapon::MarksmanThrown:
|
||||
weaptype = WeapType_ThowWeapon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
|
||||
|
||||
if(mUpdateWeapon)
|
||||
{
|
||||
forcestateupdate = (mWeaponType != weaptype);
|
||||
mWeaponType = weaptype;
|
||||
mUpdateWeapon = false;
|
||||
}
|
||||
|
||||
if(weaptype != mWeaponType)
|
||||
{
|
||||
forcestateupdate = true;
|
||||
|
||||
std::string weapgroup;
|
||||
if(weaptype == WeapType_None)
|
||||
{
|
||||
getWeaponGroup(mWeaponType, weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
1.0f, "unequip start", "unequip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_UnEquipingWeap;
|
||||
}
|
||||
else
|
||||
{
|
||||
getWeaponGroup(weaptype, weapgroup);
|
||||
mAnimation->showWeapons(false);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, true,
|
||||
1.0f, "equip start", "equip stop", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_EquipingWeap;
|
||||
}
|
||||
|
||||
mWeaponType = weaptype;
|
||||
|
||||
if(weapon != inv.end())
|
||||
{
|
||||
std::string soundid = (mWeaponType == WeapType_None) ?
|
||||
MWWorld::Class::get(*weapon).getDownSoundId(*weapon) :
|
||||
MWWorld::Class::get(*weapon).getUpSoundId(*weapon);
|
||||
if(!soundid.empty())
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(weaptype != WeapType_PickProbe && weaptype != WeapType_BowAndArrow
|
||||
&& weaptype != WeapType_Crossbow && weaptype != WeapType_ThowWeapon)
|
||||
{
|
||||
std::string weapgroup;
|
||||
getWeaponGroup(mWeaponType, weapgroup);
|
||||
float weapSpeed = 1;
|
||||
if(weapon != inv.end()) weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
|
||||
std::string start;
|
||||
std::string stop;
|
||||
float complete;
|
||||
float speedMult;
|
||||
bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop);
|
||||
|
||||
if(cls.getCreatureStats(mPtr).getAttackingOrSpell())
|
||||
{
|
||||
if(mUpperBodyState == UpperCharState_WeapEquiped)
|
||||
{
|
||||
int attackType = cls.getCreatureStats(mPtr).getAttackType();
|
||||
if (Settings::Manager::getBool("best attack", "Game") && weapon != inv.end())
|
||||
attackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
|
||||
|
||||
if (attackType == MWMechanics::CreatureStats::AT_Chop)
|
||||
mAttackType = "chop";
|
||||
else if (attackType == MWMechanics::CreatureStats::AT_Slash)
|
||||
mAttackType = "slash";
|
||||
else
|
||||
mAttackType = "thrust";
|
||||
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0);
|
||||
mUpperBodyState = UpperCharState_StartToMinAttack;
|
||||
}
|
||||
}
|
||||
else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack)
|
||||
{
|
||||
if(animPlaying)
|
||||
{
|
||||
mAnimation->disable(weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 1-complete, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 0, 0);
|
||||
}
|
||||
mUpperBodyState = UpperCharState_MaxAttackToMinHit;
|
||||
}
|
||||
|
||||
if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing;
|
||||
if(animPlaying)
|
||||
{
|
||||
if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1)
|
||||
{
|
||||
mAnimation->disable(weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" min attack", mAttackType+" max attack",0, 0);
|
||||
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
|
||||
}
|
||||
else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1)
|
||||
{
|
||||
mAnimation->disable(weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" min hit", mAttackType+" hit",0, 0);
|
||||
mUpperBodyState = UpperCharState_MinHitToHit;
|
||||
}
|
||||
else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1)
|
||||
{
|
||||
mAnimation->disable(weapgroup);
|
||||
mAnimation->play(weapgroup, Priority_Weapon,
|
||||
MWRender::Animation::Group_UpperBody, false,
|
||||
weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",0, 0);
|
||||
mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop;
|
||||
}
|
||||
else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1)
|
||||
{
|
||||
mUpperBodyState = UpperCharState_WeapEquiped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
|
||||
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name())
|
||||
{
|
||||
if(!mAnimation->isPlaying("torch"))
|
||||
mAnimation->play("torch", Priority_Torch,
|
||||
MWRender::Animation::Group_LeftArm, false,
|
||||
1.0f, "start", "stop", 0.0f, (~(size_t)0));
|
||||
}
|
||||
else if(mAnimation->isPlaying("torch"))
|
||||
mAnimation->disable("torch");
|
||||
}
|
||||
forcestateupdate = updateNpcState();
|
||||
|
||||
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
|
||||
}
|
||||
|
@ -734,8 +816,8 @@ void CharacterController::forceStateUpdate()
|
|||
refreshCurrentAnims(mIdleState, mMovementState, true);
|
||||
if(mDeathState != CharState_None)
|
||||
{
|
||||
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState));
|
||||
if(state == sStateListEnd)
|
||||
const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
|
||||
if(state == sDeathListEnd)
|
||||
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
|
||||
|
||||
mCurrentDeath = state->groupname;
|
||||
|
@ -747,22 +829,52 @@ void CharacterController::forceStateUpdate()
|
|||
|
||||
void CharacterController::kill()
|
||||
{
|
||||
static const CharacterState deathstates[] = {
|
||||
CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5
|
||||
};
|
||||
|
||||
if(mDeathState != CharState_None)
|
||||
return;
|
||||
|
||||
mDeathState = deathstates[(int)(rand()/((double)RAND_MAX+1.0)*5.0)];
|
||||
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState));
|
||||
if(state == sStateListEnd)
|
||||
if(mPtr.getTypeName() == typeid(ESM::NPC).name())
|
||||
{
|
||||
const StateInfo *state = NULL;
|
||||
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr))
|
||||
{
|
||||
mDeathState = CharState_SwimDeath;
|
||||
state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
|
||||
if(state == sDeathListEnd)
|
||||
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
|
||||
}
|
||||
|
||||
static const CharacterState deathstates[5] = {
|
||||
CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5
|
||||
};
|
||||
std::vector<CharacterState> states(&deathstates[0], &deathstates[5]);
|
||||
|
||||
while(states.size() > 1 && (!state || !mAnimation->hasAnimation(state->groupname)))
|
||||
{
|
||||
int pos = (int)(rand()/((double)RAND_MAX+1.0)*states.size());
|
||||
mDeathState = states[pos];
|
||||
states.erase(states.begin()+pos);
|
||||
|
||||
state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
|
||||
if(state == sDeathListEnd)
|
||||
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
|
||||
}
|
||||
mCurrentDeath = state->groupname;
|
||||
if(mAnimation && !mAnimation->getInfo(mCurrentDeath))
|
||||
}
|
||||
else
|
||||
{
|
||||
mDeathState = CharState_Death1;
|
||||
mCurrentDeath = "death1";
|
||||
}
|
||||
|
||||
if(mAnimation)
|
||||
{
|
||||
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
|
||||
false, 1.0f, "start", "stop", 0.0f, 0);
|
||||
mAnimation->disable(mCurrentIdle);
|
||||
}
|
||||
|
||||
mIdleState = CharState_None;
|
||||
mCurrentIdle.clear();
|
||||
}
|
||||
|
||||
void CharacterController::resurrect()
|
||||
|
@ -776,5 +888,4 @@ void CharacterController::resurrect()
|
|||
mDeathState = CharState_None;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -5,6 +5,12 @@
|
|||
|
||||
#include "../mwworld/ptr.hpp"
|
||||
|
||||
namespace MWWorld
|
||||
{
|
||||
class ContainerStoreIterator;
|
||||
class InventoryStore;
|
||||
}
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
class Animation;
|
||||
|
@ -14,6 +20,7 @@ namespace MWMechanics
|
|||
{
|
||||
|
||||
class Movement;
|
||||
class NpcStats;
|
||||
|
||||
enum Priority {
|
||||
Priority_Default,
|
||||
|
@ -72,12 +79,12 @@ enum CharacterState {
|
|||
|
||||
CharState_Jump,
|
||||
|
||||
/* Death states must be last! */
|
||||
CharState_Death1,
|
||||
CharState_Death2,
|
||||
CharState_Death3,
|
||||
CharState_Death4,
|
||||
CharState_Death5
|
||||
CharState_Death5,
|
||||
CharState_SwimDeath
|
||||
};
|
||||
|
||||
enum WeaponType {
|
||||
|
@ -104,11 +111,8 @@ enum UpperBodyCharacterState {
|
|||
UpperCharState_MinAttackToMaxAttack,
|
||||
UpperCharState_MaxAttackToMinHit,
|
||||
UpperCharState_MinHitToHit,
|
||||
UpperCharState_LargeFollowStartToLargeFollowStop,
|
||||
UpperCharState_MediumFollowStartToMediumFollowStop,
|
||||
UpperCharState_SmallFollowStartToSmallFollowStop,
|
||||
UpperCharState_EquipingSpell,
|
||||
UpperCharState_UnEquipingSpell
|
||||
UpperCharState_FollowStartToFollowStop,
|
||||
UpperCharState_CastingSpell
|
||||
};
|
||||
|
||||
class CharacterController
|
||||
|
@ -132,10 +136,9 @@ class CharacterController
|
|||
UpperBodyCharacterState mUpperBodyState;
|
||||
|
||||
WeaponType mWeaponType;
|
||||
bool mSkipAnim;
|
||||
std::string mCurrentWeapon;
|
||||
|
||||
// Workaround for playing weapon draw animation and sound when going to new cell
|
||||
bool mUpdateWeapon;
|
||||
bool mSkipAnim;
|
||||
|
||||
// counted for skill increase
|
||||
float mSecondsOfSwimming;
|
||||
|
@ -147,8 +150,14 @@ class CharacterController
|
|||
|
||||
static void getWeaponGroup(WeaponType weaptype, std::string &group);
|
||||
|
||||
static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats,
|
||||
MWWorld::InventoryStore &inv,
|
||||
WeaponType *weaptype);
|
||||
|
||||
void clearAnimQueue();
|
||||
|
||||
bool updateNpcState();
|
||||
|
||||
public:
|
||||
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
|
||||
virtual ~CharacterController();
|
||||
|
|
|
@ -19,16 +19,35 @@ namespace MWMechanics
|
|||
mAiSettings[i] = 0;
|
||||
}
|
||||
|
||||
void CreatureStats::increaseLevelHealthBonus (float value)
|
||||
{
|
||||
mLevelHealthBonus += value;
|
||||
}
|
||||
|
||||
float CreatureStats::getLevelHealthBonus () const
|
||||
{
|
||||
return mLevelHealthBonus;
|
||||
}
|
||||
|
||||
void CreatureStats::levelUp()
|
||||
{
|
||||
const MWWorld::Store<ESM::GameSetting> &gmst =
|
||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
|
||||
|
||||
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
|
||||
// "When you gain a level, in addition to increasing three primary attributes, your Health
|
||||
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
|
||||
// the Health increase is calculated from the increased Endurance"
|
||||
mLevelHealthBonus += endurance * gmst.find("fLevelUpHealthEndMult")->getFloat();
|
||||
updateHealth();
|
||||
|
||||
mLevel++;
|
||||
}
|
||||
|
||||
void CreatureStats::updateHealth()
|
||||
{
|
||||
const int endurance = getAttribute(ESM::Attribute::Endurance).getBase();
|
||||
const int strength = getAttribute(ESM::Attribute::Strength).getBase();
|
||||
|
||||
setHealth(static_cast<int> (0.5 * (strength + endurance)) + mLevelHealthBonus);
|
||||
}
|
||||
|
||||
const AiSequence& CreatureStats::getAiSequence() const
|
||||
{
|
||||
return mAiSequence;
|
||||
|
@ -299,4 +318,14 @@ namespace MWMechanics
|
|||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
void CreatureStats::setLastHitObject(const std::string& objectid)
|
||||
{
|
||||
mLastHitObject = objectid;
|
||||
}
|
||||
|
||||
const std::string &CreatureStats::getLastHitObject() const
|
||||
{
|
||||
return mLastHitObject;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -38,6 +38,8 @@ namespace MWMechanics
|
|||
|
||||
int mAttackType;
|
||||
|
||||
std::string mLastHitObject; // The last object to hit this actor
|
||||
|
||||
public:
|
||||
CreatureStats();
|
||||
|
||||
|
@ -111,10 +113,14 @@ namespace MWMechanics
|
|||
float getFatigueTerm() const;
|
||||
///< Return effective fatigue
|
||||
|
||||
// small hack to allow the fact that Health permanently increases by 10% of endurance on each level up
|
||||
void increaseLevelHealthBonus(float value);
|
||||
float getLevelHealthBonus() const;
|
||||
|
||||
void levelUp();
|
||||
|
||||
void updateHealth();
|
||||
///< Calculate health based on endurance and strength.
|
||||
/// Called at character creation and at level up.
|
||||
|
||||
bool isDead() const;
|
||||
|
||||
bool hasDied() const;
|
||||
|
@ -151,6 +157,9 @@ namespace MWMechanics
|
|||
void setHostile (bool hostile);
|
||||
|
||||
bool getCreatureTargetted() const;
|
||||
|
||||
void setLastHitObject(const std::string &objectid);
|
||||
const std::string &getLastHitObject() const;
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,7 @@ MWMechanics::NpcStats::NpcStats()
|
|||
, mWerewolf (false)
|
||||
, mWerewolfKills (0)
|
||||
, mProfit(0)
|
||||
, mAttackStrength(0.0f)
|
||||
{
|
||||
mSkillIncreases.resize (ESM::Attribute::Length);
|
||||
for (int i=0; i<ESM::Attribute::Length; ++i)
|
||||
|
@ -47,6 +48,16 @@ void MWMechanics::NpcStats::setDrawState (DrawState_ state)
|
|||
mDrawState = state;
|
||||
}
|
||||
|
||||
float MWMechanics::NpcStats::getAttackStrength() const
|
||||
{
|
||||
return mAttackStrength;
|
||||
}
|
||||
|
||||
void MWMechanics::NpcStats::setAttackStrength(float value)
|
||||
{
|
||||
mAttackStrength = value;
|
||||
}
|
||||
|
||||
int MWMechanics::NpcStats::getBaseDisposition() const
|
||||
{
|
||||
return mDisposition;
|
||||
|
|
|
@ -54,6 +54,7 @@ namespace MWMechanics
|
|||
bool mWerewolf;
|
||||
int mWerewolfKills;
|
||||
int mProfit;
|
||||
float mAttackStrength;
|
||||
|
||||
int mLevelProgress; // 0-10
|
||||
|
||||
|
@ -70,9 +71,12 @@ namespace MWMechanics
|
|||
void modifyProfit(int diff);
|
||||
|
||||
DrawState_ getDrawState() const;
|
||||
|
||||
void setDrawState (DrawState_ state);
|
||||
|
||||
/// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest)
|
||||
float getAttackStrength() const;
|
||||
void setAttackStrength(float value);
|
||||
|
||||
int getBaseDisposition() const;
|
||||
|
||||
void setBaseDisposition(int disposition);
|
||||
|
|
|
@ -16,6 +16,8 @@ Objects::Objects()
|
|||
|
||||
void Objects::addObject(const MWWorld::Ptr& ptr)
|
||||
{
|
||||
removeObject(ptr);
|
||||
|
||||
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
|
||||
if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
|
||||
}
|
||||
|
|
|
@ -98,8 +98,8 @@ namespace MWMechanics
|
|||
public:
|
||||
typedef T Type;
|
||||
|
||||
DynamicStat() : mCurrent (0) {}
|
||||
DynamicStat(T current) : mCurrent (current) {}
|
||||
DynamicStat() : mStatic (0), mCurrent (0) {}
|
||||
DynamicStat(T base) : mStatic (base), mCurrent (base) {}
|
||||
DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {}
|
||||
DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {}
|
||||
|
||||
|
|
|
@ -14,6 +14,8 @@
|
|||
#include "../mwbase/world.hpp"
|
||||
|
||||
#include "../mwmechanics/character.hpp"
|
||||
#include "../mwmechanics/creaturestats.hpp"
|
||||
#include "../mwworld/class.hpp"
|
||||
|
||||
namespace MWRender
|
||||
{
|
||||
|
@ -52,6 +54,7 @@ Animation::Animation(const MWWorld::Ptr &ptr)
|
|||
, mNonAccumRoot(NULL)
|
||||
, mNonAccumCtrl(NULL)
|
||||
, mAccumulate(0.0f)
|
||||
, mNullAnimationValuePtr(OGRE_NEW NullAnimationValue)
|
||||
{
|
||||
for(size_t i = 0;i < sNumGroups;i++)
|
||||
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this));
|
||||
|
@ -441,64 +444,58 @@ void Animation::updatePosition(float oldtime, float newtime, Ogre::Vector3 &posi
|
|||
|
||||
bool Animation::reset(AnimState &state, const NifOgre::TextKeyMap &keys, const std::string &groupname, const std::string &start, const std::string &stop, float startpoint)
|
||||
{
|
||||
std::string tag = groupname+": "+start;
|
||||
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
|
||||
while(startkey != keys.end() && startkey->second != tag)
|
||||
const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname);
|
||||
|
||||
std::string starttag = groupname+": "+start;
|
||||
NifOgre::TextKeyMap::const_iterator startkey(groupstart);
|
||||
while(startkey != keys.end() && startkey->second != starttag)
|
||||
startkey++;
|
||||
if(startkey == keys.end() && start == "loop start")
|
||||
{
|
||||
tag = groupname+": start";
|
||||
startkey = keys.begin();
|
||||
while(startkey != keys.end() && startkey->second != tag)
|
||||
starttag = groupname+": start";
|
||||
startkey = groupstart;
|
||||
while(startkey != keys.end() && startkey->second != starttag)
|
||||
startkey++;
|
||||
}
|
||||
if(startkey == keys.end())
|
||||
return false;
|
||||
|
||||
tag = groupname+": "+stop;
|
||||
NifOgre::TextKeyMap::const_iterator stopkey(startkey);
|
||||
while(stopkey != keys.end() && stopkey->second != tag)
|
||||
const std::string stoptag = groupname+": "+stop;
|
||||
NifOgre::TextKeyMap::const_iterator stopkey(groupstart);
|
||||
while(stopkey != keys.end() && stopkey->second != stoptag)
|
||||
stopkey++;
|
||||
if(stopkey == keys.end())
|
||||
return false;
|
||||
|
||||
if(startkey == stopkey)
|
||||
if(startkey->first > stopkey->first)
|
||||
return false;
|
||||
|
||||
state.mStartKey = startkey;
|
||||
state.mLoopStartKey = startkey;
|
||||
state.mStopKey = stopkey;
|
||||
state.mNextKey = startkey;
|
||||
state.mStartTime = startkey->first;
|
||||
state.mLoopStartTime = startkey->first;
|
||||
state.mLoopStopTime = stopkey->first;
|
||||
state.mStopTime = stopkey->first;
|
||||
|
||||
state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint);
|
||||
|
||||
tag = groupname+": loop start";
|
||||
while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey)
|
||||
state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint);
|
||||
if(state.mTime > state.mStartTime)
|
||||
{
|
||||
if(state.mNextKey->second == tag)
|
||||
state.mLoopStartKey = state.mNextKey;
|
||||
state.mNextKey++;
|
||||
const std::string loopstarttag = groupname+": loop start";
|
||||
const std::string loopstoptag = groupname+": loop stop";
|
||||
NifOgre::TextKeyMap::const_iterator key(groupstart);
|
||||
while(key->first <= state.mTime && key != stopkey)
|
||||
{
|
||||
if(key->second == loopstarttag)
|
||||
state.mLoopStartTime = key->first;
|
||||
else if(key->second == loopstoptag)
|
||||
state.mLoopStopTime = key->first;
|
||||
key++;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Animation::doLoop(AnimState &state)
|
||||
{
|
||||
if(state.mLoopCount == 0)
|
||||
return false;
|
||||
state.mLoopCount--;
|
||||
|
||||
state.mTime = state.mLoopStartKey->first;
|
||||
state.mNextKey = state.mLoopStartKey;
|
||||
state.mNextKey++;
|
||||
state.mPlaying = true;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key)
|
||||
void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key)
|
||||
{
|
||||
float time = key->first;
|
||||
const std::string &evt = key->second;
|
||||
|
@ -507,64 +504,53 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co
|
|||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
if(evt.compare(0, 10, "soundgen: ") == 0)
|
||||
{
|
||||
// FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds
|
||||
// to this actor type
|
||||
return true;
|
||||
std::string sound = MWWorld::Class::get(mPtr).getSoundIdFromSndGen(mPtr, evt.substr(10));
|
||||
if(!sound.empty())
|
||||
{
|
||||
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
|
||||
MWBase::SoundManager::PlayType type = MWBase::SoundManager::Play_TypeSfx;
|
||||
if(evt.compare(10, evt.size()-10, "left") == 0 || evt.compare(10, evt.size()-10, "right") == 0)
|
||||
type = MWBase::SoundManager::Play_TypeFoot;
|
||||
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(evt.compare(0, groupname.size(), groupname) != 0 ||
|
||||
evt.compare(groupname.size(), 2, ": ") != 0)
|
||||
{
|
||||
// Not ours, skip it
|
||||
return true;
|
||||
return;
|
||||
}
|
||||
size_t off = groupname.size()+2;
|
||||
size_t len = evt.size() - off;
|
||||
|
||||
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0)
|
||||
{
|
||||
state.mLoopStartKey = key;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(evt.compare(off, len, "loop stop") == 0 || evt.compare(off, len, "stop") == 0)
|
||||
{
|
||||
if(doLoop(state))
|
||||
{
|
||||
if(state.mTime >= time)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if(evt.compare(off, len, "equip attach") == 0)
|
||||
{
|
||||
if(evt.compare(off, len, "loop start") == 0)
|
||||
state.mLoopStartTime = key->first;
|
||||
else if(evt.compare(off, len, "loop stop") == 0)
|
||||
state.mLoopStopTime = key->first;
|
||||
else if(evt.compare(off, len, "equip attach") == 0)
|
||||
showWeapons(true);
|
||||
return true;
|
||||
}
|
||||
if(evt.compare(off, len, "unequip detach") == 0)
|
||||
{
|
||||
else if(evt.compare(off, len, "unequip detach") == 0)
|
||||
showWeapons(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
/* Nothing to do for these */
|
||||
if(evt.compare(off, len, "equip start") == 0 || evt.compare(off, len, "equip stop") == 0 ||
|
||||
evt.compare(off, len, "unequip start") == 0 || evt.compare(off, len, "unequip stop") == 0)
|
||||
return true;
|
||||
|
||||
std::cerr<< "Unhandled animation textkey: "<<evt <<std::endl;
|
||||
return true;
|
||||
else if(evt.compare(off, len, "chop hit") == 0)
|
||||
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Chop);
|
||||
else if(evt.compare(off, len, "slash hit") == 0)
|
||||
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
|
||||
else if(evt.compare(off, len, "thrust hit") == 0)
|
||||
MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
|
||||
else if(evt.compare(off, len, "hit") == 0)
|
||||
MWWorld::Class::get(mPtr).hit(mPtr);
|
||||
}
|
||||
|
||||
|
||||
void Animation::play(const std::string &groupname, int priority, int groups, bool autodisable, float speedmult, const std::string &start, const std::string &stop, float startpoint, size_t loops)
|
||||
{
|
||||
if(!mSkelBase)
|
||||
if(!mSkelBase || mAnimSources.size() == 0)
|
||||
return;
|
||||
|
||||
if(groupname.empty())
|
||||
|
@ -596,23 +582,47 @@ void Animation::play(const std::string &groupname, int priority, int groups, boo
|
|||
AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
|
||||
for(;iter != mAnimSources.rend();iter++)
|
||||
{
|
||||
const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys;
|
||||
AnimState state;
|
||||
if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint))
|
||||
if(reset(state, textkeys, groupname, start, stop, startpoint))
|
||||
{
|
||||
state.mSource = *iter;
|
||||
state.mSpeedMult = speedmult;
|
||||
state.mLoopCount = loops;
|
||||
state.mPlaying = true;
|
||||
state.mPlaying = (state.mTime < state.mStopTime);
|
||||
state.mPriority = priority;
|
||||
state.mGroups = groups;
|
||||
state.mAutoDisable = autodisable;
|
||||
mStates[groupname] = state;
|
||||
|
||||
NifOgre::TextKeyMap::const_iterator textkey(textkeys.lower_bound(state.mTime));
|
||||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, groupname, textkey);
|
||||
textkey++;
|
||||
}
|
||||
|
||||
if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0)
|
||||
{
|
||||
state.mLoopCount--;
|
||||
state.mTime = state.mLoopStartTime;
|
||||
state.mPlaying = true;
|
||||
if(state.mTime >= state.mLoopStopTime)
|
||||
break;
|
||||
|
||||
textkey = textkeys.lower_bound(state.mTime);
|
||||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, groupname, textkey);
|
||||
textkey++;
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(iter == mAnimSources.rend())
|
||||
std::cerr<< "Failed to find animation "<<groupname <<std::endl;
|
||||
std::cerr<< "Failed to find animation "<<groupname<<" for "<<mPtr.getCellRef().mRefID <<std::endl;
|
||||
|
||||
resetActiveGroups();
|
||||
}
|
||||
|
@ -654,7 +664,6 @@ void Animation::resetActiveGroups()
|
|||
return;
|
||||
|
||||
const Ogre::SharedPtr<AnimSource> &animsrc = state->second.mSource;
|
||||
//const NifOgre::TextKeyMap &keys = animsrc->mTextKeys;
|
||||
const std::vector<Ogre::Controller<Ogre::Real> >&ctrls = animsrc->mControllers[0];
|
||||
for(size_t i = 0;i < ctrls.size();i++)
|
||||
{
|
||||
|
@ -669,23 +678,25 @@ void Animation::resetActiveGroups()
|
|||
}
|
||||
|
||||
|
||||
bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult, std::string *start, std::string *stop) const
|
||||
bool Animation::getInfo(const std::string &groupname, float *complete, float *speedmult) const
|
||||
{
|
||||
AnimStateMap::const_iterator iter = mStates.find(groupname);
|
||||
if(iter == mStates.end())
|
||||
{
|
||||
if(complete) *complete = 0.0f;
|
||||
if(speedmult) *speedmult = 0.0f;
|
||||
if(start) *start = "";
|
||||
if(stop) *stop = "";
|
||||
return false;
|
||||
}
|
||||
|
||||
if(complete) *complete = (iter->second.mTime - iter->second.mStartKey->first) /
|
||||
(iter->second.mStopKey->first - iter->second.mStartKey->first);
|
||||
if(complete)
|
||||
{
|
||||
if(iter->second.mStopTime > iter->second.mStartTime)
|
||||
*complete = (iter->second.mTime - iter->second.mStartTime) /
|
||||
(iter->second.mStopTime - iter->second.mStartTime);
|
||||
else
|
||||
*complete = (iter->second.mPlaying ? 0.0f : 1.0f);
|
||||
}
|
||||
if(speedmult) *speedmult = iter->second.mSpeedMult;
|
||||
if(start) *start = iter->second.mStartKey->second.substr(groupname.size()+2);
|
||||
if(stop) *stop = iter->second.mStopKey->second.substr(groupname.size()+2);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -707,27 +718,52 @@ Ogre::Vector3 Animation::runAnimation(float duration)
|
|||
while(stateiter != mStates.end())
|
||||
{
|
||||
AnimState &state = stateiter->second;
|
||||
const NifOgre::TextKeyMap &textkeys = state.mSource->mTextKeys;
|
||||
NifOgre::TextKeyMap::const_iterator textkey(textkeys.upper_bound(state.mTime));
|
||||
|
||||
float timepassed = duration * state.mSpeedMult;
|
||||
while(state.mPlaying)
|
||||
{
|
||||
float targetTime = state.mTime + timepassed;
|
||||
if(state.mNextKey->first > targetTime)
|
||||
if(textkey == textkeys.end() || textkey->first > targetTime)
|
||||
{
|
||||
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
||||
updatePosition(state.mTime, targetTime, movement);
|
||||
state.mTime = targetTime;
|
||||
break;
|
||||
state.mTime = std::min(targetTime, state.mStopTime);
|
||||
}
|
||||
else
|
||||
{
|
||||
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
||||
updatePosition(state.mTime, textkey->first, movement);
|
||||
state.mTime = textkey->first;
|
||||
}
|
||||
|
||||
NifOgre::TextKeyMap::const_iterator key(state.mNextKey++);
|
||||
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
|
||||
updatePosition(state.mTime, key->first, movement);
|
||||
state.mTime = key->first;
|
||||
|
||||
state.mPlaying = (key != state.mStopKey);
|
||||
state.mPlaying = (state.mTime < state.mStopTime);
|
||||
timepassed = targetTime - state.mTime;
|
||||
|
||||
if(!handleTextKey(state, stateiter->first, key))
|
||||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, stateiter->first, textkey);
|
||||
textkey++;
|
||||
}
|
||||
|
||||
if(state.mTime >= state.mLoopStopTime && state.mLoopCount > 0)
|
||||
{
|
||||
state.mLoopCount--;
|
||||
state.mTime = state.mLoopStartTime;
|
||||
state.mPlaying = true;
|
||||
if(state.mTime >= state.mLoopStopTime)
|
||||
break;
|
||||
|
||||
textkey = textkeys.lower_bound(state.mTime);
|
||||
while(textkey != textkeys.end() && textkey->first <= state.mTime)
|
||||
{
|
||||
handleTextKey(state, stateiter->first, textkey);
|
||||
textkey++;
|
||||
}
|
||||
}
|
||||
|
||||
if(timepassed <= 0.0f)
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
|
@ -52,6 +52,15 @@ protected:
|
|||
virtual void setValue(Ogre::Real value);
|
||||
};
|
||||
|
||||
class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real>
|
||||
{
|
||||
public:
|
||||
virtual Ogre::Real getValue() const
|
||||
{ return 0.0f; }
|
||||
virtual void setValue(Ogre::Real value)
|
||||
{ }
|
||||
};
|
||||
|
||||
struct AnimSource : public Ogre::AnimationAlloc {
|
||||
NifOgre::TextKeyMap mTextKeys;
|
||||
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
|
||||
|
@ -60,10 +69,10 @@ protected:
|
|||
|
||||
struct AnimState {
|
||||
Ogre::SharedPtr<AnimSource> mSource;
|
||||
NifOgre::TextKeyMap::const_iterator mStartKey;
|
||||
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
|
||||
NifOgre::TextKeyMap::const_iterator mStopKey;
|
||||
NifOgre::TextKeyMap::const_iterator mNextKey;
|
||||
float mStartTime;
|
||||
float mLoopStartTime;
|
||||
float mLoopStopTime;
|
||||
float mStopTime;
|
||||
|
||||
float mTime;
|
||||
float mSpeedMult;
|
||||
|
@ -75,7 +84,8 @@ protected:
|
|||
int mGroups;
|
||||
bool mAutoDisable;
|
||||
|
||||
AnimState() : mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
||||
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
|
||||
mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
|
||||
mPriority(0), mGroups(0), mAutoDisable(true)
|
||||
{ }
|
||||
};
|
||||
|
@ -98,6 +108,7 @@ protected:
|
|||
AnimStateMap mStates;
|
||||
|
||||
Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups];
|
||||
Ogre::SharedPtr<NullAnimationValue> mNullAnimationValuePtr;
|
||||
|
||||
ObjectAttachMap mAttachedObjects;
|
||||
|
||||
|
@ -131,9 +142,7 @@ protected:
|
|||
const std::string &groupname, const std::string &start, const std::string &stop,
|
||||
float startpoint);
|
||||
|
||||
bool doLoop(AnimState &state);
|
||||
|
||||
bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
|
||||
void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
|
||||
|
||||
/* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle
|
||||
* systems in the model are ignored (useful for NPCs, where only the skeleton is needed for
|
||||
|
@ -199,11 +208,9 @@ public:
|
|||
* \param groupname Animation group to check.
|
||||
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
|
||||
* \param speedmult Stores the animation speed multiplier
|
||||
* \param start Stores the start key
|
||||
* \param stop Stores the stop key
|
||||
* \return True if the animation is active, false otherwise.
|
||||
*/
|
||||
bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL, std::string *start=NULL, std::string *stop=NULL) const;
|
||||
bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const;
|
||||
|
||||
/** Disables the specified animation group;
|
||||
* \param groupname Animation group to disable.
|
||||
|
|
|
@ -25,7 +25,11 @@ namespace MWRender
|
|||
mHeight(128.f),
|
||||
mCameraDistance(300.f),
|
||||
mDistanceAdjusted(false),
|
||||
mAnimation(NULL)
|
||||
mAnimation(NULL),
|
||||
mNearest(30.f),
|
||||
mFurthest(800.f),
|
||||
mIsNearest(false),
|
||||
mIsFurthest(false)
|
||||
{
|
||||
mVanity.enabled = false;
|
||||
mVanity.allowed = true;
|
||||
|
@ -82,7 +86,7 @@ namespace MWRender
|
|||
mCameraNode->getCreator()->destroySceneNode(mCameraNode);
|
||||
}
|
||||
mCameraNode = node;
|
||||
mCamera->detachFromParent();
|
||||
if(!mCamera->isAttached())
|
||||
mCameraNode->attachObject(mCamera);
|
||||
}
|
||||
|
||||
|
@ -95,9 +99,11 @@ namespace MWRender
|
|||
MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up);
|
||||
}
|
||||
|
||||
void Camera::update(float duration)
|
||||
void Camera::update(float duration, bool paused)
|
||||
{
|
||||
updateListener();
|
||||
if (paused)
|
||||
return;
|
||||
|
||||
// only show the crosshair in game mode and in first person mode.
|
||||
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
|
||||
|
@ -232,16 +238,21 @@ namespace MWRender
|
|||
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
|
||||
return;
|
||||
|
||||
mIsFurthest = false;
|
||||
mIsNearest = false;
|
||||
|
||||
Ogre::Vector3 v(0.f, 0.f, dist);
|
||||
if (adjust) {
|
||||
v += mCamera->getPosition();
|
||||
}
|
||||
if (v.z > 800.f) {
|
||||
v.z = 800.f;
|
||||
} else if (v.z < 10.f) {
|
||||
if (v.z >= mFurthest) {
|
||||
v.z = mFurthest;
|
||||
mIsFurthest = true;
|
||||
} else if (!override && v.z < 10.f) {
|
||||
v.z = 10.f;
|
||||
} else if (override && v.z < 50.f) {
|
||||
v.z = 50.f;
|
||||
} else if (override && v.z <= mNearest) {
|
||||
v.z = mNearest;
|
||||
mIsNearest = true;
|
||||
}
|
||||
mCamera->setPosition(v);
|
||||
|
||||
|
@ -302,34 +313,13 @@ namespace MWRender
|
|||
}
|
||||
}
|
||||
|
||||
float Camera::getHeight()
|
||||
{
|
||||
if(mCamera->isParentTagPoint())
|
||||
{
|
||||
Ogre::TagPoint *tag = static_cast<Ogre::TagPoint*>(mCamera->getParentNode());
|
||||
return tag->_getFullLocalTransform().getTrans().z;
|
||||
}
|
||||
return mCamera->getParentNode()->getPosition().z;
|
||||
}
|
||||
|
||||
bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera)
|
||||
void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera)
|
||||
{
|
||||
mCamera->getParentSceneNode()->needUpdate(true);
|
||||
|
||||
camera = mCamera->getRealPosition();
|
||||
player = mTrackingPtr.getRefData().getBaseNode()->getPosition();
|
||||
|
||||
return mFirstPersonView && !mVanity.enabled && !mPreviewMode;
|
||||
}
|
||||
|
||||
Ogre::Vector3 Camera::getPosition()
|
||||
{
|
||||
return mTrackingPtr.getRefData().getBaseNode()->getPosition();
|
||||
}
|
||||
|
||||
void Camera::getSightAngles(float &pitch, float &yaw)
|
||||
{
|
||||
pitch = mMainCam.pitch;
|
||||
yaw = mMainCam.yaw;
|
||||
focal = Ogre::Vector3((mCamera->getParentNode()->_getFullTransform() *
|
||||
Ogre::Vector4(0.0f, 0.0f, 0.0f, 1.0f)).ptr());
|
||||
}
|
||||
|
||||
void Camera::togglePlayerLooking(bool enable)
|
||||
|
@ -341,4 +331,14 @@ namespace MWRender
|
|||
{
|
||||
return mPreviewMode || mVanity.enabled;
|
||||
}
|
||||
|
||||
bool Camera::isNearest()
|
||||
{
|
||||
return mIsNearest;
|
||||
}
|
||||
|
||||
bool Camera::isFurthest()
|
||||
{
|
||||
return mIsFurthest;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,6 +33,10 @@ namespace MWRender
|
|||
bool mFirstPersonView;
|
||||
bool mPreviewMode;
|
||||
bool mFreeLook;
|
||||
float mNearest;
|
||||
float mFurthest;
|
||||
bool mIsNearest;
|
||||
bool mIsFurthest;
|
||||
|
||||
struct {
|
||||
bool enabled, allowed;
|
||||
|
@ -80,7 +84,7 @@ namespace MWRender
|
|||
|
||||
void processViewChange();
|
||||
|
||||
void update(float duration);
|
||||
void update(float duration, bool paused=false);
|
||||
|
||||
/// Set camera distance for current mode. Don't work on 1st person view.
|
||||
/// \param adjust Indicates should distance be adjusted or set.
|
||||
|
@ -93,18 +97,16 @@ namespace MWRender
|
|||
|
||||
void setAnimation(NpcAnimation *anim);
|
||||
|
||||
float getHeight();
|
||||
|
||||
/// Stores player and camera world positions in passed arguments
|
||||
/// \return true if camera at the eye-place
|
||||
bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera);
|
||||
Ogre::Vector3 getPosition();
|
||||
|
||||
void getSightAngles(float &pitch, float &yaw);
|
||||
/// Stores focal and camera world positions in passed arguments
|
||||
void getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera);
|
||||
|
||||
void togglePlayerLooking(bool enable);
|
||||
|
||||
bool isVanityOrPreviewModeEnabled();
|
||||
|
||||
bool isNearest();
|
||||
|
||||
bool isFurthest();
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -424,6 +424,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
|
|||
|
||||
for(size_t i = 0;i < sPartListSize;i++)
|
||||
{
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
|
||||
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
|
||||
ctrl->update();
|
||||
|
||||
Ogre::Entity *ent = mObjectParts[i].mSkelBase;
|
||||
if(!ent) continue;
|
||||
updateSkeletonInstance(baseinst, ent->getSkeleton());
|
||||
|
@ -481,6 +485,19 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
|
|||
if(type == sPartList[i].type)
|
||||
{
|
||||
mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name);
|
||||
|
||||
// TODO:
|
||||
// type == ESM::PRT_Head should get an animation source based on the current output of
|
||||
// the actor's 'say' sound (0 = silent, 1 = loud?).
|
||||
// type == ESM::PRT_Weapon should get an animation source based on the current offset
|
||||
// of the weapon attack animation (from its beginning, or start marker?)
|
||||
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
|
||||
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
|
||||
{
|
||||
if(ctrl->getSource().isNull())
|
||||
ctrl->setSource(mNullAnimationValuePtr);
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -16,6 +16,8 @@
|
|||
#include <OgreControllerManager.h>
|
||||
#include <OgreMeshManager.h>
|
||||
|
||||
#include <SDL_video.h>
|
||||
|
||||
#include <extern/shiny/Main/Factory.hpp>
|
||||
#include <extern/shiny/Platforms/Ogre/OgrePlatform.hpp>
|
||||
|
||||
|
@ -77,9 +79,9 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
}
|
||||
|
||||
mRendering.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5);
|
||||
mRendering.setWindowEventListener(this);
|
||||
|
||||
mRendering.getWindow()->addListener(this);
|
||||
mRendering.setWindowListener(this);
|
||||
|
||||
mCompositors = new Compositors(mRendering.getViewport());
|
||||
|
||||
|
@ -170,7 +172,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
|
||||
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode());
|
||||
|
||||
mVideoPlayer = new VideoPlayer(mRendering.getScene ());
|
||||
mVideoPlayer = new VideoPlayer(mRendering.getScene (), mRendering.getWindow());
|
||||
mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video"));
|
||||
|
||||
mSun = 0;
|
||||
|
@ -186,7 +188,6 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
|
|||
RenderingManager::~RenderingManager ()
|
||||
{
|
||||
mRendering.getWindow()->removeListener(this);
|
||||
mRendering.removeWindowEventListener(this);
|
||||
|
||||
delete mPlayerAnimation;
|
||||
delete mCamera;
|
||||
|
@ -303,6 +304,14 @@ RenderingManager::updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &
|
|||
}
|
||||
}
|
||||
|
||||
void RenderingManager::updatePlayerPtr(const MWWorld::Ptr &ptr)
|
||||
{
|
||||
if(mPlayerAnimation)
|
||||
mPlayerAnimation->updatePtr(ptr);
|
||||
if(mCamera->getHandle() == ptr.getRefData().getHandle())
|
||||
mCamera->attachTo(ptr);
|
||||
}
|
||||
|
||||
void RenderingManager::update (float duration, bool paused)
|
||||
{
|
||||
MWBase::World *world = MWBase::Environment::get().getWorld();
|
||||
|
@ -315,19 +324,18 @@ void RenderingManager::update (float duration, bool paused)
|
|||
|
||||
// player position
|
||||
MWWorld::RefData &data = player.getRefData();
|
||||
float *_playerPos = data.getPosition().pos;
|
||||
Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]);
|
||||
Ogre::Vector3 playerPos(data.getPosition().pos);
|
||||
|
||||
Ogre::Vector3 orig, dest;
|
||||
mCamera->setCameraDistance();
|
||||
if(!mCamera->getPosition(orig, dest))
|
||||
if(!mCamera->isFirstPerson())
|
||||
{
|
||||
orig.z += mCamera->getHeight() * mRootNode->getScale().z;
|
||||
Ogre::Vector3 orig, dest;
|
||||
mCamera->getPosition(orig, dest);
|
||||
|
||||
btVector3 btOrig(orig.x, orig.y, orig.z);
|
||||
btVector3 btDest(dest.x, dest.y, dest.z);
|
||||
std::pair<bool, float> test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest);
|
||||
if (test.first)
|
||||
std::pair<bool,float> test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest);
|
||||
if(test.first)
|
||||
mCamera->setCameraDistance(test.second * orig.distance(dest), false, false);
|
||||
}
|
||||
|
||||
|
@ -343,11 +351,11 @@ void RenderingManager::update (float duration, bool paused)
|
|||
|
||||
applyFog(world->isUnderwater(player.getCell(), cam));
|
||||
|
||||
mCamera->update(duration, paused);
|
||||
|
||||
if(paused)
|
||||
return;
|
||||
|
||||
mCamera->update(duration);
|
||||
|
||||
mActors.update (duration);
|
||||
mObjects.update (duration);
|
||||
|
||||
|
@ -711,7 +719,7 @@ Compositors* RenderingManager::getCompositors()
|
|||
|
||||
void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings)
|
||||
{
|
||||
//bool changeRes = false;
|
||||
bool changeRes = false;
|
||||
bool rebuild = false; // rebuild static geometry (necessary after any material changes)
|
||||
for (Settings::CategorySettingVector::const_iterator it=settings.begin();
|
||||
it != settings.end(); ++it)
|
||||
|
@ -725,11 +733,11 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec
|
|||
if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior())
|
||||
configureFog(*MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell());
|
||||
}
|
||||
/*else if (it->first == "Video" && (
|
||||
else if (it->first == "Video" && (
|
||||
it->second == "resolution x"
|
||||
|| it->second == "resolution y"
|
||||
|| it->second == "fullscreen"))
|
||||
changeRes = true;*/
|
||||
changeRes = true;
|
||||
else if (it->second == "field of view" && it->first == "General")
|
||||
mRendering.setFov(Settings::Manager::getFloat("field of view", "General"));
|
||||
else if ((it->second == "texture filtering" && it->first == "General")
|
||||
|
@ -783,24 +791,31 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec
|
|||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if (changeRes)
|
||||
{
|
||||
unsigned int x = Settings::Manager::getInt("resolution x", "Video");
|
||||
unsigned int y = Settings::Manager::getInt("resolution y", "Video");
|
||||
bool fullscreen = Settings::Manager::getBool("fullscreen", "Video");
|
||||
|
||||
SDL_SetWindowFullscreen(mRendering.getSDLWindow(), 0);
|
||||
SDL_Window* window = mRendering.getSDLWindow();
|
||||
|
||||
if (x != mRendering.getWindow()->getWidth() || y != mRendering.getWindow()->getHeight())
|
||||
SDL_SetWindowFullscreen(window, 0);
|
||||
|
||||
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED)
|
||||
SDL_RestoreWindow(window);
|
||||
|
||||
if (fullscreen)
|
||||
{
|
||||
SDL_SetWindowSize(mRendering.getSDLWindow(), x, y);
|
||||
mRendering.getWindow()->resize(x, y);
|
||||
SDL_DisplayMode mode;
|
||||
SDL_GetWindowDisplayMode(window, &mode);
|
||||
mode.w = x;
|
||||
mode.h = y;
|
||||
SDL_SetWindowDisplayMode(window, &mode);
|
||||
SDL_SetWindowFullscreen(window, fullscreen);
|
||||
}
|
||||
|
||||
SDL_SetWindowFullscreen(mRendering.getSDLWindow(), Settings::Manager::getBool("fullscreen", "Video") ? SDL_WINDOW_FULLSCREEN : 0);
|
||||
//mRendering.getWindow()->setFullscreen(Settings::Manager::getBool("fullscreen", "Video"), x, y);
|
||||
else
|
||||
SDL_SetWindowSize(window, x, y);
|
||||
}
|
||||
*/
|
||||
|
||||
mWater->processChangedSettings(settings);
|
||||
|
||||
|
@ -818,24 +833,14 @@ void RenderingManager::setMenuTransparency(float val)
|
|||
tex->getBuffer()->unlock();
|
||||
}
|
||||
|
||||
void RenderingManager::windowResized(Ogre::RenderWindow* rw)
|
||||
void RenderingManager::windowResized(int x, int y)
|
||||
{
|
||||
Settings::Manager::setInt("resolution x", "Video", rw->getWidth());
|
||||
Settings::Manager::setInt("resolution y", "Video", rw->getHeight());
|
||||
|
||||
mRendering.adjustViewport();
|
||||
mCompositors->recreate();
|
||||
|
||||
mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight());
|
||||
mVideoPlayer->setResolution (x, y);
|
||||
|
||||
const Settings::CategorySettingVector& changed = Settings::Manager::apply();
|
||||
MWBase::Environment::get().getInputManager()->processChangedSettings(changed);
|
||||
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);
|
||||
}
|
||||
|
||||
void RenderingManager::windowClosed(Ogre::RenderWindow* rw)
|
||||
{
|
||||
Ogre::Root::getSingleton ().queueEndRendering ();
|
||||
MWBase::Environment::get().getWindowManager()->windowResized(x,y);
|
||||
}
|
||||
|
||||
void RenderingManager::applyCompositors()
|
||||
|
@ -894,6 +899,22 @@ bool RenderingManager::vanityRotateCamera(const float *rot)
|
|||
return true;
|
||||
}
|
||||
|
||||
void RenderingManager::setCameraDistance(float dist, bool adjust, bool override)
|
||||
{
|
||||
if(!mCamera->isVanityOrPreviewModeEnabled() && !mCamera->isFirstPerson())
|
||||
{
|
||||
if(mCamera->isNearest() && dist > 0.f)
|
||||
mCamera->toggleViewMode();
|
||||
else
|
||||
mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override);
|
||||
}
|
||||
else if(mCamera->isFirstPerson() && dist < 0.f)
|
||||
{
|
||||
mCamera->toggleViewMode();
|
||||
mCamera->setCameraDistance(0.f, false, override);
|
||||
}
|
||||
}
|
||||
|
||||
void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y)
|
||||
{
|
||||
return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y);
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace MWRender
|
|||
class VideoPlayer;
|
||||
class Animation;
|
||||
|
||||
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener
|
||||
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
|
||||
{
|
||||
private:
|
||||
virtual MWRender::Objects& getObjects();
|
||||
|
@ -86,6 +86,7 @@ public:
|
|||
void resetCamera();
|
||||
|
||||
bool vanityRotateCamera(const float *rot);
|
||||
void setCameraDistance(float dist, bool adjust = false, bool override = true);
|
||||
|
||||
void setupPlayer(const MWWorld::Ptr &ptr);
|
||||
void renderPlayer(const MWWorld::Ptr &ptr);
|
||||
|
@ -127,6 +128,9 @@ public:
|
|||
/// \param cur Object reference in new cell
|
||||
void updateObjectCell(const MWWorld::Ptr &old, const MWWorld::Ptr &cur);
|
||||
|
||||
/// Specifies an updated Ptr object for the player (used on cell change).
|
||||
void updatePlayerPtr(const MWWorld::Ptr &ptr);
|
||||
|
||||
void update (float duration, bool paused);
|
||||
|
||||
void setAmbientColour(const Ogre::ColourValue& colour);
|
||||
|
@ -200,8 +204,7 @@ public:
|
|||
void frameStarted(float dt);
|
||||
|
||||
protected:
|
||||
virtual void windowResized(Ogre::RenderWindow* rw);
|
||||
virtual void windowClosed(Ogre::RenderWindow* rw);
|
||||
virtual void windowResized(int x, int y);
|
||||
|
||||
private:
|
||||
sh::Factory* mFactory;
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue