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
deque
mrcheko 11 years ago
commit fc6366fb27

2
.gitignore vendored

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

@ -56,7 +56,7 @@ include(OpenMWMacros)
# 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(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
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
if (APPLE)
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)
IF(NOT WIN32 AND NOT APPLE)
## Debian and non debian Linux building
# Linux building
# Paths
IF (DPKG_PROGRAM)
## Debian specific
SET(CMAKE_INSTALL_PREFIX "/usr")
SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon 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(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BSATOOL)
IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
# Install licenses
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
ENDIF (DPKG_PROGRAM)
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(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BSATOOL)
IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS)
# Install licenses
INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
# 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")

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

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

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

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

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

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

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

@ -4,9 +4,8 @@
#include "collectionbase.hpp"
#include "columnbase.hpp"
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering,
Viewing viewing, bool preview)
: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing), mPreview (preview)
CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features)
: mIdCollection (idCollection), mFeatures (features)
{}
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));
}
CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const
unsigned int CSMWorld::IdTable::getFeatures() const
{
return mReordering;
}
CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const
{
return mViewing;
}
bool CSMWorld::IdTable::hasPreview() const
{
return mPreview;
return mFeatures;
}
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 hint;
if (mViewing==Viewing_Cell)
if (mFeatures & Feature_ViewCell)
{
int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell);
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());
}
}
else if (mViewing==Viewing_Id)
else if (mFeatures & Feature_ViewId)
{
int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id);

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

@ -61,8 +61,8 @@ namespace
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 },
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 },
{ CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 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_Missing, "Cell", ":./cell.png" },
{ 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_Weapon, "Weapon", ":./weapon.png" },
{ 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_Preview, "Preview", 0 },
@ -320,6 +320,28 @@ std::vector<CSMWorld::UniversalId::Type> CSMWorld::UniversalId::listReferenceabl
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)
{
return left.isEqual (right);

@ -32,6 +32,8 @@ namespace CSMWorld
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
{
Type_None = 0,
@ -86,8 +88,8 @@ namespace CSMWorld
Type_References,
Type_Reference,
Type_RegionMap,
Type_Filter,
Type_Filters,
Type_Filter,
Type_Topics,
Type_Topic,
Type_Journals,
@ -147,6 +149,11 @@ namespace CSMWorld
///< Will return an empty string, if no icon is available.
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);

@ -332,6 +332,7 @@ void CSVWorld::EditWidget::remake(int row)
if (mMainWidget)
{
delete mMainWidget;
mMainWidget = 0;
}
mMainWidget = new QWidget (this);
@ -339,6 +340,7 @@ void CSVWorld::EditWidget::remake(int row)
if (mWidgetMapper)
{
delete mWidgetMapper;
mWidgetMapper = 0;
}
mWidgetMapper = new QDataWidgetMapper (this);
mWidgetMapper->setModel(mTable);
@ -396,7 +398,7 @@ void CSVWorld::EditWidget::remake(int row)
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->setWidgetResizable(true);
}
@ -407,7 +409,6 @@ void CSVWorld::EditWidget::remake(int row)
CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document,
const CreatorFactoryBase& creatorFactory, bool sorting) :
SubView (id),
mEditWidget(0),
mMainLayout(NULL),
@ -415,8 +416,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
mTable(dynamic_cast<CSMWorld::IdTable*>(document.getData().getTableModel(id))),
mRow (-1),
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&)));
mRow = mTable->getModelIndex (id.getId(), 0).row();
@ -440,7 +441,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
QToolButton* revertButton = new QToolButton(mainWidget);
revertButton->setIcon(QIcon(":/edit-undo.png"));
if (mTable->hasPreview())
if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview)
{
QToolButton* previewButton = new QToolButton(mainWidget);
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()));
}
if (mTable->getViewing()!=CSMWorld::IdTable::Viewing_None)
if (mTable->getFeatures() & CSMWorld::IdTable::Feature_View)
{
QToolButton* viewButton = new QToolButton(mainWidget);
viewButton->setIcon(QIcon(":/cell.png"));
@ -560,14 +561,12 @@ void CSVWorld::DialogueSubView::nextId()
void CSVWorld::DialogueSubView::setEditLock (bool locked)
{
mLocked = locked;
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(true);
} else
{
mEditWidget->setDisabled(mLocked);
}
mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked);
mCommandDispatcher.setEditLock (locked);
}
void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index)
@ -575,13 +574,8 @@ void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index)
if (index.row() == mRow)
{
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(true);
} else
{
mEditWidget->setDisabled(mLocked);
}
mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked);
}
}
@ -671,7 +665,8 @@ void CSVWorld::DialogueSubView::cloneRequest ()
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()), "");
}

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

@ -80,30 +80,35 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory<SceneSubView>);
//edit subviews
manager.add (CSMWorld::UniversalId::Type_Region,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Spell,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
// Dialogue subviews
static const CSMWorld::UniversalId::Type sTableTypes2[] =
{
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_Referenceable,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false));
CSMWorld::UniversalId::Type_None // end marker
};
manager.add (CSMWorld::UniversalId::Type_Birthsign,
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_Global,
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, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Race,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, NullCreatorFactory > (false));
manager.add (CSMWorld::UniversalId::Type_Class,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<GenericCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Referenceable,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceableCreator> > (false));
manager.add (CSMWorld::UniversalId::Type_Reference,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<ReferenceCreator> > (false));
@ -111,18 +116,6 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
manager.add (CSMWorld::UniversalId::Type_Cell,
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,
new CSVDoc::SubViewFactoryWithCreator<DialogueSubView, CreatorFactory<InfoCreator> > (false));

