Merge branch 'master' of https://github.com/zinnschlag/openmw into DragDrop

This commit is contained in:
Marek Kochanowicz 2014-02-12 13:13:32 +01:00
commit c142089bfe
336 changed files with 10938 additions and 3999 deletions

1
.gitignore vendored
View file

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

View file

@ -14,15 +14,30 @@ endif (APPLE)
set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/)
include (OpenMWMacros) include(OpenMWMacros)
# Version # Version
set (OPENMW_VERSION_MAJOR 0) include(GetGitRevisionDescription)
set (OPENMW_VERSION_MINOR 27)
set (OPENMW_VERSION_RELEASE 0)
set (OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}") get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if (MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" OPENMW_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" OPENMW_VERSION_RELEASE "${VERSION}")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
message(STATUS "Configuring OpenMW ${OPENMW_VERSION}...")
else (MATCH)
message(FATAL_ERROR "Failed to get valid version information from Git")
endif (MATCH)
# doxygen main page # doxygen main page
@ -93,8 +108,6 @@ set(OENGINE_GUI
) )
set(OENGINE_BULLET set(OENGINE_BULLET
${LIBDIR}/openengine/bullet/btKinematicCharacterController.cpp
${LIBDIR}/openengine/bullet/btKinematicCharacterController.h
${LIBDIR}/openengine/bullet/BtOgre.cpp ${LIBDIR}/openengine/bullet/BtOgre.cpp
${LIBDIR}/openengine/bullet/BtOgreExtras.h ${LIBDIR}/openengine/bullet/BtOgreExtras.h
${LIBDIR}/openengine/bullet/BtOgreGP.h ${LIBDIR}/openengine/bullet/BtOgreGP.h
@ -188,8 +201,6 @@ if (WIN32)
add_definitions(-DSDL_MAIN_HANDLED) add_definitions(-DSDL_MAIN_HANDLED)
else (WIN32) else (WIN32)
set(PLATFORM_INCLUDE_DIR "") set(PLATFORM_INCLUDE_DIR "")
find_path (UUID_INCLUDE_DIR uuid/uuid.h)
include_directories(${UUID_INCLUDE_DIR})
endif (WIN32) endif (WIN32)
if (MSVC10) if (MSVC10)
set(PLATFORM_INCLUDE_DIR "") set(PLATFORM_INCLUDE_DIR "")
@ -241,7 +252,6 @@ include_directories("."
${MYGUI_INCLUDE_DIRS} ${MYGUI_INCLUDE_DIRS}
${MYGUI_PLATFORM_INCLUDE_DIRS} ${MYGUI_PLATFORM_INCLUDE_DIRS}
${OPENAL_INCLUDE_DIR} ${OPENAL_INCLUDE_DIR}
${UUID_INCLUDE_DIR}
${LIBDIR} ${LIBDIR}
) )
@ -411,46 +421,6 @@ IF(NOT WIN32 AND NOT APPLE)
# Install resources # Install resources
INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources") INSTALL(DIRECTORY "${OpenMW_BINARY_DIR}/resources" DESTINATION "${DATADIR}" FILE_PERMISSIONS OWNER_READ GROUP_READ WORLD_READ COMPONENT "Resources")
INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources") INSTALL(DIRECTORY DESTINATION "${DATADIR}/data" COMPONENT "Resources")
IF (DPKG_PROGRAM)
## Debian Specific
IF(IS_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/.git")
EXEC_PROGRAM("git" ${CMAKE_CURRENT_SOURCE_DIR} ARGS "describe" OUTPUT_VARIABLE GIT_VERSION )
STRING(REGEX REPLACE "openmw-" "" VERSION_STRING "${GIT_VERSION}")
EXEC_PROGRAM("git" ARGS "config --get user.name" OUTPUT_VARIABLE GIT_NAME )
EXEC_PROGRAM("git" ARGS "config --get user.email" OUTPUT_VARIABLE GIT_EMAIL)
SET(PACKAGE_MAINTAINER "${GIT_NAME} <${GIT_EMAIL}>")
ELSE()
SET(VERSION_STRING "${OPENMW_VERSION}")
SET(PACKAGE_MAINTAINER "unknown")
ENDIF()
SET(CPACK_GENERATOR "DEB")
SET(CPACK_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_HOMEPAGE "http://openmw.org")
SET(CPACK_DEBIAN_PACKAGE_PRIORITY "optional")
SET(CPACK_DEBIAN_PACKAGE_MAINTAINER "${PACKAGE_MAINTAINER}")
SET(CPACK_DEBIAN_PACKAGE_DESCRIPTION "A reimplementation of The Elder Scrolls III: Morrowind
OpenMW is a reimplementation of the Bethesda Game Studios game The Elder Scrolls III: Morrowind.
Data files from the original game is required to run it.")
SET(CPACK_DEBIAN_PACKAGE_NAME "openmw")
SET(CPACK_DEBIAN_PACKAGE_VERSION "${VERSION_STRING}")
SET(CPACK_PACKAGE_EXECUTABLES "openmw;OpenMW opencs;OpenCS bsatool;Bsatool esmtool;Esmtool omwlauncher;OMWLauncher mwiniimporter;MWiniImporter")
SET(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.11.2), libfreetype6 (>= 2.2.1), libgcc1 (>= 1:4.1.1), libmpg123-0 (>= 1.12.1), libopenal1 (>= 1:1.12.854), libsndfile1 (>= 1.0.23), libstdc++6 (>= 4.4.5), libuuid1 (>= 2.17.2), libqtgui4 (>= 4.7.0)")
SET(CPACK_DEBIAN_PACKAGE_SECTION "Games")
STRING(TOLOWER "${CPACK_PACKAGE_NAME}" CPACK_PACKAGE_NAME_LOWERCASE)
EXECUTE_PROCESS(
COMMAND ${DPKG_PROGRAM} --print-architecture
OUTPUT_VARIABLE CPACK_DEBIAN_PACKAGE_ARCHITECTURE
OUTPUT_STRIP_TRAILING_WHITESPACE
)
SET(CPACK_PACKAGE_FILE_NAME "${CPACK_PACKAGE_NAME_LOWERCASE}_${CPACK_DEBIAN_PACKAGE_VERSION}_${CPACK_DEBIAN_PACKAGE_ARCHITECTURE}")
INCLUDE(CPack)
ENDIF(DPKG_PROGRAM)
ENDIF(NOT WIN32 AND NOT APPLE) ENDIF(NOT WIN32 AND NOT APPLE)
if(WIN32) if(WIN32)

View file

@ -236,7 +236,9 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
// Loop through all the references // Loop through all the references
ESM::CellRef ref; ESM::CellRef ref;
if(!quiet) std::cout << " References:\n"; if(!quiet) std::cout << " References:\n";
while(cell.getNextRef(esm, ref))
bool deleted = false;
while(cell.getNextRef(esm, ref, deleted))
{ {
if (save) { if (save) {
info.data.mCellRefs[&cell].push_back(ref); info.data.mCellRefs[&cell].push_back(ref);
@ -244,13 +246,14 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
if(quiet) continue; if(quiet) continue;
std::cout << " Refnum: " << ref.mRefnum << std::endl; std::cout << " Refnum: " << ref.mRefNum.mIndex << std::endl;
std::cout << " ID: '" << ref.mRefID << "'\n"; std::cout << " ID: '" << ref.mRefID << "'\n";
std::cout << " Owner: '" << ref.mOwner << "'\n"; std::cout << " Owner: '" << ref.mOwner << "'\n";
std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n"; std::cout << " Enchantment charge: '" << ref.mEnchantmentCharge << "'\n";
std::cout << " Uses/health: '" << ref.mCharge << "'\n"; std::cout << " Uses/health: '" << ref.mCharge << "'\n";
std::cout << " Gold value: '" << ref.mGoldValue << "'\n"; std::cout << " Gold value: '" << ref.mGoldValue << "'\n";
std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl; std::cout << " Blocked: '" << static_cast<int>(ref.mReferenceBlocked) << "'" << std::endl;
std::cout << " Deleted: " << deleted << std::endl;
} }
} }

View file

@ -680,7 +680,7 @@ std::string creatureFlags(int flags)
if (flags & ESM::Creature::Walks) properties += "Walks "; if (flags & ESM::Creature::Walks) properties += "Walks ";
if (flags & ESM::Creature::Swims) properties += "Swims "; if (flags & ESM::Creature::Swims) properties += "Swims ";
if (flags & ESM::Creature::Flies) properties += "Flies "; if (flags & ESM::Creature::Flies) properties += "Flies ";
if (flags & ESM::Creature::Biped) properties += "Biped "; if (flags & ESM::Creature::Bipedal) properties += "Bipedal ";
if (flags & ESM::Creature::Respawn) properties += "Respawn "; if (flags & ESM::Creature::Respawn) properties += "Respawn ";
if (flags & ESM::Creature::Weapon) properties += "Weapon "; if (flags & ESM::Creature::Weapon) properties += "Weapon ";
if (flags & ESM::Creature::Skeleton) properties += "Skeleton "; if (flags & ESM::Creature::Skeleton) properties += "Skeleton ";
@ -691,7 +691,7 @@ std::string creatureFlags(int flags)
ESM::Creature::Walks| ESM::Creature::Walks|
ESM::Creature::Swims| ESM::Creature::Swims|
ESM::Creature::Flies| ESM::Creature::Flies|
ESM::Creature::Biped| ESM::Creature::Bipedal|
ESM::Creature::Respawn| ESM::Creature::Respawn|
ESM::Creature::Weapon| ESM::Creature::Weapon|
ESM::Creature::Skeleton| ESM::Creature::Skeleton|

View file

@ -835,7 +835,6 @@ void Record<ESM::CreatureLevList>::print()
{ {
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << creatureListFlags(mData.mFlags) << std::endl;
std::cout << " Chance none: " << mData.mChanceNone << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit; std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
@ -848,7 +847,6 @@ void Record<ESM::ItemLevList>::print()
{ {
std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl; std::cout << " Chance for None: " << (int)mData.mChanceNone << std::endl;
std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl; std::cout << " Flags: " << itemListFlags(mData.mFlags) << std::endl;
std::cout << " Chance none: " << mData.mChanceNone << std::endl;
std::cout << " Number of items: " << mData.mList.size() << std::endl; std::cout << " Number of items: " << mData.mList.size() << std::endl;
std::vector<ESM::LeveledListBase::LevelItem>::iterator iit; std::vector<ESM::LeveledListBase::LevelItem>::iterator iit;
for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++) for (iit = mData.mList.begin(); iit != mData.mList.end(); iit++)
@ -960,7 +958,7 @@ void Record<ESM::MagicEffect>::print()
std::cout << " RGB Color: " << "(" std::cout << " RGB Color: " << "("
<< mData.mData.mRed << "," << mData.mData.mRed << ","
<< mData.mData.mGreen << "," << mData.mData.mGreen << ","
<< mData.mData.mGreen << ")" << std::endl; << mData.mData.mBlue << ")" << std::endl;
} }
template<> template<>

View file

@ -1,5 +1,10 @@
#include "maindialog.hpp" #include "maindialog.hpp"
#include <components/version/version.hpp>
#include <QLabel>
#include <QDate>
#include <QTime>
#include <QPushButton> #include <QPushButton>
#include <QFontDatabase> #include <QFontDatabase>
#include <QInputDialog> #include <QInputDialog>
@ -67,6 +72,22 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
// Remove what's this? button // Remove what's this? button
setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint); setWindowFlags(this->windowFlags() & ~Qt::WindowContextHelpButtonHint);
// Add version information to bottom of the window
QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH);
if (revision == tag) {
versionLabel->setText(tr("OpenMW %0 release").arg(OPENMW_VERSION));
} else {
versionLabel->setText(tr("OpenMW development (%0)").arg(revision.left(10)));
}
// Add the compile date and time
versionLabel->setToolTip(tr("Compiled on %0 %1").arg(QLocale(QLocale::C).toDate(QString(__DATE__).simplified(),
QLatin1String("MMM d yyyy")).toString(Qt::SystemLocaleLongDate),
QLocale(QLocale::C).toTime(QString(__TIME__).simplified(),
QLatin1String("hh:mm:ss")).toString(Qt::SystemLocaleShortDate)));
createIcons(); createIcons();
} }

View file

@ -235,7 +235,7 @@ namespace
{ {
for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir ) for ( bfs::recursive_directory_iterator end, dir(in); dir != end; ++dir )
{ {
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
return dir->path(); return dir->path();
} }
} }
@ -243,7 +243,7 @@ namespace
{ {
for ( bfs::directory_iterator end, dir(in); dir != end; ++dir ) for ( bfs::directory_iterator end, dir(in); dir != end; ++dir )
{ {
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
return dir->path(); return dir->path();
} }
} }
@ -255,7 +255,7 @@ namespace
{ {
for(bfs::directory_iterator end, dir(in); dir != end; ++dir) for(bfs::directory_iterator end, dir(in); dir != end; ++dir)
{ {
if(Misc::StringUtils::lowerCase(dir->path().filename().string()) == filename) if(Misc::StringUtils::ciEqual(dir->path().filename().string(), filename))
return true; return true;
} }

View file

@ -623,6 +623,17 @@ MwIniImporter::MwIniImporter()
"Moons:Masser Fade Out Finish", "Moons:Masser Fade Out Finish",
"Moons:Script Color", "Moons:Script Color",
// blood
"Blood:Model 0",
"Blood:Model 1",
"Blood:Model 2",
"Blood:Texture 0",
"Blood:Texture 1",
"Blood:Texture 2",
"Blood:Texture Name 0",
"Blood:Texture Name 1",
"Blood:Texture Name 2",
0 0
}; };

View file

@ -38,7 +38,7 @@ opencs_units (model/tools
opencs_units_noqt (model/tools opencs_units_noqt (model/tools
mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck mandatoryid skillcheck classcheck factioncheck racecheck soundcheck regioncheck
birthsigncheck spellcheck birthsigncheck spellcheck referenceablecheck
) )

View file

@ -133,16 +133,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
state==CSMWorld::RecordBase::State_ModifiedOnly || state==CSMWorld::RecordBase::State_ModifiedOnly ||
infoModified) infoModified)
{ {
// always write the topic record mState.getWriter().startRecord (topic.mModified.sRecordId);
std::string type;
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&topic.mModified.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("NAME", topic.mModified.mId); mState.getWriter().writeHNCString ("NAME", topic.mModified.mId);
topic.mModified.save (mState.getWriter()); topic.mModified.save (mState.getWriter());
mState.getWriter().endRecord (type); mState.getWriter().endRecord (topic.mModified.sRecordId);
// write modified selected info records // write modified selected info records
for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second; for (CSMWorld::InfoCollection::RecordConstIterator iter (range.first); iter!=range.second;
@ -178,15 +172,10 @@ void CSMDoc::WriteDialogueCollectionStage::perform (int stage, std::vector<std::
next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1); next->mModified.mId.substr (next->mModified.mId.find_last_of ('#')+1);
} }
std::string type; mState.getWriter().startRecord (info.sRecordId);
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&info.sRecordId)[i];
mState.getWriter().startRecord (type);
mState.getWriter().writeHNCString ("INAM", info.mId); mState.getWriter().writeHNCString ("INAM", info.mId);
info.save (mState.getWriter()); info.save (mState.getWriter());
mState.getWriter().endRecord (type); mState.getWriter().endRecord (info.sRecordId);
} }
} }
} }

View file

@ -104,10 +104,10 @@ namespace CSMDoc
/// \todo make endianess agnostic (change ESMWriter interface?) /// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i]; type += reinterpret_cast<const char *> (&mCollection.getRecord (stage).mModified.sRecordId)[i];
mState.getWriter().startRecord (type); mState.getWriter().startRecord (mCollection.getRecord (stage).mModified.sRecordId);
mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage)); mState.getWriter().writeHNCString ("NAME", mCollection.getId (stage));
mCollection.getRecord (stage).mModified.save (mState.getWriter()); mCollection.getRecord (stage).mModified.save (mState.getWriter());
mState.getWriter().endRecord (type); mState.getWriter().endRecord (mCollection.getRecord (stage).mModified.sRecordId);
} }
else if (state==CSMWorld::RecordBase::State_Deleted) else if (state==CSMWorld::RecordBase::State_Deleted)
{ {

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,78 @@
#ifndef REFERENCEABLECHECKSTAGE_H
#define REFERENCEABLECHECKSTAGE_H
#include "../world/universalid.hpp"
#include "../doc/stage.hpp"
#include "../world/data.hpp"
#include "../world/refiddata.hpp"
namespace CSMTools
{
class ReferenceableCheckStage : public CSMDoc::Stage
{
public:
ReferenceableCheckStage(const CSMWorld::RefIdData& referenceable,
const CSMWorld::IdCollection<ESM::Race>& races,
const CSMWorld::IdCollection<ESM::Class>& classes,
const CSMWorld::IdCollection<ESM::Faction>& factions);
virtual void perform(int stage, std::vector< std::string >& messages);
virtual int setup();
private:
//CONCRETE CHECKS
void bookCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Book >& records, std::vector< std::string >& messages);
void activatorCheck(int stage, const CSMWorld::RefIdDataContainer< ESM::Activator >& records, std::vector< std::string >& messages);
void potionCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Potion>& records, std::vector<std::string>& messages);
void apparatusCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Apparatus>& records, std::vector<std::string>& messages);
void armorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Armor>& records, std::vector<std::string>& messages);
void clothingCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Clothing>& records, std::vector<std::string>& messages);
void containerCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Container>& records, std::vector<std::string>& messages);
void creatureCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Creature>& records, std::vector<std::string>& messages);
void doorCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Door>& records, std::vector<std::string>& messages);
void ingredientCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Ingredient>& records, std::vector<std::string>& messages);
void creaturesLevListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::CreatureLevList>& records, std::vector<std::string>& messages);
void itemLevelledListCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::ItemLevList>& records, std::vector<std::string>& messages);
void lightCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Light>& records, std::vector<std::string>& messages);
void lockpickCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Lockpick>& records, std::vector<std::string>& messages);
void miscCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Miscellaneous>& records, std::vector<std::string>& messages);
void npcCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::NPC>& records, std::vector<std::string>& messages);
void weaponCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Weapon>& records, std::vector<std::string>& messages);
void probeCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Probe>& records, std::vector<std::string>& messages);
void repairCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Repair>& records, std::vector<std::string>& messages);
void staticCheck(int stage, const CSMWorld::RefIdDataContainer<ESM::Static>& records, std::vector<std::string>& messages);
//FINAL CHECK
void finalCheck(std::vector<std::string>& messages);
//TEMPLATE CHECKS
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
std::vector<std::string>& messages,
const std::string& someID,
bool enchantable); //for all enchantable items.
template<typename ITEM> void inventoryItemCheck(const ITEM& someItem,
std::vector<std::string>& messages,
const std::string& someID); //for non-enchantable items.
template<typename TOOL> void toolCheck(const TOOL& someTool,
std::vector<std::string>& messages,
const std::string& someID,
bool canbebroken); //for tools with uses.
template<typename TOOL> void toolCheck(const TOOL& someTool,
std::vector<std::string>& messages,
const std::string& someID); //for tools without uses.
template<typename LIST> void listCheck(const LIST& someList,
std::vector< std::string >& messages,
const std::string& someID);
const CSMWorld::RefIdData& mReferencables;
const CSMWorld::IdCollection<ESM::Race>& mRaces;
const CSMWorld::IdCollection<ESM::Class>& mClasses;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
bool mPlayerPresent;
};
}
#endif // REFERENCEABLECHECKSTAGE_H

View file

@ -19,6 +19,7 @@
#include "regioncheck.hpp" #include "regioncheck.hpp"
#include "birthsigncheck.hpp" #include "birthsigncheck.hpp"
#include "spellcheck.hpp" #include "spellcheck.hpp"
#include "referenceablecheck.hpp"
CSMDoc::Operation *CSMTools::Tools::get (int type) CSMDoc::Operation *CSMTools::Tools::get (int type)
{ {
@ -74,6 +75,8 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns())); mVerifier->appendStage (new BirthsignCheckStage (mData.getBirthsigns()));
mVerifier->appendStage (new SpellCheckStage (mData.getSpells())); mVerifier->appendStage (new SpellCheckStage (mData.getSpells()));
mVerifier->appendStage (new ReferenceableCheckStage (mData.getReferenceables().getDataSet(), mData.getRaces(), mData.getClasses(), mData.getFactions()));
} }
return mVerifier; return mVerifier;
@ -139,3 +142,4 @@ void CSMTools::Tools::verifierMessage (const QString& message, int type)
if (iter!=mActiveReports.end()) if (iter!=mActiveReports.end())
mReports[iter->second]->add (message.toStdString()); mReports[iter->second]->add (message.toStdString());
} }

View file

@ -98,6 +98,10 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None); UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
virtual void cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type);
virtual int searchId (const std::string& id) const; virtual int searchId (const std::string& id) const;
////< Search record with \a id. ////< Search record with \a id.
/// \return index of record (if found) or -1 (not found) /// \return index of record (if found) or -1 (not found)
@ -193,6 +197,19 @@ namespace CSMWorld
return true; return true;
} }
template<typename ESXRecordT, typename IdAccessorT>
void Collection<ESXRecordT, IdAccessorT>::cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type)
{
Record<ESXRecordT> copy;
copy.mModified = getRecord(origin).get();
copy.mState = RecordBase::State_ModifiedOnly;
copy.get().mId = destination;
insertRecord(copy, getAppendIndex(destination, type));
}
template<typename ESXRecordT, typename IdAccessorT> template<typename ESXRecordT, typename IdAccessorT>
Collection<ESXRecordT, IdAccessorT>::Collection() Collection<ESXRecordT, IdAccessorT>::Collection()
{} {}

View file

@ -74,6 +74,10 @@ namespace CSMWorld
UniversalId::Type type = UniversalId::Type_None) = 0; UniversalId::Type type = UniversalId::Type_None) = 0;
///< If the record type does not match, an exception is thrown. ///< If the record type does not match, an exception is thrown.
virtual void cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type) = 0;
virtual const RecordBase& getRecord (const std::string& id) const = 0; virtual const RecordBase& getRecord (const std::string& id) const = 0;
virtual const RecordBase& getRecord (int index) const = 0; virtual const RecordBase& getRecord (int index) const = 0;

