Merge branch 'master' into filter

Conflicts:
	apps/opencs/model/world/universalid.cpp
	apps/opencs/view/doc/view.cpp
	apps/opencs/view/world/subviews.cpp
This commit is contained in:
Marc Zinnschlag 2013-07-30 15:55:50 +02:00
commit adb05737ee
107 changed files with 3082 additions and 810 deletions
apps
components
extern/sdl4ogre
files/mygui

View file

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

View file

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

View file

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

View file

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

View file

@ -304,6 +304,16 @@ CSMWorld::RefIdCollection& CSMWorld::Data::getReferenceables()
return mReferenceables; return mReferenceables;
} }
const CSMWorld::RefCollection& CSMWorld::Data::getReferences() const
{
return mRefs;
}
CSMWorld::RefCollection& CSMWorld::Data::getReferences()
{
return mRefs;
}
QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id)
{ {
std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType()); std::map<UniversalId::Type, QAbstractItemModel *>::iterator iter = mModelIndex.find (id.getType());
@ -399,3 +409,21 @@ void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
} }
} }
} }
bool CSMWorld::Data::hasId (const std::string& id) const
{
return
getGlobals().searchId (id)!=-1 ||
getGmsts().searchId (id)!=-1 ||
getSkills().searchId (id)!=-1 ||
getClasses().searchId (id)!=-1 ||
getFactions().searchId (id)!=-1 ||
getRaces().searchId (id)!=-1 ||
getSounds().searchId (id)!=-1 ||
getScripts().searchId (id)!=-1 ||
getRegions().searchId (id)!=-1 ||
getBirthsigns().searchId (id)!=-1 ||
getSpells().searchId (id)!=-1 ||
getCells().searchId (id)!=-1 ||
getReferenceables().searchId (id)!=-1;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <iosfwd> #include <iosfwd>
#include <vector>
#include <QMetaType> #include <QMetaType>
@ -16,6 +17,7 @@ namespace CSMWorld
{ {
Class_None = 0, Class_None = 0,
Class_Record, Class_Record,
Class_RefRecord, // referenceable record
Class_SubRecord, Class_SubRecord,
Class_RecordList, Class_RecordList,
Class_Collection, // multiple types of records combined Class_Collection, // multiple types of records combined
@ -128,6 +130,11 @@ namespace CSMWorld
std::string toString() const; std::string toString() const;
std::string getIcon() const;
///< Will return an empty string, if no icon is available.
static std::vector<Type> listReferenceableTypes();
static std::pair<int, const char *> getIdArgPair (unsigned int index); static std::pair<int, const char *> getIdArgPair (unsigned int index);
static unsigned int getIdArgSize (); static unsigned int getIdArgSize ();
}; };

View file

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

View file

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

View file

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

View file

@ -79,6 +79,11 @@ void CSVDoc::View::setupViewMenu()
connect (newWindow, SIGNAL (triggered()), this, SLOT (newView())); connect (newWindow, SIGNAL (triggered()), this, SLOT (newView()));
view->addAction (newWindow); view->addAction (newWindow);
mShowStatusBar = new QAction (tr ("Show Status Bar"), this);
mShowStatusBar->setCheckable (true);
connect (mShowStatusBar, SIGNAL (toggled (bool)), this, SLOT (toggleShowStatusBar (bool)));
view->addAction (mShowStatusBar);
QAction *filters = new QAction (tr ("Filters"), this); QAction *filters = new QAction (tr ("Filters"), this);
connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView())); connect (filters, SIGNAL (triggered()), this, SLOT (addFiltersSubView()));
view->addAction (filters); 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) /// \todo add an user setting to reuse sub views (on a per document basis or on a per top level view basis)
SubView *view = mSubViewFactory.makeSubView (id, *mDocument); SubView *view = mSubViewFactory.makeSubView (id, *mDocument);
view->setStatusBar (mShowStatusBar->isChecked());
mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view); mSubViewWindow.addDockWidget (Qt::TopDockWidgetArea, view);
connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this, connect (view, SIGNAL (focusId (const CSMWorld::UniversalId&)), this,
@ -445,3 +453,12 @@ void CSVDoc::View::updateEditorSetting (const QString &settingName, const QStrin
else if (settingName == "Height") else if (settingName == "Height")
resizeViewHeight (settingValue.toInt()); resizeViewHeight (settingValue.toInt());
} }
void CSVDoc::View::toggleShowStatusBar (bool show)
{
foreach (QObject *view, mSubViewWindow.children())
{
if (CSVDoc::SubView *subView = dynamic_cast<CSVDoc::SubView *> (view))
subView->setStatusBar (show);
}
}

View file

@ -38,6 +38,7 @@ namespace CSVDoc
QAction *mRedo; QAction *mRedo;
QAction *mSave; QAction *mSave;
QAction *mVerify; QAction *mVerify;
QAction *mShowStatusBar;
std::vector<QAction *> mEditingActions; std::vector<QAction *> mEditingActions;
Operations *mOperations; Operations *mOperations;
SubViewFactoryManager mSubViewFactory; SubViewFactoryManager mSubViewFactory;
@ -160,6 +161,8 @@ namespace CSVDoc
void addFiltersSubView(); void addFiltersSubView();
void showUserSettings(); void showUserSettings();
void toggleShowStatusBar (bool show);
}; };
} }

View file

@ -0,0 +1,81 @@
#include "cellcreator.hpp"
#include <limits>
#include <sstream>
#include <QComboBox>
#include <QSpinBox>
#include <QLabel>
std::string CSVWorld::CellCreator::getId() const
{
if (mType->currentIndex()==0)
return GenericCreator::getId();
std::ostringstream stream;
stream << "#" << mX->value() << " " << mY->value();
return stream.str();
}
CSVWorld::CellCreator::CellCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id)
{
mY = new QSpinBox (this);
mY->setVisible (false);
mY->setMinimum (std::numeric_limits<int>::min());
mY->setMaximum (std::numeric_limits<int>::max());
connect (mY, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));
insertAtBeginning (mY, true);
mYLabel = new QLabel ("Y", this);
mYLabel->setVisible (false);
insertAtBeginning (mYLabel, false);
mX = new QSpinBox (this);
mX->setVisible (false);
mX->setMinimum (std::numeric_limits<int>::min());
mX->setMaximum (std::numeric_limits<int>::max());
connect (mX, SIGNAL (valueChanged (int)), this, SLOT (valueChanged (int)));
insertAtBeginning (mX, true);
mXLabel = new QLabel ("X", this);
mXLabel->setVisible (false);
insertAtBeginning (mXLabel, false);
mType = new QComboBox (this);
mType->addItem ("Interior Cell");
mType->addItem ("Exterior Cell");
connect (mType, SIGNAL (currentIndexChanged (int)), this, SLOT (setType (int)));
insertAtBeginning (mType, false);
}
void CSVWorld::CellCreator::reset()
{
mX->setValue (0);
mY->setValue (0);
mType->setCurrentIndex (0);
GenericCreator::reset();
}
void CSVWorld::CellCreator::setType (int index)
{
setManualEditing (index==0);
mXLabel->setVisible (index==1);
mX->setVisible (index==1);
mYLabel->setVisible (index==1);
mY->setVisible (index==1);
update();
}
void CSVWorld::CellCreator::valueChanged (int index)
{
update();
}

View file

@ -0,0 +1,40 @@
#ifndef CSV_WORLD_CELLCREATOR_H
#define CSV_WORLD_CELLCREATOR_H
class QLabel;
class QSpinBox;
class QComboBox;
#include "genericcreator.hpp"
namespace CSVWorld
{
class CellCreator : public GenericCreator
{
Q_OBJECT
QComboBox *mType;
QLabel *mXLabel;
QSpinBox *mX;
QLabel *mYLabel;
QSpinBox *mY;
protected:
virtual std::string getId() const;
public:
CellCreator (CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id);
virtual void reset();
private slots:
void setType (int index);
void valueChanged (int index);
};
}
#endif

View file

@ -0,0 +1,13 @@
#include "creator.hpp"
CSVWorld::Creator:: ~Creator() {}
CSVWorld::CreatorFactoryBase::~CreatorFactoryBase() {}
CSVWorld::Creator *CSVWorld::NullCreatorFactory::makeCreator (CSMWorld::Data& data,
QUndoStack& undoStack, const CSMWorld::UniversalId& id) const
{
return 0;
}

View file

@ -0,0 +1,86 @@
#ifndef CSV_WORLD_CREATOR_H
#define CSV_WORLD_CREATOR_H
#include <QWidget>
class QUndoStack;
namespace CSMWorld
{
class Data;
class UniversalId;
}
namespace CSVWorld
{
/// \brief Record creator UI base class
class Creator : public QWidget
{
Q_OBJECT
public:
virtual ~Creator();
virtual void reset() = 0;
virtual void setEditLock (bool locked) = 0;
signals:
void done();
void requestFocus (const std::string& id);
///< Request owner of this creator to focus the just created \a id. The owner may
/// ignore this request.
};
/// \brief Base class for Creator factory
class CreatorFactoryBase
{
public:
virtual ~CreatorFactoryBase();
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const = 0;
///< The ownership of the returned Creator is transferred to the caller.
///
/// \note The function can return a 0-pointer, which means no UI for creating/deleting
/// records should be provided.
};
/// \brief Creator factory that does not produces any creator
class NullCreatorFactory : public CreatorFactoryBase
{
public:
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const;
///< The ownership of the returned Creator is transferred to the caller.
///
/// \note The function always returns 0.
};
template<class CreatorT>
class CreatorFactory : public CreatorFactoryBase
{
public:
virtual Creator *makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const;
///< The ownership of the returned Creator is transferred to the caller.
///
/// \note The function can return a 0-pointer, which means no UI for creating/deleting
/// records should be provided.
};
template<class CreatorT>
Creator *CreatorFactory<CreatorT>::makeCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id) const
{
return new CreatorT (data, undoStack, id);
}
}
#endif

