1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-29 08:45:34 +00:00

Merge branch 'master' into pathgrid-edit

This commit is contained in:
cc9cii 2015-03-01 16:05:35 +11:00
commit 6d5899361e
94 changed files with 1455 additions and 769 deletions

View file

@ -31,7 +31,7 @@ before_script:
script: script:
- cd ./build - cd ./build
- if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j4; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ]; then make -j4; fi
- if [ "$COVERITY_SCAN_BRANCH" != 1 ] && ["${TRAVIS_OS_NAME}" = "osx"]; then make package; fi - if [ "$COVERITY_SCAN_BRANCH" != 1 ] && [ "${TRAVIS_OS_NAME}" = "osx" ]; then make package; fi
after_script: after_script:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi - if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
notifications: notifications:

View file

@ -39,6 +39,7 @@ Programmers
Eli2 Eli2
Emanuel Guével (potatoesmaster) Emanuel Guével (potatoesmaster)
eroen eroen
Evgeniy Mineev (sandstranger)
Fil Krynicki (filkry) Fil Krynicki (filkry)
Gašper Sedej Gašper Sedej
gugus/gus gugus/gus
@ -91,7 +92,6 @@ Programmers
Rohit Nirmal Rohit Nirmal
Roman Melnik (Kromgart) Roman Melnik (Kromgart)
Roman Proskuryakov (humbug) Roman Proskuryakov (humbug)
sandstranger
Sandy Carter (bwrsandman) Sandy Carter (bwrsandman)
Scott Howard Scott Howard
Sebastian Wick (swick) Sebastian Wick (swick)

View file

@ -672,6 +672,7 @@ if (WIN32)
4193 # #pragma warning(pop) : no matching '#pragma warning(push)' 4193 # #pragma warning(pop) : no matching '#pragma warning(push)'
4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY' 4251 # class 'XXXX' needs to have dll-interface to be used by clients of class 'YYYY'
4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY' 4275 # non dll-interface struct 'XXXX' used as base for dll-interface class 'YYYY'
4315 # undocumented, 'this' pointer for member might not be aligned (OgreMemoryStlAllocator.h)
# caused by boost # caused by boost
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off) 4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
@ -679,6 +680,7 @@ if (WIN32)
# OpenMW specific warnings # OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type 4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter) 4100 # Unreferenced formal parameter (-Wunused-parameter)
4101 # Unreferenced local variable (-Wunused-variable)
4127 # Conditional expression is constant 4127 # Conditional expression is constant
4242 # Storing value in a variable of a smaller type, possible loss of data 4242 # Storing value in a variable of a smaller type, possible loss of data
4244 # Storing value of one type in variable of another (size_t in int, for example) 4244 # Storing value of one type in variable of another (size_t in int, for example)
@ -700,14 +702,16 @@ if (WIN32)
# boost::wave has a few issues with signed / unsigned conversions, so we suppress those here # boost::wave has a few issues with signed / unsigned conversions, so we suppress those here
set(SHINY_WARNINGS "${WARNINGS} /wd4245") set(SHINY_WARNINGS "${WARNINGS} /wd4245")
set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}") set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}")
# there's an unreferenced local variable in the ogre platform, suppress it set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101")
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS "${SHINY_OGRE_WARNINGS} ${MT_BUILD}")
set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(sdl4ogre PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
# oics uses tinyxml, which has an initialized but unused variable # oics uses tinyxml, which has an initialized but unused variable
set(OICS_WARNINGS "${WARNINGS} /wd4189") set(OICS_WARNINGS "${WARNINGS} /wd4189")
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_WARNINGS} ${MT_BUILD}") set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_WARNINGS} ${MT_BUILD}")
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(ogre-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
if (BUILD_MYGUI_PLUGIN)
set_target_properties(Plugin_MyGUI_OpenMW_Resources PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_MYGUI_PLUGIN)
if (BUILD_LAUNCHER) if (BUILD_LAUNCHER)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_LAUNCHER) endif (BUILD_LAUNCHER)
@ -726,6 +730,9 @@ if (WIN32)
set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435") set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435")
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS}) set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS})
endif (BUILD_OPENCS) endif (BUILD_OPENCS)
if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_ESSIMPORTER)
if (BUILD_MWINIIMPORTER) if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}") set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_MWINIIMPORTER) endif (BUILD_MWINIIMPORTER)

View file

@ -43,13 +43,17 @@ namespace ESSImport
float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes float mMagicEffects[27]; // Effect attributes: https://wiki.openmw.org/index.php?title=Research:Magic#Effect_attributes
unsigned char mUnknown4[4]; unsigned char mUnknown4[4];
unsigned int mGoldPool; unsigned int mGoldPool;
unsigned char mUnknown5[4]; unsigned char mCountDown; // seen the same value as in ACSC.mCorpseClearCountdown, maybe
// this one is for respawning?
unsigned char mUnknown5[3];
}; };
struct ACSC struct ACSC
{ {
unsigned char mUnknown1[17]; unsigned char mUnknown1[17];
unsigned char mFlags; // ACSCFlags unsigned char mFlags; // ACSCFlags
unsigned char mUnknown2[94]; unsigned char mUnknown2[22];
unsigned char mCorpseClearCountdown; // hours?
unsigned char mUnknown3[71];
}; };
#pragma pack(pop) #pragma pack(pop)

View file

@ -14,10 +14,10 @@ namespace ESSImport
float scale; float scale;
esm.getHNOT(scale, "XSCL"); esm.getHNOT(scale, "XSCL");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))
esm.skipHSub(); mAiPackages.add(esm);
mInventory.load(esm); mInventory.load(esm);
} }

View file

@ -2,6 +2,7 @@
#define OPENMW_ESSIMPORT_CREC_H #define OPENMW_ESSIMPORT_CREC_H
#include "importinventory.hpp" #include "importinventory.hpp"
#include <components/esm/aipackage.hpp>
namespace ESM namespace ESM
{ {
@ -17,6 +18,7 @@ namespace ESSImport
int mIndex; int mIndex;
Inventory mInventory; Inventory mInventory;
ESM::AIPackageList mAiPackages;
void load(ESM::ESMReader& esm); void load(ESM::ESMReader& esm);
}; };

View file

@ -9,10 +9,9 @@ namespace ESSImport
{ {
esm.getHNT(mNPDT, "NPDT"); esm.getHNT(mNPDT, "NPDT");
// FIXME: use AiPackageList, need to fix getSubName()
while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F") while (esm.isNextSub("AI_W") || esm.isNextSub("AI_E") || esm.isNextSub("AI_T") || esm.isNextSub("AI_F")
|| esm.isNextSub("AI_A")) || esm.isNextSub("AI_A"))
esm.skipHSub(); mAiPackages.add(esm);
mInventory.load(esm); mInventory.load(esm);
} }

View file

@ -27,6 +27,7 @@ namespace ESSImport
} mNPDT; } mNPDT;
Inventory mInventory; Inventory mInventory;
ESM::AIPackageList mAiPackages;
void load(ESM::ESMReader &esm); void load(ESM::ESMReader &esm);
}; };

View file

@ -61,6 +61,7 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
QString revision(OPENMW_VERSION_COMMITHASH); QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH); QString tag(OPENMW_VERSION_TAGHASH);
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!revision.isEmpty() && !tag.isEmpty()) if (!revision.isEmpty() && !tag.isEmpty())
{ {
if (revision == tag) { if (revision == tag) {
@ -238,24 +239,8 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
current = previous; current = previous;
int currentIndex = iconWidget->row(current); int currentIndex = iconWidget->row(current);
// int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex); pagesWidget->setCurrentIndex(currentIndex);
mSettingsPage->resetProgressBar();
// DataFilesPage *previousPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(previousIndex));
// DataFilesPage *currentPage = dynamic_cast<DataFilesPage *>(pagesWidget->widget(currentIndex));
// //special call to update/save data files page list view when it's displayed/hidden.
// if (previousPage)
// {
// if (previousPage->objectName() == "DataFilesPage")
// previousPage->saveSettings();
// }
// else if (currentPage)
// {
// if (currentPage->objectName() == "DataFilesPage")
// currentPage->loadSettings();
// }
} }
bool Launcher::MainDialog::setupLauncherSettings() bool Launcher::MainDialog::setupLauncherSettings()

View file

@ -39,6 +39,7 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
mWizardInvoker = new ProcessInvoker(); mWizardInvoker = new ProcessInvoker();
mImporterInvoker = new ProcessInvoker(); mImporterInvoker = new ProcessInvoker();
resetProgressBar();
connect(mWizardInvoker->getProcess(), SIGNAL(started()), connect(mWizardInvoker->getProcess(), SIGNAL(started()),
this, SLOT(wizardStarted())); this, SLOT(wizardStarted()));
@ -94,7 +95,7 @@ Launcher::SettingsPage::~SettingsPage()
void Launcher::SettingsPage::on_wizardButton_clicked() void Launcher::SettingsPage::on_wizardButton_clicked()
{ {
saveSettings(); mMain->writeSettings();
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false)) if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false))
return; return;
@ -102,7 +103,7 @@ void Launcher::SettingsPage::on_wizardButton_clicked()
void Launcher::SettingsPage::on_importerButton_clicked() void Launcher::SettingsPage::on_importerButton_clicked()
{ {
saveSettings(); mMain->writeSettings();
// Create the file if it doesn't already exist, else the importer will fail // Create the file if it doesn't already exist, else the importer will fail
QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str())); QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
@ -141,8 +142,13 @@ void Launcher::SettingsPage::on_importerButton_clicked()
qDebug() << "arguments " << arguments; qDebug() << "arguments " << arguments;
// start the progress bar as a "bouncing ball"
progressBar->setMaximum(0);
progressBar->setValue(0);
if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false)) if (!mImporterInvoker->startProcess(QLatin1String("openmw-iniimporter"), arguments, false))
return; {
resetProgressBar();
}
} }
void Launcher::SettingsPage::on_browseButton_clicked() void Launcher::SettingsPage::on_browseButton_clicked()
@ -197,38 +203,35 @@ void Launcher::SettingsPage::importerStarted()
void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus) void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
{ {
if (exitCode != 0 || exitStatus == QProcess::CrashExit) if (exitCode != 0 || exitStatus == QProcess::CrashExit)
return;
// Importer may have changed settings, so refresh
mMain->reloadSettings();
// Import selected data files from openmw.cfg
if (addonsCheckBox->isChecked())
{ {
// Because we've reloaded settings, the current content list matches content in OpenMW.cfg resetProgressBar();
QString oldContentListName = mLauncherSettings.getCurrentContentListName();
if (mProfileDialog->exec() == QDialog::Accepted)
{
// remove the current content list to prevent duplication
//... except, not allowed to delete the Default content list
if (oldContentListName.compare(DataFilesPage::mDefaultContentListName) != 0)
{
mLauncherSettings.removeContentList(oldContentListName);
}
const QString newContentListName(mProfileDialog->lineEdit()->text()); QMessageBox msgBox;
const QStringList files(mGameSettings.getContentList()); msgBox.setWindowTitle(tr("Importer finished"));
mLauncherSettings.setCurrentContentListName(newContentListName); msgBox.setStandardButtons(QMessageBox::Ok);
mLauncherSettings.setContentList(newContentListName, files); msgBox.setIcon(QMessageBox::Warning);
msgBox.setText(tr("Failed to import settings from INI file."));
msgBox.exec();
}
else
{
// indicate progress finished
progressBar->setMaximum(1);
progressBar->setValue(1);
// Make DataFiles Page load the new content list. // Importer may have changed settings, so refresh
mMain->reloadSettings(); mMain->reloadSettings();
}
} }
importerButton->setEnabled(true); importerButton->setEnabled(true);
} }
void Launcher::SettingsPage::resetProgressBar()
{
// set progress bar to 0 %
progressBar->reset();
}
void Launcher::SettingsPage::updateOkButton(const QString &text) void Launcher::SettingsPage::updateOkButton(const QString &text)
{ {
// We do this here because we need to access the profiles // We do this here because we need to access the profiles

View file

@ -29,6 +29,9 @@ namespace Launcher
void saveSettings(); void saveSettings();
bool loadSettings(); bool loadSettings();
/// set progress bar on page to 0%
void resetProgressBar();
private slots: private slots:
@ -57,7 +60,6 @@ namespace Launcher
MainDialog *mMain; MainDialog *mMain;
TextInputDialog *mProfileDialog; TextInputDialog *mProfileDialog;
}; };
} }

View file

