Merge remote-tracking branch 'refs/remotes/master/master' into NonTableFields

This commit is contained in:
Marek Kochanowicz 2014-06-30 20:06:44 +02:00
commit 172f1a1301
307 changed files with 7096 additions and 2714 deletions

2
.gitignore vendored
View file

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

View file

@ -56,7 +56,7 @@ include(OpenMWMacros)
# doxygen main page # doxygen main page
configure_file ("${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/Docs/mainpage.hpp") configure_file ("${OpenMW_SOURCE_DIR}/docs/mainpage.hpp.cmake" "${OpenMW_SOURCE_DIR}/docs/mainpage.hpp")
option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE) option(MYGUI_STATIC "Link static build of Mygui into the binaries" FALSE)
option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE) option(OGRE_STATIC "Link static build of Ogre and Ogre Plugins into the binaries" FALSE)
@ -80,13 +80,6 @@ option(USE_FFMPEG "use ffmpeg for sound" ON)
# OS X deployment # OS X deployment
option(OPENMW_OSX_DEPLOYMENT OFF) option(OPENMW_OSX_DEPLOYMENT OFF)
if(UNIX AND NOT APPLE)
option(BUILD_WITH_DPKG "enable dpkg-based install for debian and debian derivatives" OFF)
if(BUILD_WITH_DPKG)
find_program(DPKG_PROGRAM dpkg DOC "dpkg program of Debian-based systems")
endif(BUILD_WITH_DPKG)
endif(UNIX AND NOT APPLE)
# Location of morrowind data files # Location of morrowind data files
if (APPLE) if (APPLE)
set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files") set(MORROWIND_DATA_FILES "./data" CACHE PATH "location of Morrowind data files")
@ -395,46 +388,36 @@ if (CMAKE_COMPILER_IS_GNUCC)
endif (CMAKE_COMPILER_IS_GNUCC) endif (CMAKE_COMPILER_IS_GNUCC)
IF(NOT WIN32 AND NOT APPLE) IF(NOT WIN32 AND NOT APPLE)
## Debian and non debian Linux building # Linux building
# Paths # Paths
IF (DPKG_PROGRAM) SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
## Debian specific SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(CMAKE_INSTALL_PREFIX "/usr") SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(DATAROOTDIR "share" CACHE PATH "Sets the root of data directories to a non-default location") SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(DATADIR "share/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location") SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
SET(ICONDIR "share/pixmaps" CACHE PATH "Set icon dir") SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
SET(SYSCONFDIR "../etc/openmw" CACHE PATH "Set config dir")
ELSE ()
## Non debian specific
SET(BINDIR "${CMAKE_INSTALL_PREFIX}/bin" CACHE PATH "Where to install binaries")
SET(DATAROOTDIR "${CMAKE_INSTALL_PREFIX}/share" CACHE PATH "Sets the root of data directories to a non-default location")
SET(DATADIR "${DATAROOTDIR}/games/openmw" CACHE PATH "Sets the openmw data directories to a non-default location")
SET(ICONDIR "${DATAROOTDIR}/pixmaps" CACHE PATH "Set icon dir")
SET(LICDIR "${DATAROOTDIR}/licenses/openmw" CACHE PATH "Sets the openmw license directory to a non-default location.")
SET(SYSCONFDIR "/etc/openmw" CACHE PATH "Set config dir")
# Install binaries # Install binaries
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/openmw" DESTINATION "${BINDIR}" )
IF(BUILD_LAUNCHER) IF(BUILD_LAUNCHER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/omwlauncher" DESTINATION "${BINDIR}" )
ENDIF(BUILD_LAUNCHER) ENDIF(BUILD_LAUNCHER)
IF(BUILD_BSATOOL) IF(BUILD_BSATOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/bsatool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_BSATOOL) ENDIF(BUILD_BSATOOL)
IF(BUILD_ESMTOOL) IF(BUILD_ESMTOOL)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/esmtool" DESTINATION "${BINDIR}" )
ENDIF(BUILD_ESMTOOL) ENDIF(BUILD_ESMTOOL)
IF(BUILD_MWINIIMPORTER) IF(BUILD_MWINIIMPORTER)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/mwiniimport" DESTINATION "${BINDIR}" )
ENDIF(BUILD_MWINIIMPORTER) ENDIF(BUILD_MWINIIMPORTER)
IF(BUILD_OPENCS) IF(BUILD_OPENCS)
INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" ) INSTALL(PROGRAMS "${OpenMW_BINARY_DIR}/opencs" DESTINATION "${BINDIR}" )
ENDIF(BUILD_OPENCS) ENDIF(BUILD_OPENCS)
# Install licenses # Install licenses
INSTALL(FILES "DejaVu Font License.txt" DESTINATION "${LICDIR}" ) INSTALL(FILES "docs/license/DejaVu Font License.txt" DESTINATION "${LICDIR}" )
INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" ) INSTALL(FILES "extern/shiny/License.txt" DESTINATION "${LICDIR}" RENAME "Shiny License.txt" )
ENDIF (DPKG_PROGRAM)
# Install icon and desktop file # Install icon and desktop file
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.desktop" DESTINATION "${DATAROOTDIR}/applications" PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ COMPONENT "openmw")
@ -463,8 +446,8 @@ if(WIN32)
INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg") INSTALL(FILES "${OpenMW_BINARY_DIR}/openmw.cfg.install" DESTINATION "." RENAME "openmw.cfg")
INSTALL(FILES INSTALL(FILES
"${OpenMW_SOURCE_DIR}/readme.txt" "${OpenMW_SOURCE_DIR}/readme.txt"
"${OpenMW_SOURCE_DIR}/GPL3.txt" "${OpenMW_SOURCE_DIR}/Docs/license/GPL3.txt"
"${OpenMW_SOURCE_DIR}/DejaVu Font License.txt" "${OpenMW_SOURCE_DIR}/Docs/license/DejaVu Font License.txt"
"${OpenMW_BINARY_DIR}/settings-default.cfg" "${OpenMW_BINARY_DIR}/settings-default.cfg"
"${OpenMW_BINARY_DIR}/transparency-overrides.cfg" "${OpenMW_BINARY_DIR}/transparency-overrides.cfg"
"${OpenMW_BINARY_DIR}/Release/openmw.exe" "${OpenMW_BINARY_DIR}/Release/openmw.exe"
@ -630,8 +613,10 @@ if (WIN32)
4127 # Conditional expression is constant 4127 # Conditional expression is constant
4242 # Storing value in a variable of a smaller type, possible loss of data 4242 # Storing value in a variable of a smaller type, possible loss of data
4244 # Storing value of one type in variable of another (size_t in int, for example) 4244 # Storing value of one type in variable of another (size_t in int, for example)
4267 # Conversion from 'size_t' to 'int', possible loss of data
4305 # Truncating value (double to float, for example) 4305 # Truncating value (double to float, for example)
4309 # Variable overflow, trying to store 128 in a signed char for example 4309 # Variable overflow, trying to store 128 in a signed char for example
4351 # New behavior: elements of array 'array' will be default initialized (desired behavior)
4355 # Using 'this' in member initialization list 4355 # Using 'this' in member initialization list
4505 # Unreferenced local function has been removed 4505 # Unreferenced local function has been removed
4701 # Potentially uninitialized local variable used 4701 # Potentially uninitialized local variable used
@ -650,7 +635,9 @@ if (WIN32)
set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101") set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101")
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${SHINY_OGRE_WARNINGS}) set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS ${SHINY_OGRE_WARNINGS})
set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS ${WARNINGS})
set_target_properties(oics PROPERTIES COMPILE_FLAGS ${WARNINGS}) # oics uses tinyxml, which has an initialized but unused variable
set(OICS_WARNINGS "${WARNINGS} /wd4189")
set_target_properties(oics PROPERTIES COMPILE_FLAGS ${OICS_WARNINGS})
set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(components PROPERTIES COMPILE_FLAGS ${WARNINGS})
if (BUILD_LAUNCHER) if (BUILD_LAUNCHER)
set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS}) set_target_properties(omwlauncher PROPERTIES COMPILE_FLAGS ${WARNINGS})

View file

@ -682,13 +682,11 @@ void Record<ESM::Faction>::print()
{ {
std::cout << " Name: " << mData.mName << std::endl; std::cout << " Name: " << mData.mName << std::endl;
std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl; std::cout << " Hidden: " << mData.mData.mIsHidden << std::endl;
if (mData.mData.mUnknown != -1)
std::cout << " Unknown: " << mData.mData.mUnknown << std::endl;
std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0]) std::cout << " Attribute1: " << attributeLabel(mData.mData.mAttribute[0])
<< " (" << mData.mData.mAttribute[0] << ")" << std::endl; << " (" << mData.mData.mAttribute[0] << ")" << std::endl;
std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1]) std::cout << " Attribute2: " << attributeLabel(mData.mData.mAttribute[1])
<< " (" << mData.mData.mAttribute[1] << ")" << std::endl; << " (" << mData.mData.mAttribute[1] << ")" << std::endl;
for (int i = 0; i != 6; i++) for (int i = 0; i < 7; i++)
if (mData.mData.mSkills[i] != -1) if (mData.mData.mSkills[i] != -1)
std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i]) std::cout << " Skill: " << skillLabel(mData.mData.mSkills[i])
<< " (" << mData.mData.mSkills[i] << ")" << std::endl; << " (" << mData.mData.mSkills[i] << ")" << std::endl;

View file

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

View file

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

View file

@ -71,7 +71,7 @@ opencs_units (view/render
opencs_units_noqt (view/render opencs_units_noqt (view/render
navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight
lightingbright lightingbright object cell
) )
opencs_units_noqt (view/world opencs_units_noqt (view/world
@ -200,10 +200,6 @@ target_link_libraries(opencs
components components
) )
if(DPKG_PROGRAM)
INSTALL(TARGETS opencs RUNTIME DESTINATION games COMPONENT opencs)
endif()
if(APPLE) if(APPLE)
INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE) INSTALL(TARGETS opencs BUNDLE DESTINATION OpenMW COMPONENT BUNDLE)
endif() endif()

View file

@ -31,8 +31,8 @@ CSMDoc::DocumentManager::DocumentManager (const Files::ConfigurationManager& con
&mLoader, SLOT (loadDocument (CSMDoc::Document *))); &mLoader, SLOT (loadDocument (CSMDoc::Document *)));
connect (&mLoader, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)), connect (&mLoader, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)),
this, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int))); this, SIGNAL (nextStage (CSMDoc::Document *, const std::string&, int)));
connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *)), connect (&mLoader, SIGNAL (nextRecord (CSMDoc::Document *, int)),
this, SIGNAL (nextRecord (CSMDoc::Document *))); this, SIGNAL (nextRecord (CSMDoc::Document *, int)));
connect (this, SIGNAL (cancelLoading (CSMDoc::Document *)), connect (this, SIGNAL (cancelLoading (CSMDoc::Document *)),
&mLoader, SLOT (abortLoading (CSMDoc::Document *))); &mLoader, SLOT (abortLoading (CSMDoc::Document *)));
connect (&mLoader, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)), connect (&mLoader, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)),

View file

@ -79,9 +79,10 @@ namespace CSMDoc
void loadingStopped (CSMDoc::Document *document, bool completed, void loadingStopped (CSMDoc::Document *document, bool completed,
const std::string& error); const std::string& error);
void nextStage (CSMDoc::Document *document, const std::string& name, int steps); void nextStage (CSMDoc::Document *document, const std::string& name,
int totalRecords);
void nextRecord (CSMDoc::Document *document); void nextRecord (CSMDoc::Document *document, int records);
void cancelLoading (CSMDoc::Document *document); void cancelLoading (CSMDoc::Document *document);

View file

@ -8,7 +8,7 @@
#include "document.hpp" #include "document.hpp"
#include "state.hpp" #include "state.hpp"
CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLeft (false) {} CSMDoc::Loader::Stage::Stage() : mFile (0), mRecordsLoaded (0), mRecordsLeft (false) {}
CSMDoc::Loader::Loader() CSMDoc::Loader::Loader()
@ -39,13 +39,14 @@ void CSMDoc::Loader::load()
Document *document = iter->first; Document *document = iter->first;
int size = static_cast<int> (document->getContentFiles().size()); int size = static_cast<int> (document->getContentFiles().size());
int editedIndex = size-1; // index of the file to be edited/created
if (document->isNew()) if (document->isNew())
--size; --size;
bool done = false; bool done = false;
const int batchingSize = 100; const int batchingSize = 50;
try try
{ {
@ -58,17 +59,21 @@ void CSMDoc::Loader::load()
iter->second.mRecordsLeft = false; iter->second.mRecordsLeft = false;
break; break;
} }
else
++(iter->second.mRecordsLoaded);
CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0); CSMWorld::UniversalId log (CSMWorld::UniversalId::Type_LoadErrorLog, 0);
{ // silence a g++ warning
for (CSMDoc::Stage::Messages::const_iterator iter (messages.begin()); for (CSMDoc::Stage::Messages::const_iterator iter (messages.begin());
iter!=messages.end(); ++iter) iter!=messages.end(); ++iter)
{ {
document->getReport (log)->add (iter->first, iter->second); document->getReport (log)->add (iter->first, iter->second);
emit loadMessage (document, iter->second); emit loadMessage (document, iter->second);
} }
}
emit nextRecord (document); emit nextRecord (document, iter->second.mRecordsLoaded);
return; return;
} }
@ -77,17 +82,19 @@ void CSMDoc::Loader::load()
{ {
boost::filesystem::path path = document->getContentFiles()[iter->second.mFile]; boost::filesystem::path path = document->getContentFiles()[iter->second.mFile];
int steps = document->getData().startLoading (path, iter->second.mFile<size-1, false); int steps = document->getData().startLoading (path, iter->second.mFile!=editedIndex, false);
iter->second.mRecordsLeft = true; iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0;
emit nextStage (document, path.filename().string(), steps/batchingSize); emit nextStage (document, path.filename().string(), steps);
} }
else if (iter->second.mFile==size) else if (iter->second.mFile==size)
{ {
int steps = document->getData().startLoading (document->getProjectPath(), false, true); int steps = document->getData().startLoading (document->getProjectPath(), false, true);
iter->second.mRecordsLeft = true; iter->second.mRecordsLeft = true;
iter->second.mRecordsLoaded = 0;
emit nextStage (document, "Project File", steps/batchingSize); emit nextStage (document, "Project File", steps);
} }
else else
{ {

View file

@ -18,6 +18,7 @@ namespace CSMDoc
struct Stage struct Stage
{ {
int mFile; int mFile;
int mRecordsLoaded;
bool mRecordsLeft; bool mRecordsLeft;
Stage(); Stage();
@ -56,9 +57,10 @@ namespace CSMDoc
///< Document load has been interrupted either because of a call to abortLoading ///< Document load has been interrupted either because of a call to abortLoading
/// or a problem during loading). In the former case error will be an empty string. /// or a problem during loading). In the former case error will be an empty string.
void nextStage (CSMDoc::Document *document, const std::string& name, int steps); void nextStage (CSMDoc::Document *document, const std::string& name,
int totalRecords);
void nextRecord (CSMDoc::Document *document); void nextRecord (CSMDoc::Document *document, int records);
///< \note This signal is only given once per group of records. The group size is ///< \note This signal is only given once per group of records. The group size is
/// approximately the total number of records divided by the steps value of the /// approximately the total number of records divided by the steps value of the
/// previous nextStage signal. /// previous nextStage signal.

View file

@ -42,7 +42,7 @@ void CSMTools::FactionCheckStage::perform (int stage, Messages& messages)
// test for non-unique skill // test for non-unique skill
std::map<int, int> skills; // ID, number of occurrences std::map<int, int> skills; // ID, number of occurrences
for (int i=0; i<6; ++i) for (int i=0; i<7; ++i)
if (faction.mData.mSkills[i]!=-1) if (faction.mData.mSkills[i]!=-1)
++skills[faction.mData.mSkills[i]]; ++skills[faction.mData.mSkills[i]];

View file

@ -217,7 +217,7 @@ namespace CSMWorld
bool hasEnums (ColumnId column); bool hasEnums (ColumnId column);
std::vector<std::string> getEnums (ColumnId column); std::vector<std::string> getEnums (ColumnId column);
///< Returns an empty vector, if \æ column isn't an enum type column. ///< Returns an empty vector, if \a column isn't an enum type column.
} }
} }

View file