View file

@ -0,0 +1,135 @@
#include "genericcreator.hpp"
#include <memory>
#include <QHBoxLayout>
#include <QPushButton>
#include <QLineEdit>
#include <QUndoStack>
#include "../../model/world/commands.hpp"
#include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp"
#include "idvalidator.hpp"
void CSVWorld::GenericCreator::update()
{
mErrors = getErrors();
mCreate->setToolTip (QString::fromUtf8 (mErrors.c_str()));
mId->setToolTip (QString::fromUtf8 (mErrors.c_str()));
mCreate->setEnabled (mErrors.empty() && !mLocked);
}
void CSVWorld::GenericCreator::setManualEditing (bool enabled)
{
mId->setVisible (enabled);
}
void CSVWorld::GenericCreator::insertAtBeginning (QWidget *widget, bool stretched)
{
mLayout->insertWidget (0, widget, stretched ? 1 : 0);
}
void CSVWorld::GenericCreator::insertBeforeButtons (QWidget *widget, bool stretched)
{
mLayout->insertWidget (mLayout->count()-2, widget, stretched ? 1 : 0);
}
std::string CSVWorld::GenericCreator::getId() const
{
return mId->text().toUtf8().constData();
}
void CSVWorld::GenericCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const {}
const CSMWorld::Data& CSVWorld::GenericCreator::getData() const
{
return mData;
}
CSMWorld::Data& CSVWorld::GenericCreator::getData()
{
return mData;
}
CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false)
{
mLayout = new QHBoxLayout;
mLayout->setContentsMargins (0, 0, 0, 0);
mId = new QLineEdit;
mId->setValidator (new IdValidator (this));
mLayout->addWidget (mId, 1);
mCreate = new QPushButton ("Create");
mLayout->addWidget (mCreate);
QPushButton *cancelButton = new QPushButton ("Cancel");
mLayout->addWidget (cancelButton);
setLayout (mLayout);
connect (cancelButton, SIGNAL (clicked (bool)), this, SIGNAL (done()));
connect (mCreate, SIGNAL (clicked (bool)), this, SLOT (create()));
connect (mId, SIGNAL (textChanged (const QString&)), this, SLOT (textChanged (const QString&)));
}
void CSVWorld::GenericCreator::setEditLock (bool locked)
{
mLocked = locked;
update();
}
void CSVWorld::GenericCreator::reset()
{
mId->setText ("");
update();
}
std::string CSVWorld::GenericCreator::getErrors() const
{
std::string errors;
std::string id = getId();
if (id.empty())
{
errors = "Missing ID";
}
else if (mData.hasId (id))
{
errors = "ID is already in use";
}
return errors;
}
void CSVWorld::GenericCreator::textChanged (const QString& text)
{
update();
}
void CSVWorld::GenericCreator::create()
{
if (!mLocked)
{
std::string id = getId();
std::auto_ptr<CSMWorld::CreateCommand> command (new CSMWorld::CreateCommand (
dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel (mListId)), id));
configureCreateCommand (*command);
mUndoStack.push (command.release());
emit done();
emit requestFocus (id);
}
}

View file

@ -0,0 +1,73 @@
#ifndef CSV_WORLD_GENERICCREATOR_H
#define CSV_WORLD_GENERICCREATOR_H
class QPushButton;
class QLineEdit;
class QHBoxLayout;
#include "creator.hpp"
#include "../../model/world/universalid.hpp"
namespace CSMWorld
{
class CreateCommand;
}
namespace CSVWorld
{
class GenericCreator : public Creator
{
Q_OBJECT
CSMWorld::Data& mData;
QUndoStack& mUndoStack;
CSMWorld::UniversalId mListId;
QPushButton *mCreate;
QLineEdit *mId;
std::string mErrors;
QHBoxLayout *mLayout;
bool mLocked;
protected:
void update();
virtual void setManualEditing (bool enabled);
///< Enable/disable manual ID editing (enabled by default).
void insertAtBeginning (QWidget *widget, bool stretched);
void insertBeforeButtons (QWidget *widget, bool stretched);
virtual std::string getId() const;
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
const CSMWorld::Data& getData() const;
CSMWorld::Data& getData();
public:
GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
virtual void setEditLock (bool locked);
virtual void reset();
virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error.
private slots:
void textChanged (const QString& text);
void create();
};
}
#endif

View file

@ -0,0 +1,26 @@
#include "idvalidator.hpp"
bool CSVWorld::IdValidator::isValid (const QChar& c, bool first) const
{
if (c.isLetter() || c=='_')
return true;
if (!first && (c.isDigit() || c.isSpace()))
return true;
return false;
}
CSVWorld::IdValidator::IdValidator (QObject *parent) : QValidator (parent) {}
QValidator::State CSVWorld::IdValidator::validate (QString& input, int& pos) const
{
bool first = true;
for (QString::const_iterator iter (input.begin()); iter!=input.end(); ++iter, first = false)
if (!isValid (*iter, first))
return QValidator::Invalid;
return QValidator::Acceptable;
}

View file

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

View file

@ -0,0 +1,43 @@
#include "referenceablecreator.hpp"
#include <QComboBox>
#include <QLabel>
#include "../../model/world/universalid.hpp"
#include "../../model/world/commands.hpp"
void CSVWorld::ReferenceableCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
command.setType (
static_cast<CSMWorld::UniversalId::Type> (mType->itemData (mType->currentIndex()).toInt()));
}
CSVWorld::ReferenceableCreator::ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id)
{
QLabel *label = new QLabel ("Type", this);
insertBeforeButtons (label, false);
std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
mType = new QComboBox (this);
for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
iter!=types.end(); ++iter)
{
CSMWorld::UniversalId id (*iter, "");
mType->addItem (QIcon (id.getIcon().c_str()), id.getTypeName().c_str(),
static_cast<int> (id.getType()));
}
insertBeforeButtons (mType, false);
}
void CSVWorld::ReferenceableCreator::reset()
{
mType->setCurrentIndex (0);
GenericCreator::reset();
}

View file

@ -0,0 +1,30 @@
#ifndef CSV_WORLD_REFERENCEABLECREATOR_H
#define CSV_WORLD_REFERENCEABLECREATOR_H
class QComboBox;
#include "genericcreator.hpp"
namespace CSVWorld
{
class ReferenceableCreator : public GenericCreator
{
Q_OBJECT
QComboBox *mType;
private:
virtual void configureCreateCommand (CSMWorld::CreateCommand& command) const;
public:
ReferenceableCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id);
virtual void reset();
};
}
#endif

View file

@ -0,0 +1,70 @@
#include "referencecreator.hpp"
#include <QLabel>
#include <QLineEdit>
#include "../../model/world/data.hpp"
#include "../../model/world/commands.hpp"
std::string CSVWorld::ReferenceCreator::getId() const
{
return mId;
}
void CSVWorld::ReferenceCreator::configureCreateCommand (CSMWorld::CreateCommand& command) const
{
/// \todo avoid using hard-coded column numbers
command.addValue (2, mCell->text());
}
CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id)
: GenericCreator (data, undoStack, id)
{
QLabel *label = new QLabel ("Cell", this);
insertBeforeButtons (label, false);
mCell = new QLineEdit (this);
insertBeforeButtons (mCell, true);
setManualEditing (false);
connect (mCell, SIGNAL (textChanged (const QString&)), this, SLOT (cellChanged()));
}
void CSVWorld::ReferenceCreator::reset()
{
mCell->setText ("");
mId = getData().getReferences().getNewId();
GenericCreator::reset();
}
std::string CSVWorld::ReferenceCreator::getErrors() const
{
std::string errors = GenericCreator::getErrors();
std::string cell = mCell->text().toUtf8().constData();
if (cell.empty())
{
if (!errors.empty())
errors += "<br>";
errors += "Missing Cell ID";
}
else if (getData().getCells().searchId (cell)==-1)
{
if (!errors.empty())
errors += "<br>";
errors += "Invalid Cell ID";
}
return errors;
}
void CSVWorld::ReferenceCreator::cellChanged()
{
update();
}

View file

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

View file

@ -27,26 +27,15 @@ CSVWorld::RefIdTypeDelegateFactory::UidTypeList CSVWorld::RefIdTypeDelegateFacto
{ {
UidTypeList list; UidTypeList list;
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Activator, ":./activator.png")); std::vector<CSMWorld::UniversalId::Type> types = CSMWorld::UniversalId::listReferenceableTypes();
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Potion, ":./potion.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Apparatus, ":./apparatus.png")); for (std::vector<CSMWorld::UniversalId::Type>::const_iterator iter (types.begin());
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Armor, ":./armor.png")); iter!=types.end(); ++iter)
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Book, ":./book.png")); {
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Clothing, ":./clothing.png")); CSMWorld::UniversalId id (*iter, "");
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Container, ":./container.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Creature, ":./creature.png")); list.push_back (std::make_pair (id.getType(), id.getIcon().c_str()));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Door, ":./door.png")); }
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Ingredient, ":./ingredient.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_CreatureLevelledList, ":./creature.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_ItemLevelledList, ":./item.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Light, ":./light.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Lockpick, ":./lockpick.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Miscellaneous, ":./misc.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Npc, ":./npc.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Probe, ":./probe.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Repair, ":./repair.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Static, ":./static.png"));
list.push_back (std::make_pair (CSMWorld::UniversalId::Type_Weapon, ":./weapon.png"));
return list; return list;
} }

View file

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

View file