@ -8,7 +8,8 @@
#include <sstream> #include <sstream>
#include <components/misc/stringops.hpp> #include <components/misc/stringops.hpp>
#include <boost/filesystem/path.hpp> #include <boost/version.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp> #include <boost/filesystem/fstream.hpp>
namespace bfs = boost::filesystem; namespace bfs = boost::filesystem;
@ -660,7 +661,7 @@ std::string MwIniImporter::numberToString(int n) {
return str.str(); return str.str();
} }
MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filename) const { MwIniImporter::multistrmap MwIniImporter::loadIniFile(const boost::filesystem::path& filename) const {
std::cout << "load ini file: " << filename << std::endl; std::cout << "load ini file: " << filename << std::endl;
std::string section(""); std::string section("");
@ -719,7 +720,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam
return map; return map;
} }
MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const std::string& filename) { MwIniImporter::multistrmap MwIniImporter::loadCfgFile(const boost::filesystem::path& filename) {
std::cout << "load cfg file: " << filename << std::endl; std::cout << "load cfg file: " << filename << std::endl;
MwIniImporter::multistrmap map; MwIniImporter::multistrmap map;
@ -825,10 +826,14 @@ void MwIniImporter::importArchives(multistrmap &cfg, const multistrmap &ini) con
} }
} }
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) const { void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const {
std::vector<std::string> contentFiles; std::vector<std::pair<std::time_t, std::string> > contentFiles;
std::string baseGameFile("Game Files:GameFile"); std::string baseGameFile("Game Files:GameFile");
std::string gameFile(""); std::string gameFile("");
std::time_t defaultTime = 0;
// assume the Game Files are all in a "Data Files" directory under the directory holding Morrowind.ini
const boost::filesystem::path gameFilesDir(iniFilename.parent_path() /= "Data Files");
multistrmap::const_iterator it = ini.begin(); multistrmap::const_iterator it = ini.begin();
for(int i=0; it != ini.end(); i++) { for(int i=0; it != ini.end(); i++) {
@ -845,18 +850,20 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini) co
Misc::StringUtils::toLower(filetype); Misc::StringUtils::toLower(filetype);
if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) { if(filetype.compare("esm") == 0 || filetype.compare("esp") == 0) {
contentFiles.push_back(*entry); boost::filesystem::path filepath(gameFilesDir);
filepath /= *entry;
contentFiles.push_back(std::make_pair(lastWriteTime(filepath, defaultTime), *entry));
} }
} }
gameFile = "";
} }
cfg.erase("content"); cfg.erase("content");
cfg.insert( std::make_pair("content", std::vector<std::string>() ) ); cfg.insert( std::make_pair("content", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) { // this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
cfg["content"].push_back(*it); sort(contentFiles.begin(), contentFiles.end());
for(std::vector<std::pair<std::time_t, std::string> >::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["content"].push_back(it->second);
} }
} }
@ -873,3 +880,27 @@ void MwIniImporter::setInputEncoding(const ToUTF8::FromType &encoding)
{ {
mEncoding = encoding; mEncoding = encoding;
} }
std::time_t MwIniImporter::lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime)
{
std::time_t writeTime(defaultTime);
if (boost::filesystem::exists(filename))
{
// FixMe: remove #if when Boost dependency for Linux builds updated
// This allows Linux to build until then
#if (BOOST_VERSION >= 104800)
// need to resolve any symlinks so that we get time of file, not symlink
boost::filesystem::path resolved = boost::filesystem::canonical(filename);
#else
boost::filesystem::path resolved = filename;
#endif
writeTime = boost::filesystem::last_write_time(resolved);
std::cout << "content file: " << resolved << " timestamp = (" << writeTime <<
") " << asctime(localtime(&writeTime)) << std::endl;
}
else
{
std::cout << "content file: " << filename << " not found" << std::endl;
}
return writeTime;
}

View file

@ -6,6 +6,7 @@
#include <vector> #include <vector>
#include <exception> #include <exception>
#include <iosfwd> #include <iosfwd>
#include <boost/filesystem/path.hpp>
#include <components/to_utf8/to_utf8.hpp> #include <components/to_utf8/to_utf8.hpp>
@ -17,17 +18,22 @@ class MwIniImporter {
MwIniImporter(); MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding); void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose); void setVerbose(bool verbose);
multistrmap loadIniFile(const std::string& filename) const; multistrmap loadIniFile(const boost::filesystem::path& filename) const;
static multistrmap loadCfgFile(const std::string& filename); static multistrmap loadCfgFile(const boost::filesystem::path& filename);
void merge(multistrmap &cfg, const multistrmap &ini) const; void merge(multistrmap &cfg, const multistrmap &ini) const;
void mergeFallback(multistrmap &cfg, const multistrmap &ini) const; void mergeFallback(multistrmap &cfg, const multistrmap &ini) const;
void importGameFiles(multistrmap &cfg, const multistrmap &ini) const; void importGameFiles(multistrmap &cfg, const multistrmap &ini,
const boost::filesystem::path& iniFilename) const;
void importArchives(multistrmap &cfg, const multistrmap &ini) const; void importArchives(multistrmap &cfg, const multistrmap &ini) const;
static void writeToFile(std::ostream &out, const multistrmap &cfg); static void writeToFile(std::ostream &out, const multistrmap &cfg);
private: private:
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value); static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
static std::string numberToString(int n); static std::string numberToString(int n);
/// \return file's "last modified time", used in original MW to determine plug-in load order
static std::time_t lastWriteTime(const boost::filesystem::path& filename, std::time_t defaultTime);
bool mVerbose; bool mVerbose;
strmap mMergeMap; strmap mMergeMap;
std::vector<std::string> mMergeFallback; std::vector<std::string> mMergeFallback;

View file

@ -93,8 +93,8 @@ int wmain(int argc, wchar_t *wargv[]) {
bpo::notify(vm); bpo::notify(vm);
std::string iniFile = vm["ini"].as<std::string>(); boost::filesystem::path iniFile(vm["ini"].as<std::string>());
std::string cfgFile = vm["cfg"].as<std::string>(); boost::filesystem::path cfgFile(vm["cfg"].as<std::string>());
// if no output is given, write back to cfg file // if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>()); std::string outputFile(vm["output"].as<std::string>());
@ -123,7 +123,7 @@ int wmain(int argc, wchar_t *wargv[]) {
importer.mergeFallback(cfg, ini); importer.mergeFallback(cfg, ini);
if(vm.count("game-files")) { if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini); importer.importGameFiles(cfg, ini, iniFile);
} }
if(!vm.count("no-archives")) { if(!vm.count("no-archives")) {

View file

@ -58,9 +58,6 @@ CSMDoc::Operation *CSMTools::Tools::getVerifier()
mandatoryIds.push_back ("GameHour"); mandatoryIds.push_back ("GameHour");
mandatoryIds.push_back ("Month"); mandatoryIds.push_back ("Month");
mandatoryIds.push_back ("PCRace"); mandatoryIds.push_back ("PCRace");
mandatoryIds.push_back ("PCVampire");
mandatoryIds.push_back ("PCWerewolf");
mandatoryIds.push_back ("PCYear");
mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(), mVerifier->appendStage (new MandatoryIdStage (mData.getGlobals(),
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds)); CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Globals), mandatoryIds));

View file

@ -5,6 +5,7 @@
#include <string> #include <string>
#include <boost/filesystem.hpp> #include <boost/filesystem.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <QHBoxLayout> #include <QHBoxLayout>
#include <QLabel> #include <QLabel>
@ -72,8 +73,11 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
{ {
boost::filesystem::path path (name.toUtf8().data()); boost::filesystem::path path (name.toUtf8().data());
bool isLegacyPath = (path.extension() == ".esm" || std::string extension = path.extension().string();
path.extension() == ".esp"); boost::algorithm::to_lower(extension);
bool isLegacyPath = (extension == ".esm" ||
extension == ".esp");
bool isFilePathChanged = (path.parent_path().string() != mLocalData.string()); bool isFilePathChanged = (path.parent_path().string() != mLocalData.string());

View file

@ -96,7 +96,7 @@ QWidget *CSVDoc::StartupDialogue::createTools()
CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2) CSVDoc::StartupDialogue::StartupDialogue() : mWidth (0), mColumn (2)
{ {
setWindowTitle ("Open CS"); setWindowTitle ("OpenMW-CS");
QVBoxLayout *layout = new QVBoxLayout (this); QVBoxLayout *layout = new QVBoxLayout (this);

View file

@ -268,6 +268,8 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0; virtual MWWorld::Ptr getFacedObject() = 0;
///< Return pointer to the object the player is looking at, if it is within activation range ///< Return pointer to the object the player is looking at, if it is within activation range
virtual float getMaxActivationDistance() = 0;
/// 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, or alternatively the "Bip01 Head" node as a basis. /// use the "Head" node, or alternatively the "Bip01 Head" node as a basis.
@ -551,7 +553,7 @@ namespace MWBase
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0; virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName) = 0; const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName) = 0;
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0; virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;

View file

@ -671,4 +671,14 @@ namespace MWGui
mEnemyHealthTimer = -1; mEnemyHealthTimer = -1;
} }
void HUD::customMarkerCreated(MyGUI::Widget *marker)
{
marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);
}
void HUD::doorMarkerCreated(MyGUI::Widget *marker)
{
marker->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onMapClicked);
}
} }

View file

@ -119,6 +119,10 @@ namespace MWGui
void onMagicClicked(MyGUI::Widget* _sender); void onMagicClicked(MyGUI::Widget* _sender);
void onMapClicked(MyGUI::Widget* _sender); void onMapClicked(MyGUI::Widget* _sender);
// LocalMapBase
virtual void customMarkerCreated(MyGUI::Widget* marker);
virtual void doorMarkerCreated(MyGUI::Widget* marker);
void updateEnemyHealthBar(); void updateEnemyHealthBar();
void updatePositions(); void updatePositions();

View file

@ -17,7 +17,7 @@ namespace MWGui
{ {
JailScreen::JailScreen() JailScreen::JailScreen()
: WindowBase("openmw_jail_screen.layout"), : WindowBase("openmw_jail_screen.layout"),
mTimeAdvancer(0.0125), mTimeAdvancer(0.01),
mDays(1), mDays(1),
mFadeTimeRemaining(0) mFadeTimeRemaining(0)
{ {
@ -39,7 +39,7 @@ namespace MWGui
mFadeTimeRemaining = 0.5; mFadeTimeRemaining = 0.5;
setVisible(false); setVisible(false);
mProgressBar->setScrollRange(days*24+1); mProgressBar->setScrollRange(100+1);
mProgressBar->setScrollPosition(0); mProgressBar->setScrollPosition(0);
mProgressBar->setTrackSize(0); mProgressBar->setTrackSize(0);
} }
@ -59,7 +59,7 @@ namespace MWGui
MWBase::Environment::get().getWorld()->teleportToClosestMarker(player, "prisonmarker"); MWBase::Environment::get().getWorld()->teleportToClosestMarker(player, "prisonmarker");
setVisible(true); setVisible(true);
mTimeAdvancer.run(mDays*24); mTimeAdvancer.run(100);
} }
} }

View file

@ -199,7 +199,7 @@ struct JournalViewModelImpl : JournalViewModel
}; };
void visitQuestNames (bool active_only, boost::function <void (const std::string&)> visitor) const void visitQuestNames (bool active_only, boost::function <void (const std::string&, bool)> visitor) const
{ {
MWBase::Journal * journal = MWBase::Environment::get ().getJournal (); MWBase::Journal * journal = MWBase::Environment::get ().getJournal ();
@ -231,7 +231,7 @@ struct JournalViewModelImpl : JournalViewModel
if (visitedQuests.find(quest.getName()) != visitedQuests.end()) if (visitedQuests.find(quest.getName()) != visitedQuests.end())
continue; continue;
visitor (quest.getName()); visitor (quest.getName(), isFinished);
visitedQuests.insert(quest.getName()); visitedQuests.insert(quest.getName());
} }

View file

@ -67,8 +67,8 @@ namespace MWGui
/// returns true if their are no journal entries to display /// returns true if their are no journal entries to display
virtual bool isEmpty () const = 0; virtual bool isEmpty () const = 0;
/// walks the active and optionally completed, quests providing the name /// walks the active and optionally completed, quests providing the name and completed status
virtual void visitQuestNames (bool active_only, boost::function <void (const std::string&)> visitor) const = 0; virtual void visitQuestNames (bool active_only, boost::function <void (const std::string&, bool)> visitor) const = 0;
/// walks over the journal entries related to all quests with the given name /// walks over the journal entries related to all quests with the given name
/// If \a questName is empty, simply visits all journal entries /// If \a questName is empty, simply visits all journal entries

View file

@ -7,6 +7,7 @@
#include <utility> #include <utility>
#include <MyGUI_TextBox.h> #include <MyGUI_TextBox.h>
#include <MyGUI_Button.h>
#include <boost/bind.hpp> #include <boost/bind.hpp>
#include <boost/function.hpp> #include <boost/function.hpp>
@ -428,11 +429,24 @@ namespace
AddNamesToList(Gui::MWList* list) : mList(list) {} AddNamesToList(Gui::MWList* list) : mList(list) {}
Gui::MWList* mList; Gui::MWList* mList;
void operator () (const std::string& name) void operator () (const std::string& name, bool finished=false)
{ {
mList->addItem(name); mList->addItem(name);
} }
}; };
struct SetNamesInactive
{
SetNamesInactive(Gui::MWList* list) : mList(list) {}
Gui::MWList* mList;
void operator () (const std::string& name, bool finished)
{
if (finished)
{
mList->getItemWidget(name)->setStateSelected(true);
}
}
};
void notifyQuests(MyGUI::Widget* _sender) void notifyQuests(MyGUI::Widget* _sender)
{ {
@ -453,6 +467,12 @@ namespace
mModel->visitQuestNames(!mAllQuests, add); mModel->visitQuestNames(!mAllQuests, add);
list->adjustSize(); list->adjustSize();
if (mAllQuests)
{
SetNamesInactive setInactive(list);
mModel->visitQuestNames(!mAllQuests, setInactive);
}
} }
void notifyShowAll(MyGUI::Widget* _sender) void notifyShowAll(MyGUI::Widget* _sender)

View file

@ -102,6 +102,31 @@ namespace MWGui
updateSpells(); updateSpells();
} }
void SpellWindow::askDeleteSpell(const std::string &spellId)
{
// delete spell, if allowed
const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
if (spell->mData.mFlags & ESM::Spell::F_Always
|| spell->mData.mType == ESM::Spell::ST_Power)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}");
}
else
{
// ask for confirmation
mSpellToDelete = spellId;
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString("sQuestionDeleteSpell", "Delete %s?");
question = boost::str(boost::format(question) % spell->mName);
dialog->open(question);
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept);
dialog->eventCancelClicked.clear();
}
}
void SpellWindow::onModelIndexSelected(SpellModel::ModelIndex index) void SpellWindow::onModelIndexSelected(SpellModel::ModelIndex index)
{ {
const Spell& spell = mSpellView->getModel()->getItem(index); const Spell& spell = mSpellView->getModel()->getItem(index);
@ -111,43 +136,19 @@ namespace MWGui
} }
else else
{ {
onSpellSelected(spell.mId); if (MyGUI::InputManager::getInstance().isShiftPressed())
askDeleteSpell(spell.mId);
else
onSpellSelected(spell.mId);
} }
} }
void SpellWindow::onSpellSelected(const std::string& spellId) void SpellWindow::onSpellSelected(const std::string& spellId)
{ {
if (MyGUI::InputManager::getInstance().isShiftPressed()) MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
{ MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
// delete spell, if allowed store.setSelectedEnchantItem(store.end());
const ESM::Spell* spell = MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
if (spell->mData.mFlags & ESM::Spell::F_Always
|| spell->mData.mType == ESM::Spell::ST_Power)
{
MWBase::Environment::get().getWindowManager()->messageBox("#{sDeleteSpellError}");
}
else
{
// ask for confirmation
mSpellToDelete = spellId;
ConfirmationDialog* dialog = MWBase::Environment::get().getWindowManager()->getConfirmationDialog();
std::string question = MWBase::Environment::get().getWindowManager()->getGameSettingString("sQuestionDeleteSpell", "Delete %s?");
question = boost::str(boost::format(question) % spell->mName);
dialog->open(question);
dialog->eventOkClicked.clear();
dialog->eventOkClicked += MyGUI::newDelegate(this, &SpellWindow::onDeleteSpellAccept);
dialog->eventCancelClicked.clear();
}
}
else
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
}
updateSpells(); updateSpells();
} }
@ -184,6 +185,10 @@ namespace MWGui
return; return;
selected = (selected + itemcount) % itemcount; selected = (selected + itemcount) % itemcount;
onModelIndexSelected(selected); const Spell& spell = mSpellView->getModel()->getItem(selected);
if (spell.mType == Spell::Type_EnchantedItem)
onEnchantedItemSelected(spell.mItem, spell.mActive);
else
onSpellSelected(spell.mId);
} }
} }

