Merge remote-tracking branch 'OpenMW/master' into FixLoadOrderReset

Fixed Conflict:
	components/contentselector/model/contentmodel.cpp
openmw-35
dteviot 10 years ago
commit 9d6efcecff

@ -6,4 +6,4 @@ export CC=clang
brew tap openmw/openmw
brew update
brew unlink boost
brew install cmake openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg pkg-config qt unshield
brew install openmw-mygui openmw-bullet openmw-sdl2 openmw-ffmpeg qt unshield

@ -20,29 +20,13 @@ set(OPENMW_VERSION_TAGHASH "")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(GIT_CHECKOUT FALSE)
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
if(NOT EXISTS ${PROJECT_SOURCE_DIR}/.git/shallow)
find_package(Git)
if(GIT_FOUND)
include(GetGitRevisionDescription)
get_git_tag_revision(TAGHASH --tags --max-count=1)
get_git_head_revision(REFSPEC COMMITHASH)
git_describe(VERSION --tags ${TAGHASH})
string(REGEX MATCH "^openmw-[^0-9]*[0-9]+\\.[0-9]+\\.[0-9]+.*" MATCH "${VERSION}")
if(MATCH)
string(REGEX REPLACE "^openmw-([0-9]+)\\..*" "\\1" GIT_VERSION_MAJOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_MINOR "${VERSION}")
string(REGEX REPLACE "^openmw-[0-9]+\\.[0-9]+\\.([0-9]+).*" "\\1" GIT_VERSION_RELEASE "${VERSION}")
set(OPENMW_VERSION_COMMITHASH "${COMMITHASH}")
set(OPENMW_VERSION_TAGHASH "${TAGHASH}")
message(STATUS "OpenMW version ${OPENMW_VERSION}")
else(MATCH)
message(WARNING "Failed to get valid version information from Git")
endif(MATCH)
set(GIT_CHECKOUT TRUE)
else(GIT_FOUND)
message(WARNING "Git executable not found")
endif(GIT_FOUND)
@ -870,4 +854,3 @@ if (DOXYGEN_FOUND)
WORKING_DIRECTORY ${OpenMW_BINARY_DIR}
COMMENT "Generating documentation for the github-pages at ${DOXYGEN_PAGES_OUTPUT_DIR}" VERBATIM)
endif ()

@ -150,34 +150,33 @@ int extractAll(Bsa::BSAFile& bsa, Arguments& info);
int main(int argc, char** argv)
{
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
// Open file
Bsa::BSAFile bsa;
try
{
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
// Open file
Bsa::BSAFile bsa;
bsa.open(info.filename);
if (info.mode == "list")
return list(bsa, info);
else if (info.mode == "extract")
return extract(bsa, info);
else if (info.mode == "extractall")
return extractAll(bsa, info);
else
{
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
return 1;
}
}
catch(std::exception &e)
catch (std::exception& e)
{
std::cout << "ERROR reading BSA archive '" << info.filename
<< "'\nDetails:\n" << e.what() << std::endl;
std::cerr << "ERROR reading BSA archive\nDetails:\n" << e.what() << std::endl;
return 2;
}
if (info.mode == "list")
return list(bsa, info);
else if (info.mode == "extract")
return extract(bsa, info);
else if (info.mode == "extractall")
return extractAll(bsa, info);
else
{
std::cout << "Unsupported mode. That is not supposed to happen." << std::endl;
return 1;
}
}
int list(Bsa::BSAFile& bsa, Arguments& info)
@ -189,9 +188,11 @@ int list(Bsa::BSAFile& bsa, Arguments& info)
if(info.longformat)
{
// Long format
std::ios::fmtflags f(std::cout.flags());
std::cout << std::setw(50) << std::left << files[i].name;
std::cout << std::setw(8) << std::left << std::dec << files[i].fileSize;
std::cout << "@ 0x" << std::hex << files[i].offset << std::endl;
std::cout.flags(f);
}
else
std::cout << files[i].name << std::endl;

@ -203,19 +203,27 @@ int comp(Arguments& info);
int main(int argc, char**argv)
{
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
if (info.mode == "dump")
return load(info);
else if (info.mode == "clone")
return clone(info);
else if (info.mode == "comp")
return comp(info);
else
try
{
Arguments info;
if(!parseOptions (argc, argv, info))
return 1;
if (info.mode == "dump")
return load(info);
else if (info.mode == "clone")
return clone(info);
else if (info.mode == "comp")
return comp(info);
else
{
std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl;
return 1;
}
}
catch (std::exception& e)
{
std::cout << "Invalid or no mode specified, dying horribly. Have a nice day." << std::endl;
std::cerr << "ERROR: " << e.what() << std::endl;
return 1;
}
@ -273,8 +281,10 @@ void printRaw(ESM::ESMReader &esm)
esm.getSubName();
esm.skipHSub();
n = esm.retSubName();
std::ios::fmtflags f(std::cout.flags());
std::cout << " " << n.toString() << " - " << esm.getSubSize()
<< " bytes @ 0x" << std::hex << offs << "\n";
std::cout.flags(f);
}
}
}

@ -159,6 +159,9 @@ bool Launcher::GraphicsPage::loadSettings()
if (mGraphicsSettings.value(QString("Video/fullscreen")) == QLatin1String("true"))
fullScreenCheckBox->setCheckState(Qt::Checked);
if (mGraphicsSettings.value(QString("Video/window border")) == QLatin1String("true"))
windowBorderCheckBox->setCheckState(Qt::Checked);
int aaIndex = antiAliasingComboBox->findText(mGraphicsSettings.value(QString("Video/antialiasing")));
if (aaIndex != -1)
antiAliasingComboBox->setCurrentIndex(aaIndex);
@ -193,6 +196,9 @@ void Launcher::GraphicsPage::saveSettings()
fullScreenCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/fullscreen"), QString("false"));
windowBorderCheckBox->checkState() ? mGraphicsSettings.setValue(QString("Video/window border"), QString("true"))
: mGraphicsSettings.setValue(QString("Video/window border"), QString("false"));
mGraphicsSettings.setValue(QString("Video/antialiasing"), antiAliasingComboBox->currentText());
mGraphicsSettings.setValue(QString("Video/render system"), rendererComboBox->currentText());
@ -331,10 +337,12 @@ void Launcher::GraphicsPage::slotFullScreenChanged(int state)
customRadioButton->setEnabled(false);
customWidthSpinBox->setEnabled(false);
customHeightSpinBox->setEnabled(false);
windowBorderCheckBox->setEnabled(false);
} else {
customRadioButton->setEnabled(true);
customWidthSpinBox->setEnabled(true);
customHeightSpinBox->setEnabled(true);
windowBorderCheckBox->setEnabled(true);
}
}

@ -1,3 +1,5 @@
#include <iostream>
#include <QApplication>
#include <QTextCodec>
#include <QDir>
@ -15,53 +17,61 @@
int main(int argc, char *argv[])
{
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0)
try
{
qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError());
return 0;
}
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
qDebug() << "SDL_Init failed: " << QString::fromStdString(SDL_GetError());
return 0;
}
QApplication app(argc, argv);
QApplication app(argc, argv);
// Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath());
// Now we make sure the current dir is set to application path
QDir dir(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
#ifdef Q_OS_MAC
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
app.setLibraryPaths(libraryPaths);
#endif
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
app.setLibraryPaths(libraryPaths);
#endif
QDir::setCurrent(dir.absolutePath());
QDir::setCurrent(dir.absolutePath());
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
// Support non-latin characters
QTextCodec::setCodecForCStrings(QTextCodec::codecForName("UTF-8"));
Launcher::MainDialog mainWin;
Launcher::MainDialog mainWin;
if (!mainWin.showFirstRunDialog())
return 0;
if (!mainWin.showFirstRunDialog())
return 0;
// if (!mainWin.setup()) {
// return 0;
// }
// if (!mainWin.setup()) {
// return 0;
// }
mainWin.show();
mainWin.show();
int returnValue = app.exec();
SDL_Quit();
return returnValue;
int returnValue = app.exec();
SDL_Quit();
return returnValue;
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 0;
}
}

@ -47,13 +47,13 @@ void ProfilesComboBox::setEditEnabled(bool editable)
void ProfilesComboBox::slotTextChanged(const QString &text)
{
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text,Qt::red);
QPalette palette;
palette.setColor(QPalette::Text,Qt::red);
int index = findText(text);
if (text.isEmpty() || (index != -1 && index != currentIndex())) {
lineEdit()->setPalette(*palette);
lineEdit()->setPalette(palette);
} else {
lineEdit()->setPalette(QApplication::palette());
}

@ -59,13 +59,13 @@ void Launcher::TextInputDialog::setOkButtonEnabled(bool enabled)
QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok);
okButton->setEnabled(enabled);
QPalette *palette = new QPalette();
palette->setColor(QPalette::Text, Qt::red);
QPalette palette;
palette.setColor(QPalette::Text, Qt::red);
if (enabled) {
mLineEdit->setPalette(QApplication::palette());
} else {
// Existing profile name, make the text red
mLineEdit->setPalette(*palette);
mLineEdit->setPalette(palette);
}
}

@ -56,93 +56,87 @@ int wmain(int argc, wchar_t *wargv[]) {
char **argv = converter.get();
boost::filesystem::path::imbue(boost::locale::generator().generate(""));
#endif
bpo::options_description desc("Syntax: mwiniimporter <options> inifile configfile\nAllowed options");
bpo::positional_options_description p_desc;
desc.add_options()
("help,h", "produce help message")
("verbose,v", "verbose output")
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
("game-files,g", "import esm and esp files")
("no-archives,A", "disable bsa archives import")
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
"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\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default")
;
p_desc.add("ini", 1).add("cfg", 1);
bpo::variables_map vm;
try
{
bpo::options_description desc("Syntax: mwiniimporter <options> inifile configfile\nAllowed options");
bpo::positional_options_description p_desc;
desc.add_options()
("help,h", "produce help message")
("verbose,v", "verbose output")
("ini,i", bpo::value<std::string>(), "morrowind.ini file")
("cfg,c", bpo::value<std::string>(), "openmw.cfg file")
("output,o", bpo::value<std::string>()->default_value(""), "openmw.cfg file")
("game-files,g", "import esm and esp files")
("no-archives,A", "disable bsa archives import")
("encoding,e", bpo::value<std::string>()-> default_value("win1252"),
"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\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default")
;
p_desc.add("ini", 1).add("cfg", 1);
bpo::variables_map vm;
bpo::parsed_options parsed = bpo::command_line_parser(argc, argv)
.options(desc)
.positional(p_desc)
.run();
bpo::store(parsed, vm);
}
catch(boost::program_options::unknown_option & x)
{
std::cerr << "ERROR: " << x.what() << std::endl;
return false;
}
catch(boost::program_options::invalid_command_line_syntax & x)
{
std::cerr << "ERROR: " << x.what() << std::endl;
return false;
}
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
std::cout << desc;
return 0;
}
bpo::notify(vm);
if(vm.count("help") || !vm.count("ini") || !vm.count("cfg")) {
std::cout << desc;
return 0;
}
std::string iniFile = vm["ini"].as<std::string>();
std::string cfgFile = vm["cfg"].as<std::string>();
bpo::notify(vm);
// if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>());
if(vm["output"].defaulted()) {
outputFile = vm["cfg"].as<std::string>();
}
std::string iniFile = vm["ini"].as<std::string>();
std::string cfgFile = vm["cfg"].as<std::string>();
if(!boost::filesystem::exists(iniFile)) {
std::cerr << "ini file does not exist" << std::endl;
return -3;
}
if(!boost::filesystem::exists(cfgFile))
std::cerr << "cfg file does not exist" << std::endl;
// if no output is given, write back to cfg file
std::string outputFile(vm["output"].as<std::string>());
if(vm["output"].defaulted()) {
outputFile = vm["cfg"].as<std::string>();
}
MwIniImporter importer;
importer.setVerbose(vm.count("verbose"));
if(!boost::filesystem::exists(iniFile)) {
std::cerr << "ini file does not exist" << std::endl;
return -3;
}
if(!boost::filesystem::exists(cfgFile))
std::cerr << "cfg file does not exist" << std::endl;
// Font encoding settings
std::string encoding(vm["encoding"].as<std::string>());
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
MwIniImporter importer;
importer.setVerbose(vm.count("verbose"));
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
// Font encoding settings
std::string encoding(vm["encoding"].as<std::string>());
importer.setInputEncoding(ToUTF8::calculateEncoding(encoding));
importer.merge(cfg, ini);
importer.mergeFallback(cfg, ini);
MwIniImporter::multistrmap ini = importer.loadIniFile(iniFile);
MwIniImporter::multistrmap cfg = importer.loadCfgFile(cfgFile);
if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini);
}
importer.merge(cfg, ini);
importer.mergeFallback(cfg, ini);
if(!vm.count("no-archives")) {
importer.importArchives(cfg, ini);
}
if(vm.count("game-files")) {
importer.importGameFiles(cfg, ini);
}
std::cout << "write to: " << outputFile << std::endl;
bfs::ofstream file((bfs::path(outputFile)));
importer.writeToFile(file, cfg);
if(!vm.count("no-archives")) {
importer.importArchives(cfg, ini);
}
std::cout << "write to: " << outputFile << std::endl;
bfs::ofstream file((bfs::path(outputFile)));
importer.writeToFile(file, cfg);
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
}
return 0;
}

@ -236,6 +236,7 @@ void CS::Editor::showSettings()
if (mSettings.isHidden())
mSettings.show();
mSettings.move (QCursor::pos());
mSettings.raise();
mSettings.activateWindow();
}

@ -46,47 +46,55 @@ class Application : public QApplication
int main(int argc, char *argv[])
{
Q_INIT_RESOURCE (resources);
try
{
Q_INIT_RESOURCE (resources);
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
qRegisterMetaType<std::string> ("std::string");
qRegisterMetaType<CSMWorld::UniversalId> ("CSMWorld::UniversalId");
OgreInit::OgreInit ogreInit;
OgreInit::OgreInit ogreInit;
std::auto_ptr<sh::Factory> shinyFactory;
std::auto_ptr<sh::Factory> shinyFactory;
Application application (argc, argv);
Application application (argc, argv);
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
#ifdef Q_OS_MAC
QDir dir(QCoreApplication::applicationDirPath());
if (dir.dirName() == "MacOS") {
dir.cdUp();
dir.cdUp();
dir.cdUp();
}
QDir::setCurrent(dir.absolutePath());
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
// force Qt to load only LOCAL plugins, don't touch system Qt installation
QDir pluginsPath(QCoreApplication::applicationDirPath());
pluginsPath.cdUp();
pluginsPath.cd("Plugins");
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
application.setLibraryPaths(libraryPaths);
#endif
QStringList libraryPaths;
libraryPaths << pluginsPath.path() << QCoreApplication::applicationDirPath();
application.setLibraryPaths(libraryPaths);
#endif
application.setWindowIcon (QIcon (":./opencs.png"));
application.setWindowIcon (QIcon (":./opencs.png"));
CS::Editor editor (ogreInit);
CS::Editor editor (ogreInit);
if(!editor.makeIPCServer())
if(!editor.makeIPCServer())
{
editor.connectToIPCServer();
return 0;
}
shinyFactory = editor.setupGraphics();
return editor.run();
}
catch (std::exception& e)
{
editor.connectToIPCServer();
std::cerr << "ERROR: " << e.what() << std::endl;
return 0;
}
shinyFactory = editor.setupGraphics();
return editor.run();
}