View file

@ -220,7 +220,7 @@ int CSMWorld::Columns::getId (const std::string& name)
std::string name2 = Misc::StringUtils::lowerCase (name); std::string name2 = Misc::StringUtils::lowerCase (name);
for (int i=0; sNames[i].mName; ++i) for (int i=0; sNames[i].mName; ++i)
if (name2==Misc::StringUtils::lowerCase (sNames[i].mName)) if (Misc::StringUtils::ciEqual(sNames[i].mName, name2))
return sNames[i].mId; return sNames[i].mId;
return -1; return -1;

View file

@ -4,10 +4,10 @@
#include <QAbstractItemModel> #include <QAbstractItemModel>
#include "idtable.hpp" #include "idtable.hpp"
#include "idtable.hpp" #include <components/misc/stringops.hpp>
CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index, CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
const QVariant& new_, QUndoCommand *parent) const QVariant& new_, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_) : QUndoCommand (parent), mModel (model), mIndex (index), mNew (new_)
{ {
mOld = mModel.data (mIndex, Qt::EditRole); mOld = mModel.data (mIndex, Qt::EditRole);
@ -25,7 +25,7 @@ void CSMWorld::ModifyCommand::undo()
mModel.setData (mIndex, mOld); mModel.setData (mIndex, mOld);
} }
CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand *parent) CSMWorld::CreateCommand::CreateCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None) : QUndoCommand (parent), mModel (model), mId (id), mType (UniversalId::Type_None)
{ {
setText (("Create record " + id).c_str()); setText (("Create record " + id).c_str());
@ -54,7 +54,7 @@ void CSMWorld::CreateCommand::undo()
mModel.removeRow (mModel.getModelIndex (mId, 0).row()); mModel.removeRow (mModel.getModelIndex (mId, 0).row());
} }
CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand *parent) CSMWorld::RevertCommand::RevertCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0) : QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{ {
setText (("Revert record " + id).c_str()); setText (("Revert record " + id).c_str());
@ -89,7 +89,7 @@ void CSMWorld::RevertCommand::undo()
mModel.setRecord (mId, *mOld); mModel.setRecord (mId, *mOld);
} }
CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand *parent) CSMWorld::DeleteCommand::DeleteCommand (IdTable& model, const std::string& id, QUndoCommand* parent)
: QUndoCommand (parent), mModel (model), mId (id), mOld (0) : QUndoCommand (parent), mModel (model), mId (id), mOld (0)
{ {
setText (("Delete record " + id).c_str()); setText (("Delete record " + id).c_str());
@ -126,7 +126,7 @@ void CSMWorld::DeleteCommand::undo()
CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex,
const std::vector<int>& newOrder) const std::vector<int>& newOrder)
: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) : mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder)
{} {}
@ -140,8 +140,35 @@ void CSMWorld::ReorderRowsCommand::undo()
int size = static_cast<int> (mNewOrder.size()); int size = static_cast<int> (mNewOrder.size());
std::vector<int> reverse (size); std::vector<int> reverse (size);
for (int i=0; i<size; ++i) for (int i=0; i< size; ++i)
reverse.at (mNewOrder[i]) = i; reverse.at (mNewOrder[i]) = i;
mModel.reorderRows (mBaseIndex, reverse); mModel.reorderRows (mBaseIndex, reverse);
} }
CSMWorld::CloneCommand::CloneCommand (CSMWorld::IdTable& model,
const std::string& idOrigin,
const std::string& IdDestination,
const CSMWorld::UniversalId::Type type,
QUndoCommand* parent) :
QUndoCommand (parent),
mModel (model),
mIdOrigin (idOrigin),
mIdDestination (Misc::StringUtils::lowerCase (IdDestination)),
mType (type)
{
setText ( ("Clone record " + idOrigin + " to the " + IdDestination).c_str());
}
void CSMWorld::CloneCommand::redo()
{
mModel.cloneRecord (mIdOrigin, mIdDestination, mType);
for (std::map<int, QVariant>::const_iterator iter (mValues.begin()); iter != mValues.end(); ++iter)
mModel.setData (mModel.getModelIndex (mIdDestination, iter->first), iter->second);
}
void CSMWorld::CloneCommand::undo()
{
mModel.removeRow (mModel.getModelIndex (mIdDestination, 0).row());
}

View file

@ -39,6 +39,26 @@ namespace CSMWorld
virtual void undo(); virtual void undo();
}; };
class CloneCommand : public QUndoCommand
{
IdTable& mModel;
std::string mIdOrigin;
std::string mIdDestination;
UniversalId::Type mType;
std::map<int, QVariant> mValues;
public:
CloneCommand (IdTable& model, const std::string& idOrigin,
const std::string& IdDestination,
const UniversalId::Type type,
QUndoCommand* parent = 0);
virtual void redo();
virtual void undo();
};
class CreateCommand : public QUndoCommand class CreateCommand : public QUndoCommand
{ {
IdTable& mModel; IdTable& mModel;

View file

@ -154,14 +154,17 @@ CSMWorld::Data::Data() : mRefs (mCells)
mTopics.addColumn (new StringIdColumn<ESM::Dialogue>); mTopics.addColumn (new StringIdColumn<ESM::Dialogue>);
mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>); mTopics.addColumn (new RecordStateColumn<ESM::Dialogue>);
mTopics.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Topic));
mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>); mTopics.addColumn (new DialogueTypeColumn<ESM::Dialogue>);
mJournals.addColumn (new StringIdColumn<ESM::Dialogue>); mJournals.addColumn (new StringIdColumn<ESM::Dialogue>);
mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>); mJournals.addColumn (new RecordStateColumn<ESM::Dialogue>);
mJournals.addColumn (new FixedRecordTypeColumn<ESM::Dialogue> (UniversalId::Type_Journal));
mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true)); mJournals.addColumn (new DialogueTypeColumn<ESM::Dialogue> (true));
mTopicInfos.addColumn (new StringIdColumn<Info> (true)); mTopicInfos.addColumn (new StringIdColumn<Info> (true));
mTopicInfos.addColumn (new RecordStateColumn<Info>); mTopicInfos.addColumn (new RecordStateColumn<Info>);
mTopicInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_TopicInfo));
mTopicInfos.addColumn (new TopicColumn<Info> (false)); mTopicInfos.addColumn (new TopicColumn<Info> (false));
mTopicInfos.addColumn (new ActorColumn<Info>); mTopicInfos.addColumn (new ActorColumn<Info>);
mTopicInfos.addColumn (new RaceColumn<Info>); mTopicInfos.addColumn (new RaceColumn<Info>);
@ -178,6 +181,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mJournalInfos.addColumn (new StringIdColumn<Info> (true)); mJournalInfos.addColumn (new StringIdColumn<Info> (true));
mJournalInfos.addColumn (new RecordStateColumn<Info>); mJournalInfos.addColumn (new RecordStateColumn<Info>);
mJournalInfos.addColumn (new FixedRecordTypeColumn<Info> (UniversalId::Type_Journal));
mJournalInfos.addColumn (new TopicColumn<Info> (true)); mJournalInfos.addColumn (new TopicColumn<Info> (true));
mJournalInfos.addColumn (new QuestStatusTypeColumn<Info>); mJournalInfos.addColumn (new QuestStatusTypeColumn<Info>);
mJournalInfos.addColumn (new QuestIndexColumn<Info>); mJournalInfos.addColumn (new QuestIndexColumn<Info>);
@ -194,6 +198,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mRefs.addColumn (new StringIdColumn<CellRef> (true)); mRefs.addColumn (new StringIdColumn<CellRef> (true));
mRefs.addColumn (new RecordStateColumn<CellRef>); mRefs.addColumn (new RecordStateColumn<CellRef>);
mRefs.addColumn (new FixedRecordTypeColumn<CellRef> (UniversalId::Type_Reference));
mRefs.addColumn (new CellColumn<CellRef>); mRefs.addColumn (new CellColumn<CellRef>);
mRefs.addColumn (new IdColumn<CellRef>); mRefs.addColumn (new IdColumn<CellRef>);
mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false)); mRefs.addColumn (new PosColumn<CellRef> (&CellRef::mPos, 0, false));
@ -224,6 +229,7 @@ CSMWorld::Data::Data() : mRefs (mCells)
mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>); mFilters.addColumn (new StringIdColumn<CSMFilter::Filter>);
mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>); mFilters.addColumn (new RecordStateColumn<CSMFilter::Filter>);
mFilters.addColumn (new FixedRecordTypeColumn<CSMFilter::Filter> (UniversalId::Type_Filter));
mFilters.addColumn (new FilterColumn<CSMFilter::Filter>); mFilters.addColumn (new FilterColumn<CSMFilter::Filter>);
mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>); mFilters.addColumn (new DescriptionColumn<CSMFilter::Filter>);
mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>); mFilters.addColumn (new ScopeColumn<CSMFilter::Filter>);

View file

@ -124,6 +124,17 @@ void CSMWorld::IdTable::addRecord (const std::string& id, UniversalId::Type type
endInsertRows(); endInsertRows();
} }
void CSMWorld::IdTable::cloneRecord(const std::string& origin,
const std::string& destination,
CSMWorld::UniversalId::Type type)
{
int index = mIdCollection->getAppendIndex (destination);
beginInsertRows (QModelIndex(), index, index);
mIdCollection->cloneRecord(origin, destination, type);
endInsertRows();
}
QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const QModelIndex CSMWorld::IdTable::getModelIndex (const std::string& id, int column) const
{ {
return index (mIdCollection->getIndex (id), column); return index (mIdCollection->getIndex (id), column);

View file

@ -63,6 +63,10 @@ namespace CSMWorld
void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None); void addRecord (const std::string& id, UniversalId::Type type = UniversalId::Type_None);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
void cloneRecord(const std::string& origin,
const std::string& destination,
UniversalId::Type type = UniversalId::Type_None);
QModelIndex getModelIndex (const std::string& id, int column) const; QModelIndex getModelIndex (const std::string& id, int column) const;
void setRecord (const std::string& id, const RecordBase& record); void setRecord (const std::string& id, const RecordBase& record);

View file

@ -67,7 +67,7 @@ int CSMWorld::InfoCollection::getIndex (const std::string& id, const std::string
std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic); std::pair<RecordConstIterator, RecordConstIterator> range = getTopicRange (topic);
for (; range.first!=range.second; ++range.first) for (; range.first!=range.second; ++range.first)
if (Misc::StringUtils::lowerCase (range.first->get().mId)==fullId) if (Misc::StringUtils::ciEqual(range.first->get().mId, fullId))
return std::distance (getRecords().begin(), range.first); return std::distance (getRecords().begin(), range.first);
return -1; return -1;
@ -177,7 +177,7 @@ CSMWorld::InfoCollection::Range CSMWorld::InfoCollection::getTopicRange (const s
RecordConstIterator end = begin; RecordConstIterator end = begin;
for (; end!=getRecords().end(); ++end) for (; end!=getRecords().end(); ++end)
if (Misc::StringUtils::lowerCase (end->get().mTopicId)!=topic2) if (!Misc::StringUtils::ciEqual(end->get().mTopicId, topic2))
break; break;
return Range (begin, end); return Range (begin, end);

View file

@ -8,6 +8,5 @@ void CSMWorld::CellRef::load (ESM::ESMReader &esm, Cell& cell, const std::string
mId = id; mId = id;
mCell = cell.mId; mCell = cell.mId;
if (!mDeleted) cell.addRef (mId);
cell.addRef (mId);
} }

View file

@ -5,6 +5,8 @@
#include "ref.hpp" #include "ref.hpp"
#include "cell.hpp" #include "cell.hpp"
#include "universalid.hpp"
#include "record.hpp"
void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base) void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool base)
{ {
@ -14,7 +16,8 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool
CellRef ref; CellRef ref;
while (cell2.getNextRef (reader, ref)) bool deleted = false;
while (cell2.getNextRef (reader, ref, deleted))
{ {
/// \todo handle deleted and moved references /// \todo handle deleted and moved references
ref.load (reader, cell2, getNewId()); ref.load (reader, cell2, getNewId());
@ -35,3 +38,14 @@ std::string CSMWorld::RefCollection::getNewId()
stream << "ref#" << mNextId++; stream << "ref#" << mNextId++;
return stream.str(); return stream.str();
} }
void CSMWorld::RefCollection::cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type,
const CSMWorld::UniversalId::ArgumentType argumentType)
{
Record<CSMWorld::CellRef> clone(getRecord(origin));
clone.mState = CSMWorld::RecordBase::State_ModifiedOnly;
clone.get().mId = destination;
insertRecord(clone, getAppendIndex(destination, type), type);
}

View file

@ -8,6 +8,7 @@
namespace CSMWorld namespace CSMWorld
{ {
struct Cell; struct Cell;
struct UniversalId;
/// \brief References in cells /// \brief References in cells
class RefCollection : public Collection<CellRef> class RefCollection : public Collection<CellRef>
@ -25,6 +26,11 @@ namespace CSMWorld
///< Load a sequence of references. ///< Load a sequence of references.
std::string getNewId(); std::string getNewId();
void cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type,
const CSMWorld::UniversalId::ArgumentType argumentType);
}; };
} }

View file

@ -31,6 +31,7 @@ namespace CSMWorld
///< If the data type does not match an exception is thrown. ///< If the data type does not match an exception is thrown.
virtual std::string getId (const RecordBase& record) const = 0; virtual std::string getId (const RecordBase& record) const = 0;
virtual void setId(RecordBase& record, const std::string& id) = 0;
}; };
} }

View file

@ -35,6 +35,8 @@ namespace CSMWorld
virtual std::string getId (const RecordBase& record) const; virtual std::string getId (const RecordBase& record) const;
virtual void setId (RecordBase& record, const std::string& id);
virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index) virtual QVariant getData (const RefIdColumn *column, const RefIdData& data, int index)
const; const;
@ -50,6 +52,12 @@ namespace CSMWorld
: mType (type), mBase (base) : mType (type), mBase (base)
{} {}
template<typename RecordT>
void BaseRefIdAdapter<RecordT>::setId (RecordBase& record, const std::string& id)
{
(dynamic_cast<Record<RecordT>&> (record).get().mId) = id;
}
template<typename RecordT> template<typename RecordT>
std::string BaseRefIdAdapter<RecordT>::getId (const RecordBase& record) const std::string BaseRefIdAdapter<RecordT>::getId (const RecordBase& record) const
{ {

View file

@ -2,6 +2,7 @@
#include "refidcollection.hpp" #include "refidcollection.hpp"
#include <stdexcept> #include <stdexcept>
#include <memory>
#include <components/esm/esmreader.hpp> #include <components/esm/esmreader.hpp>
@ -181,7 +182,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
unsigned int mFlag; unsigned int mFlag;
} sCreatureFlagTable[] = } sCreatureFlagTable[] =
{ {
{ Columns::ColumnId_Biped, ESM::Creature::Biped }, { Columns::ColumnId_Biped, ESM::Creature::Bipedal },
{ Columns::ColumnId_HasWeapon, ESM::Creature::Weapon }, { Columns::ColumnId_HasWeapon, ESM::Creature::Weapon },
{ Columns::ColumnId_NoMovement, ESM::Creature::None }, { Columns::ColumnId_NoMovement, ESM::Creature::None },
{ Columns::ColumnId_Swims, ESM::Creature::Swims }, { Columns::ColumnId_Swims, ESM::Creature::Swims },
@ -449,6 +450,16 @@ void CSMWorld::RefIdCollection::replace (int index, const RecordBase& record)
mData.getRecord (mData.globalToLocalIndex (index)).assign (record); mData.getRecord (mData.globalToLocalIndex (index)).assign (record);
} }
void CSMWorld::RefIdCollection::cloneRecord(const std::string& origin,
const std::string& destination,
const CSMWorld::UniversalId::Type type)
{
std::auto_ptr<RecordBase> newRecord(mData.getRecord(mData.searchId(origin)).clone());
newRecord->mState = RecordBase::State_ModifiedOnly;
mAdapters.find(type)->second->setId(*newRecord, destination);
mData.insertRecord(*newRecord, type, destination);
}
void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record, void CSMWorld::RefIdCollection::appendRecord (const RecordBase& record,
UniversalId::Type type) UniversalId::Type type)
{ {
@ -549,3 +560,9 @@ void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const
{ {
mData.save (index, writer); mData.save (index, writer);
} }
const CSMWorld::RefIdData& CSMWorld::RefIdCollection::getDataSet() const
{
return mData;
}

View file

@ -69,6 +69,10 @@ namespace CSMWorld
virtual void removeRows (int index, int count); virtual void removeRows (int index, int count);
virtual void cloneRecord(const std::string& origin,
const std::string& destination,
const UniversalId::Type type);
virtual void appendBlankRecord (const std::string& id, UniversalId::Type type); virtual void appendBlankRecord (const std::string& id, UniversalId::Type type);
///< \param type Will be ignored, unless the collection supports multiple record types ///< \param type Will be ignored, unless the collection supports multiple record types
@ -107,7 +111,10 @@ namespace CSMWorld
/// \return Success? /// \return Success?
void save (int index, ESM::ESMWriter& writer) const; void save (int index, ESM::ESMWriter& writer) const;
const RefIdData& getDataSet() const; //I can't figure out a better name for this one :(
}; };
} }
#endif #endif

View file

@ -231,3 +231,117 @@ void CSMWorld::RefIdData::save (int index, ESM::ESMWriter& writer) const
iter->second->save (localIndex.first, writer); iter->second->save (localIndex.first, writer);
} }
const CSMWorld::RefIdDataContainer< ESM::Book >& CSMWorld::RefIdData::getBooks() const
{
return mBooks;
}
const CSMWorld::RefIdDataContainer< ESM::Activator >& CSMWorld::RefIdData::getActivators() const
{
return mActivators;
}
const CSMWorld::RefIdDataContainer< ESM::Potion >& CSMWorld::RefIdData::getPotions() const
{
return mPotions;
}
const CSMWorld::RefIdDataContainer< ESM::Apparatus >& CSMWorld::RefIdData::getApparati() const
{
return mApparati;
}
const CSMWorld::RefIdDataContainer< ESM::Armor >& CSMWorld::RefIdData::getArmors() const
{
return mArmors;
}
const CSMWorld::RefIdDataContainer< ESM::Clothing >& CSMWorld::RefIdData::getClothing() const
{
return mClothing;
}
const CSMWorld::RefIdDataContainer< ESM::Container >& CSMWorld::RefIdData::getContainers() const
{
return mContainers;
}
const CSMWorld::RefIdDataContainer< ESM::Creature >& CSMWorld::RefIdData::getCreatures() const
{
return mCreatures;
}
const CSMWorld::RefIdDataContainer< ESM::Door >& CSMWorld::RefIdData::getDoors() const
{
return mDoors;
}
const CSMWorld::RefIdDataContainer< ESM::Ingredient >& CSMWorld::RefIdData::getIngredients() const
{
return mIngredients;
}
const CSMWorld::RefIdDataContainer< ESM::CreatureLevList >& CSMWorld::RefIdData::getCreatureLevelledLists() const
{
return mCreatureLevelledLists;
}
const CSMWorld::RefIdDataContainer< ESM::ItemLevList >& CSMWorld::RefIdData::getItemLevelledList() const
{
return mItemLevelledLists;
}
const CSMWorld::RefIdDataContainer< ESM::Light >& CSMWorld::RefIdData::getLights() const
{
return mLights;
}
const CSMWorld::RefIdDataContainer< ESM::Lockpick >& CSMWorld::RefIdData::getLocpicks() const
{
return mLockpicks;
}
const CSMWorld::RefIdDataContainer< ESM::Miscellaneous >& CSMWorld::RefIdData::getMiscellaneous() const
{
return mMiscellaneous;
}
const CSMWorld::RefIdDataContainer< ESM::NPC >& CSMWorld::RefIdData::getNPCs() const
{
return mNpcs;
}
const CSMWorld::RefIdDataContainer< ESM::Weapon >& CSMWorld::RefIdData::getWeapons() const
{
return mWeapons;
}
const CSMWorld::RefIdDataContainer< ESM::Probe >& CSMWorld::RefIdData::getProbes() const
{
return mProbes;
}
const CSMWorld::RefIdDataContainer< ESM::Repair >& CSMWorld::RefIdData::getRepairs() const
{
return mRepairs;
}
const CSMWorld::RefIdDataContainer< ESM::Static >& CSMWorld::RefIdData::getStatics() const
{
return mStatics;
}
void CSMWorld::RefIdData::insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id)
{
std::map<UniversalId::Type, RefIdDataContainerBase *>::iterator iter =
mRecordContainers.find (type);
if (iter==mRecordContainers.end())
throw std::logic_error ("invalid local index type");
iter->second->insertRecord(record);
mIndex.insert (std::make_pair (Misc::StringUtils::lowerCase (id),
LocalIndex (iter->second->getSize()-1, type)));
}

View file