@ -19,14 +19,36 @@
#include "../../model/world/columns.hpp"
#include "../../model/world/tablemimedata.hpp"
#include "../../model/world/tablemimedata.hpp"
#include "../../model/world/commanddispatcher.hpp"
#include "recordstatusdelegate.hpp"
#include "util.hpp"
void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
{
// configure dispatcher
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);
/// \todo add menu items for select all and clear selection
@ -44,22 +66,27 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (mCreateAction)
menu.addAction (mCreateAction);
/// \todo Reverting temporarily disabled on tables that support reordering, because
/// revert logic currently can not handle reordering.
if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None)
if (listRevertableSelectedIds().size()>0)
menu.addAction (mRevertAction);
if (mDispatcher->canRevert())
{
menu.addAction (mRevertAction);
if (listDeletableSelectedIds().size()>0)
if (!extendedTypes.empty())
menu.addAction (mExtendedRevertAction);
}
if (mDispatcher->canDelete())
{
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
if (selectedRows.size()==1)
{
int row =selectedRows.begin()->row();
int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic);
if (column==-1)
@ -67,14 +94,17 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (column!=-1)
{
if (row>0 && mProxyModel->data (mProxyModel->index (row, column))==
mProxyModel->data (mProxyModel->index (row-1, column)))
int row = mProxyModel->mapToSource (
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);
}
if (row<mProxyModel->rowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))==
mProxyModel->data (mProxyModel->index (row+1, column)))
if (row<mModel->rowCount()-1 && mModel->data (mModel->index (row, column))==
mModel->data (mModel->index (row+1, column)))
{
menu.addAction (mMoveDownAction);
}
@ -85,7 +115,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (selectedRows.size()==1)
{
if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None)
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View)
{
int row = selectedRows.begin()->row();
@ -101,91 +131,13 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction (mViewAction);
}
if (mModel->hasPreview())
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview)
menu.addAction (mPreviewAction);
}
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,
bool createAndDelete, bool sorting, CSMDoc::Document& document)
: mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0),
@ -196,6 +148,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
mProxyModel = new CSMWorld::IdTableProxyModel (this);
mProxyModel->setSourceModel (mModel);
mDispatcher = new CSMWorld::CommandDispatcher (document, id, this);
setModel (mProxyModel);
horizontalHeader()->setResizeMode (QHeaderView::Interactive);
verticalHeader()->hide();
@ -240,11 +194,11 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
}
mRevertAction = new QAction (tr ("Revert Record"), this);
connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord()));
connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert()));
addAction (mRevertAction);
mDeleteAction = new QAction (tr ("Delete Record"), this);
connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord()));
connect (mDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeDelete()));
addAction (mDeleteAction);
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()));
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)),
this, SLOT (tableSizeUpdate()));
@ -283,53 +249,19 @@ void CSVWorld::Table::setEditLock (bool locked)
(*iter)->setEditLock (locked);
DragRecordTable::setEditLock(locked);
mDispatcher->setEditLock (locked);
}
CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const
{
return CSMWorld::UniversalId (
static_cast<CSMWorld::UniversalId::Type> (mProxyModel->data (mProxyModel->index (row, 2)).toInt()),
mProxyModel->data (mProxyModel->index (row, 0)).toString().toUtf8().constData());
}
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
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();
}
}
}
int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int typeColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType);
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();
}
}
return CSMWorld::UniversalId (
static_cast<CSMWorld::UniversalId::Type> (mModel->data (mModel->index (row, typeColumn)).toInt()),
mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData());
}
void CSVWorld::Table::editRecord()

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

@ -142,10 +142,6 @@ if(APPLE)
endif()
endif(APPLE)
if(DPKG_PROGRAM)
INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw)
endif(DPKG_PROGRAM)
if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage)
target_link_libraries(openmw gcov)

@ -357,7 +357,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create input and UI first to set up a bootstrapping environment for
// 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);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab);
mEnvironment.setInputManager (input);

