1
0
Fork 0
mirror of https://github.com/OpenMW/openmw.git synced 2025-05-11 11:41:26 +00:00

Merge branch 'master' of https://github.com/zinnschlag/openmw.git into MeleeCombat2

This commit is contained in:
gus 2013-07-31 10:04:50 +02:00
commit b248d85b84
140 changed files with 3690 additions and 1151 deletions

View file

@ -19,7 +19,7 @@ include (OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) set (OPENMW_VERSION_MAJOR 0)
set (OPENMW_VERSION_MINOR 24) set (OPENMW_VERSION_MINOR 25)
set (OPENMW_VERSION_RELEASE 0) set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")

View file

@ -106,10 +106,10 @@ std::string ruleString(ESM::DialInfo::SelectStruct ss)
{ {
case '0': oper_str = "=="; break; case '0': oper_str = "=="; break;
case '1': oper_str = "!="; break; case '1': oper_str = "!="; break;
case '2': oper_str = "< "; break; case '2': oper_str = "> "; break;
case '3': oper_str = "<="; break; case '3': oper_str = ">="; break;
case '4': oper_str = "> "; break; case '4': oper_str = "< "; break;
case '5': oper_str = ">="; break; case '5': oper_str = "<="; break;
} }
std::ostringstream stream; std::ostringstream stream;

View file

@ -57,13 +57,14 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world 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 opencs_units_noqt (view/world
dialoguesubview subviews dialoguesubview subviews
enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate
scripthighlighter scripthighlighter idvalidator
) )

View file

@ -34,7 +34,7 @@ namespace CSMSettings
bool updateItem (const QString &value); bool updateItem (const QString &value);
bool updateItem (int valueListIndex); bool updateItem (int valueListIndex);
/// retroeve list of valid values for setting /// retrieve list of valid values for setting
inline QStringList *getValueList() { return mValueList; } inline QStringList *getValueList() { return mValueList; }
/// write list of valid values for setting /// write list of valid values for setting

View file

@ -14,7 +14,7 @@ namespace CSMSettings
typedef QList<SettingContainer *> SettingList; typedef QList<SettingContainer *> SettingList;
typedef QMap<QString, SettingContainer *> SettingMap; typedef QMap<QString, SettingContainer *> SettingMap;
typedef QMap<QString, SettingMap *> SectionMap; typedef QMap<QString, SettingMap *> SectionMap;
struct QStringPair struct QStringPair
{ {

View file

@ -42,6 +42,33 @@ CSMSettings::UserSettings::UserSettings()
mReadOnlyMessage = QObject::tr("<br><b>Could not open file for reading</b><br><br> \ 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>"); 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() 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; return mSectionSettings;
} }
const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString &sectionName) const
{
return getValidSettings(sectionName);
}
bool CSMSettings::UserSettings::loadFromFile(const QString &filePath) bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
{ {
if (filePath.isEmpty()) if (filePath.isEmpty())
return false; return false;
mSectionSettings.clear(); SectionMap loadedSettings;
QTextStream *stream = openFileStream (filePath, true); 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 //add the previous section's settings to the member map
if (settings) if (settings)
mSectionSettings.insert(section, settings); loadedSettings.insert(section, settings);
//save new section and create a new list //save new section and create a new list
section = sectionRe.cap(1); 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(); stream->device()->close();
delete stream; delete stream;
stream = 0; stream = 0;
} }
mergeMap (loadedSettings);
return success; return success;
} }
void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap &sectionSettings)
{
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) void CSMSettings::UserSettings::loadSettings (const QString &fileName)
{ {
mSectionSettings.clear();
//global //global
QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName; QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName;
bool globalOk = loadFromFile(globalFilePath); bool globalOk = loadFromFile(globalFilePath);
@ -207,10 +269,11 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName)
void CSMSettings::UserSettings::updateSettings (const QString &sectionName, const QString &settingName) void CSMSettings::UserSettings::updateSettings (const QString &sectionName, 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()) if (settingName.isEmpty())
{ {
@ -229,15 +292,12 @@ void CSMSettings::UserSettings::updateSettings (const QString &sectionName, cons
QString CSMSettings::UserSettings::getSetting (const QString &section, const QString &setting) const QString CSMSettings::UserSettings::getSetting (const QString &section, const QString &setting) const
{ {
SettingMap *settings = getValidSettings(section);
QString retVal = ""; QString retVal = "";
if (mSectionSettings.find(section) != mSectionSettings.end()) if (settings->find(setting) != settings->end())
{ retVal = settings->value(setting)->getValue();
CSMSettings::SettingMap *settings = mSectionSettings.value(section);
if (settings->find(setting) != settings->end())
retVal = settings->value(setting)->getValue();
}
return retVal; return retVal;
} }
@ -263,3 +323,33 @@ void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message,
msgBox.exec(); msgBox.exec();
} }
CSMSettings::SettingMap *
CSMSettings::UserSettings::getValidSettings (const QString &sectionName) 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;
}

View file

@ -27,6 +27,8 @@ namespace CSMSettings {
Q_OBJECT Q_OBJECT
SectionMap mSectionSettings; SectionMap mSectionSettings;
SectionMap mEditorSettingDefaults;
static UserSettings *mUserSettingsInstance; static UserSettings *mUserSettingsInstance;
QString mUserFilePath; QString mUserFilePath;
Files::ConfigurationManager mCfgMgr; Files::ConfigurationManager mCfgMgr;
@ -58,7 +60,9 @@ namespace CSMSettings {
void loadSettings (const QString &fileName); void loadSettings (const QString &fileName);
/// Returns the entire map of settings across all sections /// Returns the entire map of settings across all sections
const SectionMap &getSettings () const; const SectionMap &getSectionMap () const;
const SettingMap *getSettings (const QString &sectionName) const;
/// Retrieves the value as a QString of the specified setting in the specified section /// Retrieves the value as a QString of the specified setting in the specified section
QString getSetting(const QString &section, const QString &setting) const; QString getSetting(const QString &section, const QString &setting) const;
@ -72,8 +76,15 @@ namespace CSMSettings {
/// Parses a setting file specified in filePath from the provided text stream. /// Parses a setting file specified in filePath from the provided text stream.
bool loadFromFile (const QString &filePath = ""); bool loadFromFile (const QString &filePath = "");
/// merge the passed map into mSectionSettings
void mergeMap (const SectionMap &);
void displayFileErrorMessage(const QString &message, bool isReadOnly); void displayFileErrorMessage(const QString &message, bool isReadOnly);
void buildEditorSettingDefaults();
SettingMap *getValidSettings (const QString &sectionName) const;
signals: signals:
void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue); void signalUpdateEditorSetting (const QString &settingName, const QString &settingValue);

View file

@ -3,7 +3,7 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include "idtableproxymodel.hpp" #include "idtable.hpp"
#include "idtable.hpp" #include "idtable.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
@ -25,15 +25,28 @@ void CSMWorld::ModifyCommand::undo()
mModel.setData (mIndex, mOld); mModel.setData (mIndex, mOld);
} }
CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent) CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id) : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
{ {
setText (("Create record " + id).c_str()); 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() 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() void CSMWorld::CreateCommand::undo()

View file

@ -4,17 +4,20 @@
#include "record.hpp" #include "record.hpp"
#include <string> #include <string>
#include <map>
#include <QVariant> #include <QVariant>
#include <QUndoCommand> #include <QUndoCommand>
#include <QModelIndex> #include <QModelIndex>
#include "universalid.hpp"
class QModelIndex; class QModelIndex;
class QAbstractItemModel; class QAbstractItemModel;
namespace CSMWorld namespace CSMWorld
{ {
class IdTableProxyModel; class IdTable;
class IdTable; class IdTable;
class RecordBase; class RecordBase;
@ -37,12 +40,18 @@ namespace CSMWorld
class CreateCommand : public QUndoCommand class CreateCommand : public QUndoCommand
{ {
IdTableProxyModel& mModel; IdTable& mModel;
std::string mId; std::string mId;
UniversalId::Type mType;
std::map<int, QVariant> mValues;
public: 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(); virtual void redo();

View file

@ -300,6 +300,16 @@ CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables()
return mReferenceables; 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) QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{ {
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType()); std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
@ -394,4 +404,22 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
reader.skipRecord(); reader.skipRecord();
} }
} }
} }
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;
}

View file

@ -112,6 +112,10 @@ namespace CSMWorld
RefIdCollection& getReferenceables(); RefIdCollection& getReferenceables();
const RefCollection& getReferences() const;
RefCollection& getReferences();
QAbstractItemModel *getTableModel (const UniversalId& id); QAbstractItemModel *getTableModel (const UniversalId& id);
///< If no table model is available for \a id, an exception is thrown. ///< 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); void loadFile (const boost::filesystem::path& path, bool base);
///< Merging content of a file into base or modified. ///< Merging content of a file into base or modified.
bool hasId (const std::string& id) const;
}; };
} }

View file

@ -116,13 +116,13 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
return QModelIndex(); 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(); int index = mIdCollection->getAppendIndex();
beginInsertRows (QModelIndex(), index, index); beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendBlankRecord (id); mIdCollection->appendBlankRecord (id, type);
endInsertRows(); endInsertRows();
} }

View file

@ -3,6 +3,8 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include "universalid.hpp"
namespace CSMWorld namespace CSMWorld
{ {
class CollectionBase; class CollectionBase;
@ -44,7 +46,8 @@ namespace CSMWorld
virtual QModelIndex parent (const QModelIndex& index) const; 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; QModelIndex getModelIndex (const std::string& id, int column) const;

View file

@ -7,11 +7,6 @@ CSMWorld::IdTableProxyModel::IdTableProxyModel (QObject *parent)
: QSortFilterProxyModel (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 QModelIndex CSMWorld::IdTableProxyModel::getModelIndex (const std::string& id, int column) const
{ {
return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column)); return mapFromSource (dynamic_cast<IdTable&> (*sourceModel()).getModelIndex (id, column));

View file

@ -15,8 +15,6 @@ namespace CSMWorld
IdTableProxyModel (QObject *parent = 0); IdTableProxyModel (QObject *parent = 0);
virtual void addRecord (const std::string& id);
virtual QModelIndex getModelIndex (const std::string& id, int column) const; virtual QModelIndex getModelIndex (const std::string& id, int column) const;
}; };
} }

View file

@ -21,10 +21,7 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
while (cell2.getNextRef (reader, ref)) while (cell2.getNextRef (reader, ref))
{ {
/// \todo handle deleted and moved references /// \todo handle deleted and moved references
std::ostringstream stream; ref.load (reader, cell2, getNewId());
stream << "ref#" << mNextId++;
ref.load (reader, cell2, stream.str());
Record<CellRef> record2; Record<CellRef> record2;
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly; record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
@ -34,4 +31,11 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
} }
mCells.setRecord (cellIndex, cell); mCells.setRecord (cellIndex, cell);
}
std::string CSMWorld::RefCollection::getNewId()
{
std::ostringstream stream;
stream << "ref#" << mNextId++;
return stream.str();
} }

View file

@ -21,6 +21,8 @@ namespace CSMWorld
void load (ESM::ESMReader& reader, int cellIndex, bool base); void load (ESM::ESMReader& reader, int cellIndex, bool base);
///< Load a sequence of references. ///< Load a sequence of references.
std::string getNewId();
}; };
} }

View file

