1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-03-30 10:06:42 +00:00

Merge remote-tracking branch 'upstream/master'

Equipping_Weapon check isn't needed cause there won't be WeapType_None in
that case

Conflicts:
	apps/openmw/mwmechanics/character.cpp
This commit is contained in:
mrcheko 2014-06-13 14:12:41 +04:00
commit fc6366fb27
99 changed files with 1297 additions and 827 deletions

2
.gitignore vendored
View file

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

View file

@ -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 ()
## Non debian specific
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
# Install binaries # Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL) IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BSATOOL) ENDIF(BUILD_BSATOOL)
IF(BUILD_ESMTOOL) IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL) ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER) IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER) ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
# Install licenses # Install licenses
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) 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")

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -80,49 +80,42 @@ 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,
CSMWorld::UniversalId::Type_Spell,
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_Spell, CSMWorld::UniversalId::Type_None // end marker
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false)); };
for (int i=0; sTableTypes2[i]!=CSMWorld::UniversalId::Type_None; ++i)
manager.add (sTableTypes2[i],
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView,
CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Skill,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));
manager.add (CSMWorld::UniversalId::Type_Gmst,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));
manager.add (CSMWorld::UniversalId::Type_Referenceable, manager.add (CSMWorld::UniversalId::Type_Referenceable,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false)); new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Birthsign,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Global,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Gmst,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Race,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Class,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (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));
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));

View file

@ -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
{ {
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int typeColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
return CSMWorld::UniversalId ( return CSMWorld::UniversalId (
static_cast<CSMWorld::UniversalId::Type> (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), static_cast<CSMWorld::UniversalId::Type> (mModel->data (mModel->index (row, typeColumn)).toInt()),
mProxyModel->data (mProxyModel->index (row, 0)).toString().toUtf8().constData()); mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData());
}
void CSVWorld::Table::revertRecord()
{
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()
{
if (!mEditLock)
{
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()

View file

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

View file

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

View file

@ -357,7 +357,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create input and UI first to set up a bootstrapping environment for // Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so // showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string(); std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v1.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser); bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab); MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab);
mEnvironment.setInputManager (input); mEnvironment.setInputManager (input);

View file

@ -4,19 +4,20 @@
#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>
#include <boost/filesystem/fstream.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 +254,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 +269,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 +281,99 @@ 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());
#else
// Redirect cout to openmw.log
std::ofstream logfile ("openmw.log");
{
boost::iostreams::stream_buffer<Logger> sb;
sb.open (Logger (logfile));
#endif
std::cout.rdbuf (&sb); std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb);
#else
// Redirect cout and cerr to openmw.log
boost::filesystem::ofstream logfile (boost::filesystem::path(
cfgMgr.getLogPath() / "/openmw.log"));
ret = main (__argc, __argv); boost::iostreams::stream_buffer<Tee> coutsb;
std::ostream oldcout(cout_rdbuf);
coutsb.open (Tee(logfile, oldcout));
std::cout.rdbuf (&coutsb);
std::cout.rdbuf(old_rdbuf); 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
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)))
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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -258,12 +258,16 @@ 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 U+2019, which is nowhere to be found in // More hacks! The french game uses several win1252 characters that are not included
// the CP437 encoding of the font. Fall back to 39 (regular apostrophe) // in the cp437 encoding of the font. Fall back to similar available characters.
if (i == 39 && mEncoding == ToUTF8::CP437) // 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"); MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", 0x2019); code->addAttribute("index", additional[i]);
code->addAttribute("coord", MyGUI::utility::toString(x1) + " " code->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " " + MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " " + MyGUI::utility::toString(w) + " "

View file

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

View file

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

View file

@ -24,6 +24,7 @@
#include "spellwindow.hpp" #include "spellwindow.hpp"
#include "itemwidget.hpp" #include "itemwidget.hpp"
#include "sortfilteritemmodel.hpp"
namespace MWGui namespace MWGui
@ -134,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);
} }
@ -162,7 +164,7 @@ namespace MWGui
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item) void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
{ {
assert (mSelectedIndex > 0); assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
@ -184,7 +186,7 @@ namespace MWGui
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item) void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
{ {
assert (mSelectedIndex > 0); assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
@ -203,7 +205,7 @@ namespace MWGui
void QuickKeysMenu::onAssignMagic (const std::string& spellId) void QuickKeysMenu::onAssignMagic (const std::string& spellId)
{ {
assert (mSelectedIndex > 0); assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex]; ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0)); MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
@ -245,7 +247,7 @@ namespace MWGui
void QuickKeysMenu::activateQuickKey(int index) void QuickKeysMenu::activateQuickKey(int index)
{ {
assert (index-1 > 0); assert (index-1 >= 0);
ItemWidget* button = mQuickKeyButtons[index-1]; ItemWidget* button = mQuickKeyButtons[index-1];
QuickKeyType type = mAssigned[index-1]; QuickKeyType type = mAssigned[index-1];

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,19 +25,13 @@ 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::cleanup() void VideoWidget::stop()
{ {
mPlayer.close(); mPlayer.close();
} }

View file

@ -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,15 +26,11 @@ namespace MWGui
/// @return Is the video still playing? /// @return Is the video still playing?
bool update(); bool update();
/// Free video player resources (done automatically on destruction) /// Stop video and free resources (done automatically on destruction)
void cleanup(); void stop();
private: private:
bool mAllowSkipping;
MWRender::VideoPlayer mPlayer; MWRender::VideoPlayer mPlayer;
void onKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char);
}; };
} }

