Merge remote-tracking branch 'upstream/master'

deque
cc9cii 11 years ago
commit 36e380c6be

2
.gitignore vendored

@ -41,7 +41,7 @@ resources
## generated objects ## generated objects
apps/openmw/config.hpp apps/openmw/config.hpp
components/version/version.hpp components/version/version.hpp
Docs/mainpage.hpp docs/mainpage.hpp
moc_*.cxx moc_*.cxx
*.cxx_parameters *.cxx_parameters
*qrc_launcher.cxx *qrc_launcher.cxx

@ -56,7 +56,7 @@ include(OpenMWMacros)
# doxygen main page # doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/docs/mainpage.hpp")
option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE)
option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
@ -80,13 +80,6 @@ option(USE_FFMPEG "use ffmpeg for sound" ON)
# OS X deployment # OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF) option(OPENMW_OSX_DEPLOYMENT OFF)
if(UNIX AND NOT APPLE)
option(BUILD_WITH_DPKG "enable dpkg-based install for debian and debian derivatives" OFF)
if(BUILD_WITH_DPKG)
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
endif(BUILD_WITH_DPKG)
endif(UNIX AND NOT APPLE)
# Location of morrowind data files # Location of morrowind data files
if (APPLE) if (APPLE)
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
@ -395,46 +388,36 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC)
IF(NOT WIN32 AND NOT APPLE) IF(NOT WIN32 AND NOT APPLE)
## Debian and non debian Linux building # Linux building
# Paths # Paths
IF (DPKG_PROGRAM) SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
## Debian specific SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(CMAKE_INSTALL_PREFIX "/usr") SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location") SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon dir") SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
SET(SYSCONFDIR "../etc/openmw" CACHE PATH "Set config dir")
ELSE () # Install binaries
## Non debian specific INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries") IF(BUILD_LAUNCHER)
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") ENDIF(BUILD_LAUNCHER)
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir") IF(BUILD_BSATOOL)
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.") INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir") ENDIF(BUILD_BSATOOL)
IF(BUILD_ESMTOOL)
# Install binaries INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) ENDIF(BUILD_ESMTOOL)
IF(BUILD_LAUNCHER) IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_BSATOOL) IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BSATOOL) ENDIF(BUILD_OPENCS)
IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) # Install licenses
ENDIF(BUILD_ESMTOOL) INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
IF(BUILD_MWINIIMPORTER) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
# Install licenses
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
ENDIF (DPKG_PROGRAM)
# Install icon and desktop file # Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")

@ -119,10 +119,6 @@ endif(NOT WIN32)
if(DPKG_PROGRAM)
INSTALL(TARGETS omwlauncher RUNTIME DESTINATION games COMPONENT omwlauncher)
endif()
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(omwlauncher gcov) target_link_libraries(omwlauncher gcov)

@ -22,8 +22,3 @@ if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(mwiniimport gcov) target_link_libraries(mwiniimport gcov)
endif() endif()
if(DPKG_PROGRAM)
INSTALL(TARGETS mwiniimport RUNTIME DESTINATION games COMPONENT mwiniimport)
endif()

@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc
opencs_units (model/world opencs_units (model/world
idtable idtableproxymodel regionmap data idtable idtableproxymodel regionmap data commanddispatcher
) )
@ -199,10 +199,6 @@ target_link_libraries(opencs
components components
) )
if(DPKG_PROGRAM)
INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs)
endif()
if(APPLE) if(APPLE)
INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
endif() endif()

@ -0,0 +1,267 @@
#include "commanddispatcher.hpp"
#include <algorithm>
#include <components/misc/stringops.hpp>
#include "../doc/document.hpp"
#include "idtable.hpp"
#include "record.hpp"
#include "commands.hpp"
std::vector<std::string> CSMWorld::CommandDispatcher::getDeletableRecords() const
{
std::vector<std::string> result;
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification);
for (std::vector<std::string>::const_iterator iter (mSelection.begin());
iter!=mSelection.end(); ++iter)
{
int row = model.getModelIndex (*iter, 0).row();
// check record state
RecordBase::State state = static_cast<RecordBase::State> (
model.data (model.index (row, stateColumnIndex)).toInt());
if (state==RecordBase::State_Deleted)
continue;
// check other columns (only relevant for a subset of the tables)
int dialogueTypeIndex = model.searchColumnIndex (Columns::ColumnId_DialogueType);
if (dialogueTypeIndex!=-1)
{
int type = model.data (model.index (row, dialogueTypeIndex)).toInt();
if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
continue;
}
result.push_back (*iter);
}
return result;
}
std::vector<std::string> CSMWorld::CommandDispatcher::getRevertableRecords() const
{
std::vector<std::string> result;
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
/// \todo Reverting temporarily disabled on tables that support reordering, because
/// revert logic currently can not handle reordering.
if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic)
return result;
int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification);
for (std::vector<std::string>::const_iterator iter (mSelection.begin());
iter!=mSelection.end(); ++iter)
{
int row = model.getModelIndex (*iter, 0).row();
// check record state
RecordBase::State state = static_cast<RecordBase::State> (
model.data (model.index (row, stateColumnIndex)).toInt());
if (state==RecordBase::State_BaseOnly)
continue;
result.push_back (*iter);
}
return result;
}
CSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document,
const CSMWorld::UniversalId& id, QObject *parent)
: QObject (parent), mDocument (document), mId (id), mLocked (false)
{}
void CSMWorld::CommandDispatcher::setEditLock (bool locked)
{
mLocked = locked;
}
void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection)
{
mSelection = selection;
std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::toLower);
std::sort (mSelection.begin(), mSelection.end());
}
void CSMWorld::CommandDispatcher::setExtendedTypes (const std::vector<UniversalId>& types)
{
mExtendedTypes = types;
}
bool CSMWorld::CommandDispatcher::canDelete() const
{
if (mLocked)
return false;
return getDeletableRecords().size()!=0;
}
bool CSMWorld::CommandDispatcher::canRevert() const
{
if (mLocked)
return false;
return getRevertableRecords().size()!=0;
}
std::vector<CSMWorld::UniversalId> CSMWorld::CommandDispatcher::getExtendedTypes() const
{
std::vector<CSMWorld::UniversalId> tables;
if (mId==UniversalId::Type_Cells)
{
tables.push_back (mId);
tables.push_back (UniversalId::Type_References);
/// \todo add other cell-specific types
}
return tables;
}
void CSMWorld::CommandDispatcher::executeDelete()
{
if (mLocked)
return;
std::vector<std::string> rows = getDeletableRecords();
if (rows.empty())
return;
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
if (rows.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
{
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
toString().toUtf8().constData();
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id));
}
if (rows.size()>1)
mDocument.getUndoStack().endMacro();
}
void CSMWorld::CommandDispatcher::executeRevert()
{
if (mLocked)
return;
std::vector<std::string> rows = getRevertableRecords();
if (rows.empty())
return;
IdTable& model = dynamic_cast<IdTable&> (*mDocument.getData().getTableModel (mId));
int columnIndex = model.findColumnIndex (Columns::ColumnId_Id);
if (rows.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
for (std::vector<std::string>::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter)
{
std::string id = model.data (model.getModelIndex (*iter, columnIndex)).
toString().toUtf8().constData();
mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id));
}
if (rows.size()>1)
mDocument.getUndoStack().endMacro();
}
void CSMWorld::CommandDispatcher::executeExtendedDelete()
{
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records"));
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
iter!=mExtendedTypes.end(); ++iter)
{
if (*iter==mId)
executeDelete();
else if (*iter==UniversalId::Type_References)
{
IdTable& model = dynamic_cast<IdTable&> (
*mDocument.getData().getTableModel (*iter));
const RefCollection& collection = mDocument.getData().getReferences();
int size = collection.getSize();
for (int i=size-1; i>=0; --i)
{
const Record<CellRef>& record = collection.getRecord (i);
if (record.mState==RecordBase::State_Deleted)
continue;
if (!std::binary_search (mSelection.begin(), mSelection.end(),
Misc::StringUtils::lowerCase (record.get().mCell)))
continue;
mDocument.getUndoStack().push (
new CSMWorld::DeleteCommand (model, record.get().mId));
}
}
}
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().endMacro();
}
void CSMWorld::CommandDispatcher::executeExtendedRevert()
{
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records"));
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
iter!=mExtendedTypes.end(); ++iter)
{
if (*iter==mId)
executeRevert();
else if (*iter==UniversalId::Type_References)
{
IdTable& model = dynamic_cast<IdTable&> (
*mDocument.getData().getTableModel (*iter));
const RefCollection& collection = mDocument.getData().getReferences();
int size = collection.getSize();
for (int i=size-1; i>=0; --i)
{
const Record<CellRef>& record = collection.getRecord (i);
if (!std::binary_search (mSelection.begin(), mSelection.end(),
Misc::StringUtils::lowerCase (record.get().mCell)))
continue;
mDocument.getUndoStack().push (
new CSMWorld::RevertCommand (model, record.get().mId));
}
}
}
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().endMacro();
}

@ -0,0 +1,69 @@
#ifndef CSM_WOLRD_COMMANDDISPATCHER_H
#define CSM_WOLRD_COMMANDDISPATCHER_H
#include <vector>
#include <QObject>
#include "universalid.hpp"
namespace CSMDoc
{
class Document;
}
namespace CSMWorld
{
class CommandDispatcher : public QObject
{
Q_OBJECT
bool mLocked;
CSMDoc::Document& mDocument;
UniversalId mId;
std::vector<std::string> mSelection;
std::vector<UniversalId> mExtendedTypes;
std::vector<std::string> getDeletableRecords() const;
std::vector<std::string> getRevertableRecords() const;
public:
CommandDispatcher (CSMDoc::Document& document, const CSMWorld::UniversalId& id,
QObject *parent = 0);
///< \param id ID of the table the commands should operate on primarily.
void setEditLock (bool locked);
void setSelection (const std::vector<std::string>& selection);
void setExtendedTypes (const std::vector<UniversalId>& types);
///< Set record lists selected by the user for extended operations.
bool canDelete() const;
bool canRevert() const;
/// Return IDs of the record collection that can also be affected when
/// operating on the record collection this dispatcher is used for.
///
/// \note The returned collection contains the ID of the record collection this
/// dispatcher is used for. However if that record collection does not support
/// the extended mode, the returned vector will be empty instead.
std::vector<UniversalId> getExtendedTypes() const;
public slots:
void executeDelete();
void executeRevert();
void executeExtendedDelete();
void executeExtendedRevert();
};
}
#endif

@ -16,11 +16,12 @@
#include "regionmap.hpp" #include "regionmap.hpp"
#include "columns.hpp" #include "columns.hpp"
void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update)
UniversalId::Type type2, bool update)
{ {
mModels.push_back (model); mModels.push_back (model);
mModelIndex.insert (std::make_pair (type1, model)); mModelIndex.insert (std::make_pair (type, model));
UniversalId::Type type2 = UniversalId::getParentType (type);
if (type2!=UniversalId::Type_None) if (type2!=UniversalId::Type_None)
mModelIndex.insert (std::make_pair (type2, model)); mModelIndex.insert (std::make_pair (type2, model));
@ -235,26 +236,26 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding)
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>); mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>); mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); addModel (new IdTable (&mGlobals), UniversalId::Type_Global);
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst);
addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill, false); addModel (new IdTable (&mSkills), UniversalId::Type_Skill);
addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class); addModel (new IdTable (&mClasses), UniversalId::Type_Class);
addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction); addModel (new IdTable (&mFactions), UniversalId::Type_Faction);
addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race); addModel (new IdTable (&mRaces), UniversalId::Type_Race);
addModel (new IdTable (&mSounds), UniversalId::Type_Sounds, UniversalId::Type_Sound); addModel (new IdTable (&mSounds), UniversalId::Type_Sound);
addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script); addModel (new IdTable (&mScripts), UniversalId::Type_Script);
addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); addModel (new IdTable (&mRegions), UniversalId::Type_Region);
addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsign);
addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mSpells), UniversalId::Type_Spell);
addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mTopics), UniversalId::Type_Topic);
addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); addModel (new IdTable (&mJournals), UniversalId::Type_Journal);
addModel (new IdTable (&mTopicInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); addModel (new IdTable (&mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfo);
addModel (new IdTable (&mJournalInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo);
addModel (new IdTable (&mCells, IdTable::Reordering_None, IdTable::Viewing_Id), UniversalId::Type_Cells, UniversalId::Type_Cell); addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell);
addModel (new IdTable (&mReferenceables, IdTable::Reordering_None, IdTable::Viewing_None, true), addModel (new IdTable (&mReferenceables, IdTable::Feature_Preview),
UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); UniversalId::Type_Referenceable);
addModel (new IdTable (&mRefs, IdTable::Reordering_None, IdTable::Viewing_Cell, true), UniversalId::Type_References, UniversalId::Type_Reference, false); addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference);
addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); addModel (new IdTable (&mFilters), UniversalId::Type_Filter);
} }
CSMWorld::Data::~Data() CSMWorld::Data::~Data()
@ -469,8 +470,7 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId&
if (id.getType()==UniversalId::Type_RegionMap) if (id.getType()==UniversalId::Type_RegionMap)
{ {
RegionMap *table = 0; RegionMap *table = 0;
addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, false);
UniversalId::Type_None, false);
return table; return table;
} }
throw std::logic_error ("No table model available for " + id.toString()); throw std::logic_error ("No table model available for " + id.toString());

@ -83,8 +83,8 @@ namespace CSMWorld
Data (const Data&); Data (const Data&);
Data& operator= (const Data&); Data& operator= (const Data&);
void addModel (QAbstractItemModel *model, UniversalId::Type type1, void addModel (QAbstractItemModel *model, UniversalId::Type type,
UniversalId::Type type2 = UniversalId::Type_None, bool update = true); bool update = true);
static void appendIds (std::vector<std::string>& ids, const CollectionBase& collection, static void appendIds (std::vector<std::string>& ids, const CollectionBase& collection,
bool listDeleted); bool listDeleted);

@ -4,9 +4,8 @@
#include "collectionbase.hpp" #include "collectionbase.hpp"
#include "columnbase.hpp" #include "columnbase.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering, CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
Viewing viewing, bool preview) : mIdCollection (idCollection), mFeatures (features)
: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing), mPreview (preview)
{} {}
CSMWorld::IdTable::~IdTable() CSMWorld::IdTable::~IdTable()
@ -186,19 +185,9 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector<int>& newO
index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1));
} }
CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const unsigned int CSMWorld::IdTable::getFeatures() const
{ {
return mReordering; return mFeatures;
}
CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const
{
return mViewing;
}
bool CSMWorld::IdTable::hasPreview() const
{
return mPreview;
} }
std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row) const std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row) const
@ -206,7 +195,7 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
std::string id; std::string id;
std::string hint; std::string hint;
if (mViewing==Viewing_Cell) if (mFeatures & Feature_ViewCell)
{ {
int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell); int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell);
int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);
@ -217,7 +206,7 @@ std::pair<CSMWorld::UniversalId, std::string> CSMWorld::IdTable::view (int row)
hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData()); hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData());
} }
} }
else if (mViewing==Viewing_Id) else if (mFeatures & Feature_ViewId)
{ {
int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);

@ -19,26 +19,27 @@ namespace CSMWorld
public: public:
enum Reordering enum Features
{ {
Reordering_None, Feature_ReorderWithinTopic = 1,
Reordering_WithinTopic
};
enum Viewing /// Use ID column to generate view request (ID is transformed into
{ /// worldspace and original ID is passed as hint with c: prefix).
Viewing_None, Feature_ViewId = 2,
Viewing_Id, // use ID column to generate view request (ID is transformed into
// worldspace and original ID is passed as hint with c: prefix) /// Use cell column to generate view request (cell ID is transformed
Viewing_Cell // use cell column to generate view request (cell ID is transformed /// into worldspace and record ID is passed as hint with r: prefix).
// into worldspace and record ID is passed as hint with r: prefix) Feature_ViewCell = 4,
Feature_View = Feature_ViewId | Feature_ViewCell,
Feature_Preview = 8
}; };
private: private:
CollectionBase *mIdCollection; CollectionBase *mIdCollection;
Reordering mReordering; unsigned int mFeatures;
Viewing mViewing;
bool mPreview; bool mPreview;
// not implemented // not implemented
@ -47,8 +48,7 @@ namespace CSMWorld
public: public:
IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_None, IdTable (CollectionBase *idCollection, unsigned int features = 0);
Viewing viewing = Viewing_None, bool preview = false);
///< The ownership of \a idCollection is not transferred. ///< The ownership of \a idCollection is not transferred.
virtual ~IdTable(); virtual ~IdTable();
@ -97,11 +97,7 @@ namespace CSMWorld
///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices
/// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex).
Reordering getReordering() const; unsigned int getFeatures() const;
Viewing getViewing() const;
bool hasPreview() const;
std::pair<UniversalId, std::string> view (int row) const; std::pair<UniversalId, std::string> view (int row) const;
///< Return the UniversalId and the hint for viewing \a row. If viewing is not ///< Return the UniversalId and the hint for viewing \a row. If viewing is not

@ -61,8 +61,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 },
@ -90,7 +90,7 @@ namespace
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" },
{ CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 },
{ CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 },
@ -320,6 +320,28 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl
return list; return list;
} }
CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type)
{
for (int i=0; sIdArg[i].mType; ++i)
if (type==sIdArg[i].mType)
{
if (sIdArg[i].mClass==Class_RefRecord)
return Type_Referenceables;
if (sIdArg[i].mClass==Class_SubRecord || sIdArg[i].mClass==Class_Record)
{
if (type==Type_Cell_Missing)
return Type_Cells;
return static_cast<Type> (type-1);
}
break;
}
return Type_None;
}
bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right)
{ {
return left.isEqual (right); return left.isEqual (right);

@ -32,6 +32,8 @@ namespace CSMWorld
ArgumentType_Index ArgumentType_Index
}; };
/// \note A record list type must always be immediately followed by the matching
/// record type, if this type is of class SubRecord or Record.
enum Type enum Type
{ {
Type_None = 0, Type_None = 0,
@ -86,8 +88,8 @@ namespace CSMWorld
Type_References, Type_References,
Type_Reference, Type_Reference,
Type_RegionMap, Type_RegionMap,
Type_Filter,
Type_Filters, Type_Filters,
Type_Filter,
Type_Topics, Type_Topics,
Type_Topic, Type_Topic,
Type_Journals, Type_Journals,
@ -147,6 +149,11 @@ namespace CSMWorld
///< Will return an empty string, if no icon is available. ///< Will return an empty string, if no icon is available.
static std::vector<Type> listReferenceableTypes(); static std::vector<Type> listReferenceableTypes();
/// If \a type is a SubRecord, RefRecord or Record type return the type of the table
/// that contains records of type \a type.
/// Otherwise return Type_None.
static Type getParentType (Type type);
}; };
bool operator== (const UniversalId& left, const UniversalId& right); bool operator== (const UniversalId& left, const UniversalId& right);

@ -332,6 +332,7 @@ void CSVWorld::EditWidget::remake(int row)
if (mMainWidget) if (mMainWidget)
{ {
delete mMainWidget; delete mMainWidget;
mMainWidget = 0;
} }
mMainWidget = new QWidget (this); mMainWidget = new QWidget (this);
@ -339,6 +340,7 @@ void CSVWorld::EditWidget::remake(int row)
if (mWidgetMapper) if (mWidgetMapper)
{ {
delete mWidgetMapper; delete mWidgetMapper;
mWidgetMapper = 0;
} }
mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable); mWidgetMapper->setModel(mTable);
@ -396,7 +398,7 @@ void CSVWorld::EditWidget::remake(int row)
mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0));
this->setMinimumWidth(325); //TODO find better way to set the width or make it customizable this->setMinimumWidth(325); /// \todo replace hardcoded value with a user setting
this->setWidget(mMainWidget); this->setWidget(mMainWidget);
this->setWidgetResizable(true); this->setWidgetResizable(true);
} }
@ -407,7 +409,6 @@ void CSVWorld::EditWidget::remake(int row)
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory, bool sorting) : const CreatorFactoryBase& creatorFactory, bool sorting) :
SubView (id), SubView (id),
mEditWidget(0), mEditWidget(0),
mMainLayout(NULL), mMainLayout(NULL),
@ -415,8 +416,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))), mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mRow (-1), mRow (-1),
mLocked(false), mLocked(false),
mDocument(document) mDocument(document),
mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType()))
{ {
connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&)));
mRow = mTable->getModelIndex (id.getId(), 0).row(); mRow = mTable->getModelIndex (id.getId(), 0).row();
@ -440,7 +441,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
QToolButton* revertButton = new QToolButton(mainWidget); QToolButton* revertButton = new QToolButton(mainWidget);
revertButton->setIcon(QIcon(":/edit-undo.png")); revertButton->setIcon(QIcon(":/edit-undo.png"));
if (mTable->hasPreview()) if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview)
{ {
QToolButton* previewButton = new QToolButton(mainWidget); QToolButton* previewButton = new QToolButton(mainWidget);
previewButton->setIcon(QIcon(":/edit-preview.png")); previewButton->setIcon(QIcon(":/edit-preview.png"));
@ -448,7 +449,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview()));
} }
if (mTable->getViewing()!=CSMWorld::IdTable::Viewing_None) if (mTable->getFeatures() & CSMWorld::IdTable::Feature_View)
{ {
QToolButton* viewButton = new QToolButton(mainWidget); QToolButton* viewButton = new QToolButton(mainWidget);
viewButton->setIcon(QIcon(":/cell.png")); viewButton->setIcon(QIcon(":/cell.png"));
@ -560,14 +561,12 @@ void CSVWorld::DialogueSubView::nextId()
void CSVWorld::DialogueSubView::setEditLock (bool locked) void CSVWorld::DialogueSubView::setEditLock (bool locked)
{ {
mLocked = locked; mLocked = locked;
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt()); CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt());
if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)
{ mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked);
mEditWidget->setDisabled(true);
} else mCommandDispatcher.setEditLock (locked);
{
mEditWidget->setDisabled(mLocked);
}
} }
void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index) void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index)
@ -575,13 +574,8 @@ void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index)
if (index.row() == mRow) if (index.row() == mRow)
{ {
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt()); CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State>(mTable->data (mTable->index (mRow, 1)).toInt());
if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased)
{ mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked);
mEditWidget->setDisabled(true);
} else
{
mEditWidget->setDisabled(mLocked);
}
} }
} }
@ -671,7 +665,8 @@ void CSVWorld::DialogueSubView::cloneRequest ()
void CSVWorld::DialogueSubView::showPreview () void CSVWorld::DialogueSubView::showPreview ()
{ {
if (mTable->hasPreview() && mRow < mTable->rowCount()) if ((mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) &&
mRow < mTable->rowCount())
{ {
emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData()), ""); emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData()), "");
} }