@ -4,19 +4,20 @@
#include <components/version/version.hpp>
#include <components/files/configurationmanager.hpp>
#include <SDL.h>
#include <SDL_messagebox.h>
#include <SDL_main.h>
#include "engine.hpp"
#if defined(_WIN32) && !defined(_CONSOLE)
#include <boost/iostreams/concepts.hpp>
#include <boost/iostreams/stream_buffer.hpp>
#include <boost/filesystem/fstream.hpp>
#if defined(_WIN32)
// For OutputDebugString
#define WIN32_LEAN_AND_MEAN
#include <Windows.h>
// makes __argc and __argv available on windows
#include <cstdlib>
#endif
@ -253,58 +254,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
return true;
}
int main(int argc, char**argv)
{
#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 defined(_WIN32) && defined(_DEBUG)
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
{
public:
@ -318,11 +269,11 @@ public:
}
};
#else
class Logger : public boost::iostreams::sink
class Tee : public boost::iostreams::sink
{
public:
Logger(std::ofstream &stream)
: out(stream)
Tee(std::ostream &stream, std::ostream &stream2)
: out(stream), out2(stream2)
{
}
@ -330,38 +281,99 @@ public:
{
out.write (str, size);
out.flush();
out2.write (str, size);
out2.flush();
return size;
}
private:
std::ofstream &out;
std::ostream &out;
std::ostream &out2;
};
#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;
#if defined(_DEBUG)
// Redirect cout to VS debug output when running in debug mode
try
{
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;
sb.open(DebugOutput());
std::cout.rdbuf (&sb);
std::cerr.rdbuf (&sb);
#else
// Redirect cout to openmw.log
std::ofstream logfile ("openmw.log");
{
boost::iostreams::stream_buffer<Logger> sb;
sb.open (Logger (logfile));
// Redirect cout and cerr to openmw.log
boost::filesystem::ofstream logfile (boost::filesystem::path(
cfgMgr.getLogPath() / "/openmw.log"));
boost::iostreams::stream_buffer<Tee> coutsb;
std::ostream oldcout(cout_rdbuf);
coutsb.open (Tee(logfile, oldcout));
std::cout.rdbuf (&coutsb);
boost::iostreams::stream_buffer<Tee> cerrsb;
std::ostream oldcerr(cerr_rdbuf);
cerrsb.open (Tee(logfile, oldcerr));
std::cerr.rdbuf (&cerrsb);
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// Unix crash catcher
if ((argc == 2 && strcmp(argv[1], "--cc-handle-crash") == 0) || !is_debugger_attached())
{
int s[5] = { SIGSEGV, SIGILL, SIGFPE, SIGBUS, SIGABRT };
cc_install_handlers(argc, argv, 5, s, std::string(cfgMgr.getLogPath().string() + "/crash.log").c_str(), NULL);
std::cout << "Installing crash catcher" << std::endl;
}
else
std::cout << "Running in a debugger, not installing crash catcher" << std::endl;
#endif
#if OGRE_PLATFORM == OGRE_PLATFORM_APPLE
// set current dir to bundle path
boost::filesystem::path bundlePath = boost::filesystem::path(Ogre::macBundlePath()).parent_path();
boost::filesystem::current_path(bundlePath);
#endif
std::cout.rdbuf (&sb);
ret = main (__argc, __argv);
OMW::Engine engine(cfgMgr);
std::cout.rdbuf(old_rdbuf);
if (parseOptions(argc, argv, engine, cfgMgr))
{
engine.go();
}
}
catch (std::exception &e)
{
#if OGRE_PLATFORM == OGRE_PLATFORM_LINUX || OGRE_PLATFORM == OGRE_PLATFORM_APPLE
if (isatty(fileno(stdin)))
std::cerr << "\nERROR: " << e.what() << std::endl;
else
#endif
SDL_ShowSimpleMessageBox(0, "OpenMW: Fatal error", e.what(), NULL);
ret = 1;
}
// Restore cout and cerr
std::cout.rdbuf(cout_rdbuf);
std::cerr.rdbuf(cerr_rdbuf);
return ret;
}
// 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

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

@ -59,7 +59,12 @@ namespace MWBase
virtual int getJournalIndex (const std::string& id) const = 0;
///< 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;
///< Iterator pointing to the begin of the main journal.

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

@ -345,7 +345,9 @@ namespace MWClass
getCreatureStats(ptr).setAttacked(true);
// 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);
if(!successful)

@ -14,9 +14,12 @@ namespace MWClass
{
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);
if (!model.empty()) {
renderingInterface.getObjects().insertModel(ptr, model);
renderingInterface.getObjects().insertModel(ptr, model, !ref->mBase->mPersistent);
}
}

@ -140,7 +140,11 @@ namespace MWDialogue
mActorKnownTopics.clear();
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
updateTopics();
@ -294,7 +298,7 @@ namespace MWDialogue
{
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;
}
}
@ -472,7 +476,7 @@ namespace MWDialogue
{
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;
}
}
@ -694,6 +698,15 @@ namespace MWDialogue
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> result;

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

@ -5,16 +5,21 @@
#include <components/esm/journalentry.hpp>
#include <components/interpreter/defines.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwscript/interpretercontext.hpp"
namespace MWDialogue
{
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)
{
const ESM::Dialogue *dialogue =
@ -24,8 +29,17 @@ namespace MWDialogue
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == mInfoId)
{
/// \todo text replacement
mText = iter->mResponse;
if (actor.isEmpty())
{
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;
}
@ -49,8 +63,8 @@ namespace MWDialogue
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: Entry (topic, infoId), mTopic (topic)
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId, const MWWorld::Ptr& actor)
: Entry (topic, infoId, actor), mTopic (topic)
{}
JournalEntry::JournalEntry (const ESM::JournalEntry& record)
@ -65,7 +79,7 @@ namespace MWDialogue
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)
@ -90,7 +104,7 @@ namespace MWDialogue
StampedJournalEntry::StampedJournalEntry (const std::string& topic, const std::string& infoId,
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)

@ -8,6 +8,11 @@ namespace ESM
struct JournalEntry;
}
namespace MWWorld
{
class Ptr;
}
namespace MWDialogue
{
/// \brief Basic quest/dialogue/topic entry
@ -19,7 +24,8 @@ namespace MWDialogue
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);
@ -37,7 +43,7 @@ namespace MWDialogue
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);