@ -1,6 +1,10 @@
#include "commanddispatcher.hpp" #include "commanddispatcher.hpp"
#include <algorithm>
#include <components/misc/stringops.hpp>
#include "../doc/document.hpp" #include "../doc/document.hpp"
#include "idtable.hpp" #include "idtable.hpp"
@ -88,6 +92,13 @@ void CSMWorld::CommandDispatcher::setEditLock (bool locked)
void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection) void CSMWorld::CommandDispatcher::setSelection (const std::vector<std::string>& selection)
{ {
mSelection = 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 bool CSMWorld::CommandDispatcher::canDelete() const
@ -106,6 +117,20 @@ bool CSMWorld::CommandDispatcher::canRevert() const
return getRevertableRecords().size()!=0; 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() void CSMWorld::CommandDispatcher::executeDelete()
{ {
if (mLocked) if (mLocked)
@ -163,3 +188,80 @@ void CSMWorld::CommandDispatcher::executeRevert()
if (rows.size()>1) if (rows.size()>1)
mDocument.getUndoStack().endMacro(); mDocument.getUndoStack().endMacro();
} }
void CSMWorld::CommandDispatcher::executeExtendedDelete()
{
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records"));
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
iter!=mExtendedTypes.end(); ++iter)
{
if (*iter==mId)
executeDelete();
else if (*iter==UniversalId::Type_References)
{
IdTable& model = dynamic_cast<IdTable&> (
*mDocument.getData().getTableModel (*iter));
const RefCollection& collection = mDocument.getData().getReferences();
int size = collection.getSize();
for (int i=size-1; i>=0; --i)
{
const Record<CellRef>& record = collection.getRecord (i);
if (record.mState==RecordBase::State_Deleted)
continue;
if (!std::binary_search (mSelection.begin(), mSelection.end(),
Misc::StringUtils::lowerCase (record.get().mCell)))
continue;
mDocument.getUndoStack().push (
new CSMWorld::DeleteCommand (model, record.get().mId));
}
}
}
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().endMacro();
}
void CSMWorld::CommandDispatcher::executeExtendedRevert()
{
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records"));
for (std::vector<UniversalId>::const_iterator iter (mExtendedTypes.begin());
iter!=mExtendedTypes.end(); ++iter)
{
if (*iter==mId)
executeRevert();
else if (*iter==UniversalId::Type_References)
{
IdTable& model = dynamic_cast<IdTable&> (
*mDocument.getData().getTableModel (*iter));
const RefCollection& collection = mDocument.getData().getReferences();
int size = collection.getSize();
for (int i=size-1; i>=0; --i)
{
const Record<CellRef>& record = collection.getRecord (i);
if (!std::binary_search (mSelection.begin(), mSelection.end(),
Misc::StringUtils::lowerCase (record.get().mCell)))
continue;
mDocument.getUndoStack().push (
new CSMWorld::RevertCommand (model, record.get().mId));
}
}
}
if (mExtendedTypes.size()>1)
mDocument.getUndoStack().endMacro();
}

View file

@ -22,6 +22,7 @@ namespace CSMWorld
CSMDoc::Document& mDocument; CSMDoc::Document& mDocument;
UniversalId mId; UniversalId mId;
std::vector<std::string> mSelection; std::vector<std::string> mSelection;
std::vector<UniversalId> mExtendedTypes;
std::vector<std::string> getDeletableRecords() const; std::vector<std::string> getDeletableRecords() const;
@ -37,16 +38,31 @@ namespace CSMWorld
void setSelection (const std::vector<std::string>& selection); 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 canDelete() const;
bool canRevert() 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: public slots:
void executeDelete(); void executeDelete();
void executeRevert(); void executeRevert();
void executeExtendedDelete();
void executeExtendedRevert();
}; };
} }

View file

@ -102,7 +102,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding)
mFactions.addColumn (new AttributesColumn<ESM::Faction> (0)); mFactions.addColumn (new AttributesColumn<ESM::Faction> (0));
mFactions.addColumn (new AttributesColumn<ESM::Faction> (1)); mFactions.addColumn (new AttributesColumn<ESM::Faction> (1));
mFactions.addColumn (new HiddenColumn<ESM::Faction>); mFactions.addColumn (new HiddenColumn<ESM::Faction>);
for (int i=0; i<6; ++i) for (int i=0; i<7; ++i)
mFactions.addColumn (new SkillsColumn<ESM::Faction> (i)); mFactions.addColumn (new SkillsColumn<ESM::Faction> (i));
mRaces.addColumn (new StringIdColumn<ESM::Race>); mRaces.addColumn (new StringIdColumn<ESM::Race>);

View file

@ -45,7 +45,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false));
baseColumns.mId = &mColumns.back(); baseColumns.mId = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState, mColumns.push_back (RefIdColumn (Columns::ColumnId_Modification, ColumnBase::Display_RecordState,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, true, false));
baseColumns.mModified = &mColumns.back(); baseColumns.mModified = &mColumns.back();
mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType, mColumns.push_back (RefIdColumn (Columns::ColumnId_RecordType, ColumnBase::Display_RefRecordType,
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false)); ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue, false, false));

View file

@ -45,7 +45,7 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
mFileProgress->setValue (0); mFileProgress->setValue (0);
// record progress // record progress
mLayout->addWidget (new QLabel ("Records", this)); mLayout->addWidget (mRecords = new QLabel ("Records", this));
mRecordProgress = new QProgressBar (this); mRecordProgress = new QProgressBar (this);
@ -75,22 +75,30 @@ CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel())); connect (mButtons, SIGNAL (rejected()), this, SLOT (cancel()));
} }
void CSVDoc::LoadingDocument::nextStage (const std::string& name, int steps) void CSVDoc::LoadingDocument::nextStage (const std::string& name, int totalRecords)
{ {
mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str())); mFile->setText (QString::fromUtf8 (("Loading: " + name).c_str()));
mFileProgress->setValue (mFileProgress->value()+1); mFileProgress->setValue (mFileProgress->value()+1);
mRecordProgress->setValue (0); mRecordProgress->setValue (0);
mRecordProgress->setMaximum (steps>0 ? steps : 1); mRecordProgress->setMaximum (totalRecords>0 ? totalRecords : 1);
mTotalRecords = totalRecords;
} }
void CSVDoc::LoadingDocument::nextRecord() void CSVDoc::LoadingDocument::nextRecord (int records)
{ {
int value = mRecordProgress->value()+1; if (records<=mTotalRecords)
{
mRecordProgress->setValue (records);
if (value<=mRecordProgress->maximum()) std::ostringstream stream;
mRecordProgress->setValue (value);
stream << "Records: " << records << " of " << mTotalRecords;
mRecords->setText (QString::fromUtf8 (stream.str().c_str()));
}
} }
void CSVDoc::LoadingDocument::abort (const std::string& error) void CSVDoc::LoadingDocument::abort (const std::string& error)
@ -168,20 +176,21 @@ void CSVDoc::Loader::loadingStopped (CSMDoc::Document *document, bool completed,
} }
} }
void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name, int steps) void CSVDoc::Loader::nextStage (CSMDoc::Document *document, const std::string& name,
int totalRecords)
{ {
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document); std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end()) if (iter!=mDocuments.end())
iter->second->nextStage (name, steps); iter->second->nextStage (name, totalRecords);
} }
void CSVDoc::Loader::nextRecord (CSMDoc::Document *document) void CSVDoc::Loader::nextRecord (CSMDoc::Document *document, int records)
{ {
std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document); std::map<CSMDoc::Document *, LoadingDocument *>::iterator iter = mDocuments.find (document);
if (iter!=mDocuments.end()) if (iter!=mDocuments.end())
iter->second->nextRecord(); iter->second->nextRecord (records);
} }
void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& message) void CSVDoc::Loader::loadMessage (CSMDoc::Document *document, const std::string& message)

View file

@ -26,6 +26,7 @@ namespace CSVDoc
CSMDoc::Document *mDocument; CSMDoc::Document *mDocument;
QLabel *mFile; QLabel *mFile;
QLabel *mRecords;
QProgressBar *mFileProgress; QProgressBar *mFileProgress;
QProgressBar *mRecordProgress; QProgressBar *mRecordProgress;
bool mAborted; bool mAborted;
@ -33,6 +34,7 @@ namespace CSVDoc
QLabel *mError; QLabel *mError;
QListWidget *mMessages; QListWidget *mMessages;
QVBoxLayout *mLayout; QVBoxLayout *mLayout;
int mTotalRecords;
private: private:
@ -42,9 +44,9 @@ namespace CSVDoc
LoadingDocument (CSMDoc::Document *document); LoadingDocument (CSMDoc::Document *document);
void nextStage (const std::string& name, int steps); void nextStage (const std::string& name, int totalRecords);
void nextRecord(); void nextRecord (int records);
void abort (const std::string& error); void abort (const std::string& error);
@ -88,9 +90,9 @@ namespace CSVDoc
void loadingStopped (CSMDoc::Document *document, bool completed, void loadingStopped (CSMDoc::Document *document, bool completed,
const std::string& error); const std::string& error);
void nextStage (CSMDoc::Document *document, const std::string& name, int steps); void nextStage (CSMDoc::Document *document, const std::string& name, int totalRecords);
void nextRecord (CSMDoc::Document *document); void nextRecord (CSMDoc::Document *document, int records);
void loadMessage (CSMDoc::Document *document, const std::string& message); void loadMessage (CSMDoc::Document *document, const std::string& message);
}; };

View file

@ -97,8 +97,8 @@ CSVDoc::ViewManager::ViewManager (CSMDoc::DocumentManager& documentManager)
&mLoader, SLOT (nextStage (CSMDoc::Document *, const std::string&, int))); &mLoader, SLOT (nextStage (CSMDoc::Document *, const std::string&, int)));
connect ( connect (
&mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *)), &mDocumentManager, SIGNAL (nextRecord (CSMDoc::Document *, int)),
&mLoader, SLOT (nextRecord (CSMDoc::Document *))); &mLoader, SLOT (nextRecord (CSMDoc::Document *, int)));
connect ( connect (
&mDocumentManager, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)), &mDocumentManager, SIGNAL (loadMessage (CSMDoc::Document *, const std::string&)),

View file

@ -35,7 +35,11 @@ void CSVFilter::FilterBox::setRecordFilter (const std::string& filter)
void CSVFilter::FilterBox::dropEvent (QDropEvent* event) void CSVFilter::FilterBox::dropEvent (QDropEvent* event)
{ {
std::vector<CSMWorld::UniversalId> data = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData())->getData(); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
std::vector<CSMWorld::UniversalId> data = mime->getData();
emit recordDropped(data, event->proposedAction()); emit recordDropped(data, event->proposedAction());
} }

View file

@ -0,0 +1,201 @@
#include "cell.hpp"
#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <components/misc/stringops.hpp>
#include "../../model/world/idtable.hpp"
#include "../../model/world/columns.hpp"
#include "../../model/world/data.hpp"
bool CSVRender::Cell::removeObject (const std::string& id)
{
std::map<std::string, Object *>::iterator iter =
mObjects.find (Misc::StringUtils::lowerCase (id));
if (iter==mObjects.end())
return false;
delete iter->second;
mObjects.erase (iter);
return true;
}
bool CSVRender::Cell::addObjects (int start, int end)
{
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);
int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
bool modified = false;
for (int i=start; i<=end; ++i)
{
std::string cell = Misc::StringUtils::lowerCase (references.data (
references.index (i, cellColumn)).toString().toUtf8().constData());
int state = references.data (references.index (i, stateColumn)).toInt();
if (cell==mId && state!=CSMWorld::RecordBase::State_Deleted)
{
std::string id = Misc::StringUtils::lowerCase (references.data (
references.index (i, idColumn)).toString().toUtf8().constData());
mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false)));
modified = true;
}
}
return modified;
}
CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
const std::string& id, const Ogre::Vector3& origin)
: mData (data), mId (Misc::StringUtils::lowerCase (id))
{
mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
mCellNode->setPosition (origin);
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
int rows = references.rowCount();
addObjects (0, rows-1);
}
CSVRender::Cell::~Cell()
{
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
delete iter->second;
mCellNode->getCreator()->destroySceneNode (mCellNode);
}
bool CSVRender::Cell::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
bool modified = false;
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
if (iter->second->referenceableDataChanged (topLeft, bottomRight))
modified = true;
return modified;
}
bool CSVRender::Cell::referenceableAboutToBeRemoved (const QModelIndex& parent, int start,
int end)
{
if (parent.isValid())
return false;
bool modified = false;
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
if (iter->second->referenceableAboutToBeRemoved (parent, start, end))
modified = true;
return modified;
}
bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);
int stateColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification);
// list IDs in cell
std::map<std::string, bool> ids; // id, deleted state
for (int i=topLeft.row(); i<=bottomRight.row(); ++i)
{
std::string cell = Misc::StringUtils::lowerCase (references.data (
references.index (i, cellColumn)).toString().toUtf8().constData());
if (cell==mId)
{
std::string id = Misc::StringUtils::lowerCase (references.data (
references.index (i, idColumn)).toString().toUtf8().constData());
int state = references.data (references.index (i, stateColumn)).toInt();
ids.insert (std::make_pair (id, state==CSMWorld::RecordBase::State_Deleted));
}
}
// perform update and remove where needed
bool modified = false;
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)
{
if (iter->second->referenceDataChanged (topLeft, bottomRight))
modified = true;
std::map<std::string, bool>::iterator iter2 = ids.find (iter->first);
if (iter2!=ids.end())
{
if (iter2->second)
{
removeObject (iter->first);
modified = true;
}
ids.erase (iter2);
}
}
// add new objects
for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter)
{
mObjects.insert (std::make_pair (
iter->first, new Object (mData, mCellNode, iter->first, false)));
modified = true;
}
return modified;
}
bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int start,
int end)
{
if (parent.isValid())
return false;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References));
int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
bool modified = false;
for (int row = start; row<=end; ++row)
if (removeObject (references.data (
references.index (row, idColumn)).toString().toUtf8().constData()))
modified = true;
return modified;
}
bool CSVRender::Cell::referenceAdded (const QModelIndex& parent, int start, int end)
{
if (parent.isValid())
return false;
return addObjects (start, end);
}

View file

@ -0,0 +1,73 @@
#ifndef OPENCS_VIEW_CELL_H
#define OPENCS_VIEW_CELL_H
#include <string>
#include <map>
#include <OgreVector3.h>
#include "object.hpp"
class QModelIndex;
namespace Ogre
{
class SceneManager;
class SceneNode;
}
namespace CSMWorld
{
class Data;
}
namespace CSVRender
{
class Cell
{
CSMWorld::Data& mData;
std::string mId;
Ogre::SceneNode *mCellNode;
std::map<std::string, Object *> mObjects;
/// Ignored if cell does not have an object with the given ID.
///
/// \return Was the object deleted?
bool removeObject (const std::string& id);
/// Add objects from reference table that are within this cell.
///
/// \return Have any objects been added?
bool addObjects (int start, int end);
public:
Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
const std::string& id, const Ogre::Vector3& origin = Ogre::Vector3 (0, 0, 0));
~Cell();
/// \return Did this call result in a modification of the visual representation of
/// this cell?
bool referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight);
/// \return Did this call result in a modification of the visual representation of
/// this cell?
bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);
/// \return Did this call result in a modification of the visual representation of
/// this cell?
bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
/// \return Did this call result in a modification of the visual representation of
/// this cell?
bool referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
/// \return Did this call result in a modification of the visual representation of
/// this cell?
bool referenceAdded (const QModelIndex& parent, int start, int end);
};
}
#endif

View file

