1
0
Fork 1
mirror of https://github.com/TES3MP/openmw-tes3mp.git synced 2025-01-16 17:19:56 +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:
- cd ./build
- 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:
- if [ "${TRAVIS_OS_NAME}" = "linux" ]; then ./openmw_test_suite; fi
notifications:

View file

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

View file

@ -672,6 +672,7 @@ if (WIN32)
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'
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
4191 # 'type cast' : unsafe conversion (1.56, thread_primitives.hpp, normally off)
@ -679,6 +680,7 @@ if (WIN32)
# OpenMW specific warnings
4099 # Type mismatch, declared class or struct is defined with other type
4100 # Unreferenced formal parameter (-Wunused-parameter)
4101 # Unreferenced local variable (-Wunused-variable)
4127 # Conditional expression is constant
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)
@ -700,14 +702,16 @@ if (WIN32)
# boost::wave has a few issues with signed / unsigned conversions, so we suppress those here
set(SHINY_WARNINGS "${WARNINGS} /wd4245")
set_target_properties(shiny PROPERTIES COMPILE_FLAGS "${SHINY_WARNINGS} ${MT_BUILD}")
# there's an unreferenced local variable in the ogre platform, suppress it
set(SHINY_OGRE_WARNINGS "${WARNINGS} /wd4101")
set_target_properties(shiny.OgrePlatform PROPERTIES COMPILE_FLAGS "${SHINY_OGRE_WARNINGS} ${MT_BUILD}")
set_target_properties(shiny.OgrePlatform 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
set(OICS_WARNINGS "${WARNINGS} /wd4189")
set_target_properties(oics PROPERTIES COMPILE_FLAGS "${OICS_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)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_LAUNCHER)
@ -726,6 +730,9 @@ if (WIN32)
set(OPENCS_WARNINGS "${WARNINGS} ${MT_BUILD} /wd4435")
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS ${OPENCS_WARNINGS})
endif (BUILD_OPENCS)
if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
endif (BUILD_ESSIMPORTER)
if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS} ${MT_BUILD}")
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
unsigned char mUnknown4[4];
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
{
unsigned char mUnknown1[17];
unsigned char mFlags; // ACSCFlags
unsigned char mUnknown2[94];
unsigned char mUnknown2[22];
unsigned char mCorpseClearCountdown; // hours?
unsigned char mUnknown3[71];
};
#pragma pack(pop)

View file

@ -14,10 +14,10 @@ namespace ESSImport
float scale;
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")
|| esm.isNextSub("AI_A"))
esm.skipHSub();
mAiPackages.add(esm);
mInventory.load(esm);
}

View file

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

View file

@ -9,10 +9,9 @@ namespace ESSImport
{
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")
|| esm.isNextSub("AI_A"))
esm.skipHSub();
mAiPackages.add(esm);
mInventory.load(esm);
}

View file

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

View file