@ -8,7 +8,9 @@
#include <QScrollArea> #include <QScrollArea>
#include "../doc/subview.hpp" #include "../doc/subview.hpp"
#include "../../model/world/columnbase.hpp" #include "../../model/world/columnbase.hpp"
#include "../../model/world/commanddispatcher.hpp"
class QDataWidgetMapper; class QDataWidgetMapper;
class QSize; class QSize;
@ -169,6 +171,7 @@ namespace CSVWorld
bool mLocked; bool mLocked;
const CSMDoc::Document& mDocument; const CSMDoc::Document& mDocument;
TableBottomBox* mBottom; TableBottomBox* mBottom;
CSMWorld::CommandDispatcher mCommandDispatcher;
public: public:

@ -80,30 +80,35 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>); manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>);
//edit subviews // Dialogue subviews
manager.add (CSMWorld::UniversalId::Type_Region, static const CSMWorld::UniversalId::Type sTableTypes2[] =
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); {
CSMWorld::UniversalId::Type_Region,
manager.add (CSMWorld::UniversalId::Type_Spell, CSMWorld::UniversalId::Type_Spell,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); CSMWorld::UniversalId::Type_Birthsign,
CSMWorld::UniversalId::Type_Global,
CSMWorld::UniversalId::Type_Race,
CSMWorld::UniversalId::Type_Class,
CSMWorld::UniversalId::Type_Filter,
CSMWorld::UniversalId::Type_Sound,
CSMWorld::UniversalId::Type_Faction,
manager.add (CSMWorld::UniversalId::Type_Referenceable, CSMWorld::UniversalId::Type_None // end marker
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false)); };
manager.add (CSMWorld::UniversalId::Type_Birthsign, for (int i=0; sTableTypes2[i]!=CSMWorld::UniversalId::Type_None; ++i)
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); manager.add (sTableTypes2[i],
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,
CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Global, manager.add (CSMWorld::UniversalId::Type_Skill,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));
manager.add (CSMWorld::UniversalId::Type_Gmst, manager.add (CSMWorld::UniversalId::Type_Gmst,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));
manager.add (CSMWorld::UniversalId::Type_Race,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Class, manager.add (CSMWorld::UniversalId::Type_Referenceable,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Reference, manager.add (CSMWorld::UniversalId::Type_Reference,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceCreator> > (false));
@ -111,18 +116,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Cell, manager.add (CSMWorld::UniversalId::Type_Cell,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<CellCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<CellCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Filter,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Sound,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Faction,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Skill,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_JournalInfo, manager.add (CSMWorld::UniversalId::Type_JournalInfo,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<InfoCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<InfoCreator> > (false));

@ -19,14 +19,36 @@
#include "../../model/world/columns.hpp" #include "../../model/world/columns.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "recordstatusdelegate.hpp" #include "recordstatusdelegate.hpp"
#include "util.hpp" #include "util.hpp"
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
{ {
// configure dispatcher
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();
std::vector<std::string> records;
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row();
records.push_back (mModel->data (
mModel->index (row, columnIndex)).toString().toUtf8().constData());
}
mDispatcher->setSelection (records);
std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes();
mDispatcher->setExtendedTypes (extendedTypes);
// create context menu
QMenu menu (this); QMenu menu (this);
/// \todo add menu items for select all and clear selection /// \todo add menu items for select all and clear selection
@ -44,22 +66,27 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
/// \todo Reverting temporarily disabled on tables that support reordering, because if (mDispatcher->canRevert())
/// revert logic currently can not handle reordering. {
if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) menu.addAction (mRevertAction);
if (listRevertableSelectedIds().size()>0)
menu.addAction (mRevertAction);
if (listDeletableSelectedIds().size()>0) if (!extendedTypes.empty())
menu.addAction (mExtendedRevertAction);
}
if (mDispatcher->canDelete())
{
menu.addAction (mDeleteAction); menu.addAction (mDeleteAction);
if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) if (!extendedTypes.empty())
menu.addAction (mExtendedDeleteAction);
}
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic)
{ {
/// \todo allow reordering of multiple rows /// \todo allow reordering of multiple rows
if (selectedRows.size()==1) if (selectedRows.size()==1)
{ {
int row =selectedRows.begin()->row();
int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic);
if (column==-1) if (column==-1)
@ -67,14 +94,17 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (column!=-1) if (column!=-1)
{ {
if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== int row = mProxyModel->mapToSource (
mProxyModel->data (mProxyModel->index (row-1, column))) mProxyModel->index (selectedRows.begin()->row(), 0)).row();
if (row>0 && mModel->data (mModel->index (row, column))==
mModel->data (mModel->index (row-1, column)))
{ {
menu.addAction (mMoveUpAction); menu.addAction (mMoveUpAction);
} }
if (row<mProxyModel->rowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== if (row<mModel->rowCount()-1 && mModel->data (mModel->index (row, column))==
mProxyModel->data (mProxyModel->index (row+1, column))) mModel->data (mModel->index (row+1, column)))
{ {
menu.addAction (mMoveDownAction); menu.addAction (mMoveDownAction);
} }
@ -85,7 +115,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (selectedRows.size()==1) if (selectedRows.size()==1)
{ {
if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None) if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View)
{ {
int row = selectedRows.begin()->row(); int row = selectedRows.begin()->row();
@ -101,91 +131,13 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction (mViewAction); menu.addAction (mViewAction);
} }
if (mModel->hasPreview()) if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview)
menu.addAction (mPreviewAction); menu.addAction (mPreviewAction);
} }
menu.exec (event->globalPos()); menu.exec (event->globalPos());
} }
std::vector<std::string> CSVWorld::Table::listRevertableSelectedIds() const
{
std::vector<std::string> revertableIds;
if (mProxyModel->columnCount()>0)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
if (state!=CSMWorld::RecordBase::State_BaseOnly)
{
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
toString().toUtf8().constData();
revertableIds.push_back (id);
}
}
}
return revertableIds;
}
std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
{
std::vector<std::string> deletableIds;
if (mProxyModel->columnCount()>0)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end();
++iter)
{
QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0));
// check record state
CSMWorld::RecordBase::State state =
static_cast<CSMWorld::RecordBase::State> (
mModel->data (mModel->index (index.row(), 1)).toInt());
if (state==CSMWorld::RecordBase::State_Deleted)
continue;
// check other columns (only relevant for a subset of the tables)
int dialogueTypeIndex =
mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType);
if (dialogueTypeIndex!=-1)
{
int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt();
if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal)
continue;
}
// add the id to the collection
int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
std::string id = mModel->data (mModel->index (index.row(), columnIndex)).
toString().toUtf8().constData();
deletableIds.push_back (id);
}
}
return deletableIds;
}
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
bool createAndDelete, bool sorting, CSMDoc::Document& document) bool createAndDelete, bool sorting, CSMDoc::Document& document)
: mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0), : mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0),
@ -196,6 +148,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel = new CSMWorld::IdTableProxyModel (this);
mProxyModel->setSourceModel (mModel); mProxyModel->setSourceModel (mModel);
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
setModel (mProxyModel); setModel (mProxyModel);
horizontalHeader()->setResizeMode (QHeaderView::Interactive); horizontalHeader()->setResizeMode (QHeaderView::Interactive);
verticalHeader()->hide(); verticalHeader()->hide();
@ -240,11 +194,11 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
} }
mRevertAction = new QAction (tr ("Revert Record"), this); mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));
addAction (mRevertAction); addAction (mRevertAction);
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()), mDispatcher, SLOT (executeDelete()));
addAction (mDeleteAction); addAction (mDeleteAction);
mMoveUpAction = new QAction (tr ("Move Up"), this); mMoveUpAction = new QAction (tr ("Move Up"), this);
@ -263,6 +217,18 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord()));
addAction (mPreviewAction); addAction (mPreviewAction);
/// \todo add a user option, that redirects the extended action to an input panel (in
/// the bottom bar) that lets the user select which record collections should be
/// modified.
mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this);
connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete()));
addAction (mExtendedDeleteAction);
mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this);
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert()));
addAction (mExtendedRevertAction);
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate())); this, SLOT (tableSizeUpdate()));
@ -283,53 +249,19 @@ void CSVWorld::Table::setEditLock (bool locked)
(*iter)->setEditLock (locked); (*iter)->setEditLock (locked);
DragRecordTable::setEditLock(locked); DragRecordTable::setEditLock(locked);
mDispatcher->setEditLock (locked);
} }
CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
{ {
return CSMWorld::UniversalId ( row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
static_cast<CSMWorld::UniversalId::Type> (mProxyModel->data (mProxyModel->index (row, 2)).toInt()),
mProxyModel->data (mProxyModel->index (row, 0)).toString().toUtf8().constData());
}
void CSVWorld::Table::revertRecord() int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
{ int typeColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
if (!mEditLock)
{
std::vector<std::string> revertableIds = listRevertableSelectedIds();
if (!revertableIds.empty())
{
if (revertableIds.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Revert multiple records"));
for (std::vector<std::string>::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter)
mDocument.getUndoStack().push (new CSMWorld::RevertCommand (*mModel, *iter));
if (revertableIds.size()>1)
mDocument.getUndoStack().endMacro();
}
}
}
void CSVWorld::Table::deleteRecord() return CSMWorld::UniversalId (
{ static_cast<CSMWorld::UniversalId::Type> (mModel->data (mModel->index (row, typeColumn)).toInt()),
if (!mEditLock) mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData());
{
std::vector<std::string> deletableIds = listDeletableSelectedIds();
if (!deletableIds.empty())
{
if (deletableIds.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Delete multiple records"));
for (std::vector<std::string>::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter)
mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (*mModel, *iter));
if (deletableIds.size()>1)
mDocument.getUndoStack().endMacro();
}
}
} }
void CSVWorld::Table::editRecord() void CSVWorld::Table::editRecord()

@ -24,6 +24,7 @@ namespace CSMWorld
class UniversalId; class UniversalId;
class IdTableProxyModel; class IdTableProxyModel;
class IdTable; class IdTable;
class CommandDispatcher;
} }
namespace CSVWorld namespace CSVWorld
@ -45,18 +46,17 @@ namespace CSVWorld
QAction *mMoveDownAction; QAction *mMoveDownAction;
QAction *mViewAction; QAction *mViewAction;
QAction *mPreviewAction; QAction *mPreviewAction;
QAction *mExtendedDeleteAction;
QAction *mExtendedRevertAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
int mRecordStatusDisplay; int mRecordStatusDisplay;
CSMWorld::CommandDispatcher *mDispatcher;
private: private:
void contextMenuEvent (QContextMenuEvent *event); void contextMenuEvent (QContextMenuEvent *event);
std::vector<std::string> listRevertableSelectedIds() const;
std::vector<std::string> listDeletableSelectedIds() const;
void mouseMoveEvent(QMouseEvent *event); void mouseMoveEvent(QMouseEvent *event);
void dropEvent(QDropEvent *event); void dropEvent(QDropEvent *event);
@ -93,10 +93,6 @@ namespace CSVWorld
private slots: private slots:
void revertRecord();
void deleteRecord();
void editRecord(); void editRecord();
void cloneRecord(); void cloneRecord();

@ -33,7 +33,7 @@ add_openmw_dir (mwgui
merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks merchantrepair repair soulgemdialog companionwindow bookpage journalviewmodel journalbooks
keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview keywordsearch itemmodel containeritemmodel inventoryitemmodel sortfilteritemmodel itemview
tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog tradeitemmodel companionitemmodel pickpocketitemmodel fontloader controllers savegamedialog
recharge mode videowidget backgroundimage recharge mode videowidget backgroundimage itemwidget
) )
add_openmw_dir (mwdialogue add_openmw_dir (mwdialogue
@ -142,10 +142,6 @@ if(APPLE)
endif() endif()
endif(APPLE) endif(APPLE)
if(DPKG_PROGRAM)
INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw)
endif(DPKG_PROGRAM)
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw gcov) target_link_libraries(openmw gcov)

@ -4,19 +4,19 @@
#include <components/version/version.hpp> #include <components/version/version.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <SDL.h> #include <SDL_messagebox.h>
#include <SDL_main.h>
#include "engine.hpp" #include "engine.hpp"
#if defined(_WIN32) && !defined(_CONSOLE)
#include <boost/iostreams/concepts.hpp> #include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream_buffer.hpp> #include <boost/iostreams/stream_buffer.hpp>
#if defined(_WIN32)
// For OutputDebugString // For OutputDebugString
#define WIN32_LEAN_AND_MEAN #define WIN32_LEAN_AND_MEAN
#include <Windows.h> #include <Windows.h>
// makes __argc and __argv available on windows // makes __argc and __argv available on windows
#include <cstdlib> #include <cstdlib>
#endif #endif
@ -253,58 +253,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
return true; return true;
} }
int main(int argc, char**argv) #if defined(_WIN32) && defined(_DEBUG)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// Unix crash catcher
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
cc_install_handlers(argc, argv, 5, s, "crash.log", NULL);
std::cout << "Installing crash catcher" << std::endl;
}
else
std::cout << "Running in a debugger, not installing crash catcher" << std::endl;
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// set current dir to bundle path
boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path();
boost::filesystem::current_path(bundlePath);
#endif
try
{
Files::ConfigurationManager cfgMgr;
OMW::Engine engine(cfgMgr);
if (parseOptions(argc, argv, engine, cfgMgr))
{
engine.go();
}
}
catch (std::exception &e)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
if (isatty(fileno(stdin)) || !SDL_WasInit(SDL_INIT_VIDEO))
std::cerr << "\nERROR: " << e.what() << std::endl;
else
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
return 1;
}
return 0;
}
// Platform specific for Windows when there is no console built into the executable.
// Windows will call the WinMain function instead of main in this case, the normal
// main function is then called with the __argc and __argv parameters.
// In addition if it is a debug build it will redirect cout to the debug console in Visual Studio
#if defined(_WIN32) && !defined(_CONSOLE)
#if defined(_DEBUG)
class DebugOutput : public boost::iostreams::sink class DebugOutput : public boost::iostreams::sink
{ {
public: public:
@ -318,11 +268,11 @@ public:
} }
}; };
#else #else
class Logger : public boost::iostreams::sink class Tee : public boost::iostreams::sink
{ {
public: public:
Logger(std::ofstream &stream) Tee(std::ostream &stream, std::ostream &stream2)
: out(stream) : out(stream), out2(stream2)
{ {
} }
@ -330,38 +280,98 @@ public:
{ {
out.write (str, size); out.write (str, size);
out.flush(); out.flush();
out2.write (str, size);
out2.flush();
return size; return size;
} }
private: private:
std::ofstream &out; std::ostream &out;
std::ostream &out2;
}; };
#endif #endif
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd) int main(int argc, char**argv)
{ {
std::streambuf* old_rdbuf = std::cout.rdbuf (); std::streambuf* cout_rdbuf = std::cout.rdbuf ();
std::streambuf* cerr_rdbuf = std::cerr.rdbuf ();
int ret = 0; int ret = 0;
#if defined(_DEBUG) try
// Redirect cout to VS debug output when running in debug mode
{ {
Files::ConfigurationManager cfgMgr;
#if defined(_WIN32) && defined(_DEBUG)
// Redirect cout and cerr to VS debug output when running in debug mode
boost::iostreams::stream_buffer<DebugOutput> sb; boost::iostreams::stream_buffer<DebugOutput> sb;
sb.open(DebugOutput()); sb.open(DebugOutput());
std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb);
#else #else
// Redirect cout to openmw.log // Redirect cout and cerr to openmw.log
std::ofstream logfile ("openmw.log"); std::ofstream logfile (std::string(cfgMgr.getLogPath().string() + "/openmw.log").c_str());
{
boost::iostreams::stream_buffer<Logger> sb; boost::iostreams::stream_buffer<Tee> coutsb;
sb.open (Logger (logfile)); std::ostream oldcout(cout_rdbuf);
coutsb.open (Tee(logfile, oldcout));
std::cout.rdbuf (&coutsb);
boost::iostreams::stream_buffer<Tee> cerrsb;
std::ostream oldcerr(cerr_rdbuf);
cerrsb.open (Tee(logfile, oldcerr));
std::cerr.rdbuf (&cerrsb);
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// Unix crash catcher
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL);
std::cout << "Installing crash catcher" << std::endl;
}
else
std::cout << "Running in a debugger, not installing crash catcher" << std::endl;
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// set current dir to bundle path
boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path();
boost::filesystem::current_path(bundlePath);
#endif #endif
std::cout.rdbuf (&sb);
ret = main (__argc, __argv); OMW::Engine engine(cfgMgr);
std::cout.rdbuf(old_rdbuf); if (parseOptions(argc, argv, engine, cfgMgr))
{
engine.go();
}
}
catch (std::exception &e)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
if (isatty(fileno(stdin)))
std::cerr << "\nERROR: " << e.what() << std::endl;
else
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
ret = 1;
} }
// Restore cout and cerr
std::cout.rdbuf(cout_rdbuf);
std::cerr.rdbuf(cerr_rdbuf);
return ret; return ret;
} }
// Platform specific for Windows when there is no console built into the executable.
// Windows will call the WinMain function instead of main in this case, the normal
// main function is then called with the __argc and __argv parameters.
#if defined(_WIN32) && !defined(_CONSOLE)
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nShowCmd)
{
return main(__argc, __argv);
}
#endif #endif

@ -76,6 +76,9 @@ namespace MWBase
/// @return faction1's opinion of faction2 /// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0; virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const = 0;
/// Removes the last added topic response for the given actor from the journal
virtual void clearInfoActor (const MWWorld::Ptr& actor) const = 0;
}; };
} }

@ -59,7 +59,12 @@ namespace MWBase
virtual int getJournalIndex (const std::string& id) const = 0; virtual int getJournalIndex (const std::string& id) const = 0;
///< Get the journal index. ///< Get the journal index.
virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0; virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor) = 0;
/// \note topicId must be lowercase
virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName) = 0;
///< Removes the last topic response added for the given topicId and actor name.
/// \note topicId must be lowercase
virtual TEntryIter begin() const = 0; virtual TEntryIter begin() const = 0;
///< Iterator pointing to the begin of the main journal. ///< Iterator pointing to the begin of the main journal.

@ -328,6 +328,8 @@ namespace MWBase
/** Used when one Modal adds another Modal /** Used when one Modal adds another Modal
\param input Pointer to the current modal, to ensure proper modal is removed **/ \param input Pointer to the current modal, to ensure proper modal is removed **/
virtual void removeCurrentModal(MWGui::WindowModal* input) = 0; virtual void removeCurrentModal(MWGui::WindowModal* input) = 0;
virtual void pinWindow (MWGui::GuiWindow window) = 0;
}; };
} }

@ -345,7 +345,9 @@ namespace MWClass
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);
// Self defense // Self defense
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures
// (they have no movement or attacks anyway)
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
if(!successful) if(!successful)

@ -680,7 +680,7 @@ namespace MWClass
else else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur? getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(ishealth) if(ishealth && !attacker.isEmpty()) // Don't use armor mitigation for fall damage
{ {
// Hit percentages: // Hit percentages:
// cuirass = 30% // cuirass = 30%

@ -14,9 +14,12 @@ namespace MWClass
{ {
void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Static::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
MWWorld::LiveCellRef<ESM::Static> *ref =
ptr.get<ESM::Static>();
const std::string model = getModel(ptr); const std::string model = getModel(ptr);
if (!model.empty()) { if (!model.empty()) {
renderingInterface.getObjects().insertModel(ptr, model); renderingInterface.getObjects().insertModel(ptr, model, !ref->mBase->mPersistent);
} }
} }

@ -140,7 +140,11 @@ namespace MWDialogue
mActorKnownTopics.clear(); mActorKnownTopics.clear();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->startDialogue(actor, actor.getClass().getName (actor));
// If the dialogue window was already open, keep the existing history
bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue));
win->startDialogue(actor, actor.getClass().getName (actor), resetHistory);
//setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI
updateTopics(); updateTopics();
@ -294,7 +298,7 @@ namespace MWDialogue
{ {
if (iter->mId == info->mId) if (iter->mId == info->mId)
{ {
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor)); MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor);
break; break;
} }
} }
@ -472,7 +476,7 @@ namespace MWDialogue
{ {
if (iter->mId == info->mId) if (iter->mId == info->mId)
{ {
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor)); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor);
break; break;
} }
} }
@ -694,6 +698,15 @@ namespace MWDialogue
return diff; return diff;
} }
void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const
{
if (actor == mActor && !mLastTopic.empty())
{
MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse(
mLastTopic, actor.getClass().getName(actor));
}
}
std::vector<HyperTextToken> ParseHyperText(const std::string& text) std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{ {
std::vector<HyperTextToken> result; std::vector<HyperTextToken> result;

@ -40,7 +40,7 @@ namespace MWDialogue
bool mTalkedTo; bool mTalkedTo;
int mChoice; int mChoice;
std::string mLastTopic; std::string mLastTopic; // last topic ID, lowercase
bool mIsInChoice; bool mIsInChoice;
float mTemporaryDispositionChange; float mTemporaryDispositionChange;
@ -99,6 +99,9 @@ namespace MWDialogue
/// @return faction1's opinion of faction2 /// @return faction1's opinion of faction2
virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const; virtual int getFactionReaction (const std::string& faction1, const std::string& faction2) const;
/// Removes the last added topic response for the given actor from the journal
virtual void clearInfoActor (const MWWorld::Ptr& actor) const;
}; };