View file

@ -33,6 +33,7 @@ namespace MWGui
void onSpellSelected(const std::string& spellId); void onSpellSelected(const std::string& spellId);
void onModelIndexSelected(SpellModel::ModelIndex index); void onModelIndexSelected(SpellModel::ModelIndex index);
void onDeleteSpellAccept(); void onDeleteSpellAccept();
void askDeleteSpell(const std::string& spellId);
virtual void onPinToggled(); virtual void onPinToggled();
virtual void onTitleDoubleClicked(); virtual void onTitleDoubleClicked();

View file

@ -207,16 +207,6 @@ namespace MWMechanics
const MWWorld::Class& actorClass = actor.getClass(); const MWWorld::Class& actorClass = actor.getClass();
MWBase::World* world = MWBase::Environment::get().getWorld(); MWBase::World* world = MWBase::Environment::get().getWorld();
// can't fight if attacker can't go where target is. E.g. A fish can't attack person on land.
if (!actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
{
actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
return true;
}
//Update every frame //Update every frame
bool& combatMove = storage.mCombatMove; bool& combatMove = storage.mCombatMove;
@ -476,6 +466,19 @@ namespace MWMechanics
// for distant combat we should know if target is in LOS even if distToTarget < rangeAttack // for distant combat we should know if target is in LOS even if distToTarget < rangeAttack
bool inLOS = distantCombat ? world->getLOS(actor, target) : true; bool inLOS = distantCombat ? world->getLOS(actor, target) : true;
// can't fight if attacker can't go where target is. E.g. A fish can't attack person on land.
if (distToTarget >= rangeAttack
&& !actorClass.isNpc() && !MWMechanics::isEnvironmentCompatible(actor, target))
{
// TODO: start fleeing?
movement.mPosition[0] = 0;
movement.mPosition[1] = 0;
movement.mPosition[2] = 0;
readyToAttack = false;
actorClass.getCreatureStats(actor).setAttackingOrSpell(false);
return false;
}
// (within attack dist) || (not quite attack dist while following) // (within attack dist) || (not quite attack dist while following)
if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck))) if(inLOS && (distToTarget < rangeAttack || (distToTarget <= rangeFollow && followTarget && !isStuck)))
{ {

View file

@ -72,6 +72,8 @@ namespace MWMechanics
// Used by effect management classes (ActiveSpells, InventoryStore, Spells) to list active effect sources for GUI display // Used by effect management classes (ActiveSpells, InventoryStore, Spells) to list active effect sources for GUI display
struct EffectSourceVisitor struct EffectSourceVisitor
{ {
virtual ~EffectSourceVisitor() { }
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId, const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1) = 0; float magnitude, float remainingTime = -1, float totalTime = -1) = 0;

View file

@ -309,9 +309,11 @@ namespace MWMechanics
for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin()); for (std::vector<ESM::ENAMstruct>::const_iterator iter (effects.mList.begin());
iter!=effects.mList.end(); ++iter) iter!=effects.mList.end(); ++iter)
{ {
if (iter->mRange != range) if (iter->mRange == range)
continue; {
found = true; found = true;
break;
}
} }
if (!found) if (!found)
return; return;
@ -592,6 +594,7 @@ namespace MWMechanics
value.restore(magnitude); value.restore(magnitude);
target.getClass().getCreatureStats(target).setAttribute(attribute, value); target.getClass().getCreatureStats(target).setAttribute(attribute, value);
} }
// TODO: refactor the effect tick functions in Actors so they can be reused here
else if (effectId == ESM::MagicEffect::DamageHealth) else if (effectId == ESM::MagicEffect::DamageHealth)
{ {
applyDynamicStatsEffect(0, target, magnitude * -1); applyDynamicStatsEffect(0, target, magnitude * -1);
@ -681,7 +684,7 @@ namespace MWMechanics
void CastSpell::applyDynamicStatsEffect(int attribute, const MWWorld::Ptr& target, float magnitude) void CastSpell::applyDynamicStatsEffect(int attribute, const MWWorld::Ptr& target, float magnitude)
{ {
DynamicStat<float> value = target.getClass().getCreatureStats(target).getDynamic(attribute); DynamicStat<float> value = target.getClass().getCreatureStats(target).getDynamic(attribute);
value.modify(magnitude); value.setCurrent(value.getCurrent()+magnitude, attribute == 2);
target.getClass().getCreatureStats(target).setDynamic(attribute, value); target.getClass().getCreatureStats(target).setDynamic(attribute, value);
} }
@ -766,8 +769,7 @@ namespace MWMechanics
if (!mTarget.isEmpty()) if (!mTarget.isEmpty())
{ {
if (!mTarget.getClass().isActor() || !mTarget.getClass().getCreatureStats(mTarget).isDead()) inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
inflict(mTarget, mCaster, enchantment->mEffects, ESM::RT_Touch);
} }
std::string projectileModel; std::string projectileModel;
@ -851,10 +853,7 @@ namespace MWMechanics
if (!mTarget.isEmpty()) if (!mTarget.isEmpty())
{ {
if (!mTarget.getClass().isActor() || !mTarget.getClass().getCreatureStats(mTarget).isDead()) inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch);
{
inflict(mTarget, mCaster, spell->mEffects, ESM::RT_Touch);
}
} }

View file

@ -51,6 +51,10 @@ namespace MWMechanics
} }
UpdateSummonedCreatures::~UpdateSummonedCreatures()
{
}
void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime) void UpdateSummonedCreatures::visit(EffectKey key, const std::string &sourceName, const std::string &sourceId, int casterActorId, float magnitude, float remainingTime, float totalTime)
{ {
if (isSummoningEffect(key.mId) && magnitude > 0) if (isSummoningEffect(key.mId) && magnitude > 0)

View file

@ -14,6 +14,7 @@ namespace MWMechanics
struct UpdateSummonedCreatures : public EffectSourceVisitor struct UpdateSummonedCreatures : public EffectSourceVisitor
{ {
UpdateSummonedCreatures(const MWWorld::Ptr& actor); UpdateSummonedCreatures(const MWWorld::Ptr& actor);
virtual ~UpdateSummonedCreatures();
virtual void visit (MWMechanics::EffectKey key, virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId, const std::string& sourceName, const std::string& sourceId, int casterActorId,

View file

@ -120,14 +120,12 @@ RenderingManager::RenderingManager(OEngine::Render::OgreRenderer& _rend, const b
// Set default texture filtering options // Set default texture filtering options
TextureFilterOptions tfo; TextureFilterOptions tfo;
std::string filter = Settings::Manager::getString("texture filtering", "General"); std::string filter = Settings::Manager::getString("texture filtering", "General");
#ifndef ANDROID
if (filter == "anisotropic") tfo = TFO_ANISOTROPIC; if (filter == "anisotropic") tfo = TFO_ANISOTROPIC;
else if (filter == "trilinear") tfo = TFO_TRILINEAR; else if (filter == "trilinear") tfo = TFO_TRILINEAR;
else if (filter == "bilinear") tfo = TFO_BILINEAR; else if (filter == "bilinear") tfo = TFO_BILINEAR;
else /*if (filter == "none")*/ tfo = TFO_NONE; else /*if (filter == "none")*/ tfo = TFO_NONE;
#else
tfo = TFO_NONE;
#endif
MaterialManager::getSingleton().setDefaultTextureFiltering(tfo); MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 ); MaterialManager::getSingleton().setDefaultAnisotropy( (filter == "anisotropic") ? Settings::Manager::getInt("anisotropy", "General") : 1 );

View file

@ -113,7 +113,7 @@ void RippleSimulation::update(float dt, Ogre::Vector2 position)
position.z = 0; // Z is set by the Scene Node position.z = 0; // Z is set by the Scene Node
rotSpeed = mRippleRotSpeed; rotSpeed = mRippleRotSpeed;
rotation = Ogre::Radian(Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI)); rotation = Ogre::Radian(Ogre::Math::RangeRandom(-Ogre::Math::PI, Ogre::Math::PI));
created->setDimensions(50,50); created->setDimensions(mParticleSystem->getDefaultWidth(), mParticleSystem->getDefaultHeight());
} }
} }

View file

@ -1,16 +1,39 @@
#include "actiontrap.hpp" #include "actiontrap.hpp"
#include "../mwmechanics/spellcasting.hpp" #include "../mwmechanics/spellcasting.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWWorld namespace MWWorld
{ {
void ActionTrap::executeImp(const Ptr &actor) void ActionTrap::executeImp(const Ptr &actor)
{ {
MWMechanics::CastSpell cast(mTrapSource, actor); Ogre::Vector3 actorPosition(actor.getRefData().getPosition().pos);
cast.mHitPosition = Ogre::Vector3(actor.getRefData().getPosition().pos); Ogre::Vector3 trapPosition(mTrapSource.getRefData().getPosition().pos);
cast.cast(mSpellId); float activationDistance = MWBase::Environment::get().getWorld()->getMaxActivationDistance();
// GUI calcs if object in activation distance include object and player geometry
const float fudgeFactor = 1.25f;
// Hack: if actor is beyond activation range, then assume actor is using telekinesis
// to open door/container.
// Note, can't just detonate the trap at the trapped object's location and use the blast
// radius, because for most trap spells this is 1 foot, much less than the activation distance.
if (trapPosition.distance(actorPosition) < (activationDistance * fudgeFactor))
{
// assume actor touched trap
MWMechanics::CastSpell cast(mTrapSource, actor);
cast.mHitPosition = actorPosition;
cast.cast(mSpellId);
}
else
{
// assume telekinesis used
MWMechanics::CastSpell cast(mTrapSource, mTrapSource);
cast.mHitPosition = trapPosition;
cast.cast(mSpellId);
}
mTrapSource.getCellRef().setTrap(""); mTrapSource.getCellRef().setTrap("");
} }

View file

@ -3,6 +3,8 @@
#include <algorithm> #include <algorithm>
#include <stdexcept> #include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/shared_ptr.hpp> #include <boost/shared_ptr.hpp>
#include "livecellref.hpp" #include "livecellref.hpp"
@ -181,7 +183,12 @@ namespace MWWorld
template <class T> template <class T>
CellRefList<T>& get() { CellRefList<T>& get() {
throw std::runtime_error ("Storage for this type not exist in cells"); throw std::runtime_error ("Storage for type " + std::string(typeid(T).name())+ " does not exist in cells");
}
template <class T>
const CellRefList<T>& getReadOnly() {
throw std::runtime_error ("Read Only CellRefList access not available for type " + std::string(typeid(T).name()) );
} }
bool isPointConnected(const int start, const int end) const; bool isPointConnected(const int start, const int end) const;
@ -357,6 +364,12 @@ namespace MWWorld
return mWeapons; return mWeapons;
} }
template<>
inline const CellRefList<ESM::Door>& CellStore::getReadOnly<ESM::Door>()
{
return mDoors;
}
bool operator== (const CellStore& left, const CellStore& right); bool operator== (const CellStore& left, const CellStore& right);
bool operator!= (const CellStore& left, const CellStore& right); bool operator!= (const CellStore& left, const CellStore& right);
} }

View file

@ -33,11 +33,11 @@ namespace MWWorld
MWScript::Locals mLocals; // if we find the overhead of heaving a locals MWScript::Locals mLocals; // if we find the overhead of heaving a locals
// object in the refdata of refs without a script, // object in the refdata of refs without a script,
// we can make this a pointer later. // we can make this a pointer later.
bool mDeleted; // separate delete flag used for deletion by a content file
bool mHasLocals; bool mHasLocals;
bool mEnabled; bool mEnabled;
int mCount; // 0: deleted int mCount; // 0: deleted
bool mDeleted; // separate delete flag used for deletion by a content file
ESM::Position mPosition; ESM::Position mPosition;

View file