View file

@ -202,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()
@ -263,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);
@ -1559,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();
@ -1587,7 +1599,7 @@ namespace MWGui
mRendering->getWindow()->update(); mRendering->getWindow()->update();
} }
mVideoWidget->cleanup(); mVideoWidget->stop();
setCursorVisible(cursorWasVisible); setCursorVisible(cursorWasVisible);
@ -1628,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();
}
} }

View file

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

View file

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

View file

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

View file

@ -117,7 +117,7 @@ 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) , mAttemptJump(false)
, mControlsDisabled(false) , mControlsDisabled(false)
{ {
@ -807,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);
@ -817,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)
@ -857,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()
@ -955,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)

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -179,6 +179,7 @@ namespace MWSound
if(!mOutput->isInitialized()) if(!mOutput->isInitialized())
return; return;
std::cout <<"Playing "<<filename<< std::endl; std::cout <<"Playing "<<filename<< std::endl;
mLastPlayedMusic = filename;
try try
{ {
stopMusic(); stopMusic();
@ -203,19 +204,34 @@ namespace MWSound
void SoundManager::startRandomTitle() void SoundManager::startRandomTitle()
{ {
Ogre::StringVector filelist; Ogre::StringVector filelist;
if (mMusicFiles.find(mCurrentPlaylist) == mMusicFiles.end())
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
{ {
Ogre::StringVectorPtr resourcesInThisGroup = mResourceMgr.findResourceNames(*it, Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
"Music/"+mCurrentPlaylist+"/*"); for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end()); {
Ogre::StringVectorPtr resourcesInThisGroup = mResourceMgr.findResourceNames(*it,
"Music/"+mCurrentPlaylist+"/*");
filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end());
}
mMusicFiles[mCurrentPlaylist] = filelist;
} }
else
filelist = mMusicFiles[mCurrentPlaylist];
if(!filelist.size()) if(!filelist.size())
return; return;
int i = rand()%filelist.size(); int i = rand()%filelist.size();
// Don't play the same music track twice in a row
if (filelist[i] == mLastPlayedMusic)
{
if (i-1 == int(filelist.size()))
i = 0;
else
++i;
}
streamMusicFull(filelist[i]); streamMusicFull(filelist[i]);
} }

View file

@ -31,6 +31,10 @@ namespace MWSound
std::auto_ptr<Sound_Output> mOutput; std::auto_ptr<Sound_Output> mOutput;
// Caches available music tracks by <playlist name, (sound files) >
std::map<std::string, Ogre::StringVector> mMusicFiles;
std::string mLastPlayedMusic; // The music file that was last played
float mMasterVolume; float mMasterVolume;
float mSFXVolume; float mSFXVolume;
float mMusicVolume; float mMusicVolume;

View file

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

View file

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

View file

@ -3,12 +3,13 @@
#include <sstream> #include <sstream>
#include <stdexcept> #include <stdexcept>
#include <cctype> // std::isalnum
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
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 +29,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 +57,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();

View file

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

View file

@ -184,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);
@ -233,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());
} }
@ -246,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);
} }
} }
@ -412,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()
@ -433,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);
} }

View file

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

View file

