Merge branch 'master' into filter

Conflicts:
	apps/opencs/model/world/universalid.cpp
	apps/opencs/view/doc/view.cpp
	apps/opencs/view/world/subviews.cpp
pull/51/head
Marc Zinnschlag 12 years ago
commit adb05737ee

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

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

@ -304,6 +304,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());
@ -398,4 +408,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;
}

@ -115,6 +115,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.
///
@ -126,6 +130,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,83 +12,83 @@ 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" },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters" },
"Region Map", 0 },
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Filters, "Filters", 0 },
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0, 0 } // end marker
};
static const TypeData sIdArg[] =
{
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Skill, "Skill" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Class, "Class" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Faction, "Faction" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Race, "Race" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Sound, "Sound" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Script, "Script" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Region, "Region" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Birthsign, "Birthsign" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Activator, "Activator" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Potion, "Potion" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Apparatus, "Apparatus" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Armor, "Armor" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Book, "Book" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Clothing, "Clothing" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Container, "Container" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Creature, "Creature" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Door, "Door" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Ingredient, "Ingredient" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_CreatureLevelledList,
"Creature Levelled List" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_ItemLevelledList,
"Item Levelled List" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Light, "Light" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Lockpick, "Lockpick" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Miscellaneous,
"Miscellaneous" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Npc, "NPC" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Probe, "Probe" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Repair, "Repair" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Static, "Static" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Weapon, "Weapon" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Reference, "Reference" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "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);
@ -270,6 +270,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
@ -128,6 +130,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());
}
}

@ -79,6 +79,11 @@ void CSVDoc::View::setupViewMenu()
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);
QAction *filters = new QAction (tr ("Filters"), this);
connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView()));
view->addAction (filters);
@ -286,6 +291,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,
@ -445,3 +453,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;
@ -160,6 +161,8 @@ namespace CSVDoc
void addFiltersSubView();
void showUserSettings();
void toggleShowStatusBar (bool show);
};
}

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

@ -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,26 +27,15 @@ 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;
}

@ -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,21 +33,27 @@ 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_Filters,
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));
}

@ -44,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;
@ -64,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();
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt());
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
if (state!=CSMWorld::RecordBase::State_Deleted)
deletableIds.push_back (id);
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted)
{
std::string id = mModel->data (mModel->index (index.row(), 0)).
toString().toUtf8().constData();
deletableIds.push_back (id);
}
}
}
return deletableIds;
@ -126,7 +148,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
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);
}
@ -137,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)
@ -154,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)
@ -231,3 +248,46 @@ void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QSt
updateEditorSetting (settingName, settingValue))
emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i));
}
void CSVWorld::Table::tableSizeUpdate()
{
int size = 0;
int deleted = 0;
int modified = 0;
if (mModel->columnCount()>0)
{
int rows = mModel->rowCount();
for (int i=0; i<rows; ++i)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));
/// \todo Do not use hardcoded column numbers
int state = mModel->data (mModel->index (index.row(), 1)).toInt();
switch (state)
{
case CSMWorld::RecordBase::State_BaseOnly: ++size; break;
case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;
case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;
case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;
}
}
}
tableSizeChanged (size, deleted, modified);
}
void CSVWorld::Table::selectionSizeUpdate()
{
selectionSizeChanged (selectionModel()->selectedRows().size());
}
void CSVWorld::Table::requestFocus (const std::string& id)
{
QModelIndex index = mProxyModel->getModelIndex (id, 0);
if (index.isValid())
scrollTo (index, QAbstractItemView::PositionAtTop);
}

@ -60,15 +60,31 @@ namespace CSVWorld
void editRequest (int row);
private 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 createRecord();
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,22 +1,55 @@
#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 (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::editRequest (int row)
@ -28,3 +61,8 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con
{
mTable->updateEditorSetting(settingName, settingValue);
}
void CSVWorld::TableSubView::setStatusBar (bool show)
{
mBottom->setStatusBar (show);
}

@ -13,18 +13,26 @@ 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:

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

@ -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.
@ -372,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;
};
}

@ -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"
@ -148,6 +149,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
{
@ -355,6 +420,8 @@ namespace MWClass
return 5;
if(name == "scream")
return 6;
if(name == "land")
return 7;
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}

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

@ -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"
@ -113,7 +115,19 @@ namespace
// initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).getBase();
creatureStats.setHealth(static_cast<int> (0.5 * (strength + endurance)) + 4 * (creatureStats.getLevel() - 1));
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));
}
}
@ -288,6 +302,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(ptr);
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
{
@ -592,18 +843,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())
@ -613,28 +865,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;
}
@ -689,7 +940,7 @@ namespace MWClass
case ESM::Skill::LightArmor:
return "FootLightLeft";
case ESM::Skill::MediumArmor:
return "FootMediumLeft";
return "FootMedLeft";
case ESM::Skill::HeavyArmor:
return "FootHeavyLeft";
}
@ -714,7 +965,7 @@ namespace MWClass
case ESM::Skill::LightArmor:
return "FootLightRight";
case ESM::Skill::MediumArmor:
return "FootMediumRight";
return "FootMedRight";
case ESM::Skill::HeavyArmor:
return "FootHeavyRight";
}
@ -730,6 +981,8 @@ namespace MWClass
return "";
if(name == "scream")
return "";
if(name == "land")
return "";
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
}

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

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

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

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

