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

pull/51/head
gus 12 years ago
commit b248d85b84

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

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

@ -57,13 +57,14 @@ opencs_hdrs_noqt (view/doc
opencs_units (view/world
table tablesubview scriptsubview util regionmapsubview
table tablesubview scriptsubview util regionmapsubview tablebottombox creator genericcreator
cellcreator referenceablecreator referencecreator
)
opencs_units_noqt (view/world
dialoguesubview subviews
enumdelegate vartypedelegate recordstatusdelegate refidtypedelegate datadisplaydelegate
scripthighlighter
scripthighlighter idvalidator
)

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

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

@ -42,6 +42,33 @@ CSMSettings::UserSettings::UserSettings()
mReadOnlyMessage = QObject::tr("<br><b>Could not open file for reading</b><br><br> \
Please make sure you have the right permissions and try again.<br>");
buildEditorSettingDefaults();
}
void CSMSettings::UserSettings::buildEditorSettingDefaults()
{
SettingContainer *windowHeight = new SettingContainer("768", this);
SettingContainer *windowWidth = new SettingContainer("1024", this);
SettingContainer *rsDelegate = new SettingContainer("Icon and Text", this);
SettingContainer *refIdTypeDelegate = new SettingContainer("Icon and Text", this);
windowHeight->setObjectName ("Height");
windowWidth->setObjectName ("Width");
rsDelegate->setObjectName ("Record Status Display");
refIdTypeDelegate->setObjectName ("Referenceable ID Type Display");
SettingMap *displayFormatMap = new SettingMap;
SettingMap *windowSizeMap = new SettingMap;
displayFormatMap->insert (rsDelegate->objectName(), rsDelegate );
displayFormatMap->insert (refIdTypeDelegate->objectName(), refIdTypeDelegate);
windowSizeMap->insert (windowWidth->objectName(), windowWidth );
windowSizeMap->insert (windowHeight->objectName(), windowHeight );
mEditorSettingDefaults.insert ("Display Format", displayFormatMap);
mEditorSettingDefaults.insert ("Window Size", windowSizeMap);
}
CSMSettings::UserSettings::~UserSettings()
@ -104,17 +131,22 @@ bool CSMSettings::UserSettings::writeSettings(QMap<QString, CSMSettings::Setting
}
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSettings() const
const CSMSettings::SectionMap &CSMSettings::UserSettings::getSectionMap() const
{
return mSectionSettings;
}
const CSMSettings::SettingMap *CSMSettings::UserSettings::getSettings(const QString &sectionName) const
{
return getValidSettings(sectionName);
}
bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
{
if (filePath.isEmpty())
return false;
mSectionSettings.clear();
SectionMap loadedSettings;
QTextStream *stream = openFileStream (filePath, true);
@ -150,7 +182,7 @@ bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
{
//add the previous section's settings to the member map
if (settings)
mSectionSettings.insert(section, settings);
loadedSettings.insert(section, settings);
//save new section and create a new list
section = sectionRe.cap(1);
@ -167,18 +199,48 @@ bool CSMSettings::UserSettings::loadFromFile(const QString &filePath)
}
mSectionSettings.insert(section, settings);
loadedSettings.insert(section, settings);
stream->device()->close();
delete stream;
stream = 0;
}
mergeMap (loadedSettings);
return success;
}
void CSMSettings::UserSettings::mergeMap (const CSMSettings::SectionMap &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)
{
mSectionSettings.clear();
//global
QString globalFilePath = QString::fromStdString(mCfgMgr.getGlobalPath().string()) + fileName;
bool globalOk = loadFromFile(globalFilePath);
@ -207,10 +269,11 @@ void CSMSettings::UserSettings::loadSettings (const QString &fileName)
void CSMSettings::UserSettings::updateSettings (const QString &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())
{
@ -229,15 +292,12 @@ void CSMSettings::UserSettings::updateSettings (const QString &sectionName, cons
QString CSMSettings::UserSettings::getSetting (const QString &section, const QString &setting) const
{
QString retVal = "";
SettingMap *settings = getValidSettings(section);
if (mSectionSettings.find(section) != mSectionSettings.end())
{
CSMSettings::SettingMap *settings = mSectionSettings.value(section);
QString retVal = "";
if (settings->find(setting) != settings->end())
retVal = settings->value(setting)->getValue();
}
if (settings->find(setting) != settings->end())
retVal = settings->value(setting)->getValue();
return retVal;
}
@ -263,3 +323,33 @@ void CSMSettings::UserSettings::displayFileErrorMessage(const QString &message,
msgBox.exec();
}
CSMSettings::SettingMap *
CSMSettings::UserSettings::getValidSettings (const QString &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;
}

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

@ -3,7 +3,7 @@
#include <QAbstractItemModel>
#include "idtableproxymodel.hpp"
#include "idtable.hpp"
#include "idtable.hpp"
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
@ -25,15 +25,28 @@ void CSMWorld::ModifyCommand::undo()
mModel.setData (mIndex, mOld);
}
CSMWorld::CreateCommand::CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id)
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent)
: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
{
setText (("Create record " + id).c_str());
}
void CSMWorld::CreateCommand::addValue (int column, const QVariant& value)
{
mValues[column] = value;
}
void CSMWorld::CreateCommand::setType (UniversalId::Type type)
{
mType = type;
}
void CSMWorld::CreateCommand::redo()
{
mModel.addRecord (mId);
mModel.addRecord (mId, mType);
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter!=mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mId, iter->first), iter->second);
}
void CSMWorld::CreateCommand::undo()

@ -4,17 +4,20 @@
#include "record.hpp"
#include <string>
#include <map>
#include <QVariant>
#include <QUndoCommand>
#include <QModelIndex>
#include "universalid.hpp"
class QModelIndex;
class QAbstractItemModel;
namespace CSMWorld
{
class IdTableProxyModel;
class IdTable;
class IdTable;
class RecordBase;
@ -37,12 +40,18 @@ namespace CSMWorld
class CreateCommand : public QUndoCommand
{
IdTableProxyModel& mModel;
IdTable& mModel;
std::string mId;
UniversalId::Type mType;
std::map<int, QVariant> mValues;
public:
CreateCommand (IdTableProxyModel& model, const std::string& id, QUndoCommand *parent = 0);
CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent = 0);
void setType (UniversalId::Type type);
void addValue (int column, const QVariant& value);
virtual void redo();

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

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

@ -116,13 +116,13 @@ QModelIndex CSMWorld::IdTable::parent (const QModelIndex& index) const
return QModelIndex();
}
void CSMWorld::IdTable::addRecord (const std::string& id)
void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex();
beginInsertRows (QModelIndex(), index, index);
mIdCollection->appendBlankRecord (id);
mIdCollection->appendBlankRecord (id, type);
endInsertRows();
}

@ -3,6 +3,8 @@
#include <QAbstractItemModel>
#include "universalid.hpp"
namespace CSMWorld
{
class CollectionBase;
@ -44,7 +46,8 @@ namespace CSMWorld
virtual QModelIndex parent (const QModelIndex& index) const;
void addRecord (const std::string& id);
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types
QModelIndex getModelIndex (const std::string& id, int column) const;

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

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

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

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

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

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

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

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

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

@ -78,71 +78,88 @@ void CSVDoc::View::setupViewMenu()
QAction *newWindow = new QAction (tr ("&New View"), this);
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
view->addAction (newWindow);
mShowStatusBar = new QAction (tr ("Show Status Bar"), this);
mShowStatusBar->setCheckable (true);
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
view->addAction (mShowStatusBar);
}
void CSVDoc::View::setupWorldMenu()
{
QMenu *world = menuBar()->addMenu (tr ("&World"));
QAction *regions = new QAction (tr ("Regions"), this);
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
world->addAction (regions);
QAction *cells = new QAction (tr ("Cells"), this);
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
world->addAction (cells);
QAction *referenceables = new QAction (tr ("Referenceables"), this);
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
world->addAction (referenceables);
QAction *references = new QAction (tr ("References"), this);
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addAction (references);
world->addSeparator(); // items that don't represent single record lists follow here
QAction *regionMap = new QAction (tr ("Region Map"), this);
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
world->addAction (regionMap);
}
void CSVDoc::View::setupMechanicsMenu()
{
QMenu *mechanics = menuBar()->addMenu (tr ("&Mechanics"));
QAction *globals = new QAction (tr ("Globals"), this);
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
world->addAction (globals);
mechanics->addAction (globals);
QAction *gmsts = new QAction (tr ("Game settings"), this);
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
world->addAction (gmsts);
mechanics->addAction (gmsts);
QAction *skills = new QAction (tr ("Skills"), this);
connect (skills, SIGNAL (triggered()), this, SLOT (addSkillsSubView()));
world->addAction (skills);
mechanics->addAction (skills);
QAction *classes = new QAction (tr ("Classes"), this);
connect (classes, SIGNAL (triggered()), this, SLOT (addClassesSubView()));
world->addAction (classes);
mechanics->addAction (classes);
QAction *factions = new QAction (tr ("Factions"), this);
connect (factions, SIGNAL (triggered()), this, SLOT (addFactionsSubView()));
world->addAction (factions);
mechanics->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);
mechanics->addAction (races);
QAction *scripts = new QAction (tr ("Scripts"), this);
connect (scripts, SIGNAL (triggered()), this, SLOT (addScriptsSubView()));
world->addAction (scripts);
QAction *regions = new QAction (tr ("Regions"), this);
connect (regions, SIGNAL (triggered()), this, SLOT (addRegionsSubView()));
world->addAction (regions);
mechanics->addAction (scripts);
QAction *birthsigns = new QAction (tr ("Birthsigns"), this);
connect (birthsigns, SIGNAL (triggered()), this, SLOT (addBirthsignsSubView()));
world->addAction (birthsigns);
mechanics->addAction (birthsigns);
QAction *spells = new QAction (tr ("Spells"), this);
connect (spells, SIGNAL (triggered()), this, SLOT (addSpellsSubView()));
world->addAction (spells);
QAction *cells = new QAction (tr ("Cells"), this);
connect (cells, SIGNAL (triggered()), this, SLOT (addCellsSubView()));
world->addAction (cells);
QAction *referenceables = new QAction (tr ("Referenceables"), this);
connect (referenceables, SIGNAL (triggered()), this, SLOT (addReferenceablesSubView()));
world->addAction (referenceables);
mechanics->addAction (spells);
}
QAction *references = new QAction (tr ("References"), this);
connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView()));
world->addAction (references);
void CSVDoc::View::setupAssetsMenu()
{
QMenu *assets = menuBar()->addMenu (tr ("&Assets"));
QAction *regionMap = new QAction (tr ("Region Map"), this);
connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView()));
world->addAction (regionMap);
QAction *sounds = new QAction (tr ("Sounds"), this);
connect (sounds, SIGNAL (triggered()), this, SLOT (addSoundsSubView()));
assets->addAction (sounds);
}
void CSVDoc::View::setupUi()
@ -151,6 +168,8 @@ void CSVDoc::View::setupUi()
setupEditMenu();
setupViewMenu();
setupWorldMenu();
setupMechanicsMenu();
setupAssetsMenu();
}
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 height = CSMSettings::UserSettings::instance().getSetting(QString("Window Size"), QString("Height"));
if(width==QString() || height==QString())
resize(800, 600);
else
resize (width.toInt(), height.toInt());
resize (width.toInt(), height.toInt());
mSubViewWindow.setDockOptions (QMainWindow::AllowNestedDocks);
@ -271,6 +287,9 @@ void CSVDoc::View::addSubView (const CSMWorld::UniversalId& id)
/// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis)
SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
view->setStatusBar (mShowStatusBar->isChecked());
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this,
@ -425,3 +444,12 @@ void CSVDoc::View::updateEditorSetting (const QString &settingName, const QStrin
else if (settingName == "Height")
resizeViewHeight (settingValue.toInt());
}
void CSVDoc::View::toggleShowStatusBar (bool show)
{
foreach (QObject *view, mSubViewWindow.children())
{
if (CSVDoc::SubView *subView = dynamic_cast<CSVDoc::SubView *> (view))
subView->setStatusBar (show);
}
}

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

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

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

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

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

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