@ -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,7 +304,7 @@ namespace MWWorld
continue; // velocity updated, calculate nextpos again continue; // velocity updated, calculate nextpos again
} }
if(!newPosition.positionCloses(nextpos, 0.00000001)) if(newPosition.squaredDistance(nextpos) > 0.00000001*0.00000001)
{ {
// trace to where character would go if there were no obstructions // trace to where character would go if there were no obstructions
tracer.doTrace(colobj, newPosition, nextpos, engine); tracer.doTrace(colobj, newPosition, nextpos, engine);

View file

@ -3,22 +3,8 @@
namespace MWWorld { namespace MWWorld {
void Store<ESM::Cell>::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell)
void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
{ {
// Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,
// and we merge all this data into one Cell object. However, we can't simply search for the cell id,
// as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they
// are not available until both cells have been loaded! So first, proceed as usual.
// All cells have a name record, even nameless exterior cells.
std::string idLower = Misc::StringUtils::lowerCase(id);
ESM::Cell *cell = new ESM::Cell;
cell->mName = id;
//First part of cell loading
cell->preLoad(esm);
//Handling MovedCellRefs, there is no way to do it inside loadcell //Handling MovedCellRefs, there is no way to do it inside loadcell
while (esm.isNextSub("MVRF")) { while (esm.isNextSub("MVRF")) {
ESM::CellRef ref; ESM::CellRef ref;
@ -43,35 +29,58 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
else else
*iter = ref; *iter = ref;
} }
}
//Second part of cell loading void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
cell->postLoad(esm); {
// Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,
// and we merge all this data into one Cell object. However, we can't simply search for the cell id,
// as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they
// are not available until both cells have been loaded at least partially!
if(cell->mData.mFlags & ESM::Cell::Interior) // All cells have a name record, even nameless exterior cells.
std::string idLower = Misc::StringUtils::lowerCase(id);
ESM::Cell cell;
cell.mName = id;
// Load the (x,y) coordinates of the cell, if it is an exterior cell,
// so we can find the cell we need to merge with
cell.loadData(esm);
if(cell.mData.mFlags & ESM::Cell::Interior)
{ {
// Store interior cell by name, try to merge with existing parent data. // Store interior cell by name, try to merge with existing parent data.
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower)); ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower));
if (oldcell) { if (oldcell) {
// push the new references on the list of references to manage // merge new cell into old cell
oldcell->mContextList.push_back(cell->mContextList.at(0)); // push the new references on the list of references to manage (saveContext = true)
// copy list into new cell oldcell->mData = cell.mData;
cell->mContextList = oldcell->mContextList; oldcell->loadCell(esm, true);
// have new cell replace old cell
ESM::Cell::merge(oldcell, cell);
} else } else
mInt[idLower] = *cell; {
// spawn a new cell
cell.loadCell(esm, true);
mInt[idLower] = cell;
}
} }
else else
{ {
// Store exterior cells by grid position, try to merge with existing parent data. // Store exterior cells by grid position, try to merge with existing parent data.
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell->getGridX(), cell->getGridY())); ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell.getGridX(), cell.getGridY()));
if (oldcell) { if (oldcell) {
// merge new cell into old cell
oldcell->mData = cell.mData;
oldcell->loadCell(esm, false);
// handle moved ref (MVRF) subrecords
handleMovedCellRefs (esm, &cell);
// push the new references on the list of references to manage // push the new references on the list of references to manage
oldcell->mContextList.push_back(cell->mContextList.at(0)); oldcell->postLoad(esm);
// copy list into new cell
cell->mContextList = oldcell->mContextList;
// merge lists of leased references, use newer data in case of conflict // merge lists of leased references, use newer data in case of conflict
for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); ++it) { for (ESM::MovedCellRefTracker::const_iterator it = cell.mMovedRefs.begin(); it != cell.mMovedRefs.end(); ++it) {
// remove reference from current leased ref tracker and add it to new cell // remove reference from current leased ref tracker and add it to new cell
ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum); ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum);
if (itold != oldcell->mMovedRefs.end()) { if (itold != oldcell->mMovedRefs.end()) {
@ -82,13 +91,24 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
*itold = *it; *itold = *it;
} }
} }
cell->mMovedRefs = oldcell->mMovedRefs;
// have new cell replace old cell // We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a
ESM::Cell::merge(oldcell, cell); // reference to this cell, so the list for the new cell should be empty. The list for oldcell,
// however, could have leased refs in it and so should be kept.
} else } else
mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell; {
// spawn a new cell
cell.loadCell(esm, false);
// handle moved ref (MVRF) subrecords
handleMovedCellRefs (esm, &cell);
// push the new references on the list of references to manage
cell.postLoad(esm);
mExt[std::make_pair(cell.mData.mX, cell.mData.mY)] = cell;
}
} }
delete cell;
} }
} }

View file