@ -230,7 +230,7 @@ namespace MWWorld
cell->getCell()->getGridX(), cell->getCell()->getGridX(),
cell->getCell()->getGridY() cell->getCell()->getGridY()
); );
if (land) { if (land && land->mDataTypes&ESM::Land::DATA_VHGT) {
// Actually only VHGT is needed here, but we'll need the rest for rendering anyway. // Actually only VHGT is needed here, but we'll need the rest for rendering anyway.
// Load everything now to reduce IO overhead. // Load everything now to reduce IO overhead.
const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX; const int flags = ESM::Land::DATA_VCLR|ESM::Land::DATA_VHGT|ESM::Land::DATA_VNML|ESM::Land::DATA_VTEX;

View file

@ -423,6 +423,19 @@ namespace MWWorld
globals["werewolfclawmult"] = ESM::Variant(25.f); globals["werewolfclawmult"] = ESM::Variant(25.f);
globals["pcknownwerewolf"] = ESM::Variant(0); globals["pcknownwerewolf"] = ESM::Variant(0);
// following should exist in all versions of MW, but not necessarily in TCs
globals["gamehour"] = ESM::Variant(0.f);
globals["timescale"] = ESM::Variant(30.f);
globals["day"] = ESM::Variant(1);
globals["month"] = ESM::Variant(1);
globals["year"] = ESM::Variant(1);
globals["pcrace"] = ESM::Variant(0);
globals["pchascrimegold"] = ESM::Variant(0);
globals["pchasgolddiscount"] = ESM::Variant(0);
globals["crimegolddiscount"] = ESM::Variant(0);
globals["crimegoldturnin"] = ESM::Variant(0);
globals["pchasturnin"] = ESM::Variant(0);
for (std::map<std::string, ESM::Variant>::iterator it = gmst.begin(); it != gmst.end(); ++it) for (std::map<std::string, ESM::Variant>::iterator it = gmst.begin(); it != gmst.end(); ++it)
{ {
if (!mStore.get<ESM::GameSetting>().search(it->first)) if (!mStore.get<ESM::GameSetting>().search(it->first))
@ -2784,18 +2797,45 @@ namespace MWWorld
{ {
if (cell->isExterior()) if (cell->isExterior())
return false; return false;
MWWorld::CellRefList<ESM::Door>& doors = cell->get<ESM::Door>();
CellRefList<ESM::Door>::List& refList = doors.mList;
// Check if any door in the cell leads to an exterior directly // Search for a 'nearest' exterior, counting each cell between the starting
for (CellRefList<ESM::Door>::List::iterator it = refList.begin(); it != refList.end(); ++it) // cell and the exterior as a distance of 1. Will fail for isolated interiors.
{ std::set< std::string >checkedCells;
MWWorld::LiveCellRef<ESM::Door>& ref = *it; std::set< std::string >currentCells;
if (ref.mRef.getTeleport() && ref.mRef.getDestCell().empty()) std::set< std::string >nextCells;
{ nextCells.insert( cell->getCell()->mName );
ESM::Position pos = ref.mRef.getDoorDest();
result = Ogre::Vector3(pos.pos); while ( !nextCells.empty() ) {
return true; currentCells = nextCells;
nextCells.clear();
for( std::set< std::string >::const_iterator i = currentCells.begin(); i != currentCells.end(); ++i ) {
MWWorld::CellStore *next = getInterior( *i );
if ( !next ) continue;
const MWWorld::CellRefList<ESM::Door>& doors = next->getReadOnly<ESM::Door>();
const CellRefList<ESM::Door>::List& refList = doors.mList;
// Check if any door in the cell leads to an exterior directly
for (CellRefList<ESM::Door>::List::const_iterator it = refList.begin(); it != refList.end(); ++it)
{
const MWWorld::LiveCellRef<ESM::Door>& ref = *it;
if (!ref.mRef.getTeleport()) continue;
if (ref.mRef.getDestCell().empty())
{
ESM::Position pos = ref.mRef.getDoorDest();
result = Ogre::Vector3(pos.pos);
return true;
}
else
{
std::string dest = ref.mRef.getDestCell();
if ( !checkedCells.count(dest) && !currentCells.count(dest) )
nextCells.insert(dest);
}
}
checkedCells.insert( *i );
} }
} }
@ -2803,33 +2843,102 @@ namespace MWWorld
return false; return false;
} }
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr, MWWorld::Ptr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id )
const std::string& id)
{ {
Ogre::Vector3 worldPos; if ( ptr.getCell()->isExterior() ) {
if (!findInteriorPositionInWorldSpace(ptr.getCell(), worldPos)) return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id);
worldPos = mPlayer->getLastKnownExteriorPosition(); }
// Search for a 'nearest' marker, counting each cell between the starting
// cell and the exterior as a distance of 1. If an exterior is found, jump
// to the nearest exterior marker, without further interior searching.
std::set< std::string >checkedCells;
std::set< std::string >currentCells;
std::set< std::string >nextCells;
MWWorld::Ptr closestMarker;
nextCells.insert( ptr.getCell()->getCell()->mName );
while ( !nextCells.empty() ) {
currentCells = nextCells;
nextCells.clear();
for( std::set< std::string >::const_iterator i = currentCells.begin(); i != currentCells.end(); ++i ) {
MWWorld::CellStore *next = getInterior( *i );
checkedCells.insert( *i );
if ( !next ) continue;
closestMarker = next->search( id );
if ( !closestMarker.isEmpty() )
{
return closestMarker;
}
const MWWorld::CellRefList<ESM::Door>& doors = next->getReadOnly<ESM::Door>();
const CellRefList<ESM::Door>::List& doorList = doors.mList;
// Check if any door in the cell leads to an exterior directly
for (CellRefList<ESM::Door>::List::const_iterator it = doorList.begin(); it != doorList.end(); ++it)
{
const MWWorld::LiveCellRef<ESM::Door>& ref = *it;
if (!ref.mRef.getTeleport()) continue;
if (ref.mRef.getDestCell().empty())
{
Ogre::Vector3 worldPos = Ogre::Vector3(ref.mRef.getDoorDest().pos);
return getClosestMarkerFromExteriorPosition(worldPos, id);
}
else
{
std::string dest = ref.mRef.getDestCell();
if ( !checkedCells.count(dest) && !currentCells.count(dest) )
nextCells.insert(dest);
}
}
}
}
return MWWorld::Ptr();
}
MWWorld::Ptr World::getClosestMarkerFromExteriorPosition( const Ogre::Vector3 worldPos, const std::string &id ) {
MWWorld::Ptr closestMarker; MWWorld::Ptr closestMarker;
float closestDistance = FLT_MAX; float closestDistance = FLT_MAX;
std::vector<MWWorld::Ptr> markers; std::vector<MWWorld::Ptr> markers;
mCells.getExteriorPtrs(id, markers); mCells.getExteriorPtrs(id, markers);
for (std::vector<MWWorld::Ptr>::iterator it2 = markers.begin(); it2 != markers.end(); ++it2)
for (std::vector<MWWorld::Ptr>::iterator it = markers.begin(); it != markers.end(); ++it)
{ {
ESM::Position pos = it->getRefData().getPosition(); ESM::Position pos = it2->getRefData().getPosition();
Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos); Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos);
float distance = worldPos.squaredDistance(markerPos); float distance = worldPos.squaredDistance(markerPos);
if (distance < closestDistance) if (distance < closestDistance)
{ {
closestDistance = distance; closestDistance = distance;
closestMarker = *it; closestMarker = *it2;
} }
} }
MWWorld::ActionTeleport action("", closestMarker.getRefData().getPosition()); return closestMarker;
}
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
const std::string& id)
{
MWWorld::Ptr closestMarker = getClosestMarker( ptr, id );
if ( closestMarker.isEmpty() )
{
std::cerr << "Failed to teleport: no closest marker found" << std::endl;
return;
}
std::string cellName;
if ( !closestMarker.mCell->isExterior() )
cellName = closestMarker.mCell->getCell()->mName;
MWWorld::ActionTeleport action(cellName, closestMarker.getRefData().getPosition());
action.execute(ptr); action.execute(ptr);
} }
@ -2978,31 +3087,26 @@ namespace MWWorld
void World::confiscateStolenItems(const Ptr &ptr) void World::confiscateStolenItems(const Ptr &ptr)
{ {
Ogre::Vector3 playerPos; MWWorld::Ptr prisonMarker = getClosestMarker( ptr, "prisonmarker" );
if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos)) if ( prisonMarker.isEmpty() )
playerPos = mPlayer->getLastKnownExteriorPosition();
MWWorld::Ptr closestChest;
float closestDistance = FLT_MAX;
//Find closest stolen_goods chest
std::vector<MWWorld::Ptr> chests;
mCells.getInteriorPtrs("stolen_goods", chests);
Ogre::Vector3 chestPos;
for (std::vector<MWWorld::Ptr>::iterator it = chests.begin(); it != chests.end(); ++it)
{ {
if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos)) std::cerr << "Failed to confiscate items: no closest prison marker found." << std::endl;
continue; return;
}
float distance = playerPos.squaredDistance(chestPos); std::string prisonName = prisonMarker.mRef->mRef.getDestCell();
if (distance < closestDistance) if ( prisonName.empty() )
{ {
closestDistance = distance; std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl;
closestChest = *it; return;
} }
MWWorld::CellStore *prison = getInterior( prisonName );
if ( !prison )
{
std::cerr << "Failed to confiscate items: failed to load cell " << prisonName << std::endl;
return;
} }
MWWorld::Ptr closestChest = prison->search( "stolen_goods" );
if (!closestChest.isEmpty()) //Found a close chest if (!closestChest.isEmpty()) //Found a close chest
{ {
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest); MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
@ -3108,7 +3212,7 @@ namespace MWWorld
mRendering->spawnEffect(model, textureOverride, worldPos); mRendering->spawnEffect(model, textureOverride, worldPos);
} }
void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, int rangeType, void World::explodeSpell(const Vector3 &origin, const ESM::EffectList &effects, const Ptr &caster, ESM::RangeType rangeType,
const std::string& id, const std::string& sourceName) const std::string& id, const std::string& sourceName)
{ {
std::map<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply; std::map<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;
@ -3167,7 +3271,7 @@ namespace MWWorld
cast.mStack = false; cast.mStack = false;
ESM::EffectList effects; ESM::EffectList effects;
effects.mList = apply->second; effects.mList = apply->second;
cast.inflict(apply->first, caster, effects, (ESM::RangeType)rangeType, false, true); cast.inflict(apply->first, caster, effects, rangeType, false, true);
} }
} }

View file

@ -115,7 +115,6 @@ namespace MWWorld
void performUpdateSceneQueries (); void performUpdateSceneQueries ();
void getFacedHandle(std::string& facedHandle, float maxDistance, bool ignorePlayer=true); void getFacedHandle(std::string& facedHandle, float maxDistance, bool ignorePlayer=true);
float getMaxActivationDistance ();
float getNpcActivationDistance (); float getNpcActivationDistance ();
float getObjectActivationDistance (); float getObjectActivationDistance ();
@ -151,6 +150,9 @@ namespace MWWorld
float feetToGameUnits(float feet); float feetToGameUnits(float feet);
MWWorld::Ptr getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id );
MWWorld::Ptr getClosestMarkerFromExteriorPosition( const Ogre::Vector3 worldPos, const std::string &id );
public: public:
World (OEngine::Render::OgreRenderer& renderer, World (OEngine::Render::OgreRenderer& renderer,
@ -363,6 +365,8 @@ namespace MWWorld
virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos); virtual MWWorld::Ptr safePlaceObject(const MWWorld::Ptr& ptr, MWWorld::CellStore* cell, ESM::Position pos);
///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr. ///< place an object in a "safe" location (ie not in the void, etc). Makes a copy of the Ptr.
virtual float getMaxActivationDistance();
virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false) virtual void indexToPosition (int cellX, int cellY, float &x, float &y, bool centre = false)
const; const;
///< Convert cell numbers to position. ///< Convert cell numbers to position.
@ -633,7 +637,7 @@ namespace MWWorld
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos); virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos);
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects, virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName); const MWWorld::Ptr& caster, ESM::RangeType rangeType, const std::string& id, const std::string& sourceName);
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor); virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor);

View file