@ -12,81 +12,82 @@ namespace
CSMWorld::UniversalId::Class mClass; CSMWorld::UniversalId::Class mClass;
CSMWorld::UniversalId::Type mType; CSMWorld::UniversalId::Type mType;
const char *mName; const char *mName;
const char *mIcon;
}; };
static const TypeData sNoArg[] = static const TypeData sNoArg[] =
{ {
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Skills, "Skills", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Classes, "Classes", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Factions, "Factions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Races, "Races", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Sounds, "Sounds", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Scripts, "Scripts", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Regions, "Regions", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Birthsigns, "Birthsigns", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Spells, "Spells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Cells, "Cells", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Referenceables,
"Referenceables" }, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References,
"References" }, "References", 0 },
{ CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, { 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[] = static const TypeData sIdArg[] =
{ {
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Activator, "Activator", ":./activator.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Potion, "Potion", ":./potion.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Apparatus, "Apparatus", ":./apparatus.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Armor, "Armor", ":./armor.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Book, "Book", ":./book.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Clothing, "Clothing", ":./clothing.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Container, "Container", ":./container.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Creature, "Creature", ":./creature.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Door, "Door", ":./door.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Ingredient, "Ingredient", ":./ingredient.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_CreatureLevelledList,
"Creature Levelled List" }, "Creature Levelled List", ":./creature.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_ItemLevelledList,
"Item Levelled List" }, "Item Levelled List", ":./leveled-item.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Light, "Light", ":./light.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Lockpick, "Lockpick", ":./lockpick.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Miscellaneous,
"Miscellaneous" }, "Miscellaneous", ":./miscellaneous.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Npc, "NPC", ":./npc.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Probe, "Probe", ":./probe.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Repair, "Repair", ":./repair.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Reference, "Reference" }, { 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[] = 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); static const unsigned int IDARG_SIZE = sizeof (sIdArg) / sizeof (TypeData);
@ -268,6 +269,28 @@ std::string CSMWorld::UniversalId::toString() const
return stream.str(); 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) std::pair<int, const char *> CSMWorld::UniversalId::getIdArgPair (unsigned int index)
{ {

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <iosfwd> #include <iosfwd>
#include <vector>
#include <QMetaType> #include <QMetaType>
@ -14,13 +15,14 @@ namespace CSMWorld
enum Class enum Class
{ {
Class_None = 0, Class_None = 0,
Class_Record, Class_Record,
Class_SubRecord, Class_RefRecord, // referenceable record
Class_RecordList, Class_SubRecord,
Class_Collection, // multiple types of records combined Class_RecordList,
Class_Transient, // not part of the world data or the project data Class_Collection, // multiple types of records combined
Class_NonRecord // record like data that is not part of the world Class_Transient, // not part of the world data or the project data
Class_NonRecord // record like data that is not part of the world
}; };
enum ArgumentType enum ArgumentType
@ -126,6 +128,11 @@ namespace CSMWorld
std::string toString() const; 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 std::pair<int, const char *> getIdArgPair (unsigned int index);
static unsigned int getIdArgSize (); static unsigned int getIdArgSize ();
}; };

View file

@ -1,15 +1,10 @@
#include "subview.hpp" #include "subview.hpp"
CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id) CSVDoc::SubView::SubView (const CSMWorld::UniversalId& id) : mUniversalId (id)
{ {
/// \todo add a button to the title bar that clones this sub view /// \todo add a button to the title bar that clones this sub view
setWindowTitle (mUniversalId.toString().c_str()); setWindowTitle (mUniversalId.toString().c_str());
/// \todo remove (for testing only)
setMinimumWidth (100);
setMinimumHeight (60);
} }
CSMWorld::UniversalId CSVDoc::SubView::getUniversalId() const 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::updateEditorSetting (const QString &settingName, const QString &settingValue)
{ {
} }
void CSVDoc::SubView::setStatusBar (bool show) {}

View file

@ -37,6 +37,9 @@ namespace CSVDoc
virtual void setEditLock (bool locked) = 0; virtual void setEditLock (bool locked) = 0;
virtual void updateEditorSetting (const QString &, const QString &); virtual void updateEditorSetting (const QString &, const QString &);
virtual void setStatusBar (bool show);
///< Default implementation: ignored
signals: signals:
void focusId (const CSMWorld::UniversalId& universalId); void focusId (const CSMWorld::UniversalId& universalId);

View file

@ -22,28 +22,20 @@ namespace CSVDoc
return new SubViewT (id, document); return new SubViewT (id, document);
} }
template<class SubViewT>
class SubViewFactoryWithCreateFlag : public SubViewFactoryBase template<class SubViewT, class CreatorFactoryT>
class SubViewFactoryWithCreator : public SubViewFactoryBase
{ {
bool mCreateAndDelete;
public: public:
SubViewFactoryWithCreateFlag (bool createAndDelete);
virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document); virtual CSVDoc::SubView *makeSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document);
}; };
template<class SubViewT> template<class SubViewT, class CreatorFactoryT>
SubViewFactoryWithCreateFlag<SubViewT>::SubViewFactoryWithCreateFlag (bool createAndDelete) CSVDoc::SubView *SubViewFactoryWithCreator<SubViewT, CreatorFactoryT>::makeSubView (
: mCreateAndDelete (createAndDelete) const CSMWorld::UniversalId& id, CSMDoc::Document& document)
{}
template<class SubViewT>
CSVDoc::SubView *SubViewFactoryWithCreateFlag<SubViewT>::makeSubView (const CSMWorld::UniversalId& id,
CSMDoc::Document& document)
{ {
return new SubViewT (id, document, mCreateAndDelete); return new SubViewT (id, document, CreatorFactoryT());
} }
} }

View file

@ -78,56 +78,21 @@ void CSVDoc::View::setupViewMenu()
QAction *newWindow = new QAction (tr ("&New View"), this); QAction *newWindow = new QAction (tr ("&New View"), this);
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
view->addAction (newWindow); 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() void CSVDoc::View::setupWorldMenu()
{ {
QMenu *world = menuBar()->addMenu (tr ("&World")); 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); QAction *regions = new QAction (tr ("Regions"), this);
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView())); connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
world->addAction (regions); 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); QAction *cells = new QAction (tr ("Cells"), this);
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView())); connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
world->addAction (cells); world->addAction (cells);
@ -140,17 +105,71 @@ void CSVDoc::View::setupWorldMenu()
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addAction (references); world->addAction (references);
world->addSeparator(); // items that don't represent single record lists follow here
QAction *regionMap = new QAction (tr ("Region Map"), this); QAction *regionMap = new QAction (tr ("Region Map"), this);
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
world->addAction (regionMap); 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() void CSVDoc::View::setupUi()
{ {
setupFileMenu(); setupFileMenu();
setupEditMenu(); setupEditMenu();
setupViewMenu(); setupViewMenu();
setupWorldMenu(); setupWorldMenu();
setupMechanicsMenu();
setupAssetsMenu();
} }
void CSVDoc::View::updateTitle() void CSVDoc::View::updateTitle()
@ -189,10 +208,7 @@ CSVDoc::View::View (ViewManager& viewManager, CSMDoc::Document *document, int to
QString width = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Width")); QString width = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Width"));
QString height = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Height")); QString height = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Height"));
if(width==QString() || height==QString()) resize (width.toInt(), height.toInt());
resize(800, 600);
else
resize (width.toInt(), height.toInt());
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks); 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) /// \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); SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
view->setStatusBar (mShowStatusBar->isChecked());
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, 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") else if (settingName == "Height")
resizeViewHeight (settingValue.toInt()); 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);
}
}

View file

@ -38,6 +38,7 @@ namespace CSVDoc
QAction *mRedo; QAction *mRedo;
QAction *mSave; QAction *mSave;
QAction *mVerify; QAction *mVerify;
QAction *mShowStatusBar;
std::vector<QAction *> mEditingActions; std::vector<QAction *> mEditingActions;
Operations *mOperations; Operations *mOperations;
SubViewFactoryManager mSubViewFactory; SubViewFactoryManager mSubViewFactory;
@ -60,6 +61,10 @@ namespace CSVDoc
void setupWorldMenu(); void setupWorldMenu();
void setupMechanicsMenu();
void setupAssetsMenu();
void setupUi(); void setupUi();
void updateTitle(); void updateTitle();
@ -154,6 +159,8 @@ namespace CSVDoc
void addRegionMapSubView(); void addRegionMapSubView();
void showUserSettings(); void showUserSettings();
void toggleShowStatusBar (bool show);
}; };
} }

View file

@ -12,7 +12,7 @@ CSVSettings::GroupBlockDef *CSVSettings::DataDisplayFormatPage::setupDataDisplay
{ {
GroupBlockDef *statusBlock = new GroupBlockDef(QString(title)); 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"); *(statusItem->valueList) << QString("Icon and Text") << QString("Icon Only") << QString("Text Only");
WidgetDef statusWidget (Widget_RadioButton); WidgetDef statusWidget (Widget_RadioButton);

View file

@ -15,7 +15,7 @@ namespace CSVSettings
ItemBlock (QWidget* parent = 0); ItemBlock (QWidget* parent = 0);
/// pure virtual function not implemneted /// pure virtual function not implemented
bool updateSettings (const CSMSettings::SettingMap &settings) { return false; } bool updateSettings (const CSMSettings::SettingMap &settings) { return false; }
CSMSettings::SettingList *getSettings (); CSMSettings::SettingList *getSettings ();

View file

@ -44,7 +44,6 @@ void CSVSettings::UserSettingsDialog::closeEvent (QCloseEvent *event)
void CSVSettings::UserSettingsDialog::setWidgetStates () void CSVSettings::UserSettingsDialog::setWidgetStates ()
{ {
CSMSettings::UserSettings::instance().loadSettings("opencs.cfg"); CSMSettings::UserSettings::instance().loadSettings("opencs.cfg");
const CSMSettings::SectionMap &sectionSettings = CSMSettings::UserSettings::instance().getSettings();
//iterate the tabWidget's pages (sections) //iterate the tabWidget's pages (sections)
for (int i = 0; i < mStackedWidget->count(); i++) for (int i = 0; i < mStackedWidget->count(); i++)
@ -53,12 +52,9 @@ void CSVSettings::UserSettingsDialog::setWidgetStates ()
//and update widget //and update widget
QString pageName = mStackedWidget->widget(i)->objectName(); QString pageName = mStackedWidget->widget(i)->objectName();
if (sectionSettings.find(pageName) != sectionSettings.end()) const CSMSettings::SettingMap *settings = CSMSettings::UserSettings::instance().getSettings(pageName);
{ AbstractPage &page = getAbstractPage (i);
CSMSettings::SettingMap *settings = sectionSettings.value(pageName); page.initializeWidgets(*settings);
AbstractPage &page = getAbstractPage (i);
page.initializeWidgets(*settings);
}
} }
} }

View file

@ -19,7 +19,7 @@
#include "../../view/settings/abstractblock.hpp" #include "../../view/settings/abstractblock.hpp"
CSVSettings::WindowPage::WindowPage(QWidget *parent): CSVSettings::WindowPage::WindowPage(QWidget *parent):
AbstractPage("Window", parent) AbstractPage("Window Size", parent)
{ {
// Hacks to get the stylesheet look properly // Hacks to get the stylesheet look properly
#ifdef Q_OS_MAC #ifdef Q_OS_MAC
@ -82,7 +82,7 @@ CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildCustomWindowSize()
CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildWindowSizeToggle() CSVSettings::GroupBlockDef *CSVSettings::WindowPage::buildWindowSizeToggle()
{ {
GroupBlockDef *block = new GroupBlockDef ("Window Size"); GroupBlockDef *block = new GroupBlockDef (objectName());
// window size toggle // window size toggle
block->captions << "Pre-Defined" << "Custom"; block->captions << "Pre-Defined" << "Custom";

View 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();
}

View 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

View 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;
}

View 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

View 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);
}
}

View 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

View 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;
}

View 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

View file

@ -16,7 +16,7 @@ CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate (
return new RecordStatusDelegate (mValues, mIcons, undoStack, parent); 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") if (settingName == "Record Status Display")
{ {
@ -28,7 +28,11 @@ void CSVWorld::RecordStatusDelegate::updateEditorSetting (const QString &setting
else if (settingValue == "Text Only") else if (settingValue == "Text Only")
mDisplayMode = Mode_TextOnly; mDisplayMode = Mode_TextOnly;
return true;
} }
return false;
} }
CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory()

View file

@ -21,7 +21,7 @@ namespace CSVWorld
const IconList& icons, const IconList& icons,
QUndoStack& undoStack, QObject *parent = 0); QUndoStack& undoStack, QObject *parent = 0);
void updateEditorSetting (const QString &settingName, const QString &settingValue); virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
}; };

View 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();
}

View 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

View 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();
}

View 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

View file