@ -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"
@ -91,6 +93,7 @@ namespace MWGui
WindowBase("openmw_settings_window.layout")
{
getWidget(mOkButton, "OkButton");
getWidget(mBestAttackButton, "BestAttackButton");
getWidget(mSubtitlesButton, "SubtitlesButton");
getWidget(mCrosshairButton, "CrosshairButton");
getWidget(mResolutionList, "ResolutionList");
@ -131,6 +134,7 @@ namespace MWGui
mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
mCrosshairButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
mBestAttackButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);
mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled);
@ -170,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();
@ -200,6 +202,7 @@ namespace MWGui
mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}");
mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{sOn}" : "#{sOff}");
mBestAttackButton->setCaptionWithReplacing(Settings::Manager::getBool("best attack", "Game") ? "#{sOn}" : "#{sOff}");
float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin);
mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1));
@ -269,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()
@ -290,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()
@ -361,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)
@ -407,6 +402,8 @@ namespace MWGui
Settings::Manager::setBool("crosshair", "HUD", newState);
else if (_sender == mSubtitlesButton)
Settings::Manager::setBool("subtitles", "GUI", newState);
else if (_sender == mBestAttackButton)
Settings::Manager::setBool("best attack", "Game", newState);
apply();
}

@ -32,6 +32,7 @@ namespace MWGui
MyGUI::ScrollBar* mToolTipDelaySlider;
MyGUI::Button* mSubtitlesButton;
MyGUI::Button* mCrosshairButton;
MyGUI::Button* mBestAttackButton;
// graphics
MyGUI::ListBox* mResolutionList;

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

@ -20,6 +20,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwgui/bookwindow.hpp"
#include "../mwmechanics/creaturestats.hpp"
using namespace ICS;
@ -160,6 +161,26 @@ namespace MWInput
resetIdleTime ();
int action = channel->getNumber();
if (action == A_Use)
{
MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackingOrSpell(currentValue);
if (currentValue == 1)
{
int type = MWMechanics::CreatureStats::AT_Chop;
bool forward = (mInputBinder->getChannel(A_MoveForward)->getValue() > 0
|| mInputBinder->getChannel(A_MoveBackward)->getValue() > 0);
bool side = (mInputBinder->getChannel(A_MoveLeft)->getValue() > 0
|| mInputBinder->getChannel(A_MoveRight)->getValue() > 0);
if (side && !forward)
type = MWMechanics::CreatureStats::AT_Slash;
if (forward && !side)
type = MWMechanics::CreatureStats::AT_Thrust;
MWWorld::Class::get(mPlayer.getPlayer()).getCreatureStats(mPlayer.getPlayer()).setAttackType(type);
}
}
if (currentValue == 1)
{
// trigger action activated
@ -183,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 ();
@ -364,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)
{
@ -413,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");
@ -430,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)
@ -512,7 +528,6 @@ namespace MWInput
return true; // MyGUI has no use for these events
MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id));
if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0)
{
MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType<MyGUI::Button>(false);
@ -597,15 +612,18 @@ namespace MWInput
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()
@ -665,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
{
@ -685,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();
@ -697,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();
@ -724,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);

@ -221,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();
@ -230,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;
}
@ -238,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;
}
@ -256,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}");
}
}

@ -35,6 +35,23 @@
#include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.hpp"
namespace
{
int getBestAttack (const ESM::Weapon* weapon)
{
int slash = (weapon->mData.mSlash[0] + weapon->mData.mSlash[1])/2;
int chop = (weapon->mData.mChop[0] + weapon->mData.mChop[1])/2;
int thrust = (weapon->mData.mThrust[0] + weapon->mData.mThrust[1])/2;
if (slash >= chop && slash >= thrust)
return MWMechanics::CreatureStats::AT_Slash;
else if (chop >= slash && chop >= thrust)
return MWMechanics::CreatureStats::AT_Chop;
else
return MWMechanics::CreatureStats::AT_Thrust;
}
}
namespace MWMechanics
{
@ -215,6 +232,67 @@ 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)
@ -222,22 +300,33 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mMovementState(CharState_None)
, mMovementSpeed(0.0f)
, mDeathState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing)
, mWeaponType(WeapType_None)
, 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
{
@ -277,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);
@ -418,117 +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);
}
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);
}
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);
}
}
}
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);
}

@ -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,
@ -95,6 +102,19 @@ enum WeaponType {
WeapType_Spell
};
enum UpperBodyCharacterState {
UpperCharState_Nothing,
UpperCharState_EquipingWeap,
UpperCharState_UnEquipingWeap,
UpperCharState_WeapEquiped,
UpperCharState_StartToMinAttack,
UpperCharState_MinAttackToMaxAttack,
UpperCharState_MaxAttackToMinHit,
UpperCharState_MinHitToHit,
UpperCharState_FollowStartToFollowStop,
UpperCharState_CastingSpell
};
class CharacterController
{
MWWorld::Ptr mPtr;
@ -113,22 +133,31 @@ class CharacterController
CharacterState mDeathState;
std::string mCurrentDeath;
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;
float mSecondsOfRunning;
std::string mAttackType; // slash, chop or thrust
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);
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();

@ -12,7 +12,8 @@ namespace MWMechanics
CreatureStats::CreatureStats()
: mLevel (0), mLevelHealthBonus(0.f), mDead (false), mDied (false), mFriendlyHits (0),
mTalkedTo (false), mAlarmed (false),
mAttacked (false), mHostile (false)
mAttacked (false), mHostile (false),
mAttackingOrSpell(false)
{
for (int i=0; i<4; ++i)
mAiSettings[i] = 0;
@ -109,6 +110,11 @@ namespace MWMechanics
return mMagicEffects;
}
const bool &CreatureStats::getAttackingOrSpell() const
{
return mAttackingOrSpell;
}
int CreatureStats::getLevel() const
{
return mLevel;
@ -210,6 +216,11 @@ namespace MWMechanics
mMagicEffects = effects;
}
void CreatureStats::setAttackingOrSpell(const bool &attackingOrSpell)
{
mAttackingOrSpell = attackingOrSpell;
}
void CreatureStats::setAiSetting (int index, int value)
{
assert (index>=0 && index<4);
@ -307,4 +318,14 @@ namespace MWMechanics
{
return false;
}
void CreatureStats::setLastHitObject(const std::string& objectid)
{
mLastHitObject = objectid;
}
const std::string &CreatureStats::getLastHitObject() const
{
return mLastHitObject;
}
}