@ -223,7 +223,14 @@ void CSMWorld::ContainerRefIdAdapter::setData (const RefIdColumn *column, RefIdD
}
CSMWorld::CreatureColumns::CreatureColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns)
: ActorColumns (actorColumns),
mType(NULL),
mSoul(NULL),
mScale(NULL),
mOriginal(NULL),
mCombat(NULL),
mMagic(NULL),
mStealth(NULL)
{}
CSMWorld::CreatureRefIdAdapter::CreatureRefIdAdapter (const CreatureColumns& columns)
@ -431,7 +438,14 @@ void CSMWorld::MiscRefIdAdapter::setData (const RefIdColumn *column, RefIdData&
InventoryRefIdAdapter<ESM::Miscellaneous>::setData (column, data, index, value);
}
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns) : ActorColumns (actorColumns) {}
CSMWorld::NpcColumns::NpcColumns (const ActorColumns& actorColumns)
: ActorColumns (actorColumns),
mRace(NULL),
mClass(NULL),
mFaction(NULL),
mHair(NULL),
mHead(NULL)
{}
CSMWorld::NpcRefIdAdapter::NpcRefIdAdapter (const NpcColumns& columns)
: ActorRefIdAdapter<ESM::NPC> (UniversalId::Type_Npc, columns), mColumns (columns)

@ -131,6 +131,7 @@ namespace
}
CSMWorld::UniversalId::UniversalId (const std::string& universalId)
: mIndex(0)
{
std::string::size_type index = universalId.find (':');

@ -18,7 +18,7 @@
#include "adjusterwidget.hpp"
CSVDoc::FileDialog::FileDialog(QWidget *parent) :
QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false)
QDialog(parent), mSelector (0), mFileWidget (0), mAdjusterWidget (0), mDialogBuilt(false), mAction(ContentAction_Undefined)
{
ui.setupUi (this);
resize(400, 400);

@ -18,7 +18,7 @@ void CSVDoc::LoadingDocument::closeEvent (QCloseEvent *event)
}
CSVDoc::LoadingDocument::LoadingDocument (CSMDoc::Document *document)
: mDocument (document), mAborted (false), mMessages (0)
: mDocument (document), mAborted (false), mMessages (0), mTotalRecords (0)
{
setWindowTitle (("Opening " + document->getSavePath().filename().string()).c_str());
@ -104,7 +104,7 @@ void CSVDoc::LoadingDocument::nextRecord (int records)
void CSVDoc::LoadingDocument::abort (const std::string& error)
{
mAborted = true;
mError->setText (QString::fromUtf8 (("Loading failed: " + error).c_str()));
mError->setText (QString::fromUtf8 (("<font color=red>Loading failed: " + error + "</font>").c_str()));
mButtons->setStandardButtons (QDialogButtonBox::Close);
}

@ -61,7 +61,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics)
: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics), mX(0), mY(0)
{
mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
mCellNode->setPosition (origin);
@ -77,15 +77,14 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
int landIndex = land.searchId(mId);
if (landIndex != -1)
{
mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
Terrain::Align_XY));
const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get();
mTerrain->loadCell(esmLand->mX,
esmLand->mY);
if(esmLand)
{
mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
Terrain::Align_XY));
mTerrain->loadCell(esmLand->mX,
esmLand->mY);
float verts = ESM::Land::LAND_SIZE;
float worldsize = ESM::Land::REAL_SIZE;
mX = esmLand->mX;
@ -98,7 +97,8 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
CSVRender::Cell::~Cell()
{
mPhysics->removeHeightField(mSceneMgr, mX, mY);
if (mTerrain.get())
mPhysics->removeHeightField(mSceneMgr, mX, mY);
for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
iter!=mObjects.end(); ++iter)

@ -346,7 +346,7 @@ namespace CSVRender
//plane X, upvector Y, mOffset x : x-z plane, wheel closer/further
std::pair<Ogre::Vector3, Ogre::Vector3> MouseState::planeAxis()
{
bool screenCoord = true;
const bool screenCoord = true;
Ogre::Vector3 dir = getCamera()->getDerivedDirection();
QString wheelDir = "Closer/Further";

@ -76,7 +76,7 @@ add_openmw_dir (mwmechanics
mechanicsmanagerimp stat character creaturestats magiceffects movement actors objects
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor
aiescort aiactivate aicombat repair enchanting pathfinding pathgrid security spellsuccess spellcasting
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction actor summoning
)
add_openmw_dir (mwstate

@ -11,7 +11,6 @@
#include <errno.h>
#include <limits.h>
#include <pthread.h>
#include <stdbool.h>
#include <sys/ptrace.h>
@ -29,6 +28,7 @@
#include <signal.h>
#endif
#define UNUSED(x) (void)(x)
static const char crash_switch[] = "--cc-handle-crash";
@ -160,7 +160,11 @@ static void gdb_info(pid_t pid)
printf("Executing: %s\n", cmd_buf);
fflush(stdout);
system(cmd_buf);
{ /* another special exception for "ignoring return value..." */
int unused;
unused = system(cmd_buf);
UNUSED(unused);
}
/* Clean up */
remove(respfile);
}
@ -406,7 +410,13 @@ int cc_install_handlers(int argc, char **argv, int num_signals, int *signals, co
snprintf(argv0, sizeof(argv0), "%s", argv[0]);
else
{
getcwd(argv0, sizeof(argv0));
{
/* we don't want to disable "ignoring return value" warnings, so we make
* a special exception here. */
char * unused;
unused = getcwd(argv0, sizeof(argv0));
UNUSED(unused);
}
retval = strlen(argv0);
snprintf(argv0+retval, sizeof(argv0)-retval, "/%s", argv[0]);
}

@ -208,6 +208,8 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
OMW::Engine::~Engine()
{
if (mOgre)
mOgre->restoreWindowGammaRamp();
mEnvironment.cleanup();
delete mScriptContext;
delete mOgre;
@ -350,6 +352,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
OEngine::Render::WindowSettings windowSettings;
windowSettings.fullscreen = settings.getBool("fullscreen", "Video");
windowSettings.window_border = settings.getBool("window border", "Video");
windowSettings.window_x = settings.getInt("resolution x", "Video");
windowSettings.window_y = settings.getInt("resolution y", "Video");
windowSettings.screen = settings.getInt("screen", "Video");
@ -381,6 +384,8 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
// Create sound system
mEnvironment.setSoundManager (new MWSound::SoundManager(mUseSound));
mOgre->setWindowGammaContrast(Settings::Manager::getFloat("gamma", "General"), Settings::Manager::getFloat("contrast", "General"));
if (!mSkipMenu)
{
std::string logo = mFallbackMap["Movies_Company_Logo"];
@ -467,9 +472,13 @@ void OMW::Engine::go()
// Play some good 'ol tunes
MWBase::Environment::get().getSoundManager()->playPlaylist(std::string("Explore"));
// start in main menu
if (!mSkipMenu)
if (!mSaveGameFile.empty())
{
MWBase::Environment::get().getStateManager()->loadGame(mSaveGameFile);
}
else if (!mSkipMenu)
{
// start in main menu
MWBase::Environment::get().getWindowManager()->pushGuiMode (MWGui::GM_MainMenu);
try
{
@ -508,6 +517,11 @@ void OMW::Engine::activate()
if (MWBase::Environment::get().getWindowManager()->isGuiMode())
return;
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if (player.getClass().getCreatureStats(player).getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0
|| player.getClass().getCreatureStats(player).getKnockedDown())
return;
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->getFacedObject();
if (ptr.isEmpty())
@ -613,3 +627,8 @@ void OMW::Engine::enableFontExport(bool exportFonts)
{
mExportFonts = exportFonts;
}
void OMW::Engine::setSaveGameFile(const std::string &savegame)
{
mSaveGameFile = savegame;
}

@ -83,6 +83,7 @@ namespace OMW
bool mScriptConsoleMode;
std::string mStartupScript;
int mActivationDistanceOverride;
std::string mSaveGameFile;
// Grab mouse?
bool mGrab;
@ -204,6 +205,9 @@ namespace OMW
void enableFontExport(bool exportFonts);
/// Set the save game file to load after initialising the engine.
void setSaveGameFile(const std::string& savegame);
private:
Files::ConfigurationManager& mCfgMgr;
};

@ -152,6 +152,9 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
("script-blacklist-use", bpo::value<bool>()->implicit_value(true)
->default_value(true), "enable script blacklisting")
("load-savegame", bpo::value<std::string>()->default_value(""),
"load a save game file on game startup")
("skip-menu", bpo::value<bool>()->implicit_value(true)
->default_value(false), "skip main menu on game startup")
@ -274,6 +277,7 @@ bool parseOptions (int argc, char** argv, OMW::Engine& engine, Files::Configurat
engine.setWarningsMode (variables["script-warn"].as<int>());
engine.setScriptBlacklist (variables["script-blacklist"].as<StringsVector>());
engine.setScriptBlacklistUse (variables["script-blacklist-use"].as<bool>());
engine.setSaveGameFile (variables["load-savegame"].as<std::string>());
// other settings
engine.setSoundUsage(!variables["no-sound"].as<bool>());

@ -113,21 +113,19 @@ namespace MWBase
OT_Theft, // Taking items owned by an NPC or a faction you are not a member of
OT_Assault, // Attacking a peaceful NPC
OT_Murder, // Murdering a peaceful NPC
OT_Trespassing, // Staying in a cell you are not allowed in (where is this defined?)
OT_Trespassing, // Picking the lock of an owned door/chest
OT_SleepingInOwnedBed, // Sleeping in a bed owned by an NPC or a faction you are not a member of
OT_Pickpocket // Entering pickpocket mode, leaving it, and being detected. Any items stolen are a separate crime (Theft)
};
/**
* @brief Commit a crime. If any actors witness the crime and report it,
* reportCrime will be called automatically.
* @note victim may be empty
* @param arg Depends on \a type, e.g. for Theft, the value of the item that was stolen.
* @return was the crime reported?
* @param victimAware Is the victim already aware of the crime?
* If this parameter is false, it will be determined by a line-of-sight and awareness check.
* @return was the crime seen?
*/
virtual bool commitCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0) = 0;
virtual void reportCrime (const MWWorld::Ptr& ptr, const MWWorld::Ptr& victim,
OffenseType type, int arg=0) = 0;
OffenseType type, int arg=0, bool victimAware=false) = 0;
/// @return false if the attack was considered a "friendly hit" and forgiven
virtual bool actorAttacked (const MWWorld::Ptr& victim, const MWWorld::Ptr& attacker) = 0;
/// Utility to check if taking this item is illegal and calling commitCrime if so
@ -199,9 +197,7 @@ namespace MWBase
virtual void clear() = 0;
/// @param bias Can be used to add an additional aggression bias towards the target,
/// making it more likely for the function to return true.
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target, int bias=0, bool ignoreDistance=false) = 0;
virtual bool isAggressive (const MWWorld::Ptr& ptr, const MWWorld::Ptr& target) = 0;
/// Resurrects the player if necessary
virtual void keepPlayerAlive() = 0;

@ -62,10 +62,13 @@ namespace MWBase
///
/// \note Slot must belong to the current character.
virtual void loadGame (const MWState::Character *character, const MWState::Slot *slot) = 0;
///< Load a saved game file from \a slot.
///
/// \note \a slot must belong to \a character.
virtual void loadGame (const std::string& filepath) = 0;
///< Load a saved game directly from the given file path. This will search the CharacterManager
/// for a Character containing this save file, and set this Character current if one was found.
/// Otherwise, a new Character will be created.
virtual void loadGame (const MWState::Character *character, const std::string& filepath) = 0;
///< Load a saved game file belonging to the given character.
///Simple saver, writes over the file if already existing
/** Used for quick save and autosave **/

@ -546,7 +546,7 @@ namespace MWBase
virtual void spawnEffect (const std::string& model, const std::string& textureOverride, const Ogre::Vector3& worldPos) = 0;
virtual void explodeSpell (const Ogre::Vector3& origin, const ESM::EffectList& effects,
const MWWorld::Ptr& caster, const std::string& id, const std::string& sourceName) = 0;
const MWWorld::Ptr& caster, int rangeType, const std::string& id, const std::string& sourceName) = 0;
virtual void activate (const MWWorld::Ptr& object, const MWWorld::Ptr& actor) = 0;

@ -8,7 +8,8 @@
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld//cellstore.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/action.hpp"

@ -14,6 +14,7 @@
#include "../mwworld/actionequip.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/nullaction.hpp"
#include "../mwworld/containerstore.hpp"

@ -11,6 +11,7 @@
#include "../mwworld/actionread.hpp"
#include "../mwworld/failedaction.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwrender/objects.hpp"

@ -12,6 +12,7 @@
#include "../mwworld/actionequip.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/nullaction.hpp"

@ -15,6 +15,7 @@
#include "../mwworld/containerstore.hpp"
#include "../mwworld/customdata.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/actionopen.hpp"
#include "../mwworld/actiontrap.hpp"
#include "../mwworld/physicssystem.hpp"
@ -59,7 +60,7 @@ namespace MWClass
ptr.get<ESM::Container>();
data->mContainerStore.fill(
ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), MWBase::Environment::get().getWorld()->getStore());
ref->mBase->mInventory, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank(), MWBase::Environment::get().getWorld()->getStore());
// store
ptr.getRefData().setCustomData (data.release());
@ -81,7 +82,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Container> *ref = ptr.get<ESM::Container>();
const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction());
store.restock(list, ptr, ptr.getCellRef().getOwner(), ptr.getCellRef().getFaction(), ptr.getCellRef().getFactionRank());
}
void Container::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const

@ -139,7 +139,7 @@ namespace MWClass
// store
ptr.getRefData().setCustomData(data.release());
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "",
getContainerStore(ptr).fill(ref->mBase->mInventory, getId(ptr), "", -1,
MWBase::Environment::get().getWorld()->getStore());
if (ref->mBase->mFlags & ESM::Creature::Weapon)
@ -288,7 +288,7 @@ namespace MWClass
}
float damage = min + (max - min) * stats.getAttackStrength();
bool healthdmg = true;
if (!weapon.isEmpty())
{
const unsigned char *attack = NULL;
@ -321,10 +321,14 @@ namespace MWClass
}
}
}
else if (isBipedal(ptr))
{
MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg);
}
MWMechanics::applyElementalShields(ptr, victim);
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
if (damage > 0)
@ -332,7 +336,7 @@ namespace MWClass
MWMechanics::diseaseContact(victim, ptr);
victim.getClass().onHit(victim, damage, true, weapon, ptr, true);
victim.getClass().onHit(victim, damage, healthdmg, weapon, ptr, true);
}
void Creature::onHit(const MWWorld::Ptr &ptr, float damage, bool ishealth, const MWWorld::Ptr &object, const MWWorld::Ptr &attacker, bool successful) const
@ -678,7 +682,6 @@ namespace MWClass
if(type >= 0)
{
std::vector<const ESM::SoundGenerator*> sounds;
std::vector<const ESM::SoundGenerator*> fallbacksounds;
MWWorld::LiveCellRef<ESM::Creature>* ref = ptr.get<ESM::Creature>();
@ -689,16 +692,15 @@ namespace MWClass
{
if (type == sound->mType && !sound->mCreature.empty() && (Misc::StringUtils::ciEqual(ourId, sound->mCreature)))
sounds.push_back(&*sound);
if (type == sound->mType && sound->mCreature.empty())
fallbacksounds.push_back(&*sound);
++sound;
}
if(!sounds.empty())
return sounds[(int)(rand()/(RAND_MAX+1.0)*sounds.size())]->mSound;
if (!fallbacksounds.empty())
return fallbacksounds[(int)(rand()/(RAND_MAX+1.0)*fallbacksounds.size())]->mSound;
}
if (type == ESM::SoundGenerator::Land)
return "Body Fall Large";
return "";
}
@ -888,7 +890,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::Creature> *ref = ptr.get<ESM::Creature>();
const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1);
}
int Creature::getBaseFightRating(const MWWorld::Ptr &ptr) const