@ -27,31 +27,20 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto
{ {
UidTypeList list; UidTypeList list;
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Activator, ":./activator.png")); std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Potion, ":./potion.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Apparatus, ":./apparatus.png")); for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Armor, ":./armor.png")); iter!=types.end(); ++iter)
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Book, ":./book.png")); {
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Clothing, ":./clothing.png")); CSMWorld::UniversalId id (*iter, "");
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 (id.getType(), id.getIcon().c_str()));
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"));
return list; 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") if (settingName == "Referenceable ID Type Display")
{ {
@ -63,5 +52,9 @@ void CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingNam
else if (settingValue == "Text Only") else if (settingValue == "Text Only")
mDisplayMode = Mode_TextOnly; mDisplayMode = Mode_TextOnly;
return true;
} }
return false;
} }

View file

@ -13,7 +13,7 @@ namespace CSVWorld
public: public:
RefIdTypeDelegate (const ValueList &mValues, const IconList &icons, QUndoStack& undoStack, QObject *parent); 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);
}; };

View file

@ -7,14 +7,20 @@
#include "dialoguesubview.hpp" #include "dialoguesubview.hpp"
#include "scriptsubview.hpp" #include "scriptsubview.hpp"
#include "regionmapsubview.hpp" #include "regionmapsubview.hpp"
#include "genericcreator.hpp"
#include "cellcreator.hpp"
#include "referenceablecreator.hpp"
#include "referencecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) 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, manager.add (CSMWorld::UniversalId::Type_Gmsts,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false)); new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Skills, manager.add (CSMWorld::UniversalId::Type_Skills,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false)); new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
static const CSMWorld::UniversalId::Type sTableTypes[] = static const CSMWorld::UniversalId::Type sTableTypes[] =
{ {
@ -27,20 +33,26 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
CSMWorld::UniversalId::Type_Regions, CSMWorld::UniversalId::Type_Regions,
CSMWorld::UniversalId::Type_Birthsigns, CSMWorld::UniversalId::Type_Birthsigns,
CSMWorld::UniversalId::Type_Spells, CSMWorld::UniversalId::Type_Spells,
CSMWorld::UniversalId::Type_Cells,
CSMWorld::UniversalId::Type_Referenceables,
CSMWorld::UniversalId::Type_References,
CSMWorld::UniversalId::Type_None // end marker CSMWorld::UniversalId::Type_None // end marker
}; };
for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i) 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>); 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_RegionMap, new CSVDoc::SubViewFactory<RegionMapSubView>);
// manager.add (CSMWorld::UniversalId::Type_Global,
// new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
} }

View file

@ -26,6 +26,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (!mEditLock) if (!mEditLock)
{ {
if (selectedRows.size()==1)
menu.addAction (mEditAction);
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
@ -41,19 +44,30 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); /// \todo Do not use hardcoded column numbers
std::vector<std::string> revertableIds; 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();
CSMWorld::RecordBase::State state = for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt()); ++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
if (state!=CSMWorld::RecordBase::State_BaseOnly) CSMWorld::RecordBase::State state =
revertableIds.push_back (id); 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; return revertableIds;
@ -61,19 +75,30 @@ std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); /// \todo Do not use hardcoded column numbers
std::vector<std::string> deletableIds; 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();
CSMWorld::RecordBase::State state = for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt()); ++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
if (state!=CSMWorld::RecordBase::State_Deleted) CSMWorld::RecordBase::State state =
deletableIds.push_back (id); 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; return deletableIds;
@ -116,12 +141,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
hideColumn (i); 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) if (createAndDelete)
{ {
mCreateAction = new QAction (tr ("Add Record"), this); mCreateAction = new QAction (tr ("Add Record"), this);
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
addAction (mCreateAction); addAction (mCreateAction);
} }
@ -132,6 +159,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
mDeleteAction = new QAction (tr ("Delete Record"), this); mDeleteAction = new QAction (tr ("Delete Record"), this);
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
addAction (mDeleteAction); 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) 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()); 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() void CSVWorld::Table::revertRecord()
{ {
if (!mEditLock) 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); if (selectedRows.size()==1)
emit dataChanged(mModel->index(0,1), mModel->index(mModel->rowCount()-1, 1)); emit editRequest (selectedRows.begin()->row());
}
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));
} }
} }
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);
}

View file

@ -28,6 +28,7 @@ namespace CSVWorld
std::vector<CommandDelegate *> mDelegates; std::vector<CommandDelegate *> mDelegates;
QUndoStack& mUndoStack; QUndoStack& mUndoStack;
QAction *mEditAction;
QAction *mCreateAction; QAction *mCreateAction;
QAction *mRevertAction; QAction *mRevertAction;
QAction *mDeleteAction; QAction *mDeleteAction;
@ -55,13 +56,35 @@ namespace CSVWorld
void updateEditorSetting (const QString &settingName, const QString &settingValue); 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 revertRecord();
void deleteRecord(); void deleteRecord();
void editRecord();
public slots:
void tableSizeUpdate();
void selectionSizeUpdate();
void requestFocus (const std::string& id);
}; };
} }

View 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;
}

View 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

View file

@ -1,32 +1,68 @@
#include "tablesubview.hpp" #include "tablesubview.hpp"
#include <QVBoxLayout>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "table.hpp" #include "table.hpp"
#include "tablebottombox.hpp"
#include "creator.hpp"
CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
bool createAndDelete) const CreatorFactoryBase& creatorFactory)
: SubView (id) : 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) void CSVWorld::TableSubView::setEditLock (bool locked)
{ {
mTable->setEditLock (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) void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue)
{ {
mTable->updateEditorSetting(settingName, settingValue);
if ( (settingName == "Record Status Display") || settingName == "Referenceable ID Type Display" )
mTable->updateEditorSetting(settingName, settingValue);
} }
void CSVWorld::TableSubView::setStatusBar (bool show)
{
mBottom->setStatusBar (show);
}

View file

@ -13,22 +13,30 @@ namespace CSMDoc
namespace CSVWorld namespace CSVWorld
{ {
class Table; class Table;
class TableBottomBox;
class CreatorFactoryBase;
class TableSubView : public CSVDoc::SubView class TableSubView : public CSVDoc::SubView
{ {
Q_OBJECT Q_OBJECT
Table *mTable; Table *mTable;
TableBottomBox *mBottom;
public: 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); 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: private slots:
void rowActivated (const QModelIndex& index); void editRequest (int row);
}; };
} }

View file

@ -134,4 +134,10 @@ void CSVWorld::CommandDelegate::setEditLock (bool locked)
bool CSVWorld::CommandDelegate::isEditLocked() const bool CSVWorld::CommandDelegate::isEditLocked() const
{ {
return mEditLock; return mEditLock;
}
bool CSVWorld::CommandDelegate::updateEditorSetting (const QString &settingName,
const QString &settingValue)
{
return false;
} }

View file

@ -108,6 +108,9 @@ namespace CSVWorld
bool isEditLocked() const; bool isEditLocked() const;
virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
///< \return Does column require update?
private slots: private slots:
virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {} virtual void slotUpdateEditorSetting (const QString &settingName, const QString &settingValue) {}

View file

@ -40,6 +40,8 @@ namespace MWBase
virtual MWWorld::Ptr getActor() const = 0; virtual MWWorld::Ptr getActor() const = 0;
///< Return the actor the player is currently talking to. ///< 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 //calbacks for the GUI
virtual void keywordSelected (const std::string& keyword) = 0; virtual void keywordSelected (const std::string& keyword) = 0;
virtual void goodbyeSelected() = 0; virtual void goodbyeSelected() = 0;

View file

@ -46,9 +46,10 @@ namespace MWBase
enum PlayType { enum PlayType {
Play_TypeSfx = 1<<3, /* Normal SFX sound */ Play_TypeSfx = 1<<3, /* Normal SFX sound */
Play_TypeVoice = 1<<4, /* Voice sound */ Play_TypeVoice = 1<<4, /* Voice sound */
Play_TypeMusic = 1<<5, /* Music track */ Play_TypeFoot = 1<<5, /* Footstep sound */
Play_TypeMovie = 1<<6, /* Movie audio track */ Play_TypeMusic = 1<<6, /* Music track */
Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeMusic|Play_TypeMovie Play_TypeMovie = 1<<7, /* Movie audio track */
Play_TypeMask = Play_TypeSfx|Play_TypeVoice|Play_TypeFoot|Play_TypeMusic|Play_TypeMovie
}; };
private: private:
@ -102,11 +103,12 @@ namespace MWBase
///< Play a 2D audio track, using a custom decoder ///< Play a 2D audio track, using a custom decoder
virtual SoundPtr playSound(const std::string& soundId, float volume, float pitch, 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 ///< Play a sound, independently of 3D-position
virtual SoundPtr playSound3D(const MWWorld::Ptr &reference, const std::string& soundId, 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 ///< Play a sound from an object
virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0; virtual void stopSound3D(const MWWorld::Ptr &reference, const std::string& soundId) = 0;

View file

@ -215,6 +215,7 @@ namespace MWBase
virtual void removeStaticMessageBox() = 0; virtual void removeStaticMessageBox() = 0;
virtual void enterPressed () = 0; virtual void enterPressed () = 0;
virtual void activateKeyPressed () = 0;
virtual int readPressedButton() = 0; virtual int readPressedButton() = 0;
///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox) ///< 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 processChangedSettings(const Settings::CategorySettingVector& changed) = 0;
virtual void windowResized(int x, int y) = 0;
virtual void executeInConsole (const std::string& path) = 0; virtual void executeInConsole (const std::string& path) = 0;
virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total) = 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 changePointer (const std::string& name) = 0;
virtual void setEnemy (const MWWorld::Ptr& enemy) = 0;
virtual const Translation::Storage& getTranslationDataStorage() const = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0;
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;

View file

@ -201,6 +201,8 @@ namespace MWBase
virtual void setMoonColour (bool red) = 0; virtual void setMoonColour (bool red) = 0;
virtual void modRegion(const std::string &regionid, const std::vector<char> &chances) = 0;
virtual float getTimeScaleFactor() const = 0; virtual float getTimeScaleFactor() const = 0;
virtual void changeToInteriorCell (const std::string& cellName, virtual void changeToInteriorCell (const std::string& cellName,
@ -218,6 +220,10 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0; virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range ///< 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; virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
///< Adjust position after load to be on ground. Must be called after model load. ///< 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 togglePlayerLooking(bool enable) = 0;
virtual void changeVanityModeScale(float factor) = 0; virtual void changeVanityModeScale(float factor) = 0;
virtual bool vanityRotateCamera(float * rot) = 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 setupPlayer() = 0;
virtual void renderPlayer() = 0; virtual void renderPlayer() = 0;
@ -371,6 +378,12 @@ namespace MWBase
/// Find default position inside interior cell specified by name /// Find default position inside interior cell specified by name
/// \return false if interior with given name not exists, true otherwise /// \return false if interior with given name not exists, true otherwise
virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0; 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;
}; };
} }

View file

@ -46,7 +46,11 @@ namespace MWClass
{ {
std::auto_ptr<CustomData> data (new CustomData); 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 // store
ptr.getRefData().setCustomData (data.release()); ptr.getRefData().setCustomData (data.release());

View file

@ -11,6 +11,7 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp" #include "../mwworld/actiontalk.hpp"
@ -78,6 +79,8 @@ namespace MWClass
data->mCreatureStats.setLevel(ref->mBase->mData.mLevel); 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 (0, ref->mBase->mAiData.mHello);
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight); data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee); data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
@ -88,6 +91,10 @@ namespace MWClass
iter!=ref->mBase->mSpells.mList.end(); ++iter) iter!=ref->mBase->mSpells.mList.end(); ++iter)
data->mCreatureStats.getSpells().add (*iter); data->mCreatureStats.getSpells().add (*iter);
// inventory
data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr),
MWBase::Environment::get().getWorld()->getStore());
// store // store
ptr.getRefData().setCustomData (data.release()); ptr.getRefData().setCustomData (data.release());
} }
@ -148,6 +155,70 @@ namespace MWClass
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats; 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, boost::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const const MWWorld::Ptr& actor) const
{ {
@ -291,6 +362,33 @@ namespace MWClass
return ref->mBase->mPersistent; 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 MWWorld::Ptr
Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{ {
@ -300,6 +398,40 @@ namespace MWClass
return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell); 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::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
} }

View file

@ -12,6 +12,8 @@ namespace MWClass
virtual MWWorld::Ptr virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; 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 *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature; static const ESM::GameSetting *fMaxWalkSpeedCreature;
@ -40,6 +42,12 @@ namespace MWClass
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
///< Return creature stats ///< 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, virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
@ -69,6 +77,8 @@ namespace MWClass
virtual bool isPersistent (const MWWorld::Ptr& ptr) const; 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; virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement. ///< Return desired movement.

View file

@ -53,7 +53,9 @@ namespace MWClass
physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry); physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry);
if (!ref->mBase->mSound.empty()) 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 std::string Light::getModel(const MWWorld::Ptr &ptr) const