@ -9,6 +9,7 @@
#include <components/esm/journalentry.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -103,15 +104,25 @@ namespace MWDialogue
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);
JournalEntry entry(topicId, infoId);
entry.mActorName = actorName;
JournalEntry entry(topicId, infoId, actor);
entry.mActorName = actor.getClass().getName(actor);
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
{
TQuestContainer::const_iterator iter = mQuests.find (id);

@ -38,7 +38,12 @@ namespace MWDialogue
virtual int getJournalIndex (const std::string& id) const;
///< 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;
///< Iterator pointing to the begin of the main journal.

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

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

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

@ -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;
mEnabled = true;
bool sameActor = (mPtr == actor);
mPtr = actor;
mTopicsList->setEnabled(true);
setTitle(npcName);
@ -378,9 +379,12 @@ namespace MWGui
mTopicsList->clear();
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
delete (*it);
mHistoryContents.clear();
if (resetHistory || !sameActor)
{
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)
delete (*it);

@ -111,7 +111,7 @@ namespace MWGui
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 addResponse (const std::string& text, const std::string& title="");

@ -258,12 +258,16 @@ namespace MWGui
code->addAttribute("bearing", MyGUI::utility::toString(data[i].kerning) + " "
+ MyGUI::utility::toString((fontSize-data[i].ascent)));
// More hacks! The french game uses U+2019, which is nowhere to be found in
// the CP437 encoding of the font. Fall back to 39 (regular apostrophe)
if (i == 39 && mEncoding == ToUTF8::CP437)
// More hacks! The french game uses several win1252 characters that are not included
// in the cp437 encoding of the font. Fall back to similar available characters.
// Same for U+2013
std::map<int, int> additional;
additional[39] = 0x2019; // apostrophe
additional[45] = 0x2013; // dash
if (additional.find(i) != additional.end() && mEncoding == ToUTF8::CP437)
{
MyGUI::xml::ElementPtr code = codes->createChild("Code");
code->addAttribute("index", 0x2019);
code->addAttribute("index", additional[i]);
code->addAttribute("coord", MyGUI::utility::toString(x1) + " "
+ MyGUI::utility::toString(y1) + " "
+ MyGUI::utility::toString(w) + " "

@ -136,7 +136,7 @@ namespace MWGui
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
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());
}
}

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

@ -24,6 +24,7 @@
#include "spellwindow.hpp"
#include "itemwidget.hpp"
#include "sortfilteritemmodel.hpp"
namespace MWGui
@ -134,6 +135,7 @@ namespace MWGui
}
mItemSelectionDialog->setVisible(true);
mItemSelectionDialog->openContainer(MWBase::Environment::get().getWorld()->getPlayerPtr());
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
mAssignDialog->setVisible (false);
}
@ -162,7 +164,7 @@ namespace MWGui
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
{
assert (mSelectedIndex > 0);
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
@ -184,7 +186,7 @@ namespace MWGui
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
{
assert (mSelectedIndex > 0);
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
@ -203,7 +205,7 @@ namespace MWGui
void QuickKeysMenu::onAssignMagic (const std::string& spellId)
{
assert (mSelectedIndex > 0);
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
@ -245,7 +247,7 @@ namespace MWGui
void QuickKeysMenu::activateQuickKey(int index)
{
assert (index-1 > 0);
assert (index-1 >= 0);
ItemWidget* button = mQuickKeyButtons[index-1];
QuickKeyType type = mAssigned[index-1];

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

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

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

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

@ -16,6 +16,24 @@
#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
{
@ -52,29 +70,17 @@ namespace MWGui
MWMechanics::NpcStats& npcStats = actor.getClass().getNpcStats (actor);
// NPC can train you in his best 3 skills
std::vector< std::pair<int, int> > bestSkills;
bestSkills.push_back (std::make_pair(-1, -1));
bestSkills.push_back (std::make_pair(-1, -1));
bestSkills.push_back (std::make_pair(-1, -1));
std::vector< std::pair<int, int> > skills;
for (int i=0; i<ESM::Skill::Length; ++i)
{
int value = npcStats.getSkill (i).getBase ();
for (int j=0; j<3; ++j)
{
if (value > bestSkills[j].second)
{
if (j<2)
{
bestSkills[j+1] = bestSkills[j];
}
bestSkills[j] = std::make_pair(i, value);
break;
}
}
skills.push_back(std::make_pair(i, value));
}
std::sort(skills.begin(), skills.end(), sortSkills);
MyGUI::EnumeratorWidgetPtr widgets = mTrainingOptions->getEnumerator ();
MyGUI::Gui::getInstance ().destroyWidgets (widgets);
@ -86,20 +92,20 @@ namespace MWGui
for (int i=0; i<3; ++i)
{
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::IntCoord(5, 5+i*18, mTrainingOptions->getWidth()-10, 18), MyGUI::Align::Default);
button->setEnabled(price <= playerGold);
button->setUserData(bestSkills[i].first);
button->setUserData(skills[i].first);
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);
ToolTips::createSkillToolTip (button, bestSkills[i].first);
ToolTips::createSkillToolTip (button, skills[i].first);
}
center();

@ -4,17 +4,12 @@ namespace MWGui
{
VideoWidget::VideoWidget()
: mAllowSkipping(true)
{
eventKeyButtonPressed += MyGUI::newDelegate(this, &VideoWidget::onKeyPressed);
setNeedKeyFocus(true);
}
void VideoWidget::playVideo(const std::string &video, bool allowSkipping)
void VideoWidget::playVideo(const std::string &video)
{
mAllowSkipping = allowSkipping;
mPlayer.playVideo(video);
setImageTexture(mPlayer.getTextureName());
@ -30,19 +25,13 @@ int VideoWidget::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()
{
mPlayer.update();
return mPlayer.isPlaying();
}
void VideoWidget::cleanup()
void VideoWidget::stop()
{
mPlayer.close();
}

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

@ -202,8 +202,12 @@ namespace MWGui
MyGUI::Align::Default, "Overlay");
mVideoBackground->setImageTexture("black.png");
mVideoBackground->setVisible(false);
mVideoBackground->setNeedMouseFocus(true);
mVideoBackground->setNeedKeyFocus(true);
mVideoWidget = mVideoBackground->createWidgetReal<VideoWidget>("ImageBox", 0,0,1,1, MyGUI::Align::Default);
mVideoWidget->setNeedMouseFocus(true);
mVideoWidget->setNeedKeyFocus(true);
}
void WindowManager::initUI()
@ -263,7 +267,7 @@ namespace MWGui
mCompanionWindow = new CompanionWindow(mDragAndDrop, mMessageBoxManager);
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);
@ -1559,7 +1563,15 @@ namespace MWGui
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
mRendering->getScene()->clearSpecialCaseRenderQueues();
@ -1587,7 +1599,7 @@ namespace MWGui
mRendering->getWindow()->update();
}
mVideoWidget->cleanup();
mVideoWidget->stop();
setCursorVisible(cursorWasVisible);
@ -1628,4 +1640,33 @@ namespace MWGui
if(input == mCurrentModals.top())
mCurrentModals.pop();
}
void WindowManager::onVideoKeyPressed(MyGUI::Widget *_sender, MyGUI::KeyCode _key, MyGUI::Char _char)
{
if (_key == MyGUI::KeyCode::Escape)
mVideoWidget->stop();
}
void WindowManager::pinWindow(GuiWindow window)
{
switch (window)
{
case GW_Inventory:
mInventoryWindow->setPinned(true);
break;
case GW_Map:
mMap->setPinned(true);
break;
case GW_Magic:
mSpellWindow->setPinned(true);
break;
case GW_Stats:
mStatsWindow->setPinned(true);
break;
default:
break;
}
updateVisible();
}
}