@ -8,6 +8,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/nullaction.hpp"
@ -15,6 +16,7 @@
#include "../mwworld/actionteleport.hpp"
#include "../mwworld/actiondoor.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/actiontrap.hpp"
@ -22,7 +24,7 @@
#include "../mwgui/tooltips.hpp"
#include "../mwrender/objects.hpp"
#include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.hpp"
namespace
@ -51,7 +53,8 @@ namespace MWClass
{
const std::string model = getModel(ptr);
if (!model.empty()) {
renderingInterface.getObjects().insertModel(ptr, model);
MWRender::Actors& actors = renderingInterface.getActors();
actors.insertActivator(ptr);
}
}
@ -70,6 +73,8 @@ namespace MWClass
MWBase::Environment::get().getWorld()->activateDoor(ptr, customData.mDoorState);
}
}
MWBase::Environment::get().getMechanicsManager()->add(ptr);
}
std::string Door::getModel(const MWWorld::Ptr &ptr) const

@ -10,6 +10,7 @@
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/actioneat.hpp"
#include "../mwworld/nullaction.hpp"

@ -8,6 +8,7 @@
#include "../mwbase/world.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/ptr.hpp"
#include "../mwworld/actiontake.hpp"
@ -22,6 +23,7 @@
#include "../mwgui/tooltips.hpp"
#include "../mwrender/objects.hpp"
#include "../mwrender/actors.hpp"
#include "../mwrender/renderinginterface.hpp"
namespace
@ -54,13 +56,12 @@ namespace MWClass
void Light::insertObjectRendering (const MWWorld::Ptr& ptr, MWRender::RenderingInterface& renderingInterface) const
{
const std::string model = getModel(ptr);
MWWorld::LiveCellRef<ESM::Light> *ref =
ptr.get<ESM::Light>();
// Insert even if model is empty, so that the light is added
renderingInterface.getObjects().insertModel(ptr, model, false, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault));
MWRender::Actors& actors = renderingInterface.getActors();
actors.insertActivator(ptr, !(ref->mBase->mData.mFlags & ESM::Light::OffDefault));
}
void Light::insertObject(const MWWorld::Ptr& ptr, MWWorld::PhysicsSystem& physics) const
@ -78,6 +79,8 @@ namespace MWClass
MWBase::Environment::get().getSoundManager()->playSound3D(ptr, ref->mBase->mSound, 1.0, 1.0,
MWBase::SoundManager::Play_TypeSfx,
MWBase::SoundManager::Play_Loop);
MWBase::Environment::get().getMechanicsManager()->add(ptr);
}
std::string Light::getModel(const MWWorld::Ptr &ptr) const

@ -403,7 +403,7 @@ namespace MWClass
}
// inventory
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "",
data->mInventoryStore.fill(ref->mBase->mInventory, getId(ptr), "", -1,
MWBase::Environment::get().getWorld()->getStore());
data->mNpcStats.setGoldPool(gold);
@ -577,34 +577,7 @@ namespace MWClass
}
else
{
// Note: MCP contains an option to include Strength in hand-to-hand damage
// calculations. Some mods recommend using it, so we may want to include am
// option for it.
float minstrike = store.find("fMinHandToHandMult")->getFloat();
float maxstrike = store.find("fMaxHandToHandMult")->getFloat();
damage = stats.getSkill(weapskill).getModified();
damage *= minstrike + ((maxstrike-minstrike)*stats.getAttackStrength());
healthdmg = (otherstats.getMagicEffects().get(ESM::MagicEffect::Paralyze).getMagnitude() > 0)
|| otherstats.getKnockedDown();
if(stats.isWerewolf())
{
healthdmg = true;
// GLOB instead of GMST because it gets updated during a quest
damage *= world->getGlobalFloat("werewolfclawmult");
}
if(healthdmg)
damage *= store.find("fHandtoHandHealthPer")->getFloat();
MWBase::SoundManager *sndMgr = MWBase::Environment::get().getSoundManager();
if(stats.isWerewolf())
{
const ESM::Sound *sound = world->getStore().get<ESM::Sound>().searchRandom("WolfHit");
if(sound)
sndMgr->playSound3D(victim, sound->mId, 1.0f, 1.0f);
}
else
sndMgr->playSound3D(victim, "Hand To Hand Hit", 1.0f, 1.0f);
MWMechanics::getHandToHandDamage(ptr, victim, damage, healthdmg);
}
if(ptr.getRefData().getHandle() == "player")
{
@ -641,7 +614,7 @@ namespace MWClass
MWMechanics::applyElementalShields(ptr, victim);
if (!weapon.isEmpty() && MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
if (MWMechanics::blockMeleeAttack(ptr, victim, weapon, damage))
damage = 0;
if (healthdmg && damage > 0)
@ -1376,7 +1349,7 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
const ESM::InventoryList& list = ref->mBase->mInventory;
MWWorld::ContainerStore& store = getContainerStore(ptr);
store.restock(list, ptr, ptr.getCellRef().getRefId(), ptr.getCellRef().getFaction());
store.restock(list, ptr, ptr.getCellRef().getRefId(), "", -1);
}
int Npc::getBaseFightRating (const MWWorld::Ptr& ptr) const
@ -1384,4 +1357,9 @@ namespace MWClass
MWWorld::LiveCellRef<ESM::NPC> *ref = ptr.get<ESM::NPC>();
return ref->mBase->mAiData.mFight;
}
bool Npc::isBipedal(const MWWorld::Ptr &ptr) const
{
return true;
}
}

@ -182,6 +182,8 @@ namespace MWClass
return true;
}
virtual bool isBipedal (const MWWorld::Ptr &ptr) const;
virtual void respawn (const MWWorld::Ptr& ptr) const;
virtual void restock (const MWWorld::Ptr& ptr) const;

@ -11,6 +11,7 @@
#include "../mwworld/actiontake.hpp"
#include "../mwworld/actionapply.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/nullaction.hpp"

@ -12,6 +12,7 @@
#include "../mwworld/actionequip.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/physicssystem.hpp"
#include "../mwworld/nullaction.hpp"
@ -385,7 +386,7 @@ namespace MWClass
std::pair<int, std::string> Weapon::canBeEquipped(const MWWorld::Ptr &ptr, const MWWorld::Ptr &npc) const
{
if (ptr.getCellRef().getCharge() == 0)
if (hasItemHealth(ptr) && ptr.getCellRef().getCharge() == 0)
return std::make_pair(0, "#{sInventoryMessage1}");
std::pair<std::vector<int>, bool> slots_ = ptr.getClass().getEquipmentSlots(ptr);

@ -126,7 +126,7 @@ bool MWDialogue::Filter::testPlayer (const ESM::DialInfo& info) const
if (!info.mCell.empty())
{
// supports partial matches, just like getPcCell
const std::string& playerCell = player.getCell()->getCell()->mName;
const std::string& playerCell = MWBase::Environment::get().getWorld()->getCellName(player.getCell());
bool match = playerCell.length()>=info.mCell.length() &&
Misc::StringUtils::ciEqual(playerCell.substr (0, info.mCell.length()), info.mCell);
if (!match)
@ -417,6 +417,21 @@ int MWDialogue::Filter::getSelectStructInteger (const SelectWrapper& select) con
return value;
}
case SelectWrapper::Function_CreatureTargetted:
{
MWWorld::Ptr target;
mActor.getClass().getCreatureStats(mActor).getAiSequence().getCombatTarget(target);
if (target)
{
if (target.getClass().isNpc() && target.getClass().getNpcStats(target).isWerewolf())
return 2;
if (target.getTypeName() == typeid(ESM::Creature).name())
return 1;
}
}
return 0;
default:
throw std::runtime_error ("unknown integer select function");
@ -451,7 +466,8 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
case SelectWrapper::Function_NotCell:
return !Misc::StringUtils::ciEqual(mActor.getCell()->getCell()->mName, select.getName());
return !Misc::StringUtils::ciEqual(MWBase::Environment::get().getWorld()->getCellName(mActor.getCell())
, select.getName());
case SelectWrapper::Function_NotLocal:
{
@ -531,10 +547,6 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
return MWBase::Environment::get().getMechanicsManager()->isAggressive(mActor,
MWBase::Environment::get().getWorld()->getPlayerPtr());
case SelectWrapper::Function_CreatureTargetted:
return mActor.getClass().getCreatureStats (mActor).getCreatureTargetted();
case SelectWrapper::Function_Werewolf:
return mActor.getClass().getNpcStats (mActor).isWerewolf();

@ -205,6 +205,7 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
Function_Reputation, Function_FactionRankDiff,
Function_WerewolfKills,
Function_RankLow, Function_RankHigh,
Function_CreatureTargetted,
Function_None // end marker
};
@ -225,7 +226,6 @@ MWDialogue::SelectWrapper::Type MWDialogue::SelectWrapper::getType() const
Function_PcVampire, Function_TalkedToPc,
Function_Alarmed, Function_Detected,
Function_Attacked, Function_ShouldAttack,
Function_CreatureTargetted,
Function_Werewolf,
Function_None // end marker
};

@ -11,6 +11,9 @@
#include "../mwmechanics/magiceffects.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include <components/esm/records.hpp>
#include "inventoryitemmodel.hpp"
#include "sortfilteritemmodel.hpp"

@ -2,11 +2,13 @@
#include <boost/lexical_cast.hpp>
#include <components/esm/records.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "widgets.hpp"
@ -80,8 +82,6 @@ namespace MWGui
if (Misc::StringUtils::ciEqual(*mBirthList->getItemDataAt<std::string>(i), birthId))
{
mBirthList->setIndexSelected(i);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
break;
}
}
@ -116,9 +116,6 @@ namespace MWGui
if (_index == MyGUI::ITEM_NONE)
return;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
const std::string *birthId = mBirthList->getItemDataAt<std::string>(_index);
if (Misc::StringUtils::ciEqual(mCurrentBirthId, *birthId))
return;

@ -639,7 +639,8 @@ namespace
MyGUI::Vertex* vertices, RenderXform const & renderXform) :
mZ(Z), mOrigin (left, top),
mFont (font), mVertices (vertices),
mRenderXform (renderXform)
mRenderXform (renderXform),
mC(0)
{
mVertexColourType = MyGUI::RenderManager::getInstance().getVertexFormat();
}

@ -13,6 +13,7 @@
#include "../mwmechanics/npcstats.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/fallback.hpp"
#include "../mwworld/esmstore.hpp"
namespace
{

@ -3,12 +3,23 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "tooltips.hpp"
#undef min
#undef max
namespace
{
bool sortClasses(const std::pair<std::string, std::string>& left, const std::pair<std::string, std::string>& right)
{
return left.second.compare(right.second) < 0;
}
}
namespace MWGui
{
@ -129,8 +140,6 @@ namespace MWGui
if (Misc::StringUtils::ciEqual(*mClassList->getItemDataAt<std::string>(i), classId))
{
mClassList->setIndexSelected(i);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
break;
}
}
@ -165,9 +174,6 @@ namespace MWGui
if (_index == MyGUI::ITEM_NONE)
return;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
const std::string *classId = mClassList->getItemDataAt<std::string>(_index);
if (Misc::StringUtils::ciEqual(mCurrentClassId, *classId))
return;
@ -184,7 +190,7 @@ namespace MWGui
const MWWorld::ESMStore &store = MWBase::Environment::get().getWorld()->getStore();
int index = 0;
std::vector<std::pair<std::string, std::string> > items; // class id, class name
MWWorld::Store<ESM::Class>::iterator it = store.get<ESM::Class>().begin();
for (; it != store.get<ESM::Class>().end(); ++it)
{
@ -192,8 +198,15 @@ namespace MWGui
if (!playable) // Only display playable classes
continue;
const std::string &id = it->mId;
mClassList->addItem(it->mName, id);
items.push_back(std::make_pair(it->mId, it->mName));
}
std::sort(items.begin(), items.end(), sortClasses);
int index = 0;
for (std::vector<std::pair<std::string, std::string> >::const_iterator it = items.begin(); it != items.end(); ++it)
{
const std::string &id = it->first;
mClassList->addItem(it->second, id);
if (mCurrentClassId.empty())
{
mCurrentClassId = id;
@ -770,6 +783,7 @@ namespace MWGui
SelectSkillDialog::SelectSkillDialog()
: WindowModal("openmw_chargen_select_skill.layout")
, mSkillId(ESM::Skill::Block)
{
// Centre dialog
center();

@ -1,7 +1,8 @@
#ifndef MWGUI_CLASS_H
#define MWGUI_CLASS_H
#include <components/esm/attr.hpp>
#include <components/esm/loadclas.hpp>
#include "widgets.hpp"
#include "windowbase.hpp"

@ -10,6 +10,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/esmstore.hpp"
@ -101,8 +102,20 @@ namespace MWGui
it->second->listIdentifier (mNames);
}
// exterior cell names aren't technically identifiers, but since the COC function accepts them,
// we should list them too
for (MWWorld::Store<ESM::Cell>::iterator it = store.get<ESM::Cell>().extBegin();
it != store.get<ESM::Cell>().extEnd(); ++it)
{
if (!it->mName.empty())
mNames.push_back(it->mName);
}
// sort
std::sort (mNames.begin(), mNames.end());
// remove duplicates
mNames.erase( std::unique( mNames.begin(), mNames.end() ), mNames.end() );
}
}

@ -301,10 +301,9 @@ namespace MWGui
MWMechanics::Pickpocket pickpocket(player, mPtr);
if (pickpocket.finish())
{
MWBase::Environment::get().getMechanicsManager()->reportCrime(
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket);
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, mPtr, MWBase::MechanicsManager::OT_Pickpocket, 0, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
mPickpocketDetected = true;
return;
}
@ -384,10 +383,9 @@ namespace MWGui
if (pickpocket.pick(item.mBase, count))
{
int value = item.mBase.getClass().getValue(item.mBase) * count;
MWBase::Environment::get().getMechanicsManager()->reportCrime(
player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value);
MWBase::Environment::get().getMechanicsManager()->commitCrime(
player, MWWorld::Ptr(), MWBase::MechanicsManager::OT_Theft, value, true);
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_Container);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
mPickpocketDetected = true;
return false;
}

@ -15,6 +15,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwdialogue/dialoguemanagerimp.hpp"
@ -415,20 +416,11 @@ namespace MWGui
MWMechanics::CreatureStats &sellerStats = mPtr.getClass().getCreatureStats(mPtr);
float delay = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fBarterGoldResetDelay")->getFloat();
// Gold is restocked every 24h
if (MWBase::Environment::get().getWorld()->getTimeStamp() >= sellerStats.getLastRestockTime() + delay)
{
sellerStats.setGoldPool(mPtr.getClass().getBaseGold(mPtr));
mPtr.getClass().restock(mPtr);
// Also restock any containers owned by this merchant, which are also available to buy in the trade window
std::vector<MWWorld::Ptr> itemSources;
MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources);
for (std::vector<MWWorld::Ptr>::iterator it = itemSources.begin(); it != itemSources.end(); ++it)
{
it->getClass().restock(*it);
}
sellerStats.setLastRestockTime(MWBase::Environment::get().getWorld()->getTimeStamp());
}
}