@ -0,0 +1,213 @@
#include "object.hpp"
#include <OgreSceneManager.h>
#include <OgreSceneNode.h>
#include <OgreEntity.h>
#include "../../model/world/data.hpp"
#include "../../model/world/ref.hpp"
#include "../../model/world/refidcollection.hpp"
void CSVRender::Object::clearSceneNode (Ogre::SceneNode *node)
{
for (Ogre::SceneNode::ObjectIterator iter = node->getAttachedObjectIterator();
iter.hasMoreElements(); )
{
Ogre::MovableObject* object = dynamic_cast<Ogre::MovableObject*> (iter.getNext());
node->getCreator()->destroyMovableObject (object);
}
for (Ogre::SceneNode::ChildNodeIterator iter = node->getChildIterator();
iter.hasMoreElements(); )
{
Ogre::SceneNode* childNode = dynamic_cast<Ogre::SceneNode*> (iter.getNext());
clearSceneNode (childNode);
node->getCreator()->destroySceneNode (childNode);
}
}
void CSVRender::Object::clear()
{
mObject.setNull();
clearSceneNode (mBase);
}
void CSVRender::Object::update()
{
clear();
std::string model;
int error = 0; // 1 referemceanöe does not exist, 2 referenceable does not specify a mesh
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
int index = referenceables.searchId (mReferenceableId);
if (index==-1)
error = 1;
else
{
/// \todo check for Deleted state (error 1)
model = referenceables.getData (index,
referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Model)).
toString().toUtf8().constData();
if (model.empty())
error = 2;
}
if (error)
{
Ogre::Entity* entity = mBase->getCreator()->createEntity (Ogre::SceneManager::PT_CUBE);
entity->setMaterialName("BaseWhite"); /// \todo adjust material according to error
mBase->attachObject (entity);
}
else
{
mObject = NifOgre::Loader::createObjects (mBase, "Meshes\\" + model);
}
}
void CSVRender::Object::adjust()
{
if (mReferenceId.empty())
return;
const CSMWorld::CellRef& reference = getReference();
// position
if (!mForceBaseToZero)
mBase->setPosition (Ogre::Vector3 (
reference.mPos.pos[0], reference.mPos.pos[1], reference.mPos.pos[2]));
// orientation
Ogre::Quaternion xr (Ogre::Radian (-reference.mPos.rot[0]), Ogre::Vector3::UNIT_X);
Ogre::Quaternion yr (Ogre::Radian (-reference.mPos.rot[1]), Ogre::Vector3::UNIT_Y);
Ogre::Quaternion zr (Ogre::Radian (-reference.mPos.rot[2]), Ogre::Vector3::UNIT_Z);
mBase->setOrientation (xr*yr*zr);
// scale
mBase->setScale (reference.mScale, reference.mScale, reference.mScale);
}
const CSMWorld::CellRef& CSVRender::Object::getReference() const
{
if (mReferenceId.empty())
throw std::logic_error ("object does not represent a reference");
return mData.getReferences().getRecord (mReferenceId).get();
}
CSVRender::Object::Object (const CSMWorld::Data& data, Ogre::SceneNode *cellNode,
const std::string& id, bool referenceable, bool forceBaseToZero)
: mData (data), mBase (0), mForceBaseToZero (forceBaseToZero)
{
mBase = cellNode->createChildSceneNode();
if (referenceable)
{
mReferenceableId = id;
}
else
{
mReferenceId = id;
mReferenceableId = getReference().mRefID;
}
update();
adjust();
}
CSVRender::Object::~Object()
{
clear();
if (mBase)
mBase->getCreator()->destroySceneNode (mBase);
}
bool CSVRender::Object::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
int index = referenceables.searchId (mReferenceableId);
if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row())
{
update();
adjust();
return true;
}
return false;
}
bool CSVRender::Object::referenceableAboutToBeRemoved (const QModelIndex& parent, int start,
int end)
{
const CSMWorld::RefIdCollection& referenceables = mData.getReferenceables();
int index = referenceables.searchId (mReferenceableId);
if (index!=-1 && index>=start && index<=end)
{
// Deletion of referenceable-type objects is handled outside of Object.
if (!mReferenceId.empty())
{
update();
adjust();
return true;
}
}
return false;
}
bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
if (mReferenceId.empty())
return false;
const CSMWorld::RefCollection& references = mData.getReferences();
int index = references.searchId (mReferenceId);
if (index!=-1 && index>=topLeft.row() && index<=bottomRight.row())
{
int columnIndex =
references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId);
if (columnIndex>=topLeft.column() && columnIndex<=bottomRight.row())
{
mReferenceableId =
references.getData (index, columnIndex).toString().toUtf8().constData();
update();
}
adjust();
return true;
}
return false;
}
std::string CSVRender::Object::getReferenceId() const
{
return mReferenceId;
}
std::string CSVRender::Object::getReferenceableId() const
{
return mReferenceableId;
}

View file

@ -0,0 +1,80 @@
#ifndef OPENCS_VIEW_OBJECT_H
#define OPENCS_VIEW_OBJECT_H
#include <components/nifogre/ogrenifloader.hpp>
class QModelIndex;
namespace Ogre
{
class SceneNode;
}
namespace CSMWorld
{
class Data;
class CellRef;
}
namespace CSVRender
{
class Object
{
const CSMWorld::Data& mData;
std::string mReferenceId;
std::string mReferenceableId;
Ogre::SceneNode *mBase;
NifOgre::ObjectScenePtr mObject;
bool mForceBaseToZero;
/// Not implemented
Object (const Object&);
/// Not implemented
Object& operator= (const Object&);
/// Destroy all scene nodes and movable objects attached to node.
static void clearSceneNode (Ogre::SceneNode *node);
/// Remove object from node (includes deleting)
void clear();
/// Update model
void update();
/// Adjust position, orientation and scale
void adjust();
/// Throws an exception if *this was constructed with referenceable
const CSMWorld::CellRef& getReference() const;
public:
Object (const CSMWorld::Data& data, Ogre::SceneNode *cellNode,
const std::string& id, bool referenceable, bool forceBaseToZero = false);
/// \param forceBaseToZero If this is a reference ignore the coordinates and place
/// it at 0, 0, 0 instead.
~Object();
/// \return Did this call result in a modification of the visual representation of
/// this object?
bool referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight);
/// \return Did this call result in a modification of the visual representation of
/// this object?
bool referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);
/// \return Did this call result in a modification of the visual representation of
/// this object?
bool referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
/// Returns an empty string if this is a refereceable-type object.
std::string getReferenceId() const;
std::string getReferenceableId() const;
};
}
#endif

View file

@ -3,14 +3,137 @@
#include <sstream> #include <sstream>
#include <OgreCamera.h>
#include <QtGui/qevent.h> #include <QtGui/qevent.h>
#include <apps/opencs/model/world/tablemimedata.hpp> #include "../../model/world/tablemimedata.hpp"
#include "../../model/world/idtable.hpp"
bool CSVRender::PagedWorldspaceWidget::adjustCells()
{
bool modified = false;
bool setCamera = false;
{
// remove
std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
while (iter!=mCells.end())
{
if (!mSelection.has (iter->first))
{
delete iter->second;
mCells.erase (iter++);
modified = true;
}
else
++iter;
}
}
if (mCells.begin()==mCells.end())
setCamera = true;
// add
for (CSMWorld::CellSelection::Iterator iter (mSelection.begin()); iter!=mSelection.end();
++iter)
{
if (mCells.find (*iter)==mCells.end())
{
if (setCamera)
{
setCamera = false;
getCamera()->setPosition (8192*iter->getX()+4096, 8192*iter->getY()+4096, 0);
}
mCells.insert (std::make_pair (*iter,
new Cell (mDocument.getData(), getSceneManager(),
iter->getId ("std::default"))));
modified = true;
}
}
return modified;
}
void CSVRender::PagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
if (iter->second->referenceableDataChanged (topLeft, bottomRight))
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::referenceableAboutToBeRemoved (
const QModelIndex& parent, int start, int end)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
if (iter->second->referenceableAboutToBeRemoved (parent, start, end))
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent,
int start, int end)
{
CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (
*mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables));
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
{
QModelIndex topLeft = referenceables.index (start, 0);
QModelIndex bottomRight =
referenceables.index (end, referenceables.columnCount());
if (iter->second->referenceableDataChanged (topLeft, bottomRight))
flagAsModified();
}
}
void CSVRender::PagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
if (iter->second->referenceDataChanged (topLeft, bottomRight))
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent,
int start, int end)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
if (iter->second->referenceAboutToBeRemoved (parent, start, end))
flagAsModified();
}
void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start,
int end)
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
if (iter->second->referenceAdded (parent, start, end))
flagAsModified();
}
CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document) CSVRender::PagedWorldspaceWidget::PagedWorldspaceWidget (QWidget* parent, CSMDoc::Document& document)
: WorldspaceWidget (document, parent) : WorldspaceWidget (document, parent), mDocument (document)
{} {}
CSVRender::PagedWorldspaceWidget::~PagedWorldspaceWidget()
{
for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
iter!=mCells.end(); ++iter)
delete iter->second;
}
void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint) void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)
{ {
if (!hint.empty()) if (!hint.empty())
@ -47,6 +170,10 @@ void CSVRender::PagedWorldspaceWidget::useViewHint (const std::string& hint)
void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection) void CSVRender::PagedWorldspaceWidget::setCellSelection (const CSMWorld::CellSelection& selection)
{ {
mSelection = selection; mSelection = selection;
if (adjustCells())
flagAsModified();
emit cellSelectionChanged (mSelection); emit cellSelectionChanged (mSelection);
} }
@ -72,6 +199,9 @@ void CSVRender::PagedWorldspaceWidget::handleDrop (const std::vector< CSMWorld::
} }
if (selectionChanged) if (selectionChanged)
{ {
if (adjustCells())
flagAsModified();
emit cellSelectionChanged(mSelection); emit cellSelectionChanged(mSelection);
} }
} }

View file

@ -1,9 +1,12 @@
#ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H #ifndef OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H
#define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_PAGEDWORLDSPACEWIDGET_H
#include <map>
#include "../../model/world/cellselection.hpp" #include "../../model/world/cellselection.hpp"
#include "worldspacewidget.hpp" #include "worldspacewidget.hpp"
#include "cell.hpp"
namespace CSVRender namespace CSVRender
{ {
@ -11,12 +14,32 @@ namespace CSVRender
{ {
Q_OBJECT Q_OBJECT
CSMDoc::Document& mDocument;
CSMWorld::CellSelection mSelection; CSMWorld::CellSelection mSelection;
std::map<CSMWorld::CellCoordinates, Cell *> mCells;
private: private:
std::pair<int, int> getCoordinatesFromId(const std::string& record) const; std::pair<int, int> getCoordinatesFromId(const std::string& record) const;
/// Bring mCells into sync with mSelection again.
///
/// \return Any cells added or removed?
bool adjustCells();
virtual void referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight);
virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void referenceableAdded (const QModelIndex& index, int start, int end);
virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void referenceAdded (const QModelIndex& index, int start, int end);
public: public:
PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document); PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);
@ -24,6 +47,8 @@ namespace CSVRender
/// no cells are displayed. The cells to be displayed will be specified later through /// no cells are displayed. The cells to be displayed will be specified later through
/// hint system. /// hint system.
virtual ~PagedWorldspaceWidget();
void useViewHint (const std::string& hint); void useViewHint (const std::string& hint);
void setCellSelection (const CSMWorld::CellSelection& selection); void setCellSelection (const CSMWorld::CellSelection& selection);

View file

@ -7,194 +7,119 @@
#include "../../model/world/data.hpp" #include "../../model/world/data.hpp"
#include "../../model/world/idtable.hpp" #include "../../model/world/idtable.hpp"
void CSVRender::PreviewWidget::setup() CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,
const std::string& id, bool referenceable, QWidget *parent)
: SceneWidget (parent), mData (data),
mObject (data, getSceneManager()->getRootSceneNode(), id, referenceable, true)
{ {
setNavigation (&mOrbit); setNavigation (&mOrbit);
mNode = getSceneManager()->getRootSceneNode()->createChildSceneNode();
mNode->setPosition (Ogre::Vector3 (0, 0, 0));
setModel();
QAbstractItemModel *referenceables = QAbstractItemModel *referenceables =
mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables); mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables);
connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (ReferenceableDataChanged (const QModelIndex&, const QModelIndex&))); this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&)));
connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (ReferenceableAboutToBeRemoved (const QModelIndex&, int, int))); this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));
}
void CSVRender::PreviewWidget::setModel() if (!referenceable)
{
if (mNode)
{ {
mObject.setNull(); QAbstractItemModel *references =
mData.getTableModel (CSMWorld::UniversalId::Type_References);
if (mReferenceableId.empty()) connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
return; this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&)));
connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
int column = this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int)));
mData.getReferenceables().findColumnIndex (CSMWorld::Columns::ColumnId_Model);
int row = mData.getReferenceables().searchId (mReferenceableId);
if (row==-1)
return;
QVariant value = mData.getReferenceables().getData (row, column);
if (!value.isValid())
return;
std::string model = value.toString().toUtf8().constData();
if (model.empty())
return;
mObject = NifOgre::Loader::createObjects (mNode, "Meshes\\" + model);
} }
} }
void CSVRender::PreviewWidget::adjust() void CSVRender::PreviewWidget::referenceableDataChanged (const QModelIndex& topLeft,
{
if (mNode)
{
int row = mData.getReferences().getIndex (mReferenceId);
float scale = mData.getReferences().getData (row, mData.getReferences().
findColumnIndex (CSMWorld::Columns::ColumnId_Scale)).toFloat();
float rotX = mData.getReferences().getData (row, mData.getReferences().
findColumnIndex (CSMWorld::Columns::ColumnId_PositionXRot)).toFloat();
float rotY = mData.getReferences().getData (row, mData.getReferences().
findColumnIndex (CSMWorld::Columns::ColumnId_PositionYRot)).toFloat();
float rotZ = mData.getReferences().getData (row, mData.getReferences().
findColumnIndex (CSMWorld::Columns::ColumnId_PositionZRot)).toFloat();
mNode->setScale (scale, scale, scale);
Ogre::Quaternion xr (Ogre::Radian(-rotX), Ogre::Vector3::UNIT_X);
Ogre::Quaternion yr (Ogre::Radian(-rotY), Ogre::Vector3::UNIT_Y);
Ogre::Quaternion zr (Ogre::Radian(-rotZ), Ogre::Vector3::UNIT_Z);
mNode->setOrientation (xr*yr*zr);
}
}
CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,
const std::string& referenceableId, QWidget *parent)
: SceneWidget (parent), mData (data), mNode (0), mReferenceableId (referenceableId)
{
setup();
}
CSVRender::PreviewWidget::PreviewWidget (CSMWorld::Data& data,
const std::string& referenceableId, const std::string& referenceId, QWidget *parent)
: SceneWidget (parent), mData (data), mReferenceableId (referenceableId),
mReferenceId (referenceId)
{
setup();
adjust();
QAbstractItemModel *references =
mData.getTableModel (CSMWorld::UniversalId::Type_References);
connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (ReferenceDataChanged (const QModelIndex&, const QModelIndex&)));
connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (ReferenceAboutToBeRemoved (const QModelIndex&, int, int)));
}
void CSVRender::PreviewWidget::ReferenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
if (mReferenceableId.empty()) if (mObject.referenceableDataChanged (topLeft, bottomRight))
return;
CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables));
QModelIndex index = referenceables.getModelIndex (mReferenceableId, 0);
if (index.row()>=topLeft.row() && index.row()<=bottomRight.row())
{
/// \todo possible optimisation; check columns and only update if relevant columns have
/// changed
setModel();
flagAsModified(); flagAsModified();
if (mObject.getReferenceId().empty())
{
CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables));
QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(),
referenceables.findColumnIndex (CSMWorld::Columns::ColumnId_Modification));
if (referenceables.data (index).toInt()==CSMWorld::RecordBase::State_Deleted)
emit closeRequest();
} }
} }
void CSVRender::PreviewWidget::ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, void CSVRender::PreviewWidget::referenceableAboutToBeRemoved (const QModelIndex& parent, int start,
int end) int end)
{ {
if (mReferenceableId.empty()) if (mObject.referenceableAboutToBeRemoved (parent, start, end))
flagAsModified();
if (mObject.getReferenceableId().empty())
return; return;
CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& referenceables = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables)); *mData.getTableModel (CSMWorld::UniversalId::Type_Referenceables));
QModelIndex index = referenceables.getModelIndex (mReferenceableId, 0); QModelIndex index = referenceables.getModelIndex (mObject.getReferenceableId(), 0);
if (index.row()>=start && index.row()<=end) if (index.row()>=start && index.row()<=end)
{ {
if (mReferenceId.empty()) if (mObject.getReferenceId().empty())
{ {
// this is a preview for a referenceble // this is a preview for a referenceble
emit closeRequest(); emit closeRequest();
} }
else
{
// this is a preview for a reference
mObject.setNull();
flagAsModified();
}
} }
} }
void CSVRender::PreviewWidget::ReferenceDataChanged (const QModelIndex& topLeft, void CSVRender::PreviewWidget::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) const QModelIndex& bottomRight)
{ {
if (mReferenceId.empty()) if (mObject.referenceDataChanged (topLeft, bottomRight))
flagAsModified();
if (mObject.getReferenceId().empty())
return; return;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References)); *mData.getTableModel (CSMWorld::UniversalId::Type_References));
// check for deleted state
{
QModelIndex index = references.getModelIndex (mObject.getReferenceId(),
references.findColumnIndex (CSMWorld::Columns::ColumnId_Modification));
if (references.data (index).toInt()==CSMWorld::RecordBase::State_Deleted)
{
emit closeRequest();
return;
}
}
int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId); int columnIndex = references.findColumnIndex (CSMWorld::Columns::ColumnId_ReferenceableId);
QModelIndex index = references.getModelIndex (mReferenceId, columnIndex); QModelIndex index = references.getModelIndex (mObject.getReferenceId(), columnIndex);
if (index.row()>=topLeft.row() && index.row()<=bottomRight.row()) if (index.row()>=topLeft.row() && index.row()<=bottomRight.row())
{
/// \todo possible optimisation; check columns and only update if relevant columns have
/// changed
adjust();
if (index.column()>=topLeft.column() && index.column()<=bottomRight.row()) if (index.column()>=topLeft.column() && index.column()<=bottomRight.row())
{ emit referenceableIdChanged (mObject.getReferenceableId());
mReferenceableId = references.data (index).toString().toUtf8().constData();
emit referenceableIdChanged (mReferenceableId);
setModel();
}
flagAsModified();
}
} }
void CSVRender::PreviewWidget::ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, void CSVRender::PreviewWidget::referenceAboutToBeRemoved (const QModelIndex& parent, int start,
int end) int end)
{ {
if (mReferenceId.empty()) if (mObject.getReferenceId().empty())
return; return;
CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> ( CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
*mData.getTableModel (CSMWorld::UniversalId::Type_References)); *mData.getTableModel (CSMWorld::UniversalId::Type_References));
QModelIndex index = references.getModelIndex (mReferenceId, 0); QModelIndex index = references.getModelIndex (mObject.getReferenceId(), 0);
if (index.row()>=start && index.row()<=end) if (index.row()>=start && index.row()<=end)
emit closeRequest(); emit closeRequest();

View file

@ -1,11 +1,10 @@
#ifndef OPENCS_VIEW_PREVIEWWIDGET_H #ifndef OPENCS_VIEW_PREVIEWWIDGET_H
#define OPENCS_VIEW_PREVIEWWIDGET_H #define OPENCS_VIEW_PREVIEWWIDGET_H
#include <components/nifogre/ogrenifloader.hpp>
#include "scenewidget.hpp" #include "scenewidget.hpp"
#include "navigationorbit.hpp" #include "navigationorbit.hpp"
#include "object.hpp"
class QModelIndex; class QModelIndex;
@ -22,26 +21,13 @@ namespace CSVRender
CSMWorld::Data& mData; CSMWorld::Data& mData;
CSVRender::NavigationOrbit mOrbit; CSVRender::NavigationOrbit mOrbit;
NifOgre::ObjectScenePtr mObject; Object mObject;
Ogre::SceneNode *mNode;
std::string mReferenceId;
std::string mReferenceableId;
void setup();
void setModel();
void adjust();
///< Adjust referenceable preview according to the reference
public: public:
PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId, PreviewWidget (CSMWorld::Data& data, const std::string& id, bool referenceable,
QWidget *parent = 0); QWidget *parent = 0);
PreviewWidget (CSMWorld::Data& data, const std::string& referenceableId,
const std::string& referenceId, QWidget *parent = 0);
signals: signals:
void closeRequest(); void closeRequest();
@ -50,14 +36,14 @@ namespace CSVRender
private slots: private slots:
void ReferenceableDataChanged (const QModelIndex& topLeft, void referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight); const QModelIndex& bottomRight);
void ReferenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end); void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);
void ReferenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
void ReferenceAboutToBeRemoved (const QModelIndex& parent, int start, int end); void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
}; };
} }