@ -12,6 +12,9 @@
#include "../mwbase/windowmanager.hpp"
#include <MyGUI_KeyCode.h>
#include <MyGUI_Types.h>
namespace MyGUI
{
class Gui;
@ -316,6 +319,8 @@ namespace MWGui
\param input Pointer to the current modal, to ensure proper modal is removed **/
virtual void removeCurrentModal(WindowModal* input);
virtual void pinWindow (MWGui::GuiWindow window);
private:
bool mConsoleOnlyScripts;
@ -424,6 +429,9 @@ namespace MWGui
void onCursorChange(const std::string& name);
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);
};
}

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

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

@ -117,7 +117,7 @@ namespace MWInput
, mPreviewPOVDelay(0.f)
, mTimeIdle(0.f)
, mOverencumberedMessageDelay(0.f)
, mAlwaysRunActive(false)
, mAlwaysRunActive(Settings::Manager::getBool("always run", "Input"))
, mAttemptJump(false)
, mControlsDisabled(false)
{
@ -807,8 +807,9 @@ namespace MWInput
if (MyGUI::InputManager::getInstance ().isModalAny())
return;
// Toggle between game mode and journal mode
if(!MWBase::Environment::get().getWindowManager()->isGuiMode() && MWBase::Environment::get().getWindowManager ()->getJournalAllowed())
if((!MWBase::Environment::get().getWindowManager()->isGuiMode()
|| 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().getWindowManager()->pushGuiMode(MWGui::GM_Journal);
@ -817,7 +818,6 @@ namespace MWInput
{
MWBase::Environment::get().getWindowManager()->exitCurrentGuiMode();
}
// .. but don't touch any other mode.
}
void InputManager::quickKey (int index)
@ -857,6 +857,8 @@ namespace MWInput
{
if (MWBase::Environment::get().getWindowManager()->isGuiMode()) return;
mAlwaysRunActive = !mAlwaysRunActive;
Settings::Manager::setBool("always run", "Input", mAlwaysRunActive);
}
void InputManager::resetIdleTime()
@ -955,11 +957,6 @@ namespace MWInput
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)

@ -832,6 +832,8 @@ bool CharacterController::updateWeaponState()
MWRender::Animation::Group_UpperBody, false,
weapSpeed, mAttackType+" max attack", mAttackType+" min hit",
1.0f-complete, 0);
complete = 0.f;
mUpperBodyState = UpperCharState_MaxAttackToMinHit;
}
else if (mHitState == CharState_KnockDown)

