Merge branch 'master' of gitlab.com:openmw/openmw into lua_controller_cursor

pull/3235/head
Zackhasacat 9 months ago
commit 6491bb4910

@ -521,13 +521,13 @@ Ubuntu_GCC_integration_tests_asan:
- build/OpenMW-*.dmg
- "build/**/*.log"
macOS13_Xcode14_arm64:
macOS14_Xcode15_arm64:
extends: .MacOS
image: macos-12-xcode-14
image: macos-14-xcode-15
tags:
- saas-macos-medium-m1
cache:
key: macOS12_Xcode14_arm64.v4
key: macOS14_Xcode15_arm64.v1
variables:
CCACHE_SIZE: 3G
@ -576,7 +576,7 @@ macOS13_Xcode14_arm64:
- cd MSVC2019_64_Ninja
- .\ActivateMSVC.ps1
- cmake --build . --config $config
- ccache --show-stats
- ccache --show-stats -v
- cd $config
- 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}"))/"
@ -666,7 +666,6 @@ macOS13_Xcode14_arm64:
- choco source disable -n=chocolatey
- choco install git --force --params "/GitAndUnixToolsOnPath" -y
- choco install 7zip -y
- choco install ccache -y
- choco install vswhere -y
- choco install python -y
- choco install awscli -y
@ -689,15 +688,11 @@ macOS13_Xcode14_arm64:
- $time = (Get-Date -Format "HH:mm:ss")
- echo ${time}
- echo "started by ${GITLAB_USER_NAME}"
- $env:CCACHE_BASEDIR = Get-Location
- $env:CCACHE_DIR = "$(Get-Location)\ccache"
- New-Item -Type Directory -Force -Path $env:CCACHE_DIR
- New-Item -Type File -Force -Path MSVC2019_64\.cmake\api\v1\query\codemodel-v2
- sh CI/before_script.msvc.sh -c $config -p Win64 -v 2019 -k -V -b -t -C $multiview -E
- cd MSVC2019_64
- Get-Volume
- cmake --build . --config $config
- ccache --show-stats
- cd $config
- 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}"))/"
@ -729,7 +724,6 @@ macOS13_Xcode14_arm64:
cache:
key: msbuild-v8
paths:
- ccache
- deps
- MSVC2019_64/deps/Qt
artifacts:

@ -15,6 +15,7 @@ Programmers
Nicolay Korslund - Project leader 2008-2010
scrawl - Top contributor
AbduSharif
Adam Hogan (aurix)
Aesylwinn
aegis

@ -33,6 +33,8 @@
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 #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
Bug #6222: global map cell size can crash openmw if set to too high a value
Bug #6313: Followers with high Fight can turn hostile
@ -56,6 +58,7 @@
Bug #6973: Fade in happens after the scene load and is shown
Bug #6974: Only harmful effects are reflected
Bug #6977: Sun damage implementation does not match research
Bug #6985: Issues with Magic Cards numbers readability
Bug #6986: Sound magic effect does not make noise
Bug #6987: Set/Mod Blindness should not darken the screen
Bug #6992: Crossbow reloading doesn't look the same as in Morrowind
@ -71,12 +74,15 @@
Bug #7084: Resurrecting an actor doesn't take into account base record changes
Bug #7088: Deleting last save game of last character doesn't clear character name/details
Bug #7092: BSA archives from higher priority directories don't take priority
Bug #7102: Some HQ Creatures mod models can hit the 8 texture slots limit with 0.48
Bug #7103: Multiple paths pointing to the same plugin but with different cases lead to automatically removed config entries
Bug #7122: Teleportation to underwater should cancel active water walking effect
Bug #7131: MyGUI log spam when post processing HUD is open
Bug #7134: Saves with an invalid last generated RefNum can be loaded
Bug #7163: Myar Aranath: Wheat breaks the GUI
Bug #7168: Fix average scene luminance
Bug #7172: Current music playlist continues playing indefinitely if next playlist is empty
Bug #7202: Post-processing normals for terrain, water randomly stop rendering
Bug #7204: Missing actor scripts freeze the game
Bug #7229: Error marker loading failure is not handled
Bug #7243: Supporting loading external files from VFS from esm files
@ -93,22 +99,26 @@
Bug #7415: Unbreakable lock discrepancies
Bug #7416: Modpccrimelevel is different from vanilla
Bug #7428: AutoCalc flag is not used to calculate enchantment costs
Bug #7447: OpenMW-CS: Dragging a cell of a different type (from the initial type) into the 3D view crashes OpenMW-CS
Bug #7450: Evading obstacles does not work for actors missing certain animations
Bug #7459: Icons get stacked on the cursor when picking up multiple items simultaneously
Bug #7472: Crash when enchanting last projectiles
Bug #7475: Equipping a constant effect item doesn't update the magic menu
Bug #7502: Data directories dialog (0.48.0) forces adding subdirectory instead of intended directory
Bug #7505: Distant terrain does not support sample size greater than cell size
Bug #7535: Bookart paths for textures in OpenMW vs vanilla Morrowind
Bug #7553: Faction reaction loading is incorrect
Bug #7557: Terrain::ChunkManager::createChunk is called twice for the same position, lod on initial loading
Bug #7573: Drain Fatigue can't bring fatigue below zero by default
Bug #7585: Difference in interior lighting between OpenMW with legacy lighting method enabled and vanilla Morrowind
Bug #7587: Quick load related crash
Bug #7603: Scripts menu size is not updated properly
Bug #7604: Goblins Grunt becomes idle once injured
Bug #7609: ForceGreeting should not open dialogue for werewolves
Bug #7611: Beast races' idle animations slide after turning or jumping in place
Bug #7617: The death prompt asks the player if they wanted to load the character's last created save
Bug #7619: Long map notes may get cut off
Bug #7623: Incorrect placement of the script info in the engraved ring of healing tooltip
Bug #7630: Charm can be cast on creatures
Bug #7631: Cannot trade with/talk to Creeper or Mudcrab Merchant when they're fleeing
Bug #7633: Groundcover should ignore non-geometry Drawables
@ -129,6 +139,7 @@
Bug #7679: Scene luminance value flashes when toggling shaders
Bug #7685: Corky sometimes doesn't follow Llovyn Andus
Bug #7712: Casting doesn't support spells and enchantments with no effects
Bug #7721: CS: Special Chars Not Allowed in IDs
Bug #7723: Assaulting vampires and werewolves shouldn't be a crime
Bug #7724: Guards don't help vs werewolves
Bug #7733: Launcher shows incorrect data paths when there's two plugins with the same name
@ -136,6 +147,7 @@
Bug #7753: Editor: Actors Don't Scale According to Their Race
Bug #7758: Water walking is not taken into account to compute path cost on the water
Bug #7761: Rain and ambient loop sounds are mutually exclusive
Bug #7763: Bullet shape loading problems, assorted
Bug #7765: OpenMW-CS: Touch Record option is broken
Bug #7769: Sword of the Perithia: Broken NPCs
Bug #7770: Sword of the Perithia: Script execution failure
@ -143,22 +155,34 @@
Bug #7785: OpenMW-CS initialising Skill and Attribute fields to 0 instead of -1 on non-FortifyStat spells
Bug #7794: Fleeing NPCs name tooltip doesn't appear
Bug #7796: Absorbed enchantments don't restore magicka
Bug #7823: Game crashes when launching it.
Bug #7832: Ingredient tooltips show magnitude for Fortify Maximum Magicka effect
Bug #7840: First run of the launcher doesn't save viewing distance as the default value
Bug #7841: Editor: "Dirty" water heights are saved in modified CELLs
Bug #7859: AutoCalc flag is not used to calculate potion value
Bug #7861: OpenMW-CS: Incorrect DIAL's type in INFO records
Bug #7872: Region sounds use wrong odds
Bug #7886: Equip and unequip animations can't share the animation track section
Bug #7887: Editor: Mismatched reported script data size and actual data size causes a crash during save
Bug #7898: Editor: Invalid reference scales are allowed
Bug #7899: Editor: Doors can't be unlocked
Bug #7901: Editor: Teleport-related fields shouldn't be editable if a ref does not teleport
Bug #7908: Key bindings names in the settings menu are layout-specific
Feature #2566: Handle NAM9 records for manual cell references
Feature #3537: Shader-based water ripples
Feature #5173: Support for NiFogProperty
Feature #5492: Let rain and snow collide with statics
Feature #5926: Refraction based on water depth
Feature #5944: Option to use camera as sound listener
Feature #6149: Dehardcode Lua API_REVISION
Feature #6152: Playing music via lua scripts
Feature #6188: Specular lighting from point light sources
Feature #6411: Support translations in openmw-launcher
Feature #6447: Add LOD support to Object Paging
Feature #6491: Add support for Qt6
Feature #6556: Lua API for sounds
Feature #6679: Design a custom Input Action API
Feature #6726: Lua API for creating new objects
Feature #6727: Lua API for records of all object types
Feature #6864: Lua file access API
Feature #6922: Improve launcher appearance
Feature #6933: Support high-resolution cursor textures
@ -171,16 +195,19 @@
Feature #7125: Remembering console commands between sessions
Feature #7129: Add support for non-adaptive VSync
Feature #7130: Ability to set MyGUI logging verbosity
Feature #7142: MWScript Lua API
Feature #7148: Optimize string literal lookup in mwscript
Feature #7161: OpenMW-CS: Make adding and filtering TopicInfos easier
Feature #7194: Ori to show texture paths
Feature #7214: Searching in the in-game console
Feature #7284: Searching in the console with regex and toggleable case-sensitivity
Feature #7248: Searching in the console with regex and toggleable case-sensitivity
Feature #7468: Factions API for Lua
Feature #7477: NegativeLight Magic Effect flag
Feature #7499: OpenMW-CS: Generate record filters by drag & dropping cell content to the filters field
Feature #7546: Start the game on Fredas
Feature #7554: Controller binding for tab for menu navigation
Feature #7568: Uninterruptable scripted music
Feature #7590: [Lua] Ability to deserialize YAML data from scripts
Feature #7606: Launcher: allow Shift-select in Archives tab
Feature #7608: Make the missing dependencies warning when loading a savegame more helpful
Feature #7618: Show the player character's health in the save details
@ -193,8 +220,14 @@
Feature #7792: Support Timescale Clouds
Feature #7795: Support MaxNumberRipples INI setting
Feature #7805: Lua Menu context
Feature #7860: Lua: Expose NPC AI settings (fight, alarm, flee)
Feature #7875: Disable MyGUI windows snapping
Feature #7914: Do not allow to move GUI windows out of screen
Task #5896: Do not use deprecated MyGUI properties
Task #6085: Replace boost::filesystem with std::filesystem
Task #6149: Dehardcode Lua API_REVISION
Task #6624: Drop support for saves made prior to 0.45
Task #7048: Get rid of std::bind
Task #7113: Move from std::atoi to std::from_char
Task #7117: Replace boost::scoped_array with std::vector
Task #7151: Do not use std::strerror to get errno error message

@ -528,8 +528,12 @@ if ! [ -z $UNITY_BUILD ]; then
add_cmake_opts "-DOPENMW_UNITY_BUILD=True"
fi
if ! [ -z $USE_CCACHE ]; then
add_cmake_opts "-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache"
if [ -n "$USE_CCACHE" ]; then
if [ -n "$NMAKE" ] || [ -n "$NINJA" ]; then
add_cmake_opts "-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache -DPRECOMPILE_HEADERS_WITH_MSVC=OFF"
else
echo "Ignoring -C (CCache) as it is incompatible with Visual Studio CMake generators"
fi
fi
# turn on LTO by default

@ -4,8 +4,8 @@ set -o pipefail
LUPDATE="${LUPDATE:-lupdate}"
${LUPDATE:?} apps/wizard -ts files/lang/wizard_*.ts
${LUPDATE:?} apps/launcher -ts files/lang/launcher_*.ts
${LUPDATE:?} components/contentselector components/process -ts files/lang/components_*.ts
${LUPDATE:?} -locations none apps/wizard -ts files/lang/wizard_*.ts
${LUPDATE:?} -locations none apps/launcher -ts files/lang/launcher_*.ts
${LUPDATE:?} -locations none components/contentselector components/process -ts files/lang/components_*.ts
! (git diff --name-only | grep -q "^") || (echo -e "\033[0;31mBuild a 'translations' CMake target to update Qt localization for these files:\033[0;0m"; git diff --name-only | xargs -i echo -e "\033[0;31m{}\033[0;0m"; exit -1)

@ -20,6 +20,7 @@ apps/openmw_test_suite/lua/test_storage.cpp
apps/openmw_test_suite/lua/test_ui_content.cpp
apps/openmw_test_suite/lua/test_utilpackage.cpp
apps/openmw_test_suite/lua/test_inputactions.cpp
apps/openmw_test_suite/lua/test_yaml.cpp
apps/openmw_test_suite/misc/test_endianness.cpp
apps/openmw_test_suite/misc/test_resourcehelpers.cpp
apps/openmw_test_suite/misc/test_stringops.cpp