View file

@ -43,7 +43,7 @@ namespace CSVRender
mCamera->setPosition (300, 0, 0); mCamera->setPosition (300, 0, 0);
mCamera->lookAt (0, 0, 0); mCamera->lookAt (0, 0, 0);
mCamera->setNearClipDistance (0.1); mCamera->setNearClipDistance (0.1);
mCamera->setFarClipDistance (30000); mCamera->setFarClipDistance (300000); ///< \todo make this configurable
mCamera->roll (Ogre::Degree (90)); mCamera->roll (Ogre::Degree (90));
setLighting (&mLightingDay); setLighting (&mLightingDay);
@ -87,7 +87,7 @@ namespace CSVRender
std::stringstream windowHandle; std::stringstream windowHandle;
#ifdef WIN32 #ifdef WIN32
windowHandle << Ogre::StringConverter::toString((unsigned long)(this->winId())); windowHandle << Ogre::StringConverter::toString((uintptr_t)(this->winId()));
#else #else
windowHandle << this->winId(); windowHandle << this->winId();
#endif #endif
@ -137,6 +137,11 @@ namespace CSVRender
return mSceneMgr; return mSceneMgr;
} }
Ogre::Camera *SceneWidget::getCamera()
{
return mCamera;
}
void SceneWidget::flagAsModified() void SceneWidget::flagAsModified()
{ {
mUpdate = true; mUpdate = true;

View file

@ -49,6 +49,8 @@ namespace CSVRender
Ogre::SceneManager *getSceneManager(); Ogre::SceneManager *getSceneManager();
Ogre::Camera *getCamera();
void flagAsModified(); void flagAsModified();
void setDefaultAmbient (const Ogre::ColourValue& colour); void setDefaultAmbient (const Ogre::ColourValue& colour);

View file

@ -21,6 +21,8 @@ void CSVRender::UnpagedWorldspaceWidget::update()
setDefaultAmbient (colour); setDefaultAmbient (colour);
/// \todo deal with mSunlight and mFog/mForDensity /// \todo deal with mSunlight and mFog/mForDensity
flagAsModified();
} }
CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent) CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string& cellId, CSMDoc::Document& document, QWidget* parent)
@ -29,12 +31,17 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
mCellsModel = &dynamic_cast<CSMWorld::IdTable&> ( mCellsModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells)); *document.getData().getTableModel (CSMWorld::UniversalId::Type_Cells));
mReferenceablesModel = &dynamic_cast<CSMWorld::IdTable&> (
*document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables));
connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), connect (mCellsModel, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&))); this, SLOT (cellDataChanged (const QModelIndex&, const QModelIndex&)));
connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), connect (mCellsModel, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int))); this, SLOT (cellRowsAboutToBeRemoved (const QModelIndex&, int, int)));
update(); update();
mCell.reset (new Cell (document.getData(), getSceneManager(), mCellId));
} }
void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft, void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,
@ -72,6 +79,62 @@ void CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector< CSMWorld
mCellId = data.begin()->getId(); mCellId = data.begin()->getId();
update(); update();
emit cellChanged(*data.begin()); emit cellChanged(*data.begin());
/// \todo replace mCell
}
void CSVRender::UnpagedWorldspaceWidget::referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
if (mCell.get())
if (mCell.get()->referenceableDataChanged (topLeft, bottomRight))
flagAsModified();
}
void CSVRender::UnpagedWorldspaceWidget::referenceableAboutToBeRemoved (
const QModelIndex& parent, int start, int end)
{
if (mCell.get())
if (mCell.get()->referenceableAboutToBeRemoved (parent, start, end))
flagAsModified();
}
void CSVRender::UnpagedWorldspaceWidget::referenceableAdded (const QModelIndex& parent,
int start, int end)
{
if (mCell.get())
{
QModelIndex topLeft = mReferenceablesModel->index (start, 0);
QModelIndex bottomRight =
mReferenceablesModel->index (end, mReferenceablesModel->columnCount());
if (mCell.get()->referenceableDataChanged (topLeft, bottomRight))
flagAsModified();
}
}
void CSVRender::UnpagedWorldspaceWidget::referenceDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight)
{
if (mCell.get())
if (mCell.get()->referenceDataChanged (topLeft, bottomRight))
flagAsModified();
}
void CSVRender::UnpagedWorldspaceWidget::referenceAboutToBeRemoved (const QModelIndex& parent,
int start, int end)
{
if (mCell.get())
if (mCell.get()->referenceAboutToBeRemoved (parent, start, end))
flagAsModified();
}
void CSVRender::UnpagedWorldspaceWidget::referenceAdded (const QModelIndex& parent, int start,
int end)
{
if (mCell.get())
if (mCell.get()->referenceAdded (parent, start, end))
flagAsModified();
} }
CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::dropType type) const CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget::getDropRequirements (CSVRender::WorldspaceWidget::dropType type) const

View file

@ -2,8 +2,10 @@
#define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H #define OPENCS_VIEW_UNPAGEDWORLDSPACEWIDGET_H
#include <string> #include <string>
#include <memory>
#include "worldspacewidget.hpp" #include "worldspacewidget.hpp"
#include "cell.hpp"
class QModelIndex; class QModelIndex;
@ -25,6 +27,8 @@ namespace CSVRender
std::string mCellId; std::string mCellId;
CSMWorld::IdTable *mCellsModel; CSMWorld::IdTable *mCellsModel;
CSMWorld::IdTable *mReferenceablesModel;
std::auto_ptr<Cell> mCell;
void update(); void update();
@ -37,6 +41,21 @@ namespace CSVRender
virtual void handleDrop(const std::vector<CSMWorld::UniversalId>& data); virtual void handleDrop(const std::vector<CSMWorld::UniversalId>& data);
private:
virtual void referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight);
virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void referenceableAdded (const QModelIndex& index, int start, int end);
virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end);
virtual void referenceAdded (const QModelIndex& index, int start, int end);
private slots: private slots:
void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); void cellDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);

View file

@ -10,15 +10,30 @@
#include "../world/scenetoolmode.hpp" #include "../world/scenetoolmode.hpp"
#include <apps/opencs/model/world/universalid.hpp> #include <apps/opencs/model/world/universalid.hpp>
CSVRender::WorldspaceWidget::WorldspaceWidget (const CSMDoc::Document& document, QWidget* parent) CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidget* parent)
: SceneWidget (parent), mDocument(document) : SceneWidget (parent), mDocument(document)
{ {
Ogre::Entity* ent = getSceneManager()->createEntity("cube", Ogre::SceneManager::PT_CUBE);
ent->setMaterialName("BaseWhite");
getSceneManager()->getRootSceneNode()->attachObject(ent);
setAcceptDrops(true); setAcceptDrops(true);
QAbstractItemModel *referenceables =
document.getData().getTableModel (CSMWorld::UniversalId::Type_Referenceables);
connect (referenceables, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (referenceableDataChanged (const QModelIndex&, const QModelIndex&)));
connect (referenceables, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (referenceableAboutToBeRemoved (const QModelIndex&, int, int)));
connect (referenceables, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (referenceableAdded (const QModelIndex&, int, int)));
QAbstractItemModel *references =
document.getData().getTableModel (CSMWorld::UniversalId::Type_References);
connect (references, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
this, SLOT (referenceDataChanged (const QModelIndex&, const QModelIndex&)));
connect (references, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
this, SLOT (referenceAboutToBeRemoved (const QModelIndex&, int, int)));
connect (references, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (referenceAdded (const QModelIndex&, int, int)));
} }
void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode) void CSVRender::WorldspaceWidget::selectNavigationMode (const std::string& mode)
@ -120,6 +135,8 @@ void CSVRender::WorldspaceWidget::dragMoveEvent(QDragMoveEvent *event)
void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event) void CSVRender::WorldspaceWidget::dropEvent (QDropEvent* event)
{ {
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
if (mime->fromDocument (mDocument)) if (mime->fromDocument (mDocument))
{ {

View file

@ -47,7 +47,7 @@ namespace CSVRender
ignored //either mixed cells, or not cells ignored //either mixed cells, or not cells
}; };
WorldspaceWidget (const CSMDoc::Document& document, QWidget *parent = 0); WorldspaceWidget (CSMDoc::Document& document, QWidget *parent = 0);
CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent); CSVWorld::SceneToolMode *makeNavigationSelector (CSVWorld::SceneToolbar *parent);
///< \attention The created tool is not added to the toolbar (via addTool). Doing that ///< \attention The created tool is not added to the toolbar (via addTool). Doing that
@ -79,6 +79,19 @@ namespace CSVRender
void selectNavigationMode (const std::string& mode); void selectNavigationMode (const std::string& mode);
virtual void referenceableDataChanged (const QModelIndex& topLeft,
const QModelIndex& bottomRight) = 0;
virtual void referenceableAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;
virtual void referenceableAdded (const QModelIndex& index, int start, int end) = 0;
virtual void referenceDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;
virtual void referenceAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;
virtual void referenceAdded (const QModelIndex& index, int start, int end) = 0;
signals: signals:
void closeRequest(); void closeRequest();

View file

@ -23,10 +23,10 @@ CSVWorld::PreviewSubView::PreviewSubView (const CSMWorld::UniversalId& id, CSMDo
referenceableIdChanged (referenceableId); referenceableIdChanged (referenceableId);
mScene = mScene =
new CSVRender::PreviewWidget (document.getData(), referenceableId, id.getId(), this); new CSVRender::PreviewWidget (document.getData(), id.getId(), false, this);
} }
else else
mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), this); mScene = new CSVRender::PreviewWidget (document.getData(), id.getId(), true, this);
SceneToolbar *toolbar = new SceneToolbar (48+6, this); SceneToolbar *toolbar = new SceneToolbar (48+6, this);

View file

@ -382,6 +382,9 @@ void CSVWorld::RegionMap::dropEvent (QDropEvent* event)
} }
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
if (mime->fromDocument(mDocument) && mime->holdsType(CSMWorld::UniversalId::Type_Region)) if (mime->fromDocument(mDocument) && mime->holdsType(CSMWorld::UniversalId::Type_Region))
{ {
CSMWorld::UniversalId record (mime->returnMatching (CSMWorld::UniversalId::Type_Region)); CSMWorld::UniversalId record (mime->returnMatching (CSMWorld::UniversalId::Type_Region));

View file

@ -45,19 +45,36 @@ CSVWorld::ScriptEdit::ScriptEdit (QWidget* parent, const CSMDoc::Document& docum
void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event) void CSVWorld::ScriptEdit::dragEnterEvent (QDragEnterEvent* event)
{ {
setTextCursor (cursorForPosition (event->pos())); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
event->acceptProposedAction(); if (!mime)
QTextEdit::dragEnterEvent(event);
else
{
setTextCursor (cursorForPosition (event->pos()));
event->acceptProposedAction();
}
} }
void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event) void CSVWorld::ScriptEdit::dragMoveEvent (QDragMoveEvent* event)
{ {
setTextCursor (cursorForPosition (event->pos())); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
event->accept(); if (!mime)
QTextEdit::dragMoveEvent(event);
else
{
setTextCursor (cursorForPosition (event->pos()));
event->accept();
}
} }
void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event) void CSVWorld::ScriptEdit::dropEvent (QDropEvent* event)
{ {
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
{
QTextEdit::dropEvent(event);
return;
}
setTextCursor (cursorForPosition (event->pos())); setTextCursor (cursorForPosition (event->pos()));

View file

@ -44,6 +44,10 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
mDispatcher->setSelection (records); mDispatcher->setSelection (records);
std::vector<CSMWorld::UniversalId> extendedTypes = mDispatcher->getExtendedTypes();
mDispatcher->setExtendedTypes (extendedTypes);
// create context menu // create context menu
QMenu menu (this); QMenu menu (this);
@ -63,11 +67,21 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
if (mDispatcher->canRevert()) if (mDispatcher->canRevert())
{
menu.addAction (mRevertAction); menu.addAction (mRevertAction);
if (!extendedTypes.empty())
menu.addAction (mExtendedRevertAction);
}
if (mDispatcher->canDelete()) if (mDispatcher->canDelete())
{
menu.addAction (mDeleteAction); menu.addAction (mDeleteAction);
if (!extendedTypes.empty())
menu.addAction (mExtendedDeleteAction);
}
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic) if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic)
{ {
/// \todo allow reordering of multiple rows /// \todo allow reordering of multiple rows
@ -101,12 +115,12 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (selectedRows.size()==1) if (selectedRows.size()==1)
{ {
int row = selectedRows.begin()->row();
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View) if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View)
{ {
int row = selectedRows.begin()->row();
row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row();
CSMWorld::UniversalId id = mModel->view (row).first; CSMWorld::UniversalId id = mModel->view (row).first;
int index = mDocument.getData().getCells().searchId (id.getId()); int index = mDocument.getData().getCells().searchId (id.getId());
@ -118,7 +132,16 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
} }
if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview) if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview)
menu.addAction (mPreviewAction); {
QModelIndex index = mModel->index (row,
mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification));
CSMWorld::RecordBase::State state = static_cast<CSMWorld::RecordBase::State> (
mModel->data (index).toInt());
if (state!=CSMWorld::RecordBase::State_Deleted)
menu.addAction (mPreviewAction);
}
} }
menu.exec (event->globalPos()); menu.exec (event->globalPos());
@ -203,6 +226,18 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id,
connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord()));
addAction (mPreviewAction); addAction (mPreviewAction);
/// \todo add a user option, that redirects the extended action to an input panel (in
/// the bottom bar) that lets the user select which record collections should be
/// modified.
mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this);
connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete()));
addAction (mExtendedDeleteAction);
mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this);
connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert()));
addAction (mExtendedRevertAction);
connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate())); this, SLOT (tableSizeUpdate()));
@ -351,7 +386,12 @@ void CSVWorld::Table::previewRecord()
{ {
std::string id = getUniversalId (selectedRows.begin()->row()).getId(); std::string id = getUniversalId (selectedRows.begin()->row()).getId();
emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id) , ""); QModelIndex index = mModel->getModelIndex (id,
mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Modification));
if (mModel->data (index)!=CSMWorld::RecordBase::State_Deleted)
emit editRequest (CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Preview, id),
"");
} }
} }
@ -438,6 +478,9 @@ void CSVWorld::Table::dropEvent(QDropEvent *event)
} }
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData()); const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*> (event->mimeData());
if (!mime) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
if (mime->fromDocument (mDocument)) if (mime->fromDocument (mDocument))
{ {
CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display> CSMWorld::ColumnBase::Display display = static_cast<CSMWorld::ColumnBase::Display>

View file

@ -46,6 +46,8 @@ namespace CSVWorld
QAction *mMoveDownAction; QAction *mMoveDownAction;
QAction *mViewAction; QAction *mViewAction;
QAction *mPreviewAction; QAction *mPreviewAction;
QAction *mExtendedDeleteAction;
QAction *mExtendedRevertAction;
CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTableProxyModel *mProxyModel;
CSMWorld::IdTable *mModel; CSMWorld::IdTable *mModel;
int mRecordStatusDisplay; int mRecordStatusDisplay;

View file

@ -131,6 +131,9 @@ bool CSVWorld::TableSubView::eventFilter (QObject* object, QEvent* event)
{ {
QDropEvent* drop = dynamic_cast<QDropEvent*>(event); QDropEvent* drop = dynamic_cast<QDropEvent*>(event);
const CSMWorld::TableMimeData* data = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData()); const CSMWorld::TableMimeData* data = dynamic_cast<const CSMWorld::TableMimeData*>(drop->mimeData());
if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped
return false;
bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter); bool handled = data->holdsType(CSMWorld::UniversalId::Type_Filter);
if (handled) if (handled)
{ {

View file

@ -256,6 +256,9 @@ void CSVWorld::DropLineEdit::dragMoveEvent(QDragMoveEvent *event)
void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event) void CSVWorld::DropLineEdit::dropEvent(QDropEvent *event)
{ {
const CSMWorld::TableMimeData* data(dynamic_cast<const CSMWorld::TableMimeData*>(event->mimeData())); const CSMWorld::TableMimeData* data(dynamic_cast<const CSMWorld::TableMimeData*>(event->mimeData()));
if (!data) // May happen when non-records (e.g. plain text) are dragged and dropped
return;
emit tableMimeDataDropped(data->getData(), data->getDocumentPtr()); emit tableMimeDataDropped(data->getData(), data->getDocumentPtr());
//WIP //WIP
} }

View file

@ -73,9 +73,9 @@ namespace CSVWorld
~CommandDelegateFactoryCollection(); ~CommandDelegateFactoryCollection();
void add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory); void add (CSMWorld::ColumnBase::Display display, CommandDelegateFactory *factory);
///< The ownership of \æ factory is transferred to *this. ///< The ownership of \a factory is transferred to *this.
/// ///
/// This function must not be called more than once per value of \æ display. /// This function must not be called more than once per value of \a display.
CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, QUndoStack& undoStack, CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, QUndoStack& undoStack,
QObject *parent) const; QObject *parent) const;