@ -929,7 +929,10 @@ namespace MWMechanics
else if (type == OT_Murder)
arg = store.find("iCrimeKilling")->getInt();
else if (type == OT_Theft)
{
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}");
ptr.getClass().getNpcStats(ptr).setBounty(ptr.getClass().getNpcStats(ptr).getBounty()

@ -356,28 +356,40 @@ void Animation::addExtraLight(Ogre::SceneManager *sceneMgr, NifOgre::ObjectScene
objlist->mControllers.push_back(Ogre::Controller<Ogre::Real>(src, dest, func));
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,
// so we ignore lights if their attenuation falls below this factor.
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 attenuation = fallback->getFallbackFloat("LightAttenuation_LinearValue") / r;
float activationRange = 1.0f / (threshold * attenuation);
olight->setAttenuation(activationRange, 0, attenuation, 0);
float r = radius * quadraticRadiusMult;
quadraticAttenuation = quadraticValue / std::pow(r, 2);
activationRange = std::sqrt(1.0f / (threshold * quadraticAttenuation));
}
else
if (useLinear)
{
float r = radius * fallback->getFallbackFloat("LightAttenuation_QuadraticRadiusMult");
float attenuation = fallback->getFallbackFloat("LightAttenuation_QuadraticValue") / std::pow(r, 2);
float activationRange = std::sqrt(1.0f / (threshold * attenuation));
olight->setAttenuation(activationRange, 0, 0, attenuation);
float r = radius * linearRadiusMult;
linearAttenuation = linearValue / r;
activationRange = std::max(activationRange, 1.0f / (threshold * linearAttenuation));
}
olight->setAttenuation(activationRange, 0, linearAttenuation, quadraticAttenuation);
// 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"))
objlist->mSkelBase->attachObjectToBone("AttachLight", olight);

@ -73,7 +73,7 @@ void Objects::insertBegin(const MWWorld::Ptr& ptr)
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);
@ -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()].merge(bounds);
if(ptr.getTypeName() == typeid(ESM::Static).name() &&
if(batch &&
Settings::Manager::getBool("use static geometry", "Objects") &&
anim->canBatch())
{

@ -41,7 +41,7 @@ public:
, mRootNode(NULL)
{}
~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);

@ -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)
{
@ -256,6 +268,8 @@ namespace MWScript
interpreter.installSegment5 (Compiler::Dialogue::opcodeSameFactionExplicit, new OpSameFaction<ExplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeModFactionReaction, new OpModFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeGetFactionReaction, new OpGetFactionReaction);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActor, new OpClearInfoActor<ImplicitRef>);
interpreter.installSegment5 (Compiler::Dialogue::opcodeClearInfoActorExplicit, new OpClearInfoActor<ExplicitRef>);
}
}

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

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

@ -179,6 +179,7 @@ namespace MWSound
if(!mOutput->isInitialized())
return;
std::cout <<"Playing "<<filename<< std::endl;
mLastPlayedMusic = filename;
try
{
stopMusic();
@ -203,19 +204,34 @@ namespace MWSound
void SoundManager::startRandomTitle()
{
Ogre::StringVector filelist;
Ogre::StringVector groups = Ogre::ResourceGroupManager::getSingleton().getResourceGroups ();
for (Ogre::StringVector::iterator it = groups.begin(); it != groups.end(); ++it)
if (mMusicFiles.find(mCurrentPlaylist) == mMusicFiles.end())
{
Ogre::StringVectorPtr resourcesInThisGroup = mResourceMgr.findResourceNames(*it,
"Music/"+mCurrentPlaylist+"/*");
filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->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,
"Music/"+mCurrentPlaylist+"/*");
filelist.insert(filelist.end(), resourcesInThisGroup->begin(), resourcesInThisGroup->end());
}
mMusicFiles[mCurrentPlaylist] = filelist;
}
else
filelist = mMusicFiles[mCurrentPlaylist];
if(!filelist.size())
return;
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]);
}

@ -31,6 +31,10 @@ namespace MWSound
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 mSFXVolume;
float mMusicVolume;

