mirror of
https://github.com/TES3MP/openmw-tes3mp.git
synced 2025-01-19 21:23:52 +00:00
Merge branch 'next' of https://github.com/zinnschlag/openmw into graphics
This commit is contained in:
commit
d47090b312
81 changed files with 1895 additions and 673 deletions
|
@ -209,7 +209,10 @@ void loadCell(ESM::Cell &cell, ESM::ESMReader &esm, Arguments& info)
|
||||||
bool save = (info.mode == "clone");
|
bool save = (info.mode == "clone");
|
||||||
|
|
||||||
// Skip back to the beginning of the reference list
|
// Skip back to the beginning of the reference list
|
||||||
cell.restore(esm);
|
// FIXME: Changes to the references backend required to support multiple plugins have
|
||||||
|
// almost certainly broken this following line. I'll leave it as is for now, so that
|
||||||
|
// the compiler does not complain.
|
||||||
|
cell.restore(esm, 0);
|
||||||
|
|
||||||
// Loop through all the references
|
// Loop through all the references
|
||||||
ESM::CellRef ref;
|
ESM::CellRef ref;
|
||||||
|
|
|
@ -1,17 +1,9 @@
|
||||||
set(LAUNCHER
|
set(LAUNCHER
|
||||||
datafilespage.cpp
|
|
||||||
graphicspage.cpp
|
graphicspage.cpp
|
||||||
main.cpp
|
main.cpp
|
||||||
maindialog.cpp
|
maindialog.cpp
|
||||||
playpage.cpp
|
playpage.cpp
|
||||||
|
datafilespage.cpp
|
||||||
model/datafilesmodel.cpp
|
|
||||||
model/modelitem.cpp
|
|
||||||
model/esm/esmfile.cpp
|
|
||||||
|
|
||||||
utils/filedialog.cpp
|
|
||||||
utils/naturalsort.cpp
|
|
||||||
utils/lineedit.cpp
|
|
||||||
utils/profilescombobox.cpp
|
utils/profilescombobox.cpp
|
||||||
utils/textinputdialog.cpp
|
utils/textinputdialog.cpp
|
||||||
|
|
||||||
|
@ -19,36 +11,20 @@ set(LAUNCHER
|
||||||
)
|
)
|
||||||
|
|
||||||
set(LAUNCHER_HEADER
|
set(LAUNCHER_HEADER
|
||||||
datafilespage.hpp
|
|
||||||
graphicspage.hpp
|
graphicspage.hpp
|
||||||
maindialog.hpp
|
maindialog.hpp
|
||||||
playpage.hpp
|
playpage.hpp
|
||||||
|
datafilespage.hpp
|
||||||
model/datafilesmodel.hpp
|
|
||||||
model/modelitem.hpp
|
|
||||||
model/esm/esmfile.hpp
|
|
||||||
|
|
||||||
utils/lineedit.hpp
|
|
||||||
utils/filedialog.hpp
|
|
||||||
utils/naturalsort.hpp
|
|
||||||
utils/profilescombobox.hpp
|
utils/profilescombobox.hpp
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
# Headers that must be pre-processed
|
# Headers that must be pre-processed
|
||||||
set(LAUNCHER_HEADER_MOC
|
set(LAUNCHER_HEADER_MOC
|
||||||
datafilespage.hpp
|
|
||||||
graphicspage.hpp
|
graphicspage.hpp
|
||||||
maindialog.hpp
|
maindialog.hpp
|
||||||
playpage.hpp
|
playpage.hpp
|
||||||
|
datafilespage.hpp
|
||||||
model/datafilesmodel.hpp
|
|
||||||
model/modelitem.hpp
|
|
||||||
model/esm/esmfile.hpp
|
|
||||||
|
|
||||||
utils/lineedit.hpp
|
|
||||||
utils/filedialog.hpp
|
|
||||||
utils/profilescombobox.hpp
|
utils/profilescombobox.hpp
|
||||||
utils/textinputdialog.hpp
|
utils/textinputdialog.hpp
|
||||||
)
|
)
|
||||||
|
|
|
@ -2,14 +2,17 @@
|
||||||
|
|
||||||
#include <components/esm/esmreader.hpp>
|
#include <components/esm/esmreader.hpp>
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
#include <components/fileorderlist/datafileslist.hpp>
|
||||||
|
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||||
|
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||||
|
#include <components/fileorderlist/utils/filedialog.hpp>
|
||||||
|
|
||||||
#include "model/datafilesmodel.hpp"
|
////#include "model/datafilesmodel.hpp"
|
||||||
#include "model/esm/esmfile.hpp"
|
////#include "model/esm/esmfile.hpp"
|
||||||
|
|
||||||
#include "utils/profilescombobox.hpp"
|
#include "utils/profilescombobox.hpp"
|
||||||
#include "utils/filedialog.hpp"
|
////#include "utils/filedialog.hpp"
|
||||||
#include "utils/lineedit.hpp"
|
////#include "utils/naturalsort.hpp"
|
||||||
#include "utils/naturalsort.hpp"
|
|
||||||
#include "utils/textinputdialog.hpp"
|
#include "utils/textinputdialog.hpp"
|
||||||
|
|
||||||
#include "datafilespage.hpp"
|
#include "datafilespage.hpp"
|
||||||
|
@ -34,108 +37,11 @@ namespace boost
|
||||||
using namespace ESM;
|
using namespace ESM;
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
//sort QModelIndexList ascending
|
|
||||||
bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
|
|
||||||
{
|
|
||||||
return index1.row() >= index2.row();
|
|
||||||
}
|
|
||||||
|
|
||||||
//sort QModelIndexList descending
|
|
||||||
bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
|
|
||||||
{
|
|
||||||
return index1.row() <= index2.row();
|
|
||||||
}
|
|
||||||
|
|
||||||
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent)
|
DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, mCfgMgr(cfg)
|
, mCfgMgr(cfg)
|
||||||
{
|
{
|
||||||
// Models
|
mDataFilesList = new DataFilesList(mCfgMgr, this);
|
||||||
mMastersModel = new DataFilesModel(this);
|
|
||||||
mPluginsModel = new DataFilesModel(this);
|
|
||||||
|
|
||||||
mPluginsProxyModel = new QSortFilterProxyModel();
|
|
||||||
mPluginsProxyModel->setDynamicSortFilter(true);
|
|
||||||
mPluginsProxyModel->setSourceModel(mPluginsModel);
|
|
||||||
|
|
||||||
// Filter toolbar
|
|
||||||
QLabel *filterLabel = new QLabel(tr("&Filter:"), this);
|
|
||||||
LineEdit *filterLineEdit = new LineEdit(this);
|
|
||||||
filterLabel->setBuddy(filterLineEdit);
|
|
||||||
|
|
||||||
QToolBar *filterToolBar = new QToolBar(this);
|
|
||||||
filterToolBar->setMovable(false);
|
|
||||||
|
|
||||||
// Create a container widget and a layout to get the spacer to work
|
|
||||||
QWidget *filterWidget = new QWidget(this);
|
|
||||||
QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget);
|
|
||||||
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
|
||||||
|
|
||||||
filterLayout->addItem(hSpacer1);
|
|
||||||
filterLayout->addWidget(filterLabel);
|
|
||||||
filterLayout->addWidget(filterLineEdit);
|
|
||||||
|
|
||||||
filterToolBar->addWidget(filterWidget);
|
|
||||||
|
|
||||||
QCheckBox checkBox;
|
|
||||||
unsigned int height = checkBox.sizeHint().height() + 4;
|
|
||||||
|
|
||||||
mMastersTable = new QTableView(this);
|
|
||||||
mMastersTable->setModel(mMastersModel);
|
|
||||||
mMastersTable->setObjectName("MastersTable");
|
|
||||||
mMastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
||||||
mMastersTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
||||||
mMastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
||||||
mMastersTable->setAlternatingRowColors(true);
|
|
||||||
mMastersTable->horizontalHeader()->setStretchLastSection(true);
|
|
||||||
mMastersTable->horizontalHeader()->hide();
|
|
||||||
|
|
||||||
// Set the row height to the size of the checkboxes
|
|
||||||
mMastersTable->verticalHeader()->setDefaultSectionSize(height);
|
|
||||||
mMastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
|
||||||
mMastersTable->verticalHeader()->hide();
|
|
||||||
mMastersTable->setColumnHidden(1, true);
|
|
||||||
mMastersTable->setColumnHidden(2, true);
|
|
||||||
mMastersTable->setColumnHidden(3, true);
|
|
||||||
mMastersTable->setColumnHidden(4, true);
|
|
||||||
mMastersTable->setColumnHidden(5, true);
|
|
||||||
mMastersTable->setColumnHidden(6, true);
|
|
||||||
mMastersTable->setColumnHidden(7, true);
|
|
||||||
mMastersTable->setColumnHidden(8, true);
|
|
||||||
|
|
||||||
mPluginsTable = new QTableView(this);
|
|
||||||
mPluginsTable->setModel(mPluginsProxyModel);
|
|
||||||
mPluginsTable->setObjectName("PluginsTable");
|
|
||||||
mPluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
||||||
mPluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
||||||
mPluginsTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
||||||
mPluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
||||||
mPluginsTable->setAlternatingRowColors(true);
|
|
||||||
mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
|
||||||
mPluginsTable->horizontalHeader()->setStretchLastSection(true);
|
|
||||||
mPluginsTable->horizontalHeader()->hide();
|
|
||||||
|
|
||||||
mPluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
|
||||||
mPluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
|
||||||
mPluginsTable->setColumnHidden(1, true);
|
|
||||||
mPluginsTable->setColumnHidden(2, true);
|
|
||||||
mPluginsTable->setColumnHidden(3, true);
|
|
||||||
mPluginsTable->setColumnHidden(4, true);
|
|
||||||
mPluginsTable->setColumnHidden(5, true);
|
|
||||||
mPluginsTable->setColumnHidden(6, true);
|
|
||||||
mPluginsTable->setColumnHidden(7, true);
|
|
||||||
mPluginsTable->setColumnHidden(8, true);
|
|
||||||
|
|
||||||
// Add both tables to a splitter
|
|
||||||
QSplitter *splitter = new QSplitter(this);
|
|
||||||
splitter->setOrientation(Qt::Horizontal);
|
|
||||||
splitter->addWidget(mMastersTable);
|
|
||||||
splitter->addWidget(mPluginsTable);
|
|
||||||
|
|
||||||
// Adjust the default widget widths inside the splitter
|
|
||||||
QList<int> sizeList;
|
|
||||||
sizeList << 175 << 200;
|
|
||||||
splitter->setSizes(sizeList);
|
|
||||||
|
|
||||||
// Bottom part with profile options
|
// Bottom part with profile options
|
||||||
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
|
QLabel *profileLabel = new QLabel(tr("Current Profile: "), this);
|
||||||
|
@ -155,24 +61,14 @@ DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, QWidget *parent)
|
||||||
|
|
||||||
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
pageLayout->addWidget(filterToolBar);
|
pageLayout->addWidget(mDataFilesList);
|
||||||
pageLayout->addWidget(splitter);
|
|
||||||
pageLayout->addWidget(mProfileToolBar);
|
pageLayout->addWidget(mProfileToolBar);
|
||||||
|
|
||||||
// Create a dialog for the new profile name input
|
// Create a dialog for the new profile name input
|
||||||
mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
|
mNewProfileDialog = new TextInputDialog(tr("New Profile"), tr("Profile name:"), this);
|
||||||
|
|
||||||
connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString)));
|
connect(mNewProfileDialog->lineEdit(), SIGNAL(textChanged(QString)), this, SLOT(updateOkButton(QString)));
|
||||||
|
|
||||||
connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
|
||||||
connect(mMastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
|
||||||
|
|
||||||
connect(mMastersModel, SIGNAL(checkedItemsChanged(QStringList,QStringList)), mPluginsModel, SLOT(slotcheckedItemsChanged(QStringList,QStringList)));
|
|
||||||
|
|
||||||
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
|
||||||
|
|
||||||
connect(mPluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
|
||||||
|
|
||||||
connect(mProfilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
|
connect(mProfilesComboBox, SIGNAL(profileRenamed(QString,QString)), this, SLOT(profileRenamed(QString,QString)));
|
||||||
connect(mProfilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
|
connect(mProfilesComboBox, SIGNAL(profileChanged(QString,QString)), this, SLOT(profileChanged(QString,QString)));
|
||||||
|
|
||||||
|
@ -202,20 +98,6 @@ void DataFilesPage::createActions()
|
||||||
mProfileToolBar->addSeparator();
|
mProfileToolBar->addSeparator();
|
||||||
mProfileToolBar->addAction(mNewProfileAction);
|
mProfileToolBar->addAction(mNewProfileAction);
|
||||||
mProfileToolBar->addAction(mDeleteProfileAction);
|
mProfileToolBar->addAction(mDeleteProfileAction);
|
||||||
|
|
||||||
// Context menu actions
|
|
||||||
mCheckAction = new QAction(tr("Check selected"), this);
|
|
||||||
connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check()));
|
|
||||||
|
|
||||||
mUncheckAction = new QAction(tr("Uncheck selected"), this);
|
|
||||||
connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck()));
|
|
||||||
|
|
||||||
// Context menu for the plugins table
|
|
||||||
mContextMenu = new QMenu(this);
|
|
||||||
|
|
||||||
mContextMenu->addAction(mCheckAction);
|
|
||||||
mContextMenu->addAction(mUncheckAction);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFilesPage::setupConfig()
|
void DataFilesPage::setupConfig()
|
||||||
|
@ -267,12 +149,8 @@ void DataFilesPage::setupConfig()
|
||||||
|
|
||||||
void DataFilesPage::readConfig()
|
void DataFilesPage::readConfig()
|
||||||
{
|
{
|
||||||
// Don't read the config if no masters are found
|
|
||||||
if (mMastersModel->rowCount() < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString profile = mProfilesComboBox->currentText();
|
QString profile = mProfilesComboBox->currentText();
|
||||||
|
|
||||||
// Make sure we have no groups open
|
// Make sure we have no groups open
|
||||||
while (!mLauncherConfig->group().isEmpty()) {
|
while (!mLauncherConfig->group().isEmpty()) {
|
||||||
mLauncherConfig->endGroup();
|
mLauncherConfig->endGroup();
|
||||||
|
@ -290,54 +168,11 @@ void DataFilesPage::readConfig()
|
||||||
|
|
||||||
foreach (const QString &key, childKeys) {
|
foreach (const QString &key, childKeys) {
|
||||||
const QString keyValue = mLauncherConfig->value(key).toString();
|
const QString keyValue = mLauncherConfig->value(key).toString();
|
||||||
|
|
||||||
if (key.startsWith("Plugin")) {
|
mDataFilesList->setCheckState(keyValue, Qt::Checked);
|
||||||
//QStringList checked = mPluginsModel->checkedItems();
|
|
||||||
EsmFile *file = mPluginsModel->findItem(keyValue);
|
|
||||||
QModelIndex index = mPluginsModel->indexFromItem(file);
|
|
||||||
|
|
||||||
mPluginsModel->setCheckState(index, Qt::Checked);
|
|
||||||
// Move the row to the top of te view
|
|
||||||
//mPluginsModel->moveRow(index.row(), checked.count());
|
|
||||||
plugins << keyValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (key.startsWith("Master")) {
|
|
||||||
EsmFile *file = mMastersModel->findItem(keyValue);
|
|
||||||
mMastersModel->setCheckState(mMastersModel->indexFromItem(file), Qt::Checked);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
qDebug() << plugins;
|
qDebug() << plugins;
|
||||||
|
|
||||||
|
|
||||||
// // Set the checked item positions
|
|
||||||
// const QStringList checked = mPluginsModel->checkedItems();
|
|
||||||
// for (int i = 0; i < plugins.size(); ++i) {
|
|
||||||
// EsmFile *file = mPluginsModel->findItem(plugins.at(i));
|
|
||||||
// QModelIndex index = mPluginsModel->indexFromItem(file);
|
|
||||||
// mPluginsModel->moveRow(index.row(), i);
|
|
||||||
// qDebug() << "Moving: " << plugins.at(i) << " from: " << index.row() << " to: " << i << " count: " << checked.count();
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// Iterate over the plugins to set their checkstate and position
|
|
||||||
// for (int i = 0; i < plugins.size(); ++i) {
|
|
||||||
// const QString plugin = plugins.at(i);
|
|
||||||
|
|
||||||
// const QList<QStandardItem *> pluginList = mPluginsModel->findItems(plugin);
|
|
||||||
|
|
||||||
// if (!pluginList.isEmpty())
|
|
||||||
// {
|
|
||||||
// foreach (const QStandardItem *currentPlugin, pluginList) {
|
|
||||||
// mPluginsModel->setData(currentPlugin->index(), Qt::Checked, Qt::CheckStateRole);
|
|
||||||
|
|
||||||
// // Move the plugin to the position specified in the config file
|
|
||||||
// mPluginsModel->insertRow(i, mPluginsModel->takeRow(currentPlugin->row()));
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DataFilesPage::showDataFilesWarning()
|
bool DataFilesPage::showDataFilesWarning()
|
||||||
|
@ -386,77 +221,51 @@ bool DataFilesPage::setupDataFiles()
|
||||||
// We use the Configuration Manager to retrieve the configuration values
|
// We use the Configuration Manager to retrieve the configuration values
|
||||||
boost::program_options::variables_map variables;
|
boost::program_options::variables_map variables;
|
||||||
boost::program_options::options_description desc;
|
boost::program_options::options_description desc;
|
||||||
|
|
||||||
desc.add_options()
|
desc.add_options()
|
||||||
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
|
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
|
||||||
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
||||||
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||||
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||||
|
|
||||||
boost::program_options::notify(variables);
|
boost::program_options::notify(variables);
|
||||||
|
|
||||||
mCfgMgr.readConfiguration(variables, desc);
|
mCfgMgr.readConfiguration(variables, desc);
|
||||||
|
|
||||||
if (variables["data"].empty()) {
|
if (variables["data"].empty()) {
|
||||||
if (!showDataFilesWarning())
|
if (!showDataFilesWarning())
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else {
|
||||||
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string local = variables["data-local"].as<std::string>();
|
std::string local = variables["data-local"].as<std::string>();
|
||||||
if (!local.empty()) {
|
if (!local.empty()) {
|
||||||
mDataLocal.push_back(Files::PathContainer::value_type(local));
|
mDataLocal.push_back(Files::PathContainer::value_type(local));
|
||||||
}
|
}
|
||||||
|
|
||||||
mCfgMgr.processPaths(mDataDirs);
|
mCfgMgr.processPaths(mDataDirs);
|
||||||
mCfgMgr.processPaths(mDataLocal);
|
mCfgMgr.processPaths(mDataLocal);
|
||||||
|
|
||||||
// Second chance to display the warning, the data= entries are invalid
|
// Second chance to display the warning, the data= entries are invalid
|
||||||
while (mDataDirs.empty()) {
|
while (mDataDirs.empty()) {
|
||||||
if (!showDataFilesWarning())
|
if (!showDataFilesWarning())
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the charset for reading the esm/esp files
|
// Set the charset for reading the esm/esp files
|
||||||
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||||
if (!encoding.isEmpty() && encoding != QLatin1String("win1252")) {
|
|
||||||
mMastersModel->setEncoding(encoding);
|
Files::PathContainer paths;
|
||||||
mPluginsModel->setEncoding(encoding);
|
paths.insert(paths.end(), mDataDirs.begin(), mDataDirs.end());
|
||||||
}
|
paths.insert(paths.end(), mDataLocal.begin(), mDataLocal.end());
|
||||||
|
mDataFilesList->setupDataFiles(paths, encoding);
|
||||||
// Add the paths to the respective models
|
|
||||||
for (Files::PathContainer::iterator it = mDataDirs.begin(); it != mDataDirs.end(); ++it) {
|
|
||||||
QString path = QString::fromStdString(it->string());
|
|
||||||
path.remove(QChar('\"'));
|
|
||||||
mMastersModel->addMasters(path);
|
|
||||||
mPluginsModel->addPlugins(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Same for the data-local paths
|
|
||||||
for (Files::PathContainer::iterator it = mDataLocal.begin(); it != mDataLocal.end(); ++it) {
|
|
||||||
QString path = QString::fromStdString(it->string());
|
|
||||||
path.remove(QChar('\"'));
|
|
||||||
mMastersModel->addMasters(path);
|
|
||||||
mPluginsModel->addPlugins(path);
|
|
||||||
}
|
|
||||||
|
|
||||||
mMastersModel->sort(0);
|
|
||||||
mPluginsModel->sort(0);
|
|
||||||
// mMastersTable->sortByColumn(3, Qt::AscendingOrder);
|
|
||||||
// mPluginsTable->sortByColumn(3, Qt::AscendingOrder);
|
|
||||||
|
|
||||||
|
|
||||||
readConfig();
|
readConfig();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFilesPage::writeConfig(QString profile)
|
void DataFilesPage::writeConfig(QString profile)
|
||||||
{
|
{
|
||||||
// Don't overwrite the config if no masters are found
|
|
||||||
if (mMastersModel->rowCount() < 1)
|
|
||||||
return;
|
|
||||||
|
|
||||||
QString pathStr = QString::fromStdString(mCfgMgr.getUserPath().string());
|
QString pathStr = QString::fromStdString(mCfgMgr.getUserPath().string());
|
||||||
QDir userPath(pathStr);
|
QDir userPath(pathStr);
|
||||||
|
|
||||||
|
@ -579,24 +388,19 @@ void DataFilesPage::writeConfig(QString profile)
|
||||||
mLauncherConfig->remove(""); // Clear the subgroup
|
mLauncherConfig->remove(""); // Clear the subgroup
|
||||||
|
|
||||||
// Now write the masters to the configs
|
// Now write the masters to the configs
|
||||||
const QStringList masters = mMastersModel->checkedItems();
|
const QStringList checkedFiles = mDataFilesList->checkedFiles();
|
||||||
|
for(int i=0; i < checkedFiles.size(); i++)
|
||||||
// We don't use foreach because we need i
|
{
|
||||||
for (int i = 0; i < masters.size(); ++i) {
|
if (checkedFiles.at(i).lastIndexOf("esm") != -1)
|
||||||
const QString currentMaster = masters.at(i);
|
{
|
||||||
|
mLauncherConfig->setValue(QString("Master%0").arg(i), checkedFiles.at(i));
|
||||||
mLauncherConfig->setValue(QString("Master%0").arg(i), currentMaster);
|
gameConfig << "master=" << checkedFiles.at(i) << endl;
|
||||||
gameConfig << "master=" << currentMaster << endl;
|
}
|
||||||
|
else
|
||||||
}
|
{
|
||||||
|
mLauncherConfig->setValue(QString("Plugin%1").arg(i), checkedFiles.at(i));
|
||||||
// And finally write all checked plugins
|
gameConfig << "plugin=" << checkedFiles.at(i) << endl;
|
||||||
const QStringList plugins = mPluginsModel->checkedItems();
|
}
|
||||||
|
|
||||||
for (int i = 0; i < plugins.size(); ++i) {
|
|
||||||
const QString currentPlugin = plugins.at(i);
|
|
||||||
mLauncherConfig->setValue(QString("Plugin%1").arg(i), currentPlugin);
|
|
||||||
gameConfig << "plugin=" << currentPlugin << endl;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
file.close();
|
file.close();
|
||||||
|
@ -670,93 +474,6 @@ void DataFilesPage::deleteProfile()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFilesPage::check()
|
|
||||||
{
|
|
||||||
// Check the current selection
|
|
||||||
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
|
|
||||||
|
|
||||||
//sort selection ascending because selectedIndexes returns an unsorted list
|
|
||||||
//qSort(indexes.begin(), indexes.end(), rowSmallerThan);
|
|
||||||
|
|
||||||
foreach (const QModelIndex &index, indexes) {
|
|
||||||
if (!index.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
mPluginsModel->setCheckState(index, Qt::Checked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFilesPage::uncheck()
|
|
||||||
{
|
|
||||||
// uncheck the current selection
|
|
||||||
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
|
|
||||||
|
|
||||||
//sort selection ascending because selectedIndexes returns an unsorted list
|
|
||||||
//qSort(indexes.begin(), indexes.end(), rowSmallerThan);
|
|
||||||
|
|
||||||
foreach (const QModelIndex &index, indexes) {
|
|
||||||
if (!index.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
mPluginsModel->setCheckState(index, Qt::Unchecked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFilesPage::refresh()
|
|
||||||
{
|
|
||||||
mPluginsModel->sort(0);
|
|
||||||
|
|
||||||
|
|
||||||
// Refresh the plugins table
|
|
||||||
mPluginsTable->scrollToTop();
|
|
||||||
writeConfig();
|
|
||||||
readConfig();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
void DataFilesPage::setCheckState(QModelIndex index)
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
QObject *object = QObject::sender();
|
|
||||||
|
|
||||||
// Not a signal-slot call
|
|
||||||
if (!object)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (object->objectName() == QLatin1String("PluginsTable")) {
|
|
||||||
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index);
|
|
||||||
|
|
||||||
(mPluginsModel->checkState(sourceIndex) == Qt::Checked)
|
|
||||||
? mPluginsModel->setCheckState(sourceIndex, Qt::Unchecked)
|
|
||||||
: mPluginsModel->setCheckState(sourceIndex, Qt::Checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (object->objectName() == QLatin1String("MastersTable")) {
|
|
||||||
(mMastersModel->checkState(index) == Qt::Checked)
|
|
||||||
? mMastersModel->setCheckState(index, Qt::Unchecked)
|
|
||||||
: mMastersModel->setCheckState(index, Qt::Checked);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFilesPage::filterChanged(const QString filter)
|
|
||||||
{
|
|
||||||
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
|
||||||
mPluginsProxyModel->setFilterRegExp(regExp);
|
|
||||||
}
|
|
||||||
|
|
||||||
void DataFilesPage::profileChanged(const QString &previous, const QString ¤t)
|
void DataFilesPage::profileChanged(const QString &previous, const QString ¤t)
|
||||||
{
|
{
|
||||||
qDebug() << "Profile is changed from: " << previous << " to " << current;
|
qDebug() << "Profile is changed from: " << previous << " to " << current;
|
||||||
|
@ -780,8 +497,7 @@ void DataFilesPage::profileChanged(const QString &previous, const QString &curre
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
mMastersModel->uncheckAll();
|
mDataFilesList->uncheckAll();
|
||||||
mPluginsModel->uncheckAll();
|
|
||||||
readConfig();
|
readConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -810,35 +526,8 @@ void DataFilesPage::profileRenamed(const QString &previous, const QString &curre
|
||||||
// Remove the profile from the combobox
|
// Remove the profile from the combobox
|
||||||
mProfilesComboBox->removeItem(mProfilesComboBox->findText(previous));
|
mProfilesComboBox->removeItem(mProfilesComboBox->findText(previous));
|
||||||
|
|
||||||
mMastersModel->uncheckAll();
|
mDataFilesList->uncheckAll();
|
||||||
mPluginsModel->uncheckAll();
|
////mMastersModel->uncheckAll();
|
||||||
|
////mPluginsModel->uncheckAll();
|
||||||
readConfig();
|
readConfig();
|
||||||
}
|
}
|
||||||
|
|
||||||
void DataFilesPage::showContextMenu(const QPoint &point)
|
|
||||||
{
|
|
||||||
// Make sure there are plugins in the view
|
|
||||||
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QPoint globalPos = mPluginsTable->mapToGlobal(point);
|
|
||||||
|
|
||||||
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
|
|
||||||
|
|
||||||
// Show the check/uncheck actions depending on the state of the selected items
|
|
||||||
mUncheckAction->setEnabled(false);
|
|
||||||
mCheckAction->setEnabled(false);
|
|
||||||
|
|
||||||
foreach (const QModelIndex &index, indexes) {
|
|
||||||
if (!index.isValid())
|
|
||||||
return;
|
|
||||||
|
|
||||||
(mPluginsModel->checkState(index) == Qt::Checked)
|
|
||||||
? mUncheckAction->setEnabled(true)
|
|
||||||
: mCheckAction->setEnabled(true);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Show menu
|
|
||||||
mContextMenu->exec(globalPos);
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ class ProfilesComboBox;
|
||||||
class DataFilesModel;
|
class DataFilesModel;
|
||||||
|
|
||||||
class TextInputDialog;
|
class TextInputDialog;
|
||||||
|
class DataFilesList;
|
||||||
|
|
||||||
namespace Files { struct ConfigurationManager; }
|
namespace Files { struct ConfigurationManager; }
|
||||||
|
|
||||||
|
@ -34,10 +35,6 @@ public:
|
||||||
bool setupDataFiles();
|
bool setupDataFiles();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void setCheckState(QModelIndex index);
|
|
||||||
|
|
||||||
void filterChanged(const QString filter);
|
|
||||||
void showContextMenu(const QPoint &point);
|
|
||||||
void profileChanged(const QString &previous, const QString ¤t);
|
void profileChanged(const QString &previous, const QString ¤t);
|
||||||
void profileRenamed(const QString &previous, const QString ¤t);
|
void profileRenamed(const QString &previous, const QString ¤t);
|
||||||
void updateOkButton(const QString &text);
|
void updateOkButton(const QString &text);
|
||||||
|
@ -49,21 +46,11 @@ public slots:
|
||||||
// void moveDown();
|
// void moveDown();
|
||||||
// void moveTop();
|
// void moveTop();
|
||||||
// void moveBottom();
|
// void moveBottom();
|
||||||
void check();
|
|
||||||
void uncheck();
|
|
||||||
void refresh();
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
DataFilesModel *mMastersModel;
|
DataFilesList *mDataFilesList;
|
||||||
DataFilesModel *mPluginsModel;
|
|
||||||
|
|
||||||
QSortFilterProxyModel *mPluginsProxyModel;
|
|
||||||
|
|
||||||
QTableView *mMastersTable;
|
|
||||||
QTableView *mPluginsTable;
|
|
||||||
|
|
||||||
QToolBar *mProfileToolBar;
|
QToolBar *mProfileToolBar;
|
||||||
QMenu *mContextMenu;
|
|
||||||
|
|
||||||
QAction *mNewProfileAction;
|
QAction *mNewProfileAction;
|
||||||
QAction *mDeleteProfileAction;
|
QAction *mDeleteProfileAction;
|
||||||
|
@ -72,8 +59,6 @@ private:
|
||||||
// QAction *mMoveDownAction;
|
// QAction *mMoveDownAction;
|
||||||
// QAction *mMoveTopAction;
|
// QAction *mMoveTopAction;
|
||||||
// QAction *mMoveBottomAction;
|
// QAction *mMoveBottomAction;
|
||||||
QAction *mCheckAction;
|
|
||||||
QAction *mUncheckAction;
|
|
||||||
|
|
||||||
Files::ConfigurationManager &mCfgMgr;
|
Files::ConfigurationManager &mCfgMgr;
|
||||||
Files::PathContainer mDataDirs;
|
Files::PathContainer mDataDirs;
|
||||||
|
|
|
@ -8,8 +8,7 @@
|
||||||
#include <components/files/configurationmanager.hpp>
|
#include <components/files/configurationmanager.hpp>
|
||||||
#include <components/files/ogreplugin.hpp>
|
#include <components/files/ogreplugin.hpp>
|
||||||
#include <components/settings/settings.hpp>
|
#include <components/settings/settings.hpp>
|
||||||
|
#include <components/fileorderlist/utils/naturalsort.hpp>
|
||||||
#include "utils/naturalsort.hpp"
|
|
||||||
|
|
||||||
#include "graphicspage.hpp"
|
#include "graphicspage.hpp"
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#include <QVBoxLayout>
|
#include <QVBoxLayout>
|
||||||
#include <QValidator>
|
#include <QValidator>
|
||||||
|
|
||||||
#include "lineedit.hpp"
|
#include <components/fileorderlist/utils/lineedit.hpp>
|
||||||
|
|
||||||
#include "textinputdialog.hpp"
|
#include "textinputdialog.hpp"
|
||||||
|
|
||||||
|
|
|
@ -777,6 +777,39 @@ void MwIniImporter::insertMultistrmap(multistrmap &cfg, std::string key, std::st
|
||||||
cfg[key].push_back(value);
|
cfg[key].push_back(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void MwIniImporter::importArchives(multistrmap &cfg, multistrmap &ini) {
|
||||||
|
std::vector<std::string> archives;
|
||||||
|
std::string baseArchive("Archives:Archive ");
|
||||||
|
std::string archive;
|
||||||
|
|
||||||
|
// Search archives listed in ini file
|
||||||
|
multistrmap::iterator it = ini.begin();
|
||||||
|
for(int i=0; it != ini.end(); i++) {
|
||||||
|
archive = baseArchive;
|
||||||
|
archive.append(this->numberToString(i));
|
||||||
|
|
||||||
|
it = ini.find(archive);
|
||||||
|
if(it == ini.end()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
||||||
|
archives.push_back(*entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cfg.erase("fallback-archive");
|
||||||
|
cfg.insert( std::make_pair<std::string, std::vector<std::string> > ("fallback-archive", std::vector<std::string>()));
|
||||||
|
|
||||||
|
// Add Morrowind.bsa by default, since Vanilla loads this archive even if it
|
||||||
|
// does not appears in the ini file
|
||||||
|
cfg["fallback-archive"].push_back("Morrowind.bsa");
|
||||||
|
|
||||||
|
for(std::vector<std::string>::iterator it=archives.begin(); it!=archives.end(); ++it) {
|
||||||
|
cfg["fallback-archive"].push_back(*it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
||||||
std::vector<std::string> esmFiles;
|
std::vector<std::string> esmFiles;
|
||||||
std::vector<std::string> espFiles;
|
std::vector<std::string> espFiles;
|
||||||
|
@ -794,7 +827,7 @@ void MwIniImporter::importGameFiles(multistrmap &cfg, multistrmap &ini) {
|
||||||
}
|
}
|
||||||
|
|
||||||
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
for(std::vector<std::string>::iterator entry = it->second.begin(); entry!=it->second.end(); ++entry) {
|
||||||
std::string filetype(entry->substr(entry->length()-4, 3));
|
std::string filetype(entry->substr(entry->length()-3));
|
||||||
Misc::StringUtils::toLower(filetype);
|
Misc::StringUtils::toLower(filetype);
|
||||||
|
|
||||||
if(filetype.compare("esm") == 0) {
|
if(filetype.compare("esm") == 0) {
|
||||||
|
|
|
@ -23,6 +23,7 @@ class MwIniImporter {
|
||||||
void merge(multistrmap &cfg, multistrmap &ini);
|
void merge(multistrmap &cfg, multistrmap &ini);
|
||||||
void mergeFallback(multistrmap &cfg, multistrmap &ini);
|
void mergeFallback(multistrmap &cfg, multistrmap &ini);
|
||||||
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
void importGameFiles(multistrmap &cfg, multistrmap &ini);
|
||||||
|
void importArchives(multistrmap &cfg, multistrmap &ini);
|
||||||
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
void writeToFile(boost::iostreams::stream<boost::iostreams::file_sink> &out, multistrmap &cfg);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
|
@ -18,6 +18,7 @@ int main(int argc, char *argv[]) {
|
||||||
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
|
||||||
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
|
||||||
("game-files,g", "import esm and esp files")
|
("game-files,g", "import esm and esp files")
|
||||||
|
("no-archives,A", "disable bsa archives import")
|
||||||
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
|
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
|
||||||
"Character encoding used in OpenMW game messages:\n"
|
"Character encoding used in OpenMW game messages:\n"
|
||||||
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
"\n\twin1250 - Central and Eastern European such as Polish, Czech, Slovak, Hungarian, Slovene, Bosnian, Croatian, Serbian (Latin script), Romanian and Albanian languages\n"
|
||||||
|
@ -76,6 +77,10 @@ int main(int argc, char *argv[]) {
|
||||||
importer.importGameFiles(cfg, ini);
|
importer.importGameFiles(cfg, ini);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(!vm.count("no-archives")) {
|
||||||
|
importer.importArchives(cfg, ini);
|
||||||
|
}
|
||||||
|
|
||||||
std::cout << "write to: " << outputFile << std::endl;
|
std::cout << "write to: " << outputFile << std::endl;
|
||||||
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
boost::iostreams::stream<boost::iostreams::file_sink> file(outputFile);
|
||||||
importer.writeToFile(file, cfg);
|
importer.writeToFile(file, cfg);
|
||||||
|
|
|
@ -21,6 +21,7 @@ opencs_units (model/world
|
||||||
idtable idtableproxymodel
|
idtable idtableproxymodel
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
opencs_units_noqt (model/world
|
opencs_units_noqt (model/world
|
||||||
universalid data record idcollection commands columnbase
|
universalid data record idcollection commands columnbase
|
||||||
)
|
)
|
||||||
|
@ -40,9 +41,10 @@ opencs_units_noqt (model/tools
|
||||||
|
|
||||||
|
|
||||||
opencs_units (view/doc
|
opencs_units (view/doc
|
||||||
viewmanager view operations operation subview
|
viewmanager view operations operation subview startup opendialog
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
opencs_units_noqt (view/doc
|
opencs_units_noqt (view/doc
|
||||||
subviewfactory
|
subviewfactory
|
||||||
)
|
)
|
||||||
|
|
|
@ -11,39 +11,52 @@
|
||||||
CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0)
|
CS::Editor::Editor() : mViewManager (mDocumentManager), mNewDocumentIndex (0)
|
||||||
{
|
{
|
||||||
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
|
connect (&mViewManager, SIGNAL (newDocumentRequest ()), this, SLOT (createDocument ()));
|
||||||
|
connect (&mViewManager, SIGNAL (loadDocumentRequest ()), this, SLOT (loadDocument ()));
|
||||||
|
|
||||||
|
connect (&mStartup, SIGNAL (createDocument()), this, SLOT (createDocument ()));
|
||||||
|
connect (&mStartup, SIGNAL (loadDocument()), this, SLOT (loadDocument ()));
|
||||||
|
|
||||||
|
connect (&mOpenDialog, SIGNAL(accepted()), this, SLOT(openFiles()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void CS::Editor::createDocument()
|
void CS::Editor::createDocument()
|
||||||
{
|
{
|
||||||
|
mStartup.hide();
|
||||||
|
|
||||||
|
/// \todo open the ESX picker instead
|
||||||
|
|
||||||
std::ostringstream stream;
|
std::ostringstream stream;
|
||||||
|
|
||||||
stream << "NewDocument" << (++mNewDocumentIndex);
|
stream << "NewDocument" << (++mNewDocumentIndex);
|
||||||
|
|
||||||
CSMDoc::Document *document = mDocumentManager.addDocument (stream.str());
|
std::vector<boost::filesystem::path> files;
|
||||||
|
files.push_back (stream.str());
|
||||||
|
|
||||||
static const char *sGlobals[] =
|
CSMDoc::Document *document = mDocumentManager.addDocument (files, true);
|
||||||
{
|
|
||||||
"Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0
|
|
||||||
};
|
|
||||||
|
|
||||||
for (int i=0; sGlobals[i]; ++i)
|
mViewManager.addView (document);
|
||||||
{
|
}
|
||||||
ESM::Global record;
|
|
||||||
record.mId = sGlobals[i];
|
|
||||||
record.mValue = i==0 ? 1 : 0;
|
|
||||||
record.mType = ESM::VT_Float;
|
|
||||||
document->getData().getGlobals().add (record);
|
|
||||||
}
|
|
||||||
|
|
||||||
document->getData().merge(); /// \todo remove once proper ESX loading is implemented
|
void CS::Editor::loadDocument()
|
||||||
|
{
|
||||||
|
mStartup.hide();
|
||||||
|
mOpenDialog.show();
|
||||||
|
mOpenDialog.raise();
|
||||||
|
mOpenDialog.activateWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CS::Editor::openFiles()
|
||||||
|
{
|
||||||
|
std::vector<boost::filesystem::path> paths;
|
||||||
|
mOpenDialog.getFileList(paths);
|
||||||
|
CSMDoc::Document *document = mDocumentManager.addDocument(paths, false);
|
||||||
|
|
||||||
mViewManager.addView (document);
|
mViewManager.addView (document);
|
||||||
}
|
}
|
||||||
|
|
||||||
int CS::Editor::run()
|
int CS::Editor::run()
|
||||||
{
|
{
|
||||||
/// \todo Instead of creating an empty document, open a small welcome dialogue window with buttons for new/load/recent projects
|
mStartup.show();
|
||||||
createDocument();
|
|
||||||
|
|
||||||
return QApplication::exec();
|
return QApplication::exec();
|
||||||
}
|
}
|
|
@ -4,7 +4,10 @@
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
|
|
||||||
#include "model/doc/documentmanager.hpp"
|
#include "model/doc/documentmanager.hpp"
|
||||||
|
|
||||||
#include "view/doc/viewmanager.hpp"
|
#include "view/doc/viewmanager.hpp"
|
||||||
|
#include "view/doc/startup.hpp"
|
||||||
|
#include "view/doc/opendialog.hpp"
|
||||||
|
|
||||||
namespace CS
|
namespace CS
|
||||||
{
|
{
|
||||||
|
@ -16,6 +19,8 @@ namespace CS
|
||||||
|
|
||||||
CSMDoc::DocumentManager mDocumentManager;
|
CSMDoc::DocumentManager mDocumentManager;
|
||||||
CSVDoc::ViewManager mViewManager;
|
CSVDoc::ViewManager mViewManager;
|
||||||
|
CSVDoc::StartupDialogue mStartup;
|
||||||
|
OpenDialog mOpenDialog;
|
||||||
|
|
||||||
// not implemented
|
// not implemented
|
||||||
Editor (const Editor&);
|
Editor (const Editor&);
|
||||||
|
@ -28,9 +33,12 @@ namespace CS
|
||||||
int run();
|
int run();
|
||||||
///< \return error status
|
///< \return error status
|
||||||
|
|
||||||
public slots:
|
private slots:
|
||||||
|
|
||||||
void createDocument();
|
void createDocument();
|
||||||
|
|
||||||
|
void loadDocument();
|
||||||
|
void openFiles();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,10 +1,66 @@
|
||||||
|
|
||||||
#include "document.hpp"
|
#include "document.hpp"
|
||||||
|
|
||||||
CSMDoc::Document::Document (const std::string& name)
|
#include <cassert>
|
||||||
|
|
||||||
|
void CSMDoc::Document::load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||||
|
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified)
|
||||||
|
{
|
||||||
|
assert (begin!=end);
|
||||||
|
|
||||||
|
std::vector<boost::filesystem::path>::const_iterator end2 (end);
|
||||||
|
|
||||||
|
if (lastAsModified)
|
||||||
|
--end2;
|
||||||
|
|
||||||
|
for (std::vector<boost::filesystem::path>::const_iterator iter (begin); iter!=end2; ++iter)
|
||||||
|
getData().loadFile (*iter, true);
|
||||||
|
|
||||||
|
if (lastAsModified)
|
||||||
|
getData().loadFile (*end2, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMDoc::Document::createBase()
|
||||||
|
{
|
||||||
|
static const char *sGlobals[] =
|
||||||
|
{
|
||||||
|
"Day", "DaysPassed", "GameHour", "Month", "PCRace", "PCVampire", "PCWerewolf", "PCYear", 0
|
||||||
|
};
|
||||||
|
|
||||||
|
for (int i=0; sGlobals[i]; ++i)
|
||||||
|
{
|
||||||
|
ESM::Global record;
|
||||||
|
record.mId = sGlobals[i];
|
||||||
|
record.mValue = i==0 ? 1 : 0;
|
||||||
|
record.mType = ESM::VT_Float;
|
||||||
|
getData().getGlobals().add (record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CSMDoc::Document::Document (const std::vector<boost::filesystem::path>& files, bool new_)
|
||||||
: mTools (mData)
|
: mTools (mData)
|
||||||
{
|
{
|
||||||
mName = name; ///< \todo replace with ESX list
|
if (files.empty())
|
||||||
|
throw std::runtime_error ("Empty content file sequence");
|
||||||
|
|
||||||
|
/// \todo adjust last file name:
|
||||||
|
/// \li make sure it is located in the data-local directory (adjust path if necessary)
|
||||||
|
/// \li make sure the extension matches the new scheme (change it if necesarry)
|
||||||
|
|
||||||
|
mName = files.back().filename().string();
|
||||||
|
|
||||||
|
if (files.size()>1 || !new_)
|
||||||
|
{
|
||||||
|
std::vector<boost::filesystem::path>::const_iterator end = files.end();
|
||||||
|
|
||||||
|
if (new_)
|
||||||
|
--end;
|
||||||
|
|
||||||
|
load (files.begin(), end, !new_);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (new_ && files.size()==1)
|
||||||
|
createBase();
|
||||||
|
|
||||||
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
|
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
|
||||||
|
|
||||||
|
@ -86,10 +142,10 @@ void CSMDoc::Document::saving()
|
||||||
|
|
||||||
if (mSaveCount>15)
|
if (mSaveCount>15)
|
||||||
{
|
{
|
||||||
mSaveCount = 0;
|
mSaveCount = 0;
|
||||||
mSaveTimer.stop();
|
mSaveTimer.stop();
|
||||||
mUndoStack.setClean();
|
mUndoStack.setClean();
|
||||||
emit stateChanged (getState(), this);
|
emit stateChanged (getState(), this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <QUndoStack>
|
#include <QUndoStack>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QTimer>
|
#include <QTimer>
|
||||||
|
@ -38,10 +40,15 @@ namespace CSMDoc
|
||||||
Document (const Document&);
|
Document (const Document&);
|
||||||
Document& operator= (const Document&);
|
Document& operator= (const Document&);
|
||||||
|
|
||||||
|
void load (const std::vector<boost::filesystem::path>::const_iterator& begin,
|
||||||
|
const std::vector<boost::filesystem::path>::const_iterator& end, bool lastAsModified);
|
||||||
|
///< \param lastAsModified Store the last file in Modified instead of merging it into Base.
|
||||||
|
|
||||||
|
void createBase();
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Document (const std::string& name);
|
Document (const std::vector<boost::filesystem::path>& files, bool new_);
|
||||||
///< \todo replace name with ESX list
|
|
||||||
|
|
||||||
QUndoStack& getUndoStack();
|
QUndoStack& getUndoStack();
|
||||||
|
|
||||||
|
|
|
@ -14,9 +14,10 @@ CSMDoc::DocumentManager::~DocumentManager()
|
||||||
delete *iter;
|
delete *iter;
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::string& name)
|
CSMDoc::Document *CSMDoc::DocumentManager::addDocument (const std::vector<boost::filesystem::path>& files,
|
||||||
|
bool new_)
|
||||||
{
|
{
|
||||||
Document *document = new Document (name);
|
Document *document = new Document (files, new_);
|
||||||
|
|
||||||
mDocuments.push_back (document);
|
mDocuments.push_back (document);
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include <vector>
|
#include <vector>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
namespace CSMDoc
|
namespace CSMDoc
|
||||||
{
|
{
|
||||||
class Document;
|
class Document;
|
||||||
|
@ -21,8 +23,11 @@ namespace CSMDoc
|
||||||
|
|
||||||
~DocumentManager();
|
~DocumentManager();
|
||||||
|
|
||||||
Document *addDocument (const std::string& name);
|
Document *addDocument (const std::vector<boost::filesystem::path>& files, bool new_);
|
||||||
///< The ownership of the returned document is not transferred to the caller.
|
///< The ownership of the returned document is not transferred to the caller.
|
||||||
|
///
|
||||||
|
/// \param new_ Do not load the last content file in \a files and instead create in an
|
||||||
|
/// appropriate way.
|
||||||
|
|
||||||
bool removeDocument (Document *document);
|
bool removeDocument (Document *document);
|
||||||
///< \return last document removed?
|
///< \return last document removed?
|
||||||
|
|
|
@ -28,7 +28,8 @@ namespace CSMWorld
|
||||||
{
|
{
|
||||||
Display_String,
|
Display_String,
|
||||||
Display_Integer,
|
Display_Integer,
|
||||||
Display_Float
|
Display_Float,
|
||||||
|
Display_Var
|
||||||
};
|
};
|
||||||
|
|
||||||
std::string mTitle;
|
std::string mTitle;
|
||||||
|
|
|
@ -17,9 +17,9 @@ namespace CSMWorld
|
||||||
|
|
||||||
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
{
|
{
|
||||||
ESXRecordT base = record.getBase();
|
ESXRecordT record2 = record.get();
|
||||||
base.mValue = data.toFloat();
|
record2.mValue = data.toFloat();
|
||||||
record.setModified (base);
|
record.setModified (record2);
|
||||||
}
|
}
|
||||||
|
|
||||||
virtual bool isEditable() const
|
virtual bool isEditable() const
|
||||||
|
@ -91,6 +91,68 @@ namespace CSMWorld
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct VarTypeColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
VarTypeColumn() : Column<ESXRecordT> ("Type", ColumnBase::Display_Integer) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
return static_cast<int> (record.get().mType);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
ESXRecordT record2 = record.get();
|
||||||
|
record2.mType = static_cast<ESM::VarType> (data.toInt());
|
||||||
|
record.setModified (record2);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
struct VarValueColumn : public Column<ESXRecordT>
|
||||||
|
{
|
||||||
|
VarValueColumn() : Column<ESXRecordT> ("Value", ColumnBase::Display_Var) {}
|
||||||
|
|
||||||
|
virtual QVariant get (const Record<ESXRecordT>& record) const
|
||||||
|
{
|
||||||
|
switch (record.get().mType)
|
||||||
|
{
|
||||||
|
case ESM::VT_String: return record.get().mStr.c_str(); break;
|
||||||
|
case ESM::VT_Int: return record.get().mI; break;
|
||||||
|
case ESM::VT_Float: return record.get().mF; break;
|
||||||
|
|
||||||
|
default: return QVariant();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual void set (Record<ESXRecordT>& record, const QVariant& data)
|
||||||
|
{
|
||||||
|
ESXRecordT record2 = record.get();
|
||||||
|
|
||||||
|
switch (record2.mType)
|
||||||
|
{
|
||||||
|
case ESM::VT_String: record2.mStr = data.toString().toUtf8().constData(); break;
|
||||||
|
case ESM::VT_Int: record2.mI = data.toInt(); break;
|
||||||
|
case ESM::VT_Float: record2.mF = data.toFloat(); break;
|
||||||
|
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
record.setModified (record2);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual bool isEditable() const
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
|
@ -5,6 +5,7 @@
|
||||||
|
|
||||||
#include <QAbstractTableModel>
|
#include <QAbstractTableModel>
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
#include <components/esm/loadglob.hpp>
|
#include <components/esm/loadglob.hpp>
|
||||||
|
|
||||||
#include "idtable.hpp"
|
#include "idtable.hpp"
|
||||||
|
@ -27,7 +28,14 @@ CSMWorld::Data::Data()
|
||||||
mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));
|
mGlobals.addColumn (new FixedRecordTypeColumn<ESM::Global> (UniversalId::Type_Global));
|
||||||
mGlobals.addColumn (new FloatValueColumn<ESM::Global>);
|
mGlobals.addColumn (new FloatValueColumn<ESM::Global>);
|
||||||
|
|
||||||
|
mGmsts.addColumn (new StringIdColumn<ESM::GameSetting>);
|
||||||
|
mGmsts.addColumn (new RecordStateColumn<ESM::GameSetting>);
|
||||||
|
mGmsts.addColumn (new FixedRecordTypeColumn<ESM::GameSetting> (UniversalId::Type_Gmst));
|
||||||
|
mGmsts.addColumn (new VarTypeColumn<ESM::GameSetting>);
|
||||||
|
mGmsts.addColumn (new VarValueColumn<ESM::GameSetting>);
|
||||||
|
|
||||||
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
|
addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global);
|
||||||
|
addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst);
|
||||||
}
|
}
|
||||||
|
|
||||||
CSMWorld::Data::~Data()
|
CSMWorld::Data::~Data()
|
||||||
|
@ -59,4 +67,31 @@ QAbstractTableModel *CSMWorld::Data::getTableModel (const UniversalId& id)
|
||||||
void CSMWorld::Data::merge()
|
void CSMWorld::Data::merge()
|
||||||
{
|
{
|
||||||
mGlobals.merge();
|
mGlobals.merge();
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSMWorld::Data::loadFile (const boost::filesystem::path& path, bool base)
|
||||||
|
{
|
||||||
|
ESM::ESMReader reader;
|
||||||
|
/// \todo set encoder
|
||||||
|
reader.open (path.string());
|
||||||
|
|
||||||
|
// Note: We do not need to send update signals here, because at this point the model is not connected
|
||||||
|
// to any view.
|
||||||
|
while (reader.hasMoreRecs())
|
||||||
|
{
|
||||||
|
ESM::NAME n = reader.getRecName();
|
||||||
|
reader.getRecHeader();
|
||||||
|
|
||||||
|
switch (n.val)
|
||||||
|
{
|
||||||
|
case ESM::REC_GLOB: mGlobals.load (reader, base); break;
|
||||||
|
case ESM::REC_GMST: mGmsts.load (reader, base); break;
|
||||||
|
|
||||||
|
|
||||||
|
default:
|
||||||
|
|
||||||
|
/// \todo throw an exception instead, once all records are implemented
|
||||||
|
reader.skipRecord();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -4,7 +4,10 @@
|
||||||
#include <map>
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include <boost/filesystem/path.hpp>
|
||||||
|
|
||||||
#include <components/esm/loadglob.hpp>
|
#include <components/esm/loadglob.hpp>
|
||||||
|
#include <components/esm/loadgmst.hpp>
|
||||||
|
|
||||||
#include "idcollection.hpp"
|
#include "idcollection.hpp"
|
||||||
#include "universalid.hpp"
|
#include "universalid.hpp"
|
||||||
|
@ -16,6 +19,7 @@ namespace CSMWorld
|
||||||
class Data
|
class Data
|
||||||
{
|
{
|
||||||
IdCollection<ESM::Global> mGlobals;
|
IdCollection<ESM::Global> mGlobals;
|
||||||
|
IdCollection<ESM::GameSetting> mGmsts;
|
||||||
std::vector<QAbstractTableModel *> mModels;
|
std::vector<QAbstractTableModel *> mModels;
|
||||||
std::map<UniversalId::Type, QAbstractTableModel *> mModelIndex;
|
std::map<UniversalId::Type, QAbstractTableModel *> mModelIndex;
|
||||||
|
|
||||||
|
@ -44,6 +48,9 @@ namespace CSMWorld
|
||||||
|
|
||||||
void merge();
|
void merge();
|
||||||
///< Merge modified into base.
|
///< Merge modified into base.
|
||||||
|
|
||||||
|
void loadFile (const boost::filesystem::path& path, bool base);
|
||||||
|
///< Merging content of a file into base or modified.
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,4 +3,4 @@
|
||||||
|
|
||||||
CSMWorld::IdCollectionBase::IdCollectionBase() {}
|
CSMWorld::IdCollectionBase::IdCollectionBase() {}
|
||||||
|
|
||||||
CSMWorld::IdCollectionBase::~IdCollectionBase() {}
|
CSMWorld::IdCollectionBase::~IdCollectionBase() {}
|
||||||
|
|
|
@ -11,9 +11,12 @@
|
||||||
|
|
||||||
#include <QVariant>
|
#include <QVariant>
|
||||||
|
|
||||||
#include "columnbase.hpp"
|
#include <components/esm/esmreader.hpp>
|
||||||
|
|
||||||
#include <components/misc/stringops.hpp>
|
#include <components/misc/stringops.hpp>
|
||||||
|
|
||||||
|
#include "columnbase.hpp"
|
||||||
|
|
||||||
namespace CSMWorld
|
namespace CSMWorld
|
||||||
{
|
{
|
||||||
class IdCollectionBase
|
class IdCollectionBase
|
||||||
|
@ -67,9 +70,11 @@ namespace CSMWorld
|
||||||
virtual std::string getId (const RecordBase& record) const = 0;
|
virtual std::string getId (const RecordBase& record) const = 0;
|
||||||
///< Return ID for \a record.
|
///< Return ID for \a record.
|
||||||
///
|
///
|
||||||
/// \attention Throw san exception, if the type of \a record does not match.
|
/// \attention Throws an exception, if the type of \a record does not match.
|
||||||
|
|
||||||
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
virtual const RecordBase& getRecord (const std::string& id) const = 0;
|
||||||
|
|
||||||
|
virtual void load (ESM::ESMReader& reader, bool base) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
///< \brief Collection of ID-based records
|
///< \brief Collection of ID-based records
|
||||||
|
@ -136,6 +141,8 @@ namespace CSMWorld
|
||||||
|
|
||||||
virtual const RecordBase& getRecord (const std::string& id) const;
|
virtual const RecordBase& getRecord (const std::string& id) const;
|
||||||
|
|
||||||
|
virtual void load (ESM::ESMReader& reader, bool base);
|
||||||
|
|
||||||
void addColumn (Column<ESXRecordT> *column);
|
void addColumn (Column<ESXRecordT> *column);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -309,6 +316,62 @@ namespace CSMWorld
|
||||||
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
|
return (record2.isModified() ? record2.mModified : record2.mBase).mId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template<typename ESXRecordT>
|
||||||
|
void IdCollection<ESXRecordT>::load (ESM::ESMReader& reader, bool base)
|
||||||
|
{
|
||||||
|
std::string id = reader.getHNOString ("NAME");
|
||||||
|
|
||||||
|
int index = searchId (id);
|
||||||
|
|
||||||
|
if (reader.isNextSub ("DELE"))
|
||||||
|
{
|
||||||
|
reader.skipRecord();
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
{
|
||||||
|
// deleting a record that does not exist
|
||||||
|
|
||||||
|
// ignore it for now
|
||||||
|
|
||||||
|
/// \todo report the problem to the user
|
||||||
|
}
|
||||||
|
else if (base)
|
||||||
|
{
|
||||||
|
removeRows (index, 1);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mRecords[index].mState = RecordBase::State_Deleted;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ESXRecordT record;
|
||||||
|
record.mId = id;
|
||||||
|
record.load (reader);
|
||||||
|
|
||||||
|
if (index==-1)
|
||||||
|
{
|
||||||
|
// new record
|
||||||
|
Record<ESXRecordT> record2;
|
||||||
|
record2.mState = base ? RecordBase::State_BaseOnly : RecordBase::State_ModifiedOnly;
|
||||||
|
(base ? record2.mBase : record2.mModified) = record;
|
||||||
|
|
||||||
|
appendRecord (record2);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// old record
|
||||||
|
Record<ESXRecordT>& record2 = mRecords[index];
|
||||||
|
|
||||||
|
if (base)
|
||||||
|
record2.mBase = record;
|
||||||
|
else
|
||||||
|
record2.setModified (record);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
template<typename ESXRecordT>
|
template<typename ESXRecordT>
|
||||||
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
|
const RecordBase& IdCollection<ESXRecordT>::getRecord (const std::string& id) const
|
||||||
{
|
{
|
||||||
|
|
|
@ -18,6 +18,7 @@ namespace
|
||||||
{
|
{
|
||||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" },
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, "empty" },
|
||||||
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" },
|
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Globals, "Global Variables" },
|
||||||
|
{ CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_Gmsts, "Game Settings" },
|
||||||
|
|
||||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||||
};
|
};
|
||||||
|
@ -25,6 +26,7 @@ namespace
|
||||||
static const TypeData sIdArg[] =
|
static const TypeData sIdArg[] =
|
||||||
{
|
{
|
||||||
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
|
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Global, "Global Variable" },
|
||||||
|
{ CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Gmst, "Game Setting" },
|
||||||
|
|
||||||
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
{ CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,12 +33,12 @@ namespace CSMWorld
|
||||||
enum Type
|
enum Type
|
||||||
{
|
{
|
||||||
Type_None,
|
Type_None,
|
||||||
|
|
||||||
Type_Globals,
|
Type_Globals,
|
||||||
|
|
||||||
Type_Global,
|
Type_Global,
|
||||||
|
Type_VerificationResults,
|
||||||
|
Type_Gmsts,
|
||||||
|
Type_Gmst
|
||||||
|
|
||||||
Type_VerificationResults
|
|
||||||
};
|
};
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|
62
apps/opencs/view/doc/opendialog.cpp
Normal file
62
apps/opencs/view/doc/opendialog.cpp
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
#include <QDialogButtonBox>
|
||||||
|
|
||||||
|
#include <components/fileorderlist/datafileslist.hpp>
|
||||||
|
|
||||||
|
#include "opendialog.hpp"
|
||||||
|
|
||||||
|
OpenDialog::OpenDialog(QWidget * parent) : QDialog(parent)
|
||||||
|
{
|
||||||
|
QVBoxLayout *layout = new QVBoxLayout(this);
|
||||||
|
mFileSelector = new DataFilesList(mCfgMgr, this);
|
||||||
|
layout->addWidget(mFileSelector);
|
||||||
|
|
||||||
|
//FIXME - same as DataFilesPage::setupDataFiles
|
||||||
|
// We use the Configuration Manager to retrieve the configuration values
|
||||||
|
boost::program_options::variables_map variables;
|
||||||
|
boost::program_options::options_description desc;
|
||||||
|
|
||||||
|
desc.add_options()
|
||||||
|
("data", boost::program_options::value<Files::PathContainer>()->default_value(Files::PathContainer(), "data")->multitoken())
|
||||||
|
("data-local", boost::program_options::value<std::string>()->default_value(""))
|
||||||
|
("fs-strict", boost::program_options::value<bool>()->implicit_value(true)->default_value(false))
|
||||||
|
("encoding", boost::program_options::value<std::string>()->default_value("win1252"));
|
||||||
|
|
||||||
|
boost::program_options::notify(variables);
|
||||||
|
|
||||||
|
mCfgMgr.readConfiguration(variables, desc);
|
||||||
|
|
||||||
|
Files::PathContainer mDataDirs, mDataLocal;
|
||||||
|
if (!variables["data"].empty()) {
|
||||||
|
mDataDirs = Files::PathContainer(variables["data"].as<Files::PathContainer>());
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string local = variables["data-local"].as<std::string>();
|
||||||
|
if (!local.empty()) {
|
||||||
|
mDataLocal.push_back(Files::PathContainer::value_type(local));
|
||||||
|
}
|
||||||
|
|
||||||
|
mCfgMgr.processPaths(mDataDirs);
|
||||||
|
mCfgMgr.processPaths(mDataLocal);
|
||||||
|
|
||||||
|
// Set the charset for reading the esm/esp files
|
||||||
|
QString encoding = QString::fromStdString(variables["encoding"].as<std::string>());
|
||||||
|
|
||||||
|
Files::PathContainer dataDirs;
|
||||||
|
dataDirs.insert(dataDirs.end(), mDataDirs.begin(), mDataDirs.end());
|
||||||
|
dataDirs.insert(dataDirs.end(), mDataLocal.begin(), mDataLocal.end());
|
||||||
|
mFileSelector->setupDataFiles(dataDirs, encoding);
|
||||||
|
|
||||||
|
buttonBox = new QDialogButtonBox(QDialogButtonBox::Open | QDialogButtonBox::Cancel, Qt::Horizontal, this);
|
||||||
|
connect(buttonBox, SIGNAL(accepted()), this, SLOT(accept()));
|
||||||
|
connect(buttonBox, SIGNAL(rejected()), this, SLOT(reject()));
|
||||||
|
layout->addWidget(buttonBox);
|
||||||
|
|
||||||
|
setLayout(layout);
|
||||||
|
setWindowTitle(tr("Open"));
|
||||||
|
}
|
||||||
|
|
||||||
|
void OpenDialog::getFileList(std::vector<boost::filesystem::path>& paths)
|
||||||
|
{
|
||||||
|
mFileSelector->selectedFiles(paths);
|
||||||
|
}
|
17
apps/opencs/view/doc/opendialog.hpp
Normal file
17
apps/opencs/view/doc/opendialog.hpp
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
#include <qdialog.h>
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
class DataFilesList;
|
||||||
|
class QDialogButtonBox;
|
||||||
|
|
||||||
|
class OpenDialog : public QDialog {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
OpenDialog(QWidget * parent = 0);
|
||||||
|
|
||||||
|
void getFileList(std::vector<boost::filesystem::path>& paths);
|
||||||
|
private:
|
||||||
|
DataFilesList * mFileSelector;
|
||||||
|
QDialogButtonBox * buttonBox;
|
||||||
|
Files::ConfigurationManager mCfgMgr;
|
||||||
|
};
|
20
apps/opencs/view/doc/startup.cpp
Normal file
20
apps/opencs/view/doc/startup.cpp
Normal file
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
#include "startup.hpp"
|
||||||
|
|
||||||
|
#include <QPushButton>
|
||||||
|
#include <QHBoxLayout>
|
||||||
|
|
||||||
|
CSVDoc::StartupDialogue::StartupDialogue()
|
||||||
|
{
|
||||||
|
QHBoxLayout *layout = new QHBoxLayout (this);
|
||||||
|
|
||||||
|
QPushButton *createDocument = new QPushButton ("new", this);
|
||||||
|
connect (createDocument, SIGNAL (clicked()), this, SIGNAL (createDocument()));
|
||||||
|
layout->addWidget (createDocument);
|
||||||
|
|
||||||
|
QPushButton *loadDocument = new QPushButton ("load", this);
|
||||||
|
connect (loadDocument, SIGNAL (clicked()), this, SIGNAL (loadDocument()));
|
||||||
|
layout->addWidget (loadDocument);
|
||||||
|
|
||||||
|
setLayout (layout);
|
||||||
|
}
|
24
apps/opencs/view/doc/startup.hpp
Normal file
24
apps/opencs/view/doc/startup.hpp
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
#ifndef CSV_DOC_STARTUP_H
|
||||||
|
#define CSV_DOC_STARTUP_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace CSVDoc
|
||||||
|
{
|
||||||
|
class StartupDialogue : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
StartupDialogue();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
|
||||||
|
void createDocument();
|
||||||
|
|
||||||
|
void loadDocument();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif
|
|
@ -32,6 +32,10 @@ void CSVDoc::View::setupFileMenu()
|
||||||
connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest()));
|
connect (new_, SIGNAL (triggered()), this, SIGNAL (newDocumentRequest()));
|
||||||
file->addAction (new_);
|
file->addAction (new_);
|
||||||
|
|
||||||
|
QAction *open = new QAction (tr ("&Open"), this);
|
||||||
|
connect (open, SIGNAL (triggered()), this, SIGNAL (loadDocumentRequest()));
|
||||||
|
file->addAction (open);
|
||||||
|
|
||||||
mSave = new QAction (tr ("&Save"), this);
|
mSave = new QAction (tr ("&Save"), this);
|
||||||
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
|
connect (mSave, SIGNAL (triggered()), this, SLOT (save()));
|
||||||
file->addAction (mSave);
|
file->addAction (mSave);
|
||||||
|
@ -67,6 +71,10 @@ void CSVDoc::View::setupWorldMenu()
|
||||||
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
|
connect (globals, SIGNAL (triggered()), this, SLOT (addGlobalsSubView()));
|
||||||
world->addAction (globals);
|
world->addAction (globals);
|
||||||
|
|
||||||
|
QAction *gmsts = new QAction (tr ("Game settings"), this);
|
||||||
|
connect (gmsts, SIGNAL (triggered()), this, SLOT (addGmstsSubView()));
|
||||||
|
world->addAction (gmsts);
|
||||||
|
|
||||||
mVerify = new QAction (tr ("&Verify"), this);
|
mVerify = new QAction (tr ("&Verify"), this);
|
||||||
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
|
connect (mVerify, SIGNAL (triggered()), this, SLOT (verify()));
|
||||||
world->addAction (mVerify);
|
world->addAction (mVerify);
|
||||||
|
@ -213,4 +221,9 @@ void CSVDoc::View::verify()
|
||||||
void CSVDoc::View::addGlobalsSubView()
|
void CSVDoc::View::addGlobalsSubView()
|
||||||
{
|
{
|
||||||
addSubView (CSMWorld::UniversalId::Type_Globals);
|
addSubView (CSMWorld::UniversalId::Type_Globals);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CSVDoc::View::addGmstsSubView()
|
||||||
|
{
|
||||||
|
addSubView (CSMWorld::UniversalId::Type_Gmsts);
|
||||||
}
|
}
|
|
@ -84,6 +84,8 @@ namespace CSVDoc
|
||||||
|
|
||||||
void newDocumentRequest();
|
void newDocumentRequest();
|
||||||
|
|
||||||
|
void loadDocumentRequest();
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
|
|
||||||
void addSubView (const CSMWorld::UniversalId& id);
|
void addSubView (const CSMWorld::UniversalId& id);
|
||||||
|
@ -97,6 +99,8 @@ namespace CSVDoc
|
||||||
void verify();
|
void verify();
|
||||||
|
|
||||||
void addGlobalsSubView();
|
void addGlobalsSubView();
|
||||||
|
|
||||||
|
void addGmstsSubView();
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -57,6 +57,7 @@ CSVDoc::View *CSVDoc::ViewManager::addView (CSMDoc::Document *document)
|
||||||
view->show();
|
view->show();
|
||||||
|
|
||||||
connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest()));
|
connect (view, SIGNAL (newDocumentRequest ()), this, SIGNAL (newDocumentRequest()));
|
||||||
|
connect (view, SIGNAL (loadDocumentRequest ()), this, SIGNAL (loadDocumentRequest()));
|
||||||
|
|
||||||
updateIndices();
|
updateIndices();
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,8 @@ namespace CSVDoc
|
||||||
|
|
||||||
void newDocumentRequest();
|
void newDocumentRequest();
|
||||||
|
|
||||||
|
void loadDocumentRequest();
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
|
|
||||||
void documentStateChanged (int state, CSMDoc::Document *document);
|
void documentStateChanged (int state, CSMDoc::Document *document);
|
||||||
|
|
|
@ -64,6 +64,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
|
||||||
/// \todo configure widget properly (range, format?)
|
/// \todo configure widget properly (range, format?)
|
||||||
layout->addWidget (widget = new QDoubleSpinBox, i, 1);
|
layout->addWidget (widget = new QDoubleSpinBox, i, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default: break; // silence warnings for other times for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -76,6 +78,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM
|
||||||
|
|
||||||
layout->addWidget (widget = new QLabel, i, 1);
|
layout->addWidget (widget = new QLabel, i, 1);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default: break; // silence warnings for other times for now
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,6 +11,9 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager)
|
||||||
manager.add (CSMWorld::UniversalId::Type_Globals,
|
manager.add (CSMWorld::UniversalId::Type_Globals,
|
||||||
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (true));
|
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (true));
|
||||||
|
|
||||||
|
manager.add (CSMWorld::UniversalId::Type_Gmsts,
|
||||||
|
new CSVDoc::SubViewFactoryWithCreateFlag<TableSubView> (false));
|
||||||
|
|
||||||
manager.add (CSMWorld::UniversalId::Type_Global,
|
manager.add (CSMWorld::UniversalId::Type_Global,
|
||||||
new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
|
new CSVDoc::SubViewFactoryWithCreateFlag<DialogueSubView> (true));
|
||||||
}
|
}
|
|
@ -41,7 +41,7 @@ add_openmw_dir (mwscript
|
||||||
locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions
|
locals scriptmanagerimp compilercontext interpretercontext cellextensions miscextensions
|
||||||
guiextensions soundextensions skyextensions statsextensions containerextensions
|
guiextensions soundextensions skyextensions statsextensions containerextensions
|
||||||
aiextensions controlextensions extensions globalscripts ref dialogueextensions
|
aiextensions controlextensions extensions globalscripts ref dialogueextensions
|
||||||
animationextensions transformationextensions consoleextensions userextensions
|
animationextensions transformationextensions consoleextensions userextensions locals
|
||||||
)
|
)
|
||||||
|
|
||||||
add_openmw_dir (mwsound
|
add_openmw_dir (mwsound
|
||||||
|
|
|
@ -210,18 +210,31 @@ void OMW::Engine::setCell (const std::string& cellName)
|
||||||
|
|
||||||
// Set master file (esm)
|
// Set master file (esm)
|
||||||
// - If the given name does not have an extension, ".esm" is added automatically
|
// - If the given name does not have an extension, ".esm" is added automatically
|
||||||
// - Currently OpenMW only supports one master at the same time.
|
|
||||||
|
|
||||||
void OMW::Engine::addMaster (const std::string& master)
|
void OMW::Engine::addMaster (const std::string& master)
|
||||||
{
|
{
|
||||||
assert (mMaster.empty());
|
mMaster.push_back(master);
|
||||||
mMaster = master;
|
std::string &str = mMaster.back();
|
||||||
|
|
||||||
// Append .esm if not already there
|
// Append .esm if not already there
|
||||||
std::string::size_type sep = mMaster.find_last_of (".");
|
std::string::size_type sep = str.find_last_of (".");
|
||||||
if (sep == std::string::npos)
|
if (sep == std::string::npos)
|
||||||
{
|
{
|
||||||
mMaster += ".esm";
|
str += ".esm";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add plugin file (esp)
|
||||||
|
void OMW::Engine::addPlugin (const std::string& plugin)
|
||||||
|
{
|
||||||
|
mPlugins.push_back(plugin);
|
||||||
|
std::string &str = mPlugins.back();
|
||||||
|
|
||||||
|
// Append .esp if not already there
|
||||||
|
std::string::size_type sep = str.find_last_of (".");
|
||||||
|
if (sep == std::string::npos)
|
||||||
|
{
|
||||||
|
str += ".esp";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -326,13 +339,13 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
|
||||||
MWGui::CursorReplace replacer;
|
MWGui::CursorReplace replacer;
|
||||||
|
|
||||||
// Create the world
|
// Create the world
|
||||||
mEnvironment.setWorld (new MWWorld::World (*mOgre, mFileCollections, mMaster,
|
mEnvironment.setWorld( new MWWorld::World (*mOgre, mFileCollections, mMaster, mPlugins,
|
||||||
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
|
mResDir, mCfgMgr.getCachePath(), mNewGame, mEncoder, mFallbackMap,
|
||||||
mActivationDistanceOverride));
|
mActivationDistanceOverride));
|
||||||
|
|
||||||
//Load translation data
|
//Load translation data
|
||||||
mTranslationDataStorage.setEncoder(mEncoder);
|
mTranslationDataStorage.setEncoder(mEncoder);
|
||||||
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster);
|
mTranslationDataStorage.loadTranslationData(mFileCollections, mMaster[0]);
|
||||||
|
|
||||||
// Create window manager - this manages all the MW-specific GUI windows
|
// Create window manager - this manages all the MW-specific GUI windows
|
||||||
MWScript::registerExtensions (mExtensions);
|
MWScript::registerExtensions (mExtensions);
|
||||||
|
|
|
@ -67,7 +67,8 @@ namespace OMW
|
||||||
boost::filesystem::path mResDir;
|
boost::filesystem::path mResDir;
|
||||||
OEngine::Render::OgreRenderer *mOgre;
|
OEngine::Render::OgreRenderer *mOgre;
|
||||||
std::string mCellName;
|
std::string mCellName;
|
||||||
std::string mMaster;
|
std::vector<std::string> mMaster;
|
||||||
|
std::vector<std::string> mPlugins;
|
||||||
int mFpsLevel;
|
int mFpsLevel;
|
||||||
bool mDebug;
|
bool mDebug;
|
||||||
bool mVerboseScripts;
|
bool mVerboseScripts;
|
||||||
|
@ -132,9 +133,12 @@ namespace OMW
|
||||||
|
|
||||||
/// Set master file (esm)
|
/// Set master file (esm)
|
||||||
/// - If the given name does not have an extension, ".esm" is added automatically
|
/// - If the given name does not have an extension, ".esm" is added automatically
|
||||||
/// - Currently OpenMW only supports one master at the same time.
|
|
||||||
void addMaster(const std::string& master);
|
void addMaster(const std::string& master);
|
||||||
|
|
||||||
|
/// Same as "addMaster", but for plugin files (esp)
|
||||||
|
/// - If the given name does not have an extension, ".esp" is added automatically
|
||||||
|
void addPlugin(const std::string& plugin);
|
||||||
|
|
||||||
/// Enable fps counter
|
/// Enable fps counter
|
||||||
void showFPS(int level);
|
void showFPS(int level);
|
||||||
|
|
||||||
|
|
|
@ -211,18 +211,21 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
|
||||||
master.push_back("Morrowind");
|
master.push_back("Morrowind");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (master.size() > 1)
|
|
||||||
{
|
|
||||||
std::cout
|
|
||||||
<< "Ignoring all but the first master file (multiple master files not yet supported)."
|
|
||||||
<< std::endl;
|
|
||||||
}
|
|
||||||
engine.addMaster(master[0]);
|
|
||||||
|
|
||||||
StringsVector plugin = variables["plugin"].as<StringsVector>();
|
StringsVector plugin = variables["plugin"].as<StringsVector>();
|
||||||
if (!plugin.empty())
|
// Removed check for 255 files, which would be the hard-coded limit in Morrowind.
|
||||||
|
// I'll keep the following variable in, maybe we can use it for something different.
|
||||||
|
// Say, a feedback like "loading file x/cnt".
|
||||||
|
// Commenting this out for now to silence compiler warning.
|
||||||
|
//int cnt = master.size() + plugin.size();
|
||||||
|
|
||||||
|
// Prepare loading master/plugin files (i.e. send filenames to engine)
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++)
|
||||||
{
|
{
|
||||||
std::cout << "Ignoring plugin files (plugins not yet supported)." << std::endl;
|
engine.addMaster(master[i]);
|
||||||
|
}
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < plugin.size(); i++)
|
||||||
|
{
|
||||||
|
engine.addPlugin(plugin[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
// startup-settings
|
// startup-settings
|
||||||
|
|
|
@ -105,7 +105,7 @@ namespace MWBase
|
||||||
|
|
||||||
virtual const MWWorld::ESMStore& getStore() const = 0;
|
virtual const MWWorld::ESMStore& getStore() const = 0;
|
||||||
|
|
||||||
virtual ESM::ESMReader& getEsmReader() = 0;
|
virtual std::vector<ESM::ESMReader>& getEsmReader() = 0;
|
||||||
|
|
||||||
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
|
virtual MWWorld::LocalScripts& getLocalScripts() = 0;
|
||||||
|
|
||||||
|
|
|
@ -7,6 +7,8 @@
|
||||||
|
|
||||||
#include <boost/lexical_cast.hpp>
|
#include <boost/lexical_cast.hpp>
|
||||||
|
|
||||||
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
|
@ -240,6 +242,12 @@ namespace MWGui
|
||||||
if (it != invStore.end() && *it == item)
|
if (it != invStore.end() && *it == item)
|
||||||
{
|
{
|
||||||
invStore.equip(slot, invStore.end());
|
invStore.equip(slot, invStore.end());
|
||||||
|
std::string script = MWWorld::Class::get(*it).getScript(*it);
|
||||||
|
|
||||||
|
// Unset OnPCEquip Variable on item's script, if it has a script with that variable declared
|
||||||
|
if(script != "")
|
||||||
|
(*it).mRefData->getLocals().setVarByInt(script, "onpcequip", 0);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -144,7 +144,7 @@ namespace MWRender
|
||||||
std::map<uint16_t, int> indexes;
|
std::map<uint16_t, int> indexes;
|
||||||
initTerrainTextures(&terrainData, cellX, cellY,
|
initTerrainTextures(&terrainData, cellX, cellY,
|
||||||
x * numTextures, y * numTextures,
|
x * numTextures, y * numTextures,
|
||||||
numTextures, indexes);
|
numTextures, indexes, land->mPlugin);
|
||||||
|
|
||||||
if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL)
|
if (mTerrainGroup.getTerrain(terrainX, terrainY) == NULL)
|
||||||
{
|
{
|
||||||
|
@ -213,8 +213,13 @@ namespace MWRender
|
||||||
void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData,
|
void TerrainManager::initTerrainTextures(Terrain::ImportData* terrainData,
|
||||||
int cellX, int cellY,
|
int cellX, int cellY,
|
||||||
int fromX, int fromY, int size,
|
int fromX, int fromY, int size,
|
||||||
std::map<uint16_t, int>& indexes)
|
std::map<uint16_t, int>& indexes, size_t plugin)
|
||||||
{
|
{
|
||||||
|
// FIXME: In a multiple esm configuration, we have multiple palettes. Since this code
|
||||||
|
// crosses cell boundaries, we no longer have a unique terrain palette. Instead, we need
|
||||||
|
// to adopt the following code for a dynamic palette. And this is evil - the current design
|
||||||
|
// does not work well for this task...
|
||||||
|
|
||||||
assert(terrainData != NULL && "Must have valid terrain data");
|
assert(terrainData != NULL && "Must have valid terrain data");
|
||||||
assert(fromX >= 0 && fromY >= 0 &&
|
assert(fromX >= 0 && fromY >= 0 &&
|
||||||
"Can't get a terrain texture on terrain outside the current cell");
|
"Can't get a terrain texture on terrain outside the current cell");
|
||||||
|
@ -227,12 +232,20 @@ namespace MWRender
|
||||||
//
|
//
|
||||||
//If we don't sort the ltex indexes, the splatting order may differ between
|
//If we don't sort the ltex indexes, the splatting order may differ between
|
||||||
//cells which may lead to inconsistent results when shading between cells
|
//cells which may lead to inconsistent results when shading between cells
|
||||||
|
int num = MWBase::Environment::get().getWorld()->getStore().get<ESM::LandTexture>().getSize(plugin);
|
||||||
std::set<uint16_t> ltexIndexes;
|
std::set<uint16_t> ltexIndexes;
|
||||||
for ( int y = fromY - 1; y < fromY + size + 1; y++ )
|
for ( int y = fromY - 1; y < fromY + size + 1; y++ )
|
||||||
{
|
{
|
||||||
for ( int x = fromX - 1; x < fromX + size + 1; x++ )
|
for ( int x = fromX - 1; x < fromX + size + 1; x++ )
|
||||||
{
|
{
|
||||||
ltexIndexes.insert(getLtexIndexAt(cellX, cellY, x, y));
|
int idx = getLtexIndexAt(cellX, cellY, x, y);
|
||||||
|
// This is a quick hack to prevent the program from trying to fetch textures
|
||||||
|
// from a neighboring cell, which might originate from a different plugin,
|
||||||
|
// and use a separate texture palette. Right now, we simply cast it to the
|
||||||
|
// default texture (i.e. 0).
|
||||||
|
if (idx > num)
|
||||||
|
idx = 0;
|
||||||
|
ltexIndexes.insert(idx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -244,7 +257,7 @@ namespace MWRender
|
||||||
iter != ltexIndexes.end();
|
iter != ltexIndexes.end();
|
||||||
++iter )
|
++iter )
|
||||||
{
|
{
|
||||||
const uint16_t ltexIndex = *iter;
|
uint16_t ltexIndex = *iter;
|
||||||
//this is the base texture, so we can ignore this at present
|
//this is the base texture, so we can ignore this at present
|
||||||
if ( ltexIndex == baseTexture )
|
if ( ltexIndex == baseTexture )
|
||||||
{
|
{
|
||||||
|
@ -260,8 +273,11 @@ namespace MWRender
|
||||||
const MWWorld::Store<ESM::LandTexture> <exStore =
|
const MWWorld::Store<ESM::LandTexture> <exStore =
|
||||||
MWBase::Environment::get().getWorld()->getStore().get<ESM::LandTexture>();
|
MWBase::Environment::get().getWorld()->getStore().get<ESM::LandTexture>();
|
||||||
|
|
||||||
assert( (int)ltexStore.getSize() >= (int)ltexIndex - 1 &&
|
// NOTE: using the quick hack above, we should no longer end up with textures indices
|
||||||
"LAND.VTEX must be within the bounds of the LTEX array");
|
// that are out of bounds. However, I haven't updated the test to a multi-palette
|
||||||
|
// system yet. We probably need more work here, so we skip it for now.
|
||||||
|
//assert( (int)ltexStore.getSize() >= (int)ltexIndex - 1 &&
|
||||||
|
//"LAND.VTEX must be within the bounds of the LTEX array");
|
||||||
|
|
||||||
std::string texture;
|
std::string texture;
|
||||||
if ( ltexIndex == 0 )
|
if ( ltexIndex == 0 )
|
||||||
|
@ -270,7 +286,7 @@ namespace MWRender
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
texture = ltexStore.search(ltexIndex-1)->mTexture;
|
texture = ltexStore.search(ltexIndex-1, plugin)->mTexture;
|
||||||
//TODO this is needed due to MWs messed up texture handling
|
//TODO this is needed due to MWs messed up texture handling
|
||||||
texture = texture.substr(0, texture.rfind(".")) + ".dds";
|
texture = texture.substr(0, texture.rfind(".")) + ".dds";
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,7 +75,7 @@ namespace MWRender{
|
||||||
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
|
void initTerrainTextures(Ogre::Terrain::ImportData* terrainData,
|
||||||
int cellX, int cellY,
|
int cellX, int cellY,
|
||||||
int fromX, int fromY, int size,
|
int fromX, int fromY, int size,
|
||||||
std::map<uint16_t, int>& indexes);
|
std::map<uint16_t, int>& indexes, size_t plugin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates the blend (splatting maps) for the given terrain from the ltex data.
|
* Creates the blend (splatting maps) for the given terrain from the ltex data.
|
||||||
|
|
|
@ -50,6 +50,14 @@ namespace MWScript
|
||||||
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item);
|
MWWorld::ManualRef ref (MWBase::Environment::get().getWorld()->getStore(), item);
|
||||||
|
|
||||||
ref.getPtr().getRefData().setCount (count);
|
ref.getPtr().getRefData().setCount (count);
|
||||||
|
|
||||||
|
// Configure item's script variables
|
||||||
|
std::string script = MWWorld::Class::get(ref.getPtr()).getScript(ref.getPtr());
|
||||||
|
if (script != "")
|
||||||
|
{
|
||||||
|
const ESM::Script *esmscript = MWBase::Environment::get().getWorld()->getStore().get<ESM::Script>().find (script);
|
||||||
|
ref.getPtr().getRefData().setLocals(*esmscript);
|
||||||
|
}
|
||||||
|
|
||||||
MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr());
|
MWWorld::Class::get (ptr).getContainerStore (ptr).add (ref.getPtr());
|
||||||
}
|
}
|
||||||
|
|
41
apps/openmw/mwscript/locals.cpp
Normal file
41
apps/openmw/mwscript/locals.cpp
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
#include "locals.hpp"
|
||||||
|
|
||||||
|
#include "../mwbase/environment.hpp"
|
||||||
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
|
namespace MWScript
|
||||||
|
{
|
||||||
|
void Locals::configure (const ESM::Script& script)
|
||||||
|
{
|
||||||
|
mShorts.clear();
|
||||||
|
mShorts.resize (script.mData.mNumShorts, 0);
|
||||||
|
mLongs.clear();
|
||||||
|
mLongs.resize (script.mData.mNumLongs, 0);
|
||||||
|
mFloats.clear();
|
||||||
|
mFloats.resize (script.mData.mNumFloats, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Locals::setVarByInt(const std::string& script, const std::string& var, int val)
|
||||||
|
{
|
||||||
|
Compiler::Locals locals = MWBase::Environment::get().getScriptManager()->getLocals(script);
|
||||||
|
int index = locals.getIndex(var);
|
||||||
|
char type = locals.getType(var);
|
||||||
|
if(index != -1)
|
||||||
|
{
|
||||||
|
switch(type)
|
||||||
|
{
|
||||||
|
case 's':
|
||||||
|
mShorts.at (index) = val; break;
|
||||||
|
|
||||||
|
case 'l':
|
||||||
|
mLongs.at (index) = val; break;
|
||||||
|
|
||||||
|
case 'f':
|
||||||
|
mFloats.at (index) = val; break;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,21 +8,16 @@
|
||||||
|
|
||||||
namespace MWScript
|
namespace MWScript
|
||||||
{
|
{
|
||||||
struct Locals
|
class Locals
|
||||||
{
|
{
|
||||||
std::vector<Interpreter::Type_Short> mShorts;
|
public:
|
||||||
std::vector<Interpreter::Type_Integer> mLongs;
|
std::vector<Interpreter::Type_Short> mShorts;
|
||||||
std::vector<Interpreter::Type_Float> mFloats;
|
std::vector<Interpreter::Type_Integer> mLongs;
|
||||||
|
std::vector<Interpreter::Type_Float> mFloats;
|
||||||
|
|
||||||
|
void configure (const ESM::Script& script);
|
||||||
|
bool setVarByInt(const std::string& script, const std::string& var, int val);
|
||||||
|
|
||||||
void configure (const ESM::Script& script)
|
|
||||||
{
|
|
||||||
mShorts.clear();
|
|
||||||
mShorts.resize (script.mData.mNumShorts, 0);
|
|
||||||
mLongs.clear();
|
|
||||||
mLongs.resize (script.mData.mNumLongs, 0);
|
|
||||||
mFloats.clear();
|
|
||||||
mFloats.resize (script.mData.mNumFloats, 0);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
|
||||||
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
#include "inventorystore.hpp"
|
#include "inventorystore.hpp"
|
||||||
#include "player.hpp"
|
#include "player.hpp"
|
||||||
#include "class.hpp"
|
#include "class.hpp"
|
||||||
|
@ -35,6 +37,8 @@ namespace MWWorld
|
||||||
|
|
||||||
std::string npcRace = actor.get<ESM::NPC>()->mBase->mRace;
|
std::string npcRace = actor.get<ESM::NPC>()->mBase->mRace;
|
||||||
|
|
||||||
|
bool equipped = false;
|
||||||
|
|
||||||
// equip the item in the first free slot
|
// equip the item in the first free slot
|
||||||
for (std::vector<int>::const_iterator slot=slots.first.begin();
|
for (std::vector<int>::const_iterator slot=slots.first.begin();
|
||||||
slot!=slots.first.end(); ++slot)
|
slot!=slots.first.end(); ++slot)
|
||||||
|
@ -91,6 +95,7 @@ namespace MWWorld
|
||||||
if (slot == --slots.first.end())
|
if (slot == --slots.first.end())
|
||||||
{
|
{
|
||||||
invStore.equip(*slot, it);
|
invStore.equip(*slot, it);
|
||||||
|
equipped = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -98,8 +103,15 @@ namespace MWWorld
|
||||||
{
|
{
|
||||||
// slot is not occupied
|
// slot is not occupied
|
||||||
invStore.equip(*slot, it);
|
invStore.equip(*slot, it);
|
||||||
|
equipped = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string script = MWWorld::Class::get(*it).getScript(*it);
|
||||||
|
|
||||||
|
/* Set OnPCEquip Variable on item's script, if the player is equipping it, and it has a script with that variable declared */
|
||||||
|
if(equipped && actor == MWBase::Environment::get().getWorld()->getPlayer().getPlayer() && script != "")
|
||||||
|
(*it).mRefData->getLocals().setVarByInt(script, "onpcequip", 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -84,7 +84,7 @@ MWWorld::Ptr MWWorld::Cells::getPtrAndCache (const std::string& name, Ptr::CellS
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, ESM::ESMReader& reader)
|
MWWorld::Cells::Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader)
|
||||||
: mStore (store), mReader (reader),
|
: mStore (store), mReader (reader),
|
||||||
mIdCache (20, std::pair<std::string, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
|
mIdCache (20, std::pair<std::string, Ptr::CellStore *> ("", (Ptr::CellStore*)0)), /// \todo make cache size configurable
|
||||||
mIdCacheIndex (0)
|
mIdCacheIndex (0)
|
||||||
|
@ -119,6 +119,7 @@ MWWorld::Ptr::CellStore *MWWorld::Cells::getExterior (int x, int y)
|
||||||
|
|
||||||
if (result->second.mState!=Ptr::CellStore::State_Loaded)
|
if (result->second.mState!=Ptr::CellStore::State_Loaded)
|
||||||
{
|
{
|
||||||
|
// Multiple plugin support for landscape data is much easier than for references. The last plugin wins.
|
||||||
result->second.load (mStore, mReader);
|
result->second.load (mStore, mReader);
|
||||||
fillContainers (result->second);
|
fillContainers (result->second);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
#define GAME_MWWORLD_CELLS_H
|
#define GAME_MWWORLD_CELLS_H
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <list>
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
#include "ptr.hpp"
|
#include "ptr.hpp"
|
||||||
|
@ -19,7 +20,7 @@ namespace MWWorld
|
||||||
class Cells
|
class Cells
|
||||||
{
|
{
|
||||||
const MWWorld::ESMStore& mStore;
|
const MWWorld::ESMStore& mStore;
|
||||||
ESM::ESMReader& mReader;
|
std::vector<ESM::ESMReader>& mReader;
|
||||||
std::map<std::string, CellStore> mInteriors;
|
std::map<std::string, CellStore> mInteriors;
|
||||||
std::map<std::pair<int, int>, CellStore> mExteriors;
|
std::map<std::pair<int, int>, CellStore> mExteriors;
|
||||||
std::vector<std::pair<std::string, CellStore *> > mIdCache;
|
std::vector<std::pair<std::string, CellStore *> > mIdCache;
|
||||||
|
@ -36,7 +37,7 @@ namespace MWWorld
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
Cells (const MWWorld::ESMStore& store, ESM::ESMReader& reader);
|
Cells (const MWWorld::ESMStore& store, std::vector<ESM::ESMReader>& reader);
|
||||||
///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole
|
///< \todo pass the dynamic part of the ESMStore isntead (once it is written) of the whole
|
||||||
/// world
|
/// world
|
||||||
|
|
||||||
|
|
|
@ -10,13 +10,48 @@
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
void CellRefList<X>::load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore)
|
||||||
|
{
|
||||||
|
// Get existing reference, in case we need to overwrite it.
|
||||||
|
typename std::list<LiveRef>::iterator iter = std::find(mList.begin(), mList.end(), ref.mRefnum);
|
||||||
|
|
||||||
|
// Skip this when reference was deleted.
|
||||||
|
// TODO: Support respawning references, in this case, we need to track it somehow.
|
||||||
|
if (ref.mDeleted) {
|
||||||
|
mList.erase(iter);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// for throwing exception on unhandled record type
|
||||||
|
const MWWorld::Store<X> &store = esmStore.get<X>();
|
||||||
|
const X *ptr = store.search(ref.mRefID);
|
||||||
|
|
||||||
|
/// \note no longer redundant - changed to Store<X>::search(), don't throw
|
||||||
|
/// an exception on miss, try to continue (that's how MW does it, anyway)
|
||||||
|
if (ptr == NULL) {
|
||||||
|
std::cout << "Warning: could not resolve cell reference " << ref.mRefID << ", trying to continue anyway" << std::endl;
|
||||||
|
} else {
|
||||||
|
if (iter != mList.end())
|
||||||
|
*iter = LiveRef(ref, ptr);
|
||||||
|
else
|
||||||
|
mList.push_back(LiveRef(ref, ptr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<typename X> bool operator==(const LiveCellRef<X>& ref, int pRefnum)
|
||||||
|
{
|
||||||
|
return (ref.mRef.mRefnum == pRefnum);
|
||||||
|
}
|
||||||
|
|
||||||
CellStore::CellStore (const ESM::Cell *cell)
|
CellStore::CellStore (const ESM::Cell *cell)
|
||||||
: mCell (cell), mState (State_Unloaded)
|
: mCell (cell), mState (State_Unloaded)
|
||||||
{
|
{
|
||||||
mWaterLevel = cell->mWater;
|
mWaterLevel = cell->mWater;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::load (const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::load (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
if (mState!=State_Loaded)
|
if (mState!=State_Loaded)
|
||||||
{
|
{
|
||||||
|
@ -31,7 +66,7 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::preload (const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::preload (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
if (mState==State_Unloaded)
|
if (mState==State_Unloaded)
|
||||||
{
|
{
|
||||||
|
@ -41,57 +76,75 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::listRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::listRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
assert (mCell);
|
assert (mCell);
|
||||||
|
|
||||||
if (mCell->mContext.filename.empty())
|
if (mCell->mContextList.size() == 0)
|
||||||
return; // this is a dynamically generated cell -> skipping.
|
return; // this is a dynamically generated cell -> skipping.
|
||||||
|
|
||||||
// Reopen the ESM reader and seek to the right position.
|
// Load references from all plugins that do something with this cell.
|
||||||
mCell->restore (esm);
|
for (size_t i = 0; i < mCell->mContextList.size(); i++)
|
||||||
|
|
||||||
ESM::CellRef ref;
|
|
||||||
|
|
||||||
// Get each reference in turn
|
|
||||||
while (mCell->getNextRef (esm, ref))
|
|
||||||
{
|
{
|
||||||
std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID);
|
// Reopen the ESM reader and seek to the right position.
|
||||||
|
int index = mCell->mContextList.at(i).index;
|
||||||
|
mCell->restore (esm[index], i);
|
||||||
|
|
||||||
mIds.push_back (lowerCase);
|
ESM::CellRef ref;
|
||||||
|
|
||||||
|
// Get each reference in turn
|
||||||
|
while (mCell->getNextRef (esm[index], ref))
|
||||||
|
{
|
||||||
|
std::string lowerCase = Misc::StringUtils::lowerCase (ref.mRefID);
|
||||||
|
if (ref.mDeleted) {
|
||||||
|
// Right now, don't do anything. Where is "listRefs" actually used, anyway?
|
||||||
|
// Skipping for now...
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
mIds.push_back (lowerCase);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort (mIds.begin(), mIds.end());
|
std::sort (mIds.begin(), mIds.end());
|
||||||
}
|
}
|
||||||
|
|
||||||
void CellStore::loadRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm)
|
void CellStore::loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm)
|
||||||
{
|
{
|
||||||
assert (mCell);
|
assert (mCell);
|
||||||
|
|
||||||
if (mCell->mContext.filename.empty())
|
if (mCell->mContextList.size() == 0)
|
||||||
return; // this is a dynamically generated cell -> skipping.
|
return; // this is a dynamically generated cell -> skipping.
|
||||||
|
|
||||||
// Reopen the ESM reader and seek to the right position.
|
// Load references from all plugins that do something with this cell.
|
||||||
mCell->restore(esm);
|
for (size_t i = 0; i < mCell->mContextList.size(); i++)
|
||||||
|
|
||||||
ESM::CellRef ref;
|
|
||||||
|
|
||||||
// Get each reference in turn
|
|
||||||
while(mCell->getNextRef(esm, ref))
|
|
||||||
{
|
{
|
||||||
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
|
// Reopen the ESM reader and seek to the right position.
|
||||||
|
int index = mCell->mContextList.at(i).index;
|
||||||
|
mCell->restore (esm[index], i);
|
||||||
|
|
||||||
int rec = store.find(ref.mRefID);
|
ESM::CellRef ref;
|
||||||
|
|
||||||
ref.mRefID = lowerCase;
|
// Get each reference in turn
|
||||||
|
while(mCell->getNextRef(esm[index], ref))
|
||||||
/* We can optimize this further by storing the pointer to the
|
|
||||||
record itself in store.all, so that we don't need to look it
|
|
||||||
up again here. However, never optimize. There are infinite
|
|
||||||
opportunities to do that later.
|
|
||||||
*/
|
|
||||||
switch(rec)
|
|
||||||
{
|
{
|
||||||
|
// Don't load reference if it was moved to a different cell.
|
||||||
|
std::string lowerCase = Misc::StringUtils::lowerCase(ref.mRefID);
|
||||||
|
ESM::MovedCellRefTracker::const_iterator iter = std::find(mCell->mMovedRefs.begin(), mCell->mMovedRefs.end(), ref.mRefnum);
|
||||||
|
if (iter != mCell->mMovedRefs.end()) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
int rec = store.find(ref.mRefID);
|
||||||
|
|
||||||
|
ref.mRefID = lowerCase;
|
||||||
|
|
||||||
|
/* We can optimize this further by storing the pointer to the
|
||||||
|
record itself in store.all, so that we don't need to look it
|
||||||
|
up again here. However, never optimize. There are infinite
|
||||||
|
opportunities to do that later.
|
||||||
|
*/
|
||||||
|
switch(rec)
|
||||||
|
{
|
||||||
case ESM::REC_ACTI: mActivators.load(ref, store); break;
|
case ESM::REC_ACTI: mActivators.load(ref, store); break;
|
||||||
case ESM::REC_ALCH: mPotions.load(ref, store); break;
|
case ESM::REC_ALCH: mPotions.load(ref, store); break;
|
||||||
case ESM::REC_APPA: mAppas.load(ref, store); break;
|
case ESM::REC_APPA: mAppas.load(ref, store); break;
|
||||||
|
@ -113,10 +166,62 @@ namespace MWWorld
|
||||||
case ESM::REC_STAT: mStatics.load(ref, store); break;
|
case ESM::REC_STAT: mStatics.load(ref, store); break;
|
||||||
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
|
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
|
||||||
|
|
||||||
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
|
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
|
||||||
default:
|
default:
|
||||||
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
|
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load moved references, from separately tracked list.
|
||||||
|
for (ESM::CellRefTracker::const_iterator it = mCell->mLeasedRefs.begin(); it != mCell->mLeasedRefs.end(); it++)
|
||||||
|
{
|
||||||
|
// Doesn't seem to work in one line... huh? Too sleepy to check...
|
||||||
|
ESM::CellRef &ref = const_cast<ESM::CellRef&>(*it);
|
||||||
|
//ESM::CellRef &ref = const_cast<ESM::CellRef&>(it->second);
|
||||||
|
|
||||||
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
int rec = store.find(ref.mRefID);
|
||||||
|
|
||||||
|
ref.mRefID = lowerCase;
|
||||||
|
|
||||||
|
/* We can optimize this further by storing the pointer to the
|
||||||
|
record itself in store.all, so that we don't need to look it
|
||||||
|
up again here. However, never optimize. There are infinite
|
||||||
|
opportunities to do that later.
|
||||||
|
*/
|
||||||
|
switch(rec)
|
||||||
|
{
|
||||||
|
case ESM::REC_ACTI: mActivators.load(ref, store); break;
|
||||||
|
case ESM::REC_ALCH: mPotions.load(ref, store); break;
|
||||||
|
case ESM::REC_APPA: mAppas.load(ref, store); break;
|
||||||
|
case ESM::REC_ARMO: mArmors.load(ref, store); break;
|
||||||
|
case ESM::REC_BOOK: mBooks.load(ref, store); break;
|
||||||
|
case ESM::REC_CLOT: mClothes.load(ref, store); break;
|
||||||
|
case ESM::REC_CONT: mContainers.load(ref, store); break;
|
||||||
|
case ESM::REC_CREA: mCreatures.load(ref, store); break;
|
||||||
|
case ESM::REC_DOOR: mDoors.load(ref, store); break;
|
||||||
|
case ESM::REC_INGR: mIngreds.load(ref, store); break;
|
||||||
|
case ESM::REC_LEVC: mCreatureLists.load(ref, store); break;
|
||||||
|
case ESM::REC_LEVI: mItemLists.load(ref, store); break;
|
||||||
|
case ESM::REC_LIGH: mLights.load(ref, store); break;
|
||||||
|
case ESM::REC_LOCK: mLockpicks.load(ref, store); break;
|
||||||
|
case ESM::REC_MISC: mMiscItems.load(ref, store); break;
|
||||||
|
case ESM::REC_NPC_: mNpcs.load(ref, store); break;
|
||||||
|
case ESM::REC_PROB: mProbes.load(ref, store); break;
|
||||||
|
case ESM::REC_REPA: mRepairs.load(ref, store); break;
|
||||||
|
case ESM::REC_STAT: mStatics.load(ref, store); break;
|
||||||
|
case ESM::REC_WEAP: mWeapons.load(ref, store); break;
|
||||||
|
|
||||||
|
case 0: std::cout << "Cell reference " + ref.mRefID + " not found!\n"; break;
|
||||||
|
default:
|
||||||
|
std::cout << "WARNING: Ignoring reference '" << ref.mRefID << "' of unhandled type\n";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,17 +3,18 @@
|
||||||
|
|
||||||
#include <components/esm/records.hpp>
|
#include <components/esm/records.hpp>
|
||||||
|
|
||||||
#include <list>
|
#include <deque>
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "refdata.hpp"
|
#include "refdata.hpp"
|
||||||
#include "esmstore.hpp"
|
#include "esmstore.hpp"
|
||||||
|
|
||||||
|
struct C;
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
class Ptr;
|
class Ptr;
|
||||||
class ESMStore;
|
class ESMStore;
|
||||||
|
|
||||||
/// A reference to one object (of any type) in a cell.
|
/// A reference to one object (of any type) in a cell.
|
||||||
///
|
///
|
||||||
/// Constructing this with a CellRef instance in the constructor means that
|
/// Constructing this with a CellRef instance in the constructor means that
|
||||||
|
@ -42,6 +43,8 @@ namespace MWWorld
|
||||||
/// runtime-data
|
/// runtime-data
|
||||||
RefData mData;
|
RefData mData;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename X> bool operator==(const LiveCellRef<X>& ref, int pRefnum);
|
||||||
|
|
||||||
/// A list of cell references
|
/// A list of cell references
|
||||||
template <typename X>
|
template <typename X>
|
||||||
|
@ -51,21 +54,14 @@ namespace MWWorld
|
||||||
typedef std::list<LiveRef> List;
|
typedef std::list<LiveRef> List;
|
||||||
List mList;
|
List mList;
|
||||||
|
|
||||||
/// Searches for reference of appropriate type in given ESMStore.
|
// Search for the given reference in the given reclist from
|
||||||
/// If reference exists, loads it into container, throws an exception
|
// ESMStore. Insert the reference into the list if a match is
|
||||||
/// on miss
|
// found. If not, throw an exception.
|
||||||
void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore)
|
// Moved to cpp file, as we require a custom compare operator for it,
|
||||||
{
|
// and the build will fail with an ugly three-way cyclic header dependence
|
||||||
// for throwing exception on unhandled record type
|
// so we need to pass the instantiation of the method to the lnker, when
|
||||||
const MWWorld::Store<X> &store = esmStore.get<X>();
|
// all methods are known.
|
||||||
const X *ptr = store.find(ref.mRefID);
|
void load(ESM::CellRef &ref, const MWWorld::ESMStore &esmStore);
|
||||||
|
|
||||||
/// \note redundant because Store<X>::find() throws exception on miss
|
|
||||||
if (ptr == NULL) {
|
|
||||||
throw std::runtime_error("Error resolving cell reference " + ref.mRefID);
|
|
||||||
}
|
|
||||||
mList.push_back(LiveRef(ref, ptr));
|
|
||||||
}
|
|
||||||
|
|
||||||
LiveRef *find (const std::string& name)
|
LiveRef *find (const std::string& name)
|
||||||
{
|
{
|
||||||
|
@ -124,9 +120,9 @@ namespace MWWorld
|
||||||
CellRefList<ESM::Static> mStatics;
|
CellRefList<ESM::Static> mStatics;
|
||||||
CellRefList<ESM::Weapon> mWeapons;
|
CellRefList<ESM::Weapon> mWeapons;
|
||||||
|
|
||||||
void load (const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void load (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
|
|
||||||
void preload (const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void preload (const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
|
|
||||||
/// Call functor (ref) for each reference. functor must return a bool. Returning
|
/// Call functor (ref) for each reference. functor must return a bool. Returning
|
||||||
/// false will abort the iteration.
|
/// false will abort the iteration.
|
||||||
|
@ -185,9 +181,9 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Run through references and store IDs
|
/// Run through references and store IDs
|
||||||
void listRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void listRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
|
|
||||||
void loadRefs(const MWWorld::ESMStore &store, ESM::ESMReader &esm);
|
void loadRefs(const MWWorld::ESMStore &store, std::vector<ESM::ESMReader> &esm);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,9 +8,11 @@
|
||||||
#include <boost/algorithm/string.hpp>
|
#include <boost/algorithm/string.hpp>
|
||||||
|
|
||||||
#include <components/esm/loadcont.hpp>
|
#include <components/esm/loadcont.hpp>
|
||||||
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/world.hpp"
|
#include "../mwbase/world.hpp"
|
||||||
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
|
|
||||||
#include "manualref.hpp"
|
#include "manualref.hpp"
|
||||||
#include "refdata.hpp"
|
#include "refdata.hpp"
|
||||||
|
@ -83,9 +85,14 @@ MWWorld::ContainerStoreIterator MWWorld::ContainerStore::add (const Ptr& ptr)
|
||||||
CellStore *cell;
|
CellStore *cell;
|
||||||
|
|
||||||
Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer();
|
Ptr player = MWBase::Environment::get().getWorld ()->getPlayer().getPlayer();
|
||||||
// Items in players inventory have cell set to 0, so their scripts will never be removed
|
|
||||||
if(&(MWWorld::Class::get (player).getContainerStore (player)) == this)
|
if(&(MWWorld::Class::get (player).getContainerStore (player)) == this)
|
||||||
cell = 0;
|
{
|
||||||
|
cell = 0; // Items in player's inventory have cell set to 0, so their scripts will never be removed
|
||||||
|
|
||||||
|
// Set OnPCAdd special variable, if it is declared
|
||||||
|
item.mRefData->getLocals().setVarByInt(script, "onpcadd", 1);
|
||||||
|
}
|
||||||
else
|
else
|
||||||
cell = player.getCell();
|
cell = player.getCell();
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <iostream>
|
#include <iostream>
|
||||||
|
|
||||||
|
#include <boost/filesystem/v3/operations.hpp>
|
||||||
|
|
||||||
namespace MWWorld
|
namespace MWWorld
|
||||||
{
|
{
|
||||||
|
|
||||||
|
@ -25,6 +27,33 @@ void ESMStore::load(ESM::ESMReader &esm)
|
||||||
|
|
||||||
ESM::Dialogue *dialogue = 0;
|
ESM::Dialogue *dialogue = 0;
|
||||||
|
|
||||||
|
// Cache parent esX files by tracking their indices in the global list of
|
||||||
|
// all files/readers used by the engine. This will greaty accelerate
|
||||||
|
// refnumber mangling, as required for handling moved references.
|
||||||
|
int index = ~0;
|
||||||
|
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||||
|
std::vector<ESM::ESMReader> *allPlugins = esm.getGlobalReaderList();
|
||||||
|
for (size_t j = 0; j < masters.size(); j++) {
|
||||||
|
ESM::MasterData &mast = const_cast<ESM::MasterData&>(masters[j]);
|
||||||
|
std::string fname = mast.name;
|
||||||
|
for (int i = 0; i < esm.getIndex(); i++) {
|
||||||
|
const std::string &candidate = allPlugins->at(i).getContext().filename;
|
||||||
|
std::string fnamecandidate = boost::filesystem::path(candidate).filename().string();
|
||||||
|
if (fname == fnamecandidate) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (index == (int)~0) {
|
||||||
|
// Tried to load a parent file that has not been loaded yet. This is bad,
|
||||||
|
// the launcher should have taken care of this.
|
||||||
|
std::string fstring = "File " + fname + " asks for parent file " + masters[j].name
|
||||||
|
+ ", but it has not been loaded yet. Please check your load order.";
|
||||||
|
esm.fail(fstring);
|
||||||
|
}
|
||||||
|
mast.index = index;
|
||||||
|
}
|
||||||
|
|
||||||
// Loop through all records
|
// Loop through all records
|
||||||
while(esm.hasMoreRecs())
|
while(esm.hasMoreRecs())
|
||||||
{
|
{
|
||||||
|
@ -55,12 +84,21 @@ void ESMStore::load(ESM::ESMReader &esm)
|
||||||
} else {
|
} else {
|
||||||
// Load it
|
// Load it
|
||||||
std::string id = esm.getHNOString("NAME");
|
std::string id = esm.getHNOString("NAME");
|
||||||
|
// ... unless it got deleted! This means that the following record
|
||||||
|
// has been deleted, and trying to load it using standard assumptions
|
||||||
|
// on the structure will (probably) fail.
|
||||||
|
if (esm.isNextSub("DELE")) {
|
||||||
|
esm.skipRecord();
|
||||||
|
it->second->eraseStatic(id);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
it->second->load(esm, id);
|
it->second->load(esm, id);
|
||||||
|
|
||||||
if (n.val==ESM::REC_DIAL) {
|
if (n.val==ESM::REC_DIAL) {
|
||||||
// dirty hack, but it is better than non-const search()
|
// dirty hack, but it is better than non-const search()
|
||||||
// or friends
|
// or friends
|
||||||
dialogue = &mDialogs.mStatic.back();
|
//dialogue = &mDialogs.mStatic.back();
|
||||||
|
dialogue = const_cast<ESM::Dialogue*>(mDialogs.find(id));
|
||||||
assert (dialogue->mId == id);
|
assert (dialogue->mId == id);
|
||||||
} else {
|
} else {
|
||||||
dialogue = 0;
|
dialogue = 0;
|
||||||
|
@ -84,7 +122,6 @@ void ESMStore::load(ESM::ESMReader &esm)
|
||||||
cout << *it << " ";
|
cout << *it << " ";
|
||||||
cout << endl;
|
cout << endl;
|
||||||
*/
|
*/
|
||||||
setUp();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ESMStore::setUp()
|
void ESMStore::setUp()
|
||||||
|
@ -100,12 +137,11 @@ void ESMStore::setUp()
|
||||||
ESM::NPC item;
|
ESM::NPC item;
|
||||||
item.mId = "player";
|
item.mId = "player";
|
||||||
|
|
||||||
std::vector<ESM::NPC>::iterator pIt =
|
const ESM::NPC *pIt = mNpcs.find("player");
|
||||||
std::lower_bound(mNpcs.mStatic.begin(), mNpcs.mStatic.end(), item, RecordCmp());
|
assert(pIt != NULL);
|
||||||
assert(pIt != mNpcs.mStatic.end() && pIt->mId == "player");
|
|
||||||
|
|
||||||
mNpcs.insert(*pIt);
|
mNpcs.insert(*pIt);
|
||||||
mNpcs.mStatic.erase(pIt);
|
mNpcs.eraseStatic(pIt->mId);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // end namespace
|
} // end namespace
|
||||||
|
|
|
@ -94,6 +94,9 @@ namespace MWWorld
|
||||||
ESMStore()
|
ESMStore()
|
||||||
: mDynamicCount(0)
|
: mDynamicCount(0)
|
||||||
{
|
{
|
||||||
|
// Cell store needs access to this for tracking moved references
|
||||||
|
mCells.mEsmStore = this;
|
||||||
|
|
||||||
mStores[ESM::REC_ACTI] = &mActivators;
|
mStores[ESM::REC_ACTI] = &mActivators;
|
||||||
mStores[ESM::REC_ALCH] = &mPotions;
|
mStores[ESM::REC_ALCH] = &mPotions;
|
||||||
mStores[ESM::REC_APPA] = &mAppas;
|
mStores[ESM::REC_APPA] = &mAppas;
|
||||||
|
@ -168,7 +171,8 @@ namespace MWWorld
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
// This method must be called once, after loading all master/plugin files. This can only be done
|
||||||
|
// from the outside, so it must be public.
|
||||||
void setUp();
|
void setUp();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
65
apps/openmw/mwworld/store.cpp
Normal file
65
apps/openmw/mwworld/store.cpp
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
#include "store.hpp"
|
||||||
|
|
||||||
|
namespace MWWorld {
|
||||||
|
|
||||||
|
|
||||||
|
void Store<ESM::Cell>::load(ESM::ESMReader &esm, const std::string &id)
|
||||||
|
{
|
||||||
|
// Don't automatically assume that a new cell must be spawned. Multiple plugins write to the same cell,
|
||||||
|
// and we merge all this data into one Cell object. However, we can't simply search for the cell id,
|
||||||
|
// as many exterior cells do not have a name. Instead, we need to search by (x,y) coordinates - and they
|
||||||
|
// are not available until both cells have been loaded! So first, proceed as usual.
|
||||||
|
|
||||||
|
// All cells have a name record, even nameless exterior cells.
|
||||||
|
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||||
|
ESM::Cell *cell = new ESM::Cell;
|
||||||
|
cell->mName = id;
|
||||||
|
|
||||||
|
// The cell itself takes care of some of the hairy details
|
||||||
|
cell->load(esm, *mEsmStore);
|
||||||
|
|
||||||
|
if(cell->mData.mFlags & ESM::Cell::Interior)
|
||||||
|
{
|
||||||
|
// Store interior cell by name, try to merge with existing parent data.
|
||||||
|
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(idLower));
|
||||||
|
if (oldcell) {
|
||||||
|
// push the new references on the list of references to manage
|
||||||
|
oldcell->mContextList.push_back(cell->mContextList.at(0));
|
||||||
|
// copy list into new cell
|
||||||
|
cell->mContextList = oldcell->mContextList;
|
||||||
|
// have new cell replace old cell
|
||||||
|
*oldcell = *cell;
|
||||||
|
} else
|
||||||
|
mInt[idLower] = *cell;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Store exterior cells by grid position, try to merge with existing parent data.
|
||||||
|
ESM::Cell *oldcell = const_cast<ESM::Cell*>(search(cell->getGridX(), cell->getGridY()));
|
||||||
|
if (oldcell) {
|
||||||
|
// push the new references on the list of references to manage
|
||||||
|
oldcell->mContextList.push_back(cell->mContextList.at(0));
|
||||||
|
// copy list into new cell
|
||||||
|
cell->mContextList = oldcell->mContextList;
|
||||||
|
// merge lists of leased references, use newer data in case of conflict
|
||||||
|
for (ESM::MovedCellRefTracker::const_iterator it = cell->mMovedRefs.begin(); it != cell->mMovedRefs.end(); it++) {
|
||||||
|
// remove reference from current leased ref tracker and add it to new cell
|
||||||
|
ESM::MovedCellRefTracker::iterator itold = std::find(oldcell->mMovedRefs.begin(), oldcell->mMovedRefs.end(), it->mRefnum);
|
||||||
|
if (itold != oldcell->mMovedRefs.end()) {
|
||||||
|
ESM::MovedCellRef target0 = *itold;
|
||||||
|
ESM::Cell *wipecell = const_cast<ESM::Cell*>(search(target0.mTarget[0], target0.mTarget[1]));
|
||||||
|
ESM::CellRefTracker::iterator it_lease = std::find(wipecell->mLeasedRefs.begin(), wipecell->mLeasedRefs.end(), it->mRefnum);
|
||||||
|
wipecell->mLeasedRefs.erase(it_lease);
|
||||||
|
*itold = *it;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cell->mMovedRefs = oldcell->mMovedRefs;
|
||||||
|
// have new cell replace old cell
|
||||||
|
*oldcell = *cell;
|
||||||
|
} else
|
||||||
|
mExt[std::make_pair(cell->mData.mX, cell->mData.mY)] = *cell;
|
||||||
|
}
|
||||||
|
delete cell;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -19,6 +19,8 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual int getSize() const = 0;
|
virtual int getSize() const = 0;
|
||||||
virtual void load(ESM::ESMReader &esm, const std::string &id) = 0;
|
virtual void load(ESM::ESMReader &esm, const std::string &id) = 0;
|
||||||
|
|
||||||
|
virtual bool eraseStatic(const std::string &id) {return false;}
|
||||||
};
|
};
|
||||||
|
|
||||||
template <class T>
|
template <class T>
|
||||||
|
@ -85,7 +87,7 @@ namespace MWWorld
|
||||||
template <class T>
|
template <class T>
|
||||||
class Store : public StoreBase
|
class Store : public StoreBase
|
||||||
{
|
{
|
||||||
std::vector<T> mStatic;
|
std::map<std::string, T> mStatic;
|
||||||
std::vector<T *> mShared;
|
std::vector<T *> mShared;
|
||||||
std::map<std::string, T> mDynamic;
|
std::map<std::string, T> mDynamic;
|
||||||
|
|
||||||
|
@ -107,11 +109,10 @@ namespace MWWorld
|
||||||
T item;
|
T item;
|
||||||
item.mId = Misc::StringUtils::lowerCase(id);
|
item.mId = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
typename std::vector<T>::const_iterator it =
|
typename std::map<std::string, T>::const_iterator it = mStatic.find(item.mId);
|
||||||
std::lower_bound(mStatic.begin(), mStatic.end(), item, RecordCmp());
|
|
||||||
|
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
|
||||||
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->mId, id)) {
|
return &(it->second);
|
||||||
return &(*it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
typename Dynamic::const_iterator dit = mDynamic.find(item.mId);
|
typename Dynamic::const_iterator dit = mDynamic.find(item.mId);
|
||||||
|
@ -133,18 +134,19 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
void load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(T());
|
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||||
mStatic.back().mId = Misc::StringUtils::lowerCase(id);
|
mStatic[idLower] = T();
|
||||||
mStatic.back().load(esm);
|
mStatic[idLower].mId = idLower;
|
||||||
|
mStatic[idLower].load(esm);
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUp() {
|
void setUp() {
|
||||||
std::sort(mStatic.begin(), mStatic.end(), RecordCmp());
|
//std::sort(mStatic.begin(), mStatic.end(), RecordCmp());
|
||||||
|
|
||||||
mShared.reserve(mStatic.size());
|
mShared.reserve(mStatic.size());
|
||||||
typename std::vector<T>::iterator it = mStatic.begin();
|
typename std::map<std::string, T>::iterator it = mStatic.begin();
|
||||||
for (; it != mStatic.end(); ++it) {
|
for (; it != mStatic.end(); ++it) {
|
||||||
mShared.push_back(&(*it));
|
mShared.push_back(&(it->second));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -181,6 +183,19 @@ namespace MWWorld
|
||||||
return ptr;
|
return ptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool eraseStatic(const std::string &id) {
|
||||||
|
T item;
|
||||||
|
item.mId = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
|
typename std::map<std::string, T>::iterator it = mStatic.find(item.mId);
|
||||||
|
|
||||||
|
if (it != mStatic.end() && Misc::StringUtils::ciEqual(it->second.mId, id)) {
|
||||||
|
mStatic.erase(it);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
bool erase(const std::string &id) {
|
bool erase(const std::string &id) {
|
||||||
std::string key = Misc::StringUtils::lowerCase(id);
|
std::string key = Misc::StringUtils::lowerCase(id);
|
||||||
typename Dynamic::iterator it = mDynamic.find(key);
|
typename Dynamic::iterator it = mDynamic.find(key);
|
||||||
|
@ -204,39 +219,48 @@ namespace MWWorld
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void Store<ESM::Dialogue>::load(ESM::ESMReader &esm, const std::string &id) {
|
inline void Store<ESM::Dialogue>::load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(ESM::Dialogue());
|
std::string idLower = Misc::StringUtils::lowerCase(id);
|
||||||
mStatic.back().mId = id;
|
mStatic[idLower] = ESM::Dialogue();
|
||||||
mStatic.back().load(esm);
|
mStatic[idLower].mId = id; // don't smash case here, as this line is printed... I think
|
||||||
|
mStatic[idLower].load(esm);
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
inline void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
|
inline void Store<ESM::Script>::load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
mStatic.push_back(ESM::Script());
|
ESM::Script scpt;
|
||||||
mStatic.back().load(esm);
|
scpt.load(esm);
|
||||||
Misc::StringUtils::toLower(mStatic.back().mId);
|
Misc::StringUtils::toLower(scpt.mId);
|
||||||
|
mStatic[scpt.mId] = scpt;
|
||||||
}
|
}
|
||||||
|
|
||||||
template <>
|
template <>
|
||||||
class Store<ESM::LandTexture> : public StoreBase
|
class Store<ESM::LandTexture> : public StoreBase
|
||||||
{
|
{
|
||||||
std::vector<ESM::LandTexture> mStatic;
|
// For multiple ESM/ESP files we need one list per file.
|
||||||
|
typedef std::vector<ESM::LandTexture> LandTextureList;
|
||||||
|
std::vector<LandTextureList> mStatic;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Store<ESM::LandTexture>() {
|
Store<ESM::LandTexture>() {
|
||||||
mStatic.reserve(128);
|
mStatic.push_back(LandTextureList());
|
||||||
|
LandTextureList <exl = mStatic[0];
|
||||||
|
// More than enough to hold Morrowind.esm. Extra lists for plugins will we
|
||||||
|
// added on-the-fly in a different method.
|
||||||
|
ltexl.reserve(128);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef std::vector<ESM::LandTexture>::const_iterator iterator;
|
typedef std::vector<ESM::LandTexture>::const_iterator iterator;
|
||||||
|
|
||||||
const ESM::LandTexture *search(size_t index) const {
|
const ESM::LandTexture *search(size_t index, size_t plugin) const {
|
||||||
if (index < mStatic.size()) {
|
assert(plugin < mStatic.size());
|
||||||
return &mStatic.at(index);
|
const LandTextureList <exl = mStatic[plugin];
|
||||||
}
|
|
||||||
return 0;
|
assert(index < ltexl.size());
|
||||||
|
return <exl.at(index);
|
||||||
}
|
}
|
||||||
|
|
||||||
const ESM::LandTexture *find(size_t index) const {
|
const ESM::LandTexture *find(size_t index, size_t plugin) const {
|
||||||
const ESM::LandTexture *ptr = search(index);
|
const ESM::LandTexture *ptr = search(index, plugin);
|
||||||
if (ptr == 0) {
|
if (ptr == 0) {
|
||||||
std::ostringstream msg;
|
std::ostringstream msg;
|
||||||
msg << "Land texture with index " << index << " not found";
|
msg << "Land texture with index " << index << " not found";
|
||||||
|
@ -249,23 +273,40 @@ namespace MWWorld
|
||||||
return mStatic.size();
|
return mStatic.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
int getSize(size_t plugin) const {
|
||||||
ESM::LandTexture ltex;
|
assert(plugin < mStatic.size());
|
||||||
ltex.load(esm);
|
return mStatic[plugin].size();
|
||||||
|
}
|
||||||
|
|
||||||
if (ltex.mIndex >= (int) mStatic.size()) {
|
void load(ESM::ESMReader &esm, const std::string &id, size_t plugin) {
|
||||||
mStatic.resize(ltex.mIndex + 1);
|
ESM::LandTexture lt;
|
||||||
|
lt.load(esm);
|
||||||
|
lt.mId = id;
|
||||||
|
|
||||||
|
// Make sure we have room for the structure
|
||||||
|
if (plugin >= mStatic.size()) {
|
||||||
|
mStatic.resize(plugin+1);
|
||||||
}
|
}
|
||||||
mStatic[ltex.mIndex] = ltex;
|
LandTextureList <exl = mStatic[plugin];
|
||||||
mStatic[ltex.mIndex].mId = id;
|
if(lt.mIndex + 1 > (int)ltexl.size())
|
||||||
|
ltexl.resize(lt.mIndex+1);
|
||||||
|
|
||||||
|
// Store it
|
||||||
|
ltexl[lt.mIndex] = lt;
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator begin() const {
|
void load(ESM::ESMReader &esm, const std::string &id) {
|
||||||
return mStatic.begin();
|
load(esm, id, esm.getIndex());
|
||||||
}
|
}
|
||||||
|
|
||||||
iterator end() const {
|
iterator begin(size_t plugin) const {
|
||||||
return mStatic.end();
|
assert(plugin < mStatic.size());
|
||||||
|
return mStatic[plugin].begin();
|
||||||
|
}
|
||||||
|
|
||||||
|
iterator end(size_t plugin) const {
|
||||||
|
assert(plugin < mStatic.size());
|
||||||
|
return mStatic[plugin].end();
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -357,19 +398,18 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
std::vector<ESM::Cell> mInt;
|
typedef std::map<std::string, ESM::Cell> DynamicInt;
|
||||||
std::vector<ESM::Cell> mExt;
|
typedef std::map<std::pair<int, int>, ESM::Cell> DynamicExt;
|
||||||
|
|
||||||
|
DynamicInt mInt;
|
||||||
|
DynamicExt mExt;
|
||||||
|
|
||||||
std::vector<ESM::Cell *> mSharedInt;
|
std::vector<ESM::Cell *> mSharedInt;
|
||||||
std::vector<ESM::Cell *> mSharedExt;
|
std::vector<ESM::Cell *> mSharedExt;
|
||||||
|
|
||||||
typedef std::map<std::string, ESM::Cell> DynamicInt;
|
|
||||||
typedef std::map<std::pair<int, int>, ESM::Cell> DynamicExt;
|
|
||||||
|
|
||||||
DynamicInt mDynamicInt;
|
DynamicInt mDynamicInt;
|
||||||
DynamicExt mDynamicExt;
|
DynamicExt mDynamicExt;
|
||||||
|
|
||||||
|
|
||||||
const ESM::Cell *search(const ESM::Cell &cell) const {
|
const ESM::Cell *search(const ESM::Cell &cell) const {
|
||||||
if (cell.isExterior()) {
|
if (cell.isExterior()) {
|
||||||
return search(cell.getGridX(), cell.getGridY());
|
return search(cell.getGridX(), cell.getGridY());
|
||||||
|
@ -378,6 +418,8 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
ESMStore *mEsmStore;
|
||||||
|
|
||||||
typedef SharedIterator<ESM::Cell> iterator;
|
typedef SharedIterator<ESM::Cell> iterator;
|
||||||
|
|
||||||
Store<ESM::Cell>()
|
Store<ESM::Cell>()
|
||||||
|
@ -387,11 +429,10 @@ namespace MWWorld
|
||||||
ESM::Cell cell;
|
ESM::Cell cell;
|
||||||
cell.mName = Misc::StringUtils::lowerCase(id);
|
cell.mName = Misc::StringUtils::lowerCase(id);
|
||||||
|
|
||||||
std::vector<ESM::Cell>::const_iterator it =
|
std::map<std::string, ESM::Cell>::const_iterator it = mInt.find(cell.mName);
|
||||||
std::lower_bound(mInt.begin(), mInt.end(), cell, RecordCmp());
|
|
||||||
|
|
||||||
if (it != mInt.end() && Misc::StringUtils::ciEqual(it->mName, id)) {
|
if (it != mInt.end() && Misc::StringUtils::ciEqual(it->second.mName, id)) {
|
||||||
return &(*it);
|
return &(it->second);
|
||||||
}
|
}
|
||||||
|
|
||||||
DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName);
|
DynamicInt::const_iterator dit = mDynamicInt.find(cell.mName);
|
||||||
|
@ -406,14 +447,12 @@ namespace MWWorld
|
||||||
ESM::Cell cell;
|
ESM::Cell cell;
|
||||||
cell.mData.mX = x, cell.mData.mY = y;
|
cell.mData.mX = x, cell.mData.mY = y;
|
||||||
|
|
||||||
std::vector<ESM::Cell>::const_iterator it =
|
std::pair<int, int> key(x, y);
|
||||||
std::lower_bound(mExt.begin(), mExt.end(), cell, ExtCmp());
|
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||||
|
if (it != mExt.end()) {
|
||||||
if (it != mExt.end() && it->mData.mX == x && it->mData.mY == y) {
|
return &(it->second);
|
||||||
return &(*it);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
std::pair<int, int> key(x, y);
|
|
||||||
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
||||||
if (dit != mDynamicExt.end()) {
|
if (dit != mDynamicExt.end()) {
|
||||||
return &dit->second;
|
return &dit->second;
|
||||||
|
@ -422,6 +461,30 @@ namespace MWWorld
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const ESM::Cell *searchOrCreate(int x, int y) {
|
||||||
|
ESM::Cell cell;
|
||||||
|
cell.mData.mX = x, cell.mData.mY = y;
|
||||||
|
|
||||||
|
std::pair<int, int> key(x, y);
|
||||||
|
std::map<std::pair<int, int>, ESM::Cell>::const_iterator it = mExt.find(key);
|
||||||
|
if (it != mExt.end()) {
|
||||||
|
return &(it->second);
|
||||||
|
}
|
||||||
|
|
||||||
|
DynamicExt::const_iterator dit = mDynamicExt.find(key);
|
||||||
|
if (dit != mDynamicExt.end()) {
|
||||||
|
return &dit->second;
|
||||||
|
}
|
||||||
|
|
||||||
|
ESM::Cell *newCell = new ESM::Cell;
|
||||||
|
newCell->mData.mX = x;
|
||||||
|
newCell->mData.mY = y;
|
||||||
|
mExt[std::make_pair(x, y)] = *newCell;
|
||||||
|
delete newCell;
|
||||||
|
|
||||||
|
return &mExt[std::make_pair(x, y)];
|
||||||
|
}
|
||||||
|
|
||||||
const ESM::Cell *find(const std::string &id) const {
|
const ESM::Cell *find(const std::string &id) const {
|
||||||
const ESM::Cell *ptr = search(id);
|
const ESM::Cell *ptr = search(id);
|
||||||
if (ptr == 0) {
|
if (ptr == 0) {
|
||||||
|
@ -443,33 +506,29 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
|
|
||||||
void setUp() {
|
void setUp() {
|
||||||
typedef std::vector<ESM::Cell>::iterator Iterator;
|
//typedef std::vector<ESM::Cell>::iterator Iterator;
|
||||||
|
typedef std::map<std::pair<int, int>, ESM::Cell>::iterator ExtIterator;
|
||||||
|
typedef std::map<std::string, ESM::Cell>::iterator IntIterator;
|
||||||
|
|
||||||
std::sort(mInt.begin(), mInt.end(), RecordCmp());
|
//std::sort(mInt.begin(), mInt.end(), RecordCmp());
|
||||||
mSharedInt.reserve(mInt.size());
|
mSharedInt.reserve(mInt.size());
|
||||||
for (Iterator it = mInt.begin(); it != mInt.end(); ++it) {
|
for (IntIterator it = mInt.begin(); it != mInt.end(); ++it) {
|
||||||
mSharedInt.push_back(&(*it));
|
mSharedInt.push_back(&(it->second));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::sort(mExt.begin(), mExt.end(), ExtCmp());
|
//std::sort(mExt.begin(), mExt.end(), ExtCmp());
|
||||||
mSharedExt.reserve(mExt.size());
|
mSharedExt.reserve(mExt.size());
|
||||||
for (Iterator it = mExt.begin(); it != mExt.end(); ++it) {
|
for (ExtIterator it = mExt.begin(); it != mExt.end(); ++it) {
|
||||||
mSharedExt.push_back(&(*it));
|
mSharedExt.push_back(&(it->second));
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void load(ESM::ESMReader &esm, const std::string &id) {
|
|
||||||
ESM::Cell cell;
|
|
||||||
cell.mName = id;
|
|
||||||
cell.load(esm);
|
|
||||||
|
|
||||||
if (cell.isExterior()) {
|
|
||||||
mExt.push_back(cell);
|
|
||||||
} else {
|
|
||||||
mInt.push_back(cell);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// HACK: Method implementation had to be moved to a separate cpp file, as we would otherwise get
|
||||||
|
// errors related to the compare operator used in std::find for ESM::MovedCellRefTracker::find.
|
||||||
|
// There some nasty three-way cyclic header dependency involved, which I could only fix by moving
|
||||||
|
// this method.
|
||||||
|
void load(ESM::ESMReader &esm, const std::string &id);
|
||||||
|
|
||||||
iterator intBegin() const {
|
iterator intBegin() const {
|
||||||
return iterator(mSharedInt.begin());
|
return iterator(mSharedInt.begin());
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
#include <components/bsa/bsa_archive.hpp>
|
#include <components/bsa/bsa_archive.hpp>
|
||||||
#include <components/files/collections.hpp>
|
#include <components/files/collections.hpp>
|
||||||
|
#include <components/compiler/locals.hpp>
|
||||||
|
|
||||||
#include "../mwbase/environment.hpp"
|
#include "../mwbase/environment.hpp"
|
||||||
#include "../mwbase/soundmanager.hpp"
|
#include "../mwbase/soundmanager.hpp"
|
||||||
#include "../mwbase/mechanicsmanager.hpp"
|
#include "../mwbase/mechanicsmanager.hpp"
|
||||||
#include "../mwbase/windowmanager.hpp"
|
#include "../mwbase/windowmanager.hpp"
|
||||||
|
#include "../mwbase/scriptmanager.hpp"
|
||||||
|
|
||||||
#include "../mwrender/sky.hpp"
|
#include "../mwrender/sky.hpp"
|
||||||
#include "../mwrender/player.hpp"
|
#include "../mwrender/player.hpp"
|
||||||
|
@ -53,7 +55,7 @@ namespace
|
||||||
|
|
||||||
for (iterator iter (refList.mList.begin()); iter!=refList.mList.end(); ++iter)
|
for (iterator iter (refList.mList.begin()); iter!=refList.mList.end(); ++iter)
|
||||||
{
|
{
|
||||||
if(iter->mData.getCount() > 0 && iter->mData.getBaseNode()){
|
if (iter->mData.getCount() > 0 && iter->mData.getBaseNode()){
|
||||||
if (iter->mData.getHandle()==handle)
|
if (iter->mData.getHandle()==handle)
|
||||||
{
|
{
|
||||||
return &*iter;
|
return &*iter;
|
||||||
|
@ -169,7 +171,8 @@ namespace MWWorld
|
||||||
|
|
||||||
World::World (OEngine::Render::OgreRenderer& renderer,
|
World::World (OEngine::Render::OgreRenderer& renderer,
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
|
||||||
|
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
||||||
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride)
|
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride)
|
||||||
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
: mPlayer (0), mLocalScripts (mStore), mGlobalVariables (0),
|
||||||
mSky (true), mCells (mStore, mEsm),
|
mSky (true), mCells (mStore, mEsm),
|
||||||
|
@ -182,14 +185,42 @@ namespace MWWorld
|
||||||
|
|
||||||
mWeatherManager = new MWWorld::WeatherManager(mRendering);
|
mWeatherManager = new MWWorld::WeatherManager(mRendering);
|
||||||
|
|
||||||
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master));
|
int idx = 0;
|
||||||
|
// NOTE: We might need to reserve one more for the running game / save.
|
||||||
|
mEsm.resize(master.size() + plugins.size());
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < master.size(); i++, idx++)
|
||||||
|
{
|
||||||
|
boost::filesystem::path masterPath (fileCollections.getCollection (".esm").getPath (master[i]));
|
||||||
|
|
||||||
|
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
||||||
|
|
||||||
std::cout << "Loading ESM " << masterPath.string() << "\n";
|
// This parses the ESM file
|
||||||
|
ESM::ESMReader lEsm;
|
||||||
|
lEsm.setEncoder(encoder);
|
||||||
|
lEsm.setIndex(idx);
|
||||||
|
lEsm.setGlobalReaderList(&mEsm);
|
||||||
|
lEsm.open (masterPath.string());
|
||||||
|
mEsm[idx] = lEsm;
|
||||||
|
mStore.load (mEsm[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (std::vector<std::string>::size_type i = 0; i < plugins.size(); i++, idx++)
|
||||||
|
{
|
||||||
|
boost::filesystem::path pluginPath (fileCollections.getCollection (".esp").getPath (plugins[i]));
|
||||||
|
|
||||||
|
std::cout << "Loading ESP " << pluginPath.string() << "\n";
|
||||||
|
|
||||||
// This parses the ESM file and loads a sample cell
|
// This parses the ESP file
|
||||||
mEsm.setEncoder(encoder);
|
ESM::ESMReader lEsm;
|
||||||
mEsm.open (masterPath.string());
|
lEsm.setEncoder(encoder);
|
||||||
mStore.load (mEsm);
|
lEsm.setIndex(idx);
|
||||||
|
lEsm.setGlobalReaderList(&mEsm);
|
||||||
|
lEsm.open (pluginPath.string());
|
||||||
|
mEsm[idx] = lEsm;
|
||||||
|
mStore.load (mEsm[idx]);
|
||||||
|
}
|
||||||
|
|
||||||
|
mStore.setUp();
|
||||||
|
|
||||||
mPlayer = new MWWorld::Player (mStore.get<ESM::NPC>().find ("player"), *this);
|
mPlayer = new MWWorld::Player (mStore.get<ESM::NPC>().find ("player"), *this);
|
||||||
mRendering->attachCameraTo(mPlayer->getPlayer());
|
mRendering->attachCameraTo(mPlayer->getPlayer());
|
||||||
|
@ -268,7 +299,7 @@ namespace MWWorld
|
||||||
return mStore;
|
return mStore;
|
||||||
}
|
}
|
||||||
|
|
||||||
ESM::ESMReader& World::getEsmReader()
|
std::vector<ESM::ESMReader>& World::getEsmReader()
|
||||||
{
|
{
|
||||||
return mEsm;
|
return mEsm;
|
||||||
}
|
}
|
||||||
|
@ -698,6 +729,8 @@ namespace MWWorld
|
||||||
|
|
||||||
if (*currCell != newCell)
|
if (*currCell != newCell)
|
||||||
{
|
{
|
||||||
|
removeContainerScripts(ptr);
|
||||||
|
|
||||||
if (isPlayer)
|
if (isPlayer)
|
||||||
if (!newCell.isExterior())
|
if (!newCell.isExterior())
|
||||||
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos);
|
changeToInteriorCell(Misc::StringUtils::lowerCase(newCell.mCell->mName), pos);
|
||||||
|
@ -1229,8 +1262,8 @@ namespace MWWorld
|
||||||
std::vector<World::DoorMarker> result;
|
std::vector<World::DoorMarker> result;
|
||||||
|
|
||||||
MWWorld::CellRefList<ESM::Door>& doors = cell->mDoors;
|
MWWorld::CellRefList<ESM::Door>& doors = cell->mDoors;
|
||||||
std::list< MWWorld::LiveCellRef<ESM::Door> >& refList = doors.mList;
|
CellRefList<ESM::Door>::List& refList = doors.mList;
|
||||||
for (std::list< MWWorld::LiveCellRef<ESM::Door> >::iterator it = refList.begin(); it != refList.end(); ++it)
|
for (CellRefList<ESM::Door>::List::iterator it = refList.begin(); it != refList.end(); ++it)
|
||||||
{
|
{
|
||||||
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
MWWorld::LiveCellRef<ESM::Door>& ref = *it;
|
||||||
|
|
||||||
|
@ -1270,6 +1303,15 @@ namespace MWWorld
|
||||||
mRendering->toggleWater();
|
mRendering->toggleWater();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void World::PCDropped (const Ptr& item)
|
||||||
|
{
|
||||||
|
std::string script = MWWorld::Class::get(item).getScript(item);
|
||||||
|
|
||||||
|
// Set OnPCDrop Variable on item's script, if it has a script with that variable declared
|
||||||
|
if(script != "")
|
||||||
|
item.mRefData->getLocals().setVarByInt(script, "onpcdrop", 1);
|
||||||
|
}
|
||||||
|
|
||||||
bool World::placeObject (const Ptr& object, float cursorX, float cursorY)
|
bool World::placeObject (const Ptr& object, float cursorX, float cursorY)
|
||||||
{
|
{
|
||||||
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY);
|
std::pair<bool, Ogre::Vector3> result = mPhysics->castRay(cursorX, cursorY);
|
||||||
|
@ -1292,7 +1334,8 @@ namespace MWWorld
|
||||||
pos.pos[1] = -result.second[2];
|
pos.pos[1] = -result.second[2];
|
||||||
pos.pos[2] = result.second[1];
|
pos.pos[2] = result.second[1];
|
||||||
|
|
||||||
copyObjectToCell(object, *cell, pos);
|
Ptr dropped = copyObjectToCell(object, *cell, pos);
|
||||||
|
PCDropped(dropped);
|
||||||
object.getRefData().setCount(0);
|
object.getRefData().setCount(0);
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -1309,8 +1352,8 @@ namespace MWWorld
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos)
|
Ptr World::copyObjectToCell(const Ptr &object, CellStore &cell, const ESM::Position &pos)
|
||||||
{
|
{
|
||||||
/// \todo add searching correct cell for position specified
|
/// \todo add searching correct cell for position specified
|
||||||
MWWorld::Ptr dropped =
|
MWWorld::Ptr dropped =
|
||||||
|
@ -1334,6 +1377,8 @@ namespace MWWorld
|
||||||
}
|
}
|
||||||
addContainerScripts(dropped, &cell);
|
addContainerScripts(dropped, &cell);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return dropped;
|
||||||
}
|
}
|
||||||
|
|
||||||
void World::dropObjectOnGround (const Ptr& actor, const Ptr& object)
|
void World::dropObjectOnGround (const Ptr& actor, const Ptr& object)
|
||||||
|
@ -1354,7 +1399,9 @@ namespace MWWorld
|
||||||
mPhysics->castRay(orig, dir, len);
|
mPhysics->castRay(orig, dir, len);
|
||||||
pos.pos[2] = hit.second.z;
|
pos.pos[2] = hit.second.z;
|
||||||
|
|
||||||
copyObjectToCell(object, *cell, pos);
|
Ptr dropped = copyObjectToCell(object, *cell, pos);
|
||||||
|
if(actor == mPlayer->getPlayer()) // Only call if dropped by player
|
||||||
|
PCDropped(dropped);
|
||||||
object.getRefData().setCount(0);
|
object.getRefData().setCount(0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -54,7 +54,7 @@ namespace MWWorld
|
||||||
|
|
||||||
MWWorld::Scene *mWorldScene;
|
MWWorld::Scene *mWorldScene;
|
||||||
MWWorld::Player *mPlayer;
|
MWWorld::Player *mPlayer;
|
||||||
ESM::ESMReader mEsm;
|
std::vector<ESM::ESMReader> mEsm;
|
||||||
MWWorld::ESMStore mStore;
|
MWWorld::ESMStore mStore;
|
||||||
LocalScripts mLocalScripts;
|
LocalScripts mLocalScripts;
|
||||||
MWWorld::Globals *mGlobalVariables;
|
MWWorld::Globals *mGlobalVariables;
|
||||||
|
@ -91,8 +91,8 @@ namespace MWWorld
|
||||||
bool moveObjectImp (const Ptr& ptr, float x, float y, float z);
|
bool moveObjectImp (const Ptr& ptr, float x, float y, float z);
|
||||||
///< @return true if the active cell (cell player is in) changed
|
///< @return true if the active cell (cell player is in) changed
|
||||||
|
|
||||||
virtual void
|
|
||||||
copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos);
|
Ptr copyObjectToCell(const Ptr &ptr, CellStore &cell, const ESM::Position &pos);
|
||||||
|
|
||||||
void updateWindowManager ();
|
void updateWindowManager ();
|
||||||
void performUpdateSceneQueries ();
|
void performUpdateSceneQueries ();
|
||||||
|
@ -107,12 +107,14 @@ namespace MWWorld
|
||||||
|
|
||||||
void removeContainerScripts(const Ptr& reference);
|
void removeContainerScripts(const Ptr& reference);
|
||||||
void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell);
|
void addContainerScripts(const Ptr& reference, Ptr::CellStore* cell);
|
||||||
|
void PCDropped (const Ptr& item);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
|
|
||||||
World (OEngine::Render::OgreRenderer& renderer,
|
World (OEngine::Render::OgreRenderer& renderer,
|
||||||
const Files::Collections& fileCollections,
|
const Files::Collections& fileCollections,
|
||||||
const std::string& master, const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
const std::vector<std::string>& master, const std::vector<std::string>& plugins,
|
||||||
|
const boost::filesystem::path& resDir, const boost::filesystem::path& cacheDir, bool newGame,
|
||||||
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride);
|
ToUTF8::Utf8Encoder* encoder, std::map<std::string,std::string> fallbackMap, int mActivationDistanceOverride);
|
||||||
|
|
||||||
virtual ~World();
|
virtual ~World();
|
||||||
|
@ -142,7 +144,7 @@ namespace MWWorld
|
||||||
|
|
||||||
virtual const MWWorld::ESMStore& getStore() const;
|
virtual const MWWorld::ESMStore& getStore() const;
|
||||||
|
|
||||||
virtual ESM::ESMReader& getEsmReader();
|
virtual std::vector<ESM::ESMReader>& getEsmReader();
|
||||||
|
|
||||||
virtual LocalScripts& getLocalScripts();
|
virtual LocalScripts& getLocalScripts();
|
||||||
|
|
||||||
|
|
|
@ -23,6 +23,22 @@ endforeach (u)
|
||||||
source_group ("components\\${dir}" FILES ${files})
|
source_group ("components\\${dir}" FILES ${files})
|
||||||
endmacro (add_component_dir)
|
endmacro (add_component_dir)
|
||||||
|
|
||||||
|
macro (add_component_qt_dir dir)
|
||||||
|
set (files)
|
||||||
|
foreach (u ${ARGN})
|
||||||
|
file (GLOB ALL ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.[ch]pp")
|
||||||
|
foreach (f ${ALL})
|
||||||
|
list (APPEND files "${f}")
|
||||||
|
list (APPEND COMPONENT_FILES "${f}")
|
||||||
|
endforeach (f)
|
||||||
|
file (GLOB MOC_H ${CMAKE_CURRENT_SOURCE_DIR} "${dir}/${u}.hpp")
|
||||||
|
foreach (fi ${MOC_H})
|
||||||
|
list (APPEND COMPONENT_MOC_FILES "${fi}")
|
||||||
|
endforeach (fi)
|
||||||
|
endforeach (u)
|
||||||
|
source_group ("components\\${dir}" FILES ${files})
|
||||||
|
endmacro (add_component_qt_dir)
|
||||||
|
|
||||||
macro (copy_all_files source_dir destination_dir files)
|
macro (copy_all_files source_dir destination_dir files)
|
||||||
foreach (f ${files})
|
foreach (f ${files})
|
||||||
get_filename_component(filename ${f} NAME)
|
get_filename_component(filename ${f} NAME)
|
||||||
|
|
|
@ -66,9 +66,21 @@ add_component_dir (translation
|
||||||
translation
|
translation
|
||||||
)
|
)
|
||||||
|
|
||||||
|
find_package(Qt4 COMPONENTS QtCore QtGui)
|
||||||
|
|
||||||
|
if(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
|
||||||
|
add_component_qt_dir (fileorderlist
|
||||||
|
datafileslist model/modelitem model/datafilesmodel model/esm/esmfile
|
||||||
|
utils/filedialog utils/lineedit utils/naturalsort
|
||||||
|
)
|
||||||
|
|
||||||
|
include(${QT_USE_FILE})
|
||||||
|
QT4_WRAP_CPP(MOC_SRCS ${COMPONENT_MOC_FILES})
|
||||||
|
endif(QT_QTGUI_LIBRARY AND QT_QTCORE_LIBRARY)
|
||||||
|
|
||||||
include_directories(${BULLET_INCLUDE_DIRS})
|
include_directories(${BULLET_INCLUDE_DIRS})
|
||||||
|
|
||||||
add_library(components STATIC ${COMPONENT_FILES})
|
add_library(components STATIC ${COMPONENT_FILES} ${MOC_SRCS})
|
||||||
|
|
||||||
target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES})
|
target_link_libraries(components ${Boost_LIBRARIES} ${OGRE_LIBRARIES})
|
||||||
|
|
||||||
|
|
|
@ -89,6 +89,7 @@ struct MasterData
|
||||||
{
|
{
|
||||||
std::string name;
|
std::string name;
|
||||||
uint64_t size;
|
uint64_t size;
|
||||||
|
int index; // Position of the parent file in the global list of loaded files
|
||||||
};
|
};
|
||||||
|
|
||||||
// Data that is only present in save game files
|
// Data that is only present in save game files
|
||||||
|
@ -113,6 +114,10 @@ struct ESM_Context
|
||||||
size_t leftFile;
|
size_t leftFile;
|
||||||
NAME recName, subName;
|
NAME recName, subName;
|
||||||
HEDRstruct header;
|
HEDRstruct header;
|
||||||
|
// When working with multiple esX files, we will generate lists of all files that
|
||||||
|
// actually contribute to a specific cell. Therefore, we need to store the index
|
||||||
|
// of the file belonging to this contest. See CellStore::(list/load)refs for details.
|
||||||
|
int index;
|
||||||
|
|
||||||
// True if subName has been read but not used.
|
// True if subName has been read but not used.
|
||||||
bool subCached;
|
bool subCached;
|
||||||
|
|
|
@ -78,6 +78,17 @@ public:
|
||||||
|
|
||||||
void openRaw(const std::string &file);
|
void openRaw(const std::string &file);
|
||||||
|
|
||||||
|
// This is a quick hack for multiple esm/esp files. Each plugin introduces its own
|
||||||
|
// terrain palette, but ESMReader does not pass a reference to the correct plugin
|
||||||
|
// to the individual load() methods. This hack allows to pass this reference
|
||||||
|
// indirectly to the load() method.
|
||||||
|
int mIdx;
|
||||||
|
void setIndex(const int index) {mIdx = index; mCtx.index = index;}
|
||||||
|
const int getIndex() {return mIdx;}
|
||||||
|
|
||||||
|
void setGlobalReaderList(std::vector<ESMReader> *list) {mGlobalReaderList = list;}
|
||||||
|
std::vector<ESMReader> *getGlobalReaderList() {return mGlobalReaderList;}
|
||||||
|
|
||||||
/*************************************************************************
|
/*************************************************************************
|
||||||
*
|
*
|
||||||
* Medium-level reading shortcuts
|
* Medium-level reading shortcuts
|
||||||
|
@ -110,6 +121,14 @@ public:
|
||||||
getHT(x);
|
getHT(x);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
template <typename X>
|
||||||
|
void getHNOT(X &x, const char* name, int size)
|
||||||
|
{
|
||||||
|
assert(sizeof(X) == size);
|
||||||
|
if(isNextSub(name))
|
||||||
|
getHT(x);
|
||||||
|
}
|
||||||
|
|
||||||
int64_t getHNLong(const char *name);
|
int64_t getHNLong(const char *name);
|
||||||
|
|
||||||
// Get data of a given type/size, including subrecord header
|
// Get data of a given type/size, including subrecord header
|
||||||
|
@ -251,6 +270,7 @@ private:
|
||||||
|
|
||||||
SaveData mSaveData;
|
SaveData mSaveData;
|
||||||
MasterList mMasters;
|
MasterList mMasters;
|
||||||
|
std::vector<ESMReader> *mGlobalReaderList;
|
||||||
ToUTF8::Utf8Encoder* mEncoder;
|
ToUTF8::Utf8Encoder* mEncoder;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,13 +2,29 @@
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
|
#include <list>
|
||||||
|
#include <boost/concept_check.hpp>
|
||||||
|
|
||||||
#include "esmreader.hpp"
|
#include "esmreader.hpp"
|
||||||
#include "esmwriter.hpp"
|
#include "esmwriter.hpp"
|
||||||
|
|
||||||
|
#include <apps/openmw/mwworld/store.hpp>
|
||||||
|
#include <apps/openmw/mwworld/cellstore.hpp>
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
|
||||||
|
/// Some overloaded copare operators.
|
||||||
|
bool operator==(const MovedCellRef& ref, int pRefnum)
|
||||||
|
{
|
||||||
|
return (ref.mRefnum == pRefnum);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator==(const CellRef& ref, int pRefnum)
|
||||||
|
{
|
||||||
|
return (ref.mRefnum == pRefnum);
|
||||||
|
}
|
||||||
|
|
||||||
void CellRef::save(ESMWriter &esm)
|
void CellRef::save(ESMWriter &esm)
|
||||||
{
|
{
|
||||||
esm.writeHNT("FRMR", mRefnum);
|
esm.writeHNT("FRMR", mRefnum);
|
||||||
|
@ -63,10 +79,11 @@ void CellRef::save(ESMWriter &esm)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::load(ESMReader &esm)
|
void Cell::load(ESMReader &esm, MWWorld::ESMStore &store)
|
||||||
{
|
{
|
||||||
// Ignore this for now, it might mean we should delete the entire
|
// Ignore this for now, it might mean we should delete the entire
|
||||||
// cell?
|
// cell?
|
||||||
|
// TODO: treat the special case "another plugin moved this ref, but we want to delete it"!
|
||||||
if (esm.isNextSub("DELE")) {
|
if (esm.isNextSub("DELE")) {
|
||||||
esm.skipHSub();
|
esm.skipHSub();
|
||||||
}
|
}
|
||||||
|
@ -109,9 +126,37 @@ void Cell::load(ESMReader &esm)
|
||||||
if (esm.isNextSub("NAM0")) {
|
if (esm.isNextSub("NAM0")) {
|
||||||
esm.getHT(mNAM0);
|
esm.getHT(mNAM0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// preload moved references
|
||||||
|
while (esm.isNextSub("MVRF")) {
|
||||||
|
CellRef ref;
|
||||||
|
MovedCellRef cMRef;
|
||||||
|
getNextMVRF(esm, cMRef);
|
||||||
|
|
||||||
|
MWWorld::Store<ESM::Cell> &cStore = const_cast<MWWorld::Store<ESM::Cell>&>(store.get<ESM::Cell>());
|
||||||
|
ESM::Cell *cellAlt = const_cast<ESM::Cell*>(cStore.searchOrCreate(cMRef.mTarget[0], cMRef.mTarget[1]));
|
||||||
|
|
||||||
|
// Get regular moved reference data. Adapted from CellStore::loadRefs. Maybe we can optimize the following
|
||||||
|
// implementation when the oher implementation works as well.
|
||||||
|
getNextRef(esm, ref);
|
||||||
|
std::string lowerCase;
|
||||||
|
|
||||||
|
std::transform (ref.mRefID.begin(), ref.mRefID.end(), std::back_inserter (lowerCase),
|
||||||
|
(int(*)(int)) std::tolower);
|
||||||
|
|
||||||
|
// Add data required to make reference appear in the correct cell.
|
||||||
|
// We should not need to test for duplicates, as this part of the code is pre-cell merge.
|
||||||
|
mMovedRefs.push_back(cMRef);
|
||||||
|
// But there may be duplicates here!
|
||||||
|
ESM::CellRefTracker::iterator iter = std::find(cellAlt->mLeasedRefs.begin(), cellAlt->mLeasedRefs.end(), ref.mRefnum);
|
||||||
|
if (iter == cellAlt->mLeasedRefs.end())
|
||||||
|
cellAlt->mLeasedRefs.push_back(ref);
|
||||||
|
else
|
||||||
|
*iter = ref;
|
||||||
|
}
|
||||||
|
|
||||||
// Save position of the cell references and move on
|
// Save position of the cell references and move on
|
||||||
mContext = esm.getContext();
|
mContextList.push_back(esm.getContext());
|
||||||
esm.skipRecord();
|
esm.skipRecord();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -146,9 +191,9 @@ void Cell::save(ESMWriter &esm)
|
||||||
esm.writeHNT("NAM0", mNAM0);
|
esm.writeHNT("NAM0", mNAM0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Cell::restore(ESMReader &esm) const
|
void Cell::restore(ESMReader &esm, int iCtx) const
|
||||||
{
|
{
|
||||||
esm.restoreContext(mContext);
|
esm.restoreContext(mContextList[iCtx]);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::string Cell::getDescription() const
|
std::string Cell::getDescription() const
|
||||||
|
@ -167,17 +212,61 @@ std::string Cell::getDescription() const
|
||||||
|
|
||||||
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
{
|
{
|
||||||
|
// TODO: Try and document reference numbering, I don't think this has been done anywhere else.
|
||||||
if (!esm.hasMoreSubs())
|
if (!esm.hasMoreSubs())
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
// NOTE: We should not need this check. It is a safety check until we have checked
|
||||||
|
// more plugins, and how they treat these moved references.
|
||||||
|
if (esm.isNextSub("MVRF")) {
|
||||||
|
esm.skipRecord(); // skip MVRF
|
||||||
|
esm.skipRecord(); // skip CNDT
|
||||||
|
// That should be it, I haven't seen any other fields yet.
|
||||||
|
}
|
||||||
|
|
||||||
esm.getHNT(ref.mRefnum, "FRMR");
|
esm.getHNT(ref.mRefnum, "FRMR");
|
||||||
ref.mRefID = esm.getHNString("NAME");
|
ref.mRefID = esm.getHNString("NAME");
|
||||||
|
|
||||||
|
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||||
|
int local = (ref.mRefnum & 0xff000000) >> 24;
|
||||||
|
size_t global = esm.getIndex() + 1;
|
||||||
|
if (local)
|
||||||
|
{
|
||||||
|
// If the most significant 8 bits are used, then this reference already exists.
|
||||||
|
// In this case, do not spawn a new reference, but overwrite the old one.
|
||||||
|
ref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||||
|
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||||
|
global = masters[local-1].index + 1;
|
||||||
|
ref.mRefnum |= global << 24; // insert global plugin ID
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// This is an addition by the present plugin. Set the corresponding plugin index.
|
||||||
|
ref.mRefnum |= global << 24; // insert global plugin ID
|
||||||
|
}
|
||||||
|
|
||||||
// getHNOT will not change the existing value if the subrecord is
|
// getHNOT will not change the existing value if the subrecord is
|
||||||
// missing
|
// missing
|
||||||
ref.mScale = 1.0;
|
ref.mScale = 1.0;
|
||||||
esm.getHNOT(ref.mScale, "XSCL");
|
esm.getHNOT(ref.mScale, "XSCL");
|
||||||
|
|
||||||
|
// TODO: support loading references from saves, there are tons of keys not recognized yet.
|
||||||
|
// The following is just an incomplete list.
|
||||||
|
if (esm.isNextSub("ACTN"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("STPR"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ACDT"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ACSC"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("ACSL"))
|
||||||
|
esm.skipHSub();
|
||||||
|
if (esm.isNextSub("CHRD"))
|
||||||
|
esm.skipHSub();
|
||||||
|
else if (esm.isNextSub("CRED")) // ???
|
||||||
|
esm.skipHSub();
|
||||||
|
|
||||||
ref.mOwner = esm.getHNOString("ANAM");
|
ref.mOwner = esm.getHNOString("ANAM");
|
||||||
ref.mGlob = esm.getHNOString("BNAM");
|
ref.mGlob = esm.getHNOString("BNAM");
|
||||||
ref.mSoul = esm.getHNOString("XSOL");
|
ref.mSoul = esm.getHNOString("XSOL");
|
||||||
|
@ -215,17 +304,43 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref)
|
||||||
esm.getHNOT(ref.mUnam, "UNAM");
|
esm.getHNOT(ref.mUnam, "UNAM");
|
||||||
esm.getHNOT(ref.mFltv, "FLTV");
|
esm.getHNOT(ref.mFltv, "FLTV");
|
||||||
|
|
||||||
esm.getHNT(ref.mPos, "DATA", 24);
|
esm.getHNOT(ref.mPos, "DATA", 24);
|
||||||
|
|
||||||
// Number of references in the cell? Maximum once in each cell,
|
// Number of references in the cell? Maximum once in each cell,
|
||||||
// but not always at the beginning, and not always right. In other
|
// but not always at the beginning, and not always right. In other
|
||||||
// words, completely useless.
|
// words, completely useless.
|
||||||
|
// Update: Well, maybe not completely useless. This might actually be
|
||||||
|
// number_of_references + number_of_references_moved_here_Across_boundaries,
|
||||||
|
// and could be helpful for collecting these weird moved references.
|
||||||
ref.mNam0 = 0;
|
ref.mNam0 = 0;
|
||||||
if (esm.isNextSub("NAM0"))
|
if (esm.isNextSub("NAM0"))
|
||||||
{
|
{
|
||||||
esm.getHT(ref.mNam0);
|
esm.getHT(ref.mNam0);
|
||||||
//esm.getHNOT(NAM0, "NAM0");
|
//esm.getHNOT(NAM0, "NAM0");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (esm.isNextSub("DELE")) {
|
||||||
|
esm.skipHSub();
|
||||||
|
ref.mDeleted = 2; // Deleted, will not respawn.
|
||||||
|
// TODO: find out when references do respawn.
|
||||||
|
} else
|
||||||
|
ref.mDeleted = 0;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Cell::getNextMVRF(ESMReader &esm, MovedCellRef &mref)
|
||||||
|
{
|
||||||
|
esm.getHT(mref.mRefnum);
|
||||||
|
esm.getHNOT(mref.mTarget, "CNDT");
|
||||||
|
|
||||||
|
// Identify references belonging to a parent file and adapt the ID accordingly.
|
||||||
|
int local = (mref.mRefnum & 0xff000000) >> 24;
|
||||||
|
size_t global = esm.getIndex() + 1;
|
||||||
|
mref.mRefnum &= 0x00ffffff; // delete old plugin ID
|
||||||
|
const ESM::ESMReader::MasterList &masters = esm.getMasters();
|
||||||
|
global = masters[local-1].index + 1;
|
||||||
|
mref.mRefnum |= global << 24; // insert global plugin ID
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,9 +2,18 @@
|
||||||
#define OPENMW_ESM_CELL_H
|
#define OPENMW_ESM_CELL_H
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "esmcommon.hpp"
|
#include "esmcommon.hpp"
|
||||||
#include "defs.hpp"
|
#include "defs.hpp"
|
||||||
|
#include "apps/openmw/mwbase/world.hpp"
|
||||||
|
|
||||||
|
/*
|
||||||
|
namespace MWWorld {
|
||||||
|
class ESMStore;
|
||||||
|
class CellStore;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
namespace ESM
|
namespace ESM
|
||||||
{
|
{
|
||||||
|
@ -69,6 +78,9 @@ public:
|
||||||
|
|
||||||
// No idea - occurs ONCE in Morrowind.esm, for an activator
|
// No idea - occurs ONCE in Morrowind.esm, for an activator
|
||||||
char mUnam;
|
char mUnam;
|
||||||
|
|
||||||
|
// Track deleted references. 0 - not deleted, 1 - deleted, but respawns, 2 - deleted and does not respawn.
|
||||||
|
int mDeleted;
|
||||||
|
|
||||||
// Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza
|
// Occurs in Tribunal.esm, eg. in the cell "Mournhold, Plaza
|
||||||
// Brindisi Dorom", where it has the value 100. Also only for
|
// Brindisi Dorom", where it has the value 100. Also only for
|
||||||
|
@ -82,6 +94,31 @@ public:
|
||||||
void save(ESMWriter &esm);
|
void save(ESMWriter &esm);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/* Moved cell reference tracking object. This mainly stores the target cell
|
||||||
|
of the reference, so we can easily know where it has been moved when another
|
||||||
|
plugin tries to move it independently.
|
||||||
|
Unfortunately, we need to implement this here.
|
||||||
|
*/
|
||||||
|
class MovedCellRef
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
int mRefnum;
|
||||||
|
|
||||||
|
// Target cell (if exterior)
|
||||||
|
int mTarget[2];
|
||||||
|
|
||||||
|
// TODO: Support moving references between exterior and interior cells!
|
||||||
|
// This may happen in saves, when an NPC follows the player. Tribunal
|
||||||
|
// introduces a henchman (which no one uses), so we may need this as well.
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Overloaded copare operator used to search inside a list of cell refs.
|
||||||
|
bool operator==(const MovedCellRef& ref, int pRefnum);
|
||||||
|
bool operator==(const CellRef& ref, int pRefnum);
|
||||||
|
|
||||||
|
typedef std::list<MovedCellRef> MovedCellRefTracker;
|
||||||
|
typedef std::list<CellRef> CellRefTracker;
|
||||||
|
|
||||||
/* Cells hold data about objects, creatures, statics (rocks, walls,
|
/* Cells hold data about objects, creatures, statics (rocks, walls,
|
||||||
buildings) and landscape (for exterior cells). Cells frequently
|
buildings) and landscape (for exterior cells). Cells frequently
|
||||||
also has other associated LAND and PGRD records. Combined, all this
|
also has other associated LAND and PGRD records. Combined, all this
|
||||||
|
@ -120,15 +157,24 @@ struct Cell
|
||||||
// Optional region name for exterior and quasi-exterior cells.
|
// Optional region name for exterior and quasi-exterior cells.
|
||||||
std::string mRegion;
|
std::string mRegion;
|
||||||
|
|
||||||
ESM_Context mContext; // File position
|
std::vector<ESM_Context> mContextList; // File position; multiple positions for multiple plugin support
|
||||||
DATAstruct mData;
|
DATAstruct mData;
|
||||||
AMBIstruct mAmbi;
|
AMBIstruct mAmbi;
|
||||||
float mWater; // Water level
|
float mWater; // Water level
|
||||||
bool mWaterInt;
|
bool mWaterInt;
|
||||||
int mMapColor;
|
int mMapColor;
|
||||||
int mNAM0;
|
int mNAM0;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
// References "leased" from another cell (i.e. a different cell
|
||||||
|
// introduced this ref, and it has been moved here by a plugin)
|
||||||
|
CellRefTracker mLeasedRefs;
|
||||||
|
MovedCellRefTracker mMovedRefs;
|
||||||
|
|
||||||
|
void load(ESMReader &esm, MWWorld::ESMStore &store);
|
||||||
|
|
||||||
|
// This method is left in for compatibility with esmtool. Parsing moved references currently requires
|
||||||
|
// passing ESMStore, bit it does not know about this parameter, so we do it this way.
|
||||||
|
void load(ESMReader &esm) {};
|
||||||
void save(ESMWriter &esm);
|
void save(ESMWriter &esm);
|
||||||
|
|
||||||
bool isExterior() const
|
bool isExterior() const
|
||||||
|
@ -151,7 +197,7 @@ struct Cell
|
||||||
// somewhere other than the file system, you need to pre-open the
|
// somewhere other than the file system, you need to pre-open the
|
||||||
// ESMReader, and the filename must match the stored filename
|
// ESMReader, and the filename must match the stored filename
|
||||||
// exactly.
|
// exactly.
|
||||||
void restore(ESMReader &esm) const;
|
void restore(ESMReader &esm, int iCtx) const;
|
||||||
|
|
||||||
std::string getDescription() const;
|
std::string getDescription() const;
|
||||||
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
///< Return a short string describing the cell (mostly used for debugging/logging purpose)
|
||||||
|
@ -163,6 +209,11 @@ struct Cell
|
||||||
reuse one memory location without blanking it between calls.
|
reuse one memory location without blanking it between calls.
|
||||||
*/
|
*/
|
||||||
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
static bool getNextRef(ESMReader &esm, CellRef &ref);
|
||||||
|
|
||||||
|
/* This fetches an MVRF record, which is used to track moved references.
|
||||||
|
* Since they are comparably rare, we use a separate method for this.
|
||||||
|
*/
|
||||||
|
static bool getNextMVRF(ESMReader &esm, MovedCellRef &mref);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -76,8 +76,29 @@ std::string GameSetting::getString() const
|
||||||
{
|
{
|
||||||
if (mType==VT_String)
|
if (mType==VT_String)
|
||||||
return mStr;
|
return mStr;
|
||||||
|
|
||||||
throw std::runtime_error ("GMST " + mId + " is not a string");
|
throw std::runtime_error ("GMST " + mId + " is not a string");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void GameSetting::blank()
|
||||||
|
{
|
||||||
|
mStr.clear();
|
||||||
|
mI = 0;
|
||||||
|
mF = 0;
|
||||||
|
mType = VT_Float;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool operator== (const GameSetting& left, const GameSetting& right)
|
||||||
|
{
|
||||||
|
if (left.mType!=right.mType)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
switch (left.mType)
|
||||||
|
{
|
||||||
|
case VT_Float: return left.mF==right.mF;
|
||||||
|
case VT_Int: return left.mI==right.mI;
|
||||||
|
case VT_String: return left.mStr==right.mStr;
|
||||||
|
default: return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,17 +26,22 @@ struct GameSetting
|
||||||
VarType mType;
|
VarType mType;
|
||||||
|
|
||||||
void load(ESMReader &esm);
|
void load(ESMReader &esm);
|
||||||
|
|
||||||
int getInt() const;
|
int getInt() const;
|
||||||
///< Throws an exception if GMST is not of type int or float.
|
///< Throws an exception if GMST is not of type int or float.
|
||||||
|
|
||||||
float getFloat() const;
|
float getFloat() const;
|
||||||
///< Throws an exception if GMST is not of type int or float.
|
///< Throws an exception if GMST is not of type int or float.
|
||||||
|
|
||||||
std::string getString() const;
|
std::string getString() const;
|
||||||
///< Throwns an exception if GMST is not of type string.
|
///< Throwns an exception if GMST is not of type string.
|
||||||
|
|
||||||
void save(ESMWriter &esm);
|
void save(ESMWriter &esm);
|
||||||
|
|
||||||
|
void blank();
|
||||||
|
///< Set record to default state (does not touch the ID).
|
||||||
};
|
};
|
||||||
|
|
||||||
|
bool operator== (const GameSetting& left, const GameSetting& right);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -81,6 +81,7 @@ Land::~Land()
|
||||||
void Land::load(ESMReader &esm)
|
void Land::load(ESMReader &esm)
|
||||||
{
|
{
|
||||||
mEsm = &esm;
|
mEsm = &esm;
|
||||||
|
mPlugin = mEsm->getIndex();
|
||||||
|
|
||||||
// Get the grid location
|
// Get the grid location
|
||||||
esm.getSubNameIs("INTV");
|
esm.getSubNameIs("INTV");
|
||||||
|
|
|
@ -23,6 +23,7 @@ struct Land
|
||||||
int mFlags; // Only first four bits seem to be used, don't know what
|
int mFlags; // Only first four bits seem to be used, don't know what
|
||||||
// they mean.
|
// they mean.
|
||||||
int mX, mY; // Map coordinates.
|
int mX, mY; // Map coordinates.
|
||||||
|
int mPlugin; // Plugin index, used to reference the correct material palette.
|
||||||
|
|
||||||
// File context. This allows the ESM reader to be 'reset' to this
|
// File context. This allows the ESM reader to be 'reset' to this
|
||||||
// location later when we are ready to load the full data set.
|
// location later when we are ready to load the full data set.
|
||||||
|
|
351
components/fileorderlist/datafileslist.cpp
Normal file
351
components/fileorderlist/datafileslist.cpp
Normal file
|
@ -0,0 +1,351 @@
|
||||||
|
#include <QtGui>
|
||||||
|
|
||||||
|
#include <components/esm/esmreader.hpp>
|
||||||
|
#include <components/files/configurationmanager.hpp>
|
||||||
|
|
||||||
|
#include "model/datafilesmodel.hpp"
|
||||||
|
#include "model/esm/esmfile.hpp"
|
||||||
|
|
||||||
|
#include "utils/filedialog.hpp"
|
||||||
|
#include "utils/lineedit.hpp"
|
||||||
|
#include "utils/naturalsort.hpp"
|
||||||
|
|
||||||
|
#include "datafileslist.hpp"
|
||||||
|
|
||||||
|
#include <boost/version.hpp>
|
||||||
|
/**
|
||||||
|
* Workaround for problems with whitespaces in paths in older versions of Boost library
|
||||||
|
*/
|
||||||
|
#if (BOOST_VERSION <= 104600)
|
||||||
|
namespace boost
|
||||||
|
{
|
||||||
|
|
||||||
|
template<>
|
||||||
|
inline boost::filesystem::path lexical_cast<boost::filesystem::path, std::string>(const std::string& arg)
|
||||||
|
{
|
||||||
|
return boost::filesystem::path(arg);
|
||||||
|
}
|
||||||
|
|
||||||
|
} /* namespace boost */
|
||||||
|
#endif /* (BOOST_VERSION <= 104600) */
|
||||||
|
|
||||||
|
using namespace ESM;
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
//sort QModelIndexList ascending
|
||||||
|
bool rowGreaterThan(const QModelIndex &index1, const QModelIndex &index2)
|
||||||
|
{
|
||||||
|
return index1.row() >= index2.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
//sort QModelIndexList descending
|
||||||
|
bool rowSmallerThan(const QModelIndex &index1, const QModelIndex &index2)
|
||||||
|
{
|
||||||
|
return index1.row() <= index2.row();
|
||||||
|
}
|
||||||
|
|
||||||
|
DataFilesList::DataFilesList(Files::ConfigurationManager &cfg, QWidget *parent)
|
||||||
|
: QWidget(parent)
|
||||||
|
, mCfgMgr(cfg)
|
||||||
|
{
|
||||||
|
// Models
|
||||||
|
mMastersModel = new DataFilesModel(this);
|
||||||
|
mPluginsModel = new DataFilesModel(this);
|
||||||
|
|
||||||
|
mPluginsProxyModel = new QSortFilterProxyModel();
|
||||||
|
mPluginsProxyModel->setDynamicSortFilter(true);
|
||||||
|
mPluginsProxyModel->setSourceModel(mPluginsModel);
|
||||||
|
|
||||||
|
// Filter toolbar
|
||||||
|
QLabel *filterLabel = new QLabel(tr("&Filter:"), this);
|
||||||
|
LineEdit *filterLineEdit = new LineEdit(this);
|
||||||
|
filterLabel->setBuddy(filterLineEdit);
|
||||||
|
|
||||||
|
QToolBar *filterToolBar = new QToolBar(this);
|
||||||
|
filterToolBar->setMovable(false);
|
||||||
|
|
||||||
|
// Create a container widget and a layout to get the spacer to work
|
||||||
|
QWidget *filterWidget = new QWidget(this);
|
||||||
|
QHBoxLayout *filterLayout = new QHBoxLayout(filterWidget);
|
||||||
|
QSpacerItem *hSpacer1 = new QSpacerItem(40, 20, QSizePolicy::Expanding, QSizePolicy::Minimum);
|
||||||
|
|
||||||
|
filterLayout->addItem(hSpacer1);
|
||||||
|
filterLayout->addWidget(filterLabel);
|
||||||
|
filterLayout->addWidget(filterLineEdit);
|
||||||
|
|
||||||
|
filterToolBar->addWidget(filterWidget);
|
||||||
|
|
||||||
|
QCheckBox checkBox;
|
||||||
|
unsigned int height = checkBox.sizeHint().height() + 4;
|
||||||
|
|
||||||
|
mMastersTable = new QTableView(this);
|
||||||
|
mMastersTable->setModel(mMastersModel);
|
||||||
|
mMastersTable->setObjectName("MastersTable");
|
||||||
|
mMastersTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
mMastersTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
mMastersTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
mMastersTable->setAlternatingRowColors(true);
|
||||||
|
mMastersTable->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
mMastersTable->horizontalHeader()->hide();
|
||||||
|
|
||||||
|
// Set the row height to the size of the checkboxes
|
||||||
|
mMastersTable->verticalHeader()->setDefaultSectionSize(height);
|
||||||
|
mMastersTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||||
|
mMastersTable->verticalHeader()->hide();
|
||||||
|
mMastersTable->setColumnHidden(1, true);
|
||||||
|
mMastersTable->setColumnHidden(2, true);
|
||||||
|
mMastersTable->setColumnHidden(3, true);
|
||||||
|
mMastersTable->setColumnHidden(4, true);
|
||||||
|
mMastersTable->setColumnHidden(5, true);
|
||||||
|
mMastersTable->setColumnHidden(6, true);
|
||||||
|
mMastersTable->setColumnHidden(7, true);
|
||||||
|
mMastersTable->setColumnHidden(8, true);
|
||||||
|
|
||||||
|
mPluginsTable = new QTableView(this);
|
||||||
|
mPluginsTable->setModel(mPluginsProxyModel);
|
||||||
|
mPluginsTable->setObjectName("PluginsTable");
|
||||||
|
mPluginsTable->setContextMenuPolicy(Qt::CustomContextMenu);
|
||||||
|
mPluginsTable->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
mPluginsTable->setSelectionMode(QAbstractItemView::SingleSelection);
|
||||||
|
mPluginsTable->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||||||
|
mPluginsTable->setAlternatingRowColors(true);
|
||||||
|
mPluginsTable->setVerticalScrollMode(QAbstractItemView::ScrollPerItem);
|
||||||
|
mPluginsTable->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
mPluginsTable->horizontalHeader()->hide();
|
||||||
|
|
||||||
|
mPluginsTable->verticalHeader()->setDefaultSectionSize(height);
|
||||||
|
mPluginsTable->verticalHeader()->setResizeMode(QHeaderView::Fixed);
|
||||||
|
mPluginsTable->setColumnHidden(1, true);
|
||||||
|
mPluginsTable->setColumnHidden(2, true);
|
||||||
|
mPluginsTable->setColumnHidden(3, true);
|
||||||
|
mPluginsTable->setColumnHidden(4, true);
|
||||||
|
mPluginsTable->setColumnHidden(5, true);
|
||||||
|
mPluginsTable->setColumnHidden(6, true);
|
||||||
|
mPluginsTable->setColumnHidden(7, true);
|
||||||
|
mPluginsTable->setColumnHidden(8, true);
|
||||||
|
|
||||||
|
// Add both tables to a splitter
|
||||||
|
QSplitter *splitter = new QSplitter(this);
|
||||||
|
splitter->setOrientation(Qt::Horizontal);
|
||||||
|
splitter->addWidget(mMastersTable);
|
||||||
|
splitter->addWidget(mPluginsTable);
|
||||||
|
|
||||||
|
// Adjust the default widget widths inside the splitter
|
||||||
|
QList<int> sizeList;
|
||||||
|
sizeList << 175 << 200;
|
||||||
|
splitter->setSizes(sizeList);
|
||||||
|
|
||||||
|
QVBoxLayout *pageLayout = new QVBoxLayout(this);
|
||||||
|
|
||||||
|
pageLayout->addWidget(filterToolBar);
|
||||||
|
pageLayout->addWidget(splitter);
|
||||||
|
|
||||||
|
connect(mPluginsTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||||
|
connect(mMastersTable, SIGNAL(doubleClicked(QModelIndex)), this, SLOT(setCheckState(QModelIndex)));
|
||||||
|
|
||||||
|
connect(mMastersModel, SIGNAL(checkedItemsChanged(QStringList,QStringList)), mPluginsModel, SLOT(slotcheckedItemsChanged(QStringList,QStringList)));
|
||||||
|
|
||||||
|
connect(filterLineEdit, SIGNAL(textChanged(QString)), this, SLOT(filterChanged(QString)));
|
||||||
|
|
||||||
|
connect(mPluginsTable, SIGNAL(customContextMenuRequested(QPoint)), this, SLOT(showContextMenu(QPoint)));
|
||||||
|
|
||||||
|
createActions();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::createActions()
|
||||||
|
{
|
||||||
|
// Refresh the plugins
|
||||||
|
QAction *refreshAction = new QAction(tr("Refresh"), this);
|
||||||
|
refreshAction->setShortcut(QKeySequence(tr("F5")));
|
||||||
|
connect(refreshAction, SIGNAL(triggered()), this, SLOT(refresh()));
|
||||||
|
|
||||||
|
// Context menu actions
|
||||||
|
mCheckAction = new QAction(tr("Check selected"), this);
|
||||||
|
connect(mCheckAction, SIGNAL(triggered()), this, SLOT(check()));
|
||||||
|
|
||||||
|
mUncheckAction = new QAction(tr("Uncheck selected"), this);
|
||||||
|
connect(mUncheckAction, SIGNAL(triggered()), this, SLOT(uncheck()));
|
||||||
|
|
||||||
|
// Context menu for the plugins table
|
||||||
|
mContextMenu = new QMenu(this);
|
||||||
|
|
||||||
|
mContextMenu->addAction(mCheckAction);
|
||||||
|
mContextMenu->addAction(mUncheckAction);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
bool DataFilesList::setupDataFiles(Files::PathContainer dataDirs, const QString encoding)
|
||||||
|
{
|
||||||
|
// Set the charset for reading the esm/esp files
|
||||||
|
if (!encoding.isEmpty() && encoding != QLatin1String("win1252")) {
|
||||||
|
mMastersModel->setEncoding(encoding);
|
||||||
|
mPluginsModel->setEncoding(encoding);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add the paths to the respective models
|
||||||
|
for (Files::PathContainer::iterator it = dataDirs.begin(); it != dataDirs.end(); ++it) {
|
||||||
|
QString path = QString::fromStdString(it->string());
|
||||||
|
path.remove(QChar('\"'));
|
||||||
|
mMastersModel->addMasters(path);
|
||||||
|
mPluginsModel->addPlugins(path);
|
||||||
|
}
|
||||||
|
|
||||||
|
mMastersModel->sort(0);
|
||||||
|
mPluginsModel->sort(0);
|
||||||
|
// mMastersTable->sortByColumn(3, Qt::AscendingOrder);
|
||||||
|
// mPluginsTable->sortByColumn(3, Qt::AscendingOrder);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::selectedFiles(std::vector<boost::filesystem::path>& paths)
|
||||||
|
{
|
||||||
|
QStringList masterPaths = mMastersModel->checkedItemsPaths();
|
||||||
|
foreach (const QString &path, masterPaths)
|
||||||
|
{
|
||||||
|
paths.push_back(path.toStdString());
|
||||||
|
}
|
||||||
|
QStringList pluginPaths = mPluginsModel->checkedItemsPaths();
|
||||||
|
foreach (const QString &path, pluginPaths)
|
||||||
|
{
|
||||||
|
paths.push_back(path.toStdString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::check()
|
||||||
|
{
|
||||||
|
// Check the current selection
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
//qSort(indexes.begin(), indexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
foreach (const QModelIndex &index, indexes) {
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mPluginsModel->setCheckState(index, Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::uncheck()
|
||||||
|
{
|
||||||
|
// uncheck the current selection
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
//sort selection ascending because selectedIndexes returns an unsorted list
|
||||||
|
//qSort(indexes.begin(), indexes.end(), rowSmallerThan);
|
||||||
|
|
||||||
|
foreach (const QModelIndex &index, indexes) {
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
mPluginsModel->setCheckState(index, Qt::Unchecked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::refresh()
|
||||||
|
{
|
||||||
|
mPluginsModel->sort(0);
|
||||||
|
|
||||||
|
|
||||||
|
// Refresh the plugins table
|
||||||
|
mPluginsTable->scrollToTop();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void DataFilesList::setCheckState(QModelIndex index)
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
QObject *object = QObject::sender();
|
||||||
|
|
||||||
|
// Not a signal-slot call
|
||||||
|
if (!object)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (object->objectName() == QLatin1String("PluginsTable")) {
|
||||||
|
QModelIndex sourceIndex = mPluginsProxyModel->mapToSource(index);
|
||||||
|
|
||||||
|
(mPluginsModel->checkState(sourceIndex) == Qt::Checked)
|
||||||
|
? mPluginsModel->setCheckState(sourceIndex, Qt::Unchecked)
|
||||||
|
: mPluginsModel->setCheckState(sourceIndex, Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (object->objectName() == QLatin1String("MastersTable")) {
|
||||||
|
(mMastersModel->checkState(index) == Qt::Checked)
|
||||||
|
? mMastersModel->setCheckState(index, Qt::Unchecked)
|
||||||
|
: mMastersModel->setCheckState(index, Qt::Checked);
|
||||||
|
}
|
||||||
|
|
||||||
|
return;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::uncheckAll()
|
||||||
|
{
|
||||||
|
mMastersModel->uncheckAll();
|
||||||
|
mPluginsModel->uncheckAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::filterChanged(const QString filter)
|
||||||
|
{
|
||||||
|
QRegExp regExp(filter, Qt::CaseInsensitive, QRegExp::FixedString);
|
||||||
|
mPluginsProxyModel->setFilterRegExp(regExp);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::showContextMenu(const QPoint &point)
|
||||||
|
{
|
||||||
|
// Make sure there are plugins in the view
|
||||||
|
if (!mPluginsTable->selectionModel()->hasSelection()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPoint globalPos = mPluginsTable->mapToGlobal(point);
|
||||||
|
|
||||||
|
QModelIndexList indexes = mPluginsTable->selectionModel()->selectedIndexes();
|
||||||
|
|
||||||
|
// Show the check/uncheck actions depending on the state of the selected items
|
||||||
|
mUncheckAction->setEnabled(false);
|
||||||
|
mCheckAction->setEnabled(false);
|
||||||
|
|
||||||
|
foreach (const QModelIndex &index, indexes) {
|
||||||
|
if (!index.isValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
(mPluginsModel->checkState(index) == Qt::Checked)
|
||||||
|
? mUncheckAction->setEnabled(true)
|
||||||
|
: mCheckAction->setEnabled(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Show menu
|
||||||
|
mContextMenu->exec(globalPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DataFilesList::setCheckState(const QString& element, Qt::CheckState state)
|
||||||
|
{
|
||||||
|
EsmFile *file = mPluginsModel->findItem(element);
|
||||||
|
if (file)
|
||||||
|
{
|
||||||
|
mPluginsModel->setCheckState(mPluginsModel->indexFromItem(file), Qt::Checked);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
file = mMastersModel->findItem(element);
|
||||||
|
mMastersModel->setCheckState(mMastersModel->indexFromItem(file), Qt::Checked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList DataFilesList::checkedFiles()
|
||||||
|
{
|
||||||
|
return mMastersModel->checkedItems() + mPluginsModel->checkedItems();
|
||||||
|
}
|
77
components/fileorderlist/datafileslist.hpp
Normal file
77
components/fileorderlist/datafileslist.hpp
Normal file
|
@ -0,0 +1,77 @@
|
||||||
|
#ifndef DATAFILESLIST_H
|
||||||
|
#define DATAFILESLIST_H
|
||||||
|
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QModelIndex>
|
||||||
|
#include <components/files/collections.hpp>
|
||||||
|
|
||||||
|
|
||||||
|
class QTableView;
|
||||||
|
class QSortFilterProxyModel;
|
||||||
|
class QSettings;
|
||||||
|
class QAction;
|
||||||
|
class QToolBar;
|
||||||
|
class QMenu;
|
||||||
|
class ProfilesComboBox;
|
||||||
|
class DataFilesModel;
|
||||||
|
|
||||||
|
class TextInputDialog;
|
||||||
|
|
||||||
|
namespace Files { struct ConfigurationManager; }
|
||||||
|
|
||||||
|
class DataFilesList : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
DataFilesList(Files::ConfigurationManager& cfg, QWidget *parent = 0);
|
||||||
|
|
||||||
|
bool setupDataFiles(Files::PathContainer dataDirs, const QString encoding);
|
||||||
|
void selectedFiles(std::vector<boost::filesystem::path>& paths);
|
||||||
|
void uncheckAll();
|
||||||
|
QStringList checkedFiles();
|
||||||
|
void setCheckState(const QString& element, Qt::CheckState);
|
||||||
|
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setCheckState(QModelIndex index);
|
||||||
|
|
||||||
|
void filterChanged(const QString filter);
|
||||||
|
void showContextMenu(const QPoint &point);
|
||||||
|
|
||||||
|
// Action slots
|
||||||
|
// void moveUp();
|
||||||
|
// void moveDown();
|
||||||
|
// void moveTop();
|
||||||
|
// void moveBottom();
|
||||||
|
void check();
|
||||||
|
void uncheck();
|
||||||
|
void refresh();
|
||||||
|
|
||||||
|
private:
|
||||||
|
DataFilesModel *mMastersModel;
|
||||||
|
DataFilesModel *mPluginsModel;
|
||||||
|
|
||||||
|
QSortFilterProxyModel *mPluginsProxyModel;
|
||||||
|
|
||||||
|
QTableView *mMastersTable;
|
||||||
|
QTableView *mPluginsTable;
|
||||||
|
|
||||||
|
QMenu *mContextMenu;
|
||||||
|
|
||||||
|
// QAction *mMoveUpAction;
|
||||||
|
// QAction *mMoveDownAction;
|
||||||
|
// QAction *mMoveTopAction;
|
||||||
|
// QAction *mMoveBottomAction;
|
||||||
|
QAction *mCheckAction;
|
||||||
|
QAction *mUncheckAction;
|
||||||
|
|
||||||
|
Files::ConfigurationManager &mCfgMgr;
|
||||||
|
|
||||||
|
// const QStringList checkedPlugins();
|
||||||
|
// const QStringList selectedMasters();
|
||||||
|
|
||||||
|
void createActions();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -292,6 +292,7 @@ void DataFilesModel::addMasters(const QString &path)
|
||||||
|
|
||||||
EsmFile *file = new EsmFile(master);
|
EsmFile *file = new EsmFile(master);
|
||||||
file->setDates(info.lastModified(), info.lastRead());
|
file->setDates(info.lastModified(), info.lastRead());
|
||||||
|
file->setPath(info.absoluteFilePath());
|
||||||
|
|
||||||
// Add the master to the table
|
// Add the master to the table
|
||||||
if (findItem(master) == 0)
|
if (findItem(master) == 0)
|
||||||
|
@ -427,6 +428,25 @@ QStringList DataFilesModel::checkedItems()
|
||||||
return list;
|
return list;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QStringList DataFilesModel::checkedItemsPaths()
|
||||||
|
{
|
||||||
|
QStringList list;
|
||||||
|
|
||||||
|
QList<EsmFile *>::ConstIterator it;
|
||||||
|
QList<EsmFile *>::ConstIterator itEnd = mFiles.constEnd();
|
||||||
|
|
||||||
|
int i = 0;
|
||||||
|
for (it = mFiles.constBegin(); it != itEnd; ++it) {
|
||||||
|
EsmFile *file = item(i);
|
||||||
|
++i;
|
||||||
|
|
||||||
|
if (mCheckStates[file->fileName()] == Qt::Checked && mAvailableFiles.contains(file->fileName()))
|
||||||
|
list << file->path();
|
||||||
|
}
|
||||||
|
|
||||||
|
return list;
|
||||||
|
}
|
||||||
|
|
||||||
void DataFilesModel::uncheckAll()
|
void DataFilesModel::uncheckAll()
|
||||||
{
|
{
|
||||||
emit layoutAboutToBeChanged();
|
emit layoutAboutToBeChanged();
|
|
@ -43,6 +43,7 @@ public:
|
||||||
|
|
||||||
QStringList checkedItems();
|
QStringList checkedItems();
|
||||||
QStringList uncheckedItems();
|
QStringList uncheckedItems();
|
||||||
|
QStringList checkedItemsPaths();
|
||||||
|
|
||||||
Qt::CheckState checkState(const QModelIndex &index);
|
Qt::CheckState checkState(const QModelIndex &index);
|
||||||
void setCheckState(const QModelIndex &index, Qt::CheckState state);
|
void setCheckState(const QModelIndex &index, Qt::CheckState state);
|
Loading…
Reference in a new issue