@ -44,6 +44,7 @@ option(BUILD_BENCHMARKS "Build benchmarks with Google Benchmark" OFF)
option(BUILD_NAVMESHTOOL "Build navmesh tool" ON)
option(BUILD_BULLETOBJECTTOOL "Build Bullet object tool" ON)
option(BUILD_OPENCS_TESTS "Build OpenMW Construction Set tests" OFF)
option(PRECOMPILE_HEADERS_WITH_MSVC "Precompile most common used headers with MSVC (alternative to ccache)" ON)
set(OpenGL_GL_PREFERENCE LEGACY) # Use LEGACY as we use GL2; GLNVD is for GL3 and up.
@ -80,7 +81,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 54)
set(OPENMW_LUA_API_REVISION 59)
set(OPENMW_POSTPROCESSING_API_REVISION 1)
set(OPENMW_VERSION_COMMITHASH "")
@ -89,7 +90,7 @@ set(OPENMW_VERSION_COMMITDATE "")
set(OPENMW_VERSION "${OPENMW_VERSION_MAJOR}.${OPENMW_VERSION_MINOR}.${OPENMW_VERSION_RELEASE}")
set(OPENMW_DOC_BASEURL "https://openmw.readthedocs.io/en/stable/")
set(OPENMW_DOC_BASEURL "https://openmw.readthedocs.io/en/")
set(GIT_CHECKOUT FALSE)
if(EXISTS ${PROJECT_SOURCE_DIR}/.git)
@ -190,6 +191,22 @@ if (MSVC)
add_compile_options(/bigobj)
add_compile_options(/Zc:__cplusplus)
if (CMAKE_CXX_COMPILER_LAUNCHER OR CMAKE_C_COMPILER_LAUNCHER)
if (CMAKE_GENERATOR MATCHES "Visual Studio")
message(STATUS "A compiler launcher was specified, but will be unused by the current generator (${CMAKE_GENERATOR})")
else()
foreach (config_lower ${CMAKE_CONFIGURATION_TYPES})
string(TOUPPER "${config_lower}" config)
if (CMAKE_C_COMPILER_LAUNCHER STREQUAL "ccache")
string(REPLACE "/Zi" "/Z7" CMAKE_C_FLAGS_${config} "${CMAKE_C_FLAGS_${config}}")
endif()
if (CMAKE_CXX_COMPILER_LAUNCHER STREQUAL "ccache")
string(REPLACE "/Zi" "/Z7" CMAKE_CXX_FLAGS_${config} "${CMAKE_CXX_FLAGS_${config}}")
endif()
endforeach()
endif()
endif()
endif()
# Set up common paths
@ -718,67 +735,66 @@ if (WIN32)
)
foreach(d ${WARNINGS_DISABLE})
set(WARNINGS "${WARNINGS} /wd${d}")
list(APPEND WARNINGS "/wd${d}")
endforeach(d)
if(OPENMW_MSVC_WERROR)
set(WARNINGS "${WARNINGS} /WX")
list(APPEND WARNINGS "/WX")
endif()
set_target_properties(components PROPERTIES COMPILE_FLAGS "${WARNINGS}")
set_target_properties(osg-ffmpeg-videoplayer PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(components PRIVATE ${WARNINGS})
target_compile_options(osg-ffmpeg-videoplayer PRIVATE ${WARNINGS})
if (MSVC_VERSION GREATER_EQUAL 1915 AND MSVC_VERSION LESS 1920)
target_compile_definitions(components INTERFACE _ENABLE_EXTENDED_ALIGNED_STORAGE)
endif()
if (BUILD_BSATOOL)
set_target_properties(bsatool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(bsatool PRIVATE ${WARNINGS})
endif()
if (BUILD_ESMTOOL)
set_target_properties(esmtool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(esmtool PRIVATE ${WARNINGS})
endif()
if (BUILD_ESSIMPORTER)
set_target_properties(openmw-essimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-essimporter PRIVATE ${WARNINGS})
endif()
if (BUILD_LAUNCHER)
set_target_properties(openmw-launcher PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-launcher PRIVATE ${WARNINGS})
endif()
if (BUILD_MWINIIMPORTER)
set_target_properties(openmw-iniimporter PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-iniimporter PRIVATE ${WARNINGS})
endif()
if (BUILD_OPENCS)
set_target_properties(openmw-cs PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-cs PRIVATE ${WARNINGS})
endif()
if (BUILD_OPENMW)
set_target_properties(openmw PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw PRIVATE ${WARNINGS})
endif()
if (BUILD_WIZARD)
set_target_properties(openmw-wizard PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-wizard PRIVATE ${WARNINGS})
endif()
if (BUILD_UNITTESTS)
set_target_properties(openmw_test_suite PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw_test_suite PRIVATE ${WARNINGS})
endif()
if (BUILD_BENCHMARKS)
set_target_properties(openmw_detournavigator_navmeshtilescache_benchmark PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE ${WARNINGS})
endif()
if (BUILD_NAVMESHTOOL)
set_target_properties(openmw-navmeshtool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-navmeshtool PRIVATE ${WARNINGS})
endif()
if (BUILD_BULLETOBJECTTOOL)
set(WARNINGS "${WARNINGS} ${MT_BUILD}")
set_target_properties(openmw-bulletobjecttool PROPERTIES COMPILE_FLAGS "${WARNINGS}")
target_compile_options(openmw-bulletobjecttool PRIVATE ${WARNINGS} ${MT_BUILD})
endif()
endif(MSVC)
@ -1090,17 +1106,17 @@ if (USE_QT)
file(GLOB COMPONENTS_TS_FILES ${CMAKE_SOURCE_DIR}/files/lang/components_*.ts)
get_target_property(QT_LUPDATE_EXECUTABLE Qt::lupdate IMPORTED_LOCATION)
add_custom_target(translations
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/components/contentselector ${CMAKE_SOURCE_DIR}/components/process -ts ${COMPONENTS_TS_FILES}
COMMAND ${QT_LUPDATE_EXECUTABLE} -locations none ${CMAKE_SOURCE_DIR}/components/contentselector ${CMAKE_SOURCE_DIR}/components/process -ts ${COMPONENTS_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/components
VERBATIM
COMMAND_EXPAND_LISTS
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/wizard -ts ${WIZARD_TS_FILES}
COMMAND ${QT_LUPDATE_EXECUTABLE} -locations none ${CMAKE_SOURCE_DIR}/apps/wizard -ts ${WIZARD_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/wizard
VERBATIM
COMMAND_EXPAND_LISTS
COMMAND ${QT_LUPDATE_EXECUTABLE} ${CMAKE_SOURCE_DIR}/apps/launcher -ts ${LAUNCHER_TS_FILES}
COMMAND ${QT_LUPDATE_EXECUTABLE} -locations none ${CMAKE_SOURCE_DIR}/apps/launcher -ts ${LAUNCHER_TS_FILES}
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/apps/launcher
VERBATIM
COMMAND_EXPAND_LISTS)

@ -20,7 +20,7 @@ Font Licenses:
Current Status
--------------
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://gitlab.com/OpenMW/openmw/issues?label_name%5B%5D=1.0) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces.
The main quests in Morrowind, Tribunal and Bloodmoon are all completable. Some issues with side quests are to be expected (but rare). Check the [bug tracker](https://gitlab.com/OpenMW/openmw/-/issues/?milestone_title=openmw-1.0) for a list of issues we need to resolve before the "1.0" release. Even before the "1.0" release however, OpenMW boasts some new [features](https://wiki.openmw.org/index.php?title=Features), such as improved graphics and user interfaces.
Pre-existing modifications created for the original Morrowind engine can be hit-and-miss. The OpenMW script compiler performs more thorough error-checking than Morrowind does, meaning that a mod created for Morrowind may not necessarily run in OpenMW. Some mods also rely on quirky behaviour or engine bugs in order to work. We are considering such compatibility issues on a case-by-case basis - in some cases adding a workaround to OpenMW may be feasible, in other cases fixing the mod will be the only option. If you know of any mods that work or don't work, feel free to add them to the [Mod status](https://wiki.openmw.org/index.php?title=Mod_status) wiki page.

@ -5,7 +5,7 @@ if (UNIX AND NOT APPLE)
target_link_libraries(openmw_detournavigator_navmeshtilescache_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw_detournavigator_navmeshtilescache_benchmark PRIVATE <algorithm>)
endif()

@ -182,7 +182,7 @@ namespace
for (auto _ : state)
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
auto result = cache.get(key.mAgentBounds, key.mTilePosition, key.mRecastMesh);
benchmark::DoNotOptimize(result);
}
}
@ -241,7 +241,7 @@ namespace
while (state.KeepRunning())
{
const auto& key = keys[n++ % keys.size()];
const auto result = cache.set(
auto result = cache.set(
key.mAgentBounds, key.mTilePosition, key.mRecastMesh, std::make_unique<PreparedNavMeshData>());
benchmark::DoNotOptimize(result);
}

@ -5,7 +5,7 @@ if (UNIX AND NOT APPLE)
target_link_libraries(openmw_esm_refid_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw_esm_refid_benchmark PRIVATE <algorithm>)
endif()

@ -8,7 +8,7 @@ if (UNIX AND NOT APPLE)
target_link_libraries(openmw_settings_access_benchmark ${CMAKE_THREAD_LIBS_INIT})
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw_settings_access_benchmark PRIVATE <algorithm>)
endif()

@ -38,7 +38,7 @@ namespace
{
for (auto _ : state)
{
static const float v = Settings::Manager::getFloat("sky blending start", "Fog");
static float v = Settings::Manager::getFloat("sky blending start", "Fog");
benchmark::DoNotOptimize(v);
}
}
@ -47,8 +47,8 @@ namespace
{
for (auto _ : state)
{
static const float v1 = Settings::Manager::getFloat("near clip", "Camera");
static const bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
}
@ -58,9 +58,9 @@ namespace
{
for (auto _ : state)
{
static const float v1 = Settings::Manager::getFloat("near clip", "Camera");
static const bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
static const int v3 = Settings::Manager::getInt("reflection detail", "Water");
static float v1 = Settings::Manager::getFloat("near clip", "Camera");
static bool v2 = Settings::Manager::getBool("transparent postpass", "Post Processing");
static int v3 = Settings::Manager::getInt("reflection detail", "Water");
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
benchmark::DoNotOptimize(v3);
@ -71,7 +71,8 @@ namespace
{
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::fog().mSkyBlendingStart.get());
float v = Settings::fog().mSkyBlendingStart.get();
benchmark::DoNotOptimize(v);
}
}
@ -79,8 +80,10 @@ namespace
{
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::postProcessing().mTransparentPostpass.get());
benchmark::DoNotOptimize(Settings::camera().mNearClip.get());
bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get();
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
}
}
@ -88,9 +91,12 @@ namespace
{
for (auto _ : state)
{
benchmark::DoNotOptimize(Settings::postProcessing().mTransparentPostpass.get());
benchmark::DoNotOptimize(Settings::camera().mNearClip.get());
benchmark::DoNotOptimize(Settings::water().mReflectionDetail.get());
bool v1 = Settings::postProcessing().mTransparentPostpass.get();
float v2 = Settings::camera().mNearClip.get();
int v3 = Settings::water().mReflectionDetail.get();
benchmark::DoNotOptimize(v1);
benchmark::DoNotOptimize(v2);
benchmark::DoNotOptimize(v3);
}
}

@ -18,7 +18,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(bsatool gcov)
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(bsatool PRIVATE
<filesystem>
<fstream>

@ -329,17 +329,19 @@ int main(int argc, char** argv)
switch (bsaVersion)
{
case Bsa::BSAVER_COMPRESSED:
case Bsa::BsaVersion::Unknown:
break;
case Bsa::BsaVersion::Uncompressed:
return call<Bsa::BSAFile>(info);
case Bsa::BsaVersion::Compressed:
return call<Bsa::CompressedBSAFile>(info);
case Bsa::BSAVER_BA2_GNRL:
case Bsa::BsaVersion::BA2GNRL:
return call<Bsa::BA2GNRLFile>(info);
case Bsa::BSAVER_BA2_DX10:
case Bsa::BsaVersion::BA2DX10:
return call<Bsa::BA2DX10File>(info);
case Bsa::BSAVER_UNCOMPRESSED:
return call<Bsa::BSAFile>(info);
default:
throw std::runtime_error("Unrecognised BSA archive");
}
throw std::runtime_error("Unrecognised BSA archive");
}
catch (std::exception& e)
{

@ -19,7 +19,7 @@ if (WIN32)
install(TARGETS openmw-bulletobjecttool RUNTIME DESTINATION ".")
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-bulletobjecttool PRIVATE
<string>
<vector>

@ -25,7 +25,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(esmtool gcov)
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(esmtool PRIVATE
<fstream>
<string>

@ -1,5 +1,6 @@
#include "labels.hpp"
#include <components/esm3/loadalch.hpp>
#include <components/esm3/loadbody.hpp>
#include <components/esm3/loadcell.hpp>
#include <components/esm3/loadcont.hpp>
@ -987,3 +988,16 @@ std::string recordFlags(uint32_t flags)
properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties;
}
std::string potionFlags(int flags)
{
std::string properties;
if (flags == 0)
properties += "[None] ";
if (flags & ESM::Potion::Autocalc)
properties += "Autocalc ";
if (flags & (0xFFFFFFFF ^ ESM::Enchantment::Autocalc))
properties += "Invalid ";
properties += Misc::StringUtils::format("(0x%08X)", flags);
return properties;
}

@ -60,6 +60,7 @@ std::string itemListFlags(int flags);
std::string lightFlags(int flags);
std::string magicEffectFlags(int flags);
std::string npcFlags(int flags);
std::string potionFlags(int flags);
std::string raceFlags(int flags);
std::string spellFlags(int flags);
std::string weaponFlags(int flags);

@ -180,22 +180,23 @@ namespace
void printEffectList(const ESM::EffectList& effects)
{
int i = 0;
for (const ESM::ENAMstruct& effect : effects.mList)
for (const ESM::IndexedENAMstruct& effect : effects.mList)
{
std::cout << " Effect[" << i << "]: " << magicEffectLabel(effect.mEffectID) << " (" << effect.mEffectID
<< ")" << std::endl;
if (effect.mSkill != -1)
std::cout << " Skill: " << skillLabel(effect.mSkill) << " (" << (int)effect.mSkill << ")"
std::cout << " Effect[" << i << "]: " << magicEffectLabel(effect.mData.mEffectID) << " ("
<< effect.mData.mEffectID << ")" << std::endl;
if (effect.mData.mSkill != -1)
std::cout << " Skill: " << skillLabel(effect.mData.mSkill) << " (" << (int)effect.mData.mSkill << ")"
<< std::endl;
if (effect.mAttribute != -1)
std::cout << " Attribute: " << attributeLabel(effect.mAttribute) << " (" << (int)effect.mAttribute
<< ")" << std::endl;
std::cout << " Range: " << rangeTypeLabel(effect.mRange) << " (" << effect.mRange << ")" << std::endl;
if (effect.mData.mAttribute != -1)
std::cout << " Attribute: " << attributeLabel(effect.mData.mAttribute) << " ("
<< (int)effect.mData.mAttribute << ")" << std::endl;
std::cout << " Range: " << rangeTypeLabel(effect.mData.mRange) << " (" << effect.mData.mRange << ")"
<< std::endl;
// Area is always zero if range type is "Self"
if (effect.mRange != ESM::RT_Self)
std::cout << " Area: " << effect.mArea << std::endl;
std::cout << " Duration: " << effect.mDuration << std::endl;
std::cout << " Magnitude: " << effect.mMagnMin << "-" << effect.mMagnMax << std::endl;
if (effect.mData.mRange != ESM::RT_Self)
std::cout << " Area: " << effect.mData.mArea << std::endl;
std::cout << " Duration: " << effect.mData.mDuration << std::endl;
std::cout << " Magnitude: " << effect.mData.mMagnMin << "-" << effect.mData.mMagnMax << std::endl;
i++;
}
}
@ -479,7 +480,7 @@ namespace EsmTool
std::cout << " Script: " << mData.mScript << std::endl;
std::cout << " Weight: " << mData.mData.mWeight << std::endl;
std::cout << " Value: " << mData.mData.mValue << std::endl;
std::cout << " AutoCalc: " << mData.mData.mAutoCalc << std::endl;
std::cout << " Flags: " << potionFlags(mData.mData.mFlags) << std::endl;
printEffectList(mData.mEffects);
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
@ -612,7 +613,6 @@ namespace EsmTool
}
else
std::cout << " Map Color: " << Misc::StringUtils::format("0x%08X", mData.mMapColor) << std::endl;
std::cout << " Water Level Int: " << mData.mWaterInt << std::endl;
std::cout << " RefId counter: " << mData.mRefNumCounter << std::endl;
std::cout << " Deleted: " << mIsDeleted << std::endl;
}
@ -840,8 +840,7 @@ namespace EsmTool
std::cout << " Quest Status: " << questStatusLabel(mData.mQuestStatus) << " (" << mData.mQuestStatus << ")"
<< std::endl;
std::cout << " Unknown1: " << mData.mData.mUnknown1 << std::endl;
std::cout << " Unknown2: " << (int)mData.mData.mUnknown2 << std::endl;
std::cout << " Type: " << dialogTypeLabel(mData.mData.mType) << std::endl;
for (const ESM::DialInfo::SelectStruct& rule : mData.mSelects)
std::cout << " Select Rule: " << ruleString(rule) << std::endl;
@ -898,9 +897,6 @@ namespace EsmTool
if (const ESM::Land::LandData* data = mData.getLandData(mData.mDataTypes))
{
std::cout << " Height Offset: " << data->mHeightOffset << std::endl;
// Lots of missing members.
std::cout << " Unknown1: " << data->mUnk1 << std::endl;
std::cout << " Unknown2: " << static_cast<unsigned>(data->mUnk2) << std::endl;
}
mData.unloadData();
std::cout << " Deleted: " << mIsDeleted << std::endl;
@ -1138,7 +1134,6 @@ namespace EsmTool
std::cout << " Coordinates: (" << point.mX << "," << point.mY << "," << point.mZ << ")" << std::endl;
std::cout << " Auto-Generated: " << (int)point.mAutogenerated << std::endl;
std::cout << " Connections: " << (int)point.mConnectionNum << std::endl;
std::cout << " Unknown: " << point.mUnknown << std::endl;
i++;
}

@ -47,7 +47,7 @@ if (WIN32)
INSTALL(TARGETS openmw-essimporter RUNTIME DESTINATION ".")
endif(WIN32)
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-essimporter PRIVATE
<algorithm>
<filesystem>

@ -232,7 +232,7 @@ namespace ESSImport
esm.skip(4);
}
esm.getExact(nam8, 32);
esm.getT(nam8);
newcell.mFogOfWar.reserve(16 * 16);
for (int x = 0; x < 16; ++x)