@ -3,6 +3,7 @@
#include <iomanip>
#include <boost/lexical_cast.hpp>
#include <components/esm/records.hpp>
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
@ -11,6 +12,7 @@
#include "../mwbase/mechanicsmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "itemselection.hpp"
#include "container.hpp"
@ -109,7 +111,7 @@ namespace MWGui
mCharge->setCaption(boost::lexical_cast<std::string>(mEnchanting.getGemCharge()));
std::stringstream castCost;
castCost << mEnchanting.getCastCost();
castCost << mEnchanting.getEffectiveCastCost();
mCastCost->setCaption(castCost.str());
mPrice->setCaption(boost::lexical_cast<std::string>(mEnchanting.getEnchantPrice()));
@ -118,19 +120,19 @@ namespace MWGui
{
case ESM::Enchantment::CastOnce:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastOnce","Cast Once"));
mAddEffectDialog.constantEffect=false;
setConstantEffect(false);
break;
case ESM::Enchantment::WhenStrikes:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenStrikes", "When Strikes"));
mAddEffectDialog.constantEffect=false;
setConstantEffect(false);
break;
case ESM::Enchantment::WhenUsed:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastWhenUsed", "When Used"));
mAddEffectDialog.constantEffect=false;
setConstantEffect(false);
break;
case ESM::Enchantment::ConstantEffect:
mTypeButton->setCaption(MWBase::Environment::get().getWindowManager()->getGameSettingString("sItemCastConstant", "Cast Constant"));
mAddEffectDialog.constantEffect=true;
setConstantEffect(true);
break;
}
}
@ -281,6 +283,7 @@ namespace MWGui
{
mEnchanting.nextCastStyle();
updateLabels();
updateEffectsView();
}
void EnchantingDialog::onBuyButtonClicked(MyGUI::Widget* sender)
@ -338,9 +341,8 @@ namespace MWGui
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, item.getClass().getName(item));
MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item));
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
item.getClass().getValue(item), true);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_Enchanting);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return;

@ -7,8 +7,10 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/soundmanager.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/world.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"
@ -162,7 +164,7 @@ namespace MWGui
getWidget(mTriangleCounter, "TriangleCounter");
getWidget(mBatchCounter, "BatchCounter");
LocalMapBase::init(mMinimap, mCompass);
LocalMapBase::init(mMinimap, mCompass, Settings::Manager::getInt("local map hud widget size", "Map"));
mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);
mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);
@ -618,6 +620,11 @@ namespace MWGui
// Health is usually cast to int before displaying. Actors die whenever they are < 1 health.
// Therefore any value < 1 should show as an empty health bar. We do the same in statswindow :)
mEnemyHealth->setProgressPosition(int(stats.getHealth().getCurrent()) / stats.getHealth().getModified() * 100);
static const float fNPCHealthBarFade = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarFade")->getFloat();
if (fNPCHealthBarFade > 0.f)
mEnemyHealth->setAlpha(std::max(0.f, std::min(1.f, mEnemyHealthTimer/fNPCHealthBarFade)));
}
void HUD::update()
@ -639,7 +646,7 @@ namespace MWGui
void HUD::setEnemy(const MWWorld::Ptr &enemy)
{
mEnemyActorId = enemy.getClass().getCreatureStats(enemy).getActorId();
mEnemyHealthTimer = 5;
mEnemyHealthTimer = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fNPCHealthBarTime")->getFloat();
if (!mEnemyHealth->getVisible())
mWeaponSpellBox->setPosition(mWeaponSpellBox->getPosition() - MyGUI::IntPoint(0,20));
mEnemyHealth->setVisible(true);

@ -56,11 +56,11 @@ void ItemView::layoutWidgets()
int x = 0;
int y = 0;
MyGUI::Widget* dragArea = mScrollView->getChildAt(0);
int maxHeight = dragArea->getHeight();
int maxHeight = mScrollView->getHeight();
int rows = maxHeight/42;
rows = std::max(rows, 1);
bool showScrollbar = std::ceil(dragArea->getChildCount()/float(rows)) > mScrollView->getWidth()/42;
bool showScrollbar = int(std::ceil(dragArea->getChildCount()/float(rows))) > mScrollView->getWidth()/42;
if (showScrollbar)
maxHeight -= 18;

@ -34,6 +34,7 @@ namespace MWGui
getWidget(mLoadingText, "LoadingText");
getWidget(mProgressBar, "ProgressBar");
getWidget(mLoadingBox, "LoadingBox");
mProgressBar->setScrollViewPage(1);
@ -46,6 +47,11 @@ namespace MWGui
void LoadingScreen::setLabel(const std::string &label)
{
mLoadingText->setCaptionWithReplacing(label);
int padding = mLoadingBox->getWidth() - mLoadingText->getWidth();
MyGUI::IntSize size(mLoadingText->getTextSize().width+padding, mLoadingBox->getHeight());
size.width = std::max(300, size.width);
mLoadingBox->setSize(size);
mLoadingBox->setPosition(mMainWidget->getWidth()/2 - mLoadingBox->getWidth()/2, mLoadingBox->getTop());
}
LoadingScreen::~LoadingScreen()

@ -47,6 +47,8 @@ namespace MWGui
size_t mProgress;
MyGUI::Widget* mLoadingBox;
MyGUI::TextBox* mLoadingText;
MyGUI::ScrollBar* mProgressBar;
BackgroundImage* mBackgroundImage;

@ -176,13 +176,16 @@ namespace MWGui
int screenHeight = viewSize.height;
mVideoBackground->setSize(screenWidth, screenHeight);
double imageaspect = static_cast<double>(mVideo->getVideoWidth())/mVideo->getVideoHeight();
if (mVideo->getVideoHeight() > 0)
{
double imageaspect = static_cast<double>(mVideo->getVideoWidth())/mVideo->getVideoHeight();
int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2);
int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2);
int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2);
int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2);
mVideo->setCoord(leftPadding, topPadding,
screenWidth - leftPadding*2, screenHeight - topPadding*2);
mVideo->setCoord(leftPadding, topPadding,
screenWidth - leftPadding*2, screenHeight - topPadding*2);
}
mVideo->setVisible(true);
}

@ -11,6 +11,7 @@
#include "../mwworld/player.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwrender/globalmap.hpp"
@ -22,8 +23,6 @@
namespace
{
const int widgetSize = 512;
const int cellSize = 8192;
enum LocalMapWidgetDepth
@ -164,6 +163,7 @@ namespace MWGui
, mCompass(NULL)
, mMarkerUpdateTimer(0.0f)
, mCustomMarkers(markers)
, mMapWidgetSize(0)
{
mCustomMarkers.eventMarkersChanged += MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers);
}
@ -173,26 +173,28 @@ namespace MWGui
mCustomMarkers.eventMarkersChanged -= MyGUI::newDelegate(this, &LocalMapBase::updateCustomMarkers);
}
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass)
void LocalMapBase::init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize)
{
mLocalMap = widget;
mCompass = compass;
mMapWidgetSize = mapWidgetSize;
mLocalMap->setCanvasSize(mMapWidgetSize*3, mMapWidgetSize*3);
mCompass->setDepth(Local_CompassLayer);
mCompass->setNeedMouseFocus(false);
// create 3x3 map widgets, 512x512 each, holding a 1024x1024 texture each
for (int mx=0; mx<3; ++mx)
{
for (int my=0; my<3; ++my)
{
MyGUI::ImageBox* map = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize),
MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize),
MyGUI::Align::Top | MyGUI::Align::Left);
map->setDepth(Local_MapLayer);
MyGUI::ImageBox* fog = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(mx*widgetSize, my*widgetSize, widgetSize, widgetSize),
MyGUI::IntCoord(mx*mMapWidgetSize, my*mMapWidgetSize, mMapWidgetSize, mMapWidgetSize),
MyGUI::Align::Top | MyGUI::Align::Left);
fog->setDepth(Local_FogLayer);
@ -258,8 +260,8 @@ namespace MWGui
markerPos.cellX = cellX;
markerPos.cellY = cellY;
widgetPos = MyGUI::IntPoint(nX * widgetSize + (1+cellDx) * widgetSize,
nY * widgetSize - (cellDy-1) * widgetSize);
widgetPos = MyGUI::IntPoint(nX * mMapWidgetSize + (1+cellDx) * mMapWidgetSize,
nY * mMapWidgetSize - (cellDy-1) * mMapWidgetSize);
}
else
{
@ -271,8 +273,8 @@ namespace MWGui
markerPos.cellY = cellY;
// Image space is -Y up, cells are Y up
widgetPos = MyGUI::IntPoint(nX * widgetSize + (1+(cellX-mCurX)) * widgetSize,
nY * widgetSize + (1-(cellY-mCurY)) * widgetSize);
widgetPos = MyGUI::IntPoint(nX * mMapWidgetSize + (1+(cellX-mCurX)) * mMapWidgetSize,
nY * mMapWidgetSize + (1-(cellY-mCurY)) * mMapWidgetSize);
}
markerPos.nX = nX;
@ -425,9 +427,9 @@ namespace MWGui
void LocalMapBase::setPlayerPos(int cellX, int cellY, const float nx, const float ny)
{
MyGUI::IntPoint pos(widgetSize+nx*widgetSize-16, widgetSize+ny*widgetSize-16);
pos.left += (cellX - mCurX) * widgetSize;
pos.top -= (cellY - mCurY) * widgetSize;
MyGUI::IntPoint pos(mMapWidgetSize+nx*mMapWidgetSize-16, mMapWidgetSize+ny*mMapWidgetSize-16);
pos.left += (cellX - mCurX) * mMapWidgetSize;
pos.top -= (cellY - mCurY) * mMapWidgetSize;
if (pos != mCompass->getPosition())
{
@ -612,8 +614,7 @@ namespace MWGui
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
LocalMapBase::init(mLocalMap, mPlayerArrowLocal);
}
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, Settings::Manager::getInt("local map widget size", "Map")); }
void MapWindow::onNoteEditOk()
{
@ -657,10 +658,10 @@ namespace MWGui
MyGUI::IntPoint clickedPos = MyGUI::InputManager::getInstance().getMousePosition();
MyGUI::IntPoint widgetPos = clickedPos - mEventBoxLocal->getAbsolutePosition();
int x = int(widgetPos.left/float(widgetSize))-1;
int y = (int(widgetPos.top/float(widgetSize))-1)*-1;
float nX = widgetPos.left/float(widgetSize) - int(widgetPos.left/float(widgetSize));
float nY = widgetPos.top/float(widgetSize) - int(widgetPos.top/float(widgetSize));
int x = int(widgetPos.left/float(mMapWidgetSize))-1;
int y = (int(widgetPos.top/float(mMapWidgetSize))-1)*-1;
float nX = widgetPos.left/float(mMapWidgetSize) - int(widgetPos.left/float(mMapWidgetSize));
float nY = widgetPos.top/float(mMapWidgetSize) - int(widgetPos.top/float(mMapWidgetSize));
x += mCurX;
y += mCurY;

@ -70,7 +70,7 @@ namespace MWGui
public:
LocalMapBase(CustomMarkerCollection& markers);
virtual ~LocalMapBase();
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass);
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int mapWidgetSize);
void setCellPrefix(const std::string& prefix);
void setActiveCell(const int x, const int y, bool interior=false);
@ -99,6 +99,8 @@ namespace MWGui
bool mChanged;
bool mFogOfWar;
int mMapWidgetSize;
// Stores markers that were placed by a player. May be shared between multiple map views.
CustomMarkerCollection& mCustomMarkers;

@ -8,6 +8,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/player.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"

@ -3,6 +3,7 @@
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include "../mwworld/esmstore.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
@ -20,6 +21,12 @@ namespace
else
return index;
}
bool sortRaces(const std::pair<std::string, std::string>& left, const std::pair<std::string, std::string>& right)
{
return left.second.compare(right.second) < 0;
}
}
namespace MWGui
@ -122,7 +129,7 @@ namespace MWGui
mPreview.reset(new MWRender::RaceSelectionPreview());
mPreview->setup();
mPreview->update (0);
mPreview->update (mCurrentAngle);
const ESM::NPC proto = mPreview->getPrototype();
setRaceId(proto.mRace);
@ -143,8 +150,11 @@ namespace MWGui
mPreviewImage->setImageTexture (textureName);
mPreviewDirty = true;
}
size_t initialPos = mHeadRotate->getScrollRange()/2+mHeadRotate->getScrollRange()/10;
mHeadRotate->setScrollPosition(initialPos);
onHeadRotate(mHeadRotate, initialPos);
}
void RaceDialog::setRaceId(const std::string &raceId)
{
@ -156,8 +166,6 @@ namespace MWGui
if (Misc::StringUtils::ciEqual(*mRaceList->getItemDataAt<std::string>(i), raceId))
{
mRaceList->setIndexSelected(i);
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
break;
}
}
@ -191,10 +199,9 @@ namespace MWGui
void RaceDialog::onHeadRotate(MyGUI::ScrollBar* scroll, size_t _position)
{
float angle = (float(_position) / (scroll->getScrollRange()-1) - 0.5) * 3.14 * 2;
float diff = angle - mCurrentAngle;
mPreview->update (diff);
mPreview->update (angle);
mPreviewDirty = true;
mCurrentAngle += diff;
mCurrentAngle = angle;
}
void RaceDialog::onSelectPreviousGender(MyGUI::Widget*)
@ -242,8 +249,6 @@ namespace MWGui
if (_index == MyGUI::ITEM_NONE)
return;
MyGUI::Button* okButton;
getWidget(okButton, "OKButton");
const std::string *raceId = mRaceList->getItemDataAt<std::string>(_index);
if (Misc::StringUtils::ciEqual(mCurrentRaceId, *raceId))
return;
@ -345,8 +350,7 @@ namespace MWGui
const MWWorld::Store<ESM::Race> &races =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>();
int index = 0;
std::vector<std::pair<std::string, std::string> > items; // ID, name
MWWorld::Store<ESM::Race>::iterator it = races.begin();
for (; it != races.end(); ++it)
{
@ -354,8 +358,15 @@ namespace MWGui
if (!playable) // Only display playable races
continue;
mRaceList->addItem(it->mName, it->mId);
if (Misc::StringUtils::ciEqual(it->mId, mCurrentRaceId))
items.push_back(std::make_pair(it->mId, it->mName));
}
std::sort(items.begin(), items.end(), sortRaces);
int index = 0;
for (std::vector<std::pair<std::string, std::string> >::const_iterator it = items.begin(); it != items.end(); ++it)
{
mRaceList->addItem(it->second, it->first);
if (Misc::StringUtils::ciEqual(it->first, mCurrentRaceId))
mRaceList->setIndexSelected(index);
++index;
}

@ -3,12 +3,15 @@
#include <boost/lexical_cast.hpp>
#include <boost/format.hpp>
#include <components/esm/records.hpp>
#include "../mwbase/world.hpp"
#include "../mwbase/environment.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"

@ -5,6 +5,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "tooltips.hpp"

@ -1,6 +1,8 @@
#ifndef MWGUI_REVIEW_H
#define MWGUI_REVIEW_H
#include <components/esm/attr.hpp>
#include <components/esm/loadclas.hpp>
#include "windowbase.hpp"
#include "widgets.hpp"