@ -44,40 +44,62 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); /// \todo Do not use hardcoded column numbers
std::vector<std::string> revertableIds; std::vector<std::string> revertableIds;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) if (mProxyModel->columnCount()>0)
{ {
std::string id = mProxyModel->data (*iter).toString().toStdString(); QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
CSMWorld::RecordBase::State state = CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt()); static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_BaseOnly) if (state!=CSMWorld::RecordBase::State_BaseOnly)
{
std::string id = mModel->data (mModel->index (index.row(), 0)).
toString().toUtf8().constData();
revertableIds.push_back (id); revertableIds.push_back (id);
} }
}
}
return revertableIds; return revertableIds;
} }
std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); /// \todo Do not use hardcoded column numbers
std::vector<std::string> deletableIds; std::vector<std::string> deletableIds;
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) if (mProxyModel->columnCount()>0)
{ {
std::string id = mProxyModel->data (*iter).toString().toStdString(); QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
CSMWorld::RecordBase::State state = CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (mModel->data (mModel->getModelIndex (id, 1)).toInt()); static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted) if (state!=CSMWorld::RecordBase::State_Deleted)
{
std::string id = mModel->data (mModel->index (index.row(), 0)).
toString().toUtf8().constData();
deletableIds.push_back (id); deletableIds.push_back (id);
} }
}
}
return deletableIds; return deletableIds;
} }
@ -126,7 +148,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
if (createAndDelete) if (createAndDelete)
{ {
mCreateAction = new QAction (tr ("Add Record"), this); mCreateAction = new QAction (tr ("Add Record"), this);
connect (mCreateAction, SIGNAL (triggered()), this, SLOT (createRecord())); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
addAction (mCreateAction); addAction (mCreateAction);
} }
@ -137,6 +159,17 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
mDeleteAction = new QAction (tr ("Delete Record"), this); mDeleteAction = new QAction (tr ("Delete Record"), this);
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
addAction (mDeleteAction); addAction (mDeleteAction);
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate()));
/// \note This signal could instead be connected to a slot that filters out changes not affecting
/// the records status column (for permanence reasons)
connect (mProxyModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (tableSizeUpdate()));
connect (selectionModel(), SIGNAL (selectionChanged (const QItemSelection&, const QItemSelection&)),
this, SLOT (selectionSizeUpdate ()));
} }
void CSVWorld::Table::setEditLock (bool locked) void CSVWorld::Table::setEditLock (bool locked)
@ -154,22 +187,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString()); mProxyModel->data (mProxyModel->index (row, 0)).toString().toStdString());
} }
#include <sstream> /// \todo remove
void CSVWorld::Table::createRecord()
{
if (!mEditLock)
{
/// \todo ask the user for an ID instead.
static int index = 0;
std::ostringstream stream;
stream << "id" << index++;
mUndoStack.push (new CSMWorld::CreateCommand (*mProxyModel, stream.str()));
}
}
void CSVWorld::Table::revertRecord() void CSVWorld::Table::revertRecord()
{ {
if (!mEditLock) if (!mEditLock)
@ -231,3 +248,46 @@ void CSVWorld::Table::updateEditorSetting (const QString &settingName, const QSt
updateEditorSetting (settingName, settingValue)) updateEditorSetting (settingName, settingValue))
emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i)); emit dataChanged (mModel->index (0, i), mModel->index (mModel->rowCount()-1, i));
} }
void CSVWorld::Table::tableSizeUpdate()
{
int size = 0;
int deleted = 0;
int modified = 0;
if (mModel->columnCount()>0)
{
int rows = mModel->rowCount();
for (int i=0; i<rows; ++i)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (i, 0));
/// \todo Do not use hardcoded column numbers
int state = mModel->data (mModel->index (index.row(), 1)).toInt();
switch (state)
{
case CSMWorld::RecordBase::State_BaseOnly: ++size; break;
case CSMWorld::RecordBase::State_Modified: ++size; ++modified; break;
case CSMWorld::RecordBase::State_ModifiedOnly: ++size; ++modified; break;
case CSMWorld::RecordBase:: State_Deleted: ++deleted; ++modified; break;
}
}
}
tableSizeChanged (size, deleted, modified);
}
void CSVWorld::Table::selectionSizeUpdate()
{
selectionSizeChanged (selectionModel()->selectedRows().size());
}
void CSVWorld::Table::requestFocus (const std::string& id)
{
QModelIndex index = mProxyModel->getModelIndex (id, 0);
if (index.isValid())
scrollTo (index, QAbstractItemView::PositionAtTop);
}

View file

@ -60,15 +60,31 @@ namespace CSVWorld
void editRequest (int row); void editRequest (int row);
private slots: void selectionSizeChanged (int size);
void createRecord(); void tableSizeChanged (int size, int deleted, int modified);
///< \param size Number of not deleted records
/// \param deleted Number of deleted records
/// \param modified Number of added and modified records
void createRequest();
private slots:
void revertRecord(); void revertRecord();
void deleteRecord(); void deleteRecord();
void editRecord(); void editRecord();
public slots:
void tableSizeUpdate();
void selectionSizeUpdate();
void requestFocus (const std::string& id);
}; };
} }

View file

@ -0,0 +1,156 @@
#include "tablebottombox.hpp"
#include <sstream>
#include <QStatusBar>
#include <QStackedLayout>
#include <QLabel>
#include "creator.hpp"
void CSVWorld::TableBottomBox::updateStatus()
{
if (mShowStatusBar)
{
static const char *sLabels[4] = { "record", "deleted", "touched", "selected" };
static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" };
std::ostringstream stream;
bool first = true;
for (int i=0; i<4; ++i)
{
if (mStatusCount[i]>0)
{
if (first)
first = false;
else
stream << ", ";
stream
<< mStatusCount[i] << ' '
<< (mStatusCount[i]==1 ? sLabels[i] : sLabelsPlural[i]);
}
}
mStatus->setText (QString::fromUtf8 (stream.str().c_str()));
}
}
CSVWorld::TableBottomBox::TableBottomBox (const CreatorFactoryBase& creatorFactory,
CSMWorld::Data& data, QUndoStack& undoStack, const CSMWorld::UniversalId& id, QWidget *parent)
: QWidget (parent), mShowStatusBar (false), mCreating (false)
{
for (int i=0; i<4; ++i)
mStatusCount[i] = 0;
setVisible (false);
mLayout = new QStackedLayout;
mLayout->setContentsMargins (0, 0, 0, 0);
mStatus = new QLabel;
mStatusBar = new QStatusBar;
mStatusBar->addWidget (mStatus);
mLayout->addWidget (mStatusBar);
setLayout (mLayout);
mCreator = creatorFactory.makeCreator (data, undoStack, id);
mLayout->addWidget (mCreator);
connect (mCreator, SIGNAL (done()), this, SLOT (createRequestDone()));
connect (mCreator, SIGNAL (requestFocus (const std::string&)),
this, SIGNAL (requestFocus (const std::string&)));
}
void CSVWorld::TableBottomBox::setEditLock (bool locked)
{
if (mCreator)
mCreator->setEditLock (locked);
}
CSVWorld::TableBottomBox::~TableBottomBox()
{
delete mCreator;
}
void CSVWorld::TableBottomBox::setStatusBar (bool show)
{
if (show!=mShowStatusBar)
{
setVisible (show || mCreating);
mShowStatusBar = show;
if (show)
updateStatus();
}
}
bool CSVWorld::TableBottomBox::canCreateAndDelete() const
{
return mCreator;
}
void CSVWorld::TableBottomBox::createRequestDone()
{
if (!mShowStatusBar)
setVisible (false);
else
updateStatus();
mLayout->setCurrentWidget (mStatusBar);
mCreating = false;
}
void CSVWorld::TableBottomBox::selectionSizeChanged (int size)
{
if (mStatusCount[3]!=size)
{
mStatusCount[3] = size;
updateStatus();
}
}
void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modified)
{
bool changed = false;
if (mStatusCount[0]!=size)
{
mStatusCount[0] = size;
changed = true;
}
if (mStatusCount[1]!=deleted)
{
mStatusCount[1] = deleted;
changed = true;
}
if (mStatusCount[2]!=modified)
{
mStatusCount[2] = modified;
changed = true;
}
if (changed)
updateStatus();
}
void CSVWorld::TableBottomBox::createRequest()
{
mCreator->reset();
mLayout->setCurrentWidget (mCreator);
setVisible (true);
mCreating = true;
}

View file

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

View file