@ -34,6 +34,11 @@ namespace MWMechanics
bool mAlarmed;
bool mAttacked;
bool mHostile;
bool mAttackingOrSpell;//for the player, this is true if the left mouse button is pressed, false if not.
int mAttackType;
std::string mLastHitObject; // The last object to hit this actor
public:
CreatureStats();
@ -54,6 +59,8 @@ namespace MWMechanics
const MagicEffects & getMagicEffects() const;
const bool & getAttackingOrSpell() const;
int getLevel() const;
int getAiSetting (int index) const;
@ -83,6 +90,17 @@ namespace MWMechanics
void setMagicEffects(const MagicEffects &effects);
void setAttackingOrSpell(const bool &attackingOrSpell);
enum AttackType
{
AT_Slash,
AT_Thrust,
AT_Chop
};
void setAttackType(int attackType) { mAttackType = attackType; }
int getAttackType() { return mAttackType; }
void setLevel(int level);
void setAiSetting (int index, int value);
@ -139,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);

@ -14,6 +14,7 @@
#include "../mwbase/world.hpp"
#include "../mwmechanics/character.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwworld/class.hpp"
namespace MWRender
@ -53,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));
@ -442,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;
@ -508,7 +504,7 @@ 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)
{
@ -521,58 +517,40 @@ bool Animation::handleTextKey(AnimState &state, const std::string &groupname, co
type = MWBase::SoundManager::Play_TypeFoot;
sndMgr->playSound3D(mPtr, sound, 1.0f, 1.0f, type);
}
return true;
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())
@ -604,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();
}
@ -676,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;
}
@ -714,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;
@ -232,16 +236,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 +311,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;
@ -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;
@ -323,18 +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;
if(!mCamera->getPosition(orig, dest))
mCamera->setCameraDistance();
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);
}
@ -718,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)
@ -732,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")
@ -790,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_Window* window = mRendering.getSDLWindow();
SDL_SetWindowFullscreen(window, 0);
SDL_SetWindowFullscreen(mRendering.getSDLWindow(), 0);
if (SDL_GetWindowFlags(window) & SDL_WINDOW_MAXIMIZED)
SDL_RestoreWindow(window);
if (x != mRendering.getWindow()->getWidth() || y != mRendering.getWindow()->getHeight())
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);
@ -825,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()
@ -905,7 +903,15 @@ void RenderingManager::setCameraDistance(float dist, bool adjust, bool override)
{
if(!mCamera->isVanityOrPreviewModeEnabled() && !mCamera->isFirstPerson())
{
mCamera->setCameraDistance(-dist / 120.f * 10, adjust, override);
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);
}
}

@ -50,7 +50,7 @@ namespace MWRender
class VideoPlayer;
class Animation;
class RenderingManager: private RenderingInterface, public Ogre::WindowEventListener, public Ogre::RenderTargetListener
class RenderingManager: private RenderingInterface, public Ogre::RenderTargetListener, public OEngine::Render::WindowSizeListener
{
private:
virtual MWRender::Objects& getObjects();
@ -204,8 +204,7 @@ public:
void frameStarted(float dt);
protected:
virtual void windowResized(Ogre::RenderWindow* rw);
virtual void windowClosed(Ogre::RenderWindow* rw);
virtual void windowResized(int x, int y);
private:
sh::Factory* mFactory;

@ -8,6 +8,7 @@
#include <OgreRoot.h>
#include <OgreHardwarePixelBuffer.h>
#include <OgreRenderWindow.h>
#include <boost/thread.hpp>
@ -16,6 +17,7 @@
#include "../mwbase/soundmanager.hpp"
#include "../mwsound/sound_decoder.hpp"
#include "../mwsound/sound.hpp"
#include "../mwbase/inputmanager.hpp"
#include "renderconst.hpp"
@ -1032,13 +1034,14 @@ public:
#endif // defined OPENMW_USE_FFMPEG
VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr)
VideoPlayer::VideoPlayer(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* window)
: mState(NULL)
, mSceneMgr(sceneMgr)
, mVideoMaterial(NULL)
, mRectangle(NULL)
, mNode(NULL)
, mAllowSkipping(false)
, mWindow(window)
{
mVideoMaterial = Ogre::MaterialManager::getSingleton().getByName("VideoMaterial", "General");
if (mVideoMaterial.isNull ())
@ -1129,6 +1132,14 @@ void VideoPlayer::playVideo(const std::string &resourceName, bool allowSkipping)
try {
mState = new VideoState;
mState->init(resourceName);
while (isPlaying())
{
MWBase::Environment::get().getInputManager()->update(0, false);
update();
mWindow->update();
}
}
catch(std::exception& e) {
std::cerr<< "Failed to play video: "<<e.what() <<std::endl;

@ -8,6 +8,7 @@ namespace Ogre
class SceneManager;
class SceneNode;
class Rectangle2D;
class RenderWindow;
}
namespace MWRender
@ -17,7 +18,7 @@ namespace MWRender
class VideoPlayer
{
public:
VideoPlayer(Ogre::SceneManager* sceneMgr);
VideoPlayer(Ogre::SceneManager* sceneMgr, Ogre::RenderWindow* window);
~VideoPlayer();
void playVideo (const std::string& resourceName, bool allowSkipping);
@ -43,6 +44,7 @@ namespace MWRender
Ogre::Rectangle2D* mBackgroundRectangle;
Ogre::SceneNode* mNode;
Ogre::SceneNode* mBackgroundNode;
Ogre::RenderWindow* mWindow;
int mWidth;
int mHeight;

@ -51,7 +51,8 @@ op 0x20022: AiFollow
op 0x20023: AiFollow, explicit reference
op 0x20024: AiFollowCell
op 0x20025: AiFollowCell, explicit reference
op s 0x20026-0x3ffff unused
op 0x20026: ModRegion
opcodes 0x20027-0x3ffff unused
Segment 4:
(not implemented yet)
@ -338,5 +339,9 @@ op 0x200020f: GetStandingActor, explicit
op 0x2000210: GetStartingAngle
op 0x2000211: GetStartingAngle, explicit
op 0x2000212: GetWindSpeed
op 0x2000213: HitOnMe
op 0x2000214: HitOnMe, explicit
op 0x2000215: DisableTeleporting
op 0x2000216: EnableTeleporting
opcodes 0x2000213-0x3ffffff unused
opcodes 0x2000217-0x3ffffff unused

@ -599,6 +599,35 @@ namespace MWScript
}
};
template <class R>
class OpHitOnMe : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
std::string objectID = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
MWMechanics::CreatureStats &stats = MWWorld::Class::get(ptr).getCreatureStats(ptr);
runtime.push(::Misc::StringUtils::ciEqual(objectID, stats.getLastHitObject()));
}
};
template <bool Enable>
class OpEnableTeleporting : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWBase::World *world = MWBase::Environment::get().getWorld();
world->enableTeleporting(Enable);
}
};
const int opcodeXBox = 0x200000c;
const int opcodeOnActivate = 0x200000d;
const int opcodeActivate = 0x2000075;
@ -650,6 +679,12 @@ namespace MWScript
const int opcodePlayBink = 0x20001f7;
const int opcodeHitOnMe = 0x2000213;
const int opcodeHitOnMeExplicit = 0x2000214;
const int opcodeDisableTeleporting = 0x2000215;
const int opcodeEnableTeleporting = 0x2000216;
void registerExtensions (Compiler::Extensions& extensions)
{
extensions.registerFunction ("xbox", 'l', "", opcodeXBox);
@ -692,6 +727,9 @@ namespace MWScript
extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit);
extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit);
extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed);
extensions.registerFunction ("hitonme", 'l', "S", opcodeHitOnMe, opcodeHitOnMeExplicit);
extensions.registerInstruction ("disableteleporting", "", opcodeDisableTeleporting);
extensions.registerInstruction ("enableteleporting", "", opcodeEnableTeleporting);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -745,6 +783,10 @@ namespace MWScript
interpreter.installSegment5 (opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>);
interpreter.installSegment5 (opcodeGetStandingActorExplicit, new OpGetStandingActor<ExplicitRef>);
interpreter.installSegment5 (opcodeGetWindSpeed, new OpGetWindSpeed);
interpreter.installSegment5 (opcodeHitOnMe, new OpHitOnMe<ImplicitRef>);
interpreter.installSegment5 (opcodeHitOnMeExplicit, new OpHitOnMe<ExplicitRef>);
interpreter.installSegment5 (opcodeDisableTeleporting, new OpEnableTeleporting<false>);
interpreter.installSegment5 (opcodeEnableTeleporting, new OpEnableTeleporting<true>);
}
}
}