@ -54,9 +54,28 @@ void MWState::Character::addSlot (const ESM::SavedGame& profile)
Slot slot;
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();
// 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.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)
: mPath (saves), mNext (0)
: mPath (saves)
{
if (!boost::filesystem::is_directory (mPath))
{
@ -82,13 +101,6 @@ MWState::Character::Character (const boost::filesystem::path& saves, const std::
addSlot (slotPath, game);
}
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());

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

@ -3,12 +3,13 @@
#include <sstream>
#include <stdexcept>
#include <cctype> // std::isalnum
#include <boost/filesystem.hpp>
MWState::CharacterManager::CharacterManager (const boost::filesystem::path& saves,
const std::string& game)
: mPath (saves), mNext (0), mCurrent (0), mGame (game)
: mPath (saves), mCurrent (0), mGame (game)
{
if (!boost::filesystem::is_directory (mPath))
{
@ -28,21 +29,14 @@ MWState::CharacterManager::CharacterManager (const boost::filesystem::path& save
if (character.begin()!=character.end())
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)
createCharacter();
createCharacter(name);
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;
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();
// 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));
mCurrent = &mCharacters.back();

@ -10,7 +10,6 @@ namespace MWState
class CharacterManager
{
boost::filesystem::path mPath;
int mNext;
// Uses std::list, so that mCurrent stays valid when characters are deleted
std::list<Character> mCharacters;
@ -32,13 +31,15 @@ namespace MWState
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 name The character name to use in case a new character is created.
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
/// \param name Name for the character (does not need to be unique)
void setCurrentCharacter (const Character *character);

@ -184,9 +184,9 @@ void MWState::StateManager::saveGame (const std::string& description, const Slot
encoded->read(&profile.mScreenshot[0], encoded->size());
if (!slot)
slot = mCharacterManager.getCurrentCharacter()->createSlot (profile);
slot = getCurrentCharacter()->createSlot (profile);
else
slot = mCharacterManager.getCurrentCharacter()->updateSlot (slot, profile);
slot = getCurrentCharacter()->updateSlot (slot, profile);
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();
if (stream.fail())
throw std::runtime_error("Write operation failed");
Settings::Manager::setString ("character", "Saves",
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;
buttons.push_back("#{sOk}");
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)
{
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()
@ -433,11 +443,12 @@ void MWState::StateManager::update (float duration)
if (mAskLoadRecent)
{
int iButton = MWBase::Environment::get().getWindowManager()->readPressedButton();
if(iButton==0)
MWState::Character *curCharacter = getCurrentCharacter(false);
if(iButton==0 && curCharacter)
{
mAskLoadRecent = false;
//Load last saved game for current character
MWState::Character *curCharacter = getCurrentCharacter();
MWState::Slot lastSave = *curCharacter->begin();
loadGame(curCharacter, &lastSave);
}

@ -99,6 +99,13 @@ void ESMStore::load(ESM::ESMReader &esm, Loading::Listener* listener)
}
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) {
dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id));
} else {

@ -193,6 +193,11 @@ namespace MWWorld
const ESM::Position &refpos = ptr.getRefData().getPosition();
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? */
OEngine::Physic::PhysicActor *physicActor = engine->getCharacter(ptr.getRefData().getHandle());
if(!physicActor || !physicActor->getCollisionMode())
@ -299,7 +304,7 @@ namespace MWWorld
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
tracer.doTrace(colobj, newPosition, nextpos, engine);

@ -3,22 +3,8 @@
namespace MWWorld {
void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
void Store<ESM::Cell>::handleMovedCellRefs(ESM::ESMReader& esm, ESM::Cell* cell)
{
// 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
while (esm.isNextSub("MVRF")) {
ESM::CellRef ref;
@ -43,35 +29,58 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
else
*iter = ref;
}
}
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 at least partially!
// All cells have a name record, even nameless exterior cells.
std::string idLower = Misc::StringUtils::lowerCase(id);
ESM::Cell cell;
cell.mName = id;
//Second part of cell loading
cell->postLoad(esm);
// 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)
if(cell.mData.mFlags & ESM::Cell::Interior)
{
// Store interior cell by name, try to merge with existing parent data.
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower));
if (oldcell) {
// push the new references on the list of references to manage
oldcell->mContextList.push_back(cell->mContextList.at(0));
// copy list into new cell
cell->mContextList = oldcell->mContextList;
// have new cell replace old cell
ESM::Cell::merge(oldcell, cell);
// merge new cell into old cell
// push the new references on the list of references to manage (saveContext = true)
oldcell->mData = cell.mData;
oldcell->loadCell(esm, true);
} else
mInt[idLower] = *cell;
{
// spawn a new cell
cell.loadCell(esm, true);
mInt[idLower] = cell;
}
}
else
{
// 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) {
// 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
oldcell->mContextList.push_back(cell->mContextList.at(0));
// copy list into new cell
cell->mContextList = oldcell->mContextList;
oldcell->postLoad(esm);
// 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
ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefNum);
if (itold != oldcell->mMovedRefs.end()) {
@ -82,13 +91,24 @@ void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
*itold = *it;
}
}
cell->mMovedRefs = oldcell->mMovedRefs;
// have new cell replace old cell
ESM::Cell::merge(oldcell, cell);
// We don't need to merge mLeasedRefs of cell / oldcell. This list is filled when another cell moves a
// 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
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;
}
}

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

@ -1703,14 +1703,15 @@ namespace MWWorld
Ogre::Vector3 orig =
Ogre::Vector3(pos.pos);
orig.z += 20;
Ogre::Vector3 dir = Ogre::Vector3(0, 0, -1);
float len = (pos.pos[2] >= 0) ? pos.pos[2] : -pos.pos[2];
len += 100.0;
float len = 100.0;
std::pair<bool, Ogre::Vector3> hit =
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
int origCount = object.getRefData().getCount();

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

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

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

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

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

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