View file

@ -15,7 +15,7 @@ add_openmw_dir (mwrender
renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation renderingmanager debugging sky camera animation npcanimation creatureanimation activatoranimation
actors objects renderinginterface localmap occlusionquery water shadows actors objects renderinginterface localmap occlusionquery water shadows
characterpreview globalmap videoplayer ripplesimulation refraction characterpreview globalmap videoplayer ripplesimulation refraction
terrainstorage renderconst effectmanager weaponanimation terrainstorage renderconst effectmanager weaponanimation terraingrid
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -142,10 +142,6 @@ if(APPLE)
endif() endif()
endif(APPLE) endif(APPLE)
if(DPKG_PROGRAM)
INSTALL(TARGETS openmw RUNTIME DESTINATION games COMPONENT openmw)
endif(DPKG_PROGRAM)
if (BUILD_WITH_CODE_COVERAGE) if (BUILD_WITH_CODE_COVERAGE)
add_definitions (--coverage) add_definitions (--coverage)
target_link_libraries(openmw gcov) target_link_libraries(openmw gcov)

View file

@ -128,7 +128,7 @@ static void gdb_info(pid_t pid)
int fd; int fd;
/* Create a temp file to put gdb commands into */ /* Create a temp file to put gdb commands into */
strcpy(respfile, "gdb-respfile-XXXXXX"); strcpy(respfile, "/tmp/gdb-respfile-XXXXXX");
if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL) if((fd=mkstemp(respfile)) >= 0 && (f=fdopen(fd, "w")) != NULL)
{ {
fprintf(f, "attach %d\n" fprintf(f, "attach %d\n"
@ -381,10 +381,7 @@ static void crash_handler(const char *logfile)
if(logfile) if(logfile)
{ {
char cwd[MAXPATHLEN]; std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !";
getcwd(cwd, MAXPATHLEN);
std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '" + std::string(cwd) + "/" + std::string(logfile) + "'.\n Please report this to https://bugs.openmw.org !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL); SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), NULL);
} }
exit(0); exit(0);

View file

@ -91,7 +91,11 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (mUseSound) if (mUseSound)
MWBase::Environment::get().getSoundManager()->update(frametime); MWBase::Environment::get().getSoundManager()->update(frametime);
bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode(); // GUI active? Most game processing will be paused, but scripts still run.
bool guiActive = MWBase::Environment::get().getWindowManager()->isGuiMode();
// Main menu opened? Then scripts are also paused.
bool paused = MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_MainMenu);
// update game state // update game state
MWBase::Environment::get().getStateManager()->update (frametime); MWBase::Environment::get().getStateManager()->update (frametime);
@ -99,15 +103,18 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (MWBase::Environment::get().getStateManager()->getState()== if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running) MWBase::StateManager::State_Running)
{ {
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
// local scripts
executeLocalScripts();
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
if (!paused) if (!paused)
{
// global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
// local scripts
executeLocalScripts();
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
}
if (!guiActive)
MWBase::Environment::get().getWorld()->advanceTime( MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600); frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
} }
@ -118,14 +125,14 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
MWBase::StateManager::State_NoGame) MWBase::StateManager::State_NoGame)
{ {
MWBase::Environment::get().getMechanicsManager()->update(frametime, MWBase::Environment::get().getMechanicsManager()->update(frametime,
paused); guiActive);
} }
if (MWBase::Environment::get().getStateManager()->getState()== if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running) MWBase::StateManager::State_Running)
{ {
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr(); MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
if(!paused && player.getClass().getCreatureStats(player).isDead()) if(!guiActive && player.getClass().getCreatureStats(player).isDead())
MWBase::Environment::get().getStateManager()->endGame(); MWBase::Environment::get().getStateManager()->endGame();
} }
@ -133,7 +140,7 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (MWBase::Environment::get().getStateManager()->getState()!= if (MWBase::Environment::get().getStateManager()->getState()!=
MWBase::StateManager::State_NoGame) MWBase::StateManager::State_NoGame)
{ {
MWBase::Environment::get().getWorld()->update(frametime, paused); MWBase::Environment::get().getWorld()->update(frametime, guiActive);
} }
// update GUI // update GUI
@ -357,7 +364,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create input and UI first to set up a bootstrapping environment for // Create input and UI first to set up a bootstrapping environment for
// showing a loading screen and keeping the window responsive while doing so // showing a loading screen and keeping the window responsive while doing so
std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input.xml").string(); std::string keybinderUser = (mCfgMgr.getUserConfigPath() / "input_v1.xml").string();
bool keybinderUserExists = boost::filesystem::exists(keybinderUser); bool keybinderUserExists = boost::filesystem::exists(keybinderUser);
MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab); MWInput::InputManager* input = new MWInput::InputManager (*mOgre, *this, keybinderUser, keybinderUserExists, mGrab);
mEnvironment.setInputManager (input); mEnvironment.setInputManager (input);
@ -489,24 +496,7 @@ void OMW::Engine::activate()
if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated
return; return;
MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); MWBase::Environment::get().getWorld()->activate(ptr, MWBase::Environment::get().getWorld()->getPlayerPtr());
interpreterContext.activate (ptr);
std::string script = ptr.getClass().getScript (ptr);
MWBase::Environment::get().getWorld()->breakInvisibility(MWBase::Environment::get().getWorld()->getPlayerPtr());
if (!script.empty())
{
MWBase::Environment::get().getWorld()->getLocalScripts().setIgnore (ptr);
MWBase::Environment::get().getScriptManager()->run (script, interpreterContext);
}
if (!interpreterContext.hasActivationBeenHandled())
{
interpreterContext.executeActivation(ptr);
}
} }
void OMW::Engine::screenshot() void OMW::Engine::screenshot()

View file

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

View file

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

View file

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

View file

@ -4,6 +4,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <list> #include <list>
#include <stdint.h>
namespace Ogre namespace Ogre
{ {
@ -13,6 +14,9 @@ namespace Ogre
namespace ESM namespace ESM
{ {
struct Class; struct Class;
class ESMReader;
class ESMWriter;
} }
namespace MWWorld namespace MWWorld
@ -21,6 +25,11 @@ namespace MWWorld
class CellStore; class CellStore;
} }
namespace Loading
{
class Listener;
}
namespace MWBase namespace MWBase
{ {
/// \brief Interface for game mechanics manager (implemented in MWMechanics) /// \brief Interface for game mechanics manager (implemented in MWMechanics)
@ -111,6 +120,7 @@ namespace MWBase
/** /**
* @brief Commit a crime. If any actors witness the crime and report it, * @brief Commit a crime. If any actors witness the crime and report it,
* reportCrime will be called automatically. * reportCrime will be called automatically.
* @note victim may be empty
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen. * @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
* @return was the crime reported? * @return was the crime reported?
*/ */
@ -174,6 +184,18 @@ namespace MWBase
virtual std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) = 0; virtual std::list<MWWorld::Ptr> getActorsFighting(const MWWorld::Ptr& actor) = 0;
virtual void playerLoaded() = 0; virtual void playerLoaded() = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer, Loading::Listener& listener) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
virtual void clear() = 0;
/// @param bias Can be used to add an additional aggression bias towards the target,
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0;
}; };
} }

View file

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

View file

@ -274,6 +274,9 @@ namespace MWBase
virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
///< Adjust position after load to be on ground. Must be called after model load. ///< Adjust position after load to be on ground. Must be called after model load.
virtual void fixPosition (const MWWorld::Ptr& actor) = 0;
///< Attempt to fix position so that the Ptr is no longer inside collision geometry.
virtual void deleteObject (const MWWorld::Ptr& ptr) = 0; virtual void deleteObject (const MWWorld::Ptr& ptr) = 0;
virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0; virtual void moveObject (const MWWorld::Ptr& ptr, float x, float y, float z) = 0;
@ -407,7 +410,7 @@ namespace MWBase
virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0; virtual void getItemsOwnedBy (const MWWorld::Ptr& npc, std::vector<MWWorld::Ptr>& out) = 0;
///< get all items in active cells owned by this Npc ///< get all items in active cells owned by this Npc
virtual bool getLOS(const MWWorld::Ptr& npc,const MWWorld::Ptr& targetNpc) = 0; virtual bool getLOS(const MWWorld::Ptr& actor,const MWWorld::Ptr& targetActor) = 0;
///< get Line of Sight (morrowind stupid implementation) ///< get Line of Sight (morrowind stupid implementation)
virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0; virtual float getDistToNearestRayHit(const Ogre::Vector3& from, const Ogre::Vector3& dir, float maxDist) = 0;
@ -470,7 +473,7 @@ namespace MWBase
virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId, virtual void launchMagicBolt (const std::string& model, const std::string& sound, const std::string& spellId,
float speed, bool stack, const ESM::EffectList& effects, float speed, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0; const MWWorld::Ptr& caster, const std::string& sourceName, const Ogre::Vector3& fallbackDirection) = 0;
virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile, virtual void launchProjectile (MWWorld::Ptr actor, MWWorld::Ptr projectile,
const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0; const Ogre::Vector3& worldPos, const Ogre::Quaternion& orient, MWWorld::Ptr bow, float speed) = 0;
@ -515,8 +518,18 @@ namespace MWBase
/// Spawn a blood effect for \a ptr at \a worldPosition /// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0; virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0; const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;
/// @see MWWorld::WeatherManager::isInStorm
virtual bool isInStorm() const = 0;
/// @see MWWorld::WeatherManager::getStormDirection
virtual Ogre::Vector3 getStormDirection() const = 0;
}; };
} }

View file

@ -124,7 +124,7 @@ namespace MWClass
std::string text; std::string text;
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -168,7 +168,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Armor> *ref = MWWorld::LiveCellRef<ESM::Armor> *ref =
ptr.get<ESM::Armor>(); ptr.get<ESM::Armor>();
return ref->mBase->mData.mValue * (static_cast<float>(getItemHealth(ptr)) / getItemMaxHealth(ptr)); return ref->mBase->mData.mValue;
} }
void Armor::registerSelf() void Armor::registerSelf()
@ -244,7 +244,7 @@ namespace MWClass
+ MWGui::ToolTips::toString(ref->mBase->mData.mHealth); + MWGui::ToolTips::toString(ref->mBase->mData.mHealth);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")"; text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight) + " (" + typeText + ")";
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -136,7 +136,7 @@ namespace MWClass
std::string text; std::string text;
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -190,7 +190,7 @@ namespace MWClass
std::string text; std::string text;
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -286,7 +286,12 @@ namespace MWClass
{ {
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state); const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
ensureCustomData (ptr); if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<ContainerCustomData> data (new ContainerCustomData);
ptr.getRefData().setCustomData (data.release());
}
dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore. dynamic_cast<ContainerCustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory); readState (state2.mInventory);

View file

@ -59,35 +59,38 @@ namespace
namespace MWClass namespace MWClass
{ {
const Creature::GMST& Creature::getGmst()
{
static GMST gmst;
static bool inited = false;
if (!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
gmst.fMinWalkSpeedCreature = store.find("fMinWalkSpeedCreature");
gmst.fMaxWalkSpeedCreature = store.find("fMaxWalkSpeedCreature");
gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
gmst.fMinFlySpeed = store.find("fMinFlySpeed");
gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
gmst.fSwimRunBase = store.find("fSwimRunBase");
gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
gmst.fKnockDownMult = store.find("fKnockDownMult");
gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
inited = true;
}
return gmst;
}
void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const void Creature::ensureCustomData (const MWWorld::Ptr& ptr) const
{ {
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData); std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
static bool inited = false;
if(!inited)
{
const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature");
fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult");
fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
inited = true;
}
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>(); MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
// creature stats // creature stats
@ -345,7 +348,9 @@ namespace MWClass
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);
// Self defense // Self defense
if (!attacker.isEmpty() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) if (!attacker.isEmpty() && !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker)
&& (canWalk(ptr) || canFly(ptr) || canSwim(ptr))) // No retaliation for totally static creatures
// (they have no movement or attacks anyway)
MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker); MWBase::Environment::get().getMechanicsManager()->startCombat(ptr, attacker);
if(!successful) if(!successful)
@ -374,9 +379,9 @@ namespace MWClass
if (damage > 0.f) if (damage > 0.f)
{ {
// Check for knockdown // Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * getGmst().fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); * getGmst().iKnockDownOddsMult->getInt() * 0.01 + getGmst().iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99] int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{ {
@ -520,9 +525,10 @@ namespace MWClass
float Creature::getSpeed(const MWWorld::Ptr &ptr) const float Creature::getSpeed(const MWWorld::Ptr &ptr) const
{ {
MWMechanics::CreatureStats& stats = getCreatureStats(ptr); MWMechanics::CreatureStats& stats = getCreatureStats(ptr);
const GMST& gmst = getGmst();
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() float walkSpeed = gmst.fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat()); * (gmst.fMaxWalkSpeedCreature->getFloat() - gmst.fMinWalkSpeedCreature->getFloat());
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
@ -531,8 +537,8 @@ namespace MWClass
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) * // The Run speed difference for creatures comes from the animation speed difference (see runStateToWalkState in character.cpp)
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); float runSpeed = walkSpeed;
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)
@ -542,8 +548,8 @@ namespace MWClass
{ {
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed); flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
@ -553,8 +559,8 @@ namespace MWClass
if(running) if(running)
swimSpeed = runSpeed; swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) * swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
fSwimRunAthleticsMult->getFloat(); gmst.fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed; moveSpeed = swimSpeed;
} }
else if(running) else if(running)
@ -711,7 +717,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Swims; return ref->mBase->mFlags & ESM::Creature::Swims || ref->mBase->mFlags & ESM::Creature::Bipedal;
} }
bool Creature::canWalk(const MWWorld::Ptr &ptr) const bool Creature::canWalk(const MWWorld::Ptr &ptr) const
@ -719,7 +725,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>(); ptr.get<ESM::Creature>();
return ref->mBase->mFlags & ESM::Creature::Walks; return ref->mBase->mFlags & ESM::Creature::Walks || ref->mBase->mFlags & ESM::Creature::Bipedal;
} }
int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name) int Creature::getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name)
@ -796,7 +802,27 @@ namespace MWClass
{ {
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state); const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData (ptr); ensureCustomData(ptr);
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
/*
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<CreatureCustomData> data (new CreatureCustomData);
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Weapon)
data->mContainerStore = new MWWorld::InventoryStore();
else
data->mContainerStore = new MWWorld::ContainerStore();
ptr.getRefData().setCustomData (data.release());
}
*/
CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData()); CreatureCustomData& customData = dynamic_cast<CreatureCustomData&> (*ptr.getRefData().getCustomData());
@ -835,8 +861,7 @@ namespace MWClass
ptr.getRefData().setCount(1); ptr.getRefData().setCount(1);
// Reset to original position // Reset to original position
ESM::Position& pos = ptr.getRefData().getPosition(); ptr.getRefData().setPosition(ptr.getCellRef().getPosition());
pos = ptr.getCellRef().getPosition();
ptr.getRefData().setCustomData(NULL); ptr.getRefData().setCustomData(NULL);
} }
@ -850,19 +875,4 @@ namespace MWClass
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
} }
const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature;
const ESM::GameSetting *Creature::fEncumberedMoveEffect;
const ESM::GameSetting *Creature::fSneakSpeedMultiplier;
const ESM::GameSetting *Creature::fAthleticsRunBonus;
const ESM::GameSetting *Creature::fBaseRunMultiplier;
const ESM::GameSetting *Creature::fMinFlySpeed;
const ESM::GameSetting *Creature::fMaxFlySpeed;
const ESM::GameSetting *Creature::fSwimRunBase;
const ESM::GameSetting *Creature::fSwimRunAthleticsMult;
const ESM::GameSetting *Creature::fKnockDownMult;
const ESM::GameSetting *Creature::iKnockDownOddsMult;
const ESM::GameSetting *Creature::iKnockDownOddsBase;
} }