View file

@ -13,6 +13,8 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
@ -109,6 +111,23 @@ namespace
creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase() creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase()
+ static_cast<int>((level-1) * modifierSum+0.5), 100) ); + 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); autoCalculateAttributes(ref->mBase, data->mCreatureStats);
} }
data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello); data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight); data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee); data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
@ -206,6 +227,10 @@ namespace MWClass
iter!=ref->mBase->mSpells.mList.end(); ++iter) iter!=ref->mBase->mSpells.mList.end(); ++iter)
data->mCreatureStats.getSpells().add (*iter); data->mCreatureStats.getSpells().add (*iter);
// inventory
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr),
MWBase::Environment::get().getWorld()->getStore());
// store // store
ptr.getRefData().setCustomData (data.release()); ptr.getRefData().setCustomData (data.release());
} }
@ -283,6 +308,243 @@ namespace MWClass
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats; 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, boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const const MWWorld::Ptr& actor) const
{ {
@ -587,18 +849,19 @@ namespace MWClass
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
{ {
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
MWBase::Environment::get().getWorld()->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(); int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->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); MWWorld::ContainerStoreIterator it = invStore.getSlot(i);
if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())
@ -608,28 +871,27 @@ namespace MWClass
} }
else else
{ {
MWWorld::LiveCellRef<ESM::Armor> *ref = MWWorld::LiveCellRef<ESM::Armor> *ref = it->get<ESM::Armor>();
it->get<ESM::Armor>();
int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it); 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; ratings[i] = ref->mBase->mData.mArmor;
else else
ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; 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_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]
+ ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots] + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]
+ ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron] + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]
) * 0.1 ) * 0.1f
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet) + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet])
* 0.05 * 0.05f
+ shield; + shield;
} }
@ -663,6 +925,74 @@ namespace MWClass
return 0; 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 MWWorld::Ptr
Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{ {

View file

@ -68,6 +68,12 @@ namespace MWClass
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store ///< 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, virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation
@ -132,6 +138,8 @@ namespace MWClass
virtual bool isPersistent (const MWWorld::Ptr& ptr) const; virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
static void registerSelf(); static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;

View file

@ -22,6 +22,13 @@
namespace MWClass 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 void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
const std::string model = getModel(ptr); const std::string model = getModel(ptr);

View file

@ -12,6 +12,9 @@ namespace MWClass
public: 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; virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering ///< Add reference into a cell for rendering

View file

@ -25,6 +25,7 @@
#include "../mwbase/scriptmanager.hpp" #include "../mwbase/scriptmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
@ -557,6 +558,30 @@ namespace MWDialogue
return false; 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> ParseHyperText(const std::string& text)
{ {
std::vector<HyperTextToken> result; std::vector<HyperTextToken> result;

View file

@ -68,6 +68,8 @@ namespace MWDialogue
virtual bool checkServiceRefused (); virtual bool checkServiceRefused ();
virtual void say(const MWWorld::Ptr &actor, const std::string &topic) const;
//calbacks for the GUI //calbacks for the GUI
virtual void keywordSelected (const std::string& keyword); virtual void keywordSelected (const std::string& keyword);
virtual void goodbyeSelected(); virtual void goodbyeSelected();

View file

@ -120,7 +120,7 @@ namespace MWGui
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender) 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); MWWorld::ActionTake take(mBook);
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());

View file

@ -10,7 +10,10 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp" #include "../mwworld/fallback.hpp"
#include "../mwworld/player.hpp"
namespace namespace
{ {
@ -41,6 +44,14 @@ namespace
// Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz // Note: Order is taken from http://www.uesp.net/wiki/Morrowind:Class_Quiz
unsigned int points[3]; 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 namespace MWGui
@ -334,6 +345,8 @@ namespace MWGui
mCreationStage = CSE_ClassChosen; mCreationStage = CSE_ClassChosen;
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
} }
updatePlayerHealth();
} }
void CharacterCreation::onPickClassDialogBack() void CharacterCreation::onPickClassDialogBack()
@ -461,6 +474,8 @@ namespace MWGui
mCreationStage = CSE_RaceChosen; mCreationStage = CSE_RaceChosen;
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
} }
updatePlayerHealth();
} }
void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow) void CharacterCreation::onBirthSignDialogDone(WindowBase* parWindow)
@ -484,6 +499,8 @@ namespace MWGui
mCreationStage = CSE_BirthSignChosen; mCreationStage = CSE_BirthSignChosen;
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
} }
updatePlayerHealth();
} }
void CharacterCreation::onBirthSignDialogBack() void CharacterCreation::onBirthSignDialogBack()
@ -547,6 +564,8 @@ namespace MWGui
mCreationStage = CSE_ClassChosen; mCreationStage = CSE_ClassChosen;
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
} }
updatePlayerHealth();
} }
void CharacterCreation::onCreateClassDialogBack() void CharacterCreation::onCreateClassDialogBack()
@ -715,6 +734,8 @@ namespace MWGui
mCreationStage = CSE_ClassChosen; mCreationStage = CSE_ClassChosen;
MWBase::Environment::get().getWindowManager()->popGuiMode(); MWBase::Environment::get().getWindowManager()->popGuiMode();
} }
updatePlayerHealth();
} }
CharacterCreation::~CharacterCreation() CharacterCreation::~CharacterCreation()

View file

@ -49,11 +49,16 @@ namespace MWGui
mItemEdit->setCaption(boost::lexical_cast<std::string>(maxCount)); mItemEdit->setCaption(boost::lexical_cast<std::string>(maxCount));
} }
void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender) void CountDialog::cancel()
{ {
setVisible(false); setVisible(false);
} }
void CountDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
{
cancel();
}
void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender) void CountDialog::onOkButtonClicked(MyGUI::Widget* _sender)
{ {
eventOkClicked(NULL, mSlider->getScrollPosition()+1); eventOkClicked(NULL, mSlider->getScrollPosition()+1);

View file

@ -10,6 +10,7 @@ namespace MWGui
public: public:
CountDialog(); CountDialog();
void open(const std::string& item, const std::string& message, const int maxCount); void open(const std::string& item, const std::string& message, const int maxCount);
void cancel();
typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt; typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt;
@ -25,7 +26,7 @@ namespace MWGui
MyGUI::TextBox* mLabelText; MyGUI::TextBox* mLabelText;
MyGUI::Button* mOkButton; MyGUI::Button* mOkButton;
MyGUI::Button* mCancelButton; MyGUI::Button* mCancelButton;
void onCancelButtonClicked(MyGUI::Widget* _sender); void onCancelButtonClicked(MyGUI::Widget* _sender);
void onOkButtonClicked(MyGUI::Widget* _sender); void onOkButtonClicked(MyGUI::Widget* _sender);
void onEditTextChange(MyGUI::EditBox* _sender); void onEditTextChange(MyGUI::EditBox* _sender);

View file

@ -9,6 +9,8 @@
#include "../mwworld/player.hpp" #include "../mwworld/player.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "console.hpp" #include "console.hpp"
#include "spellicons.hpp" #include "spellicons.hpp"
@ -47,6 +49,7 @@ namespace MWGui
, mWeaponVisible(true) , mWeaponVisible(true)
, mSpellVisible(true) , mSpellVisible(true)
, mWorldMouseOver(false) , mWorldMouseOver(false)
, mEnemyHealthTimer(0)
{ {
setCoord(0,0, width, height); setCoord(0,0, width, height);
@ -55,6 +58,7 @@ namespace MWGui
getWidget(mHealth, "Health"); getWidget(mHealth, "Health");
getWidget(mMagicka, "Magicka"); getWidget(mMagicka, "Magicka");
getWidget(mStamina, "Stamina"); getWidget(mStamina, "Stamina");
getWidget(mEnemyHealth, "EnemyHealth");
mHealthManaStaminaBaseLeft = mHealthFrame->getLeft(); mHealthManaStaminaBaseLeft = mHealthFrame->getLeft();
MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame; MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame;
@ -320,6 +324,13 @@ namespace MWGui
mCellNameBox->setVisible(false); mCellNameBox->setVisible(false);
if (mWeaponSpellTimer < 0) if (mWeaponSpellTimer < 0)
mWeaponSpellBox->setVisible(false); 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) void HUD::onResChange(int width, int height)
@ -535,6 +546,25 @@ namespace MWGui
void HUD::update() void HUD::update()
{ {
mSpellIcons->updateWidgets(mEffectBox, true); 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);
} }
} }

View file

@ -46,12 +46,14 @@ namespace MWGui
void update(); void update();
void setEnemy(const MWWorld::Ptr& enemy);
private: private:
MyGUI::ProgressPtr mHealth, mMagicka, mStamina; MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth;
MyGUI::Widget* mHealthFrame; MyGUI::Widget* mHealthFrame;
MyGUI::Widget *mWeapBox, *mSpellBox; MyGUI::Widget *mWeapBox, *mSpellBox;
MyGUI::ImageBox *mWeapImage, *mSpellImage; MyGUI::ImageBox *mWeapImage, *mSpellImage;
MyGUI::ProgressPtr mWeapStatus, mSpellStatus; MyGUI::ProgressBar *mWeapStatus, *mSpellStatus;
MyGUI::Widget *mEffectBox, *mMinimapBox; MyGUI::Widget *mEffectBox, *mMinimapBox;
MyGUI::Button* mMinimapButton; MyGUI::Button* mMinimapButton;
MyGUI::ScrollView* mMinimap; MyGUI::ScrollView* mMinimap;
@ -89,6 +91,9 @@ namespace MWGui
SpellIcons* mSpellIcons; SpellIcons* mSpellIcons;
MWWorld::Ptr mEnemy;
float mEnemyHealthTimer;
void onWorldClicked(MyGUI::Widget* _sender); void onWorldClicked(MyGUI::Widget* _sender);
void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y); void onWorldMouseOver(MyGUI::Widget* _sender, int x, int y);
void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new); void onWorldMouseLostFocus(MyGUI::Widget* _sender, MyGUI::Widget* _new);

View file

@ -173,12 +173,7 @@ namespace MWGui
attribute.setBase(100); attribute.setBase(100);
} }
// "When you gain a level, in addition to increasing three primary attributes, your Health creatureStats.levelUp();
// 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);
pcStats.levelUp (); pcStats.levelUp ();
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup); MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup);

View file

@ -146,10 +146,10 @@ namespace MWGui
mMessageBoxSpeed = speed; mMessageBoxSpeed = speed;
} }
void MessageBoxManager::enterPressed () void MessageBoxManager::okayPressed ()
{ {
if(mInterMessageBoxe != NULL) if(mInterMessageBoxe != NULL)
mInterMessageBoxe->enterPressed(); mInterMessageBoxe->okayPressed();
} }
int MessageBoxManager::readPressedButton () 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}")); std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));

View file

@ -40,7 +40,7 @@ namespace MWGui
bool removeMessageBox (MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox);
void setMessageBoxSpeed (int speed); void setMessageBoxSpeed (int speed);
void enterPressed(); void okayPressed();
int readPressedButton (); int readPressedButton ();
typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int; typedef MyGUI::delegates::CMultiDelegate1<int> EventHandle_Int;
@ -82,7 +82,7 @@ namespace MWGui
{ {
public: public:
InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons); InteractiveMessageBox (MessageBoxManager& parMessageBoxManager, const std::string& message, const std::vector<std::string>& buttons);
void enterPressed (); void okayPressed ();
void mousePressed (MyGUI::Widget* _widget); void mousePressed (MyGUI::Widget* _widget);
int readPressedButton (); int readPressedButton ();

View file

@ -87,7 +87,7 @@ namespace MWGui
void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender) 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); MWWorld::ActionTake take(mScroll);
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer()); take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());