@ -1,22 +1,55 @@
#include "tablesubview.hpp" #include "tablesubview.hpp"
#include <QVBoxLayout>
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "table.hpp" #include "table.hpp"
#include "tablebottombox.hpp"
#include "creator.hpp"
CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
bool createAndDelete) const CreatorFactoryBase& creatorFactory)
: SubView (id) : SubView (id)
{ {
setWidget (mTable = new Table (id, document.getData(), document.getUndoStack(), createAndDelete)); QVBoxLayout *layout = new QVBoxLayout;
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 (editRequest (int)), this, SLOT (editRequest (int)));
connect (mTable, SIGNAL (selectionSizeChanged (int)),
mBottom, SLOT (selectionSizeChanged (int)));
connect (mTable, SIGNAL (tableSizeChanged (int, int, int)),
mBottom, SLOT (tableSizeChanged (int, int, int)));
mTable->tableSizeUpdate();
mTable->selectionSizeUpdate();
if (mBottom->canCreateAndDelete())
connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest()));
connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&)));
} }
void CSVWorld::TableSubView::setEditLock (bool locked) void CSVWorld::TableSubView::setEditLock (bool locked)
{ {
mTable->setEditLock (locked); mTable->setEditLock (locked);
mBottom->setEditLock (locked);
} }
void CSVWorld::TableSubView::editRequest (int row) void CSVWorld::TableSubView::editRequest (int row)
@ -28,3 +61,8 @@ void CSVWorld::TableSubView::updateEditorSetting(const QString &settingName, con
{ {
mTable->updateEditorSetting(settingName, settingValue); mTable->updateEditorSetting(settingName, settingValue);
} }
void CSVWorld::TableSubView::setStatusBar (bool show)
{
mBottom->setStatusBar (show);
}

View file

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

View file

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

View file

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

View file

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

View file

@ -11,6 +11,7 @@
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp" #include "../mwworld/actiontalk.hpp"
@ -148,6 +149,70 @@ namespace MWClass
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats; return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mCreatureStats;
} }
void Creature::hit(const MWWorld::Ptr& ptr, int type) const
{
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
{
// NOTE: 'object' and/or 'attacker' may be empty.
if(!successful)
{
// TODO: Handle HitAttemptOnMe script function
// Missed
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "miss", 1.0f, 1.0f);
return;
}
if(!object.isEmpty())
getCreatureStats(ptr).setLastHitObject(MWWorld::Class::get(object).getId(object));
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
{
const std::string &script = ptr.get<ESM::Creature>()->mBase->mScript;
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
if(!script.empty())
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
}
if(ishealth)
{
if(damage > 0.0f)
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage);
getCreatureStats(ptr).setFatigue(fatigue);
}
}
void Creature::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
{
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
bool wasDead = crstats.isDead();
MWMechanics::DynamicStat<float> stat(crstats.getHealth());
stat.setCurrent(health);
crstats.setHealth(stat);
if(!wasDead && crstats.isDead())
{
// actor was just killed
}
else if(wasDead && !crstats.isDead())
{
// actor was just resurrected
}
}
boost::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr, boost::shared_ptr<MWWorld::Action> Creature::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const const MWWorld::Ptr& actor) const
{ {
@ -355,6 +420,8 @@ namespace MWClass
return 5; return 5;
if(name == "scream") if(name == "scream")
return 6; return 6;
if(name == "land")
return 7;
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
} }

View file

@ -42,6 +42,12 @@ namespace MWClass
virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const; virtual MWMechanics::CreatureStats& getCreatureStats (const MWWorld::Ptr& ptr) const;
///< Return creature stats ///< Return creature stats
virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const; const MWWorld::Ptr& actor) const;
///< Generate action for activation ///< Generate action for activation

View file

@ -13,6 +13,8 @@
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
@ -113,7 +115,19 @@ namespace
// initial health // initial health
int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase(); int strength = creatureStats.getAttribute(ESM::Attribute::Strength).getBase();
int endurance = creatureStats.getAttribute(ESM::Attribute::Endurance).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; 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, boost::shared_ptr<MWWorld::Action> Npc::activate (const MWWorld::Ptr& ptr,
const MWWorld::Ptr& actor) const const MWWorld::Ptr& actor) const
{ {
@ -592,18 +843,19 @@ namespace MWClass
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
{ {
MWWorld::InventoryStore& invStore = MWWorld::Class::get(ptr).getInventoryStore(ptr); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
int ratings[MWWorld::InventoryStore::Slots]; MWMechanics::NpcStats &stats = getNpcStats(ptr);
MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat();
int unarmoredSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(ESM::Skill::Unarmored).getModified(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
for (int i = 0; i < MWWorld::InventoryStore::Slots; ++i) int ratings[MWWorld::InventoryStore::Slots];
for(int i = 0;i < MWWorld::InventoryStore::Slots;i++)
{ {
MWWorld::ContainerStoreIterator it = invStore.getSlot(i); MWWorld::ContainerStoreIterator it = invStore.getSlot(i);
if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name()) if (it == invStore.end() || it->getTypeName() != typeid(ESM::Armor).name())
@ -613,28 +865,27 @@ namespace MWClass
} }
else else
{ {
MWWorld::LiveCellRef<ESM::Armor> *ref = MWWorld::LiveCellRef<ESM::Armor> *ref = it->get<ESM::Armor>();
it->get<ESM::Armor>();
int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it); int armorSkillType = MWWorld::Class::get(*it).getEquipmentSkill(*it);
int armorSkill = MWWorld::Class::get(ptr).getNpcStats(ptr).getSkill(armorSkillType).getModified(); int armorSkill = stats.getSkill(armorSkillType).getModified();
if (ref->mBase->mData.mWeight == 0) if(ref->mBase->mData.mWeight == 0)
ratings[i] = ref->mBase->mData.mArmor; ratings[i] = ref->mBase->mData.mArmor;
else else
ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill; ratings[i] = ref->mBase->mData.mArmor * armorSkill / iBaseArmorSkill;
} }
} }
float shield = MWWorld::Class::get(ptr).getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude; float shield = getCreatureStats(ptr).getMagicEffects().get(ESM::MagicEffect::Shield).mMagnitude;
return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3 return ratings[MWWorld::InventoryStore::Slot_Cuirass] * 0.3f
+ (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet] + (ratings[MWWorld::InventoryStore::Slot_CarriedLeft] + ratings[MWWorld::InventoryStore::Slot_Helmet]
+ ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots] + ratings[MWWorld::InventoryStore::Slot_Greaves] + ratings[MWWorld::InventoryStore::Slot_Boots]
+ ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron] + ratings[MWWorld::InventoryStore::Slot_LeftPauldron] + ratings[MWWorld::InventoryStore::Slot_RightPauldron]
) * 0.1 ) * 0.1f
+ (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + MWWorld::InventoryStore::Slot_RightGauntlet) + (ratings[MWWorld::InventoryStore::Slot_LeftGauntlet] + ratings[MWWorld::InventoryStore::Slot_RightGauntlet])
* 0.05 * 0.05f
+ shield; + shield;
} }
@ -689,7 +940,7 @@ namespace MWClass
case ESM::Skill::LightArmor: case ESM::Skill::LightArmor:
return "FootLightLeft"; return "FootLightLeft";
case ESM::Skill::MediumArmor: case ESM::Skill::MediumArmor:
return "FootMediumLeft"; return "FootMedLeft";
case ESM::Skill::HeavyArmor: case ESM::Skill::HeavyArmor:
return "FootHeavyLeft"; return "FootHeavyLeft";
} }
@ -714,7 +965,7 @@ namespace MWClass
case ESM::Skill::LightArmor: case ESM::Skill::LightArmor:
return "FootLightRight"; return "FootLightRight";
case ESM::Skill::MediumArmor: case ESM::Skill::MediumArmor:
return "FootMediumRight"; return "FootMedRight";
case ESM::Skill::HeavyArmor: case ESM::Skill::HeavyArmor:
return "FootHeavyRight"; return "FootHeavyRight";
} }
@ -730,6 +981,8 @@ namespace MWClass
return ""; return "";
if(name == "scream") if(name == "scream")
return ""; return "";
if(name == "land")
return "";
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -10,6 +10,7 @@ namespace MWGui
public: public:
CountDialog(); CountDialog();
void open(const std::string& item, const std::string& message, const int maxCount); void open(const std::string& item, const std::string& message, const int maxCount);
void cancel();
typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt; typedef MyGUI::delegates::CMultiDelegate2<MyGUI::Widget*, int> EventHandle_WidgetInt;

View file

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

View file

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

View file

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

View file

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

View file

@ -7,6 +7,8 @@
#include <boost/algorithm/string.hpp> #include <boost/algorithm/string.hpp>
#include <boost/math/common_factor_rt.hpp> #include <boost/math/common_factor_rt.hpp>
#include <SDL_video.h>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
@ -91,6 +93,7 @@ namespace MWGui
WindowBase("openmw_settings_window.layout") WindowBase("openmw_settings_window.layout")
{ {
getWidget(mOkButton, "OkButton"); getWidget(mOkButton, "OkButton");
getWidget(mBestAttackButton, "BestAttackButton");
getWidget(mSubtitlesButton, "SubtitlesButton"); getWidget(mSubtitlesButton, "SubtitlesButton");
getWidget(mCrosshairButton, "CrosshairButton"); getWidget(mCrosshairButton, "CrosshairButton");
getWidget(mResolutionList, "ResolutionList"); getWidget(mResolutionList, "ResolutionList");
@ -131,6 +134,7 @@ namespace MWGui
mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled); mSubtitlesButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
mCrosshairButton->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); mInvertYButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onButtonToggled);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);
mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled); mShadersButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onShadersToggled);
@ -170,16 +174,14 @@ namespace MWGui
mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings); mResetControlsButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onResetDefaultBindings);
// fill resolution list // fill resolution list
Ogre::RenderSystem* rs = Ogre::Root::getSingleton().getRenderSystem(); int screen = Settings::Manager::getInt("screen", "Video");
Ogre::StringVector videoModes = rs->getConfigOptions()["Video Mode"].possibleValues; int numDisplayModes = SDL_GetNumDisplayModes(screen);
std::vector < std::pair<int, int> > resolutions; std::vector < std::pair<int, int> > resolutions;
for (Ogre::StringVector::const_iterator it=videoModes.begin(); for (int i = 0; i < numDisplayModes; i++)
it!=videoModes.end(); ++it)
{ {
SDL_DisplayMode mode;
int resX, resY; SDL_GetDisplayMode(screen, i, &mode);
parseResolution (resX, resY, *it); resolutions.push_back(std::make_pair(mode.w, mode.h));
resolutions.push_back(std::make_pair(resX, resY));
} }
std::sort(resolutions.begin(), resolutions.end(), sortResolutions); std::sort(resolutions.begin(), resolutions.end(), sortResolutions);
for (std::vector < std::pair<int, int> >::const_iterator it=resolutions.begin(); for (std::vector < std::pair<int, int> >::const_iterator it=resolutions.begin();
@ -200,6 +202,7 @@ namespace MWGui
mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}"); mSubtitlesButton->setCaptionWithReplacing(Settings::Manager::getBool("subtitles", "GUI") ? "#{sOn}" : "#{sOff}");
mCrosshairButton->setCaptionWithReplacing(Settings::Manager::getBool("crosshair", "HUD") ? "#{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); float fovVal = (Settings::Manager::getFloat("field of view", "General")-sFovMin)/(sFovMax-sFovMin);
mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1)); mFOVSlider->setScrollPosition(fovVal * (mFOVSlider->getScrollRange()-1));
@ -269,15 +272,12 @@ namespace MWGui
if (index == MyGUI::ITEM_NONE) if (index == MyGUI::ITEM_NONE)
return; return;
/*
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
dialog->open("#{sNotifyMessage67}"); dialog->open("#{sNotifyMessage67}");
dialog->eventOkClicked.clear(); dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept); dialog->eventOkClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionAccept);
dialog->eventCancelClicked.clear(); dialog->eventCancelClicked.clear();
dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel); dialog->eventCancelClicked += MyGUI::newDelegate(this, &SettingsWindow::onResolutionCancel);
*/
onResolutionAccept();
} }
void SettingsWindow::onResolutionAccept() void SettingsWindow::onResolutionAccept()
@ -290,9 +290,6 @@ namespace MWGui
Settings::Manager::setInt("resolution y", "Video", resY); Settings::Manager::setInt("resolution y", "Video", resY);
apply(); apply();
MWBase::Environment::get().getWindowManager()->
messageBox("New resolution will be applied after a restart", std::vector<std::string>());
} }
void SettingsWindow::onResolutionCancel() void SettingsWindow::onResolutionCancel()
@ -361,8 +358,6 @@ namespace MWGui
{ {
Settings::Manager::setBool("fullscreen", "Video", newState); Settings::Manager::setBool("fullscreen", "Video", newState);
apply(); apply();
MWBase::Environment::get().getWindowManager()->
messageBox("Fullscreen will be applied after a restart", std::vector<std::string>());
} }
} }
else if (_sender == mVSyncButton) else if (_sender == mVSyncButton)
@ -407,6 +402,8 @@ namespace MWGui
Settings::Manager::setBool("crosshair", "HUD", newState); Settings::Manager::setBool("crosshair", "HUD", newState);
else if (_sender == mSubtitlesButton) else if (_sender == mSubtitlesButton)
Settings::Manager::setBool("subtitles", "GUI", newState); Settings::Manager::setBool("subtitles", "GUI", newState);
else if (_sender == mBestAttackButton)
Settings::Manager::setBool("best attack", "Game", newState);
apply(); apply();
} }

