Compare commits

..

No commits in common. "master" and "openmw-49-rc3" have entirely different histories.

180 changed files with 1251 additions and 2140 deletions

View file

@ -1,15 +1,18 @@
Checks: >
-*,
boost-*,
portability-*,
clang-analyzer-*,
-clang-analyzer-optin.*,
-clang-analyzer-optin*,
-clang-analyzer-cplusplus.NewDeleteLeaks,
-clang-analyzer-cplusplus.NewDelete,
-clang-analyzer-core.CallAndMessage,
modernize-avoid-bind,
readability-identifier-naming
WarningsAsErrors: '*'
HeaderFilterRegex: '(apps|components)/'
CheckOptions:
- key: readability-identifier-naming.ConceptCase
value: CamelCase
-modernize-avoid-bind
WarningsAsErrors: >
-*,
boost-*,
portability-*,
clang-analyzer-*,
-clang-analyzer-optin*,
-clang-analyzer-cplusplus.NewDeleteLeaks,
-clang-analyzer-core.CallAndMessage
HeaderFilterRegex: '^(apps|components)'

View file

@ -202,11 +202,7 @@ jobs:
AWS_DEFAULT_REGION: eu-west-3
if: ${{ env.AWS_ACCESS_KEY_ID != '' && env.AWS_SECRET_ACCESS_KEY != '' && inputs.package }}
working-directory: ${{ github.workspace }}/SymStore
run: |
choco uninstall awscli -y
choco install awscli -y --version=2.22.35
aws --version
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 sync --size-only --exclude * --include *.ex_ --include *.dl_ --include *.pd_ . s3://openmw-sym
run: aws --endpoint-url https://rgw.ctrl-c.liu.se s3 sync --size-only --exclude * --include *.ex_ --include *.dl_ --include *.pd_ . s3://openmw-sym
- name: Add install directory to PATH
shell: bash

View file

@ -512,16 +512,9 @@ Ubuntu_GCC_integration_tests_asan:
- for dmg in *.dmg; do mv "$dmg" "${dmg%.dmg}_${CI_COMMIT_REF_NAME##*/}.dmg"; done
- |
if [[ -n "${AWS_ACCESS_KEY_ID}" ]]; then
echo "[default]" > ~/.s3cfg
echo "access_key = ${AWS_ACCESS_KEY_ID}" >> ~/.s3cfg
echo "secret_key = ${AWS_SECRET_ACCESS_KEY}" >> ~/.s3cfg
echo "host_base = rgw.ctrl-c.liu.se" >> ~/.s3cfg
echo "host_bucket = %(bucket)s.rgw.ctrl-c.liu.se" >> ~/.s3cfg
echo "use_https = True" >> ~/.s3cfg
artifactDirectory="${CI_PROJECT_NAMESPACE//[\"<>|$'\t'\/\\?*]/_}/${CI_COMMIT_REF_NAME//[\"<>|$'\t'\/\\?*]/_}/${CI_COMMIT_SHORT_SHA//[\"<>|$'\t'\/\\?*]/_}-${CI_JOB_ID//[\"<>|$'\t'\/\\?*]/_}/"
for dmg in *.dmg; do
s3cmd put "${dmg}" s3://openmw-artifacts/${artifactDirectory}
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "${dmg}" s3://openmw-artifacts/${artifactDirectory}
done
fi
- ccache -s
@ -547,7 +540,7 @@ macOS14_Xcode15_arm64:
script:
- apt-get update
- apt-get install -y curl gcab unzip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64-2.22.35.zip" -o awscli-exe-linux-x86_64.zip
- curl "https://awscli.amazonaws.com/awscli-exe-linux-x86_64.zip" -o awscli-exe-linux-x86_64.zip
- unzip -d awscli-exe-linux-x86_64 awscli-exe-linux-x86_64.zip
- pushd awscli-exe-linux-x86_64
- ./aws/install
@ -594,7 +587,7 @@ macOS14_Xcode15_arm64:
- choco install vswhere -y
- choco install ninja -y
- choco install python -y
- choco install awscli -y --version=2.22.35
- choco install awscli -y
- refreshenv
- |
function Make-SafeFileName {
@ -628,20 +621,15 @@ macOS14_Xcode15_arm64:
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
- $artifactDirectory = "$(Make-SafeFileName("${CI_PROJECT_NAMESPACE}"))/$(Make-SafeFileName("${CI_COMMIT_REF_NAME}"))/$(Make-SafeFileName("${CI_COMMIT_SHORT_SHA}-${CI_JOB_ID}"))/"
- Get-ChildItem -Recurse *.ilk | Remove-Item
- aws --version
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
Push-Location ..
..\CI\Store-Symbols.ps1 -SkipCompress
if(!$?) { Exit $LASTEXITCODE }
7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
Pop-Location
Get-ChildItem -Recurse *.pdb | Remove-Item
}
@ -649,20 +637,13 @@ macOS14_Xcode15_arm64:
- |
if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
- |
if ($executables) {
foreach ($exe in $executables.Split(',')) {
& .\$exe
if(!$?) { Exit $LASTEXITCODE }
}
}
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script:
- Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache:
key: ninja-2022-v12
key: ninja-2022-v11
paths:
- ccache
- deps
@ -757,7 +738,7 @@ macOS14_Xcode15_arm64:
- choco install 7zip -y
- choco install vswhere -y
- choco install python -y
- choco install awscli -y --version=2.22.35
- choco install awscli -y
- refreshenv
- |
function Make-SafeFileName {
@ -786,20 +767,15 @@ macOS14_Xcode15_arm64:
- echo "CI_COMMIT_REF_NAME ${CI_COMMIT_REF_NAME}`nCI_JOB_ID ${CI_JOB_ID}`nCI_COMMIT_SHA ${CI_COMMIT_SHA}" | Out-File -Encoding UTF8 CI-ID.txt
- $artifactDirectory = "$(Make-SafeFileName("${CI_PROJECT_NAMESPACE}"))/$(Make-SafeFileName("${CI_COMMIT_REF_NAME}"))/$(Make-SafeFileName("${CI_COMMIT_SHORT_SHA}-${CI_JOB_ID}"))/"
- Get-ChildItem -Recurse *.ilk | Remove-Item
- aws --version
- |
if (Get-ChildItem -Recurse *.pdb) {
7z a -tzip "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" '*.pdb' CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_symbols.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
Push-Location ..
..\CI\Store-Symbols.ps1 -SkipCompress
if(!$?) { Exit $LASTEXITCODE }
7z a -tzip "..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}_${CI_JOB_ID}_sym_store.zip"))" '.\SymStore\*' $config\CI-ID.txt
if(!$?) { Exit $LASTEXITCODE }
Pop-Location
Get-ChildItem -Recurse *.pdb | Remove-Item
}
@ -807,20 +783,13 @@ macOS14_Xcode15_arm64:
- |
if (Test-Path env:AWS_ACCESS_KEY_ID) {
aws --endpoint-url https://rgw.ctrl-c.liu.se s3 cp "..\..\$(Make-SafeFileName("OpenMW_MSVC2022_64_${config}_${CI_COMMIT_REF_NAME}.zip"))" s3://openmw-artifacts/${artifactDirectory}
if(!$?) { Exit $LASTEXITCODE }
}
- |
if ($executables) {
foreach ($exe in $executables.Split(',')) {
& .\$exe
if(!$?) { Exit $LASTEXITCODE }
}
}
- if ($executables) { foreach ($exe in $executables.Split(',')) { & .\$exe } }
after_script:
- Get-Volume
- Copy-Item C:\ProgramData\chocolatey\logs\chocolatey.log
cache:
key: msbuild-2022-v12
key: msbuild-2022-v11
paths:
- deps
- MSVC2022_64/deps/Qt

View file

@ -35,6 +35,7 @@
Bug #5977: Fatigueless NPCs' corpse underwater changes animation on game load
Bug #6025: Subrecords cannot overlap records
Bug #6027: Collisionshape becomes spiderweb-like when the mesh is too complex
Bug #6097: Level Progress Tooltip Sometimes Not Updated
Bug #6146: Lua command `actor:setEquipment` doesn't trigger mwscripts when equipping or unequipping a scripted item
Bug #6156: 1ft Charm or Sound magic effect vfx doesn't work properly
Bug #6190: Unintuitive sun specularity time of day dependence
@ -227,11 +228,6 @@
Bug #8252: Plugin dependencies are not required to be loaded
Bug #8295: Post-processing chain is case-sensitive
Bug #8299: Crash while smoothing landscape
Bug #8364: Crash when clicking scrollbar without handle (divide by zero)
Bug #8378: Korean bitmap fonts are unusable
Bug #8439: Creatures without models can crash the game
Bug #8441: Freeze when using video main menu replacers
Bug #8462: Crashes when resizing the window on macOS
Feature #1415: Infinite fall failsafe
Feature #2566: Handle NAM9 records for manual cell references
Feature #3501: OpenMW-CS: Instance Editing - Shortcuts for axial locking
@ -396,7 +392,6 @@
Bug #6066: Addtopic "return" does not work from within script. No errors thrown
Bug #6067: ESP loader fails for certain subrecord orders
Bug #6087: Bound items added directly to the inventory disappear if their corresponding spell effect ends
Bug #6097: Level Progress Tooltip Sometimes Not Updated
Bug #6101: Disarming trapped unlocked owned objects isn't considered a crime
Bug #6107: Fatigue is incorrectly recalculated when fortify effect is applied or removed
Bug #6109: Crash when playing a custom made menu_background file

View file

@ -7,7 +7,7 @@ export HOMEBREW_AUTOREMOVE=1
brew tap --repair
brew update --quiet
brew install curl xquartz gd fontconfig freetype harfbuzz brotli s3cmd
brew install curl xquartz gd fontconfig freetype harfbuzz brotli
command -v ccache >/dev/null 2>&1 || brew install ccache
command -v cmake >/dev/null 2>&1 || brew install cmake

View file