View file

@ -7,6 +7,8 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/math/common_factor_rt.hpp> #include <boost/math/common_factor_rt.hpp>
#include <SDL_video.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -172,16 +174,14 @@ namespace MWGui
mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
// fill resolution list // fill resolution list
Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); int screen = Settings::Manager::getInt("screen", "Video");
Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; int numDisplayModes = SDL_GetNumDisplayModes(screen);
std::vector < std::pair<int, int> > resolutions; std::vector < std::pair<int, int> > resolutions;
for (Ogre::StringVector::const_iterator it=videoModes.begin(); for (int i = 0; i < numDisplayModes; i++)
it!=videoModes.end(); ++it)
{ {
SDL_DisplayMode mode;
int resX, resY; SDL_GetDisplayMode(screen, i, &mode);
parseResolution (resX, resY, *it); resolutions.push_back(std::make_pair(mode.w, mode.h));
resolutions.push_back(std::make_pair(resX, resY));
} }
std::sort(resolutions.begin(), resolutions.end(), sortResolutions); std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
for (std::vector < std::pair<int, int> >::const_iterator it=resolutions.begin(); for (std::vector < std::pair<int, int> >::const_iterator it=resolutions.begin();
@ -272,15 +272,12 @@ namespace MWGui
if (index == MyGUI::ITEM_NONE) if (index == MyGUI::ITEM_NONE)
return; return;
/*
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
dialog->open("#{sNotifyMessage67}"); dialog->open("#{sNotifyMessage67}");
dialog->eventOkClicked.clear(); dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept);
dialog->eventCancelClicked.clear(); dialog->eventCancelClicked.clear();
dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel); dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel);
*/
onResolutionAccept();
} }
void SettingsWindow::onResolutionAccept() void SettingsWindow::onResolutionAccept()
@ -293,9 +290,6 @@ namespace MWGui
Settings::Manager::setInt("resolution y", "Video", resY); Settings::Manager::setInt("resolution y", "Video", resY);
apply(); apply();
MWBase::Environment::get().getWindowManager()->
messageBox("New resolution will be applied after a restart", std::vector<std::string>());
} }
void SettingsWindow::onResolutionCancel() void SettingsWindow::onResolutionCancel()
@ -364,8 +358,6 @@ namespace MWGui
{ {
Settings::Manager::setBool("fullscreen", "Video", newState); Settings::Manager::setBool("fullscreen", "Video", newState);
apply(); apply();
MWBase::Environment::get().getWindowManager()->
messageBox("Fullscreen will be applied after a restart", std::vector<std::string>());
} }
} }
else if (_sender == mVSyncButton) else if (_sender == mVSyncButton)

View file

@ -216,7 +216,13 @@ namespace MWGui
void WaitDialog::setCanRest (bool canRest) 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}"); mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}");
mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}"); mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}");

View file

@ -663,7 +663,13 @@ namespace MWGui
void WindowManager::enterPressed () void WindowManager::enterPressed ()
{ {
mMessageBoxManager->enterPressed(); mMessageBoxManager->okayPressed();
}
void WindowManager::activateKeyPressed ()
{
mMessageBoxManager->okayPressed();
mCountDialog->cancel();
} }
int WindowManager::readPressedButton () int WindowManager::readPressedButton ()
@ -877,49 +883,31 @@ namespace MWGui
setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI")); setUseHardwareCursors(Settings::Manager::getBool("hardware cursors", "GUI"));
//bool changeRes = false;
bool windowRecreated = false;
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); for (Settings::CategorySettingVector::const_iterator it = changed.begin();
it != changed.end(); ++it) it != changed.end(); ++it)
{ {
/*if (it->first == "Video" && ( if (it->first == "HUD" && it->second == "crosshair")
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")
mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD"); mCrosshairEnabled = Settings::Manager::getBool ("crosshair", "HUD");
else if (it->first == "GUI" && it->second == "subtitles") else if (it->first == "GUI" && it->second == "subtitles")
mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI"); mSubtitlesEnabled = Settings::Manager::getBool ("subtitles", "GUI");
} }
}
/* void WindowManager::windowResized(int x, int y)
if (changeRes) {
{ mHud->onResChange(x, y);
int x = Settings::Manager::getInt("resolution x", "Video"); mConsole->onResChange(x, y);
int y = Settings::Manager::getInt("resolution y", "Video"); mMenu->onResChange(x, y);
mHud->onResChange(x, y); mSettingsWindow->center();
mConsole->onResChange(x, y); mAlchemyWindow->center();
mMenu->onResChange(x, y); mScrollWindow->center();
mSettingsWindow->center(); mBookWindow->center();
mAlchemyWindow->center(); mQuickKeysMenu->center();
mScrollWindow->center(); mSpellBuyingWindow->center();
mBookWindow->center(); mLoadingScreen->onResChange (x,y);
mQuickKeysMenu->center(); mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y));
mSpellBuyingWindow->center(); mInputBlocker->setSize(MyGUI::IntSize(x,y));
mLoadingScreen->onResChange (x,y); mGuiManager->windowResized();
mDragAndDrop->mDragAndDropWidget->setSize(MyGUI::IntSize(x, y));
mInputBlocker->setSize(MyGUI::IntSize(x,y));
}
*/
if (windowRecreated)
{
mGuiManager->updateWindow (mRendering->getWindow ());
mLoadingScreen->updateWindow (mRendering->getWindow ());
}
} }
void WindowManager::pushGuiMode(GuiMode mode) void WindowManager::pushGuiMode(GuiMode mode)
@ -1318,4 +1306,9 @@ namespace MWGui
SDL_StopTextInput(); SDL_StopTextInput();
} }
void WindowManager::setEnemy(const MWWorld::Ptr &enemy)
{
mHud->setEnemy(enemy);
}
} }

View file

@ -204,6 +204,7 @@ namespace MWGui
virtual void staticMessageBox(const std::string& message); virtual void staticMessageBox(const std::string& message);
virtual void removeStaticMessageBox(); virtual void removeStaticMessageBox();
virtual void enterPressed (); 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 int readPressedButton (); ///< returns the index of the pressed button or -1 if no button was pressed (->MessageBoxmanager->InteractiveMessageBox)
virtual void onFrame (float frameDuration); virtual void onFrame (float frameDuration);
@ -225,6 +226,8 @@ namespace MWGui
virtual void processChangedSettings(const Settings::CategorySettingVector& changed); virtual void processChangedSettings(const Settings::CategorySettingVector& changed);
virtual void windowResized(int x, int y);
virtual void executeInConsole (const std::string& path); virtual void executeInConsole (const std::string& path);
virtual void setLoadingProgress (const std::string& stage, int depth, int current, int total); 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 changePointer (const std::string& name);
virtual void setEnemy (const MWWorld::Ptr& enemy);
virtual const Translation::Storage& getTranslationDataStorage() const; virtual const Translation::Storage& getTranslationDataStorage() const;
void onSoulgemDialogButtonPressed (int button); void onSoulgemDialogButtonPressed (int button);

View file

@ -204,13 +204,15 @@ namespace MWInput
case A_Activate: case A_Activate:
resetIdleTime(); resetIdleTime();
if (mWindows.getMode() == MWGui::GM_Container) { if (mWindows.isGuiMode())
toggleContainer (); {
} else if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { if (mWindows.getMode() == MWGui::GM_Container)
MWBase::Environment::get().getWindowManager()->enterPressed(); toggleContainer ();
} else { else
activate(); MWBase::Environment::get().getWindowManager()->activateKeyPressed();
} }
else
activate();
break; break;
case A_Journal: case A_Journal:
toggleJournal (); toggleJournal ();
@ -385,7 +387,7 @@ namespace MWInput
if (mControlSwitch["playerviewswitch"]) { if (mControlSwitch["playerviewswitch"]) {
// work around preview mode toggle when pressing Alt+Tab // 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 && if (mPreviewPOVDelay <= 0.5 &&
(mPreviewPOVDelay += dt) > 0.5) (mPreviewPOVDelay += dt) > 0.5)
{ {
@ -434,13 +436,9 @@ namespace MWInput
void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed)
{ {
bool changeRes = false;
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); for (Settings::CategorySettingVector::const_iterator it = changed.begin();
it != changed.end(); ++it) 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") if (it->first == "Input" && it->second == "invert y axis")
mInvertY = Settings::Manager::getBool("invert y axis", "Input"); mInvertY = Settings::Manager::getBool("invert y axis", "Input");
@ -451,9 +449,6 @@ namespace MWInput
mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); 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) bool InputManager::getControlSwitch (const std::string& sw)
@ -608,21 +603,27 @@ namespace MWInput
} }
if (arg.zrel) if (arg.zrel)
{
MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel); MWBase::Environment::get().getWorld()->changeVanityModeScale(arg.zrel);
MWBase::Environment::get().getWorld()->setCameraDistance(arg.zrel, true, true);
}
} }
return true; return true;
} }
bool InputManager::windowFocusChange(bool have_focus) void InputManager::windowFocusChange(bool have_focus)
{ {
return true;
} }
bool InputManager::windowVisibilityChange(bool visible) void InputManager::windowVisibilityChange(bool visible)
{ {
//TODO: Pause game? //TODO: Pause game?
return true; }
void InputManager::windowResized(int x, int y)
{
mOgre.windowResized(x,y);
} }
void InputManager::toggleMainMenu() void InputManager::toggleMainMenu()
@ -682,10 +683,8 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
bool gameMode = !mWindows.isGuiMode();
// Toggle between game mode and inventory mode // Toggle between game mode and inventory mode
if(gameMode) if(!mWindows.isGuiMode())
mWindows.pushGuiMode(MWGui::GM_Inventory); mWindows.pushGuiMode(MWGui::GM_Inventory);
else else
{ {
@ -702,9 +701,7 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
bool gameMode = !mWindows.isGuiMode(); if(mWindows.isGuiMode())
if(!gameMode)
{ {
if (mWindows.getMode() == MWGui::GM_Container) if (mWindows.getMode() == MWGui::GM_Container)
mWindows.popGuiMode(); mWindows.popGuiMode();
@ -714,17 +711,14 @@ namespace MWInput
} }
void InputManager::toggleConsole() void InputManager::toggleConsole()
{ {
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
bool gameMode = !mWindows.isGuiMode();
// Switch to console mode no matter what mode we are currently // Switch to console mode no matter what mode we are currently
// in, except of course if we are already in console mode // in, except of course if we are already in console mode
if (!gameMode) if (mWindows.isGuiMode())
{ {
if (mWindows.getMode() == MWGui::GM_Console) if (mWindows.getMode() == MWGui::GM_Console)
mWindows.popGuiMode(); mWindows.popGuiMode();
@ -741,9 +735,7 @@ namespace MWInput
return; return;
// Toggle between game mode and journal mode // Toggle between game mode and journal mode
bool gameMode = !mWindows.isGuiMode(); if(!mWindows.isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
if(gameMode && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
{ {
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
mWindows.pushGuiMode(MWGui::GM_Journal); mWindows.pushGuiMode(MWGui::GM_Journal);

View file

@ -94,8 +94,9 @@ namespace MWInput
virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ); virtual bool mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id );
virtual bool mouseMoved( const SFO::MouseMotionEvent &arg ); virtual bool mouseMoved( const SFO::MouseMotionEvent &arg );
virtual bool windowVisibilityChange( bool visible ); virtual void windowVisibilityChange( bool visible );
virtual bool windowFocusChange( bool have_focus ); virtual void windowFocusChange( bool have_focus );
virtual void windowResized (int x, int y);
virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue); virtual void channelChanged(ICS::Channel* channel, float currentValue, float previousValue);

View file

@ -76,10 +76,6 @@ namespace MWMechanics
double magickaFactor = double magickaFactor =
creatureStats.getMagicEffects().get (EffectKey (ESM::MagicEffect::FortifyMaximumMagicka)).mMagnitude * 0.1 + 0.5; 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(); DynamicStat<float> magicka = creatureStats.getMagicka();
magicka.setBase (static_cast<int> (intelligence + magickaFactor * intelligence)); magicka.setBase (static_cast<int> (intelligence + magickaFactor * intelligence));
creatureStats.setMagicka (magicka); 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 // erase previous death events since we are currently only tracking them while in an active cell
MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied(); MWWorld::Class::get(ptr).getCreatureStats(ptr).clearHasDied();
removeActor(ptr);
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); 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++) 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()) if(iter->second->isDead())
iter->second->resurrect(); iter->second->resurrect();
@ -232,7 +234,7 @@ namespace MWMechanics
if(iter->first.getTypeName() == typeid(ESM::NPC).name()) if(iter->first.getTypeName() == typeid(ESM::NPC).name())
updateNpc(iter->first, totalDuration, paused); updateNpc(iter->first, totalDuration, paused);
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) if(!stats.isDead())
continue; continue;
} }
@ -240,16 +242,15 @@ namespace MWMechanics
// \todo remove workaround, once player death can be handled // \todo remove workaround, once player death can be handled
if(iter->first.getRefData().getHandle()=="player") if(iter->first.getRefData().getHandle()=="player")
{ {
MWMechanics::DynamicStat<float> stat ( MWMechanics::DynamicStat<float> stat(stats.getHealth());
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth());
if (stat.getModified()<1) if(stat.getModified()<1)
{ {
stat.setModified (1, 0); stat.setModified(1, 0);
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat); stats.setHealth(stat);
} }
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect(); stats.resurrect();
continue; continue;
} }
@ -258,11 +259,10 @@ namespace MWMechanics
iter->second->kill(); 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)) if(cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox( MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
"#{sKilledEssential}");
} }
} }