View file

@ -19,20 +19,25 @@ namespace MWClass
static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name); static int getSndGenTypeFromName(const MWWorld::Ptr &ptr, const std::string &name);
static const ESM::GameSetting *fMinWalkSpeedCreature; // cached GMSTs
static const ESM::GameSetting *fMaxWalkSpeedCreature; struct GMST
static const ESM::GameSetting *fEncumberedMoveEffect; {
static const ESM::GameSetting *fSneakSpeedMultiplier; const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fAthleticsRunBonus; const ESM::GameSetting *fMaxWalkSpeedCreature;
static const ESM::GameSetting *fBaseRunMultiplier; const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fMinFlySpeed; const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fMaxFlySpeed; const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fSwimRunBase; const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fSwimRunAthleticsMult; const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fKnockDownMult; const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *iKnockDownOddsMult; const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *iKnockDownOddsBase; const ESM::GameSetting *fSwimRunAthleticsMult;
const ESM::GameSetting *fKnockDownMult;
const ESM::GameSetting *iKnockDownOddsMult;
const ESM::GameSetting *iKnockDownOddsBase;
};
static const GMST& getGmst();
public: public:

View file

@ -57,11 +57,13 @@ namespace MWClass
physics.addObject(ptr); physics.addObject(ptr);
// Resume the door's opening/closing animation if it wasn't finished // Resume the door's opening/closing animation if it wasn't finished
ensureCustomData(ptr); if (ptr.getRefData().getCustomData())
const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
if (customData.mDoorState > 0)
{ {
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false); const DoorCustomData& customData = dynamic_cast<const DoorCustomData&>(*ptr.getRefData().getCustomData());
if (customData.mDoorState > 0)
{
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState == 1 ? true : false);
}
} }
} }
@ -125,7 +127,7 @@ namespace MWClass
MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}"); MWBase::Environment::get().getWindowManager()->messageBox(keyName + " #{sKeyUsed}");
unlock(ptr); //Call the function here. because that makes sense. unlock(ptr); //Call the function here. because that makes sense.
// using a key disarms the trap // using a key disarms the trap
ptr.getCellRef().getTrap() = ""; ptr.getCellRef().setTrap("");
} }
if (!needKey || hasKey) if (!needKey || hasKey)

View file

@ -144,7 +144,7 @@ namespace MWClass
std::string text; std::string text;
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -184,7 +184,7 @@ namespace MWClass
std::string text; std::string text;
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -86,7 +86,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Lockpick> *ref = MWWorld::LiveCellRef<ESM::Lockpick> *ref =
ptr.get<ESM::Lockpick>(); ptr.get<ESM::Lockpick>();
return ref->mBase->mData.mValue * (static_cast<float>(getItemHealth(ptr)) / getItemMaxHealth(ptr)); return ref->mBase->mData.mValue;
} }
void Lockpick::registerSelf() void Lockpick::registerSelf()
@ -138,7 +138,7 @@ namespace MWClass
text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses);
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -228,38 +228,44 @@ namespace
namespace MWClass namespace MWClass
{ {
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const const Npc::GMST& Npc::getGmst()
{ {
static GMST gmst;
static bool inited = false; static bool inited = false;
if(!inited) if(!inited)
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
fMinWalkSpeed = gmst.find("fMinWalkSpeed"); gmst.fMinWalkSpeed = store.find("fMinWalkSpeed");
fMaxWalkSpeed = gmst.find("fMaxWalkSpeed"); gmst.fMaxWalkSpeed = store.find("fMaxWalkSpeed");
fEncumberedMoveEffect = gmst.find("fEncumberedMoveEffect"); gmst.fEncumberedMoveEffect = store.find("fEncumberedMoveEffect");
fSneakSpeedMultiplier = gmst.find("fSneakSpeedMultiplier"); gmst.fSneakSpeedMultiplier = store.find("fSneakSpeedMultiplier");
fAthleticsRunBonus = gmst.find("fAthleticsRunBonus"); gmst.fAthleticsRunBonus = store.find("fAthleticsRunBonus");
fBaseRunMultiplier = gmst.find("fBaseRunMultiplier"); gmst.fBaseRunMultiplier = store.find("fBaseRunMultiplier");
fMinFlySpeed = gmst.find("fMinFlySpeed"); gmst.fMinFlySpeed = store.find("fMinFlySpeed");
fMaxFlySpeed = gmst.find("fMaxFlySpeed"); gmst.fMaxFlySpeed = store.find("fMaxFlySpeed");
fSwimRunBase = gmst.find("fSwimRunBase"); gmst.fSwimRunBase = store.find("fSwimRunBase");
fSwimRunAthleticsMult = gmst.find("fSwimRunAthleticsMult"); gmst.fSwimRunAthleticsMult = store.find("fSwimRunAthleticsMult");
fJumpEncumbranceBase = gmst.find("fJumpEncumbranceBase"); gmst.fJumpEncumbranceBase = store.find("fJumpEncumbranceBase");
fJumpEncumbranceMultiplier = gmst.find("fJumpEncumbranceMultiplier"); gmst.fJumpEncumbranceMultiplier = store.find("fJumpEncumbranceMultiplier");
fJumpAcrobaticsBase = gmst.find("fJumpAcrobaticsBase"); gmst.fJumpAcrobaticsBase = store.find("fJumpAcrobaticsBase");
fJumpAcroMultiplier = gmst.find("fJumpAcroMultiplier"); gmst.fJumpAcroMultiplier = store.find("fJumpAcroMultiplier");
fJumpRunMultiplier = gmst.find("fJumpRunMultiplier"); gmst.fJumpRunMultiplier = store.find("fJumpRunMultiplier");
fWereWolfRunMult = gmst.find("fWereWolfRunMult"); gmst.fWereWolfRunMult = store.find("fWereWolfRunMult");
fKnockDownMult = gmst.find("fKnockDownMult"); gmst.fKnockDownMult = store.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); gmst.iKnockDownOddsMult = store.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); gmst.iKnockDownOddsBase = store.find("iKnockDownOddsBase");
fDamageStrengthBase = gmst.find("fDamageStrengthBase"); gmst.fDamageStrengthBase = store.find("fDamageStrengthBase");
fDamageStrengthMult = gmst.find("fDamageStrengthMult"); gmst.fDamageStrengthMult = store.find("fDamageStrengthMult");
inited = true; inited = true;
} }
return gmst;
}
void Npc::ensureCustomData (const MWWorld::Ptr& ptr) const
{
if (!ptr.getRefData().getCustomData()) if (!ptr.getRefData().getCustomData())
{ {
std::auto_ptr<NpcCustomData> data(new NpcCustomData); std::auto_ptr<NpcCustomData> data(new NpcCustomData);
@ -404,11 +410,6 @@ namespace MWClass
ptr.get<ESM::NPC>(); ptr.get<ESM::NPC>();
assert(ref->mBase != NULL); assert(ref->mBase != NULL);
//std::string headID = ref->mBase->mHead;
//int end = headID.find_last_of("head_") - 4;
//std::string bodyRaceID = headID.substr(0, end);
std::string model = "meshes\\base_anim.nif"; std::string model = "meshes\\base_anim.nif";
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace); const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
if(race->mData.mFlags & ESM::Race::Beast) if(race->mData.mFlags & ESM::Race::Beast)
@ -423,9 +424,9 @@ namespace MWClass
if(getNpcStats(ptr).isWerewolf()) if(getNpcStats(ptr).isWerewolf())
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
return gmst.find("sWerewolfPopup")->getString(); return store.find("sWerewolfPopup")->getString();
} }
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
@ -450,7 +451,9 @@ namespace MWClass
void Npc::hit(const MWWorld::Ptr& ptr, int type) const void Npc::hit(const MWWorld::Ptr& ptr, int type) const
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const GMST& gmst = getGmst();
const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
// Get the weapon used (if hand-to-hand, weapon = inv.end()) // Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::InventoryStore &inv = getInventoryStore(ptr); MWWorld::InventoryStore &inv = getInventoryStore(ptr);
@ -461,9 +464,9 @@ namespace MWClass
// Reduce fatigue // Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense // somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat(); const float fFatigueAttackBase = store.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat(); const float fFatigueAttackMult = store.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat(); const float fWeaponFatigueMult = store.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue(); MWMechanics::DynamicStat<float> fatigue = getCreatureStats(ptr).getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr); const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult; float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
@ -472,10 +475,10 @@ namespace MWClass
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss); fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
getCreatureStats(ptr).setFatigue(fatigue); getCreatureStats(ptr).setFatigue(fatigue);
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat(); const float fCombatDistance = store.find("fCombatDistance")->getFloat();
float dist = fCombatDistance * (!weapon.isEmpty() ? float dist = fCombatDistance * (!weapon.isEmpty() ?
weapon.get<ESM::Weapon>()->mBase->mData.mReach : weapon.get<ESM::Weapon>()->mBase->mData.mReach :
gmst.find("fHandToHandReach")->getFloat()); store.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle // TODO: Use second to work out the hit angle
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist); std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
@ -522,8 +525,8 @@ namespace MWClass
if(attack) if(attack)
{ {
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= fDamageStrengthBase->getFloat() + damage *= gmst.fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1); (stats.getAttribute(ESM::Attribute::Strength).getModified() * gmst.fDamageStrengthMult->getFloat() * 0.1);
if(weaphashealth) if(weaphashealth)
{ {
int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon); int weapmaxhealth = weapon.getClass().getItemMaxHealth(weapon);
@ -535,7 +538,7 @@ namespace MWClass
{ {
// Reduce weapon charge by at least one, but cap at 0 // Reduce weapon charge by at least one, but cap at 0
weaphealth -= std::min(std::max(1, weaphealth -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weaphealth); (int)(damage * store.find("fWeaponDamageMult")->getFloat())), weaphealth);
weapon.getCellRef().setCharge(weaphealth); weapon.getCellRef().setCharge(weaphealth);
} }
@ -552,8 +555,8 @@ namespace MWClass
// Note: MCP contains an option to include Strength in hand-to-hand damage // Note: MCP contains an option to include Strength in hand-to-hand damage
// calculations. Some mods recommend using it, so we may want to include am // calculations. Some mods recommend using it, so we may want to include am
// option for it. // option for it.
float minstrike = gmst.find("fMinHandToHandMult")->getFloat(); float minstrike = store.find("fMinHandToHandMult")->getFloat();
float maxstrike = gmst.find("fMaxHandToHandMult")->getFloat(); float maxstrike = store.find("fMaxHandToHandMult")->getFloat();
damage = stats.getSkill(weapskill).getModified(); damage = stats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength()); damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
@ -567,7 +570,7 @@ namespace MWClass
damage *= glob.find("WerewolfClawMult")->mValue.getFloat(); damage *= glob.find("WerewolfClawMult")->mValue.getFloat();
} }
if(healthdmg) if(healthdmg)
damage *= gmst.find("fHandtoHandHealthPer")->getFloat(); damage *= store.find("fHandtoHandHealthPer")->getFloat();
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager(); MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(stats.isWerewolf()) if(stats.isWerewolf())
@ -585,12 +588,12 @@ namespace MWClass
bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim); bool detected = MWBase::Environment::get().getMechanicsManager()->awarenessCheck(ptr, victim);
if(!detected) if(!detected)
{ {
damage *= gmst.find("fCombatCriticalStrikeMult")->getFloat(); damage *= store.find("fCombatCriticalStrikeMult")->getFloat();
MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}"); MWBase::Environment::get().getWindowManager()->messageBox("#{sTargetCriticalStrike}");
MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f); MWBase::Environment::get().getSoundManager()->playSound3D(victim, "critical damage", 1.0f, 1.0f);
} }
if (othercls.getCreatureStats(victim).getKnockedDown()) if (othercls.getCreatureStats(victim).getKnockedDown())
damage *= gmst.find("fCombatKODamageMult")->getFloat(); damage *= store.find("fCombatKODamageMult")->getFloat();
// Apply "On hit" enchanted weapons // Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : ""; std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
@ -624,11 +627,12 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty. // NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime // Attacking peaceful NPCs is a crime
// anything below 80 is considered peaceful (see Actors::updateActor)
if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() && if (!attacker.isEmpty() && !ptr.getClass().getCreatureStats(ptr).isHostile() &&
ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() < 80) !MWBase::Environment::get().getMechanicsManager()->isAggressive(ptr, attacker))
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault); MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
bool wasDead = getCreatureStats(ptr).isDead();
getCreatureStats(ptr).setAttacked(true); getCreatureStats(ptr).setAttacked(true);
if(!successful) if(!successful)
@ -660,6 +664,8 @@ namespace MWClass
// something, alert the character controller, scripts, etc. // something, alert the character controller, scripts, etc.
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore(); const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
const GMST& gmst = getGmst();
int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt(); int chance = store.get<ESM::GameSetting>().find("iVoiceHitOdds")->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99] int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (roll < chance) if (roll < chance)
@ -668,9 +674,9 @@ namespace MWClass
} }
// Check for knockdown // Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat(); float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * gmst.fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt(); * gmst.iKnockDownOddsMult->getInt() * 0.01 + gmst.iKnockDownOddsBase->getInt();
roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99] roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll) if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{ {
@ -752,6 +758,22 @@ namespace MWClass
fatigue.setCurrent(fatigue.getCurrent() - damage, true); fatigue.setCurrent(fatigue.getCurrent() - damage, true);
getCreatureStats(ptr).setFatigue(fatigue); getCreatureStats(ptr).setFatigue(fatigue);
} }
if (!wasDead && getCreatureStats(ptr).isDead())
{
// NPC was killed
if (!attacker.isEmpty() && attacker.getClass().isNpc() && attacker.getClass().getNpcStats(attacker).isWerewolf())
{
attacker.getClass().getNpcStats(attacker).addWerewolfKill();
}
// Simple check for who attacked first: if the player attacked first, a crimeId should be set
// Doesn't handle possible edge case where no one reported the assault, but in such a case,
// for bystanders it is not possible to tell who attacked first, anyway.
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (attacker == player && ptr.getClass().getNpcStats(ptr).getCrimeId() != -1 && ptr != player)
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, ptr, MWBase::MechanicsManager::OT_Murder);
}
} }
void Npc::block(const MWWorld::Ptr &ptr) const void Npc::block(const MWWorld::Ptr &ptr) const
@ -853,6 +875,8 @@ namespace MWClass
float Npc::getSpeed(const MWWorld::Ptr& ptr) const float Npc::getSpeed(const MWWorld::Ptr& ptr) const
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const GMST& gmst = getGmst();
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
@ -861,17 +885,17 @@ namespace MWClass
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak); bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run); bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float walkSpeed = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()* float walkSpeed = gmst.fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); (gmst.fMaxWalkSpeed->getFloat() - gmst.fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed); walkSpeed = std::max(0.0f, walkSpeed);
if(sneaking) if(sneaking)
walkSpeed *= fSneakSpeedMultiplier->getFloat(); walkSpeed *= gmst.fSneakSpeedMultiplier->getFloat();
float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() * float runSpeed = walkSpeed*(0.01f * npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified() *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat()); gmst.fAthleticsRunBonus->getFloat() + gmst.fBaseRunMultiplier->getFloat());
if(npcdata->mNpcStats.isWerewolf()) if(npcdata->mNpcStats.isWerewolf())
runSpeed *= fWereWolfRunMult->getFloat(); runSpeed *= gmst.fWereWolfRunMult->getFloat();
float moveSpeed; float moveSpeed;
if(normalizedEncumbrance >= 1.0f) if(normalizedEncumbrance >= 1.0f)
@ -881,8 +905,8 @@ namespace MWClass
{ {
float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() + float flySpeed = 0.01f*(npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude); mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat()); flySpeed = gmst.fMinFlySpeed->getFloat() + flySpeed*(gmst.fMaxFlySpeed->getFloat() - gmst.fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance; flySpeed *= 1.0f - gmst.fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed); flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed; moveSpeed = flySpeed;
} }
@ -892,8 +916,8 @@ namespace MWClass
if(running) if(running)
swimSpeed = runSpeed; swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude; swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()* swimSpeed *= gmst.fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
fSwimRunAthleticsMult->getFloat(); gmst.fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed; moveSpeed = swimSpeed;
} }
else if(running && !sneaking) else if(running && !sneaking)
@ -909,9 +933,10 @@ namespace MWClass
float Npc::getJump(const MWWorld::Ptr &ptr) const float Npc::getJump(const MWWorld::Ptr &ptr) const
{ {
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const GMST& gmst = getGmst();
const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects(); const MWMechanics::MagicEffects &mageffects = npcdata->mNpcStats.getMagicEffects();
const float encumbranceTerm = fJumpEncumbranceBase->getFloat() + const float encumbranceTerm = gmst.fJumpEncumbranceBase->getFloat() +
fJumpEncumbranceMultiplier->getFloat() * gmst.fJumpEncumbranceMultiplier->getFloat() *
(1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr)); (1.0f - Npc::getEncumbrance(ptr)/Npc::getCapacity(ptr));
float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified(); float a = npcdata->mNpcStats.getSkill(ESM::Skill::Acrobatics).getModified();
@ -922,14 +947,14 @@ namespace MWClass
a = 50.0f; a = 50.0f;
} }
float x = fJumpAcrobaticsBase->getFloat() + float x = gmst.fJumpAcrobaticsBase->getFloat() +
std::pow(a / 15.0f, fJumpAcroMultiplier->getFloat()); std::pow(a / 15.0f, gmst.fJumpAcroMultiplier->getFloat());
x += 3.0f * b * fJumpAcroMultiplier->getFloat(); x += 3.0f * b * gmst.fJumpAcroMultiplier->getFloat();
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
x *= encumbranceTerm; x *= encumbranceTerm;
if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run)) if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
x *= fJumpRunMultiplier->getFloat(); x *= gmst.fJumpRunMultiplier->getFloat();
x *= npcdata->mNpcStats.getFatigueTerm(); x *= npcdata->mNpcStats.getFatigueTerm();
x -= -627.2f;/*gravity constant*/ x -= -627.2f;/*gravity constant*/
x /= 3.0f; x /= 3.0f;
@ -940,19 +965,19 @@ namespace MWClass
float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const float Npc::getFallDamage(const MWWorld::Ptr &ptr, float fallHeight) const
{ {
MWBase::World *world = MWBase::Environment::get().getWorld(); MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
const float fallDistanceMin = gmst.find("fFallDamageDistanceMin")->getFloat(); const float fallDistanceMin = store.find("fFallDamageDistanceMin")->getFloat();
if (fallHeight >= fallDistanceMin) if (fallHeight >= fallDistanceMin)
{ {
const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified(); const float acrobaticsSkill = ptr.getClass().getNpcStats (ptr).getSkill(ESM::Skill::Acrobatics).getModified();
const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData()); const NpcCustomData *npcdata = static_cast<const NpcCustomData*>(ptr.getRefData().getCustomData());
const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude; const float jumpSpellBonus = npcdata->mNpcStats.getMagicEffects().get(ESM::MagicEffect::Jump).mMagnitude;
const float fallAcroBase = gmst.find("fFallAcroBase")->getFloat(); const float fallAcroBase = store.find("fFallAcroBase")->getFloat();
const float fallAcroMult = gmst.find("fFallAcroMult")->getFloat(); const float fallAcroMult = store.find("fFallAcroMult")->getFloat();
const float fallDistanceBase = gmst.find("fFallDistanceBase")->getFloat(); const float fallDistanceBase = store.find("fFallDistanceBase")->getFloat();
const float fallDistanceMult = gmst.find("fFallDistanceMult")->getFloat(); const float fallDistanceMult = store.find("fFallDistanceMult")->getFloat();
float x = fallHeight - fallDistanceMin; float x = fallHeight - fallDistanceMin;
x -= (1.5 * acrobaticsSkill) + jumpSpellBonus; x -= (1.5 * acrobaticsSkill) + jumpSpellBonus;
@ -1087,14 +1112,14 @@ namespace MWClass
float Npc::getArmorRating (const MWWorld::Ptr& ptr) const float Npc::getArmorRating (const MWWorld::Ptr& ptr) const
{ {
const MWBase::World *world = MWBase::Environment::get().getWorld(); const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting> &gmst = world->getStore().get<ESM::GameSetting>(); const MWWorld::Store<ESM::GameSetting> &store = world->getStore().get<ESM::GameSetting>();
MWMechanics::NpcStats &stats = getNpcStats(ptr); MWMechanics::NpcStats &stats = getNpcStats(ptr);
MWWorld::InventoryStore &invStore = getInventoryStore(ptr); MWWorld::InventoryStore &invStore = getInventoryStore(ptr);
int iBaseArmorSkill = gmst.find("iBaseArmorSkill")->getInt(); int iBaseArmorSkill = store.find("iBaseArmorSkill")->getInt();
float fUnarmoredBase1 = gmst.find("fUnarmoredBase1")->getFloat(); float fUnarmoredBase1 = store.find("fUnarmoredBase1")->getFloat();
float fUnarmoredBase2 = gmst.find("fUnarmoredBase2")->getFloat(); float fUnarmoredBase2 = store.find("fUnarmoredBase2")->getFloat();
int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified(); int unarmoredSkill = stats.getSkill(ESM::Skill::Unarmored).getModified();
int ratings[MWWorld::InventoryStore::Slots]; int ratings[MWWorld::InventoryStore::Slots];
@ -1276,7 +1301,18 @@ namespace MWClass
{ {
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state); const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData (ptr); ensureCustomData(ptr);
// If we do the following instead we get a sizable speedup, but this causes compatibility issues
// with 0.30 savegames, where some state in CreatureStats was not saved yet,
// and therefore needs to be loaded from ESM records. TODO: re-enable this in a future release.
/*
if (!ptr.getRefData().getCustomData())
{
// Create a CustomData, but don't fill it from ESM records (not needed)
std::auto_ptr<NpcCustomData> data (new NpcCustomData);
ptr.getRefData().setCustomData (data.release());
}
*/
NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData()); NpcCustomData& customData = dynamic_cast<NpcCustomData&> (*ptr.getRefData().getCustomData());
@ -1325,8 +1361,7 @@ namespace MWClass
ptr.getRefData().setCount(1); ptr.getRefData().setCount(1);
// Reset to original position // Reset to original position
ESM::Position& pos = ptr.getRefData().getPosition(); ptr.getRefData().setPosition(ptr.getCellRef().getPosition());
pos = ptr.getCellRef().getPosition();
ptr.getRefData().setCustomData(NULL); ptr.getRefData().setCustomData(NULL);
} }
@ -1340,27 +1375,4 @@ namespace MWClass
MWWorld::ContainerStore& store = getContainerStore(ptr); MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction()); store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
} }
const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect;
const ESM::GameSetting *Npc::fSneakSpeedMultiplier;
const ESM::GameSetting *Npc::fAthleticsRunBonus;
const ESM::GameSetting *Npc::fBaseRunMultiplier;
const ESM::GameSetting *Npc::fMinFlySpeed;
const ESM::GameSetting *Npc::fMaxFlySpeed;
const ESM::GameSetting *Npc::fSwimRunBase;
const ESM::GameSetting *Npc::fSwimRunAthleticsMult;
const ESM::GameSetting *Npc::fJumpEncumbranceBase;
const ESM::GameSetting *Npc::fJumpEncumbranceMultiplier;
const ESM::GameSetting *Npc::fJumpAcrobaticsBase;
const ESM::GameSetting *Npc::fJumpAcroMultiplier;
const ESM::GameSetting *Npc::fJumpRunMultiplier;
const ESM::GameSetting *Npc::fWereWolfRunMult;
const ESM::GameSetting *Npc::fKnockDownMult;
const ESM::GameSetting *Npc::iKnockDownOddsMult;
const ESM::GameSetting *Npc::iKnockDownOddsBase;
const ESM::GameSetting *Npc::fDamageStrengthBase;
const ESM::GameSetting *Npc::fDamageStrengthMult;
} }