@ -96,6 +96,28 @@ namespace MWScript
}
};
class OpModRegion : public Interpreter::Opcode1
{
public:
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
std::string region = runtime.getStringLiteral (runtime[0].mInteger);
runtime.pop();
std::vector<char> chances;
chances.reserve(10);
while(arg0 > 0)
{
chances.push_back(std::max(0, std::min(127, runtime[0].mInteger)));
runtime.pop();
arg0--;
}
MWBase::Environment::get().getWorld()->modRegion(region, chances);
}
};
const int opcodeToggleSky = 0x2000021;
const int opcodeTurnMoonWhite = 0x2000022;
const int opcodeTurnMoonRed = 0x2000023;
@ -103,6 +125,7 @@ namespace MWScript
const int opcodeGetSecundaPhase = 0x2000025;
const int opcodeGetCurrentWeather = 0x200013f;
const int opcodeChangeWeather = 0x2000140;
const int opcodeModRegion = 0x20026;
void registerExtensions (Compiler::Extensions& extensions)
{
@ -114,6 +137,7 @@ namespace MWScript
extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase);
extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase);
extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather);
extensions.registerInstruction ("modregion", "S/llllllllll", opcodeModRegion);
}
void installOpcodes (Interpreter::Interpreter& interpreter)
@ -125,6 +149,7 @@ namespace MWScript
interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase);
interpreter.installSegment5 (opcodeGetCurrentWeather, new OpGetCurrentWeather);
interpreter.installSegment5 (opcodeChangeWeather, new OpChangeWeather);
interpreter.installSegment3 (opcodeModRegion, new OpModRegion);
}
}
}

@ -499,18 +499,14 @@ namespace MWSound
soundIter = regn->mSoundList.begin();
while(soundIter != regn->mSoundList.end())
{
const std::string go = soundIter->mSound.toString();
int chance = (int) soundIter->mChance;
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
soundIter++;
if(r - pos < chance)
if(r - pos < soundIter->mChance)
{
//play sound
std::cout << "Sound: " << go <<" Chance:" << chance << "\n";
playSound(go, 1.0f, 1.0f);
playSound(soundIter->mSound.toString(), 1.0f, 1.0f);
break;
}
pos += chance;
pos += soundIter->mChance;
soundIter++;
}
}
@ -550,6 +546,13 @@ namespace MWSound
mActiveSounds.erase(snditer++);
else
{
const MWWorld::Ptr &ptr = snditer->second.first;
if(!ptr.isEmpty())
{
const ESM::Position &pos = ptr.getRefData().getPosition();
const Ogre::Vector3 objpos(pos.pos[0], pos.pos[1], pos.pos[2]);
snditer->first->setPosition(objpos);
}
snditer->first->update();
snditer++;
}