@ -5,16 +5,21 @@
#include <components/esm/journalentry.hpp> #include <components/esm/journalentry.hpp>
#include <components/interpreter/defines.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwscript/interpretercontext.hpp"
namespace MWDialogue namespace MWDialogue
{ {
Entry::Entry() {} Entry::Entry() {}
Entry::Entry (const std::string& topic, const std::string& infoId) Entry::Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor)
: mInfoId (infoId) : mInfoId (infoId)
{ {
const ESM::Dialogue *dialogue = const ESM::Dialogue *dialogue =
@ -24,8 +29,17 @@ namespace MWDialogue
iter!=dialogue->mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == mInfoId) if (iter->mId == mInfoId)
{ {
/// \todo text replacement if (actor.isEmpty())
mText = iter->mResponse; {
MWScript::InterpreterContext interpreterContext(NULL,MWWorld::Ptr());
mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext);
}
else
{
MWScript::InterpreterContext interpreterContext(&actor.getRefData().getLocals(),actor);
mText = Interpreter::fixDefinesDialog(iter->mResponse, interpreterContext);
}
return; return;
} }
@ -49,8 +63,8 @@ namespace MWDialogue
JournalEntry::JournalEntry() {} JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor)
: Entry (topic, infoId), mTopic (topic) : Entry (topic, infoId, actor), mTopic (topic)
{} {}
JournalEntry::JournalEntry (const ESM::JournalEntry& record) JournalEntry::JournalEntry (const ESM::JournalEntry& record)
@ -65,7 +79,7 @@ namespace MWDialogue
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index)
{ {
return JournalEntry (topic, idFromIndex (topic, index)); return JournalEntry (topic, idFromIndex (topic, index), MWWorld::Ptr());
} }
std::string JournalEntry::idFromIndex (const std::string& topic, int index) std::string JournalEntry::idFromIndex (const std::string& topic, int index)
@ -90,7 +104,7 @@ namespace MWDialogue
StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId, StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth) int day, int month, int dayOfMonth)
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) : JournalEntry (topic, infoId, MWWorld::Ptr()), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
{} {}
StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record) StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record)

@ -8,6 +8,11 @@ namespace ESM
struct JournalEntry; struct JournalEntry;
} }
namespace MWWorld
{
class Ptr;
}
namespace MWDialogue namespace MWDialogue
{ {
/// \brief Basic quest/dialogue/topic entry /// \brief Basic quest/dialogue/topic entry
@ -19,7 +24,8 @@ namespace MWDialogue
Entry(); Entry();
Entry (const std::string& topic, const std::string& infoId); /// actor is optional
Entry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor);
Entry (const ESM::JournalEntry& record); Entry (const ESM::JournalEntry& record);
@ -37,7 +43,7 @@ namespace MWDialogue
JournalEntry(); JournalEntry();
JournalEntry (const std::string& topic, const std::string& infoId); JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor);
JournalEntry (const ESM::JournalEntry& record); JournalEntry (const ESM::JournalEntry& record);

@ -9,6 +9,7 @@
#include <components/esm/journalentry.hpp> #include <components/esm/journalentry.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -103,15 +104,25 @@ namespace MWDialogue
quest.setIndex (index); quest.setIndex (index);
} }
void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) void Journal::addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor)
{ {
Topic& topic = getTopic (topicId); Topic& topic = getTopic (topicId);
JournalEntry entry(topicId, infoId); JournalEntry entry(topicId, infoId, actor);
entry.mActorName = actorName; entry.mActorName = actor.getClass().getName(actor);
topic.addEntry (entry); topic.addEntry (entry);
} }
void Journal::removeLastAddedTopicResponse(const std::string &topicId, const std::string &actorName)
{
Topic& topic = getTopic (topicId);
topic.removeLastAddedResponse(actorName);
if (topic.begin() == topic.end())
mTopics.erase(mTopics.find(topicId)); // All responses removed -> remove topic
}
int Journal::getJournalIndex (const std::string& id) const int Journal::getJournalIndex (const std::string& id) const
{ {
TQuestContainer::const_iterator iter = mQuests.find (id); TQuestContainer::const_iterator iter = mQuests.find (id);

@ -38,7 +38,12 @@ namespace MWDialogue
virtual int getJournalIndex (const std::string& id) const; virtual int getJournalIndex (const std::string& id) const;
///< Get the journal index. ///< Get the journal index.
virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName); virtual void addTopic (const std::string& topicId, const std::string& infoId, const MWWorld::Ptr& actor);
/// \note topicId must be lowercase
virtual void removeLastAddedTopicResponse (const std::string& topicId, const std::string& actorName);
///< Removes the last topic response added for the given topicId and actor name.
/// \note topicId must be lowercase
virtual TEntryIter begin() const; virtual TEntryIter begin() const;
///< Iterator pointing to the begin of the main journal. ///< Iterator pointing to the begin of the main journal.

@ -59,8 +59,16 @@ namespace MWDialogue
return mEntries.end(); return mEntries.end();
} }
JournalEntry Topic::getEntry (const std::string& infoId) const void Topic::removeLastAddedResponse (const std::string& actorName)
{ {
return JournalEntry (mTopic, infoId); for (std::vector<MWDialogue::Entry>::reverse_iterator it = mEntries.rbegin();
it != mEntries.rend(); ++it)
{
if (it->mActorName == actorName)
{
mEntries.erase( (++it).base() ); // erase doesn't take a reverse_iterator
return;
}
}
} }
} }

@ -48,13 +48,13 @@ namespace MWDialogue
virtual std::string getName() const; virtual std::string getName() const;
void removeLastAddedResponse (const std::string& actorName);
TEntryIter begin() const; TEntryIter begin() const;
///< Iterator pointing to the begin of the journal for this topic. ///< Iterator pointing to the begin of the journal for this topic.
TEntryIter end() const; TEntryIter end() const;
///< Iterator pointing past the end of the journal for this topic. ///< Iterator pointing past the end of the journal for this topic.
JournalEntry getEntry (const std::string& infoId) const;
}; };
} }

@ -13,20 +13,7 @@
#include "inventoryitemmodel.hpp" #include "inventoryitemmodel.hpp"
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include "itemview.hpp" #include "itemview.hpp"
#include "itemwidget.hpp"
namespace
{
std::string getIconPath(MWWorld::Ptr ptr)
{
std::string path = std::string("icons\\");
path += ptr.getClass().getInventoryIcon(ptr);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
return path;
}
}
namespace MWGui namespace MWGui
{ {
@ -149,7 +136,7 @@ namespace MWGui
{ {
mApparatus.at (index)->setUserString ("ToolTipType", "ItemPtr"); mApparatus.at (index)->setUserString ("ToolTipType", "ItemPtr");
mApparatus.at (index)->setUserData (*iter); mApparatus.at (index)->setUserData (*iter);
mApparatus.at (index)->setImageTexture (getIconPath (*iter)); mApparatus.at (index)->setItem(*iter);
} }
} }
@ -189,7 +176,7 @@ namespace MWGui
MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients (); MWMechanics::Alchemy::TIngredientsIterator it = mAlchemy.beginIngredients ();
for (int i=0; i<4; ++i) for (int i=0; i<4; ++i)
{ {
MyGUI::ImageBox* ingredient = mIngredients[i]; ItemWidget* ingredient = mIngredients[i];
MWWorld::Ptr item; MWWorld::Ptr item;
if (it != mAlchemy.endIngredients ()) if (it != mAlchemy.endIngredients ())
@ -204,15 +191,15 @@ namespace MWGui
if (ingredient->getChildCount()) if (ingredient->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(ingredient->getChildAt(0));
ingredient->setImageTexture("");
ingredient->clearUserStrings (); ingredient->clearUserStrings ();
ingredient->setItem(item);
if (item.isEmpty ()) if (item.isEmpty ())
continue; continue;
ingredient->setUserString("ToolTipType", "ItemPtr"); ingredient->setUserString("ToolTipType", "ItemPtr");
ingredient->setUserData(item); ingredient->setUserData(item);
ingredient->setImageTexture(getIconPath(item));
MyGUI::TextBox* text = ingredient->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); MyGUI::TextBox* text = ingredient->createWidget<MyGUI::TextBox>("SandBrightText", MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label"));
text->setTextAlign(MyGUI::Align::Right); text->setTextAlign(MyGUI::Align::Right);

@ -11,6 +11,7 @@
namespace MWGui namespace MWGui
{ {
class ItemView; class ItemView;
class ItemWidget;
class SortFilterItemModel; class SortFilterItemModel;
class AlchemyWindow : public WindowBase class AlchemyWindow : public WindowBase
@ -44,8 +45,8 @@ namespace MWGui
MWMechanics::Alchemy mAlchemy; MWMechanics::Alchemy mAlchemy;
std::vector<MyGUI::ImageBox *> mApparatus; std::vector<ItemWidget*> mApparatus;
std::vector<MyGUI::ImageBox *> mIngredients; std::vector<ItemWidget*> mIngredients;
}; };
} }

@ -30,9 +30,9 @@ namespace MWGui
void ConfirmationDialog::exit() void ConfirmationDialog::exit()
{ {
eventCancelClicked();
setVisible(false); setVisible(false);
eventCancelClicked();
} }
void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender) void ConfirmationDialog::onCancelButtonClicked(MyGUI::Widget* _sender)
@ -42,8 +42,8 @@ namespace MWGui
void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender) void ConfirmationDialog::onOkButtonClicked(MyGUI::Widget* _sender)
{ {
eventOkClicked();
setVisible(false); setVisible(false);
eventOkClicked();
} }
} }

@ -20,6 +20,7 @@
#include "inventorywindow.hpp" #include "inventorywindow.hpp"
#include "itemview.hpp" #include "itemview.hpp"
#include "itemwidget.hpp"
#include "inventoryitemmodel.hpp" #include "inventoryitemmodel.hpp"
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
#include "pickpocketitemmodel.hpp" #include "pickpocketitemmodel.hpp"
@ -46,22 +47,15 @@ namespace MWGui
mSourceSortModel->addDragItem(mItem.mBase, count); mSourceSortModel->addDragItem(mItem.mBase, count);
} }
std::string path = std::string("icons\\"); ItemWidget* baseWidget = mDragAndDropWidget->createWidget<ItemWidget>
path += mItem.mBase.getClass().getInventoryIcon(mItem.mBase); ("MW_ItemIcon", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default);
MyGUI::ImageBox* baseWidget = mDragAndDropWidget->createWidget<MyGUI::ImageBox>
("ImageBox", MyGUI::IntCoord(0, 0, 42, 42), MyGUI::Align::Default);
mDraggedWidget = baseWidget; mDraggedWidget = baseWidget;
MyGUI::ImageBox* image = baseWidget->createWidget<MyGUI::ImageBox>("ImageBox", baseWidget->setItem(mItem.mBase);
MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); baseWidget->setNeedMouseFocus(false);
size_t pos = path.rfind(".");
if (pos != std::string::npos)
path.erase(pos);
path.append(".dds");
image->setImageTexture(path);
image->setNeedMouseFocus(false);
// text widget that shows item count // text widget that shows item count
MyGUI::TextBox* text = image->createWidget<MyGUI::TextBox>("SandBrightText", // TODO: move to ItemWidget
MyGUI::TextBox* text = baseWidget->createWidget<MyGUI::TextBox>("SandBrightText",
MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label"));
text->setTextAlign(MyGUI::Align::Right); text->setTextAlign(MyGUI::Align::Right);
text->setNeedMouseFocus(false); text->setNeedMouseFocus(false);

@ -366,10 +366,11 @@ namespace MWGui
} }
} }
void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory)
{ {
mGoodbye = false; mGoodbye = false;
mEnabled = true; mEnabled = true;
bool sameActor = (mPtr == actor);
mPtr = actor; mPtr = actor;
mTopicsList->setEnabled(true); mTopicsList->setEnabled(true);
setTitle(npcName); setTitle(npcName);
@ -378,9 +379,12 @@ namespace MWGui
mTopicsList->clear(); mTopicsList->clear();
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) if (resetHistory || !sameActor)
delete (*it); {
mHistoryContents.clear(); for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
delete (*it);
mHistoryContents.clear();
}
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it) for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it)
delete (*it); delete (*it);

@ -111,7 +111,7 @@ namespace MWGui
void notifyLinkClicked (TypesetBook::InteractiveId link); void notifyLinkClicked (TypesetBook::InteractiveId link);
void startDialogue(MWWorld::Ptr actor, std::string npcName); void startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory);
void setKeywords(std::list<std::string> keyWord); void setKeywords(std::list<std::string> keyWord);
void addResponse (const std::string& text, const std::string& title=""); void addResponse (const std::string& text, const std::string& title="");

@ -14,6 +14,7 @@
#include "itemselection.hpp" #include "itemselection.hpp"
#include "container.hpp" #include "container.hpp"
#include "itemwidget.hpp"
#include "sortfilteritemmodel.hpp" #include "sortfilteritemmodel.hpp"
@ -57,8 +58,45 @@ namespace MWGui
void EnchantingDialog::open() void EnchantingDialog::open()
{ {
center(); center();
onRemoveItem(NULL);
onRemoveSoul(NULL); setSoulGem(MWWorld::Ptr());
setItem(MWWorld::Ptr());
}
void EnchantingDialog::setSoulGem(const MWWorld::Ptr &gem)
{
if (gem.isEmpty())
{
mSoulBox->setItem(MWWorld::Ptr());
mSoulBox->clearUserStrings();
mEnchanting.setSoulGem(MWWorld::Ptr());
}
else
{
mSoulBox->setItem(gem);
mSoulBox->setUserString ("ToolTipType", "ItemPtr");
mSoulBox->setUserData(gem);
mEnchanting.setSoulGem(gem);
}
updateLabels();
}
void EnchantingDialog::setItem(const MWWorld::Ptr &item)
{
if (item.isEmpty())
{
mItemBox->setItem(MWWorld::Ptr());
mItemBox->clearUserStrings();
mEnchanting.setOldItem(MWWorld::Ptr());
}
else
{
mItemBox->setItem(item);
mItemBox->setUserString ("ToolTipType", "ItemPtr");
mItemBox->setUserData(item);
mEnchanting.setOldItem(item);
}
updateLabels();
} }
void EnchantingDialog::exit() void EnchantingDialog::exit()
@ -122,16 +160,7 @@ namespace MWGui
startEditing(); startEditing();
mEnchanting.setSoulGem(soulgem); mEnchanting.setSoulGem(soulgem);
MyGUI::ImageBox* image = mSoulBox->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default); setSoulGem(soulgem);
std::string path = std::string("icons\\");
path += soulgem.getClass().getInventoryIcon(soulgem);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
image->setImageTexture (path);
image->setUserString ("ToolTipType", "ItemPtr");
image->setUserData(soulgem);
image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveSoul);
mPrice->setVisible(false); mPrice->setVisible(false);
mPriceText->setVisible(false); mPriceText->setVisible(false);
@ -151,46 +180,31 @@ namespace MWGui
void EnchantingDialog::onSelectItem(MyGUI::Widget *sender) void EnchantingDialog::onSelectItem(MyGUI::Widget *sender)
{ {
delete mItemSelectionDialog; if (mEnchanting.getOldItem().isEmpty())
mItemSelectionDialog = new ItemSelectionDialog("#{sEnchantItems}"); {
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected); delete mItemSelectionDialog;
mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel); mItemSelectionDialog = new ItemSelectionDialog("#{sEnchantItems}");
mItemSelectionDialog->setVisible(true); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onItemSelected);
mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onItemCancel);
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable); mItemSelectionDialog->setVisible(true);
mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr());
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyEnchantable);
}
else
{
setItem(MWWorld::Ptr());
}
} }
void EnchantingDialog::onItemSelected(MWWorld::Ptr item) void EnchantingDialog::onItemSelected(MWWorld::Ptr item)
{ {
mItemSelectionDialog->setVisible(false); mItemSelectionDialog->setVisible(false);
while (mItemBox->getChildCount ()) setItem(item);
MyGUI::Gui::getInstance ().destroyWidget (mItemBox->getChildAt(0));
MyGUI::ImageBox* image = mItemBox->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\");
path += item.getClass().getInventoryIcon(item);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
image->setImageTexture (path);
image->setUserString ("ToolTipType", "ItemPtr");
image->setUserData(item);
image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveItem);
mEnchanting.setOldItem(item);
mEnchanting.nextCastStyle(); mEnchanting.nextCastStyle();
updateLabels(); updateLabels();
} }
void EnchantingDialog::onRemoveItem(MyGUI::Widget *sender)
{
while (mItemBox->getChildCount ())
MyGUI::Gui::getInstance ().destroyWidget (mItemBox->getChildAt(0));
mEnchanting.setOldItem(MWWorld::Ptr());
updateLabels();
}
void EnchantingDialog::onItemCancel() void EnchantingDialog::onItemCancel()
{ {
mItemSelectionDialog->setVisible(false); mItemSelectionDialog->setVisible(false);
@ -207,28 +221,7 @@ namespace MWGui
return; return;
} }
while (mSoulBox->getChildCount ()) setSoulGem(item);
MyGUI::Gui::getInstance ().destroyWidget (mSoulBox->getChildAt(0));
MyGUI::ImageBox* image = mSoulBox->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(0, 0, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\");
path += item.getClass().getInventoryIcon(item);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
image->setImageTexture (path);
image->setUserString ("ToolTipType", "ItemPtr");
image->setUserData(item);
image->eventMouseButtonClick += MyGUI::newDelegate(this, &EnchantingDialog::onRemoveSoul);
updateLabels();
}
void EnchantingDialog::onRemoveSoul(MyGUI::Widget *sender)
{
while (mSoulBox->getChildCount ())
MyGUI::Gui::getInstance ().destroyWidget (mSoulBox->getChildAt(0));
mEnchanting.setSoulGem(MWWorld::Ptr());
updateLabels();
} }
void EnchantingDialog::onSoulCancel() void EnchantingDialog::onSoulCancel()
@ -238,15 +231,22 @@ namespace MWGui
void EnchantingDialog::onSelectSoul(MyGUI::Widget *sender) void EnchantingDialog::onSelectSoul(MyGUI::Widget *sender)
{ {
delete mItemSelectionDialog; if (mEnchanting.getGem().isEmpty())
mItemSelectionDialog = new ItemSelectionDialog("#{sSoulGemsWithSouls}"); {
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected); delete mItemSelectionDialog;
mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel); mItemSelectionDialog = new ItemSelectionDialog("#{sSoulGemsWithSouls}");
mItemSelectionDialog->setVisible(true); mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &EnchantingDialog::onSoulSelected);
mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->eventDialogCanceled += MyGUI::newDelegate(this, &EnchantingDialog::onSoulCancel);
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones); mItemSelectionDialog->setVisible(true);
mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr());
//MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}"); mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyChargedSoulstones);
//MWBase::Environment::get().getWindowManager()->messageBox("#{sInventorySelectNoSoul}");
}
else
{
setSoulGem(MWWorld::Ptr());
}
} }
void EnchantingDialog::notifyEffectsChanged () void EnchantingDialog::notifyEffectsChanged ()