@ -47,6 +47,8 @@ namespace CSMWorld
virtual void appendRecord (const std::string& id) = 0; virtual void appendRecord (const std::string& id) = 0;
virtual void insertRecord (RecordBase& record) = 0;
virtual void load (int index, ESM::ESMReader& reader, bool base) = 0; virtual void load (int index, ESM::ESMReader& reader, bool base) = 0;
virtual void erase (int index, int count) = 0; virtual void erase (int index, int count) = 0;
@ -69,6 +71,8 @@ namespace CSMWorld
virtual void appendRecord (const std::string& id); virtual void appendRecord (const std::string& id);
virtual void insertRecord (RecordBase& record);
virtual void load (int index, ESM::ESMReader& reader, bool base); virtual void load (int index, ESM::ESMReader& reader, bool base);
virtual void erase (int index, int count); virtual void erase (int index, int count);
@ -78,6 +82,13 @@ namespace CSMWorld
virtual void save (int index, ESM::ESMWriter& writer) const; virtual void save (int index, ESM::ESMWriter& writer) const;
}; };
template<typename RecordT>
void RefIdDataContainer<RecordT>::insertRecord(RecordBase& record)
{
Record<RecordT>& newRecord = dynamic_cast<Record<RecordT>& >(record);
mContainer.push_back(newRecord);
}
template<typename RecordT> template<typename RecordT>
int RefIdDataContainer<RecordT>::getSize() const int RefIdDataContainer<RecordT>::getSize() const
{ {
@ -136,15 +147,10 @@ namespace CSMWorld
if (state==CSMWorld::RecordBase::State_Modified || if (state==CSMWorld::RecordBase::State_Modified ||
state==CSMWorld::RecordBase::State_ModifiedOnly) state==CSMWorld::RecordBase::State_ModifiedOnly)
{ {
std::string type; writer.startRecord (mContainer.at (index).mModified.sRecordId);
for (int i=0; i<4; ++i)
/// \todo make endianess agnostic (change ESMWriter interface?)
type += reinterpret_cast<const char *> (&mContainer.at (index).mModified.sRecordId)[i];
writer.startRecord (type);
writer.writeHNCString ("NAME", getId (index)); writer.writeHNCString ("NAME", getId (index));
mContainer.at (index).mModified.save (writer); mContainer.at (index).mModified.save (writer);
writer.endRecord (type); writer.endRecord (mContainer.at (index).mModified.sRecordId);
} }
else if (state==CSMWorld::RecordBase::State_Deleted) else if (state==CSMWorld::RecordBase::State_Deleted)
{ {
@ -201,6 +207,8 @@ namespace CSMWorld
void erase (int index, int count); void erase (int index, int count);
void insertRecord(CSMWorld::RecordBase& record, CSMWorld::UniversalId::Type type, const std::string& id);
const RecordBase& getRecord (const LocalIndex& index) const; const RecordBase& getRecord (const LocalIndex& index) const;
RecordBase& getRecord (const LocalIndex& index); RecordBase& getRecord (const LocalIndex& index);
@ -219,7 +227,33 @@ namespace CSMWorld
/// \param listDeleted include deleted record in the list /// \param listDeleted include deleted record in the list
void save (int index, ESM::ESMWriter& writer) const; void save (int index, ESM::ESMWriter& writer) const;
//RECORD CONTAINERS ACCESS METHODS
const RefIdDataContainer<ESM::Book>& getBooks() const;
const RefIdDataContainer<ESM::Activator>& getActivators() const;
const RefIdDataContainer<ESM::Potion>& getPotions() const;
const RefIdDataContainer<ESM::Apparatus>& getApparati() const;
const RefIdDataContainer<ESM::Armor>& getArmors() const;
const RefIdDataContainer<ESM::Clothing>& getClothing() const;
const RefIdDataContainer<ESM::Container>& getContainers() const;
const RefIdDataContainer<ESM::Creature>& getCreatures() const;
const RefIdDataContainer<ESM::Door>& getDoors() const;
const RefIdDataContainer<ESM::Ingredient>& getIngredients() const;
const RefIdDataContainer<ESM::CreatureLevList>& getCreatureLevelledLists() const;
const RefIdDataContainer<ESM::ItemLevList>& getItemLevelledList() const;
const RefIdDataContainer<ESM::Light>& getLights() const;
const RefIdDataContainer<ESM::Lockpick>& getLocpicks() const;
const RefIdDataContainer<ESM::Miscellaneous>& getMiscellaneous() const;
const RefIdDataContainer<ESM::NPC>& getNPCs() const;
const RefIdDataContainer<ESM::Weapon >& getWeapons() const;
const RefIdDataContainer<ESM::Probe >& getProbes() const;
const RefIdDataContainer<ESM::Repair>& getRepairs() const;
const RefIdDataContainer<ESM::Static>& getStatics() const;
}; };
} }
#endif #endif

View file

@ -61,6 +61,7 @@ void CSVWorld::CellCreator::reset()
mX->setValue (0); mX->setValue (0);
mY->setValue (0); mY->setValue (0);
mType->setCurrentIndex (0); mType->setCurrentIndex (0);
setType(0);
GenericCreator::reset(); GenericCreator::reset();
} }
@ -79,3 +80,24 @@ void CSVWorld::CellCreator::valueChanged (int index)
{ {
update(); update();
} }
void CSVWorld::CellCreator::cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type)
{
CSVWorld::GenericCreator::cloneMode(originId, type);
if (*(originId.begin()) == '#') //if originid points to the exterior cell
{
setType(1); //enable x and y controls
mType->setCurrentIndex(1);
} else {
setType(0);
mType->setCurrentIndex(0);
}
}
void CSVWorld::CellCreator::toggleWidgets(bool active)
{
CSVWorld::GenericCreator::toggleWidgets(active);
mType->setEnabled(active);
}

View file

@ -29,6 +29,11 @@ namespace CSVWorld
virtual void reset(); virtual void reset();
virtual void toggleWidgets(bool active = true);
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
private slots: private slots:
void setType (int index); void setType (int index);

View file

@ -2,6 +2,7 @@
#define CSV_WORLD_CREATOR_H #define CSV_WORLD_CREATOR_H
#include <QWidget> #include <QWidget>
#include "../../model/world/universalid.hpp"
class QUndoStack; class QUndoStack;
@ -24,8 +25,13 @@ namespace CSVWorld
virtual void reset() = 0; virtual void reset() = 0;
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type) = 0;
virtual void setEditLock (bool locked) = 0; virtual void setEditLock (bool locked) = 0;
virtual void toggleWidgets(bool active = true) = 0;
signals: signals:
void done(); void done();

View file

@ -57,8 +57,15 @@ const CSMWorld::UniversalId& CSVWorld::GenericCreator::getCollectionId() const
} }
CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::GenericCreator::GenericCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id, bool relaxedIdRules) const CSMWorld::UniversalId& id, bool relaxedIdRules):
: mData (data), mUndoStack (undoStack), mListId (id), mLocked (false)
mData (data),
mUndoStack (undoStack),
mListId (id),
mLocked (false),
mCloneMode(false),
mClonedType(CSMWorld::UniversalId::Type_None)
{ {
mLayout = new QHBoxLayout; mLayout = new QHBoxLayout;
mLayout->setContentsMargins (0, 0, 0, 0); mLayout->setContentsMargins (0, 0, 0, 0);
@ -89,6 +96,7 @@ void CSVWorld::GenericCreator::setEditLock (bool locked)
void CSVWorld::GenericCreator::reset() void CSVWorld::GenericCreator::reset()
{ {
mCloneMode = false;
mId->setText (""); mId->setText ("");
update(); update();
} }
@ -120,16 +128,40 @@ void CSVWorld::GenericCreator::create()
{ {
if (!mLocked) if (!mLocked)
{ {
std::string id = getId(); if (mCloneMode)
{
std::string id = getId();
std::auto_ptr<CSMWorld::CloneCommand> command (new CSMWorld::CloneCommand (
dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel(mListId)), mClonedId, id, mClonedType));
std::auto_ptr<CSMWorld::CreateCommand> command (new CSMWorld::CreateCommand ( mUndoStack.push(command.release());
emit done();
emit requestFocus(id);
} else {
std::string id = getId();
std::auto_ptr<CSMWorld::CreateCommand> command (new CSMWorld::CreateCommand (
dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel (mListId)), id)); dynamic_cast<CSMWorld::IdTable&> (*mData.getTableModel (mListId)), id));
configureCreateCommand (*command); configureCreateCommand (*command);
mUndoStack.push (command.release()); mUndoStack.push (command.release());
emit done(); emit done();
emit requestFocus (id); emit requestFocus (id);
}
} }
} }
void CSVWorld::GenericCreator::cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type)
{
mCloneMode = true;
mClonedId = originId;
mClonedType = type;
}
void CSVWorld::GenericCreator::toggleWidgets(bool active)
{
}

View file

@ -1,6 +1,7 @@
#ifndef CSV_WORLD_GENERICCREATOR_H #ifndef CSV_WORLD_GENERICCREATOR_H
#define CSV_WORLD_GENERICCREATOR_H #define CSV_WORLD_GENERICCREATOR_H
class QString;
class QPushButton; class QPushButton;
class QLineEdit; class QLineEdit;
class QHBoxLayout; class QHBoxLayout;
@ -28,6 +29,11 @@ namespace CSVWorld
std::string mErrors; std::string mErrors;
QHBoxLayout *mLayout; QHBoxLayout *mLayout;
bool mLocked; bool mLocked;
std::string mClonedId;
CSMWorld::UniversalId::Type mClonedType;
protected:
bool mCloneMode;
protected: protected:
@ -57,11 +63,15 @@ namespace CSVWorld
virtual void reset(); virtual void reset();
virtual void toggleWidgets (bool active = true);
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
virtual std::string getErrors() const; virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty ///< Return formatted error descriptions for the current state of the creator. if an empty
/// string is returned, there is no error. /// string is returned, there is no error.
private slots: private slots:
void textChanged (const QString& text); void textChanged (const QString& text);

View file

@ -41,3 +41,9 @@ void CSVWorld::ReferenceableCreator::reset()
mType->setCurrentIndex (0); mType->setCurrentIndex (0);
GenericCreator::reset(); GenericCreator::reset();
} }
void CSVWorld::ReferenceableCreator::toggleWidgets(bool active)
{
CSVWorld::GenericCreator::toggleWidgets(active);
mType->setEnabled(active);
}

View file

@ -23,6 +23,7 @@ namespace CSVWorld
const CSMWorld::UniversalId& id); const CSMWorld::UniversalId& id);
virtual void reset(); virtual void reset();
virtual void toggleWidgets(bool active = true);
}; };
} }

View file

@ -40,15 +40,20 @@ CSVWorld::ReferenceCreator::ReferenceCreator (CSMWorld::Data& data, QUndoStack&
void CSVWorld::ReferenceCreator::reset() void CSVWorld::ReferenceCreator::reset()
{ {
GenericCreator::reset();
mCell->setText (""); mCell->setText ("");
mId = getData().getReferences().getNewId(); mId = getData().getReferences().getNewId();
GenericCreator::reset();
} }
std::string CSVWorld::ReferenceCreator::getErrors() const std::string CSVWorld::ReferenceCreator::getErrors() const
{ {
std::string errors = GenericCreator::getErrors(); std::string errors = GenericCreator::getErrors();
if (mCloneMode)
{
return errors;
}
std::string cell = mCell->text().toUtf8().constData(); std::string cell = mCell->text().toUtf8().constData();
if (cell.empty()) if (cell.empty())
@ -73,3 +78,16 @@ void CSVWorld::ReferenceCreator::cellChanged()
{ {
update(); update();
} }
void CSVWorld::ReferenceCreator::toggleWidgets(bool active)
{
CSVWorld::GenericCreator::toggleWidgets(active);
mCell->setEnabled(active);
}
void CSVWorld::ReferenceCreator::cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type)
{
CSVWorld::GenericCreator::cloneMode(originId, type);
cellChanged(); //otherwise ok button will remain disabled
}

View file

@ -25,7 +25,11 @@ namespace CSVWorld
ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack, ReferenceCreator (CSMWorld::Data& data, QUndoStack& undoStack,
const CSMWorld::UniversalId& id); const CSMWorld::UniversalId& id);
virtual void cloneMode(const std::string& originId,
const CSMWorld::UniversalId::Type type);
virtual void reset(); virtual void reset();
virtual void toggleWidgets(bool active = true);
virtual std::string getErrors() const; virtual std::string getErrors() const;
///< Return formatted error descriptions for the current state of the creator. if an empty ///< Return formatted error descriptions for the current state of the creator. if an empty

View file

@ -29,7 +29,11 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event)
if (!mEditLock) if (!mEditLock)
{ {
if (selectedRows.size()==1) if (selectedRows.size()==1)
{
menu.addAction (mEditAction); menu.addAction (mEditAction);
if (mCreateAction)
menu.addAction(mCloneAction);
}
if (mCreateAction) if (mCreateAction)
menu.addAction (mCreateAction); menu.addAction (mCreateAction);
@ -156,7 +160,7 @@ std::vector<std::string> CSVWorld::Table::listDeletableSelectedIds() const
CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack, CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, QUndoStack& undoStack,
bool createAndDelete, bool sorting) bool createAndDelete, bool sorting)
: mUndoStack (undoStack), mCreateAction (0), mEditLock (false), mRecordStatusDisplay (0) : mUndoStack (undoStack), mCreateAction (0), mCloneAction(0), mEditLock (false), mRecordStatusDisplay (0)
{ {
mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id)); mModel = &dynamic_cast<CSMWorld::IdTable&> (*data.getTableModel (id));
@ -200,6 +204,10 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q
mCreateAction = new QAction (tr ("Add Record"), this); mCreateAction = new QAction (tr ("Add Record"), this);
connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest())); connect (mCreateAction, SIGNAL (triggered()), this, SIGNAL (createRequest()));
addAction (mCreateAction); addAction (mCreateAction);
mCloneAction = new QAction (tr ("Clone Record"), this);
connect(mCloneAction, SIGNAL (triggered()), this, SLOT (cloneRecord()));
addAction(mCloneAction);
} }
mRevertAction = new QAction (tr ("Revert Record"), this); mRevertAction = new QAction (tr ("Revert Record"), this);
@ -298,6 +306,19 @@ void CSVWorld::Table::editRecord()
} }
} }
void CSVWorld::Table::cloneRecord()
{
if (!mEditLock)
{
QModelIndexList selectedRows = selectionModel()->selectedRows();
const CSMWorld::UniversalId& toClone = getUniversalId(selectedRows.begin()->row());
if (selectedRows.size()==1 && !mModel->getRecord(toClone.getId()).isDeleted())
{
emit cloneRequest (toClone);
}
}
}
void CSVWorld::Table::moveUpRecord() void CSVWorld::Table::moveUpRecord()
{ {
QModelIndexList selectedRows = selectionModel()->selectedRows(); QModelIndexList selectedRows = selectionModel()->selectedRows();

View file

@ -33,6 +33,7 @@ namespace CSVWorld
QUndoStack& mUndoStack; QUndoStack& mUndoStack;
QAction *mEditAction; QAction *mEditAction;
QAction *mCreateAction; QAction *mCreateAction;
QAction *mCloneAction;
QAction *mRevertAction; QAction *mRevertAction;
QAction *mDeleteAction; QAction *mDeleteAction;
QAction *mMoveUpAction; QAction *mMoveUpAction;
@ -83,6 +84,7 @@ namespace CSVWorld
/// \param modified Number of added and modified records /// \param modified Number of added and modified records
void createRequest(); void createRequest();
void cloneRequest(const CSMWorld::UniversalId&);
private slots: private slots:
@ -92,6 +94,8 @@ namespace CSVWorld
void editRecord(); void editRecord();
void cloneRecord();
void moveUpRecord(); void moveUpRecord();
void moveDownRecord(); void moveDownRecord();

View file

@ -153,7 +153,19 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi
void CSVWorld::TableBottomBox::createRequest() void CSVWorld::TableBottomBox::createRequest()
{ {
mCreator->reset(); mCreator->reset();
mCreator->toggleWidgets(true);
mLayout->setCurrentWidget (mCreator); mLayout->setCurrentWidget (mCreator);
setVisible (true); setVisible (true);
mCreating = true; mCreating = true;
} }
void CSVWorld::TableBottomBox::cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type)
{
mCreator->reset();
mCreator->cloneMode(id, type);
mLayout->setCurrentWidget(mCreator);
mCreator->toggleWidgets(false);
setVisible (true);
mCreating = true;
}

View file

@ -2,6 +2,7 @@
#define CSV_WORLD_BOTTOMBOX_H #define CSV_WORLD_BOTTOMBOX_H
#include <QWidget> #include <QWidget>
#include <apps/opencs/model/world/universalid.hpp>
class QLabel; class QLabel;
class QStackedLayout; class QStackedLayout;
@ -76,6 +77,8 @@ namespace CSVWorld
/// \param modified Number of added and modified records /// \param modified Number of added and modified records
void createRequest(); void createRequest();
void cloneRequest(const std::string& id,
const CSMWorld::UniversalId::Type type);
}; };
} }

View file

@ -6,7 +6,6 @@
#include "../../model/doc/document.hpp" #include "../../model/doc/document.hpp"
#include "../filter/filterbox.hpp" #include "../filter/filterbox.hpp"
#include "table.hpp" #include "table.hpp"
#include "tablebottombox.hpp" #include "tablebottombox.hpp"
#include "creator.hpp" #include "creator.hpp"
@ -46,8 +45,15 @@ CSVWorld::TableSubView::TableSubView (const CSMWorld::UniversalId& id, CSMDoc::D
mTable->selectionSizeUpdate(); mTable->selectionSizeUpdate();
if (mBottom->canCreateAndDelete()) if (mBottom->canCreateAndDelete())
{
connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest())); connect (mTable, SIGNAL (createRequest()), mBottom, SLOT (createRequest()));
connect (mTable, SIGNAL (cloneRequest(const CSMWorld::UniversalId&)), this,
SLOT(cloneRequest(const CSMWorld::UniversalId&)));
connect (this, SIGNAL(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)),
mBottom, SLOT(cloneRequest(const std::string&, const CSMWorld::UniversalId::Type)));
}
connect (mBottom, SIGNAL (requestFocus (const std::string&)), connect (mBottom, SIGNAL (requestFocus (const std::string&)),
mTable, SLOT (requestFocus (const std::string&))); mTable, SLOT (requestFocus (const std::string&)));
@ -76,3 +82,8 @@ void CSVWorld::TableSubView::setStatusBar (bool show)
{ {
mBottom->setStatusBar (show); mBottom->setStatusBar (show);
} }
void CSVWorld::TableSubView::cloneRequest(const CSMWorld::UniversalId& toClone)
{
emit cloneRequest(toClone.getId(), toClone.getType());
}

View file

@ -5,6 +5,11 @@
class QModelIndex; class QModelIndex;
namespace CSMWorld
{
class IdTable;
}
namespace CSMDoc namespace CSMDoc
{ {
class Document; class Document;
@ -34,9 +39,14 @@ namespace CSVWorld
virtual void setStatusBar (bool show); virtual void setStatusBar (bool show);
signals:
void cloneRequest(const std::string&,
const CSMWorld::UniversalId::Type);
private slots: private slots:
void editRequest (int row); void editRequest (int row);
void cloneRequest (const CSMWorld::UniversalId& toClone);
}; };
} }

View file

@ -1,7 +1,3 @@
# config file
configure_file ("${CMAKE_CURRENT_SOURCE_DIR}/config.hpp.cmake" "${CMAKE_CURRENT_SOURCE_DIR}/config.hpp")
# local files # local files
set(GAME set(GAME
main.cpp main.cpp
@ -12,7 +8,6 @@ if(NOT WIN32)
endif() endif()
set(GAME_HEADER set(GAME_HEADER
engine.hpp engine.hpp
config.hpp
) )
source_group(game FILES ${GAME} ${GAME_HEADER}) source_group(game FILES ${GAME} ${GAME_HEADER})
@ -20,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 terrainstorage renderconst effectmanager
) )
add_openmw_dir (mwinput add_openmw_dir (mwinput
@ -74,12 +69,16 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow drawstate spells activespells npcstats aipackage aisequence alchemy aiwander aitravel aifollow
aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting aiescort aiactivate aicombat repair enchanting pathfinding security spellsuccess spellcasting
disease pickpocket levelledlist disease pickpocket levelledlist combat steering
)
add_openmw_dir (mwstate
statemanagerimp charactermanager character
) )
add_openmw_dir (mwbase add_openmw_dir (mwbase
environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager environment world scriptmanager dialoguemanager journal soundmanager mechanicsmanager
inputmanager windowmanager inputmanager windowmanager statemanager
) )
# Main executable # Main executable

View file

