Merge pull request #456 from OpenMW/master while resolving conflicts

# Conflicts:
#	.travis.yml
#	README.md
#	apps/openmw/mwgui/quickkeysmenu.cpp
#	apps/openmw/mwmechanics/actors.cpp
#	apps/openmw/mwmechanics/combat.cpp
0.6.3
David Cernat 6 years ago
commit a236ffc4be

@ -8,4 +8,9 @@ insert_final_newline = true
[*.hpp]
indent_style = space
indent_size = 4
insert_final_newline = true
insert_final_newline = true
[*.glsl]
indent_style = space
indent_size = 4
insert_final_newline = false

@ -0,0 +1,46 @@
stages:
- build
Debian:
tags:
- docker
- linux
image: gcc
cache:
key: apt-cache
paths:
- apt-cache/
before_script:
- export APT_CACHE_DIR=`pwd`/apt-cache && mkdir -pv $APT_CACHE_DIR
- apt-get update -yq
- apt-get -o dir::cache::archives="$APT_CACHE_DIR" install -y cmake libboost-filesystem-dev libboost-program-options-dev libboost-system-dev libavcodec-dev libavformat-dev libavutil-dev libswscale-dev libswresample-dev libsdl2-dev libqt4-dev libopenal-dev libopenscenegraph-3.4-dev libunshield-dev libtinyxml-dev
# - apt-get install -y libmygui-dev libbullet-dev # to be updated to latest below because stretch is too old
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet-dev_2.87+dfsg-2_amd64.deb -o libbullet-dev_2.87+dfsg-2_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/b/bullet/libbullet2.87_2.87+dfsg-2_amd64.deb -o libbullet2.87_2.87+dfsg-2_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb -o libmygui.openglplatform0debian1v5_3.2.2+dfsg-1_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb -o libmyguiengine3debian1v5_3.2.2+dfsg-1_amd64.deb
- curl http://ftp.us.debian.org/debian/pool/main/m/mygui/libmygui-dev_3.2.2+dfsg-1_amd64.deb -o libmygui-dev_3.2.2+dfsg-1_amd64.deb
- dpkg --ignore-depends=libmygui.ogreplatform0debian1v5 -i *.deb
stage: build
script:
- cores_to_use=$((`nproc`-2)); if (( $cores_to_use < 1 )); then cores_to_use=1; fi
- mkdir build; cd build; cmake -DCMAKE_BUILD_TYPE=MinSizeRel ../
- make -j$cores_to_use
- DESTDIR=artifacts make install
artifacts:
paths:
- build/artifacts/
MacOS:
tags:
- macos
- xcode
stage: build
allow_failure: true
script:
- rm -fr build/* # remove anything in the build directory
- CI/before_install.osx.sh
- CI/before_script.osx.sh
- cd build; make -j2 package
artifacts:
paths:
- build/OpenMW-*.dmg

@ -1,3 +1,7 @@
os:
- linux
# - osx
osx_image: xcode9.4
language: cpp
sudo: required
dist: trusty
@ -12,7 +16,6 @@ env:
# The next declaration is the encrypted COVERITY_SCAN_TOKEN, created
# via the "travis encrypt" command using the project repo's public key
- secure: NZmvVuA0O9NJXVQ12tXQZHDJC2mbFgYNFcsicw0DgW1It2Nk5hxIkF0pfu4/Z59mhQuOPgRVjl5b0FKy2Axh0gkWc1DJEXGwNaiW5lpTMNWR1LJG5rxa8LrDUpFkycpbzfAFuTUZu5z3iYVv64XzELvBuqNGhPMu1LeBnrlech0jFNjkR9p5qtJGWb8zYcPMCC57rig8a9g1ABoVYS6UXjrKpx0946ZLRsE5ukc9pXsypGwPmOMyfzZkxxzIqFaxoE5JIEdaJTWba/6Za315ozYYIi/N35ROI1YAv5GHRe/Iw9XAa4vQpbDzjM7ZSsZdTvvQsSU598gD2xC6jFUKSrpW6GZKwM2x236fZLGnOk5Uw7DUbG+AwpcEmxBwoy9PjBl9ZF3tJykI0gROewCy8MODhdsVMKr1HGIMVBIJySm/RnNqtoDbYV8mYnSl5b8rwJiCajoiR8Zuv4CIfGneeH1a3DOQDPH/qkDsU6ilzF4ANsBlMUUpgY653KBMBmTlNuVZSH527tnD7Fg6JgHVuSQkTbRa1vSkR7Zcre604RZcAoaEdbX3bhVDasPPghU/I742L0RH3oQNlR09pPBDZ8kG7ydl4aPHwpCWnvXNM1vgxtGvnYLztwrse7IoaRXRYiMFmrso78WhMWUDKgvY4wV9aeUu0DtnMezZVIQwCKg=
- macos_qt_formula=qt
addons:
apt:
sources:

@ -22,6 +22,7 @@ Programmers
alexanderkjall
Alexander Nadeau (wareya)
Alexander Olofsson (Ace)
Alex S (docwest)
Allofich
Andrei Kortunov (akortunov)
AnyOldName3
@ -37,6 +38,7 @@ Programmers
Britt Mathis (galdor557)
Capostrophic
cc9cii
Cédric Mocquillon
Chris Boyce (slothlife)
Chris Robinson (KittyCat)
Cory F. Cohen (cfcohen)
@ -62,6 +64,8 @@ Programmers
Evgeniy Mineev (sandstranger)
Federico Guerra (FedeWar)
Fil Krynicki (filkry)
Finbar Crago(finbar-crago)
Florian Weber (Florianjw)
Gašper Sedej
gugus/gus
Hallfaer Tuilinn
@ -171,6 +175,7 @@ Programmers
Documentation
-------------
Adam Bowen (adamnbowen)
Alejandro Sanchez (HiPhish)
Bodillium
Bret Curtis (psi29a)

@ -1,8 +1,67 @@
0.45.0
------
Bug #1990: Sunrise/sunset not set correct
Bug #2222: Fatigue's effect on selling price is backwards
Bug #2326: After a bound item expires the last equipped item of that type is not automatically re-equipped
Bug #2455: Creatures attacks degrade armor
Bug #2562: Forcing AI to activate a teleport door sometimes causes a crash
Bug #2772: Non-existing class or faction freezes the game
Bug #2835: Player able to slowly move when overencumbered
Bug #2852: No murder bounty when a player follower commits murder
Bug #2862: [macOS] Can't quit launcher using Command-Q or OpenMW->Quit
Bug #2971: Compiler did not reject lines with naked expressions beginning with x.y
Bug #3374: Touch spells not hitting kwama foragers
Bug #3486: [Mod] NPC Commands does not work
Bug #3591: Angled hit distance too low
Bug #3629: DB assassin attack never triggers creature spawning
Bug #3876: Landscape texture painting is misaligned
Bug #3897: Have Goodbye give all choices the effects of Goodbye
Bug #3911: [macOS] Typing in the "Content List name" dialog box produces double characters
Bug #3993: Terrain texture blending map is not upscaled
Bug #3997: Almalexia doesn't pace
Bug #4036: Weird behaviour of AI packages if package target has non-unique ID
Bug #4047: OpenMW not reporting its version number in MacOS; OpenMW-CS not doing it fully
Bug #4125: OpenMW logo cropped on bugtracker
Bug #4215: OpenMW shows book text after last EOL tag
Bug #4221: Characters get stuck in V-shaped terrain
Bug #4251: Stationary NPCs do not return to their position after combat
Bug #4286: Scripted animations can be interrupted
Bug #4291: Non-persistent actors that started the game as dead do not play death animations
Bug #4293: Faction members are not aware of faction ownerships in barter
Bug #4307: World cleanup should remove dead bodies only if death animation is finished
Bug #4327: Missing animations during spell/weapon stance switching
Bug #4368: Settings window ok button doesn't have key focus by default
Bug #4393: NPCs walk back to where they were after using ResetActors
Bug #4416: Handle exception if we try to play non-music file
Bug #4419: MRK NiStringExtraData is handled incorrectly
Bug #4426: RotateWorld behavior is incorrect
Bug #4429: [Windows] Error on build INSTALL.vcxproj project (debug) with cmake 3.7.2
Bug #4431: "Lock 0" console command is a no-op
Bug #4432: Guards behaviour is incorrect if they do not have AI packages
Bug #4433: Guard behaviour is incorrect with Alarm = 0
Bug #4451: Script fails to compile when using "Begin, [ScriptName]" syntax
Bug #4452: Default terrain texture bleeds through texture transitions
Bug #4453: Quick keys behaviour is invalid for equipment
Bug #4454: AI opens doors too slow
Bug #4457: Item without CanCarry flag prevents shield autoequipping in dark areas
Bug #4458: AiWander console command handles idle chances incorrectly
Bug #4459: NotCell dialogue condition doesn't support partial matches
Bug #4461: "Open" spell from non-player caster isn't a crime
Bug #4469: Abot Silt Striders Model turn 90 degrees on horizontal
Bug #4474: No fallback when getVampireHead fails
Bug #4475: Scripted animations should not cause movement
Bug #4479: "Game" category on Advanced page is getting too long
Bug #4480: Segfalt in QuickKeysMenu when item no longer in inventory
Feature #3276: Editor: Search- Show number of (remaining) search results and indicate a search without any results
Feature #3641: Editor: Limit FPS in 3d preview window
Feature #4222: 360° screenshots
Feature #4256: Implement ToggleBorders (TB) console command
Feature #4324: Add CFBundleIdentifier in Info.plist to allow for macOS function key shortcuts
Feature #4345: Add equivalents for the command line commands to Launcher
Feature #4404: Editor: All EnumDelegate fields should have their items sorted alphabetically
Feature #4444: Per-group KF-animation files support
Feature #4466: Editor: Add option to ignore "Base" records when running verifier
0.44.0
------
@ -88,6 +147,7 @@
Bug #4412: openmw-iniimporter ignores data paths from config
Bug #4413: Moving with 0 strength uses all of your fatigue
Bug #4420: Camera flickering when I open up and close menus while sneaking
Bug #4424: [macOS] Cursor is either empty or garbage when compiled against macOS 10.13 SDK
Bug #4435: Item health is considered a signed integer
Bug #4441: Adding items to currently disabled weapon-wielding creatures crashes the game
Feature #1786: Round up encumbrance value in the encumbrance bar

@ -4,7 +4,7 @@ brew update
brew outdated cmake || brew upgrade cmake
brew outdated pkgconfig || brew upgrade pkgconfig
brew install $macos_qt_formula
brew install qt
curl https://downloads.openmw.org/osx/dependencies/openmw-deps-c40905f.zip -o ~/openmw-deps.zip
unzip ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null
curl -fSL -R -J https://downloads.openmw.org/osx/dependencies/openmw-deps-100d2e0.zip -o ~/openmw-deps.zip
unzip -o ~/openmw-deps.zip -d /private/tmp/openmw-deps > /dev/null

@ -4,14 +4,14 @@ export CXX=clang++
export CC=clang
DEPENDENCIES_ROOT="/private/tmp/openmw-deps/openmw-deps"
QT_PATH=`brew --prefix $macos_qt_formula`
QT_PATH=`brew --prefix qt`
mkdir build
cd build
cmake \
-D CMAKE_PREFIX_PATH="$DEPENDENCIES_ROOT;$QT_PATH" \
-D CMAKE_OSX_DEPLOYMENT_TARGET="10.9" \
-D CMAKE_OSX_SYSROOT="macosx10.12" \
-D CMAKE_OSX_SYSROOT="macosx10.13" \
-D CMAKE_BUILD_TYPE=Release \
-D OPENMW_OSX_DEPLOYMENT=TRUE \
-D DESIRED_QT_VERSION=5 \

@ -3,7 +3,7 @@ How to contribute to OpenMW
Not sure what to do with all your free time? Pick out a task from here:
https://bugs.openmw.org/
https://gitlab.com/OpenMW/openmw/issues
Currently, we are focused on completing the MW game experience and general polishing. Features out of this scope may be approved in some cases, but you should probably start a discussion first.

@ -3,7 +3,7 @@ TES3MP
[![Build Status](https://travis-ci.org/TES3MP/openmw-tes3mp.svg?branch=master)](https://travis-ci.org/TES3MP/openmw-tes3mp)
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), a free and open source engine recreation of the popular Bethesda Softworks game "The Elder Scrolls III: Morrowind".
TES3MP is a project aiming to add multiplayer functionality to [OpenMW](https://github.com/OpenMW/openmw), an open-source game engine that supports playing "The Elder Scrolls III: Morrowind" by Bethesda Softworks.
* TES3MP version: 0.6.3
* OpenMW version: 0.44.0

@ -8,6 +8,7 @@ set(LAUNCHER
settingspage.cpp
advancedpage.cpp
utils/cellnameloader.cpp
utils/profilescombobox.cpp
utils/textinputdialog.cpp
utils/lineedit.cpp
@ -24,6 +25,7 @@ set(LAUNCHER_HEADER
settingspage.hpp
advancedpage.hpp
utils/cellnameloader.hpp
utils/profilescombobox.hpp
utils/textinputdialog.hpp
utils/lineedit.hpp
@ -39,6 +41,7 @@ set(LAUNCHER_HEADER_MOC
settingspage.hpp
advancedpage.hpp
utils/cellnameloader.hpp
utils/textinputdialog.hpp
utils/profilescombobox.hpp
utils/lineedit.hpp

@ -1,10 +1,18 @@
#include "advancedpage.hpp"
#include <components/files/configurationmanager.hpp>
Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent)
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include <QFileDialog>
#include <QCompleter>
#include <components/contentselector/view/contentselector.hpp>
#include <components/contentselector/model/esmfile.hpp>
Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg,
Config::GameSettings &gameSettings,
Settings::Manager &engineSettings, QWidget *parent)
: QWidget(parent)
, mCfgMgr(cfg)
, mGameSettings(gameSettings)
, mEngineSettings(engineSettings)
{
setObjectName ("AdvancedPage");
@ -13,24 +21,60 @@ Launcher::AdvancedPage::AdvancedPage(Files::ConfigurationManager &cfg, Settings:
loadSettings();
}
void Launcher::AdvancedPage::loadCellsForAutocomplete(QStringList cellNames) {
// Set up an auto-completer for the "Start default character at" field
auto *completer = new QCompleter(cellNames);
completer->setCompletionMode(QCompleter::PopupCompletion);
completer->setCaseSensitivity(Qt::CaseSensitivity::CaseInsensitive);
startDefaultCharacterAtField->setCompleter(completer);
}
void Launcher::AdvancedPage::on_skipMenuCheckBox_stateChanged(int state) {
startDefaultCharacterAtLabel->setEnabled(state == Qt::Checked);
startDefaultCharacterAtField->setEnabled(state == Qt::Checked);
}
void Launcher::AdvancedPage::on_runScriptAfterStartupBrowseButton_clicked()
{
QString scriptFile = QFileDialog::getOpenFileName(
this,
QObject::tr("Select script file"),
QDir::currentPath(),
QString(tr("Text file (*.txt)")));
if (scriptFile.isEmpty())
return;
QFileInfo info(scriptFile);
if (!info.exists() || !info.isReadable())
return;
const QString path(QDir::toNativeSeparators(info.absoluteFilePath()));
}
bool Launcher::AdvancedPage::loadSettings()
{
// Testing
bool skipMenu = mGameSettings.value("skip-menu").toInt() == 1;
if (skipMenu) {
skipMenuCheckBox->setCheckState(Qt::Checked);
}
startDefaultCharacterAtLabel->setEnabled(skipMenu);
startDefaultCharacterAtField->setEnabled(skipMenu);
startDefaultCharacterAtField->setText(mGameSettings.value("start"));
runScriptAfterStartupField->setText(mGameSettings.value("script-run"));
// Game Settings
loadSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
loadSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
loadSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
loadSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
// Expected values are (0, 1, 2, 3)
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
// Match the index with the option. Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
// Input Settings
loadSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
loadSettingBool(grabCursorCheckBox, "grab cursor", "Input");
@ -40,6 +84,16 @@ bool Launcher::AdvancedPage::loadSettings()
loadSettingBool(timePlayedCheckbox, "timeplayed", "Saves");
maximumQuicksavesComboBox->setValue(mEngineSettings.getInt("max quicksaves", "Saves"));
// User Interface Settings
loadSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
loadSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
loadSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
loadSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
int showOwnedIndex = mEngineSettings.getInt("show owned", "Game");
// Match the index with the option (only 0, 1, 2, or 3 are valid). Will default to 0 if invalid.
if (showOwnedIndex >= 0 && showOwnedIndex <= 3)
showOwnedComboBox->setCurrentIndex(showOwnedIndex);
// Other Settings
QString screenshotFormatString = QString::fromStdString(mEngineSettings.getString("screenshot format", "General")).toUpper();
if (screenshotFormatComboBox->findText(screenshotFormatString) == -1)
@ -54,20 +108,25 @@ void Launcher::AdvancedPage::saveSettings()
// Ensure we only set the new settings if they changed. This is to avoid cluttering the
// user settings file (which by definition should only contain settings the user has touched)
// Testing
int skipMenu = skipMenuCheckBox->checkState() == Qt::Checked;
if (skipMenu != mGameSettings.value("skip-menu").toInt())
mGameSettings.setValue("skip-menu", QString::number(skipMenu));
QString startCell = startDefaultCharacterAtField->text();
if (startCell != mGameSettings.value("start")) {
mGameSettings.setValue("start", startCell);
}
QString scriptRun = runScriptAfterStartupField->text();
if (scriptRun != mGameSettings.value("script-run"))
mGameSettings.setValue("script-run", scriptRun);
// Game Settings
saveSettingBool(canLootDuringDeathAnimationCheckBox, "can loot during death animation", "Game");
saveSettingBool(followersAttackOnSightCheckBox, "followers attack on sight", "Game");
saveSettingBool(preventMerchantEquippingCheckBox, "prevent merchant equipping", "Game");
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
saveSettingBool(rebalanceSoulGemValuesCheckBox, "rebalance soul gem values", "Game");
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
// Input Settings
saveSettingBool(allowThirdPersonZoomCheckBox, "allow third person zoom", "Input");
saveSettingBool(grabCursorCheckBox, "grab cursor", "Input");
@ -80,6 +139,15 @@ void Launcher::AdvancedPage::saveSettings()
mEngineSettings.setInt("max quicksaves", "Saves", maximumQuicksaves);
}
// User Interface Settings
saveSettingBool(showEffectDurationCheckBox, "show effect duration", "Game");
saveSettingBool(showEnchantChanceCheckBox, "show enchant chance", "Game");
saveSettingBool(showMeleeInfoCheckBox, "show melee info", "Game");
saveSettingBool(showProjectileDamageCheckBox, "show projectile damage", "Game");
int showOwnedCurrentIndex = showOwnedComboBox->currentIndex();
if (showOwnedCurrentIndex != mEngineSettings.getInt("show owned", "Game"))
mEngineSettings.setInt("show owned", "Game", showOwnedCurrentIndex);
// Other Settings
std::string screenshotFormatString = screenshotFormatComboBox->currentText().toLower().toStdString();
if (screenshotFormatString != mEngineSettings.getString("screenshot format", "General"))
@ -95,4 +163,9 @@ void Launcher::AdvancedPage::saveSettingBool(QCheckBox *checkbox, const std::str
bool cValue = checkbox->checkState();
if (cValue != mEngineSettings.getBool(setting, group))
mEngineSettings.setBool(setting, group, cValue);
}
void Launcher::AdvancedPage::slotLoadedCellsChanged(QStringList cellNames)
{
loadCellsForAutocomplete(cellNames);
}

@ -8,6 +8,7 @@
#include <components/settings/settings.hpp>
namespace Files { struct ConfigurationManager; }
namespace Config { class GameSettings; }
namespace Launcher
{
@ -16,15 +17,29 @@ namespace Launcher
Q_OBJECT
public:
AdvancedPage(Files::ConfigurationManager &cfg, Settings::Manager &engineSettings, QWidget *parent = 0);
AdvancedPage(Files::ConfigurationManager &cfg, Config::GameSettings &gameSettings,
Settings::Manager &engineSettings, QWidget *parent = 0);
bool loadSettings();
void saveSettings();
public slots:
void slotLoadedCellsChanged(QStringList cellNames);
private slots:
void on_skipMenuCheckBox_stateChanged(int state);
void on_runScriptAfterStartupBrowseButton_clicked();
private:
Files::ConfigurationManager &mCfgMgr;
Config::GameSettings &mGameSettings;
Settings::Manager &mEngineSettings;
/**
* Load the cells associated with the given content files for use in autocomplete
* @param filePaths the file paths of the content files to be examined
*/
void loadCellsForAutocomplete(QStringList filePaths);
void loadSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
void saveSettingBool(QCheckBox *checkbox, const std::string& setting, const std::string& group);
};