@ -105,6 +105,12 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings)
// obtain content list from game settings (if present) // obtain content list from game settings (if present)
const QStringList files(gameSettings.getContentList()); const QStringList files(gameSettings.getContentList());
// if openmw.cfg has no content, exit so we don't create an empty content list.
if (files.isEmpty())
{
return;
}
// if any existing profile in launcher matches the content list, make that profile the default // if any existing profile in launcher matches the content list, make that profile the default
foreach(const QString &listName, getContentLists()) foreach(const QString &listName, getContentLists())
{ {

View file

@ -110,15 +110,15 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index
if (!file) if (!file)
return Qt::NoItemFlags; return Qt::NoItemFlags;
//game files can always be checked //game files are not shown
if (file->isGameFile()) if (file->isGameFile())
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable; return Qt::NoItemFlags;
Qt::ItemFlags returnFlags; Qt::ItemFlags returnFlags;
bool allDependenciesFound = true;
bool gamefileChecked = false;
//addon can be checked if its gamefile is and all other dependencies exist // addon can be checked if its gamefile is
// ... special case, addon with no dependency can be used with any gamefile.
bool gamefileChecked = (file->gameFiles().count() == 0);
foreach (const QString &fileName, file->gameFiles()) foreach (const QString &fileName, file->gameFiles())
{ {
bool depFound = false; bool depFound = false;
@ -144,16 +144,11 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index
if (gamefileChecked || !(dependency->isGameFile())) if (gamefileChecked || !(dependency->isGameFile()))
break; break;
} }
allDependenciesFound = allDependenciesFound && depFound;
} }
if (gamefileChecked) if (gamefileChecked)
{ {
if (allDependenciesFound) returnFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
else
returnFlags = Qt::ItemIsSelectable;
} }
return returnFlags; return returnFlags;
@ -467,6 +462,14 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
file->setFilePath (info.absoluteFilePath()); file->setFilePath (info.absoluteFilePath());
file->setDescription(QString::fromUtf8(fileReader.getDesc().c_str())); file->setDescription(QString::fromUtf8(fileReader.getDesc().c_str()));
// HACK
// Load order constraint of Bloodmoon.esm needing Tribunal.esm is missing
// from the file supplied by Bethesda, so we have to add it ourselves
if (file->fileName().compare("Bloodmoon.esm", Qt::CaseInsensitive) == 0)
{
file->addGameFile(QString::fromUtf8("Tribunal.esm"));
}
// Put the file in the table // Put the file in the table
addFile(file); addFile(file);
@ -481,6 +484,19 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
sortFiles(); sortFiles();
} }
QStringList ContentSelectorModel::ContentModel::gameFiles() const
{
QStringList gameFiles;
foreach(const ContentSelectorModel::EsmFile *file, mFiles)
{
if (file->isGameFile())
{
gameFiles.append(file->fileName());
}
}
return gameFiles;
}
void ContentSelectorModel::ContentModel::sortFiles() void ContentSelectorModel::ContentModel::sortFiles()
{ {
//first, sort the model such that all dependencies are ordered upstream (gamefile) first. //first, sort the model such that all dependencies are ordered upstream (gamefile) first.
@ -536,13 +552,13 @@ bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile *file) c
return mPluginsWithLoadOrderError.contains(file->filePath()); return mPluginsWithLoadOrderError.contains(file->filePath());
} }
void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList, bool isChecked) void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList)
{ {
mPluginsWithLoadOrderError.clear(); mPluginsWithLoadOrderError.clear();
int previousPosition = -1; int previousPosition = -1;
foreach (const QString &filepath, fileList) foreach (const QString &filepath, fileList)
{ {
if (setCheckState(filepath, isChecked)) if (setCheckState(filepath, true))
{ {
// as necessary, move plug-ins in visible list to match sequence of supplied filelist // as necessary, move plug-ins in visible list to match sequence of supplied filelist
const EsmFile* file = item(filepath); const EsmFile* file = item(filepath);
@ -581,7 +597,7 @@ void ContentSelectorModel::ContentModel::checkForLoadOrderErrors()
QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(const EsmFile *file, int row) const
{ {
QList<LoadOrderError> errors = QList<LoadOrderError>(); QList<LoadOrderError> errors = QList<LoadOrderError>();
foreach(QString dependentfileName, file->gameFiles()) foreach(const QString &dependentfileName, file->gameFiles())
{ {
const EsmFile* dependentFile = item(dependentfileName); const EsmFile* dependentFile = item(dependentfileName);

View file

@ -47,11 +47,12 @@ namespace ContentSelectorModel
QModelIndex indexFromItem(const EsmFile *item) const; QModelIndex indexFromItem(const EsmFile *item) const;
const EsmFile *item(const QString &name) const; const EsmFile *item(const QString &name) const;
QStringList gameFiles() const;
bool isEnabled (QModelIndex index) const; bool isEnabled (QModelIndex index) const;
bool isChecked(const QString &filepath) const; bool isChecked(const QString &filepath) const;
bool setCheckState(const QString &filepath, bool isChecked); bool setCheckState(const QString &filepath, bool isChecked);
void setContentList(const QStringList &fileList, bool isChecked); void setContentList(const QStringList &fileList);
ContentFileList checkedItems() const; ContentFileList checkedItems() const;
void uncheckAll(); void uncheckAll();

View file

@ -62,6 +62,13 @@ QByteArray ContentSelectorModel::EsmFile::encodedData() const
return encodedData; return encodedData;
} }
bool ContentSelectorModel::EsmFile::isGameFile() const
{
return (mGameFiles.size() == 0) &&
(mFileName.endsWith(QLatin1String(".esm"), Qt::CaseInsensitive) ||
mFileName.endsWith(QLatin1String(".omwgame"), Qt::CaseInsensitive));
}
QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const QVariant ContentSelectorModel::EsmFile::fileProperty(const FileProperty prop) const
{ {
switch (prop) switch (prop)

View file

@ -64,7 +64,7 @@ namespace ContentSelectorModel
.arg(mGameFiles.join(", ")); .arg(mGameFiles.join(", "));
} }
inline bool isGameFile() const { return (mGameFiles.size() == 0); } bool isGameFile() const;
QByteArray encodedData() const; QByteArray encodedData() const;
public: public:

View file

@ -10,6 +10,7 @@
#include <QGridLayout> #include <QGridLayout>
#include <QMessageBox> #include <QMessageBox>
#include <QModelIndex> #include <QModelIndex>
#include <QDir>
#include <assert.h> #include <assert.h>
ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) : ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) :
@ -33,13 +34,7 @@ void ContentSelectorView::ContentSelector::buildGameFileView()
{ {
ui.gameFileView->setVisible (true); ui.gameFileView->setVisible (true);
mGameFileProxyModel = new QSortFilterProxyModel(this);
mGameFileProxyModel->setFilterRegExp(QString::number((int)ContentSelectorModel::ContentType_GameFile));
mGameFileProxyModel->setFilterRole (Qt::UserRole);
mGameFileProxyModel->setSourceModel (mContentModel);
ui.gameFileView->setPlaceholderText(QString("Select a game file...")); ui.gameFileView->setPlaceholderText(QString("Select a game file..."));
ui.gameFileView->setModel(mGameFileProxyModel);
connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)), connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)),
this, SLOT (slotCurrentGameFileIndexChanged(int))); this, SLOT (slotCurrentGameFileIndexChanged(int)));
@ -113,7 +108,7 @@ void ContentSelectorView::ContentSelector::setContentList(const QStringList &lis
slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex()); slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex());
} }
else else
mContentModel->setContentList(list, true); mContentModel->setContentList(list);
} }
ContentSelectorModel::ContentFileList ContentSelectorModel::ContentFileList
@ -129,6 +124,15 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &path)
{ {
mContentModel->addFiles(path); mContentModel->addFiles(path);
// add any game files to the combo box
foreach(const QString gameFileName, mContentModel->gameFiles())
{
if (ui.gameFileView->findText(gameFileName) == -1)
{
ui.gameFileView->addItem(gameFileName);
}
}
if (ui.gameFileView->currentIndex() != -1) if (ui.gameFileView->currentIndex() != -1)
ui.gameFileView->setCurrentIndex(-1); ui.gameFileView->setCurrentIndex(-1);
@ -150,29 +154,33 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i
{ {
static int oldIndex = -1; static int oldIndex = -1;
QAbstractItemModel *const model = ui.gameFileView->model();
QSortFilterProxyModel *proxy = dynamic_cast<QSortFilterProxyModel *>(model);
if (proxy)
proxy->setDynamicSortFilter(false);
if (index != oldIndex) if (index != oldIndex)
{ {
if (oldIndex > -1) if (oldIndex > -1)
model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1); {
setGameFileSelected(oldIndex, false);
}
oldIndex = index; oldIndex = index;
model->setData(model->index(index, 0), true, Qt::UserRole + 1); setGameFileSelected(index, true);
mContentModel->checkForLoadOrderErrors(); mContentModel->checkForLoadOrderErrors();
} }
if (proxy)
proxy->setDynamicSortFilter(true);
emit signalCurrentGamefileIndexChanged (index); emit signalCurrentGamefileIndexChanged (index);
} }
void ContentSelectorView::ContentSelector::setGameFileSelected(int index, bool selected)
{
QString fileName = ui.gameFileView->itemText(index);
const ContentSelectorModel::EsmFile* file = mContentModel->item(fileName);
if (file != NULL)
{
QModelIndex index(mContentModel->indexFromItem(file));
mContentModel->setData(index, selected, Qt::UserRole + 1);
}
}
void ContentSelectorView::ContentSelector::slotAddonTableItemActivated(const QModelIndex &index) void ContentSelectorView::ContentSelector::slotAddonTableItemActivated(const QModelIndex &index)
{ {
QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index); QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index);

View file

@ -19,7 +19,6 @@ namespace ContentSelectorView
protected: protected:
ContentSelectorModel::ContentModel *mContentModel; ContentSelectorModel::ContentModel *mContentModel;
QSortFilterProxyModel *mGameFileProxyModel;
QSortFilterProxyModel *mAddonProxyModel; QSortFilterProxyModel *mAddonProxyModel;
public: public:
@ -52,6 +51,7 @@ namespace ContentSelectorView
void buildContentModel(); void buildContentModel();
void buildGameFileView(); void buildGameFileView();
void buildAddonView(); void buildAddonView();
void setGameFileSelected(int index, bool selected);
signals: signals:
void signalCurrentGamefileIndexChanged (int); void signalCurrentGamefileIndexChanged (int);

View file

@ -41,29 +41,6 @@ namespace ESM
} }
void AIPackageList::load(ESMReader &esm)
{
mList.clear();
while (esm.hasMoreSubs()) {
// initialize every iteration
esm.getSubName();
switch (esm.retSubName().val)
{
case AI_Wander:
case AI_Activate:
case AI_Escort:
case AI_Follow:
case AI_Travel:
case AI_CNDT:
add(esm);
break;
default:
return;
}
}
}
void AIPackageList::save(ESMWriter &esm) const void AIPackageList::save(ESMWriter &esm) const
{ {
typedef std::vector<AIPackage>::const_iterator PackageIter; typedef std::vector<AIPackage>::const_iterator PackageIter;

View file

@ -94,9 +94,6 @@ namespace ESM
/// Add a single AIPackage, assumes subrecord name was already read /// Add a single AIPackage, assumes subrecord name was already read
void add(ESMReader &esm); void add(ESMReader &esm);
/// TODO: remove this method. The ESM format does not guarantee that all AI packages follow one another
void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };
} }

View file