@ -61,6 +61,7 @@ Launcher::MainDialog::MainDialog(QWidget *parent)
QString revision(OPENMW_VERSION_COMMITHASH);
QString tag(OPENMW_VERSION_TAGHASH);
versionLabel->setTextInteractionFlags(Qt::TextSelectableByMouse);
if (!revision.isEmpty() && !tag.isEmpty())
{
if (revision == tag) {
@ -238,24 +239,8 @@ void Launcher::MainDialog::changePage(QListWidgetItem *current, QListWidgetItem
current = previous;
int currentIndex = iconWidget->row(current);
// int previousIndex = iconWidget->row(previous);
pagesWidget->setCurrentIndex(currentIndex);
// 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();
// }
mSettingsPage->resetProgressBar();
}
bool Launcher::MainDialog::setupLauncherSettings()

View file

@ -39,6 +39,7 @@ Launcher::SettingsPage::SettingsPage(Files::ConfigurationManager &cfg,
mWizardInvoker = new ProcessInvoker();
mImporterInvoker = new ProcessInvoker();
resetProgressBar();
connect(mWizardInvoker->getProcess(), SIGNAL(started()),
this, SLOT(wizardStarted()));
@ -94,7 +95,7 @@ Launcher::SettingsPage::~SettingsPage()
void Launcher::SettingsPage::on_wizardButton_clicked()
{
saveSettings();
mMain->writeSettings();
if (!mWizardInvoker->startProcess(QLatin1String("openmw-wizard"), false))
return;
@ -102,7 +103,7 @@ void Launcher::SettingsPage::on_wizardButton_clicked()
void Launcher::SettingsPage::on_importerButton_clicked()
{
saveSettings();
mMain->writeSettings();
// Create the file if it doesn't already exist, else the importer will fail
QString path(QString::fromUtf8(mCfgMgr.getUserConfigPath().string().c_str()));
@ -141,8 +142,13 @@ void Launcher::SettingsPage::on_importerButton_clicked()
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))
return;
{
resetProgressBar();
}
}
void Launcher::SettingsPage::on_browseButton_clicked()
@ -197,38 +203,35 @@ void Launcher::SettingsPage::importerStarted()
void Launcher::SettingsPage::importerFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
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
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);
}
resetProgressBar();
const QString newContentListName(mProfileDialog->lineEdit()->text());
const QStringList files(mGameSettings.getContentList());
mLauncherSettings.setCurrentContentListName(newContentListName);
mLauncherSettings.setContentList(newContentListName, files);
QMessageBox msgBox;
msgBox.setWindowTitle(tr("Importer finished"));
msgBox.setStandardButtons(QMessageBox::Ok);
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.
mMain->reloadSettings();
}
// Importer may have changed settings, so refresh
mMain->reloadSettings();
}
importerButton->setEnabled(true);
}
void Launcher::SettingsPage::resetProgressBar()
{
// set progress bar to 0 %
progressBar->reset();
}
void Launcher::SettingsPage::updateOkButton(const QString &text)
{
// We do this here because we need to access the profiles

View file

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

View file

@ -8,7 +8,8 @@
#include <sstream>
#include <components/misc/stringops.hpp>
#include <boost/filesystem/path.hpp>
#include <boost/version.hpp>
#include <boost/filesystem.hpp>
#include <boost/filesystem/fstream.hpp>
namespace bfs = boost::filesystem;
@ -660,7 +661,7 @@ std::string MwIniImporter::numberToString(int n) {
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::string section("");
@ -719,7 +720,7 @@ MwIniImporter::multistrmap MwIniImporter::loadIniFile(const std::string& filenam
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;
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 {
std::vector<std::string> contentFiles;
void MwIniImporter::importGameFiles(multistrmap &cfg, const multistrmap &ini, const boost::filesystem::path& iniFilename) const {
std::vector<std::pair<std::time_t, std::string> > contentFiles;
std::string baseGameFile("Game Files: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();
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);
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.insert( std::make_pair("content", std::vector<std::string>() ) );
for(std::vector<std::string>::const_iterator it=contentFiles.begin(); it!=contentFiles.end(); ++it) {
cfg["content"].push_back(*it);
// this will sort files by time order first, then alphabetical (maybe), I suspect non ASCII filenames will be stuffed.
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;
}
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 <exception>
#include <iosfwd>
#include <boost/filesystem/path.hpp>
#include <components/to_utf8/to_utf8.hpp>
@ -17,17 +18,22 @@ class MwIniImporter {
MwIniImporter();
void setInputEncoding(const ToUTF8::FromType& encoding);
void setVerbose(bool verbose);
multistrmap loadIniFile(const std::string& filename) const;
static multistrmap loadCfgFile(const std::string& filename);
multistrmap loadIniFile(const boost::filesystem::path& filename) const;
static multistrmap loadCfgFile(const boost::filesystem::path& filename);
void merge(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;
static void writeToFile(std::ostream &out, const multistrmap &cfg);
private:
static void insertMultistrmap(multistrmap &cfg, const std::string& key, const std::string& value);
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;
strmap mMergeMap;
std::vector<std::string> mMergeFallback;

View file

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

View file

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

View file

@ -5,6 +5,7 @@
#include <string>
#include <boost/filesystem.hpp>
#include <boost/algorithm/string/case_conv.hpp>
#include <QHBoxLayout>
#include <QLabel>
@ -72,8 +73,11 @@ void CSVDoc::AdjusterWidget::setName (const QString& name, bool addon)
{
boost::filesystem::path path (name.toUtf8().data());
bool isLegacyPath = (path.extension() == ".esm" ||
path.extension() == ".esp");
std::string extension = path.extension().string();
boost::algorithm::to_lower(extension);
bool isLegacyPath = (extension == ".esm" ||
extension == ".esp");
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)
{
setWindowTitle ("Open CS");
setWindowTitle ("OpenMW-CS");
QVBoxLayout *layout = new QVBoxLayout (this);

View file

@ -268,6 +268,8 @@ namespace MWBase
virtual MWWorld::Ptr getFacedObject() = 0;
///< 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
/// 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.
@ -551,7 +553,7 @@ namespace MWBase
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,
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;

View file

@ -671,4 +671,14 @@ namespace MWGui
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 onMapClicked(MyGUI::Widget* _sender);
// LocalMapBase
virtual void customMarkerCreated(MyGUI::Widget* marker);
virtual void doorMarkerCreated(MyGUI::Widget* marker);
void updateEnemyHealthBar();
void updatePositions();

View file

@ -17,7 +17,7 @@ namespace MWGui
{
JailScreen::JailScreen()
: WindowBase("openmw_jail_screen.layout"),
mTimeAdvancer(0.0125),
mTimeAdvancer(0.01),
mDays(1),
mFadeTimeRemaining(0)
{
@ -39,7 +39,7 @@ namespace MWGui
mFadeTimeRemaining = 0.5;
setVisible(false);
mProgressBar->setScrollRange(days*24+1);
mProgressBar->setScrollRange(100+1);
mProgressBar->setScrollPosition(0);
mProgressBar->setTrackSize(0);
}
@ -59,7 +59,7 @@ namespace MWGui
MWBase::Environment::get().getWorld()->teleportToClosestMarker(player, "prisonmarker");
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 ();
@ -231,7 +231,7 @@ struct JournalViewModelImpl : JournalViewModel
if (visitedQuests.find(quest.getName()) != visitedQuests.end())
continue;
visitor (quest.getName());
visitor (quest.getName(), isFinished);
visitedQuests.insert(quest.getName());
}

View file

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

View file

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

View file

@ -102,6 +102,31 @@ namespace MWGui
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)
{
const Spell& spell = mSpellView->getModel()->getItem(index);
@ -111,43 +136,19 @@ namespace MWGui
}
else
{
onSpellSelected(spell.mId);
if (MyGUI::InputManager::getInstance().isShiftPressed())
askDeleteSpell(spell.mId);
else
onSpellSelected(spell.mId);
}
}
void SpellWindow::onSpellSelected(const std::string& spellId)
{
if (MyGUI::InputManager::getInstance().isShiftPressed())
{
// 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();
}
}
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)));
}
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();
}
@ -184,6 +185,10 @@ namespace MWGui
return;
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 onModelIndexSelected(SpellModel::ModelIndex index);
void onDeleteSpellAccept();
void askDeleteSpell(const std::string& spellId);
virtual void onPinToggled();
virtual void onTitleDoubleClicked();

View file

@ -207,16 +207,6 @@ namespace MWMechanics
const MWWorld::Class& actorClass = actor.getClass();
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
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
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)
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
struct EffectSourceVisitor
{
virtual ~EffectSourceVisitor() { }
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, const std::string& sourceId, int casterActorId,
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());
iter!=effects.mList.end(); ++iter)
{
if (iter->mRange != range)
continue;
found = true;
if (iter->mRange == range)
{
found = true;
break;
}
}
if (!found)
return;
@ -592,6 +594,7 @@ namespace MWMechanics
value.restore(magnitude);
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)
{
applyDynamicStatsEffect(0, target, magnitude * -1);
@ -681,7 +684,7 @@ namespace MWMechanics
void CastSpell::applyDynamicStatsEffect(int attribute, const MWWorld::Ptr& target, float magnitude)
{
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);
}
@ -766,8 +769,7 @@ namespace MWMechanics
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;
@ -851,10 +853,7 @@ namespace MWMechanics
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)
{
if (isSummoningEffect(key.mId) && magnitude > 0)

View file

@ -14,6 +14,7 @@ namespace MWMechanics
struct UpdateSummonedCreatures : public EffectSourceVisitor
{
UpdateSummonedCreatures(const MWWorld::Ptr& actor);
virtual ~UpdateSummonedCreatures();
virtual void visit (MWMechanics::EffectKey key,
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
TextureFilterOptions tfo;
std::string filter = Settings::Manager::getString("texture filtering", "General");
#ifndef ANDROID
if (filter == "anisotropic") tfo = TFO_ANISOTROPIC;
else if (filter == "trilinear") tfo = TFO_TRILINEAR;
else if (filter == "bilinear") tfo = TFO_BILINEAR;
else /*if (filter == "none")*/ tfo = TFO_NONE;
#else
tfo = TFO_NONE;
#endif
MaterialManager::getSingleton().setDefaultTextureFiltering(tfo);
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
rotSpeed = mRippleRotSpeed;
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 "../mwmechanics/spellcasting.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
namespace MWWorld
{
void ActionTrap::executeImp(const Ptr &actor)
{
MWMechanics::CastSpell cast(mTrapSource, actor);
cast.mHitPosition = Ogre::Vector3(actor.getRefData().getPosition().pos);
cast.cast(mSpellId);
Ogre::Vector3 actorPosition(actor.getRefData().getPosition().pos);
Ogre::Vector3 trapPosition(mTrapSource.getRefData().getPosition().pos);
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("");
}

View file

@ -3,6 +3,8 @@
#include <algorithm>
#include <stdexcept>
#include <string>
#include <typeinfo>
#include <boost/shared_ptr.hpp>
#include "livecellref.hpp"
@ -181,7 +183,12 @@ namespace MWWorld
template <class T>
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;
@ -357,6 +364,12 @@ namespace MWWorld
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);
}

View file

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

View file

@ -230,7 +230,7 @@ namespace MWWorld
cell->getCell()->getGridX(),
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.
// 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;

View file

@ -423,6 +423,19 @@ namespace MWWorld
globals["werewolfclawmult"] = ESM::Variant(25.f);
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)
{
if (!mStore.get<ESM::GameSetting>().search(it->first))
@ -2784,18 +2797,45 @@ namespace MWWorld
{
if (cell->isExterior())
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
for (CellRefList<ESM::Door>::List::iterator it = refList.begin(); it != refList.end(); ++it)
{
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
if (ref.mRef.getTeleport() && ref.mRef.getDestCell().empty())
{
ESM::Position pos = ref.mRef.getDoorDest();
result = Ogre::Vector3(pos.pos);
return true;
// Search for a 'nearest' exterior, counting each cell between the starting
// cell and the exterior as a distance of 1. Will fail for isolated interiors.
std::set< std::string >checkedCells;
std::set< std::string >currentCells;
std::set< std::string >nextCells;
nextCells.insert( cell->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 );
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;
}
void World::teleportToClosestMarker (const MWWorld::Ptr& ptr,
const std::string& id)
MWWorld::Ptr World::getClosestMarker( const MWWorld::Ptr &ptr, const std::string &id )
{
Ogre::Vector3 worldPos;
if (!findInteriorPositionInWorldSpace(ptr.getCell(), worldPos))
worldPos = mPlayer->getLastKnownExteriorPosition();
if ( ptr.getCell()->isExterior() ) {
return getClosestMarkerFromExteriorPosition(mPlayer->getLastKnownExteriorPosition(), id);
}
// 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;
float closestDistance = FLT_MAX;
std::vector<MWWorld::Ptr> markers;
mCells.getExteriorPtrs(id, markers);
for (std::vector<MWWorld::Ptr>::iterator it = markers.begin(); it != markers.end(); ++it)
for (std::vector<MWWorld::Ptr>::iterator it2 = markers.begin(); it2 != markers.end(); ++it2)
{
ESM::Position pos = it->getRefData().getPosition();
ESM::Position pos = it2->getRefData().getPosition();
Ogre::Vector3 markerPos = Ogre::Vector3(pos.pos);
float distance = worldPos.squaredDistance(markerPos);
if (distance < closestDistance)
{
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);
}
@ -2978,31 +3087,26 @@ namespace MWWorld
void World::confiscateStolenItems(const Ptr &ptr)
{
Ogre::Vector3 playerPos;
if (!findInteriorPositionInWorldSpace(ptr.getCell(), playerPos))
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)
MWWorld::Ptr prisonMarker = getClosestMarker( ptr, "prisonmarker" );
if ( prisonMarker.isEmpty() )
{
if (!findInteriorPositionInWorldSpace(it->getCell(), chestPos))
continue;
float distance = playerPos.squaredDistance(chestPos);
if (distance < closestDistance)
{
closestDistance = distance;
closestChest = *it;
}
std::cerr << "Failed to confiscate items: no closest prison marker found." << std::endl;
return;
}
std::string prisonName = prisonMarker.mRef->mRef.getDestCell();
if ( prisonName.empty() )
{
std::cerr << "Failed to confiscate items: prison marker not linked to prison interior" << std::endl;
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
{
MWBase::Environment::get().getMechanicsManager()->confiscateStolenItems(ptr, closestChest);
@ -3108,7 +3212,7 @@ namespace MWWorld
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)
{
std::map<MWWorld::Ptr, std::vector<ESM::ENAMstruct> > toApply;
@ -3167,7 +3271,7 @@ namespace MWWorld
cast.mStack = false;
ESM::EffectList effects;
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 getFacedHandle(std::string& facedHandle, float maxDistance, bool ignorePlayer=true);
float getMaxActivationDistance ();
float getNpcActivationDistance ();
float getObjectActivationDistance ();
@ -151,6 +150,9 @@ namespace MWWorld
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:
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);
///< 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)
const;
///< 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 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);

View file

@ -105,6 +105,12 @@ void Config::LauncherSettings::setContentList(const GameSettings& gameSettings)
// obtain content list from game settings (if present)
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
foreach(const QString &listName, getContentLists())
{

View file

@ -110,15 +110,15 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index
if (!file)
return Qt::NoItemFlags;
//game files can always be checked
//game files are not shown
if (file->isGameFile())
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable;
return Qt::NoItemFlags;
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())
{
bool depFound = false;
@ -144,16 +144,11 @@ Qt::ItemFlags ContentSelectorModel::ContentModel::flags(const QModelIndex &index
if (gamefileChecked || !(dependency->isGameFile()))
break;
}
allDependenciesFound = allDependenciesFound && depFound;
}
if (gamefileChecked)
{
if (allDependenciesFound)
returnFlags = returnFlags | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
else
returnFlags = Qt::ItemIsSelectable;
returnFlags = Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsUserCheckable | Qt::ItemIsDragEnabled;
}
return returnFlags;
@ -467,6 +462,14 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
file->setFilePath (info.absoluteFilePath());
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
addFile(file);
@ -481,6 +484,19 @@ void ContentSelectorModel::ContentModel::addFiles(const QString &path)
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()
{
//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());
}
void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList, bool isChecked)
void ContentSelectorModel::ContentModel::setContentList(const QStringList &fileList)
{
mPluginsWithLoadOrderError.clear();
int previousPosition = -1;
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
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<LoadOrderError> errors = QList<LoadOrderError>();
foreach(QString dependentfileName, file->gameFiles())
foreach(const QString &dependentfileName, file->gameFiles())
{
const EsmFile* dependentFile = item(dependentfileName);

View file

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

View file

@ -62,6 +62,13 @@ QByteArray ContentSelectorModel::EsmFile::encodedData() const
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
{
switch (prop)

View file

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

View file

@ -10,6 +10,7 @@
#include <QGridLayout>
#include <QMessageBox>
#include <QModelIndex>
#include <QDir>
#include <assert.h>
ContentSelectorView::ContentSelector::ContentSelector(QWidget *parent) :
@ -33,13 +34,7 @@ void ContentSelectorView::ContentSelector::buildGameFileView()
{
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->setModel(mGameFileProxyModel);
connect (ui.gameFileView, SIGNAL (currentIndexChanged(int)),
this, SLOT (slotCurrentGameFileIndexChanged(int)));
@ -113,7 +108,7 @@ void ContentSelectorView::ContentSelector::setContentList(const QStringList &lis
slotCurrentGameFileIndexChanged (ui.gameFileView->currentIndex());
}
else
mContentModel->setContentList(list, true);
mContentModel->setContentList(list);
}
ContentSelectorModel::ContentFileList
@ -129,6 +124,15 @@ void ContentSelectorView::ContentSelector::addFiles(const QString &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)
ui.gameFileView->setCurrentIndex(-1);
@ -150,29 +154,33 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i
{
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 (oldIndex > -1)
model->setData(model->index(oldIndex, 0), false, Qt::UserRole + 1);
{
setGameFileSelected(oldIndex, false);
}
oldIndex = index;
model->setData(model->index(index, 0), true, Qt::UserRole + 1);
setGameFileSelected(index, true);
mContentModel->checkForLoadOrderErrors();
}
if (proxy)
proxy->setDynamicSortFilter(true);
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)
{
QModelIndex sourceIndex = mAddonProxyModel->mapToSource (index);

View file

@ -19,7 +19,6 @@ namespace ContentSelectorView
protected:
ContentSelectorModel::ContentModel *mContentModel;
QSortFilterProxyModel *mGameFileProxyModel;
QSortFilterProxyModel *mAddonProxyModel;
public:
@ -52,6 +51,7 @@ namespace ContentSelectorView
void buildContentModel();
void buildGameFileView();
void buildAddonView();
void setGameFileSelected(int index, bool selected);
signals:
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
{
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
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;
};
}

View file

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

View file

@ -29,11 +29,15 @@ namespace ESM
};
#pragma pack(pop)
/// EffectList, ENAM subrecord
struct EffectList
{
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 save(ESMWriter &esm) const;
};

View file

@ -8,18 +8,34 @@ namespace ESM
{
unsigned int Activator::sRecordId = REC_ACTI;
void Activator::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
mScript = esm.getHNOString("SCRI");
}
void Activator::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNOCString("SCRI", mScript);
}
void Activator::load(ESMReader &esm)
{
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<'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()
{

View file

@ -8,24 +8,51 @@ namespace ESM
{
unsigned int Potion::sRecordId = REC_ALCH;
void Potion::load(ESMReader &esm)
{
mModel = esm.getHNOString("MODL");
mIcon = esm.getHNOString("TEXT"); // not ITEX here for some reason
mScript = esm.getHNOString("SCRI");
mName = esm.getHNOString("FNAM");
esm.getHNT(mData, "ALDT", 12);
mEffects.load(esm);
}
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::load(ESMReader &esm)
{
mEffects.mList.clear();
bool hasData = false;
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<'T','E','X','T'>::value: // not ITEX here for some reason
mIcon = esm.getHString();
break;
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()
{

View file

@ -10,25 +10,35 @@ namespace ESM
void Apparatus::load(ESMReader &esm)
{
// we will not treat duplicated subrecords as errors here
bool hasData = false;
while (esm.hasMoreSubs())
{
esm.getSubName();
NAME subName = esm.retSubName();
if (subName == "MODL")
mModel = esm.getHString();
else if (subName == "FNAM")
mName = esm.getHString();
else if (subName == "AADT")
esm.getHT(mData);
else if (subName == "SCRI")
mScript = esm.getHString();
else if (subName == "ITEX")
mIcon = esm.getHString();
else
esm.fail("wrong subrecord type " + subName.toString() + " for APPA record");
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<'A','A','D','T'>::value:
esm.getHT(mData);
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 AADT");
}
void Apparatus::save(ESMWriter &esm) const

View file

@ -11,9 +11,30 @@ namespace ESM
void BodyPart::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mRace = esm.getHNOString("FNAM");
esm.getHNT(mData, "BYDT", 4);
bool hasData = false;
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:
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
{

View file

@ -10,11 +10,29 @@ namespace ESM
void BirthSign::load(ESMReader &esm)
{
mName = esm.getHNOString("FNAM");
mTexture = esm.getHNOString("TNAM");
mDescription = esm.getHNOString("DESC");
mPowers.load(esm);
mPowers.mList.clear();
while (esm.hasMoreSubs())
{
esm.getSubName();
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

View file

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

View file

@ -7,60 +7,79 @@
namespace 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"))
void InventoryList::add(ESMReader &esm)
{
add(esm);
ContItem ci;
esm.getHT(ci, 36);
mList.push_back(ci);
}
}
void InventoryList::save(ESMWriter &esm) const
{
for (std::vector<ContItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
void InventoryList::save(ESMWriter &esm) const
{
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;
void Container::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
esm.getHNT(mWeight, "CNDT", 4);
esm.getHNT(mFlags, "FLAG", 4);
void Container::load(ESMReader &esm)
{
mInventory.mList.clear();
bool hasWeight = false;
bool hasFlags = false;
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)
esm.fail("Unknown flags");
if (!(mFlags & 0x8))
esm.fail("Flag 8 not set");
void Container::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
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);
}
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);
}
mInventory.save(esm);
}
void Container::blank()
{

View file

@ -22,6 +22,7 @@ struct ContItem
NAME32 mItem;
};
/// InventoryList, NPCO subrecord
struct InventoryList
{
std::vector<ContItem> mList;
@ -29,8 +30,6 @@ struct InventoryList
/// Load one item, assumes subrecord name is already read
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;
};

View file

@ -8,55 +8,94 @@ namespace ESM {
unsigned int Creature::sRecordId = REC_CREA;
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"))
void Creature::load(ESMReader &esm)
{
esm.getHExact(&mAiData, sizeof(mAiData));
mHasAI = true;
}
else
mPersistent = esm.getRecordFlags() & 0x0400;
mAiPackage.mList.clear();
mInventory.mList.clear();
mSpells.mList.clear();
mScale = 1.f;
mHasAI = false;
mAiPackage.load(esm);
esm.skipRecord();
}
void Creature::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
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);
bool hasNpdt = false;
bool hasFlags = false;
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<'C','N','A','M'>::value:
mOriginal = esm.getHString();
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;
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);
mSpells.save(esm);
if (mHasAI) {
esm.writeHNT("AIDT", mAiData, sizeof(mAiData));
void Creature::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
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()
{

View file

@ -10,8 +10,28 @@ namespace ESM
void Enchantment::load(ESMReader &esm)
{
esm.getHNT(mData, "ENDT", 16);
mEffects.load(esm);
mEffects.mList.clear();
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

View file

@ -28,27 +28,46 @@ namespace 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 i = 0;
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
int rankCounter=0;
bool hasData = false;
while (esm.hasMoreSubs())
{
std::string faction = esm.getHNString("ANAM");
int reaction;
esm.getHNT(reaction, "INTV");
mReactions[faction] = reaction;
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
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
{

View file

@ -8,45 +8,71 @@ namespace ESM
{
unsigned int Ingredient::sRecordId = REC_INGR;
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)
void Ingredient::load(ESMReader &esm)
{
if (mData.mEffectID[i] != 85 &&
mData.mEffectID[i] != 22 &&
mData.mEffectID[i] != 17 &&
mData.mEffectID[i] != 79 &&
mData.mEffectID[i] != 74)
bool hasData = false;
while (esm.hasMoreSubs())
{
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 (mData.mEffectID[i] != 89 &&
mData.mEffectID[i] != 26 &&
mData.mEffectID[i] != 21 &&
mData.mEffectID[i] != 83 &&
mData.mEffectID[i] != 78)
if (!hasData)
esm.fail("Missing IRDT subrecord");
// horrible hack to fix broken data in records
for (int i=0; i<4; ++i)
{
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
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("IRDT", mData, 56);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Ingredient::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("IRDT", mData, 56);
esm.writeHNOCString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Ingredient::blank()
{

View file

@ -7,49 +7,51 @@
namespace ESM
{
void LevelledListBase::load(ESMReader &esm)
{
esm.getHNT(mFlags, "DATA");
esm.getHNT(mChanceNone, "NNAM");
if (esm.isNextSub("INDX"))
void LevelledListBase::load(ESMReader &esm)
{
int len;
esm.getHT(len);
mList.resize(len);
}
else
{
esm.skipRecord();
return;
}
esm.getHNT(mFlags, "DATA");
esm.getHNT(mChanceNone, "NNAM");
// 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,
// with the limited amount of information there is in the records, all merging methods
// will be flawed in some way. For a proper fix the ESM format would have to be changed
// to actually track list changes instead of including the whole list for every file
// that does something with that list.
if (esm.isNextSub("INDX"))
{
int len;
esm.getHT(len);
mList.resize(len);
}
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++)
{
LevelItem &li = mList[i];
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());
// 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,
// with the limited amount of information there is in the records, all merging methods
// will be flawed in some way. For a proper fix the ESM format would have to be changed
// to actually track list changes instead of including the whole list for every file
// that does something with that list.
for (std::vector<LevelItem>::const_iterator it = mList.begin(); it != mList.end(); ++it)
{
esm.writeHNCString(mRecName, it->mId);
esm.writeHNT("INTV", it->mLevel);
for (size_t i = 0; i < mList.size(); i++)
{
LevelItem &li = mList[i];
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()
{

View file

@ -8,25 +8,50 @@ namespace ESM
{
unsigned int Light::sRecordId = REC_LIGH;
void Light::load(ESMReader &esm)
{
mModel = esm.getHNOString("MODL");
mName = esm.getHNOString("FNAM");
mIcon = esm.getHNOString("ITEX");
assert(sizeof(mData) == 24);
esm.getHNT(mData, "LHDT", 24);
mScript = esm.getHNOString("SCRI");
mSound = esm.getHNOString("SNAM");
}
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::load(ESMReader &esm)
{
bool hasData = false;
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<'I','T','E','X'>::value:
mIcon = esm.getHString();
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()
{

View file

@ -8,26 +8,48 @@ namespace ESM
{
unsigned int Lockpick::sRecordId = REC_LOCK;
void Lockpick::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
void Lockpick::load(ESMReader &esm)
{
bool hasData = true;
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");
mIcon = esm.getHNOString("ITEX");
}
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);
}
esm.writeHNT("LKDT", mData, 16);
esm.writeHNOString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Lockpick::blank()
{

View file

@ -246,7 +246,7 @@ void MagicEffect::load(ESMReader &esm)
mDescription = esm.getHString();
break;
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
int mRed, mGreen, mBlue;
float mUnknown1;
float mUnknown1; // Called "Size X" in CS
float mSpeed; // Speed of fired projectile
float mUnknown2;
float mUnknown2; // Called "Size Cap" in CS
}; // 36 bytes
static const std::map<short,std::string> sNames;

View file

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

View file

@ -8,26 +8,48 @@ namespace ESM
{
unsigned int Probe::sRecordId = REC_PROB;
void Probe::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
void Probe::load(ESMReader &esm)
{
bool hasData = true;
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");
mIcon = esm.getHNOString("ITEX");
}
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);
}
esm.writeHNT("PBDT", mData, 16);
esm.writeHNOString("SCRI", mScript);
esm.writeHNOCString("ITEX", mIcon);
}
void Probe::blank()
{

View file

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

View file

@ -10,13 +10,35 @@ namespace ESM
void Repair::load(ESMReader &esm)
{
mModel = esm.getHNString("MODL");
mName = esm.getHNOString("FNAM");
esm.getHNT(mData, "RIDT", 16);
mScript = esm.getHNOString("SCRI");
mIcon = esm.getHNOString("ITEX");
bool hasData = true;
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<'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

View file

@ -9,17 +9,7 @@ namespace ESM
unsigned int Script::sRecordId = REC_SCPT;
void Script::load(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"))
void Script::loadSCVR(ESMReader &esm)
{
int s = mData.mStringTableSize;
@ -66,58 +56,70 @@ void Script::load(ESMReader &esm)
}
}
// Script mData
if (esm.isNextSub("SCDT"))
void Script::load(ESMReader &esm)
{
mScriptData.resize(mData.mScriptDataSize);
esm.getHExact(&mScriptData[0], mScriptData.size());
}
SCHD data;
esm.getHNT(data, "SCHD", 52);
mData = data.mData;
mId = data.mName.toString();
// Script text
mScriptText = esm.getHNOString("SCTX");
mVarNames.clear();
// NOTE: A minor hack/workaround...
//
// 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)
while (esm.hasMoreSubs())
{
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");
esm.write(reinterpret_cast<const char * >(&mScriptData[0]), mData.mScriptDataSize);
esm.endRecord("SCDT");
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);
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()
{

View file

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

View file

@ -129,23 +129,47 @@ namespace ESM
unsigned int Skill::sRecordId = REC_SKIL;
void Skill::load(ESMReader &esm)
{
esm.getHNT(mIndex, "INDX");
esm.getHNT(mData, "SKDT", 24);
mDescription = esm.getHNOString("DESC");
void Skill::load(ESMReader &esm)
{
bool hasIndex = false;
bool hasData = false;
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
// future)
mId = indexToId (mIndex);
}
// create an ID from the index and the name (only used in the editor and likely to change in the
// future)
mId = indexToId (mIndex);
}
void Skill::save(ESMWriter &esm) const
{
esm.writeHNT("INDX", mIndex);
esm.writeHNT("SKDT", mData, 24);
esm.writeHNOString("DESC", mDescription);
}
void Skill::save(ESMWriter &esm) const
{
esm.writeHNT("INDX", mIndex);
esm.writeHNT("SKDT", mData, 24);
esm.writeHNOString("DESC", mDescription);
}
void Skill::blank()
{

View file

@ -8,19 +8,38 @@ namespace ESM
{
unsigned int SoundGenerator::sRecordId = REC_SNDG;
void SoundGenerator::load(ESMReader &esm)
{
esm.getHNT(mType, "DATA", 4);
mCreature = esm.getHNOString("CNAM");
mSound = esm.getHNOString("SNAM");
}
void SoundGenerator::save(ESMWriter &esm) const
{
esm.writeHNT("DATA", mType, 4);
esm.writeHNOCString("CNAM", mCreature);
esm.writeHNOCString("SNAM", mSound);
}
void SoundGenerator::load(ESMReader &esm)
{
bool hasData = false;
while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
switch (name)
{
case ESM::FourCC<'D','A','T','A'>::value:
esm.getHT(mType, 4);
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()
{

View file

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

View file

@ -8,40 +8,41 @@ namespace ESM
{
unsigned int Spell::sRecordId = REC_SPEL;
void Spell::load(ESMReader &esm)
{
bool hasData = false;
while (esm.hasMoreSubs())
void Spell::load(ESMReader &esm)
{
esm.getSubName();
uint32_t val = esm.retSubName().val;
switch (val)
mEffects.mList.clear();
bool hasData = false;
while (esm.hasMoreSubs())
{
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
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");
}
esm.getSubName();
uint32_t val = esm.retSubName().val;
void Spell::save(ESMWriter &esm) const
{
esm.writeHNOCString("FNAM", mName);
esm.writeHNT("SPDT", mData, 12);
mEffects.save(esm);
}
switch (val)
{
case ESM::FourCC<'F','N','A','M'>::value:
mName = esm.getHString();
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()
{

View file

@ -8,15 +8,37 @@ namespace ESM
{
unsigned int StartScript::sRecordId = REC_SSCR;
void StartScript::load(ESMReader &esm)
{
mData = esm.getHNString("DATA");
mScript = esm.getHNString("NAME");
}
void StartScript::save(ESMWriter &esm) const
{
esm.writeHNString("DATA", mData);
esm.writeHNString("NAME", mScript);
}
void StartScript::load(ESMReader &esm)
{
bool hasData = false;
bool hasName = false;
while (esm.hasMoreSubs())
{
esm.getSubName();
uint32_t name = esm.retSubName().val;
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;
void Static::load(ESMReader &esm)
{
mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL");
}
void Static::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
}
void Static::load(ESMReader &esm)
{
mPersistent = esm.getRecordFlags() & 0x0400;
mModel = esm.getHNString("MODL");
}
void Static::save(ESMWriter &esm) const
{
esm.writeHNCString("MODL", mModel);
}
void Static::blank()
{

View file

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

View file

@ -5,14 +5,6 @@
namespace ESM {
void SpellList::load(ESMReader &esm)
{
mList.clear();
while (esm.isNextSub("NPCS")) {
add(esm);
}
}
void SpellList::add(ESMReader &esm)
{
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
between the records BSGN, NPC and RACE.
NPCS subrecord.
*/
struct SpellList
{
@ -22,9 +23,6 @@ namespace ESM
/// Load one spell, assumes the subrecord name was already read
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;
};
}

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;
Buffer::setData((env)->GetStringUTFChars(prompt, &iscopy));

View file

@ -1,8 +1,8 @@
/* DO NOT EDIT THIS FILE - it is machine generated */
#include <jni.h>
#ifndef _Included_org_libsdl_app_SDLActivity_getPathToJni
#define _Included_org_libsdl_app_SDLActivity_getPathToJni
#ifndef _Included_ui_activity_GameActivity_getPathToJni
#define _Included_ui_activity_GameActivity_getPathToJni
#ifdef __cplusplus
extern "C" {
#endif
@ -11,7 +11,7 @@ extern "C" {
* Method: getPathToJni
* 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
}

View file

@ -1 +0,0 @@
old_d

View file

@ -152,9 +152,9 @@ namespace Gui
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
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
virtual void setPropertyOverride(const std::string& _key, const std::string& _value);

View file

@ -243,7 +243,9 @@
}
#else
#if NORMAL_MAP && SH_GLSLES
mat3 transpose( mat3 m);
#endif
// ----------------------------------- FRAGMENT ------------------------------------------
#if UNDERWATER
@ -376,13 +378,13 @@
float3 binormal = cross(tangentPassthrough.xyz, normal.xyz);
float3x3 tbn = float3x3(tangentPassthrough.xyz, binormal, normal.xyz);
#if SH_GLSL
#if SH_GLSL || SH_GLSLES
tbn = transpose(tbn);
#endif
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
#if ENV_MAP || SPECULAR || PARALLAX
@ -576,5 +578,14 @@
// prevent negative colour output (for example with negative lights)
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

View file

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

View file

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

View file

@ -112,7 +112,8 @@
<Property key="ImageTexture" value="textures\compass.dds"/>
</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>

View file

@ -28,9 +28,9 @@
<State name="normal" colour="#{fontcolour=journal_topic}" shift="0"/>
<State name="highlighted" colour="#{fontcolour=journal_topic_over}" 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="highlighted_checked" colour="#{fontcolour=journal_topic_pressed}" shift="0"/>
<State name="pushed_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="0.4 0.4 0.4" shift="0"/>
<State name="pushed_checked" colour="0.5 0.5 0.5" shift="0"/>
</BasisSkin>
</Resource>
</MyGUI>

View file

@ -235,7 +235,9 @@
<!-- Player skills, factions, birthsign and reputation -->
<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>

View file

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