@ -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

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

@ -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

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

@ -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

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

@ -0,0 +1,23 @@
#ifndef CSV_WORLD_IDVALIDATOR_H
#define CSV_WORLD_IDVALIDATOR_H
#include <QValidator>
namespace CSVWorld
{
class IdValidator : public QValidator
{
private:
bool isValid (const QChar& c, bool first) const;
public:
IdValidator (QObject *parent = 0);
virtual State validate (QString& input, int& pos) const;
};
}
#endif

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

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

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

@ -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

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

@ -0,0 +1,40 @@
#ifndef CSV_WORLD_REFERENCECREATOR_H
#define CSV_WORLD_REFERENCECREATOR_H
#include "genericcreator.hpp"
class QLineEdit;
namespace CSVWorld
{
class ReferenceCreator : public GenericCreator
{
Q_OBJECT
QLineEdit *mCell;
std::string mId;
private:
virtual std::string getId() const;
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public:
ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
virtual void reset();
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.
private slots:
void cellChanged();
};
}
#endif

@ -27,31 +27,20 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto
{
UidTypeList list;
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Activator, ":./activator.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Potion, ":./potion.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Apparatus, ":./apparatus.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Armor, ":./armor.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Book, ":./book.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Clothing, ":./clothing.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Container, ":./container.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Creature, ":./creature.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Door, ":./door.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Ingredient, ":./ingredient.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_CreatureLevelledList, ":./creature.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_ItemLevelledList, ":./item.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Light, ":./light.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Lockpick, ":./lockpick.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Miscellaneous, ":./misc.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Npc, ":./npc.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Probe, ":./probe.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Repair, ":./repair.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Static, ":./static.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Weapon, ":./weapon.png"));
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
iter!=types.end(); ++iter)
{
CSMWorld::UniversalId id (*iter, "");
list.push_back (std::make_pair (id.getType(), id.getIcon().c_str()));
}
return list;
}
void CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
bool CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingName, const QString &settingValue)
{
if (settingName == "Referenceable ID Type Display")
{
@ -63,5 +52,9 @@ void CSVWorld::RefIdTypeDelegate::updateEditorSetting (const QString &settingNam
else if (settingValue == "Text Only")
mDisplayMode = Mode_TextOnly;
return true;
}
return false;
}

@ -13,7 +13,7 @@ namespace CSVWorld
public:
RefIdTypeDelegate (const ValueList &mValues, const IconList &icons, QUndoStack& undoStack, QObject *parent);
void updateEditorSetting (const QString &settingName, const QString &settingValue);
virtual bool updateEditorSetting (const QString &settingName, const QString &settingValue);
};

@ -7,14 +7,20 @@
#include "dialoguesubview.hpp"
#include "scriptsubview.hpp"
#include "regionmapsubview.hpp"
#include "genericcreator.hpp"
#include "cellcreator.hpp"
#include "referenceablecreator.hpp"
#include "referencecreator.hpp"
void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
{
// Regular record tables (including references which are actually sub-records, but are promoted
// to top-level records within the editor)
manager.add (CSMWorld::UniversalId::Type_Gmsts,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
manager.add (CSMWorld::UniversalId::Type_Skills,
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
new CSVDoc::SubViewFactoryWithCreator<TableSubView, NullCreatorFactory>);
static const CSMWorld::UniversalId::Type sTableTypes[] =
{
@ -27,20 +33,26 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
CSMWorld::UniversalId::Type_Regions,
CSMWorld::UniversalId::Type_Birthsigns,
CSMWorld::UniversalId::Type_Spells,
CSMWorld::UniversalId::Type_Cells,
CSMWorld::UniversalId::Type_Referenceables,
CSMWorld::UniversalId::Type_References,
CSMWorld::UniversalId::Type_None // end marker
};
for (int i=0; sTableTypes[i]!=CSMWorld::UniversalId::Type_None; ++i)
manager.add (sTableTypes[i], new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (true));
manager.add (sTableTypes[i],
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<GenericCreator> >);
manager.add (CSMWorld::UniversalId::Type_Cells,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<CellCreator> >);
manager.add (CSMWorld::UniversalId::Type_Referenceables,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceableCreator> >);
manager.add (CSMWorld::UniversalId::Type_References,
new CSVDoc::SubViewFactoryWithCreator<TableSubView, CreatorFactory<ReferenceCreator> >);
// Subviews for editing/viewing individual records
manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory<ScriptSubView>);
// Other stuff (combined record tables)
manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory<RegionMapSubView>);
// manager.add (CSMWorld::UniversalId::Type_Global,
// new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
}

@ -26,6 +26,9 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (!mEditLock)
{
if (selectedRows.size()==1)
menu.addAction (mEditAction);
if (mCreateAction)
menu.addAction (mCreateAction);
@ -41,19 +44,30 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
/// \todo Do not use hardcoded column numbers
std::vector<std::string> revertableIds;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
if (mProxyModel->columnCount()>0)
{
std::string id = mProxyModel->data (*iter).toString().toStdString();
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
if (state!=CSMWorld::RecordBase::State_BaseOnly)
{
std::string id = mModel->data (mModel->index (index.row(), 0)).
toString().toUtf8().constData();
if (state!=CSMWorld::RecordBase::State_BaseOnly)
revertableIds.push_back (id);
revertableIds.push_back (id);
}
}
}
return revertableIds;
@ -61,19 +75,30 @@ std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() 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;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter)
if (mProxyModel->columnCount()>0)
{
std::string id = mProxyModel->data (*iter).toString().toStdString();
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted)
{
std::string id = mModel->data (mModel->index (index.row(), 0)).
toString().toUtf8().constData();
if (state!=CSMWorld::RecordBase::State_Deleted)
deletableIds.push_back (id);
deletableIds.push_back (id);
}
}
}
return deletableIds;
@ -116,12 +141,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
hideColumn (i);
}
/// \todo make initial layout fill the whole width of the table
mEditAction = new QAction (tr ("Edit Record"), this);
connect (mEditAction, SIGNAL (triggered()), this, SLOT (editRecord()));
addAction (mEditAction);
if (createAndDelete)
{
mCreateAction = new QAction (tr ("Add Record"), this);
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord()));
connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
addAction (mCreateAction);
}
@ -132,6 +159,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
mDeleteAction = new QAction (tr ("Delete Record"), this);
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
addAction (mDeleteAction);
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate()));
/// \note This signal could instead be connected to a slot that filters out changes not affecting
/// the records status column (for permanence reasons)
connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (tableSizeUpdate()));
connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
this, SLOT (selectionSizeUpdate ()));
}
void CSVWorld::Table::setEditLock (bool locked)
@ -149,22 +187,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString());
}
#include <sstream> /// \todo remove
void CSVWorld::Table::createRecord()
{
if (!mEditLock)
{
/// \todo ask the user for an ID instead.
static int index = 0;
std::ostringstream stream;
stream << "id" << index++;
mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str()));
}
}
void CSVWorld::Table::revertRecord()
{
if (!mEditLock)
@ -205,21 +227,67 @@ void CSVWorld::Table::deleteRecord()
}
}
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
void CSVWorld::Table::editRecord()
{
if (settingName == "Record Status Display")
if (!mEditLock)
{
RecordStatusDelegate &rsDelegate = dynamic_cast <CSVWorld::RecordStatusDelegate &> (*itemDelegateForColumn(1));
QModelIndexList selectedRows = selectionModel()->selectedRows();
rsDelegate.updateEditorSetting (settingName, settingValue);
emit dataChanged(mModel->index(0,1), mModel->index(mModel->rowCount()-1, 1));
if (selectedRows.size()==1)
emit editRequest (selectedRows.begin()->row());
}
}
void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QString &settingValue)
{
int columns = mModel->columnCount();
for (int i=0; i<columns; ++i)
if (QAbstractItemDelegate *delegate = itemDelegateForColumn (i))
if (dynamic_cast<CommandDelegate&> (*delegate).
updateEditorSetting (settingName, settingValue))
emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i));
}
if (settingName == "Referenceable ID Type Display")
void CSVWorld::Table::tableSizeUpdate()
{
int size = 0;
int deleted = 0;
int modified = 0;
if (mModel->columnCount()>0)
{
RefIdTypeDelegate &refidDelegate = dynamic_cast <CSVWorld::RefIdTypeDelegate &> (*itemDelegateForColumn(2));
int rows = mModel->rowCount();
refidDelegate.updateEditorSetting (settingName, settingValue);
emit dataChanged(mModel->index(0,1), mModel->index(mModel->rowCount()-1, 1));
for (int i=0; i<rows; ++i)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));
/// \todo Do not use hardcoded column numbers
int state = mModel->data (mModel->index (index.row(), 1)).toInt();
switch (state)
{
case CSMWorld::RecordBase::State_BaseOnly: ++size; break;
case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;
case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;
case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;
}
}
}
tableSizeChanged (size, deleted, modified);
}
void CSVWorld::Table::selectionSizeUpdate()
{
selectionSizeChanged (selectionModel()->selectedRows().size());
}
void CSVWorld::Table::requestFocus (const std::string& id)
{
QModelIndex index = mProxyModel->getModelIndex (id, 0);
if (index.isValid())
scrollTo (index, QAbstractItemView::PositionAtTop);
}