@ -8,13 +8,18 @@ namespace ESM {
void EffectList::load(ESMReader &esm) void EffectList::load(ESMReader &esm)
{ {
mList.clear(); mList.clear();
ENAMstruct s;
while (esm.isNextSub("ENAM")) { while (esm.isNextSub("ENAM")) {
esm.getHT(s, 24); add(esm);
mList.push_back(s);
} }
} }
void EffectList::add(ESMReader &esm)
{
ENAMstruct s;
esm.getHT(s, 24);
mList.push_back(s);
}
void EffectList::save(ESMWriter &esm) const void EffectList::save(ESMWriter &esm) const
{ {
for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) { for (std::vector<ENAMstruct>::const_iterator it = mList.begin(); it != mList.end(); ++it) {

View file

@ -29,11 +29,15 @@ namespace ESM
}; };
#pragma pack(pop) #pragma pack(pop)
/// EffectList, ENAM subrecord
struct EffectList struct EffectList
{ {
std::vector<ENAMstruct> mList; std::vector<ENAMstruct> mList;
/// Load one effect, assumes subrecord name was already read
void add(ESMReader &esm);
/// Load all effects
void load(ESMReader &esm); void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

View file

@ -8,18 +8,34 @@ namespace ESM
{ {
unsigned int Activator::sRecordId = REC_ACTI; unsigned int Activator::sRecordId = REC_ACTI;
void Activator::load(ESMReader &esm) void Activator::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); while (esm.hasMoreSubs())
mName = esm.getHNOString("FNAM"); {
mScript = esm.getHNOString("SCRI"); esm.getSubName();
} uint32_t name = esm.retSubName().val;
void Activator::save(ESMWriter &esm) const switch (name)
{ {
esm.writeHNCString("MODL", mModel); case ESM::FourCC<'M','O','D','L'>::value:
esm.writeHNOCString("FNAM", mName); mModel = esm.getHString();
esm.writeHNOCString("SCRI", mScript); break;
} case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
}
void Activator::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript);
}
void Activator::blank() void Activator::blank()
{ {

View file

@ -8,24 +8,51 @@ namespace ESM
{ {
unsigned int Potion::sRecordId = REC_ALCH; unsigned int Potion::sRecordId = REC_ALCH;
void Potion::load(ESMReader &esm) void Potion::load(ESMReader &esm)
{ {
mModel = esm.getHNOString("MODL"); mEffects.mList.clear();
mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason bool hasData = false;
mScript = esm.getHNOString("SCRI"); while (esm.hasMoreSubs())
mName = esm.getHNOString("FNAM"); {
esm.getHNT(mData, "ALDT", 12); esm.getSubName();
mEffects.load(esm); uint32_t name = esm.retSubName().val;
} switch (name)
void Potion::save(ESMWriter &esm) const {
{ case ESM::FourCC<'M','O','D','L'>::value:
esm.writeHNCString("MODL", mModel); mModel = esm.getHString();
esm.writeHNOCString("TEXT", mIcon); break;
esm.writeHNOCString("SCRI", mScript); case ESM::FourCC<'T','E','X','T'>::value: // not ITEX here for some reason
esm.writeHNOCString("FNAM", mName); mIcon = esm.getHString();
esm.writeHNT("ALDT", mData, 12); break;
mEffects.save(esm); case ESM::FourCC<'S','C','R','I'>::value:
} mScript = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'A','L','D','T'>::value:
esm.getHT(mData, 12);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
mEffects.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing ALDT");
}
void Potion::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("TEXT", mIcon);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("ALDT", mData, 12);
mEffects.save(esm);
}
void Potion::blank() void Potion::blank()
{ {

View file

@ -10,25 +10,35 @@ namespace ESM
void Apparatus::load(ESMReader &esm) void Apparatus::load(ESMReader &esm)
{ {
// we will not treat duplicated subrecords as errors here bool hasData = false;
while (esm.hasMoreSubs()) while (esm.hasMoreSubs())
{ {
esm.getSubName(); esm.getSubName();
NAME subName = esm.retSubName(); uint32_t name = esm.retSubName().val;
switch (name)
if (subName == "MODL") {
mModel = esm.getHString(); case ESM::FourCC<'M','O','D','L'>::value:
else if (subName == "FNAM") mModel = esm.getHString();
mName = esm.getHString(); break;
else if (subName == "AADT") case ESM::FourCC<'F','N','A','M'>::value:
esm.getHT(mData); mName = esm.getHString();
else if (subName == "SCRI") break;
mScript = esm.getHString(); case ESM::FourCC<'A','A','D','T'>::value:
else if (subName == "ITEX") esm.getHT(mData);
mIcon = esm.getHString(); hasData = true;
else break;
esm.fail("wrong subrecord type " + subName.toString() + " for APPA record"); case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
} }
if (!hasData)
esm.fail("Missing AADT");
} }
void Apparatus::save(ESMWriter &esm) const void Apparatus::save(ESMWriter &esm) const

View file

@ -11,9 +11,30 @@ namespace ESM
void BodyPart::load(ESMReader &esm) void BodyPart::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = false;
mRace = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
esm.getHNT(mData, "BYDT", 4); {
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mRace = esm.getHString();
break;
case ESM::FourCC<'B','Y','D','T'>::value:
esm.getHT(mData, 4);
hasData = true;
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing BYDT subrecord");
} }
void BodyPart::save(ESMWriter &esm) const void BodyPart::save(ESMWriter &esm) const
{ {

View file

@ -10,11 +10,29 @@ namespace ESM
void BirthSign::load(ESMReader &esm) void BirthSign::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); mPowers.mList.clear();
mTexture = esm.getHNOString("TNAM"); while (esm.hasMoreSubs())
mDescription = esm.getHNOString("DESC"); {
esm.getSubName();
mPowers.load(esm); uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'T','N','A','M'>::value:
mTexture = esm.getHString();
break;
case ESM::FourCC<'D','E','S','C'>::value:
mDescription = esm.getHString();
break;
case ESM::FourCC<'N','P','C','S'>::value:
mPowers.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
} }
void BirthSign::save(ESMWriter &esm) const void BirthSign::save(ESMWriter &esm) const

View file

@ -10,17 +10,17 @@ namespace ESM
{ {
unsigned int Class::sRecordId = REC_CLAS; unsigned int Class::sRecordId = REC_CLAS;
const Class::Specialization Class::sSpecializationIds[3] = { const Class::Specialization Class::sSpecializationIds[3] = {
Class::Combat, Class::Combat,
Class::Magic, Class::Magic,
Class::Stealth Class::Stealth
}; };
const char *Class::sGmstSpecializationIds[3] = { const char *Class::sGmstSpecializationIds[3] = {
"sSpecializationCombat", "sSpecializationCombat",
"sSpecializationMagic", "sSpecializationMagic",
"sSpecializationStealth" "sSpecializationStealth"
}; };
int& Class::CLDTstruct::getSkill (int index, bool major) int& Class::CLDTstruct::getSkill (int index, bool major)
@ -39,22 +39,40 @@ const char *Class::sGmstSpecializationIds[3] = {
return mSkills[index][major ? 1 : 0]; return mSkills[index][major ? 1 : 0];
} }
void Class::load(ESMReader &esm) void Class::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); bool hasData = false;
esm.getHNT(mData, "CLDT", 60); while (esm.hasMoreSubs())
{
if (mData.mIsPlayable > 1) esm.getSubName();
esm.fail("Unknown bool value"); uint32_t name = esm.retSubName().val;
switch (name)
mDescription = esm.getHNOString("DESC"); {
} case ESM::FourCC<'F','N','A','M'>::value:
void Class::save(ESMWriter &esm) const mName = esm.getHString();
{ break;
esm.writeHNOCString("FNAM", mName); case ESM::FourCC<'C','L','D','T'>::value:
esm.writeHNT("CLDT", mData, 60); esm.getHT(mData, 60);
esm.writeHNOString("DESC", mDescription); if (mData.mIsPlayable > 1)
} esm.fail("Unknown bool value");
hasData = true;
break;
case ESM::FourCC<'D','E','S','C'>::value:
mDescription = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing CLDT subrecord");
}
void Class::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("CLDT", mData, 60);
esm.writeHNOString("DESC", mDescription);
}
void Class::blank() void Class::blank()
{ {

View file

@ -7,60 +7,79 @@
namespace ESM namespace ESM
{ {
void InventoryList::add(ESMReader &esm) void InventoryList::add(ESMReader &esm)
{
ContItem ci;
esm.getHT(ci, 36);
mList.push_back(ci);
}
void InventoryList::load(ESMReader &esm)
{
mList.clear();
while (esm.isNextSub("NPCO"))
{ {
add(esm); ContItem ci;
esm.getHT(ci, 36);
mList.push_back(ci);
} }
}
void InventoryList::save(ESMWriter &esm) const void InventoryList::save(ESMWriter &esm) const
{
for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{ {
esm.writeHNT("NPCO", *it, 36); for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{
esm.writeHNT("NPCO", *it, 36);
}
} }
}
unsigned int Container::sRecordId = REC_CONT; unsigned int Container::sRecordId = REC_CONT;
void Container::load(ESMReader &esm) void Container::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); mInventory.mList.clear();
mName = esm.getHNOString("FNAM"); bool hasWeight = false;
esm.getHNT(mWeight, "CNDT", 4); bool hasFlags = false;
esm.getHNT(mFlags, "FLAG", 4); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'C','N','D','T'>::value:
esm.getHT(mWeight, 4);
hasWeight = true;
break;
case ESM::FourCC<'F','L','A','G'>::value:
esm.getHT(mFlags, 4);
if (mFlags & 0xf4)
esm.fail("Unknown flags");
if (!(mFlags & 0x8))
esm.fail("Flag 8 not set");
hasFlags = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'N','P','C','O'>::value:
mInventory.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasWeight)
esm.fail("Missing CNDT subrecord");
if (!hasFlags)
esm.fail("Missing FLAG subrecord");
}
if (mFlags & 0xf4) void Container::save(ESMWriter &esm) const
esm.fail("Unknown flags"); {
if (!(mFlags & 0x8)) esm.writeHNCString("MODL", mModel);
esm.fail("Flag 8 not set"); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("CNDT", mWeight, 4);
esm.writeHNT("FLAG", mFlags, 4);
mScript = esm.getHNOString("SCRI"); esm.writeHNOCString("SCRI", mScript);
mInventory.load(esm); mInventory.save(esm);
} }
void Container::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("CNDT", mWeight, 4);
esm.writeHNT("FLAG", mFlags, 4);
esm.writeHNOCString("SCRI", mScript);
mInventory.save(esm);
}
void Container::blank() void Container::blank()
{ {

View file

@ -22,6 +22,7 @@ struct ContItem
NAME32 mItem; NAME32 mItem;
}; };
/// InventoryList, NPCO subrecord
struct InventoryList struct InventoryList
{ {
std::vector<ContItem> mList; std::vector<ContItem> mList;
@ -29,8 +30,6 @@ struct InventoryList
/// Load one item, assumes subrecord name is already read /// Load one item, assumes subrecord name is already read
void add(ESMReader &esm); void add(ESMReader &esm);
/// TODO: remove this method, the ESM format doesn't guarantee that all ContItems follow one another
void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };

View file

@ -8,55 +8,94 @@ namespace ESM {
unsigned int Creature::sRecordId = REC_CREA; unsigned int Creature::sRecordId = REC_CREA;
void Creature::load(ESMReader &esm) void Creature::load(ESMReader &esm)
{
mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL");
mOriginal = esm.getHNOString("CNAM");
mName = esm.getHNOString("FNAM");
mScript = esm.getHNOString("SCRI");
esm.getHNT(mData, "NPDT", 96);
esm.getHNT(mFlags, "FLAG");
mScale = 1.0;
esm.getHNOT(mScale, "XSCL");
mInventory.load(esm);
mSpells.load(esm);
if (esm.isNextSub("AIDT"))
{ {
esm.getHExact(&mAiData, sizeof(mAiData)); mPersistent = esm.getRecordFlags() & 0x0400;
mHasAI = true;
} mAiPackage.mList.clear();
else mInventory.mList.clear();
mSpells.mList.clear();
mScale = 1.f;
mHasAI = false; mHasAI = false;
bool hasNpdt = false;
mAiPackage.load(esm); bool hasFlags = false;
esm.skipRecord(); while (esm.hasMoreSubs())
} {
esm.getSubName();
void Creature::save(ESMWriter &esm) const uint32_t name = esm.retSubName().val;
{ switch (name)
esm.writeHNCString("MODL", mModel); {
esm.writeHNOCString("CNAM", mOriginal); case ESM::FourCC<'M','O','D','L'>::value:
esm.writeHNOCString("FNAM", mName); mModel = esm.getHString();
esm.writeHNOCString("SCRI", mScript); break;
esm.writeHNT("NPDT", mData, 96); case ESM::FourCC<'C','N','A','M'>::value:
esm.writeHNT("FLAG", mFlags); mOriginal = esm.getHString();
if (mScale != 1.0) { break;
esm.writeHNT("XSCL", mScale); case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'N','P','D','T'>::value:
esm.getHT(mData, 96);
hasNpdt = true;
break;
case ESM::FourCC<'F','L','A','G'>::value:
esm.getHT(mFlags);
hasFlags = true;
break;
case ESM::FourCC<'X','S','C','L'>::value:
esm.getHT(mScale);
break;
case ESM::FourCC<'N','P','C','O'>::value:
mInventory.add(esm);
break;
case ESM::FourCC<'N','P','C','S'>::value:
mSpells.add(esm);
break;
case ESM::FourCC<'A','I','D','T'>::value:
esm.getHExact(&mAiData, sizeof(mAiData));
mHasAI = true;
break;
case AI_Wander:
case AI_Activate:
case AI_Escort:
case AI_Follow:
case AI_Travel:
case AI_CNDT:
mAiPackage.add(esm);
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasNpdt)
esm.fail("Missing NPDT subrecord");
if (!hasFlags)
esm.fail("Missing FLAG subrecord");
} }
mInventory.save(esm); void Creature::save(ESMWriter &esm) const
mSpells.save(esm); {
if (mHasAI) { esm.writeHNCString("MODL", mModel);
esm.writeHNT("AIDT", mAiData, sizeof(mAiData)); esm.writeHNOCString("CNAM", mOriginal);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNT("NPDT", mData, 96);
esm.writeHNT("FLAG", mFlags);
if (mScale != 1.0) {
esm.writeHNT("XSCL", mScale);
}
mInventory.save(esm);
mSpells.save(esm);
if (mHasAI) {
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
}
mAiPackage.save(esm);
} }
mAiPackage.save(esm);
}
void Creature::blank() void Creature::blank()
{ {

View file

@ -10,8 +10,28 @@ namespace ESM
void Enchantment::load(ESMReader &esm) void Enchantment::load(ESMReader &esm)
{ {
esm.getHNT(mData, "ENDT", 16); mEffects.mList.clear();
mEffects.load(esm); bool hasData = false;
while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'E','N','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
mEffects.add(esm);
break;
default:
esm.fail("Unknown subrecord");
break;
}
}
if (!hasData)
esm.fail("Missing ENDT subrecord");
} }
void Enchantment::save(ESMWriter &esm) const void Enchantment::save(ESMWriter &esm) const

View file

@ -28,27 +28,46 @@ namespace ESM
void Faction::load(ESMReader &esm) void Faction::load(ESMReader &esm)
{ {
mName = esm.getHNOString("FNAM"); mReactions.clear();
for (int i=0;i<10;++i)
mRanks[i].clear();
// Read rank names. These are optional. int rankCounter=0;
int i = 0; bool hasData = false;
while (esm.isNextSub("RNAM") && i < 10)
mRanks[i++] = esm.getHString();
// Main data struct
esm.getHNT(mData, "FADT", 240);
if (mData.mIsHidden > 1)
esm.fail("Unknown flag!");
// Read faction response values
while (esm.hasMoreSubs()) while (esm.hasMoreSubs())
{ {
std::string faction = esm.getHNString("ANAM"); esm.getSubName();
int reaction; uint32_t name = esm.retSubName().val;
esm.getHNT(reaction, "INTV"); switch (name)
mReactions[faction] = reaction; {
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'R','N','A','M'>::value:
if (rankCounter >= 10)
esm.fail("Rank out of range");
mRanks[rankCounter++] = esm.getHString();
break;
case ESM::FourCC<'F','A','D','T'>::value:
esm.getHT(mData, 240);
if (mData.mIsHidden > 1)
esm.fail("Unknown flag!");
hasData = true;
break;
case ESM::FourCC<'A','N','A','M'>::value:
{
std::string faction = esm.getHString();
int reaction;
esm.getHNT(reaction, "INTV");
mReactions[faction] = reaction;
break;
}
default:
esm.fail("Unknown subrecord");
}
} }
if (!hasData)
esm.fail("Missing FADT subrecord");
} }
void Faction::save(ESMWriter &esm) const void Faction::save(ESMWriter &esm) const
{ {

View file

@ -8,45 +8,71 @@ namespace ESM
{ {
unsigned int Ingredient::sRecordId = REC_INGR; unsigned int Ingredient::sRecordId = REC_INGR;
void Ingredient::load(ESMReader &esm) void Ingredient::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
esm.getHNT(mData, "IRDT", 56);
mScript = esm.getHNOString("SCRI");
mIcon = esm.getHNOString("ITEX");
// horrible hack to fix broken data in records
for (int i=0; i<4; ++i)
{ {
if (mData.mEffectID[i] != 85 && bool hasData = false;
mData.mEffectID[i] != 22 && while (esm.hasMoreSubs())
mData.mEffectID[i] != 17 &&
mData.mEffectID[i] != 79 &&
mData.mEffectID[i] != 74)
{ {
mData.mAttributes[i] = -1; esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'I','R','D','T'>::value:
esm.getHT(mData, 56);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
} }
// is this relevant in cycle from 0 to 4? if (!hasData)
if (mData.mEffectID[i] != 89 && esm.fail("Missing IRDT subrecord");
mData.mEffectID[i] != 26 &&
mData.mEffectID[i] != 21 && // horrible hack to fix broken data in records
mData.mEffectID[i] != 83 && for (int i=0; i<4; ++i)
mData.mEffectID[i] != 78)
{ {
mData.mSkills[i] = -1; if (mData.mEffectID[i] != 85 &&
mData.mEffectID[i] != 22 &&
mData.mEffectID[i] != 17 &&
mData.mEffectID[i] != 79 &&
mData.mEffectID[i] != 74)
{
mData.mAttributes[i] = -1;
}
// is this relevant in cycle from 0 to 4?
if (mData.mEffectID[i] != 89 &&
mData.mEffectID[i] != 26 &&
mData.mEffectID[i] != 21 &&
mData.mEffectID[i] != 83 &&
mData.mEffectID[i] != 78)
{
mData.mSkills[i] = -1;
}
} }
} }
}
void Ingredient::save(ESMWriter &esm) const void Ingredient::save(ESMWriter &esm) const
{ {
esm.writeHNCString("MODL", mModel); esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName); esm.writeHNOCString("FNAM", mName);
esm.writeHNT("IRDT", mData, 56); esm.writeHNT("IRDT", mData, 56);
esm.writeHNOCString("SCRI", mScript); esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon); esm.writeHNOCString("ITEX", mIcon);
} }
void Ingredient::blank() void Ingredient::blank()
{ {

View file

@ -7,49 +7,51 @@
namespace ESM namespace ESM
{ {
void LevelledListBase::load(ESMReader &esm) void LevelledListBase::load(ESMReader &esm)
{
esm.getHNT(mFlags, "DATA");
esm.getHNT(mChanceNone, "NNAM");
if (esm.isNextSub("INDX"))
{ {
int len; esm.getHNT(mFlags, "DATA");
esm.getHT(len); esm.getHNT(mChanceNone, "NNAM");
mList.resize(len);
}
else
{
esm.skipRecord();
return;
}
// If this levelled list was already loaded by a previous content file, if (esm.isNextSub("INDX"))
// we overwrite the list. Merging lists should probably be left to external tools, {
// with the limited amount of information there is in the records, all merging methods int len;
// will be flawed in some way. For a proper fix the ESM format would have to be changed esm.getHT(len);
// to actually track list changes instead of including the whole list for every file mList.resize(len);
// that does something with that list. }
else
{
// Original engine ignores rest of the record, even if there are items following
mList.clear();
esm.skipRecord();
return;
}
for (size_t i = 0; i < mList.size(); i++) // If this levelled list was already loaded by a previous content file,
{ // we overwrite the list. Merging lists should probably be left to external tools,
LevelItem &li = mList[i]; // with the limited amount of information there is in the records, all merging methods
li.mId = esm.getHNString(mRecName); // will be flawed in some way. For a proper fix the ESM format would have to be changed
esm.getHNT(li.mLevel, "INTV"); // to actually track list changes instead of including the whole list for every file
} // that does something with that list.
}
void LevelledListBase::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mFlags);
esm.writeHNT("NNAM", mChanceNone);
esm.writeHNT<int>("INDX", mList.size());
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it) for (size_t i = 0; i < mList.size(); i++)
{ {
esm.writeHNCString(mRecName, it->mId); LevelItem &li = mList[i];
esm.writeHNT("INTV", it->mLevel); li.mId = esm.getHNString(mRecName);
esm.getHNT(li.mLevel, "INTV");
}
}
void LevelledListBase::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mFlags);
esm.writeHNT("NNAM", mChanceNone);
esm.writeHNT<int>("INDX", mList.size());
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{
esm.writeHNCString(mRecName, it->mId);
esm.writeHNT("INTV", it->mLevel);
}
} }
}
void LevelledListBase::blank() void LevelledListBase::blank()
{ {

View file

@ -8,25 +8,50 @@ namespace ESM
{ {
unsigned int Light::sRecordId = REC_LIGH; unsigned int Light::sRecordId = REC_LIGH;
void Light::load(ESMReader &esm) void Light::load(ESMReader &esm)
{ {
mModel = esm.getHNOString("MODL"); bool hasData = false;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
mIcon = esm.getHNOString("ITEX"); {
assert(sizeof(mData) == 24); esm.getSubName();
esm.getHNT(mData, "LHDT", 24); uint32_t name = esm.retSubName().val;
mScript = esm.getHNOString("SCRI"); switch (name)
mSound = esm.getHNOString("SNAM"); {
} case ESM::FourCC<'M','O','D','L'>::value:
void Light::save(ESMWriter &esm) const mModel = esm.getHString();
{ break;
esm.writeHNCString("MODL", mModel); case ESM::FourCC<'F','N','A','M'>::value:
esm.writeHNOCString("FNAM", mName); mName = esm.getHString();
esm.writeHNOCString("ITEX", mIcon); break;
esm.writeHNT("LHDT", mData, 24); case ESM::FourCC<'I','T','E','X'>::value:
esm.writeHNOCString("SCRI", mScript); mIcon = esm.getHString();
esm.writeHNOCString("SNAM", mSound); break;
} case ESM::FourCC<'L','H','D','T'>::value:
esm.getHT(mData, 24);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'S','N','A','M'>::value:
mSound = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing LHDT subrecord");
}
void Light::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("ITEX", mIcon);
esm.writeHNT("LHDT", mData, 24);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("SNAM", mSound);
}
void Light::blank() void Light::blank()
{ {

View file

@ -8,26 +8,48 @@ namespace ESM
{ {
unsigned int Lockpick::sRecordId = REC_LOCK; unsigned int Lockpick::sRecordId = REC_LOCK;
void Lockpick::load(ESMReader &esm) void Lockpick::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = true;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'L','K','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing LKDT subrecord");
}
esm.getHNT(mData, "LKDT", 16); void Lockpick::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
mScript = esm.getHNOString("SCRI"); esm.writeHNT("LKDT", mData, 16);
mIcon = esm.getHNOString("ITEX"); esm.writeHNOString("SCRI", mScript);
} esm.writeHNOCString("ITEX", mIcon);
}
void Lockpick::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("LKDT", mData, 16);
esm.writeHNOString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Lockpick::blank() void Lockpick::blank()
{ {

View file

@ -246,7 +246,7 @@ void MagicEffect::load(ESMReader &esm)
mDescription = esm.getHString(); mDescription = esm.getHString();
break; break;
default: default:
esm.fail("Unknown subrecord " + esm.retSubName().toString()); esm.fail("Unknown subrecord");
} }
} }
} }

View file

@ -57,9 +57,9 @@ struct MagicEffect
// Glow color for enchanted items with this effect // Glow color for enchanted items with this effect
int mRed, mGreen, mBlue; int mRed, mGreen, mBlue;
float mUnknown1; float mUnknown1; // Called "Size X" in CS
float mSpeed; // Speed of fired projectile float mSpeed; // Speed of fired projectile
float mUnknown2; float mUnknown2; // Called "Size Cap" in CS
}; // 36 bytes }; // 36 bytes
static const std::map<short,std::string> sNames; static const std::map<short,std::string> sNames;

View file

@ -99,7 +99,7 @@ namespace ESM
mAiPackage.add(esm); mAiPackage.add(esm);
break; break;
default: default:
esm.fail("Unknown subrecord " + esm.retSubName().toString()); esm.fail("Unknown subrecord");
} }
} }
if (!hasNpdt) if (!hasNpdt)

View file

@ -8,26 +8,48 @@ namespace ESM
{ {
unsigned int Probe::sRecordId = REC_PROB; unsigned int Probe::sRecordId = REC_PROB;
void Probe::load(ESMReader &esm) void Probe::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = true;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'P','B','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing PBDT subrecord");
}
esm.getHNT(mData, "PBDT", 16); void Probe::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
mScript = esm.getHNOString("SCRI"); esm.writeHNT("PBDT", mData, 16);
mIcon = esm.getHNOString("ITEX"); esm.writeHNOString("SCRI", mScript);
} esm.writeHNOCString("ITEX", mIcon);
}
void Probe::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("PBDT", mData, 16);
esm.writeHNOString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Probe::blank() void Probe::blank()
{ {

View file

@ -43,7 +43,7 @@ void Race::load(ESMReader &esm)
mPowers.add(esm); mPowers.add(esm);
break; break;
default: default:
esm.fail("Unknown subrecord " + esm.retSubName().toString()); esm.fail("Unknown subrecord");
} }
} }
if (!hasData) if (!hasData)

View file

@ -10,13 +10,35 @@ namespace ESM
void Repair::load(ESMReader &esm) void Repair::load(ESMReader &esm)
{ {
mModel = esm.getHNString("MODL"); bool hasData = true;
mName = esm.getHNOString("FNAM"); while (esm.hasMoreSubs())
{
esm.getHNT(mData, "RIDT", 16); esm.getSubName();
uint32_t name = esm.retSubName().val;
mScript = esm.getHNOString("SCRI"); switch (name)
mIcon = esm.getHNOString("ITEX"); {
case ESM::FourCC<'M','O','D','L'>::value:
mModel = esm.getHString();
break;
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
break;
case ESM::FourCC<'R','I','D','T'>::value:
esm.getHT(mData, 16);
hasData = true;
break;
case ESM::FourCC<'S','C','R','I'>::value:
mScript = esm.getHString();
break;
case ESM::FourCC<'I','T','E','X'>::value:
mIcon = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing RIDT subrecord");
} }
void Repair::save(ESMWriter &esm) const void Repair::save(ESMWriter &esm) const

View file

@ -9,17 +9,7 @@ namespace ESM
unsigned int Script::sRecordId = REC_SCPT; unsigned int Script::sRecordId = REC_SCPT;
void Script::load(ESMReader &esm) void Script::loadSCVR(ESMReader &esm)
{
SCHD data;
esm.getHNT(data, "SCHD", 52);
mData = data.mData;
mId = data.mName.toString();
mVarNames.clear();
// List of local variables
if (esm.isNextSub("SCVR"))
{ {
int s = mData.mStringTableSize; int s = mData.mStringTableSize;
@ -66,58 +56,70 @@ void Script::load(ESMReader &esm)
} }
} }
// Script mData void Script::load(ESMReader &esm)
if (esm.isNextSub("SCDT"))
{ {
mScriptData.resize(mData.mScriptDataSize); SCHD data;
esm.getHExact(&mScriptData[0], mScriptData.size()); esm.getHNT(data, "SCHD", 52);
} mData = data.mData;
mId = data.mName.toString();
// Script text mVarNames.clear();
mScriptText = esm.getHNOString("SCTX");
// NOTE: A minor hack/workaround... while (esm.hasMoreSubs())
//
// MAO_Containers.esp from Morrowind Acoustic Overhaul has SCVR records
// at the end (see Bug #1849). Since OpenMW does not use SCVR subrecords
// for variable names just skip these as a quick fix. An alternative
// solution would be to decode and validate SCVR subrecords even if they
// appear here.
if (esm.isNextSub("SCVR")) {
esm.skipHSub();
}
}
void Script::save(ESMWriter &esm) const
{
std::string varNameString;
if (!mVarNames.empty())
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
varNameString.append(*it);
SCHD data;
memset(&data, 0, sizeof(data));
data.mData = mData;
memcpy(data.mName.name, mId.c_str(), mId.size());
esm.writeHNT("SCHD", data, 52);
if (!mVarNames.empty())
{
esm.startSubRecord("SCVR");
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
{ {
esm.writeHCString(*it); esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'S','C','V','R'>::value:
// list of local variables
loadSCVR(esm);
break;
case ESM::FourCC<'S','C','D','T'>::value:
// compiled script
mScriptData.resize(mData.mScriptDataSize);
esm.getHExact(&mScriptData[0], mScriptData.size());
break;
case ESM::FourCC<'S','C','T','X'>::value:
mScriptText = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
} }
esm.endRecord("SCVR");
} }
esm.startSubRecord("SCDT"); void Script::save(ESMWriter &esm) const
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize); {
esm.endRecord("SCDT"); std::string varNameString;
if (!mVarNames.empty())
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
varNameString.append(*it);
esm.writeHNOString("SCTX", mScriptText); SCHD data;
} memset(&data, 0, sizeof(data));
data.mData = mData;
memcpy(data.mName.name, mId.c_str(), mId.size());
esm.writeHNT("SCHD", data, 52);
if (!mVarNames.empty())
{
esm.startSubRecord("SCVR");
for (std::vector<std::string>::const_iterator it = mVarNames.begin(); it != mVarNames.end(); ++it)
{
esm.writeHCString(*it);
}
esm.endRecord("SCVR");
}
esm.startSubRecord("SCDT");
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize);
esm.endRecord("SCDT");
esm.writeHNOString("SCTX", mScriptText);
}
void Script::blank() void Script::blank()
{ {

View file

@ -53,6 +53,9 @@ public:
void blank(); void blank();
///< Set record to default state (does not touch the ID/index). ///< Set record to default state (does not touch the ID/index).
private:
void loadSCVR(ESMReader &esm);
}; };
} }
#endif #endif

View file

@ -129,23 +129,47 @@ namespace ESM
unsigned int Skill::sRecordId = REC_SKIL; unsigned int Skill::sRecordId = REC_SKIL;
void Skill::load(ESMReader &esm) void Skill::load(ESMReader &esm)
{ {
esm.getHNT(mIndex, "INDX"); bool hasIndex = false;
esm.getHNT(mData, "SKDT", 24); bool hasData = false;
mDescription = esm.getHNOString("DESC"); while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'I','N','D','X'>::value:
esm.getHT(mIndex);
hasIndex = true;
break;
case ESM::FourCC<'S','K','D','T'>::value:
esm.getHT(mData, 24);
hasData = true;
break;
case ESM::FourCC<'D','E','S','C'>::value:
mDescription = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasIndex)
esm.fail("Missing INDX");
if (!hasData)
esm.fail("Missing SKDT");
// create an ID from the index and the name (only used in the editor and likely to change in the // create an ID from the index and the name (only used in the editor and likely to change in the
// future) // future)
mId = indexToId (mIndex); mId = indexToId (mIndex);
} }
void Skill::save(ESMWriter &esm) const void Skill::save(ESMWriter &esm) const
{ {
esm.writeHNT("INDX", mIndex); esm.writeHNT("INDX", mIndex);
esm.writeHNT("SKDT", mData, 24); esm.writeHNT("SKDT", mData, 24);
esm.writeHNOString("DESC", mDescription); esm.writeHNOString("DESC", mDescription);
} }
void Skill::blank() void Skill::blank()
{ {

View file

@ -8,19 +8,38 @@ namespace ESM
{ {
unsigned int SoundGenerator::sRecordId = REC_SNDG; unsigned int SoundGenerator::sRecordId = REC_SNDG;
void SoundGenerator::load(ESMReader &esm) void SoundGenerator::load(ESMReader &esm)
{ {
esm.getHNT(mType, "DATA", 4); bool hasData = false;
while (esm.hasMoreSubs())
mCreature = esm.getHNOString("CNAM"); {
mSound = esm.getHNOString("SNAM"); esm.getSubName();
} uint32_t name = esm.retSubName().val;
void SoundGenerator::save(ESMWriter &esm) const switch (name)
{ {
esm.writeHNT("DATA", mType, 4); case ESM::FourCC<'D','A','T','A'>::value:
esm.writeHNOCString("CNAM", mCreature); esm.getHT(mType, 4);
esm.writeHNOCString("SNAM", mSound); hasData = true;
} break;
case ESM::FourCC<'C','N','A','M'>::value:
mCreature = esm.getHString();
break;
case ESM::FourCC<'S','N','A','M'>::value:
mSound = esm.getHString();
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing DATA");
}
void SoundGenerator::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mType, 4);
esm.writeHNOCString("CNAM", mCreature);
esm.writeHNOCString("SNAM", mSound);
}
void SoundGenerator::blank() void SoundGenerator::blank()
{ {

View file

@ -8,22 +8,35 @@ namespace ESM
{ {
unsigned int Sound::sRecordId = REC_SOUN; unsigned int Sound::sRecordId = REC_SOUN;
void Sound::load(ESMReader &esm) void Sound::load(ESMReader &esm)
{ {
mSound = esm.getHNOString("FNAM"); bool hasData = false;
esm.getHNT(mData, "DATA", 3); while (esm.hasMoreSubs())
/* {
cout << "vol=" << (int)data.volume esm.getSubName();
<< " min=" << (int)data.minRange uint32_t name = esm.retSubName().val;
<< " max=" << (int)data.maxRange switch (name)
<< endl; {
*/ case ESM::FourCC<'F','N','A','M'>::value:
} mSound = esm.getHString();
void Sound::save(ESMWriter &esm) const break;
{ case ESM::FourCC<'D','A','T','A'>::value:
esm.writeHNOCString("FNAM", mSound); esm.getHT(mData, 3);
esm.writeHNT("DATA", mData, 3); hasData = true;
} break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing DATA");
}
void Sound::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mSound);
esm.writeHNT("DATA", mData, 3);
}
void Sound::blank() void Sound::blank()
{ {

View file

@ -8,40 +8,41 @@ namespace ESM
{ {
unsigned int Spell::sRecordId = REC_SPEL; unsigned int Spell::sRecordId = REC_SPEL;
void Spell::load(ESMReader &esm) void Spell::load(ESMReader &esm)
{
bool hasData = false;
while (esm.hasMoreSubs())
{ {
esm.getSubName(); mEffects.mList.clear();
uint32_t val = esm.retSubName().val; bool hasData = false;
while (esm.hasMoreSubs())
switch (val)
{ {
case ESM::FourCC<'F','N','A','M'>::value: esm.getSubName();
mName = esm.getHString(); uint32_t val = esm.retSubName().val;
break;
case ESM::FourCC<'S','P','D','T'>::value:
esm.getHT(mData, 12);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
ENAMstruct s;
esm.getHT(s, 24);
mEffects.mList.push_back(s);
break;
}
}
if (!hasData)
esm.fail("Missing SPDT subrecord");
}
void Spell::save(ESMWriter &esm) const switch (val)
{ {
esm.writeHNOCString("FNAM", mName); case ESM::FourCC<'F','N','A','M'>::value:
esm.writeHNT("SPDT", mData, 12); mName = esm.getHString();
mEffects.save(esm); break;
} case ESM::FourCC<'S','P','D','T'>::value:
esm.getHT(mData, 12);
hasData = true;
break;
case ESM::FourCC<'E','N','A','M'>::value:
ENAMstruct s;
esm.getHT(s, 24);
mEffects.mList.push_back(s);
break;
}
}
if (!hasData)
esm.fail("Missing SPDT subrecord");
}
void Spell::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("SPDT", mData, 12);
mEffects.save(esm);
}
void Spell::blank() void Spell::blank()
{ {

View file

@ -8,15 +8,37 @@ namespace ESM
{ {
unsigned int StartScript::sRecordId = REC_SSCR; unsigned int StartScript::sRecordId = REC_SSCR;
void StartScript::load(ESMReader &esm) void StartScript::load(ESMReader &esm)
{ {
mData = esm.getHNString("DATA"); bool hasData = false;
mScript = esm.getHNString("NAME"); bool hasName = false;
} while (esm.hasMoreSubs())
void StartScript::save(ESMWriter &esm) const {
{ esm.getSubName();
esm.writeHNString("DATA", mData); uint32_t name = esm.retSubName().val;
esm.writeHNString("NAME", mScript); switch (name)
} {
case ESM::FourCC<'D','A','T','A'>::value:
mData = esm.getHString();
hasData = true;
break;
case ESM::FourCC<'N','A','M','E'>::value:
mScript = esm.getHString();
hasName = true;
break;
default:
esm.fail("Unknown subrecord");
}
}
if (!hasData)
esm.fail("Missing DATA");
if (!hasName)
esm.fail("Missing NAME");
}
void StartScript::save(ESMWriter &esm) const
{
esm.writeHNString("DATA", mData);
esm.writeHNString("NAME", mScript);
}
} }

View file

@ -8,15 +8,16 @@ namespace ESM
{ {
unsigned int Static::sRecordId = REC_STAT; unsigned int Static::sRecordId = REC_STAT;
void Static::load(ESMReader &esm) void Static::load(ESMReader &esm)
{ {
mPersistent = esm.getRecordFlags() & 0x0400; mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL");
} mModel = esm.getHNString("MODL");
void Static::save(ESMWriter &esm) const }
{ void Static::save(ESMWriter &esm) const
esm.writeHNCString("MODL", mModel); {
} esm.writeHNCString("MODL", mModel);
}
void Static::blank() void Static::blank()
{ {

View file

@ -50,8 +50,8 @@ namespace ESM
NAME32 mPlayerName; NAME32 mPlayerName;
}; };
GMDT mGameData; // Used in .ess savegames only GMDT mGameData; // Used in .ess savegames only
std::vector<unsigned char> mSCRD; // Used in .ess savegames only, screenshot? std::vector<unsigned char> mSCRD; // Used in .ess savegames only, unknown
std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot? std::vector<unsigned char> mSCRS; // Used in .ess savegames only, screenshot
Data mData; Data mData;
int mFormat; int mFormat;

View file

@ -5,14 +5,6 @@
namespace ESM { namespace ESM {
void SpellList::load(ESMReader &esm)
{
mList.clear();
while (esm.isNextSub("NPCS")) {
add(esm);
}
}
void SpellList::add(ESMReader &esm) void SpellList::add(ESMReader &esm)
{ {
mList.push_back(esm.getHString()); mList.push_back(esm.getHString());

View file

@ -11,6 +11,7 @@ namespace ESM
/** A list of references to spells and spell effects. This is shared /** A list of references to spells and spell effects. This is shared
between the records BSGN, NPC and RACE. between the records BSGN, NPC and RACE.
NPCS subrecord.
*/ */
struct SpellList struct SpellList
{ {
@ -22,9 +23,6 @@ namespace ESM
/// Load one spell, assumes the subrecord name was already read /// Load one spell, assumes the subrecord name was already read
void add(ESMReader &esm); void add(ESMReader &esm);
/// Load all spells
/// TODO: remove this method, the ESM format doesn't guarantee that all spell subrecords follow one another
void load(ESMReader &esm);
void save(ESMWriter &esm) const; void save(ESMWriter &esm) const;
}; };
} }

View file

@ -27,7 +27,7 @@ char const * Buffer::getData()
} }
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_getPathToJni(JNIEnv *env, jobject obj, jstring prompt) JNIEXPORT void JNICALL Java_ui_activity_GameActivity_getPathToJni(JNIEnv *env, jobject obj, jstring prompt)
{ {
jboolean iscopy; jboolean iscopy;
Buffer::setData((env)->GetStringUTFChars(prompt, &iscopy)); Buffer::setData((env)->GetStringUTFChars(prompt, &iscopy));

View file

@ -1,8 +1,8 @@
/* DO NOT EDIT THIS FILE - it is machine generated */ /* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h> #include <jni.h>
#ifndef _Included_org_libsdl_app_SDLActivity_getPathToJni #ifndef _Included_ui_activity_GameActivity_getPathToJni
#define _Included_org_libsdl_app_SDLActivity_getPathToJni #define _Included_ui_activity_GameActivity_getPathToJni
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
#endif #endif
@ -11,7 +11,7 @@ extern "C" {
* Method: getPathToJni * Method: getPathToJni
* Signature: (I)I * Signature: (I)I
*/ */
JNIEXPORT void JNICALL Java_org_libsdl_app_SDLActivity_getPathToJni(JNIEnv *env, jobject obj, jstring prompt); JNIEXPORT void JNICALL Java_ui_activity_GameActivity_getPathToJni(JNIEnv *env, jobject obj, jstring prompt);
#ifdef __cplusplus #ifdef __cplusplus
} }

View file

@ -1 +0,0 @@
old_d

View file

@ -152,9 +152,9 @@ namespace Gui
eventWidgetSelected(_sender); eventWidgetSelected(_sender);
} }
MyGUI::Widget* MWList::getItemWidget(const std::string& name) MyGUI::Button *MWList::getItemWidget(const std::string& name)
{ {
return mScrollView->findWidget (getName() + "_item_" + name); return mScrollView->findWidget (getName() + "_item_" + name)->castType<MyGUI::Button>();
} }
} }

View file

@ -43,7 +43,7 @@ namespace Gui
std::string getItemNameAt(unsigned int at); ///< \attention if there are separators, this method will return "" at the place where the separator is std::string getItemNameAt(unsigned int at); ///< \attention if there are separators, this method will return "" at the place where the separator is
void clear(); void clear();
MyGUI::Widget* getItemWidget(const std::string& name); MyGUI::Button* getItemWidget(const std::string& name);
///< get widget for an item name, useful to set up tooltip ///< get widget for an item name, useful to set up tooltip
virtual void setPropertyOverride(const std::string& _key, const std::string& _value); virtual void setPropertyOverride(const std::string& _key, const std::string& _value);

View file

@ -243,7 +243,9 @@
} }
#else #else
#if NORMAL_MAP && SH_GLSLES
mat3 transpose( mat3 m);
#endif
// ----------------------------------- FRAGMENT ------------------------------------------ // ----------------------------------- FRAGMENT ------------------------------------------
#if UNDERWATER #if UNDERWATER
@ -376,13 +378,13 @@
float3 binormal = cross(tangentPassthrough.xyz, normal.xyz); float3 binormal = cross(tangentPassthrough.xyz, normal.xyz);
float3x3 tbn = float3x3(tangentPassthrough.xyz, binormal, normal.xyz); float3x3 tbn = float3x3(tangentPassthrough.xyz, binormal, normal.xyz);
#if SH_GLSL #if SH_GLSL || SH_GLSLES
tbn = transpose(tbn); tbn = transpose(tbn);
#endif #endif
float4 normalTex = shSample(normalMap, UV.xy); float4 normalTex = shSample(normalMap, UV.xy);
normal = normalize (shMatrixMult( transpose(tbn), normalTex.xyz * 2 - 1 )); normal = normalize (shMatrixMult( transpose(tbn), normalTex.xyz * 2.0 - float3 (1.0,1.0,1.0) ));
#endif #endif
#if ENV_MAP || SPECULAR || PARALLAX #if ENV_MAP || SPECULAR || PARALLAX
@ -576,5 +578,14 @@
// prevent negative colour output (for example with negative lights) // prevent negative colour output (for example with negative lights)
shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0.0,0.0,0.0)); shOutputColour(0).xyz = max(shOutputColour(0).xyz, float3(0.0,0.0,0.0));
} }
#if NORMAL_MAP && SH_GLSLES
mat3 transpose(mat3 m){
return mat3(
m[0][0],m[1][0],m[2][0],
m[0][1],m[1][1],m[2][1],
m[0][2],m[1][2],m[2][2]
);
}
#endif
#endif #endif

View file

@ -1,8 +1,8 @@
particle_system openmw/Ripples particle_system openmw/Ripples
{ {
material openmw/Ripple material openmw/Ripple
particle_width 50 particle_width 30
particle_height 50 particle_height 30
// To make the particles move with the scene node when the waterlevel changes // To make the particles move with the scene node when the waterlevel changes
local_space true local_space true
quota 300 quota 300
@ -17,7 +17,7 @@ particle_system openmw/Ripples
affector Scaler affector Scaler
{ {
rate 100 rate 120
} }
affector Rotator affector Rotator

View file

@ -221,6 +221,9 @@
#if UNDERWATER #if UNDERWATER
#include "underwater.h" #include "underwater.h"
#endif #endif
#if NORMAL_MAP && SH_GLSLES
mat3 transpose(mat3 m);
#endif
SH_BEGIN_PROGRAM SH_BEGIN_PROGRAM
@ -319,7 +322,7 @@ shUniform(float4, cameraPos) @shAutoConstant(cameraPos, camera_position)
// derive final matrix // derive final matrix
float3x3 tbn = float3x3(tangent, binormal, normal); float3x3 tbn = float3x3(tangent, binormal, normal);
#if SH_GLSL #if SH_GLSL || SH_GLSLES
tbn = transpose(tbn); tbn = transpose(tbn);
#endif #endif
#endif #endif
@ -492,5 +495,13 @@ albedo = shLerp(albedo, diffuseTex, blendValues@shPropertyString(blendmap_compon
shOutputColour(0).a = 1.0-previousAlpha; shOutputColour(0).a = 1.0-previousAlpha;
#endif #endif
} }
#if NORMAL_MAP && SH_GLSLES
mat3 transpose(mat3 m){
return mat3(
m[0][0],m[1][0],m[2][0],
m[0][1],m[1][1],m[2][1],
m[0][2],m[1][2],m[2][2]
);
}
#endif
#endif #endif

View file

@ -112,7 +112,8 @@
<Property key="ImageTexture" value="textures\compass.dds"/> <Property key="ImageTexture" value="textures\compass.dds"/>
</Widget> </Widget>
<Widget type="Button" skin="" position="0 0 1536 1536" name="MiniMapButton" align="Right Bottom"> <Widget type="Button" skin="" position_real="0 0 1 1" name="MiniMapButton" align="Stretch">
<Property key="Depth" value="10"/>
</Widget> </Widget>
</Widget> </Widget>

View file

@ -28,9 +28,9 @@
<State name="normal" colour="#{fontcolour=journal_topic}" shift="0"/> <State name="normal" colour="#{fontcolour=journal_topic}" shift="0"/>
<State name="highlighted" colour="#{fontcolour=journal_topic_over}" shift="0"/> <State name="highlighted" colour="#{fontcolour=journal_topic_over}" shift="0"/>
<State name="pushed" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="pushed" colour="#{fontcolour=journal_topic_pressed}" shift="0"/>
<State name="normal_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="normal_checked" colour="0.2 0.2 0.2" shift="0"/>
<State name="highlighted_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="highlighted_checked" colour="0.4 0.4 0.4" shift="0"/>
<State name="pushed_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/> <State name="pushed_checked" colour="0.5 0.5 0.5" shift="0"/>
</BasisSkin> </BasisSkin>
</Resource> </Resource>
</MyGUI> </MyGUI>

View file

@ -235,7 +235,9 @@
<!-- Player skills, factions, birthsign and reputation --> <!-- Player skills, factions, birthsign and reputation -->
<Widget type="Widget" skin="MW_Box" position="8 8 248 292" align="Left Stretch" name="Skills"> <Widget type="Widget" skin="MW_Box" position="8 8 248 292" align="Left Stretch" name="Skills">
<Widget type="ScrollView" skin="MW_ScrollView" position="4 4 240 284" align="Left Top Stretch" name="SkillView"/> <Widget type="ScrollView" skin="MW_ScrollView" position="4 4 240 284" align="Left Top Stretch" name="SkillView">
<Property key="CanvasAlign" value="Left Top"/>
</Widget>
</Widget> </Widget>
</Widget> </Widget>

View file

@ -131,6 +131,13 @@
</property> </property>
</widget> </widget>
</item> </item>
<item row="3" column="0">
<widget class="QProgressBar" name="progressBar">
<property name="maximum">
<number>4</number>
</property>
</widget>
</item>
</layout> </layout>
</item> </item>
</layout> </layout>