@ -13,6 +13,8 @@
#include "containerstore.hpp"
#include "../mwgui/tooltips.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp"
namespace MWWorld
{
@ -77,6 +79,33 @@ namespace MWWorld
throw std::runtime_error ("class does not have item health");
}
float Class::getEvasion(const Ptr& ptr) const
{
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
const MWMechanics::MagicEffects &mageffects = crstats.getMagicEffects();
float evasion = (crstats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(crstats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
evasion *= crstats.getFatigueTerm();
evasion += mageffects.get(MWMechanics::EffectKey(ESM::MagicEffect::Sanctuary)).mMagnitude;
return evasion;
}
void Class::hit(const Ptr& ptr, int type) const
{
throw std::runtime_error("class cannot hit");
}
void Class::onHit(const Ptr& ptr, float damage, bool ishealth, const Ptr& object, const Ptr& attacker, bool successful) const
{
throw std::runtime_error("class cannot be hit");
}
void Class::setActorHealth(const Ptr& ptr, float health, const Ptr& attacker) const
{
throw std::runtime_error("class does not have actor health");
}
boost::shared_ptr<Action> Class::activate (const Ptr& ptr, const Ptr& actor) const
{
return boost::shared_ptr<Action> (new NullAction);

@ -105,6 +105,28 @@ namespace MWWorld
///< Return item max health or throw an exception, if class does not have item health
/// (default implementation: throw an exceoption)
virtual float getEvasion(const Ptr& ptr) const;
///< Gets the chance the given object can evade an attack
virtual void hit(const Ptr& ptr, int type=-1) const;
///< Execute a melee hit, using the current weapon. This will check the relevant skills
/// of the given attacker, and whoever is hit.
/// \param type - type of attack, one of the MWMechanics::CreatureStats::AttackType
/// enums. ignored for creature attacks.
/// (default implementation: throw an exceoption)
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
///< Alerts \a ptr that it's being hit for \a damage points to health if \a ishealth is
/// true (else fatigue) by \a object (sword, arrow, etc). \a attacker specifies the
/// actor responsible for the attack, and \a successful specifies if the hit is
/// successful or not.
virtual void setActorHealth(const Ptr& ptr, float health, const Ptr& attacker=Ptr()) const;
///< Sets a new current health value for the actor, optionally specifying the object causing
/// the change. Use this instead of using CreatureStats directly as this will make sure the
/// correct dialog and actor states are properly handled when being hurt or healed.
/// (default implementation: throw an exceoption)
virtual boost::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
///< Generate action for activation (default implementation: return a null action).

@ -304,6 +304,19 @@ namespace MWWorld
return results;
}
std::pair<std::string,float> PhysicsSystem::getFacedHandle(const Ogre::Vector3 &origin_, const Ogre::Quaternion &orient_, float queryDistance)
{
Ogre::Vector3 dest_ = origin_ + orient_.yAxis()*queryDistance;
btVector3 origin(origin_.x, origin_.y, origin_.z);
btVector3 dest(dest_.x, dest_.y, dest_.z);
std::pair<std::string,float> result = mEngine->rayTest(origin, dest);
result.second *= queryDistance;
return result;
}
void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight)
{
// TODO: store and use

@ -55,6 +55,9 @@ namespace MWWorld
Ogre::Vector3 traceDown(const MWWorld::Ptr &ptr);
std::pair<float, std::string> getFacedHandle (MWWorld::World& world, float queryDistance);
std::pair<std::string,float> getFacedHandle(const Ogre::Vector3 &origin,
const Ogre::Quaternion &orientation,
float queryDistance);
std::vector < std::pair <float, std::string> > getFacedHandles (float queryDistance);
std::vector < std::pair <float, std::string> > getFacedHandles (float mouseX, float mouseY, float queryDistance);

@ -537,8 +537,8 @@ void WeatherManager::stopSounds(bool stopAll)
std::vector<std::string>::iterator it = mSoundsPlaying.begin();
while (it!=mSoundsPlaying.end())
{
if (stopAll || \
!((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) || \
if (stopAll ||
!((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) ||
(*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID)))
{
MWBase::Environment::get().getSoundManager()->stopSound(*it);
@ -551,29 +551,37 @@ void WeatherManager::stopSounds(bool stopAll)
Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const
{
std::vector<char> probability;
RegionModMap::const_iterator iter = mRegionMods.find(Misc::StringUtils::lowerCase(region->mId));
if(iter != mRegionMods.end())
probability = iter->second;
else
{
probability.reserve(10);
probability.push_back(region->mData.mClear);
probability.push_back(region->mData.mCloudy);
probability.push_back(region->mData.mFoggy);
probability.push_back(region->mData.mOvercast);
probability.push_back(region->mData.mRain);
probability.push_back(region->mData.mThunder);
probability.push_back(region->mData.mAsh);
probability.push_back(region->mData.mBlight);
probability.push_back(region->mData.mA);
probability.push_back(region->mData.mB);
}
/*
* All probabilities must add to 100 (responsibility of the user).
* If chances A and B has values 30 and 70 then by generating
* 100 numbers 1..100, 30% will be lesser or equal 30 and
* 70% will be greater than 30 (in theory).
*/
const int probability[] = {
region->mData.mClear,
region->mData.mCloudy,
region->mData.mFoggy,
region->mData.mOvercast,
region->mData.mRain,
region->mData.mThunder,
region->mData.mAsh,
region->mData.mBlight,
region->mData.mA,
region->mData.mB
}; // 10 elements
int chance = (rand() % 100) + 1; // 1..100
int sum = 0;
int i = 0;
for (; i < 10; ++i)
unsigned int i = 0;
for (; i < probability.size(); ++i)
{
sum += probability[i];
if (chance < sum)
@ -681,6 +689,15 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int
setWeather(weather);
}
void WeatherManager::modRegion(const std::string &regionid, const std::vector<char> &chances)
{
mRegionMods[Misc::StringUtils::lowerCase(regionid)] = chances;
// Start transitioning right away if the region no longer supports the current weather type
unsigned int current = getWeatherID();
if(current >= chances.size() || chances[current] == 0)
mWeatherUpdateTime = 0.0f;
}
float WeatherManager::getWindSpeed() const
{
return mWindSpeed;

@ -149,6 +149,8 @@ namespace MWWorld
unsigned int getWeatherID() const;
void modRegion(const std::string &regionid, const std::vector<char> &chances);
private:
float mHour;
int mDay, mMonth;
@ -188,6 +190,9 @@ namespace MWWorld
Ogre::String nextWeather(const ESM::Region* region) const;
WeatherResult mResult;
typedef std::map<std::string,std::vector<char> > RegionModMap;
RegionModMap mRegionMods;
float mSunriseTime;
float mSunsetTime;
float mSunriseDuration;

@ -18,6 +18,7 @@
#include "../mwmechanics/movement.hpp"
#include "../mwrender/sky.hpp"
#include "../mwrender/animation.hpp"
#include "../mwclass/door.hpp"
@ -165,7 +166,7 @@ namespace MWWorld
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mCells (mStore, mEsm),
mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),
mFallback(fallbackMap), mPlayIntro(0)
mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true)
{
mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine();
@ -776,6 +777,28 @@ namespace MWWorld
return object;
}
MWWorld::Ptr World::getFacedObject(const MWWorld::Ptr &ptr, float distance)
{
const ESM::Position &posdata = ptr.getRefData().getPosition();
Ogre::Vector3 pos(posdata.pos);
Ogre::Quaternion rot = Ogre::Quaternion(Ogre::Radian(posdata.rot[2]), Ogre::Vector3::NEGATIVE_UNIT_Z) *
Ogre::Quaternion(Ogre::Radian(posdata.rot[0]), Ogre::Vector3::UNIT_X);
MWRender::Animation *anim = mRendering->getAnimation(ptr);
if(anim != NULL)
{
Ogre::Node *node = anim->getNode("Head");
if(node != NULL)
pos += node->_getDerivedPosition();
}
std::pair<std::string,float> result = mPhysics->getFacedHandle(pos, rot, distance);
if(result.first.empty())
return MWWorld::Ptr();
return searchPtrViaHandle(result.first);
}
void World::deleteObject (const Ptr& ptr)
{
if (ptr.getRefData().getCount()>0)
@ -1351,6 +1374,11 @@ namespace MWWorld
mWeatherManager->changeWeather(region, id);
}
void World::modRegion(const std::string &regionid, const std::vector<char> &chances)
{
mWeatherManager->modRegion(regionid, chances);
}
OEngine::Render::Fader* World::getFader()
{
return mRendering->getFader();
@ -1821,4 +1849,15 @@ namespace MWWorld
}
return false;
}
void World::enableTeleporting(bool enable)
{
mTeleportEnabled = enable;
}
bool World::isTeleportingEnabled() const
{
return mTeleportEnabled;
}
}

@ -117,6 +117,8 @@ namespace MWWorld
int mPlayIntro;
bool mTeleportEnabled;
public:
World (OEngine::Render::OgreRenderer& renderer,
@ -235,6 +237,8 @@ namespace MWWorld
virtual void setMoonColour (bool red);
virtual void modRegion(const std::string &regionid, const std::vector<char> &chances);
virtual float getTimeScaleFactor() const;
virtual void changeToInteriorCell (const std::string& cellName,
@ -252,6 +256,10 @@ namespace MWWorld
virtual MWWorld::Ptr getFacedObject();
///< 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);
virtual void deleteObject (const Ptr& ptr);
virtual void moveObject (const Ptr& ptr, float x, float y, float z);
@ -421,6 +429,12 @@ namespace MWWorld
/// Find position in interior cell near door entrance
/// \return false if interior with given name not exists, true otherwise
virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos);
/// Enables or disables use of teleport spell effects (recall, intervention, etc).
virtual void enableTeleporting(bool enable);
/// Returns true if teleport spell effects are allowed.
virtual bool isTeleportingEnabled() const;
};
}

@ -71,16 +71,26 @@ public:
}
/// Look up the actual object from the index
X* getPtr() const
const X* getPtr() const
{
assert(ptr != NULL);
return ptr;
}
X& get() const
X* getPtr()
{
assert(ptr != NULL);
return ptr;
}
const X& get() const
{ return *getPtr(); }
X& get()
{ return *getPtr(); }
/// Syntactic sugar
X* operator->() const
const X* operator->() const
{ return getPtr(); }
X* operator->()
{ return getPtr(); }
/// Pointers are allowed to be empty
@ -116,6 +126,8 @@ public:
const Ptr& operator[](size_t index) const
{ return list.at(index); }
Ptr& operator[](size_t index)
{ return list.at(index); }
size_t length() const
{ return list.size(); }

@ -688,6 +688,21 @@ class NIFObjectLoader
}
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos));
if(nextpos != std::string::npos)
{
do {
nextpos--;
} while(nextpos > pos && ::isspace(str[nextpos]));
nextpos++;
}
else if(::isspace(*str.rbegin()))
{
std::string::const_iterator last = str.end();
do {
last--;
} while(last != str.begin() && ::isspace(*last));
nextpos = std::distance(str.begin(), ++last);
}
std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
@ -743,7 +758,7 @@ class NIFObjectLoader
e = e->extra;
}
if(!node->controller.empty())
if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode))
createNodeControllers(name, node->controller, objectlist, animflags);
if(node->recType == Nif::RC_NiCamera)