@ -591,6 +591,8 @@ namespace MWWorld
return search(cell.mName); return search(cell.mName);
} }
void handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell);
public: public:
ESMStore *mEsmStore; ESMStore *mEsmStore;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -53,6 +53,8 @@ namespace ContentSelectorModel
inline QDateTime modified() const { return mModified; } inline QDateTime modified() const { return mModified; }
inline float format() const { return mFormat; } inline float format() const { return mFormat; }
inline QString filePath() const { return mPath; } inline QString filePath() const { return mPath; }
/// @note Contains file names, not paths.
inline const QStringList &gameFiles() const { return mGameFiles; } inline const QStringList &gameFiles() const { return mGameFiles; }
inline QString description() const { return mDescription; } inline QString description() const { return mDescription; }
inline QString toolTip() const { return sToolTip.arg(mAuthor) inline QString toolTip() const { return sToolTip.arg(mAuthor)

View file

@ -10,7 +10,7 @@ namespace ESM
void Potion::load(ESMReader &esm) void Potion::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mModel = esm.getHNOString("MODL");
mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason
mScript = esm.getHNOString("SCRI"); mScript = esm.getHNOString("SCRI");
mName = esm.getHNOString("FNAM"); mName = esm.getHNOString("FNAM");

View file

@ -50,18 +50,14 @@ namespace ESM
return ref.mRefNum == refNum; return ref.mRefNum == refNum;
} }
void Cell::load(ESMReader &esm, bool saveContext) void Cell::load(ESMReader &esm, bool saveContext)
{ {
// Ignore this for now, it might mean we should delete the entire loadData(esm);
// cell? loadCell(esm, saveContext);
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"! }
if (esm.isNextSub("DELE")) {
esm.skipHSub();
}
esm.getHNT(mData, "DATA", 12);
void Cell::loadCell(ESMReader &esm, bool saveContext)
{
mNAM0 = 0; mNAM0 = 0;
if (mData.mFlags & Interior) if (mData.mFlags & Interior)
@ -73,12 +69,10 @@ void Cell::load(ESMReader &esm, bool saveContext)
esm.getHT(waterl); esm.getHT(waterl);
mWater = (float) waterl; mWater = (float) waterl;
mWaterInt = true; mWaterInt = true;
mHasWaterLevelRecord = true;
} }
else if (esm.isNextSub("WHGT")) else if (esm.isNextSub("WHGT"))
{ {
esm.getHT(mWater); esm.getHT(mWater);
mHasWaterLevelRecord = true;
} }
// Quasi-exterior cells have a region (which determines the // Quasi-exterior cells have a region (which determines the
@ -107,6 +101,18 @@ void Cell::load(ESMReader &esm, bool saveContext)
} }
} }
void Cell::loadData(ESMReader &esm)
{
// Ignore this for now, it might mean we should delete the entire
// cell?
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"!
if (esm.isNextSub("DELE")) {
esm.skipHSub();
}
esm.getHNT(mData, "DATA", 12);
}
void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool void Cell::preLoad(ESMReader &esm) //Can't be "load" because it conflicts with function in esmtool
{ {
this->load(esm, false); this->load(esm, false);
@ -124,14 +130,12 @@ void Cell::save(ESMWriter &esm) const
esm.writeHNT("DATA", mData, 12); esm.writeHNT("DATA", mData, 12);
if (mData.mFlags & Interior) if (mData.mFlags & Interior)
{ {
if (mHasWaterLevelRecord) { if (mWaterInt) {
if (mWaterInt) { int water =
int water = (mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5);
(mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5); esm.writeHNT("INTV", water);
esm.writeHNT("INTV", water); } else {
} else { esm.writeHNT("WHGT", mWater);
esm.writeHNT("WHGT", mWater);
}
} }
if (mData.mFlags & QuasiEx) if (mData.mFlags & QuasiEx)
@ -228,19 +232,6 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
mAmbi.mFogDensity = 0; mAmbi.mFogDensity = 0;
} }
void Cell::merge(Cell *original, Cell *modified)
{
float waterLevel = original->mWater;
if (modified->mHasWaterLevelRecord)
{
waterLevel = modified->mWater;
}
// else: keep original water level, instead of resetting to 0
*original = *modified;
original->mWater = waterLevel;
}
CellId Cell::getCellId() const CellId Cell::getCellId() const
{ {
CellId id; CellId id;

View file

@ -78,10 +78,7 @@ struct Cell
float mFogDensity; float mFogDensity;
}; };
Cell() : mWater(0), mHasWaterLevelRecord(false) {} Cell() : mWater(0) {}
/// Merge \a modified into \a original
static void merge (Cell* original, Cell* modified);
// Interior cells are indexed by this (it's the 'id'), for exterior // Interior cells are indexed by this (it's the 'id'), for exterior
// cells it is optional. // cells it is optional.
@ -93,8 +90,8 @@ struct Cell
std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
DATAstruct mData; DATAstruct mData;
AMBIstruct mAmbi; AMBIstruct mAmbi;
float mWater; // Water level float mWater; // Water level
bool mHasWaterLevelRecord;
bool mWaterInt; bool mWaterInt;
int mMapColor; int mMapColor;
int mNAM0; int mNAM0;
@ -109,7 +106,10 @@ struct Cell
// This method is left in for compatibility with esmtool. Parsing moved references currently requires // This method is left in for compatibility with esmtool. Parsing moved references currently requires
// passing ESMStore, bit it does not know about this parameter, so we do it this way. // passing ESMStore, bit it does not know about this parameter, so we do it this way.
void load(ESMReader &esm, bool saveContext = true); void load(ESMReader &esm, bool saveContext = true); // Load everything (except references)
void loadData(ESMReader &esm); // Load DATAstruct only
void loadCell(ESMReader &esm, bool saveContext = true); // Load everything, except DATAstruct and references
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
bool isExterior() const bool isExterior() const

View file

@ -10,6 +10,7 @@ namespace ESM
void Static::load(ESMReader &esm) void Static::load(ESMReader &esm)
{ {
mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL"); mModel = esm.getHNString("MODL");
} }
void Static::save(ESMWriter &esm) const void Static::save(ESMWriter &esm) const

View file

@ -26,6 +26,8 @@ struct Static
std::string mId, mModel; std::string mId, mModel;
bool mPersistent;
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;

View file

@ -177,42 +177,49 @@ struct KeyListT {
KeyT<T> key; KeyT<T> key;
NIFStream &nifReference = *nif; NIFStream &nifReference = *nif;
for(size_t i = 0;i < count;i++)
if(mInterpolationType == sLinearInterpolation)
{ {
if(mInterpolationType == sLinearInterpolation) for(size_t i = 0;i < count;i++)
{ {
readTimeAndValue(nifReference, key); readTimeAndValue(nifReference, key);
mKeys.push_back(key); mKeys.push_back(key);
} }
else if(mInterpolationType == sQuadraticInterpolation) }
else if(mInterpolationType == sQuadraticInterpolation)
{
for(size_t i = 0;i < count;i++)
{ {
readQuadratic(nifReference, key); readQuadratic(nifReference, key);
mKeys.push_back(key); mKeys.push_back(key);
} }
else if(mInterpolationType == sTBCInterpolation) }
else if(mInterpolationType == sTBCInterpolation)
{
for(size_t i = 0;i < count;i++)
{ {
readTBC(nifReference, key); readTBC(nifReference, key);
mKeys.push_back(key); mKeys.push_back(key);
} }
//XYZ keys aren't actually read here.
//data.hpp sees that the last type read was sXYZInterpolation and:
// Eats a floating point number, then
// Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared.
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
else if(mInterpolationType == sXYZInterpolation)
{
//Don't try to read XYZ keys into the wrong part
if ( count != 1 )
nif->file->fail("XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: "+Ogre::StringConverter::toString(count));
}
else if (0 == mInterpolationType)
{
if (count != 0)
nif->file->fail("Interpolation type 0 doesn't work with keys");
}
else
nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType));
} }
//XYZ keys aren't actually read here.
//data.hpp sees that the last type read was sXYZInterpolation and:
// Eats a floating point number, then
// Re-runs the read function 3 more times, with force enabled so that the previous values aren't cleared.
// When it does that it's reading in a bunch of sLinearInterpolation keys, not sXYZInterpolation.
else if(mInterpolationType == sXYZInterpolation)
{
//Don't try to read XYZ keys into the wrong part
if ( count != 1 )
nif->file->fail("XYZ_ROTATION_KEY count should always be '1' . Retrieved Value: "+Ogre::StringConverter::toString(count));
}
else if (0 == mInterpolationType)
{
if (count != 0)
nif->file->fail("Interpolation type 0 doesn't work with keys");
}
else
nif->file->fail("Unhandled interpolation type: "+Ogre::StringConverter::toString(mInterpolationType));
} }
private: private:

View file

@ -32,6 +32,9 @@ namespace Translation
boost::filesystem::ifstream stream ( boost::filesystem::ifstream stream (
dataFileCollections.getCollection (extension).getPath (fileName)); dataFileCollections.getCollection (extension).getPath (fileName));
// Configure the stream to throw exception upon error
stream.exceptions ( boost::filesystem::ifstream::failbit | boost::filesystem::ifstream::badbit );
if (!stream.is_open()) if (!stream.is_open())
throw std::runtime_error ("failed to open translation file: " + fileName); throw std::runtime_error ("failed to open translation file: " + fileName);
@ -41,6 +44,7 @@ namespace Translation
void Storage::loadDataFromStream(ContainerType& container, std::istream& stream) void Storage::loadDataFromStream(ContainerType& container, std::istream& stream)
{ {
// NOTE: does not handle failbit/badbit. stream must be set up beforehand to throw in these cases.
std::string line; std::string line;
while (!stream.eof()) while (!stream.eof())
{ {

View file

@ -19,8 +19,8 @@ Alexander Olofsson (Ace)
Artem Kotsynyak (greye) Artem Kotsynyak (greye)
Arthur Moore (EmperorArthur) Arthur Moore (EmperorArthur)
athile athile
Bret Curtis (psi29a)
Britt Mathis (galdor557) Britt Mathis (galdor557)
BrotherBrick
cc9cii cc9cii
Chris Boyce (slothlife) Chris Boyce (slothlife)
Chris Robinson (KittyCat) Chris Robinson (KittyCat)
@ -79,7 +79,7 @@ Torben Leif Carrington (TorbenC)
Packagers: Packagers:
Alexander Olofsson (Ace) - Windows Alexander Olofsson (Ace) - Windows
BrotherBrick - Ubuntu Linux Bret Curtis (psi29a) - Ubuntu Linux
Edmondo Tommasina (edmondo) - Gentoo Linux Edmondo Tommasina (edmondo) - Gentoo Linux
Julian Ospald (hasufell) - Gentoo Linux Julian Ospald (hasufell) - Gentoo Linux
Karl-Felix Glatzer (k1ll) - Linux Binaries Karl-Felix Glatzer (k1ll) - Linux Binaries

View file

@ -576,7 +576,7 @@ WARN_LOGFILE =
INPUT = apps \ INPUT = apps \
components \ components \
libs \ libs \
Docs docs
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View file

@ -576,7 +576,7 @@ WARN_LOGFILE =
INPUT = apps \ INPUT = apps \
components \ components \
libs \ libs \
Docs docs
# This tag can be used to specify the character encoding of the source files # This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is # that doxygen parses. Internally doxygen uses the UTF-8 encoding, which is

View file

@ -41,8 +41,6 @@ namespace ICS
this->mActive = active; this->mActive = active;
this->fillSDLKeysMap();
ICS_LOG("Channel count = " + ToString<size_t>(channelCount) ); ICS_LOG("Channel count = " + ToString<size_t>(channelCount) );
for(size_t i=0;i<channelCount;i++) for(size_t i=0;i<channelCount;i++)
{ {
@ -310,9 +308,6 @@ namespace ICS
mControlsMouseButtonBinderMap.clear(); mControlsMouseButtonBinderMap.clear();
mControlsJoystickButtonBinderMap.clear(); mControlsJoystickButtonBinderMap.clear();
mKeys.clear();
mKeyCodes.clear();
ICS_LOG(" - InputControlSystem deleted - "); ICS_LOG(" - InputControlSystem deleted - ");
} }
@ -433,7 +428,7 @@ namespace ICS
{ {
TiXmlElement keyBinder( "KeyBinder" ); TiXmlElement keyBinder( "KeyBinder" );
keyBinder.SetAttribute( "key", keyCodeToString( keyBinder.SetAttribute( "key", ToString<int>(
getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() ); getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );
keyBinder.SetAttribute( "direction", "INCREASE" ); keyBinder.SetAttribute( "direction", "INCREASE" );
control.InsertEndChild(keyBinder); control.InsertEndChild(keyBinder);
@ -443,7 +438,7 @@ namespace ICS
{ {
TiXmlElement keyBinder( "KeyBinder" ); TiXmlElement keyBinder( "KeyBinder" );
keyBinder.SetAttribute( "key", keyCodeToString( keyBinder.SetAttribute( "key", ToString<int>(
getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() ); getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );
keyBinder.SetAttribute( "direction", "DECREASE" ); keyBinder.SetAttribute( "direction", "DECREASE" );
control.InsertEndChild(keyBinder); control.InsertEndChild(keyBinder);
@ -806,128 +801,14 @@ namespace ICS
mDetectingBindingControl = NULL; mDetectingBindingControl = NULL;
} }
void InputControlSystem::fillSDLKeysMap()
{
mKeys["UNASSIGNED"]= SDLK_UNKNOWN;
mKeys["ESCAPE"]= SDLK_ESCAPE;
mKeys["1"]= SDLK_1;
mKeys["2"]= SDLK_2;
mKeys["3"]= SDLK_3;
mKeys["4"]= SDLK_4;
mKeys["5"]= SDLK_5;
mKeys["6"]= SDLK_6;
mKeys["7"]= SDLK_7;
mKeys["8"]= SDLK_8;
mKeys["9"]= SDLK_9;
mKeys["0"]= SDLK_0;
mKeys["MINUS"]= SDLK_MINUS;
mKeys["EQUALS"]= SDLK_EQUALS;
mKeys["BACK"]= SDLK_BACKSPACE;
mKeys["TAB"]= SDLK_TAB;
mKeys["Q"]= SDLK_q;
mKeys["W"]= SDLK_w;
mKeys["E"]= SDLK_e;
mKeys["R"]= SDLK_r;
mKeys["T"]= SDLK_t;
mKeys["Y"]= SDLK_y;
mKeys["U"]= SDLK_u;
mKeys["I"]= SDLK_i;
mKeys["O"]= SDLK_o;
mKeys["P"]= SDLK_p;
mKeys["LBRACKET"]= SDLK_LEFTBRACKET;
mKeys["RBRACKET"]= SDLK_RIGHTBRACKET;
mKeys["RETURN"]= SDLK_RETURN;
mKeys["LCONTROL"]= SDLK_LCTRL;
mKeys["A"]= SDLK_a;
mKeys["S"]= SDLK_s;
mKeys["D"]= SDLK_d;
mKeys["F"]= SDLK_f;
mKeys["G"]= SDLK_g;
mKeys["H"]= SDLK_h;
mKeys["J"]= SDLK_j;
mKeys["K"]= SDLK_k;
mKeys["L"]= SDLK_l;
mKeys["SEMICOLON"]= SDLK_SEMICOLON;
mKeys["APOSTROPHE"]= SDLK_QUOTE;
mKeys["GRAVE"]= SDLK_BACKQUOTE;
mKeys["LSHIFT"]= SDLK_LSHIFT;
mKeys["BACKSLASH"]= SDLK_BACKSLASH;
mKeys["Z"]= SDLK_z;
mKeys["X"]= SDLK_x;
mKeys["C"]= SDLK_c;
mKeys["V"]= SDLK_v;
mKeys["B"]= SDLK_b;
mKeys["N"]= SDLK_n;
mKeys["M"]= SDLK_m;
mKeys["COMMA"]= SDLK_COMMA;
mKeys["PERIOD"]= SDLK_PERIOD;
mKeys["SLASH"]= SDLK_SLASH;
mKeys["RSHIFT"]= SDLK_RSHIFT;
mKeys["MULTIPLY"]= SDLK_ASTERISK;
mKeys["LMENU"]= SDLK_LALT;
mKeys["SPACE"]= SDLK_SPACE;
mKeys["CAPITAL"]= SDLK_CAPSLOCK;
mKeys["F1"]= SDLK_F1;
mKeys["F2"]= SDLK_F2;
mKeys["F3"]= SDLK_F3;
mKeys["F4"]= SDLK_F4;
mKeys["F5"]= SDLK_F5;
mKeys["F6"]= SDLK_F6;
mKeys["F7"]= SDLK_F7;
mKeys["F8"]= SDLK_F8;
mKeys["F9"]= SDLK_F9;
mKeys["F10"]= SDLK_F10;
mKeys["F11"]= SDLK_F11;
mKeys["F12"]= SDLK_F12;
mKeys["NUMLOCK"]= SDLK_NUMLOCKCLEAR;
mKeys["SCROLL"]= SDLK_SCROLLLOCK;
mKeys["NUMPAD7"]= SDLK_KP_7;
mKeys["NUMPAD8"]= SDLK_KP_8;
mKeys["NUMPAD9"]= SDLK_KP_9;
mKeys["SUBTRACT"]= SDLK_KP_MINUS;
mKeys["NUMPAD4"]= SDLK_KP_4;
mKeys["NUMPAD5"]= SDLK_KP_5;
mKeys["NUMPAD6"]= SDLK_KP_6;
mKeys["ADD"]= SDLK_KP_PLUS;
mKeys["NUMPAD1"]= SDLK_KP_1;
mKeys["NUMPAD2"]= SDLK_KP_2;
mKeys["NUMPAD3"]= SDLK_KP_3;
mKeys["NUMPAD0"]= SDLK_KP_0;
mKeys["DECIMAL"]= SDLK_KP_DECIMAL;
mKeys["RCONTROL"]= SDLK_RCTRL;
mKeys["DIVIDE"]= SDLK_SLASH;
mKeys["SYSRQ"]= SDLK_SYSREQ;
mKeys["PRNTSCRN"] = SDLK_PRINTSCREEN;
mKeys["RMENU"]= SDLK_RALT;
mKeys["PAUSE"]= SDLK_PAUSE;
mKeys["HOME"]= SDLK_HOME;
mKeys["UP"]= SDLK_UP;
mKeys["PGUP"]= SDLK_PAGEUP;
mKeys["LEFT"]= SDLK_LEFT;
mKeys["RIGHT"]= SDLK_RIGHT;
mKeys["END"]= SDLK_END;
mKeys["DOWN"]= SDLK_DOWN;
mKeys["PGDOWN"]= SDLK_PAGEDOWN;
mKeys["INSERT"]= SDLK_INSERT;
mKeys["DELETE"]= SDLK_DELETE;
mKeys["NUMPADENTER"]= SDLK_KP_ENTER;
for(std::map<std::string, SDL_Keycode>::iterator it = mKeys.begin()
; it != mKeys.end() ; it++)
{
mKeyCodes[ it->second ] = it->first;
}
}
std::string InputControlSystem::keyCodeToString(SDL_Keycode key) std::string InputControlSystem::keyCodeToString(SDL_Keycode key)
{ {
return mKeyCodes[key]; return std::string(SDL_GetKeyName(key));
} }
SDL_Keycode InputControlSystem::stringToKeyCode(std::string key) SDL_Keycode InputControlSystem::stringToKeyCode(std::string key)
{ {
return mKeys[key]; return SDL_GetKeyFromName(key.c_str());
} }
void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height) void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height)

View file

@ -208,8 +208,6 @@ namespace ICS
std::vector<Channel *> mChannels; std::vector<Channel *> mChannels;
ControlsKeyBinderMapType mControlsKeyBinderMap; ControlsKeyBinderMapType mControlsKeyBinderMap;
std::map<std::string, SDL_Keycode> mKeys;
std::map<SDL_Keycode, std::string> mKeyCodes;
bool mActive; bool mActive;
InputControlSystemLog* mLog; InputControlSystemLog* mLog;
@ -227,8 +225,6 @@ namespace ICS
private: private:
void fillSDLKeysMap();
Uint16 mClientWidth; Uint16 mClientWidth;
Uint16 mClientHeight; Uint16 mClientHeight;
}; };

View file

@ -43,7 +43,7 @@ namespace ICS
dir = Control::DECREASE; dir = Control::DECREASE;
} }
addKeyBinding(mControls.back(), mKeys[xmlKeyBinder->Attribute("key")], dir); addKeyBinding(mControls.back(), FromString<int>(xmlKeyBinder->Attribute("key")), dir);
xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder"); xmlKeyBinder = xmlKeyBinder->NextSiblingElement("KeyBinder");
} }

View file

@ -36,6 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <map> #include <map>
#include <list> #include <list>
#include <limits> #include <limits>
#include <algorithm> /* std::min and std::max for MSVC 2013 */
#include "tinyxml.h" #include "tinyxml.h"
@ -90,7 +91,7 @@ namespace ICS
// from http://www.cplusplus.com/forum/articles/9645/ // from http://www.cplusplus.com/forum/articles/9645/
template <typename T> template <typename T>
T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a T FromString ( const std::string &Text )//Text not by const reference so that the function can be used with a
{ //character array as argument { //character array as argument
std::stringstream ss(Text); std::stringstream ss(Text);
T result; T result;

View file

@ -1,6 +1,8 @@
#ifndef SDL4OGRE_SDLINPUTWRAPPER_H #ifndef SDL4OGRE_SDLINPUTWRAPPER_H
#define SDL4OGRE_SDLINPUTWRAPPER_H #define SDL4OGRE_SDLINPUTWRAPPER_H
#define NOMINMAX
#include <SDL_events.h> #include <SDL_events.h>
#include <OgreRenderWindow.h> #include <OgreRenderWindow.h>

View file

@ -4,28 +4,28 @@
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 588 433" name="_Main"> <Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 588 433" name="_Main">
<Property key="MinSize" value="380 230"/> <Property key="MinSize" value="380 230"/>
<Widget type="Widget" skin="MW_Box" position="8 8 415 381" align="Stretch" name="Client"/> <Widget type="Widget" skin="MW_Box" position="8 8 381 381" align="Stretch" name="Client"/>
<Widget type="Widget" position="13 13 391 371" align="Left Top Stretch"> <Widget type="Widget" position="13 13 357 371" align="Left Top Stretch">
<Widget type="BookPage" skin="MW_BookPage" position="0 0 391 371" name="History" align="Left Top Stretch"> <Widget type="BookPage" skin="MW_BookPage" position="0 0 357 371" name="History" align="Left Top Stretch">
</Widget> </Widget>
</Widget> </Widget>
<Widget type="MWScrollBar" skin="MW_VScroll" position="404 13 14 371" align="Right VStretch" name="VScroll"> <Widget type="MWScrollBar" skin="MW_VScroll" position="370 13 14 371" align="Right VStretch" name="VScroll">
<Property key="Visible" value="false"/> <Property key="Visible" value="false"/>
</Widget> </Widget>
<!-- The disposition bar--> <!-- The disposition bar-->
<Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="432 8 132 18" <Widget type="ProgressBar" skin="MW_EnergyBar_Blue" position="398 8 166 18"
align="Right Top" name="Disposition"> align="Right Top" name="Disposition">
<Widget type="EditBox" skin="MW_DispositionEdit" position_real="0 0 1 1" align="Stretch" name="DispositionText"/> <Widget type="EditBox" skin="MW_DispositionEdit" position_real="0 0 1 1" align="Stretch" name="DispositionText"/>
</Widget> </Widget>
<!-- The list of topics --> <!-- The list of topics -->
<Widget type="MWList" skin="MW_SimpleList" position="432 31 132 328" name="TopicsList" align="Right VStretch"> <Widget type="MWList" skin="MW_SimpleList" position="398 31 166 328" name="TopicsList" align="Right VStretch">
</Widget> </Widget>
<!-- The Goodbye button --> <!-- The Goodbye button -->
<Widget type="Button" skin="MW_Button" position="432 366 132 23" name="ByeButton" align="Right Bottom"> <Widget type="Button" skin="MW_Button" position="398 366 166 23" name="ByeButton" align="Right Bottom">
<Property key="Caption" value="#{sGoodbye}"/> <Property key="Caption" value="#{sGoodbye}"/>
</Widget> </Widget>
</Widget> </Widget>

View file

@ -2,8 +2,8 @@
<MyGUI type="Layout"> <MyGUI type="Layout">
<Widget type="VBox" skin="MW_Dialog" layer="Windows" position="0 0 600 400" name="_Main"> <Widget type="VBox" skin="MW_Dialog" layer="Windows" position="0 0 600 400" name="_Main">
<Property key="Padding" value="8"/> <Property key="Padding" value="8"/>
<Property key="Spacing" value="6"/> <Property key="Spacing" value="6"/>
<Widget type="HBox" skin=""> <Widget type="HBox" skin="">
<UserString key="HStretch" value="true"/> <UserString key="HStretch" value="true"/>
@ -49,10 +49,14 @@
</Widget> </Widget>
</Widget> </Widget>
<Widget type="HBox" skin=""> <Widget type="HBox" skin="">
<UserString key="HStretch" value="true"/> <UserString key="HStretch" value="true"/>
<Widget type="AutoSizedButton" skin="MW_Button" name="DeleteButton">
<Property key="Caption" value="#{sDeleteGame}"/>
</Widget>
<Widget type="EditBox" skin="MW_TextEdit" name="SaveNameEdit"> <Widget type="EditBox" skin="MW_TextEdit" name="SaveNameEdit">
<UserString key="HStretch" value="true"/> <UserString key="HStretch" value="true"/>
<UserString key="VStretch" value="true"/> <UserString key="VStretch" value="true"/>

View file

@ -168,6 +168,8 @@ camera y multiplier = 1.0
ui y multiplier = 1.0 ui y multiplier = 1.0
always run = false
[Game] [Game]
# Always use the most powerful attack when striking with a weapon (chop, slash or thrust) # Always use the most powerful attack when striking with a weapon (chop, slash or thrust)
best attack = false best attack = false