@ -1,10 +1,30 @@
#include "importcellref.hpp"
#include <components/esm3/esmreader.hpp>
#include <components/misc/concepts.hpp>
#include <cstdint>
namespace ESSImport
{
template <Misc::SameAsWithoutCvref<ACDT> T>
void decompose(T&& v, const auto& f)
{
f(v.mUnknown, v.mFlags, v.mBreathMeter, v.mUnknown2, v.mDynamic, v.mUnknown3, v.mAttributes, v.mMagicEffects,
v.mUnknown4, v.mGoldPool, v.mCountDown, v.mUnknown5);
}
template <Misc::SameAsWithoutCvref<ACSC> T>
void decompose(T&& v, const auto& f)
{
f(v.mUnknown1, v.mFlags, v.mUnknown2, v.mCorpseClearCountdown, v.mUnknown3);
}
template <Misc::SameAsWithoutCvref<ANIS> T>
void decompose(T&& v, const auto& f)
{
f(v.mGroupIndex, v.mUnknown, v.mTime);
}
void CellRef::load(ESM::ESMReader& esm)
{
@ -45,14 +65,9 @@ namespace ESSImport
bool isDeleted = false;
ESM::CellRef::loadData(esm, isDeleted);
mActorData.mHasACDT
= esm.getHNOT("ACDT", mActorData.mACDT.mUnknown, mActorData.mACDT.mFlags, mActorData.mACDT.mBreathMeter,
mActorData.mACDT.mUnknown2, mActorData.mACDT.mDynamic, mActorData.mACDT.mUnknown3,
mActorData.mACDT.mAttributes, mActorData.mACDT.mMagicEffects, mActorData.mACDT.mUnknown4,
mActorData.mACDT.mGoldPool, mActorData.mACDT.mCountDown, mActorData.mACDT.mUnknown5);
mActorData.mHasACDT = esm.getOptionalComposite("ACDT", mActorData.mACDT);
mActorData.mHasACSC = esm.getHNOT("ACSC", mActorData.mACSC.mUnknown1, mActorData.mACSC.mFlags,
mActorData.mACSC.mUnknown2, mActorData.mACSC.mCorpseClearCountdown, mActorData.mACSC.mUnknown3);
mActorData.mHasACSC = esm.getOptionalComposite("ACSC", mActorData.mACSC);
if (esm.isNextSub("ACSL"))
esm.skipHSubSize(112);
@ -127,8 +142,7 @@ namespace ESSImport
if (esm.isNextSub("ND3D"))
esm.skipHSub();
mActorData.mHasANIS
= esm.getHNOT("ANIS", mActorData.mANIS.mGroupIndex, mActorData.mANIS.mUnknown, mActorData.mANIS.mTime);
mActorData.mHasANIS = esm.getOptionalComposite("ANIS", mActorData.mANIS);
if (esm.isNextSub("LVCR"))
{
@ -146,7 +160,7 @@ namespace ESSImport
// I've seen DATA *twice* on a creature record, and with the exact same content too! weird
// alarmvoi0000.ess
for (int i = 0; i < 2; ++i)
esm.getHNOT("DATA", mPos.pos, mPos.rot);
esm.getOptionalComposite("DATA", mPos);
mDeleted = 0;
if (esm.isNextSub("DELE"))

@ -135,7 +135,7 @@ namespace ESSImport
sub.mFileOffset = esm.getFileOffset();
sub.mName = esm.retSubName().toString();
sub.mData.resize(esm.getSubSize());
esm.getExact(&sub.mData[0], sub.mData.size());
esm.getExact(sub.mData.data(), sub.mData.size());
rec.mSubrecords.push_back(sub);
}
file.mRecords.push_back(rec);

@ -94,7 +94,7 @@ if(USE_QT)
set_property(TARGET openmw-launcher PROPERTY AUTOMOC ON)
endif(USE_QT)
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-launcher PRIVATE
<boost/program_options/options_description.hpp>

@ -819,7 +819,7 @@ void Launcher::DataFilesPage::addArchivesFromDir(const QString& path)
for (const auto& fileinfo : dir.entryInfoList(archiveFilter))
{
const auto absPath = fileinfo.absoluteFilePath();
if (Bsa::BSAFile::detectVersion(Files::pathFromQString(absPath)) == Bsa::BSAVER_UNKNOWN)
if (Bsa::BSAFile::detectVersion(Files::pathFromQString(absPath)) == Bsa::BsaVersion::Unknown)
continue;
const auto fileName = fileinfo.fileName();

@ -193,8 +193,10 @@ bool Launcher::SettingsPage::loadSettings()
loadSettingBool(Settings::game().mSmoothMovement, *smoothMovementCheckBox);
loadSettingBool(Settings::game().mPlayerMovementIgnoresAnimation, *playerMovementIgnoresAnimationCheckBox);
distantLandCheckBox->setCheckState(
Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging ? Qt::Checked : Qt::Unchecked);
connect(distantLandCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotDistantLandToggled);
bool distantLandEnabled = Settings::terrain().mDistantTerrain && Settings::terrain().mObjectPaging;
distantLandCheckBox->setCheckState(distantLandEnabled ? Qt::Checked : Qt::Unchecked);
slotDistantLandToggled(distantLandEnabled);
loadSettingBool(Settings::terrain().mObjectPagingActiveGrid, *activeGridObjectPagingCheckBox);
viewingDistanceComboBox->setValue(convertToCells(Settings::camera().mViewingDistance));
@ -244,6 +246,11 @@ bool Launcher::SettingsPage::loadSettings()
int shadowResIndex = shadowResolutionComboBox->findText(QString::number(shadowRes));
if (shadowResIndex != -1)
shadowResolutionComboBox->setCurrentIndex(shadowResIndex);
else
{
shadowResolutionComboBox->addItem(QString::number(shadowRes));
shadowResolutionComboBox->setCurrentIndex(shadowResolutionComboBox->count() - 1);
}
connect(shadowDistanceCheckBox, &QCheckBox::toggled, this, &SettingsPage::slotShadowDistLimitToggled);
@ -583,9 +590,16 @@ void Launcher::SettingsPage::slotShadowDistLimitToggled(bool checked)
fadeStartSpinBox->setEnabled(checked);
}
void Launcher::SettingsPage::slotDistantLandToggled(bool checked)
{
activeGridObjectPagingCheckBox->setEnabled(checked);
objectPagingMinSizeComboBox->setEnabled(checked);
}
void Launcher::SettingsPage::slotLightTypeCurrentIndexChanged(int index)
{
lightsMaximumDistanceSpinBox->setEnabled(index != 0);
lightFadeMultiplierSpinBox->setEnabled(index != 0);
lightsMaxLightsSpinBox->setEnabled(index != 0);
lightsBoundingSphereMultiplierSpinBox->setEnabled(index != 0);
lightsMinimumInteriorBrightnessSpinBox->setEnabled(index != 0);

@ -33,6 +33,7 @@ namespace Launcher
void slotPostProcessToggled(bool checked);
void slotSkyBlendingToggled(bool checked);
void slotShadowDistLimitToggled(bool checked);
void slotDistantLandToggled(bool checked);
void slotLightTypeCurrentIndexChanged(int index);
private:

@ -652,23 +652,46 @@
<item>
<layout class="QGridLayout" name="terrainLayout" columnstretch="0,0">
<item row="4" column="0">
<widget class="QCheckBox" name="distantLandCheckBox">
<widget class="QLabel" name="objectPagingMinSizeLabel">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls how large an object must be to be visible in the scene. The objects size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Distant land</string>
<string>Object paging min size</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
<item row="4" column="1">
<widget class="QDoubleSpinBox" name="objectPagingMinSizeComboBox">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>0.250000000000000</double>
</property>
<property name="singleStep">
<double>0.005000000000000</double>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="viewingDistanceLabel">
<property name="text">
<string>Viewing distance</string>
</property>
</widget>
</item>
<item row="2" column="1">
<widget class="QDoubleSpinBox" name="viewingDistanceComboBox">
<property name="suffix">
<string> cells</string>
</property>
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
@ -677,7 +700,7 @@
</property>
</widget>
</item>
<item row="6" column="1">
<item row="7" column="1">
<spacer name="verticalSpacer_15">
<property name="orientation">
<enum>Qt::Vertical</enum>
@ -691,39 +714,16 @@
</spacer>
</item>
<item row="3" column="0">
<widget class="QLabel" name="objectPagingMinSizeLabel">
<widget class="QCheckBox" name="distantLandCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Controls how large an object must be to be visible in the scene. The objects size is divided by its distance to the camera and the result of the division is compared with this value. The smaller this value is, the more objects you will see in the scene.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Object paging min size</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;If true, use paging and LOD algorithms to display the entire terrain. If false, only display terrain of the loaded cells.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
</widget>
</item>
<item row="2" column="0">
<widget class="QLabel" name="viewingDistanceLabel">
<property name="text">
<string>Viewing distance</string>
<string>Distant land</string>
</property>
</widget>
</item>
<item row="3" column="1">
<widget class="QDoubleSpinBox" name="objectPagingMinSizeComboBox">
<property name="decimals">
<number>3</number>
</property>
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>0.250000000000000</double>
</property>
<property name="singleStep">
<double>0.005000000000000</double>
</property>
</widget>
</item>
<item row="4" column="1">
<widget class="QCheckBox" name="activeGridObjectPagingCheckBox">
<property name="toolTip">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Use object paging for active cells grid.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
@ -869,6 +869,9 @@
<property name="maximum">
<number>81920</number>
</property>
<property name="singleStep">
<number>128</number>
</property>
<property name="value">
<number>8192</number>
</property>
@ -972,6 +975,9 @@
<property name="maximum">
<double>1.000000000000000</double>
</property>
<property name="singleStep">
<double>0.010000000000000</double>
</property>
<property name="value">
<double>0.900000000000000</double>
</property>
@ -1027,7 +1033,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum distance at which lights will appear (measured in units).&lt;/p&gt;&lt;p&gt;Set this to 0 to use an unlimited distance.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Lights maximum distance</string>
<string>Maximum light distance</string>
</property>
</widget>
</item>
@ -1050,7 +1056,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Maximum number of lights per object.&lt;/p&gt;&lt;p&gt;A low number near default will cause light popping similar to what you would see with legacy lighting.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Max light sources</string>
<string>Max lights</string>
</property>
</widget>
</item>
@ -1060,7 +1066,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Fraction of maximum distance at which lights will start to fade.&lt;/p&gt;&lt;p&gt;Set this to a low value for slower transitions or a high value for quicker transitions.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Lights fade multiplier</string>
<string>Fade start multiplier</string>
</property>
</widget>
</item>
@ -1102,7 +1108,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Multipler for bounding sphere of lights.&lt;/p&gt;&lt;p&gt;Higher numbers allows for smooth falloff but require an increase in number of max lights.&lt;/p&gt;&lt;p&gt;Does not effect the illumination or strength of lights.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Lights bounding sphere multiplier</string>
<string>Bounding sphere multiplier</string>
</property>
</widget>
</item>
@ -1112,7 +1118,7 @@
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Minimum ambient interior brightness.&lt;/p&gt;&lt;p&gt;Increase this if you feel interiors are too dark.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="text">
<string>Lights minimum interior brightness</string>
<string>Minimum interior brightness</string>
</property>
</widget>
</item>
@ -1323,7 +1329,7 @@
</item>
</layout>
</item>
<item row="0" column="0">
<item>
<widget class="QCheckBox" name="cameraListenerCheckBox">
<property name="toolTip">
<string>In third-person view, use the camera as the sound listener instead of the player character.</string>

@ -33,7 +33,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(openmw-iniimporter gcov)
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-iniimporter PRIVATE
<string>
<vector>

@ -21,7 +21,7 @@ if (WIN32)
install(TARGETS openmw-navmeshtool RUNTIME DESTINATION ".")
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-navmeshtool PRIVATE
<algorithm>
<memory>

@ -17,6 +17,6 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(niftest gcov)
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(niftest PRIVATE <filesystem>)
endif()

@ -42,29 +42,10 @@ bool isBSA(const std::filesystem::path& filename)
return hasExtension(filename, ".bsa") || hasExtension(filename, ".ba2");
}
std::unique_ptr<VFS::Archive> makeBsaArchive(const std::filesystem::path& path)
{
switch (Bsa::BSAFile::detectVersion(path))
{
case Bsa::BSAVER_COMPRESSED:
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_COMPRESSED>::type>(path);
case Bsa::BSAVER_BA2_GNRL:
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_GNRL>::type>(path);
case Bsa::BSAVER_BA2_DX10:
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_BA2_DX10>::type>(path);
case Bsa::BSAVER_UNCOMPRESSED:
return std::make_unique<VFS::ArchiveSelector<Bsa::BSAVER_UNCOMPRESSED>::type>(path);
case Bsa::BSAVER_UNKNOWN:
default:
std::cerr << "'" << Files::pathToUnicodeString(path) << "' is not a recognized BSA archive" << std::endl;
return nullptr;
}
}
std::unique_ptr<VFS::Archive> makeArchive(const std::filesystem::path& path)
{
if (isBSA(path))
return makeBsaArchive(path);
return VFS::makeBsaArchive(path);
if (std::filesystem::is_directory(path))
return std::make_unique<VFS::FileSystemArchive>(path);
return nullptr;
@ -84,10 +65,10 @@ void readNIF(
std::cout << " from '" << Files::pathToUnicodeString(isBSA(source) ? source.filename() : source) << "'";
std::cout << std::endl;
}
std::filesystem::path fullPath = !source.empty() ? source / path : path;
const std::filesystem::path fullPath = !source.empty() ? source / path : path;
try
{
Nif::NIFFile file(fullPath);
Nif::NIFFile file(Files::pathToUnicodeString(fullPath));
Nif::Reader reader(file, nullptr);
if (vfs != nullptr)
reader.parse(vfs->get(pathStr));
@ -124,17 +105,23 @@ void readVFS(std::unique_ptr<VFS::Archive>&& archive, const std::filesystem::pat
if (!archivePath.empty() && !isBSA(archivePath))
{
Files::PathContainer dataDirs = { archivePath };
const Files::Collections fileCollections = Files::Collections(dataDirs);
const Files::Collections fileCollections({ archivePath });
const Files::MultiDirCollection& bsaCol = fileCollections.getCollection(".bsa");
const Files::MultiDirCollection& ba2Col = fileCollections.getCollection(".ba2");
for (auto& file : bsaCol)
for (const Files::MultiDirCollection& collection : { bsaCol, ba2Col })
{
readVFS(makeBsaArchive(file.second), file.second, quiet);
}
for (auto& file : ba2Col)
{
readVFS(makeBsaArchive(file.second), file.second, quiet);
for (auto& file : collection)
{
try
{
readVFS(VFS::makeBsaArchive(file.second), file.second, quiet);
}
catch (const std::exception& e)
{
std::cerr << "Failed to read archive file '" << Files::pathToUnicodeString(file.second)
<< "': " << e.what() << std::endl;
}
}
}
}
}

@ -292,7 +292,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(openmw-cs-lib gcov)
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-cs-lib PRIVATE
<boost/program_options/options_description.hpp>

@ -135,7 +135,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform(int stage, Messages& messages
if (topic.mState == CSMWorld::RecordBase::State_Deleted)
{
// if the topic is deleted, we do not need to bother with INFO records.
ESM::Dialogue dialogue = topic.get();
const ESM::Dialogue& dialogue = topic.get();
writer.startRecord(dialogue.sRecordId);
dialogue.save(writer, true);
writer.endRecord(dialogue.sRecordId);
@ -187,6 +187,7 @@ void CSMDoc::WriteDialogueCollectionStage::perform(int stage, Messages& messages
{
ESM::DialInfo info = record.get();
info.mId = record.get().mOriginalId;
info.mData.mType = topic.get().mType;
if (iter == infos.begin())
info.mPrev = ESM::RefId();

@ -452,7 +452,10 @@ std::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseText()
return std::shared_ptr<Node>();
}
return std::make_shared<TextNode>(columnId, text);
auto node = std::make_shared<TextNode>(columnId, text);
if (!node->isValid())
error();
return node;
}
std::shared_ptr<CSMFilter::Node> CSMFilter::Parser::parseValue()

@ -34,6 +34,8 @@ namespace CSMFilter
///< Return a string that represents this node.
///
/// \param numericColumns Use numeric IDs instead of string to represent columns.
bool isValid() { return mRegExp.isValid(); }
};
}

@ -90,6 +90,7 @@ void CSMPrefs::State::declare()
.setTooltip(
"When editing a record, open the view in a new window,"
" rather than docked in the main view.");
declareInt(mValues->mIdTables.mFilterDelay, "Delay before applying a filter (in miliseconds)");
declareCategory("ID Dialogues");
declareBool(mValues->mIdDialogues.mToolbar, "Show toolbar");

@ -138,6 +138,7 @@ namespace CSMPrefs
EnumSettingValue mJumpToAdded{ mIndex, sName, "jump-to-added", sJumpAndSelectValues, 0 };
Settings::SettingValue<bool> mExtendedConfig{ mIndex, sName, "extended-config", false };
Settings::SettingValue<bool> mSubviewNewWindow{ mIndex, sName, "subview-new-window", false };
Settings::SettingValue<int> mFilterDelay{ mIndex, sName, "filter-delay", 500 };
};
struct IdDialoguesCategory : Settings::WithIndex

@ -60,38 +60,38 @@ void CSMTools::EnchantmentCheckStage::perform(int stage, CSMDoc::Messages& messa
}
else
{
std::vector<ESM::ENAMstruct>::const_iterator effect = enchantment.mEffects.mList.begin();
std::vector<ESM::IndexedENAMstruct>::const_iterator effect = enchantment.mEffects.mList.begin();
for (size_t i = 1; i <= enchantment.mEffects.mList.size(); i++)
{
const std::string number = std::to_string(i);
// At the time of writing this effects, attributes and skills are hardcoded
if (effect->mEffectID < 0 || effect->mEffectID > 142)
if (effect->mData.mEffectID < 0 || effect->mData.mEffectID > 142)
{
messages.add(id, "Effect #" + number + " is invalid", "", CSMDoc::Message::Severity_Error);
++effect;
continue;
}
if (effect->mSkill < -1 || effect->mSkill > 26)
if (effect->mData.mSkill < -1 || effect->mData.mSkill > 26)
messages.add(
id, "Effect #" + number + " affected skill is invalid", "", CSMDoc::Message::Severity_Error);
if (effect->mAttribute < -1 || effect->mAttribute > 7)
if (effect->mData.mAttribute < -1 || effect->mData.mAttribute > 7)
messages.add(
id, "Effect #" + number + " affected attribute is invalid", "", CSMDoc::Message::Severity_Error);
if (effect->mRange < 0 || effect->mRange > 2)
if (effect->mData.mRange < 0 || effect->mData.mRange > 2)
messages.add(id, "Effect #" + number + " range is invalid", "", CSMDoc::Message::Severity_Error);
if (effect->mArea < 0)
if (effect->mData.mArea < 0)
messages.add(id, "Effect #" + number + " area is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mDuration < 0)
if (effect->mData.mDuration < 0)
messages.add(id, "Effect #" + number + " duration is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mMagnMin < 0)
if (effect->mData.mMagnMin < 0)
messages.add(
id, "Effect #" + number + " minimum magnitude is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mMagnMax < 0)
if (effect->mData.mMagnMax < 0)
messages.add(
id, "Effect #" + number + " maximum magnitude is negative", "", CSMDoc::Message::Severity_Error);
if (effect->mMagnMin > effect->mMagnMax)
if (effect->mData.mMagnMin > effect->mData.mMagnMax)
messages.add(id, "Effect #" + number + " minimum magnitude is higher than maximum magnitude", "",
CSMDoc::Message::Severity_Error);
++effect;

@ -58,7 +58,12 @@ void CSMTools::MagicEffectCheckStage::perform(int stage, CSMDoc::Messages& messa
return;
ESM::MagicEffect effect = record.get();
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, effect.mId);
CSMWorld::UniversalId id(CSMWorld::UniversalId::Type_MagicEffect, CSMWorld::getRecordId(effect));
if (effect.mData.mSpeed <= 0.0f)
{
messages.add(id, "Speed is less than or equal to zero", "", CSMDoc::Message::Severity_Error);
}
if (effect.mDescription.empty())
{

@ -971,7 +971,7 @@ namespace CSMWorld
void set(Record<ESXRecordT>& record, const QVariant& data) override
{
ESXRecordT record2 = record.get();
record2.mScale = data.toFloat();
record2.mScale = std::clamp(data.toFloat(), 0.5f, 2.0f);
record.setModified(record2);
}
@ -1136,8 +1136,8 @@ namespace CSMWorld
template <typename ESXRecordT>
struct TeleportColumn : public Column<ESXRecordT>
{
TeleportColumn()
: Column<ESXRecordT>(Columns::ColumnId_Teleport, ColumnBase::Display_Boolean)
TeleportColumn(int flags)
: Column<ESXRecordT>(Columns::ColumnId_Teleport, ColumnBase::Display_Boolean, flags)
{
}
@ -1165,6 +1165,8 @@ namespace CSMWorld
QVariant get(const Record<ESXRecordT>& record) const override
{
if (!record.get().mTeleport)
return QVariant();
return QString::fromUtf8(record.get().mDestCell.c_str());
}
@ -1182,6 +1184,26 @@ namespace CSMWorld
bool isUserEditable() const override { return true; }
};
template <typename ESXRecordT>
struct IsLockedColumn : public Column<ESXRecordT>
{
IsLockedColumn(int flags)
: Column<ESXRecordT>(Columns::ColumnId_IsLocked, ColumnBase::Display_Boolean, flags)
{
}
QVariant get(const Record<ESXRecordT>& record) const override { return record.get().mIsLocked; }
void set(Record<ESXRecordT>& record, const QVariant& data) override
{
ESXRecordT record2 = record.get();
record2.mIsLocked = data.toBool();
record.setModified(record2);
}
bool isEditable() const override { return true; }
};
template <typename ESXRecordT>
struct LockLevelColumn : public Column<ESXRecordT>
{
@ -1190,7 +1212,12 @@ namespace CSMWorld
{
}
QVariant get(const Record<ESXRecordT>& record) const override { return record.get().mLockLevel; }
QVariant get(const Record<ESXRecordT>& record) const override
{
if (record.get().mIsLocked)
return record.get().mLockLevel;
return QVariant();
}
void set(Record<ESXRecordT>& record, const QVariant& data) override
{
@ -1212,7 +1239,9 @@ namespace CSMWorld
QVariant get(const Record<ESXRecordT>& record) const override
{
return QString::fromUtf8(record.get().mKey.getRefIdString().c_str());
if (record.get().mIsLocked)
return QString::fromUtf8(record.get().mKey.getRefIdString().c_str());
return QVariant();
}
void set(Record<ESXRecordT>& record, const QVariant& data) override
@ -1282,17 +1311,21 @@ namespace CSMWorld
{
ESM::Position ESXRecordT::*mPosition;
int mIndex;
bool mIsDoor;
PosColumn(ESM::Position ESXRecordT::*position, int index, bool door)
: Column<ESXRecordT>((door ? Columns::ColumnId_DoorPositionXPos : Columns::ColumnId_PositionXPos) + index,
ColumnBase::Display_Float)
, mPosition(position)
, mIndex(index)
, mIsDoor(door)
{
}
QVariant get(const Record<ESXRecordT>& record) const override
{
if (!record.get().mTeleport && mIsDoor)
return QVariant();
const ESM::Position& position = record.get().*mPosition;
return position.pos[mIndex];
}
@ -1316,17 +1349,21 @@ namespace CSMWorld
{
ESM::Position ESXRecordT::*mPosition;
int mIndex;
bool mIsDoor;
RotColumn(ESM::Position ESXRecordT::*position, int index, bool door)
: Column<ESXRecordT>((door ? Columns::ColumnId_DoorPositionXRot : Columns::ColumnId_PositionXRot) + index,
ColumnBase::Display_Double)
, mPosition(position)
, mIndex(index)
, mIsDoor(door)
{
}
QVariant get(const Record<ESXRecordT>& record) const override
{
if (!record.get().mTeleport && mIsDoor)
return QVariant();
const ESM::Position& position = record.get().*mPosition;
return osg::RadiansToDegrees(position.rot[mIndex]);
}
@ -2052,6 +2089,26 @@ namespace CSMWorld
bool isEditable() const override { return true; }
};
template <typename ESXRecordT>
struct ProjectileSpeedColumn : public Column<ESXRecordT>
{
ProjectileSpeedColumn()
: Column<ESXRecordT>(Columns::ColumnId_ProjectileSpeed, ColumnBase::Display_Float)
{
}
QVariant get(const Record<ESXRecordT>& record) const override { return record.get().mData.mSpeed; }
void set(Record<ESXRecordT>& record, const QVariant& data) override
{
ESXRecordT record2 = record.get();
record2.mData.mSpeed = data.toFloat();
record.setModified(record2);
}
bool isEditable() const override { return true; }
};
template <typename ESXRecordT>
struct SchoolColumn : public Column<ESXRecordT>
{

@ -57,8 +57,10 @@ namespace CSMWorld
{ ColumnId_Charges, "Charges" },
{ ColumnId_Enchantment, "Enchantment" },
{ ColumnId_StackCount, "Count" },
{ ColumnId_GoldValue, "Value" },
{ ColumnId_Teleport, "Teleport" },
{ ColumnId_TeleportCell, "Teleport Cell" },
{ ColumnId_IsLocked, "Locked" },
{ ColumnId_LockLevel, "Lock Level" },
{ ColumnId_Key, "Key" },
{ ColumnId_Trap, "Trap" },
@ -235,6 +237,7 @@ namespace CSMWorld
{ ColumnId_RegionSounds, "Sounds" },
{ ColumnId_SoundName, "Sound Name" },
{ ColumnId_SoundChance, "Chance" },
{ ColumnId_SoundProbability, "Probability" },
{ ColumnId_FactionReactions, "Reactions" },
{ ColumnId_FactionRanks, "Ranks" },
@ -376,6 +379,7 @@ namespace CSMWorld
{ ColumnId_Blocked, "Blocked" },
{ ColumnId_LevelledCreatureId, "Levelled Creature" },
{ ColumnId_ProjectileSpeed, "Projectile Speed" },
// end marker
{ -1, 0 },

@ -349,6 +349,14 @@ namespace CSMWorld
ColumnId_SelectionGroupObjects = 316,
ColumnId_SoundProbability = 317,
ColumnId_IsLocked = 318,
ColumnId_ProjectileSpeed = 319,
ColumnId_GoldValue = 320,
// Allocated to a separate value range, so we don't get a collision should we ever need
// to extend the number of use values.
ColumnId_UseValue1 = 0x10000,

@ -161,7 +161,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
defines["radialFog"] = "0";
defines["lightingModel"] = "0";
defines["reverseZ"] = "0";
defines["refraction_enabled"] = "0";
defines["waterRefraction"] = "0";
for (const auto& define : shadowDefines)
defines[define.first] = define.second;
mResourceSystem->getSceneManager()->getShaderManager().setGlobalDefines(defines);
@ -301,8 +301,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mRegions.addColumn(new NestedParentColumn<ESM::Region>(Columns::ColumnId_RegionWeather));
index = mRegions.getColumns() - 1;
mRegions.addAdapter(std::make_pair(&mRegions.getColumn(index), new RegionWeatherAdapter()));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn(Columns::ColumnId_WeatherName, ColumnBase::Display_String, false));
mRegions.getNestableColumn(index)->addColumn(new NestedChildColumn(
Columns::ColumnId_WeatherName, ColumnBase::Display_String, ColumnBase::Flag_Dialogue, false));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn(Columns::ColumnId_WeatherChance, ColumnBase::Display_UnsignedInteger8));
// Region Sounds
@ -313,6 +313,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
new NestedChildColumn(Columns::ColumnId_SoundName, ColumnBase::Display_Sound));
mRegions.getNestableColumn(index)->addColumn(
new NestedChildColumn(Columns::ColumnId_SoundChance, ColumnBase::Display_UnsignedInteger8));
mRegions.getNestableColumn(index)->addColumn(new NestedChildColumn(
Columns::ColumnId_SoundProbability, ColumnBase::Display_String, ColumnBase::Flag_Dialogue, false));
mBirthsigns.addColumn(new StringIdColumn<ESM::BirthSign>);
mBirthsigns.addColumn(new RecordStateColumn<ESM::BirthSign>);
@ -500,6 +502,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mMagicEffects.addColumn(new FixedRecordTypeColumn<ESM::MagicEffect>(UniversalId::Type_MagicEffect));
mMagicEffects.addColumn(new SchoolColumn<ESM::MagicEffect>);
mMagicEffects.addColumn(new BaseCostColumn<ESM::MagicEffect>);
mMagicEffects.addColumn(new ProjectileSpeedColumn<ESM::MagicEffect>);
mMagicEffects.addColumn(new EffectTextureColumn<ESM::MagicEffect>(Columns::ColumnId_Icon));
mMagicEffects.addColumn(new EffectTextureColumn<ESM::MagicEffect>(Columns::ColumnId_Particle));
mMagicEffects.addColumn(new EffectObjectColumn<ESM::MagicEffect>(Columns::ColumnId_CastingObject));
@ -510,6 +513,7 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mMagicEffects.addColumn(new EffectSoundColumn<ESM::MagicEffect>(Columns::ColumnId_HitSound));
mMagicEffects.addColumn(new EffectSoundColumn<ESM::MagicEffect>(Columns::ColumnId_AreaSound));
mMagicEffects.addColumn(new EffectSoundColumn<ESM::MagicEffect>(Columns::ColumnId_BoltSound));
mMagicEffects.addColumn(
new FlagColumn<ESM::MagicEffect>(Columns::ColumnId_AllowSpellmaking, ESM::MagicEffect::AllowSpellmaking));
mMagicEffects.addColumn(
@ -589,7 +593,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mRefs.addColumn(new ChargesColumn<CellRef>);
mRefs.addColumn(new EnchantmentChargesColumn<CellRef>);
mRefs.addColumn(new StackSizeColumn<CellRef>);
mRefs.addColumn(new TeleportColumn<CellRef>);
mRefs.addColumn(new TeleportColumn<CellRef>(
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mRefs.addColumn(new TeleportCellColumn<CellRef>);
mRefs.addColumn(new PosColumn<CellRef>(&CellRef::mDoorDest, 0, true));
mRefs.addColumn(new PosColumn<CellRef>(&CellRef::mDoorDest, 1, true));
@ -597,6 +602,8 @@ CSMWorld::Data::Data(ToUTF8::FromType encoding, const Files::PathContainer& data
mRefs.addColumn(new RotColumn<CellRef>(&CellRef::mDoorDest, 0, true));
mRefs.addColumn(new RotColumn<CellRef>(&CellRef::mDoorDest, 1, true));
mRefs.addColumn(new RotColumn<CellRef>(&CellRef::mDoorDest, 2, true));
mRefs.addColumn(new IsLockedColumn<CellRef>(
ColumnBase::Flag_Table | ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_Refresh));
mRefs.addColumn(new LockLevelColumn<CellRef>);
mRefs.addColumn(new KeyColumn<CellRef>);
mRefs.addColumn(new TrapColumn<CellRef>);

@ -63,9 +63,18 @@ bool CSMWorld::IdTableProxyModel::filterAcceptsRow(int sourceRow, const QModelIn
CSMWorld::IdTableProxyModel::IdTableProxyModel(QObject* parent)
: QSortFilterProxyModel(parent)
, mFilterTimer{ new QTimer(this) }
, mSourceModel(nullptr)
{
setSortCaseSensitivity(Qt::CaseInsensitive);
mFilterTimer->setSingleShot(true);
int intervalSetting = CSMPrefs::State::get()["ID Tables"]["filter-delay"].toInt();
mFilterTimer->setInterval(intervalSetting);
connect(&CSMPrefs::State::get(), &CSMPrefs::State::settingChanged, this,
[this](const CSMPrefs::Setting* setting) { this->settingChanged(setting); });
connect(mFilterTimer.get(), &QTimer::timeout, this, [this]() { this->timerTimeout(); });
}
QModelIndex CSMWorld::IdTableProxyModel::getModelIndex(const std::string& id, int column) const
@ -87,10 +96,8 @@ void CSMWorld::IdTableProxyModel::setSourceModel(QAbstractItemModel* model)
void CSMWorld::IdTableProxyModel::setFilter(const std::shared_ptr<CSMFilter::Node>& filter)
{
beginResetModel();
mFilter = filter;
updateColumnMap();
endResetModel();
mAwaitingFilter = filter;
mFilterTimer->start();
}
bool CSMWorld::IdTableProxyModel::lessThan(const QModelIndex& left, const QModelIndex& right) const
@ -131,6 +138,26 @@ void CSMWorld::IdTableProxyModel::refreshFilter()
}
}
void CSMWorld::IdTableProxyModel::timerTimeout()
{
if (mAwaitingFilter)
{
beginResetModel();
mFilter = mAwaitingFilter;
updateColumnMap();
endResetModel();
mAwaitingFilter.reset();
}
}
void CSMWorld::IdTableProxyModel::settingChanged(const CSMPrefs::Setting* setting)
{
if (*setting == "ID Tables/filter-delay")
{
mFilterTimer->setInterval(setting->toInt());
}
}
void CSMWorld::IdTableProxyModel::sourceRowsInserted(const QModelIndex& parent, int /*start*/, int end)
{
refreshFilter();

@ -10,6 +10,9 @@
#include <QModelIndex>
#include <QSortFilterProxyModel>
#include <QString>
#include <QTimer>
#include "../prefs/state.hpp"
#include "columns.hpp"
@ -29,6 +32,8 @@ namespace CSMWorld
Q_OBJECT
std::shared_ptr<CSMFilter::Node> mFilter;
std::unique_ptr<QTimer> mFilterTimer;
std::shared_ptr<CSMFilter::Node> mAwaitingFilter;
std::map<int, int> mColumnMap; // column ID, column index in this model (or -1)
// Cache of enum values for enum columns (e.g. Modified, Record Type).
@ -68,6 +73,10 @@ namespace CSMWorld
virtual void sourceDataChanged(const QModelIndex& topLeft, const QModelIndex& bottomRight);
void timerTimeout();
void settingChanged(const CSMPrefs::Setting* setting);
signals:
void rowAdded(const std::string& id);

@ -38,7 +38,6 @@ namespace CSMWorld
point.mZ = 0;
point.mAutogenerated = 0;
point.mConnectionNum = 0;
point.mUnknown = 0;
points.insert(points.begin() + position, point);
pathgrid.mData.mPoints = pathgrid.mPoints.size();
@ -414,20 +413,32 @@ namespace CSMWorld
QVariant RegionSoundListAdapter::getData(const Record<ESM::Region>& record, int subRowIndex, int subColIndex) const
{
ESM::Region region = record.get();
const ESM::Region& region = record.get();
std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;
const std::vector<ESM::Region::SoundRef>& soundList = region.mSoundList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(soundList.size()))
const size_t index = static_cast<size_t>(subRowIndex);
if (subRowIndex < 0 || index >= soundList.size())
throw std::runtime_error("index out of range");
ESM::Region::SoundRef soundRef = soundList[subRowIndex];
const ESM::Region::SoundRef& soundRef = soundList[subRowIndex];
switch (subColIndex)
{
case 0:
return QString(soundRef.mSound.getRefIdString().c_str());
case 1:
return soundRef.mChance;
case 2:
{
float probability = 1.f;
for (size_t i = 0; i < index; ++i)
{
const float p = std::min(soundList[i].mChance / 100.f, 1.f);
probability *= 1.f - p;
}
probability *= std::min(soundRef.mChance / 100.f, 1.f) * 100.f;
return QString("%1%").arg(probability, 0, 'f', 2);
}
default:
throw std::runtime_error("Region sounds subcolumn index out of range");
}
@ -463,7 +474,7 @@ namespace CSMWorld
int RegionSoundListAdapter::getColumnsCount(const Record<ESM::Region>& record) const
{
return 2;
return 3;
}
int RegionSoundListAdapter::getRowsCount(const Record<ESM::Region>& record) const
@ -996,7 +1007,10 @@ namespace CSMWorld
case 5:
{
if (isInterior && interiorWater)
{
cell.mWater = value.toFloat();
cell.setHasWaterHeightSub(true);
}
else
return; // return without saving
break;

@ -255,20 +255,22 @@ namespace CSMWorld
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
std::vector<ESM::IndexedENAMstruct>& effectsList = magic.mEffects.mList;
// blank row
ESM::ENAMstruct effect;
effect.mEffectID = 0;
effect.mSkill = -1;
effect.mAttribute = -1;
effect.mRange = 0;
effect.mArea = 0;
effect.mDuration = 0;
effect.mMagnMin = 0;
effect.mMagnMax = 0;
ESM::IndexedENAMstruct effect;
effect.mIndex = position;
effect.mData.mEffectID = 0;
effect.mData.mSkill = -1;
effect.mData.mAttribute = -1;
effect.mData.mRange = 0;
effect.mData.mArea = 0;
effect.mData.mDuration = 0;
effect.mData.mMagnMin = 0;
effect.mData.mMagnMax = 0;
effectsList.insert(effectsList.begin() + position, effect);
magic.mEffects.updateIndexes();
record.setModified(magic);
}
@ -277,12 +279,13 @@ namespace CSMWorld
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
std::vector<ESM::IndexedENAMstruct>& effectsList = magic.mEffects.mList;
if (rowToRemove < 0 || rowToRemove >= static_cast<int>(effectsList.size()))
throw std::runtime_error("index out of range");
effectsList.erase(effectsList.begin() + rowToRemove);
magic.mEffects.updateIndexes();
record.setModified(magic);
}
@ -292,7 +295,7 @@ namespace CSMWorld
ESXRecordT magic = record.get();
magic.mEffects.mList
= static_cast<const NestedTableWrapper<std::vector<ESM::ENAMstruct>>&>(nestedTable).mNestedTable;
= static_cast<const NestedTableWrapper<std::vector<ESM::IndexedENAMstruct>>&>(nestedTable).mNestedTable;
record.setModified(magic);
}
@ -300,19 +303,19 @@ namespace CSMWorld
NestedTableWrapperBase* table(const Record<ESXRecordT>& record) const override
{
// deleted by dtor of NestedTableStoring
return new NestedTableWrapper<std::vector<ESM::ENAMstruct>>(record.get().mEffects.mList);
return new NestedTableWrapper<std::vector<ESM::IndexedENAMstruct>>(record.get().mEffects.mList);
}
QVariant getData(const Record<ESXRecordT>& record, int subRowIndex, int subColIndex) const override
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
std::vector<ESM::IndexedENAMstruct>& effectsList = magic.mEffects.mList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(effectsList.size()))
throw std::runtime_error("index out of range");
ESM::ENAMstruct effect = effectsList[subRowIndex];
ESM::ENAMstruct effect = effectsList[subRowIndex].mData;
switch (subColIndex)
{
case 0:
@ -374,12 +377,12 @@ namespace CSMWorld
{
ESXRecordT magic = record.get();
std::vector<ESM::ENAMstruct>& effectsList = magic.mEffects.mList;
std::vector<ESM::IndexedENAMstruct>& effectsList = magic.mEffects.mList;
if (subRowIndex < 0 || subRowIndex >= static_cast<int>(effectsList.size()))
throw std::runtime_error("index out of range");
ESM::ENAMstruct effect = effectsList[subRowIndex];
ESM::ENAMstruct effect = effectsList[subRowIndex].mData;
switch (subColIndex)
{
case 0:
@ -438,7 +441,7 @@ namespace CSMWorld
throw std::runtime_error("Magic Effects subcolumn index out of range");
}
magic.mEffects.mList[subRowIndex] = effect;
magic.mEffects.mList[subRowIndex].mData = effect;
record.setModified(magic);
}

@ -19,6 +19,11 @@ namespace CSMWorld
State mState;
explicit RecordBase(State state)
: mState(state)
{
}
virtual ~RecordBase() = default;
virtual std::unique_ptr<RecordBase> clone() const = 0;
@ -69,21 +74,18 @@ namespace CSMWorld
template <typename ESXRecordT>
Record<ESXRecordT>::Record()
: mBase()
: RecordBase(State_BaseOnly)
, mBase()
, mModified()
{
}
template <typename ESXRecordT>
Record<ESXRecordT>::Record(State state, const ESXRecordT* base, const ESXRecordT* modified)
: RecordBase(state)
, mBase(base == nullptr ? ESXRecordT{} : *base)
, mModified(modified == nullptr ? ESXRecordT{} : *modified)
{
if (base)
mBase = *base;
if (modified)
mModified = *modified;
this->mState = state;
}
template <typename ESXRecordT>

@ -33,7 +33,7 @@ QVariant CSMWorld::PotionRefIdAdapter::getData(const RefIdColumn* column, const
data.getRecord(RefIdData::LocalIndex(index, UniversalId::Type_Potion)));
if (column == mAutoCalc)
return record.get().mData.mAutoCalc != 0;
return record.get().mData.mFlags & ESM::Potion::Autocalc;
// to show nested tables in dialogue subview, see IdTree::hasChildren()
if (column == mColumns.mEffects)
@ -51,7 +51,7 @@ void CSMWorld::PotionRefIdAdapter::setData(
ESM::Potion potion = record.get();
if (column == mAutoCalc)
potion.mData.mAutoCalc = value.toInt();
potion.mData.mFlags = value.toBool();
else
{
InventoryRefIdAdapter<ESM::Potion>::setData(column, data, index, value);

@ -97,7 +97,7 @@ CSMWorld::RefIdCollection::RefIdCollection()
inventoryColumns.mIcon = &mColumns.back();
mColumns.emplace_back(Columns::ColumnId_Weight, ColumnBase::Display_Float);
inventoryColumns.mWeight = &mColumns.back();
mColumns.emplace_back(Columns::ColumnId_StackCount, ColumnBase::Display_Integer);
mColumns.emplace_back(Columns::ColumnId_GoldValue, ColumnBase::Display_Integer);
inventoryColumns.mValue = &mColumns.back();
IngredientColumns ingredientColumns(inventoryColumns);

@ -1,7 +1,9 @@
#include "regionmap.hpp"
#include <QApplication>
#include <QBrush>
#include <QModelIndex>
#include <QPalette>
#include <QSize>
#include <QVariant>
@ -21,20 +23,33 @@
#include "data.hpp"
#include "universalid.hpp"
CSMWorld::RegionMap::CellDescription::CellDescription()
: mDeleted(false)
namespace CSMWorld
{
float getLandHeight(const CSMWorld::Cell& cell, CSMWorld::Data& data)
{
const IdCollection<Land>& lands = data.getLand();
int landIndex = lands.searchId(cell.mId);
if (landIndex == -1)
return 0.0f;
// If any part of land is above water, returns > 0 - otherwise returns < 0
const Land& land = lands.getRecord(landIndex).get();
if (land.getLandData())
return land.getLandData()->mMaxHeight - cell.mWater;
return 0.0f;
}
}
CSMWorld::RegionMap::CellDescription::CellDescription(const Record<Cell>& cell)
CSMWorld::RegionMap::CellDescription::CellDescription(const Record<Cell>& cell, float landHeight)
{
const Cell& cell2 = cell.get();
if (!cell2.isExterior())
throw std::logic_error("Interior cell in region map");
mMaxLandHeight = landHeight;
mDeleted = cell.isDeleted();
mRegion = cell2.mRegion;
mName = cell2.mName;
}
@ -92,7 +107,7 @@ void CSMWorld::RegionMap::buildMap()
if (cell2.isExterior())
{
CellDescription description(cell);
CellDescription description(cell, getLandHeight(cell2, mData));
CellCoordinates index = getIndex(cell2);
@ -140,7 +155,7 @@ void CSMWorld::RegionMap::addCells(int start, int end)
{
CellCoordinates index = getIndex(cell2);
CellDescription description(cell);
CellDescription description(cell, getLandHeight(cell.get(), mData));
addCell(index, description);
}
@ -335,10 +350,11 @@ QVariant CSMWorld::RegionMap::data(const QModelIndex& index, int role) const
auto iter = mColours.find(cell->second.mRegion);
if (iter != mColours.end())
return QBrush(QColor(iter->second & 0xff, (iter->second >> 8) & 0xff, (iter->second >> 16) & 0xff));
return QBrush(QColor(iter->second & 0xff, (iter->second >> 8) & 0xff, (iter->second >> 16) & 0xff),
cell->second.mMaxLandHeight > 0 ? Qt::SolidPattern : Qt::CrossPattern);
if (cell->second.mRegion.empty())
return QBrush(Qt::Dense6Pattern); // no region
if (cell->second.mRegion.empty()) // no region
return QBrush(cell->second.mMaxLandHeight > 0 ? Qt::Dense3Pattern : Qt::Dense6Pattern);
return QBrush(Qt::red, Qt::Dense6Pattern); // invalid region
}

@ -40,13 +40,12 @@ namespace CSMWorld
private:
struct CellDescription
{
float mMaxLandHeight;
bool mDeleted;
ESM::RefId mRegion;
std::string mName;
CellDescription();
CellDescription(const Record<Cell>& cell);
CellDescription(const Record<Cell>& cell, float landHeight);
};
Data& mData;

@ -58,7 +58,8 @@ namespace CSVRender
InstanceSelectionMode::~InstanceSelectionMode()
{
mParentNode->removeChild(mBaseNode);
if (mBaseNode)
mParentNode->removeChild(mBaseNode);
}
void InstanceSelectionMode::setDragStart(const osg::Vec3d& dragStart)

@ -8,7 +8,7 @@
#include <osg/Vec4f>
#include <osg/ref_ptr>
#include <components/esm/defs.hpp>
#include <components/esm/position.hpp>
#include <components/esm/refid.hpp>
#include "tagbase.hpp"

@ -48,6 +48,7 @@
#include <components/debug/debuglog.hpp>
#include <components/resource/resourcesystem.hpp>
#include <components/resource/scenemanager.hpp>
#include <components/sceneutil/glextensions.hpp>
#include <components/sceneutil/lightmanager.hpp>
#include "../widget/scenetoolmode.hpp"
@ -76,6 +77,8 @@ namespace CSVRender
= new osgViewer::GraphicsWindowEmbedded(0, 0, width(), height());
mWidget->setGraphicsWindowEmbedded(window);
mRenderer->setRealizeOperation(new SceneUtil::GetGLExtensionsOperation());
int frameRateLimit = CSMPrefs::get()["Rendering"]["framerate-limit"].toInt();
mRenderer->setRunMaxFrameRate(frameRateLimit);
mRenderer->setUseConfigureAffinity(false);

@ -2,17 +2,6 @@
#include <components/misc/strings/lower.hpp>
bool CSVWorld::IdValidator::isValid(const QChar& c, bool first) const
{
if (c.isLetter() || c == '_')
return true;
if (!first && (c.isDigit() || c.isSpace()))
return true;
return false;
}
CSVWorld::IdValidator::IdValidator(bool relaxed, QObject* parent)
: QValidator(parent)
, mRelaxed(relaxed)
@ -92,7 +81,7 @@ QValidator::State CSVWorld::IdValidator::validate(QString& input, int& pos) cons
{
prevScope = false;
if (!isValid(*iter, first))
if (!iter->isPrint())
return QValidator::Invalid;
}
}

@ -13,9 +13,6 @@ namespace CSVWorld
std::string mNamespace;
mutable std::string mError;
private:
bool isValid(const QChar& c, bool first) const;
public:
IdValidator(bool relaxed = false, QObject* parent = nullptr);
///< \param relaxed Relaxed rules for IDs that also functino as user visible text

@ -224,6 +224,10 @@ CSVWorld::RegionMap::RegionMap(const CSMWorld::UniversalId& universalId, CSMDoc:
addAction(mViewInTableAction);
setAcceptDrops(true);
// Make columns square incase QSizeHint doesnt apply
for (int column = 0; column < this->model()->columnCount(); ++column)
this->setColumnWidth(column, this->rowHeight(0));
}
void CSVWorld::RegionMap::selectAll()
@ -358,12 +362,23 @@ std::vector<CSMWorld::UniversalId> CSVWorld::RegionMap::getDraggedRecords() cons
return ids;
}
void CSVWorld::RegionMap::dragMoveEvent(QDragMoveEvent* event)
{
const CSMWorld::TableMimeData* mime = dynamic_cast<const CSMWorld::TableMimeData*>(event->mimeData());
if (mime != nullptr && (mime->holdsType(CSMWorld::UniversalId::Type_Region)))
{
event->accept();
return;
}
event->ignore();
}
void CSVWorld::RegionMap::dropEvent(QDropEvent* event)
{
QModelIndex index = indexAt(event->pos());
bool exists = QTableView::model()->data(index, Qt::BackgroundRole) != QBrush(Qt::DiagCrossPattern);
if (!index.isValid() || !exists)
{
return;

@ -59,6 +59,8 @@ namespace CSVWorld
void mouseMoveEvent(QMouseEvent* event) override;
void dragMoveEvent(QDragMoveEvent* event) override;
void dropEvent(QDropEvent* event) override;
public:

@ -26,7 +26,7 @@ if (BUILD_WITH_CODE_COVERAGE)
target_link_libraries(openmw-cs-tests PRIVATE gcov)
endif()
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw-cs-tests PRIVATE
<gtest/gtest.h>
)

@ -63,8 +63,8 @@ add_openmw_dir (mwlua
luamanagerimp object objectlists userdataserializer luaevents engineevents objectvariant
context menuscripts globalscripts localscripts playerscripts luabindings objectbindings cellbindings
mwscriptbindings camerabindings vfsbindings uibindings soundbindings inputbindings nearbybindings
postprocessingbindings stats debugbindings corebindings worldbindings worker magicbindings factionbindings
classbindings itemdata inputprocessor animationbindings birthsignbindings
postprocessingbindings stats recordstore debugbindings corebindings worldbindings worker magicbindings factionbindings
classbindings itemdata inputprocessor animationbindings birthsignbindings racebindings markupbindings
types/types types/door types/item types/actor types/container types/lockable types/weapon types/npc
types/creature types/player types/activator types/book types/lockpick types/probe types/apparatus
types/potion types/ingredient types/misc types/repair types/armor types/light types/static
@ -103,7 +103,7 @@ add_openmw_dir (mwmechanics
drawstate spells activespells npcstats aipackage aisequence aipursue alchemy aiwander aitravel aifollow aiavoiddoor aibreathe
aicast aiescort aiface aiactivate aicombat recharge repair enchanting pathfinding pathgrid security spellcasting spellresistance
disease pickpocket levelledlist combat steering obstacle autocalcspell difficultyscaling aicombataction summoning
character actors objects aistate trading weaponpriority spellpriority weapontype spellutil
character actors objects aistate weaponpriority spellpriority weapontype spellutil
spelleffects
)
@ -161,7 +161,7 @@ target_link_libraries(openmw
components
)
if (MSVC)
if (MSVC AND PRECOMPILE_HEADERS_WITH_MSVC)
target_precompile_headers(openmw PRIVATE
<boost/program_options/options_description.hpp>

@ -31,6 +31,7 @@
#include <components/stereo/multiview.hpp>
#include <components/stereo/stereomanager.hpp>
#include <components/sceneutil/glextensions.hpp>
#include <components/sceneutil/workqueue.hpp>
#include <components/files/configurationmanager.hpp>
@ -600,6 +601,7 @@ void OMW::Engine::createWindow()
mViewer->setRealizeOperation(realizeOperations);
osg::ref_ptr<IdentifyOpenGLOperation> identifyOp = new IdentifyOpenGLOperation();
realizeOperations->add(identifyOp);
realizeOperations->add(new SceneUtil::GetGLExtensionsOperation());
if (Debug::shouldDebugOpenGL())
realizeOperations->add(new Debug::EnableGLDebugOperation());
@ -780,13 +782,13 @@ void OMW::Engine::prepareEngine()
// gui needs our shaders path before everything else
mResourceSystem->getSceneManager()->setShaderPath(mResDir / "shaders");
osg::ref_ptr<osg::GLExtensions> exts = osg::GLExtensions::Get(0, false);
bool shadersSupported = exts && (exts->glslLanguageVersion >= 1.2f);
osg::GLExtensions& exts = SceneUtil::getGLExtensions();
bool shadersSupported = exts.glslLanguageVersion >= 1.2f;
#if OSG_VERSION_LESS_THAN(3, 6, 6)
// hack fix for https://github.com/openscenegraph/OpenSceneGraph/issues/1028
if (exts)
exts->glRenderbufferStorageMultisampleCoverageNV = nullptr;
if (!osg::isGLExtensionSupported(exts.contextID, "NV_framebuffer_multisample_coverage"))
exts.glRenderbufferStorageMultisampleCoverageNV = nullptr;
#endif
osg::ref_ptr<osg::Group> guiRoot = new osg::Group;
@ -963,17 +965,17 @@ void OMW::Engine::go()
}
// Setup profiler
osg::ref_ptr<Resource::Profiler> statshandler = new Resource::Profiler(stats.is_open(), mVFS.get());
osg::ref_ptr<Resource::Profiler> statsHandler = new Resource::Profiler(stats.is_open(), *mVFS);
initStatsHandler(*statshandler);
initStatsHandler(*statsHandler);
mViewer->addEventHandler(statshandler);
mViewer->addEventHandler(statsHandler);
osg::ref_ptr<Resource::StatsHandler> resourceshandler = new Resource::StatsHandler(stats.is_open(), mVFS.get());
mViewer->addEventHandler(resourceshandler);
osg::ref_ptr<Resource::StatsHandler> resourcesHandler = new Resource::StatsHandler(stats.is_open(), *mVFS);
mViewer->addEventHandler(resourcesHandler);
if (stats.is_open())
Resource::CollectStatistics(mViewer);
Resource::collectStatistics(*mViewer);
// Start the game
if (!mSaveGameFile.empty())

@ -2,6 +2,7 @@
#include <components/fallback/fallback.hpp>
#include <components/fallback/validate.hpp>
#include <components/files/configurationmanager.hpp>
#include <components/misc/osgpluginchecker.hpp>
#include <components/misc/rng.hpp>
#include <components/platform/platform.hpp>
#include <components/version/version.hpp>
@ -228,6 +229,9 @@ int runApplication(int argc, char* argv[])
if (parseOptions(argc, argv, *engine, cfgMgr))
{
if (!Misc::checkRequiredOSGPluginsArePresent())
return 1;
engine->go();
}

@ -265,7 +265,7 @@ namespace MWBase
virtual bool isReadyToBlock(const MWWorld::Ptr& ptr) const = 0;
virtual bool isAttackingOrSpell(const MWWorld::Ptr& ptr) const = 0;
virtual void castSpell(const MWWorld::Ptr& ptr, const ESM::RefId& spellId, bool manualSpell) = 0;
virtual void castSpell(const MWWorld::Ptr& ptr, const ESM::RefId& spellId, bool scriptedSpell) = 0;
virtual void processChangedSettings(const std::set<std::pair<std::string, std::string>>& settings) = 0;

@ -6,6 +6,8 @@
#include <string>
#include <string_view>
#include <components/vfs/pathutil.hpp>
#include "../mwsound/type.hpp"
#include "../mwworld/ptr.hpp"
@ -129,11 +131,11 @@ namespace MWBase
/// \param name of the folder that contains the playlist
/// Title music playlist is predefined
virtual void say(const MWWorld::ConstPtr& reference, const std::string& filename) = 0;
virtual void say(const MWWorld::ConstPtr& reference, VFS::Path::NormalizedView filename) = 0;
///< Make an actor say some text.
/// \param filename name of a sound file in the VFS
virtual void say(const std::string& filename) = 0;
virtual void say(VFS::Path::NormalizedView filename) = 0;
///< Say some text, without an actor ref
/// \param filename name of a sound file in the VFS

@ -136,6 +136,7 @@ namespace MWBase
virtual bool isConsoleMode() const = 0;
virtual bool isPostProcessorHudVisible() const = 0;
virtual bool isSettingsWindowVisible() const = 0;
virtual bool isInteractiveMessageBoxActive() const = 0;
virtual void toggleVisible(MWGui::GuiWindow wnd) = 0;
@ -157,7 +158,6 @@ namespace MWBase
virtual MWGui::ConfirmationDialog* getConfirmationDialog() = 0;
virtual MWGui::TradeWindow* getTradeWindow() = 0;
virtual MWGui::PostProcessorHud* getPostProcessorHud() = 0;
virtual MWGui::SettingsWindow* getSettingsWindow() = 0;
/// Make the player use an item, while updating GUI state accordingly
virtual void useItem(const MWWorld::Ptr& item, bool force = false) = 0;
@ -202,9 +202,6 @@ namespace MWBase
virtual bool getFullHelp() const = 0;
virtual void setActiveMap(int x, int y, bool interior) = 0;
///< set the indices of the map texture that should be used
/// sets the visibility of the drowning bar
virtual void setDrowningBarVisibility(bool visible) = 0;
@ -293,7 +290,7 @@ namespace MWBase
virtual void setEnemy(const MWWorld::Ptr& enemy) = 0;
virtual int getMessagesCount() const = 0;
virtual std::size_t getMessagesCount() const = 0;
virtual const Translation::Storage& getTranslationDataStorage() const = 0;
@ -345,6 +342,7 @@ namespace MWBase
virtual void toggleConsole() = 0;
virtual void toggleDebugWindow() = 0;
virtual void togglePostProcessorHud() = 0;
virtual void toggleSettingsWindow() = 0;
/// Cycle to next or previous spell
virtual void cycleSpell(bool next) = 0;

@ -183,8 +183,6 @@ namespace MWBase
/// generate a name.
virtual std::string_view getCellName(const MWWorld::Cell& cell) const = 0;
virtual std::string_view getCellName(const ESM::Cell* cell) const = 0;
virtual void removeRefScript(const MWWorld::CellRef* ref) = 0;
//< Remove the script attached to ref from mLocalScripts
@ -463,7 +461,7 @@ namespace MWBase
*/
virtual MWWorld::SpellCastState startSpellCast(const MWWorld::Ptr& actor) = 0;
virtual void castSpell(const MWWorld::Ptr& actor, bool manualSpell = false) = 0;
virtual void castSpell(const MWWorld::Ptr& actor, bool scriptedSpell = false) = 0;
virtual void launchMagicBolt(const ESM::RefId& spellId, const MWWorld::Ptr& caster,
const osg::Vec3f& fallbackDirection, ESM::RefNum item)

@ -104,13 +104,11 @@ namespace MWClass
std::string_view name = getName(ptr);
info.caption = MyGUI::TextIterator::toTagsString(MyGUI::UString(name)) + MWGui::ToolTips::getCountString(count);
std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);
return info;
}

@ -102,8 +102,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -257,8 +257,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.enchant = ref->mBase->mEnchant;

@ -121,8 +121,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.enchant = ref->mBase->mEnchant;

@ -164,8 +164,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.enchant = ref->mBase->mEnchant;

@ -265,10 +265,10 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
if (ptr.getCellRef().getRefId() == "stolen_goods")
text += "\nYou can not use evidence chests";
info.extra += "\nYou cannot use evidence chests";
}
info.text = std::move(text);

@ -316,11 +316,11 @@ namespace MWClass
{
const unsigned char* attack = nullptr;
if (type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop.data();
else if (type == ESM::Weapon::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash.data();
else if (type == ESM::Weapon::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust.data();
if (attack)
{
damage = attack[0] + ((attack[1] - attack[0]) * attackStrength);
@ -591,10 +591,8 @@ namespace MWClass
std::string_view name = getName(ptr);
info.caption = MyGUI::TextIterator::toTagsString(MyGUI::UString(name));
std::string text;
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.text = std::move(text);
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
return info;
}

@ -136,7 +136,7 @@ namespace MWClass
const ESM::MagicEffect* effect = store.get<ESM::MagicEffect>().find(ESM::MagicEffect::Telekinesis);
animation->addSpellCastGlow(
effect, 1); // 1 second glow to match the time taken for a door opening or closing
effect->getColor(), 1); // 1 second glow to match the time taken for a door opening or closing
}
}
@ -290,8 +290,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -117,8 +117,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
MWWorld::Ptr player = MWBase::Environment::get().getWorld()->getPlayerPtr();

@ -173,8 +173,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -118,8 +118,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -163,8 +163,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -635,11 +635,11 @@ namespace MWClass
{
const unsigned char* attack = nullptr;
if (type == ESM::Weapon::AT_Chop)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop;
attack = weapon.get<ESM::Weapon>()->mBase->mData.mChop.data();
else if (type == ESM::Weapon::AT_Slash)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash;
attack = weapon.get<ESM::Weapon>()->mBase->mData.mSlash.data();
else if (type == ESM::Weapon::AT_Thrust)
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust;
attack = weapon.get<ESM::Weapon>()->mBase->mData.mThrust.data();
if (attack)
{
damage = attack[0] + ((attack[1] - attack[0]) * attackStrength);
@ -1118,7 +1118,7 @@ namespace MWClass
}
if (fullHelp)
info.text = MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra = MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
return info;
}

@ -19,6 +19,7 @@
#include "../mwrender/renderinginterface.hpp"
#include "../mwmechanics/alchemy.hpp"
#include "../mwmechanics/spellutil.hpp"
#include "classmodel.hpp"
@ -65,9 +66,7 @@ namespace MWClass
int Potion::getValue(const MWWorld::ConstPtr& ptr) const
{
const MWWorld::LiveCellRef<ESM::Potion>* ref = ptr.get<ESM::Potion>();
return ref->mBase->mData.mValue;
return MWMechanics::getPotionValue(*ptr.get<ESM::Potion>()->mBase);
}
const ESM::RefId& Potion::getUpSoundId(const MWWorld::ConstPtr& ptr) const
@ -101,7 +100,7 @@ namespace MWClass
std::string text;
text += "\n#{sWeight}: " + MWGui::ToolTips::toString(ref->mBase->mData.mWeight);
text += MWGui::ToolTips::getValueString(ref->mBase->mData.mValue, "#{sValue}");
text += MWGui::ToolTips::getValueString(getValue(ptr), "#{sValue}");
info.effects = MWGui::Widgets::MWEffectList::effectListFromESM(&ref->mBase->mEffects);
@ -114,8 +113,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -117,8 +117,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -119,8 +119,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -239,8 +239,8 @@ namespace MWClass
if (MWBase::Environment::get().getWindowManager()->getFullHelp())
{
text += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
text += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
info.extra += MWGui::ToolTips::getCellRefString(ptr.getCellRef());
info.extra += MWGui::ToolTips::getMiscString(ref->mBase->mScript.getRefIdString(), "Script");
}
info.text = std::move(text);

@ -653,7 +653,7 @@ namespace MWDialogue
if (Settings::gui().mSubtitles)
winMgr->messageBox(info->mResponse);
if (!info->mSound.empty())
sndMgr->say(actor, Misc::ResourceHelpers::correctSoundPath(info->mSound));
sndMgr->say(actor, Misc::ResourceHelpers::correctSoundPath(VFS::Path::Normalized(info->mSound)));
if (!info->mResultScript.empty())
executeScript(info->mResultScript, actor);
}

@ -87,7 +87,7 @@ namespace MWDialogue
// some keywords might be longer variations of other keywords, so we definitely need a list of
// candidates the first element in the pair is length of the match, i.e. depth from the first character
// on
std::vector<typename std::pair<int, typename Entry::childen_t::const_iterator>> candidates;
std::vector<typename std::pair<std::ptrdiff_t, typename Entry::childen_t::const_iterator>> candidates;
while ((j + 1) != end)
{
@ -148,11 +148,11 @@ namespace MWDialogue
// resolve overlapping keywords
while (!matches.empty())
{
int longestKeywordSize = 0;
std::size_t longestKeywordSize = 0;
typename std::vector<Match>::iterator longestKeyword = matches.begin();
for (typename std::vector<Match>::iterator it = matches.begin(); it != matches.end(); ++it)
{
int size = it->mEnd - it->mBeg;
std::size_t size = it->mEnd - it->mBeg;
if (size > longestKeywordSize)
{
longestKeywordSize = size;
@ -199,7 +199,7 @@ namespace MWDialogue
void seed_impl(std::string_view keyword, value_t value, size_t depth, Entry& entry)
{
int ch = Misc::StringUtils::toLower(keyword.at(depth));
auto ch = Misc::StringUtils::toLower(keyword.at(depth));
typename Entry::childen_t::iterator j = entry.mChildren.find(ch);

@ -39,7 +39,7 @@ namespace
{
const std::string mText;
const Response mResponses[3];
const std::string mSound;
const VFS::Path::Normalized mSound;
};
Step sGenerateClassSteps(int number)

@ -771,11 +771,6 @@ namespace MWGui
return output.append(matches.front());
}
void Console::onResChange(int width, int height)
{
setCoord(10, 10, width - 10, height / 2);
}
void Console::updateSelectedObjectPtr(const MWWorld::Ptr& currentPtr, const MWWorld::Ptr& newPtr)
{
if (mPtr == currentPtr)

@ -47,8 +47,6 @@ namespace MWGui
void onOpen() override;
void onResChange(int width, int height) override;
// Print a message to the console, in specified color.
void print(const std::string& msg, std::string_view color = MWBase::WindowManager::sConsoleColor_Default);

@ -273,7 +273,7 @@ namespace MWGui
void EnchantingDialog::notifyEffectsChanged()
{
mEffectList.mList = mEffects;
mEffectList.populate(mEffects);
mEnchanting.setEffect(mEffectList);
updateLabels();
}

@ -427,7 +427,7 @@ namespace MWGui
{
// use the icon of the first effect
const ESM::MagicEffect* effect = MWBase::Environment::get().getESMStore()->get<ESM::MagicEffect>().find(
spell->mEffects.mList.front().mEffectID);
spell->mEffects.mList.front().mData.mEffectID);
std::string icon = effect->mIcon;
std::replace(icon.begin(), icon.end(), '/', '\\');
size_t slashPos = icon.rfind('\\');

@ -417,6 +417,8 @@ namespace MWGui
void InventoryWindow::onWindowResize(MyGUI::Window* _sender)
{
WindowBase::clampWindowCoordinates(_sender);
adjustPanes();
const WindowSettingValues settings = getModeSettings(mGuiMode);

@ -246,12 +246,12 @@ namespace MWGui
bool forward = (direction == D_Next || direction == D_Right || direction == D_Down);
int index = found - keyFocusList.begin();
std::ptrdiff_t index{ found - keyFocusList.begin() };
index = forward ? (index + 1) : (index - 1);
if (wrap)
index = (index + keyFocusList.size()) % keyFocusList.size();
else
index = std::clamp<int>(index, 0, keyFocusList.size() - 1);
index = std::clamp<std::ptrdiff_t>(index, 0, keyFocusList.size() - 1);
MyGUI::Widget* next = keyFocusList[index];
int vertdiff = next->getTop() - focus->getTop();

@ -99,7 +99,7 @@ namespace MWGui
}
else if (name == "options")
{
winMgr->getSettingsWindow()->setVisible(true);
winMgr->toggleSettingsWindow();
}
else if (name == "credits")
winMgr->playVideo("mw_credits.bik", true);
@ -212,6 +212,12 @@ namespace MWGui
bool MainMenu::exit()
{
if (MWBase::Environment::get().getWindowManager()->isSettingsWindowVisible())
{
MWBase::Environment::get().getWindowManager()->toggleSettingsWindow();
return false;
}
return MWBase::Environment::get().getStateManager()->getState() == MWBase::StateManager::State_Running;
}

@ -95,6 +95,13 @@ namespace
return std::clamp(
viewingDistanceInCells, Constants::CellGridRadius, Settings::map().mMaxLocalViewingDistance.get());
}
ESM::RefId getCellIdInWorldSpace(const MWWorld::Cell& cell, int x, int y)
{
if (cell.isExterior())
return ESM::Cell::generateIdForCell(true, {}, x, y);
return cell.getId();
}
}
namespace MWGui
@ -170,12 +177,9 @@ namespace MWGui
LocalMapBase::LocalMapBase(
CustomMarkerCollection& markers, MWRender::LocalMap* localMapRender, bool fogOfWarEnabled)
: mLocalMapRender(localMapRender)
, mCurX(0)
, mCurY(0)
, mInterior(false)
, mActiveCell(nullptr)
, mLocalMap(nullptr)
, mCompass(nullptr)
, mChanged(true)
, mFogOfWarToggled(true)
, mFogOfWarEnabled(fogOfWarEnabled)
, mNumCells(1)
@ -231,12 +235,6 @@ namespace MWGui
}
}
void LocalMapBase::setCellPrefix(const std::string& prefix)
{
mPrefix = prefix;
mChanged = true;
}
bool LocalMapBase::toggleFogOfWar()
{
mFogOfWarToggled = !mFogOfWarToggled;
@ -262,8 +260,8 @@ namespace MWGui
{
// normalized cell coordinates
auto mapWidgetSize = getWidgetSize();
return MyGUI::IntPoint(std::round(nX * mapWidgetSize + (mCellDistance + (cellX - mCurX)) * mapWidgetSize),
std::round(nY * mapWidgetSize + (mCellDistance - (cellY - mCurY)) * mapWidgetSize));
return MyGUI::IntPoint(std::round((nX + mCellDistance + cellX - mActiveCell->getGridX()) * mapWidgetSize),
std::round((nY + mCellDistance - cellY + mActiveCell->getGridY()) * mapWidgetSize));
}
MyGUI::IntPoint LocalMapBase::getMarkerPosition(float worldX, float worldY, MarkerUserData& markerPos) const
@ -272,7 +270,7 @@ namespace MWGui
// normalized cell coordinates
float nX, nY;
if (!mInterior)
if (mActiveCell->isExterior())
{
ESM::ExteriorCellLocation cellPos = ESM::positionToExteriorCellLocation(worldX, worldY);
cellIndex.x() = cellPos.mX;
@ -336,7 +334,7 @@ namespace MWGui
std::vector<MyGUI::Widget*>& LocalMapBase::currentDoorMarkersWidgets()
{
return mInterior ? mInteriorDoorMarkerWidgets : mExteriorDoorMarkerWidgets;
return mActiveCell->isExterior() ? mExteriorDoorMarkerWidgets : mInteriorDoorMarkerWidgets;
}
void LocalMapBase::updateCustomMarkers()
@ -344,12 +342,14 @@ namespace MWGui
for (MyGUI::Widget* widget : mCustomMarkerWidgets)
MyGUI::Gui::getInstance().destroyWidget(widget);
mCustomMarkerWidgets.clear();
if (!mActiveCell)
return;
for (int dX = -mCellDistance; dX <= mCellDistance; ++dX)
{
for (int dY = -mCellDistance; dY <= mCellDistance; ++dY)
{
ESM::RefId cellRefId = ESM::Cell::generateIdForCell(!mInterior, mPrefix, mCurX + dX, mCurY + dY);
ESM::RefId cellRefId
= getCellIdInWorldSpace(*mActiveCell, mActiveCell->getGridX() + dX, mActiveCell->getGridY() + dY);
CustomMarkerCollection::RangeType markers = mCustomMarkers.getMarkers(cellRefId);
for (CustomMarkerCollection::ContainerType::const_iterator it = markers.first; it != markers.second;
@ -377,16 +377,25 @@ namespace MWGui
redraw();
}
void LocalMapBase::setActiveCell(const int x, const int y, bool interior)
void LocalMapBase::setActiveCell(const MWWorld::Cell& cell)
{
if (x == mCurX && y == mCurY && mInterior == interior && !mChanged)
if (&cell == mActiveCell)
return; // don't do anything if we're still in the same cell
if (!interior && !(x == mCurX && y == mCurY))
const int x = cell.getGridX();
const int y = cell.getGridY();
if (cell.isExterior())
{
const MyGUI::IntRect intersection
= { std::max(x, mCurX) - mCellDistance, std::max(y, mCurY) - mCellDistance,
std::min(x, mCurX) + mCellDistance, std::min(y, mCurY) + mCellDistance };
int curX = 0;
int curY = 0;
if (mActiveCell)
{
curX = mActiveCell->getGridX();
curY = mActiveCell->getGridY();
}
const MyGUI::IntRect intersection = { std::max(x, curX) - mCellDistance, std::max(y, curY) - mCellDistance,
std::min(x, curX) + mCellDistance, std::min(y, curY) + mCellDistance };
const MyGUI::IntRect activeGrid = createRect({ x, y }, Constants::CellGridRadius);
const MyGUI::IntRect currentView = createRect({ x, y }, mCellDistance);
@ -407,17 +416,14 @@ namespace MWGui
for (auto& widget : mDoorMarkersToRecycle)
widget->setVisible(false);
for (auto const& cell : mMaps)
for (auto const& entry : mMaps)
{
if (mHasALastActiveCell && !intersection.inside({ cell.mCellX, cell.mCellY }))
mLocalMapRender->removeExteriorCell(cell.mCellX, cell.mCellY);
if (mHasALastActiveCell && !intersection.inside({ entry.mCellX, entry.mCellY }))
mLocalMapRender->removeExteriorCell(entry.mCellX, entry.mCellY);
}
}
mCurX = x;
mCurY = y;
mInterior = interior;
mChanged = false;
mActiveCell = &cell;
for (int mx = 0; mx < mNumCells; ++mx)
{
@ -441,7 +447,7 @@ namespace MWGui
for (MyGUI::Widget* widget : currentDoorMarkersWidgets())
widget->setCoord(getMarkerCoordinates(widget, 8));
if (!mInterior)
if (mActiveCell->isExterior())
mHasALastActiveCell = true;
updateMagicMarkers();
@ -580,7 +586,7 @@ namespace MWGui
if (!entry.mMapTexture)
{
if (!mInterior)
if (mActiveCell->isExterior())
requestMapRender(&MWBase::Environment::get().getWorldModel()->getExterior(
ESM::ExteriorCellLocation(entry.mCellX, entry.mCellY, ESM::Cell::sDefaultWorldspaceId)));
@ -626,12 +632,12 @@ namespace MWGui
mDoorMarkersToRecycle.end(), mInteriorDoorMarkerWidgets.begin(), mInteriorDoorMarkerWidgets.end());
mInteriorDoorMarkerWidgets.clear();
if (mInterior)
if (!mActiveCell->isExterior())
{
for (MyGUI::Widget* widget : mExteriorDoorMarkerWidgets)
widget->setVisible(false);
MWWorld::CellStore& cell = worldModel->getInterior(mPrefix);
MWWorld::CellStore& cell = worldModel->getInterior(mActiveCell->getNameId());
world->getDoorMarkers(cell, doors);
}
else
@ -678,7 +684,7 @@ namespace MWGui
}
currentDoorMarkersWidgets().push_back(markerWidget);
if (!mInterior)
if (mActiveCell->isExterior())
mExteriorDoorsByCell[{ data->cellX, data->cellY }].push_back(markerWidget);
}
@ -701,8 +707,7 @@ namespace MWGui
MWWorld::CellStore* markedCell = nullptr;
ESM::Position markedPosition;
MWBase::Environment::get().getWorld()->getPlayer().getMarkedPosition(markedCell, markedPosition);
if (markedCell && markedCell->isExterior() == !mInterior
&& (!mInterior || Misc::StringUtils::ciEqual(markedCell->getCell()->getNameId(), mPrefix)))
if (markedCell && markedCell->getCell()->getWorldSpace() == mActiveCell->getWorldSpace())
{
MarkerUserData markerPos(mLocalMapRender);
MyGUI::ImageBox* markerWidget = mLocalMap->createWidget<MyGUI::ImageBox>("ImageBox",
@ -870,11 +875,11 @@ namespace MWGui
int y = (int(widgetPos.top / float(mapWidgetSize)) - mCellDistance) * -1;
float nX = widgetPos.left / float(mapWidgetSize) - int(widgetPos.left / float(mapWidgetSize));
float nY = widgetPos.top / float(mapWidgetSize) - int(widgetPos.top / float(mapWidgetSize));
x += mCurX;
y += mCurY;
x += mActiveCell->getGridX();
y += mActiveCell->getGridY();
osg::Vec2f worldPos;
if (mInterior)
if (!mActiveCell->isExterior())
{
worldPos = mLocalMapRender->interiorMapToWorldPosition(nX, nY, x, y);
}
@ -886,7 +891,7 @@ namespace MWGui
mEditingMarker.mWorldX = worldPos.x();
mEditingMarker.mWorldY = worldPos.y();
ESM::RefId clickedId = ESM::Cell::generateIdForCell(!mInterior, LocalMapBase::mPrefix, x, y);
ESM::RefId clickedId = getCellIdInWorldSpace(*mActiveCell, x, y);
mEditingMarker.mCell = clickedId;
@ -977,7 +982,7 @@ namespace MWGui
resizeGlobalMap();
float x = mCurPos.x(), y = mCurPos.y();
if (mInterior)
if (!mActiveCell->isExterior())
{
auto pos = MWBase::Environment::get().getWorld()->getPlayer().getLastKnownExteriorPosition();
x = pos.x();
@ -1020,7 +1025,7 @@ namespace MWGui
resizeGlobalMap();
}
MapWindow::~MapWindow() {}
MapWindow::~MapWindow() = default;
void MapWindow::setCellName(const std::string& cellName)
{
@ -1289,7 +1294,7 @@ namespace MWGui
mMarkers.clear();
mGlobalMapRender->clear();
mChanged = true;
mActiveCell = nullptr;
for (auto& widgetPair : mGlobalMapMarkers)
MyGUI::Gui::getInstance().destroyWidget(widgetPair.first.widget);

@ -27,6 +27,7 @@ namespace ESM
namespace MWWorld
{
class Cell;
class CellStore;
}
@ -77,8 +78,7 @@ namespace MWGui
virtual ~LocalMapBase();
void init(MyGUI::ScrollView* widget, MyGUI::ImageBox* compass, int cellDistance = Constants::CellGridRadius);
void setCellPrefix(const std::string& prefix);
void setActiveCell(const int x, const int y, bool interior = false);
void setActiveCell(const MWWorld::Cell& cell);
void requestMapRender(const MWWorld::CellStore* cell);
void setPlayerDir(const float x, const float y);
void setPlayerPos(int cellX, int cellY, const float nx, const float ny);
@ -115,15 +115,12 @@ namespace MWGui
float mLocalMapZoom = 1.f;
MWRender::LocalMap* mLocalMapRender;
int mCurX, mCurY; // the position of the active cell on the global map (in cell coords)
const MWWorld::Cell* mActiveCell;
bool mHasALastActiveCell = false;
osg::Vec2f mCurPos; // the position of the player in the world (in cell coords)
bool mInterior;
MyGUI::ScrollView* mLocalMap;
MyGUI::ImageBox* mCompass;
std::string mPrefix;
bool mChanged;
bool mFogOfWarToggled;
bool mFogOfWarEnabled;

@ -28,7 +28,7 @@ namespace MWGui
MessageBoxManager::clear();
}
int MessageBoxManager::getMessagesCount()
std::size_t MessageBoxManager::getMessagesCount()
{
return mMessageBoxes.size();
}

@ -29,7 +29,7 @@ namespace MWGui
bool immediate = false, int defaultFocus = -1);
bool isInteractiveMessageBox();
int getMessagesCount();
std::size_t getMessagesCount();
const InteractiveMessageBox* getInteractiveMessageBox() const { return mInterMessageBoxe.get(); }

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

Loading…
Cancel
Save