@ -5,6 +5,7 @@ set(SDL4OGRE_LIBRARY "sdl4ogre")
set(SDL4OGRE_SOURCE_FILES
sdlinputwrapper.cpp
sdlcursormanager.cpp
sdlwindowhelper.cpp
)
set(SDL4OGRE_HEADER_FILES

@ -65,10 +65,12 @@ public:
virtual ~WindowListener() {}
/** @remarks The window's visibility changed */
virtual bool windowVisibilityChange( bool visible ) = 0;
virtual void windowVisibilityChange( bool visible ) {};
/** @remarks The window got / lost input focus */
virtual bool windowFocusChange( bool have_focus ) = 0;
virtual void windowFocusChange( bool have_focus ) {}
virtual void windowResized (int x, int y) {}
};
}

@ -4,13 +4,6 @@
#include <OgrePlatform.h>
#include <OgreRoot.h>
/*
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX
# include <X11/Xlib.h>
# include <X11/Xutil.h>
# include <X11/Xos.h>
#endif
*/
namespace SFO
{
@ -19,7 +12,6 @@ namespace SFO
InputWrapper::InputWrapper(SDL_Window* window, Ogre::RenderWindow* ogreWindow) :
mSDLWindow(window),
mOgreWindow(ogreWindow),
mOwnWindow(false),
mWarpCompensate(false),
mMouseRelative(false),
mGrabPointer(false),
@ -27,14 +19,18 @@ namespace SFO
mMouseZ(0),
mMouseY(0),
mMouseX(0),
mMouseInWindow(true)
mMouseInWindow(true),
mJoyListener(NULL),
mKeyboardListener(NULL),
mMouseListener(NULL),
mWindowListener(NULL)
{
_setupOISKeys();
}
InputWrapper::~InputWrapper()
{
if(mSDLWindow != NULL && mOwnWindow)
if(mSDLWindow != NULL)
SDL_DestroyWindow(mSDLWindow);
mSDLWindow = NULL;
}
@ -76,6 +72,25 @@ namespace SFO
case SDL_TEXTINPUT:
mKeyboardListener->textInput(evt.text);
break;
case SDL_JOYAXISMOTION:
if (mJoyListener)
mJoyListener->axisMoved(evt.jaxis, evt.jaxis.axis);
break;
case SDL_JOYBUTTONDOWN:
if (mJoyListener)
mJoyListener->buttonPressed(evt.jbutton, evt.jbutton.button);
break;
case SDL_JOYBUTTONUP:
if (mJoyListener)
mJoyListener->buttonReleased(evt.jbutton, evt.jbutton.button);
break;
case SDL_JOYDEVICEADDED:
//SDL_JoystickOpen(evt.jdevice.which);
//std::cout << "Detected a new joystick: " << SDL_JoystickNameForIndex(evt.jdevice.which) << std::endl;
break;
case SDL_JOYDEVICEREMOVED:
//std::cout << "A joystick has been removed" << std::endl;
break;
case SDL_WINDOWEVENT:
handleWindowEvent(evt);
break;
@ -100,25 +115,62 @@ namespace SFO
SDL_SetWindowGrab(mSDLWindow, SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE);
break;
case SDL_WINDOWEVENT_RESIZED:
case SDL_WINDOWEVENT_SIZE_CHANGED:
int w,h;
SDL_GetWindowSize(mSDLWindow, &w, &h);
// TODO: Fix Ogre to handle this more consistently
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
mOgreWindow->windowMovedOrResized();
#else
mOgreWindow->resize(w, h);
#endif
if (mWindowListener)
mWindowListener->windowResized(evt.window.data1, evt.window.data2);
case SDL_WINDOWEVENT_RESIZED:
// TODO: Fix Ogre to handle this more consistently
#if OGRE_PLATFORM == OGRE_PLATFORM_WIN32
mOgreWindow->windowMovedOrResized();
#else
mOgreWindow->resize(evt.window.data1, evt.window.data2);
#endif
if (mWindowListener)
mWindowListener->windowResized(evt.window.data1, evt.window.data2);
break;
case SDL_WINDOWEVENT_FOCUS_GAINED:
if (mWindowListener)
mWindowListener->windowFocusChange(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST:
if (mWindowListener)
mWindowListener->windowFocusChange(false);
break;
case SDL_WINDOWEVENT_CLOSE:
break;
case SDL_WINDOWEVENT_SHOWN:
mOgreWindow->setVisible(true);
if (mWindowListener)
mWindowListener->windowVisibilityChange(true);
break;
case SDL_WINDOWEVENT_HIDDEN:
mOgreWindow->setVisible(false);
if (mWindowListener)
mWindowListener->windowVisibilityChange(false);
break;
}
}
bool InputWrapper::isModifierHeld(int mod)
bool InputWrapper::isModifierHeld(SDL_Keymod mod)
{
return SDL_GetModState() & mod;
}
bool InputWrapper::isKeyDown(SDL_Scancode key)
{
return SDL_GetKeyboardState(NULL)[key];
}
/// \brief Moves the mouse to the specified point within the viewport
void InputWrapper::warpMouse(int x, int y)
{

@ -22,9 +22,11 @@ namespace SFO
void setMouseEventCallback(MouseListener* listen) { mMouseListener = listen; }
void setKeyboardEventCallback(KeyListener* listen) { mKeyboardListener = listen; }
void setWindowEventCallback(WindowListener* listen) { mWindowListener = listen; }
void setJoyEventCallback(JoyListener* listen) { mJoyListener = listen; }
void capture();
bool isModifierHeld(int mod);
bool isModifierHeld(SDL_Keymod mod);
bool isKeyDown(SDL_Scancode key);
void setMouseRelative(bool relative);
bool getMouseRelative() { return mMouseRelative; }
@ -47,6 +49,7 @@ namespace SFO
SFO::MouseListener* mMouseListener;
SFO::KeyListener* mKeyboardListener;
SFO::WindowListener* mWindowListener;
SFO::JoyListener* mJoyListener;
typedef boost::unordered_map<SDL_Keycode, OIS::KeyCode> KeyMap;
KeyMap mKeyMap;
@ -66,7 +69,6 @@ namespace SFO
SDL_Window* mSDLWindow;
Ogre::RenderWindow* mOgreWindow;
bool mOwnWindow;
};
}