@ -7,7 +7,10 @@
#include <QCheckBox>
#include <QMenu>
#include <QSortFilterProxyModel>
#include <thread>
#include <mutex>
#include <apps/launcher/utils/cellnameloader.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/contentselector/model/esmfile.hpp>
@ -16,6 +19,7 @@
#include <components/config/gamesettings.hpp>
#include <components/config/launchersettings.hpp>
#include <iostream>
#include "utils/textinputdialog.hpp"
#include "utils/profilescombobox.hpp"
@ -40,6 +44,13 @@ Launcher::DataFilesPage::DataFilesPage(Files::ConfigurationManager &cfg, Config:
buildView();
loadSettings();
// Connect signal and slot after the settings have been loaded. We only care about the user changing
// the addons and don't want to get signals of the system doing it during startup.
connect(mSelector, SIGNAL(signalAddonDataChanged(QModelIndex,QModelIndex)),
this, SLOT(slotAddonDataChanged()));
// Call manually to indicate all changes to addon data during startup.
slotAddonDataChanged();
}
void Launcher::DataFilesPage::buildView()
@ -142,6 +153,17 @@ void Launcher::DataFilesPage::saveSettings(const QString &profile)
mGameSettings.setContentList(fileNames);
}
QStringList Launcher::DataFilesPage::selectedFilePaths()
{
//retrieve the files selected for the profile
ContentSelectorModel::ContentFileList items = mSelector->selectedFiles();
QStringList filePaths;
foreach(const ContentSelectorModel::EsmFile *item, items) {
filePaths.append(item->filePath());
}
return filePaths;
}
void Launcher::DataFilesPage::removeProfile(const QString &profile)
{
mLauncherSettings.removeContentList(profile);
@ -308,3 +330,31 @@ bool Launcher::DataFilesPage::showDeleteMessageBox (const QString &text)
return (msgBox.clickedButton() == deleteButton);
}
void Launcher::DataFilesPage::slotAddonDataChanged()
{
QStringList selectedFiles = selectedFilePaths();
if (previousSelectedFiles != selectedFiles) {
previousSelectedFiles = selectedFiles;
// Loading cells for core Morrowind + Expansions takes about 0.2 seconds, which is enough to cause a
// barely perceptible UI lag. Splitting into its own thread to alleviate that.
std::thread loadCellsThread(&DataFilesPage::reloadCells, this, selectedFiles);
loadCellsThread.detach();
}
}
// Mutex lock to run reloadCells synchronously.
std::mutex _reloadCellsMutex;
void Launcher::DataFilesPage::reloadCells(QStringList selectedFiles)
{
// Use a mutex lock so that we can prevent two threads from executing the rest of this code at the same time
// Based on https://stackoverflow.com/a/5429695/531762
std::unique_lock<std::mutex> lock(_reloadCellsMutex);
// The following code will run only if there is not another thread currently running it
CellNameLoader cellNameLoader;
QStringList cellNamesList = QStringList::fromSet(cellNameLoader.getCellNames(selectedFiles));
std::sort(cellNamesList.begin(), cellNamesList.end());
emit signalLoadedCellsChanged(cellNamesList);
}

@ -7,6 +7,7 @@
#include <QDir>
#include <QFile>
#include <QStringList>
class QSortFilterProxyModel;
class QAbstractItemModel;
@ -41,8 +42,15 @@ namespace Launcher
void saveSettings(const QString &profile = "");
bool loadSettings();
/**
* Returns the file paths of all selected content files
* @return the file paths of all selected content files
*/
QStringList selectedFilePaths();
signals:
void signalProfileChanged (int index);
void signalLoadedCellsChanged(QStringList selectedFiles);
public slots:
void slotProfileChanged (int index);
@ -52,6 +60,7 @@ namespace Launcher
void slotProfileChangedByUser(const QString &previous, const QString &current);
void slotProfileRenamed(const QString &previous, const QString &current);
void slotProfileDeleted(const QString &item);
void slotAddonDataChanged ();
void updateOkButton(const QString &text);
@ -72,7 +81,7 @@ namespace Launcher
Config::LauncherSettings &mLauncherSettings;
QString mPreviousProfile;
QStringList previousSelectedFiles;
QString mDataLocal;
void setPluginsCheckstates(Qt::CheckState state);
@ -87,6 +96,7 @@ namespace Launcher
void addProfile (const QString &profile, bool setAsCurrent);
void checkForDefaultProfile();
void populateFileViews(const QString& contentModelName);
void reloadCells(QStringList selectedFiles);
class PathIterator
{

@ -1,6 +1,7 @@
#include "graphicspage.hpp"
#include <boost/math/common_factor.hpp>
#include <csignal>
#include <QDesktopWidget>
#include <QMessageBox>
#include <QDir>
@ -11,6 +12,7 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include <SDL_video.h>
#include <components/files/configurationmanager.hpp>
@ -46,8 +48,28 @@ Launcher::GraphicsPage::GraphicsPage(Files::ConfigurationManager &cfg, Settings:
}
bool Launcher::GraphicsPage::connectToSdl() {
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
// Required for determining screen resolution and such on the Graphics tab
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
return false;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
return true;
}
bool Launcher::GraphicsPage::setupSDL()
{
bool sdlConnectSuccessful = connectToSdl();
if (!sdlConnectSuccessful)
{
return false;
}
int displays = SDL_GetNumVideoDisplays();
if (displays < 0)
@ -67,6 +89,9 @@ bool Launcher::GraphicsPage::setupSDL()
screenComboBox->addItem(QString(tr("Screen ")) + QString::number(i + 1));
}
// Disconnect from SDL processes
SDL_Quit();
return true;
}

@ -37,6 +37,11 @@ namespace Launcher
QStringList getAvailableResolutions(int screen);
QRect getMaximumResolution();
/**
* Connect to the SDL so that we can use it to determine graphics
* @return whether or not connecting to SDL is successful
*/
bool connectToSdl();
bool setupSDL();
};
}

@ -1,5 +1,4 @@
#include <iostream>
#include <csignal>
#include <QApplication>
#include <QTextCodec>
@ -12,24 +11,12 @@
#define MAC_OS_X_VERSION_MIN_REQUIRED __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__
#endif // MAC_OS_X_VERSION_MIN_REQUIRED
#include <SDL.h>
#include "maindialog.hpp"
int main(int argc, char *argv[])
{
try
{
SDL_SetHint(SDL_HINT_RENDER_DRIVER, "software");
SDL_SetMainReady();
if (SDL_Init(SDL_INIT_VIDEO) != 0)
{
qDebug() << "SDL_Init failed: " << QString::fromUtf8(SDL_GetError());
return 0;
}
signal(SIGINT, SIG_DFL); // We don't want to use the SDL event loop in the launcher,
// so reset SIGINT which SDL wants to redirect to an SDL_Quit event.
QApplication app(argc, argv);
// Now we make sure the current dir is set to application path
@ -46,13 +33,11 @@ int main(int argc, char *argv[])
if (result == Launcher::FirstRunDialogResultContinue)
mainWin.show();
int returnValue = app.exec();
SDL_Quit();
return returnValue;
return app.exec();
}
catch (std::exception& e)
{
std::cerr << "ERROR: " << e.what() << std::endl;
return 0;
}
}
}

@ -119,7 +119,7 @@ void Launcher::MainDialog::createPages()
mDataFilesPage = new DataFilesPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mGraphicsPage = new GraphicsPage(mCfgMgr, mEngineSettings, this);
mSettingsPage = new SettingsPage(mCfgMgr, mGameSettings, mLauncherSettings, this);
mAdvancedPage = new AdvancedPage(mCfgMgr, mEngineSettings, this);
mAdvancedPage = new AdvancedPage(mCfgMgr, mGameSettings, mEngineSettings, this);
// Set the combobox of the play page to imitate the combobox on the datafilespage
mPlayPage->setProfilesModel(mDataFilesPage->profilesModel());
@ -139,6 +139,8 @@ void Launcher::MainDialog::createPages()
connect(mPlayPage, SIGNAL(signalProfileChanged(int)), mDataFilesPage, SLOT(slotProfileChanged(int)));
connect(mDataFilesPage, SIGNAL(signalProfileChanged(int)), mPlayPage, SLOT(setProfilesIndex(int)));
// Using Qt::QueuedConnection because signal is emitted in a subthread and slot is in the main thread
connect(mDataFilesPage, SIGNAL(signalLoadedCellsChanged(QStringList)), mAdvancedPage, SLOT(slotLoadedCellsChanged(QStringList)), Qt::QueuedConnection);
}