@ -28,6 +28,7 @@ namespace CSVWorld
std::vector<CommandDelegate *> mDelegates;
QUndoStack& mUndoStack;
QAction *mEditAction;
QAction *mCreateAction;
QAction *mRevertAction;
QAction *mDeleteAction;
@ -55,13 +56,35 @@ namespace CSVWorld
void updateEditorSetting (const QString &settingName, const QString &settingValue);
private slots:
signals:
void editRequest (int row);
void selectionSizeChanged (int size);
void createRecord();
void tableSizeChanged (int size, int deleted, int modified);
///< \param size Number of not deleted records
/// \param deleted Number of deleted records
/// \param modified Number of added and modified records
void createRequest();
private slots:
void revertRecord();
void deleteRecord();
void editRecord();
public slots:
void tableSizeUpdate();
void selectionSizeUpdate();
void requestFocus (const std::string& id);
};
}

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

@ -0,0 +1,82 @@
#ifndef CSV_WORLD_BOTTOMBOX_H
#define CSV_WORLD_BOTTOMBOX_H
#include <QWidget>
class QLabel;
class QStackedLayout;
class QStatusBar;
class QUndoStack;
namespace CSMWorld
{
class Data;
class UniversalId;
}
namespace CSVWorld
{
class CreatorFactoryBase;
class Creator;
class TableBottomBox : public QWidget
{
Q_OBJECT
bool mShowStatusBar;
QLabel *mStatus;
QStatusBar *mStatusBar;
int mStatusCount[4];
Creator *mCreator;
bool mCreating;
QStackedLayout *mLayout;
private:
// not implemented
TableBottomBox (const TableBottomBox&);
TableBottomBox& operator= (const TableBottomBox&);
void updateStatus();
public:
TableBottomBox (const CreatorFactoryBase& creatorFactory, CSMWorld::Data& data,
QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent = 0);
virtual ~TableBottomBox();
void setEditLock (bool locked);
void setStatusBar (bool show);
bool canCreateAndDelete() const;
///< Is record creation and deletion supported?
///
/// \note The BotomBox does not partake in the deletion of records.
signals:
void requestFocus (const std::string& id);
///< Request owner of this box to focus the just created \a id. The owner may
/// ignore this request.
private slots:
void createRequestDone();
///< \note This slot being called does not imply success.
public slots:
void selectionSizeChanged (int size);
void tableSizeChanged (int size, int deleted, int modified);
///< \param size Number of not deleted records
/// \param deleted Number of deleted records
/// \param modified Number of added and modified records
void createRequest();
};
}
#endif

@ -1,32 +1,68 @@
#include "tablesubview.hpp"
#include <QVBoxLayout>
#include "../../model/doc/document.hpp"
#include "table.hpp"
#include "tablebottombox.hpp"
#include "creator.hpp"
CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
bool createAndDelete)
const CreatorFactoryBase& creatorFactory)
: SubView (id)
{
setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete));
QVBoxLayout *layout = new QVBoxLayout;
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 (doubleClicked (const QModelIndex&)), this, SLOT (rowActivated (const QModelIndex&)));
connect (mTable, SIGNAL (editRequest (int)), this, SLOT (editRequest (int)));
connect (mTable, SIGNAL (selectionSizeChanged (int)),
mBottom, SLOT (selectionSizeChanged (int)));
connect (mTable, SIGNAL (tableSizeChanged (int, int, int)),
mBottom, SLOT (tableSizeChanged (int, int, int)));
mTable->tableSizeUpdate();
mTable->selectionSizeUpdate();
if (mBottom->canCreateAndDelete())
connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest()));
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&)));
}
void CSVWorld::TableSubView::setEditLock (bool locked)
{
mTable->setEditLock (locked);
mBottom->setEditLock (locked);
}
void CSVWorld::TableSubView::rowActivated (const QModelIndex& index)
void CSVWorld::TableSubView::editRequest (int row)
{
focusId (mTable->getUniversalId (index.row()));
focusId (mTable->getUniversalId (row));
}
void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, const QString &settingValue)
{
if ( (settingName == "Record Status Display") || settingName == "Referenceable ID Type Display" )
mTable->updateEditorSetting(settingName, settingValue);
mTable->updateEditorSetting(settingName, settingValue);
}
void CSVWorld::TableSubView::setStatusBar (bool show)
{
mBottom->setStatusBar (show);
}

@ -13,22 +13,30 @@ namespace CSMDoc
namespace CSVWorld
{
class Table;
class TableBottomBox;
class CreatorFactoryBase;
class TableSubView : public CSVDoc::SubView
{
Q_OBJECT
Table *mTable;
TableBottomBox *mBottom;
public:
TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, bool createAndDelete);
TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory);
virtual void setEditLock (bool locked);
void updateEditorSetting (const QString &, const QString &);
virtual void updateEditorSetting (const QString& key, const QString& value);
virtual void setStatusBar (bool show);
private slots:
void rowActivated (const QModelIndex& index);
void editRequest (int row);
};
}

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

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

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

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

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

@ -201,6 +201,8 @@ namespace MWBase
virtual void setMoonColour (bool red) = 0;
virtual void modRegion(const std::string &regionid, const std::vector<char> &chances) = 0;
virtual float getTimeScaleFactor() const = 0;
virtual void changeToInteriorCell (const std::string& cellName,
@ -218,6 +220,10 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range
/// Returns a pointer to the object the provided object is facing (if within the
/// specified distance). This will attempt to use the "Bip01 Head" node as a basis.
virtual MWWorld::Ptr getFacedObject(const MWWorld::Ptr &ptr, float distance) = 0;
virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
///< Adjust position after load to be on ground. Must be called after model load.
@ -327,6 +333,7 @@ namespace MWBase
virtual void togglePlayerLooking(bool enable) = 0;
virtual void changeVanityModeScale(float factor) = 0;
virtual bool vanityRotateCamera(float * rot) = 0;
virtual void setCameraDistance(float dist, bool adjust = false, bool override = true)=0;
virtual void setupPlayer() = 0;
virtual void renderPlayer() = 0;
@ -371,6 +378,12 @@ namespace MWBase
/// Find default position inside interior cell specified by name
/// \return false if interior with given name not exists, true otherwise
virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos) = 0;
/// Enables or disables use of teleport spell effects (recall, intervention, etc).
virtual void enableTeleporting(bool enable) = 0;
/// Returns true if teleport spell effects are allowed.
virtual bool isTeleportingEnabled() const = 0;
};
}