@ -0,0 +1,115 @@
#include "sdlwindowhelper.hpp"
#include <OgreStringConverter.h>
#include <OgreRoot.h>
#include <SDL_syswm.h>
#include <SDL_endian.h>
namespace SFO
{
SDLWindowHelper::SDLWindowHelper (SDL_Window* window, int w, int h,
const std::string& title, bool fullscreen, Ogre::NameValuePairList params)
: mSDLWindow(window)
{
//get the native whnd
struct SDL_SysWMinfo wmInfo;
SDL_VERSION(&wmInfo.version);
if (SDL_GetWindowWMInfo(mSDLWindow, &wmInfo) == -1)
throw std::runtime_error("Couldn't get WM Info!");
Ogre::String winHandle;
switch (wmInfo.subsystem)
{
#ifdef WIN32
case SDL_SYSWM_WINDOWS:
// Windows code
winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.win.window);
break;
#elif __MACOSX__
case SDL_SYSWM_COCOA:
//required to make OGRE play nice with our window
params.insert(std::make_pair("macAPI", "cocoa"));
params.insert(std::make_pair("macAPICocoaUseNSView", "true"));
winHandle = Ogre::StringConverter::toString(WindowContentViewHandle(wmInfo));
break;
#else
case SDL_SYSWM_X11:
winHandle = Ogre::StringConverter::toString((unsigned long)wmInfo.info.x11.window);
break;
#endif
default:
throw std::runtime_error("Unexpected WM!");
break;
}
/// \todo externalWindowHandle is deprecated according to the source code. Figure out a way to get parentWindowHandle
/// to work properly. On Linux/X11 it causes an occasional GLXBadDrawable error.
params.insert(std::make_pair("externalWindowHandle", winHandle));
mWindow = Ogre::Root::getSingleton().createRenderWindow(title, w, h, fullscreen, &params);
}
void SDLWindowHelper::setWindowIcon(const std::string &name)
{
Ogre::TexturePtr texture = Ogre::TextureManager::getSingleton().load(name, Ogre::ResourceGroupManager::AUTODETECT_RESOURCE_GROUP_NAME);
if (texture.isNull())
{
std::stringstream error;
error << "Window icon not found: " << name;
throw std::runtime_error(error.str());
}
Ogre::Image image;
texture->convertToImage(image);
SDL_Surface* surface = SDL_CreateRGBSurface(0,texture->getWidth(),texture->getHeight(),32,0xFF000000,0x00FF0000,0x0000FF00,0x000000FF);
//copy the Ogre texture to an SDL surface
for(size_t x = 0; x < texture->getWidth(); ++x)
{
for(size_t y = 0; y < texture->getHeight(); ++y)
{
Ogre::ColourValue clr = image.getColourAt(x, y, 0);
//set the pixel on the SDL surface to the same value as the Ogre texture's
int bpp = surface->format->BytesPerPixel;
/* Here p is the address to the pixel we want to set */
Uint8 *p = (Uint8 *)surface->pixels + y * surface->pitch + x * bpp;
Uint32 pixel = SDL_MapRGBA(surface->format, clr.r*255, clr.g*255, clr.b*255, clr.a*255);
switch(bpp) {
case 1:
*p = pixel;
break;
case 2:
*(Uint16 *)p = pixel;
break;
case 3:
if(SDL_BYTEORDER == SDL_BIG_ENDIAN) {
p[0] = (pixel >> 16) & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = pixel & 0xff;
} else {
p[0] = pixel & 0xff;
p[1] = (pixel >> 8) & 0xff;
p[2] = (pixel >> 16) & 0xff;
}
break;
case 4:
*(Uint32 *)p = pixel;
break;
}
}
}
SDL_SetWindowIcon(mSDLWindow, surface);
SDL_FreeSurface(surface);
}
}