@ -11,6 +11,7 @@ namespace MWGui
{ {
class ItemSelectionDialog; class ItemSelectionDialog;
class ItemWidget;
class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase class EnchantingDialog : public WindowBase, public ReferenceInterface, public EffectEditorBase
{ {
@ -22,6 +23,9 @@ namespace MWGui
virtual void exit(); virtual void exit();
void setSoulGem (const MWWorld::Ptr& gem);
void setItem (const MWWorld::Ptr& item);
void startEnchanting(MWWorld::Ptr actor); void startEnchanting(MWWorld::Ptr actor);
void startSelfEnchanting(MWWorld::Ptr soulgem); void startSelfEnchanting(MWWorld::Ptr soulgem);
@ -32,8 +36,6 @@ namespace MWGui
void onCancelButtonClicked(MyGUI::Widget* sender); void onCancelButtonClicked(MyGUI::Widget* sender);
void onSelectItem (MyGUI::Widget* sender); void onSelectItem (MyGUI::Widget* sender);
void onSelectSoul (MyGUI::Widget* sender); void onSelectSoul (MyGUI::Widget* sender);
void onRemoveItem (MyGUI::Widget* sender);
void onRemoveSoul (MyGUI::Widget* sender);
void onItemSelected(MWWorld::Ptr item); void onItemSelected(MWWorld::Ptr item);
void onItemCancel(); void onItemCancel();
@ -46,8 +48,8 @@ namespace MWGui
ItemSelectionDialog* mItemSelectionDialog; ItemSelectionDialog* mItemSelectionDialog;
MyGUI::Button* mCancelButton; MyGUI::Button* mCancelButton;
MyGUI::ImageBox* mItemBox; ItemWidget* mItemBox;
MyGUI::ImageBox* mSoulBox; ItemWidget* mSoulBox;
MyGUI::Button* mTypeButton; MyGUI::Button* mTypeButton;
MyGUI::Button* mBuyButton; MyGUI::Button* mBuyButton;

@ -64,10 +64,15 @@ namespace
return unicode; return unicode;
} }
// getUtf8, aka the worst function ever written.
// This includes various hacks for dealing with Morrowind's .fnt files that are *mostly*
// in the expected win12XX encoding, but also have randomly swapped characters sometimes.
// Looks like the Morrowind developers found standard encodings too boring and threw in some twists for fun.
std::string getUtf8 (unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding) std::string getUtf8 (unsigned char c, ToUTF8::Utf8Encoder& encoder, ToUTF8::FromType encoding)
{ {
if (encoding == ToUTF8::WINDOWS_1250) if (encoding == ToUTF8::WINDOWS_1250)
{ {
// Hacks for polish font
unsigned char win1250; unsigned char win1250;
std::map<unsigned char, unsigned char> conv; std::map<unsigned char, unsigned char> conv;
conv[0x80] = 0xc6; conv[0x80] = 0xc6;
@ -101,7 +106,8 @@ namespace
conv[0xa3] = 0xbf; conv[0xa3] = 0xbf;
conv[0xa4] = 0x0; // not contained in win1250 conv[0xa4] = 0x0; // not contained in win1250
conv[0xe1] = 0x8c; conv[0xe1] = 0x8c;
conv[0xe1] = 0x8c; // Can't remember if this was supposed to read 0xe2, or is it just an extraneous copypaste?
//conv[0xe1] = 0x8c;
conv[0xe3] = 0x0; // not contained in win1250 conv[0xe3] = 0x0; // not contained in win1250
conv[0xf5] = 0x0; // not contained in win1250 conv[0xf5] = 0x0; // not contained in win1250
@ -252,6 +258,25 @@ namespace MWGui
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent))); + MyGUI::utility::toString((fontSize-data[i].ascent)));
// More hacks! The french game uses several win1252 characters that are not included
// in the cp437 encoding of the font. Fall back to similar available characters.
// Same for U+2013
std::map<int, int> additional;
additional[39] = 0x2019; // apostrophe
additional[45] = 0x2013; // dash
if (additional.find(i) != additional.end() && mEncoding == ToUTF8::CP437)
{
MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", additional[i]);
code->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " "
+ MyGUI::utility::toString(h));
code->addAttribute("advance", data[i].width);
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
}
// ASCII vertical bar, use this as text input cursor // ASCII vertical bar, use this as text input cursor
if (i == 124) if (i == 124)
{ {
@ -265,18 +290,30 @@ namespace MWGui
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " " cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent))); + MyGUI::utility::toString((fontSize-data[i].ascent)));
} }
// Question mark, use for NotDefined marker (used for glyphs not existing in the font)
if (i == 63)
{
MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code");
cursorCode->addAttribute("index", MyGUI::FontCodeType::NotDefined);
cursorCode->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " "
+ MyGUI::utility::toString(h));
cursorCode->addAttribute("advance", data[i].width);
cursorCode->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
}
} }
// These are required as well, but the fonts don't provide them // These are required as well, but the fonts don't provide them
for (int i=0; i<3; ++i) for (int i=0; i<2; ++i)
{ {
MyGUI::FontCodeType::Enum type; MyGUI::FontCodeType::Enum type;
if(i == 0) if(i == 0)
type = MyGUI::FontCodeType::Selected; type = MyGUI::FontCodeType::Selected;
else if (i == 1) else if (i == 1)
type = MyGUI::FontCodeType::SelectedBack; type = MyGUI::FontCodeType::SelectedBack;
else if (i == 2)
type = MyGUI::FontCodeType::NotDefined;
MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code"); MyGUI::xml::ElementPtr cursorCode = codes->createChild("Code");
cursorCode->addAttribute("index", type); cursorCode->addAttribute("index", type);

@ -18,6 +18,7 @@
#include "container.hpp" #include "container.hpp"
#include "itemmodel.hpp" #include "itemmodel.hpp"
#include "itemwidget.hpp"
namespace MWGui namespace MWGui
{ {
@ -423,9 +424,6 @@ namespace MWGui
mSpellStatus->setProgressRange(100); mSpellStatus->setProgressRange(100);
mSpellStatus->setProgressPosition(successChancePercent); mSpellStatus->setProgressPosition(successChancePercent);
if (mSpellImage->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0));
mSpellBox->setUserString("ToolTipType", "Spell"); mSpellBox->setUserString("ToolTipType", "Spell");
mSpellBox->setUserString("Spell", spellId); mSpellBox->setUserString("Spell", spellId);
@ -438,7 +436,9 @@ namespace MWGui
icon.insert(slashPos+1, "b_"); icon.insert(slashPos+1, "b_");
icon = std::string("icons\\") + icon; icon = std::string("icons\\") + icon;
Widgets::fixTexturePath(icon); Widgets::fixTexturePath(icon);
mSpellImage->setImageTexture(icon);
mSpellImage->setItem(MWWorld::Ptr());
mSpellImage->setIcon(icon);
} }
void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent) void HUD::setSelectedEnchantItem(const MWWorld::Ptr& item, int chargePercent)
@ -455,21 +455,10 @@ namespace MWGui
mSpellStatus->setProgressRange(100); mSpellStatus->setProgressRange(100);
mSpellStatus->setProgressPosition(chargePercent); mSpellStatus->setProgressPosition(chargePercent);
if (mSpellImage->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0));
mSpellBox->setUserString("ToolTipType", "ItemPtr"); mSpellBox->setUserString("ToolTipType", "ItemPtr");
mSpellBox->setUserData(item); mSpellBox->setUserData(item);
mSpellImage->setImageTexture("textures\\menu_icon_magic_mini.dds"); mSpellImage->setItem(item);
MyGUI::ImageBox* itemBox = mSpellImage->createWidgetReal<MyGUI::ImageBox>("ImageBox", MyGUI::FloatCoord(0,0,1,1)
, MyGUI::Align::Stretch);
std::string path = std::string("icons\\");
path+=item.getClass().getInventoryIcon(item);
Widgets::fixTexturePath(path);
itemBox->setImageTexture(path);
itemBox->setNeedMouseFocus(false);
} }
void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent) void HUD::setSelectedWeapon(const MWWorld::Ptr& item, int durabilityPercent)
@ -489,23 +478,7 @@ namespace MWGui
mWeapStatus->setProgressRange(100); mWeapStatus->setProgressRange(100);
mWeapStatus->setProgressPosition(durabilityPercent); mWeapStatus->setProgressPosition(durabilityPercent);
if (mWeapImage->getChildCount()) mWeapImage->setItem(item);
MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0));
std::string path = std::string("icons\\");
path+=item.getClass().getInventoryIcon(item);
Widgets::fixTexturePath(path);
if (item.getClass().getEnchantment(item) != "")
{
mWeapImage->setImageTexture("textures\\menu_icon_magic_mini.dds");
MyGUI::ImageBox* itemBox = mWeapImage->createWidgetReal<MyGUI::ImageBox>("ImageBox", MyGUI::FloatCoord(0,0,1,1)
, MyGUI::Align::Stretch);
itemBox->setImageTexture(path);
itemBox->setNeedMouseFocus(false);
}
else
mWeapImage->setImageTexture(path);
} }
void HUD::unsetSelectedSpell() void HUD::unsetSelectedSpell()
@ -519,11 +492,9 @@ namespace MWGui
mWeaponSpellBox->setVisible(true); mWeaponSpellBox->setVisible(true);
} }
if (mSpellImage->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mSpellImage->getChildAt(0));
mSpellStatus->setProgressRange(100); mSpellStatus->setProgressRange(100);
mSpellStatus->setProgressPosition(0); mSpellStatus->setProgressPosition(0);
mSpellImage->setImageTexture(""); mSpellImage->setItem(MWWorld::Ptr());
mSpellBox->clearUserStrings(); mSpellBox->clearUserStrings();
} }
@ -538,17 +509,17 @@ namespace MWGui
mWeaponSpellBox->setVisible(true); mWeaponSpellBox->setVisible(true);
} }
if (mWeapImage->getChildCount())
MyGUI::Gui::getInstance().destroyWidget(mWeapImage->getChildAt(0));
mWeapStatus->setProgressRange(100); mWeapStatus->setProgressRange(100);
mWeapStatus->setProgressPosition(0); mWeapStatus->setProgressPosition(0);
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
MWWorld::Ptr player = world->getPlayerPtr(); MWWorld::Ptr player = world->getPlayerPtr();
mWeapImage->setItem(MWWorld::Ptr());
if (player.getClass().getNpcStats(player).isWerewolf()) if (player.getClass().getNpcStats(player).isWerewolf())
mWeapImage->setImageTexture("icons\\k\\tx_werewolf_hand.dds"); mWeapImage->setIcon("icons\\k\\tx_werewolf_hand.dds");
else else
mWeapImage->setImageTexture("icons\\k\\stealth_handtohand.dds"); mWeapImage->setIcon("icons\\k\\stealth_handtohand.dds");
mWeapBox->clearUserStrings(); mWeapBox->clearUserStrings();
} }

@ -10,6 +10,7 @@ namespace MWGui
{ {
class DragAndDrop; class DragAndDrop;
class SpellIcons; class SpellIcons;
class ItemWidget;
class HUD : public OEngine::GUI::Layout, public LocalMapBase class HUD : public OEngine::GUI::Layout, public LocalMapBase
{ {
@ -63,7 +64,7 @@ namespace MWGui
MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning; MyGUI::ProgressBar *mHealth, *mMagicka, *mStamina, *mEnemyHealth, *mDrowning;
MyGUI::Widget* mHealthFrame; MyGUI::Widget* mHealthFrame;
MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox; MyGUI::Widget *mWeapBox, *mSpellBox, *mSneakBox;
MyGUI::ImageBox *mWeapImage, *mSpellImage; ItemWidget *mWeapImage, *mSpellImage;
MyGUI::ProgressBar *mWeapStatus, *mSpellStatus; MyGUI::ProgressBar *mWeapStatus, *mSpellStatus;
MyGUI::Widget *mEffectBox, *mMinimapBox; MyGUI::Widget *mEffectBox, *mMinimapBox;
MyGUI::Button* mMinimapButton; MyGUI::Button* mMinimapButton;

@ -12,6 +12,7 @@
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "itemmodel.hpp" #include "itemmodel.hpp"
#include "itemwidget.hpp"
namespace MWGui namespace MWGui
{ {
@ -80,53 +81,24 @@ void ItemView::update()
const ItemStack& item = mModel->getItem(i); const ItemStack& item = mModel->getItem(i);
/// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them /// \todo performance improvement: don't create/destroy all the widgets everytime the container window changes size, only reposition them
std::string path = std::string("icons\\"); ItemWidget* itemWidget = dragArea->createWidget<ItemWidget>("MW_ItemIcon",
path += item.mBase.getClass().getInventoryIcon(item.mBase);
// background widget (for the "equipped" frame and magic item background image)
bool isMagic = (item.mFlags & ItemStack::Flag_Enchanted);
MyGUI::ImageBox* backgroundWidget = dragArea->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default); MyGUI::IntCoord(x, y, 42, 42), MyGUI::Align::Default);
backgroundWidget->setUserString("ToolTipType", "ItemModelIndex"); itemWidget->setUserString("ToolTipType", "ItemModelIndex");
backgroundWidget->setUserData(std::make_pair(i, mModel)); itemWidget->setUserData(std::make_pair(i, mModel));
ItemWidget::ItemState state = ItemWidget::None;
std::string backgroundTex = "textures\\menu_icon"; if (item.mType == ItemStack::Type_Barter)
if (isMagic) state = ItemWidget::Barter;
backgroundTex += "_magic"; if (item.mType == ItemStack::Type_Equipped)
if (item.mType == ItemStack::Type_Normal) state = ItemWidget::Equip;
{ itemWidget->setItem(item.mBase, state);
if (!isMagic)
backgroundTex = ""; itemWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedItem);
} itemWidget->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheel);
else if (item.mType == ItemStack::Type_Equipped)
backgroundTex += "_equip";
else if (item.mType == ItemStack::Type_Barter)
backgroundTex += "_barter";
if (backgroundTex != "")
backgroundTex += ".dds";
backgroundWidget->setImageTexture(backgroundTex);
if ((item.mType == ItemStack::Type_Barter) && !isMagic)
backgroundWidget->setProperty("ImageCoord", "2 2 42 42");
else
backgroundWidget->setProperty("ImageCoord", "0 0 42 42");
backgroundWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &ItemView::onSelectedItem);
backgroundWidget->eventMouseWheel += MyGUI::newDelegate(this, &ItemView::onMouseWheel);
// image
MyGUI::ImageBox* image = backgroundWidget->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
std::string::size_type pos = path.rfind(".");
if(pos != std::string::npos)
path.erase(pos);
path.append(".dds");
image->setImageTexture(path);
image->setNeedMouseFocus(false);
// text widget that shows item count // text widget that shows item count
MyGUI::TextBox* text = image->createWidget<MyGUI::TextBox>("SandBrightText", // TODO: move to ItemWidget
MyGUI::IntCoord(0, 14, 32, 18), MyGUI::Align::Default, std::string("Label")); MyGUI::TextBox* text = itemWidget->createWidget<MyGUI::TextBox>("SandBrightText",
MyGUI::IntCoord(5, 19, 32, 18), MyGUI::Align::Default, std::string("Label"));
text->setTextAlign(MyGUI::Align::Right); text->setTextAlign(MyGUI::Align::Right);
text->setNeedMouseFocus(false); text->setNeedMouseFocus(false);
text->setTextShadow(true); text->setTextShadow(true);

@ -0,0 +1,105 @@
#include "itemwidget.hpp"
#include <MyGUI_FactoryManager.h>
#include <MyGUI_ImageBox.h>
#include "../mwworld/class.hpp"
namespace MWGui
{
ItemWidget::ItemWidget()
: mItem(NULL)
{
}
void ItemWidget::registerComponents()
{
MyGUI::FactoryManager::getInstance().registerFactory<ItemWidget>("Widget");
}
void ItemWidget::initialiseOverride()
{
assignWidget(mItem, "Item");
if (mItem)
mItem->setNeedMouseFocus(false);
assignWidget(mFrame, "Frame");
if (mFrame)
mFrame->setNeedMouseFocus(false);
Base::initialiseOverride();
}
void ItemWidget::setIcon(const std::string &icon)
{
if (mItem)
mItem->setImageTexture(icon);
}
void ItemWidget::setFrame(const std::string &frame, const MyGUI::IntCoord &coord)
{
if (mFrame)
{
mFrame->setImageTexture(frame);
mFrame->setImageTile(MyGUI::IntSize(coord.width, coord.height)); // Why is this needed? MyGUI bug?
mFrame->setImageCoord(coord);
}
}
void ItemWidget::setIcon(const MWWorld::Ptr &ptr)
{
// image
std::string path = std::string("icons\\");
path += ptr.getClass().getInventoryIcon(ptr);
std::string::size_type pos = path.rfind(".");
if(pos != std::string::npos)
path.erase(pos);
path.append(".dds");
setIcon(path);
}
void ItemWidget::setItem(const MWWorld::Ptr &ptr, ItemState state)
{
if (!mItem)
return;
if (ptr.isEmpty())
{
if (mFrame)
mFrame->setImageTexture("");
mItem->setImageTexture("");
return;
}
bool isMagic = !ptr.getClass().getEnchantment(ptr).empty();
std::string backgroundTex = "textures\\menu_icon";
if (isMagic)
backgroundTex += "_magic";
if (state == None)
{
if (!isMagic)
backgroundTex = "";
}
else if (state == Equip)
{
backgroundTex += "_equip";
}
else if (state == Barter)
backgroundTex += "_barter";
if (backgroundTex != "")
backgroundTex += ".dds";
if (state == Barter && !isMagic)
setFrame(backgroundTex, MyGUI::IntCoord(2,2,42,42));
else
setFrame(backgroundTex, MyGUI::IntCoord(0,0,42,42));
setIcon(ptr);
}
}

@ -0,0 +1,49 @@
#ifndef OPENMW_MWGUI_ITEMWIDGET_H
#define OPENMW_MWGUI_ITEMWIDGET_H
#include <MyGUI_Widget.h>
namespace MWWorld
{
class Ptr;
}
namespace MWGui
{
/// @brief A widget that shows an icon for an MWWorld::Ptr
class ItemWidget : public MyGUI::Widget
{
MYGUI_RTTI_DERIVED(ItemWidget)
public:
ItemWidget();
/// Register needed components with MyGUI's factory manager
static void registerComponents ();
enum ItemState
{
None,
Equip,
Barter,
Magic
};
/// \a ptr may be empty
void setItem (const MWWorld::Ptr& ptr, ItemState state = None);
// Set icon and frame manually
void setIcon (const std::string& icon);
void setIcon (const MWWorld::Ptr& ptr);
void setFrame (const std::string& frame, const MyGUI::IntCoord& coord);
private:
virtual void initialiseOverride();
MyGUI::ImageBox* mItem;
MyGUI::ImageBox* mFrame;
};
}
#endif

@ -136,7 +136,7 @@ namespace MWGui
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups (); Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it) for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
{ {
Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "Splash_*.tga"); Ogre::StringVectorPtr resourcesInThisGroup = Ogre::ResourceGroupManager::getSingleton ().findResourceNames (*it, "Splash/*.tga");
mResources.insert(mResources.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); mResources.insert(mResources.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end());
} }
} }

@ -68,10 +68,10 @@ namespace MWGui
{ {
if (visible) if (visible)
updateMenu(); updateMenu();
else
showBackground( showBackground(
MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) && MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu) &&
MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame); MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_NoGame);
OEngine::GUI::Layout::setVisible (visible); OEngine::GUI::Layout::setVisible (visible);
} }
@ -167,7 +167,7 @@ namespace MWGui
mVideo = mVideoBackground->createWidget<VideoWidget>("ImageBox", 0,0,1,1, mVideo = mVideoBackground->createWidget<VideoWidget>("ImageBox", 0,0,1,1,
MyGUI::Align::Stretch, "Menu"); MyGUI::Align::Stretch, "Menu");
mVideo->playVideo("video\\menu_background.bik", false); mVideo->playVideo("video\\menu_background.bik");
} }
MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize(); MyGUI::IntSize viewSize = MyGUI::RenderManager::getInstance().getViewSize();
@ -204,7 +204,7 @@ namespace MWGui
if (!mVideo->update()) if (!mVideo->update())
{ {
// If finished playing, start again // If finished playing, start again
mVideo->playVideo("video\\menu_background.bik", 0); mVideo->playVideo("video\\menu_background.bik");
} }
} }
} }
@ -220,7 +220,6 @@ namespace MWGui
MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState(); MWBase::StateManager::State state = MWBase::Environment::get().getStateManager()->getState();
showBackground(state == MWBase::StateManager::State_NoGame);
mVersionText->setVisible(state == MWBase::StateManager::State_NoGame); mVersionText->setVisible(state == MWBase::StateManager::State_NoGame);
std::vector<std::string> buttons; std::vector<std::string> buttons;

@ -179,7 +179,6 @@ namespace MWGui
{ {
WindowModal::open(); WindowModal::open();
int fixedWidth = 500;
int textPadding = 10; // padding between text-widget and main-widget int textPadding = 10; // padding between text-widget and main-widget
int textButtonPadding = 20; // padding between the text-widget und the button-widget int textButtonPadding = 20; // padding between the text-widget und the button-widget
int buttonLeftPadding = 10; // padding between the buttons if horizontal int buttonLeftPadding = 10; // padding between the buttons if horizontal
@ -232,102 +231,52 @@ namespace MWGui
buttonsWidth += buttonLeftPadding; buttonsWidth += buttonLeftPadding;
MyGUI::IntSize mainWidgetSize; MyGUI::IntSize mainWidgetSize;
if(buttonsWidth < fixedWidth) // among each other
{ if(biggestButtonWidth > textSize.width) {
// on one line mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
if(textSize.width + 2*textPadding < buttonsWidth) }
{ else {
mainWidgetSize.width = buttonsWidth; mainWidgetSize.width = textSize.width + 3*textPadding;
}
else
{
mainWidgetSize.width = textSize.width + 3*textPadding;
}
mainWidgetSize.height = textSize.height + textButtonPadding + buttonHeight + buttonMainPadding;
MyGUI::IntPoint absPos;
absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setPosition(absPos);
mMainWidget->setSize(mainWidgetSize);
MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding;
mMessageWidget->setCoord(messageWidgetCoord);
mMessageWidget->setSize(textSize);
MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight);
int left = (mainWidgetSize.width - buttonsWidth)/2 + buttonPadding;
std::vector<MyGUI::Button*>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button)
{
buttonCord.left = left;
buttonCord.top = textSize.height + textButtonPadding;
buttonSize.width = (*button)->getTextSize().width + 2*buttonPadding;
buttonSize.height = (*button)->getTextSize().height + 2*buttonPadding;
(*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize);
left += buttonSize.width + buttonLeftPadding;
}
} }
else
{
// among each other
if(biggestButtonWidth > textSize.width) {
mainWidgetSize.width = biggestButtonWidth + buttonTopPadding;
}
else {
mainWidgetSize.width = textSize.width + 3*textPadding;
}
MyGUI::IntCoord buttonCord; MyGUI::IntCoord buttonCord;
MyGUI::IntSize buttonSize(0, buttonHeight); MyGUI::IntSize buttonSize(0, buttonHeight);
int top = textButtonPadding + buttonTopPadding + textSize.height; int top = textButtonPadding + buttonTopPadding + textSize.height;
std::vector<MyGUI::Button*>::const_iterator button; std::vector<MyGUI::Button*>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button) for(button = mButtons.begin(); button != mButtons.end(); ++button)
{ {
buttonSize.width = (*button)->getTextSize().width + buttonPadding*2; buttonSize.width = (*button)->getTextSize().width + buttonPadding*2;
buttonSize.height = (*button)->getTextSize().height + buttonPadding*2; buttonSize.height = (*button)->getTextSize().height + buttonPadding*2;
buttonCord.top = top; buttonCord.top = top;
buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/ buttonCord.left = (mainWidgetSize.width - buttonSize.width)/2 - 5; // FIXME: -5 is not so nice :/
(*button)->setCoord(buttonCord); (*button)->setCoord(buttonCord);
(*button)->setSize(buttonSize); (*button)->setSize(buttonSize);
top += buttonSize.height + 2*buttonTopPadding; top += buttonSize.height + 2*buttonTopPadding;
} }
mainWidgetSize.height = top + buttonMainPadding; mainWidgetSize.height = top + buttonMainPadding;
mMainWidget->setSize(mainWidgetSize); mMainWidget->setSize(mainWidgetSize);
MyGUI::IntPoint absPos; MyGUI::IntPoint absPos;
absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2; absPos.left = (gameWindowSize.width - mainWidgetSize.width)/2;
absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2; absPos.top = (gameWindowSize.height - mainWidgetSize.height)/2;
mMainWidget->setPosition(absPos); mMainWidget->setPosition(absPos);
MyGUI::IntCoord messageWidgetCoord; MyGUI::IntCoord messageWidgetCoord;
messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2; messageWidgetCoord.left = (mainWidgetSize.width - textSize.width)/2;
messageWidgetCoord.top = textPadding; messageWidgetCoord.top = textPadding;
messageWidgetCoord.width = textSize.width; messageWidgetCoord.width = textSize.width;
messageWidgetCoord.height = textSize.height; messageWidgetCoord.height = textSize.height;
mMessageWidget->setCoord(messageWidgetCoord); mMessageWidget->setCoord(messageWidgetCoord);
}
// Set key focus to "Ok" button // Set key focus to "Ok" button
std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}")); std::string ok = Misc::StringUtils::lowerCase(MyGUI::LanguageManager::getInstance().replaceTags("#{sOK}"));
std::vector<MyGUI::Button*>::const_iterator button;
for(button = mButtons.begin(); button != mButtons.end(); ++button) for(button = mButtons.begin(); button != mButtons.end(); ++button)
{ {
if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok)) if(Misc::StringUtils::ciEqual((*button)->getCaption(), ok))