@ -12,6 +12,7 @@
#include "../mwbase/environment.hpp"
#include "../mwbase/world.hpp"
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwstate/character.hpp"
@ -245,7 +246,7 @@ namespace MWGui
else
{
assert (mCurrentCharacter && mCurrentSlot);
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot);
MWBase::Environment::get().getStateManager()->loadGame (mCurrentCharacter, mCurrentSlot->mPath.string());
}
}
@ -304,7 +305,7 @@ namespace MWGui
mOkButton->setEnabled(pos != MyGUI::ITEM_NONE || mSaving);
mDeleteButton->setEnabled(pos != MyGUI::ITEM_NONE);
if (pos == MyGUI::ITEM_NONE)
if (pos == MyGUI::ITEM_NONE || !mCurrentCharacter)
{
mCurrentSlot = NULL;
mInfoText->setCaption("");

@ -166,6 +166,7 @@ namespace MWGui
getWidget(mResolutionList, "ResolutionList");
getWidget(mFullscreenButton, "FullscreenButton");
getWidget(mVSyncButton, "VSyncButton");
getWidget(mWindowBorderButton, "WindowBorderButton");
getWidget(mFPSButton, "FPSButton");
getWidget(mFOVSlider, "FOVSlider");
getWidget(mAnisotropySlider, "AnisotropySlider");
@ -181,6 +182,20 @@ namespace MWGui
getWidget(mRefractionButton, "RefractionButton");
getWidget(mDifficultySlider, "DifficultySlider");
#ifndef WIN32
// hide gamma controls since it currently does not work under Linux
MyGUI::ScrollBar *gammaSlider;
getWidget(gammaSlider, "GammaSlider");
gammaSlider->setVisible(false);
MyGUI::TextBox *textBox;
getWidget(textBox, "GammaText");
textBox->setVisible(false);
getWidget(textBox, "GammaTextDark");
textBox->setVisible(false);
getWidget(textBox, "GammaTextLight");
textBox->setVisible(false);
#endif
mMainWidget->castType<MyGUI::Window>()->eventWindowChangeCoord += MyGUI::newDelegate(this, &SettingsWindow::onWindowResize);
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &SettingsWindow::onOkButtonClicked);
@ -239,6 +254,8 @@ namespace MWGui
MyGUI::TextBox* diffText;
getWidget(diffText, "DifficultyText");
diffText->setCaptionWithReplacing("#{sDifficulty} (" + boost::lexical_cast<std::string>(int(Settings::Manager::getInt("difficulty", "Game"))) + ")");
mWindowBorderButton->setEnabled(!Settings::Manager::getBool("fullscreen", "Video"));
}
void SettingsWindow::onOkButtonClicked(MyGUI::Widget* _sender)
@ -354,6 +371,8 @@ namespace MWGui
_sender->castType<MyGUI::Button>()->setCaption(off);
return;
}
mWindowBorderButton->setEnabled(!newState);
}
if (getSettingType(_sender) == checkButtonType)

@ -28,6 +28,7 @@ namespace MWGui
MyGUI::ListBox* mResolutionList;
MyGUI::Button* mFullscreenButton;
MyGUI::Button* mVSyncButton;
MyGUI::Button* mWindowBorderButton;
MyGUI::Button* mFPSButton;
MyGUI::ScrollBar* mFOVSlider;
MyGUI::ScrollBar* mDifficultySlider;

@ -1,5 +1,7 @@
#include "sortfilteritemmodel.hpp"
#include <components/misc/stringops.hpp>
#include <components/esm/loadalch.hpp>
#include <components/esm/loadappa.hpp>
#include <components/esm/loadarmo.hpp>
@ -52,9 +54,10 @@ namespace
if (left.mBase.getTypeName() == right.mBase.getTypeName())
{
int cmp = left.mBase.getClass().getName(left.mBase).compare(
right.mBase.getClass().getName(right.mBase));
return cmp < 0;
std::string leftName = Misc::StringUtils::lowerCase(left.mBase.getClass().getName(left.mBase));
std::string rightName = Misc::StringUtils::lowerCase(right.mBase.getClass().getName(right.mBase));
return leftName.compare(rightName) < 0;
}
else
return compareType(left.mBase.getTypeName(), right.mBase.getTypeName());

@ -3,6 +3,7 @@
#include <boost/lexical_cast.hpp>
#include <components/misc/resourcehelpers.hpp>
#include <components/esm/records.hpp>
#include "../mwbase/windowmanager.hpp"
#include "../mwbase/soundmanager.hpp"
@ -12,6 +13,7 @@
#include "../mwworld/containerstore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spells.hpp"
@ -40,6 +42,7 @@ namespace MWGui
: WindowModal("openmw_edit_effect.layout")
, mEditing(false)
, mMagicEffect(NULL)
, mConstantEffect(false)
{
getWidget(mCancelButton, "CancelButton");
getWidget(mOkButton, "OkButton");
@ -69,7 +72,11 @@ namespace MWGui
mMagnitudeMaxSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onMagnitudeMaxChanged);
mDurationSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onDurationChanged);
mAreaSlider->eventScrollChangePosition += MyGUI::newDelegate(this, &EditEffectDialog::onAreaChanged);
constantEffect=false;
}
void EditEffectDialog::setConstantEffect(bool constant)
{
mConstantEffect = constant;
}
void EditEffectDialog::open()
@ -90,8 +97,8 @@ namespace MWGui
void EditEffectDialog::newEffect (const ESM::MagicEffect *effect)
{
bool allowSelf = effect->mData.mFlags & ESM::MagicEffect::CastSelf;
bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !constantEffect;
bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !constantEffect;
bool allowTouch = (effect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect;
bool allowTarget = (effect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect;
if (!allowSelf && !allowTouch && !allowTarget)
return; // TODO: Show an error message popup?
@ -181,7 +188,7 @@ namespace MWGui
mMagnitudeBox->setVisible (true);
curY += mMagnitudeBox->getSize().height;
}
if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&constantEffect==false)
if (!(mMagicEffect->mData.mFlags & ESM::MagicEffect::NoDuration)&&mConstantEffect==false)
{
mDurationBox->setPosition(mDurationBox->getPosition().left, curY);
mDurationBox->setVisible (true);
@ -202,8 +209,8 @@ namespace MWGui
// cycle through range types until we find something that's allowed
// does not handle the case where nothing is allowed (this should be prevented before opening the Add Effect dialog)
bool allowSelf = mMagicEffect->mData.mFlags & ESM::MagicEffect::CastSelf;
bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !constantEffect;
bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !constantEffect;
bool allowTouch = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTouch) && !mConstantEffect;
bool allowTarget = (mMagicEffect->mData.mFlags & ESM::MagicEffect::CastTarget) && !mConstantEffect;
if (mEffect.mRange == ESM::RT_Self && !allowSelf)
mEffect.mRange = (mEffect.mRange+1)%3;
if (mEffect.mRange == ESM::RT_Touch && !allowTouch)
@ -371,7 +378,7 @@ namespace MWGui
MWMechanics::CreatureStats& npcStats = mPtr.getClass().getCreatureStats(mPtr);
npcStats.setGoldPool(npcStats.getGoldPool() + price);
MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0);
MWBase::Environment::get().getSoundManager()->playSound ("Mysticism Hit", 1.0, 1.0);
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->createRecord(mSpell);
@ -379,8 +386,6 @@ namespace MWGui
MWMechanics::Spells& spells = stats.getSpells();
spells.add (spell->mId);
MWBase::Environment::get().getSoundManager()->playSound ("Item Gold Up", 1.0, 1.0);
MWBase::Environment::get().getWindowManager()->removeGuiMode (GM_SpellCreation);
}
@ -468,6 +473,7 @@ namespace MWGui
, mSelectedEffect(0)
, mSelectedKnownEffectId(0)
, mType(type)
, mConstantEffect(false)
{
mAddEffectDialog.eventEffectAdded += MyGUI::newDelegate(this, &EffectEditorBase::onEffectAdded);
mAddEffectDialog.eventEffectModified += MyGUI::newDelegate(this, &EffectEditorBase::onEffectModified);
@ -592,14 +598,6 @@ namespace MWGui
int buttonId = *sender->getUserData<int>();
mSelectedKnownEffectId = mButtonMapping[buttonId];
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
if (it->mEffectID == mSelectedKnownEffectId)
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}");
return;
}
}
const ESM::MagicEffect* effect =
MWBase::Environment::get().getWorld()->getStore().get<ESM::MagicEffect>().find(mSelectedKnownEffectId);
@ -622,6 +620,15 @@ namespace MWGui
}
else
{
for (std::vector<ESM::ENAMstruct>::const_iterator it = mEffects.begin(); it != mEffects.end(); ++it)
{
if (it->mEffectID == mSelectedKnownEffectId)
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sOnetypeEffectMessage}");
return;
}
}
mAddEffectDialog.newEffect(effect);
}
}
@ -658,6 +665,7 @@ namespace MWGui
params.mMagnMax = it->mMagnMax;
params.mRange = it->mRange;
params.mArea = it->mArea;
params.mIsConstant = mConstantEffect;
MyGUI::Button* button = mUsedEffectsView->createWidget<MyGUI::Button>("", MyGUI::IntCoord(0, size.height, 0, 24), MyGUI::Align::Default);
button->setUserData(i);
@ -702,4 +710,10 @@ namespace MWGui
mAddEffectDialog.editEffect (mEffects[id]);
mAddEffectDialog.setVisible (true);
}
void EffectEditorBase::setConstantEffect(bool constant)
{
mAddEffectDialog.setConstantEffect(constant);
mConstantEffect = constant;
}
}

@ -1,6 +1,8 @@
#ifndef MWGUI_SPELLCREATION_H
#define MWGUI_SPELLCREATION_H
#include <components/esm/loadmgef.hpp>
#include <components/esm/loadspel.hpp>
#include <components/widgets/list.hpp>
#include "windowbase.hpp"
@ -21,12 +23,13 @@ namespace MWGui
virtual void open();
virtual void exit();
void setConstantEffect(bool constant);
void setSkill(int skill);
void setAttribute(int attribute);
void newEffect (const ESM::MagicEffect* effect);
void editEffect (ESM::ENAMstruct effect);
bool constantEffect;
typedef MyGUI::delegates::CMultiDelegate1<ESM::ENAMstruct> EventHandle_Effect;
EventHandle_Effect eventEffectAdded;
@ -80,6 +83,8 @@ namespace MWGui
ESM::ENAMstruct mOldEffect;
const ESM::MagicEffect* mMagicEffect;
bool mConstantEffect;
};
@ -95,6 +100,8 @@ namespace MWGui
EffectEditorBase(Type type);
virtual ~EffectEditorBase();
void setConstantEffect(bool constant);
protected:
std::map<int, short> mButtonMapping; // maps button ID to effect ID
@ -108,6 +115,8 @@ namespace MWGui
int mSelectedEffect;
short mSelectedKnownEffectId;
bool mConstantEffect;
std::vector<ESM::ENAMstruct> mEffects;
void onEffectAdded(ESM::ENAMstruct effect);

@ -5,6 +5,7 @@
#include <sstream>
#include <iomanip>
#include <components/esm/loadmgef.hpp>
#include <components/misc/resourcehelpers.hpp>
#include "../mwbase/world.hpp"
@ -12,6 +13,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwworld/inventorystore.hpp"
#include "../mwmechanics/creaturestats.hpp"
@ -23,8 +25,8 @@ namespace MWGui
{
void EffectSourceVisitor::visit (MWMechanics::EffectKey key,
const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime)
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime, float totalTime)
{
MagicEffectInfo newEffectSource;
newEffectSource.mKey = key;
@ -32,6 +34,7 @@ namespace MWGui
newEffectSource.mPermanent = mIsPermanent;
newEffectSource.mRemainingTime = remainingTime;
newEffectSource.mSource = sourceName;
newEffectSource.mTotalTime = totalTime;
mEffectSources[key.mId].push_back(newEffectSource);
}
@ -67,10 +70,11 @@ namespace MWGui
MWBase::Environment::get().getWorld ()->getStore ().get<ESM::MagicEffect>().find(it->first);
float remainingDuration = 0;
float totalDuration = 0;
std::string sourcesDescription;
const float fadeTime = 5.f;
static const float fadeTime = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("fMagicStartIconBlink")->getFloat();
for (std::vector<MagicEffectInfo>::const_iterator effectIt = it->second.begin();
effectIt != it->second.end(); ++effectIt)
@ -80,9 +84,15 @@ namespace MWGui
// if at least one of the effect sources is permanent, the effect will never wear off
if (effectIt->mPermanent)
{
remainingDuration = fadeTime;
totalDuration = fadeTime;
}
else
{
remainingDuration = std::max(remainingDuration, effectIt->mRemainingTime);
totalDuration = std::max(totalDuration, effectIt->mTotalTime);
}
sourcesDescription += effectIt->mSource;
@ -158,8 +168,9 @@ namespace MWGui
ToolTipInfo* tooltipInfo = image->getUserData<ToolTipInfo>();
tooltipInfo->text = sourcesDescription;
// Fade out during the last 5 seconds
image->setAlpha(std::min(remainingDuration/fadeTime, 1.f));
// Fade out
if (totalDuration >= fadeTime && fadeTime > 0.f)
image->setAlpha(std::min(remainingDuration/fadeTime, 1.f));
}
else if (mWidgetMap.find(it->first) != mWidgetMap.end())
{

@ -26,12 +26,14 @@ namespace MWGui
MagicEffectInfo()
: mPermanent(false)
, mMagnitude(0)
, mRemainingTime(0)
, mRemainingTime(0.f)
, mTotalTime(0.f)
{}
std::string mSource; // display name for effect source (e.g. potion name)
MWMechanics::EffectKey mKey;
int mMagnitude;
float mRemainingTime;
float mTotalTime;
bool mPermanent; // the effect is permanent
};
@ -45,8 +47,8 @@ namespace MWGui
virtual ~EffectSourceVisitor() {}
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime = -1);
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1);
};
class SpellIcons

@ -21,8 +21,10 @@ namespace
if (left.mType != right.mType)
return left.mType < right.mType;
int cmp = left.mName.compare(right.mName);
return cmp < 0;
std::string leftName = Misc::StringUtils::lowerCase(left.mName);
std::string rightName = Misc::StringUtils::lowerCase(right.mName);
return leftName.compare(rightName) < 0;
}
}
@ -101,9 +103,7 @@ namespace MWGui
&& item.getClass().canBeEquipped(item, mActor).first == 0)
continue;
float enchantCost = enchant->mData.mCost;
int eSkill = mActor.getClass().getSkill(mActor, ESM::Skill::Enchant);
int castCost = std::max(1.f, enchantCost - (enchantCost / 100) * (eSkill - 10));
int castCost = MWMechanics::getEffectiveEnchantmentCastCost(enchant->mData.mCost, mActor);
std::string cost = boost::lexical_cast<std::string>(castCost);
int currentCharge = int(item.getCellRef().getEnchantmentCharge());

@ -26,6 +26,7 @@ namespace MWGui
Spell()
: mSelected(false)
, mActive(false)
, mType(Type_Spell)
{
}
};

@ -8,6 +8,7 @@
#include "../mwworld/inventorystore.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "../mwmechanics/spells.hpp"