@ -50,18 +50,14 @@ namespace ESM
return ref.mRefNum == refNum;
}
void Cell::load(ESMReader &esm, bool saveContext)
{
// 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);
loadData(esm);
loadCell(esm, saveContext);
}
void Cell::loadCell(ESMReader &esm, bool saveContext)
{
mNAM0 = 0;
if (mData.mFlags & Interior)
@ -73,12 +69,10 @@ void Cell::load(ESMReader &esm, bool saveContext)
esm.getHT(waterl);
mWater = (float) waterl;
mWaterInt = true;
mHasWaterLevelRecord = true;
}
else if (esm.isNextSub("WHGT"))
{
esm.getHT(mWater);
mHasWaterLevelRecord = true;
}
// 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
{
this->load(esm, false);
@ -124,14 +130,12 @@ void Cell::save(ESMWriter &esm) const
esm.writeHNT("DATA", mData, 12);
if (mData.mFlags & Interior)
{
if (mHasWaterLevelRecord) {
if (mWaterInt) {
int water =
(mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5);
esm.writeHNT("INTV", water);
} else {
esm.writeHNT("WHGT", mWater);
}
if (mWaterInt) {
int water =
(mWater >= 0) ? (int) (mWater + 0.5) : (int) (mWater - 0.5);
esm.writeHNT("INTV", water);
} else {
esm.writeHNT("WHGT", mWater);
}
if (mData.mFlags & QuasiEx)
@ -228,19 +232,6 @@ bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
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 id;

@ -78,10 +78,7 @@ struct Cell
float mFogDensity;
};
Cell() : mWater(0), mHasWaterLevelRecord(false) {}
/// Merge \a modified into \a original
static void merge (Cell* original, Cell* modified);
Cell() : mWater(0) {}
// Interior cells are indexed by this (it's the 'id'), for exterior
// cells it is optional.
@ -93,8 +90,8 @@ struct Cell
std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
DATAstruct mData;
AMBIstruct mAmbi;
float mWater; // Water level
bool mHasWaterLevelRecord;
bool mWaterInt;
int mMapColor;
int mNAM0;
@ -109,7 +106,10 @@ struct Cell
// 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.
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;
bool isExterior() const

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

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

@ -177,42 +177,49 @@ struct KeyListT {
KeyT<T> key;
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);
mKeys.push_back(key);
}
else if(mInterpolationType == sQuadraticInterpolation)
}
else if(mInterpolationType == sQuadraticInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readQuadratic(nifReference, key);
mKeys.push_back(key);
}
else if(mInterpolationType == sTBCInterpolation)
}
else if(mInterpolationType == sTBCInterpolation)
{
for(size_t i = 0;i < count;i++)
{
readTBC(nifReference, 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:

@ -32,6 +32,9 @@ namespace Translation
boost::filesystem::ifstream stream (
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())
throw std::runtime_error ("failed to open translation file: " + fileName);
@ -41,6 +44,7 @@ namespace Translation
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;
while (!stream.eof())
{

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

@ -576,7 +576,7 @@ WARN_LOGFILE =
INPUT = apps \
components \
libs \
Docs
docs
# 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

@ -576,7 +576,7 @@ WARN_LOGFILE =
INPUT = apps \
components \
libs \
Docs
docs
# 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

@ -41,8 +41,6 @@ namespace ICS
this->mActive = active;
this->fillSDLKeysMap();
ICS_LOG("Channel count = " + ToString<size_t>(channelCount) );
for(size_t i=0;i<channelCount;i++)
{
@ -310,9 +308,6 @@ namespace ICS
mControlsMouseButtonBinderMap.clear();
mControlsJoystickButtonBinderMap.clear();
mKeys.clear();
mKeyCodes.clear();
ICS_LOG(" - InputControlSystem deleted - ");
}
@ -433,7 +428,7 @@ namespace ICS
{
TiXmlElement keyBinder( "KeyBinder" );
keyBinder.SetAttribute( "key", keyCodeToString(
keyBinder.SetAttribute( "key", ToString<int>(
getKeyBinding(*o, Control/*::ControlChangingDirection*/::INCREASE)).c_str() );
keyBinder.SetAttribute( "direction", "INCREASE" );
control.InsertEndChild(keyBinder);
@ -443,7 +438,7 @@ namespace ICS
{
TiXmlElement keyBinder( "KeyBinder" );
keyBinder.SetAttribute( "key", keyCodeToString(
keyBinder.SetAttribute( "key", ToString<int>(
getKeyBinding(*o, Control/*::ControlChangingDirection*/::DECREASE)).c_str() );
keyBinder.SetAttribute( "direction", "DECREASE" );
control.InsertEndChild(keyBinder);
@ -806,128 +801,14 @@ namespace ICS
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)
{
return mKeyCodes[key];
return std::string(SDL_GetKeyName(key));
}
SDL_Keycode InputControlSystem::stringToKeyCode(std::string key)
{
return mKeys[key];
return SDL_GetKeyFromName(key.c_str());
}
void InputControlSystem::adjustMouseRegion(Uint16 width, Uint16 height)

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

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

@ -36,6 +36,7 @@ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
#include <map>
#include <list>
#include <limits>
#include <algorithm> /* std::min and std::max for MSVC 2013 */
#include "tinyxml.h"
@ -90,7 +91,7 @@ namespace ICS
// from http://www.cplusplus.com/forum/articles/9645/
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
std::stringstream ss(Text);
T result;

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

@ -4,28 +4,28 @@
<Widget type="Window" skin="MW_Window" layer="Windows" position="0 0 588 433" name="_Main">
<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="BookPage" skin="MW_BookPage" position="0 0 391 371" name="History" 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 357 371" name="History" align="Left Top Stretch">
</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"/>
</Widget>
<!-- 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">
<Widget type="EditBox" skin="MW_DispositionEdit" position_real="0 0 1 1" align="Stretch" name="DispositionText"/>
</Widget>
<!-- 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>
<!-- 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}"/>
</Widget>
</Widget>

@ -2,8 +2,8 @@
<MyGUI type="Layout">
<Widget type="VBox" skin="MW_Dialog" layer="Windows" position="0 0 600 400" name="_Main">
<Property key="Padding" value="8"/>
<Property key="Spacing" value="6"/>
<Property key="Padding" value="8"/>
<Property key="Spacing" value="6"/>
<Widget type="HBox" skin="">
<UserString key="HStretch" value="true"/>
@ -49,10 +49,14 @@
</Widget>
</Widget>
</Widget>
<Widget type="HBox" skin="">
<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">
<UserString key="HStretch" value="true"/>
<UserString key="VStretch" value="true"/>

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

Loading…
Cancel
Save