@ -46,7 +46,11 @@ namespace MWClass
{
std::auto_ptr<CustomData> data (new CustomData);
// \todo add initial container content
MWWorld::LiveCellRef<ESM::Container> *ref =
ptr.get<ESM::Container>();
data->mContainerStore.fill(
ref->mBase->mInventory, ptr.getCellRef().mOwner, MWBase::Environment::get().getWorld()->getStore());
// store
ptr.getRefData().setCustomData (data.release());

@ -11,6 +11,7 @@
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp"
@ -78,6 +79,8 @@ namespace MWClass
data->mCreatureStats.setLevel(ref->mBase->mData.mLevel);
data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
@ -88,6 +91,10 @@ namespace MWClass
iter!=ref->mBase->mSpells.mList.end(); ++iter)
data->mCreatureStats.getSpells().add (*iter);
// inventory
data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr),
MWBase::Environment::get().getWorld()->getStore());
// store
ptr.getRefData().setCustomData (data.release());
}
@ -148,6 +155,70 @@ namespace MWClass
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
}
void Creature::hit(const MWWorld::Ptr& ptr, int type) const
{
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
{
// NOTE: 'object' and/or 'attacker' may be empty.
if(!successful)
{
// TODO: Handle HitAttemptOnMe script function
// Missed
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
if(!object.isEmpty())
getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object));
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
{
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
if(!script.empty())
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
}
if(ishealth)
{
if(damage > 0.0f)
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage);
getCreatureStats(ptr).setFatigue(fatigue);
}
}
void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
{
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
bool wasDead = crstats.isDead();
MWMechanics::DynamicStat<float> stat(crstats.getHealth());
stat.setCurrent(health);
crstats.setHealth(stat);
if(!wasDead && crstats.isDead())
{
// actor was just killed
}
else if(wasDead && !crstats.isDead())
{
// actor was just resurrected
}
}
boost::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const
{
@ -291,6 +362,33 @@ namespace MWClass
return ref->mBase->mPersistent;
}
std::string Creature::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
{
const MWWorld::Store<ESM::SoundGenerator> &store = MWBase::Environment::get().getWorld()->getStore().get<ESM::SoundGenerator>();
int type = getSndGenTypeFromName(ptr, name);
if(type >= 0)
{
std::vector<const ESM::SoundGenerator*> sounds;
sounds.reserve(8);
std::string ptrid = Creature::getId(ptr);
MWWorld::Store<ESM::SoundGenerator>::iterator sound = store.begin();
while(sound != store.end())
{
if(type == sound->mType && sound->mCreature.size() > 0 &&
Misc::StringUtils::ciEqual(ptrid.substr(0, sound->mCreature.size()),
sound->mCreature))
sounds.push_back(&*sound);
sound++;
}
if(sounds.size() > 0)
return sounds[(int)(rand()/(RAND_MAX+1.0)*sounds.size())]->mSound;
}
return "";
}
MWWorld::Ptr
Creature::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{
@ -300,6 +398,40 @@ namespace MWClass
return MWWorld::Ptr(&cell.mCreatures.insert(*ref), &cell);
}
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
{
if(name == "left")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos))
return 2;
if(world->isOnGround(ptr))
return 0;
return -1;
}
if(name == "right")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos))
return 3;
if(world->isOnGround(ptr))
return 1;
return -1;
}
if(name == "moan")
return 4;
if(name == "roar")
return 5;
if(name == "scream")
return 6;
if(name == "land")
return 7;
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
}

@ -12,6 +12,8 @@ namespace MWClass
virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);
static const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature;
@ -40,6 +42,12 @@ namespace MWClass
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
///< Return creature stats
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< Generate action for activation
@ -69,6 +77,8 @@ namespace MWClass
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
virtual MWMechanics::Movement& getMovementSettings (const MWWorld::Ptr& ptr) const;
///< Return desired movement.

@ -53,7 +53,9 @@ namespace MWClass
physics.addObject(ptr,ref->mBase->mData.mFlags & ESM::Light::Carry);
if (!ref->mBase->mSound.empty())
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0, MWBase::SoundManager::Play_Loop);
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0,
MWBase::SoundManager::Play_TypeSfx,
MWBase::SoundManager::Play_Loop);
}
std::string Light::getModel(const MWWorld::Ptr &ptr) const

@ -13,6 +13,8 @@
#include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
@ -109,6 +111,23 @@ namespace
creatureStats.getAttribute(attribute).setBase ( std::min(creatureStats.getAttribute(attribute).getBase()
+ static_cast<int>((level-1) * modifierSum+0.5), 100) );
}
// initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
int multiplier = 3;
if (class_->mData.mSpecialization == ESM::Class::Combat)
multiplier += 2;
else if (class_->mData.mSpecialization == ESM::Class::Stealth)
multiplier += 1;
if (class_->mData.mAttribute[0] == ESM::Attribute::Endurance
|| class_->mData.mAttribute[1] == ESM::Attribute::Endurance)
multiplier += 1;
creatureStats.setHealth(static_cast<int> (0.5 * (strength + endurance)) + multiplier * (creatureStats.getLevel() - 1));
}
}
@ -196,6 +215,8 @@ namespace MWClass
autoCalculateAttributes(ref->mBase, data->mCreatureStats);
}
data->mCreatureStats.getAiSequence().fill(ref->mBase->mAiPackage);
data->mCreatureStats.setAiSetting (0, ref->mBase->mAiData.mHello);
data->mCreatureStats.setAiSetting (1, ref->mBase->mAiData.mFight);
data->mCreatureStats.setAiSetting (2, ref->mBase->mAiData.mFlee);
@ -206,6 +227,10 @@ namespace MWClass
iter!=ref->mBase->mSpells.mList.end(); ++iter)
data->mCreatureStats.getSpells().add (*iter);
// inventory
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr),
MWBase::Environment::get().getWorld()->getStore());
// store
ptr.getRefData().setCustomData (data.release());
}
@ -283,6 +308,243 @@ namespace MWClass
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mNpcStats;
}
void Npc::hit(const MWWorld::Ptr& ptr, int type) const
{
MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
// Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
MWWorld::Ptr weapon = ((weaponslot != inv.end()) ? *weaponslot : MWWorld::Ptr());
if(!weapon.isEmpty() && weapon.getTypeName() != typeid(ESM::Weapon).name())
weapon = MWWorld::Ptr();
float dist = 100.0f * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat());
MWWorld::Ptr victim = world->getFacedObject(ptr, dist);
if(victim.isEmpty()) // Didn't hit anything
return;
const MWWorld::Class &othercls = MWWorld::Class::get(victim);
if(!othercls.isActor() || othercls.getCreatureStats(victim).isDead())
{
// Can't hit non-actors, or dead actors
return;
}
if(ptr.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->setEnemy(victim);
int weapskill = ESM::Skill::HandToHand;
if(!weapon.isEmpty())
weapskill = MWWorld::Class::get(weapon).getEquipmentSkill(weapon);
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
MWMechanics::NpcStats &npcstats = getNpcStats(ptr);
const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects();
float hitchance = npcstats.getSkill(weapskill).getModified() +
(crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= crstats.getFatigueTerm();
hitchance += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::FortifyAttack)).mMagnitude -
mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Blind)).mMagnitude;
hitchance -= othercls.getEvasion(victim);
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
othercls.onHit(victim, 0.0f, false, weapon, ptr, false);
return;
}
bool healthdmg;
float damage = 0.0f;
if(!weapon.isEmpty())
{
const bool weaphashealth = get(weapon).hasItemHealth(weapon);
const unsigned char *attack = NULL;
if(type == MWMechanics::CreatureStats::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == MWMechanics::CreatureStats::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == MWMechanics::CreatureStats::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack)
{
damage = attack[0] + ((attack[1]-attack[0])*npcstats.getAttackStrength());
damage *= 0.5f + (crstats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
if(weaphashealth)
{
int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth;
if(weapon.getCellRef().mCharge == -1)
weapon.getCellRef().mCharge = weapmaxhealth;
damage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
}
if(!othercls.hasDetected(victim, ptr))
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
weapon.getCellRef().mCharge -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())),
weapon.getCellRef().mCharge);
}
healthdmg = true;
}
else
{
// Note: MCP contains an option to include Strength in hand-to-hand damage
// calculations. Some mods recommend using it, so we may want to include am
// option for it.
float minstrike = gmst.find("fMinHandToHandMult")->getFloat();
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat();
damage = npcstats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*npcstats.getAttackStrength());
if(!othercls.hasDetected(victim, ptr))
{
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
}
healthdmg = (othercls.getCreatureStats(victim).getFatigue().getCurrent() < 1.0f ||
npcstats.isWerewolf());
if(healthdmg)
damage *= gmst.find("fHandtoHandHealthPer")->getFloat();
}
if(ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, weapskill, 0);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
}
void Npc::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
// NOTE: 'object' and/or 'attacker' may be empty.
if(!successful)
{
// TODO: Handle HitAttemptOnMe script function
// Missed
sndMgr->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
if(!object.isEmpty())
getCreatureStats(ptr).setLastHitObject(get(object).getId(object));
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
{
const std::string &script = ptr.get<ESM::NPC>()->mBase->mScript;
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
if(!script.empty())
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
}
if(damage > 0.0f)
{
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
// something, alert the character controller, scripts, etc.
MWBase::Environment::get().getDialogueManager()->say(ptr, "hit");
if(object.isEmpty())
{
if(ishealth)
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
sndMgr->playSound3D(ptr, "Hand To Hand Hit", 1.0f, 1.0f);
}
else if(ishealth)
{
// Hit percentages:
// cuirass = 30%
// shield, helmet, greaves, boots, pauldrons = 10% each
// guantlets = 5% each
static const int hitslots[20] = {
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
MWWorld::InventoryStore::Slot_Cuirass, MWWorld::InventoryStore::Slot_Cuirass,
MWWorld::InventoryStore::Slot_CarriedLeft, MWWorld::InventoryStore::Slot_CarriedLeft,
MWWorld::InventoryStore::Slot_Helmet, MWWorld::InventoryStore::Slot_Helmet,
MWWorld::InventoryStore::Slot_Greaves, MWWorld::InventoryStore::Slot_Greaves,
MWWorld::InventoryStore::Slot_Boots, MWWorld::InventoryStore::Slot_Boots,
MWWorld::InventoryStore::Slot_LeftPauldron, MWWorld::InventoryStore::Slot_LeftPauldron,
MWWorld::InventoryStore::Slot_RightPauldron, MWWorld::InventoryStore::Slot_RightPauldron,
MWWorld::InventoryStore::Slot_LeftGauntlet, MWWorld::InventoryStore::Slot_RightGauntlet
};
int hitslot = hitslots[(int)(::rand()/(RAND_MAX+1.0)*20.0)];
float damagediff = damage;
damage /= std::min(1.0f + getArmorRating(ptr)/std::max(1.0f, damage), 4.0f);
damagediff -= damage;
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
{
ESM::CellRef &armorref = armor.getCellRef();
if(armorref.mCharge == -1)
armorref.mCharge = armor.get<ESM::Armor>()->mBase->mData.mHealth;
armorref.mCharge -= std::min(std::max(1, (int)damagediff),
armorref.mCharge);
switch(get(armor).getEquipmentSkill(armor))
{
case ESM::Skill::LightArmor:
sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::MediumArmor:
sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::HeavyArmor:
sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f);
break;
}
}
}
}
if(ishealth)
{
if(damage > 0.0f)
sndMgr->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage);
getCreatureStats(ptr).setFatigue(fatigue);
}
}
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
{
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
bool wasDead = crstats.isDead();
MWMechanics::DynamicStat<float> stat(crstats.getHealth());
stat.setCurrent(health);
crstats.setHealth(stat);
if(!wasDead && crstats.isDead())
{
// actor was just killed
}
else if(wasDead && !crstats.isDead())
{
// actor was just resurrected
}
}
boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const
{
@ -587,18 +849,19 @@ namespace MWClass
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
{
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr);
const MWWorld::Store<ESM::GameSetting> &gmst =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
int ratings[MWWorld::InventoryStore::Slots];
MWMechanics::NpcStats &stats = getNpcStats(ptr);
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified();
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i)
int ratings[MWWorld::InventoryStore::Slots];
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
{
MWWorld::ContainerStoreIterator it = invStore.getSlot(i);
if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())
@ -608,28 +871,27 @@ namespace MWClass
}
else
{
MWWorld::LiveCellRef<ESM::Armor> *ref =
it->get<ESM::Armor>();
MWWorld::LiveCellRef<ESM::Armor> *ref = it->get<ESM::Armor>();
int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it);
int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified();
int armorSkill = stats.getSkill(armorSkillType).getModified();
if (ref->mBase->mData.mWeight == 0)
if(ref->mBase->mData.mWeight == 0)
ratings[i] = ref->mBase->mData.mArmor;
else
ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill;
}
}
float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
float shield = getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3
return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f
+ (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]
+ ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]
+ ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]
) * 0.1
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet)
* 0.05
) * 0.1f
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet])
* 0.05f
+ shield;
}
@ -663,6 +925,74 @@ namespace MWClass
return 0;
}
std::string Npc::getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const
{
if(name == "left")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos))
return "FootWaterLeft";
if(world->isOnGround(ptr))
{
MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())
return "FootBareLeft";
switch(Class::get(*boots).getEquipmentSkill(*boots))
{
case ESM::Skill::LightArmor:
return "FootLightLeft";
case ESM::Skill::MediumArmor:
return "FootMedLeft";
case ESM::Skill::HeavyArmor:
return "FootHeavyLeft";
}
}
return "";
}
if(name == "right")
{
MWBase::World *world = MWBase::Environment::get().getWorld();
Ogre::Vector3 pos(ptr.getRefData().getPosition().pos);
if(world->isUnderwater(ptr.getCell(), pos))
return "FootWaterRight";
if(world->isOnGround(ptr))
{
MWWorld::InventoryStore &inv = Npc::getInventoryStore(ptr);
MWWorld::ContainerStoreIterator boots = inv.getSlot(MWWorld::InventoryStore::Slot_Boots);
if(boots == inv.end() || boots->getTypeName() != typeid(ESM::Armor).name())
return "FootBareRight";
switch(Class::get(*boots).getEquipmentSkill(*boots))
{
case ESM::Skill::LightArmor:
return "FootLightRight";
case ESM::Skill::MediumArmor:
return "FootMedRight";
case ESM::Skill::HeavyArmor:
return "FootHeavyRight";
}
}
return "";
}
// TODO: I have no idea what these are supposed to do for NPCs since they use
// voiced dialog for various conditions like health loss and combat taunts. Maybe
// only for biped creatures?
if(name == "moan")
return "";
if(name == "roar")
return "";
if(name == "scream")
return "";
if(name == "land")
return "";
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}
MWWorld::Ptr
Npc::copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const
{

@ -68,6 +68,12 @@ namespace MWClass
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const;
///< Generate action for activation
@ -132,6 +138,8 @@ namespace MWClass
virtual bool isPersistent (const MWWorld::Ptr& ptr) const;
virtual std::string getSoundIdFromSndGen(const MWWorld::Ptr &ptr, const std::string &name) const;
static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const;

@ -22,6 +22,13 @@
namespace MWClass
{
std::string Weapon::getId (const MWWorld::Ptr& ptr) const
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = ptr.get<ESM::Weapon>();
return ref->mBase->mId;
}
void Weapon::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
const std::string model = getModel(ptr);

@ -12,6 +12,9 @@ namespace MWClass
public:
virtual std::string getId (const MWWorld::Ptr& ptr) const;
///< Return ID of \a ptr
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering

@ -25,6 +25,7 @@
#include "../mwbase/scriptmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
@ -557,6 +558,30 @@ namespace MWDialogue
return false;
}
void DialogueManager::say(const MWWorld::Ptr &actor, const std::string &topic) const
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!sndMgr->sayDone(actor))
{
// Actor is already saying something.
return;
}
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const ESM::Dialogue *dial = store.get<ESM::Dialogue>().find(topic);
Filter filter(actor, 0, false);
const ESM::DialInfo *info = filter.search(*dial, false);
if(info != NULL)
{
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
if(winMgr->getSubtitlesEnabled())
winMgr->messageBox(info->mResponse);
sndMgr->say(actor, info->mSound);
}
}
std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{
std::vector<HyperTextToken> result;

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

@ -120,7 +120,7 @@ namespace MWGui
void BookWindow::onTakeButtonClicked (MyGUI::Widget* sender)
{
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0);
MWWorld::ActionTake take(mBook);
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());

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

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

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