@ -7,6 +7,8 @@
#include <MyGUI_WidgetManager.h> #include <MyGUI_WidgetManager.h>
#include <SDL.h>
#include <components/compiler/extensions0.hpp> #include <components/compiler/extensions0.hpp>
#include <components/bsa/bsa_archive.hpp> #include <components/bsa/bsa_archive.hpp>
@ -41,8 +43,7 @@
#include "mwmechanics/mechanicsmanagerimp.hpp" #include "mwmechanics/mechanicsmanagerimp.hpp"
#include "mwstate/statemanagerimp.hpp"
#include <SDL.h>
void OMW::Engine::executeLocalScripts() void OMW::Engine::executeLocalScripts()
{ {
@ -88,31 +89,47 @@ bool OMW::Engine::frameRenderingQueued (const Ogre::FrameEvent& evt)
if (mUseSound) if (mUseSound)
MWBase::Environment::get().getSoundManager()->update(frametime); MWBase::Environment::get().getSoundManager()->update(frametime);
// global scripts bool paused = MWBase::Environment::get().getWindowManager()->isGuiMode();
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
bool changed = MWBase::Environment::get().getWorld()->hasCellChanged(); // update game state
MWBase::Environment::get().getStateManager()->update (frametime);
// local scripts if (MWBase::Environment::get().getStateManager()->getState()==
executeLocalScripts(); // This does not handle the case where a global script causes a cell MWBase::StateManager::State_Running)
// change, followed by a cell change in a local script during the same {
// frame. // global scripts
MWBase::Environment::get().getScriptManager()->getGlobalScripts().run();
// passing of time bool changed = MWBase::Environment::get().getWorld()->hasCellChanged();
if (!MWBase::Environment::get().getWindowManager()->isGuiMode())
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
// local scripts
executeLocalScripts(); // This does not handle the case where a global script causes a
// cell change, followed by a cell change in a local script during
// the same frame.
if (changed) // keep change flag for another frame, if cell changed happened in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
if (!paused)
MWBase::Environment::get().getWorld()->advanceTime(
frametime*MWBase::Environment::get().getWorld()->getTimeScaleFactor()/3600);
}
if (changed) // keep change flag for another frame, if cell changed happend in local script
MWBase::Environment::get().getWorld()->markCellAsUnchanged();
// update actors // update actors
MWBase::Environment::get().getMechanicsManager()->update(frametime, MWBase::Environment::get().getMechanicsManager()->update(frametime,
MWBase::Environment::get().getWindowManager()->isGuiMode()); paused);
if (MWBase::Environment::get().getStateManager()->getState()==
MWBase::StateManager::State_Running)
{
MWWorld::Ptr player = mEnvironment.getWorld()->getPlayerPtr();
if(!paused && player.getClass().getCreatureStats(player).isDead())
MWBase::Environment::get().getStateManager()->endGame();
}
// update world // update world
MWBase::Environment::get().getWorld()->update(frametime, MWBase::Environment::get().getWindowManager()->isGuiMode()); MWBase::Environment::get().getWorld()->update(frametime, paused);
// update GUI // update GUI
Ogre::RenderWindow* window = mOgre->getWindow(); Ogre::RenderWindow* window = mOgre->getWindow();
@ -135,7 +152,7 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
: mOgre (0) : mOgre (0)
, mFpsLevel(0) , mFpsLevel(0)
, mVerboseScripts (false) , mVerboseScripts (false)
, mNewGame (false) , mSkipMenu (false)
, mUseSound (true) , mUseSound (true)
, mCompileAll (false) , mCompileAll (false)
, mScriptContext (0) , mScriptContext (0)
@ -265,12 +282,7 @@ void OMW::Engine::setCell (const std::string& cellName)
void OMW::Engine::addContentFile(const std::string& file) void OMW::Engine::addContentFile(const std::string& file)
{ {
if (file.find_last_of(".") == std::string::npos) mContentFiles.push_back(file);
{
throw std::runtime_error("Missing extension in content file!");
}
mContentFiles.push_back(file);
} }
void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity) void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
@ -278,9 +290,9 @@ void OMW::Engine::setScriptsVerbosity(bool scriptsVerbosity)
mVerboseScripts = scriptsVerbosity; mVerboseScripts = scriptsVerbosity;
} }
void OMW::Engine::setNewGame(bool newGame) void OMW::Engine::setSkipMenu (bool skipMenu)
{ {
mNewGame = newGame; mSkipMenu = skipMenu;
} }
std::string OMW::Engine::loadSettings (Settings::Manager & settings) std::string OMW::Engine::loadSettings (Settings::Manager & settings)
@ -326,6 +338,9 @@ std::string OMW::Engine::loadSettings (Settings::Manager & settings)
void OMW::Engine::prepareEngine (Settings::Manager & settings) void OMW::Engine::prepareEngine (Settings::Manager & settings)
{ {
mEnvironment.setStateManager (
new MWState::StateManager (mCfgMgr.getUserDataPath() / "saves", mContentFiles.at (0)));
Nif::NIFFile::CacheLock cachelock; Nif::NIFFile::CacheLock cachelock;
std::string renderSystem = settings.getString("render system", "Video"); std::string renderSystem = settings.getString("render system", "Video");
@ -392,10 +407,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
input->setPlayer(&mEnvironment.getWorld()->getPlayer()); input->setPlayer(&mEnvironment.getWorld()->getPlayer());
window->initUI(); window->initUI();
if (mNewGame)
// still redundant work here: recreate CharacterCreation(),
// double update visibility etc.
window->setNewGame(true);
window->renderWorldMap(); window->renderWorldMap();
//Load translation data //Load translation data
@ -427,12 +438,12 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
mechanics->buildPlayer(); mechanics->buildPlayer();
window->updatePlayer(); window->updatePlayer();
if (!mNewGame) // load cell
{ ESM::Position pos;
// load cell MWBase::World *world = MWBase::Environment::get().getWorld();
ESM::Position pos;
MWBase::World *world = MWBase::Environment::get().getWorld();
if (!mCellName.empty())
{
if (world->findExteriorPosition(mCellName, pos)) { if (world->findExteriorPosition(mCellName, pos)) {
world->changeToExteriorCell (pos); world->changeToExteriorCell (pos);
} }
@ -442,7 +453,11 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
} }
} }
else else
mEnvironment.getWorld()->startNewGame(); {
pos.pos[0] = pos.pos[1] = pos.pos[2] = 0;
pos.rot[0] = pos.rot[1] = pos.pos[2] = 0;
world->changeToExteriorCell (pos);
}
Ogre::FrameEvent event; Ogre::FrameEvent event;
event.timeSinceLastEvent = 0; event.timeSinceLastEvent = 0;
@ -468,7 +483,6 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
void OMW::Engine::go() void OMW::Engine::go()
{ {
assert (!mCellName.empty());
assert (!mContentFiles.empty()); assert (!mContentFiles.empty());
assert (!mOgre); assert (!mOgre);
@ -489,8 +503,14 @@ void OMW::Engine::go()
if (!mStartupScript.empty()) if (!mStartupScript.empty())
MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript); MWBase::Environment::get().getWindowManager()->executeInConsole (mStartupScript);
// start in main menu
if (!mSkipMenu)
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
else
MWBase::Environment::get().getStateManager()->newGame (true);
// Start the main rendering loop // Start the main rendering loop
while (!mEnvironment.getRequestExit()) while (!mEnvironment.get().getStateManager()->hasQuitRequest())
Ogre::Root::getSingleton().renderOneFrame(); Ogre::Root::getSingleton().renderOneFrame();
// Save user settings // Save user settings
@ -509,6 +529,9 @@ void OMW::Engine::activate()
if (ptr.isEmpty()) if (ptr.isEmpty())
return; return;
if (ptr.getClass().getName(ptr) == "") // objects without name presented to user can never be activated
return;
MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr); MWScript::InterpreterContext interpreterContext (&ptr.getRefData().getLocals(), ptr);
boost::shared_ptr<MWWorld::Action> action = boost::shared_ptr<MWWorld::Action> action =

View file

@ -71,7 +71,7 @@ namespace OMW
std::vector<std::string> mContentFiles; std::vector<std::string> mContentFiles;
int mFpsLevel; int mFpsLevel;
bool mVerboseScripts; bool mVerboseScripts;
bool mNewGame; bool mSkipMenu;
bool mUseSound; bool mUseSound;
bool mCompileAll; bool mCompileAll;
std::string mFocusName; std::string mFocusName;
@ -151,8 +151,7 @@ namespace OMW
/// Disable or enable all sounds /// Disable or enable all sounds
void setSoundUsage(bool soundUsage); void setSoundUsage(bool soundUsage);
/// Start as a new game. void setSkipMenu (bool skipMenu);
void setNewGame(bool newGame);
void setGrabMouse(bool grab) { mGrab = grab; } void setGrabMouse(bool grab) { mGrab = grab; }

View file