@ -23,6 +23,9 @@
#include "spellwindow.hpp" #include "spellwindow.hpp"
#include "itemwidget.hpp"
#include "sortfilteritemmodel.hpp"
namespace MWGui namespace MWGui
{ {
@ -46,14 +49,16 @@ namespace MWGui
for (int i = 0; i < 10; ++i) for (int i = 0; i < 10; ++i)
{ {
MyGUI::Button* button; ItemWidget* button;
getWidget(button, "QuickKey" + boost::lexical_cast<std::string>(i+1)); getWidget(button, "QuickKey" + boost::lexical_cast<std::string>(i+1));
button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked); button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
unassign(button, i);
mQuickKeyButtons.push_back(button); mQuickKeyButtons.push_back(button);
mAssigned.push_back(Type_Unassigned);
unassign(button, i);
} }
} }
@ -77,12 +82,14 @@ namespace MWGui
delete mMagicSelectionDialog; delete mMagicSelectionDialog;
} }
void QuickKeysMenu::unassign(MyGUI::Widget* key, int index) void QuickKeysMenu::unassign(ItemWidget* key, int index)
{ {
while (key->getChildCount ()) key->clearUserStrings();
MyGUI::Gui::getInstance ().destroyWidget (key->getChildAt(0)); key->setItem(MWWorld::Ptr());
while (key->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(key->getChildAt(0));
key->setUserData(Type_Unassigned); mAssigned[index] = Type_Unassigned;
MyGUI::TextBox* textBox = key->createWidgetReal<MyGUI::TextBox>("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default); MyGUI::TextBox* textBox = key->createWidgetReal<MyGUI::TextBox>("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
textBox->setTextAlign (MyGUI::Align::Center); textBox->setTextAlign (MyGUI::Align::Center);
@ -128,6 +135,7 @@ namespace MWGui
} }
mItemSelectionDialog->setVisible(true); mItemSelectionDialog->setVisible(true);
mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr()); mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr());
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
mAssignDialog->setVisible (false); mAssignDialog->setVisible (false);
} }
@ -156,27 +164,16 @@ namespace MWGui
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item) void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
{ {
MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; assert (mSelectedIndex >= 0);
while (button->getChildCount ()) ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
button->setUserData(Type_Item);
mAssigned[mSelectedIndex] = Type_Item;
MyGUI::ImageBox* frame = button->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default);
std::string backgroundTex = "textures\\menu_icon_barter.dds"; button->setItem(item, ItemWidget::Barter);
frame->setImageTexture (backgroundTex); button->setUserString ("ToolTipType", "ItemPtr");
frame->setImageCoord (MyGUI::IntCoord(4, 4, 40, 40)); button->setUserData(item);
frame->setUserString ("ToolTipType", "ItemPtr");
frame->setUserData(item);
frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
MyGUI::ImageBox* image = frame->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\");
path += item.getClass().getInventoryIcon(item);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
image->setImageTexture (path);
image->setNeedMouseFocus (false);
if (mItemSelectionDialog) if (mItemSelectionDialog)
mItemSelectionDialog->setVisible(false); mItemSelectionDialog->setVisible(false);
@ -189,28 +186,18 @@ namespace MWGui
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
{ {
MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; assert (mSelectedIndex >= 0);
while (button->getChildCount ()) ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
button->setUserData(Type_MagicItem);
mAssigned[mSelectedIndex] = Type_MagicItem;
MyGUI::ImageBox* frame = button->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default);
std::string backgroundTex = "textures\\menu_icon_select_magic_magic.dds"; button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
frame->setImageTexture (backgroundTex); button->setIcon(item);
frame->setImageCoord (MyGUI::IntCoord(2, 2, 40, 40));
frame->setUserString ("ToolTipType", "ItemPtr"); button->setUserString ("ToolTipType", "ItemPtr");
frame->setUserData(item); button->setUserData(item);
frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
MyGUI::ImageBox* image = frame->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\");
path += item.getClass().getInventoryIcon(item);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
image->setImageTexture (path);
image->setNeedMouseFocus (false);
if (mMagicSelectionDialog) if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false); mMagicSelectionDialog->setVisible(false);
@ -218,21 +205,16 @@ namespace MWGui
void QuickKeysMenu::onAssignMagic (const std::string& spellId) void QuickKeysMenu::onAssignMagic (const std::string& spellId)
{ {
MyGUI::Button* button = mQuickKeyButtons[mSelectedIndex]; assert (mSelectedIndex >= 0);
while (button->getChildCount ()) ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
MyGUI::Gui::getInstance ().destroyWidget (button->getChildAt(0)); while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
button->setUserData(Type_Magic);
MyGUI::ImageBox* frame = button->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(9, 8, 42, 42), MyGUI::Align::Default); mAssigned[mSelectedIndex] = Type_Magic;
std::string backgroundTex = "textures\\menu_icon_select_magic.dds";
frame->setImageTexture (backgroundTex);
frame->setImageCoord (MyGUI::IntCoord(2, 2, 40, 40));
frame->setUserString ("ToolTipType", "Spell");
frame->setUserString ("Spell", spellId);
frame->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
MyGUI::ImageBox* image = frame->createWidget<MyGUI::ImageBox>("ImageBox", MyGUI::IntCoord(5, 5, 32, 32), MyGUI::Align::Default); button->setItem(MWWorld::Ptr());
button->setUserString ("ToolTipType", "Spell");
button->setUserString ("Spell", spellId);
const MWWorld::ESMStore &esmStore = const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
@ -251,8 +233,8 @@ namespace MWGui
path.erase(pos); path.erase(pos);
path.append(".dds"); path.append(".dds");
image->setImageTexture (path); button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
image->setNeedMouseFocus (false); button->setIcon(path);
if (mMagicSelectionDialog) if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false); mMagicSelectionDialog->setVisible(false);
@ -265,16 +247,17 @@ namespace MWGui
void QuickKeysMenu::activateQuickKey(int index) void QuickKeysMenu::activateQuickKey(int index)
{ {
MyGUI::Button* button = mQuickKeyButtons[index-1]; assert (index-1 >= 0);
ItemWidget* button = mQuickKeyButtons[index-1];
QuickKeyType type = *button->getUserData<QuickKeyType>(); QuickKeyType type = mAssigned[index-1];
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player); MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
if (type == Type_Item || type == Type_MagicItem) if (type == Type_Item || type == Type_MagicItem)
{ {
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
// make sure the item is available // make sure the item is available
if (item.getRefData ().getCount() < 1) if (item.getRefData ().getCount() < 1)
{ {
@ -286,7 +269,7 @@ namespace MWGui
if (Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), id)) if (Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), id))
{ {
item = *it; item = *it;
button->getChildAt(0)->setUserData(item); button->setUserData(item);
break; break;
} }
} }
@ -303,7 +286,7 @@ namespace MWGui
if (type == Type_Magic) if (type == Type_Magic)
{ {
std::string spellId = button->getChildAt(0)->getUserString("Spell"); std::string spellId = button->getUserString("Spell");
// Make sure the player still has this spell // Make sure the player still has this spell
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player); MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
@ -315,13 +298,13 @@ namespace MWGui
} }
else if (type == Type_Item) else if (type == Type_Item)
{ {
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item); MWBase::Environment::get().getWindowManager()->getInventoryWindow()->useItem(item);
} }
else if (type == Type_MagicItem) else if (type == Type_MagicItem)
{ {
MWWorld::Ptr item = *button->getChildAt (0)->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
// retrieve ContainerStoreIterator to the item // retrieve ContainerStoreIterator to the item
MWWorld::ContainerStoreIterator it = store.begin(); MWWorld::ContainerStoreIterator it = store.begin();
@ -403,9 +386,9 @@ namespace MWGui
for (int i=0; i<10; ++i) for (int i=0; i<10; ++i)
{ {
MyGUI::Button* button = mQuickKeyButtons[i]; ItemWidget* button = mQuickKeyButtons[i];
int type = *button->getUserData<QuickKeyType>(); int type = mAssigned[i];
ESM::QuickKeys::QuickKey key; ESM::QuickKeys::QuickKey key;
key.mType = type; key.mType = type;
@ -417,12 +400,12 @@ namespace MWGui
case Type_Item: case Type_Item:
case Type_MagicItem: case Type_MagicItem:
{ {
MWWorld::Ptr item = *button->getChildAt(0)->getUserData<MWWorld::Ptr>(); MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
key.mId = item.getCellRef().getRefId(); key.mId = item.getCellRef().getRefId();
break; break;
} }
case Type_Magic: case Type_Magic:
std::string spellId = button->getChildAt(0)->getUserString("Spell"); std::string spellId = button->getUserString("Spell");
key.mId = spellId; key.mId = spellId;
break; break;
} }
@ -452,7 +435,7 @@ namespace MWGui
mSelectedIndex = i; mSelectedIndex = i;
int keyType = it->mType; int keyType = it->mType;
std::string id = it->mId; std::string id = it->mId;
MyGUI::Button* button = mQuickKeyButtons[i]; ItemWidget* button = mQuickKeyButtons[i];
switch (keyType) switch (keyType)
{ {

@ -11,6 +11,7 @@ namespace MWGui
class QuickKeysMenuAssign; class QuickKeysMenuAssign;
class ItemSelectionDialog; class ItemSelectionDialog;
class MagicSelectionDialog; class MagicSelectionDialog;
class ItemWidget;
class QuickKeysMenu : public WindowBase class QuickKeysMenu : public WindowBase
{ {
@ -51,7 +52,8 @@ namespace MWGui
MyGUI::EditBox* mInstructionLabel; MyGUI::EditBox* mInstructionLabel;
MyGUI::Button* mOkButton; MyGUI::Button* mOkButton;
std::vector<MyGUI::Button*> mQuickKeyButtons; std::vector<ItemWidget*> mQuickKeyButtons;
std::vector<QuickKeyType> mAssigned;
QuickKeysMenuAssign* mAssignDialog; QuickKeysMenuAssign* mAssignDialog;
ItemSelectionDialog* mItemSelectionDialog; ItemSelectionDialog* mItemSelectionDialog;
@ -63,7 +65,7 @@ namespace MWGui
void onQuickKeyButtonClicked(MyGUI::Widget* sender); void onQuickKeyButtonClicked(MyGUI::Widget* sender);
void onOkButtonClicked(MyGUI::Widget* sender); void onOkButtonClicked(MyGUI::Widget* sender);
void unassign(MyGUI::Widget* key, int index); void unassign(ItemWidget* key, int index);
}; };
class QuickKeysMenuAssign : public WindowModal class QuickKeysMenuAssign : public WindowModal

@ -14,6 +14,7 @@
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "widgets.hpp" #include "widgets.hpp"
#include "itemwidget.hpp"
namespace MWGui namespace MWGui
{ {
@ -45,12 +46,7 @@ void Recharge::exit()
void Recharge::start (const MWWorld::Ptr &item) void Recharge::start (const MWWorld::Ptr &item)
{ {
std::string path = std::string("icons\\"); mGemIcon->setItem(item);
path += item.getClass().getInventoryIcon(item);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
mGemIcon->setImageTexture (path);
mGemIcon->setUserString("ToolTipType", "ItemPtr"); mGemIcon->setUserString("ToolTipType", "ItemPtr");
mGemIcon->setUserData(item); mGemIcon->setUserData(item);
@ -108,14 +104,9 @@ void Recharge::updateView()
text->setNeedMouseFocus(false); text->setNeedMouseFocus(false);
currentY += 19; currentY += 19;
MyGUI::ImageBox* icon = mView->createWidget<MyGUI::ImageBox> ( ItemWidget* icon = mView->createWidget<ItemWidget> (
"ImageBox", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default); "MW_ItemIconSmall", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\"); icon->setItem(*iter);
path += iter->getClass().getInventoryIcon(*iter);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
icon->setImageTexture (path);
icon->setUserString("ToolTipType", "ItemPtr"); icon->setUserString("ToolTipType", "ItemPtr");
icon->setUserData(*iter); icon->setUserData(*iter);
icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onItemClicked); icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Recharge::onItemClicked);

@ -8,6 +8,8 @@
namespace MWGui namespace MWGui
{ {
class ItemWidget;
class Recharge : public WindowBase class Recharge : public WindowBase
{ {
public: public:
@ -25,7 +27,7 @@ protected:
MyGUI::Widget* mGemBox; MyGUI::Widget* mGemBox;
MyGUI::ImageBox* mGemIcon; ItemWidget* mGemIcon;
MyGUI::TextBox* mChargeLabel; MyGUI::TextBox* mChargeLabel;

@ -13,6 +13,8 @@
#include "widgets.hpp" #include "widgets.hpp"
#include "itemwidget.hpp"
namespace MWGui namespace MWGui
{ {
@ -44,12 +46,7 @@ void Repair::startRepairItem(const MWWorld::Ptr &item)
{ {
mRepair.setTool(item); mRepair.setTool(item);
std::string path = std::string("icons\\"); mToolIcon->setItem(item);
path += item.getClass().getInventoryIcon(item);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
mToolIcon->setImageTexture (path);
mToolIcon->setUserString("ToolTipType", "ItemPtr"); mToolIcon->setUserString("ToolTipType", "ItemPtr");
mToolIcon->setUserData(item); mToolIcon->setUserData(item);
@ -113,14 +110,9 @@ void Repair::updateRepairView()
text->setNeedMouseFocus(false); text->setNeedMouseFocus(false);
currentY += 19; currentY += 19;
MyGUI::ImageBox* icon = mRepairView->createWidget<MyGUI::ImageBox> ( ItemWidget* icon = mRepairView->createWidget<ItemWidget> (
"ImageBox", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default); "MW_ItemIconSmall", MyGUI::IntCoord(16, currentY, 32, 32), MyGUI::Align::Default);
std::string path = std::string("icons\\"); icon->setItem(*iter);
path += iter->getClass().getInventoryIcon(*iter);
int pos = path.rfind(".");
path.erase(pos);
path.append(".dds");
icon->setImageTexture (path);
icon->setUserString("ToolTipType", "ItemPtr"); icon->setUserString("ToolTipType", "ItemPtr");
icon->setUserData(*iter); icon->setUserData(*iter);
icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onRepairItem); icon->eventMouseButtonClick += MyGUI::newDelegate(this, &Repair::onRepairItem);

@ -8,6 +8,8 @@
namespace MWGui namespace MWGui
{ {
class ItemWidget;
class Repair : public WindowBase class Repair : public WindowBase
{ {
public: public:
@ -25,7 +27,7 @@ protected:
MyGUI::Widget* mToolBox; MyGUI::Widget* mToolBox;
MyGUI::ImageBox* mToolIcon; ItemWidget* mToolIcon;
MyGUI::TextBox* mUsesLabel; MyGUI::TextBox* mUsesLabel;
MyGUI::TextBox* mQualityLabel; MyGUI::TextBox* mQualityLabel;

@ -30,11 +30,13 @@ namespace MWGui
getWidget(mInfoText, "InfoText"); getWidget(mInfoText, "InfoText");
getWidget(mOkButton, "OkButton"); getWidget(mOkButton, "OkButton");
getWidget(mCancelButton, "CancelButton"); getWidget(mCancelButton, "CancelButton");
getWidget(mDeleteButton, "DeleteButton");
getWidget(mSaveList, "SaveList"); getWidget(mSaveList, "SaveList");
getWidget(mSaveNameEdit, "SaveNameEdit"); getWidget(mSaveNameEdit, "SaveNameEdit");
getWidget(mSpacer, "Spacer"); getWidget(mSpacer, "Spacer");
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked); mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onOkButtonClicked);
mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked); mCancelButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onCancelButtonClicked);
mDeleteButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteButtonClicked);
mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected); mCharacterSelection->eventComboChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onCharacterSelected);
mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected); mSaveList->eventListChangePosition += MyGUI::newDelegate(this, &SaveGameDialog::onSlotSelected);
mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick); mSaveList->eventListMouseItemActivate += MyGUI::newDelegate(this, &SaveGameDialog::onSlotMouseClick);
@ -54,13 +56,16 @@ namespace MWGui
onSlotSelected(sender, pos); onSlotSelected(sender, pos);
if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed()) if (pos != MyGUI::ITEM_NONE && MyGUI::InputManager::getInstance().isShiftPressed())
{ confirmDeleteSave();
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog(); }
dialog->open("#{sMessage3}");
dialog->eventOkClicked.clear(); void SaveGameDialog::confirmDeleteSave()
dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed); {
dialog->eventCancelClicked.clear(); ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
} dialog->open("#{sMessage3}");
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SaveGameDialog::onDeleteSlotConfirmed);
dialog->eventCancelClicked.clear();
} }
void SaveGameDialog::onDeleteSlotConfirmed() void SaveGameDialog::onDeleteSlotConfirmed()
@ -107,6 +112,7 @@ namespace MWGui
mCurrentCharacter = NULL; mCurrentCharacter = NULL;
mCurrentSlot = NULL; mCurrentSlot = NULL;
mSaveList->removeAllItems(); mSaveList->removeAllItems();
onSlotSelected(mSaveList, MyGUI::ITEM_NONE);
MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager(); MWBase::StateManager* mgr = MWBase::Environment::get().getStateManager();
if (mgr->characterBegin() == mgr->characterEnd()) if (mgr->characterBegin() == mgr->characterEnd())
@ -157,6 +163,8 @@ namespace MWGui
} }
mCharacterSelection->setIndexSelected(selectedIndex); mCharacterSelection->setIndexSelected(selectedIndex);
if (selectedIndex == MyGUI::ITEM_NONE)
mCharacterSelection->setCaption("Select Character ...");
fillSaveList(); fillSaveList();
@ -175,6 +183,9 @@ namespace MWGui
mCharacterSelection->setVisible(load); mCharacterSelection->setVisible(load);
mSpacer->setUserString("Hidden", load ? "false" : "true"); mSpacer->setUserString("Hidden", load ? "false" : "true");
mDeleteButton->setUserString("Hidden", load ? "false" : "true");
mDeleteButton->setVisible(load);
if (!load) if (!load)
{ {
mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false); mCurrentCharacter = MWBase::Environment::get().getStateManager()->getCurrentCharacter (false);
@ -188,6 +199,12 @@ namespace MWGui
exit(); exit();
} }
void SaveGameDialog::onDeleteButtonClicked(MyGUI::Widget *sender)
{
if (mCurrentSlot)
confirmDeleteSave();
}
void SaveGameDialog::onConfirmationGiven() void SaveGameDialog::onConfirmationGiven()
{ {
accept(true); accept(true);
@ -225,10 +242,8 @@ namespace MWGui
} }
else else
{ {
if (mCurrentCharacter && mCurrentSlot) assert (mCurrentCharacter && mCurrentSlot);
{ MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot);
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot);
}
} }
} }
@ -278,6 +293,9 @@ namespace MWGui
void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos) void SaveGameDialog::onSlotSelected(MyGUI::ListBox *sender, size_t pos)
{ {
mOkButton->setEnabled(pos != MyGUI::ITEM_NONE || mSaving);
mDeleteButton->setEnabled(pos != MyGUI::ITEM_NONE);
if (pos == MyGUI::ITEM_NONE) if (pos == MyGUI::ITEM_NONE)
{ {
mCurrentSlot = NULL; mCurrentSlot = NULL;

@ -24,8 +24,11 @@ namespace MWGui
void setLoadOrSave(bool load); void setLoadOrSave(bool load);
private: private:
void confirmDeleteSave();
void onCancelButtonClicked (MyGUI::Widget* sender); void onCancelButtonClicked (MyGUI::Widget* sender);
void onOkButtonClicked (MyGUI::Widget* sender); void onOkButtonClicked (MyGUI::Widget* sender);
void onDeleteButtonClicked (MyGUI::Widget* sender);
void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos); void onCharacterSelected (MyGUI::ComboBox* sender, size_t pos);
// Slot selected (mouse click or arrow keys) // Slot selected (mouse click or arrow keys)
void onSlotSelected (MyGUI::ListBox* sender, size_t pos); void onSlotSelected (MyGUI::ListBox* sender, size_t pos);
@ -51,6 +54,7 @@ namespace MWGui
MyGUI::EditBox* mInfoText; MyGUI::EditBox* mInfoText;
MyGUI::Button* mOkButton; MyGUI::Button* mOkButton;
MyGUI::Button* mCancelButton; MyGUI::Button* mCancelButton;
MyGUI::Button* mDeleteButton;
MyGUI::ListBox* mSaveList; MyGUI::ListBox* mSaveList;
MyGUI::EditBox* mSaveNameEdit; MyGUI::EditBox* mSaveNameEdit;
MyGUI::Widget* mSpacer; MyGUI::Widget* mSpacer;

@ -14,6 +14,7 @@
#include <components/esm/loadweap.hpp> #include <components/esm/loadweap.hpp>
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/nullaction.hpp"
namespace namespace
{ {
@ -126,6 +127,10 @@ namespace MWGui
&& !base.get<ESM::Book>()->mBase->mData.mIsScroll) && !base.get<ESM::Book>()->mBase->mData.mIsScroll)
return false; return false;
if ((mFilter & Filter_OnlyUsableItems) && typeid(*base.getClass().use(base)) == typeid(MWWorld::NullAction)
&& base.getClass().getScript(base).empty())
return false;
return true; return true;
} }

@ -36,6 +36,7 @@ namespace MWGui
static const int Filter_OnlyEnchanted = (1<<1); static const int Filter_OnlyEnchanted = (1<<1);
static const int Filter_OnlyEnchantable = (1<<2); static const int Filter_OnlyEnchantable = (1<<2);
static const int Filter_OnlyChargedSoulstones = (1<<3); static const int Filter_OnlyChargedSoulstones = (1<<3);
static const int Filter_OnlyUsableItems = (1<<4); // Only items with a Use action
private: private:

@ -400,7 +400,7 @@ namespace MWGui
if (!info.effects.empty()) if (!info.effects.empty())
{ {
MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>("", MyGUI::Widget* effectArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>("",
MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), MyGUI::IntCoord(padding.left, totalSize.height, 300-padding.left, 300-totalSize.height),
MyGUI::Align::Stretch, "ToolTipEffectArea"); MyGUI::Align::Stretch, "ToolTipEffectArea");
MyGUI::IntCoord coord(0, 6, totalSize.width, 24); MyGUI::IntCoord coord(0, 6, totalSize.width, 24);
@ -419,7 +419,7 @@ namespace MWGui
{ {
assert(enchant); assert(enchant);
MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>("", MyGUI::Widget* enchantArea = mDynamicToolTipBox->createWidget<MyGUI::Widget>("",
MyGUI::IntCoord(0, totalSize.height, 300, 300-totalSize.height), MyGUI::IntCoord(padding.left, totalSize.height, 300-padding.left, 300-totalSize.height),
MyGUI::Align::Stretch, "ToolTipEnchantArea"); MyGUI::Align::Stretch, "ToolTipEnchantArea");
MyGUI::IntCoord coord(0, 6, totalSize.width, 24); MyGUI::IntCoord coord(0, 6, totalSize.width, 24);
@ -512,7 +512,11 @@ namespace MWGui
std::string ToolTips::toString(const float value) std::string ToolTips::toString(const float value)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << std::setprecision(3) << value;
if (value != int(value))
stream << std::setprecision(3);
stream << value;
return stream.str(); return stream.str();
} }

@ -345,7 +345,7 @@ namespace MWGui
x += abs(int(npcTerm - pcTerm)); x += abs(int(npcTerm - pcTerm));
int roll = std::rand()%100 + 1; int roll = std::rand()%100 + 1;
if(roll > x) //trade refused if(roll > x || (mCurrentMerchantOffer < 0) != (mCurrentBalance < 0)) //trade refused
{ {
MWBase::Environment::get().getWindowManager()-> MWBase::Environment::get().getWindowManager()->
messageBox("#{sNotifyMessage9}"); messageBox("#{sNotifyMessage9}");

@ -16,6 +16,24 @@
#include "tooltips.hpp" #include "tooltips.hpp"
namespace
{
// Sorts a container descending by skill value. If skill value is equal, sorts ascending by skill ID.
// pair <skill ID, skill value>
bool sortSkills (const std::pair<int, int>& left, const std::pair<int, int>& right)
{
if (left == right)
return false;
if (left.second > right.second)
return true;
else if (left.second < right.second)
return false;
return left.first < right.first;
}
}
namespace MWGui namespace MWGui
{ {
@ -52,29 +70,17 @@ namespace MWGui
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor); MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor);
// NPC can train you in his best 3 skills // NPC can train you in his best 3 skills
std::vector< std::pair<int, int> > bestSkills; std::vector< std::pair<int, int> > skills;
bestSkills.push_back (std::make_pair(-1, -1));
bestSkills.push_back (std::make_pair(-1, -1));
bestSkills.push_back (std::make_pair(-1, -1));
for (int i=0; i<ESM::Skill::Length; ++i) for (int i=0; i<ESM::Skill::Length; ++i)
{ {
int value = npcStats.getSkill (i).getBase (); int value = npcStats.getSkill (i).getBase ();
for (int j=0; j<3; ++j) skills.push_back(std::make_pair(i, value));
{
if (value > bestSkills[j].second)
{
if (j<2)
{
bestSkills[j+1] = bestSkills[j];
}
bestSkills[j] = std::make_pair(i, value);
break;
}
}
} }
std::sort(skills.begin(), skills.end(), sortSkills);
MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator (); MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator ();
MyGUI::Gui::getInstance ().destroyWidgets (widgets); MyGUI::Gui::getInstance ().destroyWidgets (widgets);
@ -86,20 +92,20 @@ namespace MWGui
for (int i=0; i<3; ++i) for (int i=0; i<3; ++i)
{ {
int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer int price = MWBase::Environment::get().getMechanicsManager()->getBarterOffer
(mPtr,pcStats.getSkill (bestSkills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true); (mPtr,pcStats.getSkill (skills[i].first).getBase() * gmst.find("iTrainingMod")->getInt (),true);
MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>("SandTextButton", MyGUI::Button* button = mTrainingOptions->createWidget<MyGUI::Button>("SandTextButton",
MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default); MyGUI::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default);
button->setEnabled(price <= playerGold); button->setEnabled(price <= playerGold);
button->setUserData(bestSkills[i].first); button->setUserData(skills[i].first);
button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected); button->eventMouseButtonClick += MyGUI::newDelegate(this, &TrainingWindow::onTrainingSelected);
button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[bestSkills[i].first] + "} - " + boost::lexical_cast<std::string>(price)); button->setCaptionWithReplacing("#{" + ESM::Skill::sSkillNameIds[skills[i].first] + "} - " + boost::lexical_cast<std::string>(price));
button->setSize(button->getTextSize ().width+12, button->getSize().height); button->setSize(button->getTextSize ().width+12, button->getSize().height);
ToolTips::createSkillToolTip (button, bestSkills[i].first); ToolTips::createSkillToolTip (button, skills[i].first);
} }
center(); center();

@ -4,17 +4,12 @@ namespace MWGui
{ {
VideoWidget::VideoWidget() VideoWidget::VideoWidget()
: mAllowSkipping(true)
{ {
eventKeyButtonPressed += MyGUI::newDelegate(this, &VideoWidget::onKeyPressed);
setNeedKeyFocus(true); setNeedKeyFocus(true);
} }
void VideoWidget::playVideo(const std::string &video, bool allowSkipping) void VideoWidget::playVideo(const std::string &video)
{ {
mAllowSkipping = allowSkipping;
mPlayer.playVideo(video); mPlayer.playVideo(video);
setImageTexture(mPlayer.getTextureName()); setImageTexture(mPlayer.getTextureName());
@ -30,16 +25,15 @@ int VideoWidget::getVideoHeight()
return mPlayer.getVideoHeight(); return mPlayer.getVideoHeight();
} }
void VideoWidget::onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
{
if (_key == MyGUI::KeyCode::Escape && mAllowSkipping)
mPlayer.stopVideo();
}
bool VideoWidget::update() bool VideoWidget::update()
{ {
mPlayer.update(); mPlayer.update();
return mPlayer.isPlaying(); return mPlayer.isPlaying();
} }
void VideoWidget::stop()
{
mPlayer.close();
}
} }

@ -9,7 +9,7 @@ namespace MWGui
{ {
/** /**
* Widget that plays a video. Can be skipped by pressing Esc. * Widget that plays a video.
*/ */
class VideoWidget : public MyGUI::ImageBox class VideoWidget : public MyGUI::ImageBox
{ {
@ -18,7 +18,7 @@ namespace MWGui
VideoWidget(); VideoWidget();
void playVideo (const std::string& video, bool allowSkipping); void playVideo (const std::string& video);
int getVideoWidth(); int getVideoWidth();
int getVideoHeight(); int getVideoHeight();
@ -26,12 +26,11 @@ namespace MWGui
/// @return Is the video still playing? /// @return Is the video still playing?
bool update(); bool update();
private: /// Stop video and free resources (done automatically on destruction)
bool mAllowSkipping; void stop();
private:
MWRender::VideoPlayer mPlayer; MWRender::VideoPlayer mPlayer;
void onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char);
}; };
} }

@ -64,6 +64,7 @@
#include "fontloader.hpp" #include "fontloader.hpp"
#include "videowidget.hpp" #include "videowidget.hpp"
#include "backgroundimage.hpp" #include "backgroundimage.hpp"
#include "itemwidget.hpp"
namespace MWGui namespace MWGui
{ {
@ -166,6 +167,7 @@ namespace MWGui
MyGUI::FactoryManager::getInstance().registerFactory<BackgroundImage>("Widget"); MyGUI::FactoryManager::getInstance().registerFactory<BackgroundImage>("Widget");
BookPage::registerMyGUIComponents (); BookPage::registerMyGUIComponents ();
ItemView::registerComponents(); ItemView::registerComponents();
ItemWidget::registerComponents();
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerRepeatClick>("Controller"); MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Controllers::ControllerRepeatClick>("Controller");
@ -200,8 +202,12 @@ namespace MWGui
MyGUI::Align::Default, "Overlay"); MyGUI::Align::Default, "Overlay");
mVideoBackground->setImageTexture("black.png"); mVideoBackground->setImageTexture("black.png");
mVideoBackground->setVisible(false); mVideoBackground->setVisible(false);
mVideoBackground->setNeedMouseFocus(true);
mVideoBackground->setNeedKeyFocus(true);
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default); mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
mVideoWidget->setNeedMouseFocus(true);
mVideoWidget->setNeedKeyFocus(true);
} }
void WindowManager::initUI() void WindowManager::initUI()
@ -261,7 +267,7 @@ namespace MWGui
mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager); mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
trackWindow(mCompanionWindow, "companion"); trackWindow(mCompanionWindow, "companion");
mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Default,"Windows",""); mInputBlocker = mGui->createWidget<MyGUI::Widget>("",0,0,w,h,MyGUI::Align::Default,"Windows");
mHud->setVisible(mHudEnabled); mHud->setVisible(mHudEnabled);
@ -1557,7 +1563,15 @@ namespace MWGui
void WindowManager::playVideo(const std::string &name, bool allowSkipping) void WindowManager::playVideo(const std::string &name, bool allowSkipping)
{ {
mVideoWidget->playVideo("video\\" + name, allowSkipping); mVideoWidget->playVideo("video\\" + name);
mVideoWidget->eventKeyButtonPressed.clear();
mVideoBackground->eventKeyButtonPressed.clear();
if (allowSkipping)
{
mVideoWidget->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed);
mVideoBackground->eventKeyButtonPressed += MyGUI::newDelegate(this, &WindowManager::onVideoKeyPressed);
}
// Turn off all rendering except for the GUI // Turn off all rendering except for the GUI
mRendering->getScene()->clearSpecialCaseRenderQueues(); mRendering->getScene()->clearSpecialCaseRenderQueues();
@ -1579,12 +1593,13 @@ namespace MWGui
bool cursorWasVisible = mCursorVisible; bool cursorWasVisible = mCursorVisible;
setCursorVisible(false); setCursorVisible(false);
while (mVideoWidget->update()) while (mVideoWidget->update() && !MWBase::Environment::get().getStateManager()->hasQuitRequest())
{ {
MWBase::Environment::get().getInputManager()->update(0, true, false); MWBase::Environment::get().getInputManager()->update(0, true, false);
mRendering->getWindow()->update(); mRendering->getWindow()->update();
} }
mVideoWidget->stop();
setCursorVisible(cursorWasVisible); setCursorVisible(cursorWasVisible);
@ -1625,4 +1640,33 @@ namespace MWGui
if(input == mCurrentModals.top()) if(input == mCurrentModals.top())
mCurrentModals.pop(); mCurrentModals.pop();
} }
void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
{
if (_key == MyGUI::KeyCode::Escape)
mVideoWidget->stop();
}
void WindowManager::pinWindow(GuiWindow window)
{
switch (window)
{
case GW_Inventory:
mInventoryWindow->setPinned(true);
break;
case GW_Map:
mMap->setPinned(true);
break;
case GW_Magic:
mSpellWindow->setPinned(true);
break;
case GW_Stats:
mStatsWindow->setPinned(true);
break;
default:
break;
}
updateVisible();
}
} }