View file

@ -17,27 +17,32 @@ namespace MWClass
virtual MWWorld::Ptr virtual MWWorld::Ptr
copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const; copyToCellImpl(const MWWorld::Ptr &ptr, MWWorld::CellStore &cell) const;
static const ESM::GameSetting *fMinWalkSpeed; struct GMST
static const ESM::GameSetting *fMaxWalkSpeed; {
static const ESM::GameSetting *fEncumberedMoveEffect; const ESM::GameSetting *fMinWalkSpeed;
static const ESM::GameSetting *fSneakSpeedMultiplier; const ESM::GameSetting *fMaxWalkSpeed;
static const ESM::GameSetting *fAthleticsRunBonus; const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fBaseRunMultiplier; const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fMinFlySpeed; const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fMaxFlySpeed; const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fSwimRunBase; const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fSwimRunAthleticsMult; const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fJumpEncumbranceBase; const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fJumpEncumbranceMultiplier; const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fJumpAcrobaticsBase; const ESM::GameSetting *fJumpEncumbranceBase;
static const ESM::GameSetting *fJumpAcroMultiplier; const ESM::GameSetting *fJumpEncumbranceMultiplier;
static const ESM::GameSetting *fJumpRunMultiplier; const ESM::GameSetting *fJumpAcrobaticsBase;
static const ESM::GameSetting *fWereWolfRunMult; const ESM::GameSetting *fJumpAcroMultiplier;
static const ESM::GameSetting *fKnockDownMult; const ESM::GameSetting *fJumpRunMultiplier;
static const ESM::GameSetting *iKnockDownOddsMult; const ESM::GameSetting *fWereWolfRunMult;
static const ESM::GameSetting *iKnockDownOddsBase; const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *fDamageStrengthBase; const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *fDamageStrengthMult; const ESM::GameSetting *iKnockDownOddsBase;
const ESM::GameSetting *fDamageStrengthBase;
const ESM::GameSetting *fDamageStrengthMult;
};
static const GMST& getGmst();
public: public:

View file

@ -127,7 +127,7 @@ namespace MWClass
std::string text; std::string text;
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects); info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects);
@ -166,13 +166,8 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Potion> *ref = MWWorld::LiveCellRef<ESM::Potion> *ref =
ptr.get<ESM::Potion>(); ptr.get<ESM::Potion>();
MWWorld::Ptr actor = MWBase::Environment::get().getWorld()->getPlayerPtr();
// remove used potion (assume it is present in inventory)
ptr.getContainerStore()->remove(ptr, 1, actor);
boost::shared_ptr<MWWorld::Action> action ( boost::shared_ptr<MWWorld::Action> action (
new MWWorld::ActionApply (actor, ref->mBase->mId)); new MWWorld::ActionApply (ptr, ref->mBase->mId));
action->setSound ("Drink"); action->setSound ("Drink");

View file

@ -85,7 +85,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Probe> *ref = MWWorld::LiveCellRef<ESM::Probe> *ref =
ptr.get<ESM::Probe>(); ptr.get<ESM::Probe>();
return ref->mBase->mData.mValue * (static_cast<float>(getItemHealth(ptr)) / getItemMaxHealth(ptr)); return ref->mBase->mData.mValue;
} }
void Probe::registerSelf() void Probe::registerSelf()
@ -137,7 +137,7 @@ namespace MWClass
text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses);
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

@ -76,7 +76,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Repair> *ref = MWWorld::LiveCellRef<ESM::Repair> *ref =
ptr.get<ESM::Repair>(); ptr.get<ESM::Repair>();
return ref->mBase->mData.mValue * (static_cast<float>(getItemHealth(ptr)) / getItemMaxHealth(ptr)); return ref->mBase->mData.mValue;
} }
void Repair::registerSelf() void Repair::registerSelf()
@ -141,7 +141,7 @@ namespace MWClass
text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses); text += "\n#{sUses}: " + MWGui::ToolTips::toString(remainingUses);
text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality); text += "\n#{sQuality}: " + MWGui::ToolTips::toString(ref->mBase->mData.mQuality);
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
if (MWBase::Environment::get().getWindowManager()->getFullHelp()) { if (MWBase::Environment::get().getWindowManager()->getFullHelp()) {
text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner"); text += MWGui::ToolTips::getMiscString(ptr.getCellRef().getOwner(), "Owner");

View file

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

View file

@ -154,7 +154,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Weapon> *ref = MWWorld::LiveCellRef<ESM::Weapon> *ref =
ptr.get<ESM::Weapon>(); ptr.get<ESM::Weapon>();
return ref->mBase->mData.mValue * (static_cast<float>(getItemHealth(ptr)) / getItemMaxHealth(ptr)); return ref->mBase->mData.mValue;
} }
void Weapon::registerSelf() void Weapon::registerSelf()
@ -343,7 +343,7 @@ namespace MWClass
} }
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight); text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}"); text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
info.enchant = ref->mBase->mEnchant; info.enchant = ref->mBase->mEnchant;

View file