@ -1,6 +1,7 @@
#include <iostream> #include <iostream>
#include <cstdio> #include <cstdio>
#include <components/version/version.hpp>
#include <components/files/configurationmanager.hpp> #include <components/files/configurationmanager.hpp>
#include <SDL.h> #include <SDL.h>
@ -30,8 +31,6 @@ extern int is_debugger_attached(void);
#include <OSX/macUtils.h> #include <OSX/macUtils.h>
#endif #endif
#include "config.hpp"
#include <boost/version.hpp> #include <boost/version.hpp>
/** /**
* Workaround for problems with whitespaces in paths in older versions of Boost library * Workaround for problems with whitespaces in paths in older versions of Boost library
@ -116,7 +115,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("resources", bpo::value<std::string>()->default_value("resources"), ("resources", bpo::value<std::string>()->default_value("resources"),
"set resources directory") "set resources directory")
("start", bpo::value<std::string>()->default_value("Beshara"), ("start", bpo::value<std::string>()->default_value(""),
"set initial cell") "set initial cell")
("content", bpo::value<StringsVector>()->default_value(StringsVector(), "") ("content", bpo::value<StringsVector>()->default_value(StringsVector(), "")
@ -137,8 +136,8 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("script-run", bpo::value<std::string>()->default_value(""), ("script-run", bpo::value<std::string>()->default_value(""),
"select a file containing a list of console commands that is executed on startup") "select a file containing a list of console commands that is executed on startup")
("new-game", bpo::value<bool>()->implicit_value(true) ("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "activate char gen/new game mechanics") ->default_value(false), "skip main menu on game startup")
("fs-strict", bpo::value<bool>()->implicit_value(true) ("fs-strict", bpo::value<bool>()->implicit_value(true)
->default_value(false), "strict file system handling (no case folding)") ->default_value(false), "strict file system handling (no case folding)")
@ -232,7 +231,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
// startup-settings // startup-settings
engine.setCell(variables["start"].as<std::string>()); engine.setCell(variables["start"].as<std::string>());
engine.setNewGame(variables["new-game"].as<bool>()); engine.setSkipMenu (variables["skip-menu"].as<bool>());
// other settings // other settings
engine.setSoundUsage(!variables["no-sound"].as<bool>()); engine.setSoundUsage(!variables["no-sound"].as<bool>());

View file

@ -11,13 +11,14 @@
#include "mechanicsmanager.hpp" #include "mechanicsmanager.hpp"
#include "inputmanager.hpp" #include "inputmanager.hpp"
#include "windowmanager.hpp" #include "windowmanager.hpp"
#include "statemanager.hpp"
MWBase::Environment *MWBase::Environment::sThis = 0; MWBase::Environment *MWBase::Environment::sThis = 0;
bool MWBase::Environment::sExit = false;
MWBase::Environment::Environment() MWBase::Environment::Environment()
: mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0), : mWorld (0), mSoundManager (0), mScriptManager (0), mWindowManager (0),
mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0) mMechanicsManager (0), mDialogueManager (0), mJournal (0), mInputManager (0), mFrameDuration (0),
mStateManager (0)
{ {
assert (!sThis); assert (!sThis);
sThis = this; sThis = this;
@ -69,6 +70,11 @@ void MWBase::Environment::setInputManager (InputManager *inputManager)
mInputManager = inputManager; mInputManager = inputManager;
} }
void MWBase::Environment::setStateManager (StateManager *stateManager)
{
mStateManager = stateManager;
}
void MWBase::Environment::setFrameDuration (float duration) void MWBase::Environment::setFrameDuration (float duration)
{ {
mFrameDuration = duration; mFrameDuration = duration;
@ -122,6 +128,12 @@ MWBase::InputManager *MWBase::Environment::getInputManager() const
return mInputManager; return mInputManager;
} }
MWBase::StateManager *MWBase::Environment::getStateManager() const
{
assert (mStateManager);
return mStateManager;
}
float MWBase::Environment::getFrameDuration() const float MWBase::Environment::getFrameDuration() const
{ {
return mFrameDuration; return mFrameDuration;
@ -152,6 +164,9 @@ void MWBase::Environment::cleanup()
delete mInputManager; delete mInputManager;
mInputManager = 0; mInputManager = 0;
delete mStateManager;
mStateManager = 0;
} }
const MWBase::Environment& MWBase::Environment::get() const MWBase::Environment& MWBase::Environment::get()

View file

@ -11,6 +11,7 @@ namespace MWBase
class MechanicsManager; class MechanicsManager;
class InputManager; class InputManager;
class WindowManager; class WindowManager;
class StateManager;
/// \brief Central hub for mw-subsystems /// \brief Central hub for mw-subsystems
/// ///
@ -30,10 +31,9 @@ namespace MWBase
DialogueManager *mDialogueManager; DialogueManager *mDialogueManager;
Journal *mJournal; Journal *mJournal;
InputManager *mInputManager; InputManager *mInputManager;
StateManager *mStateManager;
float mFrameDuration; float mFrameDuration;
static bool sExit;
Environment (const Environment&); Environment (const Environment&);
///< not implemented ///< not implemented
@ -46,9 +46,6 @@ namespace MWBase
~Environment(); ~Environment();
static void setRequestExit () { sExit = true; }
static bool getRequestExit () { return sExit; }
void setWorld (World *world); void setWorld (World *world);
void setSoundManager (SoundManager *soundManager); void setSoundManager (SoundManager *soundManager);
@ -65,6 +62,8 @@ namespace MWBase
void setInputManager (InputManager *inputManager); void setInputManager (InputManager *inputManager);
void setStateManager (StateManager *stateManager);
void setFrameDuration (float duration); void setFrameDuration (float duration);
///< Set length of current frame in seconds. ///< Set length of current frame in seconds.
@ -84,6 +83,8 @@ namespace MWBase
InputManager *getInputManager() const; InputManager *getInputManager() const;
StateManager *getStateManager() const;
float getFrameDuration() const; float getFrameDuration() const;
void cleanup(); void cleanup();

View file

@ -5,10 +5,18 @@
#include <deque> #include <deque>
#include <map> #include <map>
#include <libs/platform/stdint.h>
#include "../mwdialogue/journalentry.hpp" #include "../mwdialogue/journalentry.hpp"
#include "../mwdialogue/topic.hpp" #include "../mwdialogue/topic.hpp"
#include "../mwdialogue/quest.hpp" #include "../mwdialogue/quest.hpp"
namespace ESM
{
class ESMReader;
class ESMWriter;
}
namespace MWBase namespace MWBase
{ {
/// \brief Interface for the player's journal (implemented in MWDialogue) /// \brief Interface for the player's journal (implemented in MWDialogue)
@ -46,7 +54,7 @@ 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) = 0; virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName) = 0;
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.
@ -69,6 +77,12 @@ namespace MWBase
virtual TTopicIter topicEnd() const = 0; virtual TTopicIter topicEnd() const = 0;
///< Iterator pointing past the last topic. ///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
}; };
} }

View file

@ -3,6 +3,7 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <list>
namespace Ogre namespace Ogre
{ {
@ -135,28 +136,35 @@ namespace MWBase
float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0; float currentTemporaryDispositionDelta, bool& success, float& tempChange, float& permChange) = 0;
///< Perform a persuasion action on NPC ///< Perform a persuasion action on NPC
virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0; virtual void forceStateUpdate(const MWWorld::Ptr &ptr) = 0;
///< Forces an object to refresh its animation state. ///< Forces an object to refresh its animation state.
virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0; virtual void playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number=1) = 0;
///< Run animation for a MW-reference. Calls to this function for references that are currently not ///< Run animation for a MW-reference. Calls to this function for references that are currently not
/// in the scene should be ignored. /// in the scene should be ignored.
/// ///
/// \param mode 0 normal, 1 immediate start, 2 immediate loop /// \param mode 0 normal, 1 immediate start, 2 immediate loop
/// \param count How many times the animation should be run /// \param count How many times the animation should be run
virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0; virtual void skipAnimation(const MWWorld::Ptr& ptr) = 0;
///< Skip the animation for the given MW-reference for one frame. Calls to this function for ///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored. /// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0; virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently /// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment) /// paused we may want to do it manually (after equipping permanent enchantment)
virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0; virtual void updateMagicEffects (const MWWorld::Ptr& ptr) = 0;
virtual void toggleAI() = 0; virtual void toggleAI() = 0;
virtual bool isAIActive() = 0; virtual bool isAIActive() = 0;
virtual void getObjectsInRange (const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& objects) = 0;
///return the list of actors which are following the given actor (ie AiFollow is active and the target is the actor)
virtual std::list<MWWorld::Ptr> getActorsFollowing(const MWWorld::Ptr& actor) = 0;
virtual void playerLoaded() = 0;
}; };
} }

View file

@ -35,8 +35,6 @@ namespace MWBase
virtual ~ScriptManager() {} virtual ~ScriptManager() {}
virtual void resetGlobalScripts() = 0;
virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0; virtual void run (const std::string& name, Interpreter::Context& interpreterContext) = 0;
///< Run the script with the given name (compile first, if not compiled yet) ///< Run the script with the given name (compile first, if not compiled yet)

View file

@ -147,6 +147,10 @@ namespace MWBase
virtual void update(float duration) = 0; virtual void update(float duration) = 0;
virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0; virtual void setListenerPosDir(const Ogre::Vector3 &pos, const Ogre::Vector3 &dir, const Ogre::Vector3 &up) = 0;
virtual void updatePtr (const MWWorld::Ptr& old, const MWWorld::Ptr& updated) = 0;
virtual void clear() = 0;
}; };
} }

View file

@ -0,0 +1,81 @@
#ifndef GAME_MWSTATE_STATEMANAGER_H
#define GAME_MWSTATE_STATEMANAGER_H
#include <vector>
#include <string>
namespace MWState
{
struct Slot;
class Character;
}
namespace MWBase
{
/// \brief Interface for game state manager (implemented in MWState)
class StateManager
{
public:
enum State
{
State_NoGame,
State_Ended,
State_Running
};
typedef std::vector<MWState::Character>::const_iterator CharacterIterator;
private:
StateManager (const StateManager&);
///< not implemented
StateManager& operator= (const StateManager&);
///< not implemented
public:
StateManager() {}
virtual ~StateManager() {}
virtual void requestQuit() = 0;
virtual bool hasQuitRequest() const = 0;
virtual void askLoadRecent() = 0;
virtual State getState() const = 0;
virtual void newGame (bool bypass = false) = 0;
///< Start a new game.
///
/// \param bypass Skip new game mechanics.
virtual void endGame() = 0;
virtual void saveGame (const std::string& description, const MWState::Slot *slot = 0) = 0;
///< Write a saved game to \a slot or create a new slot if \a slot == 0.
///
/// \note Slot must belong to the current character.
virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
///< Load a saved game file from \a slot.
///
/// \note \a slot must belong to \a character.
virtual MWState::Character *getCurrentCharacter (bool create = true) = 0;
///< \param create Create a new character, if there is no current character.
virtual CharacterIterator characterBegin() = 0;
///< Any call to SaveGame and getCurrentCharacter can invalidate the returned
/// iterator.
virtual CharacterIterator characterEnd() = 0;
virtual void update (float duration) = 0;
};
}
#endif

View file

@ -33,6 +33,8 @@ namespace OEngine
namespace ESM namespace ESM
{ {
struct Class; struct Class;
class ESMReader;
class ESMWriter;
} }
namespace MWWorld namespace MWWorld
@ -55,6 +57,12 @@ namespace MWGui
class InventoryWindow; class InventoryWindow;
class ContainerWindow; class ContainerWindow;
class DialogueWindow; class DialogueWindow;
enum ShowInDialogueMode {
ShowInDialogueMode_IfPossible,
ShowInDialogueMode_Only,
ShowInDialogueMode_Never
};
} }
namespace SFO namespace SFO
@ -224,7 +232,7 @@ namespace MWBase
virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0; virtual void removeDialog(OEngine::GUI::Layout* dialog) = 0;
///< Hides dialog and schedules dialog to be deleted. ///< Hides dialog and schedules dialog to be deleted.
virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>(), bool showInDialogueModeOnly = false) = 0; virtual void messageBox (const std::string& message, const std::vector<std::string>& buttons = std::vector<std::string>(), enum MWGui::ShowInDialogueMode showInDialogueMode = MWGui::ShowInDialogueMode_IfPossible) = 0;
virtual void staticMessageBox(const std::string& message) = 0; virtual void staticMessageBox(const std::string& message) = 0;
virtual void removeStaticMessageBox() = 0; virtual void removeStaticMessageBox() = 0;
virtual int readPressedButton() = 0; virtual int readPressedButton() = 0;
@ -279,12 +287,19 @@ namespace MWBase
virtual const Translation::Storage& getTranslationDataStorage() const = 0; virtual const Translation::Storage& getTranslationDataStorage() const = 0;
/// Warning: do not use MyGUI::InputManager::setKeyFocusWidget directly. Instead use this.
virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0; virtual void setKeyFocusWidget (MyGUI::Widget* widget) = 0;
virtual Loading::Listener* getLoadingScreen() = 0; virtual Loading::Listener* getLoadingScreen() = 0;
/// Should the cursor be visible? /// Should the cursor be visible?
virtual bool getCursorVisible() = 0; virtual bool getCursorVisible() = 0;
/// Clear all savegame-specific data
virtual void clear() = 0;
virtual void write (ESM::ESMWriter& writer) = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type) = 0;
}; };
} }

View file

@ -2,6 +2,7 @@
#define GAME_MWBASE_WORLD_H #define GAME_MWBASE_WORLD_H
#include <vector> #include <vector>
#include <map>
#include <components/settings/settings.hpp> #include <components/settings/settings.hpp>
@ -30,12 +31,14 @@ namespace OEngine
namespace ESM namespace ESM
{ {
class ESMReader; class ESMReader;
class ESMWriter;
struct Position; struct Position;
struct Cell; struct Cell;
struct Class; struct Class;
struct Potion; struct Potion;
struct Spell; struct Spell;
struct NPC; struct NPC;
struct CellId;
} }
namespace MWRender namespace MWRender
@ -94,13 +97,26 @@ namespace MWBase
virtual void startNewGame() = 0; virtual void startNewGame() = 0;
virtual void clear() = 0;
virtual int countSavedGameRecords() const = 0;
virtual void write (ESM::ESMWriter& writer) const = 0;
virtual void readRecord (ESM::ESMReader& reader, int32_t type,
const std::map<int, int>& contentFileMap) = 0;
virtual OEngine::Render::Fader* getFader() = 0; virtual OEngine::Render::Fader* getFader() = 0;
///< \ŧodo remove this function. Rendering details should not be exposed. ///< \todo remove this function. Rendering details should not be exposed.
virtual MWWorld::CellStore *getExterior (int x, int y) = 0; virtual MWWorld::CellStore *getExterior (int x, int y) = 0;
virtual MWWorld::CellStore *getInterior (const std::string& name) = 0; virtual MWWorld::CellStore *getInterior (const std::string& name) = 0;
virtual MWWorld::CellStore *getCell (const ESM::CellId& id) = 0;
virtual void useDeathCamera() = 0;
virtual void setWaterHeight(const float height) = 0; virtual void setWaterHeight(const float height) = 0;
virtual void toggleWater() = 0; virtual void toggleWater() = 0;
@ -139,16 +155,26 @@ namespace MWBase
virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0; virtual bool isPositionExplored (float nX, float nY, int x, int y, bool interior) = 0;
///< see MWRender::LocalMap::isPositionExplored ///< see MWRender::LocalMap::isPositionExplored
virtual MWWorld::Globals::Data& getGlobalVariable (const std::string& name) = 0; virtual void setGlobalInt (const std::string& name, int value) = 0;
///< Set value independently from real type.
virtual MWWorld::Globals::Data getGlobalVariable (const std::string& name) const = 0; virtual void setGlobalFloat (const std::string& name, float value) = 0;
///< Set value independently from real type.
virtual int getGlobalInt (const std::string& name) const = 0;
///< Get value independently from real type.
virtual float getGlobalFloat (const std::string& name) const = 0;
///< Get value independently from real type.
virtual char getGlobalVariableType (const std::string& name) const = 0; virtual char getGlobalVariableType (const std::string& name) const = 0;
///< Return ' ', if there is no global variable with this name. ///< Return ' ', if there is no global variable with this name.
virtual std::vector<std::string> getGlobals () const = 0; virtual std::string getCellName (const MWWorld::CellStore *cell = 0) const = 0;
///< Return name of the cell.
virtual std::string getCurrentCellName() const = 0; ///
/// \note If cell==0, the cell the player is currently in will be used instead to
/// generate a name.
virtual void removeRefScript (MWWorld::RefData *ref) = 0; virtual void removeRefScript (MWWorld::RefData *ref) = 0;
//< Remove the script attached to ref from mLocalScripts //< Remove the script attached to ref from mLocalScripts
@ -185,8 +211,12 @@ namespace MWBase
virtual void setDay (int day) = 0; virtual void setDay (int day) = 0;
///< Set in-game time day. ///< Set in-game time day.
virtual int getDay() = 0; virtual int getDay() const = 0;
virtual int getMonth() = 0; virtual int getMonth() const = 0;
virtual int getYear() const = 0;
virtual std::string getMonthName (int month = -1) const = 0;
///< Return name of month (-1: current month)
virtual MWWorld::TimeStamp getTimeStamp() const = 0; virtual MWWorld::TimeStamp getTimeStamp() const = 0;
///< Return current in-game time stamp. ///< Return current in-game time stamp.
@ -215,6 +245,8 @@ namespace MWBase
virtual void changeToExteriorCell (const ESM::Position& position) = 0; virtual void changeToExteriorCell (const ESM::Position& position) = 0;
///< Move to exterior cell. ///< Move to exterior cell.
virtual void changeToCell (const ESM::CellId& cellId, const ESM::Position& position) = 0;
virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0; virtual const ESM::Cell *getExterior (const std::string& cellName) const = 0;
///< Return a cell matching the given name or a 0-pointer, if there is no such cell. ///< Return a cell matching the given name or a 0-pointer, if there is no such cell.
@ -225,7 +257,7 @@ namespace MWBase
/// Returns a pointer to the object the provided object would hit (if within the /// Returns a pointer to the object the provided object would hit (if within the
/// specified distance), and the point where the hit occurs. This will attempt to /// specified distance), and the point where the hit occurs. This will attempt to
/// use the "Head" node as a basis. /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
virtual std::pair<MWWorld::Ptr,Ogre::Vector3> getHitContact(const MWWorld::Ptr &ptr, float distance) = 0; virtual std::pair<MWWorld::Ptr,Ogre::Vector3> getHitContact(const MWWorld::Ptr &ptr, float distance) = 0;
virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0; virtual void adjustPosition (const MWWorld::Ptr& ptr) = 0;
@ -336,7 +368,7 @@ namespace MWBase
virtual bool isSwimming(const MWWorld::Ptr &object) const = 0; virtual bool isSwimming(const MWWorld::Ptr &object) const = 0;
///Is the head of the creature underwater? ///Is the head of the creature underwater?
virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0; virtual bool isSubmerged(const MWWorld::Ptr &object) const = 0;
virtual bool isUnderwater(const MWWorld::Ptr::CellStore* cell, const Ogre::Vector3 &pos) const = 0; virtual bool isUnderwater(const MWWorld::CellStore* cell, const Ogre::Vector3 &pos) const = 0;
virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0; virtual bool isOnGround(const MWWorld::Ptr &ptr) const = 0;
virtual void togglePOV() = 0; virtual void togglePOV() = 0;
@ -384,6 +416,7 @@ namespace MWBase
virtual void playVideo(const std::string& name, bool allowSkipping) = 0; virtual void playVideo(const std::string& name, bool allowSkipping) = 0;
virtual void stopVideo() = 0; virtual void stopVideo() = 0;
virtual void frameStarted (float dt, bool paused) = 0; virtual void frameStarted (float dt, bool paused) = 0;
virtual void screenshot (Ogre::Image& image, int w, int h) = 0;
/// Find default position inside exterior cell specified by name /// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise /// \return false if exterior with given name not exists, true otherwise
@ -428,6 +461,8 @@ namespace MWBase
virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects, virtual void launchProjectile (const std::string& id, bool stack, const ESM::EffectList& effects,
const MWWorld::Ptr& actor, const std::string& sourceName) = 0; const MWWorld::Ptr& actor, const std::string& sourceName) = 0;
virtual const std::vector<std::string>& getContentFiles() const = 0;
virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0; virtual void breakInvisibility (const MWWorld::Ptr& actor) = 0;
// Are we in an exterior or pseudo-exterior cell and it's night? // Are we in an exterior or pseudo-exterior cell and it's night?
@ -463,6 +498,12 @@ namespace MWBase
/// Spawn a random creature from a levelled list next to the player /// Spawn a random creature from a levelled list next to the player
virtual void spawnRandomCreature(const std::string& creatureList) = 0; virtual void spawnRandomCreature(const std::string& creatureList) = 0;
/// Spawn a blood effect for \a ptr at \a worldPosition
virtual void spawnBloodEffect (const MWWorld::Ptr& ptr, const Ogre::Vector3& worldPosition) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const MWWorld::Ptr& object, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
}; };
} }

View file

@ -289,7 +289,7 @@ namespace MWClass
std::pair<int, std::string> Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const std::pair<int, std::string> Armor::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const
{ {
MWWorld::InventoryStore& invStore = MWWorld::Class::get(npc).getInventoryStore(npc); MWWorld::InventoryStore& invStore = npc.getClass().getInventoryStore(npc);
if (ptr.getCellRef().mCharge == 0) if (ptr.getCellRef().mCharge == 0)
return std::make_pair(0, "#{sInventoryMessage1}"); return std::make_pair(0, "#{sInventoryMessage1}");
@ -300,20 +300,23 @@ namespace MWClass
if (slots_.first.empty()) if (slots_.first.empty())
return std::make_pair(0, ""); return std::make_pair(0, "");
std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace; if (npc.getClass().isNpc())
// Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
{ {
std::vector<ESM::PartReference> parts = ptr.get<ESM::Armor>()->mBase->mParts.mParts; std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;
for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr) // Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
{ {
if((*itr).mPart == ESM::PRT_Head) std::vector<ESM::PartReference> parts = ptr.get<ESM::Armor>()->mBase->mParts.mParts;
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)
return std::make_pair(0, "#{sNotifyMessage14}"); {
if((*itr).mPart == ESM::PRT_Head)
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)
return std::make_pair(0, "#{sNotifyMessage14}");
}
} }
} }
@ -363,12 +366,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell); return MWWorld::Ptr(&cell.mArmors.insert(*ref), &cell);
} }
float Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Armor::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Armor> *ref = MWWorld::LiveCellRef<ESM::Armor> *ref =
ptr.get<ESM::Armor>(); ptr.get<ESM::Armor>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const bool Armor::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -79,7 +79,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
}; };

View file

@ -189,12 +189,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell); return MWWorld::Ptr(&cell.mBooks.insert(*ref), &cell);
} }
float Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Book::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Book> *ref = MWWorld::LiveCellRef<ESM::Book> *ref =
ptr.get<ESM::Book>(); ptr.get<ESM::Book>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const bool Book::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -58,7 +58,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;

View file

@ -238,20 +238,23 @@ namespace MWClass
if (slots_.first.empty()) if (slots_.first.empty())
return std::make_pair(0, ""); return std::make_pair(0, "");
std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace; if (npc.getClass().isNpc())
// Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
{ {
std::vector<ESM::PartReference> parts = ptr.get<ESM::Clothing>()->mBase->mParts.mParts; std::string npcRace = npc.get<ESM::NPC>()->mBase->mRace;
for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr) // Beast races cannot equip shoes / boots, or full helms (head part vs hair part)
const ESM::Race* race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(npcRace);
if(race->mData.mFlags & ESM::Race::Beast)
{ {
if((*itr).mPart == ESM::PRT_Head) std::vector<ESM::PartReference> parts = ptr.get<ESM::Clothing>()->mBase->mParts.mParts;
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot) for(std::vector<ESM::PartReference>::iterator itr = parts.begin(); itr != parts.end(); ++itr)
return std::make_pair(0, "#{sNotifyMessage15}"); {
if((*itr).mPart == ESM::PRT_Head)
return std::make_pair(0, "#{sNotifyMessage13}");
if((*itr).mPart == ESM::PRT_LFoot || (*itr).mPart == ESM::PRT_RFoot)
return std::make_pair(0, "#{sNotifyMessage15}");
}
} }
} }
@ -276,12 +279,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell); return MWWorld::Ptr(&cell.mClothes.insert(*ref), &cell);
} }
float Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Clothing::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Clothing> *ref = MWWorld::LiveCellRef<ESM::Clothing> *ref =
ptr.get<ESM::Clothing>(); ptr.get<ESM::Clothing>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const bool Clothing::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -71,7 +71,7 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;

View file

@ -2,6 +2,7 @@
#include "container.hpp" #include "container.hpp"
#include <components/esm/loadcont.hpp> #include <components/esm/loadcont.hpp>
#include <components/esm/containerstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -258,4 +259,26 @@ namespace MWClass
return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell); return MWWorld::Ptr(&cell.mContainers.insert(*ref), &cell);
} }
void Container::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::ContainerState& state2 = dynamic_cast<const ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
readState (state2.mInventory);
}
void Container::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::ContainerState& state2 = dynamic_cast<ESM::ContainerState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore.
writeState (state2.mInventory);
}
} }

View file

@ -54,6 +54,14 @@ namespace MWClass
virtual void unlock (const MWWorld::Ptr& ptr) const; virtual void unlock (const MWWorld::Ptr& ptr) const;
///< Unlock object ///< Unlock object
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
static void registerSelf(); static void registerSelf();
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;

View file

@ -2,10 +2,12 @@
#include "creature.hpp" #include "creature.hpp"
#include <components/esm/loadcrea.hpp> #include <components/esm/loadcrea.hpp>
#include <components/esm/creaturestate.hpp>
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/magiceffects.hpp" #include "../mwmechanics/magiceffects.hpp"
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
@ -26,22 +28,30 @@
#include "../mwgui/tooltips.hpp" #include "../mwgui/tooltips.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/npcstats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwmechanics/combat.hpp"
namespace namespace
{ {
struct CustomData : public MWWorld::CustomData struct CustomData : public MWWorld::CustomData
{ {
MWMechanics::CreatureStats mCreatureStats; MWMechanics::CreatureStats mCreatureStats;
MWWorld::ContainerStore mContainerStore; MWWorld::ContainerStore* mContainerStore; // may be InventoryStore for some creatures
MWMechanics::Movement mMovement; MWMechanics::Movement mMovement;
virtual MWWorld::CustomData *clone() const; virtual MWWorld::CustomData *clone() const;
CustomData() : mContainerStore(0) {}
virtual ~CustomData() { delete mContainerStore; }
}; };
MWWorld::CustomData *CustomData::clone() const MWWorld::CustomData *CustomData::clone() const
{ {
return new CustomData (*this); CustomData* cloned = new CustomData (*this);
cloned->mContainerStore = mContainerStore->clone();
return cloned;
} }
} }
@ -61,6 +71,17 @@ namespace MWClass
fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature"); fMinWalkSpeedCreature = gmst.find("fMinWalkSpeedCreature");
fMaxWalkSpeedCreature = gmst.find("fMaxWalkSpeedCreature"); 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; inited = true;
} }
@ -95,15 +116,23 @@ namespace MWClass
data->mCreatureStats.getSpells().add (*iter); data->mCreatureStats.getSpells().add (*iter);
// inventory // inventory
data->mContainerStore.fill(ref->mBase->mInventory, getId(ptr), "", if (ref->mBase->mFlags & ESM::Creature::Weapon)
data->mContainerStore = new MWWorld::InventoryStore();
else
data->mContainerStore = new MWWorld::ContainerStore();
// store
ptr.getRefData().setCustomData (data.release());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "",
MWBase::Environment::get().getWorld()->getStore()); MWBase::Environment::get().getWorld()->getStore());
// TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory. // TODO: this is not quite correct, in vanilla the merchant's gold pool is not available in his inventory.
// (except for gold you gave him) // (except for gold you gave him)
data->mContainerStore.add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr); getContainerStore(ptr).add(MWWorld::ContainerStore::sGoldId, ref->mBase->mData.mGold, ptr);
// store if (ref->mBase->mFlags & ESM::Creature::Weapon)
ptr.getRefData().setCustomData (data.release()); getInventoryStore(ptr).autoEquip(ptr);
} }
} }
@ -122,8 +151,10 @@ namespace MWClass
void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const void Creature::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
MWRender::Actors& actors = renderingInterface.getActors(); MWRender::Actors& actors = renderingInterface.getActors();
actors.insertCreature(ptr); actors.insertCreature(ptr, ref->mBase->mFlags & ESM::Creature::Weapon);
} }
void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const void Creature::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
@ -165,6 +196,144 @@ namespace MWClass
void Creature::hit(const MWWorld::Ptr& ptr, int type) const void Creature::hit(const MWWorld::Ptr& ptr, int type) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
const MWWorld::Store<ESM::GameSetting> &gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
MWMechanics::CreatureStats &stats = getCreatureStats(ptr);
// Get the weapon used (if hand-to-hand, weapon = inv.end())
MWWorld::Ptr weapon;
if (ptr.getClass().hasInventoryStore(ptr))
{
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator weaponslot = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
if (weaponslot != inv.end() && weaponslot->getTypeName() == typeid(ESM::Weapon).name())
weapon = *weaponslot;
}
// Reduce fatigue
// somewhat of a guess, but using the weapon weight makes sense
const float fFatigueAttackBase = gmst.find("fFatigueAttackBase")->getFloat();
const float fFatigueAttackMult = gmst.find("fFatigueAttackMult")->getFloat();
const float fWeaponFatigueMult = gmst.find("fWeaponFatigueMult")->getFloat();
MWMechanics::DynamicStat<float> fatigue = stats.getFatigue();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
float fatigueLoss = fFatigueAttackBase + normalizedEncumbrance * fFatigueAttackMult;
if (!weapon.isEmpty())
fatigueLoss += weapon.getClass().getWeight(weapon) * stats.getAttackStrength() * fWeaponFatigueMult;
fatigue.setCurrent(fatigue.getCurrent() - fatigueLoss);
stats.setFatigue(fatigue);
// TODO: where is the distance defined?
float dist = 200.f;
if (!weapon.isEmpty())
{
const float fCombatDistance = gmst.find("fCombatDistance")->getFloat();
dist = fCombatDistance * weapon.get<ESM::Weapon>()->mBase->mData.mReach;
}
std::pair<MWWorld::Ptr, Ogre::Vector3> result = MWBase::Environment::get().getWorld()->getHitContact(ptr, dist);
if (result.first.isEmpty())
return; // Didn't hit anything
MWWorld::Ptr victim = result.first;
if (!victim.getClass().isActor())
return; // Can't hit non-actors
Ogre::Vector3 hitPosition = result.second;
MWMechanics::CreatureStats &otherstats = victim.getClass().getCreatureStats(victim);
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
float hitchance = ref->mBase->mData.mCombat +
(stats.getAttribute(ESM::Attribute::Agility).getModified() / 5.0f) +
(stats.getAttribute(ESM::Attribute::Luck).getModified() / 10.0f);
hitchance *= stats.getFatigueTerm();
hitchance += mageffects.get(ESM::MagicEffect::FortifyAttack).mMagnitude -
mageffects.get(ESM::MagicEffect::Blind).mMagnitude;
hitchance -= otherstats.getEvasion();
if((::rand()/(RAND_MAX+1.0)) > hitchance/100.0f)
{
victim.getClass().onHit(victim, 0.0f, false, MWWorld::Ptr(), ptr, false);
return;
}
int min,max;
switch (type)
{
case 0:
min = ref->mBase->mData.mAttack[0];
max = ref->mBase->mData.mAttack[1];
break;
case 1:
min = ref->mBase->mData.mAttack[2];
max = ref->mBase->mData.mAttack[3];
break;
case 2:
default:
min = ref->mBase->mData.mAttack[4];
max = ref->mBase->mData.mAttack[5];
break;
}
// I think this should be random, since attack1-3 animations don't have an attack strength like NPCs do
float damage = min + (max - min) * ::rand()/(RAND_MAX+1.0);
if (!weapon.isEmpty())
{
const bool weaphashealth = get(weapon).hasItemHealth(weapon);
const unsigned char *attack = NULL;
if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == ESM::Weapon::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == ESM::Weapon::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack)
{
float weaponDamage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
weaponDamage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f);
if(weaphashealth)
{
int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth;
if(weapon.getCellRef().mCharge == -1)
weapon.getCellRef().mCharge = weapmaxhealth;
weaponDamage *= float(weapon.getCellRef().mCharge) / weapmaxhealth;
}
if (!MWBase::Environment::get().getWorld()->getGodModeState())
weapon.getCellRef().mCharge -= std::min(std::max(1,
(int)(damage * gmst.find("fWeaponDamageMult")->getFloat())), weapon.getCellRef().mCharge);
// Weapon broken? unequip it
if (weapon.getCellRef().mCharge == 0)
weapon = *getInventoryStore(ptr).unequipItem(weapon, ptr);
damage += weaponDamage;
}
// Apply "On hit" enchanted weapons
std::string enchantmentName = !weapon.isEmpty() ? weapon.getClass().getEnchantment(weapon) : "";
if (!enchantmentName.empty())
{
const ESM::Enchantment* enchantment = MWBase::Environment::get().getWorld()->getStore().get<ESM::Enchantment>().find(
enchantmentName);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{
MWMechanics::CastSpell cast(ptr, victim);
cast.mHitPosition = hitPosition;
cast.cast(weapon);
}
}
}
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
if (damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
victim.getClass().onHit(victim, damage, true, weapon, ptr, true);
} }
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
@ -191,18 +360,60 @@ namespace MWClass
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
} }
if(ishealth) if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
if (damage > 0.f)
{ {
if(damage > 0.0f) // Check for knockdown
float agilityTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified() * fKnockDownMult->getFloat();
float knockdownTerm = getCreatureStats(ptr).getAttribute(ESM::Attribute::Agility).getModified()
* iKnockDownOddsMult->getInt() * 0.01 + iKnockDownOddsBase->getInt();
int roll = std::rand()/ (static_cast<double> (RAND_MAX) + 1) * 100; // [0, 99]
if (ishealth && agilityTerm <= damage && knockdownTerm <= roll)
{
getCreatureStats(ptr).setKnockedDown(true);
}
else
getCreatureStats(ptr).setHitRecovery(true); // Is this supposed to always occur?
if(ishealth)
{
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f); MWBase::Environment::get().getSoundManager()->playSound3D(ptr, "Health Damage", 1.0f, 1.0f);
float health = getCreatureStats(ptr).getHealth().getCurrent() - damage; float health = getCreatureStats(ptr).getHealth().getCurrent() - damage;
setActorHealth(ptr, health, attacker); setActorHealth(ptr, health, attacker);
}
else
{
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue());
fatigue.setCurrent(fatigue.getCurrent() - damage, true);
getCreatureStats(ptr).setFatigue(fatigue);
}
} }
else }
void Creature::block(const MWWorld::Ptr &ptr) const
{
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield == inv.end())
return;
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
switch(shield->getClass().getEquipmentSkill(*shield))
{ {
MWMechanics::DynamicStat<float> fatigue(getCreatureStats(ptr).getFatigue()); case ESM::Skill::LightArmor:
fatigue.setCurrent(fatigue.getCurrent() - damage, true); sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
getCreatureStats(ptr).setFatigue(fatigue); break;
case ESM::Skill::MediumArmor:
sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::HeavyArmor:
sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f);
break;
default:
return;
} }
} }
@ -245,18 +456,33 @@ namespace MWClass
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) MWWorld::ContainerStore& Creature::getContainerStore (const MWWorld::Ptr& ptr) const
const
{ {
ensureCustomData (ptr); ensureCustomData (ptr);
return dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore; return *dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore;
}
MWWorld::InventoryStore& Creature::getInventoryStore(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Weapon)
return dynamic_cast<MWWorld::InventoryStore&>(getContainerStore(ptr));
else
throw std::runtime_error("this creature has no inventory store");
}
bool Creature::hasInventoryStore(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
return (ref->mBase->mFlags & ESM::Creature::Weapon);
} }
std::string Creature::getScript (const MWWorld::Ptr& ptr) const std::string Creature::getScript (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Creature> *ref = MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
ptr.get<ESM::Creature>();
return ref->mBase->mScript; return ref->mBase->mScript;
} }
@ -286,10 +512,51 @@ 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);
float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified() float walkSpeed = fMinWalkSpeedCreature->getFloat() + 0.01 * stats.getAttribute(ESM::Attribute::Speed).getModified()
* (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat()); * (fMaxWalkSpeedCreature->getFloat() - fMinWalkSpeedCreature->getFloat());
/// \todo what about the rest?
return walkSpeed; const MWBase::World *world = MWBase::Environment::get().getWorld();
const MWMechanics::MagicEffects &mageffects = stats.getMagicEffects();
const float normalizedEncumbrance = getEncumbrance(ptr) / getCapacity(ptr);
bool running = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run);
float runSpeed = walkSpeed*(0.01f * getSkill(ptr, ESM::Skill::Athletics) *
fAthleticsRunBonus->getFloat() + fBaseRunMultiplier->getFloat());
float moveSpeed;
if(normalizedEncumbrance >= 1.0f)
moveSpeed = 0.0f;
else if(isFlying(ptr) || (mageffects.get(ESM::MagicEffect::Levitate).mMagnitude > 0 &&
world->isLevitationEnabled()))
{
float flySpeed = 0.01f*(stats.getAttribute(ESM::Attribute::Speed).getModified() +
mageffects.get(ESM::MagicEffect::Levitate).mMagnitude);
flySpeed = fMinFlySpeed->getFloat() + flySpeed*(fMaxFlySpeed->getFloat() - fMinFlySpeed->getFloat());
flySpeed *= 1.0f - fEncumberedMoveEffect->getFloat() * normalizedEncumbrance;
flySpeed = std::max(0.0f, flySpeed);
moveSpeed = flySpeed;
}
else if(world->isSwimming(ptr))
{
float swimSpeed = walkSpeed;
if(running)
swimSpeed = runSpeed;
swimSpeed *= 1.0f + 0.01f * mageffects.get(ESM::MagicEffect::SwiftSwim).mMagnitude;
swimSpeed *= fSwimRunBase->getFloat() + 0.01f*getSkill(ptr, ESM::Skill::Athletics) *
fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed;
}
else if(running)
moveSpeed = runSpeed;
else
moveSpeed = walkSpeed;
if(getMovementSettings(ptr).mPosition[0] != 0 && getMovementSettings(ptr).mPosition[1] == 0)
moveSpeed *= 0.75f;
return moveSpeed;
} }
MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const MWMechanics::Movement& Creature::getMovementSettings (const MWWorld::Ptr& ptr) const
@ -461,6 +728,71 @@ namespace MWClass
throw std::runtime_error(std::string("Unexpected soundgen type: ")+name); throw std::runtime_error(std::string("Unexpected soundgen type: ")+name);
} }
int Creature::getSkill(const MWWorld::Ptr &ptr, int skill) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref =
ptr.get<ESM::Creature>();
const ESM::Skill* skillRecord = MWBase::Environment::get().getWorld()->getStore().get<ESM::Skill>().find(skill);
switch (skillRecord->mData.mSpecialization)
{
case ESM::Class::Combat:
return ref->mBase->mData.mCombat;
case ESM::Class::Magic:
return ref->mBase->mData.mMagic;
case ESM::Class::Stealth:
return ref->mBase->mData.mStealth;
default:
throw std::runtime_error("invalid specialisation");
}
}
int Creature::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
if (ref->mBase->mFlags & ESM::Creature::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::Creature::Metal)
return 2;
return 0;
}
void Creature::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::CreatureState& state2 = dynamic_cast<const ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
readState (state2.mInventory);
}
void Creature::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::CreatureState& state2 = dynamic_cast<ESM::CreatureState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mContainerStore->
writeState (state2.mInventory);
}
const ESM::GameSetting* Creature::fMinWalkSpeedCreature; const ESM::GameSetting* Creature::fMinWalkSpeedCreature;
const ESM::GameSetting* Creature::fMaxWalkSpeedCreature; 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

@ -16,6 +16,18 @@ namespace MWClass
static const ESM::GameSetting *fMinWalkSpeedCreature; static const ESM::GameSetting *fMinWalkSpeedCreature;
static const ESM::GameSetting *fMaxWalkSpeedCreature; static const ESM::GameSetting *fMaxWalkSpeedCreature;
static const ESM::GameSetting *fEncumberedMoveEffect;
static const ESM::GameSetting *fSneakSpeedMultiplier;
static const ESM::GameSetting *fAthleticsRunBonus;
static const ESM::GameSetting *fBaseRunMultiplier;
static const ESM::GameSetting *fMinFlySpeed;
static const ESM::GameSetting *fMaxFlySpeed;
static const ESM::GameSetting *fSwimRunBase;
static const ESM::GameSetting *fSwimRunAthleticsMult;
static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase;
public: public:
@ -44,6 +56,8 @@ namespace MWClass
virtual void hit(const MWWorld::Ptr& ptr, int type) const; virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void block(const MWWorld::Ptr &ptr) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
@ -56,6 +70,11 @@ namespace MWClass
const MWWorld::Ptr& ptr) const; const MWWorld::Ptr& ptr) const;
///< Return container store ///< Return container store
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store
virtual bool hasInventoryStore (const MWWorld::Ptr &ptr) const;
virtual std::string getScript (const MWWorld::Ptr& ptr) const; virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr ///< Return name of the script attached to ptr
@ -101,6 +120,19 @@ namespace MWClass
} }
virtual bool isFlying (const MWWorld::Ptr &ptr) const; virtual bool isFlying (const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr &ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
}; };
} }

View file

@ -3,6 +3,24 @@
#include <components/esm/loadlevlist.hpp> #include <components/esm/loadlevlist.hpp>
#include "../mwmechanics/levelledlist.hpp"
#include "../mwworld/customdata.hpp"
namespace
{
struct CustomData : public MWWorld::CustomData
{
// TODO: save the creature we spawned here
virtual MWWorld::CustomData *clone() const;
};
MWWorld::CustomData *CustomData::clone() const
{
return new CustomData (*this);
}
}
namespace MWClass namespace MWClass
{ {
std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const std::string CreatureLevList::getName (const MWWorld::Ptr& ptr) const
@ -16,4 +34,33 @@ namespace MWClass
registerClass (typeid (ESM::CreatureLevList).name(), instance); registerClass (typeid (ESM::CreatureLevList).name(), instance);
} }
void CreatureLevList::insertObjectRendering(const MWWorld::Ptr &ptr, MWRender::RenderingInterface &renderingInterface) const
{
ensureCustomData(ptr);
}
void CreatureLevList::ensureCustomData(const MWWorld::Ptr &ptr) const
{
if (!ptr.getRefData().getCustomData())
{
std::auto_ptr<CustomData> data (new CustomData);
MWWorld::LiveCellRef<ESM::CreatureLevList> *ref =
ptr.get<ESM::CreatureLevList>();
std::string id = MWMechanics::getLevelledItem(ref->mBase, true);
if (!id.empty())
{
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::ManualRef ref(store, id);
ref.getPtr().getCellRef().mPos = ptr.getCellRef().mPos;
// TODO: hold on to this for respawn purposes later
MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(), *ptr.getCell() , ptr.getCellRef().mPos);
}
ptr.getRefData().setCustomData(data.release());
}
}
} }

View file

@ -7,6 +7,8 @@ namespace MWClass
{ {
class CreatureLevList : public MWWorld::Class class CreatureLevList : public MWWorld::Class
{ {
void ensureCustomData (const MWWorld::Ptr& ptr) const;
public: public:
virtual std::string getName (const MWWorld::Ptr& ptr) const; virtual std::string getName (const MWWorld::Ptr& ptr) const;
@ -14,6 +16,9 @@ namespace MWClass
/// can return an empty string. /// can return an empty string.
static void registerSelf(); static void registerSelf();
virtual void insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const;
///< Add reference into a cell for rendering
}; };
} }

View file

@ -2,6 +2,7 @@
#include "light.hpp" #include "light.hpp"
#include <components/esm/loadligh.hpp> #include <components/esm/loadligh.hpp>
#include <components/esm/lightstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -269,4 +270,24 @@ namespace MWClass
} }
return std::make_pair(1,""); return std::make_pair(1,"");
} }
void Light::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::LightState& state2 = dynamic_cast<const ESM::LightState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime = state2.mTime;
}
void Light::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::LightState& state2 = dynamic_cast<ESM::LightState&> (state);
ensureCustomData (ptr);
state2.mTime = dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mTime;
}
} }

View file

@ -71,6 +71,14 @@ namespace MWClass
virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const; virtual bool canSell (const MWWorld::Ptr& item, int npcServices) const;
std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const; std::pair<int, std::string> canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const;
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
}; };
} }

View file

@ -155,11 +155,8 @@ namespace MWClass
int count = ptr.getRefData().getCount(); int count = ptr.getRefData().getCount();
bool gold = isGold(ptr); bool gold = isGold(ptr);
if (gold)
if (gold && ptr.getCellRef().mGoldValue != 1) count *= getValue(ptr);
count = ptr.getCellRef().mGoldValue;
else if (gold)
count *= ref->mBase->mData.mValue;
std::string countString; std::string countString;
if (!gold) if (!gold)
@ -204,7 +201,7 @@ namespace MWClass
MWBase::Environment::get().getWorld()->getStore(); MWBase::Environment::get().getWorld()->getStore();
if (isGold(ptr)) { if (isGold(ptr)) {
int goldAmount = ptr.getRefData().getCount(); int goldAmount = getValue(ptr) * ptr.getRefData().getCount();
std::string base = "Gold_001"; std::string base = "Gold_001";
if (goldAmount >= 100) if (goldAmount >= 100)
@ -223,6 +220,7 @@ namespace MWClass
newRef.getPtr().get<ESM::Miscellaneous>(); newRef.getPtr().get<ESM::Miscellaneous>();
newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell); newPtr = MWWorld::Ptr(&cell.mMiscItems.insert(*ref), &cell);
newPtr.getCellRef().mGoldValue = goldAmount; newPtr.getCellRef().mGoldValue = goldAmount;
newPtr.getRefData().setCount(1);
} else { } else {
MWWorld::LiveCellRef<ESM::Miscellaneous> *ref = MWWorld::LiveCellRef<ESM::Miscellaneous> *ref =
ptr.get<ESM::Miscellaneous>(); ptr.get<ESM::Miscellaneous>();

View file

@ -3,12 +3,11 @@
#include <memory> #include <memory>
#include <boost/algorithm/string.hpp>
#include <OgreSceneNode.h> #include <OgreSceneNode.h>
#include <components/esm/loadmgef.hpp> #include <components/esm/loadmgef.hpp>
#include <components/esm/loadnpc.hpp> #include <components/esm/loadnpc.hpp>
#include <components/esm/npcstate.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -22,6 +21,7 @@
#include "../mwmechanics/movement.hpp" #include "../mwmechanics/movement.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/disease.hpp" #include "../mwmechanics/disease.hpp"
#include "../mwmechanics/combat.hpp"
#include "../mwworld/ptr.hpp" #include "../mwworld/ptr.hpp"
#include "../mwworld/actiontalk.hpp" #include "../mwworld/actiontalk.hpp"
@ -38,9 +38,6 @@
namespace namespace
{ {
const Ogre::Radian kOgrePi (Ogre::Math::PI);
const Ogre::Radian kOgrePiOverTwo (Ogre::Math::PI / Ogre::Real(2.0));
struct CustomData : public MWWorld::CustomData struct CustomData : public MWWorld::CustomData
{ {
MWMechanics::NpcStats mNpcStats; MWMechanics::NpcStats mNpcStats;
@ -146,7 +143,7 @@ namespace
* *
* and by adding class, race, specialization bonus. * and by adding class, race, specialization bonus.
*/ */
void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats) void autoCalculateSkills(const ESM::NPC* npc, MWMechanics::NpcStats& npcStats, const MWWorld::Ptr& ptr)
{ {
const ESM::Class *class_ = const ESM::Class *class_ =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass); MWBase::Environment::get().getWorld()->getStore().get<ESM::Class>().find(npc->mClass);
@ -195,6 +192,18 @@ namespace
majorMultiplier = 1.0f; majorMultiplier = 1.0f;
break; break;
} }
if (class_->mData.mSkills[k][1] == skillIndex)
{
// Major skill -> add starting spells for this skill if existing
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
MWWorld::Store<ESM::Spell>::iterator it = store.get<ESM::Spell>().begin();
for (; it != store.get<ESM::Spell>().end(); ++it)
{
if (it->mData.mFlags & ESM::Spell::F_Autocalc
&& MWMechanics::spellSchoolToSkill(MWMechanics::getSpellSchool(&*it, ptr)) == skillIndex)
npcStats.getSpells().add(it->mId);
}
}
} }
// is this skill in the same Specialization as the class? // is this skill in the same Specialization as the class?
@ -245,6 +254,8 @@ namespace MWClass
fKnockDownMult = gmst.find("fKnockDownMult"); fKnockDownMult = gmst.find("fKnockDownMult");
iKnockDownOddsMult = gmst.find("iKnockDownOddsMult"); iKnockDownOddsMult = gmst.find("iKnockDownOddsMult");
iKnockDownOddsBase = gmst.find("iKnockDownOddsBase"); iKnockDownOddsBase = gmst.find("iKnockDownOddsBase");
fDamageStrengthBase = gmst.find("fDamageStrengthBase");
fDamageStrengthMult = gmst.find("fDamageStrengthMult");
inited = true; inited = true;
} }
@ -307,7 +318,15 @@ namespace MWClass
data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation); data->mNpcStats.setReputation(ref->mBase->mNpdt12.mReputation);
autoCalculateAttributes(ref->mBase, data->mNpcStats); autoCalculateAttributes(ref->mBase, data->mNpcStats);
autoCalculateSkills(ref->mBase, data->mNpcStats); autoCalculateSkills(ref->mBase, data->mNpcStats, ptr);
}
// race powers
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
for (std::vector<std::string>::const_iterator iter (race->mPowers.mList.begin());
iter!=race->mPowers.mList.end(); ++iter)
{
data->mNpcStats.getSpells().add (*iter);
} }
if (data->mNpcStats.getFactionRanks().size()) if (data->mNpcStats.getFactionRanks().size())
@ -452,12 +471,15 @@ 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();
float dist = 100.0f * (!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()); gmst.find("fHandToHandReach")->getFloat());
// TODO: Use second to work out the hit angle and where to spawn the blood effect
MWWorld::Ptr victim = world->getHitContact(ptr, dist).first; // TODO: Use second to work out the hit angle
std::pair<MWWorld::Ptr, Ogre::Vector3> result = world->getHitContact(ptr, dist);
MWWorld::Ptr victim = result.first;
Ogre::Vector3 hitPosition = result.second;
if(victim.isEmpty()) // Didn't hit anything if(victim.isEmpty()) // Didn't hit anything
return; return;
@ -471,10 +493,6 @@ namespace MWClass
if(ptr.getRefData().getHandle() == "player") if(ptr.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->setEnemy(victim); MWBase::Environment::get().getWindowManager()->setEnemy(victim);
// Attacking peaceful NPCs is a crime
if (victim.getClass().isNpc() && victim.getClass().getCreatureStats(victim).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
MWBase::Environment::get().getMechanicsManager()->commitCrime(ptr, victim, MWBase::MechanicsManager::OT_Assault);
int weapskill = ESM::Skill::HandToHand; int weapskill = ESM::Skill::HandToHand;
if(!weapon.isEmpty()) if(!weapon.isEmpty())
weapskill = get(weapon).getEquipmentSkill(weapon); weapskill = get(weapon).getEquipmentSkill(weapon);
@ -501,16 +519,17 @@ namespace MWClass
{ {
const bool weaphashealth = get(weapon).hasItemHealth(weapon); const bool weaphashealth = get(weapon).hasItemHealth(weapon);
const unsigned char *attack = NULL; const unsigned char *attack = NULL;
if(type == MWMechanics::CreatureStats::AT_Chop) if(type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop; attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
else if(type == MWMechanics::CreatureStats::AT_Slash) else if(type == ESM::Weapon::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash; attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
else if(type == MWMechanics::CreatureStats::AT_Thrust) else if(type == ESM::Weapon::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust; attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
if(attack) if(attack)
{ {
damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength()); damage = attack[0] + ((attack[1]-attack[0])*stats.getAttackStrength());
damage *= 0.5f + (stats.getAttribute(ESM::Attribute::Luck).getModified() / 100.0f); damage *= fDamageStrengthBase->getFloat() +
(stats.getAttribute(ESM::Attribute::Strength).getModified() * fDamageStrengthMult->getFloat() * 0.1);
if(weaphashealth) if(weaphashealth)
{ {
int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth; int weapmaxhealth = weapon.get<ESM::Weapon>()->mBase->mData.mHealth;
@ -583,31 +602,18 @@ namespace MWClass
enchantmentName); enchantmentName);
if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes) if (enchantment->mData.mType == ESM::Enchantment::WhenStrikes)
{ {
// Check if we have enough charges MWMechanics::CastSpell cast(ptr, victim);
const float enchantCost = enchantment->mData.mCost; cast.mHitPosition = hitPosition;
int eSkill = stats.getSkill(ESM::Skill::Enchant).getModified(); cast.cast(weapon);
const int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));
if (weapon.getCellRef().mEnchantmentCharge == -1)
weapon.getCellRef().mEnchantmentCharge = enchantment->mData.mCharge;
if (weapon.getCellRef().mEnchantmentCharge < castCost)
{
if (ptr.getRefData().getHandle() == "player")
MWBase::Environment::get().getWindowManager()->messageBox("#{sMagicInsufficientCharge}");
}
else
{
weapon.getCellRef().mEnchantmentCharge -= castCost;
MWMechanics::CastSpell cast(ptr, victim);
cast.cast(weapon);
if (ptr.getRefData().getHandle() == "player")
skillUsageSucceeded (ptr, ESM::Skill::Enchant, 3);
}
} }
} }
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
if (healthdmg && damage > 0)
MWBase::Environment::get().getWorld()->spawnBloodEffect(victim, hitPosition);
othercls.onHit(victim, damage, healthdmg, weapon, ptr, true); othercls.onHit(victim, damage, healthdmg, weapon, ptr, true);
} }
@ -617,6 +623,10 @@ namespace MWClass
// NOTE: 'object' and/or 'attacker' may be empty. // NOTE: 'object' and/or 'attacker' may be empty.
// Attacking peaceful NPCs is a crime
if (!attacker.isEmpty() && ptr.getClass().isNpc() && ptr.getClass().getCreatureStats(ptr).getAiSetting(MWMechanics::CreatureStats::AI_Fight).getModified() <= 30)
MWBase::Environment::get().getMechanicsManager()->commitCrime(attacker, ptr, MWBase::MechanicsManager::OT_Assault);
if(!successful) if(!successful)
{ {
// TODO: Handle HitAttemptOnMe script function // TODO: Handle HitAttemptOnMe script function
@ -631,7 +641,7 @@ namespace MWClass
if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player") if(!attacker.isEmpty() && attacker.getRefData().getHandle() == "player")
{ {
const std::string &script = ptr.get<ESM::NPC>()->mBase->mScript; const std::string &script = ptr.getClass().getScript(ptr);
/* Set the OnPCHitMe script variable. The script is responsible for clearing it. */ /* Set the OnPCHitMe script variable. The script is responsible for clearing it. */
if(!script.empty()) if(!script.empty())
ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1); ptr.getRefData().getLocals().setVarByInt(script, "onpchitme", 1);
@ -640,6 +650,9 @@ namespace MWClass
if (!attacker.isEmpty()) if (!attacker.isEmpty())
MWMechanics::diseaseContact(ptr, attacker); MWMechanics::diseaseContact(ptr, attacker);
if (damage > 0.0f && !object.isEmpty())
MWMechanics::resistNormalWeapon(ptr, attacker, object, damage);
if(damage > 0.0f) if(damage > 0.0f)
{ {
// 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying // 'ptr' is losing health. Play a 'hit' voiced dialog entry if not already saying
@ -711,6 +724,9 @@ namespace MWClass
if (armorref.mCharge == 0) if (armorref.mCharge == 0)
inv.unequipItem(armor, ptr); inv.unequipItem(armor, ptr);
if (ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, get(armor).getEquipmentSkill(armor), 0);
switch(get(armor).getEquipmentSkill(armor)) switch(get(armor).getEquipmentSkill(armor))
{ {
case ESM::Skill::LightArmor: case ESM::Skill::LightArmor:
@ -724,6 +740,8 @@ namespace MWClass
break; break;
} }
} }
else if(ptr.getRefData().getHandle() == "player")
skillUsageSucceeded(ptr, ESM::Skill::Unarmored, 0);
} }
} }
@ -742,6 +760,30 @@ namespace MWClass
} }
} }
void Npc::block(const MWWorld::Ptr &ptr) const
{
MWWorld::InventoryStore& inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator shield = inv.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
if (shield == inv.end())
return;
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
switch(shield->getClass().getEquipmentSkill(*shield))
{
case ESM::Skill::LightArmor:
sndMgr->playSound3D(ptr, "Light Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::MediumArmor:
sndMgr->playSound3D(ptr, "Medium Armor Hit", 1.0f, 1.0f);
break;
case ESM::Skill::HeavyArmor:
sndMgr->playSound3D(ptr, "Heavy Armor Hit", 1.0f, 1.0f);
break;
default:
return;
}
}
void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const void Npc::setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const
{ {
MWMechanics::CreatureStats &crstats = getCreatureStats(ptr); MWMechanics::CreatureStats &crstats = getCreatureStats(ptr);
@ -777,10 +819,10 @@ namespace MWClass
} }
if(getCreatureStats(ptr).isDead()) if(getCreatureStats(ptr).isDead())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true)); return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr, true));
if(get(actor).getStance(actor, MWWorld::Class::Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
if(get(ptr).getCreatureStats(ptr).isHostile()) if(get(ptr).getCreatureStats(ptr).isHostile())
return boost::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}")); return boost::shared_ptr<MWWorld::Action>(new MWWorld::FailedAction("#{sActorInCombat}"));
if(getCreatureStats(actor).getStance(MWMechanics::CreatureStats::Stance_Sneak))
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionOpen(ptr)); // stealing
return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr)); return boost::shared_ptr<MWWorld::Action>(new MWWorld::ActionTalk(ptr));
} }
@ -808,80 +850,6 @@ namespace MWClass
return ref->mBase->mScript; return ref->mBase->mScript;
} }
void Npc::setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceRun, force);
break;
case Sneak:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak, force);
break;
case Combat:
throw std::runtime_error ("combat stance not enforcable for NPCs");
}
}
void Npc::setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_Run, set);
break;
case Sneak:
stats.setMovementFlag (MWMechanics::NpcStats::Flag_Sneak, set);
break;
case Combat:
// Combat stance ignored for now; need to be determined based on draw state instead of
// being maunally set.
break;
}
}
bool Npc::getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce) const
{
MWMechanics::NpcStats& stats = getNpcStats (ptr);
switch (stance)
{
case Run:
if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceRun))
return true;
return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Run);
case Sneak:
if (!ignoreForce && stats.getMovementFlag (MWMechanics::NpcStats::Flag_ForceSneak))
return true;
return stats.getMovementFlag (MWMechanics::NpcStats::Flag_Sneak);
case Combat:
return false;
}
return false;
}
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();
@ -890,11 +858,14 @@ namespace MWClass
const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr); const float normalizedEncumbrance = Npc::getEncumbrance(ptr) / Npc::getCapacity(ptr);
bool sneaking = ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Sneak);
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 = fMinWalkSpeed->getFloat() + 0.01f*npcdata->mNpcStats.getAttribute(ESM::Attribute::Speed).getModified()*
(fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat()); (fMaxWalkSpeed->getFloat() - fMinWalkSpeed->getFloat());
walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance; walkSpeed *= 1.0f - fEncumberedMoveEffect->getFloat()*normalizedEncumbrance;
walkSpeed = std::max(0.0f, walkSpeed); walkSpeed = std::max(0.0f, walkSpeed);
if(Npc::getStance(ptr, Sneak, false)) if(sneaking)
walkSpeed *= fSneakSpeedMultiplier->getFloat(); walkSpeed *= 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() *
@ -918,14 +889,14 @@ namespace MWClass
else if(world->isSwimming(ptr)) else if(world->isSwimming(ptr))
{ {
float swimSpeed = walkSpeed; float swimSpeed = walkSpeed;
if(Npc::getStance(ptr, Run, false)) 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 *= fSwimRunBase->getFloat() + 0.01f*npcdata->mNpcStats.getSkill(ESM::Skill::Athletics).getModified()*
fSwimRunAthleticsMult->getFloat(); fSwimRunAthleticsMult->getFloat();
moveSpeed = swimSpeed; moveSpeed = swimSpeed;
} }
else if(Npc::getStance(ptr, Run, false) && !Npc::getStance(ptr, Sneak, false)) else if(running && !sneaking)
moveSpeed = runSpeed; moveSpeed = runSpeed;
else else
moveSpeed = walkSpeed; moveSpeed = walkSpeed;
@ -957,7 +928,7 @@ namespace MWClass
x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64; x += mageffects.get(ESM::MagicEffect::Jump).mMagnitude * 64;
x *= encumbranceTerm; x *= encumbranceTerm;
if(Npc::getStance(ptr, Run, false)) if(ptr.getClass().getCreatureStats(ptr).getStance(MWMechanics::CreatureStats::Stance_Run))
x *= fJumpRunMultiplier->getFloat(); x *= fJumpRunMultiplier->getFloat();
x *= npcdata->mNpcStats.getFatigueTerm(); x *= npcdata->mNpcStats.getFatigueTerm();
x -= -627.2f;/*gravity constant*/ x -= -627.2f;/*gravity constant*/
@ -1284,6 +1255,44 @@ namespace MWClass
return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell); return MWWorld::Ptr(&cell.mNpcs.insert(*ref), &cell);
} }
int Npc::getSkill(const MWWorld::Ptr& ptr, int skill) const
{
return ptr.getClass().getNpcStats(ptr).getSkill(skill).getModified();
}
int Npc::getBloodTexture(const MWWorld::Ptr &ptr) const
{
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
if (ref->mBase->mFlags & ESM::NPC::Skeleton)
return 1;
if (ref->mBase->mFlags & ESM::NPC::Metal)
return 2;
return 0;
}
void Npc::readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const
{
const ESM::NpcState& state2 = dynamic_cast<const ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
readState (state2.mInventory);
}
void Npc::writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const
{
ESM::NpcState& state2 = dynamic_cast<ESM::NpcState&> (state);
ensureCustomData (ptr);
dynamic_cast<CustomData&> (*ptr.getRefData().getCustomData()).mInventoryStore.
writeState (state2.mInventory);
}
const ESM::GameSetting *Npc::fMinWalkSpeed; const ESM::GameSetting *Npc::fMinWalkSpeed;
const ESM::GameSetting *Npc::fMaxWalkSpeed; const ESM::GameSetting *Npc::fMaxWalkSpeed;
const ESM::GameSetting *Npc::fEncumberedMoveEffect; const ESM::GameSetting *Npc::fEncumberedMoveEffect;
@ -1303,4 +1312,7 @@ namespace MWClass
const ESM::GameSetting *Npc::fKnockDownMult; const ESM::GameSetting *Npc::fKnockDownMult;
const ESM::GameSetting *Npc::iKnockDownOddsMult; const ESM::GameSetting *Npc::iKnockDownOddsMult;
const ESM::GameSetting *Npc::iKnockDownOddsBase; const ESM::GameSetting *Npc::iKnockDownOddsBase;
const ESM::GameSetting *Npc::fDamageStrengthBase;
const ESM::GameSetting *Npc::fDamageStrengthMult;
} }