View file

@ -3,6 +3,12 @@
#include "aipackage.hpp" #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) void MWMechanics::AiSequence::copy (const AiSequence& sequence)
{ {
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin()); 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()); 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);
}
}

View file

@ -3,6 +3,8 @@
#include <list> #include <list>
#include <components/esm/loadnpc.hpp>
namespace MWWorld namespace MWWorld
{ {
class Ptr; class Ptr;
@ -48,6 +50,8 @@ namespace MWMechanics
void queue (const AiPackage& package); void queue (const AiPackage& package);
///< Add \a package to the end of the sequence (executed after all other packages have been ///< Add \a package to the end of the sequence (executed after all other packages have been
/// completed) /// completed)
void fill (const ESM::AIPackageList& list);
}; };
} }

View file

@ -61,27 +61,15 @@ struct StateInfo {
const char groupname[32]; const char groupname[32];
}; };
static const StateInfo sStateList[] = { static const StateInfo sDeathList[] = {
{ 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" },
{ CharState_Death1, "death1" }, { CharState_Death1, "death1" },
{ CharState_Death2, "death2" }, { CharState_Death2, "death2" },
{ CharState_Death3, "death3" }, { CharState_Death3, "death3" },
{ CharState_Death4, "death4" }, { CharState_Death4, "death4" },
{ CharState_Death5, "death5" }, { 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[] = { static const StateInfo sMovementList[] = {
{ CharState_WalkForward, "walkforward" }, { CharState_WalkForward, "walkforward" },
@ -169,7 +157,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
idle = "idleswim"; idle = "idleswim";
else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak")) else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak"))
idle = "idlesneak"; idle = "idlesneak";
else else if(mIdleState != CharState_None)
{ {
idle = "idle"; idle = "idle";
if(weap != sWeaponTypeListEnd) if(weap != sWeaponTypeListEnd)
@ -182,8 +170,9 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
mAnimation->disable(mCurrentIdle); mAnimation->disable(mCurrentIdle);
mCurrentIdle = idle; mCurrentIdle = idle;
mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false, if(!mCurrentIdle.empty())
1.0f, "start", "stop", 0.0f, ~0ul); mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false,
1.0f, "start", "stop", 0.0f, ~0ul);
} }
if(force || movement != mMovementState) if(force || movement != mMovementState)
@ -208,13 +197,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
if(!mAnimation->hasAnimation(movement)) if(!mAnimation->hasAnimation(movement))
{ {
std::string::size_type sneakpos = movement.find("sneak"); std::string::size_type swimpos = movement.find("swim");
if(sneakpos == std::string::npos) if(swimpos == std::string::npos)
movement.clear(); movement.clear();
else else
{ {
movegroup = MWRender::Animation::Group_LowerBody; movegroup = MWRender::Animation::Group_LowerBody;
movement.erase(sneakpos, 5); movement.erase(swimpos, 4);
if(!mAnimation->hasAnimation(movement)) if(!mAnimation->hasAnimation(movement))
movement.clear(); movement.clear();
} }
@ -225,11 +214,11 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
mCurrentMovement = movement; mCurrentMovement = movement;
if(!mCurrentMovement.empty()) if(!mCurrentMovement.empty())
{ {
float vel, speed = 0.0f; float vel, speedmult = 1.0f;
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 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, 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) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr) : mPtr(ptr)
, mAnimation(anim) , mAnimation(anim)
, mIdleState(CharState_Idle) , mIdleState(CharState_None)
, mMovementState(CharState_None) , mMovementState(CharState_None)
, mMovementSpeed(0.0f) , mMovementSpeed(0.0f)
, mDeathState(CharState_None) , mDeathState(CharState_None)
@ -255,18 +305,30 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mSkipAnim(false) , mSkipAnim(false)
, mSecondsOfRunning(0) , mSecondsOfRunning(0)
, mSecondsOfSwimming(0) , mSecondsOfSwimming(0)
, mUpdateWeapon(true)
{ {
if(!mAnimation) if(!mAnimation)
return; 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 /* Accumulate along X/Y only for now, until we can figure out how we should
* handle knockout and death which moves the character down. */ * handle knockout and death which moves the character down. */
mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); 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. */ /* FIXME: Get the actual death state used. */
mDeathState = CharState_Death1; mDeathState = CharState_Death1;
@ -276,13 +338,15 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
{ {
/* Don't accumulate with non-actors. */ /* Don't accumulate with non-actors. */
mAnimation->setAccumulation(Ogre::Vector3(0.0f)); mAnimation->setAccumulation(Ogre::Vector3(0.0f));
mIdleState = CharState_Idle;
} }
refreshCurrentAnims(mIdleState, mMovementState, true); refreshCurrentAnims(mIdleState, mMovementState, true);
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
{ {
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState)); const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sStateListEnd) if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
mCurrentDeath = state->groupname; 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) void CharacterController::update(float duration, Movement &movement)
{ {
const MWWorld::Class &cls = MWWorld::Class::get(mPtr); const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
@ -443,206 +724,7 @@ void CharacterController::update(float duration, Movement &movement)
movement.mRotation[2] += rot.z; movement.mRotation[2] += rot.z;
if(mPtr.getTypeName() == typeid(ESM::NPC).name()) if(mPtr.getTypeName() == typeid(ESM::NPC).name())
{ forcestateupdate = updateNpcState();
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");
}
refreshCurrentAnims(idlestate, movestate, forcestateupdate); refreshCurrentAnims(idlestate, movestate, forcestateupdate);
} }
@ -734,8 +816,8 @@ void CharacterController::forceStateUpdate()
refreshCurrentAnims(mIdleState, mMovementState, true); refreshCurrentAnims(mIdleState, mMovementState, true);
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
{ {
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState)); const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sStateListEnd) if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
mCurrentDeath = state->groupname; mCurrentDeath = state->groupname;
@ -747,22 +829,52 @@ void CharacterController::forceStateUpdate()
void CharacterController::kill() void CharacterController::kill()
{ {
static const CharacterState deathstates[] = {
CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5
};
if(mDeathState != CharState_None) if(mDeathState != CharState_None)
return; return;
mDeathState = deathstates[(int)(rand()/((double)RAND_MAX+1.0)*5.0)]; if(mPtr.getTypeName() == typeid(ESM::NPC).name())
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState)); {
if(state == sStateListEnd) const StateInfo *state = NULL;
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState)); 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));
}
mCurrentDeath = state->groupname; static const CharacterState deathstates[5] = {
if(mAnimation && !mAnimation->getInfo(mCurrentDeath)) 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;
}
else
{
mDeathState = CharState_Death1;
mCurrentDeath = "death1";
}
if(mAnimation)
{
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All, mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", 0.0f, 0); false, 1.0f, "start", "stop", 0.0f, 0);
mAnimation->disable(mCurrentIdle);
}
mIdleState = CharState_None;
mCurrentIdle.clear();
} }
void CharacterController::resurrect() void CharacterController::resurrect()
@ -776,5 +888,4 @@ void CharacterController::resurrect()
mDeathState = CharState_None; mDeathState = CharState_None;
} }
} }

View file

@ -5,6 +5,12 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace MWWorld
{
class ContainerStoreIterator;
class InventoryStore;
}
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
@ -14,6 +20,7 @@ namespace MWMechanics
{ {
class Movement; class Movement;
class NpcStats;
enum Priority { enum Priority {
Priority_Default, Priority_Default,
@ -72,12 +79,12 @@ enum CharacterState {
CharState_Jump, CharState_Jump,
/* Death states must be last! */
CharState_Death1, CharState_Death1,
CharState_Death2, CharState_Death2,
CharState_Death3, CharState_Death3,
CharState_Death4, CharState_Death4,
CharState_Death5 CharState_Death5,
CharState_SwimDeath
}; };
enum WeaponType { enum WeaponType {
@ -104,11 +111,8 @@ enum UpperBodyCharacterState {
UpperCharState_MinAttackToMaxAttack, UpperCharState_MinAttackToMaxAttack,
UpperCharState_MaxAttackToMinHit, UpperCharState_MaxAttackToMinHit,
UpperCharState_MinHitToHit, UpperCharState_MinHitToHit,
UpperCharState_LargeFollowStartToLargeFollowStop, UpperCharState_FollowStartToFollowStop,
UpperCharState_MediumFollowStartToMediumFollowStop, UpperCharState_CastingSpell
UpperCharState_SmallFollowStartToSmallFollowStop,
UpperCharState_EquipingSpell,
UpperCharState_UnEquipingSpell
}; };
class CharacterController class CharacterController
@ -132,10 +136,9 @@ class CharacterController
UpperBodyCharacterState mUpperBodyState; UpperBodyCharacterState mUpperBodyState;
WeaponType mWeaponType; WeaponType mWeaponType;
bool mSkipAnim; std::string mCurrentWeapon;
// Workaround for playing weapon draw animation and sound when going to new cell bool mSkipAnim;
bool mUpdateWeapon;
// counted for skill increase // counted for skill increase
float mSecondsOfSwimming; float mSecondsOfSwimming;
@ -147,8 +150,14 @@ class CharacterController
static void getWeaponGroup(WeaponType weaptype, std::string &group); static void getWeaponGroup(WeaponType weaptype, std::string &group);
static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats,
MWWorld::InventoryStore &inv,
WeaponType *weaptype);
void clearAnimQueue(); void clearAnimQueue();
bool updateNpcState();
public: public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
virtual ~CharacterController(); virtual ~CharacterController();

View file

@ -19,16 +19,35 @@ namespace MWMechanics
mAiSettings[i] = 0; mAiSettings[i] = 0;
} }
void CreatureStats::increaseLevelHealthBonus (float value)
{
mLevelHealthBonus += value;
}
float CreatureStats::getLevelHealthBonus () const float CreatureStats::getLevelHealthBonus () const
{ {
return mLevelHealthBonus; 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 const AiSequence& CreatureStats::getAiSequence() const
{ {
return mAiSequence; return mAiSequence;
@ -299,4 +318,14 @@ namespace MWMechanics
{ {
return false; return false;
} }
void CreatureStats::setLastHitObject(const std::string& objectid)
{
mLastHitObject = objectid;
}
const std::string &CreatureStats::getLastHitObject() const
{
return mLastHitObject;
}
} }

View file

@ -38,6 +38,8 @@ namespace MWMechanics
int mAttackType; int mAttackType;
std::string mLastHitObject; // The last object to hit this actor
public: public:
CreatureStats(); CreatureStats();
@ -111,10 +113,14 @@ namespace MWMechanics
float getFatigueTerm() const; float getFatigueTerm() const;
///< Return effective fatigue ///< 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; 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 isDead() const;
bool hasDied() const; bool hasDied() const;
@ -151,6 +157,9 @@ namespace MWMechanics
void setHostile (bool hostile); void setHostile (bool hostile);
bool getCreatureTargetted() const; bool getCreatureTargetted() const;
void setLastHitObject(const std::string &objectid);
const std::string &getLastHitObject() const;
}; };
} }