@ -9,6 +9,8 @@
#include "../mwworld/player.hpp"
#include "../mwworld/class.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "inventorywindow.hpp"
#include "console.hpp"
#include "spellicons.hpp"
@ -47,6 +49,7 @@ namespace MWGui
, mWeaponVisible(true)
, mSpellVisible(true)
, mWorldMouseOver(false)
, mEnemyHealthTimer(0)
{
setCoord(0,0, width, height);
@ -55,6 +58,7 @@ namespace MWGui
getWidget(mHealth, "Health");
getWidget(mMagicka, "Magicka");
getWidget(mStamina, "Stamina");
getWidget(mEnemyHealth, "EnemyHealth");
mHealthManaStaminaBaseLeft = mHealthFrame->getLeft();
MyGUI::Widget *healthFrame, *magickaFrame, *fatigueFrame;
@ -320,6 +324,13 @@ namespace MWGui
mCellNameBox->setVisible(false);
if (mWeaponSpellTimer < 0)
mWeaponSpellBox->setVisible(false);
mEnemyHealthTimer -= dt;
if (mEnemyHealth->getVisible() && mEnemyHealthTimer < 0)
{
mEnemyHealth->setVisible(false);
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() + MyGUI::IntPoint(0,20));
}
}
void HUD::onResChange(int width, int height)
@ -535,6 +546,25 @@ namespace MWGui
void HUD::update()
{
mSpellIcons->updateWidgets(mEffectBox, true);
if (!mEnemy.isEmpty() && mEnemyHealth->getVisible())
{
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
mEnemyHealth->setProgressRange(100);
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
}
}
void HUD::setEnemy(const MWWorld::Ptr &enemy)
{
mEnemy = enemy;
mEnemyHealthTimer = 5;
if (!mEnemyHealth->getVisible())
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
mEnemyHealth->setVisible(true);
MWMechanics::CreatureStats& stats = MWWorld::Class::get(mEnemy).getCreatureStats(mEnemy);
mEnemyHealth->setProgressRange(100);
mEnemyHealth->setProgressPosition(stats.getHealth().getCurrent() / stats.getHealth().getModified() * 100);
}
}

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

@ -173,12 +173,7 @@ namespace MWGui
attribute.setBase(100);
}
// "When you gain a level, in addition to increasing three primary attributes, your Health
// will automatically increase by 10% of your Endurance attribute. If you increased Endurance this level,
// the Health increase is calculated from the increased Endurance"
creatureStats.increaseLevelHealthBonus (creatureStats.getAttribute(ESM::Attribute::Endurance).getBase() * 0.1f);
creatureStats.setLevel (creatureStats.getLevel()+1);
creatureStats.levelUp();
pcStats.levelUp ();
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Levelup);

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

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