@ -12,6 +12,9 @@
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
#include <MyGUI_KeyCode.h>
#include <MyGUI_Types.h>
namespace MyGUI namespace MyGUI
{ {
class Gui; class Gui;
@ -316,6 +319,8 @@ namespace MWGui
\param input Pointer to the current modal, to ensure proper modal is removed **/ \param input Pointer to the current modal, to ensure proper modal is removed **/
virtual void removeCurrentModal(WindowModal* input); virtual void removeCurrentModal(WindowModal* input);
virtual void pinWindow (MWGui::GuiWindow window);
private: private:
bool mConsoleOnlyScripts; bool mConsoleOnlyScripts;
@ -424,6 +429,9 @@ namespace MWGui
void onCursorChange(const std::string& name); void onCursorChange(const std::string& name);
void onKeyFocusChanged(MyGUI::Widget* widget); void onKeyFocusChanged(MyGUI::Widget* widget);
// Key pressed while playing a video
void onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char);
void sizeVideo(int screenWidth, int screenHeight); void sizeVideo(int screenWidth, int screenHeight);
}; };
} }

@ -25,6 +25,12 @@ namespace MWGui
onPinToggled(); onPinToggled();
} }
void WindowPinnableBase::setPinned(bool pinned)
{
if (pinned != mPinned)
onPinButtonClicked(mPinButton);
}
void WindowPinnableBase::setPinButtonVisible(bool visible) void WindowPinnableBase::setPinButtonVisible(bool visible)
{ {
mPinButton->setVisible(visible); mPinButton->setVisible(visible);

@ -12,6 +12,7 @@ namespace MWGui
public: public:
WindowPinnableBase(const std::string& parLayout); WindowPinnableBase(const std::string& parLayout);
bool pinned() { return mPinned; } bool pinned() { return mPinned; }
void setPinned (bool pinned);
void setPinButtonVisible(bool visible); void setPinButtonVisible(bool visible);
private: private:

@ -117,7 +117,8 @@ namespace MWInput
, mPreviewPOVDelay(0.f) , mPreviewPOVDelay(0.f)
, mTimeIdle(0.f) , mTimeIdle(0.f)
, mOverencumberedMessageDelay(0.f) , mOverencumberedMessageDelay(0.f)
, mAlwaysRunActive(false) , mAlwaysRunActive(Settings::Manager::getBool("always run", "Input"))
, mAttemptJump(false)
, mControlsDisabled(false) , mControlsDisabled(false)
{ {
@ -165,6 +166,21 @@ namespace MWInput
delete mInputManager; delete mInputManager;
} }
void InputManager::setPlayerControlsEnabled(bool enabled)
{
int nPlayerChannels = 17;
int playerChannels[] = {A_Activate, A_AutoMove, A_AlwaysRun, A_ToggleWeapon,
A_ToggleSpell, A_Rest, A_QuickKey1, A_QuickKey2,
A_QuickKey3, A_QuickKey4, A_QuickKey5, A_QuickKey6,
A_QuickKey7, A_QuickKey8, A_QuickKey9, A_QuickKey10,
A_Use};
for(int i = 0; i < nPlayerChannels; i++) {
int pc = playerChannels[i];
mInputBinder->getChannel(pc)->setEnabled(enabled);
}
}
void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue) void InputManager::channelChanged(ICS::Channel* channel, float currentValue, float previousValue)
{ {
if (mDragDrop) if (mDragDrop)
@ -179,6 +195,11 @@ namespace MWInput
mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).setAttackingOrSpell(currentValue); mPlayer->getPlayer().getClass().getCreatureStats(mPlayer->getPlayer()).setAttackingOrSpell(currentValue);
} }
if (action == A_Jump)
{
mAttemptJump = (currentValue == 1.0 && previousValue == 0.0);
}
if (currentValue == 1) if (currentValue == 1)
{ {
// trigger action activated // trigger action activated
@ -305,107 +326,107 @@ namespace MWInput
} }
// Disable movement in Gui mode // Disable movement in Gui mode
if (MWBase::Environment::get().getWindowManager()->isGuiMode() if (!(MWBase::Environment::get().getWindowManager()->isGuiMode()
|| MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running) || MWBase::Environment::get().getStateManager()->getState() != MWBase::StateManager::State_Running))
return;
// Configure player movement according to keyboard input. Actual movement will
// be done in the physics system.
if (mControlSwitch["playercontrols"])
{ {
bool triedToMove = false; // Configure player movement according to keyboard input. Actual movement will
if (actionIsActive(A_MoveLeft)) // be done in the physics system.
{ if (mControlSwitch["playercontrols"])
triedToMove = true;
mPlayer->setLeftRight (-1);
}
else if (actionIsActive(A_MoveRight))
{ {
triedToMove = true; bool triedToMove = false;
mPlayer->setLeftRight (1); if (actionIsActive(A_MoveLeft))
} {
triedToMove = true;
mPlayer->setLeftRight (-1);
}
else if (actionIsActive(A_MoveRight))
{
triedToMove = true;
mPlayer->setLeftRight (1);
}
if (actionIsActive(A_MoveForward)) if (actionIsActive(A_MoveForward))
{ {
triedToMove = true; triedToMove = true;
mPlayer->setAutoMove (false); mPlayer->setAutoMove (false);
mPlayer->setForwardBackward (1); mPlayer->setForwardBackward (1);
} }
else if (actionIsActive(A_MoveBackward)) else if (actionIsActive(A_MoveBackward))
{ {
triedToMove = true; triedToMove = true;
mPlayer->setAutoMove (false); mPlayer->setAutoMove (false);
mPlayer->setForwardBackward (-1); mPlayer->setForwardBackward (-1);
} }
else if(mPlayer->getAutoMove()) else if(mPlayer->getAutoMove())
{ {
triedToMove = true; triedToMove = true;
mPlayer->setForwardBackward (1); mPlayer->setForwardBackward (1);
} }
mPlayer->setSneak(actionIsActive(A_Sneak)); mPlayer->setSneak(actionIsActive(A_Sneak));
if (actionIsActive(A_Jump) && mControlSwitch["playerjumping"]) if (mAttemptJump && mControlSwitch["playerjumping"])
{ {
mPlayer->setUpDown (1); mPlayer->setUpDown (1);
triedToMove = true; triedToMove = true;
} }
if (mAlwaysRunActive) if (mAlwaysRunActive)
mPlayer->setRunState(!actionIsActive(A_Run)); mPlayer->setRunState(!actionIsActive(A_Run));
else else
mPlayer->setRunState(actionIsActive(A_Run)); mPlayer->setRunState(actionIsActive(A_Run));
// if player tried to start moving, but can't (due to being overencumbered), display a notification. // if player tried to start moving, but can't (due to being overencumbered), display a notification.
if (triedToMove) if (triedToMove)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
mOverencumberedMessageDelay -= dt;
if (player.getClass().getEncumbrance(player) >= player.getClass().getCapacity(player))
{ {
mPlayer->setAutoMove (false); MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
if (mOverencumberedMessageDelay <= 0) mOverencumberedMessageDelay -= dt;
if (player.getClass().getEncumbrance(player) >= player.getClass().getCapacity(player))
{ {
MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}"); mPlayer->setAutoMove (false);
mOverencumberedMessageDelay = 1.0; if (mOverencumberedMessageDelay <= 0)
{
MWBase::Environment::get().getWindowManager ()->messageBox("#{sNotifyMessage59}");
mOverencumberedMessageDelay = 1.0;
}
} }
} }
}
if (mControlSwitch["playerviewswitch"]) {
// work around preview mode toggle when pressing Alt+Tab if (mControlSwitch["playerviewswitch"]) {
if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) {
if (mPreviewPOVDelay <= 0.5 && // work around preview mode toggle when pressing Alt+Tab
(mPreviewPOVDelay += dt) > 0.5) if (actionIsActive(A_TogglePOV) && !mInputManager->isModifierHeld(SDL_Keymod(KMOD_ALT))) {
{ if (mPreviewPOVDelay <= 0.5 &&
mPreviewPOVDelay = 1.f; (mPreviewPOVDelay += dt) > 0.5)
MWBase::Environment::get().getWorld()->togglePreviewMode(true); {
} mPreviewPOVDelay = 1.f;
} else { MWBase::Environment::get().getWorld()->togglePreviewMode(true);
//disable preview mode }
MWBase::Environment::get().getWorld()->togglePreviewMode(false); } else {
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) { //disable preview mode
MWBase::Environment::get().getWorld()->togglePOV(); MWBase::Environment::get().getWorld()->togglePreviewMode(false);
if (mPreviewPOVDelay > 0.f && mPreviewPOVDelay <= 0.5) {
MWBase::Environment::get().getWorld()->togglePOV();
}
mPreviewPOVDelay = 0.f;
} }
mPreviewPOVDelay = 0.f;
} }
} }
if (actionIsActive(A_MoveForward) ||
actionIsActive(A_MoveBackward) ||
actionIsActive(A_MoveLeft) ||
actionIsActive(A_MoveRight) ||
actionIsActive(A_Jump) ||
actionIsActive(A_Sneak) ||
actionIsActive(A_TogglePOV))
{
resetIdleTime();
} else {
updateIdleTime(dt);
}
} }
if (actionIsActive(A_MoveForward) || mAttemptJump = false; // Can only jump on first frame input is on
actionIsActive(A_MoveBackward) ||
actionIsActive(A_MoveLeft) ||
actionIsActive(A_MoveRight) ||
actionIsActive(A_Jump) ||
actionIsActive(A_Sneak) ||
actionIsActive(A_TogglePOV))
{
resetIdleTime();
} else {
updateIdleTime(dt);
}
} }
void InputManager::setDragDrop(bool dragDrop) void InputManager::setDragDrop(bool dragDrop)
@ -517,13 +538,15 @@ namespace MWInput
} }
} }
if (!mControlsDisabled)
mInputBinder->keyPressed (arg);
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
if (kc != OIS::KC_UNASSIGNED) if (kc != OIS::KC_UNASSIGNED)
MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0); {
bool guiFocus = MyGUI::InputManager::getInstance().injectKeyPress(MyGUI::KeyCode::Enum(kc), 0);
setPlayerControlsEnabled(!guiFocus);
}
if (!mControlsDisabled)
mInputBinder->keyPressed (arg);
} }
void InputManager::textInput(const SDL_TextInputEvent &arg) void InputManager::textInput(const SDL_TextInputEvent &arg)
@ -536,36 +559,51 @@ namespace MWInput
void InputManager::keyReleased(const SDL_KeyboardEvent &arg ) void InputManager::keyReleased(const SDL_KeyboardEvent &arg )
{ {
mInputBinder->keyReleased (arg);
OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym); OIS::KeyCode kc = mInputManager->sdl2OISKeyCode(arg.keysym.sym);
MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)); setPlayerControlsEnabled(!MyGUI::InputManager::getInstance().injectKeyRelease(MyGUI::KeyCode::Enum(kc)));
mInputBinder->keyReleased (arg);
} }
void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id ) void InputManager::mousePressed( const SDL_MouseButtonEvent &arg, Uint8 id )
{ {
mInputBinder->mousePressed (arg, id); bool guiMode = false;
if (id != SDL_BUTTON_LEFT && id != SDL_BUTTON_RIGHT)
return; // MyGUI has no use for these events
MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)); if (id == SDL_BUTTON_LEFT || id == SDL_BUTTON_RIGHT) // MyGUI only uses these mouse events
if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0)
{ {
MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType<MyGUI::Button>(false); guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
if (b && b->getEnabled()) guiMode = MyGUI::InputManager::getInstance().injectMousePress(mMouseX, mMouseY, sdlButtonToMyGUI(id)) && guiMode;
if (MyGUI::InputManager::getInstance ().getMouseFocusWidget () != 0)
{ {
MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f); MyGUI::Button* b = MyGUI::InputManager::getInstance ().getMouseFocusWidget ()->castType<MyGUI::Button>(false);
if (b && b->getEnabled())
{
MWBase::Environment::get().getSoundManager ()->playSound ("Menu Click", 1.f, 1.f);
}
} }
} }
setPlayerControlsEnabled(!guiMode);
mInputBinder->mousePressed (arg, id);
} }
void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id ) void InputManager::mouseReleased( const SDL_MouseButtonEvent &arg, Uint8 id )
{ {
mInputBinder->mouseReleased (arg, id);
if(mInputBinder->detectingBindingState())
{
mInputBinder->mouseReleased (arg, id);
} else {
bool guiMode = MWBase::Environment::get().getWindowManager()->isGuiMode();
guiMode = MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)) && guiMode;
MyGUI::InputManager::getInstance().injectMouseRelease(mMouseX, mMouseY, sdlButtonToMyGUI(id)); if(mInputBinder->detectingBindingState()) return; // don't allow same mouseup to bind as initiated bind
setPlayerControlsEnabled(!guiMode);
mInputBinder->mouseReleased (arg, id);
}
} }
void InputManager::mouseMoved(const SFO::MouseMotionEvent &arg ) void InputManager::mouseMoved(const SFO::MouseMotionEvent &arg )
@ -769,8 +807,9 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny()) if (MyGUI::InputManager::getInstance ().isModalAny())
return; return;
// Toggle between game mode and journal mode if((!MWBase::Environment::get().getWindowManager()->isGuiMode()
if(!MWBase::Environment::get().getWindowManager()->isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed()) || MWBase::Environment::get().getWindowManager()->getMode() == MWGui::GM_Dialogue)
&& 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);
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal); MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Journal);
@ -779,7 +818,6 @@ namespace MWInput
{ {
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode(); MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
} }
// .. but don't touch any other mode.
} }
void InputManager::quickKey (int index) void InputManager::quickKey (int index)
@ -819,6 +857,8 @@ namespace MWInput
{ {
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return; if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;
mAlwaysRunActive = !mAlwaysRunActive; mAlwaysRunActive = !mAlwaysRunActive;
Settings::Manager::setBool("always run", "Input", mAlwaysRunActive);
} }
void InputManager::resetIdleTime() void InputManager::resetIdleTime()
@ -917,11 +957,6 @@ namespace MWInput
mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE); mInputBinder->addMouseButtonBinding (control, defaultMouseButtonBindings[i], ICS::Control::INCREASE);
} }
} }
// Printscreen key should not be allowed because it's captured by system screenshot function
// We check this explicitely here to fix up pre-0.26 config files. Can be removed after a few versions
if (mInputBinder->getKeyBinding(mInputBinder->getControl(A_Screenshot), ICS::Control::INCREASE) == SDLK_PRINTSCREEN)
mInputBinder->addKeyBinding(mInputBinder->getControl(A_Screenshot), SDLK_F12, ICS::Control::INCREASE);
} }
std::string InputManager::getActionDescription (int action) std::string InputManager::getActionDescription (int action)