View file

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

View file

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

View file

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

View file

@ -20,6 +20,7 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwgui/bookwindow.hpp" #include "../mwgui/bookwindow.hpp"
#include "../mwmechanics/creaturestats.hpp"
using namespace ICS; using namespace ICS;
@ -160,6 +161,26 @@ namespace MWInput
resetIdleTime (); resetIdleTime ();
int action = channel->getNumber(); 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) if (currentValue == 1)
{ {
// trigger action activated // trigger action activated
@ -183,13 +204,15 @@ namespace MWInput
case A_Activate: case A_Activate:
resetIdleTime(); resetIdleTime();
if (mWindows.getMode() == MWGui::GM_Container) { if (mWindows.isGuiMode())
{
if (mWindows.getMode() == MWGui::GM_Container)
toggleContainer (); toggleContainer ();
} else if (MWBase::Environment::get().getWindowManager()->isGuiMode()) { else
MWBase::Environment::get().getWindowManager()->enterPressed(); MWBase::Environment::get().getWindowManager()->activateKeyPressed();
} else {
activate();
} }
else
activate();
break; break;
case A_Journal: case A_Journal:
toggleJournal (); toggleJournal ();
@ -364,7 +387,7 @@ namespace MWInput
if (mControlSwitch["playerviewswitch"]) { if (mControlSwitch["playerviewswitch"]) {
// work around preview mode toggle when pressing Alt+Tab // work around preview mode toggle when pressing Alt+Tab
if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(KMOD_ALT)) { if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) {
if (mPreviewPOVDelay <= 0.5 && if (mPreviewPOVDelay <= 0.5 &&
(mPreviewPOVDelay += dt) > 0.5) (mPreviewPOVDelay += dt) > 0.5)
{ {
@ -413,13 +436,9 @@ namespace MWInput
void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed) void InputManager::processChangedSettings(const Settings::CategorySettingVector& changed)
{ {
bool changeRes = false;
for (Settings::CategorySettingVector::const_iterator it = changed.begin(); for (Settings::CategorySettingVector::const_iterator it = changed.begin();
it != changed.end(); ++it) it != changed.end(); ++it)
{ {
if (it->first == "Video" && (it->second == "resolution x" || it->second == "resolution y"))
changeRes = true;
if (it->first == "Input" && it->second == "invert y axis") if (it->first == "Input" && it->second == "invert y axis")
mInvertY = Settings::Manager::getBool("invert y axis", "Input"); mInvertY = Settings::Manager::getBool("invert y axis", "Input");
@ -430,9 +449,6 @@ namespace MWInput
mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input"); mUISensitivity = Settings::Manager::getFloat("ui sensitivity", "Input");
} }
if (changeRes)
adjustMouseRegion(Settings::Manager::getInt("resolution x", "Video"), Settings::Manager::getInt("resolution y", "Video"));
} }
bool InputManager::getControlSwitch (const std::string& sw) bool InputManager::getControlSwitch (const std::string& sw)
@ -512,7 +528,6 @@ namespace MWInput
return true; // MyGUI has no use for these events return true; // MyGUI has no use for these events
MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id));
if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0) if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0)
{ {
MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType<MyGUI::Button>(false); MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType<MyGUI::Button>(false);
@ -597,15 +612,18 @@ namespace MWInput
return true; return true;
} }
bool InputManager::windowFocusChange(bool have_focus) void InputManager::windowFocusChange(bool have_focus)
{ {
return true;
} }
bool InputManager::windowVisibilityChange(bool visible) void InputManager::windowVisibilityChange(bool visible)
{ {
//TODO: Pause game? //TODO: Pause game?
return true; }
void InputManager::windowResized(int x, int y)
{
mOgre.windowResized(x,y);
} }
void InputManager::toggleMainMenu() void InputManager::toggleMainMenu()
@ -665,10 +683,8 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
bool gameMode = !mWindows.isGuiMode();
// Toggle between game mode and inventory mode // Toggle between game mode and inventory mode
if(gameMode) if(!mWindows.isGuiMode())
mWindows.pushGuiMode(MWGui::GM_Inventory); mWindows.pushGuiMode(MWGui::GM_Inventory);
else else
{ {
@ -685,9 +701,7 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
bool gameMode = !mWindows.isGuiMode(); if(mWindows.isGuiMode())
if(!gameMode)
{ {
if (mWindows.getMode() == MWGui::GM_Container) if (mWindows.getMode() == MWGui::GM_Container)
mWindows.popGuiMode(); mWindows.popGuiMode();
@ -697,17 +711,14 @@ namespace MWInput
} }
void InputManager::toggleConsole() void InputManager::toggleConsole()
{ {
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
bool gameMode = !mWindows.isGuiMode();
// Switch to console mode no matter what mode we are currently // Switch to console mode no matter what mode we are currently
// in, except of course if we are already in console mode // in, except of course if we are already in console mode
if (!gameMode) if (mWindows.isGuiMode())
{ {
if (mWindows.getMode() == MWGui::GM_Console) if (mWindows.getMode() == MWGui::GM_Console)
mWindows.popGuiMode(); mWindows.popGuiMode();
@ -724,9 +735,7 @@ namespace MWInput
return; return;
// Toggle between game mode and journal mode // Toggle between game mode and journal mode
bool gameMode = !mWindows.isGuiMode(); if(!mWindows.isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
if(gameMode && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
{ {
MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound ("book open", 1.0, 1.0);
mWindows.pushGuiMode(MWGui::GM_Journal); mWindows.pushGuiMode(MWGui::GM_Journal);

View file

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

View file

@ -221,7 +221,11 @@ namespace MWMechanics
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++) for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();iter++)
{ {
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) const MWWorld::Class &cls = MWWorld::Class::get(iter->first);
CreatureStats &stats = cls.getCreatureStats(iter->first);
stats.setLastHitObject(std::string());
if(!stats.isDead())
{ {
if(iter->second->isDead()) if(iter->second->isDead())
iter->second->resurrect(); iter->second->resurrect();
@ -230,7 +234,7 @@ namespace MWMechanics
if(iter->first.getTypeName() == typeid(ESM::NPC).name()) if(iter->first.getTypeName() == typeid(ESM::NPC).name())
updateNpc(iter->first, totalDuration, paused); updateNpc(iter->first, totalDuration, paused);
if(!MWWorld::Class::get(iter->first).getCreatureStats(iter->first).isDead()) if(!stats.isDead())
continue; continue;
} }
@ -238,16 +242,15 @@ namespace MWMechanics
// \todo remove workaround, once player death can be handled // \todo remove workaround, once player death can be handled
if(iter->first.getRefData().getHandle()=="player") if(iter->first.getRefData().getHandle()=="player")
{ {
MWMechanics::DynamicStat<float> stat ( MWMechanics::DynamicStat<float> stat(stats.getHealth());
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).getHealth());
if (stat.getModified()<1) if(stat.getModified()<1)
{ {
stat.setModified (1, 0); stat.setModified(1, 0);
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).setHealth(stat); stats.setHealth(stat);
} }
MWWorld::Class::get(iter->first).getCreatureStats(iter->first).resurrect(); stats.resurrect();
continue; continue;
} }
@ -256,11 +259,10 @@ namespace MWMechanics
iter->second->kill(); iter->second->kill();
++mDeathCount[MWWorld::Class::get(iter->first).getId(iter->first)]; ++mDeathCount[cls.getId(iter->first)];
if(MWWorld::Class::get(iter->first).isEssential(iter->first)) if(cls.isEssential(iter->first))
MWBase::Environment::get().getWindowManager()->messageBox( MWBase::Environment::get().getWindowManager()->messageBox("#{sKilledEssential}");
"#{sKilledEssential}");
} }
} }

View file

@ -35,6 +35,23 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/inventorystore.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 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) CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim)
: mPtr(ptr) : mPtr(ptr)
, mAnimation(anim) , mAnimation(anim)
@ -222,22 +300,33 @@ CharacterController::CharacterController(const MWWorld::Ptr &ptr, MWRender::Anim
, mMovementState(CharState_None) , mMovementState(CharState_None)
, mMovementSpeed(0.0f) , mMovementSpeed(0.0f)
, mDeathState(CharState_None) , mDeathState(CharState_None)
, mUpperBodyState(UpperCharState_Nothing)
, mWeaponType(WeapType_None) , mWeaponType(WeapType_None)
, mSkipAnim(false) , mSkipAnim(false)
, mSecondsOfRunning(0) , mSecondsOfRunning(0)
, mSecondsOfSwimming(0) , mSecondsOfSwimming(0)
, mUpdateWeapon(true)
{ {
if(!mAnimation) if(!mAnimation)
return; return;
if(MWWorld::Class::get(mPtr).isActor()) const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
if(cls.isActor())
{ {
/* Accumulate along X/Y only for now, until we can figure out how we should /* Accumulate along X/Y only for now, until we can figure out how we should
* handle knockout and death which moves the character down. */ * handle knockout and death which moves the character down. */
mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f)); mAnimation->setAccumulation(Ogre::Vector3(1.0f, 1.0f, 0.0f));
if(!MWWorld::Class::get(mPtr).getCreatureStats(mPtr).isDead()) if(mPtr.getTypeName() == typeid(ESM::NPC).name())
{
getActiveWeapon(cls.getNpcStats(mPtr), cls.getInventoryStore(mPtr), &mWeaponType);
if(mWeaponType != WeapType_None)
{
getWeaponGroup(mWeaponType, mCurrentWeapon);
mUpperBodyState = UpperCharState_WeapEquiped;
}
}
if(!cls.getCreatureStats(mPtr).isDead())
mIdleState = CharState_Idle; mIdleState = CharState_Idle;
else 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) void CharacterController::update(float duration, Movement &movement)
{ {
const MWWorld::Class &cls = MWWorld::Class::get(mPtr); const MWWorld::Class &cls = MWWorld::Class::get(mPtr);
@ -418,117 +724,7 @@ void CharacterController::update(float duration, Movement &movement)
movement.mRotation[2] += rot.z; movement.mRotation[2] += rot.z;
if(mPtr.getTypeName() == typeid(ESM::NPC).name()) if(mPtr.getTypeName() == typeid(ESM::NPC).name())
{ forcestateupdate = updateNpcState();
NpcStats &stats = cls.getNpcStats(mPtr);
WeaponType weaptype = WeapType_None;
MWWorld::InventoryStore &inv = cls.getInventoryStore(mPtr);
MWWorld::ContainerStoreIterator weapon = inv.end();
if(stats.getDrawState() == DrawState_Spell)
weaptype = WeapType_Spell;
else if(stats.getDrawState() == MWMechanics::DrawState_Weapon)
{
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(weapon == inv.end())
weaptype = WeapType_HandToHand;
else
{
const std::string &type = weapon->getTypeName();
if(type == typeid(ESM::Lockpick).name() || type == typeid(ESM::Probe).name())
weaptype = WeapType_PickProbe;
else if(type == typeid(ESM::Weapon).name())
{
MWWorld::LiveCellRef<ESM::Weapon> *ref = weapon->get<ESM::Weapon>();
ESM::Weapon::Type type = (ESM::Weapon::Type)ref->mBase->mData.mType;
switch(type)
{
case ESM::Weapon::ShortBladeOneHand:
case ESM::Weapon::LongBladeOneHand:
case ESM::Weapon::BluntOneHand:
case ESM::Weapon::AxeOneHand:
case ESM::Weapon::Arrow:
case ESM::Weapon::Bolt:
weaptype = WeapType_OneHand;
break;
case ESM::Weapon::LongBladeTwoHand:
case ESM::Weapon::BluntTwoClose:
case ESM::Weapon::AxeTwoHand:
weaptype = WeapType_TwoHand;
break;
case ESM::Weapon::BluntTwoWide:
case ESM::Weapon::SpearTwoWide:
weaptype = WeapType_TwoWide;
break;
case ESM::Weapon::MarksmanBow:
weaptype = WeapType_BowAndArrow;
break;
case ESM::Weapon::MarksmanCrossbow:
weaptype = WeapType_Crossbow;
break;
case ESM::Weapon::MarksmanThrown:
weaptype = WeapType_ThowWeapon;
break;
}
}
}
}
else
weapon = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if(mUpdateWeapon)
{
forcestateupdate = (mWeaponType != weaptype);
mWeaponType = weaptype;
mUpdateWeapon = false;
}
if(weaptype != mWeaponType)
{
forcestateupdate = true;
std::string weapgroup;
if(weaptype == WeapType_None)
{
getWeaponGroup(mWeaponType, weapgroup);
mAnimation->play(weapgroup, Priority_Weapon,
MWRender::Animation::Group_UpperBody, true,
1.0f, "unequip start", "unequip stop", 0.0f, 0);
}
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");
}
refreshCurrentAnims(idlestate, movestate, forcestateupdate); refreshCurrentAnims(idlestate, movestate, forcestateupdate);
} }

View file

@ -5,6 +5,12 @@
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
namespace MWWorld
{
class ContainerStoreIterator;
class InventoryStore;
}
namespace MWRender namespace MWRender
{ {
class Animation; class Animation;
@ -14,6 +20,7 @@ namespace MWMechanics
{ {
class Movement; class Movement;
class NpcStats;
enum Priority { enum Priority {
Priority_Default, Priority_Default,
@ -95,6 +102,19 @@ enum WeaponType {
WeapType_Spell 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 class CharacterController
{ {
MWWorld::Ptr mPtr; MWWorld::Ptr mPtr;
@ -113,22 +133,31 @@ class CharacterController
CharacterState mDeathState; CharacterState mDeathState;
std::string mCurrentDeath; std::string mCurrentDeath;
WeaponType mWeaponType; UpperBodyCharacterState mUpperBodyState;
bool mSkipAnim;
// Workaround for playing weapon draw animation and sound when going to new cell WeaponType mWeaponType;
bool mUpdateWeapon; std::string mCurrentWeapon;
bool mSkipAnim;
// counted for skill increase // counted for skill increase
float mSecondsOfSwimming; float mSecondsOfSwimming;
float mSecondsOfRunning; float mSecondsOfRunning;
std::string mAttackType; // slash, chop or thrust
void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false); void refreshCurrentAnims(CharacterState idle, CharacterState movement, bool force=false);
static void getWeaponGroup(WeaponType weaptype, std::string &group); static void getWeaponGroup(WeaponType weaptype, std::string &group);
static MWWorld::ContainerStoreIterator getActiveWeapon(NpcStats &stats,
MWWorld::InventoryStore &inv,
WeaponType *weaptype);
void clearAnimQueue(); void clearAnimQueue();
bool updateNpcState();
public: public:
CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim); CharacterController(const MWWorld::Ptr &ptr, MWRender::Animation *anim);
virtual ~CharacterController(); virtual ~CharacterController();

View file

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

View file

@ -34,6 +34,11 @@ namespace MWMechanics
bool mAlarmed; bool mAlarmed;
bool mAttacked; bool mAttacked;
bool mHostile; 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: public:
CreatureStats(); CreatureStats();
@ -54,6 +59,8 @@ namespace MWMechanics
const MagicEffects & getMagicEffects() const; const MagicEffects & getMagicEffects() const;
const bool & getAttackingOrSpell() const;
int getLevel() const; int getLevel() const;
int getAiSetting (int index) const; int getAiSetting (int index) const;
@ -83,6 +90,17 @@ namespace MWMechanics
void setMagicEffects(const MagicEffects &effects); 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 setLevel(int level);
void setAiSetting (int index, int value); void setAiSetting (int index, int value);
@ -139,6 +157,9 @@ namespace MWMechanics
void setHostile (bool hostile); void setHostile (bool hostile);
bool getCreatureTargetted() const; bool getCreatureTargetted() const;
void setLastHitObject(const std::string &objectid);
const std::string &getLastHitObject() const;
}; };
} }

View file

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

View file

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

View file

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

View file

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

View file

@ -25,7 +25,11 @@ namespace MWRender
mHeight(128.f), mHeight(128.f),
mCameraDistance(300.f), mCameraDistance(300.f),
mDistanceAdjusted(false), mDistanceAdjusted(false),
mAnimation(NULL) mAnimation(NULL),
mNearest(30.f),
mFurthest(800.f),
mIsNearest(false),
mIsFurthest(false)
{ {
mVanity.enabled = false; mVanity.enabled = false;
mVanity.allowed = true; mVanity.allowed = true;
@ -232,16 +236,21 @@ namespace MWRender
if(mFirstPersonView && !mPreviewMode && !mVanity.enabled) if(mFirstPersonView && !mPreviewMode && !mVanity.enabled)
return; return;
mIsFurthest = false;
mIsNearest = false;
Ogre::Vector3 v(0.f, 0.f, dist); Ogre::Vector3 v(0.f, 0.f, dist);
if (adjust) { if (adjust) {
v += mCamera->getPosition(); v += mCamera->getPosition();
} }
if (v.z > 800.f) { if (v.z >= mFurthest) {
v.z = 800.f; v.z = mFurthest;
} else if (v.z < 10.f) { mIsFurthest = true;
} else if (!override && v.z < 10.f) {
v.z = 10.f; v.z = 10.f;
} else if (override && v.z < 50.f) { } else if (override && v.z <= mNearest) {
v.z = 50.f; v.z = mNearest;
mIsNearest = true;
} }
mCamera->setPosition(v); mCamera->setPosition(v);
@ -302,34 +311,13 @@ namespace MWRender
} }
} }
float Camera::getHeight() void Camera::getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera)
{
if(mCamera->isParentTagPoint())
{
Ogre::TagPoint *tag = static_cast<Ogre::TagPoint*>(mCamera->getParentNode());
return tag->_getFullLocalTransform().getTrans().z;
}
return mCamera->getParentNode()->getPosition().z;
}
bool Camera::getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera)
{ {
mCamera->getParentSceneNode()->needUpdate(true); mCamera->getParentSceneNode()->needUpdate(true);
camera = mCamera->getRealPosition(); camera = mCamera->getRealPosition();
player = mTrackingPtr.getRefData().getBaseNode()->getPosition(); focal = Ogre::Vector3((mCamera->getParentNode()->_getFullTransform() *
Ogre::Vector4(0.0f, 0.0f, 0.0f, 1.0f)).ptr());
return mFirstPersonView && !mVanity.enabled && !mPreviewMode;
}
Ogre::Vector3 Camera::getPosition()
{
return mTrackingPtr.getRefData().getBaseNode()->getPosition();
}
void Camera::getSightAngles(float &pitch, float &yaw)
{
pitch = mMainCam.pitch;
yaw = mMainCam.yaw;
} }
void Camera::togglePlayerLooking(bool enable) void Camera::togglePlayerLooking(bool enable)
@ -341,4 +329,14 @@ namespace MWRender
{ {
return mPreviewMode || mVanity.enabled; return mPreviewMode || mVanity.enabled;
} }
bool Camera::isNearest()
{
return mIsNearest;
}
bool Camera::isFurthest()
{
return mIsFurthest;
}
} }

View file

@ -33,6 +33,10 @@ namespace MWRender
bool mFirstPersonView; bool mFirstPersonView;
bool mPreviewMode; bool mPreviewMode;
bool mFreeLook; bool mFreeLook;
float mNearest;
float mFurthest;
bool mIsNearest;
bool mIsFurthest;
struct { struct {
bool enabled, allowed; bool enabled, allowed;
@ -93,18 +97,16 @@ namespace MWRender
void setAnimation(NpcAnimation *anim); void setAnimation(NpcAnimation *anim);
float getHeight(); /// Stores focal and camera world positions in passed arguments
void getPosition(Ogre::Vector3 &focal, Ogre::Vector3 &camera);
/// Stores player and camera world positions in passed arguments
/// \return true if camera at the eye-place
bool getPosition(Ogre::Vector3 &player, Ogre::Vector3 &camera);
Ogre::Vector3 getPosition();
void getSightAngles(float &pitch, float &yaw);
void togglePlayerLooking(bool enable); void togglePlayerLooking(bool enable);
bool isVanityOrPreviewModeEnabled(); bool isVanityOrPreviewModeEnabled();
bool isNearest();
bool isFurthest();
}; };
} }

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 opcodeXBox = 0x200000c;
const int opcodeOnActivate = 0x200000d; const int opcodeOnActivate = 0x200000d;
const int opcodeActivate = 0x2000075; const int opcodeActivate = 0x2000075;
@ -650,6 +679,12 @@ namespace MWScript
const int opcodePlayBink = 0x20001f7; 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) void registerExtensions (Compiler::Extensions& extensions)
{ {
extensions.registerFunction ("xbox", 'l', "", opcodeXBox); extensions.registerFunction ("xbox", 'l', "", opcodeXBox);
@ -692,6 +727,9 @@ namespace MWScript
extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit); extensions.registerFunction ("getstandingpc", 'l', "", opcodeGetStandingPc, opcodeGetStandingPcExplicit);
extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit); extensions.registerFunction ("getstandingactor", 'l', "", opcodeGetStandingActor, opcodeGetStandingActorExplicit);
extensions.registerFunction ("getwindspeed", 'f', "", opcodeGetWindSpeed); 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) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -745,6 +783,10 @@ namespace MWScript
interpreter.installSegment5 (opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>); interpreter.installSegment5 (opcodeGetStandingActor, new OpGetStandingActor<ImplicitRef>);
interpreter.installSegment5 (opcodeGetStandingActorExplicit, new OpGetStandingActor<ExplicitRef>); interpreter.installSegment5 (opcodeGetStandingActorExplicit, new OpGetStandingActor<ExplicitRef>);
interpreter.installSegment5 (opcodeGetWindSpeed, new OpGetWindSpeed); 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>);
} }
} }
} }