View file

@ -31,6 +31,7 @@ MWMechanics::NpcStats::NpcStats()
, mWerewolf (false) , mWerewolf (false)
, mWerewolfKills (0) , mWerewolfKills (0)
, mProfit(0) , mProfit(0)
, mAttackStrength(0.0f)
{ {
mSkillIncreases.resize (ESM::Attribute::Length); mSkillIncreases.resize (ESM::Attribute::Length);
for (int i=0; i<ESM::Attribute::Length; ++i) for (int i=0; i<ESM::Attribute::Length; ++i)
@ -47,6 +48,16 @@ void MWMechanics::NpcStats::setDrawState (DrawState_ state)
mDrawState = state; mDrawState = state;
} }
float MWMechanics::NpcStats::getAttackStrength() const
{
return mAttackStrength;
}
void MWMechanics::NpcStats::setAttackStrength(float value)
{
mAttackStrength = value;
}
int MWMechanics::NpcStats::getBaseDisposition() const int MWMechanics::NpcStats::getBaseDisposition() const
{ {
return mDisposition; return mDisposition;

View file

@ -54,6 +54,7 @@ namespace MWMechanics
bool mWerewolf; bool mWerewolf;
int mWerewolfKills; int mWerewolfKills;
int mProfit; int mProfit;
float mAttackStrength;
int mLevelProgress; // 0-10 int mLevelProgress; // 0-10
@ -70,9 +71,12 @@ namespace MWMechanics
void modifyProfit(int diff); void modifyProfit(int diff);
DrawState_ getDrawState() const; DrawState_ getDrawState() const;
void setDrawState (DrawState_ state); 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; int getBaseDisposition() const;
void setBaseDisposition(int disposition); void setBaseDisposition(int disposition);

View file

@ -16,6 +16,8 @@ Objects::Objects()
void Objects::addObject(const MWWorld::Ptr& ptr) void Objects::addObject(const MWWorld::Ptr& ptr)
{ {
removeObject(ptr);
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr); MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim))); if(anim) mObjects.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
} }

View file

@ -98,8 +98,8 @@ namespace MWMechanics
public: public:
typedef T Type; typedef T Type;
DynamicStat() : mCurrent (0) {} DynamicStat() : mStatic (0), mCurrent (0) {}
DynamicStat(T current) : mCurrent (current) {} DynamicStat(T base) : mStatic (base), mCurrent (base) {}
DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {} DynamicStat(T base, T modified, T current) : mStatic(base, modified), mCurrent (current) {}
DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {} DynamicStat(const Stat<T> &stat, T current) : mStatic(stat), mCurrent (current) {}

View file