@ -87,7 +87,7 @@ namespace MWGui
void ScrollWindow::onTakeButtonClicked (MyGUI::Widget* _sender)
{
MWBase::Environment::get().getSoundManager()->playSound ("Item Book Up", 1.0, 1.0, MWBase::SoundManager::Play_NoTrack);
MWBase::Environment::get().getSoundManager()->playSound("Item Book Up", 1.0, 1.0);
MWWorld::ActionTake take(mScroll);
take.execute (MWBase::Environment::get().getWorld()->getPlayer().getPlayer());

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

@ -216,7 +216,13 @@ namespace MWGui
void WaitDialog::setCanRest (bool canRest)
{
mUntilHealedButton->setVisible(canRest);
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayer().getPlayer();
MWMechanics::CreatureStats& stats = MWWorld::Class::get(player).getCreatureStats(player);
bool full = (stats.getFatigue().getCurrent() >= stats.getFatigue().getModified())
&& (stats.getHealth().getCurrent() >= stats.getHealth().getModified())
&& (stats.getMagicka().getCurrent() >= stats.getMagicka().getModified());
mUntilHealedButton->setVisible(canRest && !full);
mWaitButton->setCaptionWithReplacing (canRest ? "#{sRest}" : "#{sWait}");
mRestText->setCaptionWithReplacing (canRest ? "#{sRestMenu3}" : "#{sRestIllegal}");

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

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

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

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

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

@ -3,6 +3,12 @@
#include "aipackage.hpp"
#include "aiwander.hpp"
#include "aiescort.hpp"
#include "aitravel.hpp"
#include "aifollow.hpp"
#include "aiactivate.hpp"
void MWMechanics::AiSequence::copy (const AiSequence& sequence)
{
for (std::list<AiPackage *>::const_iterator iter (sequence.mPackages.begin());
@ -77,3 +83,40 @@ void MWMechanics::AiSequence::queue (const AiPackage& package)
{
mPackages.push_back (package.clone());
}
void MWMechanics::AiSequence::fill(const ESM::AIPackageList &list)
{
for (std::vector<ESM::AIPackage>::const_iterator it = list.mList.begin(); it != list.mList.end(); ++it)
{
MWMechanics::AiPackage* package;
if (it->mType == ESM::AI_Wander)
{
ESM::AIWander data = it->mWander;
std::vector<int> idles;
for (int i=0; i<8; ++i)
idles.push_back(data.mIdle[i]);
package = new MWMechanics::AiWander(data.mDistance, data.mDuration, data.mTimeOfDay, idles, data.mUnk);
}
else if (it->mType == ESM::AI_Escort)
{
ESM::AITarget data = it->mTarget;
package = new MWMechanics::AiEscort(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
}
else if (it->mType == ESM::AI_Travel)
{
ESM::AITravel data = it->mTravel;
package = new MWMechanics::AiTravel(data.mX, data.mY, data.mZ);
}
else if (it->mType == ESM::AI_Activate)
{
ESM::AIActivate data = it->mActivate;
package = new MWMechanics::AiActivate(data.mName.toString());
}
else //if (it->mType == ESM::AI_Follow)
{
ESM::AITarget data = it->mTarget;
package = new MWMechanics::AiFollow(data.mId.toString(), data.mDuration, data.mX, data.mY, data.mZ);
}
mPackages.push_back(package);
}
}

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

@ -61,27 +61,15 @@ struct StateInfo {
const char groupname[32];
};
static const StateInfo sStateList[] = {
{ CharState_Idle, "idle" },
{ CharState_Idle2, "idle2" },
{ CharState_Idle3, "idle3" },
{ CharState_Idle4, "idle4" },
{ CharState_Idle5, "idle5" },
{ CharState_Idle6, "idle6" },
{ CharState_Idle7, "idle7" },
{ CharState_Idle8, "idle8" },
{ CharState_Idle9, "idle9" },
{ CharState_IdleSwim, "idleswim" },
{ CharState_IdleSneak, "idlesneak" },
static const StateInfo sDeathList[] = {
{ CharState_Death1, "death1" },
{ CharState_Death2, "death2" },
{ CharState_Death3, "death3" },
{ CharState_Death4, "death4" },
{ CharState_Death5, "death5" },
{ CharState_SwimDeath, "swimdeath" },
};
static const StateInfo *sStateListEnd = &sStateList[sizeof(sStateList)/sizeof(sStateList[0])];
static const StateInfo *sDeathListEnd = &sDeathList[sizeof(sDeathList)/sizeof(sDeathList[0])];
static const StateInfo sMovementList[] = {
{ CharState_WalkForward, "walkforward" },
@ -169,7 +157,7 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
idle = "idleswim";
else if(mIdleState == CharState_IdleSneak && mAnimation->hasAnimation("idlesneak"))
idle = "idlesneak";
else
else if(mIdleState != CharState_None)
{
idle = "idle";
if(weap != sWeaponTypeListEnd)
@ -182,8 +170,9 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
mAnimation->disable(mCurrentIdle);
mCurrentIdle = idle;
mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false,
1.0f, "start", "stop", 0.0f, ~0ul);
if(!mCurrentIdle.empty())
mAnimation->play(mCurrentIdle, Priority_Default, MWRender::Animation::Group_All, false,
1.0f, "start", "stop", 0.0f, ~0ul);
}
if(force || movement != mMovementState)
@ -208,13 +197,13 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
if(!mAnimation->hasAnimation(movement))
{
std::string::size_type sneakpos = movement.find("sneak");
if(sneakpos == std::string::npos)
std::string::size_type swimpos = movement.find("swim");
if(swimpos == std::string::npos)
movement.clear();
else
{
movegroup = MWRender::Animation::Group_LowerBody;
movement.erase(sneakpos, 5);
movement.erase(swimpos, 4);
if(!mAnimation->hasAnimation(movement))
movement.clear();
}
@ -225,11 +214,11 @@ void CharacterController::refreshCurrentAnims(CharacterState idle, CharacterStat
mCurrentMovement = movement;
if(!mCurrentMovement.empty())
{
float vel, speed = 0.0f;
float vel, speedmult = 1.0f;
if(mMovementSpeed > 0.0f && (vel=mAnimation->getVelocity(mCurrentMovement)) > 1.0f)
speed = mMovementSpeed / vel;
speedmult = mMovementSpeed / vel;
mAnimation->play(mCurrentMovement, Priority_Movement, movegroup, false,
speed, "start", "stop", 0.0f, ~0ul);
speedmult, "start", "stop", 0.0f, ~0ul);
}
}
}
@ -243,10 +232,71 @@ void CharacterController::getWeaponGroup(WeaponType weaptype, std::string &group
}
MWWorld::ContainerStoreIterator CharacterController::getActiveWeapon(NpcStats &stats, MWWorld::InventoryStore &inv, WeaponType *weaptype)
{
if(stats.getDrawState() == DrawState_Spell)
{
*weaptype = WeapType_Spell;
return inv.end();
}
if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
{
MWWorld::ContainerStoreIterator weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end())
*weaptype = WeapType_HandToHand;
else
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
*weaptype = WeapType_PickProbe;
else if(type == typeid(ESM::Weapon).name())
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType;
switch(type)
{
case ESM::Weapon::ShortBladeOneHand:
case ESM::Weapon::LongBladeOneHand:
case ESM::Weapon::BluntOneHand:
case ESM::Weapon::AxeOneHand:
case ESM::Weapon::Arrow:
case ESM::Weapon::Bolt:
*weaptype = WeapType_OneHand;
break;
case ESM::Weapon::LongBladeTwoHand:
case ESM::Weapon::BluntTwoClose:
case ESM::Weapon::AxeTwoHand:
*weaptype = WeapType_TwoHand;
break;
case ESM::Weapon::BluntTwoWide:
case ESM::Weapon::SpearTwoWide:
*weaptype = WeapType_TwoWide;
break;
case ESM::Weapon::MarksmanBow:
*weaptype = WeapType_BowAndArrow;
break;
case ESM::Weapon::MarksmanCrossbow:
*weaptype = WeapType_Crossbow;
break;
case ESM::Weapon::MarksmanThrown:
*weaptype = WeapType_ThowWeapon;
break;
}
}
}
return weapon;
}
return inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
}
CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr)
, mAnimation(anim)
, mIdleState(CharState_Idle)
, mIdleState(CharState_None)
, mMovementState(CharState_None)
, mMovementSpeed(0.0f)
, mDeathState(CharState_None)
@ -255,18 +305,30 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mSkipAnim(false)
, mSecondsOfRunning(0)
, mSecondsOfSwimming(0)
, mUpdateWeapon(true)
{
if(!mAnimation)
return;
if(MWWorld::Class::get(mPtr).isActor())
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
if(cls.isActor())
{
/* Accumulate along X/Y only for now, until we can figure out how we should
* handle knockout and death which moves the character down. */
mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f));
if(MWWorld::Class::get(mPtr).getCreatureStats(mPtr).isDead())
if(mPtr.getTypeName() == typeid(ESM::NPC).name())
{
getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType);
if(mWeaponType != WeapType_None)
{
getWeaponGroup(mWeaponType, mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
}
}
if(!cls.getCreatureStats(mPtr).isDead())
mIdleState = CharState_Idle;
else
{
/* FIXME: Get the actual death state used. */
mDeathState = CharState_Death1;
@ -276,13 +338,15 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
{
/* Don't accumulate with non-actors. */
mAnimation->setAccumulation(Ogre::Vector3(0.0f));
mIdleState = CharState_Idle;
}
refreshCurrentAnims(mIdleState, mMovementState, true);
if(mDeathState != CharState_None)
{
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState));
if(state == sStateListEnd)
const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
mCurrentDeath = state->groupname;
@ -302,6 +366,223 @@ void CharacterController::updatePtr(const MWWorld::Ptr &ptr)
}
bool CharacterController::updateNpcState()
{
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
CreatureStats &crstats = cls.getCreatureStats(mPtr);
NpcStats &stats = cls.getNpcStats(mPtr);
WeaponType weaptype = WeapType_None;
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = getActiveWeapon(stats, inv, &weaptype);
bool forcestateupdate = false;
if(weaptype != mWeaponType)
{
forcestateupdate = true;
std::string weapgroup;
if(weaptype == WeapType_None)
{
getWeaponGroup(mWeaponType, weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1.0f, "unequip start", "unequip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_UnEquipingWeap;
}
else
{
getWeaponGroup(weaptype, weapgroup);
mAnimation->showWeapons(false);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1.0f, "equip start", "equip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_EquipingWeap;
}
if(weapon != inv.end() && !(weaptype == WeapType_None && mWeaponType == WeapType_Spell))
{
std::string soundid = (weaptype == WeapType_None) ?
MWWorld::Class::get(*weapon).getDownSoundId(*weapon) :
MWWorld::Class::get(*weapon).getUpSoundId(*weapon);
if(!soundid.empty())
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f);
}
}
mWeaponType = weaptype;
getWeaponGroup(mWeaponType, mCurrentWeapon);
}
bool isWeapon = (weapon != inv.end() && weapon->getTypeName() == typeid(ESM::Weapon).name());
float weapSpeed = 1.0f;
if(isWeapon)
weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
float complete;
bool animPlaying;
if(crstats.getAttackingOrSpell())
{
if(mUpperBodyState == UpperCharState_WeapEquiped)
{
mAttackType.clear();
if(mWeaponType == WeapType_Spell)
{
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const std::string spellid = crstats.getSpells().getSelectedSpell();
if(!spellid.empty())
{
static const std::string schools[] = {
"alteration", "conjuration", "destruction", "illusion", "mysticism", "restoration"
};
const ESM::Spell *spell = store.get<ESM::Spell>().find(spellid);
const ESM::ENAMstruct &effectentry = spell->mEffects.mList.at(0);
const ESM::MagicEffect *effect;
effect = store.get<ESM::MagicEffect>().find(effectentry.mEffectID);
switch(effectentry.mRange)
{
case 0: mAttackType = "self"; break;
case 1: mAttackType = "touch"; break;
case 2: mAttackType = "target"; break;
}
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
weapSpeed, mAttackType+" start", mAttackType+" stop",
0.0f, 0);
mUpperBodyState = UpperCharState_CastingSpell;
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(!effect->mCastSound.empty())
sndMgr->playSound3D(mPtr, effect->mCastSound, 1.0f, 1.0f);
else
sndMgr->playSound3D(mPtr, schools[effect->mData.mSchool]+" cast", 1.0f, 1.0f);
}
}
else if(mWeaponType != WeapType_PickProbe)
{
if(mWeaponType == WeapType_Crossbow || mWeaponType == WeapType_BowAndArrow ||
mWeaponType == WeapType_ThowWeapon)
mAttackType = "shoot";
else
{
int attackType = crstats.getAttackType();
if(isWeapon && Settings::Manager::getBool("best attack", "Game"))
attackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
if (attackType == MWMechanics::CreatureStats::AT_Chop)
mAttackType = "chop";
else if (attackType == MWMechanics::CreatureStats::AT_Slash)
mAttackType = "slash";
else
mAttackType = "thrust";
}
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" start", mAttackType+" min attack",
0.0f, 0);
mUpperBodyState = UpperCharState_StartToMinAttack;
}
}
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
}
else
{
animPlaying = mAnimation->getInfo(mCurrentWeapon, &complete);
if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack)
{
if(mAttackType != "shoot")
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
// NOTE: SwishL, SwishM, SwishS - large, medium, small.
// Based on weapon weight, speed, or attack strength?
sndMgr->playSound3D(mPtr, "SwishL", 1.0f, 1.0f);
}
stats.setAttackStrength(complete);
mAnimation->disable(mCurrentWeapon);
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
1.0f-complete, 0);
mUpperBodyState = UpperCharState_MaxAttackToMinHit;
}
}
if(!animPlaying)
{
if(mUpperBodyState == UpperCharState_EquipingWeap ||
mUpperBodyState == UpperCharState_FollowStartToFollowStop ||
mUpperBodyState == UpperCharState_CastingSpell)
mUpperBodyState = UpperCharState_WeapEquiped;
else if(mUpperBodyState == UpperCharState_UnEquipingWeap)
mUpperBodyState = UpperCharState_Nothing;
}
else if(complete >= 1.0f)
{
if(mUpperBodyState == UpperCharState_StartToMinAttack)
{
mAnimation->disable(mCurrentWeapon);
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" min attack", mAttackType+" max attack",
0.0f, 0);
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
}
else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit)
{
mAnimation->disable(mCurrentWeapon);
if(mAttackType == "shoot")
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" min hit", mAttackType+" follow start",
0.0f, 0);
else
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" min hit", mAttackType+" hit",
0.0f, 0);
mUpperBodyState = UpperCharState_MinHitToHit;
}
else if(mUpperBodyState == UpperCharState_MinHitToHit)
{
mAnimation->disable(mCurrentWeapon);
if(mAttackType == "shoot")
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
weapSpeed, mAttackType+" follow start", mAttackType+" follow stop",
0.0f, 0);
else
mAnimation->play(mCurrentWeapon, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",
0.0f, 0);
mUpperBodyState = UpperCharState_FollowStartToFollowStop;
}
}
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name())
{
if(!mAnimation->isPlaying("torch"))
mAnimation->play("torch", Priority_Torch,
MWRender::Animation::Group_LeftArm, false,
1.0f, "start", "stop", 0.0f, (~(size_t)0));
}
else if(mAnimation->isPlaying("torch"))
mAnimation->disable("torch");
return forcestateupdate;
}
void CharacterController::update(float duration, Movement &movement)
{
const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
@ -443,206 +724,7 @@ void CharacterController::update(float duration, Movement &movement)
movement.mRotation[2] += rot.z;
if(mPtr.getTypeName() == typeid(ESM::NPC).name())
{
NpcStats &stats = cls.getNpcStats(mPtr);
WeaponType weaptype = WeapType_None;
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = inv.end();
if(stats.getDrawState() == DrawState_Spell)
weaptype = WeapType_Spell;
else if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
{
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end())
weaptype = WeapType_HandToHand;
else
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
weaptype = WeapType_PickProbe;
else if(type == typeid(ESM::Weapon).name())
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType;
switch(type)
{
case ESM::Weapon::ShortBladeOneHand:
case ESM::Weapon::LongBladeOneHand:
case ESM::Weapon::BluntOneHand:
case ESM::Weapon::AxeOneHand:
case ESM::Weapon::Arrow:
case ESM::Weapon::Bolt:
weaptype = WeapType_OneHand;
break;
case ESM::Weapon::LongBladeTwoHand:
case ESM::Weapon::BluntTwoClose:
case ESM::Weapon::AxeTwoHand:
weaptype = WeapType_TwoHand;
break;
case ESM::Weapon::BluntTwoWide:
case ESM::Weapon::SpearTwoWide:
weaptype = WeapType_TwoWide;
break;
case ESM::Weapon::MarksmanBow:
weaptype = WeapType_BowAndArrow;
break;
case ESM::Weapon::MarksmanCrossbow:
weaptype = WeapType_Crossbow;
break;
case ESM::Weapon::MarksmanThrown:
weaptype = WeapType_ThowWeapon;
break;
}
}
}
}
else
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(mUpdateWeapon)
{
forcestateupdate = (mWeaponType != weaptype);
mWeaponType = weaptype;
mUpdateWeapon = false;
}
if(weaptype != mWeaponType)
{
forcestateupdate = true;
std::string weapgroup;
if(weaptype == WeapType_None)
{
getWeaponGroup(mWeaponType, weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1.0f, "unequip start", "unequip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_UnEquipingWeap;
}
else
{
getWeaponGroup(weaptype, weapgroup);
mAnimation->showWeapons(false);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1.0f, "equip start", "equip stop", 0.0f, 0);
mUpperBodyState = UpperCharState_EquipingWeap;
}
mWeaponType = weaptype;
if(weapon != inv.end())
{
std::string soundid = (mWeaponType == WeapType_None) ?
MWWorld::Class::get(*weapon).getDownSoundId(*weapon) :
MWWorld::Class::get(*weapon).getUpSoundId(*weapon);
if(!soundid.empty())
{
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
sndMgr->playSound3D(mPtr, soundid, 1.0f, 1.0f);
}
}
}
if(weaptype != WeapType_PickProbe && weaptype != WeapType_BowAndArrow
&& weaptype != WeapType_Crossbow && weaptype != WeapType_ThowWeapon)
{
std::string weapgroup;
getWeaponGroup(mWeaponType, weapgroup);
float weapSpeed = 1;
if(weapon != inv.end()) weapSpeed = weapon->get<ESM::Weapon>()->mBase->mData.mSpeed;
std::string start;
std::string stop;
float complete;
float speedMult;
bool animPlaying = mAnimation->getInfo(weapgroup,&complete,&speedMult,&start,&stop);
if(cls.getCreatureStats(mPtr).getAttackingOrSpell())
{
if(mUpperBodyState == UpperCharState_WeapEquiped)
{
int attackType = cls.getCreatureStats(mPtr).getAttackType();
if (Settings::Manager::getBool("best attack", "Game") && weapon != inv.end())
attackType = getBestAttack(weapon->get<ESM::Weapon>()->mBase);
if (attackType == MWMechanics::CreatureStats::AT_Chop)
mAttackType = "chop";
else if (attackType == MWMechanics::CreatureStats::AT_Slash)
mAttackType = "slash";
else
mAttackType = "thrust";
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" start", mAttackType+" min attack", 0.0f, 0);
mUpperBodyState = UpperCharState_StartToMinAttack;
}
}
else if(mUpperBodyState == UpperCharState_MinAttackToMaxAttack)
{
if(animPlaying)
{
mAnimation->disable(weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 1-complete, 0);
}
else
{
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" max attack", mAttackType+" min hit", 0, 0);
}
mUpperBodyState = UpperCharState_MaxAttackToMinHit;
}
if(mUpperBodyState == UpperCharState_EquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_WeapEquiped;
if(mUpperBodyState == UpperCharState_UnEquipingWeap && !animPlaying) mUpperBodyState = UpperCharState_Nothing;
if(animPlaying)
{
if(mUpperBodyState == UpperCharState_StartToMinAttack && complete == 1)
{
mAnimation->disable(weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" min attack", mAttackType+" max attack",0, 0);
mUpperBodyState = UpperCharState_MinAttackToMaxAttack;
}
else if(mUpperBodyState == UpperCharState_MaxAttackToMinHit && complete == 1)
{
mAnimation->disable(weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" min hit", mAttackType+" hit",0, 0);
mUpperBodyState = UpperCharState_MinHitToHit;
}
else if(mUpperBodyState == UpperCharState_MinHitToHit && complete == 1)
{
mAnimation->disable(weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" large follow start", mAttackType+" large follow stop",0, 0);
mUpperBodyState = UpperCharState_LargeFollowStartToLargeFollowStop;
}
else if(mUpperBodyState == UpperCharState_LargeFollowStartToLargeFollowStop && complete == 1)
{
mUpperBodyState = UpperCharState_WeapEquiped;
}
}
}
MWWorld::ContainerStoreIterator torch = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if(torch != inv.end() && torch->getTypeName() == typeid(ESM::Light).name())
{
if(!mAnimation->isPlaying("torch"))
mAnimation->play("torch", Priority_Torch,
MWRender::Animation::Group_LeftArm, false,
1.0f, "start", "stop", 0.0f, (~(size_t)0));
}
else if(mAnimation->isPlaying("torch"))
mAnimation->disable("torch");
}
forcestateupdate = updateNpcState();
refreshCurrentAnims(idlestate, movestate, forcestateupdate);
}
@ -734,8 +816,8 @@ void CharacterController::forceStateUpdate()
refreshCurrentAnims(mIdleState, mMovementState, true);
if(mDeathState != CharState_None)
{
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState));
if(state == sStateListEnd)
const StateInfo *state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
mCurrentDeath = state->groupname;
@ -747,22 +829,52 @@ void CharacterController::forceStateUpdate()
void CharacterController::kill()
{
static const CharacterState deathstates[] = {
CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5
};
if(mDeathState != CharState_None)
return;
mDeathState = deathstates[(int)(rand()/((double)RAND_MAX+1.0)*5.0)];
const StateInfo *state = std::find_if(sStateList, sStateListEnd, FindCharState(mDeathState));
if(state == sStateListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
if(mPtr.getTypeName() == typeid(ESM::NPC).name())
{
const StateInfo *state = NULL;
if(MWBase::Environment::get().getWorld()->isSwimming(mPtr))
{
mDeathState = CharState_SwimDeath;
state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
}
static const CharacterState deathstates[5] = {
CharState_Death1, CharState_Death2, CharState_Death3, CharState_Death4, CharState_Death5
};
std::vector<CharacterState> states(&deathstates[0], &deathstates[5]);
while(states.size() > 1 && (!state || !mAnimation->hasAnimation(state->groupname)))
{
int pos = (int)(rand()/((double)RAND_MAX+1.0)*states.size());
mDeathState = states[pos];
states.erase(states.begin()+pos);
state = std::find_if(sDeathList, sDeathListEnd, FindCharState(mDeathState));
if(state == sDeathListEnd)
throw std::runtime_error("Failed to find character state "+Ogre::StringConverter::toString(mDeathState));
}
mCurrentDeath = state->groupname;
}
else
{
mDeathState = CharState_Death1;
mCurrentDeath = "death1";
}
mCurrentDeath = state->groupname;
if(mAnimation && !mAnimation->getInfo(mCurrentDeath))
if(mAnimation)
{
mAnimation->play(mCurrentDeath, Priority_Death, MWRender::Animation::Group_All,
false, 1.0f, "start", "stop", 0.0f, 0);
mAnimation->disable(mCurrentIdle);
}
mIdleState = CharState_None;
mCurrentIdle.clear();
}
void CharacterController::resurrect()
@ -776,5 +888,4 @@ void CharacterController::resurrect()
mDeathState = CharState_None;
}
}

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

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