View file

@ -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 opcodeToggleSky = 0x2000021;
const int opcodeTurnMoonWhite = 0x2000022; const int opcodeTurnMoonWhite = 0x2000022;
const int opcodeTurnMoonRed = 0x2000023; const int opcodeTurnMoonRed = 0x2000023;
@ -103,6 +125,7 @@ namespace MWScript
const int opcodeGetSecundaPhase = 0x2000025; const int opcodeGetSecundaPhase = 0x2000025;
const int opcodeGetCurrentWeather = 0x200013f; const int opcodeGetCurrentWeather = 0x200013f;
const int opcodeChangeWeather = 0x2000140; const int opcodeChangeWeather = 0x2000140;
const int opcodeModRegion = 0x20026;
void registerExtensions (Compiler::Extensions& extensions) void registerExtensions (Compiler::Extensions& extensions)
{ {
@ -114,6 +137,7 @@ namespace MWScript
extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase); extensions.registerFunction ("getmasserphase", 'l', "", opcodeGetMasserPhase);
extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase); extensions.registerFunction ("getsecundaphase", 'l', "", opcodeGetSecundaPhase);
extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather); extensions.registerFunction ("getcurrentweather", 'l', "", opcodeGetCurrentWeather);
extensions.registerInstruction ("modregion", "S/llllllllll", opcodeModRegion);
} }
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
@ -125,6 +149,7 @@ namespace MWScript
interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase); interpreter.installSegment5 (opcodeGetSecundaPhase, new OpGetSecundaPhase);
interpreter.installSegment5 (opcodeGetCurrentWeather, new OpGetCurrentWeather); interpreter.installSegment5 (opcodeGetCurrentWeather, new OpGetCurrentWeather);
interpreter.installSegment5 (opcodeChangeWeather, new OpChangeWeather); interpreter.installSegment5 (opcodeChangeWeather, new OpChangeWeather);
interpreter.installSegment3 (opcodeModRegion, new OpModRegion);
} }
} }
} }