@ -163,6 +163,7 @@ namespace MWInput
int mMouseWheel; int mMouseWheel;
bool mUserFileExists; bool mUserFileExists;
bool mAlwaysRunActive; bool mAlwaysRunActive;
bool mAttemptJump;
std::map<std::string, bool> mControlSwitch; std::map<std::string, bool> mControlSwitch;
@ -173,6 +174,8 @@ namespace MWInput
void resetIdleTime(); void resetIdleTime();
void updateIdleTime(float dt); void updateIdleTime(float dt);
void setPlayerControlsEnabled(bool enabled);
private: private:
void toggleMainMenu(); void toggleMainMenu();
void toggleSpell(); void toggleSpell();

@ -932,7 +932,7 @@ namespace MWMechanics
for (std::list<MWWorld::Ptr>::iterator it = listGuards.begin(); it != listGuards.end(); ++it) for (std::list<MWWorld::Ptr>::iterator it = listGuards.begin(); it != listGuards.end(); ++it)
{ {
engageCombat(iter->first, *it, false); engageCombat(iter->first, *it, *it == player);
} }
} }
@ -1088,10 +1088,10 @@ namespace MWMechanics
CreatureStats& stats = ptr.getClass().getCreatureStats(ptr); CreatureStats& stats = ptr.getClass().getCreatureStats(ptr);
float healthHours = healthPerHour >= 0 float healthHours = healthPerHour > 0
? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour ? (stats.getHealth().getModified() - stats.getHealth().getCurrent()) / healthPerHour
: 1.0f; : 1.0f;
float magickaHours = magickaPerHour >= 0 float magickaHours = magickaPerHour > 0
? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour ? (stats.getMagicka().getModified() - stats.getMagicka().getCurrent()) / magickaPerHour
: 1.0f; : 1.0f;

@ -395,7 +395,8 @@ int MWMechanics::Alchemy::addIngredient (const MWWorld::Ptr& ingredient)
return -1; return -1;
for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter) for (TIngredientsIterator iter (mIngredients.begin()); iter!=mIngredients.end(); ++iter)
if (!iter->isEmpty() && ingredient.get<ESM::Ingredient>()==iter->get<ESM::Ingredient>()) if (!iter->isEmpty() && Misc::StringUtils::ciEqual(ingredient.getClass().getId(ingredient),
iter->getClass().getId(*iter)))
return -1; return -1;
mIngredients[slot] = ingredient; mIngredients[slot] = ingredient;

@ -409,6 +409,13 @@ MWWorld::ContainerStoreIterator getActiveWeapon(CreatureStats &stats, MWWorld::I
void CharacterController::playDeath(float startpoint, CharacterState death) void CharacterController::playDeath(float startpoint, CharacterState death)
{ {
if (mPtr == MWBase::Environment::get().getWorld()->getPlayerPtr())
{
// The first-person animations do not include death, so we need to
// force-switch to third person before playing the death animation.
MWBase::Environment::get().getWorld()->useDeathCamera();
}
switch (death) switch (death)
{ {
case CharState_SwimDeath: case CharState_SwimDeath:
@ -825,6 +832,8 @@ bool CharacterController::updateWeaponState()
MWRender::Animation::Group_UpperBody, false, MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" max attack", mAttackType+" min hit", weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
1.0f-complete, 0); 1.0f-complete, 0);
complete = 0.f;
mUpperBodyState = UpperCharState_MaxAttackToMinHit; mUpperBodyState = UpperCharState_MaxAttackToMinHit;
} }
else if (mHitState == CharState_KnockDown) else if (mHitState == CharState_KnockDown)
@ -968,7 +977,8 @@ bool CharacterController::updateWeaponState()
} }
//if playing combat animation and lowerbody is not busy switch to whole body animation //if playing combat animation and lowerbody is not busy switch to whole body animation
if((weaptype != WeapType_None || UpperCharState_UnEquipingWeap) && animPlaying) if((weaptype != WeapType_None || mUpperBodyState == UpperCharState_UnEquipingWeap
|| mUpperBodyState == UpperCharState_EquipingWeap) && animPlaying)
{ {
if( mMovementState != CharState_None || if( mMovementState != CharState_None ||
mJumpState != JumpState_None || mJumpState != JumpState_None ||

@ -73,9 +73,16 @@ namespace MWMechanics
return false; return false;
MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker); MWMechanics::CreatureStats& blockerStats = blocker.getClass().getCreatureStats(blocker);
// Don't block when in spellcasting state (shield is equipped, but not visible)
if (blockerStats.getDrawState() == DrawState_Spell) if (blockerStats.getDrawState() == DrawState_Spell)
return false; return false;
// Don't block when in hand-to-hand combat (shield is equipped, but not visible)
if (blockerStats.getDrawState() == DrawState_Weapon &&
inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight) == inv.end())
return false;
MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker); MWMechanics::CreatureStats& attackerStats = attacker.getClass().getCreatureStats(attacker);
float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified() float blockTerm = blocker.getClass().getSkill(blocker, ESM::Skill::Block) + 0.2 * blockerStats.getAttribute(ESM::Attribute::Agility).getModified()

@ -929,7 +929,10 @@ namespace MWMechanics
else if (type == OT_Murder) else if (type == OT_Murder)
arg = store.find("iCrimeKilling")->getInt(); arg = store.find("iCrimeKilling")->getInt();
else if (type == OT_Theft) else if (type == OT_Theft)
{
arg *= store.find("fCrimeStealing")->getFloat(); arg *= store.find("fCrimeStealing")->getFloat();
arg = std::max(1, arg); // Minimum bounty of 1, in case items with zero value are stolen
}
MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sCrimeMessage}");
ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty() ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()

@ -356,28 +356,40 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScene
objlist->mControllers.push_back(Ogre::Controller<Ogre::Real>(src, dest, func)); objlist->mControllers.push_back(Ogre::Controller<Ogre::Real>(src, dest, func));
bool interior = !(mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior()); bool interior = !(mPtr.isInCell() && mPtr.getCell()->getCell()->isExterior());
bool quadratic = fallback->getFallbackBool("LightAttenuation_OutQuadInLin") ?
!interior : fallback->getFallbackBool("LightAttenuation_UseQuadratic"); static bool outQuadInLin = fallback->getFallbackBool("LightAttenuation_OutQuadInLin");
static bool useQuadratic = fallback->getFallbackBool("LightAttenuation_UseQuadratic");
static float quadraticValue = fallback->getFallbackFloat("LightAttenuation_QuadraticValue");
static float quadraticRadiusMult = fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
static bool useLinear = fallback->getFallbackBool("LightAttenuation_UseLinear");
static float linearRadiusMult = fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult");
static float linearValue = fallback->getFallbackFloat("LightAttenuation_LinearValue");
bool quadratic = useQuadratic && (!outQuadInLin || !interior);
// with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero, // with the standard 1 / (c + d*l + d*d*q) equation the attenuation factor never becomes zero,
// so we ignore lights if their attenuation falls below this factor. // so we ignore lights if their attenuation falls below this factor.
const float threshold = 0.03; const float threshold = 0.03;
if (!quadratic) float quadraticAttenuation = 0;
float linearAttenuation = 0;
float activationRange = 0;
if (quadratic)
{ {
float r = radius * fallback->getFallbackFloat("LightAttenuation_LinearRadiusMult"); float r = radius * quadraticRadiusMult;
float attenuation = fallback->getFallbackFloat("LightAttenuation_LinearValue") / r; quadraticAttenuation = quadraticValue / std::pow(r, 2);
float activationRange = 1.0f / (threshold * attenuation); activationRange = std::sqrt(1.0f / (threshold * quadraticAttenuation));
olight->setAttenuation(activationRange, 0, attenuation, 0);
} }
else if (useLinear)
{ {
float r = radius * fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult"); float r = radius * linearRadiusMult;
float attenuation = fallback->getFallbackFloat("LightAttenuation_QuadraticValue") / std::pow(r, 2); linearAttenuation = linearValue / r;
float activationRange = std::sqrt(1.0f / (threshold * attenuation)); activationRange = std::max(activationRange, 1.0f / (threshold * linearAttenuation));
olight->setAttenuation(activationRange, 0, 0, attenuation);
} }
olight->setAttenuation(activationRange, 0, linearAttenuation, quadraticAttenuation);
// If there's an AttachLight bone, attach the light to that, otherwise put it in the center, // If there's an AttachLight bone, attach the light to that, otherwise put it in the center,
if(objlist->mSkelBase && objlist->mSkelBase->getSkeleton()->hasBone("AttachLight")) if(objlist->mSkelBase && objlist->mSkelBase->getSkeleton()->hasBone("AttachLight"))
objlist->mSkelBase->attachObjectToBone("AttachLight", olight); objlist->mSkelBase->attachObjectToBone("AttachLight", olight);

@ -140,11 +140,11 @@ namespace MWRender
} }
} }
void Camera::toggleViewMode() void Camera::toggleViewMode(bool force)
{ {
// Changing the view will stop all playing animations, so if we are playing // Changing the view will stop all playing animations, so if we are playing
// anything important, queue the view change for later // anything important, queue the view change for later
if (!mAnimation->allowSwitchViewMode()) if (!mAnimation->allowSwitchViewMode() && !force)
{ {
mViewModeToggleQueued = true; mViewModeToggleQueued = true;
return; return;

@ -75,7 +75,8 @@ namespace MWRender
/// Attach camera to object /// Attach camera to object
void attachTo(const MWWorld::Ptr &); void attachTo(const MWWorld::Ptr &);
void toggleViewMode(); /// @param Force view mode switch, even if currently not allowed by the animation.
void toggleViewMode(bool force=false);
bool toggleVanityMode(bool enable); bool toggleVanityMode(bool enable);
void allowVanityMode(bool allow); void allowVanityMode(bool allow);

@ -308,7 +308,7 @@ void NpcAnimation::updateParts()
ESM::PartReferenceType parts[] = { ESM::PartReferenceType parts[] = {
ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg, ESM::PRT_Groin, ESM::PRT_Skirt, ESM::PRT_RLeg, ESM::PRT_LLeg,
ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee, ESM::PRT_RUpperarm, ESM::PRT_LUpperarm, ESM::PRT_RKnee, ESM::PRT_LKnee,
ESM::PRT_RForearm, ESM::PRT_LForearm, ESM::PRT_RPauldron, ESM::PRT_LPauldron ESM::PRT_RForearm, ESM::PRT_LForearm
}; };
size_t parts_size = sizeof(parts)/sizeof(parts[0]); size_t parts_size = sizeof(parts)/sizeof(parts[0]);
for(size_t p = 0;p < parts_size;++p) for(size_t p = 0;p < parts_size;++p)
@ -351,8 +351,6 @@ void NpcAnimation::updateParts()
// Remember body parts so we only have to search through the store once for each race/gender/viewmode combination // Remember body parts so we only have to search through the store once for each race/gender/viewmode combination
static std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > sRaceMapping; static std::map< std::pair<std::string,int>,std::vector<const ESM::BodyPart*> > sRaceMapping;
static std::map <std::pair<std::string,int>, std::vector<const ESM::BodyPart*> > sVampireMapping;
static const int Flag_Female = 1<<0; static const int Flag_Female = 1<<0;
static const int Flag_FirstPerson = 1<<1; static const int Flag_FirstPerson = 1<<1;
@ -408,8 +406,6 @@ void NpcAnimation::updateParts()
if (bodypart.mData.mType != ESM::BodyPart::MT_Skin) if (bodypart.mData.mType != ESM::BodyPart::MT_Skin)
continue; continue;
if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
continue;
if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace)) if (!Misc::StringUtils::ciEqual(bodypart.mRace, mNpc->mRace))
continue; continue;
@ -435,6 +431,20 @@ void NpcAnimation::updateParts()
} }
continue; continue;
} }
if (!mNpc->isMale() != (bodypart.mData.mFlags & ESM::BodyPart::BPF_Female))
{
// Allow opposite gender's parts as fallback if parts for our gender are missing
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{
if(!parts[bIt->second])
parts[bIt->second] = &*it;
++bIt;
}
continue;
}
BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart)); BodyPartMapType::const_iterator bIt = sBodyPartMap.lower_bound(BodyPartMapType::key_type(bodypart.mData.mPart));
while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart) while(bIt != sBodyPartMap.end() && bIt->first == bodypart.mData.mPart)
{ {

@ -73,7 +73,7 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr)
ptr.getRefData().setBaseNode(insert); ptr.getRefData().setBaseNode(insert);
} }
void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh) void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh, bool batch)
{ {
insertBegin(ptr); insertBegin(ptr);
@ -99,7 +99,7 @@ void Objects::insertModel(const MWWorld::Ptr &ptr, const std::string &mesh)
mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL; mBounds[ptr.getCell()] = Ogre::AxisAlignedBox::BOX_NULL;
mBounds[ptr.getCell()].merge(bounds); mBounds[ptr.getCell()].merge(bounds);
if(ptr.getTypeName() == typeid(ESM::Static).name() && if(batch &&
Settings::Manager::getBool("use static geometry", "Objects") && Settings::Manager::getBool("use static geometry", "Objects") &&
anim->canBatch()) anim->canBatch())
{ {

@ -41,7 +41,7 @@ public:
, mRootNode(NULL) , mRootNode(NULL)
{} {}
~Objects(){} ~Objects(){}
void insertModel(const MWWorld::Ptr& ptr, const std::string &model); void insertModel(const MWWorld::Ptr& ptr, const std::string &model, bool batch=false);
ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr); ObjectAnimation* getAnimation(const MWWorld::Ptr &ptr);

@ -235,6 +235,18 @@ namespace MWScript
} }
}; };
template <class R>
class OpClearInfoActor : public Interpreter::Opcode0
{
public:
virtual void execute (Interpreter::Runtime& runtime)
{
MWWorld::Ptr ptr = R()(runtime);
MWBase::Environment::get().getDialogueManager()->clearInfoActor(ptr);
}
};
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
@ -256,6 +268,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>); interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction); interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor<ExplicitRef>);
} }
} }

@ -57,7 +57,8 @@ op 0x20028: RemoveSoulGem, explicit reference
op 0x20029: PCRaiseRank, explicit reference op 0x20029: PCRaiseRank, explicit reference
op 0x2002a: PCLowerRank, explicit reference op 0x2002a: PCLowerRank, explicit reference
op 0x2002b: PCJoinFaction, explicit reference op 0x2002b: PCJoinFaction, explicit reference
opcodes 0x2002c-0x3ffff unused op 0x2002c: MenuTest
opcodes 0x2002d-0x3ffff unused
Segment 4: Segment 4:
(not implemented yet) (not implemented yet)
@ -393,5 +394,7 @@ op 0x2000241: onKnockoutExplicit
op 0x2000242: ModFactionReaction op 0x2000242: ModFactionReaction
op 0x2000243: GetFactionReaction op 0x2000243: GetFactionReaction
op 0x2000244: Activate, explicit op 0x2000244: Activate, explicit
op 0x2000245: ClearInfoActor
op 0x2000246: ClearInfoActor, explicit
opcodes 0x2000245-0x3ffffff unused opcodes 0x2000247-0x3ffffff unused

@ -162,6 +162,47 @@ namespace MWScript
} }
}; };
class OpMenuTest : public Interpreter::Opcode1
{
public:
virtual void execute (Interpreter::Runtime& runtime, unsigned int arg0)
{
int arg=0;
if(arg0>0)
{
arg = runtime[0].mInteger;
runtime.pop();
}
if (arg == 0)
{
MWGui::GuiMode modes[] = { MWGui::GM_Inventory, MWGui::GM_Container };
for (int i=0; i<2; ++i)
{
if (MWBase::Environment::get().getWindowManager()->containsMode(modes[i]))
MWBase::Environment::get().getWindowManager()->removeGuiMode(modes[i]);
}
}
else
{
MWGui::GuiWindow gw = MWGui::GW_None;
if (arg == 3)
gw = MWGui::GW_Stats;
if (arg == 4)
gw = MWGui::GW_Inventory;
if (arg == 5)
gw = MWGui::GW_Magic;
if (arg == 6)
gw = MWGui::GW_Map;
MWBase::Environment::get().getWindowManager()->pinWindow(gw);
}
}
};
void installOpcodes (Interpreter::Interpreter& interpreter) void installOpcodes (Interpreter::Interpreter& interpreter)
{ {
@ -200,6 +241,7 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap); interpreter.installSegment5 (Compiler::Gui::opcodeShowMap, new OpShowMap);
interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap); interpreter.installSegment5 (Compiler::Gui::opcodeFillMap, new OpFillMap);
interpreter.installSegment3 (Compiler::Gui::opcodeMenuTest, new OpMenuTest);
} }
} }
} }