@ -38,6 +38,8 @@ namespace MWMechanics
int mAttackType;
std::string mLastHitObject; // The last object to hit this actor
public:
CreatureStats();
@ -111,10 +113,14 @@ namespace MWMechanics
float getFatigueTerm() const;
///< Return effective fatigue
// small hack to allow the fact that Health permanently increases by 10% of endurance on each level up
void increaseLevelHealthBonus(float value);
float getLevelHealthBonus() const;
void levelUp();
void updateHealth();
///< Calculate health based on endurance and strength.
/// Called at character creation and at level up.
bool isDead() const;
bool hasDied() const;
@ -151,6 +157,9 @@ namespace MWMechanics
void setHostile (bool hostile);
bool getCreatureTargetted() const;
void setLastHitObject(const std::string &objectid);
const std::string &getLastHitObject() const;
};
}

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

@ -54,6 +54,7 @@ namespace MWMechanics
bool mWerewolf;
int mWerewolfKills;
int mProfit;
float mAttackStrength;
int mLevelProgress; // 0-10
@ -70,9 +71,12 @@ namespace MWMechanics
void modifyProfit(int diff);
DrawState_ getDrawState() const;
void setDrawState (DrawState_ state);
/// When attacking, stores how strong the attack should be (0 = weakest, 1 = strongest)
float getAttackStrength() const;
void setAttackStrength(float value);
int getBaseDisposition() const;
void setBaseDisposition(int disposition);

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

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

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