View file

@ -499,18 +499,14 @@ namespace MWSound
soundIter = regn->mSoundList.begin(); soundIter = regn->mSoundList.begin();
while(soundIter != regn->mSoundList.end()) while(soundIter != regn->mSoundList.end())
{ {
const std::string go = soundIter->mSound.toString(); if(r - pos < soundIter->mChance)
int chance = (int) soundIter->mChance;
//std::cout << "Sound: " << go.name <<" Chance:" << chance << "\n";
soundIter++;
if(r - pos < chance)
{ {
//play sound playSound(soundIter->mSound.toString(), 1.0f, 1.0f);
std::cout << "Sound: " << go <<" Chance:" << chance << "\n";
playSound(go, 1.0f, 1.0f);
break; break;
} }
pos += chance; pos += soundIter->mChance;
soundIter++;
} }
} }
@ -550,6 +546,13 @@ namespace MWSound
mActiveSounds.erase(snditer++); mActiveSounds.erase(snditer++);
else 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->first->update();
snditer++; snditer++;
} }

View file

@ -13,6 +13,8 @@
#include "containerstore.hpp" #include "containerstore.hpp"
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp"
namespace MWWorld namespace MWWorld
{ {
@ -77,6 +79,33 @@ namespace MWWorld
throw std::runtime_error ("class does not have item health"); 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 boost::shared_ptr<Action> Class::activate (const Ptr& ptr, const Ptr& actor) const
{ {
return boost::shared_ptr<Action> (new NullAction); return boost::shared_ptr<Action> (new NullAction);

View file

@ -105,6 +105,28 @@ namespace MWWorld
///< Return item max health or throw an exception, if class does not have item health ///< Return item max health or throw an exception, if class does not have item health
/// (default implementation: throw an exceoption) /// (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; virtual boost::shared_ptr<Action> activate (const Ptr& ptr, const Ptr& actor) const;
///< Generate action for activation (default implementation: return a null action). ///< Generate action for activation (default implementation: return a null action).

View file

@ -304,6 +304,19 @@ namespace MWWorld
return results; 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) void PhysicsSystem::setCurrentWater(bool hasWater, int waterHeight)
{ {
// TODO: store and use // TODO: store and use

View file

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

View file

@ -537,8 +537,8 @@ void WeatherManager::stopSounds(bool stopAll)
std::vector<std::string>::iterator it = mSoundsPlaying.begin(); std::vector<std::string>::iterator it = mSoundsPlaying.begin();
while (it!=mSoundsPlaying.end()) while (it!=mSoundsPlaying.end())
{ {
if (stopAll || \ if (stopAll ||
!((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) || \ !((*it == mWeatherSettings[mCurrentWeather].mAmbientLoopSoundID) ||
(*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID))) (*it == mWeatherSettings[mCurrentWeather].mRainLoopSoundID)))
{ {
MWBase::Environment::get().getSoundManager()->stopSound(*it); MWBase::Environment::get().getSoundManager()->stopSound(*it);
@ -551,29 +551,37 @@ void WeatherManager::stopSounds(bool stopAll)
Ogre::String WeatherManager::nextWeather(const ESM::Region* region) const 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). * All probabilities must add to 100 (responsibility of the user).
* If chances A and B has values 30 and 70 then by generating * 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 * 100 numbers 1..100, 30% will be lesser or equal 30 and
* 70% will be greater than 30 (in theory). * 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 chance = (rand() % 100) + 1; // 1..100
int sum = 0; int sum = 0;
int i = 0; unsigned int i = 0;
for (; i < 10; ++i) for (; i < probability.size(); ++i)
{ {
sum += probability[i]; sum += probability[i];
if (chance < sum) if (chance < sum)
@ -681,6 +689,15 @@ void WeatherManager::changeWeather(const std::string& region, const unsigned int
setWeather(weather); 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 float WeatherManager::getWindSpeed() const
{ {
return mWindSpeed; return mWindSpeed;

View file

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

View file

@ -18,6 +18,7 @@
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwrender/sky.hpp" #include "../mwrender/sky.hpp"
#include "../mwrender/animation.hpp"
#include "../mwclass/door.hpp" #include "../mwclass/door.hpp"
@ -165,7 +166,7 @@ namespace MWWorld
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0), : mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
mSky (true), mCells (mStore, mEsm), mSky (true), mCells (mStore, mEsm),
mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride), mNumFacing(0), mActivationDistanceOverride (mActivationDistanceOverride),
mFallback(fallbackMap), mPlayIntro(0) mFallback(fallbackMap), mPlayIntro(0), mTeleportEnabled(true)
{ {
mPhysics = new PhysicsSystem(renderer); mPhysics = new PhysicsSystem(renderer);
mPhysEngine = mPhysics->getEngine(); mPhysEngine = mPhysics->getEngine();
@ -776,6 +777,28 @@ namespace MWWorld
return object; 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) void World::deleteObject (const Ptr& ptr)
{ {
if (ptr.getRefData().getCount()>0) if (ptr.getRefData().getCount()>0)
@ -1351,6 +1374,11 @@ namespace MWWorld
mWeatherManager->changeWeather(region, id); 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() OEngine::Render::Fader* World::getFader()
{ {
return mRendering->getFader(); return mRendering->getFader();
@ -1821,4 +1849,15 @@ namespace MWWorld
} }
return false; return false;
} }
void World::enableTeleporting(bool enable)
{
mTeleportEnabled = enable;
}
bool World::isTeleportingEnabled() const
{
return mTeleportEnabled;
}
} }

View file

@ -117,6 +117,8 @@ namespace MWWorld
int mPlayIntro; int mPlayIntro;
bool mTeleportEnabled;
public: public:
World (OEngine::Render::OgreRenderer& renderer, World (OEngine::Render::OgreRenderer& renderer,
@ -235,6 +237,8 @@ namespace MWWorld
virtual void setMoonColour (bool red); virtual void setMoonColour (bool red);
virtual void modRegion(const std::string &regionid, const std::vector<char> &chances);
virtual float getTimeScaleFactor() const; virtual float getTimeScaleFactor() const;
virtual void changeToInteriorCell (const std::string& cellName, virtual void changeToInteriorCell (const std::string& cellName,
@ -252,6 +256,10 @@ namespace MWWorld
virtual MWWorld::Ptr getFacedObject(); virtual MWWorld::Ptr getFacedObject();
///< Return pointer to the object the player is looking at, if it is within activation range ///< Return pointer to the object the player is looking at, if it is within activation range
/// Returns a pointer to the object the provided object is facing (if within the
/// specified distance). This will attempt to use the "Bip01 Head" node as a basis.
virtual MWWorld::Ptr getFacedObject(const MWWorld::Ptr &ptr, float distance);
virtual void deleteObject (const Ptr& ptr); virtual void deleteObject (const Ptr& ptr);
virtual void moveObject (const Ptr& ptr, float x, float y, float z); 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 /// Find position in interior cell near door entrance
/// \return false if interior with given name not exists, true otherwise /// \return false if interior with given name not exists, true otherwise
virtual bool findInteriorPosition(const std::string &name, ESM::Position &pos); 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;
}; };
} }

View file

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

View file

@ -688,6 +688,21 @@ class NIFObjectLoader
} }
std::string::size_type nextpos = std::min(str.find('\r', pos), str.find('\n', pos)); 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); std::string result = str.substr(pos, nextpos-pos);
textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result))); textkeys.insert(std::make_pair(tk->list[i].time, Misc::StringUtils::toLower(result)));
@ -743,7 +758,7 @@ class NIFObjectLoader
e = e->extra; e = e->extra;
} }
if(!node->controller.empty()) if(!node->controller.empty() && (node->parent || node->recType != Nif::RC_NiNode))
createNodeControllers(name, node->controller, objectlist, animflags); createNodeControllers(name, node->controller, objectlist, animflags);
if(node->recType == Nif::RC_NiCamera) if(node->recType == Nif::RC_NiCamera)

View file

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

View file

@ -65,10 +65,12 @@ public:
virtual ~WindowListener() {} virtual ~WindowListener() {}
/** @remarks The window's visibility changed */ /** @remarks The window's visibility changed */
virtual bool windowVisibilityChange( bool visible ) = 0; virtual void windowVisibilityChange( bool visible ) {};
/** @remarks The window got / lost input focus */ /** @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) {}
}; };
} }

View file

@ -4,13 +4,6 @@
#include <OgrePlatform.h> #include <OgrePlatform.h>
#include <OgreRoot.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 namespace SFO
{ {
@ -19,7 +12,6 @@ namespace SFO
InputWrapper::InputWrapper(SDL_Window* window, Ogre::RenderWindow* ogreWindow) : InputWrapper::InputWrapper(SDL_Window* window, Ogre::RenderWindow* ogreWindow) :
mSDLWindow(window), mSDLWindow(window),
mOgreWindow(ogreWindow), mOgreWindow(ogreWindow),
mOwnWindow(false),
mWarpCompensate(false), mWarpCompensate(false),
mMouseRelative(false), mMouseRelative(false),
mGrabPointer(false), mGrabPointer(false),
@ -27,14 +19,18 @@ namespace SFO
mMouseZ(0), mMouseZ(0),
mMouseY(0), mMouseY(0),
mMouseX(0), mMouseX(0),
mMouseInWindow(true) mMouseInWindow(true),
mJoyListener(NULL),
mKeyboardListener(NULL),
mMouseListener(NULL),
mWindowListener(NULL)
{ {
_setupOISKeys(); _setupOISKeys();
} }
InputWrapper::~InputWrapper() InputWrapper::~InputWrapper()
{ {
if(mSDLWindow != NULL && mOwnWindow) if(mSDLWindow != NULL)
SDL_DestroyWindow(mSDLWindow); SDL_DestroyWindow(mSDLWindow);
mSDLWindow = NULL; mSDLWindow = NULL;
} }
@ -76,6 +72,25 @@ namespace SFO
case SDL_TEXTINPUT: case SDL_TEXTINPUT:
mKeyboardListener->textInput(evt.text); mKeyboardListener->textInput(evt.text);
break; 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: case SDL_WINDOWEVENT:
handleWindowEvent(evt); handleWindowEvent(evt);
break; break;
@ -100,25 +115,62 @@ namespace SFO
SDL_SetWindowGrab(mSDLWindow, SDL_FALSE); SDL_SetWindowGrab(mSDLWindow, SDL_FALSE);
SDL_SetRelativeMouseMode(SDL_FALSE); SDL_SetRelativeMouseMode(SDL_FALSE);
break; break;
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: 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: case SDL_WINDOWEVENT_FOCUS_GAINED:
if (mWindowListener)
mWindowListener->windowFocusChange(true);
break;
case SDL_WINDOWEVENT_FOCUS_LOST: case SDL_WINDOWEVENT_FOCUS_LOST:
if (mWindowListener)
mWindowListener->windowFocusChange(false);
break;
case SDL_WINDOWEVENT_CLOSE: case SDL_WINDOWEVENT_CLOSE:
break; break;
case SDL_WINDOWEVENT_SHOWN: case SDL_WINDOWEVENT_SHOWN:
mOgreWindow->setVisible(true); mOgreWindow->setVisible(true);
if (mWindowListener)
mWindowListener->windowVisibilityChange(true);
break; break;
case SDL_WINDOWEVENT_HIDDEN: case SDL_WINDOWEVENT_HIDDEN:
mOgreWindow->setVisible(false); mOgreWindow->setVisible(false);
if (mWindowListener)
mWindowListener->windowVisibilityChange(false);
break; break;
} }
} }
bool InputWrapper::isModifierHeld(int mod) bool InputWrapper::isModifierHeld(SDL_Keymod mod)
{ {
return SDL_GetModState() & 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 /// \brief Moves the mouse to the specified point within the viewport
void InputWrapper::warpMouse(int x, int y) void InputWrapper::warpMouse(int x, int y)
{ {

View file

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

115
extern/sdl4ogre/sdlwindowhelper.cpp vendored Normal file
View file

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

31
extern/sdl4ogre/sdlwindowhelper.hpp vendored Normal file
View file

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

View file

@ -3,6 +3,11 @@
<MyGUI type="Layout"> <MyGUI type="Layout">
<Widget type="Widget" layer="HUD" position="0 0 300 200" name="_Main"> <Widget type="Widget" layer="HUD" position="0 0 300 200" name="_Main">
<!-- Energy bars --> <!-- 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"> <Widget type="Button" skin="" position="13 146 65 12" align="Left Bottom" name="HealthFrame">
<UserString key="ToolTipType" value="Layout"/> <UserString key="ToolTipType" value="Layout"/>
<UserString key="ToolTipLayout" value="HealthToolTip"/> <UserString key="ToolTipLayout" value="HealthToolTip"/>

View file

@ -42,6 +42,11 @@
<State name="normal" offset = "0 8 4 8"/> <State name="normal" offset = "0 8 4 8"/>
</BasisSkin> </BasisSkin>
</Skin> </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.--> <!-- 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"/> <Child type="Widget" skin="BlackBG" offset = "2 2 60 8" align = "ALIGN_STRETCH" name="Client"/>
</Skin> </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> </MyGUI>

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