@ -14,6 +14,8 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp" #include "../mwmechanics/character.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/class.hpp"
namespace MWRender namespace MWRender
{ {
@ -52,6 +54,7 @@ Animation::Animation(const MWWorld::Ptr &ptr)
, mNonAccumRoot(NULL) , mNonAccumRoot(NULL)
, mNonAccumCtrl(NULL) , mNonAccumCtrl(NULL)
, mAccumulate(0.0f) , mAccumulate(0.0f)
, mNullAnimationValuePtr(OGRE_NEW NullAnimationValue)
{ {
for(size_t i = 0;i < sNumGroups;i++) for(size_t i = 0;i < sNumGroups;i++)
mAnimationValuePtr[i].bind(OGRE_NEW AnimationValue(this)); 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) 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; const NifOgre::TextKeyMap::const_iterator groupstart = findGroupStart(keys, groupname);
NifOgre::TextKeyMap::const_iterator startkey(keys.begin());
while(startkey != keys.end() && startkey->second != tag) std::string starttag = groupname+": "+start;
NifOgre::TextKeyMap::const_iterator startkey(groupstart);
while(startkey != keys.end() && startkey->second != starttag)
startkey++; startkey++;
if(startkey == keys.end() && start == "loop start") if(startkey == keys.end() && start == "loop start")
{ {
tag = groupname+": start"; starttag = groupname+": start";
startkey = keys.begin(); startkey = groupstart;
while(startkey != keys.end() && startkey->second != tag) while(startkey != keys.end() && startkey->second != starttag)
startkey++; startkey++;
} }
if(startkey == keys.end()) if(startkey == keys.end())
return false; return false;
tag = groupname+": "+stop; const std::string stoptag = groupname+": "+stop;
NifOgre::TextKeyMap::const_iterator stopkey(startkey); NifOgre::TextKeyMap::const_iterator stopkey(groupstart);
while(stopkey != keys.end() && stopkey->second != tag) while(stopkey != keys.end() && stopkey->second != stoptag)
stopkey++; stopkey++;
if(stopkey == keys.end()) if(stopkey == keys.end())
return false; return false;
if(startkey == stopkey) if(startkey->first > stopkey->first)
return false; return false;
state.mStartKey = startkey; state.mStartTime = startkey->first;
state.mLoopStartKey = startkey; state.mLoopStartTime = startkey->first;
state.mStopKey = stopkey; state.mLoopStopTime = stopkey->first;
state.mNextKey = startkey; state.mStopTime = stopkey->first;
state.mTime = state.mStartKey->first + ((state.mStopKey->first - state.mStartKey->first) * startpoint); state.mTime = state.mStartTime + ((state.mStopTime - state.mStartTime) * startpoint);
if(state.mTime > state.mStartTime)
tag = groupname+": loop start";
while(state.mNextKey->first <= state.mTime && state.mNextKey != state.mStopKey)
{ {
if(state.mNextKey->second == tag) const std::string loopstarttag = groupname+": loop start";
state.mLoopStartKey = state.mNextKey; const std::string loopstoptag = groupname+": loop stop";
state.mNextKey++; 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; return true;
} }
bool Animation::doLoop(AnimState &state)
{
if(state.mLoopCount == 0)
return false;
state.mLoopCount--;
state.mTime = state.mLoopStartKey->first; void Animation::handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key)
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)
{ {
float time = key->first; float time = key->first;
const std::string &evt = key->second; 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(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f); sndMgr->playSound3D(mPtr, evt.substr(7), 1.0f, 1.0f);
return true; return;
} }
if(evt.compare(0, 10, "soundgen: ") == 0) if(evt.compare(0, 10, "soundgen: ") == 0)
{ {
// FIXME: Lookup the SoundGen (SNDG) for the specified sound that corresponds std::string sound = MWWorld::Class::get(mPtr).getSoundIdFromSndGen(mPtr, evt.substr(10));
// to this actor type if(!sound.empty())
return true; {
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 || if(evt.compare(0, groupname.size(), groupname) != 0 ||
evt.compare(groupname.size(), 2, ": ") != 0) evt.compare(groupname.size(), 2, ": ") != 0)
{ {
// Not ours, skip it // Not ours, skip it
return true; return;
} }
size_t off = groupname.size()+2; size_t off = groupname.size()+2;
size_t len = evt.size() - off; size_t len = evt.size() - off;
if(evt.compare(off, len, "start") == 0 || evt.compare(off, len, "loop start") == 0) if(evt.compare(off, len, "loop start") == 0)
{ state.mLoopStartTime = key->first;
state.mLoopStartKey = key; else if(evt.compare(off, len, "loop stop") == 0)
return true; state.mLoopStopTime = key->first;
} else if(evt.compare(off, len, "equip attach") == 0)
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)
{
showWeapons(true); showWeapons(true);
return true; else if(evt.compare(off, len, "unequip detach") == 0)
}
if(evt.compare(off, len, "unequip detach") == 0)
{
showWeapons(false); showWeapons(false);
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)
/* Nothing to do for these */ MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Slash);
if(evt.compare(off, len, "equip start") == 0 || evt.compare(off, len, "equip stop") == 0 || else if(evt.compare(off, len, "thrust hit") == 0)
evt.compare(off, len, "unequip start") == 0 || evt.compare(off, len, "unequip stop") == 0) MWWorld::Class::get(mPtr).hit(mPtr, MWMechanics::CreatureStats::AT_Thrust);
return true; else if(evt.compare(off, len, "hit") == 0)
MWWorld::Class::get(mPtr).hit(mPtr);
std::cerr<< "Unhandled animation textkey: "<<evt <<std::endl;
return true;
} }
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) 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; return;
if(groupname.empty()) 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()); AnimSourceList::reverse_iterator iter(mAnimSources.rbegin());
for(;iter != mAnimSources.rend();iter++) for(;iter != mAnimSources.rend();iter++)
{ {
const NifOgre::TextKeyMap &textkeys = (*iter)->mTextKeys;
AnimState state; AnimState state;
if(reset(state, (*iter)->mTextKeys, groupname, start, stop, startpoint)) if(reset(state, textkeys, groupname, start, stop, startpoint))
{ {
state.mSource = *iter; state.mSource = *iter;
state.mSpeedMult = speedmult; state.mSpeedMult = speedmult;
state.mLoopCount = loops; state.mLoopCount = loops;
state.mPlaying = true; state.mPlaying = (state.mTime < state.mStopTime);
state.mPriority = priority; state.mPriority = priority;
state.mGroups = groups; state.mGroups = groups;
state.mAutoDisable = autodisable; state.mAutoDisable = autodisable;
mStates[groupname] = state; 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; break;
} }
} }
if(iter == mAnimSources.rend()) 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(); resetActiveGroups();
} }
@ -654,7 +664,6 @@ void Animation::resetActiveGroups()
return; return;
const Ogre::SharedPtr<AnimSource> &animsrc = state->second.mSource; 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]; const std::vector<Ogre::Controller<Ogre::Real> >&ctrls = animsrc->mControllers[0];
for(size_t i = 0;i < ctrls.size();i++) 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); AnimStateMap::const_iterator iter = mStates.find(groupname);
if(iter == mStates.end()) if(iter == mStates.end())
{ {
if(complete) *complete = 0.0f; if(complete) *complete = 0.0f;
if(speedmult) *speedmult = 0.0f; if(speedmult) *speedmult = 0.0f;
if(start) *start = "";
if(stop) *stop = "";
return false; return false;
} }
if(complete) *complete = (iter->second.mTime - iter->second.mStartKey->first) / if(complete)
(iter->second.mStopKey->first - iter->second.mStartKey->first); {
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(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; return true;
} }
@ -707,27 +718,52 @@ Ogre::Vector3 Animation::runAnimation(float duration)
while(stateiter != mStates.end()) while(stateiter != mStates.end())
{ {
AnimState &state = stateiter->second; 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; float timepassed = duration * state.mSpeedMult;
while(state.mPlaying) while(state.mPlaying)
{ {
float targetTime = state.mTime + timepassed; float targetTime = state.mTime + timepassed;
if(state.mNextKey->first > targetTime) if(textkey == textkeys.end() || textkey->first > targetTime)
{ {
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName()) if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
updatePosition(state.mTime, targetTime, movement); updatePosition(state.mTime, targetTime, movement);
state.mTime = targetTime; state.mTime = std::min(targetTime, state.mStopTime);
break; }
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++); state.mPlaying = (state.mTime < state.mStopTime);
if(mNonAccumCtrl && stateiter->first == mAnimationValuePtr[0]->getAnimName())
updatePosition(state.mTime, key->first, movement);
state.mTime = key->first;
state.mPlaying = (key != state.mStopKey);
timepassed = targetTime - state.mTime; 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; break;
} }

View file

@ -52,6 +52,15 @@ protected:
virtual void setValue(Ogre::Real value); 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 { struct AnimSource : public Ogre::AnimationAlloc {
NifOgre::TextKeyMap mTextKeys; NifOgre::TextKeyMap mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups]; std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
@ -60,10 +69,10 @@ protected:
struct AnimState { struct AnimState {
Ogre::SharedPtr<AnimSource> mSource; Ogre::SharedPtr<AnimSource> mSource;
NifOgre::TextKeyMap::const_iterator mStartKey; float mStartTime;
NifOgre::TextKeyMap::const_iterator mLoopStartKey; float mLoopStartTime;
NifOgre::TextKeyMap::const_iterator mStopKey; float mLoopStopTime;
NifOgre::TextKeyMap::const_iterator mNextKey; float mStopTime;
float mTime; float mTime;
float mSpeedMult; float mSpeedMult;
@ -75,7 +84,8 @@ protected:
int mGroups; int mGroups;
bool mAutoDisable; 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) mPriority(0), mGroups(0), mAutoDisable(true)
{ } { }
}; };
@ -98,6 +108,7 @@ protected:
AnimStateMap mStates; AnimStateMap mStates;
Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups]; Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups];
Ogre::SharedPtr<NullAnimationValue> mNullAnimationValuePtr;
ObjectAttachMap mAttachedObjects; ObjectAttachMap mAttachedObjects;
@ -131,9 +142,7 @@ protected:
const std::string &groupname, const std::string &start, const std::string &stop, const std::string &groupname, const std::string &start, const std::string &stop,
float startpoint); float startpoint);
bool doLoop(AnimState &state); void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
bool 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 /* 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 * 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 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 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 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. * \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; /** Disables the specified animation group;
* \param groupname Animation group to disable. * \param groupname Animation group to disable.

View file

@ -25,7 +25,11 @@ namespace MWRender
mHeight(128.f), mHeight(128.f),
mCameraDistance(300.f), mCameraDistance(300.f),
mDistanceAdjusted(false), mDistanceAdjusted(false),
mAnimation(NULL) mAnimation(NULL),
mNearest(30.f),
mFurthest(800.f),
mIsNearest(false),
mIsFurthest(false)
{ {
mVanity.enabled = false; mVanity.enabled = false;
mVanity.allowed = true; mVanity.allowed = true;
@ -82,8 +86,8 @@ namespace MWRender
mCameraNode->getCreator()->destroySceneNode(mCameraNode); mCameraNode->getCreator()->destroySceneNode(mCameraNode);
} }
mCameraNode = node; mCameraNode = node;
mCamera->detachFromParent(); if(!mCamera->isAttached())
mCameraNode->attachObject(mCamera); mCameraNode->attachObject(mCamera);
} }
void Camera::updateListener() void Camera::updateListener()
@ -95,9 +99,11 @@ namespace MWRender
MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up); MWBase::Environment::get().getSoundManager()->setListenerPosDir(pos, dir, up);
} }
void Camera::update(float duration) void Camera::update(float duration, bool paused)
{ {
updateListener(); updateListener();
if (paused)
return;
// only show the crosshair in game mode and in first person mode. // only show the crosshair in game mode and in first person mode.
MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager(); MWBase::WindowManager *wm = MWBase::Environment::get().getWindowManager();
@ -232,16 +238,21 @@ namespace MWRender
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
return; return;
mIsFurthest = false;
mIsNearest = false;
Ogre::Vector3 v(0.f, 0.f, dist); Ogre::Vector3 v(0.f, 0.f, dist);
if (adjust) { if (adjust) {
v += mCamera->getPosition(); v += mCamera->getPosition();
} }
if (v.z > 800.f) { if (v.z >= mFurthest) {
v.z = 800.f; v.z = mFurthest;
} else if (v.z < 10.f) { mIsFurthest = true;
} else if (!override && v.z < 10.f) {
v.z = 10.f; v.z = 10.f;
} else if (override && v.z < 50.f) { } else if (override && v.z <= mNearest) {
v.z = 50.f; v.z = mNearest;
mIsNearest = true;
} }
mCamera->setPosition(v); mCamera->setPosition(v);
@ -302,34 +313,13 @@ namespace MWRender
} }
} }
float Camera::getHeight() void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera)
{
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)
{ {
mCamera->getParentSceneNode()->needUpdate(true); mCamera->getParentSceneNode()->needUpdate(true);
camera = mCamera->getRealPosition(); camera = mCamera->getRealPosition();
player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); focal = Ogre::Vector3((mCamera->getParentNode()->_getFullTransform() *
Ogre::Vector4(0.0f, 0.0f, 0.0f, 1.0f)).ptr());
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;
} }
void Camera::togglePlayerLooking(bool enable) void Camera::togglePlayerLooking(bool enable)
@ -341,4 +331,14 @@ namespace MWRender
{ {
return mPreviewMode || mVanity.enabled; return mPreviewMode || mVanity.enabled;
} }
bool Camera::isNearest()
{
return mIsNearest;
}
bool Camera::isFurthest()
{
return mIsFurthest;
}
} }

View file

@ -33,6 +33,10 @@ namespace MWRender
bool mFirstPersonView; bool mFirstPersonView;
bool mPreviewMode; bool mPreviewMode;
bool mFreeLook; bool mFreeLook;
float mNearest;
float mFurthest;
bool mIsNearest;
bool mIsFurthest;
struct { struct {
bool enabled, allowed; bool enabled, allowed;
@ -80,7 +84,7 @@ namespace MWRender
void processViewChange(); 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. /// Set camera distance for current mode. Don't work on 1st person view.
/// \param adjust Indicates should distance be adjusted or set. /// \param adjust Indicates should distance be adjusted or set.
@ -93,18 +97,16 @@ namespace MWRender
void setAnimation(NpcAnimation *anim); void setAnimation(NpcAnimation *anim);
float getHeight(); /// Stores focal and camera world positions in passed arguments
void getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera);
/// 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);
void togglePlayerLooking(bool enable); void togglePlayerLooking(bool enable);
bool isVanityOrPreviewModeEnabled(); bool isVanityOrPreviewModeEnabled();
bool isNearest();
bool isFurthest();
}; };
} }

View file

@ -424,6 +424,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
for(size_t i = 0;i < sPartListSize;i++) 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; Ogre::Entity *ent = mObjectParts[i].mSkelBase;
if(!ent) continue; if(!ent) continue;
updateSkeletonInstance(baseinst, ent->getSkeleton()); updateSkeletonInstance(baseinst, ent->getSkeleton());
@ -481,6 +485,19 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
if(type == sPartList[i].type) if(type == sPartList[i].type)
{ {
mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name); 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; break;
} }
} }

View file

@ -16,6 +16,8 @@
#include <OgreControllerManager.h> #include <OgreControllerManager.h>
#include <OgreMeshManager.h> #include <OgreMeshManager.h>
#include <SDL_video.h>
#include <extern/shiny/Main/Factory.hpp> #include <extern/shiny/Main/Factory.hpp>
#include <extern/shiny/Platforms/Ogre/OgrePlatform.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.createScene("PlayerCam", Settings::Manager::getFloat("field of view", "General"), 5);
mRendering.setWindowEventListener(this);
mRendering.getWindow()->addListener(this); mRendering.getWindow()->addListener(this);
mRendering.setWindowListener(this);
mCompositors = new Compositors(mRendering.getViewport()); mCompositors = new Compositors(mRendering.getViewport());
@ -170,7 +172,7 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
mOcclusionQuery = new OcclusionQuery(&mRendering, mSkyManager->getSunNode()); 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")); mVideoPlayer->setResolution (Settings::Manager::getInt ("resolution x", "Video"), Settings::Manager::getInt ("resolution y", "Video"));
mSun = 0; mSun = 0;
@ -186,7 +188,6 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
RenderingManager::~RenderingManager () RenderingManager::~RenderingManager ()
{ {
mRendering.getWindow()->removeListener(this); mRendering.getWindow()->removeListener(this);
mRendering.removeWindowEventListener(this);
delete mPlayerAnimation; delete mPlayerAnimation;
delete mCamera; 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) void RenderingManager::update (float duration, bool paused)
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
@ -315,19 +324,18 @@ void RenderingManager::update (float duration, bool paused)
// player position // player position
MWWorld::RefData &data = player.getRefData(); MWWorld::RefData &data = player.getRefData();
float *_playerPos = data.getPosition().pos; Ogre::Vector3 playerPos(data.getPosition().pos);
Ogre::Vector3 playerPos(_playerPos[0], _playerPos[1], _playerPos[2]);
Ogre::Vector3 orig, dest;
mCamera->setCameraDistance(); 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 btOrig(orig.x, orig.y, orig.z);
btVector3 btDest(dest.x, dest.y, dest.z); btVector3 btDest(dest.x, dest.y, dest.z);
std::pair<bool, float> test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest); std::pair<bool,float> test = mPhysicsEngine->sphereCast(mRendering.getCamera()->getNearClipDistance()*2.5, btOrig, btDest);
if (test.first) if(test.first)
mCamera->setCameraDistance(test.second * orig.distance(dest), false, false); 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)); applyFog(world->isUnderwater(player.getCell(), cam));
mCamera->update(duration, paused);
if(paused) if(paused)
return; return;
mCamera->update(duration);
mActors.update (duration); mActors.update (duration);
mObjects.update (duration); mObjects.update (duration);
@ -711,7 +719,7 @@ Compositors* RenderingManager::getCompositors()
void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings) void RenderingManager::processChangedSettings(const Settings::CategorySettingVector& settings)
{ {
//bool changeRes = false; bool changeRes = false;
bool rebuild = false; // rebuild static geometry (necessary after any material changes) bool rebuild = false; // rebuild static geometry (necessary after any material changes)
for (Settings::CategorySettingVector::const_iterator it=settings.begin(); for (Settings::CategorySettingVector::const_iterator it=settings.begin();
it != settings.end(); ++it) 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()) if (!MWBase::Environment::get().getWorld()->isCellExterior() && !MWBase::Environment::get().getWorld()->isCellQuasiExterior())
configureFog(*MWBase::Environment::get().getWorld()->getPlayer().getPlayer().getCell()); 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 x"
|| it->second == "resolution y" || it->second == "resolution y"
|| it->second == "fullscreen")) || it->second == "fullscreen"))
changeRes = true;*/ changeRes = true;
else if (it->second == "field of view" && it->first == "General") else if (it->second == "field of view" && it->first == "General")
mRendering.setFov(Settings::Manager::getFloat("field of view", "General")); mRendering.setFov(Settings::Manager::getFloat("field of view", "General"));
else if ((it->second == "texture filtering" && it->first == "General") else if ((it->second == "texture filtering" && it->first == "General")
@ -783,24 +791,31 @@ void RenderingManager::processChangedSettings(const Settings::CategorySettingVec
} }
} }
/*
if (changeRes) if (changeRes)
{ {
unsigned int x = Settings::Manager::getInt("resolution x", "Video"); unsigned int x = Settings::Manager::getInt("resolution x", "Video");
unsigned int y = Settings::Manager::getInt("resolution y", "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); SDL_DisplayMode mode;
mRendering.getWindow()->resize(x, y); SDL_GetWindowDisplayMode(window, &mode);
mode.w = x;
mode.h = y;
SDL_SetWindowDisplayMode(window, &mode);
SDL_SetWindowFullscreen(window, fullscreen);
} }
else
SDL_SetWindowFullscreen(mRendering.getSDLWindow(), Settings::Manager::getBool("fullscreen", "Video") ? SDL_WINDOW_FULLSCREEN : 0); SDL_SetWindowSize(window, x, y);
//mRendering.getWindow()->setFullscreen(Settings::Manager::getBool("fullscreen", "Video"), x, y);
} }
*/
mWater->processChangedSettings(settings); mWater->processChangedSettings(settings);
@ -818,24 +833,14 @@ void RenderingManager::setMenuTransparency(float val)
tex->getBuffer()->unlock(); 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(); mRendering.adjustViewport();
mCompositors->recreate(); mCompositors->recreate();
mVideoPlayer->setResolution (rw->getWidth(), rw->getHeight()); mVideoPlayer->setResolution (x, y);
const Settings::CategorySettingVector& changed = Settings::Manager::apply(); MWBase::Environment::get().getWindowManager()->windowResized(x,y);
MWBase::Environment::get().getInputManager()->processChangedSettings(changed);
MWBase::Environment::get().getWindowManager()->processChangedSettings(changed);
}
void RenderingManager::windowClosed(Ogre::RenderWindow* rw)
{
Ogre::Root::getSingleton ().queueEndRendering ();
} }
void RenderingManager::applyCompositors() void RenderingManager::applyCompositors()
@ -894,6 +899,22 @@ bool RenderingManager::vanityRotateCamera(const float *rot)
return true; 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) void RenderingManager::getInteriorMapPosition (Ogre::Vector2 position, float& nX, float& nY, int &x, int& y)
{ {
return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y); return mLocalMap->getInteriorMapPosition (position, nX, nY, x, y);

Some files were not shown because too many files have changed in this diff Show more