@ -52,6 +52,15 @@ protected:
virtual void setValue(Ogre::Real value);
};
class NullAnimationValue : public Ogre::ControllerValue<Ogre::Real>
{
public:
virtual Ogre::Real getValue() const
{ return 0.0f; }
virtual void setValue(Ogre::Real value)
{ }
};
struct AnimSource : public Ogre::AnimationAlloc {
NifOgre::TextKeyMap mTextKeys;
std::vector<Ogre::Controller<Ogre::Real> > mControllers[sNumGroups];
@ -60,10 +69,10 @@ protected:
struct AnimState {
Ogre::SharedPtr<AnimSource> mSource;
NifOgre::TextKeyMap::const_iterator mStartKey;
NifOgre::TextKeyMap::const_iterator mLoopStartKey;
NifOgre::TextKeyMap::const_iterator mStopKey;
NifOgre::TextKeyMap::const_iterator mNextKey;
float mStartTime;
float mLoopStartTime;
float mLoopStopTime;
float mStopTime;
float mTime;
float mSpeedMult;
@ -75,7 +84,8 @@ protected:
int mGroups;
bool mAutoDisable;
AnimState() : mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
AnimState() : mStartTime(0.0f), mLoopStartTime(0.0f), mLoopStopTime(0.0f), mStopTime(0.0f),
mTime(0.0f), mSpeedMult(1.0f), mPlaying(false), mLoopCount(0),
mPriority(0), mGroups(0), mAutoDisable(true)
{ }
};
@ -98,6 +108,7 @@ protected:
AnimStateMap mStates;
Ogre::SharedPtr<AnimationValue> mAnimationValuePtr[sNumGroups];
Ogre::SharedPtr<NullAnimationValue> mNullAnimationValuePtr;
ObjectAttachMap mAttachedObjects;
@ -131,9 +142,7 @@ protected:
const std::string &groupname, const std::string &start, const std::string &stop,
float startpoint);
bool doLoop(AnimState &state);
bool handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
void handleTextKey(AnimState &state, const std::string &groupname, const NifOgre::TextKeyMap::const_iterator &key);
/* Sets the root model of the object. If 'baseonly' is true, then any meshes or particle
* systems in the model are ignored (useful for NPCs, where only the skeleton is needed for
@ -199,11 +208,9 @@ public:
* \param groupname Animation group to check.
* \param complete Stores completion amount (0 = at start key, 0.5 = half way between start and stop keys), etc.
* \param speedmult Stores the animation speed multiplier
* \param start Stores the start key
* \param stop Stores the stop key
* \return True if the animation is active, false otherwise.
*/
bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL, std::string *start=NULL, std::string *stop=NULL) const;
bool getInfo(const std::string &groupname, float *complete=NULL, float *speedmult=NULL) const;
/** Disables the specified animation group;
* \param groupname Animation group to disable.

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

@ -33,6 +33,10 @@ namespace MWRender
bool mFirstPersonView;
bool mPreviewMode;
bool mFreeLook;
float mNearest;
float mFurthest;
bool mIsNearest;
bool mIsFurthest;
struct {
bool enabled, allowed;
@ -80,7 +84,7 @@ namespace MWRender
void processViewChange();
void update(float duration);
void update(float duration, bool paused=false);
/// Set camera distance for current mode. Don't work on 1st person view.
/// \param adjust Indicates should distance be adjusted or set.
@ -93,18 +97,16 @@ namespace MWRender
void setAnimation(NpcAnimation *anim);
float getHeight();
/// Stores player and camera world positions in passed arguments
/// \return true if camera at the eye-place
bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera);
Ogre::Vector3 getPosition();
void getSightAngles(float &pitch, float &yaw);
/// Stores focal and camera world positions in passed arguments
void getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera);
void togglePlayerLooking(bool enable);
bool isVanityOrPreviewModeEnabled();
bool isNearest();
bool isFurthest();
};
}

@ -424,6 +424,10 @@ Ogre::Vector3 NpcAnimation::runAnimation(float timepassed)
for(size_t i = 0;i < sPartListSize;i++)
{
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
ctrl->update();
Ogre::Entity *ent = mObjectParts[i].mSkelBase;
if(!ent) continue;
updateSkeletonInstance(baseinst, ent->getSkeleton());
@ -481,6 +485,19 @@ bool NpcAnimation::addOrReplaceIndividualPart(int type, int group, int priority,
if(type == sPartList[i].type)
{
mObjectParts[i] = insertBoundedPart(mesh, group, sPartList[i].name);
// TODO:
// type == ESM::PRT_Head should get an animation source based on the current output of
// the actor's 'say' sound (0 = silent, 1 = loud?).
// type == ESM::PRT_Weapon should get an animation source based on the current offset
// of the weapon attack animation (from its beginning, or start marker?)
std::vector<Ogre::Controller<Ogre::Real> >::iterator ctrl(mObjectParts[i].mControllers.begin());
for(;ctrl != mObjectParts[i].mControllers.end();ctrl++)
{
if(ctrl->getSource().isNull())
ctrl->setSource(mNullAnimationValuePtr);
}
break;
}
}

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

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

Loading…
Cancel
Save