@ -342,10 +342,14 @@ namespace MWGui
{
MyGUI::TextBox* skillNameWidget;
skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>("SandText", coord1 + MyGUI::IntSize(coord2.width, 0), MyGUI::Align::Default);
skillNameWidget = mSkillView->createWidget<MyGUI::TextBox>("SandText", coord1, MyGUI::Align::Default);
skillNameWidget->setCaption(text);
skillNameWidget->eventMouseWheel += MyGUI::newDelegate(this, &StatsWindow::onMouseWheel);
int textWidth = skillNameWidget->getTextSize().width;
skillNameWidget->setSize(textWidth, skillNameWidget->getHeight());
mSkillWidgets.push_back(skillNameWidget);
coord1.top += sLineHeight;

@ -61,13 +61,7 @@ namespace MWGui
void TextInputDialog::onTextAccepted(MyGUI::Edit* _sender)
{
if (mTextEdit->getCaption() == "")
{
MWBase::Environment::get().getWindowManager()->messageBox ("#{sNotifyMessage37}");
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget (mTextEdit);
}
else
eventDone(this);
onOkClicked(_sender);
}
}

@ -11,6 +11,7 @@
#include "../mwbase/windowmanager.hpp"
#include "../mwworld/class.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/spellcasting.hpp"
#include "mapwindow.hpp"

@ -7,6 +7,12 @@
#include "widgets.hpp"
namespace ESM
{
class Class;
struct Race;
}
namespace MWGui
{
// Info about tooltip that is supplied by the MWWorld::Class object

@ -94,6 +94,20 @@ namespace MWGui
setCoord(400, 0, 400, 300);
}
void TradeWindow::restock()
{
// Restock items on the actor inventory
mPtr.getClass().restock(mPtr);
// Also restock any containers owned by this merchant, which are also available to buy in the trade window
std::vector<MWWorld::Ptr> itemSources;
MWBase::Environment::get().getWorld()->getContainersOwnedBy(mPtr, itemSources);
for (std::vector<MWWorld::Ptr>::iterator it = itemSources.begin(); it != itemSources.end(); ++it)
{
it->getClass().restock(*it);
}
}
void TradeWindow::startTrade(const MWWorld::Ptr& actor)
{
mPtr = actor;
@ -101,6 +115,8 @@ namespace MWGui
mCurrentBalance = 0;
mCurrentMerchantOffer = 0;
restock();
std::vector<MWWorld::Ptr> itemSources;
MWBase::Environment::get().getWorld()->getContainersOwnedBy(actor, itemSources);
@ -290,10 +306,9 @@ namespace MWGui
if (msg.find("%s") != std::string::npos)
msg.replace(msg.find("%s"), 2, it->mBase.getClass().getName(it->mBase));
MWBase::Environment::get().getWindowManager()->messageBox(msg);
MWBase::Environment::get().getDialogueManager()->say(mPtr, "Thief");
MWBase::Environment::get().getMechanicsManager()->reportCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
MWBase::Environment::get().getMechanicsManager()->commitCrime(player, mPtr, MWBase::MechanicsManager::OT_Theft,
it->mBase.getClass().getValue(it->mBase)
* it->mCount);
* it->mCount, true);
onCancelButtonClicked(mCancelButton);
MWBase::Environment::get().getDialogueManager()->goodbyeSelected();
return;

@ -98,6 +98,8 @@ namespace MWGui
virtual void onReferenceUnavailable();
int getMerchantGold();
void restock();
};
}

@ -10,6 +10,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/containerstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/npcstats.hpp"

@ -12,6 +12,7 @@
#include "../mwworld/class.hpp"
#include "../mwworld/cellstore.hpp"
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/creaturestats.hpp"
#include "../mwmechanics/npcstats.hpp"

@ -1,4 +1,5 @@
#include "widgets.hpp"
#include "../mwworld/esmstore.hpp"
#include <boost/lexical_cast.hpp>
@ -446,7 +447,7 @@ namespace MWGui
// constant effects have no duration and no target
if (!mEffectParams.mIsConstant)
{
if (mEffectParams.mDuration >= 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
if (mEffectParams.mDuration > 0 && !(magicEffect->mData.mFlags & ESM::MagicEffect::NoDuration))
{
spellLine += " " + MWBase::Environment::get().getWindowManager()->getGameSettingString("sfor", "") + " " + boost::lexical_cast<std::string>(mEffectParams.mDuration) + ((mEffectParams.mDuration == 1) ? sec : secs);
}

@ -1,10 +1,12 @@
#ifndef MWGUI_WIDGETS_H
#define MWGUI_WIDGETS_H
#include "../mwworld/esmstore.hpp"
#include "../mwmechanics/stat.hpp"
#include "controllers.hpp"
#include <components/esm/effectlist.hpp>
#include <components/esm/loadskil.hpp>
#include <MyGUI_Button.h>
#include <MyGUI_EditBox.h>
#include <MyGUI_ScrollBar.h>

@ -1691,13 +1691,16 @@ namespace MWGui
// Use black bars to correct aspect ratio
mVideoBackground->setSize(screenWidth, screenHeight);
double imageaspect = static_cast<double>(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight();
if (mVideoWidget->getVideoHeight() > 0)
{
double imageaspect = static_cast<double>(mVideoWidget->getVideoWidth())/mVideoWidget->getVideoHeight();
int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2);
int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2);
int leftPadding = std::max(0.0, (screenWidth - screenHeight * imageaspect) / 2);
int topPadding = std::max(0.0, (screenHeight - screenWidth / imageaspect) / 2);
mVideoWidget->setCoord(leftPadding, topPadding,
screenWidth - leftPadding*2, screenHeight - topPadding*2);
mVideoWidget->setCoord(leftPadding, topPadding,
screenWidth - leftPadding*2, screenHeight - topPadding*2);
}
}
WindowModal* WindowManager::getCurrentModal() const