@ -0,0 +1,48 @@
#include "cellnameloader.hpp"
#include <components/esm/loadcell.hpp>
#include <components/contentselector/view/contentselector.hpp>
QSet<QString> CellNameLoader::getCellNames(QStringList &contentPaths)
{
QSet<QString> cellNames;
ESM::ESMReader esmReader;
// Loop through all content files
for (auto &contentPath : contentPaths) {
esmReader.open(contentPath.toStdString());
// Loop through all records
while(esmReader.hasMoreRecs())
{
ESM::NAME recordName = esmReader.getRecName();
esmReader.getRecHeader();
if (isCellRecord(recordName)) {
QString cellName = getCellName(esmReader);
if (!cellName.isEmpty()) {
cellNames.insert(cellName);
}
}
// Stop loading content for this record and continue to the next
esmReader.skipRecord();
}
}
return cellNames;
}
bool CellNameLoader::isCellRecord(ESM::NAME &recordName)
{
return recordName.intval == ESM::REC_CELL;
}
QString CellNameLoader::getCellName(ESM::ESMReader &esmReader)
{
ESM::Cell cell;
bool isDeleted = false;
cell.loadNameAndData(esmReader, isDeleted);
return QString::fromStdString(cell.mName);
}

@ -0,0 +1,41 @@
#ifndef OPENMW_CELLNAMELOADER_H
#define OPENMW_CELLNAMELOADER_H
#include <QComboBox>
#include <QSet>
#include <QString>
#include <components/esm/esmreader.hpp>
namespace ESM {class ESMReader; struct Cell;}
namespace ContentSelectorView {class ContentSelector;}
class CellNameLoader {
public:
/**
* Returns the names of all cells contained within the given content files
* @param contentPaths the file paths of each content file to be examined
* @return the names of all cells
*/
QSet<QString> getCellNames(QStringList &contentPaths);
private:
/**
* Returns whether or not the given record is of type "Cell"
* @param name The name associated with the record
* @return whether or not the given record is of type "Cell"
*/
bool isCellRecord(ESM::NAME &name);
/**
* Returns the name of the cell
* @param esmReader the reader currently pointed to a loaded cell
* @return the name of the cell
*/
QString getCellName(ESM::ESMReader &esmReader);
};
#endif //OPENMW_CELLNAMELOADER_H