@ -125,6 +125,10 @@ namespace MWDialogue
void DialogueManager::startDialogue (const MWWorld::Ptr& actor) void DialogueManager::startDialogue (const MWWorld::Ptr& actor)
{ {
// Dialogue with dead actor (e.g. through script) should not be allowed.
if (actor.getClass().getCreatureStats(actor).isDead())
return;
mLastTopic = ""; mLastTopic = "";
mPermanentDispositionChange = 0; mPermanentDispositionChange = 0;
mTemporaryDispositionChange = 0; mTemporaryDispositionChange = 0;
@ -140,7 +144,11 @@ namespace MWDialogue
mActorKnownTopics.clear(); mActorKnownTopics.clear();
MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow(); MWGui::DialogueWindow* win = MWBase::Environment::get().getWindowManager()->getDialogueWindow();
win->startDialogue(actor, actor.getClass().getName (actor));
// If the dialogue window was already open, keep the existing history
bool resetHistory = (!MWBase::Environment::get().getWindowManager()->containsMode(MWGui::GM_Dialogue));
win->startDialogue(actor, actor.getClass().getName (actor), resetHistory);
//setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI //setup the list of topics known by the actor. Topics who are also on the knownTopics list will be added to the GUI
updateTopics(); updateTopics();
@ -174,10 +182,20 @@ namespace MWDialogue
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext)); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext));
executeScript (info->mResultScript); executeScript (info->mResultScript);
mLastTopic = Misc::StringUtils::lowerCase(it->mId); mLastTopic = Misc::StringUtils::lowerCase(it->mId);
break; return;
} }
} }
} }
// No greetings found. The dialogue window should not be shown.
// If this is a companion, we must show the companion window directly (used by BM_bear_be_unique).
bool isCompanion = !mActor.getClass().getScript(mActor).empty()
&& mActor.getRefData().getLocals().getIntVar(mActor.getClass().getScript(mActor), "companion");
if (isCompanion)
{
MWBase::Environment::get().getWindowManager()->pushGuiMode(MWGui::GM_Companion);
MWBase::Environment::get().getWindowManager()->showCompanionWindow(mActor);
}
} }
bool DialogueManager::compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code) bool DialogueManager::compile (const std::string& cmd,std::vector<Interpreter::Type_Code>& code)
@ -294,7 +312,7 @@ namespace MWDialogue
{ {
if (iter->mId == info->mId) if (iter->mId == info->mId)
{ {
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor)); MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor);
break; break;
} }
} }
@ -398,7 +416,7 @@ namespace MWDialogue
win->setServices (windowServices); win->setServices (windowServices);
// sort again, because the previous sort was case-sensitive // sort again, because the previous sort was case-sensitive
keywordList.sort(Misc::StringUtils::ciEqual); keywordList.sort(Misc::StringUtils::ciLess);
win->setKeywords(keywordList); win->setKeywords(keywordList);
mChoice = choice; mChoice = choice;
@ -472,7 +490,7 @@ namespace MWDialogue
{ {
if (iter->mId == info->mId) if (iter->mId == info->mId)
{ {
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor)); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor);
break; break;
} }
} }
@ -694,6 +712,15 @@ namespace MWDialogue
return diff; return diff;
} }
void DialogueManager::clearInfoActor(const MWWorld::Ptr &actor) const
{
if (actor == mActor && !mLastTopic.empty())
{
MWBase::Environment::get().getJournal()->removeLastAddedTopicResponse(
mLastTopic, actor.getClass().getName(actor));
}
}
std::vector<HyperTextToken> ParseHyperText(const std::string& text) std::vector<HyperTextToken> ParseHyperText(const std::string& text)
{ {
std::vector<HyperTextToken> result; std::vector<HyperTextToken> result;

View file

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

View file

@ -530,7 +530,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_ShouldAttack: case SelectWrapper::Function_ShouldAttack:
return mActor.getClass().getCreatureStats(mActor).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() >= 80; return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor,
MWBase::Environment::get().getWorld()->getPlayerPtr());
case SelectWrapper::Function_CreatureTargetted: case SelectWrapper::Function_CreatureTargetted:

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -360,10 +360,16 @@ struct TypesetBookImpl::Typesetter : BookTypesetter
int spaceLeft = mPageHeight - (curPageStop - curPageStart); int spaceLeft = mPageHeight - (curPageStop - curPageStart);
int sectionHeight = i->mRect.height (); int sectionHeight = i->mRect.height ();
if (sectionHeight <= mPageHeight) // This is NOT equal to i->mRect.height(), which doesn't account for section breaks.
int spaceRequired = (i->mRect.bottom - curPageStop);
if (curPageStart == curPageStop) // If this is a new page, the section break is not needed
spaceRequired = i->mRect.height();
if (spaceRequired <= mPageHeight)
{ {
if (sectionHeight > spaceLeft) if (spaceRequired > spaceLeft)
{ {
// The section won't completely fit on the current page. Finish the current page and start a new one.
assert (curPageStart != curPageStop); assert (curPageStart != curPageStop);
mBook->mPages.push_back (Page (curPageStart, curPageStop)); mBook->mPages.push_back (Page (curPageStart, curPageStop));

View file

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

View file

@ -435,4 +435,10 @@ namespace MWGui
{ {
setSelectedObject(MWWorld::Ptr()); setSelectedObject(MWWorld::Ptr());
} }
void Console::resetReference()
{
ReferenceInterface::resetReference();
setSelectedObject(MWWorld::Ptr());
}
} }

View file

@ -66,6 +66,8 @@ namespace MWGui
void executeFile (const std::string& path); void executeFile (const std::string& path);
virtual void resetReference ();
protected: protected:
virtual void onReferenceUnavailable(); virtual void onReferenceUnavailable();

View file

@ -38,6 +38,32 @@ namespace MWGui
mIsOnDragAndDrop = true; mIsOnDragAndDrop = true;
mDragAndDropWidget->setVisible(true); mDragAndDropWidget->setVisible(true);
// If picking up an item that isn't from the player's inventory, the item gets added to player inventory backend
// immediately, even though it's still floating beneath the mouse cursor. A bit counterintuitive,
// but this is how it works in vanilla, and not doing so would break quests (BM_beasts for instance).
ItemModel* playerModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getModel();
if (mSourceModel != playerModel)
{
MWWorld::Ptr item = mSourceModel->moveItem(mItem, mDraggedCount, playerModel);
playerModel->update();
ItemModel::ModelIndex newIndex = -1;
for (unsigned int i=0; i<playerModel->getItemCount(); ++i)
{
if (playerModel->getItem(i).mBase == item)
{
newIndex = i;
break;
}
}
mItem = playerModel->getItem(newIndex);
mSourceModel = playerModel;
SortFilterItemModel* playerFilterModel = MWBase::Environment::get().getWindowManager()->getInventoryWindow()->getSortFilterModel();
mSourceSortModel = playerFilterModel;
}
std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase); std::string sound = mItem.mBase.getClass().getUpSoundId(mItem.mBase);
MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0); MWBase::Environment::get().getSoundManager()->playSound (sound, 1.0, 1.0);

View file

@ -366,10 +366,11 @@ namespace MWGui
} }
} }
void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName) void DialogueWindow::startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory)
{ {
mGoodbye = false; mGoodbye = false;
mEnabled = true; mEnabled = true;
bool sameActor = (mPtr == actor);
mPtr = actor; mPtr = actor;
mTopicsList->setEnabled(true); mTopicsList->setEnabled(true);
setTitle(npcName); setTitle(npcName);
@ -378,9 +379,12 @@ namespace MWGui
mTopicsList->clear(); mTopicsList->clear();
for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it) if (resetHistory || !sameActor)
delete (*it); {
mHistoryContents.clear(); for (std::vector<DialogueText*>::iterator it = mHistoryContents.begin(); it != mHistoryContents.end(); ++it)
delete (*it);
mHistoryContents.clear();
}
for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it) for (std::vector<Link*>::iterator it = mLinks.begin(); it != mLinks.end(); ++it)
delete (*it); delete (*it);
@ -582,13 +586,32 @@ namespace MWGui
//Clear the list of topics //Clear the list of topics
mTopicsList->clear(); mTopicsList->clear();
if (mPtr.getTypeName() == typeid(ESM::NPC).name()) bool dispositionVisible = false;
if (mPtr.getClass().isNpc())
{ {
dispositionVisible = true;
mDispositionBar->setProgressRange(100); mDispositionBar->setProgressRange(100);
mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr)); mDispositionBar->setProgressPosition(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr));
mDispositionText->eraseText(0, mDispositionText->getTextLength()); mDispositionText->eraseText(0, mDispositionText->getTextLength());
mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154"); mDispositionText->addText("#B29154"+boost::lexical_cast<std::string>(MWBase::Environment::get().getMechanicsManager()->getDerivedDisposition(mPtr))+std::string("/100")+"#B29154");
} }
bool dispositionWasVisible = mDispositionBar->getVisible();
if (dispositionVisible && !dispositionWasVisible)
{
mDispositionBar->setVisible(true);
float offset = mDispositionBar->getHeight()+5;
mTopicsList->setCoord(mTopicsList->getCoord() + MyGUI::IntCoord(0,offset,0,-offset));
mTopicsList->adjustSize();
}
else if (!dispositionVisible && dispositionWasVisible)
{
mDispositionBar->setVisible(false);
float offset = mDispositionBar->getHeight()+5;
mTopicsList->setCoord(mTopicsList->getCoord() - MyGUI::IntCoord(0,offset,0,-offset));
mTopicsList->adjustSize();
}
} }
void DialogueWindow::goodbye() void DialogueWindow::goodbye()

View file

@ -111,7 +111,7 @@ namespace MWGui
void notifyLinkClicked (TypesetBook::InteractiveId link); void notifyLinkClicked (TypesetBook::InteractiveId link);
void startDialogue(MWWorld::Ptr actor, std::string npcName); void startDialogue(MWWorld::Ptr actor, std::string npcName, bool resetHistory);
void setKeywords(std::list<std::string> keyWord); void setKeywords(std::list<std::string> keyWord);
void addResponse (const std::string& text, const std::string& title=""); void addResponse (const std::string& text, const std::string& title="");
@ -170,7 +170,7 @@ namespace MWGui
BookPage* mHistory; BookPage* mHistory;
Widgets::MWList* mTopicsList; Widgets::MWList* mTopicsList;
MyGUI::ScrollBar* mScrollBar; MyGUI::ScrollBar* mScrollBar;
MyGUI::ProgressPtr mDispositionBar; MyGUI::Progress* mDispositionBar;
MyGUI::EditBox* mDispositionText; MyGUI::EditBox* mDispositionText;
PersuasionDialog mPersuasionDialog; PersuasionDialog mPersuasionDialog;

View file

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

View file

@ -384,6 +384,8 @@ namespace MWGui
void HUD::onFrame(float dt) void HUD::onFrame(float dt)
{ {
LocalMapBase::onFrame(dt);
mCellNameTimer -= dt; mCellNameTimer -= dt;
mWeaponSpellTimer -= dt; mWeaponSpellTimer -= dt;
if (mCellNameTimer < 0) if (mCellNameTimer < 0)

View file

@ -132,6 +132,11 @@ namespace MWGui
adjustPanes(); adjustPanes();
} }
SortFilterItemModel* InventoryWindow::getSortFilterModel()
{
return mSortModel;
}
TradeItemModel* InventoryWindow::getTradeModel() TradeItemModel* InventoryWindow::getTradeModel()
{ {
return mTradeModel; return mTradeModel;

View file

@ -37,6 +37,7 @@ namespace MWGui
mPreview->rebuild(); mPreview->rebuild();
} }
SortFilterItemModel* getSortFilterModel();
TradeItemModel* getTradeModel(); TradeItemModel* getTradeModel();
ItemModel* getModel(); ItemModel* getModel();

View file

@ -33,12 +33,26 @@ namespace MWGui
void ItemWidget::setIcon(const std::string &icon) void ItemWidget::setIcon(const std::string &icon)
{ {
// HACK HACK HACK: Don't setImageTexture if it hasn't changed.
// There is a leak in MyGUI for each setImageTexture on the same widget.
// http://www.ogre3d.org/addonforums/viewtopic.php?f=17&t=30251
if (mCurrentItemTexture == icon)
return;
mCurrentItemTexture = icon;
if (mItem) if (mItem)
mItem->setImageTexture(icon); mItem->setImageTexture(icon);
} }
void ItemWidget::setFrame(const std::string &frame, const MyGUI::IntCoord &coord) void ItemWidget::setFrame(const std::string &frame, const MyGUI::IntCoord &coord)
{ {
// HACK HACK HACK: Don't setImageTexture if it hasn't changed.
// There is a leak in MyGUI for each setImageTexture on the same widget.
// http://www.ogre3d.org/addonforums/viewtopic.php?f=17&t=30251
if (mCurrentFrameTexture == frame)
return;
mCurrentFrameTexture = frame;
if (mFrame) if (mFrame)
{ {
mFrame->setImageTexture(frame); mFrame->setImageTexture(frame);
@ -69,8 +83,21 @@ namespace MWGui
if (ptr.isEmpty()) if (ptr.isEmpty())
{ {
if (mFrame) if (mFrame)
mFrame->setImageTexture(""); {
mItem->setImageTexture(""); // HACK HACK HACK: Don't setImageTexture if it hasn't changed.
// There is a leak in MyGUI for each setImageTexture on the same widget.
// http://www.ogre3d.org/addonforums/viewtopic.php?f=17&t=30251
if (!mCurrentFrameTexture.empty())
{
mFrame->setImageTexture("");
mCurrentFrameTexture = "";
}
}
if (!mCurrentItemTexture.empty())
{
mCurrentItemTexture = "";
mItem->setImageTexture("");
}
return; return;
} }

View file

@ -42,6 +42,9 @@ namespace MWGui
MyGUI::ImageBox* mItem; MyGUI::ImageBox* mItem;
MyGUI::ImageBox* mFrame; MyGUI::ImageBox* mFrame;
std::string mCurrentItemTexture;
std::string mCurrentFrameTexture;
}; };
} }

View file

@ -104,9 +104,10 @@ namespace MWGui
int attribute = mSpentAttributes[i]; int attribute = mSpentAttributes[i];
int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 30; int xdiff = mAttributeMultipliers[attribute]->getCaption() == "" ? 0 : 20;
MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(24+xdiff,-4); MyGUI::IntPoint pos = mAttributes[attribute]->getAbsolutePosition() - mMainWidget->getAbsolutePosition() - MyGUI::IntPoint(22+xdiff,0);
pos.top += (mAttributes[attribute]->getHeight() - image->getHeight())/2;
image->setPosition(pos); image->setPosition(pos);
} }

View file

@ -101,7 +101,7 @@ namespace MWGui
size_t viewRange = mScrollView->getCanvasSize().height; size_t viewRange = mScrollView->getCanvasSize().height;
if(viewPosition > viewRange) if(viewPosition > viewRange)
viewPosition = viewRange; viewPosition = viewRange;
mScrollView->setViewOffset(MyGUI::IntPoint(0, -viewPosition)); mScrollView->setViewOffset(MyGUI::IntPoint(0, viewPosition * -1));
} }
void MWList::setPropertyOverride(const std::string &_key, const std::string &_value) void MWList::setPropertyOverride(const std::string &_key, const std::string &_value)

View file

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

View file

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

View file

@ -36,6 +36,7 @@ namespace MWGui
, mLastDirectionX(0.0f) , mLastDirectionX(0.0f)
, mLastDirectionY(0.0f) , mLastDirectionY(0.0f)
, mCompass(NULL) , mCompass(NULL)
, mMarkerUpdateTimer(0.0f)
{ {
} }
@ -345,6 +346,18 @@ namespace MWGui
markerWidget->setUserString("IsMarker", "true"); markerWidget->setUserString("IsMarker", "true");
markerWidget->setUserData(markerPos); markerWidget->setUserData(markerPos);
markerWidget->setColour(markerColour); markerWidget->setColour(markerColour);
mMarkerWidgets.push_back(markerWidget);
}
}
void LocalMapBase::onFrame(float dt)
{
mMarkerUpdateTimer += dt;
if (mMarkerUpdateTimer >= 0.25)
{
mMarkerUpdateTimer = 0;
updateMarkers();
} }
} }
@ -418,6 +431,9 @@ namespace MWGui
{ {
mGlobalMapRender = new MWRender::GlobalMap(""); mGlobalMapRender = new MWRender::GlobalMap("");
mGlobalMapRender->render(loadingListener); mGlobalMapRender->render(loadingListener);
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setImageTexture("GlobalMap.png"); mGlobalMapImage->setImageTexture("GlobalMap.png");
mGlobalMapOverlay->setImageTexture("GlobalMapOverlay"); mGlobalMapOverlay->setImageTexture("GlobalMapOverlay");
} }
@ -471,6 +487,8 @@ namespace MWGui
void MapWindow::onFrame(float dt) void MapWindow::onFrame(float dt)
{ {
LocalMapBase::onFrame(dt);
for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it) for (std::vector<CellId>::iterator it = mQueuedToExplore.begin(); it != mQueuedToExplore.end(); ++it)
{ {
mGlobalMapRender->exploreCell(it->first, it->second); mGlobalMapRender->exploreCell(it->first, it->second);
@ -497,7 +515,6 @@ namespace MWGui
else else
mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff ); mGlobalMap->setViewOffset( mGlobalMap->getViewOffset() + diff );
mLastDragPos = MyGUI::IntPoint(_left, _top); mLastDragPos = MyGUI::IntPoint(_left, _top);
} }
@ -521,9 +538,6 @@ namespace MWGui
void MapWindow::open() void MapWindow::open()
{ {
mGlobalMap->setCanvasSize (mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
mGlobalMapImage->setSize(mGlobalMapRender->getWidth(), mGlobalMapRender->getHeight());
// force markers to foreground // force markers to foreground
for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i) for (unsigned int i=0; i<mGlobalMapOverlay->getChildCount (); ++i)
{ {

View file

@ -35,6 +35,8 @@ namespace MWGui
void setPlayerDir(const float x, const float y); void setPlayerDir(const float x, const float y);
void setPlayerPos(const float x, const float y); void setPlayerPos(const float x, const float y);
void onFrame(float dt);
bool toggleFogOfWar(); bool toggleFogOfWar();
struct MarkerPosition struct MarkerPosition
@ -73,12 +75,14 @@ namespace MWGui
virtual void notifyMapChanged() {} virtual void notifyMapChanged() {}
// Update markers (Detect X effects, Mark/Recall effects) // Update markers (Detect X effects, Mark/Recall effects)
// Note, door markers handled in setActiveCell // Note, door markers are handled in setActiveCell
void updateMarkers(); void updateMarkers();
void addDetectionMarkers(int type); void addDetectionMarkers(int type);
OEngine::GUI::Layout* mLayout; OEngine::GUI::Layout* mLayout;
float mMarkerUpdateTimer;
bool mMapDragAndDrop; bool mMapDragAndDrop;
float mLastPositionX; float mLastPositionX;

View file

@ -25,6 +25,23 @@ namespace MWGui
} }
} }
void MessageBoxManager::clear()
{
delete mInterMessageBoxe;
mInterMessageBoxe = NULL;
std::vector<MessageBox*>::iterator it(mMessageBoxes.begin());
for (; it != mMessageBoxes.end(); ++it)
{
if (*it == mStaticMessageBox)
mStaticMessageBox = NULL;
delete *it;
}
mMessageBoxes.clear();
mLastButtonPressed = -1;
}
void MessageBoxManager::onFrame (float frameDuration) void MessageBoxManager::onFrame (float frameDuration)
{ {
std::vector<MessageBox*>::iterator it; std::vector<MessageBox*>::iterator it;

View file

@ -30,6 +30,9 @@ namespace MWGui
bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons); bool createInteractiveMessageBox (const std::string& message, const std::vector<std::string>& buttons);
bool isInteractiveMessageBox (); bool isInteractiveMessageBox ();
/// Remove all message boxes
void clear();
bool removeMessageBox (MessageBox *msgbox); bool removeMessageBox (MessageBox *msgbox);
void setMessageBoxSpeed (int speed); void setMessageBoxSpeed (int speed);

View file

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

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