@ -195,7 +195,7 @@ namespace MWMechanics
float magnitude = effectIt->mMagnitude;
if (magnitude)
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->second.mCasterActorId, magnitude, remainingTime);
visitor.visit(MWMechanics::EffectKey(effectIt->mEffectId, effectIt->mArg), name, it->first, it->second.mCasterActorId, magnitude, remainingTime, effectIt->mDuration);
}
}
}
@ -229,6 +229,22 @@ namespace MWMechanics
mSpellsChanged = true;
}
void ActiveSpells::purgeEffect(short effectId, const std::string& sourceId)
{
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)
{
for (std::vector<ActiveEffect>::iterator effectIt = it->second.mEffects.begin();
effectIt != it->second.mEffects.end();)
{
if (effectIt->mEffectId == effectId && it->first == sourceId)
effectIt = it->second.mEffects.erase(effectIt);
else
++effectIt;
}
}
mSpellsChanged = true;
}
void ActiveSpells::purge(int casterActorId)
{
for (TContainer::iterator it = mSpells.begin(); it != mSpells.end(); ++it)

@ -82,6 +82,9 @@ namespace MWMechanics
/// Remove all active effects with this effect id
void purgeEffect (short effectId);
/// Remove all active effects with this effect id and source id
void purgeEffect (short effectId, const std::string& sourceId);
/// Remove all active effects, if roll succeeds (for each effect)
void purgeAll (float chance);

@ -0,0 +1,28 @@
#include "actor.hpp"
#include "character.hpp"
namespace MWMechanics
{
Actor::Actor(const MWWorld::Ptr &ptr, MWRender::Animation *animation)
{
mCharacterController.reset(new CharacterController(ptr, animation));
}
void Actor::updatePtr(const MWWorld::Ptr &newPtr)
{
mCharacterController->updatePtr(newPtr);
}
CharacterController* Actor::getCharacterController()
{
return mCharacterController.get();
}
AiState& Actor::getAiState()
{
return mAiState;
}
}

@ -0,0 +1,42 @@
#ifndef OPENMW_MECHANICS_ACTOR_H
#define OPENMW_MECHANICS_ACTOR_H
#include <memory>
#include "aistate.hpp"
namespace MWRender
{
class Animation;
}
namespace MWWorld
{
class Ptr;
}
namespace MWMechanics
{
class CharacterController;
/// @brief Holds temporary state for an actor that will be discarded when the actor leaves the scene.
class Actor
{
public:
Actor(const MWWorld::Ptr& ptr, MWRender::Animation* animation);
/// Notify this actor of its new base object Ptr, use when the object changed cells
void updatePtr(const MWWorld::Ptr& newPtr);
CharacterController* getCharacterController();
AiState& getAiState();
private:
std::auto_ptr<CharacterController> mCharacterController;
AiState mAiState;
};
}
#endif

@ -35,6 +35,9 @@
#include "aifollow.hpp"
#include "aipursue.hpp"
#include "actor.hpp"
#include "summoning.hpp"
namespace
{
@ -71,6 +74,8 @@ bool disintegrateSlot (MWWorld::Ptr ptr, int slot, float disintegrate)
if (charge == 0)
return false;
// FIXME: charge should be a float, not int so that damage < 1 per frame can be applied.
// This was also a bug in the original engine.
charge -=
std::min(disintegrate,
static_cast<float>(charge));
@ -101,8 +106,8 @@ public:
, mCommanded(false){}
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime = -1)
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1)
{
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();
if ( ((key.mId == ESM::MagicEffect::CommandHumanoid && mActor.getClass().isNpc())
@ -161,30 +166,6 @@ void getRestorationPerHourOfSleep (const MWWorld::Ptr& ptr, float& health, float
}
}
void cleanupSummonedCreature (MWMechanics::CreatureStats& casterStats, int creatureActorId)
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(creatureActorId);
if (!ptr.isEmpty())
{
// TODO: Show death animation before deleting? We shouldn't allow looting the corpse while the animation
// plays though, which is a rather lame exploit in vanilla.
MWBase::Environment::get().getWorld()->deleteObject(ptr);
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
.search("VFX_Summon_End");
if (fx)
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
}
else
{
// We didn't find the creature. It's probably in an inactive cell.
// Add to graveyard so we can delete it when the cell becomes active.
std::vector<int>& graveyard = casterStats.getSummonedCreatureGraveyard();
graveyard.push_back(creatureActorId);
}
}
}
namespace MWMechanics
@ -199,8 +180,8 @@ namespace MWMechanics
: mCreature(trappedCreature) {}
virtual void visit (MWMechanics::EffectKey key,
const std::string& sourceName, int casterActorId,
float magnitude, float remainingTime = -1)
const std::string& sourceName, const std::string& sourceId, int casterActorId,
float magnitude, float remainingTime = -1, float totalTime = -1)
{
if (key.mId != ESM::MagicEffect::Soultrap)
return;
@ -293,6 +274,9 @@ namespace MWMechanics
if (sqrDist > maxDistance*maxDistance)
return;
if (targetActor.getClass().getCreatureStats(targetActor).isDead())
return;
// stop tracking when target is behind the actor
Ogre::Vector3 actorDirection (actor.getRefData().getBaseNode()->getOrientation().yAxis());
Ogre::Vector3 targetDirection (Ogre::Vector3(actor2Pos.pos) - Ogre::Vector3(actor1Pos.pos));
@ -517,6 +501,9 @@ namespace MWMechanics
bool wasDead = creatureStats.isDead();
// FIXME: effect ticks should go into separate functions so they can be used with either
// magnitude (instant effect) or magnitude*duration
// attributes
for(int i = 0;i < ESM::Attribute::Length;++i)
{
@ -726,6 +713,8 @@ namespace MWMechanics
}
// Update bound effects
// Note: in vanilla MW multiple bound items of the same type can be created by different spells.
// As these extra copies are kinda useless this may or may not be important.
static std::map<int, std::string> boundItemsMap;
if (boundItemsMap.empty())
{
@ -770,131 +759,11 @@ namespace MWMechanics
}
}
// Update summon effects
static std::map<int, std::string> summonMap;
if (summonMap.empty())
{
summonMap[ESM::MagicEffect::SummonAncestralGhost] = "sMagicAncestralGhostID";
summonMap[ESM::MagicEffect::SummonBonelord] = "sMagicBonelordID";
summonMap[ESM::MagicEffect::SummonBonewalker] = "sMagicLeastBonewalkerID";
summonMap[ESM::MagicEffect::SummonCenturionSphere] = "sMagicCenturionSphereID";
summonMap[ESM::MagicEffect::SummonClannfear] = "sMagicClannfearID";
summonMap[ESM::MagicEffect::SummonDaedroth] = "sMagicDaedrothID";
summonMap[ESM::MagicEffect::SummonDremora] = "sMagicDremoraID";
summonMap[ESM::MagicEffect::SummonFabricant] = "sMagicFabricantID";
summonMap[ESM::MagicEffect::SummonFlameAtronach] = "sMagicFlameAtronachID";
summonMap[ESM::MagicEffect::SummonFrostAtronach] = "sMagicFrostAtronachID";
summonMap[ESM::MagicEffect::SummonGoldenSaint] = "sMagicGoldenSaintID";
summonMap[ESM::MagicEffect::SummonGreaterBonewalker] = "sMagicGreaterBonewalkerID";
summonMap[ESM::MagicEffect::SummonHunger] = "sMagicHungerID";
summonMap[ESM::MagicEffect::SummonScamp] = "sMagicScampID";
summonMap[ESM::MagicEffect::SummonSkeletalMinion] = "sMagicSkeletalMinionID";
summonMap[ESM::MagicEffect::SummonStormAtronach] = "sMagicStormAtronachID";
summonMap[ESM::MagicEffect::SummonWingedTwilight] = "sMagicWingedTwilightID";
summonMap[ESM::MagicEffect::SummonWolf] = "sMagicCreature01ID";
summonMap[ESM::MagicEffect::SummonBear] = "sMagicCreature02ID";
summonMap[ESM::MagicEffect::SummonBonewolf] = "sMagicCreature03ID";
summonMap[ESM::MagicEffect::SummonCreature04] = "sMagicCreature04ID";
summonMap[ESM::MagicEffect::SummonCreature05] = "sMagicCreature05ID";
}
std::map<int, int>& creatureMap = creatureStats.getSummonedCreatureMap();
for (std::map<int, std::string>::iterator it = summonMap.begin(); it != summonMap.end(); ++it)
{
bool found = creatureMap.find(it->first) != creatureMap.end();
int magnitude = creatureStats.getMagicEffects().get(it->first).getMagnitude();
if (found != (magnitude > 0))
{
if (magnitude > 0)
{
ESM::Position ipos = ptr.getRefData().getPosition();
Ogre::Vector3 pos(ipos.pos);
Ogre::Quaternion rot(Ogre::Radian(-ipos.rot[2]), Ogre::Vector3::UNIT_Z);
const float distance = 50;
pos = pos + distance*rot.yAxis();
ipos.pos[0] = pos.x;
ipos.pos[1] = pos.y;
ipos.pos[2] = pos.z;
ipos.rot[0] = 0;
ipos.rot[1] = 0;
ipos.rot[2] = 0;
std::string creatureID =
MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(it->second)->getString();
if (!creatureID.empty())
{
MWWorld::CellStore* store = ptr.getCell();
MWWorld::ManualRef ref(MWBase::Environment::get().getWorld()->getStore(), creatureID, 1);
ref.getPtr().getCellRef().setPosition(ipos);
MWMechanics::CreatureStats& summonedCreatureStats = ref.getPtr().getClass().getCreatureStats(ref.getPtr());
// Make the summoned creature follow its master and help in fights
AiFollow package(ptr.getCellRef().getRefId());
summonedCreatureStats.getAiSequence().stack(package, ref.getPtr());
int creatureActorId = summonedCreatureStats.getActorId();
MWWorld::Ptr placed = MWBase::Environment::get().getWorld()->safePlaceObject(ref.getPtr(),store,ipos);
MWRender::Animation* anim = MWBase::Environment::get().getWorld()->getAnimation(placed);
if (anim)
{
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
.search("VFX_Summon_Start");
if (fx)
anim->addEffect("meshes\\" + fx->mModel, -1, false);
}
creatureMap.insert(std::make_pair(it->first, creatureActorId));
}
}
else
{
// Effect has ended
std::map<int, int>::iterator foundCreature = creatureMap.find(it->first);
cleanupSummonedCreature(creatureStats, foundCreature->second);
creatureMap.erase(foundCreature);
}
}
}
for (std::map<int, int>::iterator it = creatureMap.begin(); it != creatureMap.end(); )
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(it->second);
if (!ptr.isEmpty() && ptr.getClass().getCreatureStats(ptr).isDead())
{
// Purge the magic effect so a new creature can be summoned if desired
creatureStats.getActiveSpells().purgeEffect(it->first);
if (ptr.getClass().hasInventoryStore(ptr))
ptr.getClass().getInventoryStore(ptr).purgeEffect(it->first);
cleanupSummonedCreature(creatureStats, it->second);
creatureMap.erase(it++);
}
else
++it;
}
std::vector<int>& graveyard = creatureStats.getSummonedCreatureGraveyard();
for (std::vector<int>::iterator it = graveyard.begin(); it != graveyard.end(); )
{
MWWorld::Ptr ptr = MWBase::Environment::get().getWorld()->searchPtrViaActorId(*it);
if (!ptr.isEmpty())
{
it = graveyard.erase(it);
const ESM::Static* fx = MWBase::Environment::get().getWorld()->getStore().get<ESM::Static>()
.search("VFX_Summon_End");
if (fx)
MWBase::Environment::get().getWorld()->spawnEffect("meshes\\" + fx->mModel,
"", Ogre::Vector3(ptr.getRefData().getPosition().pos));
MWBase::Environment::get().getWorld()->deleteObject(ptr);
}
else
++it;
}
UpdateSummonedCreatures updateSummonedCreatures(ptr);
creatureStats.getActiveSpells().visitEffectSources(updateSummonedCreatures);
if (ptr.getClass().hasInventoryStore(ptr))
ptr.getClass().getInventoryStore(ptr).visitEffectSources(updateSummonedCreatures);
updateSummonedCreatures.finish();
}
void Actors::calculateNpcStatModifiers (const MWWorld::Ptr& ptr, float duration)
@ -917,10 +786,10 @@ namespace MWMechanics
void Actors::updateDrowning(const MWWorld::Ptr& ptr, float duration)
{
PtrControllerMap::iterator it = mActors.find(ptr);
PtrActorMap::iterator it = mActors.find(ptr);
if (it == mActors.end())
return;
CharacterController* ctrl = it->second;
CharacterController* ctrl = it->second->getCharacterController();
NpcStats &stats = ptr.getClass().getNpcStats(ptr);
MWBase::World *world = MWBase::Environment::get().getWorld();
@ -1125,14 +994,14 @@ namespace MWMechanics
removeActor(ptr);
MWRender::Animation *anim = MWBase::Environment::get().getWorld()->getAnimation(ptr);
mActors.insert(std::make_pair(ptr, new CharacterController(ptr, anim)));
mActors.insert(std::make_pair(ptr, new Actor(ptr, anim)));
if (updateImmediately)
mActors[ptr]->update(0);
mActors[ptr]->getCharacterController()->update(0);
}
void Actors::removeActor (const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
{
delete iter->second;
@ -1142,20 +1011,20 @@ namespace MWMechanics
void Actors::updateActor(const MWWorld::Ptr &old, const MWWorld::Ptr &ptr)
{
PtrControllerMap::iterator iter = mActors.find(old);
PtrActorMap::iterator iter = mActors.find(old);
if(iter != mActors.end())
{
CharacterController *ctrl = iter->second;
Actor *actor = iter->second;
mActors.erase(iter);
ctrl->updatePtr(ptr);
mActors.insert(std::make_pair(ptr, ctrl));
actor->updatePtr(ptr);
mActors.insert(std::make_pair(ptr, actor));
}
}
void Actors::dropActors (const MWWorld::CellStore *cellStore, const MWWorld::Ptr& ignore)
{
PtrControllerMap::iterator iter = mActors.begin();
PtrActorMap::iterator iter = mActors.begin();
while(iter != mActors.end())
{
if(iter->first.getCell()==cellStore && iter->first != ignore)
@ -1189,8 +1058,10 @@ namespace MWMechanics
// using higher values will make a quest in Bloodmoon harder or impossible to complete (bug #1876)
const float sqrProcessingDistance = 7168*7168;
/// \todo move update logic to Actor class where appropriate
// AI and magic effects update
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
if (!iter->first.getClass().getCreatureStats(iter->first).isDead())
{
@ -1204,7 +1075,7 @@ namespace MWMechanics
if (iter->first != player)
adjustCommandedActor(iter->first);
for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{
if (it->first == iter->first || iter->first == player) // player is not AI-controlled
continue;
@ -1216,13 +1087,13 @@ namespace MWMechanics
float sqrHeadTrackDistance = std::numeric_limits<float>::max();
MWWorld::Ptr headTrackTarget;
for(PtrControllerMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
for(PtrActorMap::iterator it(mActors.begin()); it != mActors.end(); ++it)
{
if (it->first == iter->first)
continue;
updateHeadTracking(iter->first, it->first, headTrackTarget, sqrHeadTrackDistance);
}
iter->second->setHeadTrackTarget(headTrackTarget);
iter->second->getCharacterController()->setHeadTrackTarget(headTrackTarget);
}
if (iter->first.getClass().isNpc() && iter->first != player)
@ -1251,12 +1122,12 @@ namespace MWMechanics
// Reaching the text keys may trigger Hit / Spellcast (and as such, particles),
// so updating VFX immediately after that would just remove the particle effects instantly.
// There needs to be a magic effect update in between.
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
iter->second->updateContinuousVfx();
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
iter->second->getCharacterController()->updateContinuousVfx();
// Animation/movement update
CharacterController* playerCharacter = NULL;
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
if (iter->first != player &&
Ogre::Vector3(player.getRefData().getPosition().pos).squaredDistance(Ogre::Vector3(iter->first.getRefData().getPosition().pos))
@ -1265,22 +1136,22 @@ namespace MWMechanics
if (iter->first.getClass().getCreatureStats(iter->first).getMagicEffects().get(
ESM::MagicEffect::Paralyze).getMagnitude() > 0)
iter->second->skipAnim();
iter->second->getCharacterController()->skipAnim();
// Handle player last, in case a cell transition occurs by casting a teleportation spell
// (would invalidate the iterator)
if (iter->first.getCellRef().getRefId() == "player")
{
playerCharacter = iter->second;
playerCharacter = iter->second->getCharacterController();
continue;
}
iter->second->update(duration);
iter->second->getCharacterController()->update(duration);
}
if (playerCharacter)
playerCharacter->update(duration);
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
@ -1338,7 +1209,7 @@ namespace MWMechanics
bool detected = false;
for (PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for (PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
if (iter->first == player) // not the player
continue;
@ -1381,32 +1252,32 @@ namespace MWMechanics
void Actors::killDeadActors()
{
for(PtrControllerMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
for(PtrActorMap::iterator iter(mActors.begin()); iter != mActors.end(); ++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
if(!stats.isDead())
{
if(iter->second->isDead())
if(iter->second->getCharacterController()->isDead())
{
// Actor has been resurrected. Notify the CharacterController and re-enable collision.
MWBase::Environment::get().getWorld()->enableActorCollision(iter->first, true);
iter->second->resurrect();
iter->second->getCharacterController()->resurrect();
}
if(!stats.isDead())
continue;
}
if (iter->second->kill())
if (iter->second->getCharacterController()->kill())
{
iter->first.getClass().getCreatureStats(iter->first).notifyDied();
++mDeathCount[Misc::StringUtils::lowerCase(iter->first.getCellRef().getRefId())];
// Make sure spell effects with CasterLinked flag are removed
for (PtrControllerMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
for (PtrActorMap::iterator iter2(mActors.begin());iter2 != mActors.end();++iter2)
{
MWMechanics::ActiveSpells& spells = iter2->first.getClass().getCreatureStats(iter2->first).getActiveSpells();
spells.purge(stats.getActorId());
@ -1435,7 +1306,7 @@ namespace MWMechanics
void Actors::restoreDynamicStats(bool sleep)
{
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
restoreDynamicStats(iter->first, sleep);
}
@ -1467,35 +1338,35 @@ namespace MWMechanics
void Actors::forceStateUpdate(const MWWorld::Ptr & ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->forceStateUpdate();
iter->second->getCharacterController()->forceStateUpdate();
}
void Actors::playAnimationGroup(const MWWorld::Ptr& ptr, const std::string& groupName, int mode, int number)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->playGroup(groupName, mode, number);
iter->second->getCharacterController()->playGroup(groupName, mode, number);
}
void Actors::skipAnimation(const MWWorld::Ptr& ptr)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
iter->second->skipAnim();
iter->second->getCharacterController()->skipAnim();
}
bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)
{
PtrControllerMap::iterator iter = mActors.find(ptr);
PtrActorMap::iterator iter = mActors.find(ptr);
if(iter != mActors.end())
return iter->second->isAnimPlaying(groupName);
return iter->second->getCharacterController()->isAnimPlaying(groupName);
return false;
}
void Actors::getObjectsInRange(const Ogre::Vector3& position, float radius, std::vector<MWWorld::Ptr>& out)
{
for (PtrControllerMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
for (PtrActorMap::iterator iter = mActors.begin(); iter != mActors.end(); ++iter)
{
if (Ogre::Vector3(iter->first.getRefData().getPosition().pos).squaredDistance(position) <= radius*radius)
out.push_back(iter->first);
@ -1505,7 +1376,7 @@ namespace MWMechanics
std::list<MWWorld::Ptr> Actors::getActorsFollowing(const MWWorld::Ptr& actor)
{
std::list<MWWorld::Ptr> list;
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
@ -1535,7 +1406,7 @@ namespace MWMechanics
std::list<int> Actors::getActorsFollowingIndices(const MWWorld::Ptr &actor)
{
std::list<int> list;
for(PtrControllerMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
for(PtrActorMap::iterator iter(mActors.begin());iter != mActors.end();++iter)
{
const MWWorld::Class &cls = iter->first.getClass();
CreatureStats &stats = cls.getCreatureStats(iter->first);
@ -1608,7 +1479,7 @@ namespace MWMechanics
void Actors::clear()
{
PtrControllerMap::iterator it(mActors.begin());
PtrActorMap::iterator it(mActors.begin());
for (; it != mActors.end(); ++it)
{
delete it->second;
@ -1628,10 +1499,27 @@ namespace MWMechanics
bool Actors::isReadyToBlock(const MWWorld::Ptr &ptr) const
{
PtrControllerMap::const_iterator it = mActors.find(ptr);
PtrActorMap::const_iterator it = mActors.find(ptr);
if (it == mActors.end())
return false;
return it->second->isReadyToBlock();
return it->second->getCharacterController()->isReadyToBlock();
}
void Actors::fastForwardAi()
{
if (!MWBase::Environment::get().getMechanicsManager()->isAIActive())
return;
// making a copy since fast-forward could move actor to a different cell and invalidate the mActors iterator
PtrActorMap map = mActors;
for (PtrActorMap::iterator it = map.begin(); it != map.end(); ++it)
{
MWWorld::Ptr ptr = it->first;
if (ptr == MWBase::Environment::get().getWorld()->getPlayerPtr())
continue;
MWMechanics::AiSequence& seq = ptr.getClass().getCreatureStats(ptr).getAiSequence();
seq.fastForward(ptr, it->second->getAiState());
}
}
}

@ -6,7 +6,6 @@
#include <string>
#include <map>
#include "character.hpp"
#include "movement.hpp"
#include "../mwbase/world.hpp"
@ -23,6 +22,8 @@ namespace MWWorld
namespace MWMechanics
{
class Actor;
class Actors
{
std::map<std::string, int> mDeathCount;
@ -51,10 +52,10 @@ namespace MWMechanics
Actors();
~Actors();
typedef std::map<MWWorld::Ptr,CharacterController*> PtrControllerMap;
typedef std::map<MWWorld::Ptr,Actor*> PtrActorMap;
PtrControllerMap::const_iterator begin() { return mActors.begin(); }
PtrControllerMap::const_iterator end() { return mActors.end(); }
PtrActorMap::const_iterator begin() { return mActors.begin(); }
PtrActorMap::const_iterator end() { return mActors.end(); }
/// Update magic effects for an actor. Usually done automatically once per frame, but if we're currently
/// paused we may want to do it manually (after equipping permanent enchantment)
@ -100,6 +101,9 @@ namespace MWMechanics
int getHoursToRest(const MWWorld::Ptr& ptr) const;
///< Calculate how many hours the given actor needs to rest in order to be fully healed
void fastForwardAi();
///< Simulate the passing of time
int countDeaths (const std::string& id) const;
///< Return the number of deaths for actors with the given ID.
@ -131,7 +135,7 @@ namespace MWMechanics
bool isReadyToBlock(const MWWorld::Ptr& ptr) const;
private:
PtrControllerMap mActors;
PtrActorMap mActors;
};
}

@ -300,6 +300,14 @@ namespace MWMechanics
//Update with period = tReaction
// Stop attacking if target is not seen
if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0
|| target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75)
{
movement.mPosition[1] = movement.mPosition[0] = 0;
return false; // TODO: run away instead of doing nothing
}
timerReact = 0;
const MWWorld::CellStore*& currentCell = storage.mCell;
bool cellChange = currentCell && (actor.getCell() != currentCell);
@ -326,10 +334,6 @@ namespace MWMechanics
actionCooldown = currentAction->getActionCooldown();
}
// Stop attacking if target is not seen
if (!MWBase::Environment::get().getMechanicsManager()->awarenessCheck(target, actor))
return true;
if (currentAction.get())
currentAction->getCombatRange(rangeAttack, rangeFollow);
@ -576,7 +580,7 @@ namespace MWMechanics
buildNewPath(actor, target); //may fail to build a path, check before use
//delete visited path node
mPathFinder.checkWaypoint(pos.pos[0],pos.pos[1],pos.pos[2]);
mPathFinder.checkPathCompleted(pos.pos[0],pos.pos[1],pos.pos[2]);
// This works on the borders between the path grid and areas with no waypoints.
if(inLOS && mPathFinder.getPath().size() > 1)

@ -294,7 +294,10 @@ namespace MWMechanics
// Effect doesn't heal more than we need, *or* we are below 1/2 health
if (current.getModified() - current.getCurrent() > toHeal
|| current.getCurrent() < current.getModified()*0.5)
return 10000.f * priority;
{
return 10000.f * priority
- (toHeal - (current.getModified()-current.getCurrent())); // prefer the most fitting potion
}
else
return -10000.f * priority; // Save for later
}

@ -3,7 +3,6 @@
#include "pathfinding.hpp"
#include <components/esm/defs.hpp>
#include "../mwbase/world.hpp"
#include "obstacle.hpp"
#include "aistate.hpp"
@ -64,6 +63,9 @@ namespace MWMechanics
virtual void writeState (ESM::AiSequence::AiSequence& sequence) const {}
/// Simulates the passing of time
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state) {}
protected:
/// Causes the actor to attempt to walk to the specified location
/** \return If the actor has arrived at his destination **/

@ -43,6 +43,10 @@ bool AiPursue::execute (const MWWorld::Ptr& actor, AiState& state, float duratio
)
return true; //Target doesn't exist
if (target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Invisibility).getMagnitude() > 0
|| target.getClass().getCreatureStats(target).getMagicEffects().get(ESM::MagicEffect::Chameleon).getMagnitude() > 75)
return true;
if(target.getClass().getCreatureStats(target).isDead())
return true;

@ -125,19 +125,23 @@ bool AiSequence::isInCombat(const MWWorld::Ptr &actor) const
void AiSequence::stopCombat()
{
while (getTypeId() == AiPackage::TypeIdCombat)
for(std::list<AiPackage*>::iterator it = mPackages.begin(); it != mPackages.end(); )
{
delete *mPackages.begin();
mPackages.erase (mPackages.begin());
if ((*it)->getTypeId() == AiPackage::TypeIdCombat)
it = mPackages.erase(it);
else
++it;
}
}
void AiSequence::stopPursuit()
{
while (getTypeId() == AiPackage::TypeIdPursue)
for(std::list<AiPackage*>::iterator it = mPackages.begin(); it != mPackages.end(); )
{
delete *mPackages.begin();
mPackages.erase (mPackages.begin());
if ((*it)->getTypeId() == AiPackage::TypeIdPursue)
it = mPackages.erase(it);
else
++it;
}
}
@ -390,4 +394,13 @@ void AiSequence::readState(const ESM::AiSequence::AiSequence &sequence)
}
}
void AiSequence::fastForward(const MWWorld::Ptr& actor, AiState& state)
{
if (!mPackages.empty())
{
MWMechanics::AiPackage* package = mPackages.front();
package->fastForward(actor, state);
}
}
} // namespace MWMechanics

@ -97,6 +97,9 @@ namespace MWMechanics
/// Execute current package, switching if needed.
void execute (const MWWorld::Ptr& actor, MWMechanics::AiState& state, float duration);
/// Simulate the passing of time using the currently active AI package
void fastForward(const MWWorld::Ptr &actor, AiState &state);
/// Remove all packages.
void clear();

@ -76,24 +76,6 @@ namespace MWMechanics
mStorage = p;
}
/// \brief gives away ownership of object. Throws exception if storage does not contain Derived or is empty.
template< class Derived >
Derived* moveOut()
{
assert_derived<Derived>();
if(!mStorage)
throw std::runtime_error("Cant move out: empty storage.");
Derived* result = dynamic_cast<Derived*>(mStorage);
if(!mStorage)
throw std::runtime_error("Cant move out: wrong type requested.");
return result;
}
bool empty() const
{
return mStorage == NULL;
@ -120,7 +102,7 @@ namespace MWMechanics
/// \brief base class for the temporary storage of AiPackages.
/**
* Each AI package with temporary values needs a AiPackageStorage class
* which is derived from AiTemporaryBase. The CharacterController holds a container
* which is derived from AiTemporaryBase. The Actor holds a container
* AiState where one of these storages can be stored at a time.
* The execute(...) member function takes this container as an argument.
* */

@ -14,6 +14,19 @@
#include "movement.hpp"
#include "creaturestats.hpp"
namespace
{
bool isWithinMaxRange(const Ogre::Vector3& pos1, const Ogre::Vector3& pos2)
{
// Maximum travel distance for vanilla compatibility.
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
// We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
return (pos1.squaredDistance(pos2) <= 7168*7168);
}
}
namespace MWMechanics
{
AiTravel::AiTravel(float x, float y, float z)
@ -71,10 +84,7 @@ namespace MWMechanics
}
}
// Maximum travel distance for vanilla compatibility.
// Was likely meant to prevent NPCs walking into non-loaded exterior cells, but for some reason is used in interior cells as well.
// We can make this configurable at some point, but the default *must* be the below value. Anything else will break shoddily-written content (*cough* MW *cough*) in bizarre ways.
if (Ogre::Vector3(mX, mY, mZ).squaredDistance(Ogre::Vector3(pos.pos)) > 7168*7168)
if (!isWithinMaxRange(Ogre::Vector3(mX, mY, mZ), Ogre::Vector3(pos.pos)))
return false;
bool cellChange = cell->mData.mX != mCellX || cell->mData.mY != mCellY;
@ -113,6 +123,16 @@ namespace MWMechanics
return TypeIdTravel;
}
void AiTravel::fastForward(const MWWorld::Ptr& actor, AiState& state)
{
if (!isWithinMaxRange(Ogre::Vector3(mX, mY, mZ), Ogre::Vector3(actor.getRefData().getPosition().pos)))
return;
// does not do any validation on the travel target (whether it's in air, inside collision geometry, etc),
// that is the user's responsibility
MWBase::Environment::get().getWorld()->moveObject(actor, mX, mY, mZ);
actor.getClass().adjustPosition(actor, false);
}
void AiTravel::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiTravel> travel(new ESM::AiSequence::AiTravel());

@ -23,6 +23,9 @@ namespace MWMechanics
AiTravel(float x, float y, float z);
AiTravel(const ESM::AiSequence::AiTravel* travel);
/// Simulates the passing of time
virtual void fastForward(const MWWorld::Ptr& actor, AiState& state);
void writeState(ESM::AiSequence::AiSequence &sequence) const;
virtual AiTravel *clone() const;

@ -41,13 +41,6 @@ namespace MWMechanics
AiWander::GreetingState mSaidGreeting;
int mGreetingTimer;
// Cached current cell location
int mCellX;
int mCellY;
// Cell location multiplied by ESM::Land::REAL_SIZE
float mXCell;
float mYCell;
const MWWorld::CellStore* mCell; // for detecting cell change
// AiWander states
@ -66,10 +59,6 @@ namespace MWMechanics
mReaction(0),
mSaidGreeting(AiWander::Greet_None),
mGreetingTimer(0),
mCellX(std::numeric_limits<int>::max()),
mCellY(std::numeric_limits<int>::max()),
mXCell(0),
mYCell(0),
mCell(NULL),
mChooseAction(true),
mIdleNow(false),
@ -81,6 +70,7 @@ namespace MWMechanics
AiWander::AiWander(int distance, int duration, int timeOfDay, const std::vector<unsigned char>& idle, bool repeat):
mDistance(distance), mDuration(duration), mTimeOfDay(timeOfDay), mIdle(idle), mRepeat(repeat)
, mStoredInitialActorPosition(false)
{
mIdle.resize(8, 0);
init();
@ -183,7 +173,6 @@ namespace MWMechanics
currentCell = actor.getCell();
mStoredAvailableNodes = false; // prob. not needed since mDistance = 0
}
const ESM::Cell *cell = currentCell->getCell();
cStats.setDrawState(DrawState_Nothing);
cStats.setMovementFlag(CreatureStats::Flag_Run, false);
@ -213,7 +202,7 @@ namespace MWMechanics
// Are we there yet?
bool& chooseAction = storage.mChooseAction;
if(walking &&
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2]))
storage.mPathFinder.checkPathCompleted(pos.pos[0], pos.pos[1], pos.pos[2], 64.f))
{
stopWalking(actor, storage);
moveNow = false;
@ -371,81 +360,10 @@ namespace MWMechanics
}
}
int& cachedCellX = storage.mCellX;
int& cachedCellY = storage.mCellY;
float& cachedCellXposition = storage.mXCell;
float& cachedCellYposition = storage.mYCell;
// Initialization to discover & store allowed node points for this actor.
if(!mStoredAvailableNodes)
{
// infrequently used, therefore no benefit in caching it as a member
const ESM::Pathgrid *
pathgrid = world->getStore().get<ESM::Pathgrid>().search(*cell);
// cache the current cell location
cachedCellX = cell->mData.mX;
cachedCellY = cell->mData.mY;
// If there is no path this actor doesn't go anywhere. See:
// https://forum.openmw.org/viewtopic.php?t=1556
// http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833
if(!pathgrid || pathgrid->mPoints.empty())
mDistance = 0;
// A distance value passed into the constructor indicates how far the
// actor can wander from the spawn position. AiWander assumes that
// pathgrid points are available, and uses them to randomly select wander
// destinations within the allowed set of pathgrid points (nodes).
if(mDistance)
{
cachedCellXposition = 0;
cachedCellYposition = 0;
if(cell->isExterior())
{
cachedCellXposition = cachedCellX * ESM::Land::REAL_SIZE;
cachedCellYposition = cachedCellY * ESM::Land::REAL_SIZE;
}
// FIXME: There might be a bug here. The allowed node points are
// based on the actor's current position rather than the actor's
// spawn point. As a result the allowed nodes for wander can change
// between saves, for example.
//
// convert npcPos to local (i.e. cell) co-ordinates
Ogre::Vector3 npcPos(pos.pos);
npcPos[0] = npcPos[0] - cachedCellXposition;
npcPos[1] = npcPos[1] - cachedCellYposition;
// mAllowedNodes for this actor with pathgrid point indexes based on mDistance
// NOTE: mPoints and mAllowedNodes are in local co-ordinates
for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{
Ogre::Vector3 nodePos(pathgrid->mPoints[counter].mX, pathgrid->mPoints[counter].mY,
pathgrid->mPoints[counter].mZ);
if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
mAllowedNodes.push_back(pathgrid->mPoints[counter]);
}
if(!mAllowedNodes.empty())
{
Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ);
float closestNode = npcPos.squaredDistance(firstNodePos);
unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{
Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY,
mAllowedNodes[counterThree].mZ);
float tempDist = npcPos.squaredDistance(nodePos);
if(tempDist < closestNode)
index = counterThree;
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
}
}
getAllowedNodes(actor, currentCell->getCell());
}
// Actor becomes stationary - see above URL's for previous research
@ -581,9 +499,14 @@ namespace MWMechanics
// convert dest to use world co-ordinates
ESM::Pathgrid::Point dest;
dest.mX = destNodePos[0] + cachedCellXposition;
dest.mY = destNodePos[1] + cachedCellYposition;
dest.mX = destNodePos[0];
dest.mY = destNodePos[1];
dest.mZ = destNodePos[2];
if (currentCell->getCell()->isExterior())
{
dest.mX += currentCell->getCell()->mData.mX * ESM::Land::REAL_SIZE;
dest.mY += currentCell->getCell()->mData.mY * ESM::Land::REAL_SIZE;
}
// actor position is already in world co-ordinates
ESM::Pathgrid::Point start;
@ -732,6 +655,103 @@ namespace MWMechanics
}
}
void AiWander::fastForward(const MWWorld::Ptr& actor, AiState &state)
{
if (mDistance == 0)
return;
if (!mStoredAvailableNodes)
getAllowedNodes(actor, actor.getCell()->getCell());
if (mAllowedNodes.empty())
return;
state.moveIn(new AiWanderStorage());
int index = std::rand() / (static_cast<double> (RAND_MAX) + 1) * mAllowedNodes.size();
ESM::Pathgrid::Point dest = mAllowedNodes[index];
// apply a slight offset to prevent overcrowding
dest.mX += Ogre::Math::RangeRandom(-64, 64);
dest.mY += Ogre::Math::RangeRandom(-64, 64);
if (actor.getCell()->isExterior())
{
dest.mX += actor.getCell()->getCell()->mData.mX * ESM::Land::REAL_SIZE;
dest.mY += actor.getCell()->getCell()->mData.mY * ESM::Land::REAL_SIZE;
}
MWBase::Environment::get().getWorld()->moveObject(actor, dest.mX, dest.mY, dest.mZ);
actor.getClass().adjustPosition(actor, false);
}
void AiWander::getAllowedNodes(const MWWorld::Ptr& actor, const ESM::Cell* cell)
{
if (!mStoredInitialActorPosition)
{
mInitialActorPosition = Ogre::Vector3(actor.getRefData().getPosition().pos);
mStoredInitialActorPosition = true;
}
// infrequently used, therefore no benefit in caching it as a member
const ESM::Pathgrid *
pathgrid = MWBase::Environment::get().getWorld()->getStore().get<ESM::Pathgrid>().search(*cell);
// If there is no path this actor doesn't go anywhere. See:
// https://forum.openmw.org/viewtopic.php?t=1556
// http://www.fliggerty.com/phpBB3/viewtopic.php?f=30&t=5833
if(!pathgrid || pathgrid->mPoints.empty())
mDistance = 0;
// A distance value passed into the constructor indicates how far the
// actor can wander from the spawn position. AiWander assumes that
// pathgrid points are available, and uses them to randomly select wander
// destinations within the allowed set of pathgrid points (nodes).
if(mDistance)
{
float cellXOffset = 0;
float cellYOffset = 0;
if(cell->isExterior())
{
cellXOffset = cell->mData.mX * ESM::Land::REAL_SIZE;
cellYOffset = cell->mData.mY * ESM::Land::REAL_SIZE;
}
// convert npcPos to local (i.e. cell) co-ordinates
Ogre::Vector3 npcPos(mInitialActorPosition);
npcPos[0] = npcPos[0] - cellXOffset;
npcPos[1] = npcPos[1] - cellYOffset;
// mAllowedNodes for this actor with pathgrid point indexes based on mDistance
// NOTE: mPoints and mAllowedNodes are in local co-ordinates
for(unsigned int counter = 0; counter < pathgrid->mPoints.size(); counter++)
{
Ogre::Vector3 nodePos(pathgrid->mPoints[counter].mX, pathgrid->mPoints[counter].mY,
pathgrid->mPoints[counter].mZ);
if(npcPos.squaredDistance(nodePos) <= mDistance * mDistance)
mAllowedNodes.push_back(pathgrid->mPoints[counter]);
}
if(!mAllowedNodes.empty())
{
Ogre::Vector3 firstNodePos(mAllowedNodes[0].mX, mAllowedNodes[0].mY, mAllowedNodes[0].mZ);
float closestNode = npcPos.squaredDistance(firstNodePos);
unsigned int index = 0;
for(unsigned int counterThree = 1; counterThree < mAllowedNodes.size(); counterThree++)
{
Ogre::Vector3 nodePos(mAllowedNodes[counterThree].mX, mAllowedNodes[counterThree].mY,
mAllowedNodes[counterThree].mZ);
float tempDist = npcPos.squaredDistance(nodePos);
if(tempDist < closestNode)
index = counterThree;
}
mCurrentNode = mAllowedNodes[index];
mAllowedNodes.erase(mAllowedNodes.begin() + index);
mStoredAvailableNodes = true; // set only if successful in finding allowed nodes
}
}
}
void AiWander::writeState(ESM::AiSequence::AiSequence &sequence) const
{
std::auto_ptr<ESM::AiSequence::AiWander> wander(new ESM::AiSequence::AiWander());
@ -743,6 +763,9 @@ namespace MWMechanics
for (int i=0; i<8; ++i)
wander->mData.mIdle[i] = mIdle[i];
wander->mData.mShouldRepeat = mRepeat;
wander->mStoredInitialActorPosition = mStoredInitialActorPosition;
if (mStoredInitialActorPosition)
wander->mInitialActorPosition = mInitialActorPosition;
ESM::AiSequence::AiPackageContainer package;
package.mType = ESM::AiSequence::Ai_Wander;
@ -756,7 +779,10 @@ namespace MWMechanics
, mStartTime(MWWorld::TimeStamp(wander->mStartTime))
, mTimeOfDay(wander->mData.mTimeOfDay)
, mRepeat(wander->mData.mShouldRepeat)
, mStoredInitialActorPosition(wander->mStoredInitialActorPosition)
{
if (mStoredInitialActorPosition)
mInitialActorPosition = wander->mInitialActorPosition;
for (int i=0; i<8; ++i)
mIdle.push_back(wander->mData.mIdle[i]);

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

Loading…
Cancel
Save