@ -320,12 +320,13 @@ CSMDoc::Document::Document (const Files::ConfigurationManager& configuration,
connect (&mUndoStack, SIGNAL (cleanChanged (bool)), this, SLOT (modificationStateChanged (bool)));
connect (&mTools, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (&mTools, SIGNAL (done (int, bool)), this, SIGNAL (operationDone (int, bool)));
connect (&mTools, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));
connect (&mTools, SIGNAL (mergeDone (CSMDoc::Document*)),
this, SIGNAL (mergeDone (CSMDoc::Document*)));
connect (&mSaving, SIGNAL (progress (int, int, int)), this, SLOT (progress (int, int, int)));
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone (int, bool)));
connect (&mSaving, SIGNAL (done (int, bool)), this, SLOT (operationDone2 (int, bool)));
connect (
&mSaving, SIGNAL (reportMessage (const CSMDoc::Message&, int)),
@ -437,7 +438,7 @@ void CSMDoc::Document::reportMessage (const CSMDoc::Message& message, int type)
std::cout << message.mMessage << std::endl;
}
void CSMDoc::Document::operationDone (int type, bool failed)
void CSMDoc::Document::operationDone2 (int type, bool failed)
{
if (type==CSMDoc::State_Saving && !failed)
mDirty = false;

@ -168,13 +168,15 @@ namespace CSMDoc
/// document. This signal must be handled to avoid a leak.
void mergeDone (CSMDoc::Document *document);
void operationDone (int type, bool failed);
private slots:
void modificationStateChanged (bool clean);
void reportMessage (const CSMDoc::Message& message, int type);
void operationDone (int type, bool failed);
void operationDone2 (int type, bool failed);
void runStateChanged();

@ -123,6 +123,7 @@ void CSMPrefs::State::declare()
declareEnum ("double-s", "Shift Double Click", actionRemove).addValues (reportValues);
declareEnum ("double-c", "Control Double Click", actionEditAndRemove).addValues (reportValues);
declareEnum ("double-sc", "Shift Control Double Click", actionNone).addValues (reportValues);
declareBool("ignore-base-records", "Ignore base records in verifier", false);
declareCategory ("Search & Replace");
declareInt ("char-before", "Characters before search string", 10).
@ -200,6 +201,9 @@ void CSMPrefs::State::declare()
declareDouble ("rotate-factor", "Free rotation factor", 0.007).setPrecision(4).setRange(0.0001, 0.1);
declareCategory ("Rendering");
declareInt ("framerate-limit", "FPS limit", 60).
setTooltip("Framerate limit in 3D preview windows. Zero value means \"unlimited\".").
setRange(0, 10000);
declareInt ("camera-fov", "Camera FOV", 90).setRange(10, 170);
declareBool ("camera-ortho", "Orthographic projection for camera", false);
declareInt ("camera-ortho-size", "Orthographic projection size parameter", 100).

@ -5,14 +5,20 @@
#include <components/esm/loadbsgn.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::BirthsignCheckStage::BirthsignCheckStage (const CSMWorld::IdCollection<ESM::BirthSign>& birthsigns)
: mBirthsigns (birthsigns)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::BirthsignCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mBirthsigns.getSize();
}
@ -20,7 +26,8 @@ void CSMTools::BirthsignCheckStage::perform (int stage, CSMDoc::Messages& messag
{
const CSMWorld::Record<ESM::BirthSign>& record = mBirthsigns.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::BirthSign& birthsign = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class BirthsignCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::BirthSign>& mBirthsigns;
bool mIgnoreBaseRecords;
public:

@ -1,5 +1,7 @@
#include "bodypartcheck.hpp"
#include "../prefs/state.hpp"
CSMTools::BodyPartCheckStage::BodyPartCheckStage(
const CSMWorld::IdCollection<ESM::BodyPart> &bodyParts,
const CSMWorld::Resources &meshes,
@ -7,10 +9,14 @@ CSMTools::BodyPartCheckStage::BodyPartCheckStage(
mBodyParts(bodyParts),
mMeshes(meshes),
mRaces(races)
{ }
{
mIgnoreBaseRecords = false;
}
int CSMTools::BodyPartCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mBodyParts.getSize();
}
@ -18,7 +24,8 @@ void CSMTools::BodyPartCheckStage::perform (int stage, CSMDoc::Messages &message
{
const CSMWorld::Record<ESM::BodyPart> &record = mBodyParts.getRecord(stage);
if ( record.isDeleted() )
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::BodyPart &bodyPart = record.get();

@ -17,6 +17,7 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::BodyPart> &mBodyParts;
const CSMWorld::Resources &mMeshes;
const CSMWorld::IdCollection<ESM::Race> &mRaces;
bool mIgnoreBaseRecords;
public:
BodyPartCheckStage(

@ -6,14 +6,20 @@
#include <components/esm/loadclas.hpp>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::ClassCheckStage::ClassCheckStage (const CSMWorld::IdCollection<ESM::Class>& classes)
: mClasses (classes)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::ClassCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mClasses.getSize();
}
@ -21,7 +27,8 @@ void CSMTools::ClassCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Class>& record = mClasses.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Class& class_ = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class ClassCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Class>& mClasses;
bool mIgnoreBaseRecords;
public:

@ -6,14 +6,20 @@
#include <components/esm/loadfact.hpp>
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::FactionCheckStage::FactionCheckStage (const CSMWorld::IdCollection<ESM::Faction>& factions)
: mFactions (factions)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::FactionCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mFactions.getSize();
}
@ -21,7 +27,8 @@ void CSMTools::FactionCheckStage::perform (int stage, CSMDoc::Messages& messages
{
const CSMWorld::Record<ESM::Faction>& record = mFactions.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Faction& faction = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class FactionCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
bool mIgnoreBaseRecords;
public:

@ -2,14 +2,20 @@
#include <sstream>
#include "../prefs/state.hpp"
#include "../world/defaultgmsts.hpp"
CSMTools::GmstCheckStage::GmstCheckStage(const CSMWorld::IdCollection<ESM::GameSetting>& gameSettings)
: mGameSettings(gameSettings)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::GmstCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mGameSettings.getSize();
}
@ -17,7 +23,8 @@ void CSMTools::GmstCheckStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::GameSetting>& record = mGameSettings.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::GameSetting& gmst = record.get();

@ -25,6 +25,7 @@ namespace CSMTools
private:
const CSMWorld::IdCollection<ESM::GameSetting>& mGameSettings;
bool mIgnoreBaseRecords;
std::string varTypeToString(ESM::VarType);

@ -3,13 +3,19 @@
#include <set>
#include <sstream>
#include "../prefs/state.hpp"
CSMTools::JournalCheckStage::JournalCheckStage(const CSMWorld::IdCollection<ESM::Dialogue> &journals,
const CSMWorld::InfoCollection& journalInfos)
: mJournals(journals), mJournalInfos(journalInfos)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::JournalCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mJournals.getSize();
}
@ -17,7 +23,8 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Dialogue> &journalRecord = mJournals.getRecord(stage);
if (journalRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && journalRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || journalRecord.isDeleted())
return;
const ESM::Dialogue &journal = journalRecord.get();
@ -43,6 +50,10 @@ void CSMTools::JournalCheckStage::perform(int stage, CSMDoc::Messages& messages)
statusNamedCount += 1;
}
// Skip "Base" records (setting!)
if (mIgnoreBaseRecords && infoRecord.mState == CSMWorld::RecordBase::State_BaseOnly)
continue;
if (journalInfo.mResponse.empty())
{
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_JournalInfo, journalInfo.mId);

@ -28,6 +28,7 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Dialogue>& mJournals;
const CSMWorld::InfoCollection& mJournalInfos;
bool mIgnoreBaseRecords;
};
}

@ -2,6 +2,8 @@
#include <components/misc/resourcehelpers.hpp>
#include "../prefs/state.hpp"
#include "../world/resources.hpp"
#include "../world/data.hpp"
@ -77,16 +79,26 @@ CSMTools::MagicEffectCheckStage::MagicEffectCheckStage(const CSMWorld::IdCollect
mReferenceables(referenceables),
mIcons(icons),
mTextures(textures)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::MagicEffectCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mMagicEffects.getSize();
}
void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
ESM::MagicEffect effect = mMagicEffects.getRecord(stage).get();
const CSMWorld::Record<ESM::MagicEffect> &record = mMagicEffects.getRecord(stage);
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
ESM::MagicEffect effect = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
if (effect.mData.mBaseCost < 0.0f)

@ -24,6 +24,7 @@ namespace CSMTools
const CSMWorld::RefIdCollection &mReferenceables;
const CSMWorld::Resources &mIcons;
const CSMWorld::Resources &mTextures;
bool mIgnoreBaseRecords;
private:
bool isTextureExists(const std::string &texture, bool isIcon) const;

@ -3,6 +3,8 @@
#include <sstream>
#include <algorithm>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
#include "../world/idcollection.hpp"
#include "../world/subcellcollection.hpp"
@ -10,10 +12,14 @@
CSMTools::PathgridCheckStage::PathgridCheckStage (const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids)
: mPathgrids (pathgrids)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::PathgridCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mPathgrids.getSize();
}
@ -21,7 +27,8 @@ void CSMTools::PathgridCheckStage::perform (int stage, CSMDoc::Messages& message
{
const CSMWorld::Record<CSMWorld::Pathgrid>& record = mPathgrids.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const CSMWorld::Pathgrid& pathgrid = record.get();

@ -25,6 +25,7 @@ namespace CSMTools
{
const CSMWorld::SubCellCollection<CSMWorld::Pathgrid,
CSMWorld::IdAccessor<CSMWorld::Pathgrid> >& mPathgrids;
bool mIgnoreBaseRecords;
public:

@ -4,6 +4,8 @@
#include <components/esm/loadrace.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& messages)
@ -15,6 +17,14 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
const ESM::Race& race = record.get();
// Consider mPlayable flag even when "Base" records are ignored
if (race.mData.mFlags & 0x1)
mPlayable = true;
// Skip "Base" records (setting!)
if (mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly)
return;
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Race, race.mId);
// test for empty name and description
@ -38,10 +48,6 @@ void CSMTools::RaceCheckStage::performPerRecord (int stage, CSMDoc::Messages& me
if (race.mData.mWeight.mFemale<0)
messages.push_back (std::make_pair (id, "female " + race.mId + " has negative weight"));
// remember playable flag
if (race.mData.mFlags & 0x1)
mPlayable = true;
/// \todo check data members that can't be edited in the table view
}
@ -55,11 +61,15 @@ void CSMTools::RaceCheckStage::performFinal (CSMDoc::Messages& messages)
CSMTools::RaceCheckStage::RaceCheckStage (const CSMWorld::IdCollection<ESM::Race>& races)
: mRaces (races), mPlayable (false)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::RaceCheckStage::setup()
{
mPlayable = false;
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mRaces.getSize()+1;
}

@ -14,6 +14,7 @@ namespace CSMTools
{
const CSMWorld::IdCollection<ESM::Race>& mRaces;
bool mPlayable;
bool mIgnoreBaseRecords;
void performPerRecord (int stage, CSMDoc::Messages& messages);

@ -2,6 +2,8 @@
#include <components/misc/stringops.hpp>
#include "../prefs/state.hpp"
#include "../world/record.hpp"
#include "../world/universalid.hpp"
@ -18,6 +20,7 @@ CSMTools::ReferenceableCheckStage::ReferenceableCheckStage(
mScripts(scripts),
mPlayerPresent(false)
{
mIgnoreBaseRecords = false;
}
void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& messages)
@ -228,6 +231,8 @@ void CSMTools::ReferenceableCheckStage::perform (int stage, CSMDoc::Messages& me
int CSMTools::ReferenceableCheckStage::setup()
{
mPlayerPresent = false;
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mReferencables.getSize() + 1;
}
@ -238,7 +243,8 @@ void CSMTools::ReferenceableCheckStage::bookCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Book& book = (dynamic_cast<const CSMWorld::Record<ESM::Book>& >(baseRecord)).get();
@ -257,7 +263,8 @@ void CSMTools::ReferenceableCheckStage::activatorCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Activator& activator = (dynamic_cast<const CSMWorld::Record<ESM::Activator>& >(baseRecord)).get();
@ -278,7 +285,8 @@ void CSMTools::ReferenceableCheckStage::potionCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Potion& potion = (dynamic_cast<const CSMWorld::Record<ESM::Potion>& >(baseRecord)).get();
@ -299,7 +307,8 @@ void CSMTools::ReferenceableCheckStage::apparatusCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Apparatus& apparatus = (dynamic_cast<const CSMWorld::Record<ESM::Apparatus>& >(baseRecord)).get();
@ -320,7 +329,8 @@ void CSMTools::ReferenceableCheckStage::armorCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Armor& armor = (dynamic_cast<const CSMWorld::Record<ESM::Armor>& >(baseRecord)).get();
@ -347,7 +357,8 @@ void CSMTools::ReferenceableCheckStage::clothingCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Clothing& clothing = (dynamic_cast<const CSMWorld::Record<ESM::Clothing>& >(baseRecord)).get();
@ -365,7 +376,8 @@ void CSMTools::ReferenceableCheckStage::containerCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Container& container = (dynamic_cast<const CSMWorld::Record<ESM::Container>& >(baseRecord)).get();
@ -397,7 +409,8 @@ void CSMTools::ReferenceableCheckStage::creatureCheck (
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Creature& creature = (dynamic_cast<const CSMWorld::Record<ESM::Creature>&>(baseRecord)).get();
@ -473,7 +486,8 @@ void CSMTools::ReferenceableCheckStage::doorCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Door& door = (dynamic_cast<const CSMWorld::Record<ESM::Door>&>(baseRecord)).get();
@ -497,7 +511,8 @@ void CSMTools::ReferenceableCheckStage::ingredientCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Ingredient& ingredient = (dynamic_cast<const CSMWorld::Record<ESM::Ingredient>& >(baseRecord)).get();
@ -516,10 +531,9 @@ void CSMTools::ReferenceableCheckStage::creaturesLevListCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
}
const ESM::CreatureLevList& CreatureLevList = (dynamic_cast<const CSMWorld::Record<ESM::CreatureLevList>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_CreatureLevelledList, CreatureLevList.mId); //CreatureLevList but Type_CreatureLevelledList :/
@ -534,10 +548,9 @@ void CSMTools::ReferenceableCheckStage::itemLevelledListCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
{
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
}
const ESM::ItemLevList& ItemLevList = (dynamic_cast<const CSMWorld::Record<ESM::ItemLevList>& >(baseRecord)).get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_ItemLevelledList, ItemLevList.mId);
@ -551,7 +564,8 @@ void CSMTools::ReferenceableCheckStage::lightCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Light& light = (dynamic_cast<const CSMWorld::Record<ESM::Light>& >(baseRecord)).get();
@ -574,7 +588,8 @@ void CSMTools::ReferenceableCheckStage::lockpickCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Lockpick& lockpick = (dynamic_cast<const CSMWorld::Record<ESM::Lockpick>& >(baseRecord)).get();
@ -595,7 +610,8 @@ void CSMTools::ReferenceableCheckStage::miscCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Miscellaneous& miscellaneous = (dynamic_cast<const CSMWorld::Record<ESM::Miscellaneous>& >(baseRecord)).get();
@ -619,6 +635,14 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
const ESM::NPC& npc = (dynamic_cast<const CSMWorld::Record<ESM::NPC>& >(baseRecord)).get();
CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Npc, npc.mId);
//Detect if player is present
if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl?
mPlayerPresent = true;
// Skip "Base" records (setting!)
if (mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly)
return;
short level(npc.mNpdt.mLevel);
char disposition(npc.mNpdt.mDisposition);
char reputation(npc.mNpdt.mReputation);
@ -626,10 +650,6 @@ void CSMTools::ReferenceableCheckStage::npcCheck (
//Don't know what unknown is for
int gold(npc.mNpdt.mGold);
//Detect if player is present
if (Misc::StringUtils::ciEqual(npc.mId, "player")) //Happy now, scrawl?
mPlayerPresent = true;
if (npc.mNpdtType == ESM::NPC::NPC_WITH_AUTOCALCULATED_STATS) //12 = autocalculated
{
if ((npc.mFlags & ESM::NPC::Autocalc) == 0) //0x0010 = autocalculated flag
@ -728,7 +748,8 @@ void CSMTools::ReferenceableCheckStage::weaponCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Weapon& weapon = (dynamic_cast<const CSMWorld::Record<ESM::Weapon>& >(baseRecord)).get();
@ -808,7 +829,8 @@ void CSMTools::ReferenceableCheckStage::probeCheck(
{
const CSMWorld::RecordBase& baseRecord = records.getRecord(stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Probe& probe = (dynamic_cast<const CSMWorld::Record<ESM::Probe>& >(baseRecord)).get();
@ -827,7 +849,8 @@ void CSMTools::ReferenceableCheckStage::repairCheck (
{
const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Repair& repair = (dynamic_cast<const CSMWorld::Record<ESM::Repair>& >(baseRecord)).get();
@ -846,7 +869,8 @@ void CSMTools::ReferenceableCheckStage::staticCheck (
{
const CSMWorld::RecordBase& baseRecord = records.getRecord (stage);
if (baseRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && baseRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || baseRecord.isDeleted())
return;
const ESM::Static& staticElement = (dynamic_cast<const CSMWorld::Record<ESM::Static>& >(baseRecord)).get();

@ -82,6 +82,7 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mPlayerPresent;
bool mIgnoreBaseRecords;
};
}
#endif // REFERENCEABLECHECKSTAGE_H

@ -1,5 +1,7 @@
#include "referencecheck.hpp"
#include "../prefs/state.hpp"
CSMTools::ReferenceCheckStage::ReferenceCheckStage(
const CSMWorld::RefCollection& references,
const CSMWorld::RefIdCollection& referencables,
@ -12,13 +14,15 @@ CSMTools::ReferenceCheckStage::ReferenceCheckStage(
mCells(cells),
mFactions(factions)
{
mIgnoreBaseRecords = false;
}
void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
const CSMWorld::Record<CSMWorld::CellRef>& record = mReferences.getRecord(stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const CSMWorld::CellRef& cellRef = record.get();
@ -100,5 +104,7 @@ void CSMTools::ReferenceCheckStage::perform(int stage, CSMDoc::Messages &message
int CSMTools::ReferenceCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mReferences.getSize();
}

@ -23,6 +23,7 @@ namespace CSMTools
const CSMWorld::RefIdData& mDataSet;
const CSMWorld::IdCollection<CSMWorld::Cell>& mCells;
const CSMWorld::IdCollection<ESM::Faction>& mFactions;
bool mIgnoreBaseRecords;
};
}

@ -5,14 +5,20 @@
#include <components/esm/loadregn.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::RegionCheckStage::RegionCheckStage (const CSMWorld::IdCollection<ESM::Region>& regions)
: mRegions (regions)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::RegionCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mRegions.getSize();
}
@ -20,7 +26,8 @@ void CSMTools::RegionCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Region>& record = mRegions.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Region& region = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class RegionCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Region>& mRegions;
bool mIgnoreBaseRecords;
public:

@ -60,6 +60,8 @@ CSMTools::ScriptCheckStage::ScriptCheckStage (const CSMDoc::Document& document)
Compiler::registerExtensions (mExtensions);
mContext.setExtensions (&mExtensions);
mIgnoreBaseRecords = false;
}
int CSMTools::ScriptCheckStage::setup()
@ -78,17 +80,25 @@ int CSMTools::ScriptCheckStage::setup()
mId.clear();
Compiler::ErrorHandler::reset();
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mDocument.getData().getScripts().getSize();
}
void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Script> &record = mDocument.getData().getScripts().getRecord(stage);
mId = mDocument.getData().getScripts().getId (stage);
if (mDocument.isBlacklisted (
CSMWorld::UniversalId (CSMWorld::UniversalId::Type_Script, mId)))
return;
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
mMessages = &messages;
switch (mWarningMode)
@ -100,10 +110,8 @@ void CSMTools::ScriptCheckStage::perform (int stage, CSMDoc::Messages& messages)
try
{
const CSMWorld::Data& data = mDocument.getData();
mFile = data.getScripts().getRecord (stage).get().mId;
std::istringstream input (data.getScripts().getRecord (stage).get().mScriptText);
mFile = record.get().mId;
std::istringstream input (record.get().mScriptText);
Compiler::Scanner scanner (*this, input, mContext.getExtensions());

@ -32,6 +32,7 @@ namespace CSMTools
std::string mFile;
CSMDoc::Messages *mMessages;
WarningMode mWarningMode;
bool mIgnoreBaseRecords;
CSMDoc::Message::Severity getSeverity (Type type);

@ -4,14 +4,20 @@
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::SkillCheckStage::SkillCheckStage (const CSMWorld::IdCollection<ESM::Skill>& skills)
: mSkills (skills)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::SkillCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSkills.getSize();
}
@ -19,7 +25,8 @@ void CSMTools::SkillCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Skill>& record = mSkills.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Skill& skill = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class SkillCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Skill>& mSkills;
bool mIgnoreBaseRecords;
public:

@ -4,14 +4,20 @@
#include <components/esm/loadskil.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::SoundCheckStage::SoundCheckStage (const CSMWorld::IdCollection<ESM::Sound>& sounds)
: mSounds (sounds)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::SoundCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSounds.getSize();
}
@ -19,7 +25,8 @@ void CSMTools::SoundCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Sound>& record = mSounds.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Sound& sound = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class SoundCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Sound>& mSounds;
bool mIgnoreBaseRecords;
public:

@ -2,6 +2,8 @@
#include <sstream>
#include "../prefs/state.hpp"
#include "../world/refiddata.hpp"
#include "../world/universalid.hpp"
@ -11,20 +13,24 @@ CSMTools::SoundGenCheckStage::SoundGenCheckStage(const CSMWorld::IdCollection<ES
: mSoundGens(soundGens),
mSounds(sounds),
mReferenceables(referenceables)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::SoundGenCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSoundGens.getSize();
}
void CSMTools::SoundGenCheckStage::perform(int stage, CSMDoc::Messages &messages)
{
const CSMWorld::Record<ESM::SoundGenerator> &record = mSoundGens.getRecord(stage);
if (record.isDeleted())
{
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
}
const ESM::SoundGenerator& soundGen = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_SoundGen, soundGen.mId);

@ -13,6 +13,7 @@ namespace CSMTools
const CSMWorld::IdCollection<ESM::SoundGenerator> &mSoundGens;
const CSMWorld::IdCollection<ESM::Sound> &mSounds;
const CSMWorld::RefIdCollection &mReferenceables;
bool mIgnoreBaseRecords;
public:
SoundGenCheckStage(const CSMWorld::IdCollection<ESM::SoundGenerator> &soundGens,

@ -5,14 +5,20 @@
#include <components/esm/loadspel.hpp>
#include "../prefs/state.hpp"
#include "../world/universalid.hpp"
CSMTools::SpellCheckStage::SpellCheckStage (const CSMWorld::IdCollection<ESM::Spell>& spells)
: mSpells (spells)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::SpellCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mSpells.getSize();
}
@ -20,7 +26,8 @@ void CSMTools::SpellCheckStage::perform (int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::Spell>& record = mSpells.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
const ESM::Spell& spell = record.get();

@ -13,6 +13,7 @@ namespace CSMTools
class SpellCheckStage : public CSMDoc::Stage
{
const CSMWorld::IdCollection<ESM::Spell>& mSpells;
bool mIgnoreBaseRecords;
public:

@ -1,18 +1,23 @@
#include "startscriptcheck.hpp"
#include "../prefs/state.hpp"
#include <components/misc/stringops.hpp>
CSMTools::StartScriptCheckStage::StartScriptCheckStage (
const CSMWorld::IdCollection<ESM::StartScript>& startScripts,
const CSMWorld::IdCollection<ESM::Script>& scripts)
: mStartScripts (startScripts), mScripts (scripts)
{}
{
mIgnoreBaseRecords = false;
}
void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messages)
{
const CSMWorld::Record<ESM::StartScript>& record = mStartScripts.getRecord (stage);
if (record.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && record.mState == CSMWorld::RecordBase::State_BaseOnly) || record.isDeleted())
return;
std::string scriptId = record.get().mId;
@ -26,5 +31,7 @@ void CSMTools::StartScriptCheckStage::perform(int stage, CSMDoc::Messages& messa
int CSMTools::StartScriptCheckStage::setup()
{
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mStartScripts.getSize();
}

@ -14,6 +14,7 @@ namespace CSMTools
{
const CSMWorld::IdCollection<ESM::StartScript>& mStartScripts;
const CSMWorld::IdCollection<ESM::Script>& mScripts;
bool mIgnoreBaseRecords;
public:

@ -2,6 +2,8 @@
#include <sstream>
#include "../prefs/state.hpp"
#include "../world/infoselectwrapper.hpp"
CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
@ -29,7 +31,9 @@ CSMTools::TopicInfoCheckStage::TopicInfoCheckStage(
mTopics(topics),
mReferencables(referencables),
mSoundFiles(soundFiles)
{}
{
mIgnoreBaseRecords = false;
}
int CSMTools::TopicInfoCheckStage::setup()
{
@ -67,6 +71,8 @@ int CSMTools::TopicInfoCheckStage::setup()
}
}
mIgnoreBaseRecords = CSMPrefs::get()["Reports"]["ignore-base-records"].isTrue();
return mTopicInfos.getSize();
}
@ -74,7 +80,8 @@ void CSMTools::TopicInfoCheckStage::perform(int stage, CSMDoc::Messages& message
{
const CSMWorld::Record<CSMWorld::Info>& infoRecord = mTopicInfos.getRecord(stage);
if (infoRecord.isDeleted())
// Skip "Base" records (setting!) and "Deleted" records
if ((mIgnoreBaseRecords && infoRecord.mState == CSMWorld::RecordBase::State_BaseOnly) || infoRecord.isDeleted())
return;
const CSMWorld::Info& topicInfo = infoRecord.get();

@ -65,6 +65,8 @@ namespace CSMTools
std::set<std::string> mCellNames;
bool mIgnoreBaseRecords;
// These return false when not successful and write an error
bool verifyActor(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);
bool verifyCell(const std::string& name, const CSMWorld::UniversalId& id, CSMDoc::Messages& messages);

@ -88,6 +88,7 @@ namespace CSMWorld
Display_UnsignedInteger8,
Display_Integer,
Display_Float,
Display_Double,
Display_Var,
Display_GmstVarType,
Display_GlobalVarType,

@ -1371,7 +1371,7 @@ namespace CSMWorld
RotColumn (ESM::Position ESXRecordT::* position, int index, bool door)
: Column<ESXRecordT> (
(door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot)+index,
ColumnBase::Display_Float), mPosition (position), mIndex (index) {}
ColumnBase::Display_Double), mPosition (position), mIndex (index) {}
virtual QVariant get (const Record<ESXRecordT>& record) const
{

@ -17,4 +17,4 @@ bool CSMWorld::RecordBase::isErased() const
bool CSMWorld::RecordBase::isModified() const
{
return mState==State_Modified || mState==State_ModifiedOnly;
}
}

@ -156,4 +156,4 @@ namespace CSMWorld
}
}
#endif
#endif

@ -184,11 +184,11 @@ CSMWorld::RefIdCollection::RefIdCollection()
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_PosZ, CSMWorld::ColumnBase::Display_Float));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Float));
new RefIdColumn (Columns::ColumnId_RotX, CSMWorld::ColumnBase::Display_Double));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Float));
new RefIdColumn (Columns::ColumnId_RotY, CSMWorld::ColumnBase::Display_Double));
mColumns.back().addColumn(
new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Float));
new RefIdColumn (Columns::ColumnId_RotZ, CSMWorld::ColumnBase::Display_Double));
// Nested table
mColumns.push_back(RefIdColumn (Columns::ColumnId_AiPackageList,

@ -151,6 +151,9 @@ CompositeViewer::CompositeViewer()
connect( &mTimer, SIGNAL(timeout()), this, SLOT(update()) );
mTimer.start( 10 );
int frameRateLimit = CSMPrefs::get()["Rendering"]["framerate-limit"].toInt();
setRunMaxFrameRate(frameRateLimit);
}
CompositeViewer &CompositeViewer::get()
@ -168,6 +171,12 @@ void CompositeViewer::update()
mSimulationTime += dt;
frame(mSimulationTime);
double minFrameTime = _runMaxFrameRate > 0.0 ? 1.0 / _runMaxFrameRate : 0.0;
if (dt < minFrameTime)
{
OpenThreads::Thread::microSleep(1000*1000*(minFrameTime-dt));
}
}
// ---------------------------------------------------
@ -376,6 +385,10 @@ void SceneWidget::settingChanged (const CSMPrefs::Setting *setting)
{
mOrbitCamControl->setConstRoll(setting->isTrue());
}
else if (*setting=="Rendering/framerate-limit")
{
CompositeViewer::get().setRunMaxFrameRate(setting->toInt());
}
else if (*setting=="Rendering/camera-fov" ||
*setting=="Rendering/camera-ortho" ||
*setting=="Rendering/camera-ortho-size")