@ -38,7 +38,7 @@ fi
if [[ $CI_CLANG_TIDY ]]; then
CMAKE_CONF_OPTS+=(
-DCMAKE_CXX_CLANG_TIDY=clang-tidy
-DCMAKE_CXX_CLANG_TIDY="clang-tidy;--warnings-as-errors=*"
-DBUILD_COMPONENTS_TESTS=ON
-DBUILD_OPENMW_TESTS=ON
-DBUILD_OPENCS_TESTS=ON

View file

@ -95,7 +95,7 @@ declare -rA GROUPED_DEPS=(
[libasan6]="libasan6"
[android]="binutils build-essential cmake ccache curl unzip git pkg-config"
[openmw-clang-format]="
clang-format-14
git-core
@ -126,24 +126,10 @@ export APT_CACHE_DIR="${PWD}/apt-cache"
export DEBIAN_FRONTEND=noninteractive
set -x
mkdir -pv "$APT_CACHE_DIR"
while true; do
apt-get update -yqq && break
done
apt-get update -yqq
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends software-properties-common gnupg >/dev/null
while true; do
add-apt-repository -y ppa:openmw/openmw && break
done
while true; do
add-apt-repository -y ppa:openmw/openmw-daily && break
done
while true; do
add-apt-repository -y ppa:openmw/staging && break
done
add-apt-repository -y ppa:openmw/openmw
add-apt-repository -y ppa:openmw/openmw-daily
add-apt-repository -y ppa:openmw/staging
apt-get -qq -o dir::cache::archives="$APT_CACHE_DIR" install -y --no-install-recommends "${deps[@]}" >/dev/null
apt list --installed

View file

@ -9,7 +9,7 @@ git checkout FETCH_HEAD
cd ..
xvfb-run --auto-servernum --server-args='-screen 0 640x480x24x60' \
scripts/integration_tests.py --verbose --omw build/install/bin/openmw --workdir integration_tests_output example-suite/
scripts/integration_tests.py --omw build/install/bin/openmw --workdir integration_tests_output example-suite/
ls integration_tests_output/*.osg_stats.log | while read v; do
scripts/osg_stats.py --stats '.*' --regexp_match < "${v}"

View file

@ -82,7 +82,7 @@ message(STATUS "Configuring OpenMW...")
set(OPENMW_VERSION_MAJOR 0)
set(OPENMW_VERSION_MINOR 49)
set(OPENMW_VERSION_RELEASE 0)
set(OPENMW_LUA_API_REVISION 72)
set(OPENMW_LUA_API_REVISION 70)
set(OPENMW_POSTPROCESSING_API_REVISION 2)
set(OPENMW_VERSION_COMMITHASH "")
@ -860,6 +860,7 @@ if (OPENMW_OSX_DEPLOYMENT AND APPLE)
set(BU_CHMOD_BUNDLE_ITEMS ON)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH})
include(BundleUtilities)
cmake_minimum_required(VERSION 3.1)
" COMPONENT Runtime)
set(ABSOLUTE_PLUGINS "")

View file

@ -179,7 +179,7 @@ namespace
generateKeys(std::back_inserter(keys), keys.size() * (100 - hitPercentage) / 100, random);
std::size_t n = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
const auto& key = keys[n++ % keys.size()];
auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);

View file

@ -104,7 +104,7 @@ namespace
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serialize());
if (++i >= refIds.size())
@ -118,7 +118,7 @@ namespace
std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serialize(); });
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserialize(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
@ -131,7 +131,7 @@ namespace
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateStringRefIds(state.range(0), random);
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
@ -145,7 +145,7 @@ namespace
std::vector<std::string> serializedRefIds
= generateSerializedStringRefIds(state.range(0), random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
@ -158,7 +158,7 @@ namespace
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateGeneratedRefIds(random);
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
@ -172,7 +172,7 @@ namespace
std::vector<std::string> serializedRefIds
= generateSerializedGeneratedRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
@ -185,7 +185,7 @@ namespace
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateIndexRefIds(random);
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
@ -199,7 +199,7 @@ namespace
std::vector<std::string> serializedRefIds
= generateSerializedIndexRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())
@ -212,7 +212,7 @@ namespace
std::minstd_rand random;
std::vector<ESM::RefId> refIds = generateESM3ExteriorCellRefIds(random);
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(refIds[i].serializeText());
if (++i >= refIds.size())
@ -226,7 +226,7 @@ namespace
std::vector<std::string> serializedRefIds
= generateSerializedESM3ExteriorCellRefIds(random, [](ESM::RefId v) { return v.serializeText(); });
std::size_t i = 0;
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(ESM::RefId::deserializeText(serializedRefIds[i]));
if (++i >= serializedRefIds.size())

View file

@ -9,7 +9,7 @@ namespace
{
void settingsManager(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::Manager::getFloat("sky blending start", "Fog"));
}
@ -17,7 +17,7 @@ namespace
void settingsManager2(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera"));
benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing"));
@ -26,7 +26,7 @@ namespace
void settingsManager3(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::Manager::getFloat("near clip", "Camera"));
benchmark::DoNotOptimize(Settings::Manager::getBool("transparent postpass", "Post Processing"));
@ -36,7 +36,7 @@ namespace
void localStatic(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
static float v = Settings::Manager::getFloat("sky blending start", "Fog");
benchmark::DoNotOptimize(v);
@ -45,7 +45,7 @@ namespace
void localStatic2(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
@ -56,7 +56,7 @@ namespace
void localStatic3(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
@ -69,7 +69,7 @@ namespace
void settingsStorage(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
float v = Settings::fog().mSkyBlendingStart.get();
benchmark::DoNotOptimize(v);
@ -78,7 +78,7 @@ namespace
void settingsStorage2(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get();
@ -89,7 +89,7 @@ namespace
void settingsStorage3(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get();
@ -102,7 +102,7 @@ namespace
void settingsStorageGet(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::get<float>("Fog", "sky blending start"));
}
@ -110,7 +110,7 @@ namespace
void settingsStorageGet2(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass"));
benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip"));
@ -119,7 +119,7 @@ namespace
void settingsStorageGet3(benchmark::State& state)
{
for ([[maybe_unused]] auto _ : state)
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::get<bool>("Post Processing", "transparent postpass"));
benchmark::DoNotOptimize(Settings::get<float>("Camera", "near clip"));

View file

@ -53,6 +53,8 @@ namespace
bpo::options_description makeOptionsDescription()
{
using Fallback::FallbackMap;
bpo::options_description result;
auto addOption = result.add_options();
addOption("help", "print help message");
@ -85,8 +87,7 @@ namespace
"\n\twin1251 - Cyrillic alphabet such as Russian, Bulgarian, Serbian Cyrillic and other languages\n"
"\n\twin1252 - Western European (Latin) alphabet, used by default");
addOption("fallback",
bpo::value<Fallback::FallbackMap>()->default_value(Fallback::FallbackMap(), "")->multitoken()->composing(),
addOption("fallback", bpo::value<FallbackMap>()->default_value(FallbackMap(), "")->multitoken()->composing(),
"fallback values");
Files::ConfigurationManager::addCommonOptions(result);

View file

@ -5,9 +5,7 @@
#include <components/detournavigator/makenavmesh.hpp>
#include <components/detournavigator/navmeshdbutils.hpp>
#include <components/detournavigator/serialization.hpp>
#include <components/files/conversion.hpp>
#include <components/loadinglistener/loadinglistener.hpp>
#include <components/testing/util.hpp>
#include <BulletCollision/CollisionShapes/btBoxShape.h>
@ -374,106 +372,6 @@ namespace
}
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_recast_mesh)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "0.0.recastmesh.obj"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_recast_mesh_with_revision)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
mSettings.mEnableRecastMeshFileNameRevision = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "0.0.recastmesh.1.2.obj"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, writing_recast_mesh_to_absent_file_should_not_fail_tile_generation)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteRecastMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDir() / "absent";
mSettings.mRecastMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
EXPECT_FALSE(std::filesystem::exists(dir));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_navmesh)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "all_tiles_navmesh.bin"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, should_write_debug_navmesh_with_revision)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
mSettings.mEnableNavMeshFileNameRevision = true;
const std::filesystem::path dir = TestingOpenMW::outputDirPath("DetourNavigatorAsyncNavMeshUpdaterTest");
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_TRUE(std::filesystem::exists(dir / "all_tiles_navmesh.1.1.bin"));
}
TEST_F(DetourNavigatorAsyncNavMeshUpdaterTest, writing_navmesh_to_absent_file_should_not_fail_tile_generation)
{
mRecastMeshManager.setWorldspace(mWorldspace, nullptr);
addHeightFieldPlane(mRecastMeshManager);
mSettings.mEnableWriteNavMeshToFile = true;
const std::filesystem::path dir = TestingOpenMW::outputDir() / "absent";
mSettings.mNavMeshPathPrefix = Files::pathToUnicodeString(dir) + "/";
Log(Debug::Verbose) << mSettings.mRecastMeshPathPrefix;
AsyncNavMeshUpdater updater(mSettings, mRecastMeshManager, mOffMeshConnectionsManager, nullptr);
const auto navMeshCacheItem = std::make_shared<GuardedNavMeshCacheItem>(1, mSettings);
const std::map<TilePosition, ChangeType> changedTiles{ { TilePosition{ 0, 0 }, ChangeType::add } };
updater.post(mAgentBounds, navMeshCacheItem, mPlayerTile, mWorldspace, changedTiles);
updater.wait(WaitConditionType::allJobsDone, &mListener);
EXPECT_NE(navMeshCacheItem->lockConst()->getImpl().getTileRefAt(0, 0, 0), 0u);
EXPECT_FALSE(std::filesystem::exists(dir));
}
struct DetourNavigatorSpatialJobQueueTest : Test
{
const AgentBounds mAgentBounds{ CollisionShapeType::Aabb, osg::Vec3f(1, 1, 1) };

View file

@ -88,31 +88,4 @@ namespace
EXPECT_EQ(reader->getFileOffset(), sInitialOffset);
}
}
TEST_F(ESM3ReadersCacheWithContentFile, CachedSizeAndName)
{
ESM::ReadersCache readers(2);
{
readers.get(0)->openRaw(std::make_unique<std::istringstream>("123"), "closed0.omwaddon");
readers.get(1)->openRaw(std::make_unique<std::istringstream>("12345"), "closed1.omwaddon");
readers.get(2)->openRaw(std::make_unique<std::istringstream>("1234567"), "free.omwaddon");
}
auto busy = readers.get(3);
busy->openRaw(std::make_unique<std::istringstream>("123456789"), "busy.omwaddon");
EXPECT_EQ(readers.getFileSize(0), 3);
EXPECT_EQ(readers.getName(0), "closed0.omwaddon");
EXPECT_EQ(readers.getFileSize(1), 5);
EXPECT_EQ(readers.getName(1), "closed1.omwaddon");
EXPECT_EQ(readers.getFileSize(2), 7);
EXPECT_EQ(readers.getName(2), "free.omwaddon");
EXPECT_EQ(readers.getFileSize(3), 9);
EXPECT_EQ(readers.getName(3), "busy.omwaddon");
// not-yet-seen indices give zero for their size
EXPECT_EQ(readers.getFileSize(4), 0);
}
}

View file

@ -30,7 +30,7 @@ namespace
TEST(FilesGetHash, shouldClearErrors)
{
const auto fileName = outputFilePath("fileName");
const auto fileName = temporaryFilePath("fileName");
std::string content;
std::fill_n(std::back_inserter(content), 1, 'a');
std::istringstream stream(content);
@ -41,7 +41,7 @@ namespace
TEST_P(FilesGetHash, shouldReturnHashForStringStream)
{
const auto fileName = outputFilePath("fileName");
const auto fileName = temporaryFilePath("fileName");
std::string content;
std::fill_n(std::back_inserter(content), GetParam().mSize, 'a');
std::istringstream stream(content);

View file

@ -38,10 +38,10 @@ namespace
sol::state lua;
LuaUtil::InputAction::Registry registry;
LuaUtil::InputAction::Info a({ "a", LuaUtil::InputAction::Type::Boolean, "test", "a_name", "a_description",
sol::make_object(lua, false), false });
sol::make_object(lua, false) });
registry.insert(a);
LuaUtil::InputAction::Info b({ "b", LuaUtil::InputAction::Type::Boolean, "test", "b_name", "b_description",
sol::make_object(lua, false), false });
sol::make_object(lua, false) });
registry.insert(b);
LuaUtil::Callback bindA({ lua.load("return function() return true end")(), sol::table(lua, sol::create) });
LuaUtil::Callback bindBToA(

View file

@ -2,7 +2,6 @@
#include <components/misc/strings/conversion.hpp>
#include <components/settings/parser.hpp>
#include <components/settings/values.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h>
@ -25,9 +24,5 @@ int main(int argc, char** argv)
Settings::StaticValues::init();
testing::InitGoogleTest(&argc, argv);
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
return RUN_ALL_TESTS();
}

View file

@ -51,7 +51,7 @@ namespace Misc
const std::pair<osg::Quat, osg::Vec3f> eulerAnglesXZQuat[] = {
{
osg::Quat(1, 0, 0, 0),
osg::Vec3f(0, 0, osg::PIf),
osg::Vec3f(0, 0, osg::PI),
},
{
osg::Quat(0, 1, 0, 0),
@ -59,7 +59,7 @@ namespace Misc
},
{
osg::Quat(0, 0, 1, 0),
osg::Vec3f(0, 0, osg::PIf),
osg::Vec3f(0, 0, osg::PI),
},
{
osg::Quat(0, 0, 0, 1),
@ -128,15 +128,15 @@ namespace Misc
const std::pair<osg::Quat, osg::Vec3f> eulerAnglesZYXQuat[] = {
{
osg::Quat(1, 0, 0, 0),
osg::Vec3f(osg::PIf, 0, 0),
osg::Vec3f(osg::PI, 0, 0),
},
{
osg::Quat(0, 1, 0, 0),
osg::Vec3f(osg::PIf, 0, osg::PIf),
osg::Vec3f(osg::PI, 0, osg::PI),
},
{
osg::Quat(0, 0, 1, 0),
osg::Vec3f(0, 0, osg::PIf),
osg::Vec3f(0, 0, osg::PI),
},
{
osg::Quat(0, 0, 0, 1),

View file

@ -16,7 +16,7 @@ namespace
ShaderManager mManager;
ShaderManager::DefineMap mDefines;
ShaderManagerTest() { mManager.setShaderPath(TestingOpenMW::outputDir()); }
ShaderManagerTest() { mManager.setShaderPath("tests_output"); }
template <class F>
void withShaderFile(const std::string& content, F&& f)

View file

@ -8,7 +8,6 @@
#include <QList>
#include <QMessageBox>
#include <QPair>
#include <QProgressDialog>
#include <QPushButton>
#include <algorithm>
@ -352,17 +351,9 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
if (!resourcesVfs.isEmpty())
directories.insert(0, { resourcesVfs });
QIcon containsDataIcon(":/images/openmw-plugin.png");
QProgressDialog progressBar("Adding data directories", {}, 0, directories.count(), this);
progressBar.setWindowModality(Qt::WindowModal);
progressBar.setValue(0);
std::unordered_set<QString> visitedDirectories;
for (const Config::SettingValue& currentDir : directories)
{
progressBar.setValue(progressBar.value() + 1);
if (!visitedDirectories.insert(currentDir.value).second)
continue;
@ -411,7 +402,7 @@ void Launcher::DataFilesPage::populateFileViews(const QString& contentModelName)
// Add a "data file" icon if the directory contains a content file
if (mSelector->containsDataFiles(currentDir.value))
{
item->setIcon(containsDataIcon);
item->setIcon(QIcon(":/images/openmw-plugin.png"));
tooltip << tr("Contains content file(s)");
}
@ -774,7 +765,7 @@ void Launcher::DataFilesPage::addSubdirectories(bool append)
return;
QString rootPath = QFileDialog::getExistingDirectory(
this, tr("Select Directory"), {}, QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly);
this, tr("Select Directory"), QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::Option::ReadOnly);
if (rootPath.isEmpty())
return;

View file

@ -1,16 +1,20 @@
set(NAVMESHTOOL_LIB
set(NAVMESHTOOL
worldspacedata.cpp
navmesh.cpp
main.cpp
)
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL})
add_library(openmw-navmeshtool-lib STATIC
${NAVMESHTOOL}
)
source_group(apps\\navmeshtool FILES ${NAVMESHTOOL_LIB} main.cpp)
add_library(openmw-navmeshtool-lib STATIC ${NAVMESHTOOL_LIB})
if (ANDROID)
add_library(openmw-navmeshtool SHARED main.cpp)
add_library(openmw-navmeshtool SHARED
main.cpp
)
else()
openmw_add_executable(openmw-navmeshtool main.cpp)
openmw_add_executable(openmw-navmeshtool ${NAVMESHTOOL})
endif()
target_link_libraries(openmw-navmeshtool openmw-navmeshtool-lib)

View file

@ -62,6 +62,8 @@ namespace NavMeshTool
bpo::options_description makeOptionsDescription()
{
using Fallback::FallbackMap;
bpo::options_description result;
auto addOption = result.add_options();
addOption("help", "print help message");
@ -223,8 +225,7 @@ namespace NavMeshTool
Resource::SceneManager sceneManager(&vfs, &imageManager, &nifFileManager, &bgsmFileManager, expiryDelay);
Resource::BulletShapeManager bulletShapeManager(&vfs, &sceneManager, &nifFileManager, expiryDelay);
DetourNavigator::RecastGlobalAllocator::init();
DetourNavigator::Settings navigatorSettings
= DetourNavigator::makeSettingsFromSettingsManager(Debug::getRecastMaxLogLevel());
DetourNavigator::Settings navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
navigatorSettings.mRecast.mSwimHeightScale
= EsmLoader::getGameSetting(esmData.mGameSettings, "fSwimHeightScale").getFloat();

View file

@ -38,6 +38,8 @@
#include "view/doc/viewmanager.hpp"
using namespace Fallback;
CS::Editor::Editor(int argc, char** argv)
: mConfigVariables(readConfiguration())
, mSettingsState(mCfgMgr)
@ -122,10 +124,7 @@ boost::program_options::variables_map CS::Editor::readConfiguration()
->default_value(std::vector<std::string>(), "fallback-archive")
->multitoken());
addOption("fallback",
boost::program_options::value<Fallback::FallbackMap>()
->default_value(Fallback::FallbackMap(), "")
->multitoken()
->composing(),
boost::program_options::value<FallbackMap>()->default_value(FallbackMap(), "")->multitoken()->composing(),
"fallback values");
Files::ConfigurationManager::addCommonOptions(desc);
@ -142,7 +141,7 @@ std::pair<Files::PathContainer, std::vector<std::string>> CS::Editor::readConfig
{
boost::program_options::variables_map& variables = mConfigVariables;
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
mEncodingName = variables["encoding"].as<std::string>();
mDocumentManager.setEncoding(ToUTF8::calculateEncoding(mEncodingName));

View file

@ -1164,30 +1164,23 @@ void CSVDoc::View::onRequestFocus(const std::string& id)
QScreen* CSVDoc::View::getWidgetScreen(const QPoint& position)
{
QScreen* screen = QApplication::screenAt(position);
if (screen)
return screen;
const QList<QScreen*> screens = QApplication::screens();
if (screens.isEmpty())
throw std::runtime_error("No screens available");
int closestDistance = std::numeric_limits<int>::max();
for (QScreen* candidate : screens)
if (screen == nullptr)
{
const QRect geometry = candidate->geometry();
const int dx = position.x() - std::clamp(position.x(), geometry.left(), geometry.right());
const int dy = position.y() - std::clamp(position.y(), geometry.top(), geometry.bottom());
const int distance = dx * dx + dy * dy;
QPoint clampedPosition = position;
if (distance < closestDistance)
{
closestDistance = distance;
screen = candidate;
}
// If we failed to find the screen,
// clamp negative positions and try again
if (clampedPosition.x() <= 0)
clampedPosition.setX(0);
if (clampedPosition.y() <= 0)
clampedPosition.setY(0);
screen = QApplication::screenAt(clampedPosition);
}
if (screen == nullptr)
screen = screens.first();
throw std::runtime_error(
Misc::StringUtils::format("Can not detect the screen for position [%d, %d]", position.x(), position.y()));
return screen;
}

View file

@ -1,5 +1,4 @@
#include <components/debug/debugging.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h>
@ -8,9 +7,5 @@ int main(int argc, char* argv[])
Log::sMinDebugLevel = Debug::getDebugLevel();
testing::InitGoogleTest(&argc, argv);
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
return RUN_ALL_TESTS();
}

View file

@ -373,15 +373,11 @@ OMW::Engine::Engine(Files::ConfigurationManager& configurationManager)
, mScriptConsoleMode(false)
, mActivationDistanceOverride(-1)
, mGrab(true)
, mExportFonts(false)
, mRandomSeed(0)
, mNewGame(false)
, mCfgMgr(configurationManager)
, mGlMaxTextureImageUnits(0)
{
#if SDL_VERSION_ATLEAST(2, 24, 0)
SDL_SetHint(SDL_HINT_MAC_OPENGL_ASYNC_DISPATCH, "1");
#endif
SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); // We use only gamepads
Uint32 flags
@ -811,7 +807,7 @@ void OMW::Engine::prepareEngine()
rootNode->addChild(guiRoot);
mWindowManager = std::make_unique<MWGui::WindowManager>(mWindow, mViewer, guiRoot, mResourceSystem.get(),
mWorkQueue.get(), mCfgMgr.getLogPath(), mScriptConsoleMode, mTranslationDataStorage, mEncoding, mExportFonts,
mWorkQueue.get(), mCfgMgr.getLogPath(), mScriptConsoleMode, mTranslationDataStorage, mEncoding,
Version::getOpenmwVersionDescription(), shadersSupported, mCfgMgr);
mEnvironment.setWindowManager(*mWindowManager);
@ -851,7 +847,7 @@ void OMW::Engine::prepareEngine()
}
listener->loadingOff();
mWorld->init(mMaxRecastLogLevel, mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue);
mWorld->init(mViewer, std::move(rootNode), mWorkQueue.get(), *mUnrefQueue);
mEnvironment.setWorldScene(mWorld->getWorldScene());
mWorld->setupPlayer();
mWorld->setRandomSeed(mRandomSeed);
@ -1113,11 +1109,6 @@ void OMW::Engine::setWarningsMode(int mode)
mWarningsMode = mode;
}
void OMW::Engine::enableFontExport(bool exportFonts)
{
mExportFonts = exportFonts;
}
void OMW::Engine::setSaveGameFile(const std::filesystem::path& savegame)
{
mSaveGameFile = savegame;

View file

@ -4,7 +4,6 @@
#include <filesystem>
#include <components/compiler/extensions.hpp>
#include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp>
#include <components/files/collections.hpp>
#include <components/settings/settings.hpp>
@ -172,9 +171,7 @@ namespace OMW
// Grab mouse?
bool mGrab;
bool mExportFonts;
unsigned int mRandomSeed;
Debug::Level mMaxRecastLogLevel = Debug::Error;
Compiler::Extensions mExtensions;
std::unique_ptr<Compiler::Context> mScriptContext;
@ -183,9 +180,6 @@ namespace OMW
Translation::Storage mTranslationDataStorage;
bool mNewGame;
Files::ConfigurationManager& mCfgMgr;
int mGlMaxTextureImageUnits;
// not implemented
Engine(const Engine&);
Engine& operator=(const Engine&);
@ -257,14 +251,14 @@ namespace OMW
void setWarningsMode(int mode);
void enableFontExport(bool exportFonts);
/// Set the save game file to load after initialising the engine.
void setSaveGameFile(const std::filesystem::path& savegame);
void setRandomSeed(unsigned int seed);
void setRecastMaxLogLevel(Debug::Level value) { mMaxRecastLogLevel = value; }
private:
Files::ConfigurationManager& mCfgMgr;
int mGlMaxTextureImageUnits;
};
}

View file

@ -28,6 +28,8 @@ extern "C" __declspec(dllexport) DWORD AmdPowerXpressRequestHighPerformance = 0x
#include <unistd.h>
#endif
using namespace Fallback;
/**
* \brief Parses application command line and calls \ref Cfg::ConfigurationManager
* to parse configuration files.
@ -150,10 +152,9 @@ bool parseOptions(int argc, char** argv, OMW::Engine& engine, Files::Configurati
engine.setSaveGameFile(variables["load-savegame"].as<Files::MaybeQuotedPath>().u8string());
// other settings
Fallback::Map::init(variables["fallback"].as<Fallback::FallbackMap>().mMap);
Fallback::Map::init(variables["fallback"].as<FallbackMap>().mMap);
engine.setSoundUsage(!variables["no-sound"].as<bool>());
engine.setActivationDistanceOverride(variables["activate-dist"].as<int>());
engine.enableFontExport(variables["export-fonts"].as<bool>());
engine.setRandomSeed(variables["random-seed"].as<unsigned int>());
return true;
@ -219,8 +220,6 @@ int runApplication(int argc, char* argv[])
Files::ConfigurationManager cfgMgr;
std::unique_ptr<OMW::Engine> engine = std::make_unique<OMW::Engine>(cfgMgr);
engine->setRecastMaxLogLevel(Debug::getRecastMaxLogLevel());
if (parseOptions(argc, argv, *engine, cfgMgr))
{
if (!Misc::checkRequiredOSGPluginsArePresent())

View file

@ -200,7 +200,7 @@ namespace MWBase
///< Skip the animation for the given MW-reference for one frame. Calls to this function for
/// references that are currently not in the scene should be ignored.
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) = 0;
virtual bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) = 0;
virtual bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const = 0;

View file

@ -363,7 +363,7 @@ namespace MWBase
void windowVisibilityChange(bool visible) override = 0;
void windowResized(int x, int y) override = 0;
void windowClosed() override = 0;
virtual bool isWindowVisible() const = 0;
virtual bool isWindowVisible() = 0;
virtual void watchActor(const MWWorld::Ptr& ptr) = 0;
virtual MWWorld::Ptr getWatchedActor() const = 0;

View file

@ -237,12 +237,7 @@ namespace MWClass
bool Container::hasToolTip(const MWWorld::ConstPtr& ptr) const
{
if (const MWWorld::CustomData* data = ptr.getRefData().getCustomData())
{
if (!canBeHarvested(ptr))
return true;
const MWWorld::ContainerStore& store = data->asContainerCustomData().mStore;
return !store.isResolved() || store.hasVisibleItems();
}
return !canBeHarvested(ptr) || data->asContainerCustomData().mStore.hasVisibleItems();
return true;
}

View file

@ -236,8 +236,11 @@ namespace MWClass
}
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
float dist = store.find("fCombatDistance")->mValue.getFloat();
if (!weapon.isEmpty())
dist *= weapon.get<ESM::Weapon>()->mBase->mData.mReach;
const float dist = MWMechanics::getMeleeWeaponReach(ptr, weapon);
const std::pair<MWWorld::Ptr, osg::Vec3f> result = MWMechanics::getHitContact(ptr, dist);
if (result.first.isEmpty()) // Didn't hit anything
return true;
@ -278,9 +281,6 @@ namespace MWClass
if (otherstats.isDead()) // Can't hit dead actors
return;
if (!MWMechanics::isInMeleeReach(ptr, victim, MWMechanics::getMeleeWeaponReach(ptr, weapon)))
return;
if (!success)
{
victim.getClass().onHit(

View file

@ -572,8 +572,12 @@ namespace MWClass
weapon = *weaponslot;
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
float dist = fCombatDistance
* (!weapon.isEmpty() ? weapon.get<ESM::Weapon>()->mBase->mData.mReach
: store.find("fHandToHandReach")->mValue.getFloat());
const float dist = MWMechanics::getMeleeWeaponReach(ptr, weapon);
const std::pair<MWWorld::Ptr, osg::Vec3f> result = MWMechanics::getHitContact(ptr, dist);
if (result.first.isEmpty()) // Didn't hit anything
return true;
@ -611,9 +615,6 @@ namespace MWClass
if (otherstats.isDead()) // Can't hit dead actors
return;
if (!MWMechanics::isInMeleeReach(ptr, victim, MWMechanics::getMeleeWeaponReach(ptr, weapon)))
return;
if (ptr == MWMechanics::getPlayer())
MWBase::Environment::get().getWindowManager()->setEnemy(victim);

View file

@ -1343,13 +1343,12 @@ namespace MWGui
return codePoint == '\r';
}
// Normal no-break space (0x00A0) is ignored here
// because Morrowind compatibility requires us to render its glyph
static bool ucsSpace(int codePoint)
{
switch (codePoint)
{
case 0x0020: // SPACE
case 0x00A0: // NO-BREAK SPACE
case 0x1680: // OGHAM SPACE MARK
case 0x180E: // MONGOLIAN VOWEL SEPARATOR
case 0x2000: // EN QUAD
@ -1374,14 +1373,12 @@ namespace MWGui
}
}
// No-break spaces (0x00A0, 0x202F, 0xFEFF - normal, narrow, zero width)
// are ignored here for obvious reasons
// Figure space (0x2007) is not a breaking space either
static bool ucsBreakingSpace(int codePoint)
{
switch (codePoint)
{
case 0x0020: // SPACE
// case 0x00A0: // NO-BREAK SPACE
case 0x1680: // OGHAM SPACE MARK
case 0x180E: // MONGOLIAN VOWEL SEPARATOR
case 0x2000: // EN QUAD
@ -1391,12 +1388,15 @@ namespace MWGui
case 0x2004: // THREE-PER-EM SPACE
case 0x2005: // FOUR-PER-EM SPACE
case 0x2006: // SIX-PER-EM SPACE
case 0x2007: // FIGURE SPACE
case 0x2008: // PUNCTUATION SPACE
case 0x2009: // THIN SPACE
case 0x200A: // HAIR SPACE
case 0x200B: // ZERO WIDTH SPACE
case 0x202F: // NARROW NO-BREAK SPACE
case 0x205F: // MEDIUM MATHEMATICAL SPACE
case 0x3000: // IDEOGRAPHIC SPACE
// case 0xFEFF: // ZERO WIDTH NO-BREAK SPACE
return true;
default:
return false;

View file

@ -666,8 +666,7 @@ namespace MWGui
else if (scrollbar)
{
mHistory->setSize(MyGUI::IntSize(mHistory->getWidth(), book->getSize().second));
// Scroll range should be >= 2 to enable scrolling and prevent a crash
size_t range = std::max(book->getSize().second - viewHeight, size_t(2));
size_t range = book->getSize().second - viewHeight;
mScrollBar->setScrollRange(range);
mScrollBar->setScrollPosition(range - 1);
mScrollBar->setTrackSize(

View file

@ -29,26 +29,11 @@ namespace MWGui
{
Misc::FrameRateLimiter frameRateLimiter
= Misc::makeFrameRateLimiter(MWBase::Environment::get().getFrameRateLimit());
const MWBase::WindowManager& windowManager = *MWBase::Environment::get().getWindowManager();
bool paused = false;
while (mRunning)
{
if (windowManager.isWindowVisible())
{
if (paused)
{
mVideo->resume();
paused = false;
}
// If finished playing, start again
if (!mVideo->update())
mVideo->playVideo("video\\menu_background.bik");
}
else if (!paused)
{
paused = true;
mVideo->pause();
}
// If finished playing, start again
if (!mVideo->update())
mVideo->playVideo("video\\menu_background.bik");
frameRateLimiter.limit();
}
}

View file

@ -146,7 +146,7 @@ namespace MWGui
WindowManager::WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue, const std::filesystem::path& logpath,
bool consoleOnlyScripts, Translation::Storage& translationDataStorage, ToUTF8::FromType encoding,
bool exportFonts, const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr)
const std::string& versionDescription, bool useShaders, Files::ConfigurationManager& cfgMgr)
: mOldUpdateMask(0)
, mOldCullMask(0)
, mStore(nullptr)
@ -215,8 +215,7 @@ namespace MWGui
MyGUI::LanguageManager::getInstance().eventRequestTag = MyGUI::newDelegate(this, &WindowManager::onRetrieveTag);
// Load fonts
mFontLoader
= std::make_unique<Gui::FontLoader>(encoding, resourceSystem->getVFS(), mScalingFactor, exportFonts);
mFontLoader = std::make_unique<Gui::FontLoader>(encoding, resourceSystem->getVFS(), mScalingFactor);
// Register own widgets with MyGUI
MyGUI::FactoryManager::getInstance().registerFactory<MWGui::Widgets::MWSkill>("Widget");
@ -730,9 +729,6 @@ namespace MWGui
return;
}
if (mGuiModes.empty())
return;
GuiModeState& state = mGuiModeStates[mGuiModes.back()];
for (const auto& window : state.mWindows)
{
@ -1218,7 +1214,7 @@ namespace MWGui
// TODO: check if any windows are now off-screen and move them back if so
}
bool WindowManager::isWindowVisible() const
bool WindowManager::isWindowVisible()
{
return mWindowVisible;
}

View file

@ -128,7 +128,7 @@ namespace MWGui
WindowManager(SDL_Window* window, osgViewer::Viewer* viewer, osg::Group* guiRoot,
Resource::ResourceSystem* resourceSystem, SceneUtil::WorkQueue* workQueue,
const std::filesystem::path& logpath, bool consoleOnlyScripts, Translation::Storage& translationDataStorage,
ToUTF8::FromType encoding, bool exportFonts, const std::string& versionDescription, bool useShaders,
ToUTF8::FromType encoding, const std::string& versionDescription, bool useShaders,
Files::ConfigurationManager& cfgMgr);
virtual ~WindowManager();
@ -290,7 +290,7 @@ namespace MWGui
void windowVisibilityChange(bool visible) override;
void windowResized(int x, int y) override;
void windowClosed() override;
bool isWindowVisible() const override;
bool isWindowVisible() override;
void watchActor(const MWWorld::Ptr& ptr) override;
MWWorld::Ptr getWatchedActor() const override;

View file

@ -148,7 +148,7 @@ namespace MWLua
};
}
sol::table readOnlyApi = LuaUtil::makeReadOnly(api);
return context.setTypePackage(readOnlyApi, "openmw_core");
sol::table readOnly = LuaUtil::makeReadOnly(api);
return context.setTypePackage(readOnly, "openmw_core");
}
}

View file

@ -38,116 +38,94 @@ namespace MWLua
sol::table initInputPackage(const Context& context)
{
sol::object cached = context.getTypePackage("openmw_input");
if (cached != sol::nil)
return cached;
sol::state_view lua = context.sol();
{
if (lua["openmw_input"] != sol::nil)
return lua["openmw_input"];
}
context.cachePackage("openmw_input_keyevent", [&lua]() {
sol::usertype<SDL_Keysym> keyEvent = lua.new_usertype<SDL_Keysym>("KeyEvent");
keyEvent["symbol"] = sol::readonly_property([](const SDL_Keysym& e) {
if (e.sym > 0 && e.sym <= 255)
return std::string(1, static_cast<char>(e.sym));
else
return std::string();
});
keyEvent["code"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.scancode; });
keyEvent["withShift"]
= sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_SHIFT; });
keyEvent["withCtrl"]
= sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_CTRL; });
keyEvent["withAlt"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_ALT; });
keyEvent["withSuper"]
= sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_GUI; });
return sol::table(lua, sol::create);
sol::usertype<SDL_Keysym> keyEvent = lua.new_usertype<SDL_Keysym>("KeyEvent");
keyEvent["symbol"] = sol::readonly_property([](const SDL_Keysym& e) {
if (e.sym > 0 && e.sym <= 255)
return std::string(1, static_cast<char>(e.sym));
else
return std::string();
});
keyEvent["code"] = sol::readonly_property([](const SDL_Keysym& e) -> int { return e.scancode; });
keyEvent["withShift"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_SHIFT; });
keyEvent["withCtrl"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_CTRL; });
keyEvent["withAlt"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_ALT; });
keyEvent["withSuper"] = sol::readonly_property([](const SDL_Keysym& e) -> bool { return e.mod & KMOD_GUI; });
context.cachePackage("openmw_input_touchpadevent", [&lua]() {
auto touchpadEvent = lua.new_usertype<SDLUtil::TouchEvent>("TouchpadEvent");
touchpadEvent["device"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mDevice; });
touchpadEvent["finger"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mFinger; });
touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f {
return { e.mX, e.mY };
});
touchpadEvent["pressure"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> float { return e.mPressure; });
return sol::table(lua, sol::create);
auto touchpadEvent = lua.new_usertype<SDLUtil::TouchEvent>("TouchpadEvent");
touchpadEvent["device"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mDevice; });
touchpadEvent["finger"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> int { return e.mFinger; });
touchpadEvent["position"] = sol::readonly_property([](const SDLUtil::TouchEvent& e) -> osg::Vec2f {
return { e.mX, e.mY };
});
touchpadEvent["pressure"]
= sol::readonly_property([](const SDLUtil::TouchEvent& e) -> float { return e.mPressure; });
context.cachePackage("openmw_input_inputactions", [&lua]() {
auto inputActions = lua.new_usertype<LuaUtil::InputAction::Registry>("InputActions");
inputActions[sol::meta_function::index]
= [](LuaUtil::InputAction::Registry& registry, std::string_view key) { return registry[key]; };
{
auto pairs = [](LuaUtil::InputAction::Registry& registry) {
auto next = [](LuaUtil::InputAction::Registry& registry, std::string_view key)
-> sol::optional<std::tuple<std::string, LuaUtil::InputAction::Info>> {
std::optional<std::string> nextKey(registry.nextKey(key));
if (!nextKey.has_value())
return sol::nullopt;
else
return std::make_tuple(*nextKey, registry[*nextKey].value());
};
return std::make_tuple(next, registry, registry.firstKey());
auto inputActions = lua.new_usertype<LuaUtil::InputAction::Registry>("InputActions");
inputActions[sol::meta_function::index]
= [](LuaUtil::InputAction::Registry& registry, std::string_view key) { return registry[key]; };
{
auto pairs = [](LuaUtil::InputAction::Registry& registry) {
auto next
= [](LuaUtil::InputAction::Registry& registry,
std::string_view key) -> sol::optional<std::tuple<std::string, LuaUtil::InputAction::Info>> {
std::optional<std::string> nextKey(registry.nextKey(key));
if (!nextKey.has_value())
return sol::nullopt;
else
return std::make_tuple(*nextKey, registry[*nextKey].value());
};
inputActions[sol::meta_function::pairs] = pairs;
}
return sol::table(lua, sol::create);
});
return std::make_tuple(next, registry, registry.firstKey());
};
inputActions[sol::meta_function::pairs] = pairs;
}
context.cachePackage("openmw_input_actioninfo", [&lua]() {
auto actionInfo = lua.new_usertype<LuaUtil::InputAction::Info>("ActionInfo");
actionInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; });
actionInfo["name"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mName; });
actionInfo["description"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mDescription; });
actionInfo["l10n"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mL10n; });
actionInfo["type"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mType; });
actionInfo["defaultValue"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; });
return sol::table(lua, sol::create);
});
auto actionInfo = lua.new_usertype<LuaUtil::InputAction::Info>("ActionInfo");
actionInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mKey; });
actionInfo["name"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mName; });
actionInfo["description"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mDescription; });
actionInfo["l10n"] = sol::readonly_property(
[](const LuaUtil::InputAction::Info& info) -> std::string_view { return info.mL10n; });
actionInfo["type"] = sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mType; });
actionInfo["defaultValue"]
= sol::readonly_property([](const LuaUtil::InputAction::Info& info) { return info.mDefaultValue; });
context.cachePackage("openmw_input_inputtriggers", [&lua]() {
auto inputTriggers = lua.new_usertype<LuaUtil::InputTrigger::Registry>("InputTriggers");
inputTriggers[sol::meta_function::index]
= [](LuaUtil::InputTrigger::Registry& registry, std::string_view key) { return registry[key]; };
{
auto pairs = [](LuaUtil::InputTrigger::Registry& registry) {
auto next = [](LuaUtil::InputTrigger::Registry& registry, std::string_view key)
-> sol::optional<std::tuple<std::string, LuaUtil::InputTrigger::Info>> {
std::optional<std::string> nextKey(registry.nextKey(key));
if (!nextKey.has_value())
return sol::nullopt;
else
return std::make_tuple(*nextKey, registry[*nextKey].value());
};
return std::make_tuple(next, registry, registry.firstKey());
auto inputTriggers = lua.new_usertype<LuaUtil::InputTrigger::Registry>("InputTriggers");
inputTriggers[sol::meta_function::index]
= [](LuaUtil::InputTrigger::Registry& registry, std::string_view key) { return registry[key]; };
{
auto pairs = [](LuaUtil::InputTrigger::Registry& registry) {
auto next
= [](LuaUtil::InputTrigger::Registry& registry,
std::string_view key) -> sol::optional<std::tuple<std::string, LuaUtil::InputTrigger::Info>> {
std::optional<std::string> nextKey(registry.nextKey(key));
if (!nextKey.has_value())
return sol::nullopt;
else
return std::make_tuple(*nextKey, registry[*nextKey].value());
};
inputTriggers[sol::meta_function::pairs] = pairs;
}
return sol::table(lua, sol::create);
});
return std::make_tuple(next, registry, registry.firstKey());
};
inputTriggers[sol::meta_function::pairs] = pairs;
}
context.cachePackage("openmw_input_triggerinfo", [&lua]() {
auto triggerInfo = lua.new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo");
triggerInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; });
triggerInfo["name"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mName; });
triggerInfo["description"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mDescription; });
triggerInfo["l10n"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
return sol::table(lua, sol::create);
});
auto triggerInfo = lua.new_usertype<LuaUtil::InputTrigger::Info>("TriggerInfo");
triggerInfo["key"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mKey; });
triggerInfo["name"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mName; });
triggerInfo["description"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mDescription; });
triggerInfo["l10n"] = sol::readonly_property(
[](const LuaUtil::InputTrigger::Info& info) -> std::string_view { return info.mL10n; });
MWBase::InputManager* input = MWBase::Environment::get().getInputManager();
sol::table api(lua, sol::create);
@ -161,18 +139,16 @@ namespace MWLua
}));
api["actions"] = std::ref(context.mLuaManager->inputActions());
api["registerAction"]
= [manager = context.mLuaManager, persistent = context.mType == Context::Menu](sol::table options) {
LuaUtil::InputAction::Info parsedOptions;
parsedOptions.mKey = options["key"].get<std::string_view>();
parsedOptions.mType = options["type"].get<LuaUtil::InputAction::Type>();
parsedOptions.mL10n = options["l10n"].get<std::string_view>();
parsedOptions.mName = options["name"].get<std::string_view>();
parsedOptions.mDescription = options["description"].get<std::string_view>();
parsedOptions.mDefaultValue = options["defaultValue"].get<sol::main_object>();
parsedOptions.mPersistent = persistent;
manager->inputActions().insert(std::move(parsedOptions));
};
api["registerAction"] = [manager = context.mLuaManager](sol::table options) {
LuaUtil::InputAction::Info parsedOptions;
parsedOptions.mKey = options["key"].get<std::string_view>();
parsedOptions.mType = options["type"].get<LuaUtil::InputAction::Type>();
parsedOptions.mL10n = options["l10n"].get<std::string_view>();
parsedOptions.mName = options["name"].get<std::string_view>();
parsedOptions.mDescription = options["description"].get<std::string_view>();
parsedOptions.mDefaultValue = options["defaultValue"].get<sol::main_object>();
manager->inputActions().insert(std::move(parsedOptions));
};
api["bindAction"] = [manager = context.mLuaManager](
std::string_view key, const sol::table& callback, sol::table dependencies) {
std::vector<std::string_view> parsedDependencies;
@ -202,16 +178,14 @@ namespace MWLua
};
api["triggers"] = std::ref(context.mLuaManager->inputTriggers());
api["registerTrigger"]
= [manager = context.mLuaManager, persistent = context.mType == Context::Menu](sol::table options) {
LuaUtil::InputTrigger::Info parsedOptions;
parsedOptions.mKey = options["key"].get<std::string_view>();
parsedOptions.mL10n = options["l10n"].get<std::string_view>();
parsedOptions.mName = options["name"].get<std::string_view>();
parsedOptions.mDescription = options["description"].get<std::string_view>();
parsedOptions.mPersistent = persistent;
manager->inputTriggers().insert(std::move(parsedOptions));
};
api["registerTrigger"] = [manager = context.mLuaManager](sol::table options) {
LuaUtil::InputTrigger::Info parsedOptions;
parsedOptions.mKey = options["key"].get<std::string_view>();
parsedOptions.mL10n = options["l10n"].get<std::string_view>();
parsedOptions.mName = options["name"].get<std::string_view>();
parsedOptions.mDescription = options["description"].get<std::string_view>();
manager->inputTriggers().insert(std::move(parsedOptions));
};
api["registerTriggerHandler"]
= [manager = context.mLuaManager](std::string_view key, const sol::table& callback) {
manager->inputTriggers().registerHandler(key, LuaUtil::Callback::fromLua(callback));
@ -471,8 +445,8 @@ namespace MWLua
{ "Tab", SDL_SCANCODE_TAB },
}));
sol::table readOnlyApi = LuaUtil::makeReadOnly(api);
return context.setTypePackage(readOnlyApi, "openmw_input");
lua["openmw_input"] = LuaUtil::makeReadOnly(api);
return lua["openmw_input"];
}
}

View file

@ -71,8 +71,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }]
= sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &ItemData::setValue, std::monostate{}, prop }] = value;
}
else
throw std::runtime_error("Only global or self scripts can set the value");

View file

@ -54,7 +54,7 @@ namespace MWLua
{
}
MWBase::LuaManager::ActorControls mControls;
std::map<CachedStat, sol::main_object> mStatsCache;
std::map<CachedStat, sol::object> mStatsCache;
bool mIsActive;
};

View file

@ -343,7 +343,6 @@ namespace MWLua
mPlayerStorage.clearTemporaryAndRemoveCallbacks();
mInputActions.clear();
mInputTriggers.clear();
mQueuedAutoStartedScripts.clear();
for (int i = 0; i < 5; ++i)
lua_gc(mLua.unsafeState(), LUA_GCCOLLECT, 0);
}
@ -654,8 +653,8 @@ namespace MWLua
MWBase::Environment::get().getL10nManager()->dropCache();
mUiResourceManager.clear();
mLua.dropScriptCache();
mInputActions.clear(true);
mInputTriggers.clear(true);
mInputActions.clear();
mInputTriggers.clear();
initConfiguration();
ESM::LuaScripts globalData;

View file

@ -1061,7 +1061,7 @@ namespace MWLua
};
// types.Actor.activeEffects(o):removeEffect(id, ?arg)
activeEffectsT["remove"] = [getEffectKey, context](const ActorActiveEffects& effects, std::string_view idStr,
activeEffectsT["remove"] = [getEffectKey](const ActorActiveEffects& effects, std::string_view idStr,
sol::optional<std::string_view> argStr) {
if (!effects.isActor())
return;
@ -1071,14 +1071,12 @@ namespace MWLua
MWMechanics::EffectKey key = getEffectKey(idStr, argStr);
context.mLuaManager->addAction([key, effects]() {
// Note that, although this is member method of ActorActiveEffects and we are removing an effect (not a
// spell), we still need to use the active spells store to purge this effect from active spells.
const auto& ptr = effects.mActor.ptr();
// Note that, although this is member method of ActorActiveEffects and we are removing an effect (not a
// spell), we still need to use the active spells store to purge this effect from active spells.
const auto& ptr = effects.mActor.ptr();
auto& activeSpells = ptr.getClass().getCreatureStats(ptr).getActiveSpells();
activeSpells.purgeEffect(ptr, key.mId, key.mArg);
});
auto& activeSpells = ptr.getClass().getCreatureStats(ptr).getActiveSpells();
activeSpells.purgeEffect(ptr, key.mId, key.mArg);
};
// types.Actor.activeEffects(o):set(value, id, ?arg)

View file

@ -213,7 +213,6 @@ namespace MWLua
{ "NavMeshNotFound", DetourNavigator::Status::NavMeshNotFound },
{ "StartPolygonNotFound", DetourNavigator::Status::StartPolygonNotFound },
{ "EndPolygonNotFound", DetourNavigator::Status::EndPolygonNotFound },
{ "TargetPolygonNotFound", DetourNavigator::Status::TargetPolygonNotFound },
{ "MoveAlongSurfaceFailed", DetourNavigator::Status::MoveAlongSurfaceFailed },
{ "FindPathOverPolygonsFailed", DetourNavigator::Status::FindPathOverPolygonsFailed },
{ "InitNavMeshQueryFailed", DetourNavigator::Status::InitNavMeshQueryFailed },

View file

@ -123,8 +123,7 @@ namespace MWLua
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }]
= sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, attributeId, "skillIncreasesForAttribute" }] = value;
}
};
@ -160,7 +159,7 @@ namespace MWLua
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, specialization, "skillIncreasesForSpecialization" }]
= sol::main_object(value);
= value;
}
};
@ -184,8 +183,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }]
= sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &setCreatureValue, std::monostate{}, "current" }] = value;
}
sol::object getProgress(const Context& context) const
@ -206,8 +204,7 @@ namespace MWLua
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }]
= sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &setNpcValue, std::monostate{}, "progress" }] = value;
}
SkillIncreasesForAttributeStats getSkillIncreasesForAttributeStats() const
@ -261,7 +258,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &DynamicStat::setValue, mIndex, prop }] = value;
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -321,7 +318,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }] = sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &AttributeStat::setValue, mId, prop }] = value;
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -405,7 +402,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &SkillStat::setValue, mId, prop }] = value;
}
static void setValue(Index index, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)
@ -468,8 +465,7 @@ namespace MWLua
{
SelfObject* obj = mObject.asSelfObject();
addStatUpdateAction(context.mLuaManager, *obj);
obj->mStatsCache[SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }]
= sol::main_object(value);
obj->mStatsCache[SelfObject::CachedStat{ &AIStat::setValue, static_cast<int>(mIndex), prop }] = value;
}
static void setValue(Index i, std::string_view prop, const MWWorld::Ptr& ptr, const sol::object& value)

View file

@ -43,7 +43,7 @@ namespace MWLua
[](const ESM::CreatureLevList& rec) -> std::string { return rec.mId.serializeText(); });
record["chanceNone"] = sol::readonly_property(
[](const ESM::CreatureLevList& rec) -> float { return std::clamp(rec.mChanceNone / 100.f, 0.f, 1.f); });
record["creatures"] = sol::readonly_property([state](const ESM::CreatureLevList& rec) -> sol::table {
record["creatures"] = sol::readonly_property([&](const ESM::CreatureLevList& rec) -> sol::table {
sol::table res(state, sol::create);
for (size_t i = 0; i < rec.mList.size(); ++i)
res[LuaUtil::toLuaIndex(i)] = rec.mList[i];

View file

@ -168,8 +168,7 @@ namespace MWLua
if (index == LuaUi::Layer::count())
throw std::logic_error(std::string("Layer not found"));
index++;
context.mLuaManager->addAction(
[=, name = std::string(name)]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
};
layersTable["insertBefore"] = [context](
std::string_view beforename, std::string_view name, const sol::object& opt) {
@ -178,8 +177,7 @@ namespace MWLua
size_t index = LuaUi::Layer::indexOf(beforename);
if (index == LuaUi::Layer::count())
throw std::logic_error(std::string("Layer not found"));
context.mLuaManager->addAction(
[=, name = std::string(name)]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
context.mLuaManager->addAction([=]() { LuaUi::Layer::insert(index, name, options); }, "Insert UI layer");
};
sol::table layers = LuaUtil::makeReadOnly(layersTable);
sol::table layersMeta = layers[sol::metatable_key];
@ -287,9 +285,8 @@ namespace MWLua
return res;
};
api["_setWindowDisabled"]
= [windowManager, luaManager = context.mLuaManager](std::string window, bool disabled) {
luaManager->addAction(
[=, window = std::move(window)]() { windowManager->setDisabledByLua(window, disabled); });
= [windowManager, luaManager = context.mLuaManager](std::string_view window, bool disabled) {
luaManager->addAction([=]() { windowManager->setDisabledByLua(window, disabled); });
};
// TODO
@ -311,7 +308,7 @@ namespace MWLua
return res.str();
};
element["layout"] = sol::property([](const LuaUi::Element& element) { return element.mLayout; },
[](LuaUi::Element& element, const sol::main_table& layout) { element.mLayout = layout; });
[](LuaUi::Element& element, const sol::table& layout) { element.mLayout = layout; });
element["update"] = [luaManager = context.mLuaManager](const std::shared_ptr<LuaUi::Element>& element) {
if (element->mState != LuaUi::Element::Created)
return;

View file

@ -68,7 +68,7 @@ namespace MWLua
Log(Debug::Verbose) << "Read a large data chunk (" << size << " bytes) from '" << file.mFileName << "'.";
}
sol::object readFile(lua_State* lua, FileHandle& file)
sol::object readFile(sol::this_state lua, FileHandle& file)
{
std::ostringstream os;
if (file.mFilePtr && file.mFilePtr->peek() != EOF)
@ -79,7 +79,7 @@ namespace MWLua
return sol::make_object<std::string>(lua, std::move(result));
}
sol::object readLineFromFile(lua_State* lua, FileHandle& file)
sol::object readLineFromFile(sol::this_state lua, FileHandle& file)
{
std::string result;
if (file.mFilePtr && std::getline(*file.mFilePtr, result))
@ -91,7 +91,7 @@ namespace MWLua
return sol::nil;
}
sol::object readNumberFromFile(lua_State* lua, Files::IStreamPtr& file)
sol::object readNumberFromFile(sol::this_state lua, Files::IStreamPtr& file)
{
double number = 0;
if (file && *file >> number)
@ -100,7 +100,7 @@ namespace MWLua
return sol::nil;
}
sol::object readCharactersFromFile(lua_State* lua, FileHandle& file, size_t count)
sol::object readCharactersFromFile(sol::this_state lua, FileHandle& file, size_t count)
{
if (count <= 0 && file.mFilePtr->peek() != EOF)
return sol::make_object<std::string>(lua, std::string());
@ -189,7 +189,7 @@ namespace MWLua
return seek(lua, self, std::ios_base::cur, off);
});
handle["lines"] = [](sol::this_main_state lua, sol::main_object self) {
handle["lines"] = [](sol::this_state lua, sol::object self) {
if (!self.is<FileHandle*>())
throw std::runtime_error("self should be a file handle");
return sol::as_function([lua, self]() -> sol::object {
@ -199,7 +199,7 @@ namespace MWLua
});
};
api["lines"] = [vfs](sol::this_main_state lua, std::string_view fileName) {
api["lines"] = [vfs](sol::this_state lua, std::string_view fileName) {
auto normalizedName = VFS::Path::normalizeFilename(fileName);
return sol::as_function(
[lua, file = FileHandle(vfs->getNormalized(normalizedName), normalizedName)]() mutable {

View file

@ -289,9 +289,8 @@ namespace MWMechanics
const ESM::RefId& enchantmentId = slot->getClass().getEnchantment(*slot);
if (enchantmentId.empty())
continue;
const ESM::Enchantment* enchantment
= world->getStore().get<ESM::Enchantment>().search(enchantmentId);
if (enchantment == nullptr || enchantment->mData.mType != ESM::Enchantment::ConstantEffect)
const ESM::Enchantment* enchantment = world->getStore().get<ESM::Enchantment>().find(enchantmentId);
if (enchantment->mData.mType != ESM::Enchantment::ConstantEffect)
continue;
if (std::find_if(mSpells.begin(), mSpells.end(),
[&](const ActiveSpellParams& params) {

View file

@ -2028,7 +2028,7 @@ namespace MWMechanics
iter->second->getCharacterController().skipAnim();
}
bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) const
bool Actors::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const
{
const auto iter = mIndex.find(ptr.mRef);
if (iter != mIndex.end())

View file

@ -119,7 +119,7 @@ namespace MWMechanics
std::string_view startKey, std::string_view stopKey, bool forceLoop);
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable);
void skipAnimation(const MWWorld::Ptr& ptr) const;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) const;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) const;
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const;
void persistAnimationStates() const;
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted);

View file

@ -40,15 +40,15 @@ namespace MWMechanics
static const std::size_t MAX_IDLE_SIZE = 8;
const std::string_view AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = {
"idle2",
"idle3",
"idle4",
"idle5",
"idle6",
"idle7",
"idle8",
"idle9",
const std::string AiWander::sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1] = {
std::string("idle2"),
std::string("idle3"),
std::string("idle4"),
std::string("idle5"),
std::string("idle6"),
std::string("idle7"),
std::string("idle8"),
std::string("idle9"),
};
namespace
@ -680,7 +680,7 @@ namespace MWMechanics
{
if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle))
{
const std::string_view groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];
const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];
return MWBase::Environment::get().getMechanicsManager()->playAnimationGroup(actor, groupName, 0, 1);
}
else
@ -695,7 +695,7 @@ namespace MWMechanics
{
if ((GroupIndex_MinIdle <= idleSelect) && (idleSelect <= GroupIndex_MaxIdle))
{
const std::string_view groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];
const std::string& groupName = sIdleSelectToGroupName[idleSelect - GroupIndex_MinIdle];
return MWBase::Environment::get().getMechanicsManager()->checkAnimationPlaying(actor, groupName);
}
else

View file

@ -3,7 +3,6 @@
#include "typedaipackage.hpp"
#include <string_view>
#include <vector>
#include "aitemporarybase.hpp"
@ -182,7 +181,9 @@ namespace MWMechanics
const ESM::Pathgrid::Point& start, const ESM::Pathgrid::Point& end, AiWanderStorage& storage);
/// lookup table for converting idleSelect value to groupName
static const std::string_view sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
static const std::string sIdleSelectToGroupName[GroupIndex_MaxIdle - GroupIndex_MinIdle + 1];
static int OffsetToPreventOvercrowding();
};
}

View file

@ -1104,10 +1104,10 @@ namespace MWMechanics
attackType = ESM::Weapon::AT_Thrust;
// We want to avoid hit keys that come out of nowhere (e.g. in the follow animation)
// and processing multiple hit keys for a single attack
if (mReadyToHit)
if (mAttackStrength != -1.f)
{
charClass.hit(mPtr, mAttackStrength, attackType, mAttackVictim, mAttackHitPos, mAttackSuccess);
mReadyToHit = false;
mAttackStrength = -1.f;
}
}
else if (isRandomAttackAnimation(groupname) && action == "start")
@ -1153,10 +1153,10 @@ namespace MWMechanics
else if (action == "shoot release")
{
// See notes for melee release above
if (mReadyToHit)
if (mAttackStrength != -1.f)
{
mAnimation->releaseArrow(mAttackStrength);
mReadyToHit = false;
mAttackStrength = -1.f;
}
}
else if (action == "shoot follow attach")
@ -1246,7 +1246,7 @@ namespace MWMechanics
void CharacterController::prepareHit()
{
if (mReadyToHit)
if (mAttackStrength != -1.f)
return;
auto& prng = MWBase::Environment::get().getWorld()->getPrng();
@ -1261,8 +1261,6 @@ namespace MWMechanics
mAttackStrength = 0.f;
playSwishSound();
}
mReadyToHit = true;
}
bool CharacterController::updateWeaponState()
@ -1522,7 +1520,6 @@ namespace MWMechanics
&& (mHitState == CharState_None || mHitState == CharState_Block))
{
mAttackStrength = -1.f;
mReadyToHit = false;
// Randomize attacks for non-bipedal creatures
if (!cls.isBipedal(mPtr)
@ -1809,7 +1806,8 @@ namespace MWMechanics
stop = strength + ' ' + stop;
}
mReadyToHit = false;
// Reset attack strength to make extra sure hits that come out of nowhere aren't processed
mAttackStrength = -1.f;
if (animPlaying)
mAnimation->disable(mCurrentWeapon);

View file

@ -172,7 +172,6 @@ namespace MWMechanics
std::string mCurrentWeapon;
float mAttackStrength{ -1.f };
bool mReadyToHit{ false };
MWWorld::Ptr mAttackVictim;
osg::Vec3f mAttackHitPos;
bool mAttackSuccess{ false };

View file

@ -578,24 +578,6 @@ namespace MWMechanics
return dist;
}
float getMeleeWeaponReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& weapon)
{
MWBase::World* world = MWBase::Environment::get().getWorld();
const MWWorld::Store<ESM::GameSetting>& store = world->getStore().get<ESM::GameSetting>();
const float fCombatDistance = store.find("fCombatDistance")->mValue.getFloat();
if (!weapon.isEmpty())
return fCombatDistance * weapon.get<ESM::Weapon>()->mBase->mData.mReach;
if (actor.getClass().isNpc())
return fCombatDistance * store.find("fHandToHandReach")->mValue.getFloat();
return fCombatDistance;
}
bool isInMeleeReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const float reach)
{
const float heightDiff = actor.getRefData().getPosition().pos[2] - target.getRefData().getPosition().pos[2];
return std::abs(heightDiff) < reach && getDistanceToBounds(actor, target) < reach;
}
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach)
{
// Lasciate ogne speranza, voi ch'entrate
@ -632,12 +614,10 @@ namespace MWMechanics
{
if (actor == target || target.getClass().getCreatureStats(target).isDead())
continue;
const float dist = getDistanceToBounds(actor, target);
if (dist >= minDist || !isInMeleeReach(actor, target, reach))
continue;
const osg::Vec3f targetPos(target.getRefData().getPosition().asVec3());
if (dist >= reach || dist >= minDist || std::abs(targetPos.z() - actorPos.z()) >= reach)
continue;
// Horizontal angle checks.
osg::Vec2f actorToTargetXY{ targetPos.x() - actorPos.x(), targetPos.y() - actorPos.y() };

View file

@ -64,10 +64,6 @@ namespace MWMechanics
// Cursed distance calculation used for combat proximity and hit checks in Morrowind
float getDistanceToBounds(const MWWorld::Ptr& actor, const MWWorld::Ptr& target);
float getMeleeWeaponReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& weapon);
bool isInMeleeReach(const MWWorld::Ptr& actor, const MWWorld::Ptr& target, const float reach);
// Similarly cursed hit target selection
std::pair<MWWorld::Ptr, osg::Vec3f> getHitContact(const MWWorld::Ptr& actor, float reach);

View file

@ -778,8 +778,7 @@ namespace MWMechanics
else
mObjects.skipAnimation(ptr);
}
bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName)
bool MechanicsManager::checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName)
{
if (ptr.getClass().isActor())
return mActors.checkAnimationPlaying(ptr, groupName);

View file

@ -146,7 +146,7 @@ namespace MWMechanics
std::string_view startKey, std::string_view stopKey, bool forceLoop) override;
void enableLuaAnimations(const MWWorld::Ptr& ptr, bool enable) override;
void skipAnimation(const MWWorld::Ptr& ptr) override;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, std::string_view groupName) override;
bool checkAnimationPlaying(const MWWorld::Ptr& ptr, const std::string& groupName) override;
bool checkScriptedAnimationPlaying(const MWWorld::Ptr& ptr) const override;
void persistAnimationStates() override;
void clearAnimationQueue(const MWWorld::Ptr& ptr, bool clearScripted) override;

View file

@ -161,7 +161,7 @@ namespace MWRender
bool ActorAnimation::updateCarriedLeftVisible(const int weaptype) const
{
if (Settings::game().mShieldSheathing && mObjectRoot)
if (Settings::game().mShieldSheathing)
{
const MWWorld::Class& cls = mPtr.getClass();
MWMechanics::CreatureStats& stats = cls.getCreatureStats(mPtr);

View file

@ -1695,7 +1695,7 @@ namespace MWRender
mGlowUpdater->setColor(color);
mGlowUpdater->setDuration(glowDuration);
}
else if (mObjectRoot)
else
mGlowUpdater = SceneUtil::addEnchantedGlow(mObjectRoot, mResourceSystem, color, glowDuration);
}
}
@ -1869,7 +1869,7 @@ namespace MWRender
void Animation::setAlpha(float alpha)
{
if (alpha == mAlpha || !mObjectRoot)
if (alpha == mAlpha)
return;
mAlpha = alpha;

View file

@ -260,6 +260,7 @@ namespace MWRender
// turn off sky blending
stateset->addUniform(new osg::Uniform("far", 10000000.0f));
stateset->addUniform(new osg::Uniform("skyBlendingStart", 8000000.0f));
stateset->addUniform(new osg::Uniform("sky", 0));
stateset->addUniform(new osg::Uniform("screenRes", osg::Vec2f{ 1, 1 }));
stateset->addUniform(new osg::Uniform("emissiveMult", 1.f));

View file

@ -259,8 +259,7 @@ namespace MWRender
void CreatureWeaponAnimation::addControllers()
{
Animation::addControllers();
if (mObjectRoot)
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
WeaponAnimation::addControllers(mNodeMap, mActiveControllers, mObjectRoot.get());
}
osg::Vec3f CreatureWeaponAnimation::runAnimation(float duration)

View file

@ -736,6 +736,7 @@ namespace MWRender
// turn of sky blending
stateset->addUniform(new osg::Uniform("far", 10000000.0f));
stateset->addUniform(new osg::Uniform("skyBlendingStart", 8000000.0f));
stateset->addUniform(new osg::Uniform("sky", 0));
stateset->addUniform(new osg::Uniform("screenRes", osg::Vec2f{ 1, 1 }));
osg::ref_ptr<osg::LightModel> lightmodel = new osg::LightModel;

View file

@ -49,6 +49,11 @@ namespace MWRender
&& exts.glslLanguageVersion >= minimumGLVersionRequiredForCompute;
#endif
if (mUseCompute)
Log(Debug::Info) << "Initialized compute shader pipeline for water ripples";
else
Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples";
for (size_t i = 0; i < mState.size(); ++i)
{
osg::ref_ptr<osg::StateSet> stateset = new osg::StateSet;
@ -86,18 +91,6 @@ namespace MWRender
else
setupFragmentPipeline();
if (mProgramBlobber != nullptr)
{
static bool pipelineLogged = [&] {
if (mUseCompute)
Log(Debug::Info) << "Initialized compute shader pipeline for water ripples";
else
Log(Debug::Info) << "Initialized fallback fragment shader pipeline for water ripples";
return true;
}();
(void)pipelineLogged;
}
setCullCallback(new osg::NodeCallback);
setUpdateCallback(new osg::NodeCallback);
}
@ -109,36 +102,21 @@ namespace MWRender
Shader::ShaderManager::DefineMap defineMap = { { "rippleMapSize", std::to_string(sRTTSize) + ".0" } };
osg::ref_ptr<osg::Shader> vertex = shaderManager.getShader("fullscreen_tri.vert", {}, osg::Shader::VERTEX);
osg::ref_ptr<osg::Shader> blobber
= shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT);
osg::ref_ptr<osg::Shader> simulate
= shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT);
if (vertex == nullptr || blobber == nullptr || simulate == nullptr)
{
Log(Debug::Error) << "Failed to load shaders required for fragment shader ripple pipeline";
return;
}
mProgramBlobber = shaderManager.getProgram(vertex, std::move(blobber));
mProgramSimulation = shaderManager.getProgram(std::move(vertex), std::move(simulate));
mProgramBlobber = shaderManager.getProgram(
vertex, shaderManager.getShader("ripples_blobber.frag", defineMap, osg::Shader::FRAGMENT));
mProgramSimulation = shaderManager.getProgram(
std::move(vertex), shaderManager.getShader("ripples_simulate.frag", defineMap, osg::Shader::FRAGMENT));
}
void RipplesSurface::setupComputePipeline()
{
auto& shaderManager = mResourceSystem->getSceneManager()->getShaderManager();
osg::ref_ptr<osg::Shader> blobber
= shaderManager.getShader("core/ripples_blobber.comp", {}, osg::Shader::COMPUTE);
osg::ref_ptr<osg::Shader> simulate
= shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE);
if (blobber == nullptr || simulate == nullptr)
{
Log(Debug::Error) << "Failed to load shaders required for compute shader ripple pipeline";
return;
}
mProgramBlobber = shaderManager.getProgram(nullptr, std::move(blobber));
mProgramSimulation = shaderManager.getProgram(nullptr, std::move(simulate));
mProgramBlobber = shaderManager.getProgram(
nullptr, shaderManager.getShader("core/ripples_blobber.comp", {}, osg::Shader::COMPUTE));
mProgramSimulation = shaderManager.getProgram(
nullptr, shaderManager.getShader("core/ripples_simulate.comp", {}, osg::Shader::COMPUTE));
}
void RipplesSurface::updateState(const osg::FrameStamp& frameStamp, State& state)
@ -206,9 +184,6 @@ namespace MWRender
void RipplesSurface::drawImplementation(osg::RenderInfo& renderInfo) const
{
if (mProgramBlobber == nullptr || mProgramSimulation == nullptr)
return;
osg::State& state = *renderInfo.getState();
const std::size_t currentFrame = state.getFrameStamp()->getFrameNumber() % 2;
const State& frameState = mState[currentFrame];

View file

@ -262,15 +262,15 @@ namespace MWRender
, mPrecipitationAlpha(0.f)
, mDirtyParticlesEffect(false)
{
mSkyRootNode = new CameraRelativeTransform;
mSkyRootNode->setName("Sky Root");
osg::ref_ptr<CameraRelativeTransform> skyroot = new CameraRelativeTransform;
skyroot->setName("Sky Root");
// Assign empty program to specify we don't want shaders when we are rendering in FFP pipeline
if (!mSceneManager->getForceShaders())
mSkyRootNode->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(),
skyroot->getOrCreateStateSet()->setAttributeAndModes(new osg::Program(),
osg::StateAttribute::OVERRIDE | osg::StateAttribute::PROTECTED | osg::StateAttribute::ON);
mSceneManager->setUpNormalsRTForStateSet(mSkyRootNode->getOrCreateStateSet(), false);
SceneUtil::ShadowManager::instance().disableShadowsForStateSet(*mSkyRootNode->getOrCreateStateSet());
parentNode->addChild(mSkyRootNode);
mSceneManager->setUpNormalsRTForStateSet(skyroot->getOrCreateStateSet(), false);
SceneUtil::ShadowManager::instance().disableShadowsForStateSet(*skyroot->getOrCreateStateSet());
parentNode->addChild(skyroot);
mEarlyRenderBinRoot = new osg::Group;
// render before the world is rendered
@ -281,18 +281,19 @@ namespace MWRender
if (enableSkyRTT)
{
mSkyRTT = new SkyRTT(Settings::fog().mSkyRttResolution, mEarlyRenderBinRoot);
mSkyRootNode->addChild(mSkyRTT);
skyroot->addChild(mSkyRTT);
mRootNode = new osg::Group;
skyroot->addChild(mRootNode);
}
else
mRootNode = skyroot;
mSkyNode = new osg::Group;
mSkyNode->setNodeMask(Mask_Sky);
mSkyNode->addChild(mEarlyRenderBinRoot);
mSkyRootNode->addChild(mSkyNode);
mUnderwaterSwitch = new UnderwaterSwitchCallback(mSkyRootNode);
mRootNode->setNodeMask(Mask_Sky);
mRootNode->addChild(mEarlyRenderBinRoot);
mUnderwaterSwitch = new UnderwaterSwitchCallback(skyroot);
mPrecipitationOcclusion = Settings::shaders().mWeatherParticleOcclusion;
mPrecipitationOccluder = std::make_unique<PrecipitationOccluder>(mSkyRootNode, parentNode, rootNode, camera);
mPrecipitationOccluder = std::make_unique<PrecipitationOccluder>(skyroot, parentNode, rootNode, camera);
}
void SkyManager::create()
@ -463,7 +464,7 @@ namespace MWRender
mRainParticleSystem->setUserValue("particleOcclusion", true);
mSceneManager->recreateShaders(mRainNode);
mSkyNode->addChild(mRainNode);
mRootNode->addChild(mRainNode);
if (mPrecipitationOcclusion)
mPrecipitationOccluder->enable();
}
@ -473,7 +474,7 @@ namespace MWRender
if (!mRainNode)
return;
mSkyNode->removeChild(mRainNode);
mRootNode->removeChild(mRainNode);
mRainNode = nullptr;
mPlacer = nullptr;
mCounter = nullptr;
@ -484,10 +485,10 @@ namespace MWRender
SkyManager::~SkyManager()
{
if (mSkyRootNode)
if (mRootNode)
{
mSkyRootNode->getParent(0)->removeChild(mSkyRootNode);
mSkyRootNode = nullptr;
mRootNode->getParent(0)->removeChild(mRootNode);
mRootNode = nullptr;
}
}
@ -594,7 +595,7 @@ namespace MWRender
const osg::Node::NodeMask mask = enabled ? Mask_Sky : 0u;
mEarlyRenderBinRoot->setNodeMask(mask);
mSkyNode->setNodeMask(mask);
mRootNode->setNodeMask(mask);
if (!enabled && mParticleNode && mParticleEffect)
{
@ -690,7 +691,7 @@ namespace MWRender
{
if (mParticleNode)
{
mSkyNode->removeChild(mParticleNode);
mRootNode->removeChild(mParticleNode);
mParticleNode = nullptr;
}
if (mRainEffect.empty())
@ -705,7 +706,7 @@ namespace MWRender
mParticleNode = new osg::PositionAttitudeTransform;
mParticleNode->addCullCallback(mUnderwaterSwitch);
mParticleNode->setNodeMask(Mask_WeatherParticles);
mSkyNode->addChild(mParticleNode);
mRootNode->addChild(mParticleNode);
}
mParticleEffect = mSceneManager->getInstance(mCurrentParticleEffect, mParticleNode);

View file

@ -118,8 +118,7 @@ namespace MWRender
osg::Camera* mCamera;
osg::ref_ptr<CameraRelativeTransform> mSkyRootNode;
osg::ref_ptr<osg::Group> mSkyNode;
osg::ref_ptr<osg::Group> mRootNode;
osg::ref_ptr<osg::Group> mEarlyRenderBinRoot;
osg::ref_ptr<osg::PositionAttitudeTransform> mParticleNode;

View file

@ -162,17 +162,11 @@ namespace MWScript
public:
void execute(Interpreter::Runtime& runtime) override
{
MWWorld::Ptr ptr = R()(runtime, false);
MWWorld::Ptr ptr = R()(runtime);
int index = runtime[0].mInteger;
runtime.pop();
if (ptr.isEmpty())
{
runtime.push(0);
return;
}
bool ret = MWBase::Environment::get().getSoundManager()->getSoundPlaying(
ptr, ESM::RefId::stringRefId(runtime.getStringLiteral(index)));

View file

@ -38,7 +38,7 @@ MWState::Character* MWState::CharacterManager::getCurrentCharacter()
return mCurrent;
}
void MWState::CharacterManager::deleteSlot(const MWState::Slot* slot, const MWState::Character*& character)
void MWState::CharacterManager::deleteSlot(const MWState::Character* character, const MWState::Slot* slot)
{
std::list<Character>::iterator it = findCharacter(character);
@ -51,7 +51,6 @@ void MWState::CharacterManager::deleteSlot(const MWState::Slot* slot, const MWSt
if (character == mCurrent)
mCurrent = nullptr;
mCharacters.erase(it);
character = nullptr;
}
}

View file

@ -33,7 +33,7 @@ namespace MWState
Character* getCurrentCharacter();
///< @note May return null
void deleteSlot(const MWState::Slot* slot, const Character*& character);
void deleteSlot(const MWState::Character* character, const MWState::Slot* slot);
Character* createCharacter(const std::string& name);
///< Create new character within saved game management

View file

@ -706,10 +706,10 @@ void MWState::StateManager::quickLoad()
void MWState::StateManager::deleteGame(const MWState::Character* character, const MWState::Slot* slot)
{
const std::filesystem::path savePath = slot->mPath;
mCharacterManager.deleteSlot(slot, character);
mCharacterManager.deleteSlot(character, slot);
if (mLastSavegame == savePath)
{
if (character != nullptr)
if (character->begin() != character->end())
mLastSavegame = character->begin()->mPath;
else
mLastSavegame.clear();
@ -757,14 +757,12 @@ void MWState::StateManager::update(float duration)
if (mNewGameRequest)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_MainMenu);
newGame();
mNewGameRequest = false;
}
if (mLoadRequest)
{
MWBase::Environment::get().getWindowManager()->removeGuiMode(MWGui::GM_MainMenu);
loadGame(*mLoadRequest);
mLoadRequest = std::nullopt;
}

View file

@ -337,13 +337,13 @@ namespace
// helper function for forEachInternal
template <class Visitor, class List>
bool forEachImp(Visitor& visitor, List& list, MWWorld::CellStore& cellStore, bool includeDeleted)
bool forEachImp(Visitor& visitor, List& list, MWWorld::CellStore* cellStore)
{
for (auto& v : list.mList)
for (typename List::List::iterator iter(list.mList.begin()); iter != list.mList.end(); ++iter)
{
if (!includeDeleted && !MWWorld::CellStore::isAccessible(v.mData, v.mRef))
if (!MWWorld::CellStore::isAccessible(iter->mData, iter->mRef))
continue;
if (!visitor(MWWorld::Ptr(&v, &cellStore)))
if (!visitor(MWWorld::Ptr(&*iter, cellStore)))
return false;
}
return true;
@ -399,12 +399,12 @@ namespace MWWorld
// listing only objects owned by this cell. Internal use only, you probably want to use forEach() so that moved
// objects are accounted for.
template <class Visitor>
static bool forEachInternal(Visitor& visitor, MWWorld::CellStore& cellStore, bool includeDeleted)
static bool forEachInternal(Visitor& visitor, MWWorld::CellStore& cellStore)
{
bool returnValue = true;
Misc::tupleForEach(cellStore.mCellStoreImp->mRefLists, [&](auto& store) {
returnValue = returnValue && forEachImp(visitor, store, cellStore, includeDeleted);
Misc::tupleForEach(cellStore.mCellStoreImp->mRefLists, [&visitor, &returnValue, &cellStore](auto& store) {
returnValue = returnValue && forEachImp(visitor, store, &cellStore);
});
return returnValue;
@ -583,11 +583,11 @@ namespace MWWorld
mMergedRefsNeedsUpdate = true;
}
void CellStore::updateMergedRefs(bool includeDeleted) const
void CellStore::updateMergedRefs() const
{
mMergedRefs.clear();
MergeVisitor visitor(mMergedRefs, mMovedHere, mMovedToAnotherCell);
CellStoreImp::forEachInternal(visitor, const_cast<CellStore&>(*this), includeDeleted);
CellStoreImp::forEachInternal(visitor, const_cast<CellStore&>(*this));
visitor.merge();
mMergedRefsNeedsUpdate = false;
}

View file

@ -213,13 +213,13 @@ namespace MWWorld
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
/// \return Iteration completed?
template <class Visitor>
bool forEach(Visitor&& visitor, bool includeDeleted = false)
bool forEach(Visitor&& visitor)
{
if (mState != State_Loaded)
return false;
if (mMergedRefsNeedsUpdate)
updateMergedRefs(includeDeleted);
updateMergedRefs();
if (mMergedRefs.empty())
return true;
@ -227,7 +227,7 @@ namespace MWWorld
for (LiveCellRefBase* mergedRef : mMergedRefs)
{
if (!includeDeleted && !isAccessible(mergedRef->mData, mergedRef->mRef))
if (!isAccessible(mergedRef->mData, mergedRef->mRef))
continue;
if (!visitor(MWWorld::Ptr(mergedRef, this)))
@ -242,17 +242,17 @@ namespace MWWorld
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
/// \return Iteration completed?
template <class Visitor>
bool forEachConst(Visitor&& visitor, bool includeDeleted = false) const
bool forEachConst(Visitor&& visitor) const
{
if (mState != State_Loaded)
return false;
if (mMergedRefsNeedsUpdate)
updateMergedRefs(includeDeleted);
updateMergedRefs();
for (const LiveCellRefBase* mergedRef : mMergedRefs)
{
if (!includeDeleted && !isAccessible(mergedRef->mData, mergedRef->mRef))
if (!isAccessible(mergedRef->mData, mergedRef->mRef))
continue;
if (!visitor(MWWorld::ConstPtr(mergedRef, this)))
@ -267,25 +267,28 @@ namespace MWWorld
/// unintended behaviour. \attention This function also lists deleted (count 0) objects!
/// \return Iteration completed?
template <class T, class Visitor>
bool forEachType(Visitor&& visitor, bool includeDeleted = false)
bool forEachType(Visitor&& visitor)
{
if (mState != State_Loaded)
return false;
if (mMergedRefsNeedsUpdate)
updateMergedRefs(includeDeleted);
updateMergedRefs();
if (mMergedRefs.empty())
return true;
mHasState = true;
for (LiveCellRefBase& base : get<T>().mList)
CellRefList<T>& list = get<T>();
for (typename CellRefList<T>::List::iterator it(list.mList.begin()); it != list.mList.end(); ++it)
{
if (mMovedToAnotherCell.contains(&base))
LiveCellRefBase* base = &*it;
if (mMovedToAnotherCell.find(base) != mMovedToAnotherCell.end())
continue;
if (!includeDeleted && !isAccessible(base.mData, base.mRef))
if (!isAccessible(base->mData, base->mRef))
continue;
if (!visitor(MWWorld::Ptr(&base, this)))
if (!visitor(MWWorld::Ptr(base, this)))
return false;
}
@ -403,7 +406,7 @@ namespace MWWorld
/// Repopulate mMergedRefs.
void requestMergedRefsUpdate();
void updateMergedRefs(bool includeDeleted = false) const;
void updateMergedRefs() const;
// (item, max charge)
typedef std::vector<std::pair<LiveCellRefBase*, float>> TRechargingItems;

View file

@ -406,7 +406,8 @@ namespace MWWorld
template <class T>
ContainerStoreIteratorBase(const ContainerStoreIteratorBase<T>& other)
{
static_assert(IsConvertible<T, PtrType, void>::value);
char CANNOT_CONVERT_CONST_ITERATOR_TO_ITERATOR[IsConvertible<T, PtrType, void>::value ? 1 : -1];
((void)CANNOT_CONVERT_CONST_ITERATOR_TO_ITERATOR);
copy(other);
}

View file

@ -70,7 +70,7 @@ namespace MWWorld
mEncoder != nullptr ? &mEncoder->getStatelessEncoder() : nullptr);
reader.setModIndex(index);
reader.updateModIndices(mNameToIndex);
mStore.loadESM4(reader, listener);
mStore.loadESM4(reader);
break;
}
}

View file

@ -468,16 +468,9 @@ namespace MWWorld
}
}
void ESMStore::loadESM4(ESM4::Reader& reader, Loading::Listener* listener)
void ESMStore::loadESM4(ESM4::Reader& reader)
{
if (listener != nullptr)
listener->setProgressRange(::EsmLoader::fileProgress);
auto visitorRec = [this, listener](ESM4::Reader& reader) {
bool result = ESMStoreImp::readRecord(reader, *this);
if (listener != nullptr)
listener->setProgress(::EsmLoader::fileProgress * reader.getFileOffset() / reader.getFileSize());
return result;
};
auto visitorRec = [this](ESM4::Reader& reader) { return ESMStoreImp::readRecord(reader, *this); };
ESM4::ReaderUtils::readAll(reader, visitorRec, [](ESM4::Reader&) {});
}

View file

@ -219,7 +219,7 @@ namespace MWWorld
void validateDynamic();
void load(ESM::ESMReader& esm, Loading::Listener* listener, ESM::Dialogue*& dialogue);
void loadESM4(ESM4::Reader& esm, Loading::Listener* listener);
void loadESM4(ESM4::Reader& esm);
template <class T>
const Store<T>& get() const

View file

@ -367,7 +367,7 @@ namespace MWWorld
ListAndResetObjectsVisitor visitor;
cell->forEach(visitor, true); // Include objects being teleported by Lua
cell->forEach(visitor);
for (const auto& ptr : visitor.mObjects)
{
if (const auto object = mPhysics->getObject(ptr))

View file

@ -290,14 +290,14 @@ namespace MWWorld
mSwimHeightScale = mStore.get<ESM::GameSetting>().find("fSwimHeightScale")->mValue.getFloat();
}
void World::init(Debug::Level maxRecastLogLevel, osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
SceneUtil::WorkQueue* workQueue, SceneUtil::UnrefQueue& unrefQueue)
void World::init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue,
SceneUtil::UnrefQueue& unrefQueue)
{
mPhysics = std::make_unique<MWPhysics::PhysicsSystem>(mResourceSystem, rootNode);
if (Settings::navigator().mEnable)
{
auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager(maxRecastLogLevel);
auto navigatorSettings = DetourNavigator::makeSettingsFromSettingsManager();
navigatorSettings.mRecast.mSwimHeightScale = mSwimHeightScale;
mNavigator = DetourNavigator::makeNavigator(navigatorSettings, mUserDataPath);
}

View file

@ -4,7 +4,6 @@
#include <osg/Timer>
#include <osg/ref_ptr>
#include <components/debug/debuglog.hpp>
#include <components/esm3/readerscache.hpp>
#include <components/misc/rng.hpp>
#include <components/settings/settings.hpp>
@ -202,8 +201,8 @@ namespace MWWorld
Loading::Listener* listener);
// Must be called after `loadData`.
void init(Debug::Level maxRecastLogLevel, osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode,
SceneUtil::WorkQueue* workQueue, SceneUtil::UnrefQueue& unrefQueue);
void init(osgViewer::Viewer* viewer, osg::ref_ptr<osg::Group> rootNode, SceneUtil::WorkQueue* workQueue,
SceneUtil::UnrefQueue& unrefQueue);
virtual ~World();

View file

@ -2,7 +2,6 @@
#include <components/misc/strings/conversion.hpp>
#include <components/settings/parser.hpp>
#include <components/settings/values.hpp>
#include <components/testing/util.hpp>
#include <gtest/gtest.h>
@ -25,9 +24,5 @@ int main(int argc, char* argv[])
Settings::StaticValues::init();
testing::InitGoogleTest(&argc, argv);
const int result = RUN_ALL_TESTS();
if (result == 0)
std::filesystem::remove_all(TestingOpenMW::outputDir());
return result;
return RUN_ALL_TESTS();
}

View file

@ -59,7 +59,7 @@ list(APPEND COMPONENT_FILES "${OpenMW_BINARY_DIR}/${OSG_PLUGIN_CHECKER_CPP_FILE}
add_component_dir (lua
luastate scriptscontainer asyncpackage utilpackage serialization configuration l10n storage utf8
shapes/box inputactions yamlloader scripttracker luastateptr
shapes/box inputactions yamlloader scripttracker
)
add_component_dir (l10n

View file

@ -1,7 +1,6 @@
#include "gamesettings.hpp"
#include <QDir>
#include <QProgressDialog>
#include <QRegularExpression>
#include <components/files/configurationmanager.hpp>
@ -38,13 +37,8 @@ void Config::GameSettings::validatePaths()
mDataDirs.clear();
QProgressDialog progressBar("Validating paths", {}, 0, paths.count() + 1);
progressBar.setWindowModality(Qt::WindowModal);
progressBar.setValue(0);
for (const auto& dataDir : paths)
{
progressBar.setValue(progressBar.value() + 1);
if (QDir(dataDir.value).exists())
{
SettingValue copy = dataDir;
@ -56,8 +50,6 @@ void Config::GameSettings::validatePaths()
// Do the same for data-local
const QString& local = mSettings.value(QString("data-local")).value;
progressBar.setValue(progressBar.value() + 1);
if (!local.isEmpty() && QDir(local).exists())
{
mDataLocal = QDir(local).canonicalPath();

View file

@ -10,7 +10,6 @@
#include <QDataStream>
#include <QDebug>
#include <QDir>
#include <QDirIterator>
#include <QFont>
#include <QIODevice>
@ -79,10 +78,14 @@ ContentSelectorModel::EsmFile* ContentSelectorModel::ContentModel::item(int row)
}
const ContentSelectorModel::EsmFile* ContentSelectorModel::ContentModel::item(const QString& name) const
{
bool path = name.contains('/');
EsmFile::FileProperty fp = EsmFile::FileProperty_FileName;
if (name.contains('/'))
fp = EsmFile::FileProperty_FilePath;
for (const EsmFile* file : mFiles)
{
if (name.compare(path ? file->filePath() : file->fileName(), Qt::CaseInsensitive) == 0)
if (name.compare(file->fileProperty(fp).toString(), Qt::CaseInsensitive) == 0)
return file;
}
return nullptr;
@ -307,6 +310,7 @@ bool ContentSelectorModel::ContentModel::setData(const QModelIndex& index, const
{
setCheckState(file->filePath(), success);
emit dataChanged(index, index);
checkForLoadOrderErrors();
}
else
return success;
@ -421,6 +425,7 @@ bool ContentSelectorModel::ContentModel::dropMimeData(
dataChanged(index(minRow, 0), index(maxRow, 0));
// at this point we know that drag and drop has finished.
checkForLoadOrderErrors();
return true;
}
@ -547,13 +552,15 @@ void ContentSelectorModel::ContentModel::addFiles(const QString& path, bool newf
bool ContentSelectorModel::ContentModel::containsDataFiles(const QString& path)
{
QDir dir(path);
QStringList filters;
filters << "*.esp"
<< "*.esm"
<< "*.omwgame"
<< "*.omwaddon";
QDirIterator it(path, filters, QDir::Files | QDir::NoDotAndDotDot);
return it.hasNext();
dir.setNameFilters(filters);
return dir.entryList().count() != 0;
}
void ContentSelectorModel::ContentModel::clearFiles()
@ -700,13 +707,12 @@ void ContentSelectorModel::ContentModel::setNonUserContent(const QStringList& fi
bool ContentSelectorModel::ContentModel::isLoadOrderError(const EsmFile* file) const
{
int index = indexFromItem(file).row();
auto errors = checkForLoadOrderErrors(file, index);
return !errors.empty();
return mPluginsWithLoadOrderError.contains(file->filePath());
}
void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileList)
{
mPluginsWithLoadOrderError.clear();
int previousPosition = -1;
for (const QString& filepath : fileList)
{
@ -719,6 +725,7 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL
if (filePosition < previousPosition)
{
mFiles.move(filePosition, previousPosition);
emit dataChanged(index(filePosition, 0, QModelIndex()), index(previousPosition, 0, QModelIndex()));
}
else
{
@ -726,7 +733,24 @@ void ContentSelectorModel::ContentModel::setContentList(const QStringList& fileL
}
}
}
emit dataChanged(index(0, 0), index(rowCount(), columnCount()));
checkForLoadOrderErrors();
}
void ContentSelectorModel::ContentModel::checkForLoadOrderErrors()
{
for (int row = 0; row < mFiles.count(); ++row)
{
EsmFile* file = mFiles.at(row);
bool isRowInError = checkForLoadOrderErrors(file, row).count() != 0;
if (isRowInError)
{
mPluginsWithLoadOrderError.insert(file->filePath());
}
else
{
mPluginsWithLoadOrderError.remove(file->filePath());
}
}
}
QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::checkForLoadOrderErrors(
@ -767,12 +791,11 @@ QList<ContentSelectorModel::LoadOrderError> ContentSelectorModel::ContentModel::
QString ContentSelectorModel::ContentModel::toolTip(const EsmFile* file) const
{
int index = indexFromItem(file).row();
auto errors = checkForLoadOrderErrors(file, index);
if (!errors.empty())
if (isLoadOrderError(file))
{
QString text("<b>");
for (const LoadOrderError& error : errors)
int index = indexFromItem(item(file->filePath())).row();
for (const LoadOrderError& error : checkForLoadOrderErrors(file, index))
{
assert(error.errorCode() != LoadOrderError::ErrorCode::ErrorCode_None);
@ -877,6 +900,7 @@ ContentSelectorModel::ContentFileList ContentSelectorModel::ContentModel::checke
void ContentSelectorModel::ContentModel::uncheckAll()
{
emit layoutAboutToBeChanged();
mCheckedFiles.clear();
emit dataChanged(index(0, 0), index(rowCount(), columnCount()), { Qt::CheckStateRole, Qt::UserRole + 1 });
emit layoutChanged();
}

View file

@ -69,6 +69,9 @@ namespace ContentSelectorModel
void refreshModel();
/// Checks all plug-ins for load order errors and updates mPluginsWithLoadOrderError with plug-ins with issues
void checkForLoadOrderErrors();
private:
void addFile(EsmFile* file);
@ -86,6 +89,7 @@ namespace ContentSelectorModel
QStringList mNonUserContent;
std::set<const EsmFile*> mCheckedFiles;
QHash<QString, bool> mNewFiles;
QSet<QString> mPluginsWithLoadOrderError;
QString mEncoding;
QIcon mWarningIcon;
QIcon mErrorIcon;

View file

@ -48,18 +48,18 @@ namespace ContentSelectorModel
void addGameFile(const QString& name) { mGameFiles.append(name); }
QVariant fileProperty(const FileProperty prop) const;
const QString& fileName() const { return mFileName; }
const QString& author() const { return mAuthor; }
QString fileName() const { return mFileName; }
QString author() const { return mAuthor; }
QDateTime modified() const { return mModified; }
const QString& formatVersion() const { return mVersion; }
const QString& filePath() const { return mPath; }
QString formatVersion() const { return mVersion; }
QString filePath() const { return mPath; }
bool builtIn() const { return mBuiltIn; }
bool fromAnotherConfigFile() const { return mFromAnotherConfigFile; }
bool isMissing() const { return mPath.isEmpty(); }
/// @note Contains file names, not paths.
const QStringList& gameFiles() const { return mGameFiles; }
const QString& description() const { return mDescription; }
QString description() const { return mDescription; }
QString toolTip() const
{
if (isMissing())

View file

@ -211,6 +211,7 @@ void ContentSelectorView::ContentSelector::addFiles(const QString& path, bool ne
ui->gameFileView->setCurrentIndex(0);
mContentModel->uncheckAll();
mContentModel->checkForLoadOrderErrors();
}
void ContentSelectorView::ContentSelector::sortFiles()
@ -253,6 +254,7 @@ void ContentSelectorView::ContentSelector::slotCurrentGameFileIndexChanged(int i
oldIndex = index;
setGameFileSelected(index, true);
mContentModel->checkForLoadOrderErrors();
}
emit signalCurrentGamefileIndexChanged(index);

View file

@ -237,7 +237,7 @@ namespace Crash
// must remain until monitor has finished
waitMonitor();
std::string message = "OpenMW has encountered a fatal error.\nCrash dump saved to '"
std::string message = "OpenMW has encountered a fatal error.\nCrash log saved to '"
+ Misc::StringUtils::u8StringToString(getCrashDumpPath(*mShm).u8string())
+ "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);

View file

@ -21,7 +21,6 @@ namespace Crash
// the main openmw process in task manager.
static constexpr const int CrashCatcherTimeout = 2500;
static constexpr const int CrashCatcherThawTimeout = 250;
struct CrashSHM;

View file

@ -87,10 +87,9 @@ namespace Crash
SetEvent(mSignalAppEvent);
}
bool CrashMonitor::waitApp(bool thawMode) const
bool CrashMonitor::waitApp() const
{
return WaitForSingleObject(mSignalMonitorEvent, thawMode ? CrashCatcherThawTimeout : CrashCatcherTimeout)
== WAIT_OBJECT_0;
return WaitForSingleObject(mSignalMonitorEvent, CrashCatcherTimeout) == WAIT_OBJECT_0;
}
bool CrashMonitor::isAppAlive() const
@ -186,7 +185,7 @@ namespace Crash
frozen = false;
}
if (!mFreezeAbort && waitApp(frozen))
if (!mFreezeAbort && waitApp())
{
shmLock();
@ -216,7 +215,7 @@ namespace Crash
{
handleCrash(true);
TerminateProcess(mAppProcessHandle, 0xDEAD);
std::string message = "OpenMW has frozen.\nCrash dump saved to '"
std::string message = "OpenMW appears to have frozen.\nCrash log saved to '"
+ Misc::StringUtils::u8StringToString(getFreezeDumpPath(*mShm).u8string())
+ "'.\nPlease report this to https://gitlab.com/OpenMW/openmw/issues !";
SDL_ShowSimpleMessageBox(0, "Fatal Error", message.c_str(), nullptr);
@ -290,10 +289,10 @@ namespace Crash
{
std::thread messageBoxThread([&]() {
SDL_MessageBoxButtonData button = { SDL_MESSAGEBOX_BUTTON_RETURNKEY_DEFAULT, 0, "Abort" };
SDL_MessageBoxData messageBoxData = { SDL_MESSAGEBOX_ERROR, nullptr, "OpenMW has frozen",
"OpenMW has frozen. This should never happen. Press Abort to terminate it and generate a crash dump to "
"help diagnose the problem.\nOpenMW may unfreeze if you wait, and this message box will disappear "
"after it becomes responsive.",
SDL_MessageBoxData messageBoxData = { SDL_MESSAGEBOX_ERROR, nullptr, "OpenMW appears to have frozen",
"OpenMW appears to have frozen. Press Abort to terminate it and generate a crash dump.\nIf OpenMW "
"hasn't actually frozen, this message box will disappear a within a few seconds of it becoming "
"responsive.",
1, &button, nullptr };
int buttonId;

View file

@ -41,7 +41,7 @@ namespace Crash
void signalApp() const;
bool waitApp(bool thawMode) const;
bool waitApp() const;
bool isAppAlive() const;

View file

@ -106,96 +106,94 @@ namespace Debug
logListener = std::move(listener);
}
namespace
class DebugOutputBase : public boost::iostreams::sink
{
class DebugOutputBase : public boost::iostreams::sink
public:
virtual std::streamsize write(const char* str, std::streamsize size)
{
public:
virtual std::streamsize write(const char* str, std::streamsize size)
if (size <= 0)
return size;
std::string_view msg{ str, static_cast<size_t>(size) };
// Skip debug level marker
Level level = All;
if (Log::sWriteLevel)
{
if (size <= 0)
return size;
std::string_view msg{ str, static_cast<size_t>(size) };
level = getLevelMarker(msg[0]);
msg = msg.substr(1);
}
// Skip debug level marker
Level level = All;
if (Log::sWriteLevel)
{
level = getLevelMarker(msg[0]);
msg = msg.substr(1);
}
char prefix[32];
std::size_t prefixSize;
{
prefix[0] = '[';
const auto now = std::chrono::system_clock::now();
const auto time = std::chrono::system_clock::to_time_t(now);
tm time_info{};
char prefix[32];
std::size_t prefixSize;
{
prefix[0] = '[';
const auto now = std::chrono::system_clock::now();
const auto time = std::chrono::system_clock::to_time_t(now);
tm time_info{};
#ifdef _WIN32
(void)localtime_s(&time_info, &time);
(void)localtime_s(&time_info, &time);
#else
(void)localtime_r(&time, &time_info);
(void)localtime_r(&time, &time_info);
#endif
prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, "%T", &time_info) + 1;
char levelLetter = " EWIVD*"[int(level)];
const auto ms
= std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize, ".%03u %c] ",
static_cast<unsigned>(ms % 1000), levelLetter);
}
while (!msg.empty())
{
if (msg[0] == 0)
break;
size_t lineSize = 1;
while (lineSize < msg.size() && msg[lineSize - 1] != '\n')
lineSize++;
writeImpl(prefix, prefixSize, level);
writeImpl(msg.data(), lineSize, level);
if (logListener)
logListener(
level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize));
msg = msg.substr(lineSize);
}
return size;
prefixSize = std::strftime(prefix + 1, sizeof(prefix) - 1, "%T", &time_info) + 1;
char levelLetter = " EWIVD*"[int(level)];
const auto ms = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch()).count();
prefixSize += snprintf(prefix + prefixSize, sizeof(prefix) - prefixSize, ".%03u %c] ",
static_cast<unsigned>(ms % 1000), levelLetter);
}
virtual ~DebugOutputBase() = default;
protected:
static Level getLevelMarker(char marker)
while (!msg.empty())
{
if (0 <= marker && static_cast<unsigned>(marker) < static_cast<unsigned>(All))
return static_cast<Level>(marker);
return All;
if (msg[0] == 0)
break;
size_t lineSize = 1;
while (lineSize < msg.size() && msg[lineSize - 1] != '\n')
lineSize++;
writeImpl(prefix, prefixSize, level);
writeImpl(msg.data(), lineSize, level);
if (logListener)
logListener(level, std::string_view(prefix, prefixSize), std::string_view(msg.data(), lineSize));
msg = msg.substr(lineSize);
}
virtual std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel)
{
return size;
}
};
return size;
}
virtual ~DebugOutputBase() = default;
protected:
static Level getLevelMarker(char marker)
{
if (0 <= marker && static_cast<unsigned>(marker) < static_cast<unsigned>(All))
return static_cast<Level>(marker);
return All;
}
virtual std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel)
{
return size;
}
};
#if defined _WIN32 && defined _DEBUG
class DebugOutput : public DebugOutputBase
class DebugOutput : public DebugOutputBase
{
public:
std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel)
{
public:
std::streamsize writeImpl(const char* str, std::streamsize size, Level debugLevel)
{
// Make a copy for null termination
std::string tmp(str, static_cast<unsigned int>(size));
// Write string to Visual Studio Debug output
OutputDebugString(tmp.c_str());
return size;
}
// Make a copy for null termination
std::string tmp(str, static_cast<unsigned int>(size));
// Write string to Visual Studio Debug output
OutputDebugString(tmp.c_str());
return size;
}
virtual ~DebugOutput() = default;
};
virtual ~DebugOutput() = default;
};
#else
namespace
{
struct Record
{
std::string mValue;
@ -326,38 +324,22 @@ namespace Debug
First mFirst;
Second mSecond;
};
}
#endif
Level toLevel(std::string_view value)
{
if (value == "ERROR")
return Error;
if (value == "WARNING")
return Warning;
if (value == "INFO")
return Info;
if (value == "VERBOSE")
return Verbose;
if (value == "DEBUG")
return Debug;
return Verbose;
}
static std::unique_ptr<std::ostream> rawStdout = nullptr;
static std::unique_ptr<std::ostream> rawStderr = nullptr;
static std::unique_ptr<std::mutex> rawStderrMutex = nullptr;
static std::ofstream logfile;
static std::unique_ptr<std::ostream> rawStdout = nullptr;
static std::unique_ptr<std::ostream> rawStderr = nullptr;
static std::unique_ptr<std::mutex> rawStderrMutex = nullptr;
static std::ofstream logfile;
#if defined(_WIN32) && defined(_DEBUG)
static boost::iostreams::stream_buffer<DebugOutput> sb;
static boost::iostreams::stream_buffer<DebugOutput> sb;
#else
static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardOut;
static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardErr;
static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedOut;
static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedErr;
static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardOut;
static boost::iostreams::stream_buffer<Tee<Identity, Coloured>> standardErr;
static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedOut;
static boost::iostreams::stream_buffer<Tee<Buffer, Coloured>> bufferedErr;
#endif
}
std::ostream& getRawStdout()
{
@ -377,19 +359,23 @@ namespace Debug
Level getDebugLevel()
{
if (const char* env = getenv("OPENMW_DEBUG_LEVEL"))
return toLevel(env);
{
const std::string_view value(env);
if (value == "ERROR")
return Error;
if (value == "WARNING")
return Warning;
if (value == "INFO")
return Info;
if (value == "VERBOSE")
return Verbose;
if (value == "DEBUG")
return Debug;
}
return Verbose;
}
Level getRecastMaxLogLevel()
{
if (const char* env = getenv("OPENMW_RECAST_MAX_LOG_LEVEL"))
return toLevel(env);
return Error;
}
void setupLogging(const std::filesystem::path& logDir, std::string_view appName)
{
Log::sMinDebugLevel = getDebugLevel();

View file

@ -36,8 +36,6 @@ namespace Debug
Level getDebugLevel();
Level getRecastMaxLogLevel();
// Redirect cout and cerr to the log file
void setupLogging(const std::filesystem::path& logDir, std::string_view appName);

View file

@ -453,9 +453,9 @@ namespace DetourNavigator
Misc::setCurrentThreadIdlePriority();
while (!mShouldStop)
{
if (JobIt job = getNextJob(); job != mJobs.end())
try
{
try
if (JobIt job = getNextJob(); job != mJobs.end())
{
const JobStatus status = processJob(*job);
Log(Debug::Debug) << "Processed job " << job->mId << " with status=" << status
@ -480,20 +480,12 @@ namespace DetourNavigator
}
}
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Failed to process navmesh job " << job->mId
<< " for worldspace=" << job->mWorldspace << " agent=" << job->mAgentBounds
<< " changedTile=(" << job->mChangedTile << ")"
<< " changeType=" << job->mChangeType
<< " by thread=" << std::this_thread::get_id() << ": " << e.what();
unlockTile(job->mId, job->mAgentBounds, job->mChangedTile);
removeJob(job);
}
else
cleanupLastUpdates();
}
else
catch (const std::exception& e)
{
cleanupLastUpdates();
Log(Debug::Error) << "AsyncNavMeshUpdater::process exception: " << e.what();
}
}
Log(Debug::Debug) << "Stop navigator jobs processing by thread=" << std::this_thread::get_id();
@ -501,8 +493,7 @@ namespace DetourNavigator
JobStatus AsyncNavMeshUpdater::processJob(Job& job)
{
Log(Debug::Debug) << "Processing job " << job.mId << " for worldspace=" << job.mWorldspace
<< " agent=" << job.mAgentBounds << ""
Log(Debug::Debug) << "Processing job " << job.mId << " for agent=(" << job.mAgentBounds << ")"
<< " changedTile=(" << job.mChangedTile << ")"
<< " changeType=" << job.mChangeType << " by thread=" << std::this_thread::get_id();
@ -552,14 +543,7 @@ namespace DetourNavigator
return JobStatus::Done;
}
try
{
writeDebugRecastMesh(mSettings, job.mChangedTile, *recastMesh);
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Failed to write debug recast mesh: " << e.what();
}
writeDebugRecastMesh(mSettings, job.mChangedTile, *recastMesh);
NavMeshTilesCache::Value cachedNavMeshData
= mNavMeshTilesCache.get(job.mAgentBounds, job.mChangedTile, *recastMesh);
@ -682,19 +666,12 @@ namespace DetourNavigator
mPresentTiles.insert(std::make_tuple(job.mAgentBounds, job.mChangedTile));
}
try
{
writeDebugNavMesh(mSettings, navMeshCacheItem, navMeshVersion);
}
catch (const std::exception& e)
{
Log(Debug::Warning) << "Failed to write debug navmesh: " << e.what();
}
writeDebugNavMesh(mSettings, navMeshCacheItem, navMeshVersion);
return isSuccess(status) ? JobStatus::Done : JobStatus::Fail;
}
JobIt AsyncNavMeshUpdater::getNextJob() noexcept
JobIt AsyncNavMeshUpdater::getNextJob()
{
std::unique_lock<std::mutex> lock(mMutex);
@ -769,7 +746,7 @@ namespace DetourNavigator
return mJobs.size();
}
void AsyncNavMeshUpdater::cleanupLastUpdates() noexcept
void AsyncNavMeshUpdater::cleanupLastUpdates()
{
const auto now = std::chrono::steady_clock::now();

View file

@ -244,7 +244,7 @@ namespace DetourNavigator
inline JobStatus handleUpdateNavMeshStatus(UpdateNavMeshStatus status, const Job& job,
const GuardedNavMeshCacheItem& navMeshCacheItem, const RecastMesh& recastMesh);
inline JobIt getNextJob() noexcept;
JobIt getNextJob();
void postThreadJob(JobIt job, std::deque<JobIt>& queue);
@ -254,7 +254,7 @@ namespace DetourNavigator
inline std::size_t getTotalJobs() const;
inline void cleanupLastUpdates() noexcept;
void cleanupLastUpdates();
inline void waitUntilJobsDoneForNotPresentTiles(Loading::Listener* listener);

View file

@ -523,7 +523,7 @@ namespace DetourNavigator
std::unique_ptr<PreparedNavMeshData> prepareNavMeshTileData(const RecastMesh& recastMesh, ESM::RefId worldspace,
const TilePosition& tilePosition, const AgentBounds& agentBounds, const RecastSettings& settings)
{
RecastContext context(worldspace, tilePosition, agentBounds, recastMesh.getVersion(), settings.mMaxLogLevel);
RecastContext context(worldspace, tilePosition, agentBounds);
const auto [minZ, maxZ] = getBoundsByZ(recastMesh, agentBounds.mHalfExtents.z(), settings);

View file

@ -1,7 +1,7 @@
#include "recastcontext.hpp"
#include "debug.hpp"
#include <components/debug/debuglog.hpp>
#include "components/debug/debuglog.hpp"
#include <sstream>
@ -23,30 +23,25 @@ namespace DetourNavigator
return Debug::Debug;
}
std::string formatPrefix(ESM::RefId worldspace, const TilePosition& tilePosition,
const AgentBounds& agentBounds, const Version& version)
std::string formatPrefix(
ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds)
{
std::ostringstream stream;
stream << "Worldspace: " << worldspace << "; tile position: " << tilePosition.x() << ", "
<< tilePosition.y() << "; agent bounds: " << agentBounds << "; version: " << version << "; ";
<< tilePosition.y() << "; agent bounds: " << agentBounds << "; ";
return stream.str();
}
}
RecastContext::RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition,
const AgentBounds& agentBounds, const Version& version, Debug::Level maxLogLevel)
: mMaxLogLevel(maxLogLevel)
, mPrefix(formatPrefix(worldspace, tilePosition, agentBounds, version))
RecastContext::RecastContext(
ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds)
: mPrefix(formatPrefix(worldspace, tilePosition, agentBounds))
{
}
void RecastContext::doLog(const rcLogCategory category, const char* msg, const int len)
{
if (msg == nullptr || len <= 0)
return;
const Debug::Level level = getLogLevel(category);
if (level > mMaxLogLevel)
return;
Log(level) << mPrefix << std::string_view(msg, static_cast<std::size_t>(len));
if (len > 0)
Log(getLogLevel(category)) << mPrefix << std::string_view(msg, static_cast<std::size_t>(len));
}
}

View file

@ -3,7 +3,6 @@
#include "tileposition.hpp"
#include <components/debug/debuglog.hpp>
#include <components/esm/refid.hpp>
#include <string>
@ -13,18 +12,15 @@
namespace DetourNavigator
{
struct AgentBounds;
struct Version;
class RecastContext final : public rcContext
{
public:
explicit RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds,
const Version& version, Debug::Level maxLogLevel);
explicit RecastContext(ESM::RefId worldspace, const TilePosition& tilePosition, const AgentBounds& agentBounds);
const std::string& getPrefix() const { return mPrefix; }
private:
Debug::Level mMaxLogLevel;
std::string mPrefix;
void doLog(rcLogCategory category, const char* msg, int len) override;

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