@ -54,9 +54,28 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile)
Slot slot; Slot slot;
std::ostringstream stream; std::ostringstream stream;
stream << mNext++;
// The profile description is user-supplied, so we need to escape the path
for (std::string::const_iterator it = profile.mDescription.begin(); it != profile.mDescription.end(); ++it)
{
if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters
stream << *it;
else
stream << "_";
}
slot.mPath = mPath / stream.str(); slot.mPath = mPath / stream.str();
// Append an index if necessary to ensure a unique file
int i=0;
while (boost::filesystem::exists(slot.mPath))
{
std::ostringstream test;
test << stream.str();
test << " - " << ++i;
slot.mPath = mPath / test.str();
}
slot.mProfile = profile; slot.mProfile = profile;
slot.mTimeStamp = std::time (0); slot.mTimeStamp = std::time (0);
@ -64,7 +83,7 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile)
} }
MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game) MWState::Character::Character (const boost::filesystem::path& saves, const std::string& game)
: mPath (saves), mNext (0) : mPath (saves)
{ {
if (!boost::filesystem::is_directory (mPath)) if (!boost::filesystem::is_directory (mPath))
{ {
@ -82,13 +101,6 @@ MWState::Character::Character (const boost::filesystem::path& saves, const std::
addSlot (slotPath, game); addSlot (slotPath, game);
} }
catch (...) {} // ignoring bad saved game files for now catch (...) {} // ignoring bad saved game files for now
std::istringstream stream (slotPath.filename().string());
int index = 0;
if ((stream >> index) && index>=mNext)
mNext = index+1;
} }
std::sort (mSlots.begin(), mSlots.end()); std::sort (mSlots.begin(), mSlots.end());

@ -26,7 +26,6 @@ namespace MWState
boost::filesystem::path mPath; boost::filesystem::path mPath;
std::vector<Slot> mSlots; std::vector<Slot> mSlots;
int mNext;
void addSlot (const boost::filesystem::path& path, const std::string& game); void addSlot (const boost::filesystem::path& path, const std::string& game);

@ -8,7 +8,7 @@
MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves, MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves,
const std::string& game) const std::string& game)
: mPath (saves), mNext (0), mCurrent (0), mGame (game) : mPath (saves), mCurrent (0), mGame (game)
{ {
if (!boost::filesystem::is_directory (mPath)) if (!boost::filesystem::is_directory (mPath))
{ {
@ -28,21 +28,14 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save
if (character.begin()!=character.end()) if (character.begin()!=character.end())
mCharacters.push_back (character); mCharacters.push_back (character);
} }
std::istringstream stream (characterDir.filename().string());
int index = 0;
if ((stream >> index) && index>=mNext)
mNext = index+1;
} }
} }
} }
MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create) MWState::Character *MWState::CharacterManager::getCurrentCharacter (bool create, const std::string& name)
{ {
if (!mCurrent && create) if (!mCurrent && create)
createCharacter(); createCharacter(name);
return mCurrent; return mCurrent;
} }
@ -63,13 +56,31 @@ void MWState::CharacterManager::deleteSlot(const MWState::Character *character,
} }
} }
void MWState::CharacterManager::createCharacter() void MWState::CharacterManager::createCharacter(const std::string& name)
{ {
std::ostringstream stream; std::ostringstream stream;
stream << mNext++;
// The character name is user-supplied, so we need to escape the path
for (std::string::const_iterator it = name.begin(); it != name.end(); ++it)
{
if (std::isalnum(*it)) // Ignores multibyte characters and non alphanumeric characters
stream << *it;
else
stream << "_";
}
boost::filesystem::path path = mPath / stream.str(); boost::filesystem::path path = mPath / stream.str();
// Append an index if necessary to ensure a unique directory
int i=0;
while (boost::filesystem::exists(path))
{
std::ostringstream test;
test << stream.str();
test << " - " << ++i;
path = mPath / test.str();
}
mCharacters.push_back (Character (path, mGame)); mCharacters.push_back (Character (path, mGame));
mCurrent = &mCharacters.back(); mCurrent = &mCharacters.back();

@ -10,7 +10,6 @@ namespace MWState
class CharacterManager class CharacterManager
{ {
boost::filesystem::path mPath; boost::filesystem::path mPath;
int mNext;
// Uses std::list, so that mCurrent stays valid when characters are deleted // Uses std::list, so that mCurrent stays valid when characters are deleted
std::list<Character> mCharacters; std::list<Character> mCharacters;
@ -32,13 +31,15 @@ namespace MWState
CharacterManager (const boost::filesystem::path& saves, const std::string& game); CharacterManager (const boost::filesystem::path& saves, const std::string& game);
Character *getCurrentCharacter (bool create = true); Character *getCurrentCharacter (bool create, const std::string& name);
///< \param create Create a new character, if there is no current character. ///< \param create Create a new character, if there is no current character.
/// \param name The character name to use in case a new character is created.
void deleteSlot(const MWState::Character *character, const MWState::Slot *slot); void deleteSlot(const MWState::Character *character, const MWState::Slot *slot);
void createCharacter(); void createCharacter(const std::string& name);
///< Create new character within saved game management ///< Create new character within saved game management
/// \param name Name for the character (does not need to be unique)
void setCurrentCharacter (const Character *character); void setCurrentCharacter (const Character *character);

@ -144,7 +144,6 @@ void MWState::StateManager::newGame (bool bypass)
void MWState::StateManager::endGame() void MWState::StateManager::endGame()
{ {
mState = State_Ended; mState = State_Ended;
MWBase::Environment::get().getWorld()->useDeathCamera();
} }
void MWState::StateManager::saveGame (const std::string& description, const Slot *slot) void MWState::StateManager::saveGame (const std::string& description, const Slot *slot)
@ -185,9 +184,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
encoded->read(&profile.mScreenshot[0], encoded->size()); encoded->read(&profile.mScreenshot[0], encoded->size());
if (!slot) if (!slot)
slot = mCharacterManager.getCurrentCharacter()->createSlot (profile); slot = getCurrentCharacter()->createSlot (profile);
else else
slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile); slot = getCurrentCharacter()->updateSlot (slot, profile);
boost::filesystem::ofstream stream (slot->mPath, std::ios::binary); boost::filesystem::ofstream stream (slot->mPath, std::ios::binary);
@ -234,6 +233,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
writer.close(); writer.close();
if (stream.fail())
throw std::runtime_error("Write operation failed");
Settings::Manager::setString ("character", "Saves", Settings::Manager::setString ("character", "Saves",
slot->mPath.parent_path().filename().string()); slot->mPath.parent_path().filename().string());
} }
@ -247,6 +249,10 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
std::vector<std::string> buttons; std::vector<std::string> buttons;
buttons.push_back("#{sOk}"); buttons.push_back("#{sOk}");
MWBase::Environment::get().getWindowManager()->messageBox(error.str(), buttons); MWBase::Environment::get().getWindowManager()->messageBox(error.str(), buttons);
// If no file was written, clean up the slot
if (slot && !boost::filesystem::exists(slot->mPath))
getCurrentCharacter()->deleteSlot(slot);
} }
} }
@ -413,7 +419,10 @@ void MWState::StateManager::deleteGame(const MWState::Character *character, cons
MWState::Character *MWState::StateManager::getCurrentCharacter (bool create) MWState::Character *MWState::StateManager::getCurrentCharacter (bool create)
{ {
return mCharacterManager.getCurrentCharacter (create); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
std::string name = player.getClass().getName(player);
return mCharacterManager.getCurrentCharacter (create, name);
} }
MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin() MWState::StateManager::CharacterIterator MWState::StateManager::characterBegin()
@ -434,11 +443,12 @@ void MWState::StateManager::update (float duration)
if (mAskLoadRecent) if (mAskLoadRecent)
{ {
int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton(); int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if(iButton==0) MWState::Character *curCharacter = getCurrentCharacter(false);
if(iButton==0 && curCharacter)
{ {
mAskLoadRecent = false; mAskLoadRecent = false;
//Load last saved game for current character //Load last saved game for current character
MWState::Character *curCharacter = getCurrentCharacter();
MWState::Slot lastSave = *curCharacter->begin(); MWState::Slot lastSave = *curCharacter->begin();
loadGame(curCharacter, &lastSave); loadGame(curCharacter, &lastSave);
} }

@ -41,6 +41,8 @@ namespace MWWorld
// slots that this item can be equipped in // slots that this item can be equipped in
std::pair<std::vector<int>, bool> slots_ = getTarget().getClass().getEquipmentSlots(getTarget()); std::pair<std::vector<int>, bool> slots_ = getTarget().getClass().getEquipmentSlots(getTarget());
if (slots_.first.empty())
return;
// retrieve ContainerStoreIterator to the item // retrieve ContainerStoreIterator to the item
MWWorld::ContainerStoreIterator it = invStore.begin(); MWWorld::ContainerStoreIterator it = invStore.begin();
@ -55,25 +57,32 @@ namespace MWWorld
assert(it != invStore.end()); assert(it != invStore.end());
// equip the item in the first free slot // equip the item in the first free slot
for (std::vector<int>::const_iterator slot=slots_.first.begin(); std::vector<int>::const_iterator slot=slots_.first.begin();
slot!=slots_.first.end(); ++slot) for (;slot!=slots_.first.end(); ++slot)
{ {
// if the item is equipped already, nothing to do // if the item is equipped already, nothing to do
if (invStore.getSlot(*slot) == it) if (invStore.getSlot(*slot) == it)
return; return;
// if all slots are occupied, replace the last slot if (invStore.getSlot(*slot) == invStore.end())
if (slot == --slots_.first.end())
{ {
// slot is not occupied
invStore.equip(*slot, it, actor); invStore.equip(*slot, it, actor);
break; break;
} }
}
if (invStore.getSlot(*slot) == invStore.end()) // all slots are occupied -> cycle
// move all slots one towards begin(), then equip the item in the slot that is now free
if (slot == slots_.first.end())
{
for (slot=slots_.first.begin();slot!=slots_.first.end(); ++slot)
{ {
// slot is not occupied invStore.unequipSlot(*slot, actor);
invStore.equip(*slot, it, actor); if (slot+1 != slots_.first.end())
break; invStore.equip(*slot, invStore.getSlot(*(slot+1)), actor);
else
invStore.equip(*slot, it, actor);
} }
} }
} }

@ -99,6 +99,13 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
} }
it->second->load(esm, id); it->second->load(esm, id);
// DELE can also occur after the usual subrecords
if (esm.isNextSub("DELE")) {
esm.skipRecord();
it->second->eraseStatic(id);
continue;
}
if (n.val==ESM::REC_DIAL) { if (n.val==ESM::REC_DIAL) {
dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id)); dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id));
} else { } else {

@ -193,6 +193,11 @@ namespace MWWorld
const ESM::Position &refpos = ptr.getRefData().getPosition(); const ESM::Position &refpos = ptr.getRefData().getPosition();
Ogre::Vector3 position(refpos.pos); Ogre::Vector3 position(refpos.pos);
// Early-out for totally static creatures
// (Not sure if gravity should still apply?)
if (!ptr.getClass().canWalk(ptr) && !isFlying && !ptr.getClass().canSwim(ptr))
return position;
/* Anything to collide with? */ /* Anything to collide with? */
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle()); OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
if(!physicActor || !physicActor->getCollisionMode()) if(!physicActor || !physicActor->getCollisionMode())
@ -299,17 +304,33 @@ namespace MWWorld
continue; // velocity updated, calculate nextpos again continue; // velocity updated, calculate nextpos again
} }
// trace to where character would go if there were no obstructions if(!newPosition.positionCloses(nextpos, 0.00000001))
tracer.doTrace(colobj, newPosition, nextpos, engine); {
// trace to where character would go if there were no obstructions
tracer.doTrace(colobj, newPosition, nextpos, engine);
// check for obstructions // check for obstructions
if(tracer.mFraction >= 1.0f) if(tracer.mFraction >= 1.0f)
{
newPosition = tracer.mEndPos; // ok to move, so set newPosition
remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it?
break;
}
}
else
{ {
newPosition = tracer.mEndPos; // ok to move, so set newPosition // The current position and next position are nearly the same, so just exit.
// Note: Bullet can trigger an assert in debug modes if the positions
// are the same, since that causes it to attempt to normalize a zero
// length vector (which can also happen with nearly identical vectors, since
// precision can be lost due to any math Bullet does internally). Since we
// aren't performing any collision detection, we want to reject the next
// position, so that we don't slowly move inside another object.
remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it? remainingTime *= (1.0f-tracer.mFraction); // FIXME: remainingTime is no longer used so don't set it?
break; break;
} }
Ogre::Vector3 oldPosition = newPosition; Ogre::Vector3 oldPosition = newPosition;
// We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over) // We hit something. Try to step up onto it. (NOTE: stepMove does not allow stepping over)
// NOTE: stepMove modifies newPosition if successful // NOTE: stepMove modifies newPosition if successful
@ -492,7 +513,7 @@ namespace MWWorld
return std::make_pair(true, ray.getPoint(len * test.second)); return std::make_pair(true, ray.getPoint(len * test.second));
} }
std::pair<bool, Ogre::Vector3> PhysicsSystem::castRay(float mouseX, float mouseY, Ogre::Vector3* normal) std::pair<bool, Ogre::Vector3> PhysicsSystem::castRay(float mouseX, float mouseY, Ogre::Vector3* normal, std::string* hit)
{ {
Ogre::Ray ray = mRender.getCamera()->getCameraToViewportRay( Ogre::Ray ray = mRender.getCamera()->getCameraToViewportRay(
mouseX, mouseX,
@ -510,6 +531,8 @@ namespace MWWorld
return std::make_pair(false, Ogre::Vector3()); return std::make_pair(false, Ogre::Vector3());
else else
{ {
if (hit != NULL)
*hit = result.first;
return std::make_pair(true, ray.getPoint(200*result.second)); /// \todo make this distance (ray length) configurable return std::make_pair(true, ray.getPoint(200*result.second)); /// \todo make this distance (ray length) configurable
} }
} }

@ -70,9 +70,10 @@ namespace MWWorld
std::pair<bool, Ogre::Vector3> std::pair<bool, Ogre::Vector3>
castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len); castRay(const Ogre::Vector3 &orig, const Ogre::Vector3 &dir, float len);
std::pair<bool, Ogre::Vector3> castRay(float mouseX, float mouseY, Ogre::Vector3* normal = NULL); std::pair<bool, Ogre::Vector3> castRay(float mouseX, float mouseY, Ogre::Vector3* normal = NULL, std::string* hit = NULL);
///< cast ray from the mouse, return true if it hit something and the first result ///< cast ray from the mouse, return true if it hit something and the first result
/// @param normal if non-NULL, the hit normal will be written there (if there is a hit) /// @param normal if non-NULL, the hit normal will be written there (if there is a hit)
/// @param hit if non-NULL, the string handle of the hit object will be written there (if there is a hit)
OEngine::Physic::PhysicEngine* getEngine(); OEngine::Physic::PhysicEngine* getEngine();

@ -432,7 +432,7 @@ namespace MWWorld
mRendering->getCamera()->toggleVanityMode(false); mRendering->getCamera()->toggleVanityMode(false);
} }
if(mRendering->getCamera()->isFirstPerson()) if(mRendering->getCamera()->isFirstPerson())
togglePOV(); mRendering->getCamera()->toggleViewMode(true);
} }
MWWorld::Player& World::getPlayer() MWWorld::Player& World::getPlayer()
@ -1624,12 +1624,20 @@ namespace MWWorld
bool World::canPlaceObject(float cursorX, float cursorY) bool World::canPlaceObject(float cursorX, float cursorY)
{ {
Ogre::Vector3 normal(0,0,0); Ogre::Vector3 normal(0,0,0);
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY, &normal); std::string handle;
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY, &normal, &handle);
if (result.first) if (result.first)
{ {
// check if the wanted position is on a flat surface, and not e.g. against a vertical wall // check if the wanted position is on a flat surface, and not e.g. against a vertical wall
return (normal.angleBetween(Ogre::Vector3(0.f,0.f,1.f)).valueDegrees() < 30); if (normal.angleBetween(Ogre::Vector3(0.f,0.f,1.f)).valueDegrees() >= 30)
return false;
MWWorld::Ptr hitObject = searchPtrViaHandle(handle);
if (!hitObject.isEmpty() && hitObject.getClass().isActor())
return false;
return true;
} }
else else
return false; return false;
@ -1695,14 +1703,15 @@ namespace MWWorld
Ogre::Vector3 orig = Ogre::Vector3 orig =
Ogre::Vector3(pos.pos); Ogre::Vector3(pos.pos);
orig.z += 20;
Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1); Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1);
float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2]; float len = 100.0;
len += 100.0;
std::pair<bool, Ogre::Vector3> hit = std::pair<bool, Ogre::Vector3> hit =
mPhysics->castRay(orig, dir, len); mPhysics->castRay(orig, dir, len);
pos.pos[2] = hit.second.z; if (hit.first)
pos.pos[2] = hit.second.z;
// copy the object and set its count // copy the object and set its count
int origCount = object.getRefData().getCount(); int origCount = object.getRefData().getCount();
@ -1851,7 +1860,12 @@ namespace MWWorld
if (!mPlayer) if (!mPlayer)
mPlayer = new MWWorld::Player(player, *this); mPlayer = new MWWorld::Player(player, *this);
else else
{
// Remove the old CharacterController
MWBase::Environment::get().getMechanicsManager()->remove(getPlayerPtr());
mPlayer->set(player); mPlayer->set(player);
}
Ptr ptr = mPlayer->getPlayer(); Ptr ptr = mPlayer->getPlayer();
mRendering->setupPlayer(ptr); mRendering->setupPlayer(ptr);

@ -181,6 +181,7 @@ namespace Compiler
opcodeSameFactionExplicit); opcodeSameFactionExplicit);
extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction); extensions.registerInstruction("modfactionreaction", "ccl", opcodeModFactionReaction);
extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction); extensions.registerFunction("getfactionreaction", 'l', "ccl", opcodeGetFactionReaction);
extensions.registerInstruction("clearinfoactor", "", opcodeClearInfoActor, opcodeClearInfoActorExplicit);
} }
} }
@ -215,6 +216,7 @@ namespace Compiler
extensions.registerInstruction ("showmap", "S", opcodeShowMap); extensions.registerInstruction ("showmap", "S", opcodeShowMap);
extensions.registerInstruction ("fillmap", "", opcodeFillMap); extensions.registerInstruction ("fillmap", "", opcodeFillMap);
extensions.registerInstruction ("menutest", "/l", opcodeMenuTest);
} }
} }

@ -154,6 +154,8 @@ namespace Compiler
const int opcodeSameFactionExplicit = 0x20001b6; const int opcodeSameFactionExplicit = 0x20001b6;
const int opcodeModFactionReaction = 0x2000242; const int opcodeModFactionReaction = 0x2000242;
const int opcodeGetFactionReaction = 0x2000243; const int opcodeGetFactionReaction = 0x2000243;
const int opcodeClearInfoActor = 0x2000245;
const int opcodeClearInfoActorExplicit = 0x2000246;
} }
namespace Gui namespace Gui
@ -175,6 +177,7 @@ namespace Compiler
const int opcodeToggleFullHelp = 0x2000151; const int opcodeToggleFullHelp = 0x2000151;
const int opcodeShowMap = 0x20001a0; const int opcodeShowMap = 0x20001a0;
const int opcodeFillMap = 0x20001a1; const int opcodeFillMap = 0x20001a1;
const int opcodeMenuTest = 0x2002c;
} }
namespace Misc namespace Misc

@ -239,7 +239,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const
return false; return false;
EsmFile *file = item(index.row()); EsmFile *file = item(index.row());
QString fileName = file->filePath(); QString fileName = file->fileName();
bool success = false; bool success = false;
switch(role) switch(role)
@ -266,7 +266,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const
if (success) if (success)
{ {
success = setCheckState(fileName, value.toBool()); success = setCheckState(file->filePath(), value.toBool());
emit dataChanged(index, index); emit dataChanged(index, index);
} }
} }
@ -277,19 +277,19 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex &index, const
int checkValue = value.toInt(); int checkValue = value.toInt();
bool success = false; bool success = false;
bool setState = false; bool setState = false;
if ((checkValue==Qt::Checked) && !isChecked(fileName)) if ((checkValue==Qt::Checked) && !isChecked(file->filePath()))
{ {
setState = true; setState = true;
success = true; success = true;
} }
else if ((checkValue == Qt::Checked) && isChecked (fileName)) else if ((checkValue == Qt::Checked) && isChecked (file->filePath()))
setState = true; setState = true;
else if (checkValue == Qt::Unchecked) else if (checkValue == Qt::Unchecked)
setState = true; setState = true;
if (setState) if (setState)
{ {
setCheckState(fileName, success); setCheckState(file->filePath(), success);
emit dataChanged(index, index); emit dataChanged(index, index);
} }
@ -517,10 +517,10 @@ void ContentSelectorModel::ContentModel::sortFiles()
} }
} }
bool ContentSelectorModel::ContentModel::isChecked(const QString& name) const bool ContentSelectorModel::ContentModel::isChecked(const QString& filepath) const
{ {
if (mCheckStates.contains(name)) if (mCheckStates.contains(filepath))
return (mCheckStates[name] == Qt::Checked); return (mCheckStates[filepath] == Qt::Checked);
return false; return false;
} }
@ -543,12 +543,12 @@ void ContentSelectorModel::ContentModel::refreshModel()
emit dataChanged (index(0,0), index(rowCount()-1,0)); emit dataChanged (index(0,0), index(rowCount()-1,0));
} }
bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool checkState) bool ContentSelectorModel::ContentModel::setCheckState(const QString &filepath, bool checkState)
{ {
if (name.isEmpty()) if (filepath.isEmpty())
return false; return false;
const EsmFile *file = item(name); const EsmFile *file = item(filepath);
if (!file) if (!file)
return false; return false;
@ -558,8 +558,8 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool
if (checkState) if (checkState)
state = Qt::Checked; state = Qt::Checked;
mCheckStates[name] = state; mCheckStates[filepath] = state;
emit dataChanged(indexFromItem(item(name)), indexFromItem(item(name))); emit dataChanged(indexFromItem(item(filepath)), indexFromItem(item(filepath)));
if (file->isGameFile()) if (file->isGameFile())
refreshModel(); refreshModel();
@ -586,7 +586,10 @@ bool ContentSelectorModel::ContentModel::setCheckState(const QString &name, bool
{ {
foreach (const EsmFile *downstreamFile, mFiles) foreach (const EsmFile *downstreamFile, mFiles)
{ {
if (downstreamFile->gameFiles().contains(name)) QFileInfo fileInfo(filepath);
QString filename = fileInfo.fileName();
if (downstreamFile->gameFiles().contains(filename))
{ {
if (mCheckStates.contains(downstreamFile->filePath())) if (mCheckStates.contains(downstreamFile->filePath()))
mCheckStates[downstreamFile->filePath()] = Qt::Unchecked; mCheckStates[downstreamFile->filePath()] = Qt::Unchecked;

@ -45,8 +45,8 @@ namespace ContentSelectorModel
const EsmFile *item(const QString &name) const; const EsmFile *item(const QString &name) const;
bool isEnabled (QModelIndex index) const; bool isEnabled (QModelIndex index) const;
bool isChecked(const QString &name) const; bool isChecked(const QString &filepath) const;
bool setCheckState(const QString &name, bool isChecked); bool setCheckState(const QString &filepath, bool isChecked);
void setCheckStates (const QStringList &fileList, bool isChecked); void setCheckStates (const QStringList &fileList, bool isChecked);
ContentFileList checkedItems() const; ContentFileList checkedItems() const;
void uncheckAll(); void uncheckAll();

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

Loading…
Cancel
Save