@ -3,11 +3,15 @@
#include <QVBoxLayout>
#include "../../model/doc/document.hpp"
#include "../../model/doc/state.hpp"
#include "../../model/tools/search.hpp"
#include "../../model/tools/reportmodel.hpp"
#include "../../model/world/idtablebase.hpp"
#include "../../model/prefs/state.hpp"
#include "../world/tablebottombox.hpp"
#include "../world/creator.hpp"
#include "reporttable.hpp"
#include "searchbox.hpp"
@ -73,6 +77,9 @@ CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc:
layout->addWidget (mTable = new ReportTable (document, id, true), 2);
layout->addWidget (mBottom =
new CSVWorld::TableBottomBox (CSVWorld::NullCreatorFactory(), document, id, this), 0);
QWidget *widget = new QWidget;
widget->setLayout (layout);
@ -93,6 +100,15 @@ CSVTools::SearchSubView::SearchSubView (const CSMWorld::UniversalId& id, CSMDoc:
this, SLOT (startSearch (const CSMTools::Search&)));
connect (&mSearchBox, SIGNAL (replaceAll()), this, SLOT (replaceAllRequest()));
connect (document.getReport (id), SIGNAL (rowsRemoved (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate()));
connect (document.getReport (id), SIGNAL (rowsInserted (const QModelIndex&, int, int)),
this, SLOT (tableSizeUpdate()));
connect (&document, SIGNAL (operationDone (int, bool)),
this, SLOT (operationDone (int, bool)));
}
void CSVTools::SearchSubView::setEditLock (bool locked)
@ -101,6 +117,11 @@ void CSVTools::SearchSubView::setEditLock (bool locked)
mSearchBox.setEditLock (locked);
}
void CSVTools::SearchSubView::setStatusBar (bool show)
{
mBottom->setStatusBar(show);
}
void CSVTools::SearchSubView::stateChanged (int state, CSMDoc::Document *document)
{
mSearchBox.setSearchMode (!(state & CSMDoc::State_Searching));
@ -126,3 +147,17 @@ void CSVTools::SearchSubView::replaceAllRequest()
{
replace (false);
}
void CSVTools::SearchSubView::tableSizeUpdate()
{
mBottom->tableSizeChanged (mDocument.getReport (getUniversalId())->rowCount(), 0, 0);
}
void CSVTools::SearchSubView::operationDone (int type, bool failed)
{
if (type==CSMDoc::State_Searching && !failed &&
!mDocument.getReport (getUniversalId())->rowCount())
{
mBottom->setStatusMessage ("No Results");
}
}

@ -15,6 +15,11 @@ namespace CSMDoc
class Document;
}
namespace CSVWorld
{
class TableBottomBox;
}
namespace CSVTools
{
class ReportTable;
@ -28,6 +33,7 @@ namespace CSVTools
CSMDoc::Document& mDocument;
CSMTools::Search mSearch;
bool mLocked;
CSVWorld::TableBottomBox *mBottom;
private:
@ -43,6 +49,8 @@ namespace CSVTools
virtual void setEditLock (bool locked);
virtual void setStatusBar (bool show);
private slots:
void stateChanged (int state, CSMDoc::Document *document);
@ -52,6 +60,10 @@ namespace CSVTools
void replaceRequest();
void replaceAllRequest();
void tableSizeUpdate();
void operationDone (int type, bool failed);
};
}

@ -110,7 +110,11 @@ void CSVWorld::EnumDelegate::paint (QPainter *painter, const QStyleOptionViewIte
int valueIndex = getValueIndex(index);
if (valueIndex != -1)
{
#if QT_VERSION >= QT_VERSION_CHECK(5,7,0)
QStyleOptionViewItem itemOption(option);
#else
QStyleOptionViewItemV4 itemOption(option);
#endif
itemOption.text = mValues.at(valueIndex).second;
QApplication::style()->drawControl(QStyle::CE_ItemViewItem, &itemOption, painter);
}
@ -171,5 +175,16 @@ CSVWorld::CommandDelegate *CSVWorld::EnumDelegateFactory::makeDelegate (
void CSVWorld::EnumDelegateFactory::add (int value, const QString& name)
{
mValues.push_back (std::make_pair (value, name));
auto pair = std::make_pair (value, name);
for (auto it=mValues.begin(); it!=mValues.end(); ++it)
{
if (it->second > name)
{
mValues.insert(it, pair);
return;
}
}
mValues.push_back(std::make_pair (value, name));
}

@ -28,6 +28,12 @@ void CSVWorld::TableBottomBox::updateStatus()
{
if (mShowStatusBar)
{
if (!mStatusMessage.isEmpty())
{
mStatus->setText (mStatusMessage);
return;
}
static const char *sLabels[4] = { "record", "deleted", "touched", "selected" };
static const char *sLabelsPlural[4] = { "records", "deleted", "touched", "selected" };
@ -178,10 +184,17 @@ void CSVWorld::TableBottomBox::currentWidgetChanged(int /*index*/)
updateSize();
}
void CSVWorld::TableBottomBox::setStatusMessage (const QString& message)
{
mStatusMessage = message;
updateStatus();
}
void CSVWorld::TableBottomBox::selectionSizeChanged (int size)
{
if (mStatusCount[3]!=size)
{
mStatusMessage = "";
mStatusCount[3] = size;
updateStatus();
}
@ -210,7 +223,10 @@ void CSVWorld::TableBottomBox::tableSizeChanged (int size, int deleted, int modi
}
if (changed)
{
mStatusMessage = "";
updateStatus();
}
}
void CSVWorld::TableBottomBox::positionChanged (int row, int column)

@ -39,6 +39,7 @@ namespace CSVWorld
bool mHasPosition;
int mRow;
int mColumn;
QString mStatusMessage;
private:
@ -73,6 +74,8 @@ namespace CSVWorld
///
/// \note The BotomBox does not partake in the deletion of records.
void setStatusMessage (const QString& message);
signals:
void requestFocus (const std::string& id);

@ -233,6 +233,15 @@ QWidget *CSVWorld::CommandDelegate::createEditor (QWidget *parent, const QStyleO
return dsb;
}
case CSMWorld::ColumnBase::Display_Double:
{
DialogueDoubleSpinBox *dsb = new DialogueDoubleSpinBox(parent);
dsb->setRange(-FLT_MAX, FLT_MAX);
dsb->setSingleStep(0.01f);
dsb->setDecimals(6);
return dsb;
}
case CSMWorld::ColumnBase::Display_LongString:
{
QPlainTextEdit *edit = new QPlainTextEdit(parent);

@ -495,7 +495,11 @@ void OMW::Engine::createWindow(Settings::Manager& settings)
traits->windowName = SDL_GetWindowTitle(mWindow);
traits->windowDecoration = !(SDL_GetWindowFlags(mWindow)&SDL_WINDOW_BORDERLESS);
traits->screenNum = SDL_GetWindowDisplayIndex(mWindow);
// FIXME: Some way to get these settings back from the SDL window?
// We tried to get rid of the hardcoding but failed: https://github.com/OpenMW/openmw/pull/1771
// Here goes kcat's quote:
// It's ultimately a chicken and egg problem, and the reason why the code is like it was in the first place.
// It needs a context to get the current attributes, but it needs the attributes to set up the context.
// So it just specifies the same values that were given to SDL in the hopes that it's good enough to what the window eventually gets.
traits->red = 8;
traits->green = 8;
traits->blue = 8;
@ -537,9 +541,8 @@ void OMW::Engine::setWindowIcon()
else
{
osg::ref_ptr<osg::Image> image = result.getImage();
SDL_Surface* surface = SDLUtil::imageToSurface(image, true);
SDL_SetWindowIcon(mWindow, surface);
SDL_FreeSurface(surface);
auto surface = SDLUtil::imageToSurface(image, true);
SDL_SetWindowIcon(mWindow, surface.get());
}
}
@ -608,7 +611,7 @@ void OMW::Engine::prepareEngine (Settings::Manager & settings)
else
gameControllerdb = ""; //if it doesn't exist, pass in an empty string
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, keybinderUser, keybinderUserExists, gameControllerdb, mGrab);
MWInput::InputManager* input = new MWInput::InputManager (mWindow, mViewer, mScreenCaptureHandler, mScreenCaptureOperation, keybinderUser, keybinderUserExists, gameControllerdb, mGrab);
mEnvironment.setInputManager (input);
std::string myguiResources = (mResDir / "mygui").string();
@ -776,8 +779,11 @@ void OMW::Engine::go()
settingspath = loadSettings (settings);
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General")));
mScreenCaptureOperation = new WriteScreenshotToFileOperation(mCfgMgr.getUserDataPath().string(),
Settings::Manager::getString("screenshot format", "General"));
mScreenCaptureHandler = new osgViewer::ScreenCaptureHandler(mScreenCaptureOperation);
mViewer->addEventHandler(mScreenCaptureHandler);
mEnvironment.setFrameRateLimit(Settings::Manager::getFloat("framerate limit", "Video"));

@ -7,7 +7,7 @@
#include <components/settings/settings.hpp>
#include <osgViewer/Viewer>
#include <osgViewer/ViewerEventHandlers>
#include "mwbase/environment.hpp"
@ -82,6 +82,7 @@ namespace OMW
boost::filesystem::path mResDir;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
std::string mCellName;
std::vector<std::string> mContentFiles;
bool mSkipMenu;

@ -121,6 +121,7 @@ namespace MWBase
virtual bool toggleWater() = 0;
virtual bool toggleWorld() = 0;
virtual bool toggleBorders() = 0;
virtual void adjustSky() = 0;
@ -536,6 +537,7 @@ namespace MWBase
/// \todo this does not belong here
virtual void screenshot (osg::Image* image, int w, int h) = 0;
virtual bool screenshot360 (osg::Image* image, std::string settingStr) = 0;
/// Find default position inside exterior cell specified by name
/// \return false if exterior with given name not exists, true otherwise

@ -31,7 +31,7 @@ namespace MWClass
if (!model.empty())
{
physics.addActor(ptr, model);
if (getCreatureStats(ptr).isDead())
if (getCreatureStats(ptr).isDead() && getCreatureStats(ptr).isDeathAnimationFinished())
MWBase::Environment::get().getWorld()->enableActorCollision(ptr, false);
}
}

@ -151,24 +151,21 @@ namespace MWClass
const std::string trapActivationSound = "Disarm Trap Fail";
MWWorld::Ptr player = MWBase::Environment::get().getWorld ()->getPlayerPtr();
const MWWorld::InventoryStore& invStore = player.getClass().getInventoryStore(player);
MWWorld::InventoryStore& invStore = player.getClass().getInventoryStore(player);
bool isLocked = ptr.getCellRef().getLockLevel() > 0;
bool isTrapped = !ptr.getCellRef().getTrap().empty();
bool hasKey = false;
std::string keyName;
// make key id lowercase
std::string keyId = ptr.getCellRef().getKey();
Misc::StringUtils::lowerCaseInPlace(keyId);
for (MWWorld::ConstContainerStoreIterator it = invStore.cbegin(); it != invStore.cend(); ++it)
const std::string keyId = ptr.getCellRef().getKey();
if (!keyId.empty())
{
std::string refId = it->getCellRef().getRefId();
Misc::StringUtils::lowerCaseInPlace(refId);
if (refId == keyId)
MWWorld::Ptr keyPtr = invStore.search(keyId);
if (!keyPtr.isEmpty())
{
hasKey = true;
keyName = it->getClass().getName(*it);
keyName = keyPtr.getClass().getName(keyPtr);
}
}
@ -283,7 +280,8 @@ namespace MWClass
info.caption = ref->mBase->mName;
std::string text;
if (ptr.getCellRef().getLockLevel() > 0)
int lockLevel = ptr.getCellRef().getLockLevel();
if (lockLevel > 0 && lockLevel != ESM::UnbreakableLock)
text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ptr.getCellRef().getLockLevel());
else if (ptr.getCellRef().getLockLevel() < 0)
text += "\n#{sUnlocked}";
@ -315,15 +313,16 @@ namespace MWClass
void Container::lock (const MWWorld::Ptr& ptr, int lockLevel) const
{
if(lockLevel!=0)
ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, in positive
if(lockLevel != 0)
ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, if positive
else
ptr.getCellRef().setLockLevel(abs(ptr.getCellRef().getLockLevel())); //No locklevel given, just flip the original one
ptr.getCellRef().setLockLevel(ESM::UnbreakableLock); // If zero, set to max lock level
}
void Container::unlock (const MWWorld::Ptr& ptr) const
{
ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative
int lockLevel = ptr.getCellRef().getLockLevel();
ptr.getCellRef().setLockLevel(-abs(lockLevel)); //Makes lockLevel negative
}
bool Container::canLock(const MWWorld::ConstPtr &ptr) const

@ -150,8 +150,9 @@ namespace MWClass
data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Flee, ref->mBase->mAiData.mFlee);
data->mCreatureStats.setAiSetting (MWMechanics::CreatureStats::AI_Alarm, ref->mBase->mAiData.mAlarm);
// Persistent actors with 0 health do not play death animation
if (data->mCreatureStats.isDead())
data->mCreatureStats.setDeathAnimationFinished(true);
data->mCreatureStats.setDeathAnimationFinished(ptr.getClass().isPersistent(ptr));
// spells
for (std::vector<std::string>::const_iterator iter (ref->mBase->mSpells.mList.begin());
@ -947,6 +948,9 @@ namespace MWClass
if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())
return;
if (!creatureStats.isDeathAnimationFinished())
return;
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat();
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat();