@ -0,0 +1,31 @@
#ifndef SDL4OGRE_SDLWINDOWHELPER_H
#define SDL4OGRE_SDLWINDOWHELPER_H
#include <OgreRenderWindow.h>
namespace Ogre
{
class RenderWindow;
}
struct SDL_Window;
namespace SFO
{
/// @brief Creates an Ogre window from an SDL window and allows setting an Ogre texture as window icon
class SDLWindowHelper
{
public:
SDLWindowHelper (SDL_Window* window, int w, int h, const std::string& title, bool fullscreen, Ogre::NameValuePairList params);
void setWindowIcon(const std::string& name);
Ogre::RenderWindow* getWindow() { return mWindow; }
private:
Ogre::RenderWindow* mWindow;
SDL_Window* mSDLWindow;
};
}
#endif

@ -3,6 +3,11 @@
<MyGUI type="Layout">
<Widget type="Widget" layer="HUD" position="0 0 300 200" name="_Main">
<!-- Energy bars -->
<Widget type="Widget" skin="" position="13 131 65 12" align="Left Bottom" name="EnemyHealthFrame">
<Widget type="ProgressBar" skin="MW_EnergyBar_Yellow" position="0 0 65 12" align="Left Bottom" name="EnemyHealth">
<Property key="Visible" value="false"/>
</Widget>
</Widget>
<Widget type="Button" skin="" position="13 146 65 12" align="Left Bottom" name="HealthFrame">
<UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/>

@ -42,6 +42,11 @@
<State name="normal" offset = "0 8 4 8"/>
</BasisSkin>
</Skin>
<Skin name = "MW_BarTrack_Yellow" size = "4 8" texture = "smallbars.png" >
<BasisSkin type="MainSkin" offset = "0 0 4 8" align = "ALIGN_STRETCH">
<State name="normal" offset = "0 32 4 8"/>
</BasisSkin>
</Skin>
<!-- Main energy bar widget definitions. There's one for each color.-->
@ -69,4 +74,12 @@
<Child type="Widget" skin="BlackBG" offset = "2 2 60 8" align = "ALIGN_STRETCH" name="Client"/>
</Skin>
<Skin name="MW_EnergyBar_Yellow" size="64 12">
<Property key="TrackSkin" value = "MW_BarTrack_Yellow" />
<Property key="TrackWidth" value = "1" />
<Child type="Widget" skin="MW_BarFrame" offset="0 0 64 12" align="ALIGN_STRETCH"/>
<Child type="Widget" skin="BlackBG" offset = "2 2 60 8" align = "ALIGN_STRETCH" name="Client"/>
</Skin>
</MyGUI>

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

Loading…
Cancel
Save