View file

@ -36,6 +36,8 @@ namespace MWClass
static const ESM::GameSetting *fKnockDownMult; static const ESM::GameSetting *fKnockDownMult;
static const ESM::GameSetting *iKnockDownOddsMult; static const ESM::GameSetting *iKnockDownOddsMult;
static const ESM::GameSetting *iKnockDownOddsBase; static const ESM::GameSetting *iKnockDownOddsBase;
static const ESM::GameSetting *fDamageStrengthBase;
static const ESM::GameSetting *fDamageStrengthMult;
public: public:
@ -71,10 +73,14 @@ namespace MWClass
virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const; virtual MWWorld::InventoryStore& getInventoryStore (const MWWorld::Ptr& ptr) const;
///< Return inventory store ///< Return inventory store
virtual bool hasInventoryStore(const MWWorld::Ptr &ptr) const { return true; }
virtual void hit(const MWWorld::Ptr& ptr, int type) const; virtual void hit(const MWWorld::Ptr& ptr, int type) const;
virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const; virtual void onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const;
virtual void block(const MWWorld::Ptr &ptr) const;
virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const; virtual void setActorHealth(const MWWorld::Ptr& ptr, float health, const MWWorld::Ptr& attacker) const;
virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr, virtual boost::shared_ptr<MWWorld::Action> activate (const MWWorld::Ptr& ptr,
@ -84,16 +90,6 @@ namespace MWClass
virtual std::string getScript (const MWWorld::Ptr& ptr) const; virtual std::string getScript (const MWWorld::Ptr& ptr) const;
///< Return name of the script attached to ptr ///< Return name of the script attached to ptr
virtual void setForceStance (const MWWorld::Ptr& ptr, Stance stance, bool force) const;
///< Force or unforce a stance.
virtual void setStance (const MWWorld::Ptr& ptr, Stance stance, bool set) const;
///< Set or unset a stance.
virtual bool getStance (const MWWorld::Ptr& ptr, Stance stance, bool ignoreForce = false)
const;
///< Check if a stance is active or not.
virtual float getSpeed (const MWWorld::Ptr& ptr) const; virtual float getSpeed (const MWWorld::Ptr& ptr) const;
///< Return movement speed. ///< Return movement speed.
@ -150,6 +146,11 @@ namespace MWClass
virtual std::string getModel(const MWWorld::Ptr &ptr) const; virtual std::string getModel(const MWWorld::Ptr &ptr) const;
virtual int getSkill(const MWWorld::Ptr& ptr, int skill) const;
/// Get a blood texture suitable for \a ptr (see Blood Texture 0-2 in Morrowind.ini)
virtual int getBloodTexture (const MWWorld::Ptr& ptr) const;
virtual bool isActor() const { virtual bool isActor() const {
return true; return true;
} }
@ -157,6 +158,14 @@ namespace MWClass
virtual bool isNpc() const { virtual bool isNpc() const {
return true; return true;
} }
virtual void readAdditionalState (const MWWorld::Ptr& ptr, const ESM::ObjectState& state)
const;
///< Read additional state from \a state into \a ptr.
virtual void writeAdditionalState (const MWWorld::Ptr& ptr, ESM::ObjectState& state)
const;
///< Write additional state from \a ptr into \a state.
}; };
} }

View file

@ -429,12 +429,12 @@ namespace MWClass
return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell); return MWWorld::Ptr(&cell.mWeapons.insert(*ref), &cell);
} }
float Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const int Weapon::getEnchantmentPoints (const MWWorld::Ptr& ptr) const
{ {
MWWorld::LiveCellRef<ESM::Weapon> *ref = MWWorld::LiveCellRef<ESM::Weapon> *ref =
ptr.get<ESM::Weapon>(); ptr.get<ESM::Weapon>();
return ref->mBase->mData.mEnchant/10.f; return ref->mBase->mData.mEnchant;
} }
bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const bool Weapon::canSell (const MWWorld::Ptr& item, int npcServices) const

View file

@ -84,7 +84,7 @@ namespace MWClass
virtual float getWeight (const MWWorld::Ptr& ptr) const; virtual float getWeight (const MWWorld::Ptr& ptr) const;
virtual float getEnchantmentPoints (const MWWorld::Ptr& ptr) const; virtual int getEnchantmentPoints (const MWWorld::Ptr& ptr) const;
}; };
} }

View file

@ -286,7 +286,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title); win->addResponse (Interpreter::fixDefinesDialog(info->mResponse, interpreterContext), title);
MWBase::Environment::get().getJournal()->addTopic (topic, info->mId); MWBase::Environment::get().getJournal()->addTopic (topic, info->mId, mActor.getClass().getName(mActor));
executeScript (info->mResultScript); executeScript (info->mResultScript);
@ -420,7 +420,7 @@ namespace MWDialogue
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue); MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Dialogue);
// Apply disposition change to NPC's base disposition // Apply disposition change to NPC's base disposition
if (mActor.getTypeName() == typeid(ESM::NPC).name()) if (mActor.getClass().isNpc())
{ {
MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(mActor).getNpcStats(mActor);
npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange); npcStats.setBaseDisposition(npcStats.getBaseDisposition() + mPermanentDispositionChange);
@ -451,7 +451,7 @@ namespace MWDialogue
MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor); MWScript::InterpreterContext interpreterContext(&mActor.getRefData().getLocals(),mActor);
MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext)); MWBase::Environment::get().getWindowManager()->getDialogueWindow()->addResponse (Interpreter::fixDefinesDialog(text, interpreterContext));
MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId); MWBase::Environment::get().getJournal()->addTopic (mLastTopic, info->mId, mActor.getClass().getName(mActor));
executeScript (info->mResultScript); executeScript (info->mResultScript);
} }
} }
@ -586,7 +586,8 @@ namespace MWDialogue
MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager(); MWBase::WindowManager *winMgr = MWBase::Environment::get().getWindowManager();
if(winMgr->getSubtitlesEnabled()) if(winMgr->getSubtitlesEnabled())
winMgr->messageBox(info->mResponse); winMgr->messageBox(info->mResponse);
sndMgr->say(actor, info->mSound); if (!info->mSound.empty())
sndMgr->say(actor, info->mSound);
} }
} }

View file

@ -24,7 +24,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
// actor id // actor id
if (!info.mActor.empty()) if (!info.mActor.empty())
{ {
if ( Misc::StringUtils::lowerCase (info.mActor)!=MWWorld::Class::get (mActor).getId (mActor)) if ( !Misc::StringUtils::ciEqual(info.mActor, MWWorld::Class::get (mActor).getId (mActor)))
return false; return false;
} }
else if (isCreature) else if (isCreature)
@ -41,7 +41,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();
if (Misc::StringUtils::lowerCase (info.mRace)!= Misc::StringUtils::lowerCase (cellRef->mBase->mRace)) if (!Misc::StringUtils::ciEqual(info.mRace, cellRef->mBase->mRace))
return false; return false;
} }
@ -53,7 +53,7 @@ bool MWDialogue::Filter::testActor (const ESM::DialInfo& info) const
MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>(); MWWorld::LiveCellRef<ESM::NPC> *cellRef = mActor.get<ESM::NPC>();
if ( Misc::StringUtils::lowerCase (info.mClass)!= Misc::StringUtils::lowerCase (cellRef->mBase->mClass)) if ( !Misc::StringUtils::ciEqual(info.mClass, cellRef->mBase->mClass))
return false; return false;
} }
@ -110,7 +110,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
// check cell // check cell
if (!info.mCell.empty()) if (!info.mCell.empty())
if (Misc::StringUtils::lowerCase (player.getCell()->mCell->mName) != Misc::StringUtils::lowerCase (info.mCell)) if (!Misc::StringUtils::ciEqual(player.getCell()->mCell->mName, info.mCell))
return false; return false;
return true; return true;
@ -171,7 +171,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
// internally all globals are float :( // internally all globals are float :(
return select.selectCompare ( return select.selectCompare (
MWBase::Environment::get().getWorld()->getGlobalVariable (select.getName()).mFloat); MWBase::Environment::get().getWorld()->getGlobalFloat (select.getName()));
case SelectWrapper::Function_Local: case SelectWrapper::Function_Local:
{ {
@ -188,7 +188,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
int i = 0; int i = 0;
for (; i<static_cast<int> (script->mVarNames.size()); ++i) for (; i<static_cast<int> (script->mVarNames.size()); ++i)
if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) if (Misc::StringUtils::ciEqual(script->mVarNames[i], name))
break; break;
if (i>=static_cast<int> (script->mVarNames.size())) if (i>=static_cast<int> (script->mVarNames.size()))
@ -216,7 +216,7 @@ bool MWDialogue::Filter::testSelectStructNumeric (const SelectWrapper& select) c
float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() / float ratio = MWWorld::Class::get (player).getCreatureStats (player).getHealth().getCurrent() /
MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified(); MWWorld::Class::get (player).getCreatureStats (player).getHealth().getModified();
return select.selectCompare (ratio); return select.selectCompare (static_cast<int>(ratio*100));
} }
case SelectWrapper::Function_PcDynamicStat: case SelectWrapper::Function_PcDynamicStat:
@ -262,7 +262,7 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
std::string name = select.getName(); std::string name = select.getName();
for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter) for (MWWorld::ContainerStoreIterator iter (store.begin()); iter!=store.end(); ++iter)
if (Misc::StringUtils::lowerCase(iter->getCellRef().mRefID) == name) if (Misc::StringUtils::ciEqual(iter->getCellRef().mRefID, name))
sum += iter->getRefData().getCount(); sum += iter->getRefData().getCount();
return sum; return sum;
@ -429,23 +429,23 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_NotId: case SelectWrapper::Function_NotId:
return select.getName()!=Misc::StringUtils::lowerCase (MWWorld::Class::get (mActor).getId (mActor)); return !Misc::StringUtils::ciEqual(MWWorld::Class::get (mActor).getId (mActor), select.getName());
case SelectWrapper::Function_NotFaction: case SelectWrapper::Function_NotFaction:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mFaction)!=select.getName(); return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mFaction, select.getName());
case SelectWrapper::Function_NotClass: case SelectWrapper::Function_NotClass:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mClass)!=select.getName(); return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mClass, select.getName());
case SelectWrapper::Function_NotRace: case SelectWrapper::Function_NotRace:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mRace)!=select.getName(); return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, select.getName());
case SelectWrapper::Function_NotCell: case SelectWrapper::Function_NotCell:
return Misc::StringUtils::lowerCase (mActor.getCell()->mCell->mName)!=select.getName(); return !Misc::StringUtils::ciEqual(mActor.getCell()->mCell->mName, select.getName());
case SelectWrapper::Function_NotLocal: case SelectWrapper::Function_NotLocal:
{ {
@ -462,7 +462,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
int i = 0; int i = 0;
for (; i < static_cast<int> (script->mVarNames.size()); ++i) for (; i < static_cast<int> (script->mVarNames.size()); ++i)
if (Misc::StringUtils::lowerCase(script->mVarNames[i]) == name) if (Misc::StringUtils::ciEqual(script->mVarNames[i], name))
break; break;
if (i >= static_cast<int> (script->mVarNames.size())) if (i >= static_cast<int> (script->mVarNames.size()))
@ -478,8 +478,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_SameRace: case SelectWrapper::Function_SameRace:
return Misc::StringUtils::lowerCase (mActor.get<ESM::NPC>()->mBase->mRace)!= return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, player.get<ESM::NPC>()->mBase->mRace);
Misc::StringUtils::lowerCase (player.get<ESM::NPC>()->mBase->mRace);
case SelectWrapper::Function_SameFaction: case SelectWrapper::Function_SameFaction:
@ -537,7 +536,7 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_CreatureTargetted: case SelectWrapper::Function_CreatureTargetted:
return MWWorld::Class::get (mActor).getCreatureStats (mActor).getCreatureTargetted(); return mActor.getClass().getCreatureStats (mActor).getCreatureTargetted();
case SelectWrapper::Function_Werewolf: case SelectWrapper::Function_Werewolf:

View file

@ -3,6 +3,8 @@
#include <stdexcept> #include <stdexcept>
#include <components/esm/journalentry.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
@ -10,23 +12,55 @@
namespace MWDialogue namespace MWDialogue
{ {
JournalEntry::JournalEntry() {} Entry::Entry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId) Entry::Entry (const std::string& topic, const std::string& infoId)
: mTopic (topic), mInfoId (infoId) : mInfoId (infoId)
{}
std::string JournalEntry::getText (const MWWorld::ESMStore& store) const
{ {
const ESM::Dialogue *dialogue = const ESM::Dialogue *dialogue =
store.get<ESM::Dialogue>().find (mTopic); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic);
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin()); for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == mInfoId) if (iter->mId == mInfoId)
return iter->mResponse; {
/// \todo text replacement
mText = iter->mResponse;
return;
}
throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + mTopic); throw std::runtime_error ("unknown info ID " + mInfoId + " for topic " + topic);
}
Entry::Entry (const ESM::JournalEntry& record) : mInfoId (record.mInfo), mText (record.mText), mActorName(record.mActorName) {}
std::string Entry::getText() const
{
return mText;
}
void Entry::write (ESM::JournalEntry& entry) const
{
entry.mInfo = mInfoId;
entry.mText = mText;
entry.mActorName = mActorName;
}
JournalEntry::JournalEntry() {}
JournalEntry::JournalEntry (const std::string& topic, const std::string& infoId)
: Entry (topic, infoId), mTopic (topic)
{}
JournalEntry::JournalEntry (const ESM::JournalEntry& record)
: Entry (record), mTopic (record.mTopic)
{}
void JournalEntry::write (ESM::JournalEntry& entry) const
{
Entry::write (entry);
entry.mTopic = mTopic;
} }
JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index) JournalEntry JournalEntry::makeFromQuest (const std::string& topic, int index)
@ -49,6 +83,7 @@ namespace MWDialogue
throw std::runtime_error ("unknown journal index for topic " + topic); throw std::runtime_error ("unknown journal index for topic " + topic);
} }
StampedJournalEntry::StampedJournalEntry() StampedJournalEntry::StampedJournalEntry()
: mDay (0), mMonth (0), mDayOfMonth (0) : mDay (0), mMonth (0), mDayOfMonth (0)
{} {}
@ -58,11 +93,24 @@ namespace MWDialogue
: JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth) : JournalEntry (topic, infoId), mDay (day), mMonth (month), mDayOfMonth (dayOfMonth)
{} {}
StampedJournalEntry::StampedJournalEntry (const ESM::JournalEntry& record)
: JournalEntry (record), mDay (record.mDay), mMonth (record.mMonth),
mDayOfMonth (record.mDayOfMonth)
{}
void StampedJournalEntry::write (ESM::JournalEntry& entry) const
{
JournalEntry::write (entry);
entry.mDay = mDay;
entry.mMonth = mMonth;
entry.mDayOfMonth = mDayOfMonth;
}
StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index) StampedJournalEntry StampedJournalEntry::makeFromQuest (const std::string& topic, int index)
{ {
int day = MWBase::Environment::get().getWorld()->getGlobalVariable ("dayspassed").mLong; int day = MWBase::Environment::get().getWorld()->getGlobalInt ("dayspassed");
int month = MWBase::Environment::get().getWorld()->getGlobalVariable ("month").mLong; int month = MWBase::Environment::get().getWorld()->getGlobalInt ("month");
int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalVariable ("day").mLong; int dayOfMonth = MWBase::Environment::get().getWorld()->getGlobalInt ("day");
return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth); return StampedJournalEntry (topic, idFromIndex (topic, index), day, month, dayOfMonth);
} }

View file

@ -3,24 +3,45 @@
#include <string> #include <string>
namespace MWWorld namespace ESM
{ {
struct ESMStore; struct JournalEntry;
} }
namespace MWDialogue namespace MWDialogue
{ {
/// \brief A quest or dialogue entry /// \brief Basic quest/dialogue/topic entry
struct JournalEntry struct Entry
{
std::string mInfoId;
std::string mText;
std::string mActorName; // optional
Entry();
Entry (const std::string& topic, const std::string& infoId);
Entry (const ESM::JournalEntry& record);
std::string getText() const;
void write (ESM::JournalEntry& entry) const;
};
/// \brief A dialogue entry
///
/// Same as entry, but store TopicID
struct JournalEntry : public Entry
{ {
std::string mTopic; std::string mTopic;
std::string mInfoId;
JournalEntry(); JournalEntry();
JournalEntry (const std::string& topic, const std::string& infoId); JournalEntry (const std::string& topic, const std::string& infoId);
std::string getText (const MWWorld::ESMStore& store) const; JournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static JournalEntry makeFromQuest (const std::string& topic, int index); static JournalEntry makeFromQuest (const std::string& topic, int index);
@ -39,6 +60,10 @@ namespace MWDialogue
StampedJournalEntry (const std::string& topic, const std::string& infoId, StampedJournalEntry (const std::string& topic, const std::string& infoId,
int day, int month, int dayOfMonth); int day, int month, int dayOfMonth);
StampedJournalEntry (const ESM::JournalEntry& record);
void write (ESM::JournalEntry& entry) const;
static StampedJournalEntry makeFromQuest (const std::string& topic, int index); static StampedJournalEntry makeFromQuest (const std::string& topic, int index);
}; };
} }

View file

@ -1,6 +1,13 @@
#include "journalimp.hpp" #include "journalimp.hpp"
#include <iterator>
#include <components/esm/esmwriter.hpp>
#include <components/esm/esmreader.hpp>
#include <components/esm/queststate.hpp>
#include <components/esm/journalentry.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -26,6 +33,38 @@ namespace MWDialogue
return iter->second; return iter->second;
} }
Topic& Journal::getTopic (const std::string& id)
{
TTopicContainer::iterator iter = mTopics.find (id);
if (iter==mTopics.end())
{
std::pair<TTopicContainer::iterator, bool> result
= mTopics.insert (std::make_pair (id, Topic (id)));
iter = result.first;
}
return iter->second;
}
bool Journal::isThere (const std::string& topicId, const std::string& infoId) const
{
if (const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().search (topicId))
{
if (infoId.empty())
return true;
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter)
if (iter->mId == infoId)
return true;
}
return false;
}
Journal::Journal() Journal::Journal()
{} {}
@ -64,19 +103,13 @@ namespace MWDialogue
quest.setIndex (index); quest.setIndex (index);
} }
void Journal::addTopic (const std::string& topicId, const std::string& infoId) void Journal::addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName)
{ {
TTopicContainer::iterator iter = mTopics.find (topicId); Topic& topic = getTopic (topicId);
if (iter==mTopics.end()) JournalEntry entry(topicId, infoId);
{ entry.mActorName = actorName;
std::pair<TTopicContainer::iterator, bool> result topic.addEntry (entry);
= mTopics.insert (std::make_pair (topicId, Topic (topicId)));
iter = result.first;
}
iter->second.addEntry (JournalEntry (topicId, infoId));
} }
int Journal::getJournalIndex (const std::string& id) const int Journal::getJournalIndex (const std::string& id) const
@ -118,4 +151,106 @@ namespace MWDialogue
{ {
return mTopics.end(); return mTopics.end();
} }
int Journal::countSavedGameRecords() const
{
int count = static_cast<int> (mQuests.size());
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
count += std::distance (mJournal.begin(), mJournal.end());
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
count += std::distance (iter->second.begin(), iter->second.end());
return count;
}
void Journal::write (ESM::ESMWriter& writer) const
{
for (TQuestIter iter (mQuests.begin()); iter!=mQuests.end(); ++iter)
{
const Quest& quest = iter->second;
ESM::QuestState state;
quest.write (state);
writer.startRecord (ESM::REC_QUES);
state.save (writer);
writer.endRecord (ESM::REC_QUES);
for (Topic::TEntryIter iter (quest.begin()); iter!=quest.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Quest;
entry.mTopic = quest.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
for (TEntryIter iter (mJournal.begin()); iter!=mJournal.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Journal;
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
for (TTopicIter iter (mTopics.begin()); iter!=mTopics.end(); ++iter)
{
const Topic& topic = iter->second;
for (Topic::TEntryIter iter (topic.begin()); iter!=topic.end(); ++iter)
{
ESM::JournalEntry entry;
entry.mType = ESM::JournalEntry::Type_Topic;
entry.mTopic = topic.getTopic();
iter->write (entry);
writer.startRecord (ESM::REC_JOUR);
entry.save (writer);
writer.endRecord (ESM::REC_JOUR);
}
}
}
void Journal::readRecord (ESM::ESMReader& reader, int32_t type)
{
if (type==ESM::REC_JOUR)
{
ESM::JournalEntry record;
record.load (reader);
if (isThere (record.mTopic, record.mInfo))
switch (record.mType)
{
case ESM::JournalEntry::Type_Quest:
getQuest (record.mTopic).insertEntry (record);
break;
case ESM::JournalEntry::Type_Journal:
mJournal.push_back (record);
break;
case ESM::JournalEntry::Type_Topic:
getTopic (record.mTopic).insertEntry (record);
break;
}
}
else if (type==ESM::REC_QUES)
{
ESM::QuestState record;
record.load (reader);
if (isThere (record.mTopic))
mQuests.insert (std::make_pair (record.mTopic, record));
}
}
} }

View file

@ -15,8 +15,14 @@ namespace MWDialogue
TQuestContainer mQuests; TQuestContainer mQuests;
TTopicContainer mTopics; TTopicContainer mTopics;
private:
Quest& getQuest (const std::string& id); Quest& getQuest (const std::string& id);
Topic& getTopic (const std::string& id);
bool isThere (const std::string& topicId, const std::string& infoId = "") const;
public: public:
Journal(); Journal();
@ -32,7 +38,7 @@ 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); virtual void addTopic (const std::string& topicId, const std::string& infoId, const std::string& actorName);
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.
@ -55,6 +61,12 @@ namespace MWDialogue
virtual TTopicIter topicEnd() const; virtual TTopicIter topicEnd() const;
///< Iterator pointing past the last topic. ///< Iterator pointing past the last topic.
virtual int countSavedGameRecords() const;
virtual void write (ESM::ESMWriter& writer) const;
virtual void readRecord (ESM::ESMReader& reader, int32_t type);
}; };
} }

View file

@ -1,6 +1,8 @@
#include "quest.hpp" #include "quest.hpp"
#include <components/esm/queststate.hpp>
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -16,7 +18,11 @@ namespace MWDialogue
: Topic (topic), mIndex (0), mFinished (false) : Topic (topic), mIndex (0), mFinished (false)
{} {}
const std::string Quest::getName() const Quest::Quest (const ESM::QuestState& state)
: Topic (state.mTopic), mIndex (state.mState), mFinished (state.mFinished!=0)
{}
std::string Quest::getName() const
{ {
const ESM::Dialogue *dialogue = const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);
@ -39,21 +45,24 @@ namespace MWDialogue
const ESM::Dialogue *dialogue = const ESM::Dialogue *dialogue =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic); MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (mTopic);
bool found=false;
for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin()); for (std::vector<ESM::DialInfo>::const_iterator iter (dialogue->mInfo.begin());
iter!=dialogue->mInfo.end(); ++iter) iter!=dialogue->mInfo.end(); ++iter)
if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name) if (iter->mData.mDisposition==index && iter->mQuestStatus!=ESM::DialInfo::QS_Name)
{ {
mIndex = index;
if (iter->mQuestStatus==ESM::DialInfo::QS_Finished) if (iter->mQuestStatus==ESM::DialInfo::QS_Finished)
mFinished = true; mFinished = true;
else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart) else if (iter->mQuestStatus==ESM::DialInfo::QS_Restart)
mFinished = false; mFinished = false;
return; found = true;
// Don't return here. Quest status may actually be in a different info record, since we don't merge these (yet?)
} }
throw std::runtime_error ("unknown journal index for topic " + mTopic); if (found)
mIndex = index;
else
throw std::runtime_error ("unknown journal index for topic " + mTopic);
} }
bool Quest::isFinished() const bool Quest::isFinished() const
@ -79,12 +88,20 @@ namespace MWDialogue
if (index==-1) if (index==-1)
throw std::runtime_error ("unknown journal entry for topic " + mTopic); throw std::runtime_error ("unknown journal entry for topic " + mTopic);
setIndex (index); if (index > mIndex)
setIndex (index);
for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter) for (TEntryIter iter (mEntries.begin()); iter!=mEntries.end(); ++iter)
if (*iter==entry.mInfoId) if (iter->mInfoId==entry.mInfoId)
return; return;
mEntries.push_back (entry.mInfoId); mEntries.push_back (entry); // we want slicing here
}
void Quest::write (ESM::QuestState& state) const
{
state.mTopic = getTopic();
state.mState = mIndex;
state.mFinished = mFinished;
} }
} }

View file

@ -3,9 +3,14 @@
#include "topic.hpp" #include "topic.hpp"
namespace ESM
{
struct QuestState;
}
namespace MWDialogue namespace MWDialogue
{ {
/// \brief A quest in progress or a compelted quest /// \brief A quest in progress or a completed quest
class Quest : public Topic class Quest : public Topic
{ {
int mIndex; int mIndex;
@ -17,13 +22,15 @@ namespace MWDialogue
Quest (const std::string& topic); Quest (const std::string& topic);
const std::string getName() const; Quest (const ESM::QuestState& state);
virtual std::string getName() const;
///< May be an empty string ///< May be an empty string
int getIndex() const; int getIndex() const;
void setIndex (int index); void setIndex (int index);
///< Calling this function with a non-existant index while throw an exception. ///< Calling this function with a non-existent index will throw an exception.
bool isFinished() const; bool isFinished() const;
@ -31,6 +38,8 @@ namespace MWDialogue
///< Add entry and adjust index accordingly. ///< Add entry and adjust index accordingly.
/// ///
/// \note Redundant entries are ignored, but the index is still adjusted. /// \note Redundant entries are ignored, but the index is still adjusted.
void write (ESM::QuestState& state) const;
}; };
} }

View file

@ -1,6 +1,9 @@
#include "topic.hpp" #include "topic.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp" #include "../mwworld/esmstore.hpp"
namespace MWDialogue namespace MWDialogue
@ -9,7 +12,8 @@ namespace MWDialogue
{} {}
Topic::Topic (const std::string& topic) Topic::Topic (const std::string& topic)
: mTopic (topic) : mTopic (topic), mName (
MWBase::Environment::get().getWorld()->getStore().get<ESM::Dialogue>().find (topic)->mId)
{} {}
Topic::~Topic() Topic::~Topic()
@ -20,11 +24,29 @@ namespace MWDialogue
if (entry.mTopic!=mTopic) if (entry.mTopic!=mTopic)
throw std::runtime_error ("topic does not match: " + mTopic); throw std::runtime_error ("topic does not match: " + mTopic);
for (TEntryIter iter = begin(); iter!=end(); ++iter) // bail out if we already have heard this
if (*iter==entry.mInfoId) for (Topic::TEntryIter it = mEntries.begin(); it != mEntries.end(); ++it)
{
if (it->mInfoId == entry.mInfoId)
return; return;
}
mEntries.push_back (entry.mInfoId); mEntries.push_back (entry); // we want slicing here
}
void Topic::insertEntry (const ESM::JournalEntry& entry)
{
mEntries.push_back (entry);
}
std::string Topic::getTopic() const
{
return mTopic;
}
std::string Topic::getName() const
{
return mName;
} }
Topic::TEntryIter Topic::begin() const Topic::TEntryIter Topic::begin() const

View file

@ -6,6 +6,11 @@
#include "journalentry.hpp" #include "journalentry.hpp"
namespace ESM
{
struct JournalEntry;
}
namespace MWDialogue namespace MWDialogue
{ {
/// \brief Collection of seen responses for a topic /// \brief Collection of seen responses for a topic
@ -13,13 +18,14 @@ namespace MWDialogue
{ {
public: public:
typedef std::vector<std::string> TEntryContainer; typedef std::vector<Entry> TEntryContainer;
typedef TEntryContainer::const_iterator TEntryIter; typedef TEntryContainer::const_iterator TEntryIter;
protected: protected:
std::string mTopic; std::string mTopic;
TEntryContainer mEntries; // info-IDs std::string mName;
TEntryContainer mEntries;
public: public:
@ -34,7 +40,13 @@ namespace MWDialogue
/// ///
/// \note Redundant entries are ignored. /// \note Redundant entries are ignored.
std::string const & getName () const { return mTopic; } void insertEntry (const ESM::JournalEntry& entry);
///< Add entry without checking for redundant entries or modifying the state of the
/// topic otherwise
std::string getTopic() const;
virtual std::string getName() const;
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.

View file

@ -1,6 +1,5 @@
#include "birth.hpp" #include "birth.hpp"
#include <boost/algorithm/string.hpp>
#include <boost/lexical_cast.hpp> #include <boost/lexical_cast.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
@ -34,8 +33,7 @@ namespace MWGui
getWidget(mBirthList, "BirthsignList"); getWidget(mBirthList, "BirthsignList");
mBirthList->setScrollVisible(true); mBirthList->setScrollVisible(true);
mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); mBirthList->eventListSelectAccept += MyGUI::newDelegate(this, &BirthDialog::onAccept);
mBirthList->eventListMouseItemActivate += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth); mBirthList->eventListChangePosition += MyGUI::newDelegate(this, &BirthDialog::onSelectBirth);
MyGUI::Button* backButton; MyGUI::Button* backButton;
@ -77,7 +75,7 @@ namespace MWGui
size_t count = mBirthList->getItemCount(); size_t count = mBirthList->getItemCount();
for (size_t i = 0; i < count; ++i) for (size_t i = 0; i < count; ++i)
{ {
if (boost::iequals(*mBirthList->getItemDataAt<std::string>(i), birthId)) if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt<std::string>(i), birthId))
{ {
mBirthList->setIndexSelected(i); mBirthList->setIndexSelected(i);
MyGUI::Button* okButton; MyGUI::Button* okButton;
@ -98,6 +96,14 @@ namespace MWGui
eventDone(this); eventDone(this);
} }
void BirthDialog::onAccept(MyGUI::ListBox *_sender, size_t _index)
{
onSelectBirth(_sender, _index);
if(mBirthList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void BirthDialog::onBackClicked(MyGUI::Widget* _sender) void BirthDialog::onBackClicked(MyGUI::Widget* _sender)
{ {
eventBack(); eventBack();
@ -112,7 +118,7 @@ namespace MWGui
getWidget(okButton, "OKButton"); getWidget(okButton, "OKButton");
const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index); const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);
if (boost::iequals(mCurrentBirthId, *birthId)) if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId))
return; return;
mCurrentBirthId = *birthId; mCurrentBirthId = *birthId;
@ -148,7 +154,7 @@ namespace MWGui
mBirthList->setIndexSelected(index); mBirthList->setIndexSelected(index);
mCurrentBirthId = it2->first; mCurrentBirthId = it2->first;
} }
else if (boost::iequals(it2->first, mCurrentBirthId)) else if (Misc::StringUtils::ciEqual(it2->first, mCurrentBirthId))
{ {
mBirthList->setIndexSelected(index); mBirthList->setIndexSelected(index);
} }

View file

@ -38,6 +38,7 @@ namespace MWGui
protected: protected:
void onSelectBirth(MyGUI::ListBox* _sender, size_t _index); void onSelectBirth(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t index);
void onOkClicked(MyGUI::Widget* _sender); void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender);

View file

@ -10,7 +10,7 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp" #include "../mwbase/mechanicsmanager.hpp"
#include "../mwmechanics/creaturestats.hpp" #include "../mwmechanics/npcstats.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp" #include "../mwworld/fallback.hpp"
@ -47,9 +47,8 @@ namespace
void updatePlayerHealth() void updatePlayerHealth()
{ {
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr(); MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWMechanics::CreatureStats& creatureStats = MWWorld::Class::get(player).getCreatureStats(player); MWMechanics::NpcStats& npcStats = MWWorld::Class::get(player).getNpcStats(player);
npcStats.updateHealth();
creatureStats.updateHealth();
} }
} }

View file

@ -1,7 +1,5 @@
#include "class.hpp" #include "class.hpp"
#include <boost/algorithm/string.hpp>
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp" #include "../mwbase/windowmanager.hpp"
@ -84,8 +82,7 @@ namespace MWGui
getWidget(mClassList, "ClassList"); getWidget(mClassList, "ClassList");
mClassList->setScrollVisible(true); mClassList->setScrollVisible(true);
mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); mClassList->eventListSelectAccept += MyGUI::newDelegate(this, &PickClassDialog::onAccept);
mClassList->eventListMouseItemActivate += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass); mClassList->eventListChangePosition += MyGUI::newDelegate(this, &PickClassDialog::onSelectClass);
getWidget(mClassImage, "ClassImage"); getWidget(mClassImage, "ClassImage");
@ -128,7 +125,7 @@ namespace MWGui
size_t count = mClassList->getItemCount(); size_t count = mClassList->getItemCount();
for (size_t i = 0; i < count; ++i) for (size_t i = 0; i < count; ++i)
{ {
if (boost::iequals(*mClassList->getItemDataAt<std::string>(i), classId)) if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt<std::string>(i), classId))
{ {
mClassList->setIndexSelected(i); mClassList->setIndexSelected(i);
MyGUI::Button* okButton; MyGUI::Button* okButton;
@ -154,6 +151,14 @@ namespace MWGui
eventBack(); eventBack();
} }
void PickClassDialog::onAccept(MyGUI::ListBox* _sender, size_t _index)
{
onSelectClass(_sender, _index);
if(mClassList->getIndexSelected() == MyGUI::ITEM_NONE)
return;
eventDone(this);
}
void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index) void PickClassDialog::onSelectClass(MyGUI::ListBox* _sender, size_t _index)
{ {
if (_index == MyGUI::ITEM_NONE) if (_index == MyGUI::ITEM_NONE)
@ -163,7 +168,7 @@ namespace MWGui
getWidget(okButton, "OKButton"); getWidget(okButton, "OKButton");
const std::string *classId = mClassList->getItemDataAt<std::string>(_index); const std::string *classId = mClassList->getItemDataAt<std::string>(_index);
if (boost::iequals(mCurrentClassId, *classId)) if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId))
return; return;
mCurrentClassId = *classId; mCurrentClassId = *classId;
@ -193,7 +198,7 @@ namespace MWGui
mCurrentClassId = id; mCurrentClassId = id;
mClassList->setIndexSelected(index); mClassList->setIndexSelected(index);
} }
else if (boost::iequals(id, mCurrentClassId)) else if (Misc::StringUtils::ciEqual(id, mCurrentClassId))
{ {
mClassList->setIndexSelected(index); mClassList->setIndexSelected(index);
} }

View file

@ -111,6 +111,7 @@ namespace MWGui
protected: protected:
void onSelectClass(MyGUI::ListBox* _sender, size_t _index); void onSelectClass(MyGUI::ListBox* _sender, size_t _index);
void onAccept(MyGUI::ListBox* _sender, size_t _index);
void onOkClicked(MyGUI::Widget* _sender); void onOkClicked(MyGUI::Widget* _sender);
void onBackClicked(MyGUI::Widget* _sender); void onBackClicked(MyGUI::Widget* _sender);

View file

@ -12,7 +12,7 @@ namespace MWGui
void CompanionItemModel::copyItem (const ItemStack& item, size_t count) void CompanionItemModel::copyItem (const ItemStack& item, size_t count)
{ {
if (mActor.getTypeName() == typeid(ESM::NPC).name()) if (mActor.getClass().isNpc())
{ {
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); stats.modifyProfit(MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);
@ -23,7 +23,7 @@ namespace MWGui
void CompanionItemModel::removeItem (const ItemStack& item, size_t count) void CompanionItemModel::removeItem (const ItemStack& item, size_t count)
{ {
if (mActor.getTypeName() == typeid(ESM::NPC).name()) if (mActor.getClass().isNpc())
{ {
MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor); MWMechanics::NpcStats& stats = MWWorld::Class::get(mActor).getNpcStats(mActor);
stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count); stats.modifyProfit(-MWWorld::Class::get(item.mBase).getValue(item.mBase) * count);

View file

@ -235,7 +235,7 @@ namespace MWGui
mItemView->setModel (mSortModel); mItemView->setModel (mSortModel);
MyGUI::InputManager::getInstance().setKeyFocusWidget(mCloseButton); MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mCloseButton);
// Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last // Careful here. setTitle may cause size updates, causing itemview redraw, so make sure to do it last
// or we end up using a possibly invalid model. // or we end up using a possibly invalid model.

View file

@ -545,7 +545,7 @@ namespace MWGui
for (size_t i=0; i<mTopicsList->getItemCount(); ++i) for (size_t i=0; i<mTopicsList->getItemCount(); ++i)
{ {
std::string item = mTopicsList->getItemNameAt(i); std::string item = mTopicsList->getItemNameAt(i);
if (Misc::StringUtils::lowerCase(item) == title) if (Misc::StringUtils::ciEqual(item, title))
{ {
realTitle = item; realTitle = item;
break; break;

View file

@ -5,6 +5,8 @@
#include "../mwbase/environment.hpp" #include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp" #include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp" #include "../mwbase/soundmanager.hpp"
#include "../mwbase/dialoguemanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp" #include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp" #include "../mwworld/containerstore.hpp"
@ -296,6 +298,28 @@ namespace MWGui
return; return;
} }
// check if the player is attempting to use a soulstone or item that was stolen from this actor
if (mPtr != player)
{
for (int i=0; i<2; ++i)
{
MWWorld::Ptr item = (i == 0) ? mEnchanting.getOldItem() : mEnchanting.getGem();
if (Misc::StringUtils::ciEqual(item.getCellRef().mOwner, mPtr.getCellRef().mRefID))
{
std::string msg = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sNotifyMessage49")->getString();
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item));
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Dialogue);
return;
}
}
}
int result = mEnchanting.create(); int result = mEnchanting.create();
if(result==1) if(result==1)

View file

@ -12,6 +12,8 @@
#include <boost/range/algorithm/copy.hpp> #include <boost/range/algorithm/copy.hpp>
#include <OgreUTFString.h> #include <OgreUTFString.h>
#include <OgreResourceGroupManager.h>
namespace namespace
{ {
int convertFromHex(std::string hex) int convertFromHex(std::string hex)
@ -288,6 +290,16 @@ namespace MWGui
MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox", MyGUI::ImageBox* box = mParent->createWidget<MyGUI::ImageBox> ("ImageBox",
MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top, MyGUI::IntCoord(0, mHeight, width, height), MyGUI::Align::Left | MyGUI::Align::Top,
mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount())); mParent->getName() + boost::lexical_cast<std::string>(mParent->getChildCount()));
// Apparently a bug with some morrowind versions, they reference the image without the size suffix.
// So if the image isn't found, try appending the size.
if (!Ogre::ResourceGroupManager::getSingleton().resourceExistsInAnyGroup("bookart\\"+image))
{
std::stringstream str;
str << image.substr(0, image.rfind(".")) << "_" << width << "_" << height << image.substr(image.rfind("."));
image = str.str();
}
box->setImageTexture("bookart\\" + image); box->setImageTexture("bookart\\" + image);
box->setProperty("NeedMouse", "false"); box->setProperty("NeedMouse", "false");
} }

View file

@ -267,7 +267,8 @@ namespace MWGui
else if ((mode == GM_Container) || (mode == GM_Inventory)) else if ((mode == GM_Container) || (mode == GM_Inventory))
{ {
// pick up object // pick up object
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object); if (!object.isEmpty())
MWBase::Environment::get().getWindowManager()->getInventoryWindow()->pickUpObject(object);
} }
} }
} }

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