@ -126,42 +126,44 @@ namespace MWClass
const std::string lockedSound = "LockedDoor";
const std::string trapActivationSound = "Disarm Trap Fail";
const MWWorld::ContainerStore &invStore = actor.getClass().getContainerStore(actor);
MWWorld::ContainerStore &invStore = actor.getClass().getContainerStore(actor);
bool isLocked = ptr.getCellRef().getLockLevel() > 0;
bool isTrapped = !ptr.getCellRef().getTrap().empty();
bool hasKey = false;
std::string keyName;
// FIXME: If NPC activate teleporting door, it can lead to crash due to iterator invalidation in the Actors update.
// Make such activation a no-op for now, like how it is in the vanilla game.
if (actor != MWMechanics::getPlayer() && ptr.getCellRef().getTeleport())
{
std::shared_ptr<MWWorld::Action> action(new MWWorld::FailedAction(std::string(), ptr));
action->setSound(lockedSound);
return action;
}
// make door glow if player activates it with telekinesis
if (actor == MWBase::Environment::get().getWorld()->getPlayerPtr() &&
MWBase::Environment::get().getWorld()->getDistanceToFacedObject() >
if (actor == MWMechanics::getPlayer() &&
MWBase::Environment::get().getWorld()->getDistanceToFacedObject() >
MWBase::Environment::get().getWorld()->getMaxActivationDistance())
{
MWRender::Animation* animation = MWBase::Environment::get().getWorld()->getAnimation(ptr);
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
const MWWorld::ESMStore& store = MWBase::Environment::get().getWorld()->getStore();
int index = ESM::MagicEffect::effectStringToId("sEffectTelekinesis");
const ESM::MagicEffect *effect = store.get<ESM::MagicEffect>().find(index);
animation->addSpellCastGlow(effect, 1); // 1 second glow to match the time taken for a door opening or closing
}
// make key id lowercase
std::string keyId = ptr.getCellRef().getKey();
const std::string keyId = ptr.getCellRef().getKey();
if (!keyId.empty())
{
Misc::StringUtils::lowerCaseInPlace(keyId);
for (MWWorld::ConstContainerStoreIterator it = invStore.cbegin(); it != invStore.cend(); ++it)
MWWorld::Ptr keyPtr = invStore.search(keyId);
if (!keyPtr.isEmpty())
{
std::string refId = it->getCellRef().getRefId();
Misc::StringUtils::lowerCaseInPlace(refId);
if (refId == keyId)
{
hasKey = true;
keyName = it->getClass().getName(*it);
break;
}
hasKey = true;
keyName = keyPtr.getClass().getName(keyPtr);
}
}
@ -232,7 +234,7 @@ namespace MWClass
std::shared_ptr<MWWorld::Action> action(new MWWorld::ActionTeleport (ptr.getCellRef().getDestCell(), ptr.getCellRef().getDoorDest(), true));
action->setSound(openSound);
return action;
}
}
}
else
{
@ -279,15 +281,16 @@ namespace MWClass
void Door::lock (const MWWorld::Ptr& ptr, int lockLevel) const
{
if(lockLevel!=0)
ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, in positive
if(lockLevel != 0)
ptr.getCellRef().setLockLevel(abs(lockLevel)); //Changes lock to locklevel, if positive
else
ptr.getCellRef().setLockLevel(abs(ptr.getCellRef().getLockLevel())); //No locklevel given, just flip the original one
ptr.getCellRef().setLockLevel(ESM::UnbreakableLock); // If zero, set to max lock level
}
void Door::unlock (const MWWorld::Ptr& ptr) const
{
ptr.getCellRef().setLockLevel(-abs(ptr.getCellRef().getLockLevel())); //Makes lockLevel negative
int lockLevel = ptr.getCellRef().getLockLevel();
ptr.getCellRef().setLockLevel(-abs(lockLevel)); //Makes lockLevel negative
}
bool Door::canLock(const MWWorld::ConstPtr &ptr) const
@ -339,7 +342,8 @@ namespace MWClass
text += "\n" + getDestination(*ref);
}
if (ptr.getCellRef().getLockLevel() > 0)
int lockLevel = ptr.getCellRef().getLockLevel();
if (lockLevel > 0 && lockLevel != ESM::UnbreakableLock)
text += "\n#{sLockLevel}: " + MWGui::ToolTips::toString(ptr.getCellRef().getLockLevel());
else if (ptr.getCellRef().getLockLevel() < 0)
text += "\n#{sUnlocked}";

@ -367,8 +367,10 @@ namespace MWClass
data->mNpcStats.setNeedRecalcDynamicStats(true);
}
// Persistent actors with 0 health do not play death animation
if (data->mNpcStats.isDead())
data->mNpcStats.setDeathAnimationFinished(true);
data->mNpcStats.setDeathAnimationFinished(ptr.getClass().isPersistent(ptr));
// race powers
const ESM::Race *race = MWBase::Environment::get().getWorld()->getStore().get<ESM::Race>().find(ref->mBase->mRace);
@ -873,22 +875,24 @@ namespace MWClass
float x = damage / (damage + getArmorRating(ptr));
damage *= std::max(gmst.fCombatArmorMinMult->getFloat(), x);
int damageDiff = static_cast<int>(unmitigatedDamage - damage);
if (damage < 1)
damage = 1;
damage = std::max(1.f, damage);
damageDiff = std::max(1, damageDiff);
MWWorld::InventoryStore &inv = getInventoryStore(ptr);
MWWorld::ContainerStoreIterator armorslot = inv.getSlot(hitslot);
MWWorld::Ptr armor = ((armorslot != inv.end()) ? *armorslot : MWWorld::Ptr());
if(!armor.isEmpty() && armor.getTypeName() == typeid(ESM::Armor).name())
{
int armorhealth = armor.getClass().getItemHealth(armor);
armorhealth -= std::min(std::max(1, damageDiff),
armorhealth);
armor.getCellRef().setCharge(armorhealth);
if (attacker.isEmpty() || (!attacker.isEmpty() && !(object.isEmpty() && !attacker.getClass().isNpc()))) // Unarmed creature attacks don't affect armor condition
{
int armorhealth = armor.getClass().getItemHealth(armor);
armorhealth -= std::min(damageDiff, armorhealth);
armor.getCellRef().setCharge(armorhealth);
// Armor broken? unequip it
if (armorhealth == 0)
armor = *inv.unequipItem(armor, ptr);
// Armor broken? unequip it
if (armorhealth == 0)
armor = *inv.unequipItem(armor, ptr);
}
if (ptr == MWMechanics::getPlayer())
skillUsageSucceeded(ptr, armor.getClass().getEquipmentSkill(armor), 0);
@ -1509,6 +1513,9 @@ namespace MWClass
if (ptr.getRefData().getCount() > 0 && !creatureStats.isDead())
return;
if (!creatureStats.isDeathAnimationFinished())
return;
const MWWorld::Store<ESM::GameSetting>& gmst = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>();
static const float fCorpseRespawnDelay = gmst.find("fCorpseRespawnDelay")->getFloat();
static const float fCorpseClearDelay = gmst.find("fCorpseClearDelay")->getFloat();

@ -470,7 +470,6 @@ namespace MWDialogue
void DialogueManager::addChoice (const std::string& text, int choice)
{
mIsInChoice = true;
mChoices.push_back(std::make_pair(text, choice));
}

@ -491,10 +491,11 @@ bool MWDialogue::Filter::getSelectStructBoolean (const SelectWrapper& select) co
return !Misc::StringUtils::ciEqual(mActor.get<ESM::NPC>()->mBase->mRace, select.getName());
case SelectWrapper::Function_NotCell:
return !Misc::StringUtils::ciEqual(MWBase::Environment::get().getWorld()->getCellName(mActor.getCell())
, select.getName());
{
const std::string& actorCell = MWBase::Environment::get().getWorld()->getCellName(mActor.getCell());
return !(actorCell.length() >= select.getName().length()
&& Misc::StringUtils::ciEqual(actorCell.substr(0, select.getName().length()), select.getName()));
}
case SelectWrapper::Function_SameGender:
return (player.get<ESM::NPC>()->mBase->mFlags & ESM::NPC::Female)==

@ -635,6 +635,11 @@ namespace MWGui
void DialogueWindow::onChoiceActivated(int id)
{
if (mGoodbye)
{
onGoodbyeActivated();
return;
}
MWBase::Environment::get().getDialogueManager()->questionAnswered(id, mCallback.get());
updateTopics();
}

@ -31,6 +31,17 @@ namespace MWGui
boost::algorithm::replace_all(mText, "\r", "");
// vanilla game does not show any text after the last EOL tag.
const std::string lowerText = Misc::StringUtils::lowerCase(mText);
int brIndex = lowerText.rfind("<br>");
int pIndex = lowerText.rfind("<p>");
if (brIndex == pIndex)
mText = "";
else if (brIndex > pIndex)
mText = mText.substr(0, brIndex+4);
else
mText = mText.substr(0, pIndex+3);
registerTag("br", Event_BrTag);
registerTag("p", Event_PTag);
registerTag("img", Event_ImgTag);

@ -198,7 +198,9 @@ namespace MWGui
getWidget(mCrosshair, "Crosshair");
LocalMapBase::init(mMinimap, mCompass, Settings::Manager::getInt("local map hud widget size", "Map"), Settings::Manager::getInt("local map cell distance", "Map"));
int mapSize = std::max(1, Settings::Manager::getInt("local map hud widget size", "Map"));
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
LocalMapBase::init(mMinimap, mCompass, mapSize, cellDistance);
mMainWidget->eventMouseButtonClick += MyGUI::newDelegate(this, &HUD::onWorldClicked);
mMainWidget->eventMouseMove += MyGUI::newDelegate(this, &HUD::onWorldMouseOver);

@ -153,7 +153,7 @@ namespace MWGui
virtual osg::BoundingSphere computeBound(const osg::Node&) const { return osg::BoundingSphere(); }
};
void LoadingScreen::loadingOn()
void LoadingScreen::loadingOn(bool visible)
{
mLoadingOnTime = mTimer.time_m();
// Early-out if already on
@ -169,7 +169,10 @@ namespace MWGui
// We are already using node masks to avoid the scene from being updated/rendered, but node masks don't work for computeBound()
mViewer->getSceneData()->setComputeBoundingSphereCallback(new DontComputeBoundCallback);
mShowWallpaper = (MWBase::Environment::get().getStateManager()->getState()
mVisible = visible;
mLoadingBox->setVisible(mVisible);
mShowWallpaper = mVisible && (MWBase::Environment::get().getStateManager()->getState()
== MWBase::StateManager::State_NoGame);
setVisible(true);
@ -180,10 +183,15 @@ namespace MWGui
}
MWBase::Environment::get().getWindowManager()->pushGuiMode(mShowWallpaper ? GM_LoadingWallpaper : GM_Loading);
if (!mVisible)
draw();
}
void LoadingScreen::loadingOff()
{
mLoadingBox->setVisible(true); // restore
if (mLastRenderTime < mLoadingOnTime)
{
// the loading was so fast that we didn't show loading screen at all
@ -306,7 +314,7 @@ namespace MWGui
void LoadingScreen::draw()
{
if (!needToDrawLoadingScreen())
if (mVisible && !needToDrawLoadingScreen())
return;
if (mShowWallpaper && mTimer.time_m() > mLastWallpaperChangeTime + 5000*1)

@ -35,7 +35,7 @@ namespace MWGui
/// Overridden from Loading::Listener, see the Loading::Listener documentation for usage details
virtual void setLabel (const std::string& label, bool important);
virtual void loadingOn();
virtual void loadingOn(bool visible=true);
virtual void loadingOff();
virtual void setProgressRange (size_t range);
virtual void setProgress (size_t value);
@ -63,6 +63,8 @@ namespace MWGui
bool mImportantLabel;
bool mVisible;
size_t mProgress;
bool mShowWallpaper;

@ -738,7 +738,9 @@ namespace MWGui
mEventBoxLocal->eventMouseButtonPressed += MyGUI::newDelegate(this, &MapWindow::onDragStart);
mEventBoxLocal->eventMouseButtonDoubleClick += MyGUI::newDelegate(this, &MapWindow::onMapDoubleClicked);
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, Settings::Manager::getInt("local map widget size", "Map"), Settings::Manager::getInt("local map cell distance", "Map"));
int mapSize = std::max(1, Settings::Manager::getInt("local map widget size", "Map"));
int cellDistance = std::max(1, Settings::Manager::getInt("local map cell distance", "Map"));
LocalMapBase::init(mLocalMap, mPlayerArrowLocal, mapSize, cellDistance);
mGlobalMap->setVisible(mGlobal);
mLocalMap->setVisible(!mGlobal);

@ -81,7 +81,7 @@ namespace MWGui
MyGUI::Widget* getDefaultKeyFocus() override;
virtual bool exit() { return false; }
virtual bool exit() override { return false; }
bool mMarkedToDelete;

@ -45,44 +45,41 @@ namespace MWGui
QuickKeysMenu::QuickKeysMenu()
: WindowBase("openmw_quickkeys_menu.layout")
, mKey(std::vector<keyData>(10))
, mSelected(nullptr)
, mActivated(nullptr)
, mAssignDialog(0)
, mItemSelectionDialog(0)
, mMagicSelectionDialog(0)
, mSelectedIndex(-1)
, mActivatedIndex(-1)
{
getWidget(mOkButton, "OKButton");
getWidget(mInstructionLabel, "InstructionLabel");
mMainWidget->setSize(mMainWidget->getWidth(),
mMainWidget->getHeight() + (mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));
mMainWidget->getHeight() +
(mInstructionLabel->getTextSize().height - mInstructionLabel->getHeight()));
mOkButton->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onOkButtonClicked);
center();
for (int i = 0; i < 10; ++i)
{
ItemWidget* button;
getWidget(button, "QuickKey" + MyGUI::utility::toString(i+1));
button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
mQuickKeyButtons.push_back(button);
mKey[i].index = i+1;
getWidget(mKey[i].button, "QuickKey" + MyGUI::utility::toString(i+1));
mKey[i].button->eventMouseButtonClick += MyGUI::newDelegate(this, &QuickKeysMenu::onQuickKeyButtonClicked);
mAssigned.push_back(Type_Unassigned);
unassign(button, i);
unassign(&mKey[i]);
}
}
void QuickKeysMenu::clear()
{
mActivatedIndex = -1;
mActivated = nullptr;
for (int i=0; i<10; ++i)
{
unassign(mQuickKeyButtons[i], i);
unassign(&mKey[i]);
}
}
@ -93,30 +90,74 @@ namespace MWGui
delete mMagicSelectionDialog;
}
void QuickKeysMenu::unassign(ItemWidget* key, int index)
void QuickKeysMenu::onOpen()
{
key->clearUserStrings();
key->setItem(MWWorld::Ptr());
while (key->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(key->getChildAt(0));
WindowBase::onOpen();
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
if (index == 9)
// Check if quick keys are still valid
for (int i=0; i<10; ++i)
{
mAssigned[index] = Type_HandToHand;
switch (mKey[i].type)
{
case Type_Unassigned:
case Type_HandToHand:
case Type_Magic:
break;
case Type_Item:
case Type_MagicItem:
{
MWWorld::Ptr item = *mKey[i].button->getUserData<MWWorld::Ptr>();
// Make sure the item is available and is not broken
if (!item || item.getRefData().getCount() < 1 ||
(item.getClass().hasItemHealth(item) &&
item.getClass().getItemHealth(item) <= 0))
{
// Try searching for a compatible replacement
item = store.findReplacement(mKey[i].id);
if (item)
mKey[i].button->setUserData(MWWorld::Ptr(item));
MyGUI::ImageBox* image = key->createWidget<MyGUI::ImageBox>("ImageBox",
break;
}
}
}
}
}
void QuickKeysMenu::unassign(keyData* key)
{
key->button->clearUserStrings();
key->button->setItem(MWWorld::Ptr());
while (key->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(key->button->getChildAt(0));
if (key->index == 10)
{
key->type = Type_HandToHand;
MyGUI::ImageBox* image = key->button->createWidget<MyGUI::ImageBox>("ImageBox",
MyGUI::IntCoord(14, 13, 32, 32), MyGUI::Align::Default);
image->setImageTexture("icons\\k\\stealth_handtohand.dds");
image->setNeedMouseFocus(false);
}
else
{
mAssigned[index] = Type_Unassigned;
key->type = Type_Unassigned;
key->id = "";
key->name = "";
MyGUI::TextBox* textBox = key->createWidgetReal<MyGUI::TextBox>("SandText", MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
textBox->setTextAlign (MyGUI::Align::Center);
textBox->setCaption (MyGUI::utility::toString(index+1));
textBox->setNeedMouseFocus (false);
MyGUI::TextBox* textBox = key->button->createWidgetReal<MyGUI::TextBox>("SandText",
MyGUI::FloatCoord(0,0,1,1), MyGUI::Align::Default);
textBox->setTextAlign(MyGUI::Align::Center);
textBox->setCaption(MyGUI::utility::toString(key->index));
textBox->setNeedMouseFocus(false);
}
/*
@ -153,21 +194,24 @@ namespace MWGui
int index = -1;
for (int i = 0; i < 10; ++i)
{
if (sender == mQuickKeyButtons[i] || sender->getParent () == mQuickKeyButtons[i])
if (sender == mKey[i].button || sender->getParent() == mKey[i].button)
{
index = i;
break;
}
}
assert(index != -1);
mSelectedIndex = index;
mSelected = &mKey[index];
{
// open assign dialog
if (!mAssignDialog)
mAssignDialog = new QuickKeysMenuAssign(this);
mAssignDialog->setVisible (true);
}
// prevent reallocation of zero key from Type_HandToHand
if(mSelected->index == 10)
return;
// open assign dialog
if (!mAssignDialog)
mAssignDialog = new QuickKeysMenuAssign(this);
mAssignDialog->setVisible(true);
}
void QuickKeysMenu::onOkButtonClicked (MyGUI::Widget *sender)
@ -175,10 +219,9 @@ namespace MWGui
MWBase::Environment::get().getWindowManager()->removeGuiMode(GM_QuickKeysMenu);
}
void QuickKeysMenu::onItemButtonClicked(MyGUI::Widget* sender)
{
if (!mItemSelectionDialog )
if (!mItemSelectionDialog)
{
mItemSelectionDialog = new ItemSelectionDialog("#{sQuickMenu6}");
mItemSelectionDialog->eventItemSelected += MyGUI::newDelegate(this, &QuickKeysMenu::onAssignItem);
@ -188,43 +231,45 @@ namespace MWGui
mItemSelectionDialog->openContainer(MWMechanics::getPlayer());
mItemSelectionDialog->setFilter(SortFilterItemModel::Filter_OnlyUsableItems);
mAssignDialog->setVisible (false);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onMagicButtonClicked(MyGUI::Widget* sender)
{
if (!mMagicSelectionDialog )
if (!mMagicSelectionDialog)
{
mMagicSelectionDialog = new MagicSelectionDialog(this);
}
mMagicSelectionDialog->setVisible(true);
mAssignDialog->setVisible (false);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onUnassignButtonClicked(MyGUI::Widget* sender)
{
unassign(mQuickKeyButtons[mSelectedIndex], mSelectedIndex);
mAssignDialog->setVisible (false);
unassign(mSelected);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onCancelButtonClicked(MyGUI::Widget* sender)
{
mAssignDialog->setVisible (false);
mAssignDialog->setVisible(false);
}
void QuickKeysMenu::onAssignItem(MWWorld::Ptr item)
{
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
assert(mSelected);
mAssigned[mSelectedIndex] = Type_Item;
while (mSelected->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
button->setItem(item, ItemWidget::Barter);
button->setUserString ("ToolTipType", "ItemPtr");
button->setUserData(MWWorld::Ptr(item));
mSelected->type = Type_Item;
mSelected->id = item.getCellRef().getRefId();
mSelected->name = item.getClass().getName(item);
mSelected->button->setItem(item, ItemWidget::Barter);
mSelected->button->setUserString("ToolTipType", "ItemPtr");
mSelected->button->setUserData(item);
if (mItemSelectionDialog)
mItemSelectionDialog->setVisible(false);
@ -247,20 +292,20 @@ namespace MWGui
mItemSelectionDialog->setVisible(false);
}
void QuickKeysMenu::onAssignMagicItem (MWWorld::Ptr item)
void QuickKeysMenu::onAssignMagicItem(MWWorld::Ptr item)
{
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
assert(mSelected);
while (mSelected->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
mAssigned[mSelectedIndex] = Type_MagicItem;
mSelected->type = Type_MagicItem;
button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
button->setIcon(item);
mSelected->button->setFrame("textures\\menu_icon_select_magic_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
mSelected->button->setIcon(item);
button->setUserString ("ToolTipType", "ItemPtr");
button->setUserData(MWWorld::Ptr(item));
mSelected->button->setUserString("ToolTipType", "ItemPtr");
mSelected->button->setUserData(MWWorld::Ptr(item));
if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false);
@ -277,21 +322,20 @@ namespace MWGui
*/
}
void QuickKeysMenu::onAssignMagic (const std::string& spellId)
void QuickKeysMenu::onAssignMagic(const std::string& spellId)
{
assert (mSelectedIndex >= 0);
ItemWidget* button = mQuickKeyButtons[mSelectedIndex];
while (button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(button->getChildAt(0));
assert(mSelected);
while (mSelected->button->getChildCount()) // Destroy number label
MyGUI::Gui::getInstance().destroyWidget(mSelected->button->getChildAt(0));
mAssigned[mSelectedIndex] = Type_Magic;
mSelected->type = Type_Magic;
mSelected->id = spellId;
button->setItem(MWWorld::Ptr());
button->setUserString ("ToolTipType", "Spell");
button->setUserString ("Spell", spellId);
mSelected->button->setItem(MWWorld::Ptr());
mSelected->button->setUserString("ToolTipType", "Spell");
mSelected->button->setUserString("Spell", spellId);
const MWWorld::ESMStore &esmStore =
MWBase::Environment::get().getWorld()->getStore();
const MWWorld::ESMStore &esmStore = MWBase::Environment::get().getWorld()->getStore();
// use the icon of the first effect
const ESM::Spell* spell = esmStore.get<ESM::Spell>().find(spellId);
@ -304,8 +348,8 @@ namespace MWGui
path.insert(slashPos+1, "b_");
path = MWBase::Environment::get().getWindowManager()->correctIconPath(path);
button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
button->setIcon(path);
mSelected->button->setFrame("textures\\menu_icon_select_magic.dds", MyGUI::IntCoord(2, 2, 40, 40));
mSelected->button->setIcon(path);
if (mMagicSelectionDialog)
mMagicSelectionDialog->setVisible(false);
@ -322,7 +366,7 @@ namespace MWGui
*/
}
void QuickKeysMenu::onAssignMagicCancel ()
void QuickKeysMenu::onAssignMagicCancel()
{
mMagicSelectionDialog->setVisible(false);
}
@ -330,18 +374,17 @@ namespace MWGui
void QuickKeysMenu::updateActivatedQuickKey()
{
// there is no delayed action, nothing to do.
if (mActivatedIndex < 0)
if (!mActivated)
return;
activateQuickKey(mActivatedIndex);
activateQuickKey(mActivated->index);
}
void QuickKeysMenu::activateQuickKey(int index)
{
assert (index-1 >= 0);
ItemWidget* button = mQuickKeyButtons[index-1];
assert(index >= 1 && index <= 10);
QuickKeyType type = mAssigned[index-1];
keyData *key = &mKey[index-1];
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
@ -354,64 +397,100 @@ namespace MWGui
|| playerStats.getHitRecovery();
bool isReturnNeeded = playerStats.isParalyzed() || playerStats.isDead();
if (isReturnNeeded && type != Type_Item)
{
return;
}
if (isDelayNeeded && type != Type_Item)
{
mActivatedIndex = index;
if (isReturnNeeded)
return;
}
else if (isDelayNeeded)
mActivated = key;
else
mActivatedIndex = -1;
mActivated = nullptr;
if (type == Type_Item || type == Type_MagicItem)
if (key->type == Type_Item || key->type == Type_MagicItem)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
// make sure the item is available
if (item.getRefData ().getCount() < 1)
MWWorld::Ptr item = *key->button->getUserData<MWWorld::Ptr>();
MWWorld::ContainerStoreIterator it = store.begin();
for (; it != store.end(); ++it)
{
// Try searching for a compatible replacement
std::string id = item.getCellRef().getRefId();
if (*it == item)
break;
}
if (it == store.end())
item = nullptr;
for (MWWorld::ContainerStoreIterator it = store.begin(); it != store.end(); ++it)
// check the item is available and not broken
if (!item || item.getRefData().getCount() < 1 ||
(item.getClass().hasItemHealth(item) && item.getClass().getItemHealth(item) <= 0))
{
item = store.findReplacement(key->id);
if (!item || item.getRefData().getCount() < 1)
{
if (Misc::StringUtils::ciEqual(it->getCellRef().getRefId(), id))
{
item = *it;
button->setUserData(MWWorld::Ptr(item));
break;
}
MWBase::Environment::get().getWindowManager()->messageBox(
"#{sQuickMenu5} " + key->name);
return;
}
}
if (key->type == Type_Item)
{
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() ||
item.getTypeName() == typeid(ESM::Lockpick).name();
if (item.getRefData().getCount() < 1)
// delay weapon switching if player is busy
if (isDelayNeeded && (isWeapon || isTool))
{
// No replacement was found
MWBase::Environment::get().getWindowManager ()->messageBox (
"#{sQuickMenu5} " + item.getClass().getName(item));
return;
}
MWBase::Environment::get().getWindowManager()->useItem(item);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand
if (rightHand != store.end() && item == *rightHand)
{
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
}
}
}
else if (key->type == Type_MagicItem)
{
// equip, if it can be equipped
if (!item.getClass().getEquipmentSlots(item).first.empty())
{
MWBase::Environment::get().getWindowManager()->useItem(item);
// make sure that item was successfully equipped
if (!store.isEquipped(item))
return;
}
if (type == Type_Magic)
store.setSelectedEnchantItem(it);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
}
else if (key->type == Type_Magic)
{
std::string spellId = button->getUserString("Spell");
std::string spellId = key->id;
// Make sure the player still has this spell
MWMechanics::CreatureStats& stats = player.getClass().getCreatureStats(player);
MWMechanics::Spells& spells = stats.getSpells();
if (!spells.hasSpell(spellId))
{
const ESM::Spell* spell = MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
MWBase::Environment::get().getWindowManager()->messageBox (
"#{sQuickMenu5} " + spell->mName);
const ESM::Spell* spell =
MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().find(spellId);
MWBase::Environment::get().getWindowManager()->messageBox("#{sQuickMenu5} " + spell->mName);
return;
}
store.setSelectedEnchantItem(store.end());
MWBase::Environment::get().getWindowManager()->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWindowManager()
->setSelectedSpell(spellId, int(MWMechanics::getSpellSuccessChance(spellId, player)));
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
/*
@ -424,62 +503,7 @@ namespace MWGui
End of tes3mp addition
*/
}
else if (type == Type_Item)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
bool isWeapon = item.getTypeName() == typeid(ESM::Weapon).name();
bool isTool = item.getTypeName() == typeid(ESM::Probe).name() || item.getTypeName() == typeid(ESM::Lockpick).name();
// delay weapon switching if player is busy
if (isDelayNeeded && (isWeapon || isTool))
{
mActivatedIndex = index;
return;
}
// disable weapon switching if player is dead or paralyzed
if (isReturnNeeded && (isWeapon || isTool))
{
return;
}
MWBase::Environment::get().getWindowManager()->useItem(item);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand
if (rightHand != store.end() && item == *rightHand)
{
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
}
}
else if (type == Type_MagicItem)
{
MWWorld::Ptr item = *button->getUserData<MWWorld::Ptr>();
// retrieve ContainerStoreIterator to the item
MWWorld::ContainerStoreIterator it = store.begin();
for (; it != store.end(); ++it)
{
if (*it == item)
{
break;
}
}
assert(it != store.end());
// equip, if it can be equipped
if (!item.getClass().getEquipmentSlots(item).first.empty())
{
MWBase::Environment::get().getWindowManager()->useItem(item);
// make sure that item was successfully equipped
if (!store.isEquipped(item))
return;
}
store.setSelectedEnchantItem(it);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Spell);
}
else if (type == Type_HandToHand)
else if (key->type == Type_HandToHand)
{
store.unequipSlot(MWWorld::InventoryStore::Slot_CarriedRight, player);
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
@ -554,9 +578,9 @@ namespace MWGui
for (int i=0; i<10; ++i)
{
ItemWidget* button = mQuickKeyButtons[i];
ItemWidget* button = mKey[i].button;
int type = mAssigned[i];
int type = mKey[i].type;
ESM::QuickKeys::QuickKey key;
key.mType = type;
@ -595,51 +619,38 @@ namespace MWGui
ESM::QuickKeys keys;
keys.load(reader);
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
int i=0;
for (std::vector<ESM::QuickKeys::QuickKey>::const_iterator it = keys.mKeys.begin(); it != keys.mKeys.end(); ++it)
{
if (i >= 10)
return;
mSelectedIndex = i;
int keyType = it->mType;
std::string id = it->mId;
ItemWidget* button = mQuickKeyButtons[i];
mSelected = &mKey[i];
mSelected->type = (QuickKeysMenu::QuickKeyType) it->mType;
mSelected->id = it->mId;
switch (keyType)
switch (mSelected->type)
{
case Type_Magic:
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(id))
onAssignMagic(id);
if (MWBase::Environment::get().getWorld()->getStore().get<ESM::Spell>().search(mSelected->id))
onAssignMagic(mSelected->id);
break;
case Type_Item:
case Type_MagicItem:
{
// Find the item by id
MWWorld::Ptr player = MWMechanics::getPlayer();
MWWorld::InventoryStore& store = player.getClass().getInventoryStore(player);
MWWorld::Ptr item;
for (MWWorld::ContainerStoreIterator iter = store.begin(); iter != store.end(); ++iter)
{
if (Misc::StringUtils::ciEqual(iter->getCellRef().getRefId(), id))
{
if (item.isEmpty() ||
// Prefer the stack with the lowest remaining uses
!item.getClass().hasItemHealth(*iter) ||
iter->getClass().getItemHealth(*iter) < item.getClass().getItemHealth(item))
{
item = *iter;
}
}
}
MWWorld::Ptr item = store.findReplacement(mSelected->id);
if (item.isEmpty())
unassign(button, i);
unassign(&mKey[i]);
else
{
if (keyType == Type_Item)
if (mSelected->type == Type_Item)
onAssignItem(item);
else if (keyType == Type_MagicItem)
else if (mSelected->type == Type_MagicItem)
onAssignMagicItem(item);
}
@ -647,7 +658,7 @@ namespace MWGui
}
case Type_Unassigned:
case Type_HandToHand:
unassign(button, i);
unassign(&mKey[i]);
break;
}

@ -34,6 +34,7 @@ namespace MWGui
void onAssignMagicItem (MWWorld::Ptr item);
void onAssignMagic (const std::string& spellId);
void onAssignMagicCancel ();
void onOpen();
void activateQuickKey(int index);
void updateActivatedQuickKey();
@ -74,23 +75,31 @@ namespace MWGui
private:
struct keyData {
int index;
ItemWidget* button;
QuickKeysMenu::QuickKeyType type;
std::string id;
std::string name;
keyData(): index(-1), button(nullptr), type(Type_Unassigned), id(""), name("") {}
};
std::vector<keyData> mKey;
keyData* mSelected;
keyData* mActivated;
MyGUI::EditBox* mInstructionLabel;
MyGUI::Button* mOkButton;
std::vector<ItemWidget*> mQuickKeyButtons;
std::vector<QuickKeyType> mAssigned;
QuickKeysMenuAssign* mAssignDialog;
ItemSelectionDialog* mItemSelectionDialog;
MagicSelectionDialog* mMagicSelectionDialog;
int mSelectedIndex;
int mActivatedIndex;
void onQuickKeyButtonClicked(MyGUI::Widget* sender);
void onOkButtonClicked(MyGUI::Widget* sender);
void unassign(ItemWidget* key, int index);
void unassign(keyData* key);
};
class QuickKeysMenuAssign : public WindowModal

@ -577,8 +577,9 @@ namespace MWGui
void SettingsWindow::onOpen()
{
updateControlsBox ();
updateControlsBox();
resetScrollbars();
MWBase::Environment::get().getWindowManager()->setKeyFocusWidget(mOkButton);
}
void SettingsWindow::onWindowResize(MyGUI::Window *_sender)

@ -191,6 +191,9 @@ namespace MWGui
else if (type == "ItemPtr")
{
mFocusObject = *focus->getUserData<MWWorld::Ptr>();
if (!mFocusObject)
return;
tooltipSize = getToolTipViaPtr(mFocusObject.getRefData().getCount(), false, checkOwned());
}
else if (type == "ItemModelIndex")

@ -319,8 +319,10 @@ namespace MWGui
void WaitDialog::wakeUp ()
{
mSleeping = false;
mTimeAdvancer.stop();
stopWaiting();
if (mInterruptAt != -1)
onWaitingInterrupted();
else
stopWaiting();
}
}

@ -56,15 +56,15 @@ namespace MWGui
/*
* "Modal" windows cause the rest of the interface to be unaccessible while they are visible
* "Modal" windows cause the rest of the interface to be inaccessible while they are visible
*/
class WindowModal : public WindowBase
{
public:
WindowModal(const std::string& parLayout);
virtual void onOpen();
virtual void onClose();
virtual bool exit() {return true;}
virtual void onOpen() override;
virtual void onClose() override;
virtual bool exit() override {return true;}
};
/// A window that cannot be the target of a drag&drop action.

@ -48,12 +48,14 @@ namespace MWInput
SDL_Window* window,
osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
const std::string& userFile, bool userFileExists,
const std::string& controllerBindingsFile, bool grab)
: mWindow(window)
, mWindowVisible(true)
, mViewer(viewer)
, mScreenCaptureHandler(screenCaptureHandler)
, mScreenCaptureOperation(screenCaptureOperation)
, mJoystickLastUsed(false)
, mPlayer(NULL)
, mInputManager(NULL)
@ -1103,8 +1105,28 @@ namespace MWInput
void InputManager::screenshot()
{
mScreenCaptureHandler->setFramesToCapture(1);
mScreenCaptureHandler->captureNextFrame(*mViewer);
bool regularScreenshot = true;
std::string settingStr;
settingStr = Settings::Manager::getString("screenshot type","Video");
regularScreenshot = settingStr.size() == 0 || settingStr.compare("regular") == 0;
if (regularScreenshot)
{
mScreenCaptureHandler->setFramesToCapture(1);
mScreenCaptureHandler->captureNextFrame(*mViewer);
}
else
{
osg::ref_ptr<osg::Image> screenshot (new osg::Image);
if (MWBase::Environment::get().getWorld()->screenshot360(screenshot.get(),settingStr))
{
(*mScreenCaptureOperation) (*(screenshot.get()),0);
// FIXME: mScreenCaptureHandler->getCaptureOperation() causes crash for some reason
}
}
}
void InputManager::toggleInventory()

@ -4,6 +4,7 @@
#include "../mwgui/mode.hpp"
#include <osg/ref_ptr>
#include <osgViewer/ViewerEventHandlers>
#include <extern/oics/ICSChannelListener.h>
#include <extern/oics/ICSInputControlSystem.h>
@ -14,7 +15,6 @@
#include "../mwbase/inputmanager.hpp"
namespace MWWorld
{
class Player;
@ -74,6 +74,7 @@ namespace MWInput
SDL_Window* window,
osg::ref_ptr<osgViewer::Viewer> viewer,
osg::ref_ptr<osgViewer::ScreenCaptureHandler> screenCaptureHandler,
osgViewer::ScreenCaptureHandler::CaptureOperation *screenCaptureOperation,
const std::string& userFile, bool userFileExists,
const std::string& controllerBindingsFile, bool grab);
@ -158,6 +159,7 @@ namespace MWInput
bool mWindowVisible;
osg::ref_ptr<osgViewer::Viewer> mViewer;
osg::ref_ptr<osgViewer::ScreenCaptureHandler> mScreenCaptureHandler;
osgViewer::ScreenCaptureHandler::CaptureOperation *mScreenCaptureOperation;
bool mJoystickLastUsed;
MWWorld::Player* mPlayer;

@ -67,27 +67,36 @@ bool isConscious(const MWWorld::Ptr& ptr)
return !stats.isDead() && !stats.getKnockedDown();
}
void adjustBoundItem (const std::string& item, bool bound, const MWWorld::Ptr& actor)
int getBoundItemSlot (const std::string& itemId)
{
if (bound)
static std::map<std::string, int> boundItemsMap;
if (boundItemsMap.empty())
{
if (actor.getClass().getContainerStore(actor).count(item) == 0)
{
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
MWWorld::Ptr newPtr = *store.MWWorld::ContainerStore::add(item, 1, actor);
MWWorld::ActionEquip action(newPtr);
action.execute(actor);
MWWorld::ConstContainerStoreIterator rightHand = store.getSlot(MWWorld::InventoryStore::Slot_CarriedRight);
// change draw state only if the item is in player's right hand
if (actor == MWMechanics::getPlayer()
&& rightHand != store.end() && newPtr == *rightHand)
{
MWBase::Environment::get().getWorld()->getPlayer().setDrawState(MWMechanics::DrawState_Weapon);
}
}
std::string boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundBootsID")->getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Boots;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundCuirassID")->getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Cuirass;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundLeftGauntletID")->getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_LeftGauntlet;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundRightGauntletID")->getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_RightGauntlet;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundHelmID")->getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_Helmet;
boundId = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find("sMagicBoundShieldID")->getString();
boundItemsMap[boundId] = MWWorld::InventoryStore::Slot_CarriedLeft;
}
else
actor.getClass().getInventoryStore(actor).remove(item, 1, actor, true);
int slot = MWWorld::InventoryStore::Slot_CarriedRight;
std::map<std::string, int>::iterator it = boundItemsMap.find(itemId);
if (it != boundItemsMap.end())
slot = it->second;
return slot;
}
class CheckActorCommanded : public MWMechanics::EffectSourceVisitor
@ -264,6 +273,69 @@ namespace MWMechanics
}
};
void Actors::addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor)
{
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
int slot = getBoundItemSlot(itemId);
if (actor.getClass().getContainerStore(actor).count(itemId) != 0)
return;
MWWorld::ContainerStoreIterator prevItem = store.getSlot(slot);
MWWorld::Ptr boundPtr = *store.MWWorld::ContainerStore::add(itemId, 1, actor);
MWWorld::ActionEquip action(boundPtr);
action.execute(actor);
if (actor != MWMechanics::getPlayer())
return;
MWWorld::Ptr newItem = *store.getSlot(slot);
if (newItem.isEmpty() || boundPtr != newItem)
return;
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
// change draw state only if the item is in player's right hand
if (slot == MWWorld::InventoryStore::Slot_CarriedRight)
player.setDrawState(MWMechanics::DrawState_Weapon);
if (prevItem != store.end())
player.setPreviousItem(itemId, prevItem->getCellRef().getRefId());
}
void Actors::removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor)
{
MWWorld::InventoryStore& store = actor.getClass().getInventoryStore(actor);
int slot = getBoundItemSlot(itemId);
MWWorld::ContainerStoreIterator currentItem = store.getSlot(slot);
bool wasEquipped = currentItem != store.end() && Misc::StringUtils::ciEqual(currentItem->getCellRef().getRefId(), itemId);
store.remove(itemId, 1, actor, true);
if (actor != MWMechanics::getPlayer())
return;
MWWorld::Player& player = MWBase::Environment::get().getWorld()->getPlayer();
std::string prevItemId = player.getPreviousItem(itemId);
player.erasePreviousItem(itemId);
if (prevItemId.empty())
return;
// Find previous item (or its replacement) by id.
// we should equip previous item only if expired bound item was equipped.
MWWorld::Ptr item = store.findReplacement(prevItemId);
if (item.isEmpty() || !wasEquipped)
return;
MWWorld::ActionEquip action(item);
action.execute(actor);
}
void Actors::updateActor (const MWWorld::Ptr& ptr, float duration)
{
// magic effects
@ -738,11 +810,14 @@ namespace MWMechanics
{
// The actor was killed by a magic effect. Figure out if the player was responsible for it.
const ActiveSpells& spells = creatureStats.getActiveSpells();
bool killedByPlayer = false;
MWWorld::Ptr player = getPlayer();
std::set<MWWorld::Ptr> playerFollowers;
getActorsSidingWith(player, playerFollowers);
for (ActiveSpells::TIterator it = spells.begin(); it != spells.end(); ++it)
{
const ActiveSpells::ActiveSpellParams& spell = it->second;
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
for (std::vector<ActiveSpells::ActiveEffect>::const_iterator effectIt = spell.mEffects.begin();
effectIt != spell.mEffects.end(); ++effectIt)
{
@ -760,17 +835,19 @@ namespace MWMechanics
isDamageEffect = true;
}
MWWorld::Ptr caster = MWBase::Environment::get().getWorld()->searchPtrViaActorId(spell.mCasterActorId);
if (isDamageEffect && caster == player)
killedByPlayer = true;
if (isDamageEffect)
{
if (caster == player || playerFollowers.find(caster) != playerFollowers.end())
{
if (caster.getClass().getNpcStats(caster).isWerewolf())
caster.getClass().getNpcStats(caster).addWerewolfKill();
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
break;
}
}
}
}
if (killedByPlayer)
{
MWBase::Environment::get().getMechanicsManager()->actorKilled(ptr, player);
if (player.getClass().getNpcStats(player).isWerewolf())
player.getClass().getNpcStats(player).addWerewolfKill();
}
}
// TODO: dirty flag for magic effects to avoid some unnecessary work below?
@ -805,25 +882,23 @@ namespace MWMechanics
float magnitude = effects.get(it->first).getMagnitude();
if (found != (magnitude > 0))
{
if (magnitude > 0)
creatureStats.mBoundItems.insert(it->first);
else
creatureStats.mBoundItems.erase(it->first);
std::string itemGmst = it->second;
std::string item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
itemGmst)->getString();
magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);
if (it->first == ESM::MagicEffect::BoundGloves)
{
item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"sMagicBoundLeftGauntletID")->getString();
adjustBoundItem(item, magnitude > 0, ptr);
item = MWBase::Environment::get().getWorld()->getStore().get<ESM::GameSetting>().find(
"sMagicBoundRightGauntletID")->getString();
adjustBoundItem(item, magnitude > 0, ptr);
magnitude > 0 ? addBoundItem(item, ptr) : removeBoundItem(item, ptr);
}
else
adjustBoundItem(item, magnitude > 0, ptr);
if (magnitude > 0)
creatureStats.mBoundItems.insert(it->first);
else
creatureStats.mBoundItems.erase(it->first);
}
}
@ -975,7 +1050,8 @@ namespace MWMechanics
MWWorld::ContainerStoreIterator torch = inventoryStore.end();
for (MWWorld::ContainerStoreIterator it = inventoryStore.begin(); it != inventoryStore.end(); ++it)
{
if (it->getTypeName() == typeid(ESM::Light).name())
if (it->getTypeName() == typeid(ESM::Light).name() &&
it->getClass().canBeEquipped(*it, ptr).first)
{
torch = it;
break;
@ -996,8 +1072,7 @@ namespace MWMechanics
heldIter = inventoryStore.getSlot(MWWorld::InventoryStore::Slot_CarriedLeft);
// If we have a torch and can equip it, then equip it now.
if (heldIter == inventoryStore.end()
&& torch->getClass().canBeEquipped(*torch, ptr).first == 1)
if (heldIter == inventoryStore.end())
{
inventoryStore.equip(MWWorld::InventoryStore::Slot_CarriedLeft, torch, ptr);
}

@ -4,7 +4,6 @@
#include <set>
#include <vector>
#include <string>
#include <map>
#include <list>
#include "../mwbase/world.hpp"
@ -26,6 +25,9 @@ namespace MWMechanics
{
std::map<std::string, int> mDeathCount;
void addBoundItem (const std::string& itemId, const MWWorld::Ptr& actor);
void removeBoundItem (const std::string& itemId, const MWWorld::Ptr& actor);
void updateNpc(const MWWorld::Ptr &ptr, float duration);
void adjustMagicEffects (const MWWorld::Ptr& creature);